From 82c77fa244d8cbc1d09c985bf788fd60d7ac64a0 Mon Sep 17 00:00:00 2001 From: Raphael Graf Date: Thu, 10 Jan 2019 11:54:45 +0100 Subject: [PATCH 01/23] dnn: remove malloc.h include --- modules/dnn/src/torch/THGeneral.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/modules/dnn/src/torch/THGeneral.cpp b/modules/dnn/src/torch/THGeneral.cpp index 8a52745770..0c27edc6fb 100644 --- a/modules/dnn/src/torch/THGeneral.cpp +++ b/modules/dnn/src/torch/THGeneral.cpp @@ -1,10 +1,2 @@ #include "../precomp.hpp" - -#if defined(TH_DISABLE_HEAP_TRACKING) -#elif (defined(__unix) || defined(_WIN32)) -#include -#elif defined(__APPLE__) -#include -#endif - #include "THGeneral.h" From 4ae5df550995467384fddd80f8a5955ad026d84c Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 11 Jan 2019 10:50:52 +0300 Subject: [PATCH 02/23] SSD with FPN proposals from TensorFlow --- samples/dnn/tf_text_graph_common.py | 2 +- samples/dnn/tf_text_graph_faster_rcnn.py | 2 +- samples/dnn/tf_text_graph_mask_rcnn.py | 2 +- samples/dnn/tf_text_graph_ssd.py | 168 +++++++++++++++++------ 4 files changed, 132 insertions(+), 42 deletions(-) diff --git a/samples/dnn/tf_text_graph_common.py b/samples/dnn/tf_text_graph_common.py index bf04c42174..a644420780 100644 --- a/samples/dnn/tf_text_graph_common.py +++ b/samples/dnn/tf_text_graph_common.py @@ -289,7 +289,7 @@ def removeUnusedNodesAndAttrs(to_remove, graph_def): op = graph_def.node[i].op name = graph_def.node[i].name - if op == 'Const' or to_remove(name, op): + if to_remove(name, op): if op != 'Const': removedNodes.append(name) diff --git a/samples/dnn/tf_text_graph_faster_rcnn.py b/samples/dnn/tf_text_graph_faster_rcnn.py index 13a9c29ec0..e3d0ad0127 100644 --- a/samples/dnn/tf_text_graph_faster_rcnn.py +++ b/samples/dnn/tf_text_graph_faster_rcnn.py @@ -49,7 +49,7 @@ def createFasterRCNNGraph(modelPath, configPath, outputPath): removeIdentity(graph_def) def to_remove(name, op): - return name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep) or \ + return op == 'Const' or name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep) or \ (name.startswith('CropAndResize') and op != 'CropAndResize') removeUnusedNodesAndAttrs(to_remove, graph_def) diff --git a/samples/dnn/tf_text_graph_mask_rcnn.py b/samples/dnn/tf_text_graph_mask_rcnn.py index aaefe456ad..c8803088f9 100644 --- a/samples/dnn/tf_text_graph_mask_rcnn.py +++ b/samples/dnn/tf_text_graph_mask_rcnn.py @@ -55,7 +55,7 @@ graph_def = parseTextGraph(args.output) removeIdentity(graph_def) def to_remove(name, op): - return name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep) or \ + return op == 'Const' or name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep) or \ (name.startswith('CropAndResize') and op != 'CropAndResize') removeUnusedNodesAndAttrs(to_remove, graph_def) diff --git a/samples/dnn/tf_text_graph_ssd.py b/samples/dnn/tf_text_graph_ssd.py index 5017dba7a7..1576380646 100644 --- a/samples/dnn/tf_text_graph_ssd.py +++ b/samples/dnn/tf_text_graph_ssd.py @@ -10,14 +10,60 @@ # Then you can import it with a binary frozen graph (.pb) using readNetFromTensorflow() function. # See details and examples on the following wiki page: https://github.com/opencv/opencv/wiki/TensorFlow-Object-Detection-API import argparse +import re from math import sqrt from tf_text_graph_common import * +class SSDAnchorGenerator: + def __init__(self, min_scale, max_scale, num_layers, aspect_ratios, + reduce_boxes_in_lowest_layer, image_width, image_height): + self.min_scale = min_scale + self.aspect_ratios = aspect_ratios + self.reduce_boxes_in_lowest_layer = reduce_boxes_in_lowest_layer + self.image_width = image_width + self.image_height = image_height + self.scales = [min_scale + (max_scale - min_scale) * i / (num_layers - 1) + for i in range(num_layers)] + [1.0] + + def get(self, layer_id): + if layer_id == 0 and self.reduce_boxes_in_lowest_layer: + widths = [0.1, self.min_scale * sqrt(2.0), self.min_scale * sqrt(0.5)] + heights = [0.1, self.min_scale / sqrt(2.0), self.min_scale / sqrt(0.5)] + else: + widths = [self.scales[layer_id] * sqrt(ar) for ar in self.aspect_ratios] + heights = [self.scales[layer_id] / sqrt(ar) for ar in self.aspect_ratios] + + widths += [sqrt(self.scales[layer_id] * self.scales[layer_id + 1])] + heights += [sqrt(self.scales[layer_id] * self.scales[layer_id + 1])] + widths = [w * self.image_width for w in widths] + heights = [h * self.image_height for h in heights] + return widths, heights + + +class MultiscaleAnchorGenerator: + def __init__(self, min_level, aspect_ratios, scales_per_octave, anchor_scale): + self.min_level = min_level + self.aspect_ratios = aspect_ratios + self.anchor_scale = anchor_scale + self.scales = [2**(float(s) / scales_per_octave) for s in range(scales_per_octave)] + + def get(self, layer_id): + widths = [] + heights = [] + for a in self.aspect_ratios: + for s in self.scales: + base_anchor_size = 2**(self.min_level + layer_id) * self.anchor_scale + ar = sqrt(a) + heights.append(base_anchor_size * s / ar) + widths.append(base_anchor_size * s * ar) + return widths, heights + + def createSSDGraph(modelPath, configPath, outputPath): # Nodes that should be kept. - keepOps = ['Conv2D', 'BiasAdd', 'Add', 'Relu6', 'Placeholder', 'FusedBatchNorm', + keepOps = ['Conv2D', 'BiasAdd', 'Add', 'Relu', 'Relu6', 'Placeholder', 'FusedBatchNorm', 'DepthwiseConv2dNative', 'ConcatV2', 'Mul', 'MaxPool', 'AvgPool', 'Identity', - 'Sub'] + 'Sub', 'ResizeNearestNeighbor', 'Pad'] # Node with which prefixes should be removed prefixesToRemove = ('MultipleGridAnchorGenerator/', 'Postprocessor/', 'Preprocessor/map') @@ -27,26 +73,50 @@ def createSSDGraph(modelPath, configPath, outputPath): config = config['model'][0]['ssd'][0] num_classes = int(config['num_classes'][0]) - ssd_anchor_generator = config['anchor_generator'][0]['ssd_anchor_generator'][0] - min_scale = float(ssd_anchor_generator['min_scale'][0]) - max_scale = float(ssd_anchor_generator['max_scale'][0]) - num_layers = int(ssd_anchor_generator['num_layers'][0]) - aspect_ratios = [float(ar) for ar in ssd_anchor_generator['aspect_ratios']] - reduce_boxes_in_lowest_layer = True - if 'reduce_boxes_in_lowest_layer' in ssd_anchor_generator: - reduce_boxes_in_lowest_layer = ssd_anchor_generator['reduce_boxes_in_lowest_layer'][0] == 'true' - fixed_shape_resizer = config['image_resizer'][0]['fixed_shape_resizer'][0] image_width = int(fixed_shape_resizer['width'][0]) image_height = int(fixed_shape_resizer['height'][0]) box_predictor = 'convolutional' if 'convolutional_box_predictor' in config['box_predictor'][0] else 'weight_shared_convolutional' + anchor_generator = config['anchor_generator'][0] + if 'ssd_anchor_generator' in anchor_generator: + ssd_anchor_generator = anchor_generator['ssd_anchor_generator'][0] + min_scale = float(ssd_anchor_generator['min_scale'][0]) + max_scale = float(ssd_anchor_generator['max_scale'][0]) + num_layers = int(ssd_anchor_generator['num_layers'][0]) + aspect_ratios = [float(ar) for ar in ssd_anchor_generator['aspect_ratios']] + reduce_boxes_in_lowest_layer = True + if 'reduce_boxes_in_lowest_layer' in ssd_anchor_generator: + reduce_boxes_in_lowest_layer = ssd_anchor_generator['reduce_boxes_in_lowest_layer'][0] == 'true' + priors_generator = SSDAnchorGenerator(min_scale, max_scale, num_layers, + aspect_ratios, reduce_boxes_in_lowest_layer, + image_width, image_height) + + + print('Scale: [%f-%f]' % (min_scale, max_scale)) + print('Aspect ratios: %s' % str(aspect_ratios)) + print('Reduce boxes in the lowest layer: %s' % str(reduce_boxes_in_lowest_layer)) + elif 'multiscale_anchor_generator' in anchor_generator: + multiscale_anchor_generator = anchor_generator['multiscale_anchor_generator'][0] + min_level = int(multiscale_anchor_generator['min_level'][0]) + max_level = int(multiscale_anchor_generator['max_level'][0]) + anchor_scale = float(multiscale_anchor_generator['anchor_scale'][0]) + aspect_ratios = [float(ar) for ar in multiscale_anchor_generator['aspect_ratios']] + scales_per_octave = int(multiscale_anchor_generator['scales_per_octave'][0]) + num_layers = max_level - min_level + 1 + priors_generator = MultiscaleAnchorGenerator(min_level, aspect_ratios, + scales_per_octave, anchor_scale) + print('Levels: [%d-%d]' % (min_level, max_level)) + print('Anchor scale: %f' % anchor_scale) + print('Scales per octave: %d' % scales_per_octave) + print('Aspect ratios: %s' % str(aspect_ratios)) + else: + print('Unknown anchor_generator') + exit(0) + print('Number of classes: %d' % num_classes) print('Number of layers: %d' % num_layers) - print('Scale: [%f-%f]' % (min_scale, max_scale)) - print('Aspect ratios: %s' % str(aspect_ratios)) - print('Reduce boxes in the lowest layer: %s' % str(reduce_boxes_in_lowest_layer)) print('box predictor: %s' % box_predictor) print('Input image size: %dx%d' % (image_width, image_height)) @@ -67,8 +137,8 @@ def createSSDGraph(modelPath, configPath, outputPath): return unconnected - # Detect unfused batch normalization nodes and fuse them. - def fuse_batch_normalization(): + def fuse_nodes(nodesToKeep): + # Detect unfused batch normalization nodes and fuse them. # Add_0 <-- moving_variance, add_y # Rsqrt <-- Add_0 # Mul_0 <-- Rsqrt, gamma @@ -77,9 +147,15 @@ def createSSDGraph(modelPath, configPath, outputPath): # Sub_0 <-- beta, Mul_2 # Add_1 <-- Mul_1, Sub_0 nodesMap = {node.name: node for node in graph_def.node} - subgraph = ['Add', + subgraphBatchNorm = ['Add', ['Mul', 'input', ['Mul', ['Rsqrt', ['Add', 'moving_variance', 'add_y']], 'gamma']], ['Sub', 'beta', ['Mul', 'moving_mean', 'Mul_0']]] + # Detect unfused nearest neighbor resize. + subgraphResizeNN = ['Reshape', + ['Mul', ['Reshape', 'input', ['Pack', 'shape_1', 'shape_2', 'shape_3', 'shape_4', 'shape_5']], + 'ones'], + ['Pack', ['StridedSlice', ['Shape', 'input'], 'stack', 'stack_1', 'stack_2'], + 'out_height', 'out_width', 'out_channels']] def checkSubgraph(node, targetNode, inputs, fusedNodes): op = targetNode[0] if node.op == op and (len(node.input) >= len(targetNode) - 1): @@ -100,7 +176,7 @@ def createSSDGraph(modelPath, configPath, outputPath): for node in graph_def.node: inputs = {} fusedNodes = [] - if checkSubgraph(node, subgraph, inputs, fusedNodes): + if checkSubgraph(node, subgraphBatchNorm, inputs, fusedNodes): name = node.name node.Clear() node.name = name @@ -112,15 +188,41 @@ def createSSDGraph(modelPath, configPath, outputPath): node.input.append(inputs['moving_variance']) node.addAttr('epsilon', 0.001) nodesToRemove += fusedNodes[1:] + + inputs = {} + fusedNodes = [] + if checkSubgraph(node, subgraphResizeNN, inputs, fusedNodes): + name = node.name + node.Clear() + node.name = name + node.op = 'ResizeNearestNeighbor' + node.input.append(inputs['input']) + node.input.append(name + '/output_shape') + + out_height_node = nodesMap[inputs['out_height']] + out_width_node = nodesMap[inputs['out_width']] + out_height = int(out_height_node.attr['value']['tensor'][0]['int_val'][0]) + out_width = int(out_width_node.attr['value']['tensor'][0]['int_val'][0]) + + shapeNode = NodeDef() + shapeNode.name = name + '/output_shape' + shapeNode.op = 'Const' + shapeNode.addAttr('value', [out_height, out_width]) + graph_def.node.insert(graph_def.node.index(node), shapeNode) + nodesToKeep.append(shapeNode.name) + + nodesToRemove += fusedNodes[1:] for node in nodesToRemove: graph_def.node.remove(node) - fuse_batch_normalization() + nodesToKeep = [] + fuse_nodes(nodesToKeep) removeIdentity(graph_def) def to_remove(name, op): - return (not op in keepOps) or name.startswith(prefixesToRemove) + return (not name in nodesToKeep) and \ + (op == 'Const' or (not op in keepOps) or name.startswith(prefixesToRemove)) removeUnusedNodesAndAttrs(to_remove, graph_def) @@ -169,19 +271,15 @@ def createSSDGraph(modelPath, configPath, outputPath): graph_def.node.extend([flatten]) addConcatNode('%s/concat' % label, concatInputs, 'concat/axis_flatten') - idx = 0 + num_matched_layers = 0 for node in graph_def.node: - if node.name == ('BoxPredictor_%d/BoxEncodingPredictor/Conv2D' % idx) or \ - node.name == ('WeightSharedConvolutionalBoxPredictor_%d/BoxPredictor/Conv2D' % idx) or \ - node.name == 'WeightSharedConvolutionalBoxPredictor/BoxPredictor/Conv2D': + if re.match('BoxPredictor_\d/BoxEncodingPredictor/Conv2D', node.name) or \ + re.match('WeightSharedConvolutionalBoxPredictor(_\d)*/BoxPredictor/Conv2D', node.name): node.addAttr('loc_pred_transposed', True) - idx += 1 - assert(idx == num_layers) + num_matched_layers += 1 + assert(num_matched_layers == num_layers) # Add layers that generate anchors (bounding boxes proposals). - scales = [min_scale + (max_scale - min_scale) * i / (num_layers - 1) - for i in range(num_layers)] + [1.0] - priorBoxes = [] for i in range(num_layers): priorBox = NodeDef() @@ -199,17 +297,8 @@ def createSSDGraph(modelPath, configPath, outputPath): priorBox.addAttr('flip', False) priorBox.addAttr('clip', False) - if i == 0 and reduce_boxes_in_lowest_layer: - widths = [0.1, min_scale * sqrt(2.0), min_scale * sqrt(0.5)] - heights = [0.1, min_scale / sqrt(2.0), min_scale / sqrt(0.5)] - else: - widths = [scales[i] * sqrt(ar) for ar in aspect_ratios] - heights = [scales[i] / sqrt(ar) for ar in aspect_ratios] + widths, heights = priors_generator.get(i) - widths += [sqrt(scales[i] * scales[i + 1])] - heights += [sqrt(scales[i] * scales[i + 1])] - widths = [w * image_width for w in widths] - heights = [h * image_height for h in heights] priorBox.addAttr('width', widths) priorBox.addAttr('height', heights) priorBox.addAttr('variance', [0.1, 0.1, 0.2, 0.2]) @@ -217,6 +306,7 @@ def createSSDGraph(modelPath, configPath, outputPath): graph_def.node.extend([priorBox]) priorBoxes.append(priorBox.name) + # Compare this layer's output with Postprocessor/Reshape addConcatNode('PriorBox/concat', priorBoxes, 'concat/axis_flatten') # Sigmoid for classes predictions and DetectionOutput layer From 78bd55c8df9bef7b87c4cdce029b805f58345fd5 Mon Sep 17 00:00:00 2001 From: Jim Zhou Date: Fri, 11 Jan 2019 21:58:47 +0800 Subject: [PATCH 03/23] Merge pull request #13601 from JimZhou-001:JimZhou-001 * Fix the bug in case determinant of rotation matrix is -1 * calib3d(test): check det(R) == 1 --- modules/calib3d/src/homography_decomp.cpp | 8 +++++ .../calib3d/test/test_homography_decomp.cpp | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/modules/calib3d/src/homography_decomp.cpp b/modules/calib3d/src/homography_decomp.cpp index fea8882c5a..3bfb62ec2c 100644 --- a/modules/calib3d/src/homography_decomp.cpp +++ b/modules/calib3d/src/homography_decomp.cpp @@ -185,6 +185,10 @@ bool HomographyDecompZhang::findMotionFrom_tstar_n(const cv::Vec3d& tstar, const temp(1, 1) += 1.0; temp(2, 2) += 1.0; motion.R = getHnorm() * temp.inv(); + if (cv::determinant(motion.R) < 0) + { + motion.R *= -1; + } motion.t = motion.R * tstar; motion.n = n; return passesSameSideOfPlaneConstraint(motion); @@ -312,6 +316,10 @@ void HomographyDecompInria::findRmatFrom_tstar_n(const cv::Vec3d& tstar, const c 0.0, 0.0, 1.0); R = getHnorm() * (I - (2/v) * tstar_m * n_m.t() ); + if (cv::determinant(R) < 0) + { + R *= -1; + } } void HomographyDecompInria::decompose(std::vector& camMotions) diff --git a/modules/calib3d/test/test_homography_decomp.cpp b/modules/calib3d/test/test_homography_decomp.cpp index 45f5ae63ee..9ddc0e913d 100644 --- a/modules/calib3d/test/test_homography_decomp.cpp +++ b/modules/calib3d/test/test_homography_decomp.cpp @@ -134,4 +134,36 @@ private: TEST(Calib3d_DecomposeHomography, regression) { CV_HomographyDecompTest test; test.safe_run(); } + +TEST(Calib3d_DecomposeHomography, issue_4978) +{ + Matx33d K( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ); + + Matx33d H( + -0.102896, 0.270191, -0.0031153, + 0.0406387, 1.19569, -0.0120456, + 0.445351, 0.0410889, 1 + ); + + vector rotations; + vector translations; + vector normals; + + decomposeHomographyMat(H, K, rotations, translations, normals); + + ASSERT_GT(rotations.size(), (size_t)0u); + for (size_t i = 0; i < rotations.size(); i++) + { + // check: det(R) = 1 + EXPECT_TRUE(std::fabs(cv::determinant(rotations[i]) - 1.0) < 0.01) + << "R: det=" << cv::determinant(rotations[0]) << std::endl << rotations[i] << std::endl + << "T:" << std::endl << translations[i] << std::endl; + } +} + + }} // namespace From ea882d58c65eb5f8198bffbdf253822129fce0dc Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Fri, 11 Jan 2019 22:40:35 +0300 Subject: [PATCH 04/23] Added CV_ALWAYS_INLINE macro --- modules/core/include/opencv2/core/cvdef.h | 10 ++++++++++ modules/imgproc/src/fixedpoint.inl.hpp | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index afc603ab33..deb08fa003 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -200,6 +200,16 @@ namespace cv { namespace debug_build_guard { } using namespace debug_build_guard # endif #endif +#ifndef CV_ALWAYS_INLINE +#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define CV_ALWAYS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define CV_ALWAYS_INLINE __forceinline +#else +#define CV_ALWAYS_INLINE inline +#endif +#endif + #if defined CV_DISABLE_OPTIMIZATION || (defined CV_ICC && !defined CV_ENABLE_UNROLLED) # define CV_ENABLE_UNROLLED 0 #else diff --git a/modules/imgproc/src/fixedpoint.inl.hpp b/modules/imgproc/src/fixedpoint.inl.hpp index 0878dc456f..a1a75a29e1 100644 --- a/modules/imgproc/src/fixedpoint.inl.hpp +++ b/modules/imgproc/src/fixedpoint.inl.hpp @@ -11,16 +11,6 @@ #include "opencv2/core/softfloat.hpp" -#ifndef CV_ALWAYS_INLINE - #if defined(__GNUC__) && (__GNUC__ > 3 ||(__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) - #define CV_ALWAYS_INLINE inline __attribute__((always_inline)) - #elif defined(_MSC_VER) - #define CV_ALWAYS_INLINE __forceinline - #else - #define CV_ALWAYS_INLINE inline - #endif -#endif - namespace { From 6bd5d7f0378ada407f76b02c6045fd69f987dd37 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 12 Jan 2019 11:14:18 +0000 Subject: [PATCH 05/23] cmake: don't use LIB_SUFFIX with CMAKE_INSTALL_LIBDIR --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f17220b9e0..930e48c787 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -592,7 +592,7 @@ else() ocv_update(OPENCV_CONFIG_INSTALL_PATH ".") else() include(GNUInstallDirs) - ocv_update(OPENCV_LIB_INSTALL_PATH ${CMAKE_INSTALL_LIBDIR}${LIB_SUFFIX}) + ocv_update(OPENCV_LIB_INSTALL_PATH ${CMAKE_INSTALL_LIBDIR}) ocv_update(OPENCV_3P_LIB_INSTALL_PATH share/OpenCV/3rdparty/${OPENCV_LIB_INSTALL_PATH}) ocv_update(OPENCV_SAMPLES_SRC_INSTALL_PATH share/OpenCV/samples) ocv_update(OPENCV_JAR_INSTALL_PATH share/OpenCV/java) @@ -601,7 +601,7 @@ else() if(NOT DEFINED OPENCV_CONFIG_INSTALL_PATH) math(EXPR SIZEOF_VOID_P_BITS "8 * ${CMAKE_SIZEOF_VOID_P}") if(LIB_SUFFIX AND NOT SIZEOF_VOID_P_BITS EQUAL LIB_SUFFIX) - ocv_update(OPENCV_CONFIG_INSTALL_PATH ${CMAKE_INSTALL_LIBDIR}${LIB_SUFFIX}/cmake/opencv) + ocv_update(OPENCV_CONFIG_INSTALL_PATH ${CMAKE_INSTALL_LIBDIR}/cmake/opencv) else() ocv_update(OPENCV_CONFIG_INSTALL_PATH share/OpenCV) endif() From e48682a9f7cba8767d8fea69213f44ca5b0726df Mon Sep 17 00:00:00 2001 From: atinfinity Date: Sun, 13 Jan 2019 18:34:05 +0900 Subject: [PATCH 06/23] Merge pull request #13616 from atinfinity:fixed-py_matcher-tutorial * fixed tutorial code of py_matcher * fixed imread mode --- .../py_matcher/py_matcher.markdown | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.markdown b/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.markdown index 8ab4c53908..ca7853d96a 100644 --- a/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.markdown +++ b/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.markdown @@ -53,8 +53,8 @@ import numpy as np import cv2 as cv import matplotlib.pyplot as plt -img1 = cv.imread('box.png',0) # queryImage -img2 = cv.imread('box_in_scene.png',0) # trainImage +img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # queryImage +img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # trainImage # Initiate ORB detector orb = cv.ORB_create() @@ -79,7 +79,7 @@ matches = bf.match(des1,des2) matches = sorted(matches, key = lambda x:x.distance) # Draw first 10 matches. -img3 = cv.drawMatches(img1,kp1,img2,kp2,matches[:10], flags=2) +img3 = cv.drawMatches(img1,kp1,img2,kp2,matches[:10],None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) plt.imshow(img3),plt.show() @endcode @@ -104,13 +104,13 @@ so that we can apply ratio test explained by D.Lowe in his paper. @code{.py} import numpy as np import cv2 as cv -from matplotlib import pyplot as plt +import matplotlib.pyplot as plt -img1 = cv.imread('box.png',0) # queryImage -img2 = cv.imread('box_in_scene.png',0) # trainImage +img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # queryImage +img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # trainImage # Initiate SIFT detector -sift = cv.SIFT() +sift = cv.xfeatures2d.SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(img1,None) @@ -118,7 +118,7 @@ kp2, des2 = sift.detectAndCompute(img2,None) # BFMatcher with default params bf = cv.BFMatcher() -matches = bf.knnMatch(des1,des2, k=2) +matches = bf.knnMatch(des1,des2,k=2) # Apply ratio test good = [] @@ -127,7 +127,7 @@ for m,n in matches: good.append([m]) # cv.drawMatchesKnn expects list of lists as matches. -img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,good,flags=2) +img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) plt.imshow(img3),plt.show() @endcode @@ -168,13 +168,13 @@ With this information, we are good to go. @code{.py} import numpy as np import cv2 as cv -from matplotlib import pyplot as plt +import matplotlib.pyplot as plt -img1 = cv.imread('box.png',0) # queryImage -img2 = cv.imread('box_in_scene.png',0) # trainImage +img1 = cv.imread('box.png',cv.IMREAD_GRAYSCALE) # queryImage +img2 = cv.imread('box_in_scene.png',cv.IMREAD_GRAYSCALE) # trainImage # Initiate SIFT detector -sift = cv.SIFT() +sift = cv.xfeatures2d.SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(img1,None) @@ -190,7 +190,7 @@ flann = cv.FlannBasedMatcher(index_params,search_params) matches = flann.knnMatch(des1,des2,k=2) # Need to draw only good matches, so create a mask -matchesMask = [[0,0] for i in xrange(len(matches))] +matchesMask = [[0,0] for i in range(len(matches))] # ratio test as per Lowe's paper for i,(m,n) in enumerate(matches): @@ -200,7 +200,7 @@ for i,(m,n) in enumerate(matches): draw_params = dict(matchColor = (0,255,0), singlePointColor = (255,0,0), matchesMask = matchesMask, - flags = 0) + flags = cv.DrawMatchesFlags_DEFAULT) img3 = cv.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params) From 4366c8734fa7cf69c4a9bdcea91f41d1af345e9c Mon Sep 17 00:00:00 2001 From: Namgoo Lee Date: Fri, 11 Jan 2019 04:00:08 +0000 Subject: [PATCH 07/23] Fix Farneback Optical Flow Algorithm - Before this PR, following tests failed on some platform. CUDA_OptFlow/FarnebackOpticalFlow.Accuracy/19 CUDA_OptFlow/FarnebackOpticalFlow.Accuracy/23 - The algorithm now recognizes the OPTFLOW_USE_INITIAL_FLOW flag. Previously, when the flag was set, it did not use the flow data passed as input, instead used some garbage data in memory. - More strict test limit. --- modules/cudaoptflow/src/farneback.cpp | 29 +++++++++++++++++------ modules/cudaoptflow/test/test_optflow.cpp | 10 +++++++- modules/video/src/optflowgf.cpp | 24 ++++++++++++++----- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/modules/cudaoptflow/src/farneback.cpp b/modules/cudaoptflow/src/farneback.cpp index 43032b4f8f..69ea437ec4 100644 --- a/modules/cudaoptflow/src/farneback.cpp +++ b/modules/cudaoptflow/src/farneback.cpp @@ -165,10 +165,29 @@ namespace { const GpuMat frame0 = _frame0.getGpuMat(); const GpuMat frame1 = _frame1.getGpuMat(); + GpuMat flow = _flow.getGpuMat(); - BufferPool pool(stream); - GpuMat flowx = pool.getBuffer(frame0.size(), CV_32FC1); - GpuMat flowy = pool.getBuffer(frame0.size(), CV_32FC1); + CV_Assert(frame0.channels() == 1 && frame1.channels() == 1); + CV_Assert(frame0.size() == frame1.size()); + + GpuMat flowx, flowy; + + // If flag is set, check for integrity; if not set, allocate memory space + if (flags_ & OPTFLOW_USE_INITIAL_FLOW) + { + CV_Assert(flow.size() == frame0.size() && flow.channels() == 2 && + flow.depth() == CV_32F); + + std::vector _flows(2); + cuda::split(flow, _flows, stream); + flowx = _flows[0]; + flowy = _flows[1]; + } + else + { + flowx.create(frame0.size(), CV_32FC1); + flowy.create(frame0.size(), CV_32FC1); + } calcImpl(frame0, frame1, flowx, flowy, stream); @@ -291,8 +310,6 @@ namespace void FarnebackOpticalFlowImpl::calcImpl(const GpuMat &frame0, const GpuMat &frame1, GpuMat &flowx, GpuMat &flowy, Stream &stream) { - CV_Assert(frame0.channels() == 1 && frame1.channels() == 1); - CV_Assert(frame0.size() == frame1.size()); CV_Assert(polyN_ == 5 || polyN_ == 7); CV_Assert(!fastPyramids_ || std::abs(pyrScale_ - 0.5) < 1e-6); @@ -303,8 +320,6 @@ namespace Size size = frame0.size(); GpuMat prevFlowX, prevFlowY, curFlowX, curFlowY; - flowx.create(size, CV_32F); - flowy.create(size, CV_32F); GpuMat flowx0 = flowx; GpuMat flowy0 = flowy; diff --git a/modules/cudaoptflow/test/test_optflow.cpp b/modules/cudaoptflow/test/test_optflow.cpp index c856474363..37ffe9e5c4 100644 --- a/modules/cudaoptflow/test/test_optflow.cpp +++ b/modules/cudaoptflow/test/test_optflow.cpp @@ -337,7 +337,15 @@ CUDA_TEST_P(FarnebackOpticalFlow, Accuracy) frame0, frame1, flow, farn->getPyrScale(), farn->getNumLevels(), farn->getWinSize(), farn->getNumIters(), farn->getPolyN(), farn->getPolySigma(), farn->getFlags()); - EXPECT_MAT_SIMILAR(flow, d_flow, 0.1); + // Relax test limit when the flag is set + if (farn->getFlags() & cv::OPTFLOW_FARNEBACK_GAUSSIAN) + { + EXPECT_MAT_SIMILAR(flow, d_flow, 2e-2); + } + else + { + EXPECT_MAT_SIMILAR(flow, d_flow, 1e-4); + } } INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, FarnebackOpticalFlow, testing::Combine( diff --git a/modules/video/src/optflowgf.cpp b/modules/video/src/optflowgf.cpp index 2e6251b210..e06dbbf379 100644 --- a/modules/video/src/optflowgf.cpp +++ b/modules/video/src/optflowgf.cpp @@ -646,8 +646,6 @@ private: Size size = frame0.size(); UMat prevFlowX, prevFlowY, curFlowX, curFlowY; - flowx.create(size, CV_32F); - flowy.create(size, CV_32F); UMat flowx0 = flowx; UMat flowy0 = flowy; @@ -1075,12 +1073,19 @@ private: return false; std::vector flowar; - if (!_flow0.empty()) + + // If flag is set, check for integrity; if not set, allocate memory space + if (flags_ & OPTFLOW_USE_INITIAL_FLOW) + { + if (_flow0.empty() || _flow0.size() != _prev0.size() || _flow0.channels() != 2 || + _flow0.depth() != CV_32F) + return false; split(_flow0, flowar); + } else { - flowar.push_back(UMat()); - flowar.push_back(UMat()); + flowar.push_back(UMat(_prev0.size(), CV_32FC1)); + flowar.push_back(UMat(_prev0.size(), CV_32FC1)); } if(!this->operator()(_prev0.getUMat(), _next0.getUMat(), flowar[0], flowar[1])){ return false; @@ -1112,7 +1117,14 @@ void FarnebackOpticalFlowImpl::calc(InputArray _prev0, InputArray _next0, CV_Assert( prev0.size() == next0.size() && prev0.channels() == next0.channels() && prev0.channels() == 1 && pyrScale_ < 1 ); - _flow0.create( prev0.size(), CV_32FC2 ); + + // If flag is set, check for integrity; if not set, allocate memory space + if( flags_ & OPTFLOW_USE_INITIAL_FLOW ) + CV_Assert( _flow0.size() == prev0.size() && _flow0.channels() == 2 && + _flow0.depth() == CV_32F ); + else + _flow0.create( prev0.size(), CV_32FC2 ); + Mat flow0 = _flow0.getMat(); for( k = 0, scale = 1; k < levels; k++ ) From 012e43de4bd2243b429dd0b6231b26540a81e526 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Thu, 10 Jan 2019 19:08:55 +0300 Subject: [PATCH 08/23] Morphology reworked to use wide universal intrinsics --- modules/imgproc/src/filter.cpp | 2 +- modules/imgproc/src/morph.cpp | 595 ++++++++++++--------------------- 2 files changed, 220 insertions(+), 377 deletions(-) diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index 3d865408eb..43200218dc 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -213,7 +213,7 @@ int FilterEngine::start(const Size &_wholeSize, const Size &sz, const Point &ofs } // adjust bufstep so that the used part of the ring buffer stays compact in memory - bufStep = bufElemSize*(int)alignSize(roi.width + (!isSeparable() ? ksize.width - 1 : 0),16); + bufStep = bufElemSize*(int)alignSize(roi.width + (!isSeparable() ? ksize.width - 1 : 0),VEC_ALIGN); dx1 = std::max(anchor.x - roi.x, 0); dx2 = std::max(ksize.width - anchor.x - 1 + roi.x + roi.width - wholeSize.width, 0); diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index c724e3eb36..cb25a50c7b 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -45,6 +45,7 @@ #include "opencl_kernels_imgproc.hpp" #include #include "hal_replacement.hpp" +#include "opencv2/core/hal/intrin.hpp" #include /****************************************************************************************\ @@ -97,73 +98,65 @@ struct MorphNoVec int operator()(uchar**, int, uchar*, int) const { return 0; } }; -#if CV_SSE2 +#if CV_SIMD -template struct MorphRowIVec +template struct MorphRowVec { - enum { ESZ = VecUpdate::ESZ }; - - MorphRowIVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} + typedef typename VecUpdate::vtype vtype; + typedef typename vtype::lane_type stype; + MorphRowVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} int operator()(const uchar* src, uchar* dst, int width, int cn) const { - if( !checkHardwareSupport(CV_CPU_SSE2) ) - return 0; - - cn *= ESZ; int i, k, _ksize = ksize*cn; - width = (width & -4)*cn; + width *= cn; VecUpdate updateOp; - for( i = 0; i <= width - 16; i += 16 ) + for( i = 0; i <= width - 4*vtype::nlanes; i += 4*vtype::nlanes ) { - __m128i s = _mm_loadu_si128((const __m128i*)(src + i)); - for( k = cn; k < _ksize; k += cn ) + vtype s0 = vx_load((const stype*)src + i); + vtype s1 = vx_load((const stype*)src + i + vtype::nlanes); + vtype s2 = vx_load((const stype*)src + i + 2*vtype::nlanes); + vtype s3 = vx_load((const stype*)src + i + 3*vtype::nlanes); + for (k = cn; k < _ksize; k += cn) { - __m128i x = _mm_loadu_si128((const __m128i*)(src + i + k)); - s = updateOp(s, x); + s0 = updateOp(s0, vx_load((const stype*)src + i + k)); + s1 = updateOp(s1, vx_load((const stype*)src + i + k + vtype::nlanes)); + s2 = updateOp(s2, vx_load((const stype*)src + i + k + 2*vtype::nlanes)); + s3 = updateOp(s3, vx_load((const stype*)src + i + k + 3*vtype::nlanes)); } - _mm_storeu_si128((__m128i*)(dst + i), s); + v_store((stype*)dst + i, s0); + v_store((stype*)dst + i + vtype::nlanes, s1); + v_store((stype*)dst + i + 2*vtype::nlanes, s2); + v_store((stype*)dst + i + 3*vtype::nlanes, s3); } - - for( ; i < width; i += 4 ) + if( i <= width - 2*vtype::nlanes ) { - __m128i s = _mm_cvtsi32_si128(*(const int*)(src + i)); + vtype s0 = vx_load((const stype*)src + i); + vtype s1 = vx_load((const stype*)src + i + vtype::nlanes); for( k = cn; k < _ksize; k += cn ) { - __m128i x = _mm_cvtsi32_si128(*(const int*)(src + i + k)); - s = updateOp(s, x); + s0 = updateOp(s0, vx_load((const stype*)src + i + k)); + s1 = updateOp(s1, vx_load((const stype*)src + i + k + vtype::nlanes)); } - *(int*)(dst + i) = _mm_cvtsi128_si32(s); + v_store((stype*)dst + i, s0); + v_store((stype*)dst + i + vtype::nlanes, s1); + i += 2*vtype::nlanes; } - - return i/ESZ; - } - - int ksize, anchor; -}; - - -template struct MorphRowFVec -{ - MorphRowFVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} - int operator()(const uchar* src, uchar* dst, int width, int cn) const - { - if( !checkHardwareSupport(CV_CPU_SSE) ) - return 0; - - int i, k, _ksize = ksize*cn; - width = (width & -4)*cn; - VecUpdate updateOp; - - for( i = 0; i < width; i += 4 ) + if( i <= width - vtype::nlanes ) { - __m128 s = _mm_loadu_ps((const float*)src + i); + vtype s = vx_load((const stype*)src + i); for( k = cn; k < _ksize; k += cn ) - { - __m128 x = _mm_loadu_ps((const float*)src + i + k); - s = updateOp(s, x); - } - _mm_storeu_ps((float*)dst + i, s); + s = updateOp(s, vx_load((const stype*)src + i + k)); + v_store((stype*)dst + i, s); + i += vtype::nlanes; + } + if( i <= width - vtype::nlanes/2 ) + { + vtype s = vx_load_low((const stype*)src + i); + for( k = cn; k < _ksize; k += cn ) + s = updateOp(s, vx_load_low((const stype*)src + i + k)); + v_store_low((stype*)dst + i, s); + i += vtype::nlanes/2; } return i; @@ -173,230 +166,156 @@ template struct MorphRowFVec }; -template struct MorphColumnIVec +template struct MorphColumnVec { - enum { ESZ = VecUpdate::ESZ }; - - MorphColumnIVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} - int operator()(const uchar** src, uchar* dst, int dststep, int count, int width) const - { - if( !checkHardwareSupport(CV_CPU_SSE2) ) - return 0; - - int i = 0, k, _ksize = ksize; - width *= ESZ; - VecUpdate updateOp; - - for( i = 0; i < count + ksize - 1; i++ ) - CV_Assert( ((size_t)src[i] & 15) == 0 ); - - for( ; _ksize > 1 && count > 1; count -= 2, dst += dststep*2, src += 2 ) - { - for( i = 0; i <= width - 32; i += 32 ) - { - const uchar* sptr = src[1] + i; - __m128i s0 = _mm_load_si128((const __m128i*)sptr); - __m128i s1 = _mm_load_si128((const __m128i*)(sptr + 16)); - __m128i x0, x1; - - for( k = 2; k < _ksize; k++ ) - { - sptr = src[k] + i; - x0 = _mm_load_si128((const __m128i*)sptr); - x1 = _mm_load_si128((const __m128i*)(sptr + 16)); - s0 = updateOp(s0, x0); - s1 = updateOp(s1, x1); - } - - sptr = src[0] + i; - x0 = _mm_load_si128((const __m128i*)sptr); - x1 = _mm_load_si128((const __m128i*)(sptr + 16)); - _mm_storeu_si128((__m128i*)(dst + i), updateOp(s0, x0)); - _mm_storeu_si128((__m128i*)(dst + i + 16), updateOp(s1, x1)); - - sptr = src[k] + i; - x0 = _mm_load_si128((const __m128i*)sptr); - x1 = _mm_load_si128((const __m128i*)(sptr + 16)); - _mm_storeu_si128((__m128i*)(dst + dststep + i), updateOp(s0, x0)); - _mm_storeu_si128((__m128i*)(dst + dststep + i + 16), updateOp(s1, x1)); - } - - for( ; i <= width - 8; i += 8 ) - { - __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[1] + i)), x0; - - for( k = 2; k < _ksize; k++ ) - { - x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); - s0 = updateOp(s0, x0); - } - - x0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)); - _mm_storel_epi64((__m128i*)(dst + i), updateOp(s0, x0)); - x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); - _mm_storel_epi64((__m128i*)(dst + dststep + i), updateOp(s0, x0)); - } - } - - for( ; count > 0; count--, dst += dststep, src++ ) - { - for( i = 0; i <= width - 32; i += 32 ) - { - const uchar* sptr = src[0] + i; - __m128i s0 = _mm_load_si128((const __m128i*)sptr); - __m128i s1 = _mm_load_si128((const __m128i*)(sptr + 16)); - __m128i x0, x1; - - for( k = 1; k < _ksize; k++ ) - { - sptr = src[k] + i; - x0 = _mm_load_si128((const __m128i*)sptr); - x1 = _mm_load_si128((const __m128i*)(sptr + 16)); - s0 = updateOp(s0, x0); - s1 = updateOp(s1, x1); - } - _mm_storeu_si128((__m128i*)(dst + i), s0); - _mm_storeu_si128((__m128i*)(dst + i + 16), s1); - } - - for( ; i <= width - 8; i += 8 ) - { - __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)), x0; - - for( k = 1; k < _ksize; k++ ) - { - x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); - s0 = updateOp(s0, x0); - } - _mm_storel_epi64((__m128i*)(dst + i), s0); - } - } - - return i/ESZ; - } - - int ksize, anchor; -}; - - -template struct MorphColumnFVec -{ - MorphColumnFVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} + typedef typename VecUpdate::vtype vtype; + typedef typename vtype::lane_type stype; + MorphColumnVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} int operator()(const uchar** _src, uchar* _dst, int dststep, int count, int width) const { - if( !checkHardwareSupport(CV_CPU_SSE) ) - return 0; - int i = 0, k, _ksize = ksize; VecUpdate updateOp; for( i = 0; i < count + ksize - 1; i++ ) - CV_Assert( ((size_t)_src[i] & 15) == 0 ); + CV_Assert( ((size_t)_src[i] & (CV_SIMD_WIDTH-1)) == 0 ); - const float** src = (const float**)_src; - float* dst = (float*)_dst; + const stype** src = (const stype**)_src; + stype* dst = (stype*)_dst; dststep /= sizeof(dst[0]); for( ; _ksize > 1 && count > 1; count -= 2, dst += dststep*2, src += 2 ) { - for( i = 0; i <= width - 16; i += 16 ) + for( i = 0; i <= width - 4*vtype::nlanes; i += 4*vtype::nlanes) { - const float* sptr = src[1] + i; - __m128 s0 = _mm_load_ps(sptr); - __m128 s1 = _mm_load_ps(sptr + 4); - __m128 s2 = _mm_load_ps(sptr + 8); - __m128 s3 = _mm_load_ps(sptr + 12); - __m128 x0, x1, x2, x3; + const stype* sptr = src[1] + i; + vtype s0 = vx_load_aligned(sptr); + vtype s1 = vx_load_aligned(sptr + vtype::nlanes); + vtype s2 = vx_load_aligned(sptr + 2*vtype::nlanes); + vtype s3 = vx_load_aligned(sptr + 3*vtype::nlanes); for( k = 2; k < _ksize; k++ ) { sptr = src[k] + i; - x0 = _mm_load_ps(sptr); - x1 = _mm_load_ps(sptr + 4); - s0 = updateOp(s0, x0); - s1 = updateOp(s1, x1); - x2 = _mm_load_ps(sptr + 8); - x3 = _mm_load_ps(sptr + 12); - s2 = updateOp(s2, x2); - s3 = updateOp(s3, x3); + s0 = updateOp(s0, vx_load_aligned(sptr)); + s1 = updateOp(s1, vx_load_aligned(sptr + vtype::nlanes)); + s2 = updateOp(s2, vx_load_aligned(sptr + 2*vtype::nlanes)); + s3 = updateOp(s3, vx_load_aligned(sptr + 3*vtype::nlanes)); } sptr = src[0] + i; - x0 = _mm_load_ps(sptr); - x1 = _mm_load_ps(sptr + 4); - x2 = _mm_load_ps(sptr + 8); - x3 = _mm_load_ps(sptr + 12); - _mm_storeu_ps(dst + i, updateOp(s0, x0)); - _mm_storeu_ps(dst + i + 4, updateOp(s1, x1)); - _mm_storeu_ps(dst + i + 8, updateOp(s2, x2)); - _mm_storeu_ps(dst + i + 12, updateOp(s3, x3)); + v_store(dst + i, updateOp(s0, vx_load_aligned(sptr))); + v_store(dst + i + vtype::nlanes, updateOp(s1, vx_load_aligned(sptr + vtype::nlanes))); + v_store(dst + i + 2*vtype::nlanes, updateOp(s2, vx_load_aligned(sptr + 2*vtype::nlanes))); + v_store(dst + i + 3*vtype::nlanes, updateOp(s3, vx_load_aligned(sptr + 3*vtype::nlanes))); sptr = src[k] + i; - x0 = _mm_load_ps(sptr); - x1 = _mm_load_ps(sptr + 4); - x2 = _mm_load_ps(sptr + 8); - x3 = _mm_load_ps(sptr + 12); - _mm_storeu_ps(dst + dststep + i, updateOp(s0, x0)); - _mm_storeu_ps(dst + dststep + i + 4, updateOp(s1, x1)); - _mm_storeu_ps(dst + dststep + i + 8, updateOp(s2, x2)); - _mm_storeu_ps(dst + dststep + i + 12, updateOp(s3, x3)); + v_store(dst + dststep + i, updateOp(s0, vx_load_aligned(sptr))); + v_store(dst + dststep + i + vtype::nlanes, updateOp(s1, vx_load_aligned(sptr + vtype::nlanes))); + v_store(dst + dststep + i + 2*vtype::nlanes, updateOp(s2, vx_load_aligned(sptr + 2*vtype::nlanes))); + v_store(dst + dststep + i + 3*vtype::nlanes, updateOp(s3, vx_load_aligned(sptr + 3*vtype::nlanes))); } - - for( ; i <= width - 4; i += 4 ) + if( i <= width - 2*vtype::nlanes ) { - __m128 s0 = _mm_load_ps(src[1] + i), x0; + const stype* sptr = src[1] + i; + vtype s0 = vx_load_aligned(sptr); + vtype s1 = vx_load_aligned(sptr + vtype::nlanes); for( k = 2; k < _ksize; k++ ) { - x0 = _mm_load_ps(src[k] + i); - s0 = updateOp(s0, x0); + sptr = src[k] + i; + s0 = updateOp(s0, vx_load_aligned(sptr)); + s1 = updateOp(s1, vx_load_aligned(sptr + vtype::nlanes)); } - x0 = _mm_load_ps(src[0] + i); - _mm_storeu_ps(dst + i, updateOp(s0, x0)); - x0 = _mm_load_ps(src[k] + i); - _mm_storeu_ps(dst + dststep + i, updateOp(s0, x0)); + sptr = src[0] + i; + v_store(dst + i, updateOp(s0, vx_load_aligned(sptr))); + v_store(dst + i + vtype::nlanes, updateOp(s1, vx_load_aligned(sptr + vtype::nlanes))); + + sptr = src[k] + i; + v_store(dst + dststep + i, updateOp(s0, vx_load_aligned(sptr))); + v_store(dst + dststep + i + vtype::nlanes, updateOp(s1, vx_load_aligned(sptr + vtype::nlanes))); + i += 2*vtype::nlanes; + } + if( i <= width - vtype::nlanes ) + { + vtype s0 = vx_load_aligned(src[1] + i); + + for( k = 2; k < _ksize; k++ ) + s0 = updateOp(s0, vx_load_aligned(src[k] + i)); + + v_store(dst + i, updateOp(s0, vx_load_aligned(src[0] + i))); + v_store(dst + dststep + i, updateOp(s0, vx_load_aligned(src[k] + i))); + i += vtype::nlanes; + } + if( i <= width - vtype::nlanes/2 ) + { + vtype s0 = vx_load_low(src[1] + i); + + for( k = 2; k < _ksize; k++ ) + s0 = updateOp(s0, vx_load_low(src[k] + i)); + + v_store_low(dst + i, updateOp(s0, vx_load_low(src[0] + i))); + v_store_low(dst + dststep + i, updateOp(s0, vx_load_low(src[k] + i))); + i += vtype::nlanes/2; } } for( ; count > 0; count--, dst += dststep, src++ ) { - for( i = 0; i <= width - 16; i += 16 ) + for( i = 0; i <= width - 4*vtype::nlanes; i += 4*vtype::nlanes) { - const float* sptr = src[0] + i; - __m128 s0 = _mm_load_ps(sptr); - __m128 s1 = _mm_load_ps(sptr + 4); - __m128 s2 = _mm_load_ps(sptr + 8); - __m128 s3 = _mm_load_ps(sptr + 12); - __m128 x0, x1, x2, x3; + const stype* sptr = src[0] + i; + vtype s0 = vx_load_aligned(sptr); + vtype s1 = vx_load_aligned(sptr + vtype::nlanes); + vtype s2 = vx_load_aligned(sptr + 2*vtype::nlanes); + vtype s3 = vx_load_aligned(sptr + 3*vtype::nlanes); for( k = 1; k < _ksize; k++ ) { sptr = src[k] + i; - x0 = _mm_load_ps(sptr); - x1 = _mm_load_ps(sptr + 4); - s0 = updateOp(s0, x0); - s1 = updateOp(s1, x1); - x2 = _mm_load_ps(sptr + 8); - x3 = _mm_load_ps(sptr + 12); - s2 = updateOp(s2, x2); - s3 = updateOp(s3, x3); + s0 = updateOp(s0, vx_load_aligned(sptr)); + s1 = updateOp(s1, vx_load_aligned(sptr + vtype::nlanes)); + s2 = updateOp(s2, vx_load_aligned(sptr + 2*vtype::nlanes)); + s3 = updateOp(s3, vx_load_aligned(sptr + 3*vtype::nlanes)); } - _mm_storeu_ps(dst + i, s0); - _mm_storeu_ps(dst + i + 4, s1); - _mm_storeu_ps(dst + i + 8, s2); - _mm_storeu_ps(dst + i + 12, s3); + v_store(dst + i, s0); + v_store(dst + i + vtype::nlanes, s1); + v_store(dst + i + 2*vtype::nlanes, s2); + v_store(dst + i + 3*vtype::nlanes, s3); } - - for( i = 0; i <= width - 4; i += 4 ) + if( i <= width - 2*vtype::nlanes ) { - __m128 s0 = _mm_load_ps(src[0] + i), x0; + const stype* sptr = src[0] + i; + vtype s0 = vx_load_aligned(sptr); + vtype s1 = vx_load_aligned(sptr + vtype::nlanes); + for( k = 1; k < _ksize; k++ ) { - x0 = _mm_load_ps(src[k] + i); - s0 = updateOp(s0, x0); + sptr = src[k] + i; + s0 = updateOp(s0, vx_load_aligned(sptr)); + s1 = updateOp(s1, vx_load_aligned(sptr + vtype::nlanes)); } - _mm_storeu_ps(dst + i, s0); + v_store(dst + i, s0); + v_store(dst + i + vtype::nlanes, s1); + i += 2*vtype::nlanes; + } + if( i <= width - vtype::nlanes ) + { + vtype s0 = vx_load_aligned(src[0] + i); + + for( k = 1; k < _ksize; k++ ) + s0 = updateOp(s0, vx_load_aligned(src[k] + i)); + v_store(dst + i, s0); + i += vtype::nlanes; + } + if( i <= width - vtype::nlanes/2 ) + { + vtype s0 = vx_load_low(src[0] + i); + + for( k = 1; k < _ksize; k++ ) + s0 = updateOp(s0, vx_load_low(src[k] + i)); + v_store_low(dst + i, s0); + i += vtype::nlanes/2; } } @@ -407,185 +326,109 @@ template struct MorphColumnFVec }; -template struct MorphIVec -{ - enum { ESZ = VecUpdate::ESZ }; - - int operator()(uchar** src, int nz, uchar* dst, int width) const - { - if( !checkHardwareSupport(CV_CPU_SSE2) ) - return 0; - - int i, k; - width *= ESZ; - VecUpdate updateOp; - - for( i = 0; i <= width - 32; i += 32 ) - { - const uchar* sptr = src[0] + i; - __m128i s0 = _mm_loadu_si128((const __m128i*)sptr); - __m128i s1 = _mm_loadu_si128((const __m128i*)(sptr + 16)); - __m128i x0, x1; - - for( k = 1; k < nz; k++ ) - { - sptr = src[k] + i; - x0 = _mm_loadu_si128((const __m128i*)sptr); - x1 = _mm_loadu_si128((const __m128i*)(sptr + 16)); - s0 = updateOp(s0, x0); - s1 = updateOp(s1, x1); - } - _mm_storeu_si128((__m128i*)(dst + i), s0); - _mm_storeu_si128((__m128i*)(dst + i + 16), s1); - } - - for( ; i <= width - 8; i += 8 ) - { - __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)), x0; - - for( k = 1; k < nz; k++ ) - { - x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); - s0 = updateOp(s0, x0); - } - _mm_storel_epi64((__m128i*)(dst + i), s0); - } - - return i/ESZ; - } -}; - - -template struct MorphFVec +template struct MorphVec { + typedef typename VecUpdate::vtype vtype; + typedef typename vtype::lane_type stype; int operator()(uchar** _src, int nz, uchar* _dst, int width) const { - if( !checkHardwareSupport(CV_CPU_SSE) ) - return 0; - - const float** src = (const float**)_src; - float* dst = (float*)_dst; + const stype** src = (const stype**)_src; + stype* dst = (stype*)_dst; int i, k; VecUpdate updateOp; - for( i = 0; i <= width - 16; i += 16 ) + for( i = 0; i <= width - 4*vtype::nlanes; i += 4*vtype::nlanes ) { - const float* sptr = src[0] + i; - __m128 s0 = _mm_loadu_ps(sptr); - __m128 s1 = _mm_loadu_ps(sptr + 4); - __m128 s2 = _mm_loadu_ps(sptr + 8); - __m128 s3 = _mm_loadu_ps(sptr + 12); - __m128 x0, x1, x2, x3; - + const stype* sptr = src[0] + i; + vtype s0 = vx_load(sptr); + vtype s1 = vx_load(sptr + vtype::nlanes); + vtype s2 = vx_load(sptr + 2*vtype::nlanes); + vtype s3 = vx_load(sptr + 3*vtype::nlanes); for( k = 1; k < nz; k++ ) { sptr = src[k] + i; - x0 = _mm_loadu_ps(sptr); - x1 = _mm_loadu_ps(sptr + 4); - x2 = _mm_loadu_ps(sptr + 8); - x3 = _mm_loadu_ps(sptr + 12); - s0 = updateOp(s0, x0); - s1 = updateOp(s1, x1); - s2 = updateOp(s2, x2); - s3 = updateOp(s3, x3); + s0 = updateOp(s0, vx_load(sptr)); + s1 = updateOp(s1, vx_load(sptr + vtype::nlanes)); + s2 = updateOp(s2, vx_load(sptr + 2*vtype::nlanes)); + s3 = updateOp(s3, vx_load(sptr + 3*vtype::nlanes)); } - _mm_storeu_ps(dst + i, s0); - _mm_storeu_ps(dst + i + 4, s1); - _mm_storeu_ps(dst + i + 8, s2); - _mm_storeu_ps(dst + i + 12, s3); + v_store(dst + i, s0); + v_store(dst + i + vtype::nlanes, s1); + v_store(dst + i + 2*vtype::nlanes, s2); + v_store(dst + i + 3*vtype::nlanes, s3); } - - for( ; i <= width - 4; i += 4 ) + if( i <= width - 2*vtype::nlanes ) { - __m128 s0 = _mm_loadu_ps(src[0] + i), x0; - + const stype* sptr = src[0] + i; + vtype s0 = vx_load(sptr); + vtype s1 = vx_load(sptr + vtype::nlanes); for( k = 1; k < nz; k++ ) { - x0 = _mm_loadu_ps(src[k] + i); - s0 = updateOp(s0, x0); + sptr = src[k] + i; + s0 = updateOp(s0, vx_load(sptr)); + s1 = updateOp(s1, vx_load(sptr + vtype::nlanes)); } - _mm_storeu_ps(dst + i, s0); + v_store(dst + i, s0); + v_store(dst + i + vtype::nlanes, s1); + i += 2*vtype::nlanes; } - - for( ; i < width; i++ ) + if( i <= width - vtype::nlanes ) { - __m128 s0 = _mm_load_ss(src[0] + i), x0; - + vtype s0 = vx_load(src[0] + i); for( k = 1; k < nz; k++ ) - { - x0 = _mm_load_ss(src[k] + i); - s0 = updateOp(s0, x0); - } - _mm_store_ss(dst + i, s0); + s0 = updateOp(s0, vx_load(src[k] + i)); + v_store(dst + i, s0); + i += vtype::nlanes; + } + if( i <= width - vtype::nlanes/2 ) + { + vtype s0 = vx_load_low(src[0] + i); + for( k = 1; k < nz; k++ ) + s0 = updateOp(s0, vx_load_low(src[k] + i)); + v_store_low(dst + i, s0); + i += vtype::nlanes/2; } - return i; } }; -struct VMin8u +template struct VMin { - enum { ESZ = 1 }; - __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_min_epu8(a,b); } + typedef T vtype; + vtype operator()(const vtype& a, const vtype& b) const { return v_min(a,b); } }; -struct VMax8u +template struct VMax { - enum { ESZ = 1 }; - __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_max_epu8(a,b); } + typedef T vtype; + vtype operator()(const vtype& a, const vtype& b) const { return v_max(a,b); } }; -struct VMin16u -{ - enum { ESZ = 2 }; - __m128i operator()(const __m128i& a, const __m128i& b) const - { return _mm_subs_epu16(a,_mm_subs_epu16(a,b)); } -}; -struct VMax16u -{ - enum { ESZ = 2 }; - __m128i operator()(const __m128i& a, const __m128i& b) const - { return _mm_adds_epu16(_mm_subs_epu16(a,b), b); } -}; -struct VMin16s -{ - enum { ESZ = 2 }; - __m128i operator()(const __m128i& a, const __m128i& b) const - { return _mm_min_epi16(a, b); } -}; -struct VMax16s -{ - enum { ESZ = 2 }; - __m128i operator()(const __m128i& a, const __m128i& b) const - { return _mm_max_epi16(a, b); } -}; -struct VMin32f { __m128 operator()(const __m128& a, const __m128& b) const { return _mm_min_ps(a,b); }}; -struct VMax32f { __m128 operator()(const __m128& a, const __m128& b) const { return _mm_max_ps(a,b); }}; -typedef MorphRowIVec ErodeRowVec8u; -typedef MorphRowIVec DilateRowVec8u; -typedef MorphRowIVec ErodeRowVec16u; -typedef MorphRowIVec DilateRowVec16u; -typedef MorphRowIVec ErodeRowVec16s; -typedef MorphRowIVec DilateRowVec16s; -typedef MorphRowFVec ErodeRowVec32f; -typedef MorphRowFVec DilateRowVec32f; +typedef MorphRowVec > ErodeRowVec8u; +typedef MorphRowVec > DilateRowVec8u; +typedef MorphRowVec > ErodeRowVec16u; +typedef MorphRowVec > DilateRowVec16u; +typedef MorphRowVec > ErodeRowVec16s; +typedef MorphRowVec > DilateRowVec16s; +typedef MorphRowVec > ErodeRowVec32f; +typedef MorphRowVec > DilateRowVec32f; -typedef MorphColumnIVec ErodeColumnVec8u; -typedef MorphColumnIVec DilateColumnVec8u; -typedef MorphColumnIVec ErodeColumnVec16u; -typedef MorphColumnIVec DilateColumnVec16u; -typedef MorphColumnIVec ErodeColumnVec16s; -typedef MorphColumnIVec DilateColumnVec16s; -typedef MorphColumnFVec ErodeColumnVec32f; -typedef MorphColumnFVec DilateColumnVec32f; +typedef MorphColumnVec > ErodeColumnVec8u; +typedef MorphColumnVec > DilateColumnVec8u; +typedef MorphColumnVec > ErodeColumnVec16u; +typedef MorphColumnVec > DilateColumnVec16u; +typedef MorphColumnVec > ErodeColumnVec16s; +typedef MorphColumnVec > DilateColumnVec16s; +typedef MorphColumnVec > ErodeColumnVec32f; +typedef MorphColumnVec > DilateColumnVec32f; -typedef MorphIVec ErodeVec8u; -typedef MorphIVec DilateVec8u; -typedef MorphIVec ErodeVec16u; -typedef MorphIVec DilateVec16u; -typedef MorphIVec ErodeVec16s; -typedef MorphIVec DilateVec16s; -typedef MorphFVec ErodeVec32f; -typedef MorphFVec DilateVec32f; +typedef MorphVec > ErodeVec8u; +typedef MorphVec > DilateVec8u; +typedef MorphVec > ErodeVec16u; +typedef MorphVec > DilateVec16u; +typedef MorphVec > ErodeVec16s; +typedef MorphVec > DilateVec16s; +typedef MorphVec > ErodeVec32f; +typedef MorphVec > DilateVec32f; #else From 794c14b29ac5d21e3090c200ebe3690e6e7b354d Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Fri, 11 Jan 2019 20:31:55 +0300 Subject: [PATCH 09/23] code clean up --- modules/calib3d/src/circlesgrid.cpp | 15 +++++++-------- modules/calib3d/src/quadsubpix.cpp | 5 ++--- modules/calib3d/test/test_cameracalibration.cpp | 6 +++--- .../test/test_cameracalibration_artificial.cpp | 2 +- .../calib3d/test/test_chessboardgenerator.cpp | 16 ++++++++-------- modules/calib3d/test/test_solvepnp_ransac.cpp | 6 +++--- modules/features2d/src/blobdetector.cpp | 13 ++++++------- modules/objdetect/src/qrcode.cpp | 6 +++--- 8 files changed, 33 insertions(+), 36 deletions(-) diff --git a/modules/calib3d/src/circlesgrid.cpp b/modules/calib3d/src/circlesgrid.cpp index c27604873c..e19a719c8d 100644 --- a/modules/calib3d/src/circlesgrid.cpp +++ b/modules/calib3d/src/circlesgrid.cpp @@ -160,7 +160,7 @@ void CirclesGridClusterFinder::findGrid(const std::vector &points, #endif std::vector hull2f; - convexHull(Mat(patternPoints), hull2f, false); + convexHull(patternPoints, hull2f, false); const size_t cornersCount = isAsymmetricGrid ? 6 : 4; if(hull2f.size() < cornersCount) return; @@ -411,7 +411,7 @@ void CirclesGridClusterFinder::rectifyPatternPoints(const std::vector dstKeypoints; convertPointsFromHomogeneous(dstKeypointsMat, dstKeypoints); @@ -1176,7 +1176,7 @@ void CirclesGridFinder::findBasis(const std::vector &samples, std::vect } for (size_t i = 0; i < basis.size(); i++) { - convexHull(Mat(clusters[i]), hulls[i]); + convexHull(clusters[i], hulls[i]); } basisGraphs.resize(basis.size(), Graph(keypoints.size())); @@ -1191,7 +1191,7 @@ void CirclesGridFinder::findBasis(const std::vector &samples, std::vect for (size_t k = 0; k < hulls.size(); k++) { - if (pointPolygonTest(Mat(hulls[k]), vec, false) >= 0) + if (pointPolygonTest(hulls[k], vec, false) >= 0) { basisGraphs[k].addEdge(i, j); } @@ -1422,7 +1422,6 @@ void CirclesGridFinder::drawHoles(const Mat &srcImage, Mat &drawImage) const if (i != holes.size() - 1) line(drawImage, keypoints[holes[i][j]], keypoints[holes[i + 1][j]], Scalar(255, 0, 0), 2); - //circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness); circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness); } } diff --git a/modules/calib3d/src/quadsubpix.cpp b/modules/calib3d/src/quadsubpix.cpp index 77bc498591..b4100a22f9 100644 --- a/modules/calib3d/src/quadsubpix.cpp +++ b/modules/calib3d/src/quadsubpix.cpp @@ -194,9 +194,8 @@ bool cv::find4QuadCornerSubpix(InputArray _img, InputOutputArray _corners, Size erode(white_comp, white_comp, Mat(), Point(-1, -1), erode_count); std::vector > white_contours, black_contours; - std::vector white_hierarchy, black_hierarchy; - findContours(black_comp, black_contours, black_hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE); - findContours(white_comp, white_contours, white_hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE); + findContours(black_comp, black_contours, RETR_LIST, CHAIN_APPROX_SIMPLE); + findContours(white_comp, white_contours, RETR_LIST, CHAIN_APPROX_SIMPLE); if(black_contours.size() < 5 || white_contours.size() < 5) continue; diff --git a/modules/calib3d/test/test_cameracalibration.cpp b/modules/calib3d/test/test_cameracalibration.cpp index f20edfea27..f35fd30771 100644 --- a/modules/calib3d/test/test_cameracalibration.cpp +++ b/modules/calib3d/test/test_cameracalibration.cpp @@ -1514,7 +1514,7 @@ bool CV_StereoCalibrationTest::checkPandROI( int test_case_idx, const Mat& M, co for( x = 0; x < N; x++ ) pts.push_back(Point2f((float)x*imgsize.width/(N-1), (float)y*imgsize.height/(N-1))); - undistortPoints(Mat(pts), upts, M, D, R, P ); + undistortPoints(pts, upts, M, D, R, P ); for( k = 0; k < N*N; k++ ) if( upts[k].x < -imgsize.width*eps || upts[k].x > imgsize.width*(1+eps) || upts[k].y < -imgsize.height*eps || upts[k].y > imgsize.height*(1+eps) ) @@ -1823,8 +1823,8 @@ void CV_StereoCalibrationTest::run( int ) for( int i = 0, k = 0; i < nframes; i++ ) { vector temp[2]; - undistortPoints(Mat(imgpt1[i]), temp[0], M1, D1, R1, P1); - undistortPoints(Mat(imgpt2[i]), temp[1], M2, D2, R2, P2); + undistortPoints(imgpt1[i], temp[0], M1, D1, R1, P1); + undistortPoints(imgpt2[i], temp[1], M2, D2, R2, P2); for( int j = 0; j < npoints; j++, k++ ) { diff --git a/modules/calib3d/test/test_cameracalibration_artificial.cpp b/modules/calib3d/test/test_cameracalibration_artificial.cpp index 165a66a7b1..a8351b6b66 100644 --- a/modules/calib3d/test/test_cameracalibration_artificial.cpp +++ b/modules/calib3d/test/test_cameracalibration_artificial.cpp @@ -353,7 +353,7 @@ protected: rvecs_spnp.resize(brdsNum); tvecs_spnp.resize(brdsNum); for(size_t i = 0; i < brdsNum; ++i) - solvePnP(Mat(objectPoints[i]), Mat(imagePoints[i]), camMat, distCoeffs, rvecs_spnp[i], tvecs_spnp[i]); + solvePnP(objectPoints[i], imagePoints[i], camMat, distCoeffs, rvecs_spnp[i], tvecs_spnp[i]); compareShiftVecs(tvecs_exp, tvecs_spnp); compareRotationVecs(rvecs_exp, rvecs_spnp); diff --git a/modules/calib3d/test/test_chessboardgenerator.cpp b/modules/calib3d/test/test_chessboardgenerator.cpp index 3a8c17345f..6926cb6e72 100644 --- a/modules/calib3d/test/test_chessboardgenerator.cpp +++ b/modules/calib3d/test/test_chessboardgenerator.cpp @@ -126,10 +126,10 @@ Mat ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat, co generateEdge(p3, p4, pts_square3d); generateEdge(p4, p1, pts_square3d); - projectPoints(Mat(pts_square3d), rvec, tvec, camMat, distCoeffs, pts_square2d); + projectPoints(pts_square3d, rvec, tvec, camMat, distCoeffs, pts_square2d); squares_black.resize(squares_black.size() + 1); vector temp; - approxPolyDP(Mat(pts_square2d), temp, 1.0, true); + approxPolyDP(pts_square2d, temp, 1.0, true); transform(temp.begin(), temp.end(), back_inserter(squares_black.back()), Mult(rendererResolutionMultiplier)); } @@ -139,7 +139,7 @@ Mat ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat, co for(int i = 0; i < patternSize.width - 1; ++i) corners3d.push_back(zero + (i + 1) * sqWidth * pb1 + (j + 1) * sqHeight * pb2); corners.clear(); - projectPoints(Mat(corners3d), rvec, tvec, camMat, distCoeffs, corners); + projectPoints(corners3d, rvec, tvec, camMat, distCoeffs, corners); vector whole3d; vector whole2d; @@ -147,9 +147,9 @@ Mat ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat, co generateEdge(whole[1], whole[2], whole3d); generateEdge(whole[2], whole[3], whole3d); generateEdge(whole[3], whole[0], whole3d); - projectPoints(Mat(whole3d), rvec, tvec, camMat, distCoeffs, whole2d); + projectPoints(whole3d, rvec, tvec, camMat, distCoeffs, whole2d); vector temp_whole2d; - approxPolyDP(Mat(whole2d), temp_whole2d, 1.0, true); + approxPolyDP(whole2d, temp_whole2d, 1.0, true); vector< vector > whole_contour(1); transform(temp_whole2d.begin(), temp_whole2d.end(), @@ -213,7 +213,7 @@ Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2; /* can remake with better perf */ - projectPoints(Mat(pts3d), rvec, tvec, camMat, distCoeffs, pts2d); + projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d); bool inrect1 = pts2d[0].x < bg.cols && pts2d[0].y < bg.rows && pts2d[0].x > 0 && pts2d[0].y > 0; bool inrect2 = pts2d[1].x < bg.cols && pts2d[1].y < bg.rows && pts2d[1].x > 0 && pts2d[1].y > 0; @@ -278,7 +278,7 @@ Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2; /* can remake with better perf */ - projectPoints(Mat(pts3d), rvec, tvec, camMat, distCoeffs, pts2d); + projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d); bool inrect1 = pts2d[0].x < bg.cols && pts2d[0].y < bg.rows && pts2d[0].x > 0 && pts2d[0].y > 0; bool inrect2 = pts2d[1].x < bg.cols && pts2d[1].y < bg.rows && pts2d[1].x > 0 && pts2d[1].y > 0; @@ -320,7 +320,7 @@ Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2; /* can remake with better perf */ - projectPoints(Mat(pts3d), rvec, tvec, camMat, distCoeffs, pts2d); + projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d); Point3f zero = p - pb1 * cbHalfWidth - cbHalfHeight * pb2; diff --git a/modules/calib3d/test/test_solvepnp_ransac.cpp b/modules/calib3d/test/test_solvepnp_ransac.cpp index 8eec7a7167..2359fa9282 100644 --- a/modules/calib3d/test/test_solvepnp_ransac.cpp +++ b/modules/calib3d/test/test_solvepnp_ransac.cpp @@ -124,7 +124,7 @@ protected: vector projectedPoints; projectedPoints.resize(points.size()); - projectPoints(Mat(points), trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints); + projectPoints(points, trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints); for (size_t i = 0; i < projectedPoints.size(); i++) { if (i % 20 == 0) @@ -241,7 +241,7 @@ protected: vector projectedPoints; projectedPoints.resize(opoints.size()); - projectPoints(Mat(opoints), trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints); + projectPoints(opoints, trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints); bool isEstimateSuccess = solvePnP(opoints, projectedPoints, intrinsics, distCoeffs, rvec, tvec, false, method); if (isEstimateSuccess == false) @@ -291,7 +291,7 @@ class CV_solveP3P_Test : public CV_solvePnPRansac_Test vector projectedPoints; projectedPoints.resize(opoints.size()); - projectPoints(Mat(opoints), trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints); + projectPoints(opoints, trueRvec, trueTvec, intrinsics, distCoeffs, projectedPoints); int num_of_solutions = solveP3P(opoints, projectedPoints, intrinsics, distCoeffs, rvecs, tvecs, method); if (num_of_solutions != (int) rvecs.size() || num_of_solutions != (int) tvecs.size() || num_of_solutions == 0) diff --git a/modules/features2d/src/blobdetector.cpp b/modules/features2d/src/blobdetector.cpp index 9076c23545..f1e8b63799 100644 --- a/modules/features2d/src/blobdetector.cpp +++ b/modules/features2d/src/blobdetector.cpp @@ -197,8 +197,7 @@ void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImag centers.clear(); std::vector < std::vector > contours; - Mat tmpBinaryImage = binaryImage.clone(); - findContours(tmpBinaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE); + findContours(binaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE); #ifdef DEBUG_BLOB_DETECTOR // Mat keypointsImage; @@ -214,7 +213,7 @@ void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImag { Center center; center.confidence = 1; - Moments moms = moments(Mat(contours[contourIdx])); + Moments moms = moments(contours[contourIdx]); if (params.filterByArea) { double area = moms.m00; @@ -225,7 +224,7 @@ void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImag if (params.filterByCircularity) { double area = moms.m00; - double perimeter = arcLength(Mat(contours[contourIdx]), true); + double perimeter = arcLength(contours[contourIdx], true); double ratio = 4 * CV_PI * area / (perimeter * perimeter); if (ratio < params.minCircularity || ratio >= params.maxCircularity) continue; @@ -261,9 +260,9 @@ void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImag if (params.filterByConvexity) { std::vector < Point > hull; - convexHull(Mat(contours[contourIdx]), hull); - double area = contourArea(Mat(contours[contourIdx])); - double hullArea = contourArea(Mat(hull)); + convexHull(contours[contourIdx], hull); + double area = contourArea(contours[contourIdx]); + double hullArea = contourArea(hull); if (fabs(hullArea) < DBL_EPSILON) continue; double ratio = area / hullArea; diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index dd3c48eb17..9884bcc0f1 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -387,7 +387,7 @@ bool QRDetect::computeTransformationPoints() findNonZero(mask_roi, non_zero_elem[i]); newHull.insert(newHull.end(), non_zero_elem[i].begin(), non_zero_elem[i].end()); } - convexHull(Mat(newHull), locations); + convexHull(newHull, locations); for (size_t i = 0; i < locations.size(); i++) { for (size_t j = 0; j < 3; j++) @@ -556,7 +556,7 @@ vector QRDetect::getQuadrilateral(vector angle_list) } vector integer_hull; - convexHull(Mat(locations), integer_hull); + convexHull(locations, integer_hull); int hull_size = (int)integer_hull.size(); vector hull(hull_size); for (int i = 0; i < hull_size; i++) @@ -910,7 +910,7 @@ bool QRDecode::versionDefinition() vector locations, non_zero_elem; Mat mask_roi = mask(Range(1, intermediate.rows - 1), Range(1, intermediate.cols - 1)); findNonZero(mask_roi, non_zero_elem); - convexHull(Mat(non_zero_elem), locations); + convexHull(non_zero_elem, locations); Point offset = computeOffset(locations); Point temp_remote = locations[0], remote_point; From 0165ffa90dfc9a705f624d65e7784020a13f0806 Mon Sep 17 00:00:00 2001 From: Brad Kelly Date: Tue, 13 Nov 2018 20:03:11 -0800 Subject: [PATCH 10/23] Implementing AVX512 support for 3 channel cv::integral for CV_64F --- modules/imgproc/perf/perf_integral.cpp | 2 +- modules/imgproc/src/sumpixels.avx512_skx.cpp | 262 +++++++++++++++++++ modules/imgproc/src/sumpixels.cpp | 34 ++- modules/imgproc/src/sumpixels.hpp | 25 ++ 4 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 modules/imgproc/src/sumpixels.avx512_skx.cpp create mode 100644 modules/imgproc/src/sumpixels.hpp diff --git a/modules/imgproc/perf/perf_integral.cpp b/modules/imgproc/perf/perf_integral.cpp index 4b2ba97148..d64c49e0a9 100644 --- a/modules/imgproc/perf/perf_integral.cpp +++ b/modules/imgproc/perf/perf_integral.cpp @@ -11,7 +11,7 @@ typedef perf::TestBaseWithParam Size_MatType_OutMatD PERF_TEST_P(Size_MatType_OutMatDepth, integral, testing::Combine( testing::Values(TYPICAL_MAT_SIZES), - testing::Values(CV_8UC1, CV_8UC4), + testing::Values(CV_8UC1, CV_8UC3, CV_8UC4), testing::Values(CV_32S, CV_32F, CV_64F) ) ) diff --git a/modules/imgproc/src/sumpixels.avx512_skx.cpp b/modules/imgproc/src/sumpixels.avx512_skx.cpp new file mode 100644 index 0000000000..7e5cbdcf88 --- /dev/null +++ b/modules/imgproc/src/sumpixels.avx512_skx.cpp @@ -0,0 +1,262 @@ +// 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, all rights reserved. +#include "precomp.hpp" +#include "sumpixels.hpp" + +namespace cv { +namespace { // Anonymous namespace to avoid exposing the implementation classes + +// +// NOTE: Look at the bottom of the file for the entry-point function for external callers +// + +// At the moment only 3 channel support untilted is supported +// More channel support coming soon. +// TODO: Add support for sqsum and 1,2, and 4 channels +class IntegralCalculator_3Channel { +public: + IntegralCalculator_3Channel() {}; + + + void calculate_integral_avx512(const uchar *src, size_t _srcstep, + double *sum, size_t _sumstep, + double *sqsum, size_t _sqsumstep, + int width, int height, int cn) + { + const int srcstep = (int)(_srcstep/sizeof(uchar)); + const int sumstep = (int)(_sumstep/sizeof(double)); + const int sqsumstep = (int)(_sqsumstep/sizeof(double)); + const int ops_per_line = width * cn; + + // Clear the first line of the sum as per spec (see integral documentation) + // Also adjust the index of sum and sqsum to be at the real 0th element + // and not point to the border pixel so it stays in sync with the src pointer + memset( sum, 0, (ops_per_line+cn)*sizeof(double)); + sum += cn; + + if (sqsum) { + memset( sqsum, 0, (ops_per_line+cn)*sizeof(double)); + sqsum += cn; + } + + // Now calculate the integral over the whole image one line at a time + for(int y = 0; y < height; y++) { + const uchar * src_line = &src[y*srcstep]; + double * sum_above = &sum[y*sumstep]; + double * sum_line = &sum_above[sumstep]; + double * sqsum_above = (sqsum) ? &sqsum[y*sqsumstep] : NULL; + double * sqsum_line = (sqsum) ? &sqsum_above[sqsumstep] : NULL; + + integral_line_3channel_avx512(src_line, sum_line, sum_above, sqsum_line, sqsum_above, ops_per_line); + + } + } + + static inline + void integral_line_3channel_avx512(const uchar *srcs, + double *sums, double *sums_above, + double *sqsums, double *sqsums_above, + int num_ops_in_line) + { + __m512i sum_accumulator = _mm512_setzero_si512(); // holds rolling sums for the line + __m512i sqsum_accumulator = _mm512_setzero_si512(); // holds rolling sqsums for the line + + // The first element on each line must be zeroes as per spec (see integral documentation) + set_border_pixel_value(sums, sqsums); + + // Do all 64 byte chunk operations then do the last bits that don't fit in a 64 byte chunk + aligned_integral( srcs, sums, sums_above, sqsums, sqsums_above, sum_accumulator, sqsum_accumulator, num_ops_in_line); + post_aligned_integral(srcs, sums, sums_above, sqsums, sqsums_above, sum_accumulator, sqsum_accumulator, num_ops_in_line); + + } + + + static inline + void set_border_pixel_value(double *sums, double *sqsums) + { + // Sets the border pixel value to 0s. + // Note the hard coded -3 and the 0x7 mask is because we only support 3 channel right now + __m512i zeroes = _mm512_setzero_si512(); + + _mm512_mask_storeu_epi64(&sums[-3], 0x7, zeroes); + if (sqsums) + _mm512_mask_storeu_epi64(&sqsums[-3], 0x7, zeroes); + } + + + static inline + void aligned_integral(const uchar *&srcs, + double *&sums, double *&sums_above, + double *&sqsum, double *&sqsum_above, + __m512i &sum_accumulator, __m512i &sqsum_accumulator, + int num_ops_in_line) + { + // This function handles full 64 byte chunks of the source data at a time until it gets to the part of + // the line that no longer contains a full 64 byte chunk. Other code will handle the last part. + + const int num_chunks = num_ops_in_line >> 6; // quick int divide by 64 + + for (int index_64byte_chunk = 0; index_64byte_chunk < num_chunks; index_64byte_chunk++){ + integral_64_operations_avx512((__m512i *) srcs, + (__m512i *) sums, (__m512i *) sums_above, + (__m512i *) sqsum, (__m512i *) sqsum_above, + 0xFFFFFFFFFFFFFFFF, sum_accumulator, sqsum_accumulator); + srcs+=64; sums+=64; sums_above+=64; + if (sqsum){ sqsum+= 64; sqsum_above+=64; } + } + } + + + static inline + void post_aligned_integral(const uchar *srcs, + const double *sums, const double *sums_above, + const double *sqsum, const double *sqsum_above, + __m512i &sum_accumulator, __m512i &sqsum_accumulator, + int num_ops_in_line) + { + // This function handles the last few straggling operations that are not a full chunk of 64 operations + // We use the same algorithm, but we calculate a different operation mask using (num_ops % 64). + + const unsigned int num_operations = (unsigned int) num_ops_in_line & 0x3F; // Quick int modulo 64 + + if (num_operations > 0) { + __mmask64 operation_mask = (1ULL << num_operations) - 1ULL; + + integral_64_operations_avx512((__m512i *) srcs, (__m512i *) sums, (__m512i *) sums_above, + (__m512i *) sqsum, (__m512i *) sqsum_above, + operation_mask, sum_accumulator, sqsum_accumulator); + } + } + + + static inline + void integral_64_operations_avx512(const __m512i *srcs, + __m512i *sums, const __m512i *sums_above, + __m512i *sqsums, const __m512i *sqsums_above, + __mmask64 data_mask, + __m512i &sum_accumulator, __m512i &sqsum_accumulator) + { + __m512i src_64byte_chunk = read_64_bytes(srcs, data_mask); + + for(int num_16byte_chunks=0; num_16byte_chunks<4; num_16byte_chunks++) { + __m128i src_16bytes = _mm512_extracti64x2_epi64(src_64byte_chunk, 0x0); // Get lower 16 bytes of data + + for (int num_8byte_chunks = 0; num_8byte_chunks < 2; num_8byte_chunks++) { + + __m512i src_longs = convert_lower_8bytes_to_longs(src_16bytes); + + // Calculate integral for the sum on the 8 entries + integral_8_operations(src_longs, sums_above, data_mask, sums, sum_accumulator); + sums++; sums_above++; + + if (sqsums){ // Calculate integral for the sum on the 8 entries + __m512i squared_source = _mm512_mullo_epi64(src_longs, src_longs); + + integral_8_operations(squared_source, sqsums_above, data_mask, sqsums, sqsum_accumulator); + sqsums++; sqsums_above++; + } + + // Prepare for next iteration of inner loop + // shift source to align next 8 bytes to lane 0 and shift the mask + src_16bytes = shift_right_8_bytes(src_16bytes); + data_mask = data_mask >> 8; + + } + + // Prepare for next iteration of outer loop + src_64byte_chunk = shift_right_16_bytes(src_64byte_chunk); + } + } + + + static inline + void integral_8_operations(const __m512i src_longs, const __m512i *above_values_ptr, __mmask64 data_mask, + __m512i *results_ptr, __m512i &accumulator) + { + _mm512_mask_storeu_pd( + results_ptr, // Store the result here + data_mask, // Using the data mask to avoid overrunning the line + calculate_integral( // Writing the value of the integral derived from: + src_longs, // input data + _mm512_maskz_loadu_pd(data_mask, above_values_ptr), // and the results from line above + accumulator // keeping track of the accumulator + ) + ); + } + + + static inline + __m512d calculate_integral(__m512i src_longs, const __m512d above_values, __m512i &accumulator) + { + __m512i carryover_idxs = _mm512_set_epi64(6, 5, 7, 6, 5, 7, 6, 5); + + // Align data to prepare for the adds: + // shifts data left by 3 and 6 qwords(lanes) and gets rolling sum in all lanes + // Vertical LANES: 76543210 + // src_longs : HGFEDCBA + // shited3lanes : + EDCBA + // shifted6lanes : + BA + // carry_over_idxs : + 65765765 (index position of result from previous iteration) + // = integral + __m512i shifted3lanes = _mm512_maskz_expand_epi64(0xF8, src_longs); + __m512i shifted6lanes = _mm512_maskz_expand_epi64(0xC0, src_longs); + __m512i carry_over = _mm512_permutex2var_epi64(accumulator, carryover_idxs, accumulator); + + // Do the adds in tree form (shift3 + shift 6) + (current_source_values + accumulator) + __m512i sum_shift3and6 = _mm512_add_epi64(shifted3lanes, shifted6lanes); + __m512i sum_src_carry = _mm512_add_epi64(src_longs, carry_over); + accumulator = _mm512_add_epi64(sum_shift3and6, sum_src_carry); + + // Convert to packed double and add to the line above to get the true integral value + __m512d accumulator_pd = _mm512_cvtepu64_pd(accumulator); + __m512d integral_pd = _mm512_add_pd(accumulator_pd, above_values); + return integral_pd; + } + + + static inline + __m512i read_64_bytes(const __m512i *srcs, __mmask64 data_mask) { + return _mm512_maskz_loadu_epi8(data_mask, srcs); + } + + + static inline + __m512i convert_lower_8bytes_to_longs(__m128i src_16bytes) { + return _mm512_cvtepu8_epi64(src_16bytes); + } + + + static inline + __m128i shift_right_8_bytes(__m128i src_16bytes) { + return _mm_maskz_compress_epi64(2, src_16bytes); + } + + + static inline + __m512i shift_right_16_bytes(__m512i src_64byte_chunk) { + return _mm512_maskz_compress_epi64(0xFC, src_64byte_chunk); + } + +}; +} // end of anonymous namespace + +namespace opt_AVX512_SKX { + +// This is the implementation for the external callers interface entry point. +// It should be the only function called into this file from outside +// Any new implementations should be directed from here +void calculate_integral_avx512(const uchar *src, size_t _srcstep, + double *sum, size_t _sumstep, + double *sqsum, size_t _sqsumstep, + int width, int height, int cn) +{ + IntegralCalculator_3Channel calculator; + calculator.calculate_integral_avx512(src, _srcstep, sum, _sumstep, sqsum, _sqsumstep, width, height, cn); +} + + +} // end namespace opt_AVX512_SXK +} // end namespace cv diff --git a/modules/imgproc/src/sumpixels.cpp b/modules/imgproc/src/sumpixels.cpp index 3c49aaf773..ae7647b8bd 100755 --- a/modules/imgproc/src/sumpixels.cpp +++ b/modules/imgproc/src/sumpixels.cpp @@ -10,7 +10,7 @@ // License Agreement // For Open Source Computer Vision Library // -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2000-2008,2019 Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2014, Itseez Inc., all rights reserved. // Third party copyrights are property of their respective owners. @@ -44,6 +44,7 @@ #include "precomp.hpp" #include "opencl_kernels_imgproc.hpp" #include "opencv2/core/hal/intrin.hpp" +#include "sumpixels.hpp" namespace cv @@ -62,6 +63,37 @@ struct Integral_SIMD } }; + +template <> +struct Integral_SIMD { + Integral_SIMD() {}; + + + bool operator()(const uchar *src, size_t _srcstep, + double *sum, size_t _sumstep, + double *sqsum, size_t _sqsumstep, + double *tilted, size_t _tiltedstep, + int width, int height, int cn) const + { +#if CV_TRY_AVX512_SKX + CV_UNUSED(_tiltedstep); + // TODO: Add support for 1,2, and 4 channels + if (CV_CPU_HAS_SUPPORT_AVX512_SKX && !tilted && cn == 3){ + opt_AVX512_SKX::calculate_integral_avx512(src, _srcstep, sum, _sumstep, + sqsum, _sqsumstep, width, height, cn); + return true; + } +#else + // Avoid warnings in some builds + CV_UNUSED(src); CV_UNUSED(_srcstep); CV_UNUSED(sum); CV_UNUSED(_sumstep); + CV_UNUSED(sqsum); CV_UNUSED(_sqsumstep); CV_UNUSED(tilted); CV_UNUSED(_tiltedstep); + CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(cn); +#endif + return false; + } + +}; + #if CV_SIMD && CV_SIMD_WIDTH <= 64 template <> diff --git a/modules/imgproc/src/sumpixels.hpp b/modules/imgproc/src/sumpixels.hpp new file mode 100644 index 0000000000..8d5ab0a851 --- /dev/null +++ b/modules/imgproc/src/sumpixels.hpp @@ -0,0 +1,25 @@ +// 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, all rights reserved. +#ifndef OPENCV_IMGPROC_SUM_PIXELS_HPP +#define OPENCV_IMGPROC_SUM_PIXELS_HPP + +namespace cv +{ + +namespace opt_AVX512_SKX +{ +#if CV_TRY_AVX512_SKX + void calculate_integral_avx512( + const uchar *src, size_t _srcstep, + double *sum, size_t _sumstep, + double *sqsum, size_t _sqsumstep, + int width, int height, int cn); + +#endif +} // end namespace opt_AVX512_SKX +} // end namespace cv + +#endif From a202dc9a904961aa0021e6c9f71dcfbba06e6c1a Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Tue, 15 Jan 2019 19:15:19 +0300 Subject: [PATCH 11/23] threshold() reworked to use wide universal intrinsics --- modules/imgproc/src/thresh.cpp | 1142 ++++++++++++++++++-------------- 1 file changed, 631 insertions(+), 511 deletions(-) diff --git a/modules/imgproc/src/thresh.cpp b/modules/imgproc/src/thresh.cpp index 9c6168916d..d724db4020 100644 --- a/modules/imgproc/src/thresh.cpp +++ b/modules/imgproc/src/thresh.cpp @@ -195,82 +195,78 @@ thresh_8u( const Mat& _src, Mat& _dst, uchar thresh, uchar maxval, int type ) int j = 0; const uchar* src = _src.ptr(); uchar* dst = _dst.ptr(); -#if CV_SIMD128 - bool useSIMD = checkHardwareSupport( CV_CPU_SSE2 ) || checkHardwareSupport( CV_CPU_NEON ); - if( useSIMD ) +#if CV_SIMD + v_uint8 thresh_u = vx_setall_u8( thresh ); + v_uint8 maxval16 = vx_setall_u8( maxval ); + + switch( type ) { - v_uint8x16 thresh_u = v_setall_u8( thresh ); - v_uint8x16 maxval16 = v_setall_u8( maxval ); - - switch( type ) + case THRESH_BINARY: + for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) { - case THRESH_BINARY: - for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + for( j = 0; j <= roi.width - v_uint8::nlanes; j += v_uint8::nlanes) { - for( j = 0; j <= roi.width - 16; j += 16 ) - { - v_uint8x16 v0; - v0 = v_load( src + j ); - v0 = thresh_u < v0; - v0 = v0 & maxval16; - v_store( dst + j, v0 ); - } + v_uint8 v0; + v0 = vx_load( src + j ); + v0 = thresh_u < v0; + v0 = v0 & maxval16; + v_store( dst + j, v0 ); } - break; - - case THRESH_BINARY_INV: - for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - for( j = 0; j <= roi.width - 16; j += 16 ) - { - v_uint8x16 v0; - v0 = v_load( src + j ); - v0 = v0 <= thresh_u; - v0 = v0 & maxval16; - v_store( dst + j, v0 ); - } - } - break; - - case THRESH_TRUNC: - for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - for( j = 0; j <= roi.width - 16; j += 16 ) - { - v_uint8x16 v0; - v0 = v_load( src + j ); - v0 = v0 - ( v0 - thresh_u ); - v_store( dst + j, v0 ); - } - } - break; - - case THRESH_TOZERO: - for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - for( j = 0; j <= roi.width - 16; j += 16 ) - { - v_uint8x16 v0; - v0 = v_load( src + j ); - v0 = ( thresh_u < v0 ) & v0; - v_store( dst + j, v0 ); - } - } - break; - - case THRESH_TOZERO_INV: - for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - for( j = 0; j <= roi.width - 16; j += 16 ) - { - v_uint8x16 v0; - v0 = v_load( src + j ); - v0 = ( v0 <= thresh_u ) & v0; - v_store( dst + j, v0 ); - } - } - break; } + break; + + case THRESH_BINARY_INV: + for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + for( j = 0; j <= roi.width - v_uint8::nlanes; j += v_uint8::nlanes) + { + v_uint8 v0; + v0 = vx_load( src + j ); + v0 = v0 <= thresh_u; + v0 = v0 & maxval16; + v_store( dst + j, v0 ); + } + } + break; + + case THRESH_TRUNC: + for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + for( j = 0; j <= roi.width - v_uint8::nlanes; j += v_uint8::nlanes) + { + v_uint8 v0; + v0 = vx_load( src + j ); + v0 = v0 - ( v0 - thresh_u ); + v_store( dst + j, v0 ); + } + } + break; + + case THRESH_TOZERO: + for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + for( j = 0; j <= roi.width - v_uint8::nlanes; j += v_uint8::nlanes) + { + v_uint8 v0; + v0 = vx_load( src + j ); + v0 = ( thresh_u < v0 ) & v0; + v_store( dst + j, v0 ); + } + } + break; + + case THRESH_TOZERO_INV: + for( int i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + for( j = 0; j <= roi.width - v_uint8::nlanes; j += v_uint8::nlanes) + { + v_uint8 v0; + v0 = vx_load( src + j ); + v0 = ( v0 <= thresh_u ) & v0; + v_store( dst + j, v0 ); + } + } + break; } #endif @@ -362,125 +358,156 @@ thresh_16u(const Mat& _src, Mat& _dst, ushort thresh, ushort maxval, int type) const ushort* src = _src.ptr(); ushort* dst = _dst.ptr(); -#if CV_SIMD128 - bool useSIMD = checkHardwareSupport(CV_CPU_SSE2) || checkHardwareSupport(CV_CPU_NEON); - if (useSIMD) - { - int i, j; - v_uint16x8 thresh_u = v_setall_u16(thresh); - v_uint16x8 maxval16 = v_setall_u16(maxval); +#if CV_SIMD + int i, j; + v_uint16 thresh_u = vx_setall_u16(thresh); + v_uint16 maxval16 = vx_setall_u16(maxval); - switch (type) + switch (type) + { + case THRESH_BINARY: + for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) { - case THRESH_BINARY: - for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) + for (j = 0; j <= roi.width - 2*v_uint16::nlanes; j += 2*v_uint16::nlanes) { - for (j = 0; j <= roi.width - 16; j += 16) - { - v_uint16x8 v0, v1; - v0 = v_load(src + j); - v1 = v_load(src + j + 8); - v0 = thresh_u < v0; - v1 = thresh_u < v1; - v0 = v0 & maxval16; - v1 = v1 & maxval16; - v_store(dst + j, v0); - v_store(dst + j + 8, v1); - } - - for (; j < roi.width; j++) - dst[j] = threshBinary(src[j], thresh, maxval); + v_uint16 v0, v1; + v0 = vx_load(src + j); + v1 = vx_load(src + j + v_uint16::nlanes); + v0 = thresh_u < v0; + v1 = thresh_u < v1; + v0 = v0 & maxval16; + v1 = v1 & maxval16; + v_store(dst + j, v0); + v_store(dst + j + v_uint16::nlanes, v1); } - break; - - case THRESH_BINARY_INV: - for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) + if (j <= roi.width - v_uint16::nlanes) { - j = 0; - for (; j <= roi.width - 16; j += 16) - { - v_uint16x8 v0, v1; - v0 = v_load(src + j); - v1 = v_load(src + j + 8); - v0 = v0 <= thresh_u; - v1 = v1 <= thresh_u; - v0 = v0 & maxval16; - v1 = v1 & maxval16; - v_store(dst + j, v0); - v_store(dst + j + 8, v1); - } - - for (; j < roi.width; j++) - dst[j] = threshBinaryInv(src[j], thresh, maxval); + v_uint16 v0 = vx_load(src + j); + v0 = thresh_u < v0; + v0 = v0 & maxval16; + v_store(dst + j, v0); + j += v_uint16::nlanes; } - break; - case THRESH_TRUNC: - for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) - { - j = 0; - for (; j <= roi.width - 16; j += 16) - { - v_uint16x8 v0, v1; - v0 = v_load(src + j); - v1 = v_load(src + j + 8); - v0 = v_min(v0, thresh_u); - v1 = v_min(v1, thresh_u); - v_store(dst + j, v0); - v_store(dst + j + 8, v1); - } - - for (; j < roi.width; j++) - dst[j] = threshTrunc(src[j], thresh); - } - break; - - case THRESH_TOZERO: - for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) - { - j = 0; - for (; j <= roi.width - 16; j += 16) - { - v_uint16x8 v0, v1; - v0 = v_load(src + j); - v1 = v_load(src + j + 8); - v0 = (thresh_u < v0) & v0; - v1 = (thresh_u < v1) & v1; - v_store(dst + j, v0); - v_store(dst + j + 8, v1); - } - - for (; j < roi.width; j++) - dst[j] = threshToZero(src[j], thresh); - } - break; - - case THRESH_TOZERO_INV: - for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) - { - j = 0; - for (; j <= roi.width - 16; j += 16) - { - v_uint16x8 v0, v1; - v0 = v_load(src + j); - v1 = v_load(src + j + 8); - v0 = (v0 <= thresh_u) & v0; - v1 = (v1 <= thresh_u) & v1; - v_store(dst + j, v0); - v_store(dst + j + 8, v1); - } - - for (; j < roi.width; j++) - dst[j] = threshToZeroInv(src[j], thresh); - } - break; + for (; j < roi.width; j++) + dst[j] = threshBinary(src[j], thresh, maxval); } + break; + + case THRESH_BINARY_INV: + for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) + { + j = 0; + for (; j <= roi.width - 2*v_uint16::nlanes; j += 2*v_uint16::nlanes) + { + v_uint16 v0, v1; + v0 = vx_load(src + j); + v1 = vx_load(src + j + v_uint16::nlanes); + v0 = v0 <= thresh_u; + v1 = v1 <= thresh_u; + v0 = v0 & maxval16; + v1 = v1 & maxval16; + v_store(dst + j, v0); + v_store(dst + j + v_uint16::nlanes, v1); + } + if (j <= roi.width - v_uint16::nlanes) + { + v_uint16 v0 = vx_load(src + j); + v0 = v0 <= thresh_u; + v0 = v0 & maxval16; + v_store(dst + j, v0); + j += v_uint16::nlanes; + } + + for (; j < roi.width; j++) + dst[j] = threshBinaryInv(src[j], thresh, maxval); + } + break; + + case THRESH_TRUNC: + for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) + { + j = 0; + for (; j <= roi.width - 2*v_uint16::nlanes; j += 2*v_uint16::nlanes) + { + v_uint16 v0, v1; + v0 = vx_load(src + j); + v1 = vx_load(src + j + v_uint16::nlanes); + v0 = v_min(v0, thresh_u); + v1 = v_min(v1, thresh_u); + v_store(dst + j, v0); + v_store(dst + j + v_uint16::nlanes, v1); + } + if (j <= roi.width - v_uint16::nlanes) + { + v_uint16 v0 = vx_load(src + j); + v0 = v_min(v0, thresh_u); + v_store(dst + j, v0); + j += v_uint16::nlanes; + } + + for (; j < roi.width; j++) + dst[j] = threshTrunc(src[j], thresh); + } + break; + + case THRESH_TOZERO: + for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) + { + j = 0; + for (; j <= roi.width - 2*v_uint16::nlanes; j += 2*v_uint16::nlanes) + { + v_uint16 v0, v1; + v0 = vx_load(src + j); + v1 = vx_load(src + j + v_uint16::nlanes); + v0 = (thresh_u < v0) & v0; + v1 = (thresh_u < v1) & v1; + v_store(dst + j, v0); + v_store(dst + j + v_uint16::nlanes, v1); + } + if (j <= roi.width - v_uint16::nlanes) + { + v_uint16 v0 = vx_load(src + j); + v0 = (thresh_u < v0) & v0; + v_store(dst + j, v0); + j += v_uint16::nlanes; + } + + for (; j < roi.width; j++) + dst[j] = threshToZero(src[j], thresh); + } + break; + + case THRESH_TOZERO_INV: + for (i = 0; i < roi.height; i++, src += src_step, dst += dst_step) + { + j = 0; + for (; j <= roi.width - 2*v_uint16::nlanes; j += 2*v_uint16::nlanes) + { + v_uint16 v0, v1; + v0 = vx_load(src + j); + v1 = vx_load(src + j + v_uint16::nlanes); + v0 = (v0 <= thresh_u) & v0; + v1 = (v1 <= thresh_u) & v1; + v_store(dst + j, v0); + v_store(dst + j + v_uint16::nlanes, v1); + } + if (j <= roi.width - v_uint16::nlanes) + { + v_uint16 v0 = vx_load(src + j); + v0 = (v0 <= thresh_u) & v0; + v_store(dst + j, v0); + j += v_uint16::nlanes; + } + + for (; j < roi.width; j++) + dst[j] = threshToZeroInv(src[j], thresh); + } + break; } - else +#else + threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); #endif - { - threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); - } } static void @@ -556,128 +583,159 @@ thresh_16s( const Mat& _src, Mat& _dst, short thresh, short maxval, int type ) } #endif -#if CV_SIMD128 - bool useSIMD = checkHardwareSupport( CV_CPU_SSE2 ) || checkHardwareSupport( CV_CPU_NEON ); - if( useSIMD ) - { - int i, j; - v_int16x8 thresh8 = v_setall_s16( thresh ); - v_int16x8 maxval8 = v_setall_s16( maxval ); +#if CV_SIMD + int i, j; + v_int16 thresh8 = vx_setall_s16( thresh ); + v_int16 maxval8 = vx_setall_s16( maxval ); - switch( type ) + switch( type ) + { + case THRESH_BINARY: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) { - case THRESH_BINARY: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + j = 0; + for( ; j <= roi.width - 2*v_int16::nlanes; j += 2*v_int16::nlanes ) { - j = 0; - for( ; j <= roi.width - 16; j += 16 ) - { - v_int16x8 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 8 ); - v0 = thresh8 < v0; - v1 = thresh8 < v1; - v0 = v0 & maxval8; - v1 = v1 & maxval8; - v_store( dst + j, v0 ); - v_store( dst + j + 8, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshBinary(src[j], thresh, maxval); + v_int16 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_int16::nlanes ); + v0 = thresh8 < v0; + v1 = thresh8 < v1; + v0 = v0 & maxval8; + v1 = v1 & maxval8; + v_store( dst + j, v0 ); + v_store( dst + j + v_int16::nlanes, v1 ); } - break; - - case THRESH_BINARY_INV: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + if( j <= roi.width - v_int16::nlanes ) { - j = 0; - for( ; j <= roi.width - 16; j += 16 ) - { - v_int16x8 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 8 ); - v0 = v0 <= thresh8; - v1 = v1 <= thresh8; - v0 = v0 & maxval8; - v1 = v1 & maxval8; - v_store( dst + j, v0 ); - v_store( dst + j + 8, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshBinaryInv(src[j], thresh, maxval); + v_int16 v0 = vx_load( src + j ); + v0 = thresh8 < v0; + v0 = v0 & maxval8; + v_store( dst + j, v0 ); + j += v_int16::nlanes; } - break; - case THRESH_TRUNC: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - j = 0; - for( ; j <= roi.width - 16; j += 16 ) - { - v_int16x8 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 8 ); - v0 = v_min( v0, thresh8 ); - v1 = v_min( v1, thresh8 ); - v_store( dst + j, v0 ); - v_store( dst + j + 8, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshTrunc( src[j], thresh ); - } - break; - - case THRESH_TOZERO: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - j = 0; - for( ; j <= roi.width - 16; j += 16 ) - { - v_int16x8 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 8 ); - v0 = ( thresh8 < v0 ) & v0; - v1 = ( thresh8 < v1 ) & v1; - v_store( dst + j, v0 ); - v_store( dst + j + 8, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshToZero(src[j], thresh); - } - break; - - case THRESH_TOZERO_INV: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - j = 0; - for( ; j <= roi.width - 16; j += 16 ) - { - v_int16x8 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 8 ); - v0 = ( v0 <= thresh8 ) & v0; - v1 = ( v1 <= thresh8 ) & v1; - v_store( dst + j, v0 ); - v_store( dst + j + 8, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshToZeroInv(src[j], thresh); - } - break; - default: - CV_Error( CV_StsBadArg, "" ); return; + for( ; j < roi.width; j++ ) + dst[j] = threshBinary(src[j], thresh, maxval); } + break; + + case THRESH_BINARY_INV: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_int16::nlanes; j += 2*v_int16::nlanes ) + { + v_int16 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_int16::nlanes ); + v0 = v0 <= thresh8; + v1 = v1 <= thresh8; + v0 = v0 & maxval8; + v1 = v1 & maxval8; + v_store( dst + j, v0 ); + v_store( dst + j + v_int16::nlanes, v1 ); + } + if( j <= roi.width - v_int16::nlanes ) + { + v_int16 v0 = vx_load( src + j ); + v0 = v0 <= thresh8; + v0 = v0 & maxval8; + v_store( dst + j, v0 ); + j += v_int16::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshBinaryInv(src[j], thresh, maxval); + } + break; + + case THRESH_TRUNC: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_int16::nlanes; j += 2*v_int16::nlanes ) + { + v_int16 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_int16::nlanes ); + v0 = v_min( v0, thresh8 ); + v1 = v_min( v1, thresh8 ); + v_store( dst + j, v0 ); + v_store( dst + j + v_int16::nlanes, v1 ); + } + if( j <= roi.width - v_int16::nlanes ) + { + v_int16 v0 = vx_load( src + j ); + v0 = v_min( v0, thresh8 ); + v_store( dst + j, v0 ); + j += v_int16::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshTrunc( src[j], thresh ); + } + break; + + case THRESH_TOZERO: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_int16::nlanes; j += 2*v_int16::nlanes ) + { + v_int16 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_int16::nlanes ); + v0 = ( thresh8 < v0 ) & v0; + v1 = ( thresh8 < v1 ) & v1; + v_store( dst + j, v0 ); + v_store( dst + j + v_int16::nlanes, v1 ); + } + if( j <= roi.width - v_int16::nlanes ) + { + v_int16 v0 = vx_load( src + j ); + v0 = ( thresh8 < v0 ) & v0; + v_store( dst + j, v0 ); + j += v_int16::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshToZero(src[j], thresh); + } + break; + + case THRESH_TOZERO_INV: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_int16::nlanes; j += 2*v_int16::nlanes ) + { + v_int16 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_int16::nlanes ); + v0 = ( v0 <= thresh8 ) & v0; + v1 = ( v1 <= thresh8 ) & v1; + v_store( dst + j, v0 ); + v_store( dst + j + v_int16::nlanes, v1 ); + } + if( j <= roi.width - v_int16::nlanes ) + { + v_int16 v0 = vx_load( src + j ); + v0 = ( v0 <= thresh8 ) & v0; + v_store( dst + j, v0 ); + j += v_int16::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshToZeroInv(src[j], thresh); + } + break; + default: + CV_Error( CV_StsBadArg, "" ); return; } - else +#else + threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); #endif - { - threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); - } } @@ -736,128 +794,159 @@ thresh_32f( const Mat& _src, Mat& _dst, float thresh, float maxval, int type ) } #endif -#if CV_SIMD128 - bool useSIMD = checkHardwareSupport( CV_CPU_SSE2 ) || checkHardwareSupport( CV_CPU_NEON ); - if( useSIMD ) +#if CV_SIMD + int i, j; + v_float32 thresh4 = vx_setall_f32( thresh ); + v_float32 maxval4 = vx_setall_f32( maxval ); + + switch( type ) { - int i, j; - v_float32x4 thresh4 = v_setall_f32( thresh ); - v_float32x4 maxval4 = v_setall_f32( maxval ); - - switch( type ) - { - case THRESH_BINARY: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + case THRESH_BINARY: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float32::nlanes; j += 2*v_float32::nlanes ) { - j = 0; - for( ; j <= roi.width - 8; j += 8 ) - { - v_float32x4 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 4 ); - v0 = thresh4 < v0; - v1 = thresh4 < v1; - v0 = v0 & maxval4; - v1 = v1 & maxval4; - v_store( dst + j, v0 ); - v_store( dst + j + 4, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshBinary(src[j], thresh, maxval); + v_float32 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float32::nlanes ); + v0 = thresh4 < v0; + v1 = thresh4 < v1; + v0 = v0 & maxval4; + v1 = v1 & maxval4; + v_store( dst + j, v0 ); + v_store( dst + j + v_float32::nlanes, v1 ); } - break; - - case THRESH_BINARY_INV: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + if( j <= roi.width - v_float32::nlanes ) { - j = 0; - for( ; j <= roi.width - 8; j += 8 ) - { - v_float32x4 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 4 ); - v0 = v0 <= thresh4; - v1 = v1 <= thresh4; - v0 = v0 & maxval4; - v1 = v1 & maxval4; - v_store( dst + j, v0 ); - v_store( dst + j + 4, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshBinaryInv(src[j], thresh, maxval); + v_float32 v0 = vx_load( src + j ); + v0 = thresh4 < v0; + v0 = v0 & maxval4; + v_store( dst + j, v0 ); + j += v_float32::nlanes; } - break; - case THRESH_TRUNC: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + for( ; j < roi.width; j++ ) + dst[j] = threshBinary(src[j], thresh, maxval); + } + break; + + case THRESH_BINARY_INV: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float32::nlanes; j += 2*v_float32::nlanes ) { - j = 0; - for( ; j <= roi.width - 8; j += 8 ) - { - v_float32x4 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 4 ); - v0 = v_min( v0, thresh4 ); - v1 = v_min( v1, thresh4 ); - v_store( dst + j, v0 ); - v_store( dst + j + 4, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshTrunc(src[j], thresh); + v_float32 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float32::nlanes ); + v0 = v0 <= thresh4; + v1 = v1 <= thresh4; + v0 = v0 & maxval4; + v1 = v1 & maxval4; + v_store( dst + j, v0 ); + v_store( dst + j + v_float32::nlanes, v1 ); } - break; - - case THRESH_TOZERO: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + if( j <= roi.width - v_float32::nlanes ) { - j = 0; - for( ; j <= roi.width - 8; j += 8 ) - { - v_float32x4 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 4 ); - v0 = ( thresh4 < v0 ) & v0; - v1 = ( thresh4 < v1 ) & v1; - v_store( dst + j, v0 ); - v_store( dst + j + 4, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshToZero(src[j], thresh); + v_float32 v0 = vx_load( src + j ); + v0 = v0 <= thresh4; + v0 = v0 & maxval4; + v_store( dst + j, v0 ); + j += v_float32::nlanes; } - break; - case THRESH_TOZERO_INV: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + for( ; j < roi.width; j++ ) + dst[j] = threshBinaryInv(src[j], thresh, maxval); + } + break; + + case THRESH_TRUNC: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float32::nlanes; j += 2*v_float32::nlanes ) { - j = 0; - for( ; j <= roi.width - 8; j += 8 ) - { - v_float32x4 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 4 ); - v0 = ( v0 <= thresh4 ) & v0; - v1 = ( v1 <= thresh4 ) & v1; - v_store( dst + j, v0 ); - v_store( dst + j + 4, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshToZeroInv(src[j], thresh); + v_float32 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float32::nlanes ); + v0 = v_min( v0, thresh4 ); + v1 = v_min( v1, thresh4 ); + v_store( dst + j, v0 ); + v_store( dst + j + v_float32::nlanes, v1 ); } - break; - default: - CV_Error( CV_StsBadArg, "" ); return; - } + if( j <= roi.width - v_float32::nlanes ) + { + v_float32 v0 = vx_load( src + j ); + v0 = v_min( v0, thresh4 ); + v_store( dst + j, v0 ); + j += v_float32::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshTrunc(src[j], thresh); + } + break; + + case THRESH_TOZERO: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float32::nlanes; j += 2*v_float32::nlanes ) + { + v_float32 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float32::nlanes ); + v0 = ( thresh4 < v0 ) & v0; + v1 = ( thresh4 < v1 ) & v1; + v_store( dst + j, v0 ); + v_store( dst + j + v_float32::nlanes, v1 ); + } + if( j <= roi.width - v_float32::nlanes ) + { + v_float32 v0 = vx_load( src + j ); + v0 = ( thresh4 < v0 ) & v0; + v_store( dst + j, v0 ); + j += v_float32::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshToZero(src[j], thresh); + } + break; + + case THRESH_TOZERO_INV: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float32::nlanes; j += 2*v_float32::nlanes ) + { + v_float32 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float32::nlanes ); + v0 = ( v0 <= thresh4 ) & v0; + v1 = ( v1 <= thresh4 ) & v1; + v_store( dst + j, v0 ); + v_store( dst + j + v_float32::nlanes, v1 ); + } + if( j <= roi.width - v_float32::nlanes ) + { + v_float32 v0 = vx_load( src + j ); + v0 = ( v0 <= thresh4 ) & v0; + v_store( dst + j, v0 ); + j += v_float32::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshToZeroInv(src[j], thresh); + } + break; + default: + CV_Error( CV_StsBadArg, "" ); return; } - else +#else + threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); #endif - { - threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); - } } static void @@ -876,128 +965,159 @@ thresh_64f(const Mat& _src, Mat& _dst, double thresh, double maxval, int type) roi.height = 1; } -#if CV_SIMD128_64F - bool useSIMD = checkHardwareSupport( CV_CPU_SSE2 ) || checkHardwareSupport( CV_CPU_NEON ); - if( useSIMD ) - { - int i, j; - v_float64x2 thresh2 = v_setall_f64( thresh ); - v_float64x2 maxval2 = v_setall_f64( maxval ); +#if CV_SIMD_64F + int i, j; + v_float64 thresh2 = vx_setall_f64( thresh ); + v_float64 maxval2 = vx_setall_f64( maxval ); - switch( type ) + switch( type ) + { + case THRESH_BINARY: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) { - case THRESH_BINARY: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + j = 0; + for( ; j <= roi.width - 2*v_float64::nlanes; j += 2*v_float64::nlanes ) { - j = 0; - for( ; j <= roi.width - 4; j += 4 ) - { - v_float64x2 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 2 ); - v0 = thresh2 < v0; - v1 = thresh2 < v1; - v0 = v0 & maxval2; - v1 = v1 & maxval2; - v_store( dst + j, v0 ); - v_store( dst + j + 2, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshBinary(src[j], thresh, maxval); + v_float64 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float64::nlanes ); + v0 = thresh2 < v0; + v1 = thresh2 < v1; + v0 = v0 & maxval2; + v1 = v1 & maxval2; + v_store( dst + j, v0 ); + v_store( dst + j + v_float64::nlanes, v1 ); } - break; - - case THRESH_BINARY_INV: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + if( j <= roi.width - v_float64::nlanes ) { - j = 0; - for( ; j <= roi.width - 4; j += 4 ) - { - v_float64x2 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 2 ); - v0 = v0 <= thresh2; - v1 = v1 <= thresh2; - v0 = v0 & maxval2; - v1 = v1 & maxval2; - v_store( dst + j, v0 ); - v_store( dst + j + 2, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshBinaryInv(src[j], thresh, maxval); + v_float64 v0 = vx_load( src + j ); + v0 = thresh2 < v0; + v0 = v0 & maxval2; + v_store( dst + j, v0 ); + j += v_float64::nlanes; } - break; - case THRESH_TRUNC: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - j = 0; - for( ; j <= roi.width - 4; j += 4 ) - { - v_float64x2 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 2 ); - v0 = v_min( v0, thresh2 ); - v1 = v_min( v1, thresh2 ); - v_store( dst + j, v0 ); - v_store( dst + j + 2, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshTrunc(src[j], thresh); - } - break; - - case THRESH_TOZERO: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - j = 0; - for( ; j <= roi.width - 4; j += 4 ) - { - v_float64x2 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 2 ); - v0 = ( thresh2 < v0 ) & v0; - v1 = ( thresh2 < v1 ) & v1; - v_store( dst + j, v0 ); - v_store( dst + j + 2, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshToZero(src[j], thresh); - } - break; - - case THRESH_TOZERO_INV: - for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) - { - j = 0; - for( ; j <= roi.width - 4; j += 4 ) - { - v_float64x2 v0, v1; - v0 = v_load( src + j ); - v1 = v_load( src + j + 2 ); - v0 = ( v0 <= thresh2 ) & v0; - v1 = ( v1 <= thresh2 ) & v1; - v_store( dst + j, v0 ); - v_store( dst + j + 2, v1 ); - } - - for( ; j < roi.width; j++ ) - dst[j] = threshToZeroInv(src[j], thresh); - } - break; - default: - CV_Error(CV_StsBadArg, ""); return; + for( ; j < roi.width; j++ ) + dst[j] = threshBinary(src[j], thresh, maxval); } + break; + + case THRESH_BINARY_INV: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float64::nlanes; j += 2*v_float64::nlanes ) + { + v_float64 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float64::nlanes ); + v0 = v0 <= thresh2; + v1 = v1 <= thresh2; + v0 = v0 & maxval2; + v1 = v1 & maxval2; + v_store( dst + j, v0 ); + v_store( dst + j + v_float64::nlanes, v1 ); + } + if( j <= roi.width - v_float64::nlanes ) + { + v_float64 v0 = vx_load( src + j ); + v0 = v0 <= thresh2; + v0 = v0 & maxval2; + v_store( dst + j, v0 ); + j += v_float64::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshBinaryInv(src[j], thresh, maxval); + } + break; + + case THRESH_TRUNC: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float64::nlanes; j += 2*v_float64::nlanes ) + { + v_float64 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float64::nlanes ); + v0 = v_min( v0, thresh2 ); + v1 = v_min( v1, thresh2 ); + v_store( dst + j, v0 ); + v_store( dst + j + v_float64::nlanes, v1 ); + } + if( j <= roi.width - v_float64::nlanes ) + { + v_float64 v0 = vx_load( src + j ); + v0 = v_min( v0, thresh2 ); + v_store( dst + j, v0 ); + j += v_float64::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshTrunc(src[j], thresh); + } + break; + + case THRESH_TOZERO: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float64::nlanes; j += 2*v_float64::nlanes ) + { + v_float64 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float64::nlanes ); + v0 = ( thresh2 < v0 ) & v0; + v1 = ( thresh2 < v1 ) & v1; + v_store( dst + j, v0 ); + v_store( dst + j + v_float64::nlanes, v1 ); + } + if( j <= roi.width - v_float64::nlanes ) + { + v_float64 v0 = vx_load( src + j ); + v0 = ( thresh2 < v0 ) & v0; + v_store( dst + j, v0 ); + j += v_float64::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshToZero(src[j], thresh); + } + break; + + case THRESH_TOZERO_INV: + for( i = 0; i < roi.height; i++, src += src_step, dst += dst_step ) + { + j = 0; + for( ; j <= roi.width - 2*v_float64::nlanes; j += 2*v_float64::nlanes ) + { + v_float64 v0, v1; + v0 = vx_load( src + j ); + v1 = vx_load( src + j + v_float64::nlanes ); + v0 = ( v0 <= thresh2 ) & v0; + v1 = ( v1 <= thresh2 ) & v1; + v_store( dst + j, v0 ); + v_store( dst + j + v_float64::nlanes, v1 ); + } + if( j <= roi.width - v_float64::nlanes ) + { + v_float64 v0 = vx_load( src + j ); + v0 = ( v0 <= thresh2 ) & v0; + v_store( dst + j, v0 ); + j += v_float64::nlanes; + } + + for( ; j < roi.width; j++ ) + dst[j] = threshToZeroInv(src[j], thresh); + } + break; + default: + CV_Error(CV_StsBadArg, ""); return; } - else +#else + threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); #endif - { - threshGeneric(roi, src, src_step, dst, dst_step, thresh, maxval, type); - } } #ifdef HAVE_IPP From 9205ad69906475841c292df53aeff21765ac3e11 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 17 Jan 2019 13:57:40 +0300 Subject: [PATCH 12/23] Enabled #include documentation in all members --- doc/CMakeLists.txt | 1 + doc/Doxyfile.in | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 94dff9e762..8d7593e6bf 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -127,6 +127,7 @@ if(DOXYGEN_FOUND) string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_IMAGE_PATH "${paths_doc} ; ${tutorial_path} ; ${tutorial_py_path} ; ${tutorial_js_path} ; ${paths_tutorial}") # TODO: remove paths_doc from EXAMPLE_PATH after face module tutorials/samples moved to separate folders string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_EXAMPLE_PATH "${example_path} ; ${paths_doc} ; ${paths_sample}") + string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_INCLUDE_ROOTS "${paths_include}") set(CMAKE_DOXYGEN_LAYOUT "${CMAKE_CURRENT_BINARY_DIR}/DoxygenLayout.xml") set(CMAKE_DOXYGEN_OUTPUT_PATH "doxygen") set(CMAKE_DOXYGEN_MAIN_REFERENCE "${refs_main}") diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 609a70cfc8..8386b14ff9 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -22,8 +22,8 @@ ABBREVIATE_BRIEF = "The $name class" \ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES -STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/modules -STRIP_FROM_INC_PATH = +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/modules @CMAKE_DOXYGEN_INCLUDE_ROOTS@ +STRIP_FROM_INC_PATH = @CMAKE_DOXYGEN_INCLUDE_ROOTS@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO @@ -72,8 +72,8 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES -SHOW_GROUPED_MEMB_INC = NO -FORCE_LOCAL_INCLUDES = YES +SHOW_GROUPED_MEMB_INC = YES +FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = YES From 6596eab66ceb1b4f481d177766a40bcc0c4124f0 Mon Sep 17 00:00:00 2001 From: berak Date: Thu, 17 Jan 2019 11:41:25 +0100 Subject: [PATCH 13/23] dnn/samples: add googlenet to model zoo --- samples/dnn/models.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/samples/dnn/models.yml b/samples/dnn/models.yml index 0e7198a660..d177a09aab 100644 --- a/samples/dnn/models.yml +++ b/samples/dnn/models.yml @@ -90,6 +90,18 @@ squeezenet: classes: "classification_classes_ILSVRC2012.txt" sample: "classification" +# Googlenet from https://github.com/BVLC/caffe/tree/master/models/bvlc_googlenet +googlenet: + model: "bvlc_googlenet.caffemodel" + config: "bvlc_googlenet.prototxt" + mean: [104, 117, 123] + scale: 1.0 + width: 224 + height: 224 + rgb: false + classes: "classification_classes_ILSVRC2012.txt" + sample: "classification" + ################################################################################ # Semantic segmentation models. ################################################################################ From f0ddf302b2d0136da9f529d05895c731e3d1a6f2 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Mon, 14 Jan 2019 09:55:44 +0300 Subject: [PATCH 14/23] Move Inference Engine to new API --- modules/dnn/perf/perf_net.cpp | 13 +- modules/dnn/src/dnn.cpp | 74 ++++++++-- modules/dnn/src/layers/batch_norm_layer.cpp | 9 ++ modules/dnn/src/layers/blank_layer.cpp | 6 + modules/dnn/src/layers/concat_layer.cpp | 9 ++ modules/dnn/src/layers/convolution_layer.cpp | 102 +++++++++---- modules/dnn/src/layers/crop_layer.cpp | 15 +- .../dnn/src/layers/detection_output_layer.cpp | 20 +++ modules/dnn/src/layers/elementwise_layers.cpp | 74 ++++++++++ modules/dnn/src/layers/eltwise_layer.cpp | 25 +++- modules/dnn/src/layers/flatten_layer.cpp | 13 +- .../dnn/src/layers/fully_connected_layer.cpp | 13 ++ modules/dnn/src/layers/lrn_layer.cpp | 12 ++ modules/dnn/src/layers/mvn_layer.cpp | 8 + .../dnn/src/layers/normalize_bbox_layer.cpp | 44 ++++++ modules/dnn/src/layers/permute_layer.cpp | 6 + modules/dnn/src/layers/pooling_layer.cpp | 43 ++++++ modules/dnn/src/layers/prior_box_layer.cpp | 53 +++++++ modules/dnn/src/layers/proposal_layer.cpp | 23 +++ modules/dnn/src/layers/reorg_layer.cpp | 6 + modules/dnn/src/layers/reshape_layer.cpp | 24 ++- modules/dnn/src/layers/resize_layer.cpp | 41 ++++++ modules/dnn/src/layers/scale_layer.cpp | 24 +++ modules/dnn/src/layers/slice_layer.cpp | 19 ++- modules/dnn/src/layers/softmax_layer.cpp | 8 + modules/dnn/src/op_inf_engine.cpp | 137 +++++++++++++++++- modules/dnn/src/op_inf_engine.hpp | 66 ++++++++- modules/dnn/test/test_backends.cpp | 4 +- modules/dnn/test/test_darknet_importer.cpp | 2 +- modules/dnn/test/test_halide_layers.cpp | 3 +- modules/dnn/test/test_layers.cpp | 4 - modules/dnn/test/test_onnx_importer.cpp | 10 +- modules/dnn/test/test_tf_importer.cpp | 13 +- modules/dnn/test/test_torch_importer.cpp | 9 +- 34 files changed, 852 insertions(+), 80 deletions(-) diff --git a/modules/dnn/perf/perf_net.cpp b/modules/dnn/perf/perf_net.cpp index cc95cc58ae..d06689a7fb 100644 --- a/modules/dnn/perf/perf_net.cpp +++ b/modules/dnn/perf/perf_net.cpp @@ -157,8 +157,7 @@ PERF_TEST_P_(DNNTestNetwork, MobileNet_SSD_v2_TensorFlow) PERF_TEST_P_(DNNTestNetwork, DenseNet_121) { - if (backend == DNN_BACKEND_HALIDE || - (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD))) + if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); processNet("dnn/DenseNet_121.caffemodel", "dnn/DenseNet_121.prototxt", "", Mat(cv::Size(224, 224), CV_32FC3)); @@ -211,8 +210,7 @@ PERF_TEST_P_(DNNTestNetwork, Inception_v2_SSD_TensorFlow) PERF_TEST_P_(DNNTestNetwork, YOLOv3) { - if (backend == DNN_BACKEND_HALIDE || - (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) + if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/dog416.png", false)); Mat inp; @@ -222,8 +220,11 @@ PERF_TEST_P_(DNNTestNetwork, YOLOv3) PERF_TEST_P_(DNNTestNetwork, EAST_text_detection) { - if (backend == DNN_BACKEND_HALIDE || - (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) + if (backend == DNN_BACKEND_HALIDE +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE < 2018030000 + || (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) +#endif + ) throw SkipTestException(""); processNet("dnn/frozen_east_text_detection.pb", "", "", Mat(cv::Size(320, 320), CV_32FC3)); } diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index b6eff6c3b5..b83630a67f 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -701,12 +701,6 @@ struct DataLayer : public Layer virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE - InferenceEngine::LayerParams lp; - lp.name = name; - lp.type = "ScaleShift"; - lp.precision = InferenceEngine::Precision::FP32; - std::shared_ptr ieLayer(new InferenceEngine::ScaleShiftLayer(lp)); - CV_CheckEQ(inputsData.size(), (size_t)1, ""); CV_CheckEQ(inputsData[0].dims, 4, ""); const size_t numChannels = inputsData[0].size[1]; @@ -717,7 +711,6 @@ struct DataLayer : public Layer {numChannels}); weights->allocate(); weights->set(std::vector(numChannels, scaleFactors[0])); - ieLayer->_weights = weights; // Mean subtraction auto biases = InferenceEngine::make_shared_blob(InferenceEngine::Precision::FP32, @@ -729,8 +722,21 @@ struct DataLayer : public Layer biasesVec[i] = -means[0][i] * scaleFactors[0]; } biases->set(biasesVec); - ieLayer->_biases = biases; +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::ScaleShiftLayer ieLayer(name); + ieLayer.setWeights(weights); + ieLayer.setBiases(biases); +#else + InferenceEngine::LayerParams lp; + lp.name = name; + lp.type = "ScaleShift"; + lp.precision = InferenceEngine::Precision::FP32; + std::shared_ptr ieLayer(new InferenceEngine::ScaleShiftLayer(lp)); + + ieLayer->_weights = weights; + ieLayer->_biases = biases; +#endif return Ptr(new InfEngineBackendNode(ieLayer)); #endif // HAVE_INF_ENGINE return Ptr(); @@ -1451,7 +1457,11 @@ struct Net::Impl if (layerNet != ieInpNode->net) { // layerNet is empty or nodes are from different graphs. +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + ieInpNode->net->addOutput(ieInpNode->layer.getName()); +#else ieInpNode->net->addOutput(ieInpNode->layer->name); +#endif } } } @@ -1527,7 +1537,7 @@ struct Net::Impl // Build Inference Engine networks from sets of layers that support this // backend. Split a whole model on several Inference Engine networks if - // some of layers is not implemented. + // some of layers are not implemented. // Set of all input and output blobs wrappers for current network. std::map > netBlobsWrappers; @@ -1543,7 +1553,7 @@ struct Net::Impl { addInfEngineNetOutputs(ld); net = Ptr(); - netBlobsWrappers.clear(); + netBlobsWrappers.clear(); // Is not used for R5 release but we don't wrap it to #ifdef. layer->preferableTarget = DNN_TARGET_CPU; continue; } @@ -1561,12 +1571,13 @@ struct Net::Impl if (ieInpNode->net != net) { net = Ptr(); - netBlobsWrappers.clear(); + netBlobsWrappers.clear(); // Is not used for R5 release but we don't wrap it to #ifdef. break; } } } +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) // The same blobs wrappers cannot be shared between two Inference Engine // networks because of explicit references between layers and blobs. // So we need to rewrap all the external blobs. @@ -1583,6 +1594,7 @@ struct Net::Impl ld.inputBlobsWrappers[i] = it->second; } netBlobsWrappers[LayerPin(ld.id, 0)] = ld.outputBlobsWrappers[0]; +#endif // IE < R5 Ptr node; if (!net.empty()) @@ -1613,6 +1625,40 @@ struct Net::Impl CV_Assert(!ieNode.empty()); ieNode->net = net; + // Convert weights in FP16 for specific targets. +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + if ((preferableTarget == DNN_TARGET_OPENCL_FP16 || + preferableTarget == DNN_TARGET_MYRIAD || + preferableTarget == DNN_TARGET_FPGA) && !fused) + { + auto& blobs = ieNode->layer.getConstantData(); + if (blobs.empty()) + { + // In case of non weightable layer we have to specify + // it's precision adding dummy blob. + auto blob = InferenceEngine::make_shared_blob( + InferenceEngine::Precision::FP16, + InferenceEngine::Layout::C, {1}); + blob->allocate(); + blobs[""] = blob; + } + else + { + for (auto& it : blobs) + it.second = convertFp16(std::const_pointer_cast(it.second)); + } + } + + if (!fused) + net->addLayer(ieNode->layer); + + net->connect(ld.inputBlobsWrappers, ld.outputBlobsWrappers, ieNode->layer.getName()); + net->addBlobs(ld.inputBlobsWrappers); + net->addBlobs(ld.outputBlobsWrappers); + addInfEngineNetOutputs(ld); + +#else // IE >= R5 + auto weightableLayer = std::dynamic_pointer_cast(ieNode->layer); if ((preferableTarget == DNN_TARGET_OPENCL_FP16 || preferableTarget == DNN_TARGET_MYRIAD || @@ -1650,10 +1696,10 @@ struct Net::Impl if (!fused) net->addLayer(ieNode->layer); addInfEngineNetOutputs(ld); +#endif // IE >= R5 } // Initialize all networks. - std::set initializedNets; for (MapIdToLayerData::reverse_iterator it = layers.rbegin(); it != layers.rend(); ++it) { LayerData &ld = it->second; @@ -2546,7 +2592,11 @@ Net Net::readFromModelOptimizer(const String& xml, const String& bin) Net cvNet; cvNet.setInputsNames(inputsNames); +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + Ptr backendNode(new InfEngineBackendNode(InferenceEngine::Builder::Layer(""))); +#else Ptr backendNode(new InfEngineBackendNode(0)); +#endif backendNode->net = Ptr(new InfEngineBackendNet(ieNet)); for (auto& it : ieNet.getOutputsInfo()) { diff --git a/modules/dnn/src/layers/batch_norm_layer.cpp b/modules/dnn/src/layers/batch_norm_layer.cpp index 9a1707a3e8..522d0229ba 100644 --- a/modules/dnn/src/layers/batch_norm_layer.cpp +++ b/modules/dnn/src/layers/batch_norm_layer.cpp @@ -349,6 +349,14 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::ScaleShiftLayer ieLayer(name); + + const size_t numChannels = weights_.total(); + ieLayer.setWeights(wrapToInfEngineBlob(weights_, {numChannels}, InferenceEngine::Layout::C)); + ieLayer.setBiases(wrapToInfEngineBlob(bias_, {numChannels}, InferenceEngine::Layout::C)); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "ScaleShift"; @@ -360,6 +368,7 @@ public: ieLayer->_biases = wrapToInfEngineBlob(bias_, {numChannels}, InferenceEngine::Layout::C); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/blank_layer.cpp b/modules/dnn/src/layers/blank_layer.cpp index 1eb149b3d1..9f8590bea7 100644 --- a/modules/dnn/src/layers/blank_layer.cpp +++ b/modules/dnn/src/layers/blank_layer.cpp @@ -110,6 +110,11 @@ public: virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::SplitLayer ieLayer(name); + ieLayer.setOutputPorts({InferenceEngine::Port()}); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); CV_Assert(!input->dims.empty()); @@ -123,6 +128,7 @@ public: ieLayer->params["out_sizes"] = format("%d", (int)input->dims[0]); #endif return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/concat_layer.cpp b/modules/dnn/src/layers/concat_layer.cpp index 6bad580334..90def743a4 100644 --- a/modules/dnn/src/layers/concat_layer.cpp +++ b/modules/dnn/src/layers/concat_layer.cpp @@ -301,6 +301,14 @@ public: virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); + + InferenceEngine::Builder::ConcatLayer ieLayer(name); + ieLayer.setAxis(clamp(axis, input->dims.size())); + ieLayer.setInputPorts(std::vector(inputs.size())); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); InferenceEngine::LayerParams lp; lp.name = name; @@ -309,6 +317,7 @@ public: std::shared_ptr ieLayer(new InferenceEngine::ConcatLayer(lp)); ieLayer->_axis = clamp(axis, input->dims.size()); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 5dbd5ba895..c54db528cc 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -451,6 +451,54 @@ public: const int inpGroupCn = blobs[0].size[1]; const int group = inpCn / inpGroupCn; + auto ieWeights = wrapToInfEngineBlob(blobs[0], InferenceEngine::Layout::OIHW); + if (newWeightAndBias) + { + if (weightsMat.isContinuous()) + { + Mat fusedWeights = weightsMat.reshape(1, blobs[0].dims, blobs[0].size); + ieWeights = wrapToInfEngineBlob(fusedWeights, InferenceEngine::Layout::OIHW); + } + else + { + ieWeights = InferenceEngine::make_shared_blob( + InferenceEngine::Precision::FP32, InferenceEngine::Layout::OIHW, + ieWeights->dims()); + ieWeights->allocate(); + + Mat newWeights = infEngineBlobToMat(ieWeights).reshape(1, outCn); + Mat fusedWeights = weightsMat.colRange(0, newWeights.cols); + fusedWeights.copyTo(newWeights); + } + } + InferenceEngine::Blob::Ptr ieBiases; + if (hasBias() || fusedBias) + { + Mat biasesMat({outCn}, CV_32F, &biasvec[0]); + ieBiases = wrapToInfEngineBlob(biasesMat, {(size_t)outCn}, InferenceEngine::Layout::C); + } + +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::ConvolutionLayer ieLayer(name); + + ieLayer.setKernel({kernel.height, kernel.width}); + ieLayer.setStrides({stride.height, stride.width}); + ieLayer.setDilation({dilation.height, dilation.width}); + ieLayer.setPaddingsBegin({pad.height, pad.width}); + ieLayer.setPaddingsEnd({pad.height, pad.width}); + ieLayer.setGroup(group); + ieLayer.setOutDepth(outCn); + + ieLayer.setWeights(ieWeights); + if (ieBiases) + ieLayer.setBiases(ieBiases); + + InferenceEngine::Builder::Layer l = ieLayer; + if (!padMode.empty()) + l.getParameters()["auto_pad"] = padMode == "VALID" ? std::string("valid") : std::string("same_upper"); + + return Ptr(new InfEngineBackendNode(l)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Convolution"; @@ -487,32 +535,11 @@ public: ieLayer->_out_depth = outCn; ieLayer->_group = group; - ieLayer->_weights = wrapToInfEngineBlob(blobs[0], InferenceEngine::Layout::OIHW); - if (newWeightAndBias) - { - if (weightsMat.isContinuous()) - { - Mat fusedWeights = weightsMat.reshape(1, blobs[0].dims, blobs[0].size); - ieLayer->_weights = wrapToInfEngineBlob(fusedWeights, InferenceEngine::Layout::OIHW); - } - else - { - ieLayer->_weights = InferenceEngine::make_shared_blob( - InferenceEngine::Precision::FP32, InferenceEngine::Layout::OIHW, - ieLayer->_weights->dims()); - ieLayer->_weights->allocate(); - - Mat newWeights = infEngineBlobToMat(ieLayer->_weights).reshape(1, outCn); - Mat fusedWeights = weightsMat.colRange(0, newWeights.cols); - fusedWeights.copyTo(newWeights); - } - } - if (hasBias() || fusedBias) - { - Mat biasesMat({outCn}, CV_32F, &biasvec[0]); - ieLayer->_biases = wrapToInfEngineBlob(biasesMat, {(size_t)outCn}, InferenceEngine::Layout::C); - } + ieLayer->_weights = ieWeights; + if (ieBiases) + ieLayer->_biases = ieBiases; return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } @@ -1123,6 +1150,9 @@ public: #ifdef HAVE_INF_ENGINE if (backendId == DNN_BACKEND_INFERENCE_ENGINE) { + if (INF_ENGINE_RELEASE == 2018050000 && (adjustPad.height || adjustPad.width)) + return false; + const int outGroupCn = blobs[0].size[1]; // Weights are in IOHW layout const int group = numOutput / outGroupCn; if (group != 1) @@ -1677,6 +1707,27 @@ public: virtual Ptr initInfEngine(const std::vector > &) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + const int outGroupCn = blobs[0].size[1]; // Weights are in IOHW layout + const int group = numOutput / outGroupCn; + + InferenceEngine::Builder::DeconvolutionLayer ieLayer(name); + + ieLayer.setKernel({kernel.height, kernel.width}); + ieLayer.setStrides({stride.height, stride.width}); + ieLayer.setDilation({dilation.height, dilation.width}); + ieLayer.setPaddingsBegin({pad.height, pad.width}); + ieLayer.setPaddingsEnd({pad.height, pad.width}); + ieLayer.setGroup(group); + ieLayer.setOutDepth(numOutput); + + ieLayer.setWeights(wrapToInfEngineBlob(blobs[0], InferenceEngine::Layout::OIHW)); + if (hasBias()) + { + ieLayer.setBiases(wrapToInfEngineBlob(blobs[1], {(size_t)numOutput}, InferenceEngine::Layout::C)); + } + return Ptr(new InfEngineBackendNode(ieLayer)); +#else const int outGroupCn = blobs[0].size[1]; // Weights are in IOHW layout const int group = numOutput / outGroupCn; @@ -1716,6 +1767,7 @@ public: ieLayer->_biases = wrapToInfEngineBlob(blobs[1], {(size_t)numOutput}, InferenceEngine::Layout::C); } return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/crop_layer.cpp b/modules/dnn/src/layers/crop_layer.cpp index 32cdbbaa00..c7cd99c9aa 100644 --- a/modules/dnn/src/layers/crop_layer.cpp +++ b/modules/dnn/src/layers/crop_layer.cpp @@ -67,8 +67,12 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { - return backendId == DNN_BACKEND_OPENCV || - (backendId == DNN_BACKEND_INFERENCE_ENGINE && crop_ranges.size() == 4); +#ifdef HAVE_INF_ENGINE + if (backendId == DNN_BACKEND_INFERENCE_ENGINE) + return INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) && crop_ranges.size() == 4; + else +#endif + return backendId == DNN_BACKEND_OPENCV; } bool getMemoryShapes(const std::vector &inputs, @@ -145,9 +149,10 @@ public: input(&crop_ranges[0]).copyTo(outputs[0]); } +#ifdef HAVE_INF_ENGINE virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { -#ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Crop"; @@ -181,9 +186,11 @@ public: ieLayer->dim.push_back(crop_ranges[3].end - crop_ranges[3].start); #endif return Ptr(new InfEngineBackendNode(ieLayer)); -#endif // HAVE_INF_ENGINE +#else return Ptr(); +#endif // IE < R5 } +#endif std::vector crop_ranges; }; diff --git a/modules/dnn/src/layers/detection_output_layer.cpp b/modules/dnn/src/layers/detection_output_layer.cpp index 4c341d74dc..0226cf26a5 100644 --- a/modules/dnn/src/layers/detection_output_layer.cpp +++ b/modules/dnn/src/layers/detection_output_layer.cpp @@ -939,6 +939,25 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::DetectionOutputLayer ieLayer(name); + + ieLayer.setNumClasses(_numClasses); + ieLayer.setShareLocation(_shareLocation); + ieLayer.setBackgroudLabelId(_backgroundLabelId); + ieLayer.setNMSThreshold(_nmsThreshold); + ieLayer.setTopK(_topK); + ieLayer.setKeepTopK(_keepTopK); + ieLayer.setConfidenceThreshold(_confidenceThreshold); + ieLayer.setVariantEncodedInTarget(_varianceEncodedInTarget); + ieLayer.setCodeType("caffe.PriorBoxParameter." + _codeType); + ieLayer.setInputPorts(std::vector(3)); + + InferenceEngine::Builder::Layer l = ieLayer; + l.getParameters()["eta"] = std::string("1.0"); + + return Ptr(new InfEngineBackendNode(l)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "DetectionOutput"; @@ -956,6 +975,7 @@ public: ieLayer->params["variance_encoded_in_target"] = _varianceEncodedInTarget ? "1" : "0"; ieLayer->params["code_type"] = "caffe.PriorBoxParameter." + _codeType; return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index dea7c6c0d6..b2e0621d6d 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -152,10 +152,16 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer ieLayer = func.initInfEngineBuilderAPI(); + ieLayer.setName(this->name); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = this->name; lp.precision = InferenceEngine::Precision::FP32; return Ptr(new InfEngineBackendNode(func.initInfEngine(lp))); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } @@ -345,6 +351,12 @@ struct ReLUFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + return InferenceEngine::Builder::ReLULayer("").setNegativeSlope(slope); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { lp.type = "ReLU"; @@ -353,6 +365,7 @@ struct ReLUFunctor ieLayer->params["negative_slope"] = format("%f", slope); return ieLayer; } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } @@ -452,6 +465,12 @@ struct ReLU6Functor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + return InferenceEngine::Builder::ClampLayer("").setMinValue(minValue).setMaxValue(maxValue); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { lp.type = "Clamp"; @@ -462,6 +481,7 @@ struct ReLU6Functor ieLayer->params["max"] = format("%f", maxValue); return ieLayer; } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } @@ -530,12 +550,19 @@ struct TanHFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + return InferenceEngine::Builder::TanHLayer(""); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { lp.type = "TanH"; std::shared_ptr ieLayer(new InferenceEngine::CNNLayer(lp)); return ieLayer; } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } @@ -604,12 +631,19 @@ struct SigmoidFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + return InferenceEngine::Builder::SigmoidLayer(""); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { lp.type = "Sigmoid"; std::shared_ptr ieLayer(new InferenceEngine::CNNLayer(lp)); return ieLayer; } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } @@ -680,11 +714,18 @@ struct ELUFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + return InferenceEngine::Builder::ELULayer(""); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { lp.type = "ELU"; return InferenceEngine::CNNLayerPtr(new InferenceEngine::CNNLayer(lp)); } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } @@ -753,6 +794,12 @@ struct AbsValFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + return InferenceEngine::Builder::ReLULayer("").setNegativeSlope(-1); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { lp.type = "ReLU"; @@ -761,6 +808,7 @@ struct AbsValFunctor ieLayer->params["negative_slope"] = "-1.0"; return ieLayer; } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } @@ -808,11 +856,18 @@ struct BNLLFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + CV_Error(Error::StsNotImplemented, ""); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { CV_Error(Error::StsNotImplemented, "BNLL"); return InferenceEngine::CNNLayerPtr(); } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } @@ -917,6 +972,14 @@ struct PowerFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + return InferenceEngine::Builder::PowerLayer("").setPower(power) + .setScale(scale) + .setShift(shift); + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { if (power == 1.0f && scale == 1.0f && shift == 0.0f) @@ -936,6 +999,7 @@ struct PowerFunctor return ieLayer; } } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr& top) @@ -1067,6 +1131,15 @@ struct ChannelsPReLUFunctor #endif // HAVE_HALIDE #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer initInfEngineBuilderAPI() + { + InferenceEngine::Builder::PReLULayer ieLayer(""); + const size_t numChannels = scale.total(); + ieLayer.setWeights(wrapToInfEngineBlob(scale, {numChannels}, InferenceEngine::Layout::C)); + return ieLayer; + } +#else InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { lp.type = "PReLU"; @@ -1075,6 +1148,7 @@ struct ChannelsPReLUFunctor ieLayer->_weights = wrapToInfEngineBlob(scale, {numChannels}, InferenceEngine::Layout::C); return ieLayer; } +#endif #endif // HAVE_INF_ENGINE bool tryFuse(Ptr&) { return false; } diff --git a/modules/dnn/src/layers/eltwise_layer.cpp b/modules/dnn/src/layers/eltwise_layer.cpp index c038eb19e0..18925010d0 100644 --- a/modules/dnn/src/layers/eltwise_layer.cpp +++ b/modules/dnn/src/layers/eltwise_layer.cpp @@ -99,7 +99,7 @@ public: return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE || (backendId == DNN_BACKEND_INFERENCE_ENGINE && - (preferableTarget != DNN_TARGET_MYRIAD || coeffs.empty())); + (preferableTarget != DNN_TARGET_OPENCL || coeffs.empty())); } bool getMemoryShapes(const std::vector &inputs, @@ -420,9 +420,29 @@ public: return Ptr(); } - virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE + virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::EltwiseLayer ieLayer(name); + + ieLayer.setInputPorts(std::vector(inputs.size())); + + if (op == SUM) + ieLayer.setEltwiseType(InferenceEngine::Builder::EltwiseLayer::EltwiseType::SUM); + else if (op == PROD) + ieLayer.setEltwiseType(InferenceEngine::Builder::EltwiseLayer::EltwiseType::MUL); + else if (op == MAX) + ieLayer.setEltwiseType(InferenceEngine::Builder::EltwiseLayer::EltwiseType::MAX); + else + CV_Error(Error::StsNotImplemented, "Unsupported eltwise operation"); + + InferenceEngine::Builder::Layer l = ieLayer; + if (!coeffs.empty()) + l.getParameters()["coeff"] = coeffs; + + return Ptr(new InfEngineBackendNode(l)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Eltwise"; @@ -438,6 +458,7 @@ public: else CV_Error(Error::StsNotImplemented, "Unsupported eltwise operation"); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/flatten_layer.cpp b/modules/dnn/src/layers/flatten_layer.cpp index e3382f2d53..3a704dca81 100644 --- a/modules/dnn/src/layers/flatten_layer.cpp +++ b/modules/dnn/src/layers/flatten_layer.cpp @@ -152,9 +152,19 @@ public: } } - virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE + virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer ieLayer(name); + ieLayer.setName(name); + ieLayer.setType("Flatten"); + ieLayer.getParameters()["axis"] = _startAxis; + ieLayer.getParameters()["end_axis"] = _endAxis; + ieLayer.setInputPorts(std::vector(1)); + ieLayer.setOutputPorts(std::vector(1)); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Flatten"; @@ -163,6 +173,7 @@ public: ieLayer->params["axis"] = format("%d", _startAxis); ieLayer->params["end_axis"] = format("%d", _endAxis); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index 78d3e809b5..3a71a872fe 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -442,6 +442,18 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::FullyConnectedLayer ieLayer(name); + + const int outNum = blobs[0].size[0]; + ieLayer.setOutputNum(outNum); + + ieLayer.setWeights(wrapToInfEngineBlob(blobs[0], {(size_t)blobs[0].size[0], (size_t)blobs[0].size[1], 1, 1}, InferenceEngine::Layout::OIHW)); + if (blobs.size() > 1) + ieLayer.setBiases(wrapToInfEngineBlob(blobs[1], {(size_t)outNum}, InferenceEngine::Layout::C)); + + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "FullyConnected"; @@ -456,6 +468,7 @@ public: if (blobs.size() > 1) ieLayer->_biases = wrapToInfEngineBlob(blobs[1], {(size_t)ieLayer->_out_num}, InferenceEngine::Layout::C); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/lrn_layer.cpp b/modules/dnn/src/layers/lrn_layer.cpp index edaa212d64..f34faff338 100644 --- a/modules/dnn/src/layers/lrn_layer.cpp +++ b/modules/dnn/src/layers/lrn_layer.cpp @@ -382,6 +382,17 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::NormLayer ieLayer(name); + ieLayer.setSize(size); + ieLayer.setAlpha(alpha); + ieLayer.setBeta(beta); + ieLayer.setAcrossMaps(type == CHANNEL_NRM); + + InferenceEngine::Builder::Layer l = ieLayer; + l.getParameters()["k"] = bias; + return Ptr(new InfEngineBackendNode(l)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Norm"; @@ -394,6 +405,7 @@ public: ieLayer->_alpha = alpha; ieLayer->_isAcrossMaps = (type == CHANNEL_NRM); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/mvn_layer.cpp b/modules/dnn/src/layers/mvn_layer.cpp index 93dd5f05f6..772902ca01 100644 --- a/modules/dnn/src/layers/mvn_layer.cpp +++ b/modules/dnn/src/layers/mvn_layer.cpp @@ -371,6 +371,13 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::MVNLayer ieLayer(name); + ieLayer.setAcrossChannels(acrossChannels); + ieLayer.setNormalize(normVariance); + ieLayer.setEpsilon(eps); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "MVN"; @@ -380,6 +387,7 @@ public: ieLayer->params["normalize_variance"] = normVariance ? "1" : "0"; ieLayer->params["eps"] = format("%f", eps); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/normalize_bbox_layer.cpp b/modules/dnn/src/layers/normalize_bbox_layer.cpp index b3ca64f24a..4766f1704e 100644 --- a/modules/dnn/src/layers/normalize_bbox_layer.cpp +++ b/modules/dnn/src/layers/normalize_bbox_layer.cpp @@ -264,6 +264,49 @@ public: virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); + if (input->dims.size() == 4) + { + InferenceEngine::Builder::NormalizeLayer ieLayer(name); + + ieLayer.setChannelShared(false); + ieLayer.setAcrossMaps(acrossSpatial); + ieLayer.setEpsilon(epsilon); + + InferenceEngine::Builder::Layer l = ieLayer; + const int numChannels = input->dims[2]; // NOTE: input->dims are reversed (whcn) + if (blobs.empty()) + { + auto weights = InferenceEngine::make_shared_blob(InferenceEngine::Precision::FP32, + InferenceEngine::Layout::C, + {(size_t)numChannels}); + weights->allocate(); + std::vector ones(numChannels, 1); + weights->set(ones); + l.addConstantData("weights", weights); + l.getParameters()["channel_shared"] = false; + } + else + { + CV_Assert(numChannels == blobs[0].total()); + l.addConstantData("weights", wrapToInfEngineBlob(blobs[0], {(size_t)numChannels}, InferenceEngine::Layout::C)); + l.getParameters()["channel_shared"] = blobs[0].total() == 1; + } + l.getParameters()["across_spatial"] = acrossSpatial; + return Ptr(new InfEngineBackendNode(l)); + } + else + { + InferenceEngine::Builder::GRNLayer ieLayer(name); + ieLayer.setBeta(epsilon); + + InferenceEngine::Builder::Layer l = ieLayer; + l.getParameters()["bias"] = epsilon; + + return Ptr(new InfEngineBackendNode(l)); + } +#else InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); InferenceEngine::LayerParams lp; @@ -307,6 +350,7 @@ public: ieLayer->params["bias"] = format("%f", epsilon); return Ptr(new InfEngineBackendNode(ieLayer)); } +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/permute_layer.cpp b/modules/dnn/src/layers/permute_layer.cpp index 00796d52b8..9e529216e9 100644 --- a/modules/dnn/src/layers/permute_layer.cpp +++ b/modules/dnn/src/layers/permute_layer.cpp @@ -373,6 +373,11 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::PermuteLayer ieLayer(name); + ieLayer.setOrder(_order); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Permute"; @@ -385,6 +390,7 @@ public: ieLayer->params["order"] += format(",%d", _order[i]); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/pooling_layer.cpp b/modules/dnn/src/layers/pooling_layer.cpp index bf17415561..79b5e3f7d2 100644 --- a/modules/dnn/src/layers/pooling_layer.cpp +++ b/modules/dnn/src/layers/pooling_layer.cpp @@ -257,6 +257,48 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + if (type == MAX || type == AVE) + { + InferenceEngine::Builder::PoolingLayer ieLayer(name); + ieLayer.setKernel({kernel.height, kernel.width}); + ieLayer.setStrides({stride.height, stride.width}); + ieLayer.setPaddingsBegin({pad_t, pad_l}); + ieLayer.setPaddingsEnd({pad_b, pad_r}); + ieLayer.setPoolingType(type == MAX ? + InferenceEngine::Builder::PoolingLayer::PoolingType::MAX : + InferenceEngine::Builder::PoolingLayer::PoolingType::AVG); + ieLayer.setRoundingType(ceilMode ? + InferenceEngine::Builder::PoolingLayer::RoundingType::CEIL : + InferenceEngine::Builder::PoolingLayer::RoundingType::FLOOR); + ieLayer.setExcludePad(type == AVE && padMode == "SAME"); + + InferenceEngine::Builder::Layer l = ieLayer; + if (!padMode.empty()) + l.getParameters()["auto_pad"] = padMode == "VALID" ? std::string("valid") : std::string("same_upper"); + return Ptr(new InfEngineBackendNode(l)); + } + else if (type == ROI) + { + InferenceEngine::Builder::ROIPoolingLayer ieLayer(name); + ieLayer.setSpatialScale(spatialScale); + ieLayer.setPooled({pooledSize.height, pooledSize.width}); + ieLayer.setInputPorts(std::vector(2)); + return Ptr(new InfEngineBackendNode(ieLayer)); + } + else if (type == PSROI) + { + InferenceEngine::Builder::PSROIPoolingLayer ieLayer(name); + ieLayer.setSpatialScale(spatialScale); + ieLayer.setOutputDim(psRoiOutChannels); + ieLayer.setGroupSize(pooledSize.width); + ieLayer.setInputPorts(std::vector(2)); + return Ptr(new InfEngineBackendNode(ieLayer)); + } + else + CV_Error(Error::StsNotImplemented, "Unsupported pooling type"); + return Ptr(); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.precision = InferenceEngine::Precision::FP32; @@ -315,6 +357,7 @@ public: CV_Error(Error::StsNotImplemented, "Unsupported pooling type"); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/prior_box_layer.cpp b/modules/dnn/src/layers/prior_box_layer.cpp index 44f18ec5c0..458d667cbc 100644 --- a/modules/dnn/src/layers/prior_box_layer.cpp +++ b/modules/dnn/src/layers/prior_box_layer.cpp @@ -483,6 +483,58 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + if (_explicitSizes) + { + InferenceEngine::Builder::PriorBoxClusteredLayer ieLayer(name); + + CV_Assert(_stepX == _stepY); + ieLayer.setStep(_stepX); + + CV_CheckEQ(_offsetsX.size(), (size_t)1, ""); CV_CheckEQ(_offsetsY.size(), (size_t)1, ""); CV_CheckEQ(_offsetsX[0], _offsetsY[0], ""); + ieLayer.setOffset(_offsetsX[0]); + + ieLayer.setClip(_clip); + ieLayer.setFlip(false); // We already flipped aspect ratios. + + InferenceEngine::Builder::Layer l = ieLayer; + + CV_Assert_N(!_boxWidths.empty(), !_boxHeights.empty(), !_variance.empty()); + CV_Assert(_boxWidths.size() == _boxHeights.size()); + l.getParameters()["width"] = _boxWidths; + l.getParameters()["height"] = _boxHeights; + l.getParameters()["variance"] = _variance; + return Ptr(new InfEngineBackendNode(l)); + } + else + { + InferenceEngine::Builder::PriorBoxLayer ieLayer(name); + + CV_Assert(!_explicitSizes); + + ieLayer.setMinSize(_minSize); + if (_maxSize > 0) + ieLayer.setMaxSize(_maxSize); + + CV_Assert(_stepX == _stepY); + ieLayer.setStep(_stepX); + + CV_CheckEQ(_offsetsX.size(), (size_t)1, ""); CV_CheckEQ(_offsetsY.size(), (size_t)1, ""); CV_CheckEQ(_offsetsX[0], _offsetsY[0], ""); + ieLayer.setOffset(_offsetsX[0]); + + ieLayer.setClip(_clip); + ieLayer.setFlip(false); // We already flipped aspect ratios. + + InferenceEngine::Builder::Layer l = ieLayer; + if (!_aspectRatios.empty()) + { + l.getParameters()["aspect_ratio"] = _aspectRatios; + } + CV_Assert(!_variance.empty()); + l.getParameters()["variance"] = _variance; + return Ptr(new InfEngineBackendNode(l)); + } +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = _explicitSizes ? "PriorBoxClustered" : "PriorBox"; @@ -538,6 +590,7 @@ public: ieLayer->params["offset"] = format("%f", _offsetsX[0]); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/proposal_layer.cpp b/modules/dnn/src/layers/proposal_layer.cpp index f559ee40e2..6514ed3a5c 100644 --- a/modules/dnn/src/layers/proposal_layer.cpp +++ b/modules/dnn/src/layers/proposal_layer.cpp @@ -328,6 +328,28 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::ProposalLayer ieLayer(name); + + ieLayer.setBaseSize(baseSize); + ieLayer.setFeatStride(featStride); + ieLayer.setMinSize(16); + ieLayer.setNMSThresh(nmsThreshold); + ieLayer.setPostNMSTopN(keepTopAfterNMS); + ieLayer.setPreNMSTopN(keepTopBeforeNMS); + + std::vector scalesVec(scales.size()); + for (int i = 0; i < scales.size(); ++i) + scalesVec[i] = scales.get(i); + ieLayer.setScale(scalesVec); + + std::vector ratiosVec(ratios.size()); + for (int i = 0; i < ratios.size(); ++i) + ratiosVec[i] = ratios.get(i); + ieLayer.setRatio(ratiosVec); + + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Proposal"; @@ -353,6 +375,7 @@ public: ieLayer->params["scale"] += format(",%f", scales.get(i)); } return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/reorg_layer.cpp b/modules/dnn/src/layers/reorg_layer.cpp index a98f690e65..3e42db5de1 100644 --- a/modules/dnn/src/layers/reorg_layer.cpp +++ b/modules/dnn/src/layers/reorg_layer.cpp @@ -181,6 +181,11 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::ReorgYoloLayer ieLayer(name); + ieLayer.setStride(reorgStride); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "ReorgYolo"; @@ -188,6 +193,7 @@ public: std::shared_ptr ieLayer(new InferenceEngine::CNNLayer(lp)); ieLayer->params["stride"] = format("%d", reorgStride); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/reshape_layer.cpp b/modules/dnn/src/layers/reshape_layer.cpp index 4109802a66..d6290456fa 100644 --- a/modules/dnn/src/layers/reshape_layer.cpp +++ b/modules/dnn/src/layers/reshape_layer.cpp @@ -203,6 +203,17 @@ public: return true; } + void finalize(InputArrayOfArrays, OutputArrayOfArrays outputs_arr) CV_OVERRIDE + { + std::vector outputs; + outputs_arr.getMatVector(outputs); + + CV_Assert(!outputs.empty()); + outShapes.resize(outputs.size()); + for (int i = 0; i < outputs.size(); ++i) + outShapes[i] = shape(outputs[i]); + } + bool forward_ocl(InputArrayOfArrays inps, OutputArrayOfArrays outs, OutputArrayOfArrays internals) { std::vector inputs; @@ -218,8 +229,7 @@ public: void *dst_handle = outputs[i].handle(ACCESS_WRITE); if (src_handle != dst_handle) { - MatShape outShape = shape(outputs[i]); - UMat umat = srcBlob.reshape(1, (int)outShape.size(), &outShape[0]); + UMat umat = srcBlob.reshape(1, (int)outShapes[i].size(), &outShapes[i][0]); umat.copyTo(outputs[i]); } } @@ -250,6 +260,12 @@ public: virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::ReshapeLayer ieLayer(name); + CV_Assert(outShapes.size() == 1); + ieLayer.setDims(outShapes[0]); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Reshape"; @@ -265,9 +281,13 @@ public: ieLayer->shape = std::vector(shapeSrc->dims.rbegin(), shapeSrc->dims.rend()); } return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } + +private: + std::vector outShapes; }; Ptr ReshapeLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/resize_layer.cpp b/modules/dnn/src/layers/resize_layer.cpp index 6aa32150b6..03d806ad2c 100644 --- a/modules/dnn/src/layers/resize_layer.cpp +++ b/modules/dnn/src/layers/resize_layer.cpp @@ -163,6 +163,33 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer ieLayer(name); + ieLayer.setName(name); + if (interpolation == "nearest") + { + ieLayer.setType("Resample"); + ieLayer.getParameters()["type"] = std::string("caffe.ResampleParameter.NEAREST"); + ieLayer.getParameters()["antialias"] = false; + if (scaleWidth != scaleHeight) + CV_Error(Error::StsNotImplemented, "resample with sw != sh"); + ieLayer.getParameters()["factor"] = 1.0 / scaleWidth; + } + else if (interpolation == "bilinear") + { + ieLayer.setType("Interp"); + ieLayer.getParameters()["pad_beg"] = 0; + ieLayer.getParameters()["pad_end"] = 0; + ieLayer.getParameters()["align_corners"] = false; + } + else + CV_Error(Error::StsNotImplemented, "Unsupported interpolation: " + interpolation); + ieLayer.getParameters()["width"] = outWidth; + ieLayer.getParameters()["height"] = outHeight; + ieLayer.setInputPorts(std::vector(1)); + ieLayer.setOutputPorts(std::vector(1)); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.precision = InferenceEngine::Precision::FP32; @@ -187,6 +214,7 @@ public: ieLayer->params["width"] = cv::format("%d", outWidth); ieLayer->params["height"] = cv::format("%d", outHeight); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } @@ -247,6 +275,18 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer ieLayer(name); + ieLayer.setName(name); + ieLayer.setType("Interp"); + ieLayer.getParameters()["pad_beg"] = 0; + ieLayer.getParameters()["pad_end"] = 0; + ieLayer.getParameters()["width"] = outWidth; + ieLayer.getParameters()["height"] = outHeight; + ieLayer.setInputPorts(std::vector(1)); + ieLayer.setOutputPorts(std::vector(1)); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "Interp"; @@ -256,6 +296,7 @@ public: ieLayer->params["pad_beg"] = "0"; ieLayer->params["pad_end"] = "0"; return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/scale_layer.cpp b/modules/dnn/src/layers/scale_layer.cpp index b217632584..a11fd379a2 100644 --- a/modules/dnn/src/layers/scale_layer.cpp +++ b/modules/dnn/src/layers/scale_layer.cpp @@ -197,6 +197,29 @@ public: virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::ScaleShiftLayer ieLayer(name); + + CV_Assert(!blobs.empty()); + const size_t numChannels = blobs[0].total(); + if (hasWeights) + { + ieLayer.setWeights(wrapToInfEngineBlob(blobs[0], {numChannels}, InferenceEngine::Layout::C)); + } + else + { + auto weights = InferenceEngine::make_shared_blob(InferenceEngine::Precision::FP32, + {numChannels}); + weights->allocate(); + + std::vector ones(numChannels, 1); + weights->set(ones); + ieLayer.setWeights(weights); + } + if (hasBias) + ieLayer.setBiases(wrapToInfEngineBlob(blobs.back(), {numChannels}, InferenceEngine::Layout::C)); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::LayerParams lp; lp.name = name; lp.type = "ScaleShift"; @@ -223,6 +246,7 @@ public: ieLayer->_biases = wrapToInfEngineBlob(blobs.back(), {numChannels}, InferenceEngine::Layout::C); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/layers/slice_layer.cpp b/modules/dnn/src/layers/slice_layer.cpp index 66f9aea440..0821979376 100644 --- a/modules/dnn/src/layers/slice_layer.cpp +++ b/modules/dnn/src/layers/slice_layer.cpp @@ -110,8 +110,15 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { - return backendId == DNN_BACKEND_OPENCV || - (backendId == DNN_BACKEND_INFERENCE_ENGINE && sliceRanges.size() == 1 && sliceRanges[0].size() == 4); +#ifdef HAVE_INF_ENGINE + if (backendId == DNN_BACKEND_INFERENCE_ENGINE) + { + return INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) && + sliceRanges.size() == 1 && sliceRanges[0].size() == 4; + } + else +#endif + return backendId == DNN_BACKEND_OPENCV; } bool getMemoryShapes(const std::vector &inputs, @@ -254,9 +261,10 @@ public: } } +#ifdef HAVE_INF_ENGINE virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { -#ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); InferenceEngine::LayerParams lp; lp.name = name; @@ -286,10 +294,11 @@ public: ieLayer->dim.push_back(sliceRanges[0][i].end - sliceRanges[0][i].start); } return Ptr(new InfEngineBackendNode(ieLayer)); - -#endif // HAVE_INF_ENGINE +#else return Ptr(); +#endif // IE < R5 } +#endif }; Ptr SliceLayer::create(const LayerParams& params) diff --git a/modules/dnn/src/layers/softmax_layer.cpp b/modules/dnn/src/layers/softmax_layer.cpp index 5d440c0c96..7bd2d0c20a 100644 --- a/modules/dnn/src/layers/softmax_layer.cpp +++ b/modules/dnn/src/layers/softmax_layer.cpp @@ -312,6 +312,13 @@ public: virtual Ptr initInfEngine(const std::vector >& inputs) CV_OVERRIDE { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); + + InferenceEngine::Builder::SoftMaxLayer ieLayer(name); + ieLayer.setAxis(clamp(axisRaw, input->dims.size())); + return Ptr(new InfEngineBackendNode(ieLayer)); +#else InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]); InferenceEngine::LayerParams lp; @@ -321,6 +328,7 @@ public: std::shared_ptr ieLayer(new InferenceEngine::SoftMaxLayer(lp)); ieLayer->axis = clamp(axisRaw, input->dims.size()); return Ptr(new InfEngineBackendNode(ieLayer)); +#endif #endif // HAVE_INF_ENGINE return Ptr(); } diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index 8bab885014..658ffd0a2e 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -18,6 +18,10 @@ namespace cv { namespace dnn { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) +InfEngineBackendNode::InfEngineBackendNode(const InferenceEngine::Builder::Layer& _layer) + : BackendNode(DNN_BACKEND_INFERENCE_ENGINE), layer(_layer) {} +#else InfEngineBackendNode::InfEngineBackendNode(const InferenceEngine::CNNLayerPtr& _layer) : BackendNode(DNN_BACKEND_INFERENCE_ENGINE), layer(_layer) {} @@ -40,6 +44,7 @@ void InfEngineBackendNode::connect(std::vector >& inputs, layer->outData[0] = dataPtr; dataPtr->creatorLayer = InferenceEngine::CNNLayerWeakPtr(layer); } +#endif static std::vector > infEngineWrappers(const std::vector >& ptrs) @@ -54,6 +59,129 @@ infEngineWrappers(const std::vector >& ptrs) return wrappers; } +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + +InfEngineBackendNet::InfEngineBackendNet() : netBuilder("") +{ + hasNetOwner = false; + targetDevice = InferenceEngine::TargetDevice::eCPU; +} + +InfEngineBackendNet::InfEngineBackendNet(InferenceEngine::CNNNetwork& net) : netBuilder(""), cnn(net) +{ + hasNetOwner = true; + targetDevice = InferenceEngine::TargetDevice::eCPU; +} + +void InfEngineBackendNet::connect(const std::vector >& inputs, + const std::vector >& outputs, + const std::string& layerName) +{ + std::vector > inpWrappers = infEngineWrappers(inputs); + std::map::iterator it = layers.find(layerName); + CV_Assert(it != layers.end()); + + const int layerId = it->second; + for (int i = 0; i < inpWrappers.size(); ++i) + { + const auto& inp = inpWrappers[i]; + const std::string& inpName = inp->dataPtr->name; + int inpId; + it = layers.find(inpName); + if (it == layers.end()) + { + InferenceEngine::Builder::InputLayer inpLayer(inpName); + + std::vector shape(inp->blob->dims()); + std::reverse(shape.begin(), shape.end()); + + inpLayer.setPort(InferenceEngine::Port(shape)); + inpId = netBuilder.addLayer(inpLayer); + + layers.insert({inpName, inpId}); + } + else + inpId = it->second; + + netBuilder.connect(inpId, {layerId, i}); + unconnectedLayersIds.erase(inpId); + } + CV_Assert(!outputs.empty()); + InferenceEngine::DataPtr dataPtr = infEngineDataNode(outputs[0]); + dataPtr->name = layerName; +} + +void InfEngineBackendNet::init(int targetId) +{ + if (!hasNetOwner) + { + CV_Assert(!unconnectedLayersIds.empty()); + for (int id : unconnectedLayersIds) + { + InferenceEngine::Builder::OutputLayer outLayer("myconv1"); + netBuilder.addLayer({id}, outLayer); + } + cnn = InferenceEngine::CNNNetwork(InferenceEngine::Builder::convertToICNNNetwork(netBuilder.build())); + } + + switch (targetId) + { + case DNN_TARGET_CPU: + targetDevice = InferenceEngine::TargetDevice::eCPU; + break; + case DNN_TARGET_OPENCL: case DNN_TARGET_OPENCL_FP16: + targetDevice = InferenceEngine::TargetDevice::eGPU; + break; + case DNN_TARGET_MYRIAD: + targetDevice = InferenceEngine::TargetDevice::eMYRIAD; + break; + case DNN_TARGET_FPGA: + targetDevice = InferenceEngine::TargetDevice::eFPGA; + break; + default: + CV_Error(Error::StsError, format("Unknown target identifier: %d", targetId)); + } + + for (const auto& name : requestedOutputs) + { + cnn.addOutput(name); + } + + for (const auto& it : cnn.getInputsInfo()) + { + const std::string& name = it.first; + auto blobIt = allBlobs.find(name); + CV_Assert(blobIt != allBlobs.end()); + inpBlobs[name] = blobIt->second; + it.second->setPrecision(blobIt->second->precision()); + } + for (const auto& it : cnn.getOutputsInfo()) + { + const std::string& name = it.first; + auto blobIt = allBlobs.find(name); + CV_Assert(blobIt != allBlobs.end()); + outBlobs[name] = blobIt->second; + it.second->setPrecision(blobIt->second->precision()); // Should be always FP32 + } + + initPlugin(cnn); +} + +void InfEngineBackendNet::addLayer(const InferenceEngine::Builder::Layer& layer) +{ + int id = netBuilder.addLayer(layer); + const std::string& layerName = layer.getName(); + CV_Assert(layers.insert({layerName, id}).second); + unconnectedLayersIds.insert(id); +} + +void InfEngineBackendNet::addOutput(const std::string& name) +{ + requestedOutputs.push_back(name); +} + +#endif // IE >= R5 + static InferenceEngine::Layout estimateLayout(const Mat& m) { if (m.dims == 4) @@ -148,6 +276,7 @@ void InfEngineBackendWrapper::setHostDirty() } +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) InfEngineBackendNet::InfEngineBackendNet() { targetDevice = InferenceEngine::TargetDevice::eCPU; @@ -491,6 +620,8 @@ void InfEngineBackendNet::init(int targetId) initPlugin(*this); } +#endif // IE < R5 + static std::map sharedPlugins; void InfEngineBackendNet::initPlugin(InferenceEngine::ICNNNetwork& net) @@ -566,7 +697,11 @@ void InfEngineBackendNet::addBlobs(const std::vector >& ptrs auto wrappers = infEngineWrappers(ptrs); for (const auto& wrapper : wrappers) { - allBlobs.insert({wrapper->dataPtr->name, wrapper->blob}); + std::string name = wrapper->dataPtr->name; +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + name = name.empty() ? "id1" : name; // TODO: drop the magic input name. +#endif + allBlobs.insert({name, wrapper->blob}); } } diff --git a/modules/dnn/src/op_inf_engine.hpp b/modules/dnn/src/op_inf_engine.hpp index a1144b4b53..122de51659 100644 --- a/modules/dnn/src/op_inf_engine.hpp +++ b/modules/dnn/src/op_inf_engine.hpp @@ -35,6 +35,11 @@ #define INF_ENGINE_VER_MAJOR_GT(ver) (((INF_ENGINE_RELEASE) / 10000) > ((ver) / 10000)) #define INF_ENGINE_VER_MAJOR_GE(ver) (((INF_ENGINE_RELEASE) / 10000) >= ((ver) / 10000)) +#define INF_ENGINE_VER_MAJOR_LT(ver) (((INF_ENGINE_RELEASE) / 10000) < ((ver) / 10000)) + +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) +#include +#endif #endif // HAVE_INF_ENGINE @@ -42,6 +47,7 @@ namespace cv { namespace dnn { #ifdef HAVE_INF_ENGINE +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2018R5) class InfEngineBackendNet : public InferenceEngine::ICNNNetwork { public: @@ -146,17 +152,75 @@ private: void initPlugin(InferenceEngine::ICNNNetwork& net); }; +#else // IE < R5 + +class InfEngineBackendNet +{ +public: + InfEngineBackendNet(); + + InfEngineBackendNet(InferenceEngine::CNNNetwork& net); + + void addLayer(const InferenceEngine::Builder::Layer& layer); + + void addOutput(const std::string& name); + + void connect(const std::vector >& inputs, + const std::vector >& outputs, + const std::string& layerName); + + bool isInitialized(); + + void init(int targetId); + + void forward(); + + void initPlugin(InferenceEngine::ICNNNetwork& net); + + void addBlobs(const std::vector >& ptrs); + +private: + InferenceEngine::Builder::Network netBuilder; + + InferenceEngine::InferenceEnginePluginPtr enginePtr; + InferenceEngine::InferencePlugin plugin; + InferenceEngine::ExecutableNetwork netExec; + InferenceEngine::InferRequest infRequest; + InferenceEngine::BlobMap allBlobs; + InferenceEngine::BlobMap inpBlobs; + InferenceEngine::BlobMap outBlobs; + InferenceEngine::TargetDevice targetDevice; + + InferenceEngine::CNNNetwork cnn; + bool hasNetOwner; + + std::map layers; + std::vector requestedOutputs; + + std::set unconnectedLayersIds; +}; +#endif // IE < R5 + class InfEngineBackendNode : public BackendNode { public: +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InfEngineBackendNode(const InferenceEngine::Builder::Layer& layer); +#else InfEngineBackendNode(const InferenceEngine::CNNLayerPtr& layer); +#endif void connect(std::vector >& inputs, std::vector >& outputs); - InferenceEngine::CNNLayerPtr layer; // Inference Engine network object that allows to obtain the outputs of this layer. +#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5) + InferenceEngine::Builder::Layer layer; Ptr net; +#else + InferenceEngine::CNNLayerPtr layer; + Ptr net; +#endif }; class InfEngineBackendWrapper : public BackendWrapper diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 75591e14e6..1d97cfc088 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -180,7 +180,7 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_v2_TensorFlow) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/street.png", false)); Mat inp = blobFromImage(sample, 1.0f, Size(300, 300), Scalar(), false); - float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.013 : 0.0; + float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.013 : 2e-5; float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.062 : 0.0; processNet("dnn/ssd_mobilenet_v2_coco_2018_03_29.pb", "dnn/ssd_mobilenet_v2_coco_2018_03_29.pbtxt", inp, "detection_out", "", l1, lInf, 0.25); @@ -288,7 +288,7 @@ TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16) Mat inp = blobFromImage(img, 1.0, Size(320, 240), Scalar(103.939, 116.779, 123.68), false, false); // Output image has values in range [-143.526, 148.539]. float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.3 : 4e-5; - float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 7.0 : 2e-3; + float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 7.28 : 2e-3; processNet("dnn/fast_neural_style_eccv16_starry_night.t7", "", inp, "", "", l1, lInf); } diff --git a/modules/dnn/test/test_darknet_importer.cpp b/modules/dnn/test/test_darknet_importer.cpp index 5d41b4b916..d7c14f2714 100644 --- a/modules/dnn/test/test_darknet_importer.cpp +++ b/modules/dnn/test/test_darknet_importer.cpp @@ -306,7 +306,7 @@ TEST_P(Test_Darknet_nets, TinyYoloVoc) // batch size 1 testDarknetModel(config_file, weights_file, ref.rowRange(0, 2), scoreDiff, iouDiff); -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE >= 2018040000 +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE == 2018040000 if (backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_MYRIAD) #endif // batch size 2 diff --git a/modules/dnn/test/test_halide_layers.cpp b/modules/dnn/test/test_halide_layers.cpp index 468953fe7e..9cbfb0c402 100644 --- a/modules/dnn/test/test_halide_layers.cpp +++ b/modules/dnn/test/test_halide_layers.cpp @@ -163,7 +163,7 @@ TEST_P(Deconvolution, Accuracy) bool hasBias = get<6>(GetParam()); Backend backendId = get<0>(get<7>(GetParam())); Target targetId = get<1>(get<7>(GetParam())); - if (backendId == DNN_BACKEND_INFERENCE_ENGINE && targetId == DNN_TARGET_CPU && + if (backendId == DNN_BACKEND_INFERENCE_ENGINE && (targetId == DNN_TARGET_CPU || targetId == DNN_TARGET_MYRIAD) && dilation.width == 2 && dilation.height == 2) throw SkipTestException(""); #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE >= 2018040000 @@ -466,6 +466,7 @@ void testInPlaceActivation(LayerParams& lp, Backend backendId, Target targetId) pool.set("stride_w", 2); pool.set("stride_h", 2); pool.type = "Pooling"; + pool.name = "ave_pool"; Net net; int poolId = net.addLayer(pool.name, pool.type, pool); diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 4ccefd28a9..62e625f03c 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -295,10 +295,6 @@ TEST_P(Test_Caffe_layers, Eltwise) { if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) throw SkipTestException(""); -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE == 2018050000 - if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL) - throw SkipTestException("Test is disabled for OpenVINO 2018R5"); -#endif testLayerUsingCaffeModels("layer_eltwise"); } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index deccbfb0eb..acdd66631c 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -351,6 +351,10 @@ TEST_P(Test_ONNX_nets, LResNet100E_IR) l1 = 0.009; lInf = 0.035; } + else if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_CPU) { + l1 = 4.5e-5; + lInf = 1.9e-4; + } testONNXModels("LResNet100E_IR", pb, l1, lInf); } @@ -366,6 +370,10 @@ TEST_P(Test_ONNX_nets, Emotion_ferplus) l1 = 0.021; lInf = 0.034; } + else if (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_CPU || target == DNN_TARGET_OPENCL)) { + l1 = 2.4e-4; + lInf = 6e-4; + } testONNXModels("emotion_ferplus", pb, l1, lInf); } @@ -389,7 +397,7 @@ TEST_P(Test_ONNX_nets, Inception_v1) { #if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE == 2018050000 if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) - throw SkipTestException(""); + throw SkipTestException("Test is disabled for OpenVINO 2018R5"); #endif testONNXModels("inception_v1", pb); } diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index ce4997cd4e..cbf9782a44 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -351,8 +351,8 @@ TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD) Mat out = net.forward(); Mat ref = blobFromNPY(findDataFile("dnn/tensorflow/ssd_mobilenet_v1_coco_2017_11_17.detection_out.npy")); - float scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 7e-3 : 1e-5; - float iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.0098 : 1e-3; + float scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 7e-3 : 1.5e-5; + float iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.012 : 1e-3; normAssertDetections(ref, out, "", 0.3, scoreDiff, iouDiff); } @@ -366,6 +366,7 @@ TEST_P(Test_TensorFlow_nets, Faster_RCNN) (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)) throw SkipTestException(""); + double scoresDiff = backend == DNN_BACKEND_INFERENCE_ENGINE ? 2.9e-5 : 1e-5; for (int i = 0; i < 2; ++i) { std::string proto = findDataFile("dnn/" + names[i] + ".pbtxt", false); @@ -381,7 +382,7 @@ TEST_P(Test_TensorFlow_nets, Faster_RCNN) Mat out = net.forward(); Mat ref = blobFromNPY(findDataFile("dnn/tensorflow/" + names[i] + ".detection_out.npy")); - normAssertDetections(ref, out, names[i].c_str(), 0.3); + normAssertDetections(ref, out, names[i].c_str(), 0.3, scoresDiff); } } @@ -406,7 +407,7 @@ TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD_PPN) net.setInput(blob); Mat out = net.forward(); - double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.011 : default_l1; + double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.011 : 1.1e-5; double iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.021 : default_lInf; normAssertDetections(ref, out, "", 0.4, scoreDiff, iouDiff); } @@ -568,10 +569,6 @@ TEST_P(Test_TensorFlow_layers, slice) if (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL || target == DNN_TARGET_OPENCL_FP16)) throw SkipTestException(""); -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE == 2018050000 - if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) - throw SkipTestException(""); -#endif runTensorFlowNet("slice_4d"); } diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index c63cf26e45..046bd65b86 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -260,6 +260,11 @@ TEST_P(Test_Torch_layers, run_paralel) TEST_P(Test_Torch_layers, net_residual) { +#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE == 2018050000 + if (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL || + target == DNN_TARGET_OPENCL_FP16)) + throw SkipTestException("Test is disabled for OpenVINO 2018R5"); +#endif runTorchNet("net_residual", "", false, true); } @@ -390,10 +395,6 @@ TEST_P(Test_Torch_nets, ENet_accuracy) // -model models/instance_norm/feathers.t7 TEST_P(Test_Torch_nets, FastNeuralStyle_accuracy) { -#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_RELEASE == 2018050000 - if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) - throw SkipTestException(""); -#endif checkBackend(); std::string models[] = {"dnn/fast_neural_style_eccv16_starry_night.t7", "dnn/fast_neural_style_instance_norm_feathers.t7"}; From 3721c8bb068aceafce068ab459810440057d8254 Mon Sep 17 00:00:00 2001 From: Lee Jaehwan Date: Thu, 17 Jan 2019 23:23:09 +0900 Subject: [PATCH 15/23] Merge pull request #13586 from eightco:Core_bugfix3 * Add Operator override for multi-channel Mat with literal constant. * simple test * Operator overloading channel constraint for primitive types * fix some test for #13586 --- .../core/include/opencv2/core/operations.hpp | 20 +++++++++++ modules/core/test/test_mat.cpp | 36 +++++++++++++++++++ modules/dnn/test/test_tf_importer.cpp | 2 +- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/operations.hpp b/modules/core/include/opencv2/core/operations.hpp index 082fef4440..2c38469446 100644 --- a/modules/core/include/opencv2/core/operations.hpp +++ b/modules/core/include/opencv2/core/operations.hpp @@ -256,6 +256,14 @@ Matx<_Tp, n, l> Matx<_Tp, m, n>::solve(const Matx<_Tp, m, l>& rhs, int method) c template static inline A& operator op (A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } \ template static inline const A& operator op (const A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } +#define CV_MAT_AUG_OPERATOR_FOR1CHANNEL(op, cvop, A) \ + CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, A, double) \ + CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, const A, double) + +#define CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(op, cvop, A) \ + template CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, A, double) \ + template CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, const A, double) + CV_MAT_AUG_OPERATOR (+=, cv::add(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (+=, cv::add(a,b,a), Mat, Scalar) CV_MAT_AUG_OPERATOR_T(+=, cv::add(a,b,a), Mat_<_Tp>, Mat) @@ -263,6 +271,8 @@ CV_MAT_AUG_OPERATOR_T(+=, cv::add(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(+=, cv::add(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a,Mat(b),a), Mat) CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a,Mat(b),a), Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_FOR1CHANNEL (+=, cv::add(a, b, a), Mat) +CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(+=, cv::add(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (-=, cv::subtract(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (-=, cv::subtract(a,b,a), Mat, Scalar) @@ -271,6 +281,8 @@ CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a,Mat(b),a), Mat) CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a,Mat(b),a), Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_FOR1CHANNEL (-=, cv::subtract(a, b, a), Mat) +CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(-=, cv::subtract(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat, Mat) CV_MAT_AUG_OPERATOR_T(*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat_<_Tp>, Mat) @@ -295,6 +307,8 @@ CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), a), Mat) CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), a), Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_FOR1CHANNEL (&=, cv::bitwise_and(a, b, a), Mat) +CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(&=, cv::bitwise_and(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a,b,a), Mat, Scalar) @@ -303,6 +317,8 @@ CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), a), Mat) CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), a), Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_FOR1CHANNEL (|=, cv::bitwise_or(a, b, a), Mat) +CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(|=, cv::bitwise_or(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a,b,a), Mat, Scalar) @@ -311,7 +327,11 @@ CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), a), Mat) CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), a), Mat_<_Tp>) +CV_MAT_AUG_OPERATOR_FOR1CHANNEL (^=, cv::bitwise_xor(a, b, a), Mat) +CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(^=, cv::bitwise_xor(a, b, a), Mat_<_Tp>) +#undef CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL +#undef CV_MAT_AUG_OPERATOR_FOR1CHANNEL #undef CV_MAT_AUG_OPERATOR_TN #undef CV_MAT_AUG_OPERATOR_T #undef CV_MAT_AUG_OPERATOR diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index f69140bb8c..14174e560e 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1644,7 +1644,43 @@ TEST(Mat, regression_10507_mat_setTo) } } +template +static void OverloadingTestFor1Channel_EachOp(const MatType& m, Type c) +{ + EXPECT_ANY_THROW(m += c); + EXPECT_ANY_THROW(m -= c); + EXPECT_ANY_THROW(m &= c); + EXPECT_ANY_THROW(m |= c); + EXPECT_ANY_THROW(m ^= c); +} + +template +static void OverloadingTestFor1Channel_EachType(const MatType& m) +{ + OverloadingTestFor1Channel_EachOp< MatType, bool> (m, true); + OverloadingTestFor1Channel_EachOp< MatType, char> (m, (char)1); + OverloadingTestFor1Channel_EachOp< MatType, unsigned char> (m, (unsigned char)1); + OverloadingTestFor1Channel_EachOp< MatType, short> (m, (short)1); + OverloadingTestFor1Channel_EachOp< MatType, unsigned short> (m, (unsigned short)1); + OverloadingTestFor1Channel_EachOp< MatType, int> (m, 1); + OverloadingTestFor1Channel_EachOp< MatType, unsigned int> (m, (unsigned int)1); + OverloadingTestFor1Channel_EachOp< MatType, long> (m, (long)1); + OverloadingTestFor1Channel_EachOp< MatType, unsigned long> (m, (unsigned long)1); + OverloadingTestFor1Channel_EachOp< MatType, float> (m, 1.0f); + OverloadingTestFor1Channel_EachOp< MatType, double> (m, 1.0); +} + +TEST(Mat, regression_13586) +{ + Mat m1(2, 2, CV_8UC3, Scalar::all(1)); + OverloadingTestFor1Channel_EachType(m1); + Mat4b m2(2, 2); + m2.setTo(0); + OverloadingTestFor1Channel_EachType(m2); +} + #ifdef CV_CXX_STD_ARRAY + TEST(Core_Mat_array, outputArray_create_getMat) { cv::Mat_ src_base(5, 1); diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index ce4997cd4e..cc7f26196b 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -40,7 +40,7 @@ TEST(Test_TensorFlow, read_inception) ASSERT_TRUE(!sample.empty()); Mat input; resize(sample, input, Size(224, 224)); - input -= 128; // mean sub + input -= Scalar::all(117); // mean sub Mat inputBlob = blobFromImage(input); From 78f80c35d214eb3b54f7ac536ec74f62fb84bfd3 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Fri, 18 Jan 2019 14:59:03 +0300 Subject: [PATCH 16/23] Performance test for bounding rect estimation --- modules/imgproc/perf/perf_contours.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/imgproc/perf/perf_contours.cpp b/modules/imgproc/perf/perf_contours.cpp index d3a70cfdd7..bc8b530016 100644 --- a/modules/imgproc/perf/perf_contours.cpp +++ b/modules/imgproc/perf/perf_contours.cpp @@ -84,4 +84,26 @@ PERF_TEST_P(TestFindContoursFF, findContours, SANITY_CHECK_NOTHING(); } +typedef TestBaseWithParam< tuple > TestBoundingRect; + +PERF_TEST_P(TestBoundingRect, BoundingRect, + Combine( + testing::Values(CV_32S, CV_32F), // points type + Values(400, 511, 1000, 10000, 100000) // points count + ) +) + +{ + int ptType = get<0>(GetParam()); + int n = get<1>(GetParam()); + + Mat pts(n, 2, ptType); + declare.in(pts, WARMUP_RNG); + + cv::Rect rect; + TEST_CYCLE() rect = boundingRect(pts); + + SANITY_CHECK_NOTHING(); +} + } } // namespace From dc5e69b4d4ecffc69e571f37f22245cc4f8d66f7 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 18 Jan 2019 17:32:11 +0300 Subject: [PATCH 17/23] Revert "Merge pull request #13586 from eightco:Core_bugfix3" This reverts commit 3721c8bb068aceafce068ab459810440057d8254 except changes in modules/dnn/test/test_tf_importer.cpp --- .../core/include/opencv2/core/operations.hpp | 20 ----------- modules/core/test/test_mat.cpp | 36 ------------------- 2 files changed, 56 deletions(-) diff --git a/modules/core/include/opencv2/core/operations.hpp b/modules/core/include/opencv2/core/operations.hpp index 2c38469446..082fef4440 100644 --- a/modules/core/include/opencv2/core/operations.hpp +++ b/modules/core/include/opencv2/core/operations.hpp @@ -256,14 +256,6 @@ Matx<_Tp, n, l> Matx<_Tp, m, n>::solve(const Matx<_Tp, m, l>& rhs, int method) c template static inline A& operator op (A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } \ template static inline const A& operator op (const A& a, const Matx<_Tp,m,n>& b) { cvop; return a; } -#define CV_MAT_AUG_OPERATOR_FOR1CHANNEL(op, cvop, A) \ - CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, A, double) \ - CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, const A, double) - -#define CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(op, cvop, A) \ - template CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, A, double) \ - template CV_MAT_AUG_OPERATOR1(op, CV_Assert(a.channels() == 1); cvop, const A, double) - CV_MAT_AUG_OPERATOR (+=, cv::add(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (+=, cv::add(a,b,a), Mat, Scalar) CV_MAT_AUG_OPERATOR_T(+=, cv::add(a,b,a), Mat_<_Tp>, Mat) @@ -271,8 +263,6 @@ CV_MAT_AUG_OPERATOR_T(+=, cv::add(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(+=, cv::add(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a,Mat(b),a), Mat) CV_MAT_AUG_OPERATOR_TN(+=, cv::add(a,Mat(b),a), Mat_<_Tp>) -CV_MAT_AUG_OPERATOR_FOR1CHANNEL (+=, cv::add(a, b, a), Mat) -CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(+=, cv::add(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (-=, cv::subtract(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (-=, cv::subtract(a,b,a), Mat, Scalar) @@ -281,8 +271,6 @@ CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(-=, cv::subtract(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a,Mat(b),a), Mat) CV_MAT_AUG_OPERATOR_TN(-=, cv::subtract(a,Mat(b),a), Mat_<_Tp>) -CV_MAT_AUG_OPERATOR_FOR1CHANNEL (-=, cv::subtract(a, b, a), Mat) -CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(-=, cv::subtract(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat, Mat) CV_MAT_AUG_OPERATOR_T(*=, cv::gemm(a, b, 1, Mat(), 0, a, 0), Mat_<_Tp>, Mat) @@ -307,8 +295,6 @@ CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(&=, cv::bitwise_and(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), a), Mat) CV_MAT_AUG_OPERATOR_TN(&=, cv::bitwise_and(a, Mat(b), a), Mat_<_Tp>) -CV_MAT_AUG_OPERATOR_FOR1CHANNEL (&=, cv::bitwise_and(a, b, a), Mat) -CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(&=, cv::bitwise_and(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (|=, cv::bitwise_or(a,b,a), Mat, Scalar) @@ -317,8 +303,6 @@ CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(|=, cv::bitwise_or(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), a), Mat) CV_MAT_AUG_OPERATOR_TN(|=, cv::bitwise_or(a, Mat(b), a), Mat_<_Tp>) -CV_MAT_AUG_OPERATOR_FOR1CHANNEL (|=, cv::bitwise_or(a, b, a), Mat) -CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(|=, cv::bitwise_or(a, b, a), Mat_<_Tp>) CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a,b,a), Mat, Mat) CV_MAT_AUG_OPERATOR (^=, cv::bitwise_xor(a,b,a), Mat, Scalar) @@ -327,11 +311,7 @@ CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a,b,a), Mat_<_Tp>, Scalar) CV_MAT_AUG_OPERATOR_T(^=, cv::bitwise_xor(a,b,a), Mat_<_Tp>, Mat_<_Tp>) CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), a), Mat) CV_MAT_AUG_OPERATOR_TN(^=, cv::bitwise_xor(a, Mat(b), a), Mat_<_Tp>) -CV_MAT_AUG_OPERATOR_FOR1CHANNEL (^=, cv::bitwise_xor(a, b, a), Mat) -CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL(^=, cv::bitwise_xor(a, b, a), Mat_<_Tp>) -#undef CV_MAT_AUG_OPERATOR_T_FOR1CHANNEL -#undef CV_MAT_AUG_OPERATOR_FOR1CHANNEL #undef CV_MAT_AUG_OPERATOR_TN #undef CV_MAT_AUG_OPERATOR_T #undef CV_MAT_AUG_OPERATOR diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 14174e560e..f69140bb8c 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1644,43 +1644,7 @@ TEST(Mat, regression_10507_mat_setTo) } } -template -static void OverloadingTestFor1Channel_EachOp(const MatType& m, Type c) -{ - EXPECT_ANY_THROW(m += c); - EXPECT_ANY_THROW(m -= c); - EXPECT_ANY_THROW(m &= c); - EXPECT_ANY_THROW(m |= c); - EXPECT_ANY_THROW(m ^= c); -} - -template -static void OverloadingTestFor1Channel_EachType(const MatType& m) -{ - OverloadingTestFor1Channel_EachOp< MatType, bool> (m, true); - OverloadingTestFor1Channel_EachOp< MatType, char> (m, (char)1); - OverloadingTestFor1Channel_EachOp< MatType, unsigned char> (m, (unsigned char)1); - OverloadingTestFor1Channel_EachOp< MatType, short> (m, (short)1); - OverloadingTestFor1Channel_EachOp< MatType, unsigned short> (m, (unsigned short)1); - OverloadingTestFor1Channel_EachOp< MatType, int> (m, 1); - OverloadingTestFor1Channel_EachOp< MatType, unsigned int> (m, (unsigned int)1); - OverloadingTestFor1Channel_EachOp< MatType, long> (m, (long)1); - OverloadingTestFor1Channel_EachOp< MatType, unsigned long> (m, (unsigned long)1); - OverloadingTestFor1Channel_EachOp< MatType, float> (m, 1.0f); - OverloadingTestFor1Channel_EachOp< MatType, double> (m, 1.0); -} - -TEST(Mat, regression_13586) -{ - Mat m1(2, 2, CV_8UC3, Scalar::all(1)); - OverloadingTestFor1Channel_EachType(m1); - Mat4b m2(2, 2); - m2.setTo(0); - OverloadingTestFor1Channel_EachType(m2); -} - #ifdef CV_CXX_STD_ARRAY - TEST(Core_Mat_array, outputArray_create_getMat) { cv::Mat_ src_base(5, 1); From a84bbc62b1188fc39b18296a0c8cbe6de19e5bfe Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Fri, 18 Jan 2019 00:11:52 +0300 Subject: [PATCH 18/23] boundingRect() reworked to use wide universal intrinsics --- modules/imgproc/src/shapedescr.cpp | 218 ++++++++++++++++++----------- 1 file changed, 136 insertions(+), 82 deletions(-) diff --git a/modules/imgproc/src/shapedescr.cpp b/modules/imgproc/src/shapedescr.cpp index d505fde4fc..436c74eade 100644 --- a/modules/imgproc/src/shapedescr.cpp +++ b/modules/imgproc/src/shapedescr.cpp @@ -39,6 +39,8 @@ // //M*/ #include "precomp.hpp" +#include "opencv2/core/hal/intrin.hpp" + namespace cv { @@ -746,109 +748,161 @@ static Rect pointSetBoundingRect( const Mat& points ) if( npoints == 0 ) return Rect(); +#if CV_SIMD + const int64_t* pts = points.ptr(); + + if( !is_float ) + { + v_int32 minval, maxval; + minval = maxval = v_reinterpret_as_s32(vx_setall_s64(*pts)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y + for( i = 1; i <= npoints - v_int32::nlanes/2; i+= v_int32::nlanes/2 ) + { + v_int32 ptXY2 = v_reinterpret_as_s32(vx_load(pts + i)); + minval = v_min(ptXY2, minval); + maxval = v_max(ptXY2, maxval); + } + minval = v_min(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval)))); + maxval = v_max(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval)))); + if( i <= npoints - v_int32::nlanes/4 ) + { + v_int32 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + i)))); + minval = v_min(ptXY, minval); + maxval = v_max(ptXY, maxval); + i += v_int64::nlanes/2; + } + for(int j = 16; j < CV_SIMD_WIDTH; j*=2) + { + minval = v_min(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval)))); + maxval = v_max(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval)))); + } + xmin = minval.get0(); + xmax = maxval.get0(); + ymin = v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval))).get0(); + ymax = v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval))).get0(); +#if CV_SIMD_WIDTH > 16 + if( i < npoints ) + { + v_int32x4 minval2, maxval2; + minval2 = maxval2 = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i)))); + for( i++; i < npoints; i++ ) + { + v_int32x4 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i)))); + minval2 = v_min(ptXY, minval2); + maxval2 = v_max(ptXY, maxval2); + } + xmin = min(xmin, minval2.get0()); + xmax = max(xmax, maxval2.get0()); + ymin = min(ymin, v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval2))).get0()); + ymax = max(ymax, v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval2))).get0()); + } +#endif + } + else + { + v_float32 minval, maxval; + minval = maxval = v_reinterpret_as_f32(vx_setall_s64(*pts)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y + for( i = 1; i <= npoints - v_float32::nlanes/2; i+= v_float32::nlanes/2 ) + { + v_float32 ptXY2 = v_reinterpret_as_f32(vx_load(pts + i)); + minval = v_min(ptXY2, minval); + maxval = v_max(ptXY2, maxval); + } + minval = v_min(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval)))); + maxval = v_max(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval)))); + if( i <= npoints - v_float32::nlanes/4 ) + { + v_float32 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + i)))); + minval = v_min(ptXY, minval); + maxval = v_max(ptXY, maxval); + i += v_float32::nlanes/4; + } + for(int j = 16; j < CV_SIMD_WIDTH; j*=2) + { + minval = v_min(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval)))); + maxval = v_max(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval)))); + } + xmin = cvFloor(minval.get0()); + xmax = cvFloor(maxval.get0()); + ymin = cvFloor(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval))).get0()); + ymax = cvFloor(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval))).get0()); +#if CV_SIMD_WIDTH > 16 + if( i < npoints ) + { + v_float32x4 minval2, maxval2; + minval2 = maxval2 = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i)))); + for( i++; i < npoints; i++ ) + { + v_float32x4 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + i)))); + minval2 = v_min(ptXY, minval2); + maxval2 = v_max(ptXY, maxval2); + } + xmin = min(xmin, cvFloor(minval2.get0())); + xmax = max(xmax, cvFloor(maxval2.get0())); + ymin = min(ymin, cvFloor(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval2))).get0())); + ymax = max(ymax, cvFloor(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval2))).get0())); + } +#endif + } +#else const Point* pts = points.ptr(); Point pt = pts[0]; -#if CV_SSE4_2 - if(cv::checkHardwareSupport(CV_CPU_SSE4_2)) + if( !is_float ) { - if( !is_float ) + xmin = xmax = pt.x; + ymin = ymax = pt.y; + + for( i = 1; i < npoints; i++ ) { - __m128i minval, maxval; - minval = maxval = _mm_loadl_epi64((const __m128i*)(&pt)); //min[0]=pt.x, min[1]=pt.y + pt = pts[i]; - for( i = 1; i < npoints; i++ ) - { - __m128i ptXY = _mm_loadl_epi64((const __m128i*)&pts[i]); - minval = _mm_min_epi32(ptXY, minval); - maxval = _mm_max_epi32(ptXY, maxval); - } - xmin = _mm_cvtsi128_si32(minval); - ymin = _mm_cvtsi128_si32(_mm_srli_si128(minval, 4)); - xmax = _mm_cvtsi128_si32(maxval); - ymax = _mm_cvtsi128_si32(_mm_srli_si128(maxval, 4)); - } - else - { - __m128 minvalf, maxvalf, z = _mm_setzero_ps(), ptXY = _mm_setzero_ps(); - minvalf = maxvalf = _mm_loadl_pi(z, (const __m64*)(&pt)); + if( xmin > pt.x ) + xmin = pt.x; - for( i = 1; i < npoints; i++ ) - { - ptXY = _mm_loadl_pi(ptXY, (const __m64*)&pts[i]); + if( xmax < pt.x ) + xmax = pt.x; - minvalf = _mm_min_ps(minvalf, ptXY); - maxvalf = _mm_max_ps(maxvalf, ptXY); - } + if( ymin > pt.y ) + ymin = pt.y; - float xyminf[2], xymaxf[2]; - _mm_storel_pi((__m64*)xyminf, minvalf); - _mm_storel_pi((__m64*)xymaxf, maxvalf); - xmin = cvFloor(xyminf[0]); - ymin = cvFloor(xyminf[1]); - xmax = cvFloor(xymaxf[0]); - ymax = cvFloor(xymaxf[1]); + if( ymax < pt.y ) + ymax = pt.y; } } else -#endif { - if( !is_float ) + Cv32suf v; + // init values + xmin = xmax = CV_TOGGLE_FLT(pt.x); + ymin = ymax = CV_TOGGLE_FLT(pt.y); + + for( i = 1; i < npoints; i++ ) { - xmin = xmax = pt.x; - ymin = ymax = pt.y; + pt = pts[i]; + pt.x = CV_TOGGLE_FLT(pt.x); + pt.y = CV_TOGGLE_FLT(pt.y); - for( i = 1; i < npoints; i++ ) - { - pt = pts[i]; + if( xmin > pt.x ) + xmin = pt.x; - if( xmin > pt.x ) - xmin = pt.x; + if( xmax < pt.x ) + xmax = pt.x; - if( xmax < pt.x ) - xmax = pt.x; + if( ymin > pt.y ) + ymin = pt.y; - if( ymin > pt.y ) - ymin = pt.y; - - if( ymax < pt.y ) - ymax = pt.y; - } + if( ymax < pt.y ) + ymax = pt.y; } - else - { - Cv32suf v; - // init values - xmin = xmax = CV_TOGGLE_FLT(pt.x); - ymin = ymax = CV_TOGGLE_FLT(pt.y); - for( i = 1; i < npoints; i++ ) - { - pt = pts[i]; - pt.x = CV_TOGGLE_FLT(pt.x); - pt.y = CV_TOGGLE_FLT(pt.y); - - if( xmin > pt.x ) - xmin = pt.x; - - if( xmax < pt.x ) - xmax = pt.x; - - if( ymin > pt.y ) - ymin = pt.y; - - if( ymax < pt.y ) - ymax = pt.y; - } - - v.i = CV_TOGGLE_FLT(xmin); xmin = cvFloor(v.f); - v.i = CV_TOGGLE_FLT(ymin); ymin = cvFloor(v.f); - // because right and bottom sides of the bounding rectangle are not inclusive - // (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil - v.i = CV_TOGGLE_FLT(xmax); xmax = cvFloor(v.f); - v.i = CV_TOGGLE_FLT(ymax); ymax = cvFloor(v.f); - } + v.i = CV_TOGGLE_FLT(xmin); xmin = cvFloor(v.f); + v.i = CV_TOGGLE_FLT(ymin); ymin = cvFloor(v.f); + // because right and bottom sides of the bounding rectangle are not inclusive + // (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil + v.i = CV_TOGGLE_FLT(xmax); xmax = cvFloor(v.f); + v.i = CV_TOGGLE_FLT(ymax); ymax = cvFloor(v.f); } +#endif return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); } From 67e6a6077d40fb64637ebcf5453ae8be375d7e51 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 18 Jan 2019 18:46:52 +0300 Subject: [PATCH 19/23] Create text graphs for Faster-RCNN from TensorFlow with dilated convolutions --- samples/dnn/tf_text_graph_faster_rcnn.py | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/samples/dnn/tf_text_graph_faster_rcnn.py b/samples/dnn/tf_text_graph_faster_rcnn.py index e3d0ad0127..e1dfba9fee 100644 --- a/samples/dnn/tf_text_graph_faster_rcnn.py +++ b/samples/dnn/tf_text_graph_faster_rcnn.py @@ -48,10 +48,42 @@ def createFasterRCNNGraph(modelPath, configPath, outputPath): removeIdentity(graph_def) + nodesToKeep = [] def to_remove(name, op): + if name in nodesToKeep: + return False return op == 'Const' or name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep) or \ (name.startswith('CropAndResize') and op != 'CropAndResize') + # Fuse atrous convolutions (with dilations). + nodesMap = {node.name: node for node in graph_def.node} + for node in reversed(graph_def.node): + if node.op == 'BatchToSpaceND': + del node.input[2] + conv = nodesMap[node.input[0]] + spaceToBatchND = nodesMap[conv.input[0]] + + # Extract paddings + stridedSlice = nodesMap[spaceToBatchND.input[2]] + assert(stridedSlice.op == 'StridedSlice') + pack = nodesMap[stridedSlice.input[0]] + assert(pack.op == 'Pack') + + padNodeH = nodesMap[nodesMap[pack.input[0]].input[0]] + padNodeW = nodesMap[nodesMap[pack.input[1]].input[0]] + padH = int(padNodeH.attr['value']['tensor'][0]['int_val'][0]) + padW = int(padNodeW.attr['value']['tensor'][0]['int_val'][0]) + + paddingsNode = NodeDef() + paddingsNode.name = conv.name + '/paddings' + paddingsNode.op = 'Const' + paddingsNode.addAttr('value', [padH, padH, padW, padW]) + graph_def.node.insert(graph_def.node.index(spaceToBatchND), paddingsNode) + nodesToKeep.append(paddingsNode.name) + + spaceToBatchND.input[2] = paddingsNode.name + + removeUnusedNodesAndAttrs(to_remove, graph_def) @@ -225,6 +257,26 @@ def createFasterRCNNGraph(modelPath, configPath, outputPath): detectionOut.addAttr('variance_encoded_in_target', True) graph_def.node.extend([detectionOut]) + def getUnconnectedNodes(): + unconnected = [node.name for node in graph_def.node] + for node in graph_def.node: + for inp in node.input: + if inp in unconnected: + unconnected.remove(inp) + return unconnected + + while True: + unconnectedNodes = getUnconnectedNodes() + unconnectedNodes.remove(detectionOut.name) + if not unconnectedNodes: + break + + for name in unconnectedNodes: + for i in range(len(graph_def.node)): + if graph_def.node[i].name == name: + del graph_def.node[i] + break + # Save as text. graph_def.save(outputPath) From 3812ae7949555a37df578c54e619f8e38f171b6a Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Fri, 18 Jan 2019 19:06:29 +0300 Subject: [PATCH 20/23] Merge pull request #13649 from savuor:yuv_wide YUV/YCrCb conversions rewritten to wide intrinsics (#13649) * YUV: minors * YUV42x conversions template-merged * more template-merged YUV42x conversions; some NEON code removed * rgb2yuv vectorized * yuv2rgb vectorized * memcpy removed * Yuv2RGB vectorized * unused code removed * rgb2yuv vectorized * rgb2yuv vectorized * v_pack_u used (up to +30% perf) * yuv2rgb vectorized * fixed compilation --- modules/imgproc/src/color_yuv.cpp | 2467 +++++++++-------------------- 1 file changed, 784 insertions(+), 1683 deletions(-) diff --git a/modules/imgproc/src/color_yuv.cpp b/modules/imgproc/src/color_yuv.cpp index f12a92e36e..acc290ad9b 100644 --- a/modules/imgproc/src/color_yuv.cpp +++ b/modules/imgproc/src/color_yuv.cpp @@ -11,33 +11,33 @@ namespace cv //constants for conversion from/to RGB and YUV, YCrCb according to BT.601 //to YCbCr -const float YCBF = 0.564f; // == 1/2/(1-B2YF) -const float YCRF = 0.713f; // == 1/2/(1-R2YF) -const int YCBI = 9241; // == YCBF*16384 -const int YCRI = 11682; // == YCRF*16384 +static const float YCBF = 0.564f; // == 1/2/(1-B2YF) +static const float YCRF = 0.713f; // == 1/2/(1-R2YF) +static const int YCBI = 9241; // == YCBF*16384 +static const int YCRI = 11682; // == YCRF*16384 //to YUV -const float B2UF = 0.492f; -const float R2VF = 0.877f; -const int B2UI = 8061; // == B2UF*16384 -const int R2VI = 14369; // == R2VF*16384 +static const float B2UF = 0.492f; +static const float R2VF = 0.877f; +static const int B2UI = 8061; // == B2UF*16384 +static const int R2VI = 14369; // == R2VF*16384 //from YUV -const float U2BF = 2.032f; -const float U2GF = -0.395f; -const float V2GF = -0.581f; -const float V2RF = 1.140f; -const int U2BI = 33292; -const int U2GI = -6472; -const int V2GI = -9519; -const int V2RI = 18678; +static const float U2BF = 2.032f; +static const float U2GF = -0.395f; +static const float V2GF = -0.581f; +static const float V2RF = 1.140f; +static const int U2BI = 33292; +static const int U2GI = -6472; +static const int V2GI = -9519; +static const int V2RI = 18678; //from YCrCb -const float CB2BF = 1.773f; -const float CB2GF = -0.344f; -const float CR2GF = -0.714f; -const float CR2RF = 1.403f; -const int CB2BI = 29049; -const int CB2GI = -5636; -const int CR2GI = -11698; -const int CR2RI = 22987; +static const float CB2BF = 1.773f; +static const float CB2GF = -0.344f; +static const float CR2GF = -0.714f; +static const float CR2RF = 1.403f; +static const int CB2BI = 29049; +static const int CB2GI = -5636; +static const int CR2GI = -11698; +static const int CR2RI = 22987; ///////////////////////////////////// RGB <-> YCrCb ////////////////////////////////////// @@ -45,12 +45,17 @@ template struct RGB2YCrCb_f { typedef _Tp channel_type; - RGB2YCrCb_f(int _srccn, int _blueIdx, bool _isCrCb) : srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) + RGB2YCrCb_f(int _srccn, int _blueIdx, bool _isCrCb) : + srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) { static const float coeffs_crb[] = { R2YF, G2YF, B2YF, YCRF, YCBF }; static const float coeffs_yuv[] = { R2YF, G2YF, B2YF, R2VF, B2UF }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); - if(blueIdx==0) std::swap(coeffs[0], coeffs[2]); + for(int i = 0; i < 5; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } + if(blueIdx == 0) + std::swap(coeffs[0], coeffs[2]); } void operator()(const _Tp* src, _Tp* dst, int n) const @@ -73,8 +78,6 @@ template struct RGB2YCrCb_f float coeffs[5]; }; -#if CV_NEON - template <> struct RGB2YCrCb_f { @@ -85,179 +88,92 @@ struct RGB2YCrCb_f { static const float coeffs_crb[] = { R2YF, G2YF, B2YF, YCRF, YCBF }; static const float coeffs_yuv[] = { R2YF, G2YF, B2YF, R2VF, B2UF }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); - if(blueIdx==0) + for(int i = 0; i < 5; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } + if(blueIdx == 0) std::swap(coeffs[0], coeffs[2]); - - v_c0 = vdupq_n_f32(coeffs[0]); - v_c1 = vdupq_n_f32(coeffs[1]); - v_c2 = vdupq_n_f32(coeffs[2]); - v_c3 = vdupq_n_f32(coeffs[3]); - v_c4 = vdupq_n_f32(coeffs[4]); - v_delta = vdupq_n_f32(ColorChannel::half()); } void operator()(const float * src, float * dst, int n) const { - int scn = srccn, bidx = blueIdx, i = 0; + int scn = srccn, bidx = blueIdx; int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb const float delta = ColorChannel::half(); float C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; - n *= 3; - if (scn == 3) - for ( ; i <= n - 12; i += 12, src += 12) + int i = 0; +#if CV_SIMD + v_float32 vc0 = vx_setall_f32(C0), vc1 = vx_setall_f32(C1), vc2 = vx_setall_f32(C2); + v_float32 vc3 = vx_setall_f32(C3), vc4 = vx_setall_f32(C4); + v_float32 vdelta = vx_setall_f32(delta); + const int vsize = v_float32::nlanes; + for( ; i <= n-vsize; + i += vsize, src += vsize*scn, dst += vsize*3) + { + v_float32 b, g, r, dummy; + if(scn == 3) { - float32x4x3_t v_src = vld3q_f32(src), v_dst; - v_dst.val[0] = vmlaq_f32(vmlaq_f32(vmulq_f32(v_src.val[0], v_c0), v_src.val[1], v_c1), v_src.val[2], v_c2); - v_dst.val[1+yuvOrder] = vmlaq_f32(v_delta, vsubq_f32(v_src.val[bidx^2], v_dst.val[0]), v_c3); - v_dst.val[2-yuvOrder] = vmlaq_f32(v_delta, vsubq_f32(v_src.val[bidx], v_dst.val[0]), v_c4); - - vst3q_f32(dst + i, v_dst); + v_load_deinterleave(src, b, g, r); } - else - for ( ; i <= n - 12; i += 12, src += 16) + else { - float32x4x4_t v_src = vld4q_f32(src); - float32x4x3_t v_dst; - v_dst.val[0] = vmlaq_f32(vmlaq_f32(vmulq_f32(v_src.val[0], v_c0), v_src.val[1], v_c1), v_src.val[2], v_c2); - v_dst.val[1+yuvOrder] = vmlaq_f32(v_delta, vsubq_f32(v_src.val[bidx^2], v_dst.val[0]), v_c3); - v_dst.val[2-yuvOrder] = vmlaq_f32(v_delta, vsubq_f32(v_src.val[bidx], v_dst.val[0]), v_c4); - - vst3q_f32(dst + i, v_dst); + v_load_deinterleave(src, b, g, r, dummy); } - for ( ; i < n; i += 3, src += scn) - { - float Y = src[0]*C0 + src[1]*C1 + src[2]*C2; - float Cr = (src[bidx^2] - Y)*C3 + delta; - float Cb = (src[bidx] - Y)*C4 + delta; - dst[i] = Y; dst[i+1+yuvOrder] = Cr; dst[i+2-yuvOrder] = Cb; - } - } - int srccn, blueIdx; - bool isCrCb; - float coeffs[5]; - float32x4_t v_c0, v_c1, v_c2, v_c3, v_c4, v_delta; -}; + v_float32 y, cr, cb; + y = b*vc0 + g*vc1 + r*vc2; -#elif CV_SSE2 + if(bidx) + std::swap(r, b); -template <> -struct RGB2YCrCb_f -{ - typedef float channel_type; + cr = v_fma(r - y, vc3, vdelta); + cb = v_fma(b - y, vc4, vdelta); - RGB2YCrCb_f(int _srccn, int _blueIdx, bool _isCrCb) : - srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) - { - static const float coeffs_crb[] = { R2YF, G2YF, B2YF, YCRF, YCBF }; - static const float coeffs_yuv[] = { R2YF, G2YF, B2YF, R2VF, B2UF }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); - if (blueIdx==0) - std::swap(coeffs[0], coeffs[2]); - - v_c0 = _mm_set1_ps(coeffs[0]); - v_c1 = _mm_set1_ps(coeffs[1]); - v_c2 = _mm_set1_ps(coeffs[2]); - v_c3 = _mm_set1_ps(coeffs[3]); - v_c4 = _mm_set1_ps(coeffs[4]); - v_delta = _mm_set1_ps(ColorChannel::half()); - - haveSIMD = checkHardwareSupport(CV_CPU_SSE2); - } - - void process(__m128 v_r, __m128 v_g, __m128 v_b, - __m128 & v_y, __m128 & v_cr, __m128 & v_cb) const - { - v_y = _mm_mul_ps(v_r, v_c0); - v_y = _mm_add_ps(v_y, _mm_mul_ps(v_g, v_c1)); - v_y = _mm_add_ps(v_y, _mm_mul_ps(v_b, v_c2)); - - v_cr = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(blueIdx == 0 ? v_b : v_r, v_y), v_c3), v_delta); - v_cb = _mm_add_ps(_mm_mul_ps(_mm_sub_ps(blueIdx == 2 ? v_b : v_r, v_y), v_c4), v_delta); - } - - void operator()(const float * src, float * dst, int n) const - { - int scn = srccn, bidx = blueIdx, i = 0; - int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb - const float delta = ColorChannel::half(); - float C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; - n *= 3; - - if (haveSIMD) - { - for ( ; i <= n - 24; i += 24, src += 8 * scn) + if(yuvOrder) { - __m128 v_r0 = _mm_loadu_ps(src); - __m128 v_r1 = _mm_loadu_ps(src + 4); - __m128 v_g0 = _mm_loadu_ps(src + 8); - __m128 v_g1 = _mm_loadu_ps(src + 12); - __m128 v_b0 = _mm_loadu_ps(src + 16); - __m128 v_b1 = _mm_loadu_ps(src + 20); - - if (scn == 4) - { - __m128 v_a0 = _mm_loadu_ps(src + 24); - __m128 v_a1 = _mm_loadu_ps(src + 28); - _mm_deinterleave_ps(v_r0, v_r1, v_g0, v_g1, - v_b0, v_b1, v_a0, v_a1); - } - else - _mm_deinterleave_ps(v_r0, v_r1, v_g0, v_g1, v_b0, v_b1); - - __m128 v_y0, v_cr0, v_cb0; - process(v_r0, v_g0, v_b0, - v_y0, v_cr0, v_cb0); - - __m128 v_y1, v_cr1, v_cb1; - process(v_r1, v_g1, v_b1, - v_y1, v_cr1, v_cb1); - - if(isCrCb) - _mm_interleave_ps(v_y0, v_y1, v_cr0, v_cr1, v_cb0, v_cb1); - else //YUV - { - _mm_interleave_ps(v_y0, v_y1, v_cb0, v_cb1, v_cr0, v_cr1); - } - - _mm_storeu_ps(dst + i, v_y0); - _mm_storeu_ps(dst + i + 4, v_y1); - _mm_storeu_ps(dst + i + 8 + yuvOrder*8, v_cr0); - _mm_storeu_ps(dst + i + 12 + yuvOrder*8, v_cr1); - _mm_storeu_ps(dst + i + 16 - yuvOrder*8, v_cb0); - _mm_storeu_ps(dst + i + 20 - yuvOrder*8, v_cb1); + v_store_interleave(dst, y, cb, cr); + } + else + { + v_store_interleave(dst, y, cr, cb); } } - - for ( ; i < n; i += 3, src += scn) - { - float Y = src[0]*C0 + src[1]*C1 + src[2]*C2; - float Cr = (src[bidx^2] - Y)*C3 + delta; - float Cb = (src[bidx] - Y)*C4 + delta; - dst[i] = Y; dst[i+1+yuvOrder] = Cr; dst[i+2-yuvOrder] = Cb; - } - } - int srccn, blueIdx; - bool isCrCb; - float coeffs[5]; - __m128 v_c0, v_c1, v_c2, v_c3, v_c4, v_delta; - bool haveSIMD; -}; - + vx_cleanup(); #endif + for ( ; i < n; i ++, src += scn, dst += 3) + { + float Y = src[0]*C0 + src[1]*C1 + src[2]*C2; + float Cr = (src[bidx^2] - Y)*C3 + delta; + float Cb = (src[bidx] - Y)*C4 + delta; + dst[0 ] = Y; + dst[1+yuvOrder] = Cr; + dst[2-yuvOrder] = Cb; + } + } + + int srccn, blueIdx; + bool isCrCb; + float coeffs[5]; +}; + template struct RGB2YCrCb_i { typedef _Tp channel_type; + static const int shift = yuv_shift; RGB2YCrCb_i(int _srccn, int _blueIdx, bool _isCrCb) : srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) { static const int coeffs_crb[] = { R2Y, G2Y, B2Y, YCRI, YCBI }; static const int coeffs_yuv[] = { R2Y, G2Y, B2Y, R2VI, B2UI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); + + for(int i = 0; i < 5; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } if(blueIdx==0) std::swap(coeffs[0], coeffs[2]); } void operator()(const _Tp* src, _Tp* dst, int n) const @@ -265,13 +181,13 @@ template struct RGB2YCrCb_i int scn = srccn, bidx = blueIdx; int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; - int delta = ColorChannel<_Tp>::half()*(1 << yuv_shift); + int delta = ColorChannel<_Tp>::half()*(1 << shift); n *= 3; for(int i = 0; i < n; i += 3, src += scn) { - int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, yuv_shift); - int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, yuv_shift); - int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, yuv_shift); + int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, shift); + int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, shift); + int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, shift); dst[i] = saturate_cast<_Tp>(Y); dst[i+1+yuvOrder] = saturate_cast<_Tp>(Cr); dst[i+2-yuvOrder] = saturate_cast<_Tp>(Cb); @@ -282,29 +198,167 @@ template struct RGB2YCrCb_i int coeffs[5]; }; -#if CV_NEON -template <> -struct RGB2YCrCb_i +template<> +struct RGB2YCrCb_i { - typedef uchar channel_type; + typedef ushort channel_type; + static const int shift = yuv_shift; + static const int fix_shift = (int)(sizeof(short)*8 - shift); RGB2YCrCb_i(int _srccn, int _blueIdx, bool _isCrCb) : srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) { static const int coeffs_crb[] = { R2Y, G2Y, B2Y, YCRI, YCBI }; static const int coeffs_yuv[] = { R2Y, G2Y, B2Y, R2VI, B2UI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); + + for(int i = 0; i < 5; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } + if(blueIdx==0) + std::swap(coeffs[0], coeffs[2]); + } + + void operator()(const ushort* src, ushort* dst, int n) const + { + int scn = srccn, bidx = blueIdx; + int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb + int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; + int sdelta = ColorChannel::half()*(1 << shift); + int i = 0; +#if CV_SIMD + const int vsize = v_uint16::nlanes; + const int descale = 1 << (shift-1); + + v_int16 b2y = vx_setall_s16((short)C0); + v_int16 g2y = vx_setall_s16((short)C1); + v_int16 r2y = vx_setall_s16((short)C2); + v_int16 one = vx_setall_s16(1); + v_int16 z = vx_setzero_s16(); + + v_int16 bg2y, r12y; + v_int16 dummy; + v_zip(b2y, g2y, bg2y, dummy); + v_zip(r2y, one, r12y, dummy); + + v_int16 vdescale = vx_setall_s16(1 << (shift-1)); + v_int32 vc3 = vx_setall_s32(C3); + v_int32 vc4 = vx_setall_s32(C4); + v_int32 vdd = vx_setall_s32(sdelta + descale); + + for(; i <= n-vsize; + i += vsize, src += vsize*scn, dst += vsize*3) + { + v_uint16 r, g, b, a; + if(scn == 3) + { + v_load_deinterleave(src, b, g, r); + } + else + { + v_load_deinterleave(src, b, g, r, a); + } + + v_uint16 y, cr, cb; + + v_int16 sb = v_reinterpret_as_s16(b); + v_int16 sr = v_reinterpret_as_s16(r); + v_int16 sg = v_reinterpret_as_s16(g); + + v_int16 bg0, bg1; + v_int16 rd0, rd1; + v_zip(sb, sg, bg0, bg1); + v_zip(sr, vdescale, rd0, rd1); + + // fixing 16bit signed multiplication + v_int16 mr, mg, mb; + mr = (sr < z) & r2y; + mg = (sg < z) & g2y; + mb = (sb < z) & b2y; + v_int16 fixmul = v_add_wrap(mr, v_add_wrap(mg, mb)) << fix_shift; + + v_int32 ssy0 = (v_dotprod(bg0, bg2y) + v_dotprod(rd0, r12y)) >> shift; + v_int32 ssy1 = (v_dotprod(bg1, bg2y) + v_dotprod(rd1, r12y)) >> shift; + + y = v_reinterpret_as_u16(v_add_wrap(v_pack(ssy0, ssy1), fixmul)); + + if(bidx) + swap(r, b); + + // (r-Y) and (b-Y) don't fit into int16 or uint16 range + v_uint32 r0, r1, b0, b1; + v_expand(r, r0, r1); + v_expand(b, b0, b1); + + v_uint32 uy0, uy1; + v_expand(y, uy0, uy1); + + v_int32 sr0 = v_reinterpret_as_s32(r0); + v_int32 sr1 = v_reinterpret_as_s32(r1); + v_int32 sb0 = v_reinterpret_as_s32(b0); + v_int32 sb1 = v_reinterpret_as_s32(b1); + v_int32 sy0 = v_reinterpret_as_s32(uy0); + v_int32 sy1 = v_reinterpret_as_s32(uy1); + + sr0 = sr0 - sy0; sr1 = sr1 - sy1; + sb0 = sb0 - sy0; sb1 = sb1 - sy1; + + v_int32 scr0, scr1, scb0, scb1; + + scr0 = (sr0*vc3 + vdd) >> shift; + scr1 = (sr1*vc3 + vdd) >> shift; + scb0 = (sb0*vc4 + vdd) >> shift; + scb1 = (sb1*vc4 + vdd) >> shift; + + // saturate and pack + cr = v_pack_u(scr0, scr1); + cb = v_pack_u(scb0, scb1); + + if(yuvOrder) + { + v_store_interleave(dst, y, cb, cr); + } + else + { + v_store_interleave(dst, y, cr, cb); + } + } + vx_cleanup(); +#endif + for( ; i < n; i++, src += scn, dst += 3) + { + int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, shift); + int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + sdelta, shift); + int Cb = CV_DESCALE((src[bidx] - Y)*C4 + sdelta, shift); + dst[0] = saturate_cast(Y); + dst[1+yuvOrder] = saturate_cast(Cr); + dst[2-yuvOrder] = saturate_cast(Cb); + } + } + int srccn, blueIdx; + bool isCrCb; + int coeffs[5]; +}; + + +template <> +struct RGB2YCrCb_i +{ + typedef uchar channel_type; + static const int shift = yuv_shift; + + RGB2YCrCb_i(int _srccn, int _blueIdx, bool _isCrCb) + : srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) + { + static const int coeffs_crb[] = { R2Y, G2Y, B2Y, YCRI, YCBI }; + static const int coeffs_yuv[] = { R2Y, G2Y, B2Y, R2VI, B2UI }; + for(int i = 0; i < 5; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } if (blueIdx==0) std::swap(coeffs[0], coeffs[2]); - - v_c0 = vdup_n_s16(coeffs[0]); - v_c1 = vdup_n_s16(coeffs[1]); - v_c2 = vdup_n_s16(coeffs[2]); - v_c3 = vdupq_n_s32(coeffs[3]); - v_c4 = vdupq_n_s32(coeffs[4]); - v_delta = vdupq_n_s32(ColorChannel::half()*(1 << yuv_shift)); - v_delta2 = vdupq_n_s32(1 << (yuv_shift - 1)); } void operator()(const uchar * src, uchar * dst, int n) const @@ -312,503 +366,157 @@ struct RGB2YCrCb_i int scn = srccn, bidx = blueIdx, i = 0; int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; - int delta = ColorChannel::half()*(1 << yuv_shift); - n *= 3; + int delta = ColorChannel::half()*(1 << shift); - for ( ; i <= n - 24; i += 24, src += scn * 8) +#if CV_SIMD + const int vsize = v_uint8::nlanes; + const int descaleShift = 1 << (shift-1); + v_int16 bg2y; + v_int16 r12y; + v_int16 dummy; + v_zip(vx_setall_s16((short)C0), vx_setall_s16((short)C1), bg2y, dummy); + v_zip(vx_setall_s16((short)C2), vx_setall_s16( 1), r12y, dummy); + + // delta + descaleShift == descaleShift*(half*2+1) + v_int16 c3h, c4h; + const short h21 = (short)(ColorChannel::half()*2+1); + v_zip(vx_setall_s16((short)C3), vx_setall_s16(h21), c3h, dummy); + v_zip(vx_setall_s16((short)C4), vx_setall_s16(h21), c4h, dummy); + + v_int16 vdescale = vx_setall_s16(descaleShift); + + for( ; i <= n-vsize; + i += vsize, src += scn*vsize, dst += 3*vsize) { - uint8x8x3_t v_dst; - int16x8x3_t v_src16; - - if (scn == 3) + v_uint8 r, g, b, a; + if(scn == 3) { - uint8x8x3_t v_src = vld3_u8(src); - v_src16.val[0] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[0])); - v_src16.val[1] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[1])); - v_src16.val[2] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[2])); + v_load_deinterleave(src, b, g, r); } else { - uint8x8x4_t v_src = vld4_u8(src); - v_src16.val[0] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[0])); - v_src16.val[1] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[1])); - v_src16.val[2] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[2])); + v_load_deinterleave(src, b, g, r, a); } - int16x4x3_t v_src0; - v_src0.val[0] = vget_low_s16(v_src16.val[0]); - v_src0.val[1] = vget_low_s16(v_src16.val[1]); - v_src0.val[2] = vget_low_s16(v_src16.val[2]); + v_uint8 y; - int32x4_t v_Y0 = vmlal_s16(vmlal_s16(vmull_s16(v_src0.val[0], v_c0), v_src0.val[1], v_c1), v_src0.val[2], v_c2); - v_Y0 = vshrq_n_s32(vaddq_s32(v_Y0, v_delta2), yuv_shift); - int32x4_t v_Cr0 = vmlaq_s32(v_delta, vsubq_s32(vmovl_s16(v_src0.val[bidx^2]), v_Y0), v_c3); - v_Cr0 = vshrq_n_s32(vaddq_s32(v_Cr0, v_delta2), yuv_shift); - int32x4_t v_Cb0 = vmlaq_s32(v_delta, vsubq_s32(vmovl_s16(v_src0.val[bidx]), v_Y0), v_c4); - v_Cb0 = vshrq_n_s32(vaddq_s32(v_Cb0, v_delta2), yuv_shift); + v_uint16 r0, r1, g0, g1, b0, b1; + v_expand(r, r0, r1); + v_expand(g, g0, g1); + v_expand(b, b0, b1); - v_src0.val[0] = vget_high_s16(v_src16.val[0]); - v_src0.val[1] = vget_high_s16(v_src16.val[1]); - v_src0.val[2] = vget_high_s16(v_src16.val[2]); + v_int16 sr0, sr1, sg0, sg1, sb0, sb1; + sr0 = v_reinterpret_as_s16(r0); sr1 = v_reinterpret_as_s16(r1); + sg0 = v_reinterpret_as_s16(g0); sg1 = v_reinterpret_as_s16(g1); + sb0 = v_reinterpret_as_s16(b0); sb1 = v_reinterpret_as_s16(b1); - int32x4_t v_Y1 = vmlal_s16(vmlal_s16(vmull_s16(v_src0.val[0], v_c0), v_src0.val[1], v_c1), v_src0.val[2], v_c2); - v_Y1 = vshrq_n_s32(vaddq_s32(v_Y1, v_delta2), yuv_shift); - int32x4_t v_Cr1 = vmlaq_s32(v_delta, vsubq_s32(vmovl_s16(v_src0.val[bidx^2]), v_Y1), v_c3); - v_Cr1 = vshrq_n_s32(vaddq_s32(v_Cr1, v_delta2), yuv_shift); - int32x4_t v_Cb1 = vmlaq_s32(v_delta, vsubq_s32(vmovl_s16(v_src0.val[bidx]), v_Y1), v_c4); - v_Cb1 = vshrq_n_s32(vaddq_s32(v_Cb1, v_delta2), yuv_shift); - - v_dst.val[0] = vqmovun_s16(vcombine_s16(vqmovn_s32(v_Y0), vqmovn_s32(v_Y1))); - v_dst.val[1+yuvOrder] = vqmovun_s16(vcombine_s16(vqmovn_s32(v_Cr0), vqmovn_s32(v_Cr1))); - v_dst.val[2-yuvOrder] = vqmovun_s16(vcombine_s16(vqmovn_s32(v_Cb0), vqmovn_s32(v_Cb1))); - - vst3_u8(dst + i, v_dst); - } - - for ( ; i < n; i += 3, src += scn) - { - int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, yuv_shift); - int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, yuv_shift); - int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, yuv_shift); - dst[i] = saturate_cast(Y); - dst[i+1+yuvOrder] = saturate_cast(Cr); - dst[i+2-yuvOrder] = saturate_cast(Cb); - } - } - int srccn, blueIdx, coeffs[5]; - bool isCrCb; - int16x4_t v_c0, v_c1, v_c2; - int32x4_t v_c3, v_c4, v_delta, v_delta2; -}; - -template <> -struct RGB2YCrCb_i -{ - typedef ushort channel_type; - - RGB2YCrCb_i(int _srccn, int _blueIdx, bool _isCrCb) - : srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) - { - static const int coeffs_crb[] = { R2Y, G2Y, B2Y, YCRI, YCBI }; - static const int coeffs_yuv[] = { R2Y, G2Y, B2Y, R2VI, B2UI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); - if (blueIdx==0) - std::swap(coeffs[0], coeffs[2]); - - v_c0 = vdupq_n_s32(coeffs[0]); - v_c1 = vdupq_n_s32(coeffs[1]); - v_c2 = vdupq_n_s32(coeffs[2]); - v_c3 = vdupq_n_s32(coeffs[3]); - v_c4 = vdupq_n_s32(coeffs[4]); - v_delta = vdupq_n_s32(ColorChannel::half()*(1 << yuv_shift)); - v_delta2 = vdupq_n_s32(1 << (yuv_shift - 1)); - } - - void operator()(const ushort * src, ushort * dst, int n) const - { - int scn = srccn, bidx = blueIdx, i = 0; - int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb - int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; - int delta = ColorChannel::half()*(1 << yuv_shift); - n *= 3; - - for ( ; i <= n - 24; i += 24, src += scn * 8) - { - uint16x8x3_t v_src, v_dst; - int32x4x3_t v_src0; - - if (scn == 3) - v_src = vld3q_u16(src); - else + v_uint32 y00, y01, y10, y11; { - uint16x8x4_t v_src_ = vld4q_u16(src); - v_src.val[0] = v_src_.val[0]; - v_src.val[1] = v_src_.val[1]; - v_src.val[2] = v_src_.val[2]; + v_int16 bg00, bg01, bg10, bg11; + v_int16 rd00, rd01, rd10, rd11; + v_zip(sb0, sg0, bg00, bg01); + v_zip(sb1, sg1, bg10, bg11); + v_zip(sr0, vdescale, rd00, rd01); + v_zip(sr1, vdescale, rd10, rd11); + + y00 = v_reinterpret_as_u32(v_dotprod(bg00, bg2y) + v_dotprod(rd00, r12y)) >> shift; + y01 = v_reinterpret_as_u32(v_dotprod(bg01, bg2y) + v_dotprod(rd01, r12y)) >> shift; + y10 = v_reinterpret_as_u32(v_dotprod(bg10, bg2y) + v_dotprod(rd10, r12y)) >> shift; + y11 = v_reinterpret_as_u32(v_dotprod(bg11, bg2y) + v_dotprod(rd11, r12y)) >> shift; } - v_src0.val[0] = vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(v_src.val[0]))); - v_src0.val[1] = vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(v_src.val[1]))); - v_src0.val[2] = vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(v_src.val[2]))); + v_uint16 y0, y1; + y0 = v_pack(y00, y01); + y1 = v_pack(y10, y11); - int32x4_t v_Y0 = vmlaq_s32(vmlaq_s32(vmulq_s32(v_src0.val[0], v_c0), v_src0.val[1], v_c1), v_src0.val[2], v_c2); - v_Y0 = vshrq_n_s32(vaddq_s32(v_Y0, v_delta2), yuv_shift); - int32x4_t v_Cr0 = vmlaq_s32(v_delta, vsubq_s32(v_src0.val[bidx^2], v_Y0), v_c3); - v_Cr0 = vshrq_n_s32(vaddq_s32(v_Cr0, v_delta2), yuv_shift); - int32x4_t v_Cb0 = vmlaq_s32(v_delta, vsubq_s32(v_src0.val[bidx], v_Y0), v_c4); - v_Cb0 = vshrq_n_s32(vaddq_s32(v_Cb0, v_delta2), yuv_shift); + y = v_pack(y0, y1); - v_src0.val[0] = vreinterpretq_s32_u32(vmovl_u16(vget_high_u16(v_src.val[0]))); - v_src0.val[1] = vreinterpretq_s32_u32(vmovl_u16(vget_high_u16(v_src.val[1]))); - v_src0.val[2] = vreinterpretq_s32_u32(vmovl_u16(vget_high_u16(v_src.val[2]))); + v_int16 sy0, sy1; + sy0 = v_reinterpret_as_s16(y0); + sy1 = v_reinterpret_as_s16(y1); - int32x4_t v_Y1 = vmlaq_s32(vmlaq_s32(vmulq_s32(v_src0.val[0], v_c0), v_src0.val[1], v_c1), v_src0.val[2], v_c2); - v_Y1 = vshrq_n_s32(vaddq_s32(v_Y1, v_delta2), yuv_shift); - int32x4_t v_Cr1 = vmlaq_s32(v_delta, vsubq_s32(v_src0.val[bidx^2], v_Y1), v_c3); - v_Cr1 = vshrq_n_s32(vaddq_s32(v_Cr1, v_delta2), yuv_shift); - int32x4_t v_Cb1 = vmlaq_s32(v_delta, vsubq_s32(v_src0.val[bidx], v_Y1), v_c4); - v_Cb1 = vshrq_n_s32(vaddq_s32(v_Cb1, v_delta2), yuv_shift); + // (r-Y) and (b-Y) don't fit into 8 bit, use 16 bits instead + sr0 = v_sub_wrap(sr0, sy0); + sr1 = v_sub_wrap(sr1, sy1); + sb0 = v_sub_wrap(sb0, sy0); + sb1 = v_sub_wrap(sb1, sy1); - v_dst.val[0] = vcombine_u16(vqmovun_s32(v_Y0), vqmovun_s32(v_Y1)); - v_dst.val[1+yuvOrder] = vcombine_u16(vqmovun_s32(v_Cr0), vqmovun_s32(v_Cr1)); - v_dst.val[2-yuvOrder] = vcombine_u16(vqmovun_s32(v_Cb0), vqmovun_s32(v_Cb1)); - - vst3q_u16(dst + i, v_dst); - } - - for ( ; i <= n - 12; i += 12, src += scn * 4) - { - uint16x4x3_t v_dst; - int32x4x3_t v_src0; - - if (scn == 3) + if(bidx) { - uint16x4x3_t v_src = vld3_u16(src); - v_src0.val[0] = vreinterpretq_s32_u32(vmovl_u16(v_src.val[0])); - v_src0.val[1] = vreinterpretq_s32_u32(vmovl_u16(v_src.val[1])); - v_src0.val[2] = vreinterpretq_s32_u32(vmovl_u16(v_src.val[2])); + swap(sr0, sb0); swap(sr1, sb1); + } + + v_uint32 cr00, cr01, cr10, cr11; + v_uint32 cb00, cb01, cb10, cb11; + + // delta + descaleShift == descaleShift*(half*2+1) + { + v_int16 rd00, rd01, rd10, rd11; + v_int16 bd00, bd01, bd10, bd11; + + v_zip(sr0, vdescale, rd00, rd01); + v_zip(sr1, vdescale, rd10, rd11); + + v_zip(sb0, vdescale, bd00, bd01); + v_zip(sb1, vdescale, bd10, bd11); + + cr00 = v_reinterpret_as_u32(v_dotprod(rd00, c3h)); + cr01 = v_reinterpret_as_u32(v_dotprod(rd01, c3h)); + cr10 = v_reinterpret_as_u32(v_dotprod(rd10, c3h)); + cr11 = v_reinterpret_as_u32(v_dotprod(rd11, c3h)); + + cb00 = v_reinterpret_as_u32(v_dotprod(bd00, c4h)); + cb01 = v_reinterpret_as_u32(v_dotprod(bd01, c4h)); + cb10 = v_reinterpret_as_u32(v_dotprod(bd10, c4h)); + cb11 = v_reinterpret_as_u32(v_dotprod(bd11, c4h)); + } + + v_uint8 cr, cb; + + cr00 = cr00 >> shift; + cr01 = cr01 >> shift; + cr10 = cr10 >> shift; + cr11 = cr11 >> shift; + + cb00 = cb00 >> shift; + cb01 = cb01 >> shift; + cb10 = cb10 >> shift; + cb11 = cb11 >> shift; + + v_uint16 cr0, cr1, cb0, cb1; + cr0 = v_pack(cr00, cr01); cr1 = v_pack(cr10, cr11); + cb0 = v_pack(cb00, cb01); cb1 = v_pack(cb10, cb11); + + cr = v_pack(cr0, cr1); + cb = v_pack(cb0, cb1); + + if(yuvOrder) + { + v_store_interleave(dst, y, cb, cr); } else { - uint16x4x4_t v_src = vld4_u16(src); - v_src0.val[0] = vreinterpretq_s32_u32(vmovl_u16(v_src.val[0])); - v_src0.val[1] = vreinterpretq_s32_u32(vmovl_u16(v_src.val[1])); - v_src0.val[2] = vreinterpretq_s32_u32(vmovl_u16(v_src.val[2])); - } - - int32x4_t v_Y = vmlaq_s32(vmlaq_s32(vmulq_s32(v_src0.val[0], v_c0), v_src0.val[1], v_c1), v_src0.val[2], v_c2); - v_Y = vshrq_n_s32(vaddq_s32(v_Y, v_delta2), yuv_shift); - int32x4_t v_Cr = vmlaq_s32(v_delta, vsubq_s32(v_src0.val[bidx^2], v_Y), v_c3); - v_Cr = vshrq_n_s32(vaddq_s32(v_Cr, v_delta2), yuv_shift); - int32x4_t v_Cb = vmlaq_s32(v_delta, vsubq_s32(v_src0.val[bidx], v_Y), v_c4); - v_Cb = vshrq_n_s32(vaddq_s32(v_Cb, v_delta2), yuv_shift); - - v_dst.val[0] = vqmovun_s32(v_Y); - v_dst.val[1+yuvOrder] = vqmovun_s32(v_Cr); - v_dst.val[2-yuvOrder] = vqmovun_s32(v_Cb); - - vst3_u16(dst + i, v_dst); - } - - for ( ; i < n; i += 3, src += scn) - { - int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, yuv_shift); - int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, yuv_shift); - int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, yuv_shift); - dst[i] = saturate_cast(Y); - dst[i+1+yuvOrder] = saturate_cast(Cr); - dst[i+2-yuvOrder] = saturate_cast(Cb); - } - } - int srccn, blueIdx, coeffs[5]; - bool isCrCb; - int32x4_t v_c0, v_c1, v_c2, v_c3, v_c4, v_delta, v_delta2; -}; - -#elif CV_SSE4_1 - -template <> -struct RGB2YCrCb_i -{ - typedef uchar channel_type; - - RGB2YCrCb_i(int _srccn, int _blueIdx, bool _isCrCb) - : srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) - { - static const int coeffs_crb[] = { R2Y, G2Y, B2Y, YCRI, YCBI }; - static const int coeffs_yuv[] = { R2Y, G2Y, B2Y, R2VI, B2UI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); - if (blueIdx==0) - std::swap(coeffs[0], coeffs[2]); - - short delta = 1 << (yuv_shift - 1); - v_delta_16 = _mm_set1_epi16(delta); - v_delta_32 = _mm_set1_epi32(delta); - short delta2 = 1 + ColorChannel::half() * 2; - v_coeff = _mm_set_epi16(delta2, (short)coeffs[4], delta2, (short)coeffs[3], delta2, (short)coeffs[4], delta2, (short)coeffs[3]); - if(isCrCb) - v_shuffle2 = _mm_set_epi8(0x0, 0x0, 0x0, 0x0, 0xf, 0xe, 0xc, 0xb, 0xa, 0x8, 0x7, 0x6, 0x4, 0x3, 0x2, 0x0); - else //if YUV - v_shuffle2 = _mm_set_epi8(0x0, 0x0, 0x0, 0x0, 0xe, 0xf, 0xc, 0xa, 0xb, 0x8, 0x6, 0x7, 0x4, 0x2, 0x3, 0x0); - haveSIMD = checkHardwareSupport(CV_CPU_SSE4_1); - } - - // 16u x 8 - void process(__m128i* v_rgb, __m128i & v_crgb, - __m128i* v_rb, uchar * dst) const - { - v_rgb[0] = _mm_madd_epi16(v_rgb[0], v_crgb); - v_rgb[1] = _mm_madd_epi16(v_rgb[1], v_crgb); - v_rgb[2] = _mm_madd_epi16(v_rgb[2], v_crgb); - v_rgb[3] = _mm_madd_epi16(v_rgb[3], v_crgb); - v_rgb[0] = _mm_hadd_epi32(v_rgb[0], v_rgb[1]); - v_rgb[2] = _mm_hadd_epi32(v_rgb[2], v_rgb[3]); - v_rgb[0] = _mm_add_epi32(v_rgb[0], v_delta_32); - v_rgb[2] = _mm_add_epi32(v_rgb[2], v_delta_32); - v_rgb[0] = _mm_srai_epi32(v_rgb[0], yuv_shift); - v_rgb[2] = _mm_srai_epi32(v_rgb[2], yuv_shift); - __m128i v_y = _mm_packs_epi32(v_rgb[0], v_rgb[2]); - - v_rb[0] = _mm_cvtepu8_epi16(v_rb[0]); - v_rb[1] = _mm_cvtepu8_epi16(v_rb[1]); - v_rb[0] = _mm_sub_epi16(v_rb[0], _mm_unpacklo_epi16(v_y, v_y)); - v_rb[1] = _mm_sub_epi16(v_rb[1], _mm_unpackhi_epi16(v_y, v_y)); - v_rgb[0] = _mm_unpacklo_epi16(v_rb[0], v_delta_16); - v_rgb[1] = _mm_unpackhi_epi16(v_rb[0], v_delta_16); - v_rgb[2] = _mm_unpacklo_epi16(v_rb[1], v_delta_16); - v_rgb[3] = _mm_unpackhi_epi16(v_rb[1], v_delta_16); - v_rgb[0] = _mm_madd_epi16(v_rgb[0], v_coeff); - v_rgb[1] = _mm_madd_epi16(v_rgb[1], v_coeff); - v_rgb[2] = _mm_madd_epi16(v_rgb[2], v_coeff); - v_rgb[3] = _mm_madd_epi16(v_rgb[3], v_coeff); - v_rgb[0] = _mm_srai_epi32(v_rgb[0], yuv_shift); - v_rgb[1] = _mm_srai_epi32(v_rgb[1], yuv_shift); - v_rgb[2] = _mm_srai_epi32(v_rgb[2], yuv_shift); - v_rgb[3] = _mm_srai_epi32(v_rgb[3], yuv_shift); - v_rgb[0] = _mm_packs_epi32(v_rgb[0], v_rgb[1]); - v_rgb[2] = _mm_packs_epi32(v_rgb[2], v_rgb[3]); - v_rgb[0] = _mm_packus_epi16(v_rgb[0], v_rgb[2]); - - v_rb[0] = _mm_unpacklo_epi16(v_y, v_rgb[0]); - v_rb[1] = _mm_unpackhi_epi16(v_y, v_rgb[0]); - - v_rb[0] = _mm_shuffle_epi8(v_rb[0], v_shuffle2); - v_rb[1] = _mm_shuffle_epi8(v_rb[1], v_shuffle2); - v_rb[1] = _mm_alignr_epi8(v_rb[1], _mm_slli_si128(v_rb[0], 4), 12); - - _mm_storel_epi64((__m128i *)(dst), v_rb[0]); - _mm_storeu_si128((__m128i *)(dst + 8), v_rb[1]); - } - - void operator()(const uchar * src, uchar * dst, int n) const - { - int scn = srccn, bidx = blueIdx, i = 0; - int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb - int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; - int delta = ColorChannel::half()*(1 << yuv_shift); - n *= 3; - - if (haveSIMD) - { - __m128i v_shuffle; - __m128i v_crgb; - if (scn == 4) - { - if (bidx == 0) - { - v_shuffle = _mm_set_epi8(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xe, 0x8, 0xa, 0x4, 0x6, 0x0, 0x2); - } - else - { - v_shuffle = _mm_set_epi8(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2, 0x0); - } - v_crgb = _mm_set_epi16(0, (short)C2, (short)C1, (short)C0, 0, (short)C2, (short)C1, (short)C0); - for ( ; i <= n - 24; i += 24, src += scn * 8) - { - __m128i v_src[2]; - v_src[0] = _mm_loadu_si128((__m128i const *)(src)); - v_src[1] = _mm_loadu_si128((__m128i const *)(src + 16)); - - __m128i v_rgb[4]; - v_rgb[0] = _mm_cvtepu8_epi16(v_src[0]); - v_rgb[1] = _mm_cvtepu8_epi16(_mm_srli_si128(v_src[0], 8)); - v_rgb[2] = _mm_cvtepu8_epi16(v_src[1]); - v_rgb[3] = _mm_cvtepu8_epi16(_mm_srli_si128(v_src[1], 8)); - - __m128i v_rb[2]; - v_rb[0] = _mm_shuffle_epi8(v_src[0], v_shuffle); - v_rb[1] = _mm_shuffle_epi8(v_src[1], v_shuffle); - - process(v_rgb, v_crgb, v_rb, dst + i); - } - } - else - { - if (bidx == 0) - { - v_shuffle = _mm_set_epi8(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xb, 0x6, 0x8, 0x3, 0x5, 0x0, 0x2); - } - else - { - v_shuffle = _mm_set_epi8(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x9, 0x8, 0x6, 0x5, 0x3, 0x2, 0x0); - } - v_crgb = _mm_set_epi16(0, (short)C2, (short)C1, (short)C0, (short)C2, (short)C1, (short)C0, 0); - for ( ; i <= n - 24; i += 24, src += scn * 8) - { - __m128i v_src[2]; - v_src[0] = _mm_loadu_si128((__m128i const *)(src)); - v_src[1] = _mm_loadl_epi64((__m128i const *)(src + 16)); - - __m128i v_rgb[4]; - v_rgb[0] = _mm_cvtepu8_epi16(_mm_slli_si128(v_src[0], 1)); - v_rgb[1] = _mm_cvtepu8_epi16(_mm_srli_si128(v_src[0], 5)); - v_rgb[2] = _mm_cvtepu8_epi16(_mm_alignr_epi8(v_src[1], v_src[0], 11)); - v_rgb[3] = _mm_cvtepu8_epi16(_mm_srli_si128(v_src[1], 1)); - - __m128i v_rb[2]; - v_rb[0] = _mm_shuffle_epi8(v_src[0], v_shuffle); - v_rb[1] = _mm_shuffle_epi8(_mm_alignr_epi8(v_src[1], v_src[0], 12), v_shuffle); - - process(v_rgb, v_crgb, v_rb, dst + i); - } + v_store_interleave(dst, y, cr, cb); } } + vx_cleanup(); +#endif - for ( ; i < n; i += 3, src += scn) + for ( ; i < n; i++, src += scn, dst += 3) { - int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, yuv_shift); - int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, yuv_shift); - int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, yuv_shift); - dst[i] = saturate_cast(Y); - dst[i+1+yuvOrder] = saturate_cast(Cr); - dst[i+2-yuvOrder] = saturate_cast(Cb); - } - } - - __m128i v_delta_16, v_delta_32; - __m128i v_coeff; - __m128i v_shuffle2; - int srccn, blueIdx, coeffs[5]; - bool isCrCb; - bool haveSIMD; -}; - -template <> -struct RGB2YCrCb_i -{ - typedef ushort channel_type; - - RGB2YCrCb_i(int _srccn, int _blueIdx, bool _isCrCb) - : srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb) - { - static const int coeffs_crb[] = { R2Y, G2Y, B2Y, YCRI, YCBI }; - static const int coeffs_yuv[] = { R2Y, G2Y, B2Y, R2VI, B2UI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0])); - if (blueIdx==0) - std::swap(coeffs[0], coeffs[2]); - - v_c0 = _mm_set1_epi32(coeffs[0]); - v_c1 = _mm_set1_epi32(coeffs[1]); - v_c2 = _mm_set1_epi32(coeffs[2]); - v_c3 = _mm_set1_epi32(coeffs[3]); - v_c4 = _mm_set1_epi32(coeffs[4]); - v_delta2 = _mm_set1_epi32(1 << (yuv_shift - 1)); - v_delta = _mm_set1_epi32(ColorChannel::half()*(1 << yuv_shift)); - v_delta = _mm_add_epi32(v_delta, v_delta2); - v_zero = _mm_setzero_si128(); - - haveSIMD = checkHardwareSupport(CV_CPU_SSE4_1); - } - - // 16u x 8 - void process(__m128i v_r, __m128i v_g, __m128i v_b, - __m128i & v_y, __m128i & v_cr, __m128i & v_cb) const - { - __m128i v_r_p = _mm_unpacklo_epi16(v_r, v_zero); - __m128i v_g_p = _mm_unpacklo_epi16(v_g, v_zero); - __m128i v_b_p = _mm_unpacklo_epi16(v_b, v_zero); - - __m128i v_y0 = _mm_add_epi32(_mm_mullo_epi32(v_r_p, v_c0), - _mm_add_epi32(_mm_mullo_epi32(v_g_p, v_c1), - _mm_mullo_epi32(v_b_p, v_c2))); - v_y0 = _mm_srli_epi32(_mm_add_epi32(v_delta2, v_y0), yuv_shift); - - __m128i v_cr0 = _mm_mullo_epi32(_mm_sub_epi32(blueIdx == 2 ? v_r_p : v_b_p, v_y0), v_c3); - __m128i v_cb0 = _mm_mullo_epi32(_mm_sub_epi32(blueIdx == 0 ? v_r_p : v_b_p, v_y0), v_c4); - v_cr0 = _mm_srai_epi32(_mm_add_epi32(v_delta, v_cr0), yuv_shift); - v_cb0 = _mm_srai_epi32(_mm_add_epi32(v_delta, v_cb0), yuv_shift); - - v_r_p = _mm_unpackhi_epi16(v_r, v_zero); - v_g_p = _mm_unpackhi_epi16(v_g, v_zero); - v_b_p = _mm_unpackhi_epi16(v_b, v_zero); - - __m128i v_y1 = _mm_add_epi32(_mm_mullo_epi32(v_r_p, v_c0), - _mm_add_epi32(_mm_mullo_epi32(v_g_p, v_c1), - _mm_mullo_epi32(v_b_p, v_c2))); - v_y1 = _mm_srli_epi32(_mm_add_epi32(v_delta2, v_y1), yuv_shift); - - __m128i v_cr1 = _mm_mullo_epi32(_mm_sub_epi32(blueIdx == 2 ? v_r_p : v_b_p, v_y1), v_c3); - __m128i v_cb1 = _mm_mullo_epi32(_mm_sub_epi32(blueIdx == 0 ? v_r_p : v_b_p, v_y1), v_c4); - v_cr1 = _mm_srai_epi32(_mm_add_epi32(v_delta, v_cr1), yuv_shift); - v_cb1 = _mm_srai_epi32(_mm_add_epi32(v_delta, v_cb1), yuv_shift); - - v_y = _mm_packus_epi32(v_y0, v_y1); - v_cr = _mm_packus_epi32(v_cr0, v_cr1); - v_cb = _mm_packus_epi32(v_cb0, v_cb1); - } - - void operator()(const ushort * src, ushort * dst, int n) const - { - int scn = srccn, bidx = blueIdx, i = 0; - int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb - int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4]; - int delta = ColorChannel::half()*(1 << yuv_shift); - n *= 3; - - if (haveSIMD) - { - for ( ; i <= n - 48; i += 48, src += scn * 16) - { - __m128i v_r0 = _mm_loadu_si128((__m128i const *)(src)); - __m128i v_r1 = _mm_loadu_si128((__m128i const *)(src + 8)); - __m128i v_g0 = _mm_loadu_si128((__m128i const *)(src + 16)); - __m128i v_g1 = _mm_loadu_si128((__m128i const *)(src + 24)); - __m128i v_b0 = _mm_loadu_si128((__m128i const *)(src + 32)); - __m128i v_b1 = _mm_loadu_si128((__m128i const *)(src + 40)); - - if (scn == 4) - { - __m128i v_a0 = _mm_loadu_si128((__m128i const *)(src + 48)); - __m128i v_a1 = _mm_loadu_si128((__m128i const *)(src + 56)); - - _mm_deinterleave_epi16(v_r0, v_r1, v_g0, v_g1, - v_b0, v_b1, v_a0, v_a1); - } - else - _mm_deinterleave_epi16(v_r0, v_r1, v_g0, v_g1, v_b0, v_b1); - - __m128i v_y0 = v_zero, v_cr0 = v_zero, v_cb0 = v_zero; - process(v_r0, v_g0, v_b0, - v_y0, v_cr0, v_cb0); - - __m128i v_y1 = v_zero, v_cr1 = v_zero, v_cb1 = v_zero; - process(v_r1, v_g1, v_b1, - v_y1, v_cr1, v_cb1); - - if(isCrCb) - _mm_interleave_epi16(v_y0, v_y1, v_cr0, v_cr1, v_cb0, v_cb1); - else //YUV - _mm_interleave_epi16(v_y0, v_y1, v_cb0, v_cb1, v_cr0, v_cr1); - - _mm_storeu_si128((__m128i *)(dst + i), v_y0); - _mm_storeu_si128((__m128i *)(dst + i + 8), v_y1); - _mm_storeu_si128((__m128i *)(dst + i + 16 + yuvOrder*16), v_cr0); - _mm_storeu_si128((__m128i *)(dst + i + 24 + yuvOrder*16), v_cr1); - _mm_storeu_si128((__m128i *)(dst + i + 32 - yuvOrder*16), v_cb0); - _mm_storeu_si128((__m128i *)(dst + i + 40 - yuvOrder*16), v_cb1); - } - } - - for ( ; i < n; i += 3, src += scn) - { - int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, yuv_shift); - int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, yuv_shift); - int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, yuv_shift); - dst[i] = saturate_cast(Y); - dst[i+1+yuvOrder] = saturate_cast(Cr); - dst[i+2-yuvOrder] = saturate_cast(Cb); + int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, shift); + int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, shift); + int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, shift); + dst[0] = saturate_cast(Y); + dst[1+yuvOrder] = saturate_cast(Cr); + dst[2-yuvOrder] = saturate_cast(Cb); } } int srccn, blueIdx, coeffs[5]; bool isCrCb; - __m128i v_c0, v_c1, v_c2; - __m128i v_c3, v_c4, v_delta, v_delta2; - __m128i v_zero; - bool haveSIMD; }; -#endif // CV_SSE4_1 template struct YCrCb2RGB_f { @@ -819,7 +527,10 @@ template struct YCrCb2RGB_f { static const float coeffs_cbr[] = {CR2RF, CR2GF, CB2GF, CB2BF}; static const float coeffs_yuv[] = { V2RF, V2GF, U2GF, U2BF}; - memcpy(coeffs, isCrCb ? coeffs_cbr : coeffs_yuv, 4*sizeof(coeffs[0])); + for(int i = 0; i < 4; i++) + { + coeffs[i] = isCrCb ? coeffs_cbr[i] : coeffs_yuv[i]; + } } void operator()(const _Tp* src, _Tp* dst, int n) const { @@ -848,9 +559,8 @@ template struct YCrCb2RGB_f float coeffs[4]; }; -#if CV_NEON -template <> +template<> struct YCrCb2RGB_f { typedef float channel_type; @@ -860,200 +570,87 @@ struct YCrCb2RGB_f { static const float coeffs_cbr[] = {CR2RF, CR2GF, CB2GF, CB2BF}; static const float coeffs_yuv[] = { V2RF, V2GF, U2GF, U2BF}; - memcpy(coeffs, isCrCb ? coeffs_cbr : coeffs_yuv, 4*sizeof(coeffs[0])); - - v_c0 = vdupq_n_f32(coeffs[0]); - v_c1 = vdupq_n_f32(coeffs[1]); - v_c2 = vdupq_n_f32(coeffs[2]); - v_c3 = vdupq_n_f32(coeffs[3]); - v_delta = vdupq_n_f32(ColorChannel::half()); - v_alpha = vdupq_n_f32(ColorChannel::max()); + for(int i = 0; i < 4; i++) + { + coeffs[i] = isCrCb ? coeffs_cbr[i] : coeffs_yuv[i]; + } } void operator()(const float* src, float* dst, int n) const { - int dcn = dstcn, bidx = blueIdx, i = 0; + int dcn = dstcn, bidx = blueIdx; int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb const float delta = ColorChannel::half(), alpha = ColorChannel::max(); float C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3]; - n *= 3; - if (dcn == 3) - for ( ; i <= n - 12; i += 12, dst += 12) - { - float32x4x3_t v_src = vld3q_f32(src + i), v_dst; - float32x4_t v_Y = v_src.val[0], v_Cr = v_src.val[1+yuvOrder], v_Cb = v_src.val[2-yuvOrder]; - - v_dst.val[bidx] = vmlaq_f32(v_Y, vsubq_f32(v_Cb, v_delta), v_c3); - v_dst.val[1] = vaddq_f32(vmlaq_f32(vmulq_f32(vsubq_f32(v_Cb, v_delta), v_c2), vsubq_f32(v_Cr, v_delta), v_c1), v_Y); - v_dst.val[bidx^2] = vmlaq_f32(v_Y, vsubq_f32(v_Cr, v_delta), v_c0); - - vst3q_f32(dst, v_dst); - } - else - for ( ; i <= n - 12; i += 12, dst += 16) - { - float32x4x3_t v_src = vld3q_f32(src + i); - float32x4x4_t v_dst; - float32x4_t v_Y = v_src.val[0], v_Cr = v_src.val[1+yuvOrder], v_Cb = v_src.val[2-yuvOrder]; - - v_dst.val[bidx] = vmlaq_f32(v_Y, vsubq_f32(v_Cb, v_delta), v_c3); - v_dst.val[1] = vaddq_f32(vmlaq_f32(vmulq_f32(vsubq_f32(v_Cb, v_delta), v_c2), vsubq_f32(v_Cr, v_delta), v_c1), v_Y); - v_dst.val[bidx^2] = vmlaq_f32(v_Y, vsubq_f32(v_Cr, v_delta), v_c0); - v_dst.val[3] = v_alpha; - - vst4q_f32(dst, v_dst); - } - - for ( ; i < n; i += 3, dst += dcn) + int i = 0; +#if CV_SIMD + v_float32 vc0 = vx_setall_f32(C0), vc1 = vx_setall_f32(C1); + v_float32 vc2 = vx_setall_f32(C2), vc3 = vx_setall_f32(C3); + v_float32 vdelta = vx_setall_f32(delta); + v_float32 valpha = vx_setall_f32(alpha); + const int vsize = v_float32::nlanes; + for( ; i <= n-vsize; + i += vsize, src += vsize*3, dst += vsize*dcn) { - float Y = src[i], Cr = src[i+1+yuvOrder], Cb = src[i+2-yuvOrder]; + v_float32 y, cr, cb; + if(yuvOrder) + v_load_deinterleave(src, y, cb, cr); + else + v_load_deinterleave(src, y, cr, cb); - float b = Y + (Cb - delta)*C3; - float g = Y + (Cb - delta)*C2 + (Cr - delta)*C1; - float r = Y + (Cr - delta)*C0; + v_float32 b, g, r; - dst[bidx] = b; dst[1] = g; dst[bidx^2] = r; - if( dcn == 4 ) - dst[3] = alpha; + cb -= vdelta; cr -= vdelta; + b = v_fma(cb, vc3, y); + g = v_fma(cr, vc1, v_fma(cb, vc2, y)); + r = v_fma(cr, vc0, y); + + if(bidx) + swap(r, b); + + if(dcn == 3) + v_store_interleave(dst, b, g, r); + else + v_store_interleave(dst, b, g, r, valpha); } - } - int dstcn, blueIdx; - bool isCrCb; - float coeffs[4]; - float32x4_t v_c0, v_c1, v_c2, v_c3, v_alpha, v_delta; -}; - -#elif CV_SSE2 - -template <> -struct YCrCb2RGB_f -{ - typedef float channel_type; - - YCrCb2RGB_f(int _dstcn, int _blueIdx, bool _isCrCb) - : dstcn(_dstcn), blueIdx(_blueIdx), isCrCb(_isCrCb) - { - static const float coeffs_cbr[] = {CR2RF, CR2GF, CB2GF, CB2BF}; - static const float coeffs_yuv[] = { V2RF, V2GF, U2GF, U2BF}; - memcpy(coeffs, isCrCb ? coeffs_cbr : coeffs_yuv, 4*sizeof(coeffs[0])); - - v_c0 = _mm_set1_ps(coeffs[0]); - v_c1 = _mm_set1_ps(coeffs[1]); - v_c2 = _mm_set1_ps(coeffs[2]); - v_c3 = _mm_set1_ps(coeffs[3]); - v_delta = _mm_set1_ps(ColorChannel::half()); - v_alpha = _mm_set1_ps(ColorChannel::max()); - - haveSIMD = checkHardwareSupport(CV_CPU_SSE2); - } - - void process(__m128 v_y, __m128 v_cr, __m128 v_cb, - __m128 & v_r, __m128 & v_g, __m128 & v_b) const - { - v_cb = _mm_sub_ps(v_cb, v_delta); - v_cr = _mm_sub_ps(v_cr, v_delta); - - if (!isCrCb) - std::swap(v_cb, v_cr); - - v_b = _mm_mul_ps(v_cb, v_c3); - v_g = _mm_add_ps(_mm_mul_ps(v_cb, v_c2), _mm_mul_ps(v_cr, v_c1)); - v_r = _mm_mul_ps(v_cr, v_c0); - - v_b = _mm_add_ps(v_b, v_y); - v_g = _mm_add_ps(v_g, v_y); - v_r = _mm_add_ps(v_r, v_y); - - if (blueIdx == 0) - std::swap(v_b, v_r); - } - - void operator()(const float* src, float* dst, int n) const - { - int dcn = dstcn, bidx = blueIdx, i = 0; - int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb - const float delta = ColorChannel::half(), alpha = ColorChannel::max(); - float C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3]; - n *= 3; - - if (haveSIMD) - { - for ( ; i <= n - 24; i += 24, dst += 8 * dcn) - { - __m128 v_y0 = _mm_loadu_ps(src + i); - __m128 v_y1 = _mm_loadu_ps(src + i + 4); - __m128 v_cr0 = _mm_loadu_ps(src + i + 8); - __m128 v_cr1 = _mm_loadu_ps(src + i + 12); - __m128 v_cb0 = _mm_loadu_ps(src + i + 16); - __m128 v_cb1 = _mm_loadu_ps(src + i + 20); - - _mm_deinterleave_ps(v_y0, v_y1, v_cr0, v_cr1, v_cb0, v_cb1); - - __m128 v_r0, v_g0, v_b0; - process(v_y0, v_cr0, v_cb0, - v_r0, v_g0, v_b0); - - __m128 v_r1, v_g1, v_b1; - process(v_y1, v_cr1, v_cb1, - v_r1, v_g1, v_b1); - - __m128 v_a0 = v_alpha, v_a1 = v_alpha; - - if (dcn == 3) - _mm_interleave_ps(v_r0, v_r1, v_g0, v_g1, v_b0, v_b1); - else - _mm_interleave_ps(v_r0, v_r1, v_g0, v_g1, - v_b0, v_b1, v_a0, v_a1); - - _mm_storeu_ps(dst, v_r0); - _mm_storeu_ps(dst + 4, v_r1); - _mm_storeu_ps(dst + 8, v_g0); - _mm_storeu_ps(dst + 12, v_g1); - _mm_storeu_ps(dst + 16, v_b0); - _mm_storeu_ps(dst + 20, v_b1); - - if (dcn == 4) - { - _mm_storeu_ps(dst + 24, v_a0); - _mm_storeu_ps(dst + 28, v_a1); - } - } - } - - for ( ; i < n; i += 3, dst += dcn) - { - float Y = src[i], Cr = src[i+1+yuvOrder], Cb = src[i+2-yuvOrder]; - - float b = Y + (Cb - delta)*C3; - float g = Y + (Cb - delta)*C2 + (Cr - delta)*C1; - float r = Y + (Cr - delta)*C0; - - dst[bidx] = b; dst[1] = g; dst[bidx^2] = r; - if( dcn == 4 ) - dst[3] = alpha; - } - } - int dstcn, blueIdx; - bool isCrCb; - float coeffs[4]; - - __m128 v_c0, v_c1, v_c2, v_c3, v_alpha, v_delta; - bool haveSIMD; -}; - + vx_cleanup(); #endif + for(; i < n; i++, src += 3, dst += dcn) + { + float Y = src[0]; + float Cr = src[1+yuvOrder]; + float Cb = src[2-yuvOrder]; + + float b = Y + (Cb - delta)*C3; + float g = Y + (Cb - delta)*C2 + (Cr - delta)*C1; + float r = Y + (Cr - delta)*C0; + + dst[bidx] = b; dst[1] = g; dst[bidx^2] = r; + if( dcn == 4 ) + dst[3] = alpha; + } + } + int dstcn, blueIdx; + bool isCrCb; + float coeffs[4]; +}; + template struct YCrCb2RGB_i { typedef _Tp channel_type; + static const int shift = yuv_shift; YCrCb2RGB_i(int _dstcn, int _blueIdx, bool _isCrCb) : dstcn(_dstcn), blueIdx(_blueIdx), isCrCb(_isCrCb) { static const int coeffs_crb[] = { CR2RI, CR2GI, CB2GI, CB2BI}; static const int coeffs_yuv[] = { V2RI, V2GI, U2GI, U2BI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 4*sizeof(coeffs[0])); + for(int i = 0; i < 4; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } } void operator()(const _Tp* src, _Tp* dst, int n) const @@ -1069,9 +666,9 @@ template struct YCrCb2RGB_i _Tp Cr = src[i+1+yuvOrder]; _Tp Cb = src[i+2-yuvOrder]; - int b = Y + CV_DESCALE((Cb - delta)*C3, yuv_shift); - int g = Y + CV_DESCALE((Cb - delta)*C2 + (Cr - delta)*C1, yuv_shift); - int r = Y + CV_DESCALE((Cr - delta)*C0, yuv_shift); + int b = Y + CV_DESCALE((Cb - delta)*C3, shift); + int g = Y + CV_DESCALE((Cb - delta)*C2 + (Cr - delta)*C1, shift); + int r = Y + CV_DESCALE((Cr - delta)*C0, shift); dst[bidx] = saturate_cast<_Tp>(b); dst[1] = saturate_cast<_Tp>(g); @@ -1085,27 +682,22 @@ template struct YCrCb2RGB_i int coeffs[4]; }; -#if CV_NEON template <> struct YCrCb2RGB_i { typedef uchar channel_type; + static const int shift = yuv_shift; YCrCb2RGB_i(int _dstcn, int _blueIdx, bool _isCrCb) : dstcn(_dstcn), blueIdx(_blueIdx), isCrCb(_isCrCb) { static const int coeffs_crb[] = { CR2RI, CR2GI, CB2GI, CB2BI}; static const int coeffs_yuv[] = { V2RI, V2GI, U2GI, U2BI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 4*sizeof(coeffs[0])); - - v_c0 = vdupq_n_s32(coeffs[0]); - v_c1 = vdupq_n_s32(coeffs[1]); - v_c2 = vdupq_n_s32(coeffs[2]); - v_c3 = vdupq_n_s32(coeffs[3]); - v_delta = vdup_n_s16(ColorChannel::half()); - v_delta2 = vdupq_n_s32(1 << (yuv_shift - 1)); - v_alpha = vdup_n_u8(ColorChannel::max()); + for(int i = 0; i < 4; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } } void operator()(const uchar* src, uchar* dst, int n) const @@ -1114,70 +706,120 @@ struct YCrCb2RGB_i int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb const uchar delta = ColorChannel::half(), alpha = ColorChannel::max(); int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3]; - n *= 3; - for ( ; i <= n - 24; i += 24, dst += dcn * 8) +#if CV_SIMD + const int vsize = v_uint8::nlanes; + v_uint8 valpha = vx_setall_u8(alpha); + v_uint8 vdelta = vx_setall_u8(delta); + const int descaleShift = 1 << (shift - 1); + v_int32 vdescale = vx_setall_s32(descaleShift); + + v_int16 vc0 = vx_setall_s16((short)C0), vc1 = vx_setall_s16((short)C1), vc2 = vx_setall_s16((short)C2); + // if YUV then C3 > 2^15, need to subtract it + // to fit in short by short multiplication + v_int16 vc3 = vx_setall_s16(yuvOrder ? (short)(C3-(1 << 15)) : (short)C3); + + for( ; i <= n-vsize; + i += vsize, src += 3*vsize, dst += dcn*vsize) { - uint8x8x3_t v_src = vld3_u8(src + i); - int16x8x3_t v_src16; - v_src16.val[0] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[0])); - v_src16.val[1] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[1])); - v_src16.val[2] = vreinterpretq_s16_u16(vmovl_u8(v_src.val[2])); - - int16x4_t v_Y = vget_low_s16(v_src16.val[0]), - v_Cr = vget_low_s16(v_src16.val[1+yuvOrder]), - v_Cb = vget_low_s16(v_src16.val[2-yuvOrder]); - - int32x4_t v_b0 = vmulq_s32(v_c3, vsubl_s16(v_Cb, v_delta)); - v_b0 = vaddw_s16(vshrq_n_s32(vaddq_s32(v_b0, v_delta2), yuv_shift), v_Y); - int32x4_t v_g0 = vmlaq_s32(vmulq_s32(vsubl_s16(v_Cr, v_delta), v_c1), vsubl_s16(v_Cb, v_delta), v_c2); - v_g0 = vaddw_s16(vshrq_n_s32(vaddq_s32(v_g0, v_delta2), yuv_shift), v_Y); - int32x4_t v_r0 = vmulq_s32(v_c0, vsubl_s16(v_Cr, v_delta)); - v_r0 = vaddw_s16(vshrq_n_s32(vaddq_s32(v_r0, v_delta2), yuv_shift), v_Y); - - v_Y = vget_high_s16(v_src16.val[0]); - v_Cr = vget_high_s16(v_src16.val[1+yuvOrder]); - v_Cb = vget_high_s16(v_src16.val[2-yuvOrder]); - - int32x4_t v_b1 = vmulq_s32(v_c3, vsubl_s16(v_Cb, v_delta)); - v_b1 = vaddw_s16(vshrq_n_s32(vaddq_s32(v_b1, v_delta2), yuv_shift), v_Y); - int32x4_t v_g1 = vmlaq_s32(vmulq_s32(vsubl_s16(v_Cr, v_delta), v_c1), vsubl_s16(v_Cb, v_delta), v_c2); - v_g1 = vaddw_s16(vshrq_n_s32(vaddq_s32(v_g1, v_delta2), yuv_shift), v_Y); - int32x4_t v_r1 = vmulq_s32(v_c0, vsubl_s16(v_Cr, v_delta)); - v_r1 = vaddw_s16(vshrq_n_s32(vaddq_s32(v_r1, v_delta2), yuv_shift), v_Y); - - uint8x8_t v_b = vqmovun_s16(vcombine_s16(vmovn_s32(v_b0), vmovn_s32(v_b1))); - uint8x8_t v_g = vqmovun_s16(vcombine_s16(vmovn_s32(v_g0), vmovn_s32(v_g1))); - uint8x8_t v_r = vqmovun_s16(vcombine_s16(vmovn_s32(v_r0), vmovn_s32(v_r1))); - - if (dcn == 3) + v_uint8 y, cr, cb; + if(yuvOrder) { - uint8x8x3_t v_dst; - v_dst.val[bidx] = v_b; - v_dst.val[1] = v_g; - v_dst.val[bidx^2] = v_r; - vst3_u8(dst, v_dst); + v_load_deinterleave(src, y, cb, cr); } else { - uint8x8x4_t v_dst; - v_dst.val[bidx] = v_b; - v_dst.val[1] = v_g; - v_dst.val[bidx^2] = v_r; - v_dst.val[3] = v_alpha; - vst4_u8(dst, v_dst); + v_load_deinterleave(src, y, cr, cb); + } + + cr = v_sub_wrap(cr, vdelta); + cb = v_sub_wrap(cb, vdelta); + + v_int8 scr = v_reinterpret_as_s8(cr); + v_int8 scb = v_reinterpret_as_s8(cb); + + v_int16 scr0, scr1, scb0, scb1; + v_expand(scr, scr0, scr1); + v_expand(scb, scb0, scb1); + + v_int32 b00, b01, b10, b11; + v_int32 g00, g01, g10, g11; + v_int32 r00, r01, r10, r11; + + v_mul_expand(scb0, vc3, b00, b01); + v_mul_expand(scb1, vc3, b10, b11); + if(yuvOrder) + { + // if YUV then C3 > 2^15 + // so we fix the multiplication + v_int32 cb00, cb01, cb10, cb11; + v_expand(scb0, cb00, cb01); + v_expand(scb1, cb10, cb11); + b00 += cb00 << 15; b01 += cb01 << 15; + b10 += cb10 << 15; b11 += cb11 << 15; + } + + v_int32 t00, t01, t10, t11; + v_mul_expand(scb0, vc2, t00, t01); + v_mul_expand(scb1, vc2, t10, t11); + v_mul_expand(scr0, vc1, g00, g01); + v_mul_expand(scr1, vc1, g10, g11); + g00 += t00; g01 += t01; + g10 += t10; g11 += t11; + v_mul_expand(scr0, vc0, r00, r01); + v_mul_expand(scr1, vc0, r10, r11); + + b00 = (b00 + vdescale) >> shift; b01 = (b01 + vdescale) >> shift; + b10 = (b10 + vdescale) >> shift; b11 = (b11 + vdescale) >> shift; + g00 = (g00 + vdescale) >> shift; g01 = (g01 + vdescale) >> shift; + g10 = (g10 + vdescale) >> shift; g11 = (g11 + vdescale) >> shift; + r00 = (r00 + vdescale) >> shift; r01 = (r01 + vdescale) >> shift; + r10 = (r10 + vdescale) >> shift; r11 = (r11 + vdescale) >> shift; + + v_int16 b0, b1, g0, g1, r0, r1; + b0 = v_pack(b00, b01); b1 = v_pack(b10, b11); + g0 = v_pack(g00, g01); g1 = v_pack(g10, g11); + r0 = v_pack(r00, r01); r1 = v_pack(r10, r11); + + v_uint16 y0, y1; + v_expand(y, y0, y1); + v_int16 sy0, sy1; + sy0 = v_reinterpret_as_s16(y0); + sy1 = v_reinterpret_as_s16(y1); + + b0 = v_add_wrap(b0, sy0); b1 = v_add_wrap(b1, sy1); + g0 = v_add_wrap(g0, sy0); g1 = v_add_wrap(g1, sy1); + r0 = v_add_wrap(r0, sy0); r1 = v_add_wrap(r1, sy1); + + v_uint8 b, g, r; + b = v_pack_u(b0, b1); + g = v_pack_u(g0, g1); + r = v_pack_u(r0, r1); + + if(bidx) + swap(r, b); + + if(dcn == 3) + { + v_store_interleave(dst, b, g, r); + } + else + { + v_store_interleave(dst, b, g, r, valpha); } } + vx_cleanup(); +#endif - for ( ; i < n; i += 3, dst += dcn) + for ( ; i < n; i++, src += 3, dst += dcn) { - uchar Y = src[i]; - uchar Cr = src[i+1+yuvOrder]; - uchar Cb = src[i+2-yuvOrder]; + uchar Y = src[0]; + uchar Cr = src[1+yuvOrder]; + uchar Cb = src[2-yuvOrder]; - int b = Y + CV_DESCALE((Cb - delta)*C3, yuv_shift); - int g = Y + CV_DESCALE((Cb - delta)*C2 + (Cr - delta)*C1, yuv_shift); - int r = Y + CV_DESCALE((Cr - delta)*C0, yuv_shift); + int b = Y + CV_DESCALE((Cb - delta)*C3, shift); + int g = Y + CV_DESCALE((Cb - delta)*C2 + (Cr - delta)*C1, shift); + int r = Y + CV_DESCALE((Cr - delta)*C0, shift); dst[bidx] = saturate_cast(b); dst[1] = saturate_cast(g); @@ -1189,32 +831,24 @@ struct YCrCb2RGB_i int dstcn, blueIdx; bool isCrCb; int coeffs[4]; - - int32x4_t v_c0, v_c1, v_c2, v_c3, v_delta2; - int16x4_t v_delta; - uint8x8_t v_alpha; }; + template <> struct YCrCb2RGB_i { typedef ushort channel_type; + static const int shift = yuv_shift; YCrCb2RGB_i(int _dstcn, int _blueIdx, bool _isCrCb) : dstcn(_dstcn), blueIdx(_blueIdx), isCrCb(_isCrCb) { static const int coeffs_crb[] = { CR2RI, CR2GI, CB2GI, CB2BI}; static const int coeffs_yuv[] = { V2RI, V2GI, U2GI, U2BI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 4*sizeof(coeffs[0])); - - v_c0 = vdupq_n_s32(coeffs[0]); - v_c1 = vdupq_n_s32(coeffs[1]); - v_c2 = vdupq_n_s32(coeffs[2]); - v_c3 = vdupq_n_s32(coeffs[3]); - v_delta = vdupq_n_s32(ColorChannel::half()); - v_delta2 = vdupq_n_s32(1 << (yuv_shift - 1)); - v_alpha = vdupq_n_u16(ColorChannel::max()); - v_alpha2 = vget_low_u16(v_alpha); + for(int i = 0; i < 4; i++) + { + coeffs[i] = isCrCb ? coeffs_crb[i] : coeffs_yuv[i]; + } } void operator()(const ushort* src, ushort* dst, int n) const @@ -1223,107 +857,99 @@ struct YCrCb2RGB_i int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb const ushort delta = ColorChannel::half(), alpha = ColorChannel::max(); int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3]; - n *= 3; - for ( ; i <= n - 24; i += 24, dst += dcn * 8) +#if CV_SIMD + const int vsize = v_uint16::nlanes; + const int descaleShift = 1 << (shift-1); + v_uint16 valpha = vx_setall_u16(alpha); + v_uint16 vdelta = vx_setall_u16(delta); + v_int16 vc0 = vx_setall_s16((short)C0), vc1 = vx_setall_s16((short)C1), vc2 = vx_setall_s16((short)C2); + // if YUV then C3 > 2^15, need to subtract it + // to fit in short by short multiplication + v_int16 vc3 = vx_setall_s16(yuvOrder ? (short)(C3-(1 << 15)) : (short)C3); + v_int32 vdescale = vx_setall_s32(descaleShift); + for(; i <= n-vsize; + i += vsize, src += vsize*3, dst += vsize*dcn) { - uint16x8x3_t v_src = vld3q_u16(src + i); - - int32x4_t v_Y = vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(v_src.val[0]))), - v_Cr = vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(v_src.val[1+yuvOrder]))), - v_Cb = vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(v_src.val[2-yuvOrder]))); - - int32x4_t v_b0 = vmulq_s32(v_c3, vsubq_s32(v_Cb, v_delta)); - v_b0 = vaddq_s32(vshrq_n_s32(vaddq_s32(v_b0, v_delta2), yuv_shift), v_Y); - int32x4_t v_g0 = vmlaq_s32(vmulq_s32(vsubq_s32(v_Cr, v_delta), v_c1), vsubq_s32(v_Cb, v_delta), v_c2); - v_g0 = vaddq_s32(vshrq_n_s32(vaddq_s32(v_g0, v_delta2), yuv_shift), v_Y); - int32x4_t v_r0 = vmulq_s32(v_c0, vsubq_s32(v_Cr, v_delta)); - v_r0 = vaddq_s32(vshrq_n_s32(vaddq_s32(v_r0, v_delta2), yuv_shift), v_Y); - - v_Y = vreinterpretq_s32_u32(vmovl_u16(vget_high_u16(v_src.val[0]))), - v_Cr = vreinterpretq_s32_u32(vmovl_u16(vget_high_u16(v_src.val[1+yuvOrder]))), - v_Cb = vreinterpretq_s32_u32(vmovl_u16(vget_high_u16(v_src.val[2-yuvOrder]))); - - int32x4_t v_b1 = vmulq_s32(v_c3, vsubq_s32(v_Cb, v_delta)); - v_b1 = vaddq_s32(vshrq_n_s32(vaddq_s32(v_b1, v_delta2), yuv_shift), v_Y); - int32x4_t v_g1 = vmlaq_s32(vmulq_s32(vsubq_s32(v_Cr, v_delta), v_c1), vsubq_s32(v_Cb, v_delta), v_c2); - v_g1 = vaddq_s32(vshrq_n_s32(vaddq_s32(v_g1, v_delta2), yuv_shift), v_Y); - int32x4_t v_r1 = vmulq_s32(v_c0, vsubq_s32(v_Cr, v_delta)); - v_r1 = vaddq_s32(vshrq_n_s32(vaddq_s32(v_r1, v_delta2), yuv_shift), v_Y); - - uint16x8_t v_b = vcombine_u16(vqmovun_s32(v_b0), vqmovun_s32(v_b1)); - uint16x8_t v_g = vcombine_u16(vqmovun_s32(v_g0), vqmovun_s32(v_g1)); - uint16x8_t v_r = vcombine_u16(vqmovun_s32(v_r0), vqmovun_s32(v_r1)); - - if (dcn == 3) + v_uint16 y, cr, cb; + if(yuvOrder) { - uint16x8x3_t v_dst; - v_dst.val[bidx] = v_b; - v_dst.val[1] = v_g; - v_dst.val[bidx^2] = v_r; - vst3q_u16(dst, v_dst); + v_load_deinterleave(src, y, cb, cr); } else { - uint16x8x4_t v_dst; - v_dst.val[bidx] = v_b; - v_dst.val[1] = v_g; - v_dst.val[bidx^2] = v_r; - v_dst.val[3] = v_alpha; - vst4q_u16(dst, v_dst); + v_load_deinterleave(src, y, cr, cb); } - } - for ( ; i <= n - 12; i += 12, dst += dcn * 4) - { - uint16x4x3_t v_src = vld3_u16(src + i); + v_uint32 uy0, uy1; + v_expand(y, uy0, uy1); + v_int32 y0 = v_reinterpret_as_s32(uy0); + v_int32 y1 = v_reinterpret_as_s32(uy1); - int32x4_t v_Y = vreinterpretq_s32_u32(vmovl_u16(v_src.val[0])), - v_Cr = vreinterpretq_s32_u32(vmovl_u16(v_src.val[1+yuvOrder])), - v_Cb = vreinterpretq_s32_u32(vmovl_u16(v_src.val[2-yuvOrder])); + cr = v_sub_wrap(cr, vdelta); + cb = v_sub_wrap(cb, vdelta); - int32x4_t v_b = vmulq_s32(v_c3, vsubq_s32(v_Cb, v_delta)); - v_b = vaddq_s32(vshrq_n_s32(vaddq_s32(v_b, v_delta2), yuv_shift), v_Y); - int32x4_t v_g = vmlaq_s32(vmulq_s32(vsubq_s32(v_Cr, v_delta), v_c1), vsubq_s32(v_Cb, v_delta), v_c2); - v_g = vaddq_s32(vshrq_n_s32(vaddq_s32(v_g, v_delta2), yuv_shift), v_Y); - int32x4_t v_r = vmulq_s32(vsubq_s32(v_Cr, v_delta), v_c0); - v_r = vaddq_s32(vshrq_n_s32(vaddq_s32(v_r, v_delta2), yuv_shift), v_Y); + v_int32 b0, b1, g0, g1, r0, r1; - uint16x4_t v_bd = vqmovun_s32(v_b); - uint16x4_t v_gd = vqmovun_s32(v_g); - uint16x4_t v_rd = vqmovun_s32(v_r); - - if (dcn == 3) + v_int16 scb = v_reinterpret_as_s16(cb); + v_int16 scr = v_reinterpret_as_s16(cr); + v_mul_expand(scb, vc3, b0, b1); + if(yuvOrder) { - uint16x4x3_t v_dst; - v_dst.val[bidx] = v_bd; - v_dst.val[1] = v_gd; - v_dst.val[bidx^2] = v_rd; - vst3_u16(dst, v_dst); + // if YUV then C3 > 2^15 + // so we fix the multiplication + v_int32 cb0, cb1; + v_expand(scb, cb0, cb1); + b0 += cb0 << 15; + b1 += cb1 << 15; + } + v_int32 t0, t1; + v_mul_expand(scb, vc2, t0, t1); + v_mul_expand(scr, vc1, g0, g1); + g0 += t0; g1 += t1; + v_mul_expand(scr, vc0, r0, r1); + + // shifted term doesn't fit into 16 bits, addition is to be done in 32 bits + b0 = ((b0 + vdescale) >> shift) + y0; + b1 = ((b1 + vdescale) >> shift) + y1; + g0 = ((g0 + vdescale) >> shift) + y0; + g1 = ((g1 + vdescale) >> shift) + y1; + r0 = ((r0 + vdescale) >> shift) + y0; + r1 = ((r1 + vdescale) >> shift) + y1; + + // saturate and pack + v_uint16 b, g, r; + b = v_pack_u(b0, b1); + g = v_pack_u(g0, g1); + r = v_pack_u(r0, r1); + + if(bidx) + swap(r, b); + + if(dcn == 3) + { + v_store_interleave(dst, b, g, r); } else { - uint16x4x4_t v_dst; - v_dst.val[bidx] = v_bd; - v_dst.val[1] = v_gd; - v_dst.val[bidx^2] = v_rd; - v_dst.val[3] = v_alpha2; - vst4_u16(dst, v_dst); + v_store_interleave(dst, b, g, r, valpha); } } + vx_cleanup(); +#endif - for ( ; i < n; i += 3, dst += dcn) + for ( ; i < n; i++, src += 3, dst += dcn) { - ushort Y = src[i]; - ushort Cr = src[i+1+yuvOrder]; - ushort Cb = src[i+2-yuvOrder]; + ushort Y = src[0]; + ushort Cr = src[1+yuvOrder]; + ushort Cb = src[2-yuvOrder]; - int b = Y + CV_DESCALE((Cb - delta)*C3, yuv_shift); - int g = Y + CV_DESCALE((Cb - delta)*C2 + (Cr - delta)*C1, yuv_shift); - int r = Y + CV_DESCALE((Cr - delta)*C0, yuv_shift); + int b = Y + CV_DESCALE((Cb - delta)*C3, shift); + int g = Y + CV_DESCALE((Cb - delta)*C2 + (Cr - delta)*C1, shift); + int r = Y + CV_DESCALE((Cr - delta)*C0, shift); - dst[bidx] = saturate_cast(b); - dst[1] = saturate_cast(g); + dst[bidx] = saturate_cast(b); + dst[1] = saturate_cast(g); dst[bidx^2] = saturate_cast(r); if( dcn == 4 ) dst[3] = alpha; @@ -1332,348 +958,8 @@ struct YCrCb2RGB_i int dstcn, blueIdx; bool isCrCb; int coeffs[4]; - - int32x4_t v_c0, v_c1, v_c2, v_c3, v_delta2, v_delta; - uint16x8_t v_alpha; - uint16x4_t v_alpha2; }; -#elif CV_SSE2 - -template <> -struct YCrCb2RGB_i -{ - typedef uchar channel_type; - - YCrCb2RGB_i(int _dstcn, int _blueIdx, bool _isCrCb) - : dstcn(_dstcn), blueIdx(_blueIdx), isCrCb(_isCrCb) - { - static const int coeffs_crb[] = { CR2RI, CR2GI, CB2GI, CB2BI}; - static const int coeffs_yuv[] = { V2RI, V2GI, U2GI, U2BI }; - memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 4*sizeof(coeffs[0])); - - v_c0 = _mm_set1_epi16((short)coeffs[0]); - v_c1 = _mm_set1_epi16((short)coeffs[1]); - v_c2 = _mm_set1_epi16((short)coeffs[2]); - v_c3 = _mm_set1_epi16((short)coeffs[3]); - v_delta = _mm_set1_epi16(ColorChannel::half()); - v_delta2 = _mm_set1_epi32(1 << (yuv_shift - 1)); - v_zero = _mm_setzero_si128(); - - uchar alpha = ColorChannel::max(); - v_alpha = _mm_set1_epi8(*(char *)&alpha); - - // when using YUV, one of coefficients is bigger than std::numeric_limits::max(), - //which is not appropriate for SSE - useSSE = isCrCb; - haveSIMD = checkHardwareSupport(CV_CPU_SSE2); - } - -#if CV_SSE4_1 - // 16s x 8 - void process(__m128i* v_src, __m128i* v_shuffle, - __m128i* v_coeffs) const - { - __m128i v_ycrcb[3]; - v_ycrcb[0] = _mm_shuffle_epi8(v_src[0], v_shuffle[0]); - v_ycrcb[1] = _mm_shuffle_epi8(_mm_alignr_epi8(v_src[1], v_src[0], 8), v_shuffle[0]); - v_ycrcb[2] = _mm_shuffle_epi8(v_src[1], v_shuffle[0]); - - __m128i v_y[3]; - v_y[1] = _mm_shuffle_epi8(v_src[0], v_shuffle[1]); - v_y[2] = _mm_srli_si128(_mm_shuffle_epi8(_mm_alignr_epi8(v_src[1], v_src[0], 15), v_shuffle[1]), 1); - v_y[0] = _mm_unpacklo_epi8(v_y[1], v_zero); - v_y[1] = _mm_unpackhi_epi8(v_y[1], v_zero); - v_y[2] = _mm_unpacklo_epi8(v_y[2], v_zero); - - __m128i v_rgb[6]; - v_rgb[0] = _mm_unpacklo_epi8(v_ycrcb[0], v_zero); - v_rgb[1] = _mm_unpackhi_epi8(v_ycrcb[0], v_zero); - v_rgb[2] = _mm_unpacklo_epi8(v_ycrcb[1], v_zero); - v_rgb[3] = _mm_unpackhi_epi8(v_ycrcb[1], v_zero); - v_rgb[4] = _mm_unpacklo_epi8(v_ycrcb[2], v_zero); - v_rgb[5] = _mm_unpackhi_epi8(v_ycrcb[2], v_zero); - - v_rgb[0] = _mm_sub_epi16(v_rgb[0], v_delta); - v_rgb[1] = _mm_sub_epi16(v_rgb[1], v_delta); - v_rgb[2] = _mm_sub_epi16(v_rgb[2], v_delta); - v_rgb[3] = _mm_sub_epi16(v_rgb[3], v_delta); - v_rgb[4] = _mm_sub_epi16(v_rgb[4], v_delta); - v_rgb[5] = _mm_sub_epi16(v_rgb[5], v_delta); - - v_rgb[0] = _mm_madd_epi16(v_rgb[0], v_coeffs[0]); - v_rgb[1] = _mm_madd_epi16(v_rgb[1], v_coeffs[1]); - v_rgb[2] = _mm_madd_epi16(v_rgb[2], v_coeffs[2]); - v_rgb[3] = _mm_madd_epi16(v_rgb[3], v_coeffs[0]); - v_rgb[4] = _mm_madd_epi16(v_rgb[4], v_coeffs[1]); - v_rgb[5] = _mm_madd_epi16(v_rgb[5], v_coeffs[2]); - - v_rgb[0] = _mm_add_epi32(v_rgb[0], v_delta2); - v_rgb[1] = _mm_add_epi32(v_rgb[1], v_delta2); - v_rgb[2] = _mm_add_epi32(v_rgb[2], v_delta2); - v_rgb[3] = _mm_add_epi32(v_rgb[3], v_delta2); - v_rgb[4] = _mm_add_epi32(v_rgb[4], v_delta2); - v_rgb[5] = _mm_add_epi32(v_rgb[5], v_delta2); - - v_rgb[0] = _mm_srai_epi32(v_rgb[0], yuv_shift); - v_rgb[1] = _mm_srai_epi32(v_rgb[1], yuv_shift); - v_rgb[2] = _mm_srai_epi32(v_rgb[2], yuv_shift); - v_rgb[3] = _mm_srai_epi32(v_rgb[3], yuv_shift); - v_rgb[4] = _mm_srai_epi32(v_rgb[4], yuv_shift); - v_rgb[5] = _mm_srai_epi32(v_rgb[5], yuv_shift); - - v_rgb[0] = _mm_packs_epi32(v_rgb[0], v_rgb[1]); - v_rgb[2] = _mm_packs_epi32(v_rgb[2], v_rgb[3]); - v_rgb[4] = _mm_packs_epi32(v_rgb[4], v_rgb[5]); - - v_rgb[0] = _mm_add_epi16(v_rgb[0], v_y[0]); - v_rgb[2] = _mm_add_epi16(v_rgb[2], v_y[1]); - v_rgb[4] = _mm_add_epi16(v_rgb[4], v_y[2]); - - v_src[0] = _mm_packus_epi16(v_rgb[0], v_rgb[2]); - v_src[1] = _mm_packus_epi16(v_rgb[4], v_rgb[4]); - } -#endif // CV_SSE4_1 - - // 16s x 8 - void process(__m128i v_y, __m128i v_cr, __m128i v_cb, - __m128i & v_r, __m128i & v_g, __m128i & v_b) const - { - v_cr = _mm_sub_epi16(v_cr, v_delta); - v_cb = _mm_sub_epi16(v_cb, v_delta); - - __m128i v_y_p = _mm_unpacklo_epi16(v_y, v_zero); - - __m128i v_mullo_3 = _mm_mullo_epi16(v_cb, v_c3); - __m128i v_mullo_2 = _mm_mullo_epi16(v_cb, v_c2); - __m128i v_mullo_1 = _mm_mullo_epi16(v_cr, v_c1); - __m128i v_mullo_0 = _mm_mullo_epi16(v_cr, v_c0); - - __m128i v_mulhi_3 = _mm_mulhi_epi16(v_cb, v_c3); - __m128i v_mulhi_2 = _mm_mulhi_epi16(v_cb, v_c2); - __m128i v_mulhi_1 = _mm_mulhi_epi16(v_cr, v_c1); - __m128i v_mulhi_0 = _mm_mulhi_epi16(v_cr, v_c0); - - __m128i v_b0 = _mm_srai_epi32(_mm_add_epi32(_mm_unpacklo_epi16(v_mullo_3, v_mulhi_3), v_delta2), yuv_shift); - __m128i v_g0 = _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(_mm_unpacklo_epi16(v_mullo_2, v_mulhi_2), - _mm_unpacklo_epi16(v_mullo_1, v_mulhi_1)), v_delta2), - yuv_shift); - __m128i v_r0 = _mm_srai_epi32(_mm_add_epi32(_mm_unpacklo_epi16(v_mullo_0, v_mulhi_0), v_delta2), yuv_shift); - - v_r0 = _mm_add_epi32(v_r0, v_y_p); - v_g0 = _mm_add_epi32(v_g0, v_y_p); - v_b0 = _mm_add_epi32(v_b0, v_y_p); - - v_y_p = _mm_unpackhi_epi16(v_y, v_zero); - - __m128i v_b1 = _mm_srai_epi32(_mm_add_epi32(_mm_unpackhi_epi16(v_mullo_3, v_mulhi_3), v_delta2), yuv_shift); - __m128i v_g1 = _mm_srai_epi32(_mm_add_epi32(_mm_add_epi32(_mm_unpackhi_epi16(v_mullo_2, v_mulhi_2), - _mm_unpackhi_epi16(v_mullo_1, v_mulhi_1)), v_delta2), - yuv_shift); - __m128i v_r1 = _mm_srai_epi32(_mm_add_epi32(_mm_unpackhi_epi16(v_mullo_0, v_mulhi_0), v_delta2), yuv_shift); - - v_r1 = _mm_add_epi32(v_r1, v_y_p); - v_g1 = _mm_add_epi32(v_g1, v_y_p); - v_b1 = _mm_add_epi32(v_b1, v_y_p); - - v_r = _mm_packs_epi32(v_r0, v_r1); - v_g = _mm_packs_epi32(v_g0, v_g1); - v_b = _mm_packs_epi32(v_b0, v_b1); - } - - void operator()(const uchar* src, uchar* dst, int n) const - { - int dcn = dstcn, bidx = blueIdx, i = 0; - int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb - const uchar delta = ColorChannel::half(), alpha = ColorChannel::max(); - int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3]; - n *= 3; - -#if CV_SSE4_1 - if (checkHardwareSupport(CV_CPU_SSE4_1) && useSSE) - { - __m128i v_shuffle[2]; - v_shuffle[0] = _mm_set_epi8(0x8, 0x7, 0x7, 0x6, 0x6, 0x5, 0x5, 0x4, 0x4, 0x3, 0x3, 0x2, 0x2, 0x1, 0x1, 0x0); - v_shuffle[1] = _mm_set_epi8(0xf, 0xc, 0xc, 0xc, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0); - __m128i v_coeffs[3]; - v_coeffs[0] = _mm_set_epi16((short)C0, 0, 0, (short)C3, (short)C2, (short)C1, (short)C0, 0); - v_coeffs[1] = _mm_set_epi16((short)C2, (short)C1, (short)C0, 0, 0, (short)C3, (short)C2, (short)C1); - v_coeffs[2] = _mm_set_epi16(0, (short)C3, (short)C2, (short)C1, (short)C0, 0, 0, (short)C3); - - if (dcn == 3) - { - if (bidx == 0) - { - __m128i v_shuffle_dst = _mm_set_epi8(0xf, 0xc, 0xd, 0xe, 0x9, 0xa, 0xb, 0x6, 0x7, 0x8, 0x3, 0x4, 0x5, 0x0, 0x1, 0x2); - for ( ; i <= n - 24; i += 24, dst += dcn * 8) - { - __m128i v_src[2]; - v_src[0] = _mm_loadu_si128((__m128i const *)(src + i)); - v_src[1] = _mm_loadl_epi64((__m128i const *)(src + i + 16)); - - process(v_src, v_shuffle, v_coeffs); - - __m128i v_dst[2]; - v_dst[0] = _mm_shuffle_epi8(v_src[0], v_shuffle_dst); - v_dst[1] = _mm_shuffle_epi8(_mm_alignr_epi8(v_src[1], v_src[0], 15), v_shuffle_dst); - - _mm_storeu_si128((__m128i *)(dst), _mm_alignr_epi8(v_dst[1], _mm_slli_si128(v_dst[0], 1), 1)); - _mm_storel_epi64((__m128i *)(dst + 16), _mm_srli_si128(v_dst[1], 1)); - } - } - else - { - for ( ; i <= n - 24; i += 24, dst += dcn * 8) - { - __m128i v_src[2]; - v_src[0] = _mm_loadu_si128((__m128i const *)(src + i)); - v_src[1] = _mm_loadl_epi64((__m128i const *)(src + i + 16)); - - process(v_src, v_shuffle, v_coeffs); - - _mm_storeu_si128((__m128i *)(dst), v_src[0]); - _mm_storel_epi64((__m128i *)(dst + 16), v_src[1]); - } - } - } - else - { - if (bidx == 0) - { - __m128i v_shuffle_dst = _mm_set_epi8(0x0, 0xa, 0xb, 0xc, 0x0, 0x7, 0x8, 0x9, 0x0, 0x4, 0x5, 0x6, 0x0, 0x1, 0x2, 0x3); - - for ( ; i <= n - 24; i += 24, dst += dcn * 8) - { - __m128i v_src[2]; - v_src[0] = _mm_loadu_si128((__m128i const *)(src + i)); - v_src[1] = _mm_loadl_epi64((__m128i const *)(src + i + 16)); - - process(v_src, v_shuffle, v_coeffs); - - _mm_storeu_si128((__m128i *)(dst), _mm_shuffle_epi8(_mm_alignr_epi8(v_src[0], v_alpha, 15), v_shuffle_dst)); - _mm_storeu_si128((__m128i *)(dst + 16), _mm_shuffle_epi8(_mm_alignr_epi8(_mm_alignr_epi8(v_src[1], v_src[0], 12), v_alpha, 15), v_shuffle_dst)); - } - } - else - { - __m128i v_shuffle_dst = _mm_set_epi8(0x0, 0xc, 0xb, 0xa, 0x0, 0x9, 0x8, 0x7, 0x0, 0x6, 0x5, 0x4, 0x0, 0x3, 0x2, 0x1); - - for ( ; i <= n - 24; i += 24, dst += dcn * 8) - { - __m128i v_src[2]; - v_src[0] = _mm_loadu_si128((__m128i const *)(src + i)); - v_src[1] = _mm_loadl_epi64((__m128i const *)(src + i + 16)); - - process(v_src, v_shuffle, v_coeffs); - - _mm_storeu_si128((__m128i *)(dst), _mm_shuffle_epi8(_mm_alignr_epi8(v_src[0], v_alpha, 15), v_shuffle_dst)); - _mm_storeu_si128((__m128i *)(dst + 16), _mm_shuffle_epi8(_mm_alignr_epi8(_mm_alignr_epi8(v_src[1], v_src[0], 12), v_alpha, 15), v_shuffle_dst)); - } - } - } - } - else -#endif // CV_SSE4_1 - if (haveSIMD && useSSE) - { - for ( ; i <= n - 96; i += 96, dst += dcn * 32) - { - __m128i v_y0 = _mm_loadu_si128((__m128i const *)(src + i)); - __m128i v_y1 = _mm_loadu_si128((__m128i const *)(src + i + 16)); - __m128i v_cr0 = _mm_loadu_si128((__m128i const *)(src + i + 32)); - __m128i v_cr1 = _mm_loadu_si128((__m128i const *)(src + i + 48)); - __m128i v_cb0 = _mm_loadu_si128((__m128i const *)(src + i + 64)); - __m128i v_cb1 = _mm_loadu_si128((__m128i const *)(src + i + 80)); - - _mm_deinterleave_epi8(v_y0, v_y1, v_cr0, v_cr1, v_cb0, v_cb1); - - __m128i v_r_0 = v_zero, v_g_0 = v_zero, v_b_0 = v_zero; - process(_mm_unpacklo_epi8(v_y0, v_zero), - _mm_unpacklo_epi8(v_cr0, v_zero), - _mm_unpacklo_epi8(v_cb0, v_zero), - v_r_0, v_g_0, v_b_0); - - __m128i v_r_1 = v_zero, v_g_1 = v_zero, v_b_1 = v_zero; - process(_mm_unpackhi_epi8(v_y0, v_zero), - _mm_unpackhi_epi8(v_cr0, v_zero), - _mm_unpackhi_epi8(v_cb0, v_zero), - v_r_1, v_g_1, v_b_1); - - __m128i v_r0 = _mm_packus_epi16(v_r_0, v_r_1); - __m128i v_g0 = _mm_packus_epi16(v_g_0, v_g_1); - __m128i v_b0 = _mm_packus_epi16(v_b_0, v_b_1); - - process(_mm_unpacklo_epi8(v_y1, v_zero), - _mm_unpacklo_epi8(v_cr1, v_zero), - _mm_unpacklo_epi8(v_cb1, v_zero), - v_r_0, v_g_0, v_b_0); - - process(_mm_unpackhi_epi8(v_y1, v_zero), - _mm_unpackhi_epi8(v_cr1, v_zero), - _mm_unpackhi_epi8(v_cb1, v_zero), - v_r_1, v_g_1, v_b_1); - - __m128i v_r1 = _mm_packus_epi16(v_r_0, v_r_1); - __m128i v_g1 = _mm_packus_epi16(v_g_0, v_g_1); - __m128i v_b1 = _mm_packus_epi16(v_b_0, v_b_1); - - if (bidx == 0) - { - std::swap(v_r0, v_b0); - std::swap(v_r1, v_b1); - } - - __m128i v_a0 = v_alpha, v_a1 = v_alpha; - - if (dcn == 3) - _mm_interleave_epi8(v_r0, v_r1, v_g0, v_g1, v_b0, v_b1); - else - _mm_interleave_epi8(v_r0, v_r1, v_g0, v_g1, - v_b0, v_b1, v_a0, v_a1); - - _mm_storeu_si128((__m128i *)(dst), v_r0); - _mm_storeu_si128((__m128i *)(dst + 16), v_r1); - _mm_storeu_si128((__m128i *)(dst + 32), v_g0); - _mm_storeu_si128((__m128i *)(dst + 48), v_g1); - _mm_storeu_si128((__m128i *)(dst + 64), v_b0); - _mm_storeu_si128((__m128i *)(dst + 80), v_b1); - - if (dcn == 4) - { - _mm_storeu_si128((__m128i *)(dst + 96), v_a0); - _mm_storeu_si128((__m128i *)(dst + 112), v_a1); - } - } - } - - for ( ; i < n; i += 3, dst += dcn) - { - uchar Y = src[i]; - uchar Cr = src[i+1+yuvOrder]; - uchar Cb = src[i+2-yuvOrder]; - - int b = Y + CV_DESCALE((Cb - delta)*C3, yuv_shift); - int g = Y + CV_DESCALE((Cb - delta)*C2 + (Cr - delta)*C1, yuv_shift); - int r = Y + CV_DESCALE((Cr - delta)*C0, yuv_shift); - - dst[bidx] = saturate_cast(b); - dst[1] = saturate_cast(g); - dst[bidx^2] = saturate_cast(r); - if( dcn == 4 ) - dst[3] = alpha; - } - } - int dstcn, blueIdx; - int coeffs[4]; - bool isCrCb; - bool useSSE, haveSIMD; - - __m128i v_c0, v_c1, v_c2, v_c3, v_delta2; - __m128i v_delta, v_alpha, v_zero; -}; - -#endif // CV_SSE2 - ///////////////////////////////////// YUV420 -> RGB ///////////////////////////////////// @@ -1694,8 +980,59 @@ const int ITUR_BT_601_CBU = 460324; const int ITUR_BT_601_CGV = -385875; const int ITUR_BT_601_CBV = -74448; -template -struct YUV420sp2RGB888Invoker : ParallelLoopBody +//R = 1.164(Y - 16) + 1.596(V - 128) +//G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) +//B = 1.164(Y - 16) + 2.018(U - 128) + +//R = (1220542(Y - 16) + 1673527(V - 128) + (1 << 19)) >> 20 +//G = (1220542(Y - 16) - 852492(V - 128) - 409993(U - 128) + (1 << 19)) >> 20 +//B = (1220542(Y - 16) + 2116026(U - 128) + (1 << 19)) >> 20 + +template +static inline void cvtYuv42xxp2RGB8(int u, int v, int vy01, int vy11, int vy02, int vy12, + uchar* row1, uchar* row2) +{ + u = u - 128; + v = v - 128; + + int ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * v; + int guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * v + ITUR_BT_601_CUG * u; + int buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * u; + + int y00 = std::max(0, vy01 - 16) * ITUR_BT_601_CY; + row1[2-bIdx] = saturate_cast((y00 + ruv) >> ITUR_BT_601_SHIFT); + row1[1] = saturate_cast((y00 + guv) >> ITUR_BT_601_SHIFT); + row1[bIdx] = saturate_cast((y00 + buv) >> ITUR_BT_601_SHIFT); + if(dcn == 4) + row1[3] = uchar(0xff); + + int y01 = std::max(0, vy11 - 16) * ITUR_BT_601_CY; + row1[dcn+2-bIdx] = saturate_cast((y01 + ruv) >> ITUR_BT_601_SHIFT); + row1[dcn+1] = saturate_cast((y01 + guv) >> ITUR_BT_601_SHIFT); + row1[dcn+0+bIdx] = saturate_cast((y01 + buv) >> ITUR_BT_601_SHIFT); + if(dcn == 4) + row1[7] = uchar(0xff); + + if(is420) + { + int y10 = std::max(0, vy02 - 16) * ITUR_BT_601_CY; + row2[2-bIdx] = saturate_cast((y10 + ruv) >> ITUR_BT_601_SHIFT); + row2[1] = saturate_cast((y10 + guv) >> ITUR_BT_601_SHIFT); + row2[bIdx] = saturate_cast((y10 + buv) >> ITUR_BT_601_SHIFT); + if(dcn == 4) + row2[3] = uchar(0xff); + + int y11 = std::max(0, vy12 - 16) * ITUR_BT_601_CY; + row2[dcn+2-bIdx] = saturate_cast((y11 + ruv) >> ITUR_BT_601_SHIFT); + row2[dcn+1] = saturate_cast((y11 + guv) >> ITUR_BT_601_SHIFT); + row2[dcn+0+bIdx] = saturate_cast((y11 + buv) >> ITUR_BT_601_SHIFT); + if(dcn == 4) + row2[7] = uchar(0xff); + } +} + +template +struct YUV420sp2RGB8Invoker : ParallelLoopBody { uchar * dst_data; size_t dst_step; @@ -1703,21 +1040,13 @@ struct YUV420sp2RGB888Invoker : ParallelLoopBody const uchar* my1, *muv; size_t stride; - YUV420sp2RGB888Invoker(uchar * _dst_data, size_t _dst_step, int _dst_width, size_t _stride, const uchar* _y1, const uchar* _uv) + YUV420sp2RGB8Invoker(uchar * _dst_data, size_t _dst_step, int _dst_width, size_t _stride, const uchar* _y1, const uchar* _uv) : dst_data(_dst_data), dst_step(_dst_step), width(_dst_width), my1(_y1), muv(_uv), stride(_stride) {} void operator()(const Range& range) const CV_OVERRIDE { - int rangeBegin = range.start * 2; - int rangeEnd = range.end * 2; - - //R = 1.164(Y - 16) + 1.596(V - 128) - //G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) - //B = 1.164(Y - 16) + 2.018(U - 128) - - //R = (1220542(Y - 16) + 1673527(V - 128) + (1 << 19)) >> 20 - //G = (1220542(Y - 16) - 852492(V - 128) - 409993(U - 128) + (1 << 19)) >> 20 - //B = (1220542(Y - 16) + 2116026(U - 128) + (1 << 19)) >> 20 + const int rangeBegin = range.start * 2; + const int rangeEnd = range.end * 2; const uchar* y1 = my1 + rangeBegin * stride, *uv = muv + rangeBegin * stride / 2; @@ -1727,111 +1056,24 @@ struct YUV420sp2RGB888Invoker : ParallelLoopBody uchar* row2 = dst_data + dst_step * (j + 1); const uchar* y2 = y1 + stride; - for (int i = 0; i < width; i += 2, row1 += 6, row2 += 6) + for (int i = 0; i < width; i += 2, row1 += dcn*2, row2 += dcn*2) { - int u = int(uv[i + 0 + uIdx]) - 128; - int v = int(uv[i + 1 - uIdx]) - 128; + int u = int(uv[i + 0 + uIdx]); + int v = int(uv[i + 1 - uIdx]); - int ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * v; - int guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * v + ITUR_BT_601_CUG * u; - int buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * u; + int vy01 = int(y1[i]); + int vy11 = int(y1[i + 1]); + int vy02 = int(y2[i]); + int vy12 = int(y2[i + 1]); - int y00 = std::max(0, int(y1[i]) - 16) * ITUR_BT_601_CY; - row1[2-bIdx] = saturate_cast((y00 + ruv) >> ITUR_BT_601_SHIFT); - row1[1] = saturate_cast((y00 + guv) >> ITUR_BT_601_SHIFT); - row1[bIdx] = saturate_cast((y00 + buv) >> ITUR_BT_601_SHIFT); - - int y01 = std::max(0, int(y1[i + 1]) - 16) * ITUR_BT_601_CY; - row1[5-bIdx] = saturate_cast((y01 + ruv) >> ITUR_BT_601_SHIFT); - row1[4] = saturate_cast((y01 + guv) >> ITUR_BT_601_SHIFT); - row1[3+bIdx] = saturate_cast((y01 + buv) >> ITUR_BT_601_SHIFT); - - int y10 = std::max(0, int(y2[i]) - 16) * ITUR_BT_601_CY; - row2[2-bIdx] = saturate_cast((y10 + ruv) >> ITUR_BT_601_SHIFT); - row2[1] = saturate_cast((y10 + guv) >> ITUR_BT_601_SHIFT); - row2[bIdx] = saturate_cast((y10 + buv) >> ITUR_BT_601_SHIFT); - - int y11 = std::max(0, int(y2[i + 1]) - 16) * ITUR_BT_601_CY; - row2[5-bIdx] = saturate_cast((y11 + ruv) >> ITUR_BT_601_SHIFT); - row2[4] = saturate_cast((y11 + guv) >> ITUR_BT_601_SHIFT); - row2[3+bIdx] = saturate_cast((y11 + buv) >> ITUR_BT_601_SHIFT); + cvtYuv42xxp2RGB8(u, v, vy01, vy11, vy02, vy12, row1, row2); } } } }; -template -struct YUV420sp2RGBA8888Invoker : ParallelLoopBody -{ - uchar * dst_data; - size_t dst_step; - int width; - const uchar* my1, *muv; - size_t stride; - - YUV420sp2RGBA8888Invoker(uchar * _dst_data, size_t _dst_step, int _dst_width, size_t _stride, const uchar* _y1, const uchar* _uv) - : dst_data(_dst_data), dst_step(_dst_step), width(_dst_width), my1(_y1), muv(_uv), stride(_stride) {} - - void operator()(const Range& range) const CV_OVERRIDE - { - int rangeBegin = range.start * 2; - int rangeEnd = range.end * 2; - - //R = 1.164(Y - 16) + 1.596(V - 128) - //G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) - //B = 1.164(Y - 16) + 2.018(U - 128) - - //R = (1220542(Y - 16) + 1673527(V - 128) + (1 << 19)) >> 20 - //G = (1220542(Y - 16) - 852492(V - 128) - 409993(U - 128) + (1 << 19)) >> 20 - //B = (1220542(Y - 16) + 2116026(U - 128) + (1 << 19)) >> 20 - - const uchar* y1 = my1 + rangeBegin * stride, *uv = muv + rangeBegin * stride / 2; - - for (int j = rangeBegin; j < rangeEnd; j += 2, y1 += stride * 2, uv += stride) - { - uchar* row1 = dst_data + dst_step * j; - uchar* row2 = dst_data + dst_step * (j + 1); - const uchar* y2 = y1 + stride; - - for (int i = 0; i < width; i += 2, row1 += 8, row2 += 8) - { - int u = int(uv[i + 0 + uIdx]) - 128; - int v = int(uv[i + 1 - uIdx]) - 128; - - int ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * v; - int guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * v + ITUR_BT_601_CUG * u; - int buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * u; - - int y00 = std::max(0, int(y1[i]) - 16) * ITUR_BT_601_CY; - row1[2-bIdx] = saturate_cast((y00 + ruv) >> ITUR_BT_601_SHIFT); - row1[1] = saturate_cast((y00 + guv) >> ITUR_BT_601_SHIFT); - row1[bIdx] = saturate_cast((y00 + buv) >> ITUR_BT_601_SHIFT); - row1[3] = uchar(0xff); - - int y01 = std::max(0, int(y1[i + 1]) - 16) * ITUR_BT_601_CY; - row1[6-bIdx] = saturate_cast((y01 + ruv) >> ITUR_BT_601_SHIFT); - row1[5] = saturate_cast((y01 + guv) >> ITUR_BT_601_SHIFT); - row1[4+bIdx] = saturate_cast((y01 + buv) >> ITUR_BT_601_SHIFT); - row1[7] = uchar(0xff); - - int y10 = std::max(0, int(y2[i]) - 16) * ITUR_BT_601_CY; - row2[2-bIdx] = saturate_cast((y10 + ruv) >> ITUR_BT_601_SHIFT); - row2[1] = saturate_cast((y10 + guv) >> ITUR_BT_601_SHIFT); - row2[bIdx] = saturate_cast((y10 + buv) >> ITUR_BT_601_SHIFT); - row2[3] = uchar(0xff); - - int y11 = std::max(0, int(y2[i + 1]) - 16) * ITUR_BT_601_CY; - row2[6-bIdx] = saturate_cast((y11 + ruv) >> ITUR_BT_601_SHIFT); - row2[5] = saturate_cast((y11 + guv) >> ITUR_BT_601_SHIFT); - row2[4+bIdx] = saturate_cast((y11 + buv) >> ITUR_BT_601_SHIFT); - row2[7] = uchar(0xff); - } - } - } -}; - -template -struct YUV420p2RGB888Invoker : ParallelLoopBody +template +struct YUV420p2RGB8Invoker : ParallelLoopBody { uchar * dst_data; size_t dst_step; @@ -1840,7 +1082,7 @@ struct YUV420p2RGB888Invoker : ParallelLoopBody size_t stride; int ustepIdx, vstepIdx; - YUV420p2RGB888Invoker(uchar * _dst_data, size_t _dst_step, int _dst_width, size_t _stride, const uchar* _y1, const uchar* _u, const uchar* _v, int _ustepIdx, int _vstepIdx) + YUV420p2RGB8Invoker(uchar * _dst_data, size_t _dst_step, int _dst_width, size_t _stride, const uchar* _y1, const uchar* _u, const uchar* _v, int _ustepIdx, int _vstepIdx) : dst_data(_dst_data), dst_step(_dst_step), width(_dst_width), my1(_y1), mu(_u), mv(_v), stride(_stride), ustepIdx(_ustepIdx), vstepIdx(_vstepIdx) {} void operator()(const Range& range) const CV_OVERRIDE @@ -1867,149 +1109,39 @@ struct YUV420p2RGB888Invoker : ParallelLoopBody uchar* row2 = dst_data + dst_step * (j + 1); const uchar* y2 = y1 + stride; - for (int i = 0; i < width / 2; i += 1, row1 += 6, row2 += 6) + for (int i = 0; i < width / 2; i += 1, row1 += dcn*2, row2 += dcn*2) { - int u = int(u1[i]) - 128; - int v = int(v1[i]) - 128; + int u = int(u1[i]); + int v = int(v1[i]); - int ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * v; - int guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * v + ITUR_BT_601_CUG * u; - int buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * u; + int vy01 = int(y1[2 * i]); + int vy11 = int(y1[2 * i + 1]); + int vy02 = int(y2[2 * i]); + int vy12 = int(y2[2 * i + 1]); - int y00 = std::max(0, int(y1[2 * i]) - 16) * ITUR_BT_601_CY; - row1[2-bIdx] = saturate_cast((y00 + ruv) >> ITUR_BT_601_SHIFT); - row1[1] = saturate_cast((y00 + guv) >> ITUR_BT_601_SHIFT); - row1[bIdx] = saturate_cast((y00 + buv) >> ITUR_BT_601_SHIFT); - - int y01 = std::max(0, int(y1[2 * i + 1]) - 16) * ITUR_BT_601_CY; - row1[5-bIdx] = saturate_cast((y01 + ruv) >> ITUR_BT_601_SHIFT); - row1[4] = saturate_cast((y01 + guv) >> ITUR_BT_601_SHIFT); - row1[3+bIdx] = saturate_cast((y01 + buv) >> ITUR_BT_601_SHIFT); - - int y10 = std::max(0, int(y2[2 * i]) - 16) * ITUR_BT_601_CY; - row2[2-bIdx] = saturate_cast((y10 + ruv) >> ITUR_BT_601_SHIFT); - row2[1] = saturate_cast((y10 + guv) >> ITUR_BT_601_SHIFT); - row2[bIdx] = saturate_cast((y10 + buv) >> ITUR_BT_601_SHIFT); - - int y11 = std::max(0, int(y2[2 * i + 1]) - 16) * ITUR_BT_601_CY; - row2[5-bIdx] = saturate_cast((y11 + ruv) >> ITUR_BT_601_SHIFT); - row2[4] = saturate_cast((y11 + guv) >> ITUR_BT_601_SHIFT); - row2[3+bIdx] = saturate_cast((y11 + buv) >> ITUR_BT_601_SHIFT); + cvtYuv42xxp2RGB8(u, v, vy01, vy11, vy02, vy12, row1, row2); } } } }; -template -struct YUV420p2RGBA8888Invoker : ParallelLoopBody -{ - uchar * dst_data; - size_t dst_step; - int width; - const uchar* my1, *mu, *mv; - size_t stride; - int ustepIdx, vstepIdx; - - YUV420p2RGBA8888Invoker(uchar * _dst_data, size_t _dst_step, int _dst_width, size_t _stride, const uchar* _y1, const uchar* _u, const uchar* _v, int _ustepIdx, int _vstepIdx) - : dst_data(_dst_data), dst_step(_dst_step), width(_dst_width), my1(_y1), mu(_u), mv(_v), stride(_stride), ustepIdx(_ustepIdx), vstepIdx(_vstepIdx) {} - - void operator()(const Range& range) const CV_OVERRIDE - { - int rangeBegin = range.start * 2; - int rangeEnd = range.end * 2; - - int uvsteps[2] = {width/2, static_cast(stride) - width/2}; - int usIdx = ustepIdx, vsIdx = vstepIdx; - - const uchar* y1 = my1 + rangeBegin * stride; - const uchar* u1 = mu + (range.start / 2) * stride; - const uchar* v1 = mv + (range.start / 2) * stride; - - if(range.start % 2 == 1) - { - u1 += uvsteps[(usIdx++) & 1]; - v1 += uvsteps[(vsIdx++) & 1]; - } - - for (int j = rangeBegin; j < rangeEnd; j += 2, y1 += stride * 2, u1 += uvsteps[(usIdx++) & 1], v1 += uvsteps[(vsIdx++) & 1]) - { - uchar* row1 = dst_data + dst_step * j; - uchar* row2 = dst_data + dst_step * (j + 1); - const uchar* y2 = y1 + stride; - - for (int i = 0; i < width / 2; i += 1, row1 += 8, row2 += 8) - { - int u = int(u1[i]) - 128; - int v = int(v1[i]) - 128; - - int ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * v; - int guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * v + ITUR_BT_601_CUG * u; - int buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * u; - - int y00 = std::max(0, int(y1[2 * i]) - 16) * ITUR_BT_601_CY; - row1[2-bIdx] = saturate_cast((y00 + ruv) >> ITUR_BT_601_SHIFT); - row1[1] = saturate_cast((y00 + guv) >> ITUR_BT_601_SHIFT); - row1[bIdx] = saturate_cast((y00 + buv) >> ITUR_BT_601_SHIFT); - row1[3] = uchar(0xff); - - int y01 = std::max(0, int(y1[2 * i + 1]) - 16) * ITUR_BT_601_CY; - row1[6-bIdx] = saturate_cast((y01 + ruv) >> ITUR_BT_601_SHIFT); - row1[5] = saturate_cast((y01 + guv) >> ITUR_BT_601_SHIFT); - row1[4+bIdx] = saturate_cast((y01 + buv) >> ITUR_BT_601_SHIFT); - row1[7] = uchar(0xff); - - int y10 = std::max(0, int(y2[2 * i]) - 16) * ITUR_BT_601_CY; - row2[2-bIdx] = saturate_cast((y10 + ruv) >> ITUR_BT_601_SHIFT); - row2[1] = saturate_cast((y10 + guv) >> ITUR_BT_601_SHIFT); - row2[bIdx] = saturate_cast((y10 + buv) >> ITUR_BT_601_SHIFT); - row2[3] = uchar(0xff); - - int y11 = std::max(0, int(y2[2 * i + 1]) - 16) * ITUR_BT_601_CY; - row2[6-bIdx] = saturate_cast((y11 + ruv) >> ITUR_BT_601_SHIFT); - row2[5] = saturate_cast((y11 + guv) >> ITUR_BT_601_SHIFT); - row2[4+bIdx] = saturate_cast((y11 + buv) >> ITUR_BT_601_SHIFT); - row2[7] = uchar(0xff); - } - } - } -}; #define MIN_SIZE_FOR_PARALLEL_YUV420_CONVERSION (320*240) -template +template inline void cvtYUV420sp2RGB(uchar * dst_data, size_t dst_step, int dst_width, int dst_height, size_t _stride, const uchar* _y1, const uchar* _uv) { - YUV420sp2RGB888Invoker converter(dst_data, dst_step, dst_width, _stride, _y1, _uv); + YUV420sp2RGB8Invoker converter(dst_data, dst_step, dst_width, _stride, _y1, _uv); if (dst_width * dst_height >= MIN_SIZE_FOR_PARALLEL_YUV420_CONVERSION) parallel_for_(Range(0, dst_height/2), converter); else converter(Range(0, dst_height/2)); } -template -inline void cvtYUV420sp2RGBA(uchar * dst_data, size_t dst_step, int dst_width, int dst_height, size_t _stride, const uchar* _y1, const uchar* _uv) -{ - YUV420sp2RGBA8888Invoker converter(dst_data, dst_step, dst_width, _stride, _y1, _uv); - if (dst_width * dst_height >= MIN_SIZE_FOR_PARALLEL_YUV420_CONVERSION) - parallel_for_(Range(0, dst_height/2), converter); - else - converter(Range(0, dst_height/2)); -} - -template +template inline void cvtYUV420p2RGB(uchar * dst_data, size_t dst_step, int dst_width, int dst_height, size_t _stride, const uchar* _y1, const uchar* _u, const uchar* _v, int ustepIdx, int vstepIdx) { - YUV420p2RGB888Invoker converter(dst_data, dst_step, dst_width, _stride, _y1, _u, _v, ustepIdx, vstepIdx); - if (dst_width * dst_height >= MIN_SIZE_FOR_PARALLEL_YUV420_CONVERSION) - parallel_for_(Range(0, dst_height/2), converter); - else - converter(Range(0, dst_height/2)); -} - -template -inline void cvtYUV420p2RGBA(uchar * dst_data, size_t dst_step, int dst_width, int dst_height, size_t _stride, const uchar* _y1, const uchar* _u, const uchar* _v, int ustepIdx, int vstepIdx) -{ - YUV420p2RGBA8888Invoker converter(dst_data, dst_step, dst_width, _stride, _y1, _u, _v, ustepIdx, vstepIdx); + YUV420p2RGB8Invoker converter(dst_data, dst_step, dst_width, _stride, _y1, _u, _v, ustepIdx, vstepIdx); if (dst_width * dst_height >= MIN_SIZE_FOR_PARALLEL_YUV420_CONVERSION) parallel_for_(Range(0, dst_height/2), converter); else @@ -2018,9 +1150,9 @@ inline void cvtYUV420p2RGBA(uchar * dst_data, size_t dst_step, int dst_width, in ///////////////////////////////////// RGB -> YUV420p ///////////////////////////////////// -struct RGB888toYUV420pInvoker: public ParallelLoopBody +struct RGB8toYUV420pInvoker: public ParallelLoopBody { - RGB888toYUV420pInvoker(const uchar * _src_data, size_t _src_step, + RGB8toYUV420pInvoker(const uchar * _src_data, size_t _src_step, uchar * _y_data, uchar * _uv_data, size_t _dst_step, int _src_width, int _src_height, int _scn, bool swapBlue_, bool swapUV_, bool interleaved_) : src_data(_src_data), src_step(_src_step), @@ -2103,17 +1235,6 @@ struct RGB888toYUV420pInvoker: public ParallelLoopBody } } - void convert() const - { - if( src_width * src_height >= 320*240 ) - parallel_for_(Range(0, src_height/2), *this); - else - operator()(Range(0, src_height/2)); - } - -private: - RGB888toYUV420pInvoker& operator=(const RGB888toYUV420pInvoker&); - const uchar * src_data; size_t src_step; uchar *y_data, *uv_data; @@ -2129,8 +1250,8 @@ private: ///////////////////////////////////// YUV422 -> RGB ///////////////////////////////////// -template -struct YUV422toRGB888Invoker : ParallelLoopBody +template +struct YUV422toRGB8Invoker : ParallelLoopBody { uchar * dst_data; size_t dst_step; @@ -2138,9 +1259,9 @@ struct YUV422toRGB888Invoker : ParallelLoopBody size_t src_step; int width; - YUV422toRGB888Invoker(uchar * _dst_data, size_t _dst_step, - const uchar * _src_data, size_t _src_step, - int _width) + YUV422toRGB8Invoker(uchar * _dst_data, size_t _dst_step, + const uchar * _src_data, size_t _src_step, + int _width) : dst_data(_dst_data), dst_step(_dst_step), src_data(_src_data), src_step(_src_step), width(_width) {} void operator()(const Range& range) const CV_OVERRIDE @@ -2156,76 +1277,15 @@ struct YUV422toRGB888Invoker : ParallelLoopBody { uchar* row = dst_data + dst_step * j; - for (int i = 0; i < 2 * width; i += 4, row += 6) + for (int i = 0; i < 2 * width; i += 4, row += dcn*2) { - int u = int(yuv_src[i + uidx]) - 128; - int v = int(yuv_src[i + vidx]) - 128; + int u = int(yuv_src[i + uidx]); + int v = int(yuv_src[i + vidx]); - int ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * v; - int guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * v + ITUR_BT_601_CUG * u; - int buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * u; + int vy0 = int(yuv_src[i + yIdx]); + int vy1 = int(yuv_src[i + yIdx + 2]); - int y00 = std::max(0, int(yuv_src[i + yIdx]) - 16) * ITUR_BT_601_CY; - row[2-bIdx] = saturate_cast((y00 + ruv) >> ITUR_BT_601_SHIFT); - row[1] = saturate_cast((y00 + guv) >> ITUR_BT_601_SHIFT); - row[bIdx] = saturate_cast((y00 + buv) >> ITUR_BT_601_SHIFT); - - int y01 = std::max(0, int(yuv_src[i + yIdx + 2]) - 16) * ITUR_BT_601_CY; - row[5-bIdx] = saturate_cast((y01 + ruv) >> ITUR_BT_601_SHIFT); - row[4] = saturate_cast((y01 + guv) >> ITUR_BT_601_SHIFT); - row[3+bIdx] = saturate_cast((y01 + buv) >> ITUR_BT_601_SHIFT); - } - } - } -}; - -template -struct YUV422toRGBA8888Invoker : ParallelLoopBody -{ - uchar * dst_data; - size_t dst_step; - const uchar * src_data; - size_t src_step; - int width; - - YUV422toRGBA8888Invoker(uchar * _dst_data, size_t _dst_step, - const uchar * _src_data, size_t _src_step, - int _width) - : dst_data(_dst_data), dst_step(_dst_step), src_data(_src_data), src_step(_src_step), width(_width) {} - - void operator()(const Range& range) const CV_OVERRIDE - { - int rangeBegin = range.start; - int rangeEnd = range.end; - - const int uidx = 1 - yIdx + uIdx * 2; - const int vidx = (2 + uidx) % 4; - const uchar* yuv_src = src_data + rangeBegin * src_step; - - for (int j = rangeBegin; j < rangeEnd; j++, yuv_src += src_step) - { - uchar* row = dst_data + dst_step * j; - - for (int i = 0; i < 2 * width; i += 4, row += 8) - { - int u = int(yuv_src[i + uidx]) - 128; - int v = int(yuv_src[i + vidx]) - 128; - - int ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * v; - int guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * v + ITUR_BT_601_CUG * u; - int buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * u; - - int y00 = std::max(0, int(yuv_src[i + yIdx]) - 16) * ITUR_BT_601_CY; - row[2-bIdx] = saturate_cast((y00 + ruv) >> ITUR_BT_601_SHIFT); - row[1] = saturate_cast((y00 + guv) >> ITUR_BT_601_SHIFT); - row[bIdx] = saturate_cast((y00 + buv) >> ITUR_BT_601_SHIFT); - row[3] = uchar(0xff); - - int y01 = std::max(0, int(yuv_src[i + yIdx + 2]) - 16) * ITUR_BT_601_CY; - row[6-bIdx] = saturate_cast((y01 + ruv) >> ITUR_BT_601_SHIFT); - row[5] = saturate_cast((y01 + guv) >> ITUR_BT_601_SHIFT); - row[4+bIdx] = saturate_cast((y01 + buv) >> ITUR_BT_601_SHIFT); - row[7] = uchar(0xff); + cvtYuv42xxp2RGB8(u, v, vy0, vy1, 0, 0, row, (uchar*)(0)); } } } @@ -2233,22 +1293,11 @@ struct YUV422toRGBA8888Invoker : ParallelLoopBody #define MIN_SIZE_FOR_PARALLEL_YUV422_CONVERSION (320*240) -template +template inline void cvtYUV422toRGB(uchar * dst_data, size_t dst_step, const uchar * src_data, size_t src_step, int width, int height) { - YUV422toRGB888Invoker converter(dst_data, dst_step, src_data, src_step, width); - if (width * height >= MIN_SIZE_FOR_PARALLEL_YUV422_CONVERSION) - parallel_for_(Range(0, height), converter); - else - converter(Range(0, height)); -} - -template -inline void cvtYUV422toRGBA(uchar * dst_data, size_t dst_step, const uchar * src_data, size_t src_step, - int width, int height) -{ - YUV422toRGBA8888Invoker converter(dst_data, dst_step, src_data, src_step, width); + YUV422toRGB8Invoker converter(dst_data, dst_step, src_data, src_step, width); if (width * height >= MIN_SIZE_FOR_PARALLEL_YUV422_CONVERSION) parallel_for_(Range(0, height), converter); else @@ -2382,6 +1431,14 @@ void cvtTwoPlaneYUVtoBGR(const uchar * src_data, size_t src_step, cvtTwoPlaneYUVtoBGR(src_data, uv, src_step, dst_data, dst_step, dst_width, dst_height, dcn, swapBlue, uIdx); } +typedef void (*cvt_2plane_yuv_ptr_t)(uchar * /* dst_data*/, + size_t /* dst_step */, + int /* dst_width */, + int /* dst_height */, + size_t /* _stride */, + const uchar* /* _y1 */, + const uchar* /* _uv */); + void cvtTwoPlaneYUVtoBGR(const uchar * y_data, const uchar * uv_data, size_t src_step, uchar * dst_data, size_t dst_step, int dst_width, int dst_height, @@ -2390,21 +1447,37 @@ void cvtTwoPlaneYUVtoBGR(const uchar * y_data, const uchar * uv_data, size_t src CV_INSTRUMENT_REGION(); // TODO: add hal replacement method + int blueIdx = swapBlue ? 2 : 0; + + cvt_2plane_yuv_ptr_t cvtPtr; switch(dcn*100 + blueIdx * 10 + uIdx) { - case 300: cvtYUV420sp2RGB<0, 0> (dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; - case 301: cvtYUV420sp2RGB<0, 1> (dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; - case 320: cvtYUV420sp2RGB<2, 0> (dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; - case 321: cvtYUV420sp2RGB<2, 1> (dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; - case 400: cvtYUV420sp2RGBA<0, 0>(dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; - case 401: cvtYUV420sp2RGBA<0, 1>(dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; - case 420: cvtYUV420sp2RGBA<2, 0>(dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; - case 421: cvtYUV420sp2RGBA<2, 1>(dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); break; + case 300: cvtPtr = cvtYUV420sp2RGB<0, 0, 3>; break; + case 301: cvtPtr = cvtYUV420sp2RGB<0, 1, 3>; break; + case 320: cvtPtr = cvtYUV420sp2RGB<2, 0, 3>; break; + case 321: cvtPtr = cvtYUV420sp2RGB<2, 1, 3>; break; + case 400: cvtPtr = cvtYUV420sp2RGB<0, 0, 4>; break; + case 401: cvtPtr = cvtYUV420sp2RGB<0, 1, 4>; break; + case 420: cvtPtr = cvtYUV420sp2RGB<2, 0, 4>; break; + case 421: cvtPtr = cvtYUV420sp2RGB<2, 1, 4>; break; default: CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" ); break; }; + + cvtPtr(dst_data, dst_step, dst_width, dst_height, src_step, y_data, uv_data); } +typedef void (*cvt_3plane_yuv_ptr_t)(uchar * /* dst_data */, + size_t /* dst_step */, + int /* dst_width */, + int /* dst_height */, + size_t /* _stride */, + const uchar* /* _y1 */, + const uchar* /* _u */, + const uchar* /* _v */, + int /* ustepIdx */, + int /* vstepIdx */); + void cvtThreePlaneYUVtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int dst_width, int dst_height, @@ -2422,14 +1495,17 @@ void cvtThreePlaneYUVtoBGR(const uchar * src_data, size_t src_step, if(uIdx == 1) { std::swap(u ,v), std::swap(ustepIdx, vstepIdx); } int blueIdx = swapBlue ? 2 : 0; + cvt_3plane_yuv_ptr_t cvtPtr; switch(dcn*10 + blueIdx) { - case 30: cvtYUV420p2RGB<0>(dst_data, dst_step, dst_width, dst_height, src_step, src_data, u, v, ustepIdx, vstepIdx); break; - case 32: cvtYUV420p2RGB<2>(dst_data, dst_step, dst_width, dst_height, src_step, src_data, u, v, ustepIdx, vstepIdx); break; - case 40: cvtYUV420p2RGBA<0>(dst_data, dst_step, dst_width, dst_height, src_step, src_data, u, v, ustepIdx, vstepIdx); break; - case 42: cvtYUV420p2RGBA<2>(dst_data, dst_step, dst_width, dst_height, src_step, src_data, u, v, ustepIdx, vstepIdx); break; + case 30: cvtPtr = cvtYUV420p2RGB<0, 3>; break; + case 32: cvtPtr = cvtYUV420p2RGB<2, 3>; break; + case 40: cvtPtr = cvtYUV420p2RGB<0, 4>; break; + case 42: cvtPtr = cvtYUV420p2RGB<2, 4>; break; default: CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" ); break; }; + + cvtPtr(dst_data, dst_step, dst_width, dst_height, src_step, src_data, u, v, ustepIdx, vstepIdx); } void cvtBGRtoThreePlaneYUV(const uchar * src_data, size_t src_step, @@ -2441,7 +1517,14 @@ void cvtBGRtoThreePlaneYUV(const uchar * src_data, size_t src_step, CALL_HAL(cvtBGRtoThreePlaneYUV, cv_hal_cvtBGRtoThreePlaneYUV, src_data, src_step, dst_data, dst_step, width, height, scn, swapBlue, uIdx); uchar * uv_data = dst_data + dst_step * height; - RGB888toYUV420pInvoker(src_data, src_step, dst_data, uv_data, dst_step, width, height, scn, swapBlue, uIdx == 2, false).convert(); + + RGB8toYUV420pInvoker cvt(src_data, src_step, dst_data, uv_data, dst_step, width, height, + scn, swapBlue, uIdx == 2, false); + + if( width * height >= 320*240 ) + parallel_for_(Range(0, height/2), cvt); + else + cvt(Range(0, height/2)); } void cvtBGRtoTwoPlaneYUV(const uchar * src_data, size_t src_step, @@ -2452,9 +1535,23 @@ void cvtBGRtoTwoPlaneYUV(const uchar * src_data, size_t src_step, CV_INSTRUMENT_REGION(); // TODO: add hal replacement method - RGB888toYUV420pInvoker(src_data, src_step, y_data, uv_data, dst_step, width, height, scn, swapBlue, uIdx == 2, true).convert(); + + RGB8toYUV420pInvoker cvt(src_data, src_step, y_data, uv_data, dst_step, width, height, + scn, swapBlue, uIdx == 2, true); + + if( width * height >= 320*240 ) + parallel_for_(Range(0, height/2), cvt); + else + cvt(Range(0, height/2)); } +typedef void (*cvt_1plane_yuv_ptr_t)(uchar * /* dst_data */, + size_t /* dst_step */, + const uchar * /* src_data */, + size_t /* src_step */, + int /* width */, + int /* height */); + void cvtOnePlaneYUVtoBGR(const uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, @@ -2463,23 +1560,27 @@ void cvtOnePlaneYUVtoBGR(const uchar * src_data, size_t src_step, CV_INSTRUMENT_REGION(); CALL_HAL(cvtOnePlaneYUVtoBGR, cv_hal_cvtOnePlaneYUVtoBGR, src_data, src_step, dst_data, dst_step, width, height, dcn, swapBlue, uIdx, ycn); + + cvt_1plane_yuv_ptr_t cvtPtr; int blueIdx = swapBlue ? 2 : 0; switch(dcn*1000 + blueIdx*100 + uIdx*10 + ycn) { - case 3000: cvtYUV422toRGB<0,0,0>(dst_data, dst_step, src_data, src_step, width, height); break; - case 3001: cvtYUV422toRGB<0,0,1>(dst_data, dst_step, src_data, src_step, width, height); break; - case 3010: cvtYUV422toRGB<0,1,0>(dst_data, dst_step, src_data, src_step, width, height); break; - case 3200: cvtYUV422toRGB<2,0,0>(dst_data, dst_step, src_data, src_step, width, height); break; - case 3201: cvtYUV422toRGB<2,0,1>(dst_data, dst_step, src_data, src_step, width, height); break; - case 3210: cvtYUV422toRGB<2,1,0>(dst_data, dst_step, src_data, src_step, width, height); break; - case 4000: cvtYUV422toRGBA<0,0,0>(dst_data, dst_step, src_data, src_step, width, height); break; - case 4001: cvtYUV422toRGBA<0,0,1>(dst_data, dst_step, src_data, src_step, width, height); break; - case 4010: cvtYUV422toRGBA<0,1,0>(dst_data, dst_step, src_data, src_step, width, height); break; - case 4200: cvtYUV422toRGBA<2,0,0>(dst_data, dst_step, src_data, src_step, width, height); break; - case 4201: cvtYUV422toRGBA<2,0,1>(dst_data, dst_step, src_data, src_step, width, height); break; - case 4210: cvtYUV422toRGBA<2,1,0>(dst_data, dst_step, src_data, src_step, width, height); break; + case 3000: cvtPtr = cvtYUV422toRGB<0,0,0,3>; break; + case 3001: cvtPtr = cvtYUV422toRGB<0,0,1,3>; break; + case 3010: cvtPtr = cvtYUV422toRGB<0,1,0,3>; break; + case 3200: cvtPtr = cvtYUV422toRGB<2,0,0,3>; break; + case 3201: cvtPtr = cvtYUV422toRGB<2,0,1,3>; break; + case 3210: cvtPtr = cvtYUV422toRGB<2,1,0,3>; break; + case 4000: cvtPtr = cvtYUV422toRGB<0,0,0,4>; break; + case 4001: cvtPtr = cvtYUV422toRGB<0,0,1,4>; break; + case 4010: cvtPtr = cvtYUV422toRGB<0,1,0,4>; break; + case 4200: cvtPtr = cvtYUV422toRGB<2,0,0,4>; break; + case 4201: cvtPtr = cvtYUV422toRGB<2,0,1,4>; break; + case 4210: cvtPtr = cvtYUV422toRGB<2,1,0,4>; break; default: CV_Error( CV_StsBadFlag, "Unknown/unsupported color conversion code" ); break; }; + + cvtPtr(dst_data, dst_step, src_data, src_step, width, height); } } // namespace hal From a84e11451b6e638a63f7c39ffb0a3330be882491 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 21 Jan 2019 16:04:23 +0300 Subject: [PATCH 21/23] imgproc(test): RGB2YUV regression test --- modules/imgproc/test/test_color.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/modules/imgproc/test/test_color.cpp b/modules/imgproc/test/test_color.cpp index c36bc1fd6e..6ad51ad512 100644 --- a/modules/imgproc/test/test_color.cpp +++ b/modules/imgproc/test/test_color.cpp @@ -3062,4 +3062,14 @@ TEST(ImgProc_BGR2RGBA, 3ch24ch) EXPECT_DOUBLE_EQ(cvtest::norm(expected - dst, NORM_INF), 0.); } +TEST(ImgProc_RGB2YUV, regression_13668) +{ + Mat src(Size(32, 4), CV_8UC3, Scalar(9, 250, 82)); // Ensure that SIMD code path works + Mat dst; + cvtColor(src, dst, COLOR_RGB2YUV); + Vec3b res = dst.at(0, 0); + Vec3b ref(159, 90, 0); + EXPECT_EQ(res, ref); +} + }} // namespace From 74ba4b7ae2baf8a01112fbbac011409d7797b67d Mon Sep 17 00:00:00 2001 From: Rostislav Vasilikhin Date: Mon, 21 Jan 2019 18:01:44 +0300 Subject: [PATCH 22/23] fixed (un)signed packing s16 -> u8 --- modules/imgproc/src/color_yuv.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/imgproc/src/color_yuv.cpp b/modules/imgproc/src/color_yuv.cpp index acc290ad9b..f80c9c36fc 100644 --- a/modules/imgproc/src/color_yuv.cpp +++ b/modules/imgproc/src/color_yuv.cpp @@ -446,8 +446,8 @@ struct RGB2YCrCb_i swap(sr0, sb0); swap(sr1, sb1); } - v_uint32 cr00, cr01, cr10, cr11; - v_uint32 cb00, cb01, cb10, cb11; + v_int32 cr00, cr01, cr10, cr11; + v_int32 cb00, cb01, cb10, cb11; // delta + descaleShift == descaleShift*(half*2+1) { @@ -460,15 +460,15 @@ struct RGB2YCrCb_i v_zip(sb0, vdescale, bd00, bd01); v_zip(sb1, vdescale, bd10, bd11); - cr00 = v_reinterpret_as_u32(v_dotprod(rd00, c3h)); - cr01 = v_reinterpret_as_u32(v_dotprod(rd01, c3h)); - cr10 = v_reinterpret_as_u32(v_dotprod(rd10, c3h)); - cr11 = v_reinterpret_as_u32(v_dotprod(rd11, c3h)); + cr00 = v_dotprod(rd00, c3h); + cr01 = v_dotprod(rd01, c3h); + cr10 = v_dotprod(rd10, c3h); + cr11 = v_dotprod(rd11, c3h); - cb00 = v_reinterpret_as_u32(v_dotprod(bd00, c4h)); - cb01 = v_reinterpret_as_u32(v_dotprod(bd01, c4h)); - cb10 = v_reinterpret_as_u32(v_dotprod(bd10, c4h)); - cb11 = v_reinterpret_as_u32(v_dotprod(bd11, c4h)); + cb00 = v_dotprod(bd00, c4h); + cb01 = v_dotprod(bd01, c4h); + cb10 = v_dotprod(bd10, c4h); + cb11 = v_dotprod(bd11, c4h); } v_uint8 cr, cb; @@ -483,12 +483,12 @@ struct RGB2YCrCb_i cb10 = cb10 >> shift; cb11 = cb11 >> shift; - v_uint16 cr0, cr1, cb0, cb1; + v_int16 cr0, cr1, cb0, cb1; cr0 = v_pack(cr00, cr01); cr1 = v_pack(cr10, cr11); cb0 = v_pack(cb00, cb01); cb1 = v_pack(cb10, cb11); - cr = v_pack(cr0, cr1); - cb = v_pack(cb0, cb1); + cr = v_pack_u(cr0, cr1); + cb = v_pack_u(cb0, cb1); if(yuvOrder) { From 970293a229ef314603ffaf77fc62495bf849aba8 Mon Sep 17 00:00:00 2001 From: Namgoo Lee Date: Mon, 21 Jan 2019 15:31:05 +0000 Subject: [PATCH 23/23] __shfl_up_sync with mask for CUDA >= 9 * __shfl_up_sync with proper mask value for CUDA >= 9 * BlockScanInclusive for CUDA >= 9 * compatible_shfl_up for use in integral.hpp * Use CLAHE in cudev * Add tests for BlockScan --- modules/cudaimgproc/src/cuda/clahe.cu | 24 ++- .../include/opencv2/cudev/block/scan.hpp | 145 +++++++++++++++++- .../opencv2/cudev/grid/detail/integral.hpp | 8 +- .../cudev/include/opencv2/cudev/warp/scan.hpp | 49 ++++-- .../include/opencv2/cudev/warp/shuffle.hpp | 53 ++++++- modules/cudev/test/test_scan.cu | 140 +++++++++++++++++ 6 files changed, 385 insertions(+), 34 deletions(-) create mode 100644 modules/cudev/test/test_scan.cu diff --git a/modules/cudaimgproc/src/cuda/clahe.cu b/modules/cudaimgproc/src/cuda/clahe.cu index 455aa20d98..b66a7d8a66 100644 --- a/modules/cudaimgproc/src/cuda/clahe.cu +++ b/modules/cudaimgproc/src/cuda/clahe.cu @@ -42,15 +42,9 @@ #if !defined CUDA_DISABLER -#include "opencv2/core/cuda/common.hpp" -#include "opencv2/core/cuda/functional.hpp" -#include "opencv2/core/cuda/emulation.hpp" -#include "opencv2/core/cuda/scan.hpp" -#include "opencv2/core/cuda/reduce.hpp" -#include "opencv2/core/cuda/saturate_cast.hpp" +#include "opencv2/cudev.hpp" -using namespace cv::cuda; -using namespace cv::cuda::device; +using namespace cv::cudev; namespace clahe { @@ -73,7 +67,7 @@ namespace clahe for (int j = threadIdx.x; j < tileSize.x; j += blockDim.x) { const int data = srcPtr[j]; - Emulation::smem::atomicAdd(&smem[data], 1); + ::atomicAdd(&smem[data], 1); } } @@ -96,7 +90,7 @@ namespace clahe // find number of overall clipped samples - reduce<256>(smem, clipped, tid, plus()); + blockReduce<256>(smem, clipped, tid, plus()); // broadcast evaluated value @@ -128,10 +122,10 @@ namespace clahe calcLutKernel<<>>(src, lut, tileSize, tilesX, clipLimit, lutScale); - cudaSafeCall( cudaGetLastError() ); + CV_CUDEV_SAFE_CALL( cudaGetLastError() ); if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); + CV_CUDEV_SAFE_CALL( cudaDeviceSynchronize() ); } __global__ void transformKernel(const PtrStepSzb src, PtrStepb dst, const PtrStepb lut, const int2 tileSize, const int tilesX, const int tilesY) @@ -173,13 +167,13 @@ namespace clahe const dim3 block(32, 8); const dim3 grid(divUp(src.cols, block.x), divUp(src.rows, block.y)); - cudaSafeCall( cudaFuncSetCacheConfig(transformKernel, cudaFuncCachePreferL1) ); + CV_CUDEV_SAFE_CALL( cudaFuncSetCacheConfig(transformKernel, cudaFuncCachePreferL1) ); transformKernel<<>>(src, dst, lut, tileSize, tilesX, tilesY); - cudaSafeCall( cudaGetLastError() ); + CV_CUDEV_SAFE_CALL( cudaGetLastError() ); if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); + CV_CUDEV_SAFE_CALL( cudaDeviceSynchronize() ); } } diff --git a/modules/cudev/include/opencv2/cudev/block/scan.hpp b/modules/cudev/include/opencv2/cudev/block/scan.hpp index cd75a3e197..705f875a6e 100644 --- a/modules/cudev/include/opencv2/cudev/block/scan.hpp +++ b/modules/cudev/include/opencv2/cudev/block/scan.hpp @@ -48,12 +48,134 @@ #include "../common.hpp" #include "../warp/scan.hpp" +#include "../warp/warp.hpp" namespace cv { namespace cudev { //! @addtogroup cudev //! @{ +#if __CUDACC_VER_MAJOR__ >= 9 + +// Usage Note +// - THREADS_NUM should be equal to the number of threads in this block. +// - smem must be able to contain at least n elements of type T, where n is equal to the number +// of warps in this block. The number can be calculated by divUp(THREADS_NUM, WARP_SIZE). +// +// Dev Note +// - Starting from CUDA 9.0, support for Fermi is dropped. So CV_CUDEV_ARCH >= 300 is implied. +// - "For Pascal and earlier architectures (CV_CUDEV_ARCH < 700), all threads in mask must execute +// the same warp intrinsic instruction in convergence, and the union of all values in mask must +// be equal to the warp's active mask." +// (https://docs.nvidia.com/cuda/archive/10.0/cuda-c-programming-guide#independent-thread-scheduling-7-x) +// - Above restriction does not apply starting from Volta (CV_CUDEV_ARCH >= 700). We just need to +// take care so that "all non-exited threads named in mask must execute the same intrinsic with +// the same mask." +// (https://docs.nvidia.com/cuda/archive/10.0/cuda-c-programming-guide#warp-description) + +template +__device__ T blockScanInclusive(T data, volatile T* smem, uint tid) +{ + const int residual = THREADS_NUM & (WARP_SIZE - 1); + +#if CV_CUDEV_ARCH < 700 + const uint residual_mask = (1U << residual) - 1; +#endif + + if (THREADS_NUM > WARP_SIZE) + { + // bottom-level inclusive warp scan + #if CV_CUDEV_ARCH >= 700 + T warpResult = warpScanInclusive(0xFFFFFFFFU, data); + #else + T warpResult; + + if (0 == residual) + warpResult = warpScanInclusive(0xFFFFFFFFU, data); + else + { + const int n_warps = divUp(THREADS_NUM, WARP_SIZE); + const int warp_num = Warp::warpId(); + + if (warp_num < n_warps - 1) + warpResult = warpScanInclusive(0xFFFFFFFFU, data); + else + { + // We are at the last threads of a block whose number of threads + // is not a multiple of the warp size + warpResult = warpScanInclusive(residual_mask, data); + } + } + #endif + + __syncthreads(); + + // save top elements of each warp for exclusive warp scan + // sync to wait for warp scans to complete (because smem is being overwritten) + if ((tid & (WARP_SIZE - 1)) == (WARP_SIZE - 1)) + { + smem[tid >> LOG_WARP_SIZE] = warpResult; + } + + __syncthreads(); + + int quot = THREADS_NUM / WARP_SIZE; + + if (tid < quot) + { + // grab top warp elements + T val = smem[tid]; + + uint mask = (1LLU << quot) - 1; + + if (0 == residual) + { + // calculate exclusive scan and write back to shared memory + smem[tid] = warpScanExclusive(mask, val); + } + else + { + // calculate inclusive scan and write back to shared memory with offset 1 + smem[tid + 1] = warpScanInclusive(mask, val); + + if (tid == 0) + smem[0] = 0; + } + } + + __syncthreads(); + + // return updated warp scans + return warpResult + smem[tid >> LOG_WARP_SIZE]; + } + else + { + #if CV_CUDEV_ARCH >= 700 + return warpScanInclusive(0xFFFFFFFFU, data); + #else + if (THREADS_NUM == WARP_SIZE) + return warpScanInclusive(0xFFFFFFFFU, data); + else + return warpScanInclusive(residual_mask, data); + #endif + } +} + +template +__device__ __forceinline__ T blockScanExclusive(T data, volatile T* smem, uint tid) +{ + return blockScanInclusive(data, smem, tid) - data; +} + +#else // __CUDACC_VER_MAJOR__ >= 9 + +// Usage Note +// - THREADS_NUM should be equal to the number of threads in this block. +// - (>= Kepler) smem must be able to contain at least n elements of type T, where n is equal to the number +// of warps in this block. The number can be calculated by divUp(THREADS_NUM, WARP_SIZE). +// - (Fermi) smem must be able to contain at least n elements of type T, where n is equal to the number +// of threads in this block (= THREADS_NUM). + template __device__ T blockScanInclusive(T data, volatile T* smem, uint tid) { @@ -73,18 +195,31 @@ __device__ T blockScanInclusive(T data, volatile T* smem, uint tid) __syncthreads(); - if (tid < (THREADS_NUM / WARP_SIZE)) + int quot = THREADS_NUM / WARP_SIZE; + + if (tid < quot) { // grab top warp elements T val = smem[tid]; - // calculate exclusive scan and write back to shared memory - smem[tid] = warpScanExclusive(val, smem, tid); + if (0 == (THREADS_NUM & (WARP_SIZE - 1))) + { + // calculate exclusive scan and write back to shared memory + smem[tid] = warpScanExclusive(val, smem, tid); + } + else + { + // calculate inclusive scan and write back to shared memory with offset 1 + smem[tid + 1] = warpScanInclusive(val, smem, tid); + + if (tid == 0) + smem[0] = 0; + } } __syncthreads(); - // return updated warp scans with exclusive scan results + // return updated warp scans return warpResult + smem[tid >> LOG_WARP_SIZE]; } else @@ -99,6 +234,8 @@ __device__ __forceinline__ T blockScanExclusive(T data, volatile T* smem, uint t return blockScanInclusive(data, smem, tid) - data; } +#endif // __CUDACC_VER_MAJOR__ >= 9 + //! @} }} diff --git a/modules/cudev/include/opencv2/cudev/grid/detail/integral.hpp b/modules/cudev/include/opencv2/cudev/grid/detail/integral.hpp index 7672aca71d..d1014b3ceb 100644 --- a/modules/cudev/include/opencv2/cudev/grid/detail/integral.hpp +++ b/modules/cudev/include/opencv2/cudev/grid/detail/integral.hpp @@ -215,7 +215,7 @@ namespace integral_detail #pragma unroll for (int i = 1; i < 32; i *= 2) { - const int n = shfl_up(sum, i, 32); + const int n = compatible_shfl_up(sum, i, 32); if (lane_id >= i) { @@ -245,9 +245,9 @@ namespace integral_detail int warp_sum = sums[lane_id]; #pragma unroll - for (int i = 1; i <= 32; i *= 2) + for (int i = 1; i < 32; i *= 2) { - const int n = shfl_up(warp_sum, i, 32); + const int n = compatible_shfl_up(warp_sum, i, 32); if (lane_id >= i) warp_sum += n; @@ -453,7 +453,7 @@ namespace integral_detail for (int i = 1; i <= 8; i *= 2) { - T n = shfl_up(partial_sum, i, 32); + T n = compatible_shfl_up(partial_sum, i, 32); if (lane_id >= i) partial_sum += n; diff --git a/modules/cudev/include/opencv2/cudev/warp/scan.hpp b/modules/cudev/include/opencv2/cudev/warp/scan.hpp index a5007f881a..bab462973f 100644 --- a/modules/cudev/include/opencv2/cudev/warp/scan.hpp +++ b/modules/cudev/include/opencv2/cudev/warp/scan.hpp @@ -55,6 +55,36 @@ namespace cv { namespace cudev { //! @addtogroup cudev //! @{ +#if __CUDACC_VER_MAJOR__ >= 9 + +// Starting from CUDA 9.0, support for Fermi is dropped. +// So CV_CUDEV_ARCH >= 300 is implied. + +template +__device__ T warpScanInclusive(uint mask, T data) +{ + const uint laneId = Warp::laneId(); + + // scan on shufl functions + #pragma unroll + for (int i = 1; i <= (WARP_SIZE / 2); i *= 2) + { + const T val = shfl_up_sync(mask, data, i); + if (laneId >= i) + data += val; + } + + return data; +} + +template +__device__ __forceinline__ T warpScanExclusive(uint mask, T data) +{ + return warpScanInclusive(mask, data) - data; +} + +#else // __CUDACC_VER_MAJOR__ >= 9 + template __device__ T warpScanInclusive(T data, volatile T* smem, uint tid) { @@ -75,19 +105,16 @@ __device__ T warpScanInclusive(T data, volatile T* smem, uint tid) return data; #else - uint pos = 2 * tid - (tid & (WARP_SIZE - 1)); - smem[pos] = 0; + const uint laneId = Warp::laneId(); - pos += WARP_SIZE; - smem[pos] = data; + smem[tid] = data; - smem[pos] += smem[pos - 1]; - smem[pos] += smem[pos - 2]; - smem[pos] += smem[pos - 4]; - smem[pos] += smem[pos - 8]; - smem[pos] += smem[pos - 16]; + #pragma unroll + for (int i = 1; i <= (WARP_SIZE / 2); i *= 2) + if (laneId >= i) + smem[tid] += smem[tid - i]; - return smem[pos]; + return smem[tid]; #endif } @@ -97,6 +124,8 @@ __device__ __forceinline__ T warpScanExclusive(T data, volatile T* smem, uint ti return warpScanInclusive(data, smem, tid) - data; } +#endif // __CUDACC_VER_MAJOR__ >= 9 + //! @} }} diff --git a/modules/cudev/include/opencv2/cudev/warp/shuffle.hpp b/modules/cudev/include/opencv2/cudev/warp/shuffle.hpp index e776dd65df..dd142c6719 100644 --- a/modules/cudev/include/opencv2/cudev/warp/shuffle.hpp +++ b/modules/cudev/include/opencv2/cudev/warp/shuffle.hpp @@ -48,6 +48,8 @@ #include "../common.hpp" #include "../util/vec_traits.hpp" +#include "../block/block.hpp" +#include "warp.hpp" namespace cv { namespace cudev { @@ -59,7 +61,7 @@ namespace cv { namespace cudev { #if __CUDACC_VER_MAJOR__ >= 9 # define __shfl(x, y, z) __shfl_sync(0xFFFFFFFFU, x, y, z) # define __shfl_xor(x, y, z) __shfl_xor_sync(0xFFFFFFFFU, x, y, z) -# define __shfl_up(x, y, z) __shfl_up_sync(0xFFFFFFFFU, x, y, z) +//# define __shfl_up(x, y, z) __shfl_up_sync(0xFFFFFFFFU, x, y, z) # define __shfl_down(x, y, z) __shfl_down_sync(0xFFFFFFFFU, x, y, z) #endif @@ -155,6 +157,53 @@ CV_CUDEV_SHFL_VEC_INST(double) // shfl_up +template +__device__ __forceinline__ T compatible_shfl_up(T val, uint delta, int width = warpSize) +{ +#if __CUDACC_VER_MAJOR__ < 9 + + return shfl_up(val, delta, width); + +#else // __CUDACC_VER_MAJOR__ < 9 + +#if CV_CUDEV_ARCH >= 700 + return shfl_up_sync(0xFFFFFFFFU, val, delta, width); +#else + const int block_size = Block::blockSize(); + const int residual = block_size & (warpSize - 1); + + if (0 == residual) + return shfl_up_sync(0xFFFFFFFFU, val, delta, width); + else + { + const int n_warps = divUp(block_size, warpSize); + const int warp_id = Warp::warpId(); + + if (warp_id < n_warps - 1) + return shfl_up_sync(0xFFFFFFFFU, val, delta, width); + else + { + // We are at the last threads of a block whose number of threads + // is not a multiple of the warp size + uint mask = (1LU << residual) - 1; + return shfl_up_sync(mask, val, delta, width); + } + } +#endif + +#endif // __CUDACC_VER_MAJOR__ < 9 +} + +#if __CUDACC_VER_MAJOR__ >= 9 + +template +__device__ __forceinline__ T shfl_up_sync(uint mask, T val, uint delta, int width = warpSize) +{ + return (T) __shfl_up_sync(mask, val, delta, width); +} + +#else + __device__ __forceinline__ uchar shfl_up(uchar val, uint delta, int width = warpSize) { return (uchar) __shfl_up((int) val, delta, width); @@ -244,6 +293,8 @@ CV_CUDEV_SHFL_UP_VEC_INST(double) #undef CV_CUDEV_SHFL_UP_VEC_INST +#endif + // shfl_down __device__ __forceinline__ uchar shfl_down(uchar val, uint delta, int width = warpSize) diff --git a/modules/cudev/test/test_scan.cu b/modules/cudev/test/test_scan.cu new file mode 100644 index 0000000000..e5404d8890 --- /dev/null +++ b/modules/cudev/test/test_scan.cu @@ -0,0 +1,140 @@ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace cv::cudev; +using namespace cvtest; + +// BlockScanInt + +template +__global__ void int_kernel(int* data) +{ + uint tid = Block::threadLineId(); + +#if CV_CUDEV_ARCH >= 300 + const int n_warps = (THREADS_NUM - 1) / WARP_SIZE + 1; + __shared__ int smem[n_warps]; +#else + __shared__ int smem[THREADS_NUM]; +#endif + + data[tid] = blockScanInclusive(data[tid], smem, tid); +} + +#define BLOCK_SCAN_INT_TEST(block_size) \ + TEST(BlockScanInt, BlockSize##block_size) \ + { \ + Mat src = randomMat(Size(block_size, 1), CV_32SC1, 0, 1024); \ + \ + GpuMat d_src; \ + d_src.upload(src); \ + \ + for (int col = 1; col < block_size; col++) \ + src.at(0, col) += src.at(0, col - 1); \ + \ + int_kernel<<<1, block_size>>>((int*)d_src.data); \ + \ + CV_CUDEV_SAFE_CALL(cudaDeviceSynchronize()); \ + \ + EXPECT_MAT_NEAR(d_src, src, 0); \ + } + +BLOCK_SCAN_INT_TEST(29) +BLOCK_SCAN_INT_TEST(30) +BLOCK_SCAN_INT_TEST(32) +BLOCK_SCAN_INT_TEST(40) +BLOCK_SCAN_INT_TEST(41) + +BLOCK_SCAN_INT_TEST(59) +BLOCK_SCAN_INT_TEST(60) +BLOCK_SCAN_INT_TEST(64) +BLOCK_SCAN_INT_TEST(70) +BLOCK_SCAN_INT_TEST(71) + +BLOCK_SCAN_INT_TEST(109) +BLOCK_SCAN_INT_TEST(110) +BLOCK_SCAN_INT_TEST(128) +BLOCK_SCAN_INT_TEST(130) +BLOCK_SCAN_INT_TEST(131) + +BLOCK_SCAN_INT_TEST(189) +BLOCK_SCAN_INT_TEST(200) +BLOCK_SCAN_INT_TEST(256) +BLOCK_SCAN_INT_TEST(300) +BLOCK_SCAN_INT_TEST(311) + +BLOCK_SCAN_INT_TEST(489) +BLOCK_SCAN_INT_TEST(500) +BLOCK_SCAN_INT_TEST(512) +BLOCK_SCAN_INT_TEST(600) +BLOCK_SCAN_INT_TEST(611) + +BLOCK_SCAN_INT_TEST(1024) + +// BlockScanDouble + +template +__global__ void double_kernel(double* data) +{ + uint tid = Block::threadLineId(); + +#if CV_CUDEV_ARCH >= 300 + const int n_warps = (THREADS_NUM - 1) / WARP_SIZE + 1; + __shared__ double smem[n_warps]; +#else + __shared__ double smem[THREADS_NUM]; +#endif + + data[tid] = blockScanInclusive(data[tid], smem, tid); +} + +#define BLOCK_SCAN_DOUBLE_TEST(block_size) \ + TEST(BlockScanDouble, BlockSize##block_size) \ + { \ + Mat src = randomMat(Size(block_size, 1), CV_64FC1, 0.0, 1.0); \ + \ + GpuMat d_src; \ + d_src.upload(src); \ + \ + for (int col = 1; col < block_size; col++) \ + src.at(0, col) += src.at(0, col - 1); \ + \ + double_kernel<<<1, block_size>>>((double*)d_src.data); \ + \ + CV_CUDEV_SAFE_CALL(cudaDeviceSynchronize()); \ + \ + EXPECT_MAT_NEAR(d_src, src, 1e-10); \ + } + +BLOCK_SCAN_DOUBLE_TEST(29) +BLOCK_SCAN_DOUBLE_TEST(30) +BLOCK_SCAN_DOUBLE_TEST(32) +BLOCK_SCAN_DOUBLE_TEST(40) +BLOCK_SCAN_DOUBLE_TEST(41) + +BLOCK_SCAN_DOUBLE_TEST(59) +BLOCK_SCAN_DOUBLE_TEST(60) +BLOCK_SCAN_DOUBLE_TEST(64) +BLOCK_SCAN_DOUBLE_TEST(70) +BLOCK_SCAN_DOUBLE_TEST(71) + +BLOCK_SCAN_DOUBLE_TEST(109) +BLOCK_SCAN_DOUBLE_TEST(110) +BLOCK_SCAN_DOUBLE_TEST(128) +BLOCK_SCAN_DOUBLE_TEST(130) +BLOCK_SCAN_DOUBLE_TEST(131) + +BLOCK_SCAN_DOUBLE_TEST(189) +BLOCK_SCAN_DOUBLE_TEST(200) +BLOCK_SCAN_DOUBLE_TEST(256) +BLOCK_SCAN_DOUBLE_TEST(300) +BLOCK_SCAN_DOUBLE_TEST(311) + +BLOCK_SCAN_DOUBLE_TEST(489) +BLOCK_SCAN_DOUBLE_TEST(500) +BLOCK_SCAN_DOUBLE_TEST(512) +BLOCK_SCAN_DOUBLE_TEST(600) +BLOCK_SCAN_DOUBLE_TEST(611) + +BLOCK_SCAN_DOUBLE_TEST(1024)