From bed388bafb98ca4f1c263c663bf54200a1ccea23 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Mon, 27 Aug 2018 01:04:46 +0300 Subject: [PATCH 01/47] Update opengl_interop.cpp resolves #12307 --- samples/opengl/opengl_interop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/opengl/opengl_interop.cpp b/samples/opengl/opengl_interop.cpp index 1d606f13fe..87f1f95939 100644 --- a/samples/opengl/opengl_interop.cpp +++ b/samples/opengl/opengl_interop.cpp @@ -360,7 +360,7 @@ protected: if (m_demo_processing) { // blur texture image with OpenCV on CPU - cv::blur(m, m, cv::Size(15, 15), cv::Point(-7, -7)); + cv::blur(m, m, cv::Size(15, 15)); } if (do_buffer) @@ -385,7 +385,7 @@ protected: if (m_demo_processing) { // blur texture image with OpenCV on GPU with OpenCL - cv::blur(u, u, cv::Size(15, 15), cv::Point(-7, -7)); + cv::blur(u, u, cv::Size(15, 15)); } if (do_buffer) From 1ae477a46d50b70e5b8e71ef370d537583024463 Mon Sep 17 00:00:00 2001 From: take1014 Date: Tue, 28 Aug 2018 22:14:47 +0900 Subject: [PATCH 02/47] fix typo --- modules/imgproc/src/distransform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index 8e7774ae48..0d60e92d34 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -247,7 +247,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels, int srcstep = (int)(_src.step/sizeof(src[0])); int step = (int)(_temp.step/sizeof(temp[0])); int dststep = (int)(_dist.step/sizeof(dist[0])); - int lstep = (int)(_labels.step/sizeof(dist[0])); + int lstep = (int)(_labels.step/sizeof(labels[0])); Size size = _src.size(); initTopBottom( _temp, BORDER ); From 4062ef5fcbf0a0453104eb3b4b3027bb6cf25579 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Wed, 29 Aug 2018 13:26:43 +0300 Subject: [PATCH 03/47] Fix lifetime of networks which are loaded from Model Optimizer IRs --- modules/dnn/src/op_inf_engine.cpp | 1 + modules/dnn/src/op_inf_engine.hpp | 2 ++ modules/dnn/test/test_ie_models.cpp | 4 ---- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index f354d6966e..43d8d1eb2d 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -161,6 +161,7 @@ InfEngineBackendNet::InfEngineBackendNet(InferenceEngine::CNNNetwork& net) inputs = net.getInputsInfo(); outputs = net.getOutputsInfo(); layers.resize(net.layerCount()); // A hack to execute InfEngineBackendNet::layerCount correctly. + netOwner = net; } void InfEngineBackendNet::Release() noexcept diff --git a/modules/dnn/src/op_inf_engine.hpp b/modules/dnn/src/op_inf_engine.hpp index 841cb13e13..f49a8e0445 100644 --- a/modules/dnn/src/op_inf_engine.hpp +++ b/modules/dnn/src/op_inf_engine.hpp @@ -131,6 +131,8 @@ private: InferenceEngine::InferencePlugin plugin; InferenceEngine::ExecutableNetwork netExec; InferenceEngine::InferRequest infRequest; + // In case of models from Model Optimizer we need to manage their lifetime. + InferenceEngine::CNNNetwork netOwner; std::string name; diff --git a/modules/dnn/test/test_ie_models.cpp b/modules/dnn/test/test_ie_models.cpp index 9fefe4fd04..22dc1fe65f 100644 --- a/modules/dnn/test/test_ie_models.cpp +++ b/modules/dnn/test/test_ie_models.cpp @@ -177,10 +177,6 @@ TEST_P(DNNTestOpenVINO, models) Target target = (dnn::Target)(int)get<0>(GetParam()); std::string modelName = get<1>(GetParam()); - if ((modelName == "semantic-segmentation-adas-0001" && target == DNN_TARGET_OPENCL_FP16) || - (modelName == "vehicle-license-plate-detection-barrier-0106")) - throw SkipTestException(""); - std::string precision = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? "FP16" : "FP32"; std::string prefix = utils::fs::join("intel_models", utils::fs::join(modelName, From ca51bbb7ffe2c05379edd2b0078dddf05433b4d8 Mon Sep 17 00:00:00 2001 From: Wu Zhiwen Date: Wed, 29 Aug 2018 11:47:13 +0800 Subject: [PATCH 04/47] dnn: fix variance setting bug for PriorBoxLayer - The size of second channel should be size[2] of output tensor, - The Scalar should be {variance[0], variance[0], variance[0], variance[0]} for _variance.size() == 1 case. Signed-off-by: Wu Zhiwen --- modules/dnn/src/layers/prior_box_layer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/layers/prior_box_layer.cpp b/modules/dnn/src/layers/prior_box_layer.cpp index 6be6efa8a6..d4ffbbaa97 100644 --- a/modules/dnn/src/layers/prior_box_layer.cpp +++ b/modules/dnn/src/layers/prior_box_layer.cpp @@ -453,8 +453,8 @@ public: outputPtr = outputs[0].ptr(0, 1); if(_variance.size() == 1) { - Mat secondChannel(outputs[0].size[2], outputs[0].size[3], CV_32F, outputPtr); - secondChannel.setTo(Scalar(_variance[0])); + Mat secondChannel(1, outputs[0].size[2], CV_32F, outputPtr); + secondChannel.setTo(Scalar::all(_variance[0])); } else { From 72f422c7dca23748bb706f24277ba6892ebe7051 Mon Sep 17 00:00:00 2001 From: berak Date: Thu, 30 Aug 2018 10:50:25 +0200 Subject: [PATCH 05/47] java: fix LIST_GET macro --- .../misc/java/test/DnnListRegressionTest.java | 119 ++++++++++++++++++ .../java/generator/src/cpp/opencv_java.hpp | 2 +- 2 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 modules/dnn/misc/java/test/DnnListRegressionTest.java diff --git a/modules/dnn/misc/java/test/DnnListRegressionTest.java b/modules/dnn/misc/java/test/DnnListRegressionTest.java new file mode 100644 index 0000000000..4c357aff86 --- /dev/null +++ b/modules/dnn/misc/java/test/DnnListRegressionTest.java @@ -0,0 +1,119 @@ +package org.opencv.test.dnn; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfInt; +import org.opencv.core.MatOfFloat; +import org.opencv.core.MatOfByte; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.dnn.DictValue; +import org.opencv.dnn.Dnn; +import org.opencv.dnn.Layer; +import org.opencv.dnn.Net; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; +import org.opencv.test.OpenCVTestCase; + +/* +* regression test for #12324, +* testing various java.util.List invocations, +* which use the LIST_GET macro +*/ + +public class DnnListRegressionTest extends OpenCVTestCase { + + private final static String ENV_OPENCV_DNN_TEST_DATA_PATH = "OPENCV_DNN_TEST_DATA_PATH"; + + private final static String ENV_OPENCV_TEST_DATA_PATH = "OPENCV_TEST_DATA_PATH"; + + String modelFileName = ""; + String sourceImageFile = ""; + + Net net; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + String envDnnTestDataPath = System.getenv(ENV_OPENCV_DNN_TEST_DATA_PATH); + + if(envDnnTestDataPath == null){ + isTestCaseEnabled = false; + return; + } + + File dnnTestDataPath = new File(envDnnTestDataPath); + modelFileName = new File(dnnTestDataPath, "dnn/tensorflow_inception_graph.pb").toString(); + + String envTestDataPath = System.getenv(ENV_OPENCV_TEST_DATA_PATH); + + if(envTestDataPath == null) throw new Exception(ENV_OPENCV_TEST_DATA_PATH + " has to be defined!"); + + File testDataPath = new File(envTestDataPath); + + File f = new File(testDataPath, "dnn/grace_hopper_227.png"); + sourceImageFile = f.toString(); + if(!f.exists()) throw new Exception("Test image is missing: " + sourceImageFile); + + net = Dnn.readNetFromTensorflow(modelFileName); + + Mat image = Imgcodecs.imread(sourceImageFile); + assertNotNull("Loading image from file failed!", image); + + Mat inputBlob = Dnn.blobFromImage(image, 1.0, new Size(224, 224), new Scalar(0), true, true); + assertNotNull("Converting image to blob failed!", inputBlob); + + net.setInput(inputBlob, "input"); + } + + public void testSetInputsNames() { + List inputs = new ArrayList(); + inputs.add("input"); + try { + net.setInputsNames(inputs); + } catch(Exception e) { + fail("Net setInputsNames failed: " + e.getMessage()); + } + } + + public void testForward() { + List outs = new ArrayList(); + List outNames = new ArrayList(); + outNames.add("softmax2"); + try { + net.forward(outs,outNames); + } catch(Exception e) { + fail("Net forward failed: " + e.getMessage()); + } + } + + public void testGetMemoryConsumption() { + int layerId = 1; + List netInputShapes = new ArrayList(); + netInputShapes.add(new MatOfInt(1, 3, 224, 224)); + long[] weights=null; + long[] blobs=null; + try { + net.getMemoryConsumption(layerId, netInputShapes, weights, blobs); + } catch(Exception e) { + fail("Net getMemoryConsumption failed: " + e.getMessage()); + } + } + + public void testGetFLOPS() { + int layerId = 1; + List netInputShapes = new ArrayList(); + netInputShapes.add(new MatOfInt(1, 3, 224, 224)); + try { + net.getFLOPS(layerId, netInputShapes); + } catch(Exception e) { + fail("Net getFLOPS failed: " + e.getMessage()); + } + } +} diff --git a/modules/java/generator/src/cpp/opencv_java.hpp b/modules/java/generator/src/cpp/opencv_java.hpp index e5cd622e31..fb8625bea9 100644 --- a/modules/java/generator/src/cpp/opencv_java.hpp +++ b/modules/java/generator/src/cpp/opencv_java.hpp @@ -29,7 +29,7 @@ #define ARRAYLIST(ENV) static_cast(ENV->NewGlobalRef(ENV->FindClass("java/util/ArrayList"))) #define LIST_ADD(ENV, LIST) ENV->GetMethodID(LIST, "add", "(Ljava/lang/Object;)Z") -#define LIST_GET(ENV, LIST) ENV->GetMethodID(LIST, "get", "((I)Ljava/lang/Object;") +#define LIST_GET(ENV, LIST) ENV->GetMethodID(LIST, "get", "(I)Ljava/lang/Object;") #define LIST_SIZE(ENV, LIST) ENV->GetMethodID(LIST, "size", "()I") #define LIST_CLEAR(ENV, LIST) ENV->GetMethodID(LIST, "clear", "()V") From cecf84410c9db0262e022328e385b9087944139a Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 30 Aug 2018 13:06:26 +0300 Subject: [PATCH 06/47] Added option to fail on missing testdata --- modules/ts/src/ts.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ts/src/ts.cpp b/modules/ts/src/ts.cpp index 66dd7656be..67f48d5041 100644 --- a/modules/ts/src/ts.cpp +++ b/modules/ts/src/ts.cpp @@ -721,6 +721,7 @@ void checkIppStatus() } } +static bool checkTestData = false; bool skipUnstableTests = false; bool runBigDataTests = false; int testThreads = 0; @@ -733,6 +734,7 @@ void parseCustomOptions(int argc, char **argv) "{ test_threads |-1 |the number of worker threads, if parallel execution is enabled}" "{ skip_unstable |false |skip unstable tests }" "{ test_bigdata |false |run BigData tests (>=2Gb) }" + "{ test_require_data |false |fail on missing non-required test data instead of skip}" "{ h help |false |print help info }"; cv::CommandLineParser parser(argc, argv, command_line_keys); @@ -756,6 +758,7 @@ void parseCustomOptions(int argc, char **argv) skipUnstableTests = parser.get("skip_unstable"); runBigDataTests = parser.get("test_bigdata"); + checkTestData = parser.get("test_require_data"); } @@ -870,7 +873,7 @@ static std::string findData(const std::string& relative_path, bool required, boo #endif #endif const char* type = findDirectory ? "directory" : "data file"; - if (required) + if (required || checkTestData) CV_Error(cv::Error::StsError, cv::format("OpenCV tests: Can't find required %s: %s", type, relative_path.c_str())); throw SkipTestException(cv::format("OpenCV tests: Can't find %s: %s", type, relative_path.c_str())); } From 8ef2d19337af285b4e07883a41a2aa6ec54b80c7 Mon Sep 17 00:00:00 2001 From: HarshDolhare <38937839+HarshDolhare@users.noreply.github.com> Date: Thu, 30 Aug 2018 17:47:27 +0530 Subject: [PATCH 07/47] Adds a small note describing BUILD_opencv_world (#12332) * Added a mall note describing BUILD_opencv_world cmake option to the Installation in Windows tutorial. * Made slight changes in BUILD_opencv_world documentation. * Update windows_install.markdown improved grammar --- .../introduction/windows_install/windows_install.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/tutorials/introduction/windows_install/windows_install.markdown b/doc/tutorials/introduction/windows_install/windows_install.markdown index 7f491d8fdd..0b77e8821f 100644 --- a/doc/tutorials/introduction/windows_install/windows_install.markdown +++ b/doc/tutorials/introduction/windows_install/windows_install.markdown @@ -319,6 +319,7 @@ libraries). If you do not need the support for some of these, you can just freel you are concerned about performance, build them and run. - *BUILD_opencv_python* -\> Self-explanatory. Create the binaries to use OpenCV from the Python language. + - *BUILD_opencv_world* -\> Generate a single "opencv_world" binary (a shared or static library, depending on *BUILD_SHARED_LIBS*) including all the modules instead of a collection of separate binaries, one binary per module. Press again the *Configure* button and ensure no errors are reported. If this is the case, you can tell CMake to create the project files by pushing the *Generate* button. Go to the build From ea43e28a37836dd5e630f471f25f425bb65ecca0 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Wed, 29 Aug 2018 16:33:54 +0300 Subject: [PATCH 08/47] Replace Slice layer to Crop in Faster-RCNN networks from Caffe --- modules/dnn/src/layers/crop_layer.cpp | 45 ++++++++++++++++++- .../dnn/src/layers/fully_connected_layer.cpp | 22 +++++++-- modules/dnn/test/test_caffe_importer.cpp | 13 +++--- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/modules/dnn/src/layers/crop_layer.cpp b/modules/dnn/src/layers/crop_layer.cpp index 8b56f6f439..3572b88337 100644 --- a/modules/dnn/src/layers/crop_layer.cpp +++ b/modules/dnn/src/layers/crop_layer.cpp @@ -41,6 +41,7 @@ //M*/ #include "../precomp.hpp" +#include "../op_inf_engine.hpp" #include "layers_common.hpp" namespace cv @@ -64,6 +65,12 @@ public: } } + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_OPENCV || + backendId == DNN_BACKEND_INFERENCE_ENGINE && crop_ranges.size() == 4; + } + bool getMemoryShapes(const std::vector &inputs, const int requiredOutputs, std::vector &outputs, @@ -109,7 +116,11 @@ public: offset_final[i] = offset[i - start_axis]; } - crop_ranges.resize(dims, Range::all()); + crop_ranges.resize(dims); + for (int i = 0; i < start_axis; i++) + { + crop_ranges[i] = Range(0, inpBlob.size[i]); + } for (int i = start_axis; i < dims; i++) { if (offset_final[i] < 0 || offset_final[i] + inpSzBlob.size[i] > inpBlob.size[i]) @@ -138,6 +149,38 @@ public: input(&crop_ranges[0]).copyTo(output); } + virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE + { +#ifdef HAVE_INF_ENGINE + InferenceEngine::LayerParams lp; + lp.name = name; + lp.type = "Crop"; + lp.precision = InferenceEngine::Precision::FP32; + std::shared_ptr ieLayer(new InferenceEngine::CropLayer(lp)); + + CV_Assert(crop_ranges.size() == 4); + + ieLayer->axis.push_back(0); // batch + ieLayer->offset.push_back(crop_ranges[0].start); + ieLayer->dim.push_back(crop_ranges[0].end - crop_ranges[0].start); + + ieLayer->axis.push_back(1); // channels + ieLayer->offset.push_back(crop_ranges[1].start); + ieLayer->dim.push_back(crop_ranges[1].end - crop_ranges[1].start); + + ieLayer->axis.push_back(3); // height + ieLayer->offset.push_back(crop_ranges[2].start); + ieLayer->dim.push_back(crop_ranges[2].end - crop_ranges[2].start); + + ieLayer->axis.push_back(2); // width + ieLayer->offset.push_back(crop_ranges[3].start); + ieLayer->dim.push_back(crop_ranges[3].end - crop_ranges[3].start); + + return Ptr(new InfEngineBackendNode(ieLayer)); +#endif // HAVE_INF_ENGINE + return Ptr(); + } + std::vector crop_ranges; }; diff --git a/modules/dnn/src/layers/fully_connected_layer.cpp b/modules/dnn/src/layers/fully_connected_layer.cpp index d17ca27383..e40f8c6bdd 100644 --- a/modules/dnn/src/layers/fully_connected_layer.cpp +++ b/modules/dnn/src/layers/fully_connected_layer.cpp @@ -350,17 +350,33 @@ public: inshape = shape(outerSize, innerSize); outshape = shape(outerSize, numOutput); - UMat srcMat, dstMat; + UMat srcMat, dstMat, srcMat_fp32, dstMat_fp32; srcMat = inputs[i].reshape(1, inshape.size(), &inshape[0]); dstMat = outputs[i].reshape(1, outshape.size(), &outshape[0]); - cv::gemm(srcMat, weights, 1, noArray(), 0, dstMat, GEMM_2_T); + if (use_half) + { + convertFp16(srcMat, srcMat_fp32); + convertFp16(dstMat, dstMat_fp32); + } + else + { + srcMat_fp32 = srcMat; + dstMat_fp32 = dstMat; + } + + cv::gemm(srcMat_fp32, weights, 1, noArray(), 0, dstMat_fp32, GEMM_2_T); if (bias) { UMat biasOnesMat = UMat::ones(outerSize, 1, umat_blobs[0].type()); UMat& biases = umat_blobs[1]; - cv::gemm(biasOnesMat, biases, 1, dstMat, 1, dstMat, 0); + cv::gemm(biasOnesMat, biases, 1, dstMat_fp32, 1, dstMat_fp32, 0); + } + if (use_half) + { + convertFp16(srcMat_fp32, srcMat); + convertFp16(dstMat_fp32, dstMat); } } diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index 4491fde5a9..640cb945be 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -490,8 +490,7 @@ INSTANTIATE_TEST_CASE_P(Test_Caffe, opencv_face_detector, TEST_P(Test_Caffe_nets, FasterRCNN_vgg16) { - if ((backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) || - (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)) + if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) throw SkipTestException(""); static Mat ref = (Mat_(3, 7) << 0, 2, 0.949398, 99.2454, 210.141, 601.205, 462.849, 0, 7, 0.997022, 481.841, 92.3218, 722.685, 175.953, @@ -502,8 +501,7 @@ TEST_P(Test_Caffe_nets, FasterRCNN_vgg16) TEST_P(Test_Caffe_nets, FasterRCNN_zf) { if ((backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16) || - (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) || - (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)) + (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) throw SkipTestException(""); static Mat ref = (Mat_(3, 7) << 0, 2, 0.90121, 120.407, 115.83, 570.586, 528.395, 0, 7, 0.988779, 469.849, 75.1756, 718.64, 186.762, @@ -514,12 +512,13 @@ TEST_P(Test_Caffe_nets, FasterRCNN_zf) TEST_P(Test_Caffe_nets, RFCN) { if ((backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16) || - (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) || - (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)) + (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD)) throw SkipTestException(""); + double scoreDiff = (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) ? 4e-3 : default_l1; + double iouDiff = (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) ? 8e-2 : default_lInf; static Mat ref = (Mat_(2, 7) << 0, 7, 0.991359, 491.822, 81.1668, 702.573, 178.234, 0, 12, 0.94786, 132.093, 223.903, 338.077, 566.16); - testFaster("rfcn_pascal_voc_resnet50.prototxt", "resnet50_rfcn_final.caffemodel", ref); + testFaster("rfcn_pascal_voc_resnet50.prototxt", "resnet50_rfcn_final.caffemodel", ref, scoreDiff, iouDiff); } INSTANTIATE_TEST_CASE_P(/**/, Test_Caffe_nets, dnnBackendsAndTargets()); From ade57c8bd047977f604155381c495a16872d2748 Mon Sep 17 00:00:00 2001 From: Nobuo Tsukamoto Date: Fri, 31 Aug 2018 00:21:54 +0900 Subject: [PATCH 09/47] Fixed that object_detection.py does not work in python3. --- samples/dnn/object_detection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/dnn/object_detection.py b/samples/dnn/object_detection.py index 329c349e49..7063fd2abb 100644 --- a/samples/dnn/object_detection.py +++ b/samples/dnn/object_detection.py @@ -142,8 +142,8 @@ def postprocess(frame, outs): center_y = int(detection[1] * frameHeight) width = int(detection[2] * frameWidth) height = int(detection[3] * frameHeight) - left = center_x - width / 2 - top = center_y - height / 2 + left = int(center_x - width / 2) + top = int(center_y - height / 2) classIds.append(classId) confidences.append(float(confidence)) boxes.append([left, top, width, height]) From e86287d8aeb74330677ec1a01968afd2155e048a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 30 Aug 2018 18:53:07 +0300 Subject: [PATCH 10/47] cleanup: IPP Async (IPP_A) except header file with conversion routines (will be removed in OpenCV 4.0) --- cmake/OpenCVFindIPPAsync.cmake | 45 ----- cmake/OpenCVFindLibsPerf.cmake | 11 -- cmake/templates/cvconfig.h.in | 3 - doc/Doxyfile.in | 1 - .../how_to_use_OpenCV_parallel_for_.markdown | 2 +- .../how_to_use_ippa_conversion.markdown | 146 --------------- .../images/How_To_Use_IPPA_Result.jpg | Bin 62891 -> 0 bytes doc/tutorials/core/images/How_To_Use_IPPA.jpg | Bin 6991 -> 0 bytes .../interoperability_with_OpenCV_1.markdown | 2 +- .../core/table_of_content_core.markdown | 9 - .../windows_install/windows_install.markdown | 6 - .../core/include/opencv2/core/ippasync.hpp | 2 +- modules/core/test/test_ippasync.cpp | 171 ------------------ samples/cpp/CMakeLists.txt | 3 - .../core/ippasync/ippasync_sample.cpp | 168 ----------------- 15 files changed, 3 insertions(+), 566 deletions(-) delete mode 100644 cmake/OpenCVFindIPPAsync.cmake delete mode 100644 doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.markdown delete mode 100644 doc/tutorials/core/how_to_use_ippa_conversion/images/How_To_Use_IPPA_Result.jpg delete mode 100644 doc/tutorials/core/images/How_To_Use_IPPA.jpg delete mode 100644 modules/core/test/test_ippasync.cpp delete mode 100644 samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp diff --git a/cmake/OpenCVFindIPPAsync.cmake b/cmake/OpenCVFindIPPAsync.cmake deleted file mode 100644 index 6f4765cbc9..0000000000 --- a/cmake/OpenCVFindIPPAsync.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# Main variables: -# IPP_A_LIBRARIES and IPP_A_INCLUDE to use IPP Async -# HAVE_IPP_A for conditional compilation OpenCV with/without IPP Async - -# IPP_ASYNC_ROOT - root of IPP Async installation - -if(X86_64) - find_path( - IPP_A_INCLUDE_DIR - NAMES ipp_async_defs.h - PATHS $ENV{IPP_ASYNC_ROOT} - PATH_SUFFIXES include - DOC "Path to Intel IPP Async interface headers") - - find_file( - IPP_A_LIBRARIES - NAMES ipp_async_preview.lib - PATHS $ENV{IPP_ASYNC_ROOT} - PATH_SUFFIXES lib/intel64 - DOC "Path to Intel IPP Async interface libraries") - -else() - find_path( - IPP_A_INCLUDE_DIR - NAMES ipp_async_defs.h - PATHS $ENV{IPP_ASYNC_ROOT} - PATH_SUFFIXES include - DOC "Path to Intel IPP Async interface headers") - - find_file( - IPP_A_LIBRARIES - NAMES ipp_async_preview.lib - PATHS $ENV{IPP_ASYNC_ROOT} - PATH_SUFFIXES lib/ia32 - DOC "Path to Intel IPP Async interface libraries") -endif() - -if(IPP_A_INCLUDE_DIR AND IPP_A_LIBRARIES) - set(HAVE_IPP_A TRUE) -else() - set(HAVE_IPP_A FALSE) - message(WARNING "Intel IPP Async library directory (set by IPP_A_LIBRARIES_DIR variable) is not found or does not have Intel IPP Async libraries.") -endif() - -mark_as_advanced(FORCE IPP_A_LIBRARIES IPP_A_INCLUDE_DIR) \ No newline at end of file diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index 4dfd7aab4b..84fc79df6f 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -28,17 +28,6 @@ if(WITH_IPP) endif() endif() -# --- IPP Async --- - -if(WITH_IPP_A) - include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindIPPAsync.cmake") - if(IPP_A_INCLUDE_DIR AND IPP_A_LIBRARIES) - ocv_include_directories(${IPP_A_INCLUDE_DIR}) - link_directories(${IPP_A_LIBRARIES}) - set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${IPP_A_LIBRARIES}) - endif() -endif(WITH_IPP_A) - # --- CUDA --- if(WITH_CUDA) include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDA.cmake") diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index c661a20abd..0606a40a6b 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -106,9 +106,6 @@ #cmakedefine HAVE_IPP_ICV #cmakedefine HAVE_IPP_IW -/* Intel IPP Async */ -#cmakedefine HAVE_IPP_A - /* JPEG-2000 codec */ #cmakedefine HAVE_JASPER diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 1ac09c4351..8f9d02d811 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -227,7 +227,6 @@ SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = __cplusplus=1 \ - HAVE_IPP_A=1 \ CVAPI(x)=x \ CV_DOXYGEN= \ CV_EXPORTS= \ diff --git a/doc/tutorials/core/how_to_use_OpenCV_parallel_for_/how_to_use_OpenCV_parallel_for_.markdown b/doc/tutorials/core/how_to_use_OpenCV_parallel_for_/how_to_use_OpenCV_parallel_for_.markdown index 3f5f556d35..eeeb94b4c4 100644 --- a/doc/tutorials/core/how_to_use_OpenCV_parallel_for_/how_to_use_OpenCV_parallel_for_.markdown +++ b/doc/tutorials/core/how_to_use_OpenCV_parallel_for_/how_to_use_OpenCV_parallel_for_.markdown @@ -1,7 +1,7 @@ How to use the OpenCV parallel_for_ to parallelize your code {#tutorial_how_to_use_OpenCV_parallel_for_} ================================================================== -@prev_tutorial{tutorial_how_to_use_ippa_conversion} +@prev_tutorial{tutorial_interoperability_with_OpenCV_1} Goal ---- diff --git a/doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.markdown b/doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.markdown deleted file mode 100644 index e5e0e4a1eb..0000000000 --- a/doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.markdown +++ /dev/null @@ -1,146 +0,0 @@ -Intel® IPP Asynchronous C/C++ library in OpenCV {#tutorial_how_to_use_ippa_conversion} -=============================================== - -@prev_tutorial{tutorial_interoperability_with_OpenCV_1} -@next_tutorial{tutorial_how_to_use_OpenCV_parallel_for_} - -Goal ----- - -The tutorial demonstrates the [Intel® IPP Asynchronous -C/C++](http://software.intel.com/en-us/intel-ipp-preview) library usage with OpenCV. The code -example below illustrates implementation of the Sobel operation, accelerated with Intel® IPP -Asynchronous C/C++ functions. In this code example, @ref cv::hpp::getMat and @ref cv::hpp::getHpp -functions are used for data conversion between -[hppiMatrix](http://software.intel.com/en-us/node/501660) and Mat matrices. - -Code ----- - -You may also find the source code in the -`samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp` file of the OpenCV source library or -download it from [here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp). - -@include cpp/tutorial_code/core/ippasync/ippasync_sample.cpp - -Explanation ------------ - --# Create parameters for OpenCV: - @code{.cpp} - VideoCapture cap; - Mat image, gray, result; - @endcode - and IPP Async: - @code{.cpp} - hppiMatrix* src,* dst; - hppAccel accel = 0; - hppAccelType accelType; - hppStatus sts; - hppiVirtualMatrix * virtMatrix; - @endcode --# Load input image or video. How to open and read video stream you can see in the - @ref tutorial_video_input_psnr_ssim tutorial. - @code{.cpp} - if( useCamera ) - { - printf("used camera\n"); - cap.open(0); - } - else - { - printf("used image %s\n", file.c_str()); - cap.open(file.c_str()); - } - - if( !cap.isOpened() ) - { - printf("can not open camera or video file\n"); - return -1; - } - @endcode --# Create accelerator instance using - [hppCreateInstance](http://software.intel.com/en-us/node/501686): - @code{.cpp} - accelType = sAccel == "cpu" ? HPP_ACCEL_TYPE_CPU: - sAccel == "gpu" ? HPP_ACCEL_TYPE_GPU: - HPP_ACCEL_TYPE_ANY; - - //Create accelerator instance - sts = hppCreateInstance(accelType, 0, &accel); - CHECK_STATUS(sts, "hppCreateInstance"); - @endcode --# Create an array of virtual matrices using - [hppiCreateVirtualMatrices](http://software.intel.com/en-us/node/501700) function. - @code{.cpp} - virtMatrix = hppiCreateVirtualMatrices(accel, 1); - @endcode --# Prepare a matrix for input and output data: - @code{.cpp} - cap >> image; - if(image.empty()) - break; - - cvtColor( image, gray, COLOR_BGR2GRAY ); - - result.create( image.rows, image.cols, CV_8U); - @endcode --# Convert Mat to [hppiMatrix](http://software.intel.com/en-us/node/501660) using @ref cv::hpp::getHpp - and call [hppiSobel](http://software.intel.com/en-us/node/474701) function. - @code{.cpp} - //convert Mat to hppiMatrix - src = getHpp(gray, accel); - dst = getHpp(result, accel); - - sts = hppiSobel(accel,src, HPP_MASK_SIZE_3X3,HPP_NORM_L1,virtMatrix[0]); - CHECK_STATUS(sts,"hppiSobel"); - - sts = hppiConvert(accel, virtMatrix[0], 0, HPP_RND_MODE_NEAR, dst, HPP_DATA_TYPE_8U); - CHECK_STATUS(sts,"hppiConvert"); - - // Wait for tasks to complete - sts = hppWait(accel, HPP_TIME_OUT_INFINITE); - CHECK_STATUS(sts, "hppWait"); - @endcode - We use [hppiConvert](http://software.intel.com/en-us/node/501746) because - [hppiSobel](http://software.intel.com/en-us/node/474701) returns destination matrix with - HPP_DATA_TYPE_16S data type for source matrix with HPP_DATA_TYPE_8U type. You should check - hppStatus after each call IPP Async function. - --# Create windows and show the images, the usual way. - @code{.cpp} - imshow("image", image); - imshow("rez", result); - - waitKey(15); - @endcode --# Delete hpp matrices. - @code{.cpp} - sts = hppiFreeMatrix(src); - CHECK_DEL_STATUS(sts,"hppiFreeMatrix"); - - sts = hppiFreeMatrix(dst); - CHECK_DEL_STATUS(sts,"hppiFreeMatrix"); - @endcode --# Delete virtual matrices and accelerator instance. - @code{.cpp} - if (virtMatrix) - { - sts = hppiDeleteVirtualMatrices(accel, virtMatrix); - CHECK_DEL_STATUS(sts,"hppiDeleteVirtualMatrices"); - } - - if (accel) - { - sts = hppDeleteInstance(accel); - CHECK_DEL_STATUS(sts, "hppDeleteInstance"); - } - @endcode - -Result ------- - -After compiling the code above we can execute it giving an image or video path and accelerator type -as an argument. For this tutorial we use baboon.png image as input. The result is below. - -![](images/How_To_Use_IPPA_Result.jpg) diff --git a/doc/tutorials/core/how_to_use_ippa_conversion/images/How_To_Use_IPPA_Result.jpg b/doc/tutorials/core/how_to_use_ippa_conversion/images/How_To_Use_IPPA_Result.jpg deleted file mode 100644 index 2bdeb830cb4309307fb476b860d44c6db1793b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62891 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 OpenCV 2.0 - - *Author:* Elena Gvozdeva - - You will see how to use the IPP Async with OpenCV. - - - @subpage tutorial_how_to_use_OpenCV_parallel_for_ *Compatibility:* \>= OpenCV 2.4.3 diff --git a/doc/tutorials/introduction/windows_install/windows_install.markdown b/doc/tutorials/introduction/windows_install/windows_install.markdown index 0b77e8821f..c8f46cbdce 100644 --- a/doc/tutorials/introduction/windows_install/windows_install.markdown +++ b/doc/tutorials/introduction/windows_install/windows_install.markdown @@ -142,8 +142,6 @@ of them, you need to download and install them on your system. - [Intel Integrated Performance Primitives (*IPP*)](http://software.intel.com/en-us/articles/intel-ipp/) may be used to improve the performance of color conversion, Haar training and DFT functions of the OpenCV library. Watch out, since this is not a free service. -- [Intel IPP Asynchronous C/C++](http://software.intel.com/en-us/intel-ipp-preview) is currently focused delivering Intel Graphics - support for advanced image processing and computer vision functions. - OpenCV offers a somewhat fancier and more useful graphical user interface, than the default one by using the [Qt framework](http://qt.nokia.com/downloads). For a quick overview of what this has to offer, look into the documentations *highgui* module, under the *Qt New Functions* section. Version 4.6 or later of @@ -204,10 +202,6 @@ libraries). If you do not need the support for some of these, you can just freel ![](images/IntelTBB.png) - -# For the [Intel IPP Asynchronous C/C++](http://software.intel.com/en-us/intel-ipp-preview) download the source files and set environment - variable **IPP_ASYNC_ROOT**. It should point to - `/Intel/IPP Preview */ipp directory`. Here \* denotes the - particular preview name. -# In case of the [Eigen](http://eigen.tuxfamily.org/index.php?title=Main_Page#Download) library it is again a case of download and extract to the `D:/OpenCV/dep` directory. -# Same as above with [OpenEXR](http://www.openexr.com/downloads.html). diff --git a/modules/core/include/opencv2/core/ippasync.hpp b/modules/core/include/opencv2/core/ippasync.hpp index 0ed8264e14..c35d8d81a1 100644 --- a/modules/core/include/opencv2/core/ippasync.hpp +++ b/modules/core/include/opencv2/core/ippasync.hpp @@ -45,7 +45,7 @@ #ifndef OPENCV_CORE_IPPASYNC_HPP #define OPENCV_CORE_IPPASYNC_HPP -#ifdef HAVE_IPP_A +#ifdef HAVE_IPP_A // this file will be removed in OpenCV 4.0 #include "opencv2/core.hpp" #include diff --git a/modules/core/test/test_ippasync.cpp b/modules/core/test/test_ippasync.cpp deleted file mode 100644 index d52f5c419d..0000000000 --- a/modules/core/test/test_ippasync.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// 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. -#include "test_precomp.hpp" -#include "opencv2/ts/ocl_test.hpp" - -#ifdef HAVE_IPP_A -#include "opencv2/core/ippasync.hpp" - -using namespace cv; -using namespace std; -using namespace opencv_test; - -namespace opencv_test { -namespace ocl { - -PARAM_TEST_CASE(IPPAsync, MatDepth, Channels, hppAccelType) -{ - int type; - int cn; - int depth; - hppAccelType accelType; - - Mat matrix, result; - hppiMatrix * hppMat; - hppAccel accel; - hppiVirtualMatrix * virtMatrix; - hppStatus sts; - - virtual void SetUp() - { - type = CV_MAKE_TYPE(GET_PARAM(0), GET_PARAM(1)); - depth = GET_PARAM(0); - cn = GET_PARAM(1); - accelType = GET_PARAM(2); - } - - void generateTestData() - { - Size matrix_Size = randomSize(2, 100); - const double upValue = 100; - - matrix = randomMat(matrix_Size, type, -upValue, upValue); - } - - void Near(double threshold = 0.0) - { - EXPECT_MAT_NEAR(matrix, result, threshold); - } -}; - -TEST_P(IPPAsync, accuracy) -{ - sts = hppCreateInstance(accelType, 0, &accel); - if (sts!=HPP_STATUS_NO_ERROR) printf("hppStatus = %d\n",sts); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - - virtMatrix = hppiCreateVirtualMatrices(accel, 2); - - for (int j = 0; j < test_loop_times; j++) - { - generateTestData(); - hppMat = hpp::getHpp(matrix,accel); - - hppScalar a = 3; - - sts = hppiAddC(accel, hppMat, a, 0, virtMatrix[0]); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - sts = hppiSubC(accel, virtMatrix[0], a, 0, virtMatrix[1]); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - - sts = hppWait(accel, HPP_TIME_OUT_INFINITE); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - - result = hpp::getMat(virtMatrix[1], accel, cn); - - Near(5.0e-6); - - sts = hppiFreeMatrix(hppMat); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - } - - sts = hppiDeleteVirtualMatrices(accel, virtMatrix); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - sts = hppDeleteInstance(accel); - CV_Assert(sts==HPP_STATUS_NO_ERROR); -} - -PARAM_TEST_CASE(IPPAsyncShared, Channels, hppAccelType) -{ - int cn; - int type; - hppAccelType accelType; - - Mat matrix, result; - hppiMatrix* hppMat; - hppAccel accel; - hppiVirtualMatrix * virtMatrix; - hppStatus sts; - - virtual void SetUp() - { - cn = GET_PARAM(0); - accelType = GET_PARAM(1); - type=CV_MAKE_TYPE(CV_8U, GET_PARAM(0)); - } - - void generateTestData() - { - Size matrix_Size = randomSize(2, 100); - hpp32u pitch, size; - const int upValue = 100; - - sts = hppQueryMatrixAllocParams(accel, (hpp32u)(matrix_Size.width*cn), (hpp32u)matrix_Size.height, HPP_DATA_TYPE_8U, &pitch, &size); - - matrix = randomMat(matrix_Size, type, 0, upValue); - } - - void Near(double threshold = 0.0) - { - EXPECT_MAT_NEAR(matrix, result, threshold); - } -}; - -TEST_P(IPPAsyncShared, accuracy) -{ - sts = hppCreateInstance(accelType, 0, &accel); - if (sts!=HPP_STATUS_NO_ERROR) printf("hppStatus = %d\n",sts); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - - virtMatrix = hppiCreateVirtualMatrices(accel, 2); - - for (int j = 0; j < test_loop_times; j++) - { - generateTestData(); - hppMat = hpp::getHpp(matrix,accel); - - hppScalar a = 3; - - sts = hppiAddC(accel, hppMat, a, 0, virtMatrix[0]); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - sts = hppiSubC(accel, virtMatrix[0], a, 0, virtMatrix[1]); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - - sts = hppWait(accel, HPP_TIME_OUT_INFINITE); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - - result = hpp::getMat(virtMatrix[1], accel, cn); - - Near(0); - - sts = hppiFreeMatrix(hppMat); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - } - - sts = hppiDeleteVirtualMatrices(accel, virtMatrix); - CV_Assert(sts==HPP_STATUS_NO_ERROR); - sts = hppDeleteInstance(accel); - CV_Assert(sts==HPP_STATUS_NO_ERROR); -} - -INSTANTIATE_TEST_CASE_P(IppATest, IPPAsyncShared, Combine(Values(1, 2, 3, 4), - Values( HPP_ACCEL_TYPE_CPU, HPP_ACCEL_TYPE_GPU))); - -INSTANTIATE_TEST_CASE_P(IppATest, IPPAsync, Combine(Values(CV_8U, CV_16U, CV_16S, CV_32F), - Values(1, 2, 3, 4), - Values( HPP_ACCEL_TYPE_CPU, HPP_ACCEL_TYPE_GPU))); - -} -} -#endif \ No newline at end of file diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index a96b9e6ad8..d12cc9d7d6 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -36,9 +36,6 @@ endif() if(NOT BUILD_opencv_viz OR NOT VTK_USE_FILE) ocv_list_filterout(cpp_samples "/viz/") endif() -if(NOT HAVE_IPP_A) - ocv_list_filterout(cpp_samples "/ippasync/") -endif() ocv_list_filterout(cpp_samples "real_time_pose_estimation/") foreach(sample_filename ${cpp_samples}) if(sample_filename MATCHES "/viz/") diff --git a/samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp b/samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp deleted file mode 100644 index 0a25f15d12..0000000000 --- a/samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include - -#include "opencv2/core/utility.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" -#include "cvconfig.h" - -using namespace std; -using namespace cv; - -#ifdef HAVE_IPP_A -#include "opencv2/core/ippasync.hpp" - -#define CHECK_STATUS(STATUS, NAME)\ - if(STATUS!=HPP_STATUS_NO_ERROR){ printf("%s error %d\n", NAME, STATUS);\ - if (virtMatrix) {hppStatus delSts = hppiDeleteVirtualMatrices(accel, virtMatrix); CHECK_DEL_STATUS(delSts,"hppiDeleteVirtualMatrices");}\ - if (accel) {hppStatus delSts = hppDeleteInstance(accel); CHECK_DEL_STATUS(delSts, "hppDeleteInstance");}\ - return -1;} - -#define CHECK_DEL_STATUS(STATUS, NAME)\ - if(STATUS!=HPP_STATUS_NO_ERROR){ printf("%s error %d\n", NAME, STATUS); return -1;} - -#endif - -static void help() -{ - printf("\nThis program shows how to use the conversion for IPP Async.\n" -"This example uses the Sobel filter.\n" -"You can use cv::Sobel or hppiSobel.\n" -"Usage: \n" -"./ipp_async_sobel [--camera]=, \n" -" [--file_name]=\n" -" [--accel]=\n\n"); -} - -const char* keys = -{ - "{c camera | | use camera or not}" - "{fn file_name|../data/baboon.jpg | image file }" - "{a accel |auto | accelerator type: auto (default), cpu, gpu}" -}; - -//this is a sample for hppiSobel functions -int main(int argc, const char** argv) -{ - help(); - - VideoCapture cap; - CommandLineParser parser(argc, argv, keys); - Mat image, gray, result; - -#ifdef HAVE_IPP_A - - hppiMatrix* src,* dst; - hppAccel accel = 0; - hppAccelType accelType; - hppStatus sts; - hppiVirtualMatrix * virtMatrix; - - bool useCamera = parser.has("camera"); - string file = parser.get("file_name"); - string sAccel = parser.get("accel"); - - parser.printMessage(); - - if( useCamera ) - { - printf("used camera\n"); - cap.open(0); - } - else - { - printf("used image %s\n", file.c_str()); - cap.open(file.c_str()); - } - - if( !cap.isOpened() ) - { - printf("can not open camera or video file\n"); - return -1; - } - - accelType = sAccel == "cpu" ? HPP_ACCEL_TYPE_CPU: - sAccel == "gpu" ? HPP_ACCEL_TYPE_GPU: - HPP_ACCEL_TYPE_ANY; - - //Create accelerator instance - sts = hppCreateInstance(accelType, 0, &accel); - CHECK_STATUS(sts, "hppCreateInstance"); - - accelType = hppQueryAccelType(accel); - - sAccel = accelType == HPP_ACCEL_TYPE_CPU ? "cpu": - accelType == HPP_ACCEL_TYPE_GPU ? "gpu": - accelType == HPP_ACCEL_TYPE_GPU_VIA_DX9 ? "gpu dx9": "?"; - - printf("accelType %s\n", sAccel.c_str()); - - virtMatrix = hppiCreateVirtualMatrices(accel, 1); - - for(;;) - { - cap >> image; - if(image.empty()) - break; - - cvtColor( image, gray, COLOR_BGR2GRAY ); - - result.create( image.rows, image.cols, CV_8U); - - double execTime = (double)getTickCount(); - - //convert Mat to hppiMatrix - src = hpp::getHpp(gray,accel); - dst = hpp::getHpp(result,accel); - - sts = hppiSobel(accel,src, HPP_MASK_SIZE_3X3,HPP_NORM_L1,virtMatrix[0]); - CHECK_STATUS(sts,"hppiSobel"); - - sts = hppiConvert(accel, virtMatrix[0], 0, HPP_RND_MODE_NEAR, dst, HPP_DATA_TYPE_8U); - CHECK_STATUS(sts,"hppiConvert"); - - // Wait for tasks to complete - sts = hppWait(accel, HPP_TIME_OUT_INFINITE); - CHECK_STATUS(sts, "hppWait"); - - execTime = ((double)getTickCount() - execTime)*1000./getTickFrequency(); - - printf("Time : %0.3fms\n", execTime); - - imshow("image", image); - imshow("rez", result); - - waitKey(15); - - sts = hppiFreeMatrix(src); - CHECK_DEL_STATUS(sts,"hppiFreeMatrix"); - - sts = hppiFreeMatrix(dst); - CHECK_DEL_STATUS(sts,"hppiFreeMatrix"); - - } - - if (!useCamera) - waitKey(0); - - if (virtMatrix) - { - sts = hppiDeleteVirtualMatrices(accel, virtMatrix); - CHECK_DEL_STATUS(sts,"hppiDeleteVirtualMatrices"); - } - - if (accel) - { - sts = hppDeleteInstance(accel); - CHECK_DEL_STATUS(sts, "hppDeleteInstance"); - } - - printf("SUCCESS\n"); - -#else - - printf("IPP Async not supported\n"); - -#endif - - return 0; -} From e1ee744e1552b44b51bc61c393cc947c79506743 Mon Sep 17 00:00:00 2001 From: take1014 Date: Fri, 31 Aug 2018 00:59:00 +0900 Subject: [PATCH 11/47] resolve #5788 --- .../imgproc/misc/java/test/Subdiv2DTest.java | 2 +- modules/imgproc/src/subdivision2d.cpp | 4 +- modules/imgproc/test/test_subdivision2d.cpp | 53 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 modules/imgproc/test/test_subdivision2d.cpp diff --git a/modules/imgproc/misc/java/test/Subdiv2DTest.java b/modules/imgproc/misc/java/test/Subdiv2DTest.java index 630d317378..1f27bc641c 100644 --- a/modules/imgproc/misc/java/test/Subdiv2DTest.java +++ b/modules/imgproc/misc/java/test/Subdiv2DTest.java @@ -52,7 +52,7 @@ public class Subdiv2DTest extends OpenCVTestCase { s2d.insert( new Point(10, 20) ); MatOfFloat6 triangles = new MatOfFloat6(); s2d.getTriangleList(triangles); - assertEquals(10, triangles.rows()); + assertEquals(2, triangles.rows()); /* int cnt = triangles.rows(); float buff[] = new float[cnt*6]; diff --git a/modules/imgproc/src/subdivision2d.cpp b/modules/imgproc/src/subdivision2d.cpp index 93c8b4c91b..596806c3ea 100644 --- a/modules/imgproc/src/subdivision2d.cpp +++ b/modules/imgproc/src/subdivision2d.cpp @@ -758,6 +758,7 @@ void Subdiv2D::getTriangleList(std::vector& triangleList) const triangleList.clear(); int i, total = (int)(qedges.size()*4); std::vector edgemask(total, false); + Rect2f rect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); for( i = 4; i < total; i += 2 ) { @@ -773,7 +774,8 @@ void Subdiv2D::getTriangleList(std::vector& triangleList) const edge = getEdge(edge, NEXT_AROUND_LEFT); edgeOrg(edge, &c); edgemask[edge] = true; - triangleList.push_back(Vec6f(a.x, a.y, b.x, b.y, c.x, c.y)); + if( rect.contains(a) && rect.contains(b) && rect.contains(c) ) + triangleList.push_back(Vec6f(a.x, a.y, b.x, b.y, c.x, c.y)); } } diff --git a/modules/imgproc/test/test_subdivision2d.cpp b/modules/imgproc/test/test_subdivision2d.cpp new file mode 100644 index 0000000000..0a366206b3 --- /dev/null +++ b/modules/imgproc/test/test_subdivision2d.cpp @@ -0,0 +1,53 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// 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. +//M*/ +#include "test_precomp.hpp" + +namespace opencv_test { namespace { +TEST(Imgproc_Subdiv2D_getTriangleList, regression_5788) +{ + const float points[65][2] = { + { 390, 802}, { 397, 883}, { 414, 963 }, { 439, 1042 }, { 472, 1113}, + { 521, 1181}, { 591, 1238}, { 678, 1284 }, { 771, 1292 }, { 853, 1281}, + { 921, 1243}, { 982, 1191}, {1030, 1121 }, {1059, 1038 }, {1072, 945}, + {1081, 849}, {1082, 749}, { 459, 734 }, { 502, 704 }, { 554, 696}, + { 609, 698}, { 660, 707}, { 818, 688 }, { 874, 661 }, { 929, 646}, + { 982, 653}, {1026, 682}, { 740, 771 }, { 748, 834 }, { 756, 897}, + { 762, 960}, { 700, 998}, { 733, 1006 }, { 766, 1011 }, { 797, 999}, + { 825, 987}, { 528, 796}, { 566, 766 }, { 617, 763 }, { 659, 794}, + { 619, 808}, { 569, 812}, { 834, 777 }, { 870, 735 }, { 918, 729}, + { 958, 750}, { 929, 773}, { 882, 780 }, { 652, 1102 }, { 701, 1079}, + { 743, 1063}, { 774, 1068}, { 807, 1057 }, { 852, 1065 }, { 896, 1077}, + { 860, 1117}, { 820, 1135}, { 783, 1141 }, { 751, 1140 }, { 706, 1130}, + { 675, 1102}, { 743, 1094}, { 774, 1094 }, { 809, 1088 }, { 878, 1082} + }; + std::vector pts; + cv::Rect rect(0, 0, 1500, 2000); + cv::Subdiv2D subdiv(rect); + for( int i = 0; i < 65; i++ ) + { + cv::Point2f pt(points[i][0], points[i][1]); + pts.push_back(pt); + } + + subdiv.insert(pts); + + std::vector triangles; + subdiv.getTriangleList(triangles); + + int trig_cnt = 0; + for( std::vector::const_iterator it = triangles.begin(); it != triangles.end(); it++, trig_cnt++ ) + { + EXPECT_TRUE( (0 <= triangles.at(trig_cnt).val[0] && triangles.at(trig_cnt).val[0] < 1500) && + (0 <= triangles.at(trig_cnt).val[1] && triangles.at(trig_cnt).val[1] < 2000) && + (0 <= triangles.at(trig_cnt).val[2] && triangles.at(trig_cnt).val[2] < 1500) && + (0 <= triangles.at(trig_cnt).val[3] && triangles.at(trig_cnt).val[3] < 2000) && + (0 <= triangles.at(trig_cnt).val[4] && triangles.at(trig_cnt).val[4] < 1500) && + (0 <= triangles.at(trig_cnt).val[5] && triangles.at(trig_cnt).val[5] < 2000) ); + } + EXPECT_EQ(trig_cnt, 105); +} + +}}; From c894fc5bae40bac7e6af17b29b532c8ee34f1424 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Wed, 29 Aug 2018 20:31:15 +0300 Subject: [PATCH 12/47] countNonZero function reworked to use wide universal intrinsics instead of SSE2 intrinsics --- modules/core/src/count_non_zero.cpp | 266 +++++++++++----------------- 1 file changed, 101 insertions(+), 165 deletions(-) diff --git a/modules/core/src/count_non_zero.cpp b/modules/core/src/count_non_zero.cpp index 368dcfc3a6..142a4983c1 100644 --- a/modules/core/src/count_non_zero.cpp +++ b/modules/core/src/count_non_zero.cpp @@ -25,51 +25,34 @@ static int countNonZero_(const T* src, int len ) static int countNonZero8u( const uchar* src, int len ) { int i=0, nz = 0; -#if CV_SSE2 - if(USE_SSE2)//5x-6x - { - __m128i v_zero = _mm_setzero_si128(); - __m128i sum = _mm_setzero_si128(); - - for (; i<=len-16; i+=16) - { - __m128i r0 = _mm_loadu_si128((const __m128i*)(src+i)); - sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_cmpeq_epi8(r0, v_zero)), v_zero)); - } - nz = i - _mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))); - } -#elif CV_NEON - int len0 = len & -16, blockSize1 = (1 << 8) - 16, blockSize0 = blockSize1 << 6; - uint32x4_t v_nz = vdupq_n_u32(0u); - uint8x16_t v_zero = vdupq_n_u8(0), v_1 = vdupq_n_u8(1); - const uchar * src0 = src; +#if CV_SIMD + int len0 = len & -v_uint8::nlanes; + v_uint8 v_zero = vx_setzero_u8(); + v_uint8 v_one = vx_setall_u8(1); - while( i < len0 ) + v_uint32 v_sum32 = vx_setzero_u32(); + while (i < len0) { - int blockSizei = std::min(len0 - i, blockSize0), j = 0; - - while (j < blockSizei) + v_uint16 v_sum16 = vx_setzero_u16(); + int j = i; + while (j < std::min(len0, i + 65280 * v_uint16::nlanes)) { - int blockSizej = std::min(blockSizei - j, blockSize1), k = 0; - uint8x16_t v_pz = v_zero; - - for( ; k <= blockSizej - 16; k += 16 ) - v_pz = vaddq_u8(v_pz, vandq_u8(vceqq_u8(vld1q_u8(src0 + k), v_zero), v_1)); - - uint16x8_t v_p1 = vmovl_u8(vget_low_u8(v_pz)), v_p2 = vmovl_u8(vget_high_u8(v_pz)); - v_nz = vaddq_u32(vaddl_u16(vget_low_u16(v_p1), vget_high_u16(v_p1)), v_nz); - v_nz = vaddq_u32(vaddl_u16(vget_low_u16(v_p2), vget_high_u16(v_p2)), v_nz); - - src0 += blockSizej; - j += blockSizej; + v_uint8 v_sum8 = vx_setzero_u8(); + int k = j; + for (; k < std::min(len0, j + 255 * v_uint8::nlanes); k += v_uint8::nlanes) + v_sum8 += v_one & (vx_load(src + k) == v_zero); + v_uint16 part1, part2; + v_expand(v_sum8, part1, part2); + v_sum16 += part1 + part2; + j = k; } - - i += blockSizei; + v_uint32 part1, part2; + v_expand(v_sum16, part1, part2); + v_sum32 += part1 + part2; + i = j; } - - CV_DECL_ALIGNED(16) unsigned int buf[4]; - vst1q_u32(buf, v_nz); - nz += i - saturate_cast(buf[0] + buf[1] + buf[2] + buf[3]); + nz = i - v_reduce_sum(v_sum32); + v_cleanup(); #endif for( ; i < len; i++ ) nz += src[i] != 0; @@ -79,159 +62,112 @@ static int countNonZero8u( const uchar* src, int len ) static int countNonZero16u( const ushort* src, int len ) { int i = 0, nz = 0; -#if CV_SSE2 - if (USE_SSE2) - { - __m128i v_zero = _mm_setzero_si128 (); - __m128i sum = _mm_setzero_si128(); +#if CV_SIMD + int len0 = len & -v_int8::nlanes; + v_uint16 v_zero = vx_setzero_u16(); + v_int8 v_one = vx_setall_s8(1); - for ( ; i <= len - 8; i += 8) - { - __m128i r0 = _mm_loadu_si128((const __m128i*)(src + i)); - sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_cmpeq_epi16(r0, v_zero)), v_zero)); - } - - nz = i - (_mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))) >> 1); - src += i; - } -#elif CV_NEON - int len0 = len & -8, blockSize1 = (1 << 15), blockSize0 = blockSize1 << 6; - uint32x4_t v_nz = vdupq_n_u32(0u); - uint16x8_t v_zero = vdupq_n_u16(0), v_1 = vdupq_n_u16(1); - - while( i < len0 ) + v_int32 v_sum32 = vx_setzero_s32(); + while (i < len0) { - int blockSizei = std::min(len0 - i, blockSize0), j = 0; - - while (j < blockSizei) + v_int16 v_sum16 = vx_setzero_s16(); + int j = i; + while (j < std::min(len0, i + 32766 * v_int16::nlanes)) { - int blockSizej = std::min(blockSizei - j, blockSize1), k = 0; - uint16x8_t v_pz = v_zero; - - for( ; k <= blockSizej - 8; k += 8 ) - v_pz = vaddq_u16(v_pz, vandq_u16(vceqq_u16(vld1q_u16(src + k), v_zero), v_1)); - - v_nz = vaddq_u32(vaddl_u16(vget_low_u16(v_pz), vget_high_u16(v_pz)), v_nz); - - src += blockSizej; - j += blockSizej; + v_int8 v_sum8 = vx_setzero_s8(); + int k = j; + for (; k < std::min(len0, j + 127 * v_int8::nlanes); k += v_int8::nlanes) + v_sum8 += v_one & v_pack(v_reinterpret_as_s16(vx_load(src + k) == v_zero), v_reinterpret_as_s16(vx_load(src + k + v_uint16::nlanes) == v_zero)); + v_int16 part1, part2; + v_expand(v_sum8, part1, part2); + v_sum16 += part1 + part2; + j = k; } - - i += blockSizei; + v_int32 part1, part2; + v_expand(v_sum16, part1, part2); + v_sum32 += part1 + part2; + i = j; } - - CV_DECL_ALIGNED(16) unsigned int buf[4]; - vst1q_u32(buf, v_nz); - nz += i - saturate_cast(buf[0] + buf[1] + buf[2] + buf[3]); + nz = i - v_reduce_sum(v_sum32); + v_cleanup(); #endif - return nz + countNonZero_(src, len - i); + return nz + countNonZero_(src + i, len - i); } static int countNonZero32s( const int* src, int len ) { int i = 0, nz = 0; -#if CV_SSE2 - if (USE_SSE2) - { - __m128i v_zero = _mm_setzero_si128 (); - __m128i sum = _mm_setzero_si128(); +#if CV_SIMD + int len0 = len & -v_int8::nlanes; + v_int32 v_zero = vx_setzero_s32(); + v_int8 v_one = vx_setall_s8(1); - for ( ; i <= len - 4; i += 4) - { - __m128i r0 = _mm_loadu_si128((const __m128i*)(src + i)); - sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_cmpeq_epi32(r0, v_zero)), v_zero)); - } - - nz = i - (_mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))) >> 2); - src += i; - } -#elif CV_NEON - int len0 = len & -8, blockSize1 = (1 << 15), blockSize0 = blockSize1 << 6; - uint32x4_t v_nz = vdupq_n_u32(0u); - int32x4_t v_zero = vdupq_n_s32(0.0f); - uint16x8_t v_1 = vdupq_n_u16(1u), v_zerou = vdupq_n_u16(0u); - - while( i < len0 ) + v_int32 v_sum32 = vx_setzero_s32(); + while (i < len0) { - int blockSizei = std::min(len0 - i, blockSize0), j = 0; - - while (j < blockSizei) + v_int16 v_sum16 = vx_setzero_s16(); + int j = i; + while (j < std::min(len0, i + 32766 * v_int16::nlanes)) { - int blockSizej = std::min(blockSizei - j, blockSize1), k = 0; - uint16x8_t v_pz = v_zerou; - - for( ; k <= blockSizej - 8; k += 8 ) - v_pz = vaddq_u16(v_pz, vandq_u16(vcombine_u16(vmovn_u32(vceqq_s32(vld1q_s32(src + k), v_zero)), - vmovn_u32(vceqq_s32(vld1q_s32(src + k + 4), v_zero))), v_1)); - - v_nz = vaddq_u32(vaddl_u16(vget_low_u16(v_pz), vget_high_u16(v_pz)), v_nz); - - src += blockSizej; - j += blockSizej; + v_int8 v_sum8 = vx_setzero_s8(); + int k = j; + for (; k < std::min(len0, j + 127 * v_int8::nlanes); k += v_int8::nlanes) + v_sum8 += v_one & v_pack( + v_pack(vx_load(src + k ) == v_zero, vx_load(src + k + v_int32::nlanes) == v_zero), + v_pack(vx_load(src + k + 2*v_int32::nlanes) == v_zero, vx_load(src + k + 3*v_int32::nlanes) == v_zero) + ); + v_int16 part1, part2; + v_expand(v_sum8, part1, part2); + v_sum16 += part1 + part2; + j = k; } - - i += blockSizei; + v_int32 part1, part2; + v_expand(v_sum16, part1, part2); + v_sum32 += part1 + part2; + i = j; } - - CV_DECL_ALIGNED(16) unsigned int buf[4]; - vst1q_u32(buf, v_nz); - nz += i - saturate_cast(buf[0] + buf[1] + buf[2] + buf[3]); + nz = i - v_reduce_sum(v_sum32); + v_cleanup(); #endif - return nz + countNonZero_(src, len - i); + return nz + countNonZero_(src + i, len - i); } static int countNonZero32f( const float* src, int len ) { int i = 0, nz = 0; -#if CV_SSE2 - if (USE_SSE2) - { - __m128 v_zero_f = _mm_setzero_ps(); - __m128i v_zero = _mm_setzero_si128 (); - __m128i sum = _mm_setzero_si128(); +#if CV_SIMD + int len0 = len & -v_int8::nlanes; + v_float32 v_zero = vx_setzero_f32(); + v_int8 v_one = vx_setall_s8(1); - for ( ; i <= len - 4; i += 4) - { - __m128 r0 = _mm_loadu_ps(src + i); - sum = _mm_add_epi32(sum, _mm_sad_epu8(_mm_sub_epi8(v_zero, _mm_castps_si128(_mm_cmpeq_ps(r0, v_zero_f))), v_zero)); - } - - nz = i - (_mm_cvtsi128_si32(_mm_add_epi32(sum, _mm_unpackhi_epi64(sum, sum))) >> 2); - src += i; - } -#elif CV_NEON - int len0 = len & -8, blockSize1 = (1 << 15), blockSize0 = blockSize1 << 6; - uint32x4_t v_nz = vdupq_n_u32(0u); - float32x4_t v_zero = vdupq_n_f32(0.0f); - uint16x8_t v_1 = vdupq_n_u16(1u), v_zerou = vdupq_n_u16(0u); - - while( i < len0 ) + v_int32 v_sum32 = vx_setzero_s32(); + while (i < len0) { - int blockSizei = std::min(len0 - i, blockSize0), j = 0; - - while (j < blockSizei) + v_int16 v_sum16 = vx_setzero_s16(); + int j = i; + while (j < std::min(len0, i + 32766 * v_int16::nlanes)) { - int blockSizej = std::min(blockSizei - j, blockSize1), k = 0; - uint16x8_t v_pz = v_zerou; - - for( ; k <= blockSizej - 8; k += 8 ) - v_pz = vaddq_u16(v_pz, vandq_u16(vcombine_u16(vmovn_u32(vceqq_f32(vld1q_f32(src + k), v_zero)), - vmovn_u32(vceqq_f32(vld1q_f32(src + k + 4), v_zero))), v_1)); - - v_nz = vaddq_u32(vaddl_u16(vget_low_u16(v_pz), vget_high_u16(v_pz)), v_nz); - - src += blockSizej; - j += blockSizej; + v_int8 v_sum8 = vx_setzero_s8(); + int k = j; + for (; k < std::min(len0, j + 127 * v_int8::nlanes); k += v_int8::nlanes) + v_sum8 += v_one & v_pack( + v_pack(v_reinterpret_as_s32(vx_load(src + k ) == v_zero), v_reinterpret_as_s32(vx_load(src + k + v_float32::nlanes) == v_zero)), + v_pack(v_reinterpret_as_s32(vx_load(src + k + 2*v_float32::nlanes) == v_zero), v_reinterpret_as_s32(vx_load(src + k + 3*v_float32::nlanes) == v_zero)) + ); + v_int16 part1, part2; + v_expand(v_sum8, part1, part2); + v_sum16 += part1 + part2; + j = k; } - - i += blockSizei; + v_int32 part1, part2; + v_expand(v_sum16, part1, part2); + v_sum32 += part1 + part2; + i = j; } - - CV_DECL_ALIGNED(16) unsigned int buf[4]; - vst1q_u32(buf, v_nz); - nz += i - saturate_cast(buf[0] + buf[1] + buf[2] + buf[3]); + nz = i - v_reduce_sum(v_sum32); + v_cleanup(); #endif - return nz + countNonZero_(src, len - i); + return nz + countNonZero_(src + i, len - i); } static int countNonZero64f( const double* src, int len ) From 0515f930e87f2817dc40674913cc696558489ddf Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 30 Aug 2018 19:12:01 +0000 Subject: [PATCH 13/47] imgcodecs(webp): multiple fixes - don't reallocate passed 'img' (test fixed - must use IMREAD_UNCHANGED / IMREAD_ANYCOLOR) - avoid memory DDOS - avoid reading of whole file during header processing - avoid data access after allocated buffer during header processing (missing checks) - use WebPFree() to free allocated buffers (libwebp >= 0.5.0) - drop unused & undefined `.close()` method - added checks for channels >= 5 in encoder --- modules/imgcodecs/src/grfmt_webp.cpp | 191 ++++++++++++++------------- modules/imgcodecs/src/grfmt_webp.hpp | 5 +- modules/imgcodecs/src/loadsave.cpp | 21 ++- modules/imgcodecs/test/test_webp.cpp | 7 +- 4 files changed, 127 insertions(+), 97 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index 53dd056f59..7b5abfaad9 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -54,15 +54,21 @@ #include "opencv2/imgproc.hpp" -const size_t WEBP_HEADER_SIZE = 32; +#include namespace cv { +// 64Mb limit to avoid memory DDOS +static size_t param_maxFileSize = utils::getConfigurationParameterSizeT("OPENCV_IMGCODECS_WEBP_MAX_FILE_SIZE", 64*1024*1024); + +static const size_t WEBP_HEADER_SIZE = 32; + WebPDecoder::WebPDecoder() { m_buf_supported = true; channels = 0; + fs_size = 0; } WebPDecoder::~WebPDecoder() {} @@ -96,48 +102,29 @@ ImageDecoder WebPDecoder::newDecoder() const bool WebPDecoder::readHeader() { + uint8_t header[WEBP_HEADER_SIZE] = { 0 }; if (m_buf.empty()) { - FILE * wfile = NULL; - - wfile = fopen(m_filename.c_str(), "rb"); - - if(wfile == NULL) - { - return false; - } - - fseek(wfile, 0, SEEK_END); - long int wfile_size = ftell(wfile); - fseek(wfile, 0, SEEK_SET); - - if(wfile_size > static_cast(INT_MAX)) - { - fclose(wfile); - return false; - } - - data.create(1, (int)wfile_size, CV_8U); - - size_t data_size = fread(data.ptr(), 1, wfile_size, wfile); - - if(wfile) - { - fclose(wfile); - } - - if(static_cast(data_size) != wfile_size) - { - return false; - } + fs.open(m_filename.c_str(), std::ios::binary); + fs.seekg(0, std::ios::end); + fs_size = fs.tellg(); + fs.seekg(0, std::ios::beg); + CV_Assert(fs && "File stream error"); + CV_CheckGE(fs_size, WEBP_HEADER_SIZE, "File is too small"); + CV_CheckLE(fs_size, param_maxFileSize, "File is too large. Increase OPENCV_IMGCODECS_WEBP_MAX_FILE_SIZE parameter if you want to process large files"); + + fs.read((char*)header, sizeof(header)); + CV_Assert(fs && "Can't read WEBP_HEADER_SIZE bytes"); } else { + CV_CheckGE(m_buf.total(), WEBP_HEADER_SIZE, "Buffer is too small"); + memcpy(header, m_buf.ptr(), sizeof(header)); data = m_buf; } WebPBitstreamFeatures features; - if(VP8_STATUS_OK == WebPGetFeatures(data.ptr(), WEBP_HEADER_SIZE, &features)) + if (VP8_STATUS_OK == WebPGetFeatures(header, sizeof(header), &features)) { m_width = features.width; m_height = features.height; @@ -161,41 +148,75 @@ bool WebPDecoder::readHeader() bool WebPDecoder::readData(Mat &img) { - if( m_width > 0 && m_height > 0 ) + CV_CheckGE(m_width, 0, ""); CV_CheckGE(m_height, 0, ""); + + CV_CheckEQ(img.cols, m_width, ""); + CV_CheckEQ(img.rows, m_height, ""); + + if (m_buf.empty()) { - bool convert_grayscale = (img.type() == CV_8UC1); // IMREAD_GRAYSCALE requested + fs.seekg(0, std::ios::beg); CV_Assert(fs && "File stream error"); + data.create(1, validateToInt(fs_size), CV_8UC1); + fs.read((char*)data.ptr(), fs_size); + CV_Assert(fs && "Can't read file data"); + fs.close(); + } + CV_Assert(data.type() == CV_8UC1); CV_Assert(data.rows == 1); - if (img.cols != m_width || img.rows != m_height || img.type() != m_type) + { + Mat read_img; + CV_CheckType(img.type(), img.type() == CV_8UC1 || img.type() == CV_8UC3 || img.type() == CV_8UC4, ""); + if (img.type() != m_type) + { + read_img.create(m_height, m_width, m_type); + } + else { - img.create(m_height, m_width, m_type); + read_img = img; // copy header } - uchar* out_data = img.ptr(); - size_t out_data_size = img.cols * img.rows * img.elemSize(); + uchar* out_data = read_img.ptr(); + size_t out_data_size = read_img.dataend - out_data; - uchar *res_ptr = 0; + uchar *res_ptr = NULL; if (channels == 3) { + CV_CheckTypeEQ(read_img.type(), CV_8UC3, ""); res_ptr = WebPDecodeBGRInto(data.ptr(), data.total(), out_data, - (int)out_data_size, (int)img.step); + (int)out_data_size, (int)read_img.step); } else if (channels == 4) { + CV_CheckTypeEQ(read_img.type(), CV_8UC4, ""); res_ptr = WebPDecodeBGRAInto(data.ptr(), data.total(), out_data, - (int)out_data_size, (int)img.step); + (int)out_data_size, (int)read_img.step); } - if(res_ptr == out_data) + if (res_ptr != out_data) + return false; + + if (read_img.data == img.data && img.type() == m_type) { - if (convert_grayscale) - { - cvtColor(img, img, COLOR_BGR2GRAY); - } - return true; + // nothing + } + else if (img.type() == CV_8UC1) + { + cvtColor(read_img, img, COLOR_BGR2GRAY); + } + else if (img.type() == CV_8UC3 && m_type == CV_8UC4) + { + cvtColor(read_img, img, COLOR_BGRA2BGR); + } + else if (img.type() == CV_8UC3 && m_type == CV_8UC4) + { + cvtColor(read_img, img, COLOR_BGRA2BGR); + } + else + { + CV_Error(Error::StsInternal, ""); } } - - return false; + return true; } WebPEncoder::WebPEncoder() @@ -213,12 +234,9 @@ ImageEncoder WebPEncoder::newEncoder() const bool WebPEncoder::write(const Mat& img, const std::vector& params) { - int channels = img.channels(), depth = img.depth(); - int width = img.cols, height = img.rows; + CV_CheckDepthEQ(img.depth(), CV_8U, "WebP codec supports 8U images only"); - const Mat *image = &img; - Mat temp; - size_t size = 0; + const int width = img.cols, height = img.rows; bool comp_lossless = true; float quality = 100.0f; @@ -240,69 +258,64 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) } } - uint8_t *out = NULL; + int channels = img.channels(); + CV_Check(channels, channels == 1 || channels == 3 || channels == 4, ""); - if(depth != CV_8U) - { - return false; - } + const Mat *image = &img; + Mat temp; - if(channels == 1) + if (channels == 1) { cvtColor(*image, temp, CV_GRAY2BGR); image = &temp; channels = 3; } - else if (channels == 2) - { - return false; - } + uint8_t *out = NULL; + size_t size = 0; if (comp_lossless) { - if(channels == 3) + if (channels == 3) { size = WebPEncodeLosslessBGR(image->ptr(), width, height, (int)image->step, &out); } - else if(channels == 4) + else if (channels == 4) { size = WebPEncodeLosslessBGRA(image->ptr(), width, height, (int)image->step, &out); } } else { - if(channels == 3) + if (channels == 3) { size = WebPEncodeBGR(image->ptr(), width, height, (int)image->step, quality, &out); } - else if(channels == 4) + else if (channels == 4) { size = WebPEncodeBGRA(image->ptr(), width, height, (int)image->step, quality, &out); } } +#if WEBP_DECODER_ABI_VERSION >= 0x0206 + Ptr out_cleaner(out, WebPFree); +#else + Ptr out_cleaner(out, free); +#endif + + CV_Assert(size > 0); - if(size > 0) + if (m_buf) { - if(m_buf) - { - m_buf->resize(size); - memcpy(&(*m_buf)[0], out, size); - } - else - { - FILE *fd = fopen(m_filename.c_str(), "wb"); - if(fd != NULL) - { - fwrite(out, size, sizeof(uint8_t), fd); - fclose(fd); fd = NULL; - } - } + m_buf->resize(size); + memcpy(&(*m_buf)[0], out, size); } - - if (out != NULL) + else { - free(out); - out = NULL; + FILE *fd = fopen(m_filename.c_str(), "wb"); + if (fd != NULL) + { + fwrite(out, size, sizeof(uint8_t), fd); + fclose(fd); fd = NULL; + } } return size > 0; diff --git a/modules/imgcodecs/src/grfmt_webp.hpp b/modules/imgcodecs/src/grfmt_webp.hpp index 79e041b212..6d833e0db6 100644 --- a/modules/imgcodecs/src/grfmt_webp.hpp +++ b/modules/imgcodecs/src/grfmt_webp.hpp @@ -47,7 +47,7 @@ #ifdef HAVE_WEBP - +#include namespace cv { @@ -61,7 +61,6 @@ public: bool readData( Mat& img ) CV_OVERRIDE; bool readHeader() CV_OVERRIDE; - void close(); size_t signatureLength() const CV_OVERRIDE; bool checkSignature( const String& signature) const CV_OVERRIDE; @@ -69,6 +68,8 @@ public: ImageDecoder newDecoder() const CV_OVERRIDE; protected: + std::ifstream fs; + size_t fs_size; Mat data; int channels; }; diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index f5d9c98a3b..cfa5cc3bf5 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -707,11 +707,22 @@ static bool imwrite_( const String& filename, const std::vector& img_vec, encoder->setDestination( filename ); CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2); - bool code; - if (!isMultiImg) - code = encoder->write( write_vec[0], params ); - else - code = encoder->writemulti( write_vec, params ); //to be implemented + bool code = false; + try + { + if (!isMultiImg) + code = encoder->write( write_vec[0], params ); + else + code = encoder->writemulti( write_vec, params ); //to be implemented + } + catch (const cv::Exception& e) + { + std::cerr << "imwrite_('" << filename << "'): can't write data: " << e.what() << std::endl << std::flush; + } + catch (...) + { + std::cerr << "imwrite_('" << filename << "'): can't write data: unknown exception" << std::endl << std::flush; + } // CV_Assert( code ); return code; diff --git a/modules/imgcodecs/test/test_webp.cpp b/modules/imgcodecs/test/test_webp.cpp index e527659aa4..1f2cad7d89 100644 --- a/modules/imgcodecs/test/test_webp.cpp +++ b/modules/imgcodecs/test/test_webp.cpp @@ -96,12 +96,17 @@ TEST(Imgcodecs_WebP, encode_decode_with_alpha_webp) string output = cv::tempfile(".webp"); EXPECT_NO_THROW(cv::imwrite(output, img)); - cv::Mat img_webp = cv::imread(output); + cv::Mat img_webp = cv::imread(output, IMREAD_UNCHANGED); + cv::Mat img_webp_bgr = cv::imread(output); // IMREAD_COLOR by default EXPECT_EQ(0, remove(output.c_str())); EXPECT_FALSE(img_webp.empty()); EXPECT_EQ(4, img_webp.channels()); EXPECT_EQ(512, img_webp.cols); EXPECT_EQ(512, img_webp.rows); + EXPECT_FALSE(img_webp_bgr.empty()); + EXPECT_EQ(3, img_webp_bgr.channels()); + EXPECT_EQ(512, img_webp_bgr.cols); + EXPECT_EQ(512, img_webp_bgr.rows); } #endif // HAVE_WEBP From 0d4fd20a73539573d72222c6c2dc3b8726e7e02f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 31 Aug 2018 12:56:02 +0300 Subject: [PATCH 14/47] imgcodecs: add null pointer check --- modules/imgcodecs/src/loadsave.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index f5d9c98a3b..d4be6ae4fb 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -396,6 +396,8 @@ static void ApplyExifOrientation(const Mat& buf, Mat& img) static void* imread_( const String& filename, int flags, int hdrtype, Mat* mat=0 ) { + CV_Assert(mat || hdrtype != LOAD_MAT); // mat is required in LOAD_MAT case + IplImage* image = 0; CvMat *matrix = 0; Mat temp, *data = &temp; From d52cef1719eedfb06dcd91868666d7f5841fd972 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 31 Aug 2018 14:48:44 +0300 Subject: [PATCH 15/47] js: use generated list of OpenCV headers - replaces hand-written list --- modules/js/CMakeLists.txt | 2 +- modules/js/src/core_bindings.cpp | 9 ++------- modules/js/src/embindgen.py | 5 +++++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/js/CMakeLists.txt b/modules/js/CMakeLists.txt index 7db438b8a9..6d713d4bd9 100644 --- a/modules/js/CMakeLists.txt +++ b/modules/js/CMakeLists.txt @@ -56,7 +56,7 @@ add_custom_command( DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/embindgen.py DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/templates.py DEPENDS ${scripts_hdr_parser} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/headers.txt + #(not needed - generated by CMake) DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/headers.txt DEPENDS ${opencv_hdrs} DEPENDS ${JS_HELPER}) diff --git a/modules/js/src/core_bindings.cpp b/modules/js/src/core_bindings.cpp index 18427d18fe..e8f0ee7f85 100644 --- a/modules/js/src/core_bindings.cpp +++ b/modules/js/src/core_bindings.cpp @@ -68,15 +68,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //M*/ -#include "opencv2/core.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/video/tracking.hpp" -#include "opencv2/video/background_segm.hpp" -#include "opencv2/objdetect.hpp" -#include "opencv2/dnn.hpp" - #include +@INCLUDES@ + using namespace emscripten; using namespace cv; using namespace dnn; diff --git a/modules/js/src/embindgen.py b/modules/js/src/embindgen.py index a27fba2f21..2e9811ada2 100644 --- a/modules/js/src/embindgen.py +++ b/modules/js/src/embindgen.py @@ -733,12 +733,14 @@ class JSWrapperGenerator(object): def gen(self, dst_file, src_files, core_bindings): # step 1: scan the headers and extract classes, enums and functions + headers = [] for hdr in src_files: decls = self.parser.parse(hdr) # print(hdr); # self.print_decls(decls); if len(decls) == 0: continue + headers.append(hdr[hdr.rindex('opencv2/'):]) for decl in decls: name = decl[0] type = name[:name.find(" ")] @@ -890,6 +892,9 @@ class JSWrapperGenerator(object): with open(core_bindings) as f: ret = f.read() + header_includes = '\n'.join(['#include "{}"'.format(hdr) for hdr in headers]) + ret = ret.replace('@INCLUDES@', header_includes) + defis = '\n'.join(self.wrapper_funcs) ret += wrapper_codes_template.substitute(ns=wrapper_namespace, defs=defis) ret += emscripten_binding_template.substitute(binding_name='testBinding', bindings=''.join(self.bindings)) From 3e6b3a68567021b4380aa9da59278fb7a3de5791 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 2 Aug 2018 17:59:45 +0300 Subject: [PATCH 16/47] dnn(perf): fix and merge Convolution tests - OpenCL tests didn't run any OpenCL kernels - use real configuration from existed models (the first 100 cases) - batch size = 1 --- modules/dnn/perf/opencl/perf_convolution.cpp | 107 --- modules/dnn/perf/perf_convolution.cpp | 722 +++++++++++++++++-- modules/dnn/perf/perf_precomp.hpp | 2 + modules/dnn/test/test_common.hpp | 37 + modules/dnn/test/test_precomp.hpp | 29 - 5 files changed, 694 insertions(+), 203 deletions(-) delete mode 100644 modules/dnn/perf/opencl/perf_convolution.cpp diff --git a/modules/dnn/perf/opencl/perf_convolution.cpp b/modules/dnn/perf/opencl/perf_convolution.cpp deleted file mode 100644 index 28fbf3ae83..0000000000 --- a/modules/dnn/perf/opencl/perf_convolution.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "../perf_precomp.hpp" -#include "opencv2/ts/ocl_perf.hpp" -#include - -#ifdef HAVE_OPENCL - -namespace opencv_test { namespace ocl { -using namespace ::perf; - -namespace { -enum {STRIDE_OFF = 1, STRIDE_ON = 2}; -CV_ENUM(StrideSize, STRIDE_OFF, STRIDE_ON); - -enum {GROUP_OFF = 1, GROUP_2 = 2}; -CV_ENUM(GroupSize, GROUP_OFF, GROUP_2); -} // namespace - -//Squared Size -#define SSZ(n) cv::Size(n, n) - -typedef std::pair InpShapeNumOut; -typedef tuple ConvParam; //kernel_size, inp shape, groups, stride -typedef TestBaseWithParam ConvolutionPerfTest; - -static inline MatShape blobShape(int count, int nplanes, int height, int width) -{ - int data[] = {count, nplanes, height, width}; - return MatShape(data, data+4); -} - -OCL_PERF_TEST_P( ConvolutionPerfTest, perf, Combine( - Values(Size(1, 1), Size(3, 3), Size(5, 5), Size(11, 11)), - Values(make_pair(blobShape(1, 4, 224, 224), 64), - make_pair(blobShape(1, 64, 112, 122), 128), - make_pair(blobShape(1, 256, 28, 28), 512)), - GroupSize::all(), - StrideSize::all()) -) -{ - RNG rng(0); - - ConvParam params = GetParam(); - int ksz = get<0>(params).width; - MatShape inpShape = get<1>(params).first; - int outCn = get<1>(params).second; - int groups = get<2>(params); - int stride = (ksz >= 11) ? 4 : (int)get<3>(params); - - int inpCn = inpShape[1]; - int wgtSize[] = { outCn, inpCn/groups, ksz, ksz }; - int biasSize[] = { outCn, 1, 1, 1 }; - const int wtype = CV_32F; - Mat wgtBlob(4, wgtSize, wtype), biasBlob(4, biasSize, wtype); - Mat inpBlob(4, &inpShape[0], wtype); - rng.fill(biasBlob, RNG::UNIFORM, -1, +1); - rng.fill(wgtBlob, RNG::UNIFORM, -1, +1); - rng.fill(inpBlob, RNG::UNIFORM, -1, +1); - - LayerParams lp; - lp.set("num_output", outCn); - lp.set("group", groups); - lp.set("stride", stride); - lp.set("kernel_size", ksz); - lp.blobs.reserve(2); - lp.blobs.push_back(wgtBlob); - lp.blobs.push_back(biasBlob); - - std::vector inpBlobs(1, &inpBlob); - std::vector outBlobs, internalBlobs; - - Ptr layer = cv::dnn::LayerFactory::createLayerInstance("Convolution", lp); - std::vector inputShapes(1, shape(inpBlob)), outShapes, internals; - layer->getMemoryShapes(inputShapes, 0, outShapes, internals); - for (size_t i = 0; i < outShapes.size(); i++) - { - outBlobs.push_back(Mat(outShapes[i], CV_32F)); - } - for (size_t i = 0; i < internals.size(); i++) - { - internalBlobs.push_back(Mat()); - if (total(internals[i])) - internalBlobs.back().create(internals[i], CV_32F); - } - - layer->finalize(inpBlobs, outBlobs); - layer->preferableTarget = DNN_TARGET_OPENCL; - - Mat inpBlob2D = inpBlob.reshape(1, outCn); - Mat wgtBlob2D = wgtBlob.reshape(1, outCn*(inpCn/groups)); - Mat outBlob2D = outBlobs[0].reshape(1, outBlobs[0].size[0]); - declare.in(inpBlob2D, wgtBlob2D, WARMUP_RNG).out(outBlob2D); - - // warmup - layer->forward(inpBlobs, outBlobs, internalBlobs); - - TEST_CYCLE() - { - layer->forward(inpBlobs, outBlobs, internalBlobs); - } - - SANITY_CHECK_NOTHING(); -} - -} -} - -#endif diff --git a/modules/dnn/perf/perf_convolution.cpp b/modules/dnn/perf/perf_convolution.cpp index 16006d75da..5164ac0130 100644 --- a/modules/dnn/perf/perf_convolution.cpp +++ b/modules/dnn/perf/perf_convolution.cpp @@ -1,92 +1,680 @@ +// 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. + #include "perf_precomp.hpp" #include namespace opencv_test { -enum {STRIDE_OFF = 1, STRIDE_ON = 2}; -CV_ENUM(StrideSize, STRIDE_OFF, STRIDE_ON); +// Flops_Kernel_Input_OutCN_Group_Stride_Pad_Dilation_PadAdjust_PadMode_Bias +struct TestSize_ { + int width, height; + operator Size() const { return Size(width, height); } +}; +struct ConvParam_t { + struct TestSize_ kernel; + struct BlobShape { int dims[4]; } shapeIn; + int outCN; + int groups; + struct TestSize_ stride; + struct TestSize_ dilation; + struct TestSize_ pad; + struct TestSize_ padAdjust; + const char* padMode; + bool hasBias; + double declared_flops; +}; +// Details: #12142 +static const ConvParam_t testConvolutionConfigs[] = { + /* GFLOPS 10.087 x 1 = 10.087 */ {{3, 3}, {{1, 576, 38, 50}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10086963200.}, + /* GFLOPS 1.704 x 5 = 8.518 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 512, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1703596544.}, + /* GFLOPS 1.704 x 5 = 8.518 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1703596544.}, + /* GFLOPS 6.641 x 1 = 6.641 */ {{3, 3}, {{1, 64, 150, 200}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6641280000.}, + /* GFLOPS 1.659 x 3 = 4.977 */ {{3, 3}, {{1, 960, 10, 10}}, 960, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1658976000.}, + /* GFLOPS 2.156 x 2 = 4.312 */ {{3, 3}, {{1, 576, 19, 19}}, 576, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2156088384.}, + /* GFLOPS 0.958 x 4 = 3.833 */ {{3, 3}, {{1, 384, 19, 19}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 958307712.}, + /* GFLOPS 0.830 x 4 = 3.321 */ {{3, 3}, {{1, 64, 75, 100}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 830160000.}, + /* GFLOPS 1.245 x 2 = 2.490 */ {{3, 3}, {{1, 96, 75, 100}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1244880000.}, + /* GFLOPS 2.100 x 1 = 2.100 */ {{3, 3}, {{1, 144, 75, 75}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2100330000.}, + /* GFLOPS 1.022 x 2 = 2.044 */ {{3, 3}, {{1, 576, 19, 19}}, 273, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1021896057.}, + /* GFLOPS 0.958 x 2 = 1.917 */ {{3, 3}, {{1, 192, 38, 38}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 958446336.}, + /* GFLOPS 1.888 x 1 = 1.888 */ {{3, 3}, {{1, 1024, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1887539200.}, + /* GFLOPS 1.888 x 1 = 1.888 */ {{3, 3}, {{1, 1024, 10, 10}}, 1024, 1024, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1887539200.}, + /* GFLOPS 1.704 x 1 = 1.704 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 256, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1703781376.}, + /* GFLOPS 1.704 x 1 = 1.704 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1703781376.}, + /* GFLOPS 1.660 x 1 = 1.660 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 128, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1659600000.}, + /* GFLOPS 1.660 x 1 = 1.660 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1659600000.}, + /* GFLOPS 0.280 x 5 = 1.402 */ {{1, 1}, {{1, 576, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 280409600.}, + /* GFLOPS 0.701 x 2 = 1.401 */ {{3, 3}, {{1, 128, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 700720000.}, + /* GFLOPS 0.231 x 6 = 1.388 */ {{3, 3}, {{1, 128, 56, 56}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231311360.}, + /* GFLOPS 0.231 x 6 = 1.388 */ {{3, 3}, {{1, 256, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231261184.}, + /* GFLOPS 0.210 x 6 = 1.262 */ {{1, 1}, {{1, 576, 38, 50}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 210307200.}, + /* GFLOPS 0.420 x 3 = 1.261 */ {{3, 3}, {{1, 96, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 420492800.}, + /* GFLOPS 1.261 x 1 = 1.261 */ {{3, 3}, {{1, 192, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1261113600.}, + /* GFLOPS 1.258 x 1 = 1.258 */ {{3, 3}, {{1, 1280, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1258038600.}, + /* GFLOPS 1.245 x 1 = 1.245 */ {{3, 3}, {{1, 64, 75, 75}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1245240000.}, + /* GFLOPS 0.561 x 2 = 1.121 */ {{3, 3}, {{1, 128, 38, 50}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 560576000.}, + /* GFLOPS 1.051 x 1 = 1.051 */ {{3, 3}, {{1, 160, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1050988800.}, + /* GFLOPS 1.006 x 1 = 1.006 */ {{3, 3}, {{1, 1024, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1006441800.}, + /* GFLOPS 0.246 x 4 = 0.985 */ {{1, 1}, {{1, 256, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 246240000.}, + /* GFLOPS 0.189 x 5 = 0.947 */ {{1, 1}, {{1, 512, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 189452800.}, + /* GFLOPS 0.189 x 5 = 0.947 */ {{1, 1}, {{1, 512, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 189452800.}, + /* GFLOPS 0.934 x 1 = 0.934 */ {{3, 3}, {{1, 96, 150, 150}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 933660000.}, + /* GFLOPS 0.231 x 4 = 0.925 */ {{3, 3}, {{1, 128, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231311360.}, + /* GFLOPS 0.896 x 1 = 0.896 */ {{5, 5}, {{1, 96, 27, 27}}, 256, 2, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 895981824.}, + /* GFLOPS 0.876 x 1 = 0.876 */ {{3, 3}, {{1, 160, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 875824000.}, + /* GFLOPS 0.850 x 1 = 0.850 */ {{7, 7}, {{1, 3, 600, 800}}, 24, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 849600000.}, + /* GFLOPS 0.841 x 1 = 0.841 */ {{3, 3}, {{1, 128, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 840864000.}, + /* GFLOPS 0.415 x 2 = 0.831 */ {{3, 3}, {{1, 32, 150, 150}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 415440000.}, + /* GFLOPS 0.351 x 2 = 0.701 */ {{1, 1}, {{1, 576, 38, 50}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 350512000.}, + /* GFLOPS 0.701 x 1 = 0.701 */ {{3, 3}, {{1, 128, 75, 100}}, 160, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 700720000.}, + /* GFLOPS 0.694 x 1 = 0.694 */ {{3, 3}, {{1, 64, 56, 56}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 694235136.}, + /* GFLOPS 0.694 x 1 = 0.694 */ {{3, 3}, {{1, 64, 56, 56}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 694235136.}, + /* GFLOPS 0.231 x 3 = 0.694 */ {{3, 3}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231411712.}, + /* GFLOPS 0.058 x 12 = 0.694 */ {{3, 3}, {{1, 128, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 57827840.}, + /* GFLOPS 0.231 x 3 = 0.694 */ {{3, 3}, {{1, 512, 7, 7}}, 512, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 231236096.}, + /* GFLOPS 0.160 x 4 = 0.639 */ {{3, 3}, {{1, 64, 38, 38}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 159833472.}, + /* GFLOPS 0.103 x 6 = 0.618 */ {{1, 1}, {{1, 256, 14, 14}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102961152.}, + /* GFLOPS 0.615 x 1 = 0.615 */ {{1, 1}, {{1, 320, 75, 100}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 615360000.}, + /* GFLOPS 0.597 x 1 = 0.597 */ {{3, 3}, {{1, 576, 19, 19}}, 576, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 597254400.}, + /* GFLOPS 0.185 x 3 = 0.554 */ {{1, 1}, {{1, 192, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 184800000.}, + /* GFLOPS 0.553 x 1 = 0.553 */ {{3, 3}, {{1, 64, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 553440000.}, + /* GFLOPS 0.539 x 1 = 0.539 */ {{3, 3}, {{1, 144, 75, 75}}, 144, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 539178048.}, + /* GFLOPS 0.103 x 5 = 0.514 */ {{1, 1}, {{1, 1024, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102810624.}, + /* GFLOPS 0.491 x 1 = 0.491 */ {{1, 1}, {{1, 576, 38, 50}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 490716800.}, + /* GFLOPS 0.240 x 2 = 0.479 */ {{3, 3}, {{1, 96, 38, 38}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 239680896.}, + /* GFLOPS 0.237 x 2 = 0.474 */ {{7, 7}, {{1, 3, 224, 224}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", true, 236830720.}, + /* GFLOPS 0.472 x 1 = 0.472 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 512, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 471910400.}, + /* GFLOPS 0.472 x 1 = 0.472 */ {{3, 3}, {{1, 512, 19, 19}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 471910400.}, + /* GFLOPS 0.449 x 1 = 0.449 */ {{3, 3}, {{1, 384, 13, 13}}, 384, 2, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 448626048.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 128, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 426037760.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 75, 75}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 426037760.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 128, 38, 38}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 426037760.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 256, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 425945344.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 256, 38, 38}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 425945344.}, + /* GFLOPS 0.426 x 1 = 0.426 */ {{3, 3}, {{1, 256, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 425945344.}, + /* GFLOPS 0.421 x 1 = 0.421 */ {{1, 1}, {{1, 576, 38, 50}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 420614400.}, + /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 32, 150, 150}}, 32, 32, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 415440000.}, + /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 64, 150, 150}}, 64, 64, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 415080000.}, + /* GFLOPS 0.415 x 1 = 0.415 */ {{3, 3}, {{1, 64, 150, 150}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 415080000.}, + /* GFLOPS 0.104 x 4 = 0.414 */ {{1, 1}, {{1, 64, 56, 56}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 103563264.}, + /* GFLOPS 0.103 x 4 = 0.413 */ {{1, 1}, {{1, 128, 28, 28}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 103161856.}, + /* GFLOPS 0.376 x 1 = 0.376 */ {{1, 1}, {{1, 24, 300, 400}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 376320000.}, + /* GFLOPS 0.347 x 1 = 0.347 */ {{3, 3}, {{1, 128, 28, 28}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 346967040.}, + /* GFLOPS 0.347 x 1 = 0.347 */ {{3, 3}, {{1, 128, 28, 28}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 346967040.}, + /* GFLOPS 0.014 x 24 = 0.347 */ {{3, 3}, {{1, 128, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 14456960.}, + /* GFLOPS 0.053 x 6 = 0.320 */ {{1, 1}, {{1, 576, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 53277824.}, + /* GFLOPS 0.319 x 1 = 0.319 */ {{3, 3}, {{1, 192, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 319482112.}, + /* GFLOPS 0.315 x 1 = 0.315 */ {{3, 3}, {{1, 96, 75, 100}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 315369600.}, + /* GFLOPS 0.103 x 3 = 0.309 */ {{1, 1}, {{1, 512, 7, 7}}, 2048, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102860800.}, + /* GFLOPS 0.103 x 3 = 0.309 */ {{1, 1}, {{1, 512, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102860800.}, + /* GFLOPS 0.308 x 1 = 0.308 */ {{1, 1}, {{1, 320, 75, 100}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 307680000.}, + /* GFLOPS 0.299 x 1 = 0.299 */ {{3, 3}, {{1, 256, 13, 13}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 299105664.}, + /* GFLOPS 0.299 x 1 = 0.299 */ {{3, 3}, {{1, 384, 13, 13}}, 256, 2, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 299084032.}, + /* GFLOPS 0.017 x 17 = 0.290 */ {{1, 1}, {{1, 32, 32, 64}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17039360.}, + /* GFLOPS 0.017 x 16 = 0.269 */ {{1, 1}, {{1, 128, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16842752.}, + /* GFLOPS 0.133 x 2 = 0.266 */ {{3, 3}, {{1, 128, 19, 19}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 133136800.}, + /* GFLOPS 0.038 x 7 = 0.265 */ {{3, 3}, {{1, 16, 64, 128}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 37879808.}, + /* GFLOPS 0.126 x 2 = 0.252 */ {{3, 3}, {{1, 512, 5, 5}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 125812050.}, + /* GFLOPS 0.248 x 1 = 0.248 */ {{1, 1}, {{1, 64, 150, 200}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 247680000.}, + /* GFLOPS 0.040 x 6 = 0.240 */ {{1, 1}, {{1, 576, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39958368.}, + /* GFLOPS 0.080 x 3 = 0.240 */ {{3, 3}, {{1, 96, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 79893632.}, + /* GFLOPS 0.240 x 1 = 0.240 */ {{3, 3}, {{1, 192, 38, 38}}, 192, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 239611584.}, + /* GFLOPS 0.240 x 1 = 0.240 */ {{3, 3}, {{1, 192, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 239611584.}, + /* GFLOPS 0.237 x 1 = 0.237 */ {{7, 7}, {{1, 3, 224, 224}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", false, 236830720.}, + /* GFLOPS 0.237 x 1 = 0.237 */ {{7, 7}, {{1, 3, 224, 224}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 236830720.}, + /* GFLOPS 0.111 x 2 = 0.221 */ {{3, 3}, {{1, 192, 10, 10}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 110624000.}, + /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 128, 38, 38}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 213018880.}, + /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 128, 19, 19}}, 256, 1, {1, 1}, {2, 2}, {2, 2}, {0, 0}, "", false, 213018880.}, + /* GFLOPS 0.107 x 2 = 0.213 */ {{3, 3}, {{1, 128, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 106509440.}, + /* GFLOPS 0.213 x 1 = 0.213 */ {{3, 3}, {{1, 256, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 212972672.}, + /* GFLOPS 0.212 x 1 = 0.212 */ {{7, 7}, {{1, 3, 300, 300}}, 32, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", true, 212400000.}, + /* GFLOPS 0.211 x 1 = 0.211 */ {{11, 11}, {{1, 3, 227, 227}}, 96, 1, {4, 4}, {1, 1}, {0, 0}, {0, 0}, "", true, 211120800.}, + /* GFLOPS 0.210 x 1 = 0.210 */ {{3, 3}, {{1, 64, 38, 50}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 210307200.}, + /* GFLOPS 0.210 x 1 = 0.210 */ {{1, 1}, {{1, 1024, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 209817600.}, + /* GFLOPS 0.210 x 1 = 0.210 */ {{1, 1}, {{1, 1024, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 209817600.}, + /* GFLOPS 0.104 x 2 = 0.208 */ {{3, 3}, {{1, 32, 75, 75}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 103860000.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 256, 56, 56}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205922304.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 256, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 205922304.}, + /* GFLOPS 0.103 x 2 = 0.206 */ {{1, 1}, {{1, 256, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102961152.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 512, 28, 28}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 205721600.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 512, 28, 28}}, 1024, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205721600.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 1024, 14, 14}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 205621248.}, + /* GFLOPS 0.206 x 1 = 0.206 */ {{1, 1}, {{1, 1024, 14, 14}}, 2048, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 205621248.}, + /* GFLOPS 0.103 x 2 = 0.206 */ {{1, 1}, {{1, 2048, 7, 7}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 102785536.}, + /* GFLOPS 0.201 x 1 = 0.201 */ {{1, 1}, {{1, 512, 14, 14}}, 1000, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 200900000.}, + /* GFLOPS 0.200 x 1 = 0.200 */ {{3, 3}, {{1, 160, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 199687872.}, + /* GFLOPS 0.190 x 1 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 189637632.}, + /* GFLOPS 0.190 x 1 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 189637632.}, + /* GFLOPS 0.047 x 4 = 0.190 */ {{1, 1}, {{1, 256, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 47409408.}, + /* GFLOPS 0.038 x 5 = 0.189 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 37814272.}, + /* GFLOPS 0.185 x 1 = 0.185 */ {{1, 1}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 185040000.}, + /* GFLOPS 0.185 x 1 = 0.185 */ {{1, 1}, {{1, 128, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 185040000.}, + /* GFLOPS 0.181 x 1 = 0.181 */ {{3, 3}, {{1, 160, 14, 14}}, 320, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 180696320.}, + /* GFLOPS 0.181 x 1 = 0.181 */ {{3, 3}, {{1, 160, 14, 14}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 180696320.}, + /* GFLOPS 0.090 x 2 = 0.181 */ {{3, 3}, {{1, 224, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 90339200.}, + /* GFLOPS 0.180 x 1 = 0.180 */ {{1, 1}, {{1, 224, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 180232192.}, + /* GFLOPS 0.174 x 1 = 0.174 */ {{3, 3}, {{1, 96, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 173508608.}, + /* GFLOPS 0.174 x 1 = 0.174 */ {{3, 3}, {{1, 96, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 173508608.}, + /* GFLOPS 0.166 x 1 = 0.166 */ {{3, 3}, {{1, 160, 19, 19}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 166406560.}, + /* GFLOPS 0.080 x 2 = 0.160 */ {{1, 1}, {{1, 576, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 79916736.}, + /* GFLOPS 0.160 x 1 = 0.160 */ {{3, 3}, {{1, 128, 19, 19}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 159764160.}, + /* GFLOPS 0.159 x 1 = 0.159 */ {{7, 7}, {{1, 3, 300, 300}}, 24, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 159300000.}, + /* GFLOPS 0.155 x 1 = 0.155 */ {{1, 1}, {{1, 192, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 154542080.}, + /* GFLOPS 0.146 x 1 = 0.146 */ {{3, 3}, {{1, 144, 14, 14}}, 288, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 146369664.}, + /* GFLOPS 0.146 x 1 = 0.146 */ {{3, 3}, {{1, 144, 14, 14}}, 288, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 146369664.}, + /* GFLOPS 0.072 x 2 = 0.144 */ {{1, 1}, {{1, 1024, 10, 10}}, 352, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 72124800.}, + /* GFLOPS 0.140 x 1 = 0.140 */ {{1, 1}, {{1, 576, 38, 50}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 140204800.}, + /* GFLOPS 0.017 x 8 = 0.138 */ {{1, 1}, {{1, 16, 64, 128}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17301504.}, + /* GFLOPS 0.067 x 2 = 0.133 */ {{1, 1}, {{1, 576, 19, 19}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 66597280.}, + /* GFLOPS 0.133 x 1 = 0.133 */ {{3, 3}, {{1, 128, 38, 38}}, 160, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 133136800.}, + /* GFLOPS 0.129 x 1 = 0.129 */ {{1, 1}, {{1, 160, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 128851968.}, + /* GFLOPS 0.128 x 1 = 0.128 */ {{3, 3}, {{1, 64, 24, 24}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 127512576.}, + /* GFLOPS 0.120 x 1 = 0.120 */ {{5, 5}, {{1, 32, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 120497664.}, + /* GFLOPS 0.120 x 1 = 0.120 */ {{5, 5}, {{1, 32, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 120497664.}, + /* GFLOPS 0.040 x 3 = 0.120 */ {{1, 1}, {{1, 96, 19, 19}}, 576, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 40131648.}, + /* GFLOPS 0.118 x 1 = 0.118 */ {{1, 1}, {{1, 320, 38, 38}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 118477312.}, + /* GFLOPS 0.017 x 7 = 0.118 */ {{1, 1}, {{1, 64, 64, 128}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16908288.}, + /* GFLOPS 0.039 x 3 = 0.118 */ {{1, 1}, {{1, 1024, 10, 10}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39340800.}, + /* GFLOPS 0.118 x 1 = 0.118 */ {{3, 3}, {{1, 256, 19, 19}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 117990400.}, + /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 16, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 58003456.}, + /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 32, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 57903104.}, + /* GFLOPS 0.058 x 2 = 0.116 */ {{3, 3}, {{1, 64, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 57852928.}, + /* GFLOPS 0.116 x 1 = 0.116 */ {{3, 3}, {{1, 128, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 115655680.}, + /* GFLOPS 0.116 x 1 = 0.116 */ {{3, 3}, {{1, 128, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 115655680.}, + /* GFLOPS 0.112 x 1 = 0.112 */ {{1, 1}, {{1, 1024, 10, 10}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 111875400.}, + /* GFLOPS 0.036 x 3 = 0.107 */ {{1, 1}, {{1, 192, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 35580160.}, + /* GFLOPS 0.107 x 1 = 0.107 */ {{3, 3}, {{1, 32, 75, 75}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", false, 106648064.}, + /* GFLOPS 0.107 x 1 = 0.107 */ {{3, 3}, {{1, 64, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 106555648.}, + /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 512, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 104960000.}, + /* GFLOPS 0.105 x 1 = 0.105 */ {{1, 1}, {{1, 512, 10, 10}}, 1024, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 104960000.}, + /* GFLOPS 0.103 x 1 = 0.103 */ {{1, 1}, {{1, 128, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 103161856.}, + /* GFLOPS 0.051 x 2 = 0.103 */ {{1, 1}, {{1, 256, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 51480576.}, + /* GFLOPS 0.051 x 2 = 0.103 */ {{1, 1}, {{1, 256, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 51480576.}, + /* GFLOPS 0.101 x 1 = 0.101 */ {{1, 1}, {{1, 512, 19, 19}}, 273, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 101016825.}, + /* GFLOPS 0.096 x 1 = 0.096 */ {{1, 1}, {{1, 480, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 96438272.}, + /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 128, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 95003648.}, + /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 128, 38, 38}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 95003648.}, + /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 256, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 94818816.}, + /* GFLOPS 0.095 x 1 = 0.095 */ {{1, 1}, {{1, 256, 19, 19}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 94818816.}, + /* GFLOPS 0.094 x 1 = 0.094 */ {{1, 1}, {{1, 32, 150, 150}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 93600000.}, + /* GFLOPS 0.094 x 1 = 0.094 */ {{1, 1}, {{1, 32, 150, 150}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 93600000.}, + /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 512, 38, 50}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 93480000.}, + /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 576, 19, 19}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 93236192.}, + /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 64, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 92880000.}, + /* GFLOPS 0.093 x 1 = 0.093 */ {{1, 1}, {{1, 64, 75, 75}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 92880000.}, + /* GFLOPS 0.031 x 3 = 0.092 */ {{1, 1}, {{1, 160, 10, 10}}, 960, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 30816000.}, + /* GFLOPS 0.092 x 1 = 0.092 */ {{1, 1}, {{1, 192, 75, 100}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 92400000.}, + /* GFLOPS 0.090 x 1 = 0.090 */ {{1, 1}, {{1, 448, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 90015744.}, + /* GFLOPS 0.045 x 2 = 0.090 */ {{3, 3}, {{1, 576, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44918508.}, + /* GFLOPS 0.089 x 1 = 0.089 */ {{3, 3}, {{1, 112, 14, 14}}, 224, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 88554368.}, + /* GFLOPS 0.089 x 1 = 0.089 */ {{3, 3}, {{1, 112, 14, 14}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 88554368.}, + /* GFLOPS 0.021 x 4 = 0.084 */ {{5, 1}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {2, 0}, {0, 0}, "", false, 21037056.}, + /* GFLOPS 0.021 x 4 = 0.084 */ {{1, 5}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {1, 1}, {0, 2}, {0, 0}, "", true, 21037056.}, + /* GFLOPS 0.084 x 1 = 0.084 */ {{1, 1}, {{1, 416, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 83593216.}, + /* GFLOPS 0.082 x 1 = 0.082 */ {{1, 1}, {{1, 320, 10, 10}}, 1280, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 82048000.}, + /* GFLOPS 0.040 x 2 = 0.080 */ {{1, 1}, {{1, 576, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39958368.}, + /* GFLOPS 0.040 x 2 = 0.079 */ {{1, 1}, {{1, 24, 75, 75}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39690000.}, + /* GFLOPS 0.040 x 2 = 0.079 */ {{3, 3}, {{1, 3, 300, 300}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39600000.}, + /* GFLOPS 0.077 x 1 = 0.077 */ {{1, 1}, {{1, 96, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 77471744.}, + /* GFLOPS 0.077 x 1 = 0.077 */ {{3, 3}, {{1, 192, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 77436800.}, + /* GFLOPS 0.077 x 1 = 0.077 */ {{1, 1}, {{1, 384, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 77170688.}, + /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {8, 8}, {8, 8}, {0, 0}, "", true, 37814272.}, + /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {4, 4}, {4, 4}, {0, 0}, "", true, 37814272.}, + /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {2, 2}, {2, 2}, {0, 0}, "", true, 37814272.}, + /* GFLOPS 0.038 x 2 = 0.076 */ {{3, 3}, {{1, 32, 32, 64}}, 32, 1, {1, 1}, {16, 16}, {16, 16}, {0, 0}, "", true, 37814272.}, + /* GFLOPS 0.018 x 4 = 0.072 */ {{1, 1}, {{1, 64, 19, 19}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 17882496.}, + /* GFLOPS 0.071 x 1 = 0.071 */ {{1, 1}, {{1, 16, 150, 150}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 71280000.}, + /* GFLOPS 0.071 x 1 = 0.071 */ {{1, 1}, {{1, 352, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 70748160.}, + /* GFLOPS 0.071 x 1 = 0.071 */ {{1, 1}, {{1, 24, 150, 150}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 70560000.}, + /* GFLOPS 0.070 x 1 = 0.070 */ {{3, 3}, {{1, 96, 14, 14}}, 208, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 70487872.}, + /* GFLOPS 0.069 x 1 = 0.069 */ {{3, 3}, {{1, 96, 14, 14}}, 204, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 69132336.}, + /* GFLOPS 0.066 x 1 = 0.066 */ {{1, 1}, {{1, 1280, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 65561600.}, + /* GFLOPS 0.033 x 2 = 0.065 */ {{3, 3}, {{1, 48, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 32551680.}, + /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 192, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 65046912.}, + /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 192, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 65046912.}, + /* GFLOPS 0.065 x 1 = 0.065 */ {{3, 3}, {{1, 160, 10, 10}}, 224, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 64534400.}, + /* GFLOPS 0.064 x 1 = 0.064 */ {{1, 1}, {{1, 320, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 64325632.}, + /* GFLOPS 0.032 x 2 = 0.064 */ {{3, 3}, {{1, 96, 12, 12}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 31868928.}, + /* GFLOPS 0.061 x 1 = 0.061 */ {{1, 1}, {{1, 960, 10, 10}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 61472000.}, + /* GFLOPS 0.031 x 2 = 0.061 */ {{1, 1}, {{1, 960, 10, 10}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 30736000.}, + /* GFLOPS 0.060 x 1 = 0.060 */ {{3, 3}, {{1, 96, 38, 38}}, 96, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 59920224.}, + /* GFLOPS 0.059 x 1 = 0.059 */ {{1, 1}, {{1, 320, 38, 38}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 59238656.}, + /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 128, 19, 19}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 59008000.}, + /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 256, 10, 10}}, 512, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 58995200.}, + /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 256, 10, 10}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 58995200.}, + /* GFLOPS 0.059 x 1 = 0.059 */ {{3, 3}, {{1, 256, 10, 10}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 58995200.}, + /* GFLOPS 0.058 x 1 = 0.058 */ {{1, 1}, {{1, 288, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 57903104.}, + /* GFLOPS 0.004 x 16 = 0.058 */ {{3, 3}, {{1, 128, 7, 7}}, 32, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", false, 3614240.}, + /* GFLOPS 0.055 x 1 = 0.055 */ {{3, 3}, {{1, 1280, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 55298400.}, + /* GFLOPS 0.018 x 3 = 0.054 */ {{1, 1}, {{1, 32, 38, 38}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 18021120.}, + /* GFLOPS 0.018 x 3 = 0.053 */ {{1, 1}, {{1, 384, 19, 19}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 17766976.}, + /* GFLOPS 0.053 x 1 = 0.053 */ {{3, 3}, {{1, 128, 38, 38}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 53254720.}, + /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 528, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 53036032.}, + /* GFLOPS 0.053 x 1 = 0.053 */ {{1, 1}, {{1, 528, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 53036032.}, + /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 52454400.}, + /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 52454400.}, + /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 52454400.}, + /* GFLOPS 0.026 x 2 = 0.052 */ {{1, 1}, {{1, 1024, 10, 10}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 26227200.}, + /* GFLOPS 0.052 x 1 = 0.052 */ {{1, 1}, {{1, 64, 56, 56}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 51781632.}, + /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 256, 56, 56}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 51480576.}, + /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 256, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 51480576.}, + /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 512, 28, 28}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 51430400.}, + /* GFLOPS 0.026 x 2 = 0.051 */ {{1, 1}, {{1, 512, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25715200.}, + /* GFLOPS 0.026 x 2 = 0.051 */ {{1, 1}, {{1, 512, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 25715200.}, + /* GFLOPS 0.013 x 4 = 0.051 */ {{1, 1}, {{1, 512, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12857600.}, + /* GFLOPS 0.051 x 1 = 0.051 */ {{1, 1}, {{1, 1024, 14, 14}}, 512, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 51405312.}, + /* GFLOPS 0.050 x 1 = 0.050 */ {{1, 1}, {{1, 992, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 49799680.}, + /* GFLOPS 0.048 x 1 = 0.048 */ {{1, 1}, {{1, 960, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 48194048.}, + /* GFLOPS 0.047 x 1 = 0.047 */ {{1, 1}, {{1, 256, 19, 19}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 47409408.}, + /* GFLOPS 0.047 x 1 = 0.047 */ {{1, 1}, {{1, 512, 38, 50}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 46740000.}, + /* GFLOPS 0.047 x 1 = 0.047 */ {{1, 1}, {{1, 928, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 46588416.}, + /* GFLOPS 0.046 x 1 = 0.046 */ {{1, 1}, {{1, 64, 75, 75}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 46440000.}, + /* GFLOPS 0.023 x 2 = 0.045 */ {{3, 3}, {{1, 256, 3, 3}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 22648626.}, + /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 160, 7, 7}}, 320, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 45174080.}, + /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 160, 7, 7}}, 320, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 45174080.}, + /* GFLOPS 0.045 x 1 = 0.045 */ {{1, 1}, {{1, 224, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 45058048.}, + /* GFLOPS 0.023 x 2 = 0.045 */ {{1, 1}, {{1, 512, 14, 14}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 22500800.}, + /* GFLOPS 0.045 x 1 = 0.045 */ {{1, 1}, {{1, 896, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 44982784.}, + /* GFLOPS 0.045 x 1 = 0.045 */ {{3, 3}, {{1, 3, 227, 227}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", true, 44946880.}, + /* GFLOPS 0.044 x 1 = 0.044 */ {{3, 3}, {{1, 128, 19, 19}}, 192, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44256000.}, + /* GFLOPS 0.044 x 1 = 0.044 */ {{3, 3}, {{1, 1024, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 44239200.}, + /* GFLOPS 0.043 x 1 = 0.043 */ {{7, 7}, {{1, 3, 96, 96}}, 64, 1, {2, 2}, {1, 1}, {3, 3}, {0, 0}, "", true, 43499520.}, + /* GFLOPS 0.043 x 1 = 0.043 */ {{1, 1}, {{1, 864, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 43377152.}, + /* GFLOPS 0.042 x 1 = 0.042 */ {{1, 1}, {{1, 832, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 41771520.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{5, 5}, {{1, 32, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 40165888.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{5, 5}, {{1, 32, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 40165888.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{1, 1}, {{1, 800, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 40165888.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 64, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 39958368.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 256, 19, 19}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 39932376.}, + /* GFLOPS 0.040 x 1 = 0.040 */ {{3, 3}, {{1, 3, 300, 300}}, 32, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 39600000.}, + /* GFLOPS 0.039 x 1 = 0.039 */ {{1, 1}, {{1, 144, 75, 75}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 39015000.}, + /* GFLOPS 0.039 x 1 = 0.039 */ {{1, 1}, {{1, 192, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 38635520.}, + /* GFLOPS 0.039 x 1 = 0.039 */ {{1, 1}, {{1, 768, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 38560256.}, + /* GFLOPS 0.037 x 1 = 0.037 */ {{1, 1}, {{1, 736, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 36954624.}, + /* GFLOPS 0.036 x 1 = 0.036 */ {{1, 1}, {{1, 480, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 36164352.}, + /* GFLOPS 0.036 x 1 = 0.036 */ {{1, 1}, {{1, 480, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 36164352.}, + /* GFLOPS 0.018 x 2 = 0.036 */ {{1, 1}, {{1, 192, 38, 38}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 17790080.}, + /* GFLOPS 0.035 x 1 = 0.035 */ {{1, 1}, {{1, 704, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 35348992.}, + /* GFLOPS 0.034 x 1 = 0.034 */ {{1, 1}, {{1, 672, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 33743360.}, + /* GFLOPS 0.034 x 1 = 0.034 */ {{1, 1}, {{1, 128, 32, 64}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 33685504.}, + /* GFLOPS 0.034 x 1 = 0.034 */ {{2, 2}, {{1, 64, 64, 128}}, 32, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 33619968.}, + /* GFLOPS 0.033 x 1 = 0.033 */ {{1, 1}, {{1, 528, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 33147520.}, + /* GFLOPS 0.033 x 1 = 0.033 */ {{1, 1}, {{1, 528, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 33147520.}, + /* GFLOPS 0.033 x 1 = 0.033 */ {{1, 1}, {{1, 1024, 10, 10}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 32784000.}, + /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 160, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 32212992.}, + /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 512, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 32144000.}, + /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 640, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 32137728.}, + /* GFLOPS 0.032 x 1 = 0.032 */ {{1, 1}, {{1, 508, 14, 14}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 31893120.}, + /* GFLOPS 0.031 x 1 = 0.031 */ {{1, 1}, {{1, 832, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 31328640.}, + /* GFLOPS 0.031 x 1 = 0.031 */ {{1, 1}, {{1, 832, 7, 7}}, 384, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 31328640.}, + /* GFLOPS 0.031 x 1 = 0.031 */ {{1, 1}, {{1, 608, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 30532096.}, + /* GFLOPS 0.015 x 2 = 0.030 */ {{5, 5}, {{1, 24, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 15065344.}, + /* GFLOPS 0.015 x 2 = 0.030 */ {{5, 5}, {{1, 24, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 15065344.}, + /* GFLOPS 0.015 x 2 = 0.030 */ {{5, 5}, {{1, 48, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 15059072.}, + /* GFLOPS 0.029 x 1 = 0.029 */ {{3, 3}, {{1, 256, 10, 10}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 29497600.}, + /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 192, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 28976640.}, + /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 192, 28, 28}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 28976640.}, + /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 512, 14, 14}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 28929600.}, + /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 512, 14, 14}}, 144, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 28929600.}, + /* GFLOPS 0.029 x 1 = 0.029 */ {{1, 1}, {{1, 576, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 28926464.}, + /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 544, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 27320832.}, + /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 384, 19, 19}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 26650464.}, + /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 576, 19, 19}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 26638912.}, + /* GFLOPS 0.027 x 1 = 0.027 */ {{3, 3}, {{1, 128, 38, 38}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 26627360.}, + /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 528, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 26518016.}, + /* GFLOPS 0.027 x 1 = 0.027 */ {{1, 1}, {{1, 528, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 26518016.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 96, 75, 75}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 26055000.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "VALID", true, 25890816.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25890816.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 64, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 25890816.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 1024, 10, 10}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25817400.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 128, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 25790464.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 256, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 25740288.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 256, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 25740288.}, + /* GFLOPS 0.013 x 2 = 0.026 */ {{1, 1}, {{1, 256, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12870144.}, + /* GFLOPS 0.026 x 1 = 0.026 */ {{1, 1}, {{1, 512, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 25715200.}, + /* GFLOPS 0.013 x 2 = 0.026 */ {{1, 1}, {{1, 512, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12857600.}, + /* GFLOPS 0.024 x 1 = 0.024 */ {{1, 1}, {{1, 480, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 24109568.}, + /* GFLOPS 0.024 x 1 = 0.024 */ {{1, 1}, {{1, 128, 38, 38}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 23750912.}, + /* GFLOPS 0.024 x 1 = 0.024 */ {{1, 1}, {{1, 256, 19, 19}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 23704704.}, + /* GFLOPS 0.023 x 1 = 0.023 */ {{3, 3}, {{1, 3, 256, 512}}, 13, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 23429120.}, + /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 32, 150, 150}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 23400000.}, + /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 512, 19, 19}}, 63, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 23311575.}, + /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 448, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 22503936.}, + /* GFLOPS 0.023 x 1 = 0.023 */ {{1, 1}, {{1, 512, 14, 14}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 22500800.}, + /* GFLOPS 0.022 x 1 = 0.022 */ {{1, 1}, {{1, 508, 14, 14}}, 112, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 22325184.}, + /* GFLOPS 0.021 x 1 = 0.021 */ {{3, 3}, {{1, 128, 12, 12}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 21242880.}, + /* GFLOPS 0.021 x 1 = 0.021 */ {{1, 1}, {{1, 416, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 20898304.}, + /* GFLOPS 0.021 x 1 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 20885760.}, + /* GFLOPS 0.021 x 1 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 20885760.}, + /* GFLOPS 0.010 x 2 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 10442880.}, + /* GFLOPS 0.010 x 2 = 0.021 */ {{1, 1}, {{1, 832, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10442880.}, + /* GFLOPS 0.010 x 2 = 0.020 */ {{3, 3}, {{1, 256, 2, 2}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 10066056.}, + /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 16, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 20095488.}, + /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 16, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 20095488.}, + /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 32, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 20082944.}, + /* GFLOPS 0.020 x 1 = 0.020 */ {{5, 5}, {{1, 32, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 20082944.}, + /* GFLOPS 0.020 x 1 = 0.020 */ {{3, 3}, {{1, 256, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 19966188.}, + /* GFLOPS 0.019 x 1 = 0.019 */ {{1, 1}, {{1, 192, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 19317760.}, + /* GFLOPS 0.019 x 1 = 0.019 */ {{1, 1}, {{1, 192, 28, 28}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 19317760.}, + /* GFLOPS 0.019 x 1 = 0.019 */ {{1, 1}, {{1, 384, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 19292672.}, + /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 576, 10, 10}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 18448000.}, + /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 480, 14, 14}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 18082176.}, + /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 480, 14, 14}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 18082176.}, + /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 192, 38, 38}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 17790080.}, + /* GFLOPS 0.018 x 1 = 0.018 */ {{1, 1}, {{1, 352, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 17687040.}, + /* GFLOPS 0.017 x 1 = 0.017 */ {{2, 2}, {{1, 16, 128, 256}}, 16, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 16908288.}, + /* GFLOPS 0.016 x 1 = 0.016 */ {{1, 1}, {{1, 320, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 16081408.}, + /* GFLOPS 0.016 x 1 = 0.016 */ {{1, 1}, {{1, 832, 7, 7}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 15664320.}, + /* GFLOPS 0.016 x 1 = 0.016 */ {{1, 1}, {{1, 832, 7, 7}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 15664320.}, + /* GFLOPS 0.015 x 1 = 0.015 */ {{5, 5}, {{1, 48, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 15059072.}, + /* GFLOPS 0.015 x 1 = 0.015 */ {{5, 5}, {{1, 32, 12, 12}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 14754816.}, + /* GFLOPS 0.014 x 1 = 0.014 */ {{1, 1}, {{1, 288, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 14475776.}, + /* GFLOPS 0.014 x 1 = 0.014 */ {{1, 1}, {{1, 512, 5, 5}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 13991250.}, + /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 144, 38, 38}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 13354112.}, + /* GFLOPS 0.007 x 2 = 0.013 */ {{1, 1}, {{1, 16, 56, 56}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6623232.}, + /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 832, 7, 7}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 13053600.}, + /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 832, 7, 7}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 13053600.}, + /* GFLOPS 0.007 x 2 = 0.013 */ {{1, 1}, {{1, 32, 28, 28}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6522880.}, + /* GFLOPS 0.006 x 2 = 0.013 */ {{1, 1}, {{1, 64, 14, 14}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6472704.}, + /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 128, 56, 56}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12895232.}, + /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 256, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12870144.}, + /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 256, 14, 14}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 12870144.}, + /* GFLOPS 0.013 x 1 = 0.013 */ {{1, 1}, {{1, 508, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12757248.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 992, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 12449920.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 480, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 12054784.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 480, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 12054784.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 960, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 12048512.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 32, 75, 75}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "", false, 12014080.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{3, 3}, {{1, 96, 6, 6}}, 192, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 11950848.}, + /* GFLOPS 0.006 x 2 = 0.012 */ {{3, 3}, {{1, 96, 3, 3}}, 384, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 5975424.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 320, 12, 12}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 11814912.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 640, 6, 6}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 11805696.}, + /* GFLOPS 0.012 x 1 = 0.012 */ {{1, 1}, {{1, 928, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 11647104.}, + /* GFLOPS 0.011 x 1 = 0.011 */ {{1, 1}, {{1, 896, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 11245696.}, + /* GFLOPS 0.011 x 1 = 0.011 */ {{3, 3}, {{1, 256, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 11061600.}, + /* GFLOPS 0.006 x 2 = 0.011 */ {{3, 3}, {{1, 512, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5530200.}, + /* GFLOPS 0.011 x 1 = 0.011 */ {{1, 1}, {{1, 864, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 10844288.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 832, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 10442880.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{5, 5}, {{1, 32, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 10041472.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 800, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 10041472.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 192, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9658880.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 192, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 9658880.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 384, 14, 14}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 9646336.}, + /* GFLOPS 0.005 x 2 = 0.010 */ {{1, 1}, {{1, 512, 14, 14}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4821600.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{1, 1}, {{1, 768, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 9640064.}, + /* GFLOPS 0.010 x 1 = 0.010 */ {{3, 3}, {{1, 4, 128, 256}}, 4, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 9568256.}, + /* GFLOPS 0.005 x 2 = 0.009 */ {{1, 1}, {{1, 4, 128, 256}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 4718592.}, + /* GFLOPS 0.009 x 1 = 0.009 */ {{1, 1}, {{1, 736, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 9238656.}, + /* GFLOPS 0.009 x 1 = 0.009 */ {{1, 1}, {{1, 192, 19, 19}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 8895040.}, + /* GFLOPS 0.009 x 1 = 0.009 */ {{1, 1}, {{1, 704, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8837248.}, + /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 672, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8435840.}, + /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 128, 32, 64}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8421376.}, + /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 640, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 8034432.}, + /* GFLOPS 0.004 x 2 = 0.008 */ {{1, 1}, {{1, 832, 7, 7}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 3916080.}, + /* GFLOPS 0.008 x 1 = 0.008 */ {{1, 1}, {{1, 608, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 7633024.}, + /* GFLOPS 0.008 x 1 = 0.008 */ {{5, 5}, {{1, 16, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 7535808.}, + /* GFLOPS 0.008 x 1 = 0.008 */ {{5, 5}, {{1, 16, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 7535808.}, + /* GFLOPS 0.004 x 2 = 0.007 */ {{3, 3}, {{1, 64, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 3689600.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 640, 6, 6}}, 160, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7378560.}, + /* GFLOPS 0.004 x 2 = 0.007 */ {{1, 1}, {{1, 48, 14, 14}}, 192, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3650304.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 384, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7234752.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 576, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 7231616.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 256, 12, 12}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 7091712.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 544, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 6830208.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{3, 3}, {{1, 160, 6, 6}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 6637824.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 528, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6629504.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 528, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6629504.}, + /* GFLOPS 0.007 x 1 = 0.007 */ {{1, 1}, {{1, 256, 5, 5}}, 512, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 6566400.}, + /* GFLOPS 0.003 x 2 = 0.007 */ {{1, 1}, {{1, 512, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 3280000.}, + /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 64, 56, 56}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6472704.}, + /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 128, 28, 28}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6447616.}, + /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 512, 7, 7}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 6428800.}, + /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 512, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6428800.}, + /* GFLOPS 0.006 x 1 = 0.006 */ {{1, 1}, {{1, 512, 14, 14}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6428800.}, + /* GFLOPS 0.006 x 1 = 0.006 */ {{3, 3}, {{1, 256, 10, 10}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 5530800.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 192, 12, 12}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 5322240.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{3, 3}, {{1, 128, 5, 5}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 5310720.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{3, 3}, {{1, 128, 5, 5}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 5310720.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{3, 3}, {{1, 128, 5, 5}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 5310720.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 1024, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4917600.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 1024, 10, 10}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4917600.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 192, 28, 28}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4829440.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 192, 28, 28}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4829440.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 256, 14, 14}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4826304.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 512, 14, 14}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4821600.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 508, 14, 14}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4783968.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 64, 24, 24}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4755456.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 256, 12, 12}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4727808.}, + /* GFLOPS 0.005 x 1 = 0.005 */ {{1, 1}, {{1, 1024, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4720896.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 512, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4440300.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 512, 19, 19}}, 12, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 4440300.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 640, 6, 6}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 4427136.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 16, 128, 256}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 4325376.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 64, 64, 128}}, 4, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", false, 4227072.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 832, 7, 7}}, 48, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3916080.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 16, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 3691008.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{3, 3}, {{1, 64, 10, 10}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 3689600.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 32, 6, 6}}, 64, 1, {1, 1}, {1, 1}, {2, 2}, {0, 0}, "", true, 3688704.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 32, 12, 12}}, 64, 1, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 3688704.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{5, 5}, {{1, 64, 6, 6}}, 128, 1, {2, 2}, {1, 1}, {2, 2}, {0, 0}, "", true, 3687552.}, + /* GFLOPS 0.004 x 1 = 0.004 */ {{1, 1}, {{1, 192, 12, 12}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3548160.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 736, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3393792.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 256, 10, 10}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3283200.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 512, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3280000.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 512, 5, 5}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 3280000.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 512, 5, 5}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3228750.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 480, 14, 14}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 3013696.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 480, 14, 14}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 3013696.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 320, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2953728.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 640, 6, 6}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2951424.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{3, 3}, {{1, 128, 5, 5}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2655360.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 832, 7, 7}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2610720.}, + /* GFLOPS 0.003 x 1 = 0.003 */ {{1, 1}, {{1, 256, 3, 3}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2520882.}, + /* GFLOPS 0.001 x 2 = 0.003 */ {{3, 3}, {{1, 128, 1, 1}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1258530.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 256, 12, 12}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2363904.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 2360320.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2360320.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{3, 3}, {{1, 128, 3, 3}}, 256, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 2360320.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 528, 4, 4}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2164736.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 508, 4, 4}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 2082816.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 1024, 1, 1}}, 1000, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 2049000.}, + /* GFLOPS 0.001 x 2 = 0.002 */ {{3, 3}, {{1, 256, 3, 3}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 995544.}, + /* GFLOPS 0.001 x 2 = 0.002 */ {{3, 3}, {{1, 128, 5, 5}}, 16, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 922000.}, + /* GFLOPS 0.002 x 1 = 0.002 */ {{1, 1}, {{1, 1024, 3, 3}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1770336.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 640, 6, 6}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1475712.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 1383000.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 736, 3, 3}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 1272672.}, + /* GFLOPS 0.001 x 2 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 590976.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 3, 3}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 1180160.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 2, 2}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 1120392.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{3, 3}, {{1, 128, 5, 5}}, 8, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 461000.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 192, 12, 12}}, 16, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 887040.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{3, 3}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 442464.}, + /* GFLOPS 0.000 x 2 = 0.001 */ {{1, 1}, {{1, 128, 5, 5}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 411200.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{3, 3}, {{1, 128, 5, 5}}, 12, 1, {1, 1}, {1, 1}, {1, 1}, {0, 0}, "", true, 691500.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 640, 2, 2}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 655872.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 512, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 615000.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 512, 5, 5}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 615000.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 128, 3, 3}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 592128.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 590976.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 590976.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 3, 3}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 581742.}, + /* GFLOPS 0.001 x 1 = 0.001 */ {{1, 1}, {{1, 256, 4, 4}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 525312.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 192, 5, 5}}, 32, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 308000.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 2, 2}}, 256, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 263168.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 131328.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 258552.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 1024, 1, 1}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 196704.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 128, 1, {2, 2}, {1, 1}, {1, 1}, {0, 0}, "", true, 147584.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 147584.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 128, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 147584.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 736, 1, 1}}, 96, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 141408.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 546, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 140322.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 131328.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 64, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 131328.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 3, 3}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 110808.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 3, 3}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 110808.}, + /* GFLOPS 0.000 x 2 = 0.000 */ {{3, 3}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 55320.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{3, 3}, {{1, 64, 2, 2}}, 64, 1, {2, 2}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 73792.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 49248.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 256, 2, 2}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 49248.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 126, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 32382.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 64, 1, 1}}, 128, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", false, 16512.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "", true, 6168.}, + /* GFLOPS 0.000 x 1 = 0.000 */ {{1, 1}, {{1, 128, 1, 1}}, 24, 1, {1, 1}, {1, 1}, {0, 0}, {0, 0}, "SAME", true, 6168.} +}; +struct ConvParamID +{ + enum { + CONV_0 = 0, + CONV_100 = 100, + CONV_LAST = sizeof(testConvolutionConfigs) / sizeof(testConvolutionConfigs[0]) + }; + int val_; \ + ConvParamID(int val = 0) : val_(val) {} + operator int() const { return val_; } + static ::testing::internal::ParamGenerator all() + { +#if 0 + enum { NUM = (int)CONV_LAST }; +#else + enum { NUM = (int)CONV_100 }; +#endif + ConvParamID v_[NUM]; for (int i = 0; i < NUM; ++i) { v_[i] = ConvParamID(i); } // reduce generated code size + return ::testing::ValuesIn(v_, v_ + NUM); + } +}; \ +static inline void PrintTo(const ConvParamID& v, std::ostream* os) +{ + CV_Assert((int)v >= 0); CV_Assert((int)v < ConvParamID::CONV_LAST); + const ConvParam_t& p = testConvolutionConfigs[(int)v]; + + *os << "GFLOPS=" << cv::format("%.3f", p.declared_flops * 1e-9) + << ", K=" << (Size)p.kernel + << ", IN={" << p.shapeIn.dims[0] << ", " << p.shapeIn.dims[1] << ", " << p.shapeIn.dims[2] << ", " << p.shapeIn.dims[3] << "}" + << ", OCN=" << p.outCN; + if (p.groups > 1) + *os << ", G=" << p.groups; + if (((Size)p.stride).area() != 1) + *os << ", S=" << ((Size)p.stride); + if (((Size)p.dilation).area() != 1) + *os << ", D=" << ((Size)p.dilation); + if (((Size)p.pad).area() != 0) + *os << ", P=" << ((Size)p.pad); + if (((Size)p.padAdjust).area() != 0) + *os << ", PAdj=" << ((Size)p.padAdjust); + if (!((std::string)p.padMode).empty()) + *os << ", PM=" << ((std::string)p.padMode); + if (p.hasBias) + *os << ", BIAS"; +} -enum {GROUP_OFF = 1, GROUP_2 = 2}; -CV_ENUM(GroupSize, GROUP_OFF, GROUP_2); -typedef std::pair InpShapeNumOut; -typedef tuple ConvParam; //kernel_size, inp shape, groups, stride -typedef TestBaseWithParam ConvolutionPerfTest; -static inline MatShape blobShape(int count, int nplanes, int height, int width) -{ - int data[] = {count, nplanes, height, width}; - return MatShape(data, data+4); -} +typedef tuple > ConvTestParam_t; +typedef TestBaseWithParam Conv; -PERF_TEST_P( ConvolutionPerfTest, perf, Combine( - Values(Size(1, 1), Size(3, 3), Size(5, 5), Size(11, 11)), - Values(make_pair(blobShape(1, 4, 224, 224), 64), - make_pair(blobShape(1, 64, 112, 122), 128), - make_pair(blobShape(1, 256, 28, 28), 512)), - GroupSize::all(), - StrideSize::all()) -) +PERF_TEST_P_(Conv, conv) { - RNG rng(0); - - ConvParam params = GetParam(); - int ksz = get<0>(params).width; - MatShape inpShape = get<1>(params).first; - int outCn = get<1>(params).second; - int groups = get<2>(params); - int stride = (ksz >= 11) ? 4 : (int)get<3>(params); - - int inpCn = inpShape[1]; - int wgtSize[] = { outCn, inpCn/groups, ksz, ksz }; - int biasSize[] = { outCn, 1, 1, 1 }; - const int wtype = CV_32F; - Mat wgtBlob(4, wgtSize, wtype), biasBlob(4, biasSize, wtype); - Mat inpBlob(4, &inpShape[0], wtype); - rng.fill(biasBlob, RNG::UNIFORM, -1, +1); - rng.fill(wgtBlob, RNG::UNIFORM, -1, +1); - rng.fill(inpBlob, RNG::UNIFORM, -1, +1); + int test_id = (int)get<0>(GetParam()); + ASSERT_GE(test_id, 0); ASSERT_LT(test_id, ConvParamID::CONV_LAST); + const ConvParam_t& params = testConvolutionConfigs[test_id]; + double declared_flops = params.declared_flops; + Size kernel = params.kernel; + MatShape inputShape = MatShape(params.shapeIn.dims, params.shapeIn.dims + 4); + int outChannels = params.outCN; + int groups = params.groups; + Size stride = params.stride; + Size dilation = params.dilation; + Size pad = params.pad; + Size padAdjust = params.padAdjust; + std::string padMode(params.padMode); + bool hasBias = params.hasBias; + Backend backendId = get<0>(get<1>(GetParam())); + Target targetId = get<1>(get<1>(GetParam())); + + int inChannels = inputShape[1]; + Size inSize(inputShape[3], inputShape[2]); + + int sz[] = {outChannels, inChannels / groups, kernel.height, kernel.width}; + Mat weights(4, &sz[0], CV_32F); + randu(weights, -1.0f, 1.0f); LayerParams lp; - lp.set("num_output", outCn); - lp.set("group", groups); - lp.set("stride", stride); - lp.set("kernel_size", ksz); - lp.blobs.reserve(2); - lp.blobs.push_back(wgtBlob); - lp.blobs.push_back(biasBlob); - - std::vector inpBlobs(1, &inpBlob); - std::vector outBlobs, internalBlobs; - - Ptr layer = cv::dnn::LayerFactory::createLayerInstance("Convolution", lp); - std::vector inputShapes(1, shape(inpBlob)), outShapes, internals; - layer->getMemoryShapes(inputShapes, 0, outShapes, internals); - for (size_t i = 0; i < outShapes.size(); i++) + lp.set("kernel_w", kernel.width); + lp.set("kernel_h", kernel.height); + lp.set("pad_w", pad.width); + lp.set("pad_h", pad.height); + if (padAdjust.width > 0 || padAdjust.height > 0) { - outBlobs.push_back(Mat(outShapes[i], CV_32F)); + lp.set("adj_w", padAdjust.width); + lp.set("adj_h", padAdjust.height); } - for (size_t i = 0; i < internals.size(); i++) + if (!padMode.empty()) + lp.set("pad_mode", padMode); + lp.set("stride_w", stride.width); + lp.set("stride_h", stride.height); + lp.set("dilation_w", dilation.width); + lp.set("dilation_h", dilation.height); + lp.set("num_output", outChannels); + lp.set("group", groups); + lp.set("bias_term", hasBias); + lp.type = "Convolution"; + lp.name = "testLayer"; + lp.blobs.push_back(weights); + if (hasBias) { - internalBlobs.push_back(Mat()); - if (total(internals[i])) - internalBlobs.back().create(internals[i], CV_32F); + Mat bias(1, outChannels, CV_32F); + randu(bias, -1.0f, 1.0f); + lp.blobs.push_back(bias); } + int inpSz[] = {1, inChannels, inSize.height, inSize.width}; + Mat input(4, &inpSz[0], CV_32F); + randu(input, -1.0f, 1.0f); - layer->finalize(inpBlobs, outBlobs); + Net net; + net.addLayerToPrev(lp.name, lp.type, lp); - Mat inpBlob2D = inpBlob.reshape(1, outCn); - Mat wgtBlob2D = wgtBlob.reshape(1, outCn*(inpCn/groups)); - Mat outBlob2D = outBlobs[0].reshape(1, outBlobs[0].size[0]); - declare.in(inpBlob2D, wgtBlob2D, WARMUP_RNG).out(outBlob2D); + net.setInput(input); + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); - layer->forward(inpBlobs, outBlobs, internalBlobs); /// warmup + // warmup + Mat output = net.forward(); - PERF_SAMPLE_BEGIN() - layer->forward(inpBlobs, outBlobs, internalBlobs); - PERF_SAMPLE_END() + MatShape netInputShape = shape(input); + size_t weightsMemory = 0, blobsMemory = 0; + net.getMemoryConsumption(netInputShape, weightsMemory, blobsMemory); + int64 flops = net.getFLOPS(netInputShape); + CV_Assert(flops > 0); + std::cout + << "IN=" << divUp(input.total() * input.elemSize(), 1u<<10) << " Kb " << netInputShape + << " OUT=" << divUp(output.total() * output.elemSize(), 1u<<10) << " Kb " << shape(output) + << " Weights(parameters): " << divUp(weightsMemory, 1u<<10) << " Kb" + << " MFLOPS=" << flops * 1e-6 << std::endl; + + TEST_CYCLE() + { + Mat res = net.forward(); + } + + EXPECT_NEAR(flops, declared_flops, declared_flops * 1e-6); SANITY_CHECK_NOTHING(); } +static const tuple testBackendsAndTargets[] = { + tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU), + tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL), + tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16) +}; + +INSTANTIATE_TEST_CASE_P(/**/, Conv, Combine( + ConvParamID::all(), + testing::ValuesIn(testBackendsAndTargets) +)); + } // namespace diff --git a/modules/dnn/perf/perf_precomp.hpp b/modules/dnn/perf/perf_precomp.hpp index 5e58a312fb..81816ca7ec 100644 --- a/modules/dnn/perf/perf_precomp.hpp +++ b/modules/dnn/perf/perf_precomp.hpp @@ -4,6 +4,8 @@ #include #include +#include "../test/test_common.hpp" + namespace opencv_test { using namespace perf; using namespace cv::dnn; diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index ec43f3e046..b752f4f5ec 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -42,6 +42,43 @@ #ifndef __OPENCV_TEST_COMMON_HPP__ #define __OPENCV_TEST_COMMON_HPP__ +namespace cv { namespace dnn { +CV__DNN_EXPERIMENTAL_NS_BEGIN +static inline void PrintTo(const cv::dnn::Backend& v, std::ostream* os) +{ + switch (v) { + case DNN_BACKEND_DEFAULT: *os << "DEFAULT"; return; + case DNN_BACKEND_HALIDE: *os << "HALIDE"; return; + case DNN_BACKEND_INFERENCE_ENGINE: *os << "DLIE"; return; + case DNN_BACKEND_OPENCV: *os << "OCV"; return; + } // don't use "default:" to emit compiler warnings + *os << "DNN_BACKEND_UNKNOWN(" << v << ")"; +} + +static inline void PrintTo(const cv::dnn::Target& v, std::ostream* os) +{ + switch (v) { + case DNN_TARGET_CPU: *os << "CPU"; return; + case DNN_TARGET_OPENCL: *os << "OCL"; return; + case DNN_TARGET_OPENCL_FP16: *os << "OCL_FP16"; return; + case DNN_TARGET_MYRIAD: *os << "MYRIAD"; return; + } // don't use "default:" to emit compiler warnings + *os << "DNN_TARGET_UNKNOWN(" << v << ")"; +} + +using opencv_test::tuple; +using opencv_test::get; +static inline void PrintTo(const tuple v, std::ostream* os) +{ + PrintTo(get<0>(v), os); + *os << "/"; + PrintTo(get<1>(v), os); +} + +CV__DNN_EXPERIMENTAL_NS_END +}} // namespace + + static inline const std::string &getOpenCVExtraDir() { return cvtest::TS::ptr()->get_data_path(); diff --git a/modules/dnn/test/test_precomp.hpp b/modules/dnn/test/test_precomp.hpp index a0a46ab271..0cd8b582e6 100644 --- a/modules/dnn/test/test_precomp.hpp +++ b/modules/dnn/test/test_precomp.hpp @@ -49,35 +49,6 @@ #include "opencv2/dnn.hpp" #include "test_common.hpp" -namespace cv { -namespace dnn { -CV__DNN_EXPERIMENTAL_NS_BEGIN - -static inline void PrintTo(const cv::dnn::Backend& v, std::ostream* os) -{ - switch (v) { - case DNN_BACKEND_DEFAULT: *os << "DNN_BACKEND_DEFAULT"; return; - case DNN_BACKEND_HALIDE: *os << "DNN_BACKEND_HALIDE"; return; - case DNN_BACKEND_INFERENCE_ENGINE: *os << "DNN_BACKEND_INFERENCE_ENGINE"; return; - case DNN_BACKEND_OPENCV: *os << "DNN_BACKEND_OPENCV"; return; - } // don't use "default:" to emit compiler warnings - *os << "DNN_BACKEND_UNKNOWN(" << v << ")"; -} - -static inline void PrintTo(const cv::dnn::Target& v, std::ostream* os) -{ - switch (v) { - case DNN_TARGET_CPU: *os << "DNN_TARGET_CPU"; return; - case DNN_TARGET_OPENCL: *os << "DNN_TARGET_OPENCL"; return; - case DNN_TARGET_OPENCL_FP16: *os << "DNN_TARGET_OPENCL_FP16"; return; - case DNN_TARGET_MYRIAD: *os << "DNN_TARGET_MYRIAD"; return; - } // don't use "default:" to emit compiler warnings - *os << "DNN_TARGET_UNKNOWN(" << v << ")"; -} - -CV__DNN_EXPERIMENTAL_NS_END -}} // namespace - namespace opencv_test { using namespace cv::dnn; From c557193b8c3684cea403e30c5374edcaf86a9793 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 30 Aug 2018 17:53:41 +0300 Subject: [PATCH 17/47] dnn(test): use dnnBackendsAndTargets() param generator --- modules/dnn/perf/perf_convolution.cpp | 8 +--- modules/dnn/perf/perf_net.cpp | 23 +----------- modules/dnn/test/test_backends.cpp | 17 +-------- modules/dnn/test/test_common.hpp | 50 +++++++++++++++++++++++++ modules/dnn/test/test_halide_layers.cpp | 18 +-------- modules/dnn/test/test_precomp.hpp | 16 -------- 6 files changed, 56 insertions(+), 76 deletions(-) diff --git a/modules/dnn/perf/perf_convolution.cpp b/modules/dnn/perf/perf_convolution.cpp index 5164ac0130..be742ea48b 100644 --- a/modules/dnn/perf/perf_convolution.cpp +++ b/modules/dnn/perf/perf_convolution.cpp @@ -666,15 +666,9 @@ PERF_TEST_P_(Conv, conv) SANITY_CHECK_NOTHING(); } -static const tuple testBackendsAndTargets[] = { - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16) -}; - INSTANTIATE_TEST_CASE_P(/**/, Conv, Combine( ConvParamID::all(), - testing::ValuesIn(testBackendsAndTargets) + dnnBackendsAndTargets(false, false) // defined in ../test/test_common.hpp )); } // namespace diff --git a/modules/dnn/perf/perf_net.cpp b/modules/dnn/perf/perf_net.cpp index c6cef9f4f7..192604b861 100644 --- a/modules/dnn/perf/perf_net.cpp +++ b/modules/dnn/perf/perf_net.cpp @@ -14,10 +14,7 @@ namespace opencv_test { -CV_ENUM(DNNBackend, DNN_BACKEND_DEFAULT, DNN_BACKEND_HALIDE, DNN_BACKEND_INFERENCE_ENGINE, DNN_BACKEND_OPENCV) -CV_ENUM(DNNTarget, DNN_TARGET_CPU, DNN_TARGET_OPENCL, DNN_TARGET_OPENCL_FP16, DNN_TARGET_MYRIAD) - -class DNNTestNetwork : public ::perf::TestBaseWithParam< tuple > +class DNNTestNetwork : public ::perf::TestBaseWithParam< tuple > { public: dnn::Backend backend; @@ -269,22 +266,6 @@ PERF_TEST_P_(DNNTestNetwork, Inception_v2_Faster_RCNN) Mat(cv::Size(800, 600), CV_32FC3)); } -const tuple testCases[] = { -#ifdef HAVE_HALIDE - tuple(DNN_BACKEND_HALIDE, DNN_TARGET_CPU), - tuple(DNN_BACKEND_HALIDE, DNN_TARGET_OPENCL), -#endif -#ifdef HAVE_INF_ENGINE - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD), -#endif - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16) -}; - -INSTANTIATE_TEST_CASE_P(/*nothing*/, DNNTestNetwork, testing::ValuesIn(testCases)); +INSTANTIATE_TEST_CASE_P(/*nothing*/, DNNTestNetwork, dnnBackendsAndTargets()); } // namespace diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 309f0010e4..8a3653957c 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -285,21 +285,6 @@ TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16) processNet("dnn/fast_neural_style_eccv16_starry_night.t7", "", inp, "", "", l1, lInf); } -const tuple testCases[] = { -#ifdef HAVE_HALIDE - tuple(DNN_BACKEND_HALIDE, DNN_TARGET_CPU), - tuple(DNN_BACKEND_HALIDE, DNN_TARGET_OPENCL), -#endif -#ifdef HAVE_INF_ENGINE - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD), -#endif - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16) -}; - -INSTANTIATE_TEST_CASE_P(/*nothing*/, DNNTestNetwork, testing::ValuesIn(testCases)); +INSTANTIATE_TEST_CASE_P(/*nothing*/, DNNTestNetwork, dnnBackendsAndTargets(true, true, false)); }} // namespace diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index b752f4f5ec..e5fb5859b6 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -42,6 +42,10 @@ #ifndef __OPENCV_TEST_COMMON_HPP__ #define __OPENCV_TEST_COMMON_HPP__ +#ifdef HAVE_OPENCL +#include "opencv2/core/ocl.hpp" +#endif + namespace cv { namespace dnn { CV__DNN_EXPERIMENTAL_NS_BEGIN static inline void PrintTo(const cv::dnn::Backend& v, std::ostream* os) @@ -227,4 +231,50 @@ static inline bool readFileInMemory(const std::string& filename, std::string& co return true; } +namespace opencv_test { + +using namespace cv::dnn; + +static testing::internal::ParamGenerator > dnnBackendsAndTargets( + bool withInferenceEngine = true, + bool withHalide = false, + bool withCpuOCV = true +) +{ + std::vector > targets; +#ifdef HAVE_HALIDE + if (withHalide) + { + targets.push_back(make_tuple(DNN_BACKEND_HALIDE, DNN_TARGET_CPU)); + if (cv::ocl::useOpenCL()) + targets.push_back(make_tuple(DNN_BACKEND_HALIDE, DNN_TARGET_OPENCL)); + } +#endif +#ifdef HAVE_INF_ENGINE + if (withInferenceEngine) + { + targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU)); + if (cv::ocl::useOpenCL()) + { + targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL)); + targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16)); + } + if (checkMyriadTarget()) + targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD)); + } +#endif + if (withCpuOCV) + targets.push_back(make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)); +#ifdef HAVE_OPENCL + if (cv::ocl::useOpenCL()) + { + targets.push_back(make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL)); + targets.push_back(make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16)); + } +#endif + return testing::ValuesIn(targets); +} + +} // namespace + #endif diff --git a/modules/dnn/test/test_halide_layers.cpp b/modules/dnn/test/test_halide_layers.cpp index 11d059223c..445f85b376 100644 --- a/modules/dnn/test/test_halide_layers.cpp +++ b/modules/dnn/test/test_halide_layers.cpp @@ -44,23 +44,9 @@ static void test(LayerParams& params, Mat& input, Backend backendId, Target targ test(input, net, backendId, targetId, skipCheck); } -static testing::internal::ParamGenerator > dnnBackendsAndTargetsWithHalide() +static inline testing::internal::ParamGenerator > dnnBackendsAndTargetsWithHalide() { - static const tuple testCases[] = { -#ifdef HAVE_HALIDE - tuple(DNN_BACKEND_HALIDE, DNN_TARGET_CPU), - tuple(DNN_BACKEND_HALIDE, DNN_TARGET_OPENCL), -#endif -#ifdef HAVE_INF_ENGINE - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD), -#endif - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16) - }; - return testing::ValuesIn(testCases); + return dnnBackendsAndTargets(true, true, false); // OpenCV/CPU is used as reference } class Test_Halide_layers : public DNNTestLayer {}; diff --git a/modules/dnn/test/test_precomp.hpp b/modules/dnn/test/test_precomp.hpp index 0cd8b582e6..6c1fbd64b1 100644 --- a/modules/dnn/test/test_precomp.hpp +++ b/modules/dnn/test/test_precomp.hpp @@ -66,22 +66,6 @@ static testing::internal::ParamGenerator availableDnnTargets() return testing::ValuesIn(targets); } -static testing::internal::ParamGenerator > dnnBackendsAndTargets() -{ - static const tuple testCases[] = { - #ifdef HAVE_INF_ENGINE - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16), - tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD), - #endif - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL), - tuple(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16) - }; - return testing::ValuesIn(testCases); -} - class DNNTestLayer : public TestWithParam > { public: From 50bceea03879b4310e28782b15f686a77a20f876 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 31 Aug 2018 15:41:56 +0300 Subject: [PATCH 18/47] Include preprocessing nodes to object detection TensorFlow networks (#12211) * Include preprocessing nodes to object detection TensorFlow networks * Enable more fusion * faster_rcnn_resnet50_coco_2018_01_28 test --- modules/dnn/CMakeLists.txt | 2 +- modules/dnn/src/dnn.cpp | 15 ++-- modules/dnn/src/layers/convolution_layer.cpp | 4 +- modules/dnn/src/layers/elementwise_layers.cpp | 90 +++++++++++++++++-- modules/dnn/test/test_backends.cpp | 8 +- modules/dnn/test/test_caffe_importer.cpp | 2 +- modules/dnn/test/test_tf_importer.cpp | 41 +++++---- modules/dnn/test/test_torch_importer.cpp | 4 +- samples/dnn/tf_text_graph_faster_rcnn.py | 2 + samples/dnn/tf_text_graph_ssd.py | 5 +- 10 files changed, 131 insertions(+), 42 deletions(-) diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index 64fefb3509..40b573f45a 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -95,7 +95,7 @@ ocv_glob_module_sources(${sources_options} SOURCES ${fw_srcs}) ocv_create_module(${libs} ${INF_ENGINE_TARGET}) ocv_add_samples() ocv_add_accuracy_tests(${INF_ENGINE_TARGET}) -ocv_add_perf_tests() +ocv_add_perf_tests(${INF_ENGINE_TARGET}) ocv_option(${the_module}_PERF_CAFFE "Add performance tests of Caffe framework" OFF) ocv_option(${the_module}_PERF_CLCAFFE "Add performance tests of clCaffe framework" OFF) diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index bc18695521..214ac9961b 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -1676,14 +1676,6 @@ struct Net::Impl // with the current layer if they follow it. Normally, the are fused with the convolution layer, // but some of them (like activation) may be fused with fully-connected, elemwise (+) and // some other layers. - - // TODO: OpenCL target support more fusion styles. - if ( preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget) && - (!cv::ocl::useOpenCL() || (ld.layerInstance->type != "Convolution" && - ld.layerInstance->type != "MVN" && ld.layerInstance->type != "Pooling" && - ld.layerInstance->type != "Concat")) ) - continue; - Ptr& currLayer = ld.layerInstance; if( ld.consumers.size() == 1 && pinsToKeep.count(LayerPin(lid, 0)) == 0 ) { @@ -1717,6 +1709,13 @@ struct Net::Impl if (preferableBackend != DNN_BACKEND_OPENCV) continue; // Go to the next layer. + // TODO: OpenCL target support more fusion styles. + if ( preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget) && + (!cv::ocl::useOpenCL() || (ld.layerInstance->type != "Convolution" && + ld.layerInstance->type != "MVN" && ld.layerInstance->type != "Pooling" && + ld.layerInstance->type != "Concat")) ) + continue; + while (nextData) { // For now, OpenCL target support fusion with activation of ReLU/ChannelsPReLU/Power/Tanh diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 169e280840..54b324538a 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -350,12 +350,14 @@ public: return false; } - void fuseWeights(const Mat& w, const Mat& b) + void fuseWeights(const Mat& w_, const Mat& b_) { // Convolution weights have OIHW data layout. Parameters fusion in case of // (conv(I) + b1 ) * w + b2 // means to replace convolution's weights to [w*conv(I)] and bias to [b1 * w + b2] const int outCn = weightsMat.size[0]; + Mat w = w_.total() == 1 ? Mat(1, outCn, CV_32F, Scalar(w_.at(0))) : w_; + Mat b = b_.total() == 1 ? Mat(1, outCn, CV_32F, Scalar(b_.at(0))) : b_; CV_Assert_N(!weightsMat.empty(), biasvec.size() == outCn + 2, w.empty() || outCn == w.total(), b.empty() || outCn == b.total()); diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 0a5ed54ca8..74c89e62de 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -161,6 +161,16 @@ public: return Ptr(); } + virtual bool tryFuse(Ptr& top) CV_OVERRIDE + { + return func.tryFuse(top); + } + + void getScaleShift(Mat& scale_, Mat& shift_) const CV_OVERRIDE + { + func.getScaleShift(scale_, shift_); + } + bool getMemoryShapes(const std::vector &inputs, const int requiredOutputs, std::vector &outputs, @@ -343,6 +353,10 @@ struct ReLUFunctor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 1; } }; @@ -448,6 +462,10 @@ struct ReLU6Functor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 2; } }; @@ -518,6 +536,10 @@ struct TanHFunctor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 1; } }; @@ -588,6 +610,10 @@ struct SigmoidFunctor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 3; } }; @@ -659,6 +685,10 @@ struct ELUFunctor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 2; } }; @@ -727,6 +757,10 @@ struct AbsValFunctor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 1; } }; @@ -775,6 +809,10 @@ struct BNLLFunctor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 5; } }; @@ -875,15 +913,51 @@ struct PowerFunctor #ifdef HAVE_INF_ENGINE InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { - lp.type = "Power"; - std::shared_ptr ieLayer(new InferenceEngine::PowerLayer(lp)); - ieLayer->power = power; - ieLayer->scale = scale; - ieLayer->offset = shift; - return ieLayer; + if (power == 1.0f && scale == 1.0f && shift == 0.0f) + { + // It looks like there is a bug in Inference Engine for DNN_TARGET_OPENCL and DNN_TARGET_OPENCL_FP16 + // if power layer do nothing so we replace it to Identity. + lp.type = "Split"; + return std::shared_ptr(new InferenceEngine::SplitLayer(lp)); + } + else + { + lp.type = "Power"; + std::shared_ptr ieLayer(new InferenceEngine::PowerLayer(lp)); + ieLayer->power = power; + ieLayer->scale = scale; + ieLayer->offset = shift; + return ieLayer; + } } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr& top) + { + if (power != 1.0f && shift != 0.0f) + return false; + + Mat w, b; + top->getScaleShift(w, b); + if ((w.empty() && b.empty()) || w.total() > 1 || b.total() > 1) + return false; + + float nextScale = w.empty() ? 1.0f : w.at(0); + float nextShift = b.empty() ? 0.0f : b.at(0); + scale = std::pow(scale, power) * nextScale; + shift = nextScale * shift + nextShift; + return true; + } + + void getScaleShift(Mat& _scale, Mat& _shift) const + { + if (power == 1.0f) + { + _scale = Mat(1, 1, CV_32F, Scalar(scale)); + _shift = Mat(1, 1, CV_32F, Scalar(shift)); + } + } + int64 getFLOPSPerElement() const { return power == 1 ? 2 : 10; } }; @@ -989,6 +1063,10 @@ struct ChannelsPReLUFunctor } #endif // HAVE_INF_ENGINE + bool tryFuse(Ptr&) { return false; } + + void getScaleShift(Mat&, Mat&) const {} + int64 getFLOPSPerElement() const { return 1; } }; diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 309f0010e4..5ab7992792 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -161,7 +161,7 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_v1_TensorFlow) if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/street.png", false)); - Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false); + Mat inp = blobFromImage(sample, 1.0f, Size(300, 300), Scalar(), false); float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.011 : 0.0; float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.06 : 0.0; processNet("dnn/ssd_mobilenet_v1_coco_2017_11_17.pb", "dnn/ssd_mobilenet_v1_coco_2017_11_17.pbtxt", @@ -173,7 +173,7 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_v2_TensorFlow) if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/street.png", false)); - Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false); + Mat inp = blobFromImage(sample, 1.0f, Size(300, 300), Scalar(), false); float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.011 : 0.0; 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", @@ -247,8 +247,8 @@ TEST_P(DNNTestNetwork, Inception_v2_SSD_TensorFlow) if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/street.png", false)); - Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false); - float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.008 : 0.0; + Mat inp = blobFromImage(sample, 1.0f, Size(300, 300), Scalar(), false); + float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.015 : 0.0; float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.0731 : 0.0; processNet("dnn/ssd_inception_v2_coco_2017_11_17.pb", "dnn/ssd_inception_v2_coco_2017_11_17.pbtxt", inp, "detection_out", "", l1, lInf); diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index 4491fde5a9..ff0bbb75af 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -417,7 +417,7 @@ TEST_P(Test_Caffe_nets, DenseNet_121) float l1 = default_l1, lInf = default_lInf; if (target == DNN_TARGET_OPENCL_FP16) { - l1 = 0.017; lInf = 0.067; + l1 = 0.017; lInf = 0.0795; } else if (target == DNN_TARGET_MYRIAD) { diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index d95f6f5081..c1a55cd701 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -296,7 +296,7 @@ TEST_P(Test_TensorFlow_nets, Inception_v2_SSD) Net net = readNetFromTensorflow(model, proto); Mat img = imread(findDataFile("dnn/street.png", false)); - Mat blob = blobFromImage(img, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), true, false); + Mat blob = blobFromImage(img, 1.0f, Size(300, 300), Scalar(), true, false); net.setPreferableBackend(backend); net.setPreferableTarget(target); @@ -310,32 +310,38 @@ TEST_P(Test_TensorFlow_nets, Inception_v2_SSD) 0, 3, 0.75838411, 0.44668293, 0.45907149, 0.49459291, 0.52197015, 0, 10, 0.95932811, 0.38349164, 0.32528657, 0.40387636, 0.39165527, 0, 10, 0.93973452, 0.66561931, 0.37841269, 0.68074018, 0.42907384); - double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 5e-3 : default_l1; + double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.0097 : default_l1; double iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.09 : default_lInf; normAssertDetections(ref, out, "", 0.5, scoreDiff, iouDiff); } -TEST_P(Test_TensorFlow_nets, Inception_v2_Faster_RCNN) +TEST_P(Test_TensorFlow_nets, Faster_RCNN) { + static std::string names[] = {"faster_rcnn_inception_v2_coco_2018_01_28", + "faster_rcnn_resnet50_coco_2018_01_28"}; + checkBackend(); if ((backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) || (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16)) throw SkipTestException(""); - std::string proto = findDataFile("dnn/faster_rcnn_inception_v2_coco_2018_01_28.pbtxt", false); - std::string model = findDataFile("dnn/faster_rcnn_inception_v2_coco_2018_01_28.pb", false); + for (int i = 1; i < 2; ++i) + { + std::string proto = findDataFile("dnn/" + names[i] + ".pbtxt", false); + std::string model = findDataFile("dnn/" + names[i] + ".pb", false); - Net net = readNetFromTensorflow(model, proto); - net.setPreferableBackend(backend); - net.setPreferableTarget(target); - Mat img = imread(findDataFile("dnn/dog416.png", false)); - Mat blob = blobFromImage(img, 1.0f / 127.5, Size(800, 600), Scalar(127.5, 127.5, 127.5), true, false); + Net net = readNetFromTensorflow(model, proto); + net.setPreferableBackend(backend); + net.setPreferableTarget(target); + Mat img = imread(findDataFile("dnn/dog416.png", false)); + Mat blob = blobFromImage(img, 1.0f, Size(800, 600), Scalar(), true, false); - net.setInput(blob); - Mat out = net.forward(); + net.setInput(blob); + Mat out = net.forward(); - Mat ref = blobFromNPY(findDataFile("dnn/tensorflow/faster_rcnn_inception_v2_coco_2018_01_28.detection_out.npy")); - normAssertDetections(ref, out, "", 0.3); + Mat ref = blobFromNPY(findDataFile("dnn/tensorflow/" + names[i] + ".detection_out.npy")); + normAssertDetections(ref, out, names[i].c_str(), 0.3); + } } TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD_PPN) @@ -347,15 +353,16 @@ TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD_PPN) Net net = readNetFromTensorflow(model, proto); Mat img = imread(findDataFile("dnn/dog416.png", false)); Mat ref = blobFromNPY(findDataFile("dnn/tensorflow/ssd_mobilenet_v1_ppn_coco.detection_out.npy", false)); - Mat blob = blobFromImage(img, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), true, false); + Mat blob = blobFromImage(img, 1.0f, Size(300, 300), Scalar(), true, false); net.setPreferableBackend(backend); net.setPreferableTarget(target); net.setInput(blob); Mat out = net.forward(); - double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.006 : default_l1; - normAssertDetections(ref, out, "", 0.4, scoreDiff, default_lInf); + double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.008 : default_l1; + double iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.021 : default_lInf; + normAssertDetections(ref, out, "", 0.4, scoreDiff, iouDiff); } TEST_P(Test_TensorFlow_nets, opencv_face_detector_uint8) diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index 13e3ddeacb..b6583da7ef 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -301,14 +301,14 @@ TEST_P(Test_Torch_nets, ENet_accuracy) // Due to numerical instability in Pooling-Unpooling layers (indexes jittering) // thresholds for ENet must be changed. Accuracy of results was checked on // Cityscapes dataset and difference in mIOU with Torch is 10E-4% - normAssert(ref, out, "", 0.00044, 0.44); + normAssert(ref, out, "", 0.00044, target == DNN_TARGET_CPU ? 0.453 : 0.44); const int N = 3; for (int i = 0; i < N; i++) { net.setInput(inputBlob, ""); Mat out = net.forward(); - normAssert(ref, out, "", 0.00044, 0.44); + normAssert(ref, out, "", 0.00044, target == DNN_TARGET_CPU ? 0.453 : 0.44); } } diff --git a/samples/dnn/tf_text_graph_faster_rcnn.py b/samples/dnn/tf_text_graph_faster_rcnn.py index d18d82bfae..b02b0c5ca6 100644 --- a/samples/dnn/tf_text_graph_faster_rcnn.py +++ b/samples/dnn/tf_text_graph_faster_rcnn.py @@ -29,6 +29,8 @@ scopesToKeep = ('FirstStageFeatureExtractor', 'Conv', 'MaxPool2D', 'SecondStageFeatureExtractor', 'SecondStageBoxPredictor', + 'Preprocessor/sub', + 'Preprocessor/mul', 'image_tensor') scopesToIgnore = ('FirstStageFeatureExtractor/Assert', diff --git a/samples/dnn/tf_text_graph_ssd.py b/samples/dnn/tf_text_graph_ssd.py index 0d4a41f34a..c07bc760a8 100644 --- a/samples/dnn/tf_text_graph_ssd.py +++ b/samples/dnn/tf_text_graph_ssd.py @@ -39,10 +39,11 @@ args = parser.parse_args() # Nodes that should be kept. keepOps = ['Conv2D', 'BiasAdd', 'Add', 'Relu6', 'Placeholder', 'FusedBatchNorm', - 'DepthwiseConv2dNative', 'ConcatV2', 'Mul', 'MaxPool', 'AvgPool', 'Identity'] + 'DepthwiseConv2dNative', 'ConcatV2', 'Mul', 'MaxPool', 'AvgPool', 'Identity', + 'Sub'] # Node with which prefixes should be removed -prefixesToRemove = ('MultipleGridAnchorGenerator/', 'Postprocessor/', 'Preprocessor/') +prefixesToRemove = ('MultipleGridAnchorGenerator/', 'Postprocessor/', 'Preprocessor/map') # Read the graph. with tf.gfile.FastGFile(args.input, 'rb') as f: From e13f6ded7f0c5e1921e45d51f7b825c8e39b4f00 Mon Sep 17 00:00:00 2001 From: berak Date: Fri, 31 Aug 2018 15:07:53 +0200 Subject: [PATCH 19/47] ml: fix adjusting K in KNearest (#12358) --- modules/ml/src/knearest.cpp | 9 +++++---- modules/ml/test/test_emknearestkmeans.cpp | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/modules/ml/src/knearest.cpp b/modules/ml/src/knearest.cpp index d608012dfb..df48b00f24 100644 --- a/modules/ml/src/knearest.cpp +++ b/modules/ml/src/knearest.cpp @@ -140,13 +140,12 @@ public: String getModelName() const CV_OVERRIDE { return NAME_BRUTE_FORCE; } int getType() const CV_OVERRIDE { return ml::KNearest::BRUTE_FORCE; } - void findNearestCore( const Mat& _samples, int k0, const Range& range, + void findNearestCore( const Mat& _samples, int k, const Range& range, Mat* results, Mat* neighbor_responses, Mat* dists, float* presult ) const { int testidx, baseidx, i, j, d = samples.cols, nsamples = samples.rows; int testcount = range.end - range.start; - int k = std::min(k0, nsamples); AutoBuffer buf(testcount*k*2); float* dbuf = buf.data(); @@ -215,7 +214,7 @@ public: float* nr = neighbor_responses->ptr(testidx + range.start); for( j = 0; j < k; j++ ) nr[j] = rbuf[testidx*k + j]; - for( ; j < k0; j++ ) + for( ; j < k; j++ ) nr[j] = 0.f; } @@ -224,7 +223,7 @@ public: float* dptr = dists->ptr(testidx + range.start); for( j = 0; j < k; j++ ) dptr[j] = dbuf[testidx*k + j]; - for( ; j < k0; j++ ) + for( ; j < k; j++ ) dptr[j] = 0.f; } @@ -307,6 +306,7 @@ public: { float result = 0.f; CV_Assert( 0 < k ); + k = std::min(k, samples.rows); Mat test_samples = _samples.getMat(); CV_Assert( test_samples.type() == CV_32F && test_samples.cols == samples.cols ); @@ -363,6 +363,7 @@ public: { float result = 0.f; CV_Assert( 0 < k ); + k = std::min(k, samples.rows); Mat test_samples = _samples.getMat(); CV_Assert( test_samples.type() == CV_32F && test_samples.cols == samples.cols ); diff --git a/modules/ml/test/test_emknearestkmeans.cpp b/modules/ml/test/test_emknearestkmeans.cpp index 6755c2e9e4..691815c52a 100644 --- a/modules/ml/test/test_emknearestkmeans.cpp +++ b/modules/ml/test/test_emknearestkmeans.cpp @@ -702,4 +702,26 @@ TEST(ML_EM, accuracy) { CV_EMTest test; test.safe_run(); } TEST(ML_EM, save_load) { CV_EMTest_SaveLoad test; test.safe_run(); } TEST(ML_EM, classification) { CV_EMTest_Classification test; test.safe_run(); } +TEST(ML_KNearest, regression_12347) +{ + Mat xTrainData = (Mat_(5,2) << 1, 1.1, 1.1, 1, 2, 2, 2.1, 2, 2.1, 2.1); + Mat yTrainLabels = (Mat_(5,1) << 1, 1, 2, 2, 2); + Ptr knn = KNearest::create(); + knn->train(xTrainData, ml::ROW_SAMPLE, yTrainLabels); + + Mat xTestData = (Mat_(2,2) << 1.1, 1.1, 2, 2.2); + Mat zBestLabels, neighbours, dist; + // check output shapes: + int K = 16, Kexp = std::min(K, xTrainData.rows); + knn->findNearest(xTestData, K, zBestLabels, neighbours, dist); + EXPECT_EQ(xTestData.rows, zBestLabels.rows); + EXPECT_EQ(neighbours.cols, Kexp); + EXPECT_EQ(dist.cols, Kexp); + // see if the result is still correct: + K = 2; + knn->findNearest(xTestData, K, zBestLabels, neighbours, dist); + EXPECT_EQ(1, zBestLabels.at(0,0)); + EXPECT_EQ(2, zBestLabels.at(1,0)); +} + }} // namespace From e345cb03d5c93f85673d1df832c69f241de94645 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Fri, 31 Aug 2018 16:54:05 +0300 Subject: [PATCH 20/47] Bit-exact resize reworked to use wide intrinsics (#12038) * Bit-exact resize reworked to use wide intrinsics * Reworked bit-exact resize row data loading * Added bit-exact resize row data loaders for SIMD256 and SIMD512 * Fixed type punned pointer dereferencing warning * Reworked loading of source data for SIMD256 and SIMD512 bit-exact resize --- modules/imgproc/src/resize.cpp | 399 ++++++++++++++++++++++++--------- 1 file changed, 289 insertions(+), 110 deletions(-) diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index 016fff03d9..2ff84753f7 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -340,51 +340,187 @@ static void hlineResizeCn(ET* src, int cn, int *ofst, FT* m, FT* dst, int dst_mi hline::ResizeCn(src, cn, ofst, m, dst, dst_min, dst_max, dst_width); }; +#if CV_SIMD512 +inline void v_load_indexed1(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + v_expand(v_reinterpret_as_u8(v_uint16( + *((uint16_t*)(src + ofst[ 0])), *((uint16_t*)(src + ofst[ 1])), *((uint16_t*)(src + ofst[ 2])), *((uint16_t*)(src + ofst[ 3])), + *((uint16_t*)(src + ofst[ 4])), *((uint16_t*)(src + ofst[ 5])), *((uint16_t*)(src + ofst[ 6])), *((uint16_t*)(src + ofst[ 7])), + *((uint16_t*)(src + ofst[ 8])), *((uint16_t*)(src + ofst[ 9])), *((uint16_t*)(src + ofst[10])), *((uint16_t*)(src + ofst[11])), + *((uint16_t*)(src + ofst[12])), *((uint16_t*)(src + ofst[13])), *((uint16_t*)(src + ofst[14])), *((uint16_t*)(src + ofst[15])), + *((uint16_t*)(src + ofst[16])), *((uint16_t*)(src + ofst[17])), *((uint16_t*)(src + ofst[14])), *((uint16_t*)(src + ofst[15])), + *((uint16_t*)(src + ofst[20])), *((uint16_t*)(src + ofst[21])), *((uint16_t*)(src + ofst[14])), *((uint16_t*)(src + ofst[15])), + *((uint16_t*)(src + ofst[24])), *((uint16_t*)(src + ofst[25])), *((uint16_t*)(src + ofst[14])), *((uint16_t*)(src + ofst[15])), + *((uint16_t*)(src + ofst[28])), *((uint16_t*)(src + ofst[29])), *((uint16_t*)(src + ofst[14])), *((uint16_t*)(src + ofst[15])))), + v_src0, v_src1); +} +inline void v_load_indexed2(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + v_expand(v_reinterpret_as_u8(v_uint32( + *((uint32_t*)(src + 2 * ofst[ 0])), *((uint32_t*)(src + 2 * ofst[ 1])), *((uint32_t*)(src + 2 * ofst[ 2])), *((uint32_t*)(src + 2 * ofst[ 3])), + *((uint32_t*)(src + 2 * ofst[ 4])), *((uint32_t*)(src + 2 * ofst[ 5])), *((uint32_t*)(src + 2 * ofst[ 6])), *((uint32_t*)(src + 2 * ofst[ 7])), + *((uint32_t*)(src + 2 * ofst[ 8])), *((uint32_t*)(src + 2 * ofst[ 9])), *((uint32_t*)(src + 2 * ofst[10])), *((uint32_t*)(src + 2 * ofst[11])), + *((uint32_t*)(src + 2 * ofst[12])), *((uint32_t*)(src + 2 * ofst[13])), *((uint32_t*)(src + 2 * ofst[14])), *((uint32_t*)(src + 2 * ofst[15])))), + v_src0, v_src1); + v_uint32 v_tmp0, v_tmp1, v_tmp2, v_tmp3; + v_zip(v_reinterpret_as_u32(v_src0), v_reinterpret_as_u32(v_src1), v_tmp2, v_tmp3); + v_zip(v_tmp2, v_tmp3, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_tmp2, v_tmp3); + v_zip(v_tmp2, v_tmp3, v_tmp0, v_tmp1); + v_zip(v_reinterpret_as_u16(v_tmp0), v_reinterpret_as_u16(v_tmp1), v_src0, v_src1); +} +inline void v_load_indexed4(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + v_expand(v_reinterpret_as_u8(v_uint64( + *((uint64_t*)(src + 4 * ofst[0])), *((uint64_t*)(src + 4 * ofst[1])), *((uint64_t*)(src + 4 * ofst[2])), *((uint64_t*)(src + 4 * ofst[3])), + *((uint64_t*)(src + 4 * ofst[4])), *((uint64_t*)(src + 4 * ofst[5])), *((uint64_t*)(src + 4 * ofst[6])), *((uint64_t*)(src + 4 * ofst[7])))), + v_src0, v_src1); + v_uint64 v_tmp0, v_tmp1, v_tmp2, v_tmp3; + v_zip(v_reinterpret_as_u64(v_src0), v_reinterpret_as_u64(v_src1), v_tmp2, v_tmp3); + v_zip(v_tmp2, v_tmp3, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_tmp2, v_tmp3); + v_zip(v_reinterpret_as_u16(v_tmp2), v_reinterpret_as_u16(v_tmp3), v_src0, v_src1); +} +inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) +{ + v_expand(v_reinterpret_as_u16(v_uint32( + *((uint32_t*)(src + ofst[ 0])), *((uint32_t*)(src + ofst[ 1])), *((uint32_t*)(src + ofst[ 2])), *((uint32_t*)(src + ofst[ 3])), + *((uint32_t*)(src + ofst[ 4])), *((uint32_t*)(src + ofst[ 5])), *((uint32_t*)(src + ofst[ 6])), *((uint32_t*)(src + ofst[ 7])), + *((uint32_t*)(src + ofst[ 8])), *((uint32_t*)(src + ofst[ 9])), *((uint32_t*)(src + ofst[10])), *((uint32_t*)(src + ofst[11])), + *((uint32_t*)(src + ofst[12])), *((uint32_t*)(src + ofst[13])), *((uint32_t*)(src + ofst[14])), *((uint32_t*)(src + ofst[15])))), + v_src0, v_src1); +} +#elif CV_SIMD256 +inline void v_load_indexed1(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + v_expand(v_reinterpret_as_u8(v_uint16( + *((uint16_t*)(src + ofst[ 0])), *((uint16_t*)(src + ofst[ 1])), *((uint16_t*)(src + ofst[ 2])), *((uint16_t*)(src + ofst[ 3])), + *((uint16_t*)(src + ofst[ 4])), *((uint16_t*)(src + ofst[ 5])), *((uint16_t*)(src + ofst[ 6])), *((uint16_t*)(src + ofst[ 7])), + *((uint16_t*)(src + ofst[ 8])), *((uint16_t*)(src + ofst[ 9])), *((uint16_t*)(src + ofst[10])), *((uint16_t*)(src + ofst[11])), + *((uint16_t*)(src + ofst[12])), *((uint16_t*)(src + ofst[13])), *((uint16_t*)(src + ofst[14])), *((uint16_t*)(src + ofst[15])))), + v_src0, v_src1); +} +inline void v_load_indexed2(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + v_expand(v_reinterpret_as_u8(v_uint32( + *((uint32_t*)(src + 2 * ofst[0])), *((uint32_t*)(src + 2 * ofst[1])), *((uint32_t*)(src + 2 * ofst[2])), *((uint32_t*)(src + 2 * ofst[3])), + *((uint32_t*)(src + 2 * ofst[4])), *((uint32_t*)(src + 2 * ofst[5])), *((uint32_t*)(src + 2 * ofst[6])), *((uint32_t*)(src + 2 * ofst[7])))), + v_src0, v_src1); + v_uint32 v_tmp0, v_tmp1, v_tmp2, v_tmp3; + v_zip(v_reinterpret_as_u32(v_src0), v_reinterpret_as_u32(v_src1), v_tmp2, v_tmp3); + v_zip(v_tmp2, v_tmp3, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_tmp2, v_tmp3); + v_zip(v_reinterpret_as_u16(v_tmp2), v_reinterpret_as_u16(v_tmp3), v_src0, v_src1); +} +inline void v_load_indexed4(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + v_expand(v_reinterpret_as_u8(v_uint64( + *((uint64_t*)(src + 4 * ofst[0])), *((uint64_t*)(src + 4 * ofst[1])), *((uint64_t*)(src + 4 * ofst[2])), *((uint64_t*)(src + 4 * ofst[3])))), + v_src0, v_src1); + v_uint64 v_tmp0, v_tmp1, v_tmp2, v_tmp3; + v_zip(v_reinterpret_as_u64(v_src0), v_reinterpret_as_u64(v_src1), v_tmp2, v_tmp3); + v_zip(v_tmp2, v_tmp3, v_tmp0, v_tmp1); + v_zip(v_reinterpret_as_u16(v_tmp0), v_reinterpret_as_u16(v_tmp1), v_src0, v_src1); +} +inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) +{ + v_expand(v_reinterpret_as_u16(v_uint32( + *((uint32_t*)(src + ofst[0])), *((uint32_t*)(src + ofst[1])), *((uint32_t*)(src + ofst[2])), *((uint32_t*)(src + ofst[3])), + *((uint32_t*)(src + ofst[4])), *((uint32_t*)(src + ofst[5])), *((uint32_t*)(src + ofst[6])), *((uint32_t*)(src + ofst[7])))), + v_src0, v_src1); +} +#elif CV_SIMD128 +inline void v_load_indexed1(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + uint16_t buf[8]; + buf[0] = *((uint16_t*)(src + ofst[0])); + buf[1] = *((uint16_t*)(src + ofst[1])); + buf[2] = *((uint16_t*)(src + ofst[2])); + buf[3] = *((uint16_t*)(src + ofst[3])); + buf[4] = *((uint16_t*)(src + ofst[4])); + buf[5] = *((uint16_t*)(src + ofst[5])); + buf[6] = *((uint16_t*)(src + ofst[6])); + buf[7] = *((uint16_t*)(src + ofst[7])); + v_src0 = vx_load_expand((uint8_t*)buf); + v_src1 = vx_load_expand((uint8_t*)buf + 8); +} +inline void v_load_indexed2(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + uint32_t buf[4]; + buf[0] = *((uint32_t*)(src + 2 * ofst[0])); + buf[1] = *((uint32_t*)(src + 2 * ofst[1])); + buf[2] = *((uint32_t*)(src + 2 * ofst[2])); + buf[3] = *((uint32_t*)(src + 2 * ofst[3])); + v_uint32 v_tmp0, v_tmp1, v_tmp2, v_tmp3; + v_tmp0 = v_reinterpret_as_u32(vx_load_expand((uint8_t*)buf)); + v_tmp1 = v_reinterpret_as_u32(vx_load_expand((uint8_t*)buf + 8)); + v_zip(v_tmp0, v_tmp1, v_tmp2, v_tmp3); + v_zip(v_tmp2, v_tmp3, v_tmp0, v_tmp1); + v_zip(v_reinterpret_as_u16(v_tmp0), v_reinterpret_as_u16(v_tmp1), v_src0, v_src1); +} +inline void v_load_indexed4(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) +{ + v_uint16 v_tmp0, v_tmp1; + v_src0 = vx_load_expand(src + 4 * ofst[0]); + v_src1 = vx_load_expand(src + 4 * ofst[1]); + v_recombine(v_src0, v_src1, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_src0, v_src1); +} +inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) +{ + uint32_t buf[4]; + buf[0] = *((uint32_t*)(src + ofst[0])); + buf[1] = *((uint32_t*)(src + ofst[1])); + buf[2] = *((uint32_t*)(src + ofst[2])); + buf[3] = *((uint32_t*)(src + ofst[3])); + v_src0 = vx_load_expand((uint16_t*)buf); + v_src1 = vx_load_expand((uint16_t*)buf + 4); +} +#endif template <> void hlineResizeCn(uint8_t* src, int, int *ofst, ufixedpoint16* m, ufixedpoint16* dst, int dst_min, int dst_max, int dst_width) { int i = 0; ufixedpoint16 src_0(src[0]); - v_uint16x8 v_src_0 = v_setall_u16(*((uint16_t*)&src_0)); - for (; i < dst_min - 7; i += 8, m += 16, dst += 8) // Points that fall left from src image so became equal to leftmost src point +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + v_uint16 v_src_0 = vx_setall_u16(*((uint16_t*)&src_0)); + for (; i <= dst_min - VECSZ; i += VECSZ, m += 2*VECSZ, dst += VECSZ) // Points that fall left from src image so became equal to leftmost src point { v_store((uint16_t*)dst, v_src_0); } +#endif for (; i < dst_min; i++, m += 2) { *(dst++) = src_0; } - for (; i < dst_max - 7 && ofst[i + 7] + 15 <= ofst[dst_width - 1]; i += 8, m += 16, dst += 8) +#if CV_SIMD + for (; i <= dst_max - VECSZ; i += VECSZ, m += 2*VECSZ, dst += VECSZ) { - v_uint32x4 v_src01 = v_combine_low(v_reinterpret_as_u32(v_load_expand(src + ofst[i ])), v_reinterpret_as_u32(v_load_expand(src + ofst[i + 1]))); - v_uint32x4 v_src23 = v_combine_low(v_reinterpret_as_u32(v_load_expand(src + ofst[i + 2])), v_reinterpret_as_u32(v_load_expand(src + ofst[i + 3]))); - v_uint32x4 v_src45 = v_combine_low(v_reinterpret_as_u32(v_load_expand(src + ofst[i + 4])), v_reinterpret_as_u32(v_load_expand(src + ofst[i + 5]))); - v_uint32x4 v_src67 = v_combine_low(v_reinterpret_as_u32(v_load_expand(src + ofst[i + 6])), v_reinterpret_as_u32(v_load_expand(src + ofst[i + 7]))); + v_uint16 v_src0, v_src1; + v_load_indexed1(src, ofst + i, v_src0, v_src1); - v_uint32x4 v_zip02, v_zip13, v_zip46, v_zip57; - v_zip(v_src01, v_src23, v_zip02, v_zip13); - v_zip(v_src45, v_src67, v_zip46, v_zip57); - - v_uint32x4 v_src0, v_src1; - v_zip(v_combine_low(v_zip02, v_zip46), v_combine_low(v_zip13, v_zip57), v_src0, v_src1); - - v_int16x8 v_mul0 = v_load((int16_t*)m); - v_int16x8 v_mul1 = v_load((int16_t*)m + 8); - v_uint32x4 v_res0 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src0), v_mul0)); - v_uint32x4 v_res1 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src1), v_mul1)); + v_int16 v_mul0 = vx_load((int16_t*)m); + v_int16 v_mul1 = vx_load((int16_t*)m + VECSZ); + v_uint32 v_res0 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src0), v_mul0)); + v_uint32 v_res1 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src1), v_mul1)); v_store((uint16_t*)dst, v_pack(v_res0, v_res1)); } +#endif for (; i < dst_max; i += 1, m += 2) { uint8_t* px = src + ofst[i]; *(dst++) = m[0] * px[0] + m[1] * px[1]; } src_0 = (src + ofst[dst_width - 1])[0]; - v_src_0 = v_setall_u16(*((uint16_t*)&src_0)); - for (; i < dst_width - 7; i += 8, dst += 8) // Points that fall left from src image so became equal to leftmost src point +#if CV_SIMD + v_src_0 = vx_setall_u16(*((uint16_t*)&src_0)); + for (; i <= dst_width - VECSZ; i += VECSZ, dst += VECSZ) // Points that fall left from src image so became equal to leftmost src point { v_store((uint16_t*)dst, v_src_0); } + vx_cleanup(); +#endif for (; i < dst_width; i++) { *(dst++) = src_0; @@ -394,87 +530,109 @@ template <> void hlineResizeCn(uint8_t* src, int, int *ofst, ufixedpoint16* m, ufixedpoint16* dst, int dst_min, int dst_max, int dst_width) { int i = 0; - ufixedpoint16 srccn[8] = { src[0], src[1], src[0], src[1], src[0], src[1], src[0], src[1] }; - v_uint16x8 v_srccn = v_load((uint16_t*)srccn); - for (; i < dst_min - 3; i += 4, m += 8, dst += 8) // Points that fall left from src image so became equal to leftmost src point + union { + uint32_t d; + uint16_t w[2]; + } srccn; + ((ufixedpoint16*)(srccn.w))[0] = src[0]; + ((ufixedpoint16*)(srccn.w))[1] = src[1]; +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + v_uint16 v_srccn = v_reinterpret_as_u16(vx_setall_u32(srccn.d)); + for (; i <= dst_min - VECSZ/2; i += VECSZ/2, m += VECSZ, dst += VECSZ) // Points that fall left from src image so became equal to leftmost src point { v_store((uint16_t*)dst, v_srccn); } +#endif for (; i < dst_min; i++, m += 2) { - *(dst++) = srccn[0]; - *(dst++) = srccn[1]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[0]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[1]; } - for (; i < dst_max - 3 && ofst[i + 3] + 7 <= ofst[dst_width - 1]; i += 4, m += 8, dst += 8) +#if CV_SIMD + for (; i <= dst_max - VECSZ/2; i += VECSZ/2, m += VECSZ, dst += VECSZ) { - v_uint32x4 v_src0 = v_combine_low(v_reinterpret_as_u32(v_load_expand(src + 2 * ofst[i ])), v_reinterpret_as_u32(v_load_expand(src + 2 * ofst[i + 1]))); - v_uint32x4 v_src1 = v_combine_low(v_reinterpret_as_u32(v_load_expand(src + 2 * ofst[i + 2])), v_reinterpret_as_u32(v_load_expand(src + 2 * ofst[i + 3]))); - - v_uint32x4 v_zip0, v_zip1; - v_zip(v_src0, v_src1, v_zip0, v_zip1); - v_zip(v_zip0, v_zip1, v_src0, v_src1); - - v_int16x8 v_src0123, v_src4567; - v_zip(v_reinterpret_as_s16(v_src0), v_reinterpret_as_s16(v_src1), v_src0123, v_src4567); + v_uint16 v_src0, v_src1; + v_load_indexed2(src, ofst + i, v_src0, v_src1); - v_uint32x4 v_mul = v_load((uint32_t*)m);//AaBbCcDd + v_uint32 v_mul = vx_load((uint32_t*)m);//AaBbCcDd + v_uint32 v_zip0, v_zip1; v_zip(v_mul, v_mul, v_zip0, v_zip1);//AaAaBbBb CcCcDdDd - v_uint32x4 v_res0 = v_reinterpret_as_u32(v_dotprod(v_src0123, v_reinterpret_as_s16(v_zip0))); - v_uint32x4 v_res1 = v_reinterpret_as_u32(v_dotprod(v_src4567, v_reinterpret_as_s16(v_zip1))); + v_uint32 v_res0 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src0), v_reinterpret_as_s16(v_zip0))); + v_uint32 v_res1 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src1), v_reinterpret_as_s16(v_zip1))); v_store((uint16_t*)dst, v_pack(v_res0, v_res1));//AB1AB2CD1CD2 } +#endif for (; i < dst_max; i += 1, m += 2) { uint8_t* px = src + 2 * ofst[i]; *(dst++) = m[0] * px[0] + m[1] * px[2]; *(dst++) = m[0] * px[1] + m[1] * px[3]; } - srccn[0] = (src + 2 * ofst[dst_width - 1])[0]; srccn[1] = (src + 2 * ofst[dst_width - 1])[1]; srccn[2] = (src + 2 * ofst[dst_width - 1])[0]; srccn[3] = (src + 2 * ofst[dst_width - 1])[1]; - srccn[4] = (src + 2 * ofst[dst_width - 1])[0]; srccn[5] = (src + 2 * ofst[dst_width - 1])[1]; srccn[6] = (src + 2 * ofst[dst_width - 1])[0]; srccn[7] = (src + 2 * ofst[dst_width - 1])[1]; - v_srccn = v_load((uint16_t*)srccn); - for (; i < dst_width - 3; i += 4, dst += 8) // Points that fall left from src image so became equal to leftmost src point + ((ufixedpoint16*)(srccn.w))[0] = (src + 2 * ofst[dst_width - 1])[0]; ((ufixedpoint16*)(srccn.w))[1] = (src + 2 * ofst[dst_width - 1])[1]; +#if CV_SIMD + v_srccn = v_reinterpret_as_u16(vx_setall_u32(srccn.d)); + for (; i <= dst_width - VECSZ/2; i += VECSZ/2, dst += VECSZ) // Points that fall left from src image so became equal to leftmost src point { v_store((uint16_t*)dst, v_srccn); } + vx_cleanup(); +#endif for (; i < dst_width; i++) { - *(dst++) = srccn[0]; - *(dst++) = srccn[1]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[0]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[1]; } } template <> void hlineResizeCn(uint8_t* src, int, int *ofst, ufixedpoint16* m, ufixedpoint16* dst, int dst_min, int dst_max, int dst_width) { int i = 0; - ufixedpoint16 srccn[8] = { src[0], src[1], src[2], src[3], src[0], src[1], src[2], src[3] }; - v_uint16x8 v_srccn = v_load((uint16_t*)srccn); - for (; i < dst_min - 1; i += 2, m += 4, dst += 8) // Points that fall left from src image so became equal to leftmost src point + union { + uint64_t q; + uint16_t w[4]; + } srccn; + ((ufixedpoint16*)(srccn.w))[0] = src[0]; + ((ufixedpoint16*)(srccn.w))[1] = src[1]; + ((ufixedpoint16*)(srccn.w))[2] = src[2]; + ((ufixedpoint16*)(srccn.w))[3] = src[3]; +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + v_uint16 v_srccn = v_reinterpret_as_u16(vx_setall_u64(srccn.q)); + for (; i <= dst_min - VECSZ/4; i += VECSZ/4, m += VECSZ/2, dst += VECSZ) // Points that fall left from src image so became equal to leftmost src point { v_store((uint16_t*)dst, v_srccn); } +#endif if (i < dst_min) // Points that fall left from src image so became equal to leftmost src point { - *(dst++) = srccn[0]; - *(dst++) = srccn[1]; - *(dst++) = srccn[2]; - *(dst++) = srccn[3]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[0]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[1]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[2]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[3]; i++; m += 2; } - for (; i < dst_max - 1 && ofst[i + 1] + 3 <= ofst[dst_width - 1]; i += 2, m += 4, dst += 8) - { - v_int16x8 v_src01 = v_reinterpret_as_s16(v_load_expand(src + 4 * ofst[i ])); - v_int16x8 v_src23 = v_reinterpret_as_s16(v_load_expand(src + 4 * ofst[i + 1])); - - v_int16x8 v_tmp0, v_tmp1; - v_recombine(v_src01, v_src23, v_tmp0, v_tmp1); - v_zip(v_tmp0, v_tmp1, v_src01, v_src23); - - v_int16x8 v_mul01 = v_reinterpret_as_s16(v_setall_u32(((uint32_t*)m)[0]));//AaAaAaAa - v_int16x8 v_mul23 = v_reinterpret_as_s16(v_setall_u32(((uint32_t*)m)[1]));//BbBbBbBb - v_uint32x4 v_res0 = v_reinterpret_as_u32(v_dotprod(v_src01, v_mul01)); - v_uint32x4 v_res1 = v_reinterpret_as_u32(v_dotprod(v_src23, v_mul23)); - v_store((uint16_t*)dst, v_pack(v_res0, v_res1));//AB1AB2CD1CD2 +#if CV_SIMD + for (; i <= dst_max - VECSZ/2; i += VECSZ/2, m += VECSZ, dst += 2*VECSZ) + { + v_uint16 v_src0, v_src1, v_src2, v_src3; + v_load_indexed4(src, ofst + i, v_src0, v_src1); + v_load_indexed4(src, ofst + i + 2, v_src2, v_src3); + + v_uint32 v_mul0, v_mul1, v_mul2, v_mul3, v_tmp; + v_mul0 = vx_load((uint32_t*)m);//AaBbCcDd + v_zip(v_mul0, v_mul0, v_mul3, v_tmp );//AaAaBbBb CcCcDdDd + v_zip(v_mul3, v_mul3, v_mul0, v_mul1);//AaAaAaAa BbBbBbBb + v_zip(v_tmp , v_tmp , v_mul2, v_mul3);//CcCcCcCc DdDdDdDd + + v_uint32 v_res0 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src0), v_reinterpret_as_s16(v_mul0))); + v_uint32 v_res1 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src1), v_reinterpret_as_s16(v_mul1))); + v_uint32 v_res2 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src2), v_reinterpret_as_s16(v_mul2))); + v_uint32 v_res3 = v_reinterpret_as_u32(v_dotprod(v_reinterpret_as_s16(v_src3), v_reinterpret_as_s16(v_mul3))); + v_store((uint16_t*)dst , v_pack(v_res0, v_res1)); + v_store((uint16_t*)dst + VECSZ, v_pack(v_res2, v_res3)); } +#endif for (; i < dst_max; i += 1, m += 2) { uint8_t* px = src + 4 * ofst[i]; @@ -483,19 +641,22 @@ void hlineResizeCn(uint8_t* src, int, int *o *(dst++) = m[0] * px[2] + m[1] * px[6]; *(dst++) = m[0] * px[3] + m[1] * px[7]; } - srccn[0] = (src + 4 * ofst[dst_width - 1])[0]; srccn[1] = (src + 4 * ofst[dst_width - 1])[1]; srccn[2] = (src + 4 * ofst[dst_width - 1])[2]; srccn[3] = (src + 4 * ofst[dst_width - 1])[3]; - srccn[4] = (src + 4 * ofst[dst_width - 1])[0]; srccn[5] = (src + 4 * ofst[dst_width - 1])[1]; srccn[6] = (src + 4 * ofst[dst_width - 1])[2]; srccn[7] = (src + 4 * ofst[dst_width - 1])[3]; - v_srccn = v_load((uint16_t*)srccn); - for (; i < dst_width - 1; i += 2, dst += 8) // Points that fall right from src image so became equal to rightmost src point + ((ufixedpoint16*)(srccn.w))[0] = (src + 4 * ofst[dst_width - 1])[0]; ((ufixedpoint16*)(srccn.w))[1] = (src + 4 * ofst[dst_width - 1])[1]; + ((ufixedpoint16*)(srccn.w))[2] = (src + 4 * ofst[dst_width - 1])[2]; ((ufixedpoint16*)(srccn.w))[3] = (src + 4 * ofst[dst_width - 1])[3]; +#if CV_SIMD + v_srccn = v_reinterpret_as_u16(vx_setall_u64(srccn.q)); + for (; i <= dst_width - VECSZ/4; i += VECSZ/4, dst += VECSZ) // Points that fall right from src image so became equal to rightmost src point { v_store((uint16_t*)dst, v_srccn); } + vx_cleanup(); +#endif if (i < dst_width) { - *(dst++) = srccn[0]; - *(dst++) = srccn[1]; - *(dst++) = srccn[2]; - *(dst++) = srccn[3]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[0]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[1]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[2]; + *(dst++) = ((ufixedpoint16*)(srccn.w))[3]; } } template <> @@ -503,40 +664,50 @@ void hlineResizeCn(uint16_t* src, int, int { int i = 0; ufixedpoint32 src_0(src[0]); - v_uint32x4 v_src_0 = v_setall_u32(*((uint32_t*)&src_0)); - for (; i < dst_min - 3; i += 4, m += 8, dst += 4) // Points that fall left from src image so became equal to leftmost src point +#if CV_SIMD + const int VECSZ = v_uint32::nlanes; + v_uint32 v_src_0 = vx_setall_u32(*((uint32_t*)&src_0)); + for (; i <= dst_min - VECSZ; i += VECSZ, m += 2*VECSZ, dst += VECSZ) // Points that fall left from src image so became equal to leftmost src point { v_store((uint32_t*)dst, v_src_0); } +#endif for (; i < dst_min; i++, m += 2) { *(dst++) = src_0; } - for (; i < dst_max - 3 && ofst[i + 3] + 8 <= ofst[dst_width - 1]; i += 4, m += 8, dst += 4) +#if CV_SIMD + for (; i <= dst_max - VECSZ; i += VECSZ, m += 2*VECSZ, dst += VECSZ) { - v_uint32x4 v_src0 = v_combine_low(v_load_expand(src + ofst[i]), v_load_expand(src + ofst[i + 1])); - v_uint32x4 v_mul0 = v_load((uint32_t*)m); - v_uint32x4 v_src1 = v_combine_low(v_load_expand(src + ofst[i + 2]), v_load_expand(src + ofst[i + 3])); - v_uint32x4 v_mul1 = v_load((uint32_t*)m + 4); - v_uint32x4 v_res0 = v_src0 * v_mul0;//a1a2b1b2 - v_uint32x4 v_res1 = v_src1 * v_mul1;//c1c2d1d2 - v_uint32x4 v_tmp0, v_tmp1; + v_uint32 v_src0, v_src1; + v_load_indexed1(src, ofst + i, v_src0, v_src1); + + v_uint32 v_mul0 = vx_load((uint32_t*)m); + v_uint32 v_mul1 = vx_load((uint32_t*)m + 4); + + v_uint32 v_res0 = v_src0 * v_mul0;//a1a2b1b2 + v_uint32 v_res1 = v_src1 * v_mul1;//c1c2d1d2 + v_uint32 v_tmp0, v_tmp1; v_recombine(v_res0, v_res1, v_tmp0, v_tmp1);//a1a2c1c2 b1b2d1d2 v_zip(v_tmp0, v_tmp1, v_res0, v_res1);//a1b1a2b2 c1d1c2d2 v_recombine(v_res0, v_res1, v_tmp0, v_tmp1);//a1b1c1d1 a2b2c2d2 v_store((uint32_t*)dst, v_tmp0 + v_tmp1);//abcd } +#endif for (; i < dst_max; i += 1, m += 2) { uint16_t* px = src + ofst[i]; *(dst++) = m[0] * px[0] + m[1] * px[1]; } src_0 = (src + ofst[dst_width - 1])[0]; - v_src_0 = v_setall_u32(*((uint32_t*)&src_0)); +#if CV_SIMD + v_src_0 = vx_setall_u32(*((uint32_t*)&src_0)); for (; i < dst_width - 3; i += 4, dst += 4) { v_store((uint32_t*)dst, v_src_0); } + vx_cleanup(); +#endif for (; i < dst_width; i++) { *(dst++) = src_0; @@ -552,18 +723,22 @@ void vlineSet(FT* src, ET* dst, int dst_width) template <> void vlineSet(ufixedpoint16* src, uint8_t* dst, int dst_width) { - static const v_uint16x8 v_fixedRound = v_setall_u16((uint16_t)((1U << 8) >> 1)); int i = 0; - for (; i < dst_width - 15; i += 16, src += 16, dst += 16) +#if CV_SIMD + const int VECSZ = v_uint8::nlanes; + static const v_uint16 v_fixedRound = vx_setall_u16((uint16_t)((1U << 8) >> 1)); + for (; i <= dst_width - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) { - v_uint16x8 v_src0 = v_load((uint16_t*)src); - v_uint16x8 v_src1 = v_load((uint16_t*)src + 8); + v_uint16 v_src0 = vx_load((uint16_t*)src); + v_uint16 v_src1 = vx_load((uint16_t*)src + VECSZ/2); - v_uint16x8 v_res0 = (v_src0 + v_fixedRound) >> 8; - v_uint16x8 v_res1 = (v_src1 + v_fixedRound) >> 8; + v_uint16 v_res0 = (v_src0 + v_fixedRound) >> 8; + v_uint16 v_res1 = (v_src1 + v_fixedRound) >> 8; v_store(dst, v_pack(v_res0, v_res1)); } + vx_cleanup(); +#endif for (; i < dst_width; i++) *(dst++) = *(src++); } @@ -582,36 +757,40 @@ void vlineResize(FT* src, size_t src_step, FT* m, ET* dst, int dst_width) template <> void vlineResize(ufixedpoint16* src, size_t src_step, ufixedpoint16* m, uint8_t* dst, int dst_width) { - static const v_int32x4 v_fixedRound = v_setall_s32((int32_t)((1 << 16) >> 1)); - static const v_int16x8 v_128 = v_reinterpret_as_s16(v_setall_u16((uint16_t)1<<15)); - static const v_int8x16 v_128_16 = v_reinterpret_as_s8 (v_setall_u8 ((uint8_t) 1<<7)); - int i = 0; ufixedpoint16* src1 = src + src_step; - v_int16x8 v_mul = v_reinterpret_as_s16(v_setall_u32(((uint32_t*)m)[0])); - for (; i < dst_width - 15; i += 16, src += 16, src1 += 16, dst += 16) - { - v_int16x8 v_src00 = v_load((int16_t*)src); - v_int16x8 v_src10 = v_load((int16_t*)src1); - v_int16x8 v_tmp0, v_tmp1; +#if CV_SIMD + const int VECSZ = v_uint8::nlanes; + static const v_int32 v_fixedRound = vx_setall_s32((int32_t)((1 << 16) >> 1)); + static const v_int16 v_128 = v_reinterpret_as_s16(vx_setall_u16((uint16_t)1<<15)); + static const v_int8 v_128_16 = v_reinterpret_as_s8 (vx_setall_u8 ((uint8_t) 1<<7)); + + v_int16 v_mul = v_reinterpret_as_s16(vx_setall_u32(((uint32_t*)m)[0])); + for (; i <= dst_width - VECSZ; i += VECSZ, src += VECSZ, src1 += VECSZ, dst += VECSZ) + { + v_int16 v_src00 = vx_load((int16_t*)src); + v_int16 v_src10 = vx_load((int16_t*)src1); + v_int16 v_tmp0, v_tmp1; v_zip(v_add_wrap(v_src00,v_128), v_add_wrap(v_src10,v_128), v_tmp0, v_tmp1); - v_int32x4 v_res0 = v_dotprod(v_tmp0, v_mul); - v_int32x4 v_res1 = v_dotprod(v_tmp1, v_mul); + v_int32 v_res0 = v_dotprod(v_tmp0, v_mul); + v_int32 v_res1 = v_dotprod(v_tmp1, v_mul); - v_int16x8 v_src01 = v_load((int16_t*)src + 8); - v_int16x8 v_src11 = v_load((int16_t*)src1 + 8); + v_int16 v_src01 = vx_load((int16_t*)src + VECSZ/2); + v_int16 v_src11 = vx_load((int16_t*)src1 + VECSZ/2); v_zip(v_add_wrap(v_src01,v_128), v_add_wrap(v_src11,v_128), v_tmp0, v_tmp1); - v_int32x4 v_res2 = v_dotprod(v_tmp0, v_mul); - v_int32x4 v_res3 = v_dotprod(v_tmp1, v_mul); + v_int32 v_res2 = v_dotprod(v_tmp0, v_mul); + v_int32 v_res3 = v_dotprod(v_tmp1, v_mul); - v_int8x16 v_res = v_pack(v_pack((v_res0 + v_fixedRound) >> 16, - (v_res1 + v_fixedRound) >> 16), - v_pack((v_res2 + v_fixedRound) >> 16, - (v_res3 + v_fixedRound) >> 16)); + v_int8 v_res = v_pack(v_pack((v_res0 + v_fixedRound) >> 16, + (v_res1 + v_fixedRound) >> 16), + v_pack((v_res2 + v_fixedRound) >> 16, + (v_res3 + v_fixedRound) >> 16)); v_store(dst, v_reinterpret_as_u8(v_sub_wrap(v_res, v_128_16))); } + vx_cleanup(); +#endif for (; i < dst_width; i++) { *(dst++) = (uint8_t)(*(src++) * m[0] + *(src1++) * m[1]); From 0f2b535fccd1143d542fd02424904bc9ab85a958 Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Fri, 31 Aug 2018 17:05:00 +0300 Subject: [PATCH 21/47] Bit-exact GaussianBlur reworked to use wide intrinsics (#12073) * Bit-exact GaussianBlur reworked to use wide intrinsics * Added v_mul_hi universal intrinsic * Removed custom SSE2 branch from bit-exact GaussianBlur * Removed loop unrolling for gaussianBlur horizontal smoothing --- .../include/opencv2/core/hal/intrin_avx.hpp | 2 + .../include/opencv2/core/hal/intrin_cpp.hpp | 14 + .../include/opencv2/core/hal/intrin_neon.hpp | 15 + .../include/opencv2/core/hal/intrin_sse.hpp | 3 + .../include/opencv2/core/hal/intrin_vsx.hpp | 15 + modules/imgproc/src/smooth.cpp | 712 +++++++++--------- 6 files changed, 385 insertions(+), 376 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_avx.hpp b/modules/core/include/opencv2/core/hal/intrin_avx.hpp index 1c5ffbd1ca..36c7c0f1a1 100644 --- a/modules/core/include/opencv2/core/hal/intrin_avx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_avx.hpp @@ -664,6 +664,8 @@ inline void v_mul_expand(const v_uint32x8& a, const v_uint32x8& b, v_zip(v_uint64x4(v0), v_uint64x4(v1), c, d); } +inline v_int16x16 v_mul_hi(const v_int16x16& a, const v_int16x16& b) { return v_int16x16(_mm256_mulhi_epi16(a.val, b.val)); } +inline v_uint16x16 v_mul_hi(const v_uint16x16& a, const v_uint16x16& b) { return v_uint16x16(_mm256_mulhi_epu16(a.val, b.val)); } /** Non-saturating arithmetics **/ #define OPENCV_HAL_IMPL_AVX_BIN_FUNC(func, _Tpvec, intrin) \ diff --git a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp index 61d58dbb06..ccd317682d 100644 --- a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp @@ -891,6 +891,20 @@ template inline void v_mul_expand(const v_reg<_Tp, n>& a, c } } +/** @brief Multiply and extract high part + +Multiply values two registers and store high part of the results. +Implemented only for 16-bit source types (v_int16x8, v_uint16x8). Returns \f$ a*b >> 16 \f$ +*/ +template inline v_reg<_Tp, n> v_mul_hi(const v_reg<_Tp, n>& a, const v_reg<_Tp, n>& b) +{ + typedef typename V_TypeTraits<_Tp>::w_type w_type; + v_reg<_Tp, n> c; + for (int i = 0; i < n; i++) + c.s[i] = (_Tp)(((w_type)a.s[i] * b.s[i]) >> sizeof(_Tp)*8); + return c; +} + //! @cond IGNORED template inline void v_hsum(const v_reg<_Tp, n>& a, v_reg::w_type, n/2>& c) diff --git a/modules/core/include/opencv2/core/hal/intrin_neon.hpp b/modules/core/include/opencv2/core/hal/intrin_neon.hpp index 04b6ba2259..c017b075f1 100644 --- a/modules/core/include/opencv2/core/hal/intrin_neon.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_neon.hpp @@ -553,6 +553,21 @@ inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, d.val = vmull_u32(vget_high_u32(a.val), vget_high_u32(b.val)); } +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + return v_int16x8(vcombine_s16( + vshrn_n_s32(vmull_s16( vget_low_s16(a.val), vget_low_s16(b.val)), 16), + vshrn_n_s32(vmull_s16(vget_high_s16(a.val), vget_high_s16(b.val)), 16) + )); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + return v_uint16x8(vcombine_u16( + vshrn_n_u32(vmull_u16( vget_low_u16(a.val), vget_low_u16(b.val)), 16), + vshrn_n_u32(vmull_u16(vget_high_u16(a.val), vget_high_u16(b.val)), 16) + )); +} + inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) { int32x4_t c = vmull_s16(vget_low_s16(a.val), vget_low_s16(b.val)); diff --git a/modules/core/include/opencv2/core/hal/intrin_sse.hpp b/modules/core/include/opencv2/core/hal/intrin_sse.hpp index 42a39d07f9..159ef356b5 100644 --- a/modules/core/include/opencv2/core/hal/intrin_sse.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_sse.hpp @@ -737,6 +737,9 @@ inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, d.val = _mm_unpackhi_epi64(c0, c1); } +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) { return v_int16x8(_mm_mulhi_epi16(a.val, b.val)); } +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) { return v_uint16x8(_mm_mulhi_epu16(a.val, b.val)); } + inline v_int32x4 v_dotprod(const v_int16x8& a, const v_int16x8& b) { return v_int32x4(_mm_madd_epi16(a.val, b.val)); diff --git a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp index 52bc2cc0ba..a45e7a875f 100644 --- a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp @@ -457,6 +457,21 @@ inline void v_mul_expand(const v_uint32x4& a, const v_uint32x4& b, v_uint64x2& c d.val = vec_mul(vec_unpacklu(a.val), vec_unpacklu(b.val)); } +inline v_int16x8 v_mul_hi(const v_int16x8& a, const v_int16x8& b) +{ + return v_int16x8(vec_packs( + vec_sra(vec_mul(vec_unpackh(a.val), vec_unpackh(b.val)), vec_uint4_sp(16)), + vec_sra(vec_mul(vec_unpackl(a.val), vec_unpackl(b.val)), vec_uint4_sp(16)) + )); +} +inline v_uint16x8 v_mul_hi(const v_uint16x8& a, const v_uint16x8& b) +{ + return v_uint16x8(vec_packs( + vec_sr(vec_mul(vec_unpackhu(a.val), vec_unpackhu(b.val)), vec_uint4_sp(16)), + vec_sr(vec_mul(vec_unpacklu(a.val), vec_unpacklu(b.val)), vec_uint4_sp(16)) + )); +} + /** Non-saturating arithmetics **/ #define OPENCV_HAL_IMPL_VSX_BIN_FUNC(func, intrin) \ template \ diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index d54065f801..212698bce0 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -1820,22 +1820,13 @@ template <> void hlineSmooth1N(const uint8_t* src, int cn, const ufixedpoint16* m, int, ufixedpoint16* dst, int len, int) { int lencn = len*cn; - v_uint16x8 v_mul = v_setall_u16(*((uint16_t*)m)); int i = 0; - for (; i <= lencn - 16; i += 16) - { - v_uint8x16 v_src = v_load(src + i); - v_uint16x8 v_tmp0, v_tmp1; - v_expand(v_src, v_tmp0, v_tmp1); - v_store((uint16_t*)dst + i, v_mul*v_tmp0); - v_store((uint16_t*)dst + i + 8, v_mul*v_tmp1); - } - if (i <= lencn - 8) - { - v_uint16x8 v_src = v_load_expand(src + i); - v_store((uint16_t*)dst + i, v_mul*v_src); - i += 8; - } +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + v_uint16 v_mul = vx_setall_u16(*((uint16_t*)m)); + for (; i <= lencn - VECSZ; i += VECSZ) + v_store((uint16_t*)dst + i, v_mul*vx_load_expand(src + i)); +#endif for (; i < lencn; i++) dst[i] = m[0] * src[i]; } @@ -1850,20 +1841,11 @@ void hlineSmooth1N1(const uint8_t* src, int cn, const uf { int lencn = len*cn; int i = 0; - for (; i <= lencn - 16; i += 16) - { - v_uint8x16 v_src = v_load(src + i); - v_uint16x8 v_tmp0, v_tmp1; - v_expand(v_src, v_tmp0, v_tmp1); - v_store((uint16_t*)dst + i, v_shl<8>(v_tmp0)); - v_store((uint16_t*)dst + i + 8, v_shl<8>(v_tmp1)); - } - if (i <= lencn - 8) - { - v_uint16x8 v_src = v_load_expand(src + i); - v_store((uint16_t*)dst + i, v_shl<8>(v_src)); - i += 8; - } +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + for (; i <= lencn - VECSZ; i += VECSZ) + v_store((uint16_t*)dst + i, v_shl<8>(vx_load_expand(src + i))); +#endif for (; i < lencn; i++) dst[i] = src[i]; } @@ -1926,18 +1908,15 @@ void hlineSmooth3N(const uint8_t* src, int cn, const ufi src += cn; dst += cn; int i = cn, lencn = (len - 1)*cn; - v_uint16x8 v_mul0 = v_setall_u16(*((uint16_t*)m)); - v_uint16x8 v_mul1 = v_setall_u16(*((uint16_t*)(m + 1))); - v_uint16x8 v_mul2 = v_setall_u16(*((uint16_t*)(m + 2))); - for (; i <= lencn - 16; i += 16, src += 16, dst += 16) - { - v_uint16x8 v_src00, v_src01, v_src10, v_src11, v_src20, v_src21; - v_expand(v_load(src - cn), v_src00, v_src01); - v_expand(v_load(src), v_src10, v_src11); - v_expand(v_load(src + cn), v_src20, v_src21); - v_store((uint16_t*)dst, v_src00 * v_mul0 + v_src10 * v_mul1 + v_src20 * v_mul2); - v_store((uint16_t*)dst + 8, v_src01 * v_mul0 + v_src11 * v_mul1 + v_src21 * v_mul2); - } +#if CV_SIMD + const uint16_t* _m = (const uint16_t*)m; + const int VECSZ = v_uint16::nlanes; + v_uint16 v_mul0 = vx_setall_u16(_m[0]); + v_uint16 v_mul1 = vx_setall_u16(_m[1]); + v_uint16 v_mul2 = vx_setall_u16(_m[2]); + for (; i <= lencn - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) + v_store((uint16_t*)dst, vx_load_expand(src - cn) * v_mul0 + vx_load_expand(src) * v_mul1 + vx_load_expand(src + cn) * v_mul2); +#endif for (; i < lencn; i++, src++, dst++) *dst = m[0] * src[-cn] + m[1] * src[0] + m[2] * src[cn]; @@ -2017,15 +1996,11 @@ void hlineSmooth3N121(const uint8_t* src, int cn, const src += cn; dst += cn; int i = cn, lencn = (len - 1)*cn; - for (; i <= lencn - 16; i += 16, src += 16, dst += 16) - { - v_uint16x8 v_src00, v_src01, v_src10, v_src11, v_src20, v_src21; - v_expand(v_load(src - cn), v_src00, v_src01); - v_expand(v_load(src), v_src10, v_src11); - v_expand(v_load(src + cn), v_src20, v_src21); - v_store((uint16_t*)dst, (v_src00 + v_src20 + (v_src10 << 1)) << 6); - v_store((uint16_t*)dst + 8, (v_src01 + v_src21 + (v_src11 << 1)) << 6); - } +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + for (; i <= lencn - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) + v_store((uint16_t*)dst, (vx_load_expand(src - cn) + vx_load_expand(src + cn) + (vx_load_expand(src) << 1)) << 6); +#endif for (; i < lencn; i++, src++, dst++) *((uint16_t*)dst) = (uint16_t(src[-cn]) + uint16_t(src[cn]) + (uint16_t(src[0]) << 1)) << 6; @@ -2108,17 +2083,14 @@ void hlineSmooth3Naba(const uint8_t* src, int cn, const src += cn; dst += cn; int i = cn, lencn = (len - 1)*cn; - v_uint16x8 v_mul0 = v_setall_u16(*((uint16_t*)m)); - v_uint16x8 v_mul1 = v_setall_u16(*((uint16_t*)m+1)); - for (; i <= lencn - 16; i += 16, src += 16, dst += 16) - { - v_uint16x8 v_src00, v_src01, v_src10, v_src11, v_src20, v_src21; - v_expand(v_load(src - cn), v_src00, v_src01); - v_expand(v_load(src), v_src10, v_src11); - v_expand(v_load(src + cn), v_src20, v_src21); - v_store((uint16_t*)dst, (v_src00 + v_src20) * v_mul0 + v_src10 * v_mul1); - v_store((uint16_t*)dst + 8, (v_src01 + v_src21) * v_mul0 + v_src11 * v_mul1); - } +#if CV_SIMD + const uint16_t* _m = (const uint16_t*)m; + const int VECSZ = v_uint16::nlanes; + v_uint16 v_mul0 = vx_setall_u16(_m[0]); + v_uint16 v_mul1 = vx_setall_u16(_m[1]); + for (; i <= lencn - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) + v_store((uint16_t*)dst, (vx_load_expand(src - cn) + vx_load_expand(src + cn)) * v_mul0 + vx_load_expand(src) * v_mul1); +#endif for (; i < lencn; i++, src++, dst++) *((uint16_t*)dst) = ((uint16_t*)m)[1] * src[0] + ((uint16_t*)m)[0] * ((uint16_t)(src[-cn]) + (uint16_t)(src[cn])); @@ -2304,22 +2276,17 @@ void hlineSmooth5N(const uint8_t* src, int cn, const ufi src += 2 * cn; dst += 2 * cn; int i = 2*cn, lencn = (len - 2)*cn; - v_uint16x8 v_mul0 = v_setall_u16(*((uint16_t*)m)); - v_uint16x8 v_mul1 = v_setall_u16(*((uint16_t*)(m + 1))); - v_uint16x8 v_mul2 = v_setall_u16(*((uint16_t*)(m + 2))); - v_uint16x8 v_mul3 = v_setall_u16(*((uint16_t*)(m + 3))); - v_uint16x8 v_mul4 = v_setall_u16(*((uint16_t*)(m + 4))); - for (; i <= lencn - 16; i += 16, src += 16, dst += 16) - { - v_uint16x8 v_src00, v_src01, v_src10, v_src11, v_src20, v_src21, v_src30, v_src31, v_src40, v_src41; - v_expand(v_load(src - 2*cn), v_src00, v_src01); - v_expand(v_load(src - cn), v_src10, v_src11); - v_expand(v_load(src), v_src20, v_src21); - v_expand(v_load(src + cn), v_src30, v_src31); - v_expand(v_load(src + 2*cn), v_src40, v_src41); - v_store((uint16_t*)dst, v_src00 * v_mul0 + v_src10 * v_mul1 + v_src20 * v_mul2 + v_src30 * v_mul3 + v_src40 * v_mul4); - v_store((uint16_t*)dst + 8, v_src01 * v_mul0 + v_src11 * v_mul1 + v_src21 * v_mul2 + v_src31 * v_mul3 + v_src41 * v_mul4); - } +#if CV_SIMD + const uint16_t* _m = (const uint16_t*)m; + const int VECSZ = v_uint16::nlanes; + v_uint16 v_mul0 = vx_setall_u16(_m[0]); + v_uint16 v_mul1 = vx_setall_u16(_m[1]); + v_uint16 v_mul2 = vx_setall_u16(_m[2]); + v_uint16 v_mul3 = vx_setall_u16(_m[3]); + v_uint16 v_mul4 = vx_setall_u16(_m[4]); + for (; i <= lencn - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) + v_store((uint16_t*)dst, vx_load_expand(src - 2 * cn) * v_mul0 + vx_load_expand(src - cn) * v_mul1 + vx_load_expand(src) * v_mul2 + vx_load_expand(src + cn) * v_mul3 + vx_load_expand(src + 2 * cn) * v_mul4); +#endif for (; i < lencn; i++, src++, dst++) *dst = m[0] * src[-2*cn] + m[1] * src[-cn] + m[2] * src[0] + m[3] * src[cn] + m[4] * src[2*cn]; @@ -2517,18 +2484,12 @@ void hlineSmooth5N14641(const uint8_t* src, int cn, cons src += 2 * cn; dst += 2 * cn; int i = 2 * cn, lencn = (len - 2)*cn; - v_uint16x8 v_6 = v_setall_u16(6); - for (; i <= lencn - 16; i += 16, src += 16, dst += 16) - { - v_uint16x8 v_src00, v_src01, v_src10, v_src11, v_src20, v_src21, v_src30, v_src31, v_src40, v_src41; - v_expand(v_load(src - 2*cn), v_src00, v_src01); - v_expand(v_load(src - cn), v_src10, v_src11); - v_expand(v_load(src), v_src20, v_src21); - v_expand(v_load(src + cn), v_src30, v_src31); - v_expand(v_load(src + 2*cn), v_src40, v_src41); - v_store((uint16_t*)dst, (v_src20 * v_6 + ((v_src10 + v_src30) << 2) + v_src00 + v_src40) << 4); - v_store((uint16_t*)dst + 8, (v_src21 * v_6 + ((v_src11 + v_src31) << 2) + v_src01 + v_src41) << 4); - } +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + v_uint16 v_6 = vx_setall_u16(6); + for (; i <= lencn - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) + v_store((uint16_t*)dst, (vx_load_expand(src) * v_6 + ((vx_load_expand(src - cn) + vx_load_expand(src + cn)) << 2) + vx_load_expand(src - 2 * cn) + vx_load_expand(src + 2 * cn)) << 4); +#endif for (; i < lencn; i++, src++, dst++) *((uint16_t*)dst) = (uint16_t(src[0]) * 6 + ((uint16_t(src[-cn]) + uint16_t(src[cn])) << 2) + uint16_t(src[-2 * cn]) + uint16_t(src[2 * cn])) << 4; @@ -2721,20 +2682,15 @@ void hlineSmooth5Nabcba(const uint8_t* src, int cn, cons src += 2 * cn; dst += 2 * cn; int i = 2 * cn, lencn = (len - 2)*cn; - v_uint16x8 v_mul0 = v_setall_u16(*((uint16_t*)m)); - v_uint16x8 v_mul1 = v_setall_u16(*((uint16_t*)(m + 1))); - v_uint16x8 v_mul2 = v_setall_u16(*((uint16_t*)(m + 2))); - for (; i <= lencn - 16; i += 16, src += 16, dst += 16) - { - v_uint16x8 v_src00, v_src01, v_src10, v_src11, v_src20, v_src21, v_src30, v_src31, v_src40, v_src41; - v_expand(v_load(src - 2 * cn), v_src00, v_src01); - v_expand(v_load(src - cn), v_src10, v_src11); - v_expand(v_load(src), v_src20, v_src21); - v_expand(v_load(src + cn), v_src30, v_src31); - v_expand(v_load(src + 2 * cn), v_src40, v_src41); - v_store((uint16_t*)dst, (v_src00 + v_src40) * v_mul0 + (v_src10 + v_src30)* v_mul1 + v_src20 * v_mul2); - v_store((uint16_t*)dst + 8, (v_src01 + v_src41) * v_mul0 + (v_src11 + v_src31) * v_mul1 + v_src21 * v_mul2); - } +#if CV_SIMD + const uint16_t* _m = (const uint16_t*)m; + const int VECSZ = v_uint16::nlanes; + v_uint16 v_mul0 = vx_setall_u16(_m[0]); + v_uint16 v_mul1 = vx_setall_u16(_m[1]); + v_uint16 v_mul2 = vx_setall_u16(_m[2]); + for (; i <= lencn - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) + v_store((uint16_t*)dst, (vx_load_expand(src - 2 * cn) + vx_load_expand(src + 2 * cn)) * v_mul0 + (vx_load_expand(src - cn) + vx_load_expand(src + cn))* v_mul1 + vx_load_expand(src) * v_mul2); +#endif for (; i < lencn; i++, src++, dst++) *((uint16_t*)dst) = ((uint16_t*)m)[0] * ((uint16_t)(src[-2 * cn]) + (uint16_t)(src[2 * cn])) + ((uint16_t*)m)[1] * ((uint16_t)(src[-cn]) + (uint16_t)(src[cn])) + ((uint16_t*)m)[2] * src[0]; @@ -2844,23 +2800,16 @@ void hlineSmooth(const uint8_t* src, int cn, const ufixe } i *= cn; int lencn = (len - post_shift + 1)*cn; - for (; i <= lencn - 16; i+=16, src+=16, dst+=16) +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + for (; i <= lencn - VECSZ; i+=VECSZ, src+=VECSZ, dst+=VECSZ) { - v_uint16x8 v_src0, v_src1; - v_uint16x8 v_mul = v_setall_u16(*((uint16_t*)m)); - v_expand(v_load(src), v_src0, v_src1); - v_uint16x8 v_res0 = v_src0 * v_mul; - v_uint16x8 v_res1 = v_src1 * v_mul; + v_uint16 v_res0 = vx_load_expand(src) * vx_setall_u16(*((uint16_t*)m)); for (int j = 1; j < n; j++) - { - v_mul = v_setall_u16(*((uint16_t*)(m + j))); - v_expand(v_load(src + j * cn), v_src0, v_src1); - v_res0 += v_src0 * v_mul; - v_res1 += v_src1 * v_mul; - } + v_res0 += vx_load_expand(src + j * cn) * vx_setall_u16(*((uint16_t*)(m + j))); v_store((uint16_t*)dst, v_res0); - v_store((uint16_t*)dst+8, v_res1); } +#endif for (; i < lencn; i++, src++, dst++) { *dst = m[0] * src[0]; @@ -2970,26 +2919,16 @@ void hlineSmoothONa_yzy_a(const uint8_t* src, int cn, co } i *= cn; int lencn = (len - post_shift + 1)*cn; - for (; i <= lencn - 16; i += 16, src += 16, dst += 16) +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + for (; i <= lencn - VECSZ; i += VECSZ, src += VECSZ, dst += VECSZ) { - v_uint16x8 v_src00, v_src01, v_srcN00, v_srcN01; - - v_uint16x8 v_mul = v_setall_u16(*((uint16_t*)(m + pre_shift))); - v_expand(v_load(src + pre_shift * cn), v_src00, v_src01); - v_uint16x8 v_res0 = v_src00 * v_mul; - v_uint16x8 v_res1 = v_src01 * v_mul; + v_uint16 v_res0 = vx_load_expand(src + pre_shift * cn) * vx_setall_u16(*((uint16_t*)(m + pre_shift))); for (int j = 0; j < pre_shift; j ++) - { - v_mul = v_setall_u16(*((uint16_t*)(m + j))); - v_expand(v_load(src + j * cn), v_src00, v_src01); - v_expand(v_load(src + (n - 1 - j)*cn), v_srcN00, v_srcN01); - v_res0 += (v_src00 + v_srcN00) * v_mul; - v_res1 += (v_src01 + v_srcN01) * v_mul; - } - + v_res0 += (vx_load_expand(src + j * cn) + vx_load_expand(src + (n - 1 - j)*cn)) * vx_setall_u16(*((uint16_t*)(m + j))); v_store((uint16_t*)dst, v_res0); - v_store((uint16_t*)dst + 8, v_res1); } +#endif for (; i < lencn; i++, src++, dst++) { *dst = m[pre_shift] * src[pre_shift*cn]; @@ -3025,28 +2964,13 @@ template <> void vlineSmooth1N(const ufixedpoint16* const * src, const ufixedpoint16* m, int, uint8_t* dst, int len) { const ufixedpoint16* src0 = src[0]; - v_uint16x8 v_mul = v_setall_u16(*((uint16_t*)m)); -#if CV_SSE2 - v_uint16x8 v_1 = v_setall_u16(1); - v_mul += v_mul; -#endif int i = 0; - for (; i <= len - 16; i += 16) - { - v_uint16x8 v_src0 = v_load((uint16_t*)src0 + i); - v_uint16x8 v_src1 = v_load((uint16_t*)src0 + i + 8); - v_uint8x16 v_res; -#if CV_SSE2 - v_res.val = _mm_packus_epi16(_mm_srli_epi16(_mm_add_epi16(v_1.val, _mm_mulhi_epu16(v_src0.val, v_mul.val)),1), - _mm_srli_epi16(_mm_add_epi16(v_1.val, _mm_mulhi_epu16(v_src1.val, v_mul.val)),1)); -#else - v_uint32x4 v_res0, v_res1, v_res2, v_res3; - v_mul_expand(v_src0, v_mul, v_res0, v_res1); - v_mul_expand(v_src1, v_mul, v_res2, v_res3); - v_res = v_pack(v_rshr_pack<16>(v_res0, v_res1), v_rshr_pack<16>(v_res2, v_res3)); +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + v_uint16 v_mul = vx_setall_u16(*((uint16_t*)m)<<1); + for (; i <= len - VECSZ; i += VECSZ) + v_rshr_pack_store<1>(dst + i, v_mul_hi(vx_load((uint16_t*)src0 + i), v_mul)); #endif - v_store(dst + i, v_res); - } for (; i < len; i++) dst[i] = m[0] * src0[i]; } @@ -3062,8 +2986,11 @@ void vlineSmooth1N1(const ufixedpoint16* const * src, co { const ufixedpoint16* src0 = src[0]; int i = 0; - for (; i <= len - 8; i += 8) - v_rshr_pack_store<8>(dst + i, v_load((uint16_t*)(src0 + i))); +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + for (; i <= len - VECSZ; i += VECSZ) + v_rshr_pack_store<8>(dst + i, vx_load((uint16_t*)(src0 + i))); +#endif for (; i < len; i++) dst[i] = src0[i]; } @@ -3077,46 +3004,51 @@ template <> void vlineSmooth3N(const ufixedpoint16* const * src, const ufixedpoint16* m, int, uint8_t* dst, int len) { int i = 0; - static const v_int16x8 v_128 = v_reinterpret_as_s16(v_setall_u16((uint16_t)1 << 15)); - v_int32x4 v_128_4 = v_setall_s32(128 << 16); - if (len > 7) +#if CV_SIMD + static const v_int16 v_128 = v_reinterpret_as_s16(vx_setall_u16((uint16_t)1 << 15)); + v_int32 v_128_4 = vx_setall_s32(128 << 16); + const int VECSZ = v_uint16::nlanes; + if (len >= VECSZ) { ufixedpoint32 val[] = { (m[0] + m[1] + m[2]) * ufixedpoint16((uint8_t)128) }; - v_128_4 = v_setall_s32(*((int32_t*)val)); - } - v_int16x8 v_mul01 = v_reinterpret_as_s16(v_setall_u32(*((uint32_t*)m))); - v_int16x8 v_mul2 = v_reinterpret_as_s16(v_setall_u16(*((uint16_t*)(m + 2)))); - for (; i <= len - 32; i += 32) - { - v_int16x8 v_src00, v_src10, v_src01, v_src11, v_src02, v_src12, v_src03, v_src13; - v_int16x8 v_tmp0, v_tmp1; - - v_src00 = v_load((int16_t*)(src[0]) + i); - v_src01 = v_load((int16_t*)(src[0]) + i + 8); - v_src02 = v_load((int16_t*)(src[0]) + i + 16); - v_src03 = v_load((int16_t*)(src[0]) + i + 24); - v_src10 = v_load((int16_t*)(src[1]) + i); - v_src11 = v_load((int16_t*)(src[1]) + i + 8); - v_src12 = v_load((int16_t*)(src[1]) + i + 16); - v_src13 = v_load((int16_t*)(src[1]) + i + 24); + v_128_4 = vx_setall_s32(*((int32_t*)val)); + } + v_int16 v_mul01 = v_reinterpret_as_s16(vx_setall_u32(*((uint32_t*)m))); + v_int16 v_mul2 = v_reinterpret_as_s16(vx_setall_u16(*((uint16_t*)(m + 2)))); + for (; i <= len - 4*VECSZ; i += 4*VECSZ) + { + v_int16 v_src00, v_src10, v_src01, v_src11, v_src02, v_src12, v_src03, v_src13; + v_int16 v_tmp0, v_tmp1; + + const int16_t* src0 = (const int16_t*)src[0] + i; + const int16_t* src1 = (const int16_t*)src[1] + i; + v_src00 = vx_load(src0); + v_src01 = vx_load(src0 + VECSZ); + v_src02 = vx_load(src0 + 2*VECSZ); + v_src03 = vx_load(src0 + 3*VECSZ); + v_src10 = vx_load(src1); + v_src11 = vx_load(src1 + VECSZ); + v_src12 = vx_load(src1 + 2*VECSZ); + v_src13 = vx_load(src1 + 3*VECSZ); v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src10, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res0 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res1 = v_dotprod(v_tmp1, v_mul01); + v_int32 v_res0 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res1 = v_dotprod(v_tmp1, v_mul01); v_zip(v_add_wrap(v_src01, v_128), v_add_wrap(v_src11, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res2 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res3 = v_dotprod(v_tmp1, v_mul01); + v_int32 v_res2 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res3 = v_dotprod(v_tmp1, v_mul01); v_zip(v_add_wrap(v_src02, v_128), v_add_wrap(v_src12, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res4 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res5 = v_dotprod(v_tmp1, v_mul01); + v_int32 v_res4 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res5 = v_dotprod(v_tmp1, v_mul01); v_zip(v_add_wrap(v_src03, v_128), v_add_wrap(v_src13, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res6 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res7 = v_dotprod(v_tmp1, v_mul01); - - v_int32x4 v_resj0, v_resj1; - v_src00 = v_load((int16_t*)(src[2]) + i); - v_src01 = v_load((int16_t*)(src[2]) + i + 8); - v_src02 = v_load((int16_t*)(src[2]) + i + 16); - v_src03 = v_load((int16_t*)(src[2]) + i + 24); + v_int32 v_res6 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res7 = v_dotprod(v_tmp1, v_mul01); + + v_int32 v_resj0, v_resj1; + const int16_t* src2 = (const int16_t*)src[2] + i; + v_src00 = vx_load(src2); + v_src01 = vx_load(src2 + VECSZ); + v_src02 = vx_load(src2 + 2*VECSZ); + v_src03 = vx_load(src2 + 3*VECSZ); v_mul_expand(v_add_wrap(v_src00, v_128), v_mul2, v_resj0, v_resj1); v_res0 += v_resj0; v_res1 += v_resj1; @@ -3139,11 +3071,12 @@ void vlineSmooth3N(const ufixedpoint16* const * src, con v_res6 += v_128_4; v_res7 += v_128_4; - v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); - v_store(dst + i + 16, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); + v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); + v_store(dst + i + 2*VECSZ, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); } +#endif for (; i < len; i++) dst[i] = m[0] * src[0][i] + m[1] * src[1][i] + m[2] * src[2][i]; } @@ -3157,18 +3090,21 @@ template <> void vlineSmooth3N121(const ufixedpoint16* const * src, const ufixedpoint16*, int, uint8_t* dst, int len) { int i = 0; - for (; i <= len - 16; i += 16) - { - v_uint32x4 v_src00, v_src01, v_src02, v_src03, v_src10, v_src11, v_src12, v_src13, v_src20, v_src21, v_src22, v_src23; - v_expand(v_load((uint16_t*)(src[0]) + i), v_src00, v_src01); - v_expand(v_load((uint16_t*)(src[0]) + i + 8), v_src02, v_src03); - v_expand(v_load((uint16_t*)(src[1]) + i), v_src10, v_src11); - v_expand(v_load((uint16_t*)(src[1]) + i + 8), v_src12, v_src13); - v_expand(v_load((uint16_t*)(src[2]) + i), v_src20, v_src21); - v_expand(v_load((uint16_t*)(src[2]) + i + 8), v_src22, v_src23); +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + for (; i <= len - 2*VECSZ; i += 2*VECSZ) + { + v_uint32 v_src00, v_src01, v_src02, v_src03, v_src10, v_src11, v_src12, v_src13, v_src20, v_src21, v_src22, v_src23; + v_expand(vx_load((uint16_t*)(src[0]) + i), v_src00, v_src01); + v_expand(vx_load((uint16_t*)(src[0]) + i + VECSZ), v_src02, v_src03); + v_expand(vx_load((uint16_t*)(src[1]) + i), v_src10, v_src11); + v_expand(vx_load((uint16_t*)(src[1]) + i + VECSZ), v_src12, v_src13); + v_expand(vx_load((uint16_t*)(src[2]) + i), v_src20, v_src21); + v_expand(vx_load((uint16_t*)(src[2]) + i + VECSZ), v_src22, v_src23); v_store(dst + i, v_pack(v_rshr_pack<10>(v_src00 + v_src20 + (v_src10 + v_src10), v_src01 + v_src21 + (v_src11 + v_src11)), v_rshr_pack<10>(v_src02 + v_src22 + (v_src12 + v_src12), v_src03 + v_src23 + (v_src13 + v_src13)))); } +#endif for (; i < len; i++) dst[i] = (((uint32_t)(((uint16_t*)(src[0]))[i]) + (uint32_t)(((uint16_t*)(src[2]))[i]) + ((uint32_t)(((uint16_t*)(src[1]))[i]) << 1)) + (1 << 9)) >> 10; } @@ -3182,95 +3118,102 @@ template <> void vlineSmooth5N(const ufixedpoint16* const * src, const ufixedpoint16* m, int, uint8_t* dst, int len) { int i = 0; - static const v_int16x8 v_128 = v_reinterpret_as_s16(v_setall_u16((uint16_t)1 << 15)); - v_int32x4 v_128_4 = v_setall_s32(128 << 16); - if (len > 7) +#if CV_SIMD + const int VECSZ = v_uint16::nlanes; + if (len >= 4 * VECSZ) { ufixedpoint32 val[] = { (m[0] + m[1] + m[2] + m[3] + m[4]) * ufixedpoint16((uint8_t)128) }; - v_128_4 = v_setall_s32(*((int32_t*)val)); - } - v_int16x8 v_mul01 = v_reinterpret_as_s16(v_setall_u32(*((uint32_t*)m))); - v_int16x8 v_mul23 = v_reinterpret_as_s16(v_setall_u32(*((uint32_t*)(m + 2)))); - v_int16x8 v_mul4 = v_reinterpret_as_s16(v_setall_u16(*((uint16_t*)(m + 4)))); - for (; i <= len - 32; i += 32) - { - v_int16x8 v_src00, v_src10, v_src01, v_src11, v_src02, v_src12, v_src03, v_src13; - v_int16x8 v_tmp0, v_tmp1; - - v_src00 = v_load((int16_t*)(src[0]) + i); - v_src01 = v_load((int16_t*)(src[0]) + i + 8); - v_src02 = v_load((int16_t*)(src[0]) + i + 16); - v_src03 = v_load((int16_t*)(src[0]) + i + 24); - v_src10 = v_load((int16_t*)(src[1]) + i); - v_src11 = v_load((int16_t*)(src[1]) + i + 8); - v_src12 = v_load((int16_t*)(src[1]) + i + 16); - v_src13 = v_load((int16_t*)(src[1]) + i + 24); - v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src10, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res0 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res1 = v_dotprod(v_tmp1, v_mul01); - v_zip(v_add_wrap(v_src01, v_128), v_add_wrap(v_src11, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res2 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res3 = v_dotprod(v_tmp1, v_mul01); - v_zip(v_add_wrap(v_src02, v_128), v_add_wrap(v_src12, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res4 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res5 = v_dotprod(v_tmp1, v_mul01); - v_zip(v_add_wrap(v_src03, v_128), v_add_wrap(v_src13, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res6 = v_dotprod(v_tmp0, v_mul01); - v_int32x4 v_res7 = v_dotprod(v_tmp1, v_mul01); - - v_src00 = v_load((int16_t*)(src[2]) + i); - v_src01 = v_load((int16_t*)(src[2]) + i + 8); - v_src02 = v_load((int16_t*)(src[2]) + i + 16); - v_src03 = v_load((int16_t*)(src[2]) + i + 24); - v_src10 = v_load((int16_t*)(src[3]) + i); - v_src11 = v_load((int16_t*)(src[3]) + i + 8); - v_src12 = v_load((int16_t*)(src[3]) + i + 16); - v_src13 = v_load((int16_t*)(src[3]) + i + 24); - v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src10, v_128), v_tmp0, v_tmp1); - v_res0 += v_dotprod(v_tmp0, v_mul23); - v_res1 += v_dotprod(v_tmp1, v_mul23); - v_zip(v_add_wrap(v_src01, v_128), v_add_wrap(v_src11, v_128), v_tmp0, v_tmp1); - v_res2 += v_dotprod(v_tmp0, v_mul23); - v_res3 += v_dotprod(v_tmp1, v_mul23); - v_zip(v_add_wrap(v_src02, v_128), v_add_wrap(v_src12, v_128), v_tmp0, v_tmp1); - v_res4 += v_dotprod(v_tmp0, v_mul23); - v_res5 += v_dotprod(v_tmp1, v_mul23); - v_zip(v_add_wrap(v_src03, v_128), v_add_wrap(v_src13, v_128), v_tmp0, v_tmp1); - v_res6 += v_dotprod(v_tmp0, v_mul23); - v_res7 += v_dotprod(v_tmp1, v_mul23); - - v_int32x4 v_resj0, v_resj1; - v_src00 = v_load((int16_t*)(src[4]) + i); - v_src01 = v_load((int16_t*)(src[4]) + i + 8); - v_src02 = v_load((int16_t*)(src[4]) + i + 16); - v_src03 = v_load((int16_t*)(src[4]) + i + 24); - v_mul_expand(v_add_wrap(v_src00, v_128), v_mul4, v_resj0, v_resj1); - v_res0 += v_resj0; - v_res1 += v_resj1; - v_mul_expand(v_add_wrap(v_src01, v_128), v_mul4, v_resj0, v_resj1); - v_res2 += v_resj0; - v_res3 += v_resj1; - v_mul_expand(v_add_wrap(v_src02, v_128), v_mul4, v_resj0, v_resj1); - v_res4 += v_resj0; - v_res5 += v_resj1; - v_mul_expand(v_add_wrap(v_src03, v_128), v_mul4, v_resj0, v_resj1); - v_res6 += v_resj0; - v_res7 += v_resj1; + v_int32 v_128_4 = vx_setall_s32(*((int32_t*)val)); + static const v_int16 v_128 = v_reinterpret_as_s16(vx_setall_u16((uint16_t)1 << 15)); + v_int16 v_mul01 = v_reinterpret_as_s16(vx_setall_u32(*((uint32_t*)m))); + v_int16 v_mul23 = v_reinterpret_as_s16(vx_setall_u32(*((uint32_t*)(m + 2)))); + v_int16 v_mul4 = v_reinterpret_as_s16(vx_setall_u16(*((uint16_t*)(m + 4)))); + for (; i <= len - 4*VECSZ; i += 4*VECSZ) + { + v_int16 v_src00, v_src10, v_src01, v_src11, v_src02, v_src12, v_src03, v_src13; + v_int16 v_tmp0, v_tmp1; + + const int16_t* src0 = (const int16_t*)src[0] + i; + const int16_t* src1 = (const int16_t*)src[1] + i; + v_src00 = vx_load(src0); + v_src01 = vx_load(src0 + VECSZ); + v_src02 = vx_load(src0 + 2*VECSZ); + v_src03 = vx_load(src0 + 3*VECSZ); + v_src10 = vx_load(src1); + v_src11 = vx_load(src1 + VECSZ); + v_src12 = vx_load(src1 + 2*VECSZ); + v_src13 = vx_load(src1 + 3*VECSZ); + v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src10, v_128), v_tmp0, v_tmp1); + v_int32 v_res0 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res1 = v_dotprod(v_tmp1, v_mul01); + v_zip(v_add_wrap(v_src01, v_128), v_add_wrap(v_src11, v_128), v_tmp0, v_tmp1); + v_int32 v_res2 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res3 = v_dotprod(v_tmp1, v_mul01); + v_zip(v_add_wrap(v_src02, v_128), v_add_wrap(v_src12, v_128), v_tmp0, v_tmp1); + v_int32 v_res4 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res5 = v_dotprod(v_tmp1, v_mul01); + v_zip(v_add_wrap(v_src03, v_128), v_add_wrap(v_src13, v_128), v_tmp0, v_tmp1); + v_int32 v_res6 = v_dotprod(v_tmp0, v_mul01); + v_int32 v_res7 = v_dotprod(v_tmp1, v_mul01); + + const int16_t* src2 = (const int16_t*)src[2] + i; + const int16_t* src3 = (const int16_t*)src[3] + i; + v_src00 = vx_load(src2); + v_src01 = vx_load(src2 + VECSZ); + v_src02 = vx_load(src2 + 2*VECSZ); + v_src03 = vx_load(src2 + 3*VECSZ); + v_src10 = vx_load(src3); + v_src11 = vx_load(src3 + VECSZ); + v_src12 = vx_load(src3 + 2*VECSZ); + v_src13 = vx_load(src3 + 3*VECSZ); + v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src10, v_128), v_tmp0, v_tmp1); + v_res0 += v_dotprod(v_tmp0, v_mul23); + v_res1 += v_dotprod(v_tmp1, v_mul23); + v_zip(v_add_wrap(v_src01, v_128), v_add_wrap(v_src11, v_128), v_tmp0, v_tmp1); + v_res2 += v_dotprod(v_tmp0, v_mul23); + v_res3 += v_dotprod(v_tmp1, v_mul23); + v_zip(v_add_wrap(v_src02, v_128), v_add_wrap(v_src12, v_128), v_tmp0, v_tmp1); + v_res4 += v_dotprod(v_tmp0, v_mul23); + v_res5 += v_dotprod(v_tmp1, v_mul23); + v_zip(v_add_wrap(v_src03, v_128), v_add_wrap(v_src13, v_128), v_tmp0, v_tmp1); + v_res6 += v_dotprod(v_tmp0, v_mul23); + v_res7 += v_dotprod(v_tmp1, v_mul23); + + v_int32 v_resj0, v_resj1; + const int16_t* src4 = (const int16_t*)src[4] + i; + v_src00 = vx_load(src4); + v_src01 = vx_load(src4 + VECSZ); + v_src02 = vx_load(src4 + 2*VECSZ); + v_src03 = vx_load(src4 + 3*VECSZ); + v_mul_expand(v_add_wrap(v_src00, v_128), v_mul4, v_resj0, v_resj1); + v_res0 += v_resj0; + v_res1 += v_resj1; + v_mul_expand(v_add_wrap(v_src01, v_128), v_mul4, v_resj0, v_resj1); + v_res2 += v_resj0; + v_res3 += v_resj1; + v_mul_expand(v_add_wrap(v_src02, v_128), v_mul4, v_resj0, v_resj1); + v_res4 += v_resj0; + v_res5 += v_resj1; + v_mul_expand(v_add_wrap(v_src03, v_128), v_mul4, v_resj0, v_resj1); + v_res6 += v_resj0; + v_res7 += v_resj1; - v_res0 += v_128_4; - v_res1 += v_128_4; - v_res2 += v_128_4; - v_res3 += v_128_4; - v_res4 += v_128_4; - v_res5 += v_128_4; - v_res6 += v_128_4; - v_res7 += v_128_4; + v_res0 += v_128_4; + v_res1 += v_128_4; + v_res2 += v_128_4; + v_res3 += v_128_4; + v_res4 += v_128_4; + v_res5 += v_128_4; + v_res6 += v_128_4; + v_res7 += v_128_4; - v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); - v_store(dst + i + 16, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); + v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); + v_store(dst + i + 2*VECSZ, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); + } } +#endif for (; i < len; i++) dst[i] = m[0] * src[0][i] + m[1] * src[1][i] + m[2] * src[2][i] + m[3] * src[3][i] + m[4] * src[4][i]; } @@ -3284,28 +3227,31 @@ template <> void vlineSmooth5N14641(const ufixedpoint16* const * src, const ufixedpoint16*, int, uint8_t* dst, int len) { int i = 0; - v_uint32x4 v_6 = v_setall_u32(6); - for (; i <= len - 16; i += 16) - { - v_uint32x4 v_src00, v_src10, v_src20, v_src30, v_src40; - v_uint32x4 v_src01, v_src11, v_src21, v_src31, v_src41; - v_uint32x4 v_src02, v_src12, v_src22, v_src32, v_src42; - v_uint32x4 v_src03, v_src13, v_src23, v_src33, v_src43; - v_expand(v_load((uint16_t*)(src[0]) + i), v_src00, v_src01); - v_expand(v_load((uint16_t*)(src[0]) + i + 8), v_src02, v_src03); - v_expand(v_load((uint16_t*)(src[1]) + i), v_src10, v_src11); - v_expand(v_load((uint16_t*)(src[1]) + i + 8), v_src12, v_src13); - v_expand(v_load((uint16_t*)(src[2]) + i), v_src20, v_src21); - v_expand(v_load((uint16_t*)(src[2]) + i + 8), v_src22, v_src23); - v_expand(v_load((uint16_t*)(src[3]) + i), v_src30, v_src31); - v_expand(v_load((uint16_t*)(src[3]) + i + 8), v_src32, v_src33); - v_expand(v_load((uint16_t*)(src[4]) + i), v_src40, v_src41); - v_expand(v_load((uint16_t*)(src[4]) + i + 8), v_src42, v_src43); +#if CV_SIMD + v_uint32 v_6 = vx_setall_u32(6); + const int VECSZ = v_uint16::nlanes; + for (; i <= len - 2*VECSZ; i += 2*VECSZ) + { + v_uint32 v_src00, v_src10, v_src20, v_src30, v_src40; + v_uint32 v_src01, v_src11, v_src21, v_src31, v_src41; + v_uint32 v_src02, v_src12, v_src22, v_src32, v_src42; + v_uint32 v_src03, v_src13, v_src23, v_src33, v_src43; + v_expand(vx_load((uint16_t*)(src[0]) + i), v_src00, v_src01); + v_expand(vx_load((uint16_t*)(src[0]) + i + VECSZ), v_src02, v_src03); + v_expand(vx_load((uint16_t*)(src[1]) + i), v_src10, v_src11); + v_expand(vx_load((uint16_t*)(src[1]) + i + VECSZ), v_src12, v_src13); + v_expand(vx_load((uint16_t*)(src[2]) + i), v_src20, v_src21); + v_expand(vx_load((uint16_t*)(src[2]) + i + VECSZ), v_src22, v_src23); + v_expand(vx_load((uint16_t*)(src[3]) + i), v_src30, v_src31); + v_expand(vx_load((uint16_t*)(src[3]) + i + VECSZ), v_src32, v_src33); + v_expand(vx_load((uint16_t*)(src[4]) + i), v_src40, v_src41); + v_expand(vx_load((uint16_t*)(src[4]) + i + VECSZ), v_src42, v_src43); v_store(dst + i, v_pack(v_rshr_pack<12>(v_src20*v_6 + ((v_src10 + v_src30) << 2) + v_src00 + v_src40, v_src21*v_6 + ((v_src11 + v_src31) << 2) + v_src01 + v_src41), v_rshr_pack<12>(v_src22*v_6 + ((v_src12 + v_src32) << 2) + v_src02 + v_src42, v_src23*v_6 + ((v_src13 + v_src33) << 2) + v_src03 + v_src43))); } +#endif for (; i < len; i++) dst[i] = ((uint32_t)(((uint16_t*)(src[2]))[i]) * 6 + (((uint32_t)(((uint16_t*)(src[1]))[i]) + (uint32_t)(((uint16_t*)(src[3]))[i])) << 2) + @@ -3326,57 +3272,63 @@ template <> void vlineSmooth(const ufixedpoint16* const * src, const ufixedpoint16* m, int n, uint8_t* dst, int len) { int i = 0; - static const v_int16x8 v_128 = v_reinterpret_as_s16(v_setall_u16((uint16_t)1 << 15)); - v_int32x4 v_128_4 = v_setall_s32(128 << 16); - if (len > 7) +#if CV_SIMD + static const v_int16 v_128 = v_reinterpret_as_s16(vx_setall_u16((uint16_t)1 << 15)); + v_int32 v_128_4 = vx_setall_s32(128 << 16); + const int VECSZ = v_uint16::nlanes; + if (len >= VECSZ) { ufixedpoint16 msum = m[0] + m[1]; for (int j = 2; j < n; j++) msum = msum + m[j]; ufixedpoint32 val[] = { msum * ufixedpoint16((uint8_t)128) }; - v_128_4 = v_setall_s32(*((int32_t*)val)); + v_128_4 = vx_setall_s32(*((int32_t*)val)); } - for (; i <= len - 32; i += 32) + for (; i <= len - 4*VECSZ; i += 4*VECSZ) { - v_int16x8 v_src00, v_src10, v_src01, v_src11, v_src02, v_src12, v_src03, v_src13; - v_int16x8 v_tmp0, v_tmp1; + v_int16 v_src00, v_src10, v_src01, v_src11, v_src02, v_src12, v_src03, v_src13; + v_int16 v_tmp0, v_tmp1; - v_int16x8 v_mul = v_reinterpret_as_s16(v_setall_u32(*((uint32_t*)m))); + v_int16 v_mul = v_reinterpret_as_s16(vx_setall_u32(*((uint32_t*)m))); - v_src00 = v_load((int16_t*)(src[0]) + i); - v_src01 = v_load((int16_t*)(src[0]) + i + 8); - v_src02 = v_load((int16_t*)(src[0]) + i + 16); - v_src03 = v_load((int16_t*)(src[0]) + i + 24); - v_src10 = v_load((int16_t*)(src[1]) + i); - v_src11 = v_load((int16_t*)(src[1]) + i + 8); - v_src12 = v_load((int16_t*)(src[1]) + i + 16); - v_src13 = v_load((int16_t*)(src[1]) + i + 24); + const int16_t* src0 = (const int16_t*)src[0] + i; + const int16_t* src1 = (const int16_t*)src[1] + i; + v_src00 = vx_load(src0); + v_src01 = vx_load(src0 + VECSZ); + v_src02 = vx_load(src0 + 2*VECSZ); + v_src03 = vx_load(src0 + 3*VECSZ); + v_src10 = vx_load(src1); + v_src11 = vx_load(src1 + VECSZ); + v_src12 = vx_load(src1 + 2*VECSZ); + v_src13 = vx_load(src1 + 3*VECSZ); v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src10, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res0 = v_dotprod(v_tmp0, v_mul); - v_int32x4 v_res1 = v_dotprod(v_tmp1, v_mul); + v_int32 v_res0 = v_dotprod(v_tmp0, v_mul); + v_int32 v_res1 = v_dotprod(v_tmp1, v_mul); v_zip(v_add_wrap(v_src01, v_128), v_add_wrap(v_src11, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res2 = v_dotprod(v_tmp0, v_mul); - v_int32x4 v_res3 = v_dotprod(v_tmp1, v_mul); + v_int32 v_res2 = v_dotprod(v_tmp0, v_mul); + v_int32 v_res3 = v_dotprod(v_tmp1, v_mul); v_zip(v_add_wrap(v_src02, v_128), v_add_wrap(v_src12, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res4 = v_dotprod(v_tmp0, v_mul); - v_int32x4 v_res5 = v_dotprod(v_tmp1, v_mul); + v_int32 v_res4 = v_dotprod(v_tmp0, v_mul); + v_int32 v_res5 = v_dotprod(v_tmp1, v_mul); v_zip(v_add_wrap(v_src03, v_128), v_add_wrap(v_src13, v_128), v_tmp0, v_tmp1); - v_int32x4 v_res6 = v_dotprod(v_tmp0, v_mul); - v_int32x4 v_res7 = v_dotprod(v_tmp1, v_mul); + v_int32 v_res6 = v_dotprod(v_tmp0, v_mul); + v_int32 v_res7 = v_dotprod(v_tmp1, v_mul); int j = 2; for (; j < n - 1; j+=2) { - v_mul = v_reinterpret_as_s16(v_setall_u32(*((uint32_t*)(m+j)))); - - v_src00 = v_load((int16_t*)(src[j]) + i); - v_src01 = v_load((int16_t*)(src[j]) + i + 8); - v_src02 = v_load((int16_t*)(src[j]) + i + 16); - v_src03 = v_load((int16_t*)(src[j]) + i + 24); - v_src10 = v_load((int16_t*)(src[j+1]) + i); - v_src11 = v_load((int16_t*)(src[j+1]) + i + 8); - v_src12 = v_load((int16_t*)(src[j+1]) + i + 16); - v_src13 = v_load((int16_t*)(src[j+1]) + i + 24); + v_mul = v_reinterpret_as_s16(vx_setall_u32(*((uint32_t*)(m+j)))); + + const int16_t* srcj0 = (const int16_t*)src[j] + i; + const int16_t* srcj1 = (const int16_t*)src[j + 1] + i; + v_src00 = vx_load(srcj0); + v_src01 = vx_load(srcj0 + VECSZ); + v_src02 = vx_load(srcj0 + 2*VECSZ); + v_src03 = vx_load(srcj0 + 3*VECSZ); + v_src10 = vx_load(srcj1); + v_src11 = vx_load(srcj1 + VECSZ); + v_src12 = vx_load(srcj1 + 2*VECSZ); + v_src13 = vx_load(srcj1 + 3*VECSZ); v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src10, v_128), v_tmp0, v_tmp1); v_res0 += v_dotprod(v_tmp0, v_mul); v_res1 += v_dotprod(v_tmp1, v_mul); @@ -3392,12 +3344,13 @@ void vlineSmooth(const ufixedpoint16* const * src, const } if(j < n) { - v_int32x4 v_resj0, v_resj1; - v_mul = v_reinterpret_as_s16(v_setall_u16(*((uint16_t*)(m + j)))); - v_src00 = v_load((int16_t*)(src[j]) + i); - v_src01 = v_load((int16_t*)(src[j]) + i + 8); - v_src02 = v_load((int16_t*)(src[j]) + i + 16); - v_src03 = v_load((int16_t*)(src[j]) + i + 24); + v_int32 v_resj0, v_resj1; + v_mul = v_reinterpret_as_s16(vx_setall_u16(*((uint16_t*)(m + j)))); + const int16_t* srcj = (const int16_t*)src[j] + i; + v_src00 = vx_load(srcj); + v_src01 = vx_load(srcj + VECSZ); + v_src02 = vx_load(srcj + 2*VECSZ); + v_src03 = vx_load(srcj + 3*VECSZ); v_mul_expand(v_add_wrap(v_src00, v_128), v_mul, v_resj0, v_resj1); v_res0 += v_resj0; v_res1 += v_resj1; @@ -3420,11 +3373,12 @@ void vlineSmooth(const ufixedpoint16* const * src, const v_res6 += v_128_4; v_res7 += v_128_4; - v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); - v_store(dst + i + 16, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); + v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); + v_store(dst + i + 2*VECSZ, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); } +#endif for (; i < len; i++) { ufixedpoint32 val = m[0] * src[0][i]; @@ -3450,29 +3404,32 @@ void vlineSmoothONa_yzy_a(const FT* const * src, const FT* m, int n, ET* dst, in template <> void vlineSmoothONa_yzy_a(const ufixedpoint16* const * src, const ufixedpoint16* m, int n, uint8_t* dst, int len) { - int pre_shift = n / 2; int i = 0; - static const v_int16x8 v_128 = v_reinterpret_as_s16(v_setall_u16((uint16_t)1 << 15)); - v_int32x4 v_128_4 = v_setall_s32(128 << 16); - if (len > 7) +#if CV_SIMD + int pre_shift = n / 2; + static const v_int16 v_128 = v_reinterpret_as_s16(vx_setall_u16((uint16_t)1 << 15)); + v_int32 v_128_4 = vx_setall_s32(128 << 16); + const int VECSZ = v_uint16::nlanes; + if (len >= VECSZ) { ufixedpoint16 msum = m[0] + m[pre_shift] + m[n - 1]; for (int j = 1; j < pre_shift; j++) msum = msum + m[j] + m[n - 1 - j]; ufixedpoint32 val[] = { msum * ufixedpoint16((uint8_t)128) }; - v_128_4 = v_setall_s32(*((int32_t*)val)); + v_128_4 = vx_setall_s32(*((int32_t*)val)); } - for (; i <= len - 32; i += 32) + for (; i <= len - 4*VECSZ; i += 4*VECSZ) { - v_int16x8 v_src00, v_src10, v_src20, v_src30, v_src01, v_src11, v_src21, v_src31; - v_int32x4 v_res0, v_res1, v_res2, v_res3, v_res4, v_res5, v_res6, v_res7; - v_int16x8 v_tmp0, v_tmp1, v_tmp2, v_tmp3, v_tmp4, v_tmp5, v_tmp6, v_tmp7; + v_int16 v_src00, v_src10, v_src20, v_src30, v_src01, v_src11, v_src21, v_src31; + v_int32 v_res0, v_res1, v_res2, v_res3, v_res4, v_res5, v_res6, v_res7; + v_int16 v_tmp0, v_tmp1, v_tmp2, v_tmp3, v_tmp4, v_tmp5, v_tmp6, v_tmp7; - v_int16x8 v_mul = v_reinterpret_as_s16(v_setall_u16(*((uint16_t*)(m + pre_shift)))); - v_src00 = v_load((int16_t*)(src[pre_shift]) + i); - v_src10 = v_load((int16_t*)(src[pre_shift]) + i + 8); - v_src20 = v_load((int16_t*)(src[pre_shift]) + i + 16); - v_src30 = v_load((int16_t*)(src[pre_shift]) + i + 24); + v_int16 v_mul = v_reinterpret_as_s16(vx_setall_u16(*((uint16_t*)(m + pre_shift)))); + const int16_t* srcp = (const int16_t*)src[pre_shift] + i; + v_src00 = vx_load(srcp); + v_src10 = vx_load(srcp + VECSZ); + v_src20 = vx_load(srcp + 2*VECSZ); + v_src30 = vx_load(srcp + 3*VECSZ); v_mul_expand(v_add_wrap(v_src00, v_128), v_mul, v_res0, v_res1); v_mul_expand(v_add_wrap(v_src10, v_128), v_mul, v_res2, v_res3); v_mul_expand(v_add_wrap(v_src20, v_128), v_mul, v_res4, v_res5); @@ -3481,16 +3438,18 @@ void vlineSmoothONa_yzy_a(const ufixedpoint16* const * s int j = 0; for (; j < pre_shift; j++) { - v_mul = v_reinterpret_as_s16(v_setall_u16(*((uint16_t*)(m + j)))); - - v_src00 = v_load((int16_t*)(src[j]) + i); - v_src10 = v_load((int16_t*)(src[j]) + i + 8); - v_src20 = v_load((int16_t*)(src[j]) + i + 16); - v_src30 = v_load((int16_t*)(src[j]) + i + 24); - v_src01 = v_load((int16_t*)(src[n - 1 - j]) + i); - v_src11 = v_load((int16_t*)(src[n - 1 - j]) + i + 8); - v_src21 = v_load((int16_t*)(src[n - 1 - j]) + i + 16); - v_src31 = v_load((int16_t*)(src[n - 1 - j]) + i + 24); + v_mul = v_reinterpret_as_s16(vx_setall_u16(*((uint16_t*)(m + j)))); + + const int16_t* srcj0 = (const int16_t*)src[j] + i; + const int16_t* srcj1 = (const int16_t*)src[n - 1 - j] + i; + v_src00 = vx_load(srcj0); + v_src10 = vx_load(srcj0 + VECSZ); + v_src20 = vx_load(srcj0 + 2*VECSZ); + v_src30 = vx_load(srcj0 + 3*VECSZ); + v_src01 = vx_load(srcj1); + v_src11 = vx_load(srcj1 + VECSZ); + v_src21 = vx_load(srcj1 + 2*VECSZ); + v_src31 = vx_load(srcj1 + 3*VECSZ); v_zip(v_add_wrap(v_src00, v_128), v_add_wrap(v_src01, v_128), v_tmp0, v_tmp1); v_res0 += v_dotprod(v_tmp0, v_mul); v_res1 += v_dotprod(v_tmp1, v_mul); @@ -3514,11 +3473,12 @@ void vlineSmoothONa_yzy_a(const ufixedpoint16* const * s v_res6 += v_128_4; v_res7 += v_128_4; - v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); - v_store(dst + i + 16, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), - v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); + v_store(dst + i , v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res0, v_res1)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res2, v_res3)))); + v_store(dst + i + 2*VECSZ, v_pack(v_reinterpret_as_u16(v_rshr_pack<16>(v_res4, v_res5)), + v_reinterpret_as_u16(v_rshr_pack<16>(v_res6, v_res7)))); } +#endif for (; i < len; i++) { ufixedpoint32 val = m[0] * src[0][i]; @@ -3816,8 +3776,8 @@ static void createGaussianKernels( T & kx, T & ky, int type, Size &ksize, if( ksize.height <= 0 && sigma2 > 0 ) ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1; - CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 && - ksize.height > 0 && ksize.height % 2 == 1 ); + CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 && + ksize.height > 0 && ksize.height % 2 == 1 ); sigma1 = std::max( sigma1, 0. ); sigma2 = std::max( sigma2, 0. ); From f36790d52194b52124702a644abd8a76db71f1ab Mon Sep 17 00:00:00 2001 From: Vlad Karpushin <40189112+VladKarpushin@users.noreply.github.com> Date: Fri, 31 Aug 2018 21:24:37 +0700 Subject: [PATCH 22/47] doc: fix English gramma in tutorial out-of-focus-deblur filter (#12214) * doc: fix English gramma in tutorial out-of-focus-deblur filter * Update out_of_focus_deblur_filter.markdown slightly modified one sentence --- .../out_of_focus_deblur_filter.markdown | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown b/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown index 993442168d..0e1da4efae 100755 --- a/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown +++ b/doc/tutorials/imgproc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.markdown @@ -8,54 +8,54 @@ Goal In this tutorial you will learn: -- what is a degradation image model -- what is PSF of out-of-focus image +- what a degradation image model is +- what the PSF of an out-of-focus image is - how to restore a blurred image -- what is Wiener filter +- what is a Wiener filter Theory ------ -@note The explanation is based on the books @cite gonzalez and @cite gruzman. Also, you can refer to Matlab's tutorial [Image Deblurring in Matlab] and an article [SmartDeblur]. -@note An out-of-focus image on this page is a real world image. An out-of-focus was done manually by camera optics. +@note The explanation is based on the books @cite gonzalez and @cite gruzman. Also, you can refer to Matlab's tutorial [Image Deblurring in Matlab] and the article [SmartDeblur]. +@note The out-of-focus image on this page is a real world image. The out-of-focus was achieved manually by camera optics. ### What is a degradation image model? -A mathematical model of the image degradation in frequency domain representation is: +Here is a mathematical model of the image degradation in frequency domain representation: \f[S = H\cdot U + N\f] where \f$S\f$ is a spectrum of blurred (degraded) image, \f$U\f$ is a spectrum of original true (undegraded) image, -\f$H\f$ is frequency response of point spread function (PSF), +\f$H\f$ is a frequency response of point spread function (PSF), \f$N\f$ is a spectrum of additive noise. -Circular PSF is a good approximation of out-of-focus distortion. Such PSF is specified by only one parameter - radius \f$R\f$. Circular PSF is used in this work. +The circular PSF is a good approximation of out-of-focus distortion. Such a PSF is specified by only one parameter - radius \f$R\f$. Circular PSF is used in this work. ![Circular point spread function](psf.png) -### How to restore an blurred image? +### How to restore a blurred image? -The objective of restoration (deblurring) is to obtain an estimate of the original image. Restoration formula in frequency domain is: +The objective of restoration (deblurring) is to obtain an estimate of the original image. The restoration formula in frequency domain is: \f[U' = H_w\cdot S\f] where -\f$U'\f$ is spectrum of estimation of original image \f$U\f$, -\f$H_w\f$ is restoration filter, for example, Wiener filter. +\f$U'\f$ is the spectrum of estimation of original image \f$U\f$, and +\f$H_w\f$ is the restoration filter, for example, the Wiener filter. -### What is Wiener filter? +### What is the Wiener filter? -Wiener filter is a way to restore a blurred image. Let's suppose that PSF is a real and symmetric signal, a power spectrum of the original true image and noise are not known, -then simplified Wiener formula is: +The Wiener filter is a way to restore a blurred image. Let's suppose that the PSF is a real and symmetric signal, a power spectrum of the original true image and noise are not known, +then a simplified Wiener formula is: \f[H_w = \frac{H}{|H|^2+\frac{1}{SNR}} \f] where \f$SNR\f$ is signal-to-noise ratio. -So, in order to recover an out-of-focus image by Wiener filter, it needs to know \f$SNR\f$ and \f$R\f$ of circular PSF. +So, in order to recover an out-of-focus image by Wiener filter, it needs to know the \f$SNR\f$ and \f$R\f$ of the circular PSF. Source code @@ -68,36 +68,36 @@ You can find source code in the `samples/cpp/tutorial_code/ImgProc/out_of_focus_ Explanation ----------- -An out-of-focus image recovering algorithm consists of PSF generation, Wiener filter generation and filtering an blurred image in frequency domain: +An out-of-focus image recovering algorithm consists of PSF generation, Wiener filter generation and filtering a blurred image in frequency domain: @snippet samples/cpp/tutorial_code/ImgProc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.cpp main -A function calcPSF() forms an circular PSF according to input parameter radius \f$R\f$: +A function calcPSF() forms a circular PSF according to input parameter radius \f$R\f$: @snippet samples/cpp/tutorial_code/ImgProc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.cpp calcPSF -A function calcWnrFilter() synthesizes simplified Wiener filter \f$H_w\f$ according to formula described above: +A function calcWnrFilter() synthesizes the simplified Wiener filter \f$H_w\f$ according to the formula described above: @snippet samples/cpp/tutorial_code/ImgProc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.cpp calcWnrFilter -A function fftshift() rearranges PSF. This code was just copied from tutorial @ref tutorial_discrete_fourier_transform "Discrete Fourier Transform": +A function fftshift() rearranges the PSF. This code was just copied from the tutorial @ref tutorial_discrete_fourier_transform "Discrete Fourier Transform": @snippet samples/cpp/tutorial_code/ImgProc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.cpp fftshift -A function filter2DFreq() filters an blurred image in frequency domain: +A function filter2DFreq() filters the blurred image in the frequency domain: @snippet samples/cpp/tutorial_code/ImgProc/out_of_focus_deblur_filter/out_of_focus_deblur_filter.cpp filter2DFreq Result ------ -Below you can see real out-of-focus image: +Below you can see the real out-of-focus image: ![Out-of-focus image](images/original.jpg) -Below result was done by \f$R\f$ = 53 and \f$SNR\f$ = 5200 parameters: +And the following result has been computed with \f$R\f$ = 53 and \f$SNR\f$ = 5200 parameters: ![The restored (deblurred) image](images/recovered.jpg) -The Wiener filter was used, values of \f$R\f$ and \f$SNR\f$ were selected manually to give the best possible visual result. -We can see that the result is not perfect, but it gives us a hint to the image content. With some difficulty, the text is readable. +The Wiener filter was used, and values of \f$R\f$ and \f$SNR\f$ were selected manually to give the best possible visual result. +We can see that the result is not perfect, but it gives us a hint to the image's content. With some difficulty, the text is readable. @note The parameter \f$R\f$ is the most important. So you should adjust \f$R\f$ first, then \f$SNR\f$. -@note Sometimes you can observe the ringing effect in an restored image. This effect can be reduced by several methods. For example, you can taper input image edges. +@note Sometimes you can observe the ringing effect in a restored image. This effect can be reduced with several methods. For example, you can taper input image edges. You can also find a quick video demonstration of this on [YouTube](https://youtu.be/0bEcE4B0XP4). From 7d3a1657c90dfe7b66da9f0ea8e364801e8cb324 Mon Sep 17 00:00:00 2001 From: Vlad Karpushin <40189112+VladKarpushin@users.noreply.github.com> Date: Fri, 31 Aug 2018 21:41:22 +0700 Subject: [PATCH 23/47] doc: add new tutorial motion deblur filter (#12215) * doc: add new tutorial motion deblur filter * Update motion_deblur_filter.markdown a few minor changes --- .../motion_deblur_filter/images/black_car.jpg | Bin 0 -> 33690 bytes .../images/motion_original.jpg | Bin 0 -> 51996 bytes .../images/motion_psf.png | Bin 0 -> 556 bytes .../motion_deblur_filter/images/white_car.jpg | Bin 0 -> 20184 bytes .../motion_deblur_filter.markdown | 72 +++++++ .../imgproc/table_of_content_imgproc.markdown | 10 + .../motion_deblur_filter.cpp | 184 ++++++++++++++++++ 7 files changed, 266 insertions(+) create mode 100755 doc/tutorials/imgproc/motion_deblur_filter/images/black_car.jpg create mode 100755 doc/tutorials/imgproc/motion_deblur_filter/images/motion_original.jpg create mode 100755 doc/tutorials/imgproc/motion_deblur_filter/images/motion_psf.png create mode 100755 doc/tutorials/imgproc/motion_deblur_filter/images/white_car.jpg create mode 100755 doc/tutorials/imgproc/motion_deblur_filter/motion_deblur_filter.markdown create mode 100755 samples/cpp/tutorial_code/ImgProc/motion_deblur_filter/motion_deblur_filter.cpp diff --git a/doc/tutorials/imgproc/motion_deblur_filter/images/black_car.jpg b/doc/tutorials/imgproc/motion_deblur_filter/images/black_car.jpg new file mode 100755 index 0000000000000000000000000000000000000000..1305057729f6c07b4b04039dbde114733bba7193 GIT binary patch literal 33690 zcmbTd1z429`ZxS+uuHRa!y+x+9V$2- ziT;P--mk|2CAy3tvGzX84O8{)q+uLGw?n`WM@|y4m4r{?^&U&eiTO?#1I+KVN%1hUnsP zw4al`KOTR=V=fn8S0_9^#$y^+dmC>6fRg@=_pwJi;;|?mlX@8$s^PIBUL;5z{tMgw z7xu9az}FK1)Z9G+y__5zeUN-;ex#V3oGeny-rvRE$4AJ(2JLL)WrtLAclEGw3j~0F zw)t-^fa-7GBJrIpEhZ-|EhH|C@BV+g|6}66ss7L4@7Vs6V*k!R#tcL^{GYi0a zdocjW9^m^X;lJhmyYG%ZzDXlVb?-pk$%Uz9Ok%ADLB@Wbt9=j7w$?uK-7``?Z5|IcjyRfoUupK^@{ z)TNgI%>!WoF-8MGwuS*HH3XUBFc$v` z_I7eW{*6{MG(w_%z5agr@DBR-K>!c~6!>o)Bfti50(^iFAP&d?3VE0{nqsARLGW5`YvS6UYOKfKs3ms0QkRX5bCb1@r?SfN@|7_y#NhtH3W{ z2RH=IKp+qtLMs6by<0C4kaEd7x*Y zN>ClB71RY90!@HsK#QQCpgqtj7y>2*(|}pQJYW&9EEomW0ULoW!465#Nos_#MQ)o#IwZvBqSu)NaRTF zkT{Wqk>rrnk_?h8lAMrIkzOZNBefv)B~2zRC+#GiA>AhL!&PRU}mjRS(rNHH4azT8$b_9YI}6-ABDj z1Eb-k(WZGslR)!|W|U@!mWo!A)|fVc_9^XK+66iY9S@y0oeLe7uAc5Q-6=gQJ&N9* zK90Vcev zGnzS`xt{qe3z+3Piy=!8O9{&`%Kn0l=n=0EQwhXp+Y(Lql*_GLy z*fZEW*f)^0NEFfqnS<;_?r^YhXmR**JmVPUIJ?Gs?cO!awVG>loWz{6oDVrOIJ-G_ zxY)V$xPrM}aDC+_;+Exh^eEWa~<0sr`Q@O7!{j@O@F9}@rzNDDX#6bMWR5(vr(x(OBwei0%SLJ9c^y%1Uy zrW4i`#t1hHZ;Nn=n2Dr{42WEcN{PCNmWqB8qY}F%h7oHM+Y`Sojuw9^{z-yNLPH`{ zqD5j?Qb5vPvQTnbidsrfDn_bX>OxvZ+Dp1x`lk$+jI~U@%okZ2Sp(Tb*+Dr1xf^n! za&P2LmnWsBoy*s1&O# zsdB2?sXkZzf#OF!LcK(7-w?gwb))gdk(#{P6SYoth`OeFy!z-(s+;$2=G~mvxTfKt z@j_!qQ%W;Pvr~&eOGhhJ>x(w4Hd?zg4Gx>0a0M(rwcN>)p~z z*PFe~ecSDJvp%4&t)H$xXTWRVY0!QLcE{k((>rU1Vurzn19z$KKDb+T_t5Bu5!UG2 zJ-&Os_qvTKj4g~SjgRiD-%q>0U?O7j#N>l1lc|$wiy7R^*zCF4f%#4IO!HL>DT`=} z&z8KF0hU8nOja&dZy!)RuzB#>n!x(Lb(QtGjlNB>&4I19ZNBXeS`D3z{$;0XmtptQ zUdcYq{>MY5hiMPj9h4l>9ez5hIA%F+I;lD3Iqg2udQ|l2$XVaH-1*AozDtcOp{uoP zs~eS@vs(_Iv7g z>~HMUwjtf2oS~7St6`d9 z72(9;&fy~%QA`@IUwH{6^}=_$E+OVAD>s zWAj3bY0E_Gt=8@~g|^0a;r8-39B-b!rG1NiNBl1G-Briqj)P8*&dn}|uI26r-QRjl zdOr2u=^gFU=^O0V=u^oOQlso};EsgcG}>CvV!nX#5} zx$(9M#ff*5DwACw)j#%s(*E>e>h{#c=X;;0zgT=(n6{f|59@!EF2- zVlHc*Wxn`3|M!{&>4mq8>WjlmcbC2`qnCfJc(0tVMy`>qW&L3PQMoR@{^qB~&+!fO zjn!Wszs@$Jw($j&CqPLEl7gH;KpX&=5(J?HU3~*00U9z2 zN(xdMN(3bxEe$=JIFgN-nN3MRm`hwsMOQ~tMMK@d%-hD`o{O=%hFz?^i{IlgOqiZ^ zQg-5#Ebox8AP_qThY+ujiKwVaYmie=>;GqW)eEHI^%e+Tb@;2K{L4W=U?>3$jsQSV zJc7U=Faihxo&toYq=Zs&5Qw4fP+zm55%&yFeFnQ>NXuzk*R>#r*6(+h*cP;jG~tc z2L;8nj1LVlJv4`!^=yN@GZc0~99xGdPQ>9E=Ztgn1ZcDTjPUU8MI^jF6V7~AO1b%+ zqF5;x%7UI@8xBH}%WYP94yrcyXbUH&@Q)aA5NH{M-2f3nk%%xu%$6(m&^TG#k@5i- zlS70$@RGN1kXV(@z+9&ZcTbBm53b3Y{J_2=W=UTA$wzfup-7_5daL5N4mzk+GId>u zAep7B!-!I|La+AG7#A&+?&M0Up@tB58Z%tZtec1*b z8UU$D^GBdfIKa*B>Wr~7f=MSxZ)T6Q6pWc*x%xPZAkr?83W0jDMpcqLkD|9UaKn;~ z^oVd)^>F$WwLuFXsm{T2N@#s*7G=L9l@MCVtx?*H!L2xxsNN}_9MBJ92f)R?+pJSiBiHuhU&Lxs5U_|A$s&uv92|tqry2t zHR8Bt1xq+x7m5f^3ZvY{77u0Vir+Q+%2`F)fB(>ZLdV|M4TKWcH9b-7$iq4Xuq~V- zT@2+q>3dne-+r{rIuGDD88UxIo&wX>@;uyuaf+|s*1 z`O4n5;u5hL9H5?9G4MMM&KEYFKe3JHu}o+z!Y(&?z92>(QW|&j!&OLj&i8?zHWY!ZpzE zn~$E-5DKSw{)q7cOxdyAXHl?xL2tX((%@`W+efNv9v9V_cD>Gl z*bPigU+VB%sXdc|O4hfP?(FD zxI#+PtOC=90OX*|Vqe`x+~*#Vc{&z<&sBXB)A+J@E!(d#Y{(d+j#+;pHSF15V?^F0 z*S`7qv_y??dJlCMvDNWNHvxP`Uq)+yo_ksihmi>g@L-;%`3pybX7Y zg7MXTmmEpLnJ1B~x8*8|(36WTuW0>bkrPj`IrLMEt}T!`iQ%qRreIW93BSY`xb`o4 zsC6V%Tme~GO-oX5VoRw!F))Q7GZI4PQ>Ir7$%CL|a1r1~NC8V-G=y}Q252GsX#zqj zIC~7x$4D&61RH%9Xz?-MP6~{hjlLMteNPNZBUYxY)QTJu^pO*!t|dW@6(nnpu*iQz0u3)!;%OGQJsp-Z{ti4b47|_-y zn9GyLZ7nZ(lFlq)cvws<_}cfPHd`JL_Vpw^?^l0o|AZ=pHHC9mLvnv-+;i4fbnX$7 zb;?66Ae#{VTI`%CPjVx(@k14b!f`n{sW)UfO-NYY3jMJ2$SPDowO}M!pSHw$>H7Es zdSaJ&Z=j^H{gS?9(bHyOtuLt{6?2$1!_iAuWN6@cuGhP7-Df8|z1<(aJvoz$+#TTJYrN(3+h0SJs&xjV&)TmQd@YopCPE+H7sI1TR4xYF7O48QymV}87VfjTE|n57 zo(Qw=wCaBc2fZcC zTEUCE6un3v3(^7LaLAMc@`Td+Y%On=Wb#~kvdz3tViBO5aZ&<*>k=Nw zLd?i~_B5`iBj*X#04$l#;v<}LrXhmdN?5^NN3%Dxb4iX0jbXUy*T7(%r5(pxHAiDV zwdy|wn{{NZF!F0R$#EMeN>ub4WzmZ7;e!vtZOi*1ias%GDDwn44h>vB6{WSgyT`RoM5>l2yDaeH4F;1VkQ<|dD zGXa(A>J7ZZd45PhKFEkg`aCN&pl>y^3y7yKpI3ZQ4BwgA-9?9J7&i|t>$)H9ndc7J zWH`APJxbv}zu_$JRI*T?m@D;rtOez7ud9C?b^j|DuYXY$nMd6#>ky;WusyQ1yJoXQ zCO*0sR%Nh0p(Vnv?h z2SaWNq*)k*sO^_kW5gHzViMT-R0IMP?*pm_#~d0Vh#|Kyh?{Qlgy1U8VsMNd?rznPhL;mNrgZ#X;L|k%)fe2A^*B#aX*_( zA_+wInWR9hqOs>_WWhdKij7E>ujw6s<37&QEq7Jm)t6-VXzqQuT;WqD)YqUGRv10IW|XVdVZ=xX$I|caCBdG)UW~)ggPb(7J>1<|-JE@SHz8?h z$R2aCtnIUX-b(}9bN&9+ftR|}Ridy~f5{*bvCWrxV#xRC)u3i;bQ9}lB(vD@ZOVY% zmK0~z*U#zM*XS!2%cIfDsVs^4l3IlhwRDR7!_(s?1{kx&k^XW6 zAlR(aQAMu5ILextnW7uex4Y$jEoLgHD=scB@h2DBjNzN-Ry~E-6;LJ4vy>DQCC#wp zpAn?G7L@K>HA>dYlY7EQy{vw8HXN1gp(1YJ8WAZDhksBg4N$lzDje?p>mlT_+EHvQ zlb_rs>6gRCy4I=f)~^gM=)!BbiL zIxKAB^UZGFl^&x1vM+rY@U3Om&t; zO`&RTpQMhRpirDk@Z$RhA7!6XA;|mqqX|MTp}a?Q`Msyl zw+6C2V$u`JWlKHeRP?#(OQ!7mVy)mpMX5cy8`%*GyRsxzu`LS$g_|ym2x_7{mC40nGyyfj z!jmNHSAcS?%tLz=R9qoi2m7l0C=Zo_{S&olKcf8kggcu5LDkm8ml??ZTGGefHKpXw z<3EB#aVpXA53Otl`gd~aVg?1ZD`I5MRw5k86OwHf&fF6gP|wf*SS_v-AF%KQTXFYX zyV##a$%p%8KSK3Jwcb8=Tc1fyId?2@<5-DK;zftNsFYdK_3kr`N_vj6Q?}heKN0j^ ziHI0zxsT{ua)?LE%SerW{?eyMrb5uSWVp7{(Lb6qc=~lvQrR3zm*q}DD8c4}C0CVd zNN^H(0O{!;Ow2`l&+Ac00TCY;cUVFR(>~H$qP|5HQcP1jBszZB=OsTX<*H@RBk+9P z*P{4t0%Dy`qhNiF?RMsT=BS?t+qr^DT=M4?=~u5{|3<>{ZKHNo<}0%mvzg{kLi!}?Vri15&9*zk77#5@)I_ga)oi0X+9%l$ zv);hy9ihYbb-`ZaOZqBXRzyOt$4cf`^!WG_It|;Nm^Y`hp;3_KWkmNQQit^KRkO20 z5*)?Ltbc;8fMRkHL?43L-lOz7MslyWYV7>@c(8bg zH4jVdzy;)hfB=SO{;duiL)kdlwFZWfseL9rZ(Q%5S+IMPP2T^h8ZuoI6s@bQ#bPBf z7Ne{3(UD=EJ2`MkS6M8h>uGFY5AwJ+lHMi9O?mN@ra#ep`}+_?U98=v(tHM_l7ozh zbhf>B8#14Tfb4;om-Nwb&MnDx&lg{7zGJlaGpg{Znm+2bLToWTY@R~{YcH?m`<@|N;%}FksP7?6Zv4h574#yN@+)?T z@yA~!IDd6wACaFrim8M0W~6t;7R%qaw%&g?D3`u3z>+`C-DiZ0#2c_~fXqZcA}J*2 z%OHCt3FxGk^&Mh5K3ETD7H!9$bFkS_w%K5Ydb2I={)YCv@5fejyKVvtYt8*}sd8U$ zce=q3P!s3+-D1U*1F?$o%(Y&iK;77tq`qojteOodK@{`wJ=!ksv7(7pn=p50z;hzM zz(Xg-kyoDZC4P8*R}9Qb$ne7zaC}e{oU-)&$nrt2qHH3emuTdqO<(hGu3DL`5wJwd zmn}9+HypTAxjJ%oH#Y`imB|yV)^{ zNsMV27t4ZdO-LjgZCBy+1Nw#*^U|VOr(Y9rnVnJnEz^C``V#bR=jYEFb7K2Htlms^ zFxV`6%m!%CNqWLveqSfoBzVeZ!qxr3S+d2P?QAb|ej%7WW|`_doJZ~VhdLp4WWL&#esie;1Y+|xO(f+;P!k-NM;gs*65*^Qv?ut(5IyQCGjHtw* zWpvah8`CI0Fsl+ut*F5S^@ASn%x-PA=dJF9_6ij`^dN)sK3)pE{Y1KS;vJ`1r~SC? zkGX8{jKR{z2alBfJ!a082yY(h=J#ylPh>2%xg!K8a+XpLzK+}W54>Mob}W82O`@Nj z)UJ`v;Iv^m1YQ_PKu(Ze0a~T!Nn)B+vYS^xE`ditqMN77HfeQU{QAa#=cl2|C`gGf zmZzM2)pE>nwuK^(p!H>j%NaYZ=8fsTChP|vhI%`{yl4AmW8QXwyf?AxL}_w4+LWxB zjA@7Mw+ocU{dcUBwbY}&6$ZT;b~fW}JIz*$x;q*vn989r`P>J^LKN53nYkR-h(g=F2Ag6kj5L+3t~)6FPxl&!x)ud0;XJ zxOC>wd*_)tOsLgAs?F;2M&FA~qYBgvc(fQ6+nG{%xb8n@t|w6D`9t&iZQ~qFJJwyi zUR$p15%goxaQX#ej5g-|k4Prw)bs~S3LDkpULEFFzy-4C(fbRx(@I9u&?7cwN8YdX z12XL!FC`@4{H#~NPe}2S=}V7mny$Z`(V+-?$-pq>hVY`(c=z!s&Ryo;!~HmC6-4bm zNGbo3j28#Qyw4=?jxGzTyx@s}#lz<2In74VKWDfiN2m3dypM>eDU1TTK+BB~j&~Il zPVQblg6VIA$30s@IA42nh9{#f_DA|L zM**QR)^^tV%%$&q#XkN)JyqFRqtoz+U}g$-Vr8q7&`+Uk6i)PB^GGHhc&I)b)3@4E zFj8Xezh)441@wCB_doC7@!VVLb9`M5(c{|xx}}uE9^+}_?4y?|b%z@}dsKys2p52O zGkDqfM=g;VGUL9j2=eSU+1jpB%mfN$QAva2+(+P=JI)@%X?FHwiQQyUH6G-=ZHGdm zJs!$zM&y6c-Q<2jEeU!1nB&cOxBAa5PqH}Dj}zcA%DNRC!`He%ovjhAfE&Uql(b#q zaJY43abFHg&YhA(3O#%qzp*>dbzTvzIL(-|HDx~*rpR7Q?%&|?(wzeJX*_sISDx!J zPe<}jv#bmg2i+A=5E;tF;3d1VqhDE0R}Lks z$kdL`mmZ@}x%Dz;qr+M1ZxFuKpw<#yu^keY+iiKl<*ws!Uh1LWMcQyg&mF#D{MJ>_IkXc64MoOHVa+5+B1S3-L-9RQjQ)SXw%AN5m6`%f0Xc*3()5haW~*9IZqM3O%2nH ztUIARs$|mKGpjqk6W(UZQ?uk9Jt(xvqTfsI=9iVM=Q!h*EZ8IoSHda{s!nXNs;T}F zC`y~%(s=snZ5S$9D^5ni-f?TOB5J4qqD(COG%vzti6IHAl|j$GFvHz4JtIsWobuh8 zA8tlh&*Ky+eZi799@rB6iXbHrx#+pxk{&1f(aHV9HSlmCm-Wo3I{ku=bD#$u`h?l}=fLF~Ya5m0%(|mL;@a)5U-Aq>u3^qlOas=K#dS>E`|8&l7}A(2gTY@7v?BghB?)6bBpxY8 zTB-H-jTp<&M{BjZ10<+upGCA%Mxb)wUuz)oXuL(*KHXRn7|WGk3?@)UH6uHG`A{(Xr0+T9zOvh#U^)rZ6?jm(PS zll!Wc$OHDO(ppW?N|P_3_v7Yz08q8*#pa(&H)nTwct~q~hvJW3dUQ-!Fi+k0&WM_59wFZjE>qU9viil;d zoTlv{D);U1@&vg=X9l~($6?M3tuq!j^FQA|@l)(3*KnZx?31fDV5rGGn)UF_=jb9M zvaZF~4?8}Tm9%5;eT;!8cru zeFPWD8WeNdYD-wyLvQZyk`yk{Vry?RpYriEqk`ZSI>EaQ%)aO2-u2!w_w)Xk6bLKE z%s;_ZP%@XNgz)c5JpLpw1=pc%(|Yy*+;nb0hY7!Xd+?iG+MDmx!#2F5_$}pvy?w&W zcd4ZP4dtCu2rIK*bmPEVAv!{+2E22UbiZ-!yyyzhA^D1$Jx{rVKU7`s_O}$g`@Rpi zvF!BCej?^}YCRdU(L}ior+AZ%w~9Dd=^kmXmumCvAg*@a;~S)VcGz($O9_e#(QqnF z<|ERQ^%PJ4+K>t+QL9C3Y?X?`5dK^*eEpD7fd*cyRWi+e1#}ZJP$fsP^@yPNRN2^t z+*t4{^vk0>C-58jHJW2TjJjoJuW~HIbhNiNOa8PF#2ln&#|<&3)ZM|_Q7@$7 zdNtK)1#vI5$TCb&hN^40x6hyf!>k$jorHuI-)2S3or7B;M!t1~;v?y--Xuk-3WU>< zEGP4mdWJ)sQMkMD&rpBu0e=mLD5R++DczfU4b&SERbSrmB*Y(XQ>_k>yA}Du?&~nj zjLx3YoTJHWgY{z@*ova$IiHuhUI976``4pr=xz|MXG-)6iu~F^t;yC<4F*0R(p$Z+ zGpdtvw31aY=8Q5J%1z-)7V3~$EBiE%Il|PiuW;f7I}gTu-UM~1{}=_BT$u3I%*|FN z%eA*B%oY}(Z>qN*8d#%A=2S24ev3bRI$hX_+qRaFCcPuU;haal^5sl;UWE?JNjQ)- zYW0%?ChfpZud6rh<9K^?SUeh|7K<9$&7X8TdZ%rurF1$Qzn7F^T~NpF11o%?AQthx zAu~9|HG;_`swrP#Pe3~lg__gkBHFcc>6&QlK8f7hGyj2mVjx4TAwDP{@}ZIqJv;9M zuaRV8%sYLqIDVjqjT>eIAx~+Tmq(mCKTTCOEczhSa#*fcoxTiYh)iI&W67@|LCx4o zG2-};sS0Xu7gX|`C733q=iqA0aw}w<3vIF|RPSXRPmrI;CRyfgyn+kJGcpm8a|gcT zfJPtk$(qXUk<=Qy6J0PLqKXOae(9jqA{-M~4UF$MmINd{7Iyhf;W-H=z>gj)Ltu5X ziX^KAddP=k1>p!4uo}FsMO=z|=CNI2)iXuFDxhL;%H8Hz;}1p5>=M!KH1@Euys9<( zok!-<-LIJQ@y~4r*`CWBlyXm^%1;920r*7&<)+DxJ&LL#n}I8!LAead!s1qkt*Ts{ zF5i2?f+>%(Z7+Fuo8KV<%~nql5l_rUM4?^AzM+}@m&|=V%sM?;n9r$b##kt`q(b1`_t@oR=jZ>H2i|}A+9+L ztVp?z38uV=x{qoa6kib?XzSuJa9UHYkZ2bTd5+D~dHuvdXeRkWm0Lk*;1zp3L4G#- zaK_L4P;p{L%eSpUW4!xFo!Ow5dkRx9|1vg5zFUZY8o%(ut|i$fn=7w6)w&%~!sg91 zHxo+hVBd$Fr#nxv&P|o;8cynbEEHh!j=#x!)8H{1d-SeZ-dm{a_&6BG#|`!>+bQ8< zlB#X&DoR}IK6^gCZoqYk>>YfV_)@w(^uy(cxV~?(Rx`U1dG3B0)Z`L-wstw&&g~6M z{u7Uq1+!!yMNWJhCMHj0x(V+W^rqJ_x3h7AT~l)FCN{l8;4=D%X4j#lT=2_4$tqhM^8O}Qko>PlcsXlDy)wbq!HzLF`1+b`1 zucZ#lcc&~Z=%!N1`=dOVRQK*z5_5$iB>M?}J4&=eA?h}`J?9olXh|*K~`6C(w%X(+zO=(lTEVrpQ^>;AdEiBcUb2WCq4%`|ifj{=dU=^BRw5vNuBV2heCOLaHC~&6iHXo}1w! z+=APfa^HX45V`@F#G(ZTW*6{lEy{1lubFWB60k`y)GKN5Gw(DRN@ zK42v3$2mu{gK$1WEfYubaU^T7?h~o9nvb)Y9oY}6h}NEcF_y#aKZtw1?~x-ER(lye zeNrR6E%!3;D@EvaXyS0-gLe}8ukIh1@4g{sZxdomW4+XpS?&vaV4tSpJnwv@LOT_e z^Dt~ABb21Ml(`toUb^Mq3*9B<4RRtBoW_A8IF$~G`w6v>z1<45=gvDI9dR zR`?C+u(@ubSbuZcY3AN66E8xq{k$H5*<8qJ(^3}gxa_Wb7{cTfn;#q((I)c%e|T!d zLNE3CyJNobl{dq+~hGF676;9XL2#6uCrMXscf3Fh{k5#d9w>5mNxY8?*z59n%FqzmlOZ<06ZBA%uyt4U+o*{^^J`adS$d+T$5+eBQNsuYv{NK7bS*4dZ2 zMS&Q|aQRVN=r@U}Uy}wc5xKr~d~V}8-I#LnN9K7sa=RzXi-|-6Vs5NZkvr!5>Fb3S zYG~MH^esosg=9->ik+LftIS|hC7gfv7SX5f%4f5Gii!crFYc*#E5cvDTW2M|UG$_h z{1fkSofd3?!Vse_=rQe*$s?xxOMeKaDcRUdsm_NQkC`WVmM{Uo&tAN))}p#Q1a-kw z6Yx!|R*K-Xo*beIHPT1yQnN>34$Cw57)tlNh#1tdf#!#SD!BwPWd54`7dcnJCrnSY z$^jl;UIBLS&E_MOn-H+)&?;zihV1;yF0rCu>O>hdW>uzq{6*q$lTYX%u4O6Lb$xeV zb1VPZ#1&u!-+EIW{@w7U?EIY-%PU|V=MsFMuP6YUH$_1?$YrWx#Jspfv9@H~xQYn(q-Aw5R%QyU?`yUeR`TvWi9hNt`-DNSz8$To z|2-eBOzE~G;4hX70a72ry? zH&@EldW_pjCcDDl8s#o$f6%gZ;`Dfenz6zuoum4X2M4T4*^f+ci3;vDd1zwnciWjj zMw)uV4AsEMy=1zGQ`Tt1YrWZ;B#FC(%TVWN5MVxei89 zJ6q~I^6RAy#QgXLG>J#;r!^Js@>-wA-VnogueH#6*z2U_K|vYDp3FV|iM1|)n$E~% zzb}#WyLDQ;Gx-!dt>vdxG1hPA&+7_%X_)GYlfLx{jJ--d|C~z1et1Z2aI!A^dl&t=aG9od?4kn|YJ)G5mb5c)V=O_) zwYM)e&2!cL^d@q4+76sJu#z{fI-7>UroLL6bgmVUoVgQjh4A;dK`ODPeltkWjf5sw zc4eab;RTk9SRAEx1?;aY=@C3s2LJY7UR?_-bRo1A>jeG$E^2IBtfa$D(>5*bO9< zG=?|!As{MdJ7B%-?w*(4eY~8nvp2_ZNkqtQN9V@ogPgbct>mki5~4$MIsH8Z{ccK2 zZeQ8smruv{Aw#(WAFJj48Q65f^|>R$tYGP&iI~E6Z&dDyN%>r_)A8nOt;07bXlHma zNj;I)UW&@6n$3N(57?nWi9y0Za;~C#My!NSRww$`BPtWyL^r{3Y+fGU^&94S;kTW% zgKj~UlFJGvTGGNr&@nf4RQsPxpN4YLL!ZkSsD9qib;Z@eGU%u2_QBsHlgGRrH+o+FM?j&Rw+d9>a-iotw1GK&>9$b5hLKEOe>RKb^EdK^ zGOkoF+GZr<=PSdCLa8jSIR0O|j@M+e$cWbK2pv6|F$98uhIU+gexT3MP650`cz~*5 zOp>-Rx2*KtVb*sm{$5O>$E>ENdz;*I?JKTLsEtlw99bVR1Q$t`C4{|I^=_&m>rJj? zF|o&AT9k8h>JU?*dSO0Z9HAbt?PJ%hE#N?bxBJ=w#!Jl&tct;279=FGN<_0laCX$S z{56G^29oS(wAOb8nP28KwjR@8ReW@`X8lib)elGq13t|T#qu5q`@CW2np%lTo%rI& zC(CxXU@h23A=9R-o{6O3dEAZda2j@{;Gbvt58;UHQ3(+&GHjGdub#gR_U9 z$=cH0o7{0Mmg91RQs@xQB_F7*JTui&P5N;}+^CKsp`N!8H;k|4a}Y^~Ka&1kc1m;K zaeTnc4>ye~pl^ua;ko)z9(nG!drY@3>X-C$!Z#3_TlCs3A2n-^>Yce#uYdzIaG4fS z*3;s%&ny9SjiK!y3kH~zK+Fh^RKMLkRIRilgB^Ia!?NiWc!xF0&u1gjc^qxt#;TO= znI;uBw0Xz1muQ>kOA)C(KF;oCb{|#?T`*tx^1!^wB6Tw{`pAcVNOm! z-?NcK;?3Oj1U(*(y#|&y&y`9%R4vH4CVwFhHUe>rb}iY6rHs!l+cGCOYo9R7nf0u- zwjmD{N!sd-+l5)-iRoHpW$EUDlce6vnSFQQH`ts@k|yAHnbX#LBUHU0DfDe6$%8aY z8B5_5%5lP+ZNym(2dV>3d|ayXp`mk?JGV$;V~w3zR8|Rk-L~)bx^H9Vna?VAisn=9 z_XM2350>%efUYZjqAOe%D48;9o+2Owwkxy<1*+Tlcj0!FHLjTFZ^zAhualNfrhp9} zm-HSGB0Hw|x8HU65GfoO$v6u3&+#@!r&vf#nD=lN6Mft{t*F@^$8)+f{LQ_dbh07* z@&iqC8!2`pZQGGA67kO~IJ7}HOe9?Lcqx$C5RO=wsJ%xnsJv5yA)EecMX+U=ZO)h^ z84{~-E?N7zrs`MHfF0E{m@+q(Ylp61Q}|}UJ8qJ?Qg5rT-bvdL@mGMvSghP*!4 ze^AenCX$c7fAnoz5o(3g8)IvVUm#M^{0WZX*CsdA}o#^!F=`R=h?O zkz>gc5-XoGM|XUR4B)HBlzqzj!M09!lo7|pC-F{gwv0Z5*11sMuD|s%mTB@&|1UlU zNM;X3G0aJ6Ds0=pT)zsat{_&Lcz$6(&>(}q;7=B6WAyXac|36|u1qfJK(%;0YuO@& ze_Lwg`p5waxfw&f5pLh>e`kqu4tiYinefMvDtLaQv(}rBVW3|rWN4WOka0a9H+iV4 z^0WEZi2fEs=vE^%WHQkU9TH!?r2CUOB=&kh3O`%;(hKgLi{dfaLz@utw! z*Y8;$P*(cQq6r z4pk!@*jCkSZHA3|8ftJ}_9~*orpubP)SSnP7Y6#xvLUwjVyHaUFe6Ku!?d1#re6$6 zl11nrA3v9g?+uiLz^M|G@+n3vJb}lN@Mb=_xlYvG93?PJNB_pr!#{Ylv8*k0%4OK@Dqgq_aQ7E8-*RUnXMYyi-bHf ze|bl+?nx`Mz4)_6fAR{gKONmqN;#ome21zjcR%UN&MTGKh-f7yLea&r1R$i7|&_V>s-@ zoHA^{JL^&tG~!3v2e{}pIi4S`(rPD}Oy1sGlzK0h2nV=|MNr!$sSDSzV}`GhQ(8x? zqz=U8u0L2>z5T9v*n==MJDua5tLPJ`>B;8;W_$%Yo*MuMK z$B%j1AFS_bmbJh94b*F!Z*R#~b3-n%Fr88F8s5@%9FDb_&%ZK{a~l=hJA#DTv?dzw z#R5aE_Rfra!->g{ZWz|M`}r&}+9mWl=Jm{S!)BO@qU?PwVuy{-ow?Kpx++FWbUbr{ zxQ9e77vlc_pTYYCCbhJx!mSG_Y1e=AhAimD_|F5Lw!f9_i{Vuf7OC-FoXMejd+&R9 z+BF3Aoi(k@Jil>X3SJtT4TrPqPr%-!rY|x^;a(`^Op5Zw(JYdDxG?-v@pAp{T*K&N z3hAR6Xqc{Qe`9Q1Ya`aqD#NJ zWb7z_cb_)#1C+JVZDNxpt{(g?ZhcAYPksPLk!(GU@)M@UFBR>vGjgX^PYRe~ zNDXX%ZG1-x4rjXa(+M4CADmkBtsT(e@GuFy46LV+w4F@$GUJKEx1J+TLf0W=Z6hKqzglI6Ie4NQP9bhB-~iw*4T_ z#YZqF+`wC|{~erZ)?*6)mdtxE5pUnd4L_${@U5qzU%$vDc_&)9VH&3@0!j%6q?9uT zo6!Z-*0L|_WvZdzV}I&!Y6Sx%#b1A<+#e-p$?uU%cbC8Adb5|P_P&Dn*gn)F_vMam z_jWq&>-X`cJ+cRa#D73F+{K}`+xGMiXR7hHS2^$^Qs`l9T0|RoJ}LDzx%jwqjSMdO zO?2baI@xWwtmZ6X8kj1v>i!fhJ7j-@b{h8l`$FhaQtj=O)aG|1=pXR5E=M3&BuJO* z3_|4S>gdbg+5s2pE>^#9PJPhng8>6$7rwfdq`iBA1oDz;b4=F#e7^Gq9U#?!EyX7F z@Hwypa$C#A^8N@5vk}(iH5r<}pNF_6YE*)r@sb?Gw#g zf8@_heAsYkr(Q##Rv*3{NC{%xG8>+JqQs!NeHz0O+8LvqdC{~3x zr|*#Jta+tOpB& znBm|vb129RQ=F=14g+i8c!mio1`|iH;l;r}faIyp+LJ%Cs!#gw{vEz`W`E;k^l>DM z98}CBfetkj*mYZPhZt< zbK9OsydqMhi^WWGX!Q1U-mlksb?)nCI0T*=3YlA64d#ya>~>+^4Eh?w)lS-JKM)9- zy2d^%B;!fW!Fq0Qv_~?!y*{M&40_>8Yx;vEHBt-=7xTDZOvqcgW-ZA2>Ff6K zcd+NWv?Y%Y{GY!ytdX$_c8`f`*lL}cGP>(71es%8WLI@~4Mt06c#;(^6eJRB!yY3< z4Ij$6h~JBkBpj*;Wb`{(4CmBjfB?LoKMz0`FHM!c3Do9>?|tF#qu%^BmmtjjH0L** zeXdQ&`wexjXW28E3{n2?cd+4=#BR|Wo4 z1zT0OZza#~g(WMHHa92b@8>)b|KLAxfm*i@vy$GKA2NC#VVOwb)|GdSd&?R2ZP-VJ zm>Rd^k>dj18-A7{dv6Ulm9EH82MRTPQi&0~xwR%R4D5mg?4tt3<!qjyXk1Tay5gcaVn`v{HxXkB;w`Ps285IT%ber(tG&6tD@=4wqQeg?H? z3w3ActdTATVtoH<0a@hz$ne>Jsu3&%nK0l{=~(Y%9ihm3sb1xeaa)kI0+D9-M(!6B zHO07vq>h2~BX33tN}+5(wkBm*@ab0AHvV(}0ywM-och}YR51GwaOND;3&E%XBafWa z*o-9a!l}(kmDU6P^y*o~nI%K{5?UIbexMcmb^xUDVt)0zWb264aEs##WAArIIsaUr zO-wCsAGzkl4cq@Hz+*<1Fh0rtI)tE8d{fEd?58t|Hb}aCDkFkjV>oCaeib~tDJr>9 zx&3{~&G*xBCP)+YG%Nh=mVVBLxHZ0vYhy?^gF z#lByo|1PbLT~l%=-X2h%l_c7qJM#iFS+xEVbsGNB*g*kCX{omfKYTf7*iKAat)hT= zNhV<(SsCiH%-0x@8b!{-Evvz^LCe?nP@++qEd`)*P~2dL`L(v%|81K;%wqM~mwrf@ z!ItO6J9fnwxw}Gk3Zw?$GezAaYcY%KoVTN0M{oTNa4^%q{R(WHcbrnis)}(_I`R%5 zJFwsdb(wJ`^W?r7U7+njJ4Xeeu8DnxZe=hpyn-J#FMGu5G$ z{Rmy#vqMB_+zf8aX%8k znwwyAQZPo^k@S7os7>2@Fm*A_ z!H*yIW21aZeO==&K_|GvgYkzQWVCMO8;S?dp*#UI&a2weJD$c9vRlZ$!=V(Co&mxM z{i|~;OK25ev@vVDB0EEvv8PkVv91PPSeem;V!Vzi(wew?);6DfaK*Zgk#?_*cB&sT zjrh3UyhdG{OfQnMZ6@SNX|gLsoI4HEWvN^_tuV!}eG(?hzui4_6_Mox0*zQ-o5xUCFH9z|(Kb z@8cpQpefq;5Eo|Ma>aWY9~{SeeCGYRl!IE4i%h9igETa0Hk%Xl08MnDR3-rU=*GHI#WXV z?l>K%4Te`n{zmtalvjdt@^tR&%D1rLBhA?OZ3XKPxY&vuY<;bHY5L zY=0g*zP(&*(oTUsbgQo0&XbdvyAU9>gr$LIxgDbCr& zLYKxYjLa{pCxiD6=OccV3U&+*C#iJ&nhCf53+JF-uy}B(`uy*&2#H9hBVkp+e}G)M z$T~V`tPn$fad&?d#|EELRjU_%&B%S8F_V1$h(|)xpzQv$&Mk6|+qc{T(?z)K z%9$&6# z@WVcmqtE9)kGoPN1(BN*Bn^{2xIgz8i#$qLz)h_iO8P#zQFjQg$|_M}SjGHxNr3u% zKN{KQ(cyggX7J5hslOcU6!r<{!4f1psFZv63*QE}+c0!}(0`uzv->ZHrEpYpfcrC2 zsqEyDb^>Oln&b*-FMdnlAi2 zL@I7s0!loejbE_t&xN@z%1ZFc7TmNH&lZw4KX@FXzIBDp6LjBYBKq8KP5FJOWwBBN za&W+vT^z@bqb}Y@QD5wN{5_3dqukuI@6&wq8XGE$_2aka*zdOu=g@s;vt@+n&0$L= z)eq8H9%<}=b1pBe4)9dhvpM>2|7OQirJp!(?cNwckK)_c&&{XyYk2%A1NUV;)M;Ny zl=)s(r-)v&i z;9V2oT0gqyWj_{yKMC0{58Wxul zQ`!bd(IOt9&iV8Y5NnzQ#v|X8r96YhtzPPqi>su;IXJa?Jmc62EU7(0Ncsi1dE+A` z)tn6NDp1iPdYJbT901L~1eCTK85wm<>a}4i_*x4$kdzfm=TJ+5w$PnuNy5(mBGP|1 zq#Q~P6&I)mvh$_s=iPCMg&(gd`cjk@jWbArc$vwGvE&dzExuxJ@75D&hghu@ZdAQ# zePneX9Zv6&Ds^t6RG@1|?cA^rR8E;dH6%KTG2LiPx-D0d6!m5HNzZ(;&!u&LV~}Lt z54ouJGzfY{-XxuBmq<;UZ3Ap(SOA`NnueuwwH-uGf((?LB+jFie;7)C`d%DPa!;sX z`UHwyU7#6j=OLDRAw3y8ea6o7=fw#$#a)*4@{KvxdSl!BjKx%p`wTU@DRbD~H_}4S zVnQd)h+PY7T6S2?LeFuFMqOaS!VJ9s0hGsGh(B1#9NH-R{9Z^I|%7!TRqA1>O`{iv7s2wv-_w;sRt_V!k#q0YH?KyUS zGw!I)e znxWqpakQ?%d4n(Hz$Ji1dRDmt(bwmf{G&w)M>C9|kw*A#WeAf!R|tTjM6#M$UPx#^ znz_`z#`-P@mqVS>6_{4hD=Ug&A($|ykqwg;AAm7b{czt=VDI*)OYn#mGr8iuBwzfx z?9MRw=Xzb=y(Sp~;9*__hkRCC2o)?dA1(04 zkRo~1dy5D%b;NLq^pnpF|4EXU&!&1lsnFuC_7-gKYpp#(k^Gvw5EPTR7p#JPh%6g4 zVrSJ}0e@p7pss7(Fo7q}5{{u7U9Up|%#`nO!|w(|)ynWC-ky8p67p7F8y2jzhz)g$ zGr1EZAG<{mwPX8I=1=w|Pe_hq{UeSNHL30FWoZh39Jixc`>C*K`T1SFnq1;kuexm1 zEC|IJf-KSAAVY4d5i}V${fyGS>EBQ93M##VU83o&rIBtNJcahqu>L^E@Sz1ozX%#Y7sTwjSd5E;5>O zj&-E>(A<5#OHI(_RrlEVL1Ela%*U*f=IODJzjwEFH8@haXum}C%a3#?G?P&M+E;Zk zTy5?~YecKSYopcbj`FP?u{(RZUC4YP{i^v2C{OL?#!BCl$6APZl%gEzYl;IiBe$Eh zdpv)Fl5Th5t)|rGCfkD+=IV z-QgkHHHaHnldUQvH8cF&es_G;F!JZf#kK%w9Y%xRqOXtp2@U(bS*hz z+=5qjx_ghBJ9Qx6dRKNRo7d5FgUID3%nbST30&(>mFJ9snyM4JvOro9dngoTI9qDZ8k{>Zt5mjl|GxCGW?nmlD#^m>O zm&A|Bg{<=<3W;haq2aU$VTXN_cPDHc;Ey0$R%}%@<$Uor2<-giQnXJm1O3t#bz~?U$U;w z66dG)$tDS(bD4Bcgt$k)8o((x(wrNlCKxOqj$};4gAcq_Wt4m&`A3M#?Uu(4Eo1VJ z3FsJupuvN_{1Y;pM?&(sNpG@XRmTt{>#?+>_UhnA&vg3`TAVcuLJVr9STs*|uE~P- zWE}vWe`)H#ORlKiA#%(6<>tw4AZ>87ks<-@JjtfIBbL9A5e#c64!B$WOf}!sW66P%%2Y5PO9}8ZY zP&kRDC?(q2vc|v=`Q_k)r}rLil4^6e<_-I+AfatkQ5WE}gtL%$5?6L6C9U?IOASrf zOwOT-=N{wn{jpuc;t~Q?{5OfTdan=NYg5y!z_rglY~u?!M^h4B;YgeAP3Na@7b^8S zpq3E-08&tsvx1S)O4CVNw%tOaW+Pp4rSkvS<$=FVkP-chpy<2M zSs>DEXA!_zW&%(!s4)Z0NninU;zW*cVF7Y}QDS5Ov&Z8EA)8e z$J}HU&bWp?Mk(Pqk){7Hbj!0T4)ca}i|ZSaF`&AyCBFY5W-&F5FZL%{i_M+15miLj z7Y!q~5cJ-x{Xz;x?SUnss-YTceR|7prESIV}uMZX7O8vA7QijVQi ze?#lO@?uqA&YQ|!RdZgl=?lCUG@n#NKhM@td%!+6dp9Yi3y&0m({#Rw|3>;s7V>kB zKWpgrSmPIe)dPCJRxp$qTBq}L@NlRJBqw<94e+ag-NdNvo ziF)PSt7mOJ{Q^j|-fq%)Ez~*>u-px*qV7@{reDsSDS!2W=hxx})aE8ii1at=GT61*& z-Z;+ZT@+iq#!7v5l1~!ziYT;w@FZLRi_8@ini59NGb-^=rT!nFF2ToVhCI-0q%$zJ z?HF=I%8Gd&R&c9DGoC0}pxKjbI-NS*xuI+z@f)6aaGzJTxHPTt%7m!p4b1z|a)Kc; zEWVk7AK}7zMQ~%n&(cC@Hv2(%?+35P2)@zy7tr_G>$(9dqrj_=b!*R>(PFs^n5~Ux z5S6LLbE@R*zP{)3HU6CLZ%Td2D60oJFEbfASNn1H{J}x(C^Lt>%EgFSy`tE@K!uLX zXo)!56kZap`0V0i=sOEMy54`k992lCXvgW}NTy4=D{E8iJN?;)n@1)nZucCjuwuNIh|9&UO=Z$&Otmj)Lg7a%7Z)+N#h|sFc*oih2VPqorwc4(uWCs23G(J@8%2cYO8dWNt zNu&@41<#k`XpYulC7#xiu>)1H!%VYVUL#+jA6Pg1{8#s?4W8P$i}ZUp=kDy#fKQrD zgVW+B>FZA*6`ZhpEwgnBMKe3X@*%UgKFvSm8@DRBI@6Hfg>uEgJ%Gc`4u}Sr}Gf)*SmC&^I`GJ%|LLN-Vlsr9+tY z+4z3T%I}%d_Wp5oSePzle7hg-GJW;Hk4S>57JaMDb|lEZo{Ken6WhB%A0V}py+t40 zG^_D8+ADB?QOSX=)VELe#>i2PSzR2Y=aydKN{OyiC+CH-MB$3iOi@U)MGO-L{r(KX z3&emk!K%U=NaHc0AGw)~QAXBSq;V)%T$g06G6ZZB4G)IImnwh^kByN3@&1#8_;QKV zDNII#If_t)RB;n@f2tk|%(r4)k~@5yz0_MDl@@-l{)0L2Ub~BepQ>0wFi1)J)^GU$ ztnC;=c`8JpC#xx-H$UcH68{U#wyNR`Z9c97C<4f2mwL3-&xv*Cjtx5c%>hw~ZJGeP zq|f6G%~8C`Zb^+7F|5vRdLQ!%H0}4Hv$4iwVHWMG*kwnikXv;W)loD0eV+us7Ua@) zGd6s5@fxr5dpqqp9tzg$0gyNGw^MyG#|98=SKeHtXUsCUxl3;KV~kx`5LBm&!i^(U z`St}4x7hrw#e7wdcHVvOGH`3}d+kg5`L$93T)1T`qvor%&gmYtl-?oBzM<5@UrUt-0^J}EsBkLD(S`W3y{lX=G?Mi5PhGLkm?o`?JRXzS8= zX}8_9CI_EKbVrU~b>icdrO+_6{XjTvpkmly|LwmE`m9mdZsoNeU@b?lh9quX`&0btTw4x0=@dp%Z;dbcr)vb)?A3wP*wW61Xn) z@vX(@@32mO33powEy);yHk?_P+!#)245tysMd708AKq!fpAXnb^V=Kue zS`4k0fXR`$))dh?qS-;|&iH$5s2aB&%l9v%nw%!z^@|9To^6hL zOpA(C2Q(RD0%hQY;)DYkdL=emtHUB$1e(l0zYtb)VDXapVL%lbI)V*&qyEFj<~L`* zu?SA1ktI;Y=&of;3F^m~cr{I6j#2^dB*@OPf6|??zs!|v14%{I-u2wzERDBmJTF`c z98XkE>DU89M zcC|&){wJI4y^G`Tx=JkY_Z(>Cv{OCZ(%wLPWl@>`0HIlTG40O>XF0U&JdrExi|@0j zTn9laYxun*b4hage}Ld}0?d|+C#(Wo9~1H12%KF(*n5_Efl_16$T=<6FC!DINNU4jgFCaVf)-5MxlFuRF-?McEOp7mO zOZy25`YrK4}(&iJu>QodsRIGW8Hc38H3EHYC$&+dFC-EwZknEuNHJC^{Ay>k1@Zy6? zy&U-3NaTnZa@)?3KPQ`Vfe|(gB5IqEj>b*eY9YV7twftB!9Ua_4h!TbX%n1=K&`8n zh|NW}xck~go|l1;vtIb(6xp+}N7_@#11IFt5A;aay*85Fg%&~WKhRd=4~A?WxA|Gs z*}4@BV*Q-cmv)w4=y(T-%OFVOITk4Q8#IVMRjVsb^K-1kW_1+3xT=122fXafFiVPG zATh{JjhAum_m5hWg~o#$M&{GEoiwQ^Bmmb&K6UKaU$mX+@Kb1@8nl*d*wuXm>_W3# zM~=wa9`dgc58n^mrx)!yI5%RKtOLPCHb4XnonSY-Pe^m+J+dk00DBVwiIFK363>DC z*w66c1ZcL!#}VdXpv7Q4l6<$)W)`HUD}44J;u~1;X_4p|Jg5}hSmJsKMjImp$OSvd zX}G=vq8Gr@k*mNs5O1D4(%({xNeL{w%j6zan?!!s91dk71moS!WD*eRXWaNa3>3!y z-@Wml>|^~H;vJda(D67)VSaSTHA^<7haY51%`iKAC}_U2iDMVl5-md4#2tS75eG|@ zcWp}xw0hZ4!U415)KfLrg4$_Opx;HL8sHVudkpxk;NHrMpP=l4lzRvn$Tb>~oJ;4r zqkdwm7(Dc>Y>?3WjjQZ>5C^HKZ*Fv0bfip$&_+72I@}qKLxl$DhTJw#J+Qx_m((y= z$-`sfCfS8PK}}illn&O9egR!~i~@Wd+~yEgT;jHGIF&yty|4!9thEkEFYQa0b2y;* z^k8=;SPth67L+V!%`~_ZIravz;zMpWki0uB)*8jUWrLF0+YZds*Y(0DAO)m$Tq2mJ zjS@2D@|$>t?BOL?x7SxfsDgNp#D>pWvdf38^+S9DNDjh9X2v+P#M40k@eIE8MDUE4 z)Cwc5vuR%U6~8d1mVrp}*B1Hld^u^F)UA_zo2D?SV{~@z63suMHwCTcAU1-q}5tUIL{(_{8l0`SdNhd=){rcD_Z`J zOV3Y^_rC>B55{Y9&e{XfG9F)OE4Z1E`>|A&ThX2_T=)Z6Q6bRFKLRf?z|mM3(fD@Y z`4L4`>azjT*G}N*&ozw`P1<|*7Ogc|;P~Xr*3lX@{AYRmogdLVhMGjOfzGPs9u18g z+E>R=X9wj(Y?mF2f>Ur?+Q-lPXO|e81a6=&HM~Rk18XRndru&YF=R>$FbNH zS$bJ=juq)7%IfGv@(ZwOk@pr$P{UVGT7okMZneQ52u-(SCkPAKFpeE$)GkxZt??@1 zov1Agrot!$lQhdh4;7vTK(qoOCL%<-~eoGhKPQ8wLw{H1bo)jfb+=`f8rbLTa9{&w8-Wl6yKn9h4R=M}AF z5Oe@kyP!AZ;cPdc6qe!3AwX~Q1b_k-cA>pat@s^syP`3Hz;_AEtI7QRJ9Q9nWv~Zs z+DJtVTmYmG;-Rhl&eGUEas_^hS^PK+sfS8EE#%4=#7h0{eA22~NnK4d_wf|+S@efS zK#tXZVO1-CUOoL*;{sO$f2;8RejB4F)_APRd=`+%;cLoOpG=DP+eg`&Dp^$GLKz!- z1czx6^HrD@Z;9XO>S&H39gUSJezInn4zoA>fXhJUeoqfl>I%4Wv`|6OFn>(ayl0>h zIEs8i&nuuXd9fpuONHJewo0tAId6+{oXNv`fr*{Z-kRn?&al;f08RWDX-LVJmwX<0 z34a{p_?Nx@OwQ?agVW=?31U-7x%w@c&hq6G$jTJTe&1+Ynrr?{nDN~1(&X*>L_rZH zFPQ=Dvj8bY@b2>yJJwhXuJ;3i3v2eN(0GWs7V`3rOB>?yW8JzoteKel{;HLNX&ZyH4u+4I}3zdmxQTPaH^WCe*KHV6d#u0mn+LY=}nkWzO{@V7C2(T7=Icz z%g1Whm?S?gB`Obs1XYsQ1(?=EAZYAcl?}}R;T4h)H?HrTBZJEeabA2pSX z5VFf8cxy@15L6az5bJjcl4f%iT}lOk1+>7lizCQj=RY;pe}ftzf-tblZ|!N?9s;Fk ztM2wK3ss#_w=`w1b}iDbW<|6}`Y{Zq_;%o_+Rnmtjs_2l^T3~(CAaEHLRfrPa#}4> zIZy7CDgyYn{Q8kz*fKx2X~Mcn9S^aybf8i-i>YrO0Zpq`+bm(@P%a)kizhnfmC(^R zaHbDUgo>wqe2RsrX#X-$2y6d3zmFAefwNs2b&C|Tvhqq z)^C9Ll=o<({s2b*B;|jAMM-n{(~b=$7Qtk+Tj4GW?B{x2oIQckcZ>o+g(j*P2DGC< zWksaR+RYw2C3N@d-2Jx^qN4df-6Wv=kM@4#Qk$}~5Xt@0z}MXhM_Z++(B2G=rShTh z@4^A~9IL_JOL6a>SNK2oHz4{-Z%D|5c+}fa&k5K!=8rHGA@gVTy#qD|GE7KlBdQXv zFx!Qo+~XxZeP5Uve)rr3TKOP_fHBD&nTI_La0wNi!ir=07`(34mTP4NeI-hqcF=GfAl??h*X!se1 zr7I$t?GrF1uiQR@!XWei0Mz`QY+3s_VB4*Ho=S~FogMl+`ego8(yt|}=1@#C0mEhZ z+J^wg-yl+xLV%ey`J|nZ-40T$*o0yt*{%xcDm)R2k!;O})4(V3M(k)#q{kx#;9RXQ z1yF>x1oXiUQ}7a&1>$;gsT1JO6fLfx)5AJ%W7)ZI}FA$S~dhBkb?EO7^xy9|?o3 zIKOsPeXH8hDd7%HfZbX)nz)H^n!c?@CQ>DFW*L`ZouCBXc;%97yWQ~W)B<_`$yRzi zgkzXw6UilHpxpSP*-tk<{>wr6(M!JHWMe%1T*^U)N}KpDl~Q667Ip_Nc^OZ;SSo`9gw%@7thsj@P(IJ-Bq8zK;rSmUDHwh&sby zk()DkZ?8Nn@pE*q87h-WBRY3|ujEm`WSS&9_5~bkA<+Y?tq+ zJ-9}p@sKT4e%4k9ULnlFHwX$CtF=fdS#Uwj_(7Xk6}k%MbMapX0b?=~n5q7Ekie}Q z1(9IH?Q)W%64osVnyTR!yDcpyXlZOffG@3Ezj74Wm@%;4${^J|U%U;n!vFIrkFK5{ z=gtJ=FM^2n&zEmH_wR*p5?CAR1A!5Ng-Ij#mlGD(LB`~YuSaLRPHa{)o}z5{4ctot zcu6>9A{>3B>RBFSj2R}kQXH{I%Bdwb`xy#>Yxz#Cn8MI`y~8Jn@|X>PK&N>KqUHL+@>pkZ3fa4r zLSeqC6g{P6q4C>#_I+9BhqFO^!J+PoSUZheu1_pVOymVD3eWujW6!JFc% z>Ra8ZQsm z3tUBoS&o&YN0YH#h1H=YotiyO-*hdVlPQ1NYC_D!(C?BpCM3|mkU7chuV=I)xx_Xi z>4t#=Vj6=w_H8ck2d_u>9ncI@KS6yA{XRtdbUfYSZipkA*R#&$P|I(jD6pK3zd80$ zkBe{M3S1k7pX7B*wphS9_IW$Kk1}*htE2AeFy8P!y<0Pm`B>lcA=L9mRF;-` z2W8911_-cHP0}>BNqwD&rvW!VbE~gUHClJ^tzSwej3{Ws#?sgPe|qeGRw!&DxKWEf zKek3aRyF+*#SZJX-hHlm=HK9q_TuMaof#xE4mj!1owEjE5j@hYb#J#aN{Ce~`NS4y zNYQ?FEi=M3@sq$tRBY~rc=NG+l#Hxl!UoDPmE`;>i<F814uu|8L*qfsXimP&EeF9=ZF8TD)w`zssL@k zp~WqRfga!=jkb!jvdCvdd(I!24J-qh$m&Th4Tp_!#PrTtUq6O$yFmwjMCpW1S9a~D zEh-wHLZ2nSlJhU#so$#4opE91eH0)+Ps}3eLtEks(t%3g z=0C;wY+FMJvY}NNoa^Oagr|RWZ@JYxhK_Ghn1rfN%jCNf1}WMq0#qm~dY{SgW9)W# zWaq&pSezxQ-W6(MZW{_;lT*IbtQ{VsrU+W8C%RDtO0Z5sK# zPnXfU19C&zdqYfY`iI(sM=yomfV1R{nk1f(Qsf${NGif6Fu0N1Y3V;A+^tttLnN$S^;}h3C7Gy|=h@C309gXd%pE z!S)k}+i<5Q7y0o!Ik34$NmgkKq{)Tq3%&XAZ4yYv*wc3k3-b(*pu<@;ZWZ5C9onGc zG|G!l__wlC41~6jJwri%a8?K*Hy&xC8TV6bZ#-QO@u)O7RCk;M?V7yq@qJ#G1*qqf>1vP>#pbueD0 zm;k3R1^SwPHdI$d+!C};HTtZB{(XsdOn2tip5jE>26!MIxT=hOJcqcRYB$E*T8r-I z>#go55(ymmnCQIE?n0uH;&4d(XIkfbk;| z4#HBcQY9mOdrS0{q0%`21iklfc{$1^*3a+e@K!gLssb~8yhg&DqCUJjV%iG{hzjCPP*HjyMJwA}{8Y(i z!Tp9MY+oQ=LZmm2j}w)*4oo-lnYsQN_Azb6SHh)X_S^cK8GRhdBhie0UJ_sV69bpC zgjz?RrI~TJ_%uGhC!6*?$8H?8km9bo0C{q|CSS%UsiHJ0tr5cFocaP{l=^u;?di+Rb zK9!gyqf;eeYT`w7B!fT3wSr@I5i^&N(4S4iWOm7YZb3c)0Vh^=O3yNp0ukAGSanF^;j z$`gi6{Wf?8ExJ#3U-g5YAOfLL8_zbQYemO-fj^(9Atu2sXvDf2~}KS zRO(u9#`X|&?tGQB&-RhDPbyR0PXAyk22ah;GMN=v%AI&-Ax(NancKwl0xJSst(YVM zt=*`4Ytp_?)^9|k&QpuXsB?&{QzyBld={sW-@VLLBLu&${FhLQHaU# zWL5rTfu5tLhD=I(_Q>uxRl6FdGc%R#Fr~>V=x65Fr(6D$I}kfohwsTzG9_2Ht!c!D zqN}#X?1*%8lAme)<&O%Z!6aO4ah9EX3GHTeJLd^f_9e)t*v!%Kb~}uu%-!f$L#pk4 z?;A|5ciu75@zfZ7>;9s7QAnd}8QjTS965dtEqyC3jsD5`@K4uGR*EtMp^i9j@`DuS zB`1be*)LA@md1GoHJYP+QoBOyAef4QS@&oNUejm0h8Zen$RWnCDNfLxFpm zs|T7vJ4T9}a{8VRSTAC6@pOVx&n;3dWJto3kLi?Cf~s({Nnf|nWU}HKZOJi|YqlhM zUR}?8Wd2n3+Xg@D?(QcJi+3gcpatIV>1npNceC-5S-!gS_(3uo-Ju@B=)F5iZclFH zaO>Vra9C^gKrfFd@^X-BIBv@C@D6u1==tC~;am;Ex$JZd9m$b}QHleuEiC1m zXQ7k`jA;0{6$hOqk1q7Rn9*vz!v{_~zQ1|lI?zOK4$&y^H6zxR`RP7hf~RrtyE-!L zN$R9w(vA_gTo2(zw9aD|yi`iG9%j7$B9km>=l?!#=9Hi8W4R?OV6Va>%IP@PP?9~s z;4gd7EMRaj_M`TMG3fo^&Ik=uuGI3f_s23Q(qW4sSJ`I z8TzK)T8lcO^DXXHq37l`{&<6BA4IMpWw`MZQ36^R3U$E$Uzih0mh3SlOsEYPO%=j@ z*mIcYX>>7|FlRQu<0W$p+0y@2yEirCE4vVdvJG*+UG=p>**I+}9ePs2TDI0YGWSv^ z@>axo^TlqIGhtwV0C!=7hD=9?{(j>LS$&z9wrK9 z^s(rra8`>KKFCMbz6hGI6Y;Sc-Z_slw;RQ&X*g z50KG0b`E`^jfv2(eP#otbLa=AQNFH};ZxCC`8Yj3>`(>=Ona(dN>40>ZZgy3PiwwK z%jf6oDx1d!>x4uK;s^05zuaW5GS>?LE@G$`8|#1jh5zzhJ$+@grS)-taKJCQrHZGk z4&-?d2`Pd|iW$mb^N^N2`L3BS(tlee&H za}LDOHD)aYqpKj+tbb>F(YQO!VGh)?!EI?b8PJ@c97b=3 Cl8+w% literal 0 HcmV?d00001 diff --git a/doc/tutorials/imgproc/motion_deblur_filter/images/motion_original.jpg b/doc/tutorials/imgproc/motion_deblur_filter/images/motion_original.jpg new file mode 100755 index 0000000000000000000000000000000000000000..6c9b35dd5235b938b98efad93615a05c802aad2a GIT binary patch literal 51996 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≻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 OpenCV 2.0 + + *Author:* Karpushin Vladislav + + You will learn how to recover an image with motion blur distortion using a Wiener filter. diff --git a/samples/cpp/tutorial_code/ImgProc/motion_deblur_filter/motion_deblur_filter.cpp b/samples/cpp/tutorial_code/ImgProc/motion_deblur_filter/motion_deblur_filter.cpp new file mode 100755 index 0000000000..f1ea892c89 --- /dev/null +++ b/samples/cpp/tutorial_code/ImgProc/motion_deblur_filter/motion_deblur_filter.cpp @@ -0,0 +1,184 @@ +/** +* @brief You will learn how to recover an image with motion blur distortion using a Wiener filter +* @author Karpushin Vladislav, karpushin@ngs.ru, https://github.com/VladKarpushin +*/ +#include +#include "opencv2/imgproc.hpp" +#include "opencv2/imgcodecs.hpp" + +using namespace cv; +using namespace std; + +void help(); +void calcPSF(Mat& outputImg, Size filterSize, int len, double theta); +void fftshift(const Mat& inputImg, Mat& outputImg); +void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H); +void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr); +void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma = 5.0, double beta = 0.2); + +const String keys = +"{help h usage ? | | print this message }" +"{image |input.png | input image name }" +"{LEN |125 | length of a motion }" +"{THETA |0 | angle of a motion in degrees }" +"{SNR |700 | signal to noise ratio }" +; + +int main(int argc, char *argv[]) +{ + help(); + CommandLineParser parser(argc, argv, keys); + if (parser.has("help")) + { + parser.printMessage(); + return 0; + } + + int LEN = parser.get("LEN"); + double THETA = parser.get("THETA"); + int snr = parser.get("SNR"); + string strInFileName = parser.get("image"); + + if (!parser.check()) + { + parser.printErrors(); + return 0; + } + + Mat imgIn; + imgIn = imread(strInFileName, IMREAD_GRAYSCALE); + if (imgIn.empty()) //check whether the image is loaded or not + { + cout << "ERROR : Image cannot be loaded..!!" << endl; + return -1; + } + + Mat imgOut; + +//! [main] + // it needs to process even image only + Rect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2); + + //Hw calculation (start) + Mat Hw, h; + calcPSF(h, roi.size(), LEN, THETA); + calcWnrFilter(h, Hw, 1.0 / double(snr)); + //Hw calculation (stop) + + imgIn.convertTo(imgIn, CV_32F); + edgetaper(imgIn, imgIn); + + // filtering (start) + filter2DFreq(imgIn(roi), imgOut, Hw); + // filtering (stop) +//! [main] + + imgOut.convertTo(imgOut, CV_8U); + normalize(imgOut, imgOut, 0, 255, NORM_MINMAX); + imwrite("result.jpg", imgOut); + return 0; +} + +void help() +{ + cout << "2018-08-14" << endl; + cout << "Motion_deblur_v2" << endl; + cout << "You will learn how to recover an image with motion blur distortion using a Wiener filter" << endl; +} + +//! [calcPSF] +void calcPSF(Mat& outputImg, Size filterSize, int len, double theta) +{ + Mat h(filterSize, CV_32F, Scalar(0)); + Point point(filterSize.width / 2, filterSize.height / 2); + ellipse(h, point, Size(0, cvRound(float(len) / 2.0)), 90.0 - theta, 0, 360, Scalar(255), FILLED); + Scalar summa = sum(h); + outputImg = h / summa[0]; +} +//! [calcPSF] + +//! [fftshift] +void fftshift(const Mat& inputImg, Mat& outputImg) +{ + outputImg = inputImg.clone(); + int cx = outputImg.cols / 2; + int cy = outputImg.rows / 2; + Mat q0(outputImg, Rect(0, 0, cx, cy)); + Mat q1(outputImg, Rect(cx, 0, cx, cy)); + Mat q2(outputImg, Rect(0, cy, cx, cy)); + Mat q3(outputImg, Rect(cx, cy, cx, cy)); + Mat tmp; + q0.copyTo(tmp); + q3.copyTo(q0); + tmp.copyTo(q3); + q1.copyTo(tmp); + q2.copyTo(q1); + tmp.copyTo(q2); +} +//! [fftshift] + +//! [filter2DFreq] +void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H) +{ + Mat planes[2] = { Mat_(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) }; + Mat complexI; + merge(planes, 2, complexI); + dft(complexI, complexI, DFT_SCALE); + + Mat planesH[2] = { Mat_(H.clone()), Mat::zeros(H.size(), CV_32F) }; + Mat complexH; + merge(planesH, 2, complexH); + Mat complexIH; + mulSpectrums(complexI, complexH, complexIH, 0); + + idft(complexIH, complexIH); + split(complexIH, planes); + outputImg = planes[0]; +} +//! [filter2DFreq] + +//! [calcWnrFilter] +void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr) +{ + Mat h_PSF_shifted; + fftshift(input_h_PSF, h_PSF_shifted); + Mat planes[2] = { Mat_(h_PSF_shifted.clone()), Mat::zeros(h_PSF_shifted.size(), CV_32F) }; + Mat complexI; + merge(planes, 2, complexI); + dft(complexI, complexI); + split(complexI, planes); + Mat denom; + pow(abs(planes[0]), 2, denom); + denom += nsr; + divide(planes[0], denom, output_G); +} +//! [calcWnrFilter] + +//! [edgetaper] +void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma, double beta) +{ + int Nx = inputImg.cols; + int Ny = inputImg.rows; + Mat w1(1, Nx, CV_32F, Scalar(0)); + Mat w2(Ny, 1, CV_32F, Scalar(0)); + + float* p1 = w1.ptr(0); + float* p2 = w2.ptr(0); + float dx = float(2.0 * CV_PI / Nx); + float x = float(-CV_PI); + for (int i = 0; i < Nx; i++) + { + p1[i] = float(0.5 * (tanh((x + gamma / 2) / beta) - tanh((x - gamma / 2) / beta))); + x += dx; + } + float dy = float(2.0 * CV_PI / Ny); + float y = float(-CV_PI); + for (int i = 0; i < Ny; i++) + { + p2[i] = float(0.5 * (tanh((y + gamma / 2) / beta) - tanh((y - gamma / 2) / beta))); + y += dy; + } + Mat w = w2 * w1; + multiply(inputImg, w, outputImg); +} +//! [edgetaper] From 65a0587ce881e697bcfef7077297b6060b56c812 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 1 Sep 2018 12:38:57 +0000 Subject: [PATCH 24/47] imgproc(segmentation): don't increase mask NULL pointer Avoid manual address arithmetic --- modules/imgproc/src/segmentation.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/modules/imgproc/src/segmentation.cpp b/modules/imgproc/src/segmentation.cpp index b803770f12..b08d1b3ac0 100644 --- a/modules/imgproc/src/segmentation.cpp +++ b/modules/imgproc/src/segmentation.cpp @@ -407,27 +407,25 @@ void cv::pyrMeanShiftFiltering( InputArray _src, OutputArray _dst, cv::Size size = src.size(); const uchar* sptr = src.ptr(); int sstep = (int)src.step; - uchar* mask = 0; - int mstep = 0; uchar* dptr; int dstep; float sp = (float)(sp0 / (1 << level)); sp = MAX( sp, 1 ); + cv::Mat m; if( level < max_level ) { cv::Size size1 = dst_pyramid[level+1].size(); - cv::Mat m( size.height, size.width, CV_8UC1, mask0.ptr() ); + m = cv::Mat(size.height, size.width, CV_8UC1, mask0.ptr()); dstep = (int)dst_pyramid[level+1].step; dptr = dst_pyramid[level+1].ptr() + dstep + cn; - mstep = (int)m.step; - mask = m.ptr() + mstep; //cvResize( dst_pyramid[level+1], dst_pyramid[level], CV_INTER_CUBIC ); cv::pyrUp( dst_pyramid[level+1], dst_pyramid[level], dst_pyramid[level].size() ); m.setTo(cv::Scalar::all(0)); - for( i = 1; i < size1.height-1; i++, dptr += dstep - (size1.width-2)*3, mask += mstep*2 ) + for( i = 1; i < size1.height-1; i++, dptr += dstep - (size1.width-2)*3) { + uchar* mask = m.ptr(1 + i * 2); for( j = 1; j < size1.width-1; j++, dptr += cn ) { int c0 = dptr[0], c1 = dptr[1], c2 = dptr[2]; @@ -437,16 +435,16 @@ void cv::pyrMeanShiftFiltering( InputArray _src, OutputArray _dst, } cv::dilate( m, m, cv::Mat() ); - mask = m.ptr(); } dptr = dst_pyramid[level].ptr(); dstep = (int)dst_pyramid[level].step; for( i = 0; i < size.height; i++, sptr += sstep - size.width*3, - dptr += dstep - size.width*3, - mask += mstep ) + dptr += dstep - size.width*3 + ) { + uchar* mask = m.empty() ? NULL : m.ptr(i); for( j = 0; j < size.width; j++, sptr += 3, dptr += 3 ) { int x0 = j, y0 = i, x1, y1, iter; From 0f9f2696be1e357b85b5fe1cb18ad7859d6e1cb4 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 1 Sep 2018 12:25:10 +0000 Subject: [PATCH 25/47] imgproc: small code refactoring (findContours -> l_cinfo) --- modules/imgproc/src/contours.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/modules/imgproc/src/contours.cpp b/modules/imgproc/src/contours.cpp index eb47816c86..456b3cc2bb 100644 --- a/modules/imgproc/src/contours.cpp +++ b/modules/imgproc/src/contours.cpp @@ -1123,7 +1123,6 @@ cvFindNextContour( CvContourScanner scanner ) #endif { _CvContourInfo *par_info = 0; - _CvContourInfo *l_cinfo = 0; CvSeq *seq = 0; int is_hole = 0; CvPoint origin; @@ -1215,6 +1214,7 @@ cvFindNextContour( CvContourScanner scanner ) seq->flags |= is_hole ? CV_SEQ_FLAG_HOLE : 0; /* initialize header */ + _CvContourInfo *l_cinfo = 0; if( mode <= 1 ) { l_cinfo = &(scanner->cinfo_temp); @@ -1225,10 +1225,8 @@ cvFindNextContour( CvContourScanner scanner ) } else { - union { _CvContourInfo* ci; CvSetElem* se; } v; - v.ci = l_cinfo; - cvSetAdd( scanner->cinfo_set, 0, &v.se ); - l_cinfo = v.ci; + cvSetAdd(scanner->cinfo_set, 0, (CvSetElem**)&l_cinfo); + CV_Assert(l_cinfo); int lval; if( img_i ) @@ -1298,16 +1296,16 @@ cvFindNextContour( CvContourScanner scanner ) scanner->img = (schar *) img; scanner->nbd = nbd; return l_cinfo->contour; - - resume_scan: - + } + resume_scan: + { prev = p; /* update lnbd */ if( prev & -2 ) { lnbd.x = x; } - } /* end of prev != p */ + } } /* end of loop on x */ lnbd.x = 0; From 701760dbeb4a91aacbcd2876cb7ef7889afb44fd Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 1 Sep 2018 13:18:08 +0000 Subject: [PATCH 26/47] calib3d: use `Mat::reserveBuffer()` instead of `.create()` to allocate memory >2Gb --- modules/calib3d/src/stereosgbm.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/calib3d/src/stereosgbm.cpp b/modules/calib3d/src/stereosgbm.cpp index 374d7353d9..12d3f50bed 100644 --- a/modules/calib3d/src/stereosgbm.cpp +++ b/modules/calib3d/src/stereosgbm.cpp @@ -1487,12 +1487,14 @@ static void computeDisparitySGBM_HH4( const Mat& img1, const Mat& img2, size_t minLrSize = width1 , LrSize = minLrSize*D2; int hsumBufNRows = SH2*2 + 2; size_t totalBufSize = (LrSize + minLrSize)*NLR*sizeof(CostType) + // minLr[] and Lr[] - costBufSize*hsumBufNRows*sizeof(CostType) + // hsumBuf - CSBufSize*2*sizeof(CostType) + 1024; // C, S + costBufSize*hsumBufNRows*sizeof(CostType) + // hsumBuf + CSBufSize*2*sizeof(CostType) + 1024; // C, S if( buffer.empty() || !buffer.isContinuous() || buffer.cols*buffer.rows*buffer.elemSize() < totalBufSize ) - buffer.create(1, (int)totalBufSize, CV_8U); + { + buffer.reserveBuffer(totalBufSize); + } // summary cost over different (nDirs) directions CostType* Cbuf = (CostType*)alignPtr(buffer.ptr(), ALIGN); From 70a27c7dd6a781c7d4d8729084283beffb4a0fc9 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 1 Sep 2018 14:31:15 +0000 Subject: [PATCH 27/47] core: add solveLP type checks for output to forbid Mat1f Checks are not reliable: empty uninitialized `cv::Mat` has `CV_8UC1` type --- modules/core/src/lpsolver.cpp | 4 ++++ modules/core/test/test_lpsolver.cpp | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/modules/core/src/lpsolver.cpp b/modules/core/src/lpsolver.cpp index e5f4d99739..1a1307d5b5 100644 --- a/modules/core/src/lpsolver.cpp +++ b/modules/core/src/lpsolver.cpp @@ -98,6 +98,10 @@ int solveLP(const Mat& Func, const Mat& Constr, Mat& z){ CV_Assert(Constr.type()==CV_64FC1 || Constr.type()==CV_32FC1); CV_Assert((Func.rows==1 && (Constr.cols-Func.cols==1))|| (Func.cols==1 && (Constr.cols-Func.rows==1))); + if (!z.empty()) + CV_CheckTypeEQ(z.type(), CV_64FC1, ""); + else + CV_CheckType(z.type(), z.type() == CV_64FC1 || z.type() == CV_8UC1/*empty cv::Mat*/, ""); //copy arguments for we will shall modify them Mat_ bigC=Mat_(1,(Func.rows==1?Func.cols:Func.rows)+1), diff --git a/modules/core/test/test_lpsolver.cpp b/modules/core/test/test_lpsolver.cpp index b47a6eb080..97f87f4c2f 100644 --- a/modules/core/test/test_lpsolver.cpp +++ b/modules/core/test/test_lpsolver.cpp @@ -141,4 +141,14 @@ TEST(Core_LPSolver, regression_cycling){ #endif } +TEST(Core_LPSolver, issue_12337) +{ + Mat A=(cv::Mat_(3,1)<<3,1,2); + Mat B=(cv::Mat_(3,4)<<1,1,3,30,2,2,5,24,4,1,2,36); + EXPECT_ANY_THROW(Mat1f z_float; cv::solveLP(A, B, z_float)); + EXPECT_NO_THROW(Mat1d z_double; cv::solveLP(A, B, z_double)); + EXPECT_ANY_THROW(Mat1i z_int; cv::solveLP(A, B, z_int)); + //need to update interface: EXPECT_ANY_THROW(Mat1b z_8u; cv::solveLP(A, B, z_8u)); +} + }} // namespace From 20bf7b6a79091263078251eed64b738717d59eb5 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 2 Sep 2018 09:53:41 +0000 Subject: [PATCH 28/47] imgcodecs(webp): use safe cast to size_t on Win32 --- modules/imgcodecs/src/grfmt_webp.cpp | 2 +- modules/imgcodecs/src/utils.hpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index 7b5abfaad9..58ddec3665 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -107,7 +107,7 @@ bool WebPDecoder::readHeader() { fs.open(m_filename.c_str(), std::ios::binary); fs.seekg(0, std::ios::end); - fs_size = fs.tellg(); + fs_size = safeCastToSizeT(fs.tellg(), "File is too large"); fs.seekg(0, std::ios::beg); CV_Assert(fs && "File stream error"); CV_CheckGE(fs_size, WEBP_HEADER_SIZE, "File is too small"); diff --git a/modules/imgcodecs/src/utils.hpp b/modules/imgcodecs/src/utils.hpp index 7af4c6174e..6e0ec95826 100644 --- a/modules/imgcodecs/src/utils.hpp +++ b/modules/imgcodecs/src/utils.hpp @@ -44,6 +44,15 @@ int validateToInt(size_t step); +template static inline +size_t safeCastToSizeT(const _Tp v_origin, const char* msg) +{ + const size_t value_cast = (size_t)v_origin; + if ((_Tp)value_cast != v_origin) + CV_Error(cv::Error::StsError, msg ? msg : "Can't cast value into size_t"); + return value_cast; +} + struct PaletteEntry { unsigned char b, g, r, a; From 546fcd990e634101a88a841447ea07d613a9fe7b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 2 Sep 2018 12:07:15 +0000 Subject: [PATCH 29/47] ts: restore Google Test 1.8.0 (capture OpenCV specific patch) --- modules/ts/include/opencv2/ts/ts_gtest.h | 251 +++++++---------------- modules/ts/src/ts_gtest.cpp | 152 +++++--------- 2 files changed, 125 insertions(+), 278 deletions(-) diff --git a/modules/ts/include/opencv2/ts/ts_gtest.h b/modules/ts/include/opencv2/ts/ts_gtest.h index b687a5722e..c04205d017 100644 --- a/modules/ts/include/opencv2/ts/ts_gtest.h +++ b/modules/ts/include/opencv2/ts/ts_gtest.h @@ -412,105 +412,57 @@ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ // Determines the platform on which Google Test is compiled. -#define GTEST_OS_CYGWIN 0 -#define GTEST_OS_SYMBIAN 0 -#define GTEST_OS_WINDOWS 0 -#define GTEST_OS_WINDOWS_MOBILE 0 -#define GTEST_OS_WINDOWS_MINGW 0 -#define GTEST_OS_WINDOWS_DESKTOP 0 -#define GTEST_OS_WINDOWS_PHONE 0 -#define GTEST_OS_WINDOWS_RT 0 -#define GTEST_OS_MAC 0 -#define GTEST_OS_LINUX 0 -#define GTEST_OS_LINUX_ANDROID 0 -#define GTEST_OS_ZOS 0 -#define GTEST_OS_SOLARIS 0 -#define GTEST_OS_AIX 0 -#define GTEST_OS_HPUX 0 -#define GTEST_OS_NACL 0 -#define GTEST_OS_OPENBSD 0 -#define GTEST_OS_QNX 0 -#define GTEST_OS_IOS 0 -#define GTEST_OS_IOS_SIMULATOR 0 -#define GTEST_OS_FREEBSD 0 - #ifdef __CYGWIN__ -# undef GTEST_OS_CYGWIN # define GTEST_OS_CYGWIN 1 #elif defined __SYMBIAN32__ -# undef GTEST_OS_SYMBIAN # define GTEST_OS_SYMBIAN 1 #elif defined _WIN32 -# undef GTEST_OS_WINDOWS # define GTEST_OS_WINDOWS 1 # ifdef _WIN32_WCE -# undef GTEST_OS_WINDOWS_MOBILE # define GTEST_OS_WINDOWS_MOBILE 1 # elif defined(__MINGW__) || defined(__MINGW32__) -# undef GTEST_OS_WINDOWS_MINGW # define GTEST_OS_WINDOWS_MINGW 1 # elif defined(WINAPI_FAMILY) # include # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) -# undef GTEST_OS_WINDOWS_DESKTOP # define GTEST_OS_WINDOWS_DESKTOP 1 # elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) -# undef GTEST_OS_WINDOWS_PHONE # define GTEST_OS_WINDOWS_PHONE 1 # elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) -# undef GTEST_OS_WINDOWS_RT # define GTEST_OS_WINDOWS_RT 1 # else // WINAPI_FAMILY defined but no known partition matched. // Default to desktop. -# undef GTEST_OS_WINDOWS_DESKTOP # define GTEST_OS_WINDOWS_DESKTOP 1 # endif # else -# undef GTEST_OS_WINDOWS_DESKTOP # define GTEST_OS_WINDOWS_DESKTOP 1 # endif // _WIN32_WCE #elif defined __APPLE__ -# undef GTEST_OS_MAC # define GTEST_OS_MAC 1 # if TARGET_OS_IPHONE -# undef GTEST_OS_IOS # define GTEST_OS_IOS 1 -# if TARGET_IPHONE_SIMULATOR -# undef GTEST_OS_IOS_SIMULATOR -# define GTEST_OS_IOS_SIMULATOR 1 -# endif # endif #elif defined __FreeBSD__ -# undef GTEST_OS_FREEBSD # define GTEST_OS_FREEBSD 1 #elif defined __linux__ -# undef GTEST_OS_LINUX # define GTEST_OS_LINUX 1 # if defined __ANDROID__ -# undef GTEST_OS_LINUX_ANDROID # define GTEST_OS_LINUX_ANDROID 1 # endif #elif defined __MVS__ -# undef GTEST_OS_ZOS # define GTEST_OS_ZOS 1 #elif defined(__sun) && defined(__SVR4) -# undef GTEST_OS_SOLARIS # define GTEST_OS_SOLARIS 1 #elif defined(_AIX) -# undef GTEST_OS_AIX # define GTEST_OS_AIX 1 #elif defined(__hpux) -# undef GTEST_OS_HPUX # define GTEST_OS_HPUX 1 #elif defined __native_client__ -# undef GTEST_OS_NACL # define GTEST_OS_NACL 1 #elif defined __OpenBSD__ -# undef GTEST_OS_OPENBSD # define GTEST_OS_OPENBSD 1 #elif defined __QNX__ -# undef GTEST_OS_QNX # define GTEST_OS_QNX 1 #endif // __CYGWIN__ @@ -585,13 +537,6 @@ #endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ -#ifndef GTEST_HAS_NOTIFICATION_ -#define GTEST_HAS_NOTIFICATION_ 0 -#endif -#ifndef GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ -#define GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ 0 -#endif - #if !defined(GTEST_DEV_EMAIL_) # define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" # define GTEST_FLAG_PREFIX_ "gtest_" @@ -617,7 +562,7 @@ // GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) // /* code that triggers warnings C4800 and C4385 */ // GTEST_DISABLE_MSC_WARNINGS_POP_() -#if defined(_MSC_VER) && _MSC_VER >= 1500 +#if _MSC_VER >= 1500 # define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ __pragma(warning(push)) \ __pragma(warning(disable: warnings)) @@ -634,7 +579,7 @@ // -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a // value for __cplusplus, and recent versions of clang, gcc, and // probably other compilers set that too in C++11 mode. -# if defined __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L // Compiling in at least C++11 mode. # define GTEST_LANG_CXX11 1 # else @@ -660,8 +605,6 @@ __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \ __GLIBCXX__ != 20120702ul)) /* GCC 4.5.4 */ # define GTEST_STDLIB_CXX11 1 -#else -# define GTEST_STDLIB_CXX11 0 #endif // Only use C++11 library features if the library provides them. @@ -674,15 +617,6 @@ # define GTEST_HAS_STD_SHARED_PTR_ 1 # define GTEST_HAS_STD_TYPE_TRAITS_ 1 # define GTEST_HAS_STD_UNIQUE_PTR_ 1 -#else -# define GTEST_HAS_STD_BEGIN_AND_END_ 0 -# define GTEST_HAS_STD_FORWARD_LIST_ 0 -# define GTEST_HAS_STD_FUNCTION_ 0 -# define GTEST_HAS_STD_INITIALIZER_LIST_ 0 -# define GTEST_HAS_STD_MOVE_ 0 -# define GTEST_HAS_STD_SHARED_PTR_ 0 -# define GTEST_HAS_STD_TYPE_TRAITS_ 0 -# define GTEST_HAS_STD_UNIQUE_PTR_ 0 #endif // C++11 specifies that provides std::tuple. @@ -707,8 +641,6 @@ # undef GTEST_HAS_STD_TUPLE_ # endif # endif -#else -# define GTEST_HAS_STD_TUPLE_ 0 #endif // Brings in definitions for functions used in the testing::internal::posix @@ -720,16 +652,10 @@ # include # endif // In order to avoid having to include , use forward declaration -#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) - // MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two - // separate (equivalent) structs, instead of using typedef - typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; -# else - // assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. - // This assumption is verified by - // WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. - typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; -# endif +// assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +struct _RTL_CRITICAL_SECTION; #else // This assumes that non-Windows OSes provide unistd.h. For OSes where this // is not the case, we need to include headers that provide the functions @@ -746,14 +672,14 @@ // Defines this to true iff Google Test can use POSIX regular expressions. #ifndef GTEST_HAS_POSIX_RE # if GTEST_OS_LINUX_ANDROID -// On Android, is only available starting with Froyo. -# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 8) +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) # else -# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) -#endif +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# endif #endif -#if defined(GTEST_USES_PCRE) && GTEST_USES_PCRE +#if GTEST_USES_PCRE // The appropriate headers have already been included. #elif GTEST_HAS_POSIX_RE @@ -765,21 +691,18 @@ # include // NOLINT # define GTEST_USES_POSIX_RE 1 -# define GTEST_USES_SIMPLE_RE 0 #elif GTEST_OS_WINDOWS // is not available on Windows. Use our own simple regex // implementation instead. # define GTEST_USES_SIMPLE_RE 1 -# define GTEST_USES_POSIX_RE 0 #else // may not be available on this platform. Use our own // simple regex implementation instead. # define GTEST_USES_SIMPLE_RE 1 -# define GTEST_USES_POSIX_RE 0 #endif // GTEST_USES_PCRE @@ -888,8 +811,8 @@ !defined(__EXCEPTIONS) # define GTEST_HAS_RTTI 0 # else -# define GTEST_HAS_RTTI 1 -# endif // GTEST_OS_LINUX_ANDROID && _STLPORT_MAJOR && !__EXCEPTIONS +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS # else # define GTEST_HAS_RTTI 0 # endif // __GXX_RTTI @@ -949,7 +872,7 @@ // Determines if hash_map/hash_set are available. // Only used for testing against those containers. #if !defined(GTEST_HAS_HASH_MAP_) -# ifdef _MSC_VER +# if _MSC_VER # define GTEST_HAS_HASH_MAP_ 1 // Indicates that hash_map is available. # define GTEST_HAS_HASH_SET_ 1 // Indicates that hash_set is available. # endif // _MSC_VER @@ -964,7 +887,7 @@ # define GTEST_HAS_TR1_TUPLE 0 # else // The user didn't tell us not to do it, so we assume it's OK. -# define GTEST_HAS_TR1_TUPLE 1 +# define GTEST_HAS_TR1_TUPLE 1 # endif #endif // GTEST_HAS_TR1_TUPLE @@ -984,11 +907,8 @@ // support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, // and it can be used with some compilers that define __GNUC__. # if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ - && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) && !defined(_STLPORT_MAJOR) \ - || (defined(_MSC_VER) && _MSC_VER >= 1600) + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 # define GTEST_ENV_HAS_TR1_TUPLE_ 1 -# else -# define GTEST_ENV_HAS_TR1_TUPLE_ 0 # endif // C++11 specifies that provides std::tuple. Use that if gtest is used @@ -996,16 +916,12 @@ // can build with clang but need to use gcc4.2's libstdc++). # if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) # define GTEST_ENV_HAS_STD_TUPLE_ 1 -# else -# define GTEST_ENV_HAS_STD_TUPLE_ 0 # endif # if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ # define GTEST_USE_OWN_TR1_TUPLE 0 # else # define GTEST_USE_OWN_TR1_TUPLE 1 -# undef GTEST_HAS_TR1_TUPLE -# define GTEST_HAS_TR1_TUPLE 1 # endif #endif // GTEST_USE_OWN_TR1_TUPLE @@ -1178,52 +1094,52 @@ template struct TupleElement; template -struct TupleElement { +struct TupleElement { typedef T0 type; }; template -struct TupleElement { +struct TupleElement { typedef T1 type; }; template -struct TupleElement { +struct TupleElement { typedef T2 type; }; template -struct TupleElement { +struct TupleElement { typedef T3 type; }; template -struct TupleElement { +struct TupleElement { typedef T4 type; }; template -struct TupleElement { +struct TupleElement { typedef T5 type; }; template -struct TupleElement { +struct TupleElement { typedef T6 type; }; template -struct TupleElement { +struct TupleElement { typedef T7 type; }; template -struct TupleElement { +struct TupleElement { typedef T8 type; }; template -struct TupleElement { +struct TupleElement { typedef T9 type; }; @@ -1766,57 +1682,57 @@ inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, template struct tuple_size; template -struct tuple_size { +struct tuple_size { static const int value = 0; }; template -struct tuple_size { +struct tuple_size { static const int value = 1; }; template -struct tuple_size { +struct tuple_size { static const int value = 2; }; template -struct tuple_size { +struct tuple_size { static const int value = 3; }; template -struct tuple_size { +struct tuple_size { static const int value = 4; }; template -struct tuple_size { +struct tuple_size { static const int value = 5; }; template -struct tuple_size { +struct tuple_size { static const int value = 6; }; template -struct tuple_size { +struct tuple_size { static const int value = 7; }; template -struct tuple_size { +struct tuple_size { static const int value = 8; }; template -struct tuple_size { +struct tuple_size { static const int value = 9; }; template -struct tuple_size { +struct tuple_size { static const int value = 10; }; @@ -2002,8 +1918,8 @@ template inline bool operator==(const GTEST_10_TUPLE_(T)& t, const GTEST_10_TUPLE_(U)& u) { return gtest_internal::SameSizeTuplePrefixComparator< - tuple_size::value, - tuple_size::value>::Eq(t, u); + tuple_size::value, + tuple_size::value>::Eq(t, u); } template @@ -2114,13 +2030,13 @@ using ::std::tuple_size; # if GTEST_OS_LINUX && !defined(__ia64__) # if GTEST_OS_LINUX_ANDROID // On Android, clone() is only available on ARM starting with Gingerbread. -# if (defined(__arm__) || defined(__mips__)) && __ANDROID_API__ >= 9 +# if defined(__arm__) && __ANDROID_API__ >= 9 # define GTEST_HAS_CLONE 1 # else # define GTEST_HAS_CLONE 0 # endif # else -# define GTEST_HAS_CLONE 1 +# define GTEST_HAS_CLONE 1 # endif # else # define GTEST_HAS_CLONE 0 @@ -2151,9 +2067,6 @@ using ::std::tuple_size; GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD) # define GTEST_HAS_DEATH_TEST 1 -# include // NOLINT -#else -# define GTEST_HAS_DEATH_TEST 0 #endif // We don't support MSVC 7.1 with exceptions disabled now. Therefore @@ -2186,8 +2099,6 @@ using ::std::tuple_size; // Determines whether test results can be streamed to a socket. #if GTEST_OS_LINUX # define GTEST_CAN_STREAM_RESULTS_ 1 -#else -# define GTEST_CAN_STREAM_RESULTS_ 0 #endif // Defines some utility macros. @@ -2471,9 +2382,6 @@ typedef ::std::wstring wstring; // returns 'condition'. GTEST_API_ bool IsTrue(bool condition); -template class scoped_ptr; -template static void swap(scoped_ptr& a, scoped_ptr& b); - // Defines scoped_ptr. // This implementation of scoped_ptr is PARTIAL - it only contains @@ -2505,7 +2413,10 @@ class scoped_ptr { } } - friend void swap(scoped_ptr& a, scoped_ptr& b); + friend void swap(scoped_ptr& a, scoped_ptr& b) { + using std::swap; + swap(a.ptr_, b.ptr_); + } private: T* ptr_; @@ -2513,12 +2424,6 @@ class scoped_ptr { GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); }; -template -static void swap(scoped_ptr& a, scoped_ptr& b) { - using std::swap; - swap(a.ptr_, b.ptr_); -} - // Defines RE. // A simple C++ wrapper for . It uses the POSIX Extended @@ -2652,7 +2557,7 @@ inline void FlushInfoLog() { fflush(NULL); } // // GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition // is not satisfied. -// Synopsis: +// Synopsys: // GTEST_CHECK_(boolean_condition); // or // GTEST_CHECK_(boolean_condition) << "Additional message"; @@ -2696,7 +2601,7 @@ const T& move(const T& t) { // const Foo*). When you use ImplicitCast_, the compiler checks that // the cast is safe. Such explicit ImplicitCast_s are necessary in // surprisingly many situations where C++ demands an exact type match -// instead of an argument type convertible to a target type. +// instead of an argument type convertable to a target type. // // The syntax for using ImplicitCast_ is the same as for static_cast: // @@ -2764,7 +2669,7 @@ Derived* CheckedDowncastToActualType(Base* base) { GTEST_CHECK_(typeid(*base) == typeid(Derived)); #endif -#if defined(GTEST_HAS_DOWNCAST_) && GTEST_HAS_DOWNCAST_ +#if GTEST_HAS_DOWNCAST_ return ::down_cast(base); #elif GTEST_HAS_RTTI return dynamic_cast(base); // NOLINT @@ -3063,7 +2968,7 @@ class GTEST_API_ Mutex { // by the linker. MutexType type_; long critical_section_init_phase_; // NOLINT - GTEST_CRITICAL_SECTION* critical_section_; + _RTL_CRITICAL_SECTION* critical_section_; GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); }; @@ -3755,7 +3660,7 @@ inline int Close(int fd) { return close(fd); } inline const char* StrError(int errnum) { return strerror(errnum); } #endif inline const char* GetEnv(const char* name) { -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE | GTEST_OS_WINDOWS_RT // We are on Windows CE, which has no environment variables. static_cast(name); // To prevent 'unused argument' warning. return NULL; @@ -3787,7 +3692,7 @@ inline void Abort() { abort(); } // MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate // function in order to achieve that. We use macro definition here because // snprintf is a variadic function. -#if defined(_MSC_VER) && _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE // MSVC 2005 and above support variadic macros. # define GTEST_SNPRINTF_(buffer, size, format, ...) \ _snprintf_s(buffer, size, size, format, __VA_ARGS__) @@ -4552,7 +4457,7 @@ class GTEST_API_ FilePath { void Normalize(); - // Returns a pointer to the last occurrence of a valid path separator in + // Returns a pointer to the last occurence of a valid path separator in // the FilePath. On Windows, for example, both '/' and '\' are valid path // separators. Returns NULL if no path separator was found. const char* FindLastPathSeparator() const; @@ -7975,7 +7880,6 @@ GTEST_API_ std::string AppendUserMessage( // errors presumably detectable only at run time. Since // std::runtime_error inherits from std::exception, many testing // frameworks know how to extract and print the message inside it. - class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { public: explicit GoogleTestFailureException(const TestPartResult& failure); @@ -8006,7 +7910,7 @@ namespace edit_distance { // Returns the optimal edits to go from 'left' to 'right'. // All edits cost the same, with replace having lower priority than // add/remove. -// Simple implementation of the Wagner-Fischer algorithm. +// Simple implementation of the Wagner–Fischer algorithm. // See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm enum EditType { kMatch, kAdd, kRemove, kReplace }; GTEST_API_ std::vector CalculateOptimalEdits( @@ -9013,7 +8917,7 @@ class NativeArray { // Implements Boolean test assertions such as EXPECT_TRUE. expression can be // either a boolean expression or an AssertionResult. text is a textual -// representation of expression as it was passed into the EXPECT_TRUE. +// represenation of expression as it was passed into the EXPECT_TRUE. #define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (const ::testing::AssertionResult gtest_ar_ = \ @@ -10325,7 +10229,7 @@ class TypeWithoutFormatter { // T is not an enum, printing it as an integer is the best we can do // given that it has no user-defined printer. static void PrintValue(const T& value, ::std::ostream* os) { - const internal::BiggestInt kBigInt = static_cast(value); + const internal::BiggestInt kBigInt = value; *os << kBigInt; } }; @@ -11539,15 +11443,12 @@ typename ParamNameGenFunc::Type *GetParamNameGen() { return DefaultParamName; } -} // namespace internal:: // fixes MacOS X issue with "friend class internal/*::anon*/::ParameterizedTestFactory;" -namespace { // wrap into anynomous namespace to avoid build warnings like GCC's -Wsubobject-linkage - // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // Stores a parameter value and later creates tests parameterized with that // value. template -class ParameterizedTestFactory : public internal::TestFactoryBase { +class ParameterizedTestFactory : public TestFactoryBase { public: typedef typename TestClass::ParamType ParamType; explicit ParameterizedTestFactory(ParamType parameter) : @@ -11562,8 +11463,6 @@ class ParameterizedTestFactory : public internal::TestFactoryBase { GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); }; -} // namespace -namespace internal { // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // @@ -18245,9 +18144,9 @@ internal::CartesianProductHolder10 \ + ::testing::internal::ParamGenerator \ gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ - static ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \ + ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \ const ::testing::TestParamInfo& info) { \ return ::testing::internal::GetParamNameGen \ (__VA_ARGS__)(info); \ @@ -18469,7 +18368,7 @@ class GTEST_API_ TestPartResultArray { }; // This interface knows how to report a test part result. -class GTEST_API_ TestPartResultReporterInterface { +class TestPartResultReporterInterface { public: virtual ~TestPartResultReporterInterface() {} @@ -18803,9 +18702,6 @@ GTEST_DECLARE_string_(color); // the tests to run. If the filter is not given all tests are executed. GTEST_DECLARE_string_(filter); -// OpenCV extension: same as filter, but for the parameters string. -GTEST_DECLARE_string_(param_filter); - // This flag causes the Google Test to list tests. None of the tests listed // are actually run if the flag is provided. GTEST_DECLARE_bool_(list_tests); @@ -19990,7 +19886,7 @@ class GTEST_API_ UnitTest { internal::UnitTestImpl* impl() { return impl_; } const internal::UnitTestImpl* impl() const { return impl_; } - // These classes and functions are friends as they need to access private + // These classes and funcions are friends as they need to access private // members of UnitTest. friend class Test; friend class internal::AssertHelper; @@ -20394,8 +20290,8 @@ class GTEST_API_ AssertHelper { : type(t), file(srcfile), line(line_num), message(msg) { } TestPartResult::Type const type; - const char* const file; - int const line; + const char* const file; + int const line; std::string const message; private: @@ -20410,12 +20306,6 @@ class GTEST_API_ AssertHelper { } // namespace internal #if GTEST_HAS_PARAM_TEST - -namespace internal { -// Static value used for accessing test parameter during a test lifetime. -extern void* g_parameter_; -} // namespace internal - // The pure interface class that all value-parameterized tests inherit from. // A value-parameterized class must inherit from both ::testing::Test and // ::testing::WithParamInterface. In most cases that just means inheriting @@ -20462,28 +20352,29 @@ class WithParamInterface { // like writing 'WithParamInterface::GetParam()' for a test that // uses a fixture whose parameter type is int. const ParamType& GetParam() const { - GTEST_CHECK_(GetParameterPtrRef_() != NULL) + GTEST_CHECK_(parameter_ != NULL) << "GetParam() can only be called inside a value-parameterized test " << "-- did you intend to write TEST_P instead of TEST_F?"; - return *GetParameterPtrRef_(); + return *parameter_; } private: // Sets parameter value. The caller is responsible for making sure the value // remains alive and unchanged throughout the current test. static void SetParam(const ParamType* parameter) { - GetParameterPtrRef_() = parameter; + parameter_ = parameter; } - static const ParamType*& GetParameterPtrRef_() - { - return (const ParamType*&)internal::g_parameter_; - } + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; // TestClass must be a subclass of WithParamInterface and Test. - template friend class /*internal::*/ParameterizedTestFactory; + template friend class internal::ParameterizedTestFactory; }; +template +const T* WithParamInterface::parameter_ = NULL; + // Most value-parameterized classes can ignore the existence of // WithParamInterface, and can just inherit from ::testing::TestWithParam. diff --git a/modules/ts/src/ts_gtest.cpp b/modules/ts/src/ts_gtest.cpp index f71efbb0eb..4f8c08aa3d 100644 --- a/modules/ts/src/ts_gtest.cpp +++ b/modules/ts/src/ts_gtest.cpp @@ -36,15 +36,7 @@ // This line ensures that gtest.h can be compiled on its own, even // when it's fused. -#include "precomp.hpp" - -#ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wmissing-declarations" -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -# if __GNUC__ >= 5 -# pragma GCC diagnostic ignored "-Wsuggest-override" -# endif -#endif +#include "gtest/gtest.h" // The following lines pull in the real gtest *.cc files. // Copyright 2005, Google Inc. @@ -506,7 +498,6 @@ const char kBreakOnFailureFlag[] = "break_on_failure"; const char kCatchExceptionsFlag[] = "catch_exceptions"; const char kColorFlag[] = "color"; const char kFilterFlag[] = "filter"; -const char kParamFilterFlag[] = "param_filter"; const char kListTestsFlag[] = "list_tests"; const char kOutputFlag[] = "output"; const char kPrintTimeFlag[] = "print_time"; @@ -586,7 +577,6 @@ class GTestFlagSaver { death_test_style_ = GTEST_FLAG(death_test_style); death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); filter_ = GTEST_FLAG(filter); - param_filter_ = GTEST_FLAG(param_filter); internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); list_tests_ = GTEST_FLAG(list_tests); output_ = GTEST_FLAG(output); @@ -608,7 +598,6 @@ class GTestFlagSaver { GTEST_FLAG(death_test_style) = death_test_style_; GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; GTEST_FLAG(filter) = filter_; - GTEST_FLAG(param_filter) = param_filter_; GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; GTEST_FLAG(list_tests) = list_tests_; GTEST_FLAG(output) = output_; @@ -630,7 +619,6 @@ class GTestFlagSaver { std::string death_test_style_; bool death_test_use_fork_; std::string filter_; - std::string param_filter_; std::string internal_run_death_test_; bool list_tests_; std::string output_; @@ -1359,7 +1347,6 @@ GTEST_API_ bool IsAsciiWhiteSpace(char ch); GTEST_API_ bool IsAsciiWordChar(char ch); GTEST_API_ bool IsValidEscape(char ch); GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); -GTEST_API_ std::string FormatRegexSyntaxError(const char* regex, int index); GTEST_API_ bool ValidateRegex(const char* regex); GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); GTEST_API_ bool MatchRepetitionAndRegexAtHead( @@ -1693,12 +1680,6 @@ GTEST_DEFINE_string_( "exclude). A test is run if it matches one of the positive " "patterns and does not match any of the negative patterns."); -GTEST_DEFINE_string_( - param_filter, - internal::StringFromGTestEnv("param_filter", GetDefaultFilter()), - "Same syntax and semantics as for param, but these patterns " - "have to match the test's parameters."); - GTEST_DEFINE_bool_(list_tests, false, "List all tests without running them."); @@ -2095,7 +2076,7 @@ extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); // This predicate-formatter checks that 'results' contains a test part // failure of the given type and that the failure message contains the // given substring. -static AssertionResult HasOneFailure(const char* /* results_expr */, +AssertionResult HasOneFailure(const char* /* results_expr */, const char* /* type_expr */, const char* /* substr_expr */, const TestPartResultArray& results, @@ -2638,7 +2619,7 @@ class Hunk { // Print a unified diff header for one hunk. // The format is // "@@ -, +, @@" - // where the left/right parts are omitted if unnecessary. + // where the left/right parts are ommitted if unnecessary. void PrintHeader(std::ostream* ss) const { *ss << "@@ "; if (removes_) { @@ -3605,7 +3586,7 @@ static std::string FormatWordList(const std::vector& words) { return word_list.GetString(); } -static bool ValidateTestPropertyName(const std::string& property_name, +bool ValidateTestPropertyName(const std::string& property_name, const std::vector& reserved_names) { if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != reserved_names.end()) { @@ -3834,7 +3815,7 @@ static std::string PrintTestPartResultToString( GoogleTestFailureException::GoogleTestFailureException( const TestPartResult& failure) - : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} #endif // GTEST_HAS_EXCEPTIONS @@ -4368,7 +4349,7 @@ WORD GetColorAttribute(GTestColor color) { // Returns the ANSI color code for the given color. COLOR_DEFAULT is // an invalid input. -static const char* GetAnsiColorCode(GTestColor color) { +const char* GetAnsiColorCode(GTestColor color) { switch (color) { case COLOR_RED: return "1"; case COLOR_GREEN: return "2"; @@ -4420,7 +4401,7 @@ bool ShouldUseColor(bool stdout_is_tty) { // cannot simply emit special characters and have the terminal change colors. // This routine must actually emit the characters rather than return a string // that would be colored when printed, as can be done on Linux. -static void ColoredPrintf(GTestColor color, const char* fmt, ...) { +void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_list args; va_start(args, fmt); @@ -4473,7 +4454,7 @@ static void ColoredPrintf(GTestColor color, const char* fmt, ...) { static const char kTypeParamLabel[] = "TypeParam"; static const char kValueParamLabel[] = "GetParam()"; -static void PrintFullTestCommentIfPresent(const TestInfo& test_info) { +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { const char* const type_param = test_info.type_param(); const char* const value_param = test_info.value_param(); @@ -4534,14 +4515,6 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart( "Note: %s filter = %s\n", GTEST_NAME_, filter); } - const char* const param_filter = GTEST_FLAG(param_filter).c_str(); - - // Ditto. - if (!String::CStringEquals(param_filter, kUniversalFilter)) { - ColoredPrintf(COLOR_YELLOW, - "Note: %s parameter filter = %s\n", GTEST_NAME_, param_filter); - } - if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); ColoredPrintf(COLOR_YELLOW, @@ -4586,7 +4559,6 @@ void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { ColoredPrintf(COLOR_GREEN, "[ RUN ] "); PrintTestName(test_info.test_case_name(), test_info.name()); - PrintFullTestCommentIfPresent(test_info); printf("\n"); fflush(stdout); } @@ -4956,38 +4928,38 @@ std::string XmlUnitTestResultPrinter::EscapeXml( for (size_t i = 0; i < str.size(); ++i) { const char ch = str[i]; switch (ch) { - case '<': - m << "<"; - break; - case '>': - m << ">"; - break; - case '&': - m << "&"; - break; - case '\'': - if (is_attribute) - m << "'"; - else - m << '\''; - break; - case '"': - if (is_attribute) - m << """; - else - m << '"'; - break; - default: + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: if (IsValidXmlCharacter(ch)) { if (is_attribute && IsNormalizableWhitespace(ch)) m << "&#x" << String::FormatByte(static_cast(ch)) << ";"; - else + else m << ch; - } - break; - } + } + break; } + } return m.GetString(); } @@ -5445,7 +5417,7 @@ void TestEventListeners::SuppressEventForwarding() { // We don't protect this under mutex_ as a user is not supposed to // call this before main() starts, from which point on the return // value will never change. -UnitTest * UnitTest::GetInstance() { +UnitTest* UnitTest::GetInstance() { // When compiled with MSVC 7.1 in optimized mode, destroying the // UnitTest object upon exiting the program messes up the exit code, // causing successful tests to appear failed. We have to use a @@ -5457,13 +5429,13 @@ UnitTest * UnitTest::GetInstance() { // default implementation. Use this implementation to keep good OO // design with private destructor. -#if (defined(_MSC_VER) && _MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) static UnitTest* const instance = new UnitTest; return instance; #else static UnitTest instance; return &instance; -#endif // (defined(_MSC_VER) && _MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) } // Gets the number of successful test cases. @@ -6291,15 +6263,9 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { kDisableTestFilter); test_info->is_disabled_ = is_disabled; - const std::string value_param(test_info->value_param() == NULL ? - "" : test_info->value_param()); - const bool matches_filter = internal::UnitTestOptions::FilterMatchesTest(test_case_name, - test_name) && - internal::UnitTestOptions::MatchesFilter(value_param, - GTEST_FLAG(param_filter).c_str()); - + test_name); test_info->matches_filter_ = matches_filter; const bool is_runnable = @@ -6364,7 +6330,7 @@ void UnitTestImpl::ListTestsMatchingFilter() { // We print the type parameter on a single line to make // the output easy to parse by a program. PrintOnOneLine(test_case->type_param(), kMaxParamLength); - } + } printf("\n"); } printf(" %s", test_info->name()); @@ -6494,7 +6460,7 @@ bool SkipPrefix(const char* prefix, const char** pstr) { // part can be omitted. // // Returns the value of the flag, or NULL if the parsing failed. -static const char* ParseFlagValue(const char* str, +const char* ParseFlagValue(const char* str, const char* flag, bool def_optional) { // str and flag must not be NULL. @@ -6532,7 +6498,7 @@ static const char* ParseFlagValue(const char* str, // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -static bool ParseBoolFlag(const char* str, const char* flag, bool* value) { +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { // Gets the value of the flag as a string. const char* const value_str = ParseFlagValue(str, flag, true); @@ -6566,7 +6532,7 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -static bool ParseStringFlag(const char* str, const char* flag, std::string* value) { +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { // Gets the value of the flag as a string. const char* const value_str = ParseFlagValue(str, flag, false); @@ -6651,12 +6617,6 @@ static const char kColorEncodedHelpMessage[] = " Run only the tests whose name matches one of the positive patterns but\n" " none of the negative patterns. '?' matches any single character; '*'\n" " matches any substring; ':' separates two patterns.\n" -" @G--" GTEST_FLAG_PREFIX_ "param_filter=@YPOSITIVE_PATTERNS" - "[@G-@YNEGATIVE_PATTERNS]@D\n" -" Like @G--" GTEST_FLAG_PREFIX_ - "filter@D, but applies to the test's parameter. If a\n" -" test is not parameterized, its parameter is considered to be the\n" -" empty string.\n" " @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" " Run all disabled tests too.\n" "\n" @@ -6708,7 +6668,7 @@ static const char kColorEncodedHelpMessage[] = "(not one in your own code or tests), please report it to\n" "@G<" GTEST_DEV_EMAIL_ ">@D.\n"; -static bool ParseGoogleTestFlag(const char* const arg) { +bool ParseGoogleTestFlag(const char* const arg) { return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, >EST_FLAG(also_run_disabled_tests)) || ParseBoolFlag(arg, kBreakOnFailureFlag, @@ -6721,7 +6681,6 @@ static bool ParseGoogleTestFlag(const char* const arg) { ParseBoolFlag(arg, kDeathTestUseFork, >EST_FLAG(death_test_use_fork)) || ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || - ParseStringFlag(arg, kParamFilterFlag, >EST_FLAG(param_filter)) || ParseStringFlag(arg, kInternalRunDeathTestFlag, >EST_FLAG(internal_run_death_test)) || ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || @@ -6739,7 +6698,7 @@ static bool ParseGoogleTestFlag(const char* const arg) { } #if GTEST_USE_OWN_FLAGFILE_FLAG_ -static void LoadFlagsFromFile(const std::string& path) { +void LoadFlagsFromFile(const std::string& path) { FILE* flagfile = posix::FOpen(path.c_str(), "r"); if (!flagfile) { fprintf(stderr, @@ -7130,7 +7089,7 @@ enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; // message is propagated back to the parent process. Otherwise, the // message is simply printed to stderr. In either case, the program // then exits with status 1. -static void DeathTestAbort(const std::string& message) { +void DeathTestAbort(const std::string& message) { // On a POSIX system, this function may be called from a threadsafe-style // death test child process, which operates on a very small stack. Use // the heap for any additional non-minuscule memory requirements. @@ -7872,15 +7831,14 @@ void StackLowerThanAddress(const void* ptr, bool* result) { *result = (&dummy < ptr); } -#if GTEST_HAS_CLONE +// Make sure AddressSanitizer does not tamper with the stack here. GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ -static bool StackGrowsDown() { +bool StackGrowsDown() { int dummy; bool result; StackLowerThanAddress(&dummy, &result); return result; } -#endif // Spawns a child process with the same executable as the current process in // a thread-safe manner and instructs it to run the death test. The @@ -8066,6 +8024,7 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, } # if GTEST_OS_WINDOWS + if (GTEST_FLAG(death_test_style) == "threadsafe" || GTEST_FLAG(death_test_style) == "fast") { *test = new WindowsDeathTest(statement, regex, file, line); @@ -8174,6 +8133,7 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { int write_fd = -1; # if GTEST_OS_WINDOWS + unsigned int parent_process_id = 0; size_t write_handle_as_size_t = 0; size_t event_handle_as_size_t = 0; @@ -8278,7 +8238,6 @@ namespace internal { // of them. const char kPathSeparator = '\\'; const char kAlternatePathSeparator = '/'; -//const char kPathSeparatorString[] = "\\"; const char kAlternatePathSeparatorString[] = "/"; # if GTEST_OS_WINDOWS_MOBILE // Windows CE doesn't have a current directory. You should not use @@ -8292,7 +8251,6 @@ const char kCurrentDirectoryString[] = ".\\"; # endif // GTEST_OS_WINDOWS_MOBILE #else const char kPathSeparator = '/'; -//const char kPathSeparatorString[] = "/"; const char kCurrentDirectoryString[] = "./"; #endif // GTEST_OS_WINDOWS @@ -8340,7 +8298,7 @@ FilePath FilePath::RemoveExtension(const char* extension) const { return *this; } -// Returns a pointer to the last occurrence of a valid path separator in +// Returns a pointer to the last occurence of a valid path separator in // the FilePath. On Windows, for example, both '/' and '\' are valid path // separators. Returns NULL if no path separator was found. const char* FilePath::FindLastPathSeparator() const { @@ -9606,7 +9564,7 @@ static CapturedStream* g_captured_stderr = NULL; static CapturedStream* g_captured_stdout = NULL; // Starts capturing an output stream (stdout/stderr). -static void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { +void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { if (*stream != NULL) { GTEST_LOG_(FATAL) << "Only one " << stream_name << " capturer can exist at a time."; @@ -9615,7 +9573,7 @@ static void CaptureStream(int fd, const char* stream_name, CapturedStream** stre } // Stops capturing the output stream and returns the captured string. -static std::string GetCapturedStream(CapturedStream** captured_stream) { +std::string GetCapturedStream(CapturedStream** captured_stream) { const std::string content = (*captured_stream)->GetCapturedString(); delete *captured_stream; @@ -9971,7 +9929,7 @@ namespace internal { // Depending on the value of a char (or wchar_t), we print it in one // of three formats: // - as is if it's a printable ASCII (e.g. 'a', '2', ' '), -// - as a hexadecimal escape sequence (e.g. '\x7F'), or +// - as a hexidecimal escape sequence (e.g. '\x7F'), or // - as a special escape sequence (e.g. '\r', '\n'). enum CharFormat { kAsIs, @@ -10075,7 +10033,7 @@ void PrintCharAndCodeTo(Char c, ostream* os) { return; *os << " (" << static_cast(c); - // For more convenience, we print c's code again in hexadecimal, + // For more convenience, we print c's code again in hexidecimal, // unless c was already printed in the form '\x##' or the code is in // [1, 9]. if (format == kHexEscape || (1 <= c && c <= 9)) { @@ -10441,7 +10399,5 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames( #endif // GTEST_HAS_TYPED_TEST_P -void* g_parameter_ = NULL; - } // namespace internal } // namespace testing From f7ccc74e0955d5af06f4b2fc09606500d230059b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 2 Sep 2018 12:21:29 +0000 Subject: [PATCH 30/47] ts: update Google Test 1.8.1 Helper script from googletest: ./scripts/fuse_gtest_files.py --- modules/ts/include/opencv2/ts/ts_gtest.h | 4043 +++++++++++++--------- modules/ts/src/ts_gtest.cpp | 1929 ++++++++--- 2 files changed, 3940 insertions(+), 2032 deletions(-) diff --git a/modules/ts/include/opencv2/ts/ts_gtest.h b/modules/ts/include/opencv2/ts/ts_gtest.h index c04205d017..ebb16db7b0 100644 --- a/modules/ts/include/opencv2/ts/ts_gtest.h +++ b/modules/ts/include/opencv2/ts/ts_gtest.h @@ -26,10 +26,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // // This header file defines the public API for Google Test. It should be // included by any test program that uses Google Test. @@ -48,6 +47,8 @@ // registration from Barthelemy Dagenais' (barthelemy@prologique.com) // easyUnit framework. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_GTEST_H_ #define GTEST_INCLUDE_GTEST_GTEST_H_ @@ -84,13 +85,13 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // // This header file declares functions and macros used internally by // Google Test. They are subject to change without notice. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ @@ -123,8 +124,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Authors: wan@google.com (Zhanyong Wan) -// // Low-level types and utilities for porting Google Test to various // platforms. All macros ending with _ and symbols defined in an // internal namespace are subject to change without notice. Code @@ -136,6 +135,8 @@ // files are expected to #include this. Therefore, it cannot #include // any other Google Test header. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ @@ -169,11 +170,9 @@ // GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions // are enabled. // GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string -// is/isn't available (some systems define -// ::string, which is different to std::string). -// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string -// is/isn't available (some systems define -// ::wstring, which is different to std::wstring). +// is/isn't available +// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::wstring +// is/isn't available // GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular // expressions are/aren't available. // GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that @@ -205,6 +204,12 @@ // GTEST_CREATE_SHARED_LIBRARY // - Define to 1 when compiling Google Test itself // as a shared library. +// GTEST_DEFAULT_DEATH_TEST_STYLE +// - The default value of --gtest_death_test_style. +// The legacy default has been "fast" in the open +// source version since 2008. The recommended value +// is "threadsafe", and can be set in +// custom/gtest-port.h. // Platform-indicating macros // -------------------------- @@ -218,12 +223,14 @@ // GTEST_OS_AIX - IBM AIX // GTEST_OS_CYGWIN - Cygwin // GTEST_OS_FREEBSD - FreeBSD +// GTEST_OS_FUCHSIA - Fuchsia // GTEST_OS_HPUX - HP-UX // GTEST_OS_LINUX - Linux // GTEST_OS_LINUX_ANDROID - Google Android // GTEST_OS_MAC - Mac OS X // GTEST_OS_IOS - iOS // GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_NETBSD - NetBSD // GTEST_OS_OPENBSD - OpenBSD // GTEST_OS_QNX - QNX // GTEST_OS_SOLARIS - Sun Solaris @@ -265,15 +272,15 @@ // GTEST_HAS_COMBINE - the Combine() function (for value-parameterized // tests) // GTEST_HAS_DEATH_TEST - death tests -// GTEST_HAS_PARAM_TEST - value-parameterized tests // GTEST_HAS_TYPED_TEST - typed tests // GTEST_HAS_TYPED_TEST_P - type-parameterized tests // GTEST_IS_THREADSAFE - Google Test is thread-safe. +// GOOGLETEST_CM0007 DO NOT DELETE // GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with // GTEST_HAS_POSIX_RE (see above) which users can // define themselves. // GTEST_USES_SIMPLE_RE - our own simple regex is used; -// the above two are mutually exclusive. +// the above RE\b(s) are mutually exclusive. // GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). // Misc public macros @@ -302,6 +309,7 @@ // // C++11 feature wrappers: // +// testing::internal::forward - portability wrapper for std::forward. // testing::internal::move - portability wrapper for std::move. // // Synchronization: @@ -318,10 +326,10 @@ // // Regular expressions: // RE - a simple regular expression class using the POSIX -// Extended Regular Expression syntax on UNIX-like -// platforms, or a reduced regular exception syntax on -// other platforms, including Windows. -// +// Extended Regular Expression syntax on UNIX-like platforms +// GOOGLETEST_CM0008 DO NOT DELETE +// or a reduced regular exception syntax on other +// platforms, including Windows. // Logging: // GTEST_LOG_() - logs messages at the specified severity level. // LogToStderr() - directs all log messages to stderr. @@ -367,10 +375,12 @@ # include #endif +// Brings in the definition of HAS_GLOBAL_STRING. This must be done +// BEFORE we test HAS_GLOBAL_STRING. +#include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT -#include // NOLINT #include #include // NOLINT @@ -403,7 +413,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // // This header file defines the GTEST_OS_* macro. // It is separate from gtest-port.h so that custom/gtest-port.h can include it. @@ -430,6 +440,9 @@ # define GTEST_OS_WINDOWS_PHONE 1 # elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) # define GTEST_OS_WINDOWS_RT 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +# define GTEST_OS_WINDOWS_PHONE 1 +# define GTEST_OS_WINDOWS_TV_TITLE 1 # else // WINAPI_FAMILY defined but no known partition matched. // Default to desktop. @@ -445,6 +458,8 @@ # endif #elif defined __FreeBSD__ # define GTEST_OS_FREEBSD 1 +#elif defined __Fuchsia__ +# define GTEST_OS_FUCHSIA 1 #elif defined __linux__ # define GTEST_OS_LINUX 1 # if defined __ANDROID__ @@ -460,6 +475,8 @@ # define GTEST_OS_HPUX 1 #elif defined __native_client__ # define GTEST_OS_NACL 1 +#elif defined __NetBSD__ +# define GTEST_OS_NETBSD 1 #elif defined __OpenBSD__ # define GTEST_OS_OPENBSD 1 #elif defined __QNX__ @@ -496,39 +513,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Injection point for custom user configurations. -// The following macros can be defined: -// -// Flag related macros: -// GTEST_FLAG(flag_name) -// GTEST_USE_OWN_FLAGFILE_FLAG_ - Define to 0 when the system provides its -// own flagfile flag parsing. -// GTEST_DECLARE_bool_(name) -// GTEST_DECLARE_int32_(name) -// GTEST_DECLARE_string_(name) -// GTEST_DEFINE_bool_(name, default_val, doc) -// GTEST_DEFINE_int32_(name, default_val, doc) -// GTEST_DEFINE_string_(name, default_val, doc) -// -// Test filtering: -// GTEST_TEST_FILTER_ENV_VAR_ - The name of an environment variable that -// will be used if --GTEST_FLAG(test_filter) -// is not provided. -// -// Logging: -// GTEST_LOG_(severity) -// GTEST_CHECK_(condition) -// Functions LogToStderr() and FlushInfoLog() have to be provided too. -// -// Threading: -// GTEST_HAS_NOTIFICATION_ - Enabled if Notification is already provided. -// GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ - Enabled if Mutex and ThreadLocal are -// already provided. -// Must also provide GTEST_DECLARE_STATIC_MUTEX_(mutex) and -// GTEST_DEFINE_STATIC_MUTEX_(mutex) -// -// GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) -// GTEST_LOCK_EXCLUDED_(locks) +// Injection point for custom user configurations. See README for details // // ** Custom implementation starts here ** @@ -562,7 +547,7 @@ // GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) // /* code that triggers warnings C4800 and C4385 */ // GTEST_DISABLE_MSC_WARNINGS_POP_() -#if _MSC_VER >= 1500 +#if _MSC_VER >= 1400 # define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ __pragma(warning(push)) \ __pragma(warning(disable: warnings)) @@ -574,12 +559,28 @@ # define GTEST_DISABLE_MSC_WARNINGS_POP_() #endif +// Clang on Windows does not understand MSVC's pragma warning. +// We need clang-specific way to disable function deprecation warning. +#ifdef __clang__ +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"") +#define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + _Pragma("clang diagnostic pop") +#else +# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +# define GTEST_DISABLE_MSC_DEPRECATED_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + #ifndef GTEST_LANG_CXX11 // gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when // -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a // value for __cplusplus, and recent versions of clang, gcc, and // probably other compilers set that too in C++11 mode. -# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L || _MSC_VER >= 1900 // Compiling in at least C++11 mode. # define GTEST_LANG_CXX11 1 # else @@ -611,12 +612,16 @@ #if GTEST_STDLIB_CXX11 # define GTEST_HAS_STD_BEGIN_AND_END_ 1 # define GTEST_HAS_STD_FORWARD_LIST_ 1 -# define GTEST_HAS_STD_FUNCTION_ 1 +# if !defined(_MSC_VER) || (_MSC_FULL_VER >= 190023824) +// works only with VS2015U2 and better +# define GTEST_HAS_STD_FUNCTION_ 1 +# endif # define GTEST_HAS_STD_INITIALIZER_LIST_ 1 # define GTEST_HAS_STD_MOVE_ 1 -# define GTEST_HAS_STD_SHARED_PTR_ 1 -# define GTEST_HAS_STD_TYPE_TRAITS_ 1 # define GTEST_HAS_STD_UNIQUE_PTR_ 1 +# define GTEST_HAS_STD_SHARED_PTR_ 1 +# define GTEST_HAS_UNORDERED_MAP_ 1 +# define GTEST_HAS_UNORDERED_SET_ 1 #endif // C++11 specifies that provides std::tuple. @@ -624,7 +629,8 @@ #if GTEST_LANG_CXX11 # define GTEST_HAS_STD_TUPLE_ 1 # if defined(__clang__) -// Inspired by http://clang.llvm.org/docs/LanguageExtensions.html#__has_include +// Inspired by +// https://clang.llvm.org/docs/LanguageExtensions.html#include-file-checking-macros # if defined(__has_include) && !__has_include() # undef GTEST_HAS_STD_TUPLE_ # endif @@ -636,7 +642,7 @@ # elif defined(__GLIBCXX__) // Inspired by boost/config/stdlib/libstdcpp3.hpp, // http://gcc.gnu.org/gcc-4.2/changes.html and -// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x +// https://web.archive.org/web/20140227044429/gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x # if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2) # undef GTEST_HAS_STD_TUPLE_ # endif @@ -652,10 +658,16 @@ # include # endif // In order to avoid having to include , use forward declaration -// assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR) +// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two +// separate (equivalent) structs, instead of using typedef +typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#else +// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. // This assumption is verified by // WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. -struct _RTL_CRITICAL_SECTION; +typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; +#endif #else // This assumes that non-Windows OSes provide unistd.h. For OSes where this // is not the case, we need to include headers that provide the functions @@ -709,8 +721,11 @@ struct _RTL_CRITICAL_SECTION; #ifndef GTEST_HAS_EXCEPTIONS // The user didn't tell us whether exceptions are enabled, so we need // to figure it out. -# if defined(_MSC_VER) || defined(__BORLANDC__) -// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS +# if defined(_MSC_VER) && defined(_CPPUNWIND) +// MSVC defines _CPPUNWIND to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__BORLANDC__) +// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS // macro to enable exceptions, so we'll do the same. // Assumes that exceptions are enabled by default. # ifndef _HAS_EXCEPTIONS @@ -754,21 +769,17 @@ struct _RTL_CRITICAL_SECTION; # define GTEST_HAS_STD_STRING 1 #elif !GTEST_HAS_STD_STRING // The user told us that ::std::string isn't available. -# error "Google Test cannot be used where ::std::string isn't available." +# error "::std::string isn't available." #endif // !defined(GTEST_HAS_STD_STRING) #ifndef GTEST_HAS_GLOBAL_STRING -// The user didn't tell us whether ::string is available, so we need -// to figure it out. - # define GTEST_HAS_GLOBAL_STRING 0 - #endif // GTEST_HAS_GLOBAL_STRING #ifndef GTEST_HAS_STD_WSTRING // The user didn't tell us whether ::std::wstring is available, so we need // to figure it out. -// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring +// FIXME: uses autoconf to detect whether ::std::wstring // is available. // Cygwin 1.7 and below doesn't support ::std::wstring. @@ -856,8 +867,9 @@ struct _RTL_CRITICAL_SECTION; // // To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 // to your compiler flags. -# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \ - || GTEST_OS_QNX || GTEST_OS_FREEBSD || GTEST_OS_NACL) +#define GTEST_HAS_PTHREAD \ + (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX || \ + GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA) #endif // GTEST_HAS_PTHREAD #if GTEST_HAS_PTHREAD @@ -872,7 +884,7 @@ struct _RTL_CRITICAL_SECTION; // Determines if hash_map/hash_set are available. // Only used for testing against those containers. #if !defined(GTEST_HAS_HASH_MAP_) -# if _MSC_VER +# if defined(_MSC_VER) && (_MSC_VER < 1900) # define GTEST_HAS_HASH_MAP_ 1 // Indicates that hash_map is available. # define GTEST_HAS_HASH_SET_ 1 // Indicates that hash_set is available. # endif // _MSC_VER @@ -885,6 +897,14 @@ struct _RTL_CRITICAL_SECTION; # if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) // STLport, provided with the Android NDK, has neither or . # define GTEST_HAS_TR1_TUPLE 0 +# elif defined(_MSC_VER) && (_MSC_VER >= 1910) +// Prevent `warning C4996: 'std::tr1': warning STL4002: +// The non-Standard std::tr1 namespace and TR1-only machinery +// are deprecated and will be REMOVED.` +# define GTEST_HAS_TR1_TUPLE 0 +# elif GTEST_LANG_CXX11 && defined(_LIBCPP_VERSION) +// libc++ doesn't support TR1. +# define GTEST_HAS_TR1_TUPLE 0 # else // The user didn't tell us not to do it, so we assume it's OK. # define GTEST_HAS_TR1_TUPLE 1 @@ -894,6 +914,10 @@ struct _RTL_CRITICAL_SECTION; // Determines whether Google Test's own tr1 tuple implementation // should be used. #ifndef GTEST_USE_OWN_TR1_TUPLE +// We use our own tuple implementation on Symbian. +# if GTEST_OS_SYMBIAN +# define GTEST_USE_OWN_TR1_TUPLE 1 +# else // The user didn't tell us, so we need to figure it out. // We use our own TR1 tuple if we aren't sure the user has an @@ -907,7 +931,8 @@ struct _RTL_CRITICAL_SECTION; // support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, // and it can be used with some compilers that define __GNUC__. # if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ - && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) \ + || (_MSC_VER >= 1600 && _MSC_VER < 1900) # define GTEST_ENV_HAS_TR1_TUPLE_ 1 # endif @@ -923,12 +948,11 @@ struct _RTL_CRITICAL_SECTION; # else # define GTEST_USE_OWN_TR1_TUPLE 1 # endif - +# endif // GTEST_OS_SYMBIAN #endif // GTEST_USE_OWN_TR1_TUPLE -// To avoid conditional compilation everywhere, we make it -// gtest-port.h's responsibility to #include the header implementing -// tuple. +// To avoid conditional compilation we make it gtest-port.h's responsibility +// to #include the header implementing tuple. #if GTEST_HAS_STD_TUPLE_ # include // IWYU pragma: export # define GTEST_TUPLE_NAMESPACE_ ::std @@ -974,11 +998,12 @@ struct _RTL_CRITICAL_SECTION; // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) + // Implements a subset of TR1 tuple needed by Google Test and Google Mock. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ @@ -986,7 +1011,7 @@ struct _RTL_CRITICAL_SECTION; // The compiler used in Symbian has a bug that prevents us from declaring the // tuple template as a friend (it complains that tuple is redefined). This -// hack bypasses the bug by declaring the members that should otherwise be +// bypasses the bug by declaring the members that should otherwise be // private as public. // Sun Studio versions < 12 also have the above bug. #if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) @@ -1962,22 +1987,6 @@ inline bool operator!=(const GTEST_10_TUPLE_(T)& t, #undef GTEST_TUPLE_ELEMENT_ #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ -# elif GTEST_ENV_HAS_STD_TUPLE_ -# include -// C++11 puts its tuple into the ::std namespace rather than -// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there. -// This causes undefined behavior, but supported compilers react in -// the way we intend. -namespace std { -namespace tr1 { -using ::std::get; -using ::std::make_tuple; -using ::std::tuple; -using ::std::tuple_element; -using ::std::tuple_size; -} -} - # elif GTEST_OS_SYMBIAN // On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to @@ -2002,20 +2011,22 @@ using ::std::tuple_size; // Until version 4.3.2, gcc has a bug that causes , // which is #included by , to not compile when RTTI is // disabled. _TR1_FUNCTIONAL is the header guard for -// . Hence the following #define is a hack to prevent +// . Hence the following #define is used to prevent // from being included. # define _TR1_FUNCTIONAL 1 # include # undef _TR1_FUNCTIONAL // Allows the user to #include - // if he chooses to. + // if they choose to. # else # include // NOLINT # endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 -# else -// If the compiler is not GCC 4.0+, we assume the user is using a -// spec-conforming TR1 implementation. +// VS 2010 now has tr1 support. +# elif _MSC_VER >= 1600 # include // IWYU pragma: export // NOLINT + +# else // GTEST_USE_OWN_TR1_TUPLE +# include // IWYU pragma: export // NOLINT # endif // GTEST_USE_OWN_TR1_TUPLE #endif // GTEST_HAS_TR1_TUPLE @@ -2029,8 +2040,12 @@ using ::std::tuple_size; # if GTEST_OS_LINUX && !defined(__ia64__) # if GTEST_OS_LINUX_ANDROID -// On Android, clone() is only available on ARM starting with Gingerbread. -# if defined(__arm__) && __ANDROID_API__ >= 9 +// On Android, clone() became available at different API levels for each 32-bit +// architecture. +# if defined(__LP64__) || \ + (defined(__arm__) && __ANDROID_API__ >= 9) || \ + (defined(__mips__) && __ANDROID_API__ >= 12) || \ + (defined(__i386__) && __ANDROID_API__ >= 17) # define GTEST_HAS_CLONE 1 # else # define GTEST_HAS_CLONE 0 @@ -2061,19 +2076,15 @@ using ::std::tuple_size; // Google Test does not support death tests for VC 7.1 and earlier as // abort() in a VC 7.1 application compiled as GUI in debug config // pops up a dialog window that cannot be suppressed programmatically. -#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ - (GTEST_OS_MAC && !GTEST_OS_IOS) || \ - (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ - GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD) + GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD || \ + GTEST_OS_NETBSD || GTEST_OS_FUCHSIA) # define GTEST_HAS_DEATH_TEST 1 #endif -// We don't support MSVC 7.1 with exceptions disabled now. Therefore -// all the compilers we care about are adequate for supporting -// value-parameterized tests. -#define GTEST_HAS_PARAM_TEST 1 - // Determines whether to support type-driven tests. // Typed tests need and variadic macros, which GCC, VC++ 8.0, @@ -2088,7 +2099,7 @@ using ::std::tuple_size; // value-parameterized tests are enabled. The implementation doesn't // work on Sun Studio since it doesn't understand templated conversion // operators. -#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) +#if (GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_) && !defined(__SUNPRO_CC) # define GTEST_HAS_COMBINE 1 #endif @@ -2139,15 +2150,39 @@ using ::std::tuple_size; # define GTEST_ATTRIBUTE_UNUSED_ #endif +#if GTEST_LANG_CXX11 +# define GTEST_CXX11_EQUALS_DELETE_ = delete +#else // GTEST_LANG_CXX11 +# define GTEST_CXX11_EQUALS_DELETE_ +#endif // GTEST_LANG_CXX11 + +// Use this annotation before a function that takes a printf format string. +#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC) +# if defined(__MINGW_PRINTF_FORMAT) +// MinGW has two different printf implementations. Ensure the format macro +// matches the selected implementation. See +// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/. +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \ + first_to_check))) +# else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \ + __attribute__((__format__(__printf__, string_index, first_to_check))) +# endif +#else +# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) +#endif + + // A macro to disallow operator= // This should be used in the private: declarations for a class. -#define GTEST_DISALLOW_ASSIGN_(type)\ - void operator=(type const &) +#define GTEST_DISALLOW_ASSIGN_(type) \ + void operator=(type const &) GTEST_CXX11_EQUALS_DELETE_ // A macro to disallow copy constructor and operator= // This should be used in the private: declarations for a class. -#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\ - type(type const &);\ +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \ + type(type const &) GTEST_CXX11_EQUALS_DELETE_; \ GTEST_DISALLOW_ASSIGN_(type) // Tell the compiler to warn about unused return values for functions declared @@ -2195,6 +2230,11 @@ using ::std::tuple_size; #endif // GTEST_HAS_SEH +// GTEST_API_ qualifies all symbols that must be exported. The definitions below +// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in +// gtest/internal/custom/gtest-port.h +#ifndef GTEST_API_ + #ifdef _MSC_VER # if GTEST_LINKED_AS_SHARED_LIBRARY # define GTEST_API_ __declspec(dllimport) @@ -2203,11 +2243,17 @@ using ::std::tuple_size; # endif #elif __GNUC__ >= 4 || defined(__clang__) # define GTEST_API_ __attribute__((visibility ("default"))) -#endif // _MSC_VER +#endif // _MSC_VER + +#endif // GTEST_API_ #ifndef GTEST_API_ # define GTEST_API_ -#endif +#endif // GTEST_API_ + +#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE +# define GTEST_DEFAULT_DEATH_TEST_STYLE "fast" +#endif // GTEST_DEFAULT_DEATH_TEST_STYLE #ifdef __GNUC__ // Ask the compiler to never inline a given function. @@ -2217,10 +2263,12 @@ using ::std::tuple_size; #endif // _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. -#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) -# define GTEST_HAS_CXXABI_H_ 1 -#else -# define GTEST_HAS_CXXABI_H_ 0 +#if !defined(GTEST_HAS_CXXABI_H_) +# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) +# define GTEST_HAS_CXXABI_H_ 1 +# else +# define GTEST_HAS_CXXABI_H_ 0 +# endif #endif // A function level attribute to disable checking for use of uninitialized @@ -2363,6 +2411,16 @@ struct StaticAssertTypeEqHelper { enum { value = true }; }; +// Same as std::is_same<>. +template +struct IsSame { + enum { value = false }; +}; +template +struct IsSame { + enum { value = true }; +}; + // Evaluates to the number of elements in 'array'. #define GTEST_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0])) @@ -2426,6 +2484,10 @@ class scoped_ptr { // Defines RE. +#if GTEST_USES_PCRE +// if used, PCRE is injected by custom/gtest-port.h +#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE + // A simple C++ wrapper for . It uses the POSIX Extended // Regular Expression syntax. class GTEST_API_ RE { @@ -2437,11 +2499,11 @@ class GTEST_API_ RE { // Constructs an RE from a string. RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT -#if GTEST_HAS_GLOBAL_STRING +# if GTEST_HAS_GLOBAL_STRING RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT -#endif // GTEST_HAS_GLOBAL_STRING +# endif // GTEST_HAS_GLOBAL_STRING RE(const char* regex) { Init(regex); } // NOLINT ~RE(); @@ -2454,7 +2516,7 @@ class GTEST_API_ RE { // PartialMatch(str, re) returns true iff regular expression re // matches a substring of str (including str itself). // - // TODO(wan@google.com): make FullMatch() and PartialMatch() work + // FIXME: make FullMatch() and PartialMatch() work // when str contains NUL characters. static bool FullMatch(const ::std::string& str, const RE& re) { return FullMatch(str.c_str(), re); @@ -2463,7 +2525,7 @@ class GTEST_API_ RE { return PartialMatch(str.c_str(), re); } -#if GTEST_HAS_GLOBAL_STRING +# if GTEST_HAS_GLOBAL_STRING static bool FullMatch(const ::string& str, const RE& re) { return FullMatch(str.c_str(), re); @@ -2472,7 +2534,7 @@ class GTEST_API_ RE { return PartialMatch(str.c_str(), re); } -#endif // GTEST_HAS_GLOBAL_STRING +# endif // GTEST_HAS_GLOBAL_STRING static bool FullMatch(const char* str, const RE& re); static bool PartialMatch(const char* str, const RE& re); @@ -2481,25 +2543,27 @@ class GTEST_API_ RE { void Init(const char* regex); // We use a const char* instead of an std::string, as Google Test used to be - // used where std::string is not available. TODO(wan@google.com): change to + // used where std::string is not available. FIXME: change to // std::string. const char* pattern_; bool is_valid_; -#if GTEST_USES_POSIX_RE +# if GTEST_USES_POSIX_RE regex_t full_regex_; // For FullMatch(). regex_t partial_regex_; // For PartialMatch(). -#else // GTEST_USES_SIMPLE_RE +# else // GTEST_USES_SIMPLE_RE const char* full_pattern_; // For FullMatch(); -#endif +# endif GTEST_DISALLOW_ASSIGN_(RE); }; +#endif // GTEST_USES_PCRE + // Formats a source file path and a line number as they would appear // in an error message from the compiler used to compile this code. GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); @@ -2585,13 +2649,59 @@ inline void FlushInfoLog() { fflush(NULL); } GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ << gtest_error +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template +struct AddReference { typedef T& type; }; // NOLINT +template +struct AddReference { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference::type + +// Transforms "T" into "const T&" according to standard reference collapsing +// rules (this is only needed as a backport for C++98 compilers that do not +// support reference collapsing). Specifically, it transforms: +// +// char ==> const char& +// const char ==> const char& +// char& ==> char& +// const char& ==> const char& +// +// Note that the non-const reference will not have "const" added. This is +// standard, and necessary so that "T" can always bind to "const T&". +template +struct ConstRef { typedef const T& type; }; +template +struct ConstRef { typedef T& type; }; + +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + typename ::testing::internal::ConstRef::type + #if GTEST_HAS_STD_MOVE_ +using std::forward; using std::move; + +template +struct RvalueRef { + typedef T&& type; +}; #else // GTEST_HAS_STD_MOVE_ template const T& move(const T& t) { return t; } +template +GTEST_ADD_REFERENCE_(T) forward(GTEST_ADD_REFERENCE_(T) t) { return t; } + +template +struct RvalueRef { + typedef const T& type; +}; #endif // GTEST_HAS_STD_MOVE_ // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. @@ -2692,10 +2802,6 @@ GTEST_API_ void CaptureStderr(); GTEST_API_ std::string GetCapturedStderr(); #endif // GTEST_HAS_STREAM_REDIRECTION - -// Returns a path to temporary directory. -GTEST_API_ std::string TempDir(); - // Returns the size (in bytes) of a file. GTEST_API_ size_t GetFileSize(FILE* file); @@ -2703,14 +2809,18 @@ GTEST_API_ size_t GetFileSize(FILE* file); GTEST_API_ std::string ReadEntireFile(FILE* file); // All command line arguments. -GTEST_API_ const ::std::vector& GetArgvs(); +GTEST_API_ std::vector GetArgvs(); #if GTEST_HAS_DEATH_TEST -const ::std::vector& GetInjectableArgvs(); -void SetInjectableArgvs(const ::std::vector* - new_argvs); - +std::vector GetInjectableArgvs(); +// Deprecated: pass the args vector by value instead. +void SetInjectableArgvs(const std::vector* new_argvs); +void SetInjectableArgvs(const std::vector& new_argvs); +#if GTEST_HAS_GLOBAL_STRING +void SetInjectableArgvs(const std::vector< ::string>& new_argvs); +#endif // GTEST_HAS_GLOBAL_STRING +void ClearInjectableArgvs(); #endif // GTEST_HAS_DEATH_TEST @@ -2960,7 +3070,7 @@ class GTEST_API_ Mutex { // Initializes owner_thread_id_ and critical_section_ in static mutexes. void ThreadSafeLazyInit(); - // Per http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx, + // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503, // we assume that 0 is an invalid value for thread IDs. unsigned int owner_thread_id_; @@ -2968,7 +3078,7 @@ class GTEST_API_ Mutex { // by the linker. MutexType type_; long critical_section_init_phase_; // NOLINT - _RTL_CRITICAL_SECTION* critical_section_; + GTEST_CRITICAL_SECTION* critical_section_; GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); }; @@ -3244,8 +3354,13 @@ class MutexBase { extern ::testing::internal::MutexBase mutex // Defines and statically (i.e. at link time) initializes a static mutex. -# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ - ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false, pthread_t() } +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0} // The Mutex class can only be used for mutexes created at runtime. It // shares its API with MutexBase otherwise. @@ -3302,7 +3417,7 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) { // Implements thread-local storage on pthreads-based systems. template -class ThreadLocal { +class GTEST_API_ ThreadLocal { public: ThreadLocal() : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {} @@ -3434,7 +3549,7 @@ class GTestMutexLock { typedef GTestMutexLock MutexLock; template -class ThreadLocal { +class GTEST_API_ ThreadLocal { public: ThreadLocal() : value_() {} explicit ThreadLocal(const T& value) : value_(value) {} @@ -3453,12 +3568,13 @@ class ThreadLocal { GTEST_API_ size_t GetThreadCount(); // Passing non-POD classes through ellipsis (...) crashes the ARM -// compiler and generates a warning in Sun Studio. The Nokia Symbian +// compiler and generates a warning in Sun Studio before 12u4. The Nokia Symbian // and the IBM XL C/C++ compiler try to instantiate a copy constructor // for objects passed through ellipsis (...), failing for uncopyable // objects. We define this to ensure that only POD is passed through // ellipsis on these systems. -#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x5130) // We lose support for NULL detection where the compiler doesn't like // passing non-POD classes through ellipsis (...). # define GTEST_ELLIPSIS_NEEDS_POD_ 1 @@ -3484,6 +3600,13 @@ template const bool bool_constant::value; typedef bool_constant false_type; typedef bool_constant true_type; +template +struct is_same : public false_type {}; + +template +struct is_same : public true_type {}; + + template struct is_pointer : public false_type {}; @@ -3495,6 +3618,7 @@ struct IteratorTraits { typedef typename Iterator::value_type value_type; }; + template struct IteratorTraits { typedef T value_type; @@ -3626,7 +3750,7 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } // Functions deprecated by MSVC 8.0. -GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996 /* deprecated function */) +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() inline const char* StrNCpy(char* dest, const char* src, size_t n) { return strncpy(dest, src, n); @@ -3660,7 +3784,7 @@ inline int Close(int fd) { return close(fd); } inline const char* StrError(int errnum) { return strerror(errnum); } #endif inline const char* GetEnv(const char* name) { -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE | GTEST_OS_WINDOWS_RT +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT // We are on Windows CE, which has no environment variables. static_cast(name); // To prevent 'unused argument' warning. return NULL; @@ -3674,7 +3798,7 @@ inline const char* GetEnv(const char* name) { #endif } -GTEST_DISABLE_MSC_WARNINGS_POP_() +GTEST_DISABLE_MSC_DEPRECATED_POP_() #if GTEST_OS_WINDOWS_MOBILE // Windows CE has no C library. The abort() function is used in @@ -3790,15 +3914,15 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. # define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) # define GTEST_DECLARE_int32_(name) \ GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) -#define GTEST_DECLARE_string_(name) \ +# define GTEST_DECLARE_string_(name) \ GTEST_API_ extern ::std::string GTEST_FLAG(name) // Macros for defining flags. -#define GTEST_DEFINE_bool_(name, default_val, doc) \ +# define GTEST_DEFINE_bool_(name, default_val, doc) \ GTEST_API_ bool GTEST_FLAG(name) = (default_val) -#define GTEST_DEFINE_int32_(name, default_val, doc) \ +# define GTEST_DEFINE_int32_(name, default_val, doc) \ GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) -#define GTEST_DEFINE_string_(name, default_val, doc) \ +# define GTEST_DEFINE_string_(name, default_val, doc) \ GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) #endif // !defined(GTEST_DECLARE_bool_) @@ -3812,7 +3936,7 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. // Parses 'str' for a 32-bit signed integer. If successful, writes the result // to *value and returns true; otherwise leaves *value unchanged and returns // false. -// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// FIXME: Find a better way to refactor flag and environment parsing // out of both gtest-port.cc and gtest.cc to avoid exporting this utility // function. bool ParseInt32(const Message& src_text, const char* str, Int32* value); @@ -3821,7 +3945,8 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value); // corresponding to the given Google Test flag. bool BoolFromGTestEnv(const char* flag, bool default_val); GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); -std::string StringFromGTestEnv(const char* flag, const char* default_val); +std::string OutputFlagAlsoCheckEnvVar(); +const char* StringFromGTestEnv(const char* flag, const char* default_val); } // namespace internal } // namespace testing @@ -3877,10 +4002,9 @@ std::string StringFromGTestEnv(const char* flag, const char* default_val); // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // // This header file defines the Message class. // @@ -3894,12 +4018,17 @@ std::string StringFromGTestEnv(const char* flag, const char* default_val); // to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user // program! +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ #define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ #include +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + // Ensures that there is at least one operator<< in the global namespace. // See Message& operator<<(...) below for why. void operator<<(const testing::internal::Secret&, int); @@ -4046,7 +4175,6 @@ class GTEST_API_ Message { std::string GetString() const; private: - #if GTEST_OS_SYMBIAN // These are needed as the Nokia Symbian Compiler cannot decide between // const T& and const T* in a function template. The Nokia compiler _can_ @@ -4097,8 +4225,10 @@ std::string StreamableToString(const T& streamable) { } // namespace internal } // namespace testing +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + #endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ -// Copyright 2005, Google Inc. +// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -4127,17 +4257,59 @@ std::string StreamableToString(const T& streamable) { // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in gtest/internal/gtest-internal.h. +// Do not include this header file separately! + +// GOOGLETEST_CM0001 DO NOT DELETE + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // // This header file declares the String class and functions used internally by // Google Test. They are subject to change without notice. They should not used // by code external to Google Test. // -// This header file is #included by . +// This header file is #included by gtest-internal.h. // It should not be #included by other files. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ @@ -4264,48 +4436,9 @@ GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); } // namespace testing #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: keith.ray@gmail.com (Keith Ray) -// -// Google Test filepath utilities -// -// This header file declares classes and functions used internally by -// Google Test. They are subject to change without notice. -// -// This file is #included in . -// Do not include this header file separately! - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) namespace testing { namespace internal { @@ -4468,6 +4601,8 @@ class GTEST_API_ FilePath { } // namespace internal } // namespace testing +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ // This file was GENERATED by command: // pump.py gtest-type-util.h.pump @@ -4501,8 +4636,7 @@ class GTEST_API_ FilePath { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) + // Type utilities needed for implementing typed and type-parameterized // tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! @@ -4512,6 +4646,8 @@ class GTEST_API_ FilePath { // Please contact googletestframework@googlegroups.com if you need // more. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ @@ -4527,6 +4663,22 @@ class GTEST_API_ FilePath { namespace testing { namespace internal { +// Canonicalizes a given name with respect to the Standard C++ Library. +// This handles removing the inline namespace within `std` that is +// used by various standard libraries (e.g., `std::__1`). Names outside +// of namespace std are returned unmodified. +inline std::string CanonicalizeForStdLibVersioning(std::string s) { + static const char prefix[] = "std::__"; + if (s.compare(0, strlen(prefix), prefix) == 0) { + std::string::size_type end = s.find("::", strlen(prefix)); + if (end != s.npos) { + // Erase everything between the initial `std` and the second `::`. + s.erase(strlen("std"), end - strlen("std")); + } + } + return s; +} + // GetTypeName() returns a human-readable name of type T. // NB: This function is also used in Google Mock, so don't move it inside of // the typed-test-only section below. @@ -4545,7 +4697,7 @@ std::string GetTypeName() { char* const readable_name = __cxa_demangle(name, 0, 0, &status); const std::string name_str(status == 0 ? readable_name : name); free(readable_name); - return name_str; + return CanonicalizeForStdLibVersioning(name_str); # else return name; # endif // GTEST_HAS_CXXABI_H_ || __HP_aCC @@ -7811,6 +7963,9 @@ struct TypeList namespace internal { struct TraceInfo; // Information about a trace point. -class ScopedTrace; // Implements scoped trace. class TestInfoImpl; // Opaque implementation of TestInfo class UnitTestImpl; // Opaque implementation of UnitTest @@ -7874,6 +8028,9 @@ GTEST_API_ std::string AppendUserMessage( #if GTEST_HAS_EXCEPTIONS +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4275 \ +/* an exported class was derived from a class that was not exported */) + // This exception is thrown by (and only by) a failed Google Test // assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions // are enabled). We derive it from std::runtime_error, which is for @@ -7885,32 +8042,15 @@ class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { explicit GoogleTestFailureException(const TestPartResult& failure); }; -#endif // GTEST_HAS_EXCEPTIONS - -// A helper class for creating scoped traces in user programs. -class GTEST_API_ ScopedTrace { - public: - // The c'tor pushes the given source file location and message onto - // a trace stack maintained by Google Test. - ScopedTrace(const char* file, int line, const Message& message); - - // The d'tor pops the info pushed by the c'tor. - // - // Note that the d'tor is not virtual in order to be efficient. - // Don't inherit from ScopedTrace! - ~ScopedTrace(); +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4275 - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); -} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its - // c'tor and d'tor. Therefore it doesn't - // need to be used otherwise. +#endif // GTEST_HAS_EXCEPTIONS namespace edit_distance { // Returns the optimal edits to go from 'left' to 'right'. // All edits cost the same, with replace having lower priority than // add/remove. -// Simple implementation of the Wagner–Fischer algorithm. +// Simple implementation of the Wagner-Fischer algorithm. // See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm enum EditType { kMatch, kAdd, kRemove, kReplace }; GTEST_API_ std::vector CalculateOptimalEdits( @@ -8237,9 +8377,10 @@ typedef void (*SetUpTestCaseFunc)(); typedef void (*TearDownTestCaseFunc)(); struct CodeLocation { - CodeLocation(const string& a_file, int a_line) : file(a_file), line(a_line) {} + CodeLocation(const std::string& a_file, int a_line) + : file(a_file), line(a_line) {} - string file; + std::string file; int line; }; @@ -8279,6 +8420,9 @@ GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); #if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + // State of the definition of a type-parameterized test case. class GTEST_API_ TypedTestCasePState { public: @@ -8324,6 +8468,8 @@ class GTEST_API_ TypedTestCasePState { RegisteredTestsMap registered_tests_; }; +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + // Skips to the first non-space char after the first comma in 'str'; // returns NULL if no comma is found in 'str'. inline const char* SkipComma(const char* str) { @@ -8347,6 +8493,37 @@ inline std::string GetPrefixUntilComma(const char* str) { void SplitString(const ::std::string& str, char delimiter, ::std::vector< ::std::string>* dest); +// The default argument to the template below for the case when the user does +// not provide a name generator. +struct DefaultNameGenerator { + template + static std::string GetName(int i) { + return StreamableToString(i); + } +}; + +template +struct NameGeneratorSelector { + typedef Provided type; +}; + +template +void GenerateNamesRecursively(Types0, std::vector*, int) {} + +template +void GenerateNamesRecursively(Types, std::vector* result, int i) { + result->push_back(NameGenerator::template GetName(i)); + GenerateNamesRecursively(typename Types::Tail(), result, + i + 1); +} + +template +std::vector GenerateNames() { + std::vector result; + GenerateNamesRecursively(Types(), &result, 0); + return result; +} + // TypeParameterizedTest::Register() // registers a list of type-parameterized tests with Google Test. The // return value is insignificant - we just need to return something @@ -8361,10 +8538,10 @@ class TypeParameterizedTest { // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, // Types). Valid values for 'index' are [0, N - 1] where N is the // length of Types. - static bool Register(const char* prefix, - CodeLocation code_location, - const char* case_name, const char* test_names, - int index) { + static bool Register(const char* prefix, const CodeLocation& code_location, + const char* case_name, const char* test_names, int index, + const std::vector& type_names = + GenerateNames()) { typedef typename Types::Head Type; typedef Fixture FixtureClass; typedef typename GTEST_BIND_(TestSel, Type) TestClass; @@ -8372,20 +8549,23 @@ class TypeParameterizedTest { // First, registers the first type-parameterized test in the type // list. MakeAndRegisterTestInfo( - (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" - + StreamableToString(index)).c_str(), + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + + "/" + type_names[index]) + .c_str(), StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), GetTypeName().c_str(), NULL, // No value parameter. - code_location, - GetTypeId(), - TestClass::SetUpTestCase, - TestClass::TearDownTestCase, - new TestFactoryImpl); + code_location, GetTypeId(), TestClass::SetUpTestCase, + TestClass::TearDownTestCase, new TestFactoryImpl); // Next, recurses (at compile time) with the tail of the type list. - return TypeParameterizedTest - ::Register(prefix, code_location, case_name, test_names, index + 1); + return TypeParameterizedTest::Register(prefix, + code_location, + case_name, + test_names, + index + 1, + type_names); } }; @@ -8393,9 +8573,11 @@ class TypeParameterizedTest { template class TypeParameterizedTest { public: - static bool Register(const char* /*prefix*/, CodeLocation, + static bool Register(const char* /*prefix*/, const CodeLocation&, const char* /*case_name*/, const char* /*test_names*/, - int /*index*/) { + int /*index*/, + const std::vector& = + std::vector() /*type_names*/) { return true; } }; @@ -8408,8 +8590,10 @@ template class TypeParameterizedTestCase { public: static bool Register(const char* prefix, CodeLocation code_location, - const TypedTestCasePState* state, - const char* case_name, const char* test_names) { + const TypedTestCasePState* state, const char* case_name, + const char* test_names, + const std::vector& type_names = + GenerateNames()) { std::string test_name = StripTrailingSpaces( GetPrefixUntilComma(test_names)); if (!state->TestExists(test_name)) { @@ -8426,12 +8610,14 @@ class TypeParameterizedTestCase { // First, register the first test in 'Test' for each type in 'Types'. TypeParameterizedTest::Register( - prefix, test_location, case_name, test_names, 0); + prefix, test_location, case_name, test_names, 0, type_names); // Next, recurses (at compile time) with the tail of the test list. - return TypeParameterizedTestCase - ::Register(prefix, code_location, state, - case_name, SkipComma(test_names)); + return TypeParameterizedTestCase::Register(prefix, code_location, + state, case_name, + SkipComma(test_names), + type_names); } }; @@ -8439,9 +8625,11 @@ class TypeParameterizedTestCase { template class TypeParameterizedTestCase { public: - static bool Register(const char* /*prefix*/, CodeLocation, + static bool Register(const char* /*prefix*/, const CodeLocation&, const TypedTestCasePState* /*state*/, - const char* /*case_name*/, const char* /*test_names*/) { + const char* /*case_name*/, const char* /*test_names*/, + const std::vector& = + std::vector() /*type_names*/) { return true; } }; @@ -8558,31 +8746,6 @@ struct RemoveConst { #define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) -// Adds reference to a type if it is not a reference type, -// otherwise leaves it unchanged. This is the same as -// tr1::add_reference, which is not widely available yet. -template -struct AddReference { typedef T& type; }; // NOLINT -template -struct AddReference { typedef T& type; }; // NOLINT - -// A handy wrapper around AddReference that works when the argument T -// depends on template parameters. -#define GTEST_ADD_REFERENCE_(T) \ - typename ::testing::internal::AddReference::type - -// Adds a reference to const on top of T as necessary. For example, -// it transforms -// -// char ==> const char& -// const char ==> const char& -// char& ==> const char& -// const char& ==> const char& -// -// The argument T must depend on some template parameters. -#define GTEST_REFERENCE_TO_CONST_(T) \ - GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) - // ImplicitlyConvertible::value is a compile-time bool // constant that's true iff type From can be implicitly converted to // type To. @@ -8652,8 +8815,11 @@ struct IsAProtocolMessage // a container class by checking the type of IsContainerTest(0). // The value of the expression is insignificant. // -// Note that we look for both C::iterator and C::const_iterator. The -// reason is that C++ injects the name of a class as a member of the +// In C++11 mode we check the existence of a const_iterator and that an +// iterator is properly implemented for the container. +// +// For pre-C++11 that we look for both C::iterator and C::const_iterator. +// The reason is that C++ injects the name of a class as a member of the // class itself (e.g. you can refer to class iterator as either // 'iterator' or 'iterator::iterator'). If we look for C::iterator // only, for example, we would mistakenly think that a class named @@ -8663,17 +8829,96 @@ struct IsAProtocolMessage // IsContainerTest(typename C::const_iterator*) and // IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. typedef int IsContainer; +#if GTEST_LANG_CXX11 +template ().begin()), + class = decltype(::std::declval().end()), + class = decltype(++::std::declval()), + class = decltype(*::std::declval()), + class = typename C::const_iterator> +IsContainer IsContainerTest(int /* dummy */) { + return 0; +} +#else template IsContainer IsContainerTest(int /* dummy */, typename C::iterator* /* it */ = NULL, typename C::const_iterator* /* const_it */ = NULL) { return 0; } +#endif // GTEST_LANG_CXX11 typedef char IsNotContainer; template IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } +// Trait to detect whether a type T is a hash table. +// The heuristic used is that the type contains an inner type `hasher` and does +// not contain an inner type `reverse_iterator`. +// If the container is iterable in reverse, then order might actually matter. +template +struct IsHashTable { + private: + template + static char test(typename U::hasher*, typename U::reverse_iterator*); + template + static int test(typename U::hasher*, ...); + template + static char test(...); + + public: + static const bool value = sizeof(test(0, 0)) == sizeof(int); +}; + +template +const bool IsHashTable::value; + +template +struct VoidT { + typedef void value_type; +}; + +template +struct HasValueType : false_type {}; +template +struct HasValueType > : true_type { +}; + +template (0)) == sizeof(IsContainer), + bool = HasValueType::value> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public false_type {}; + +// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to +// obey the same inconsistencies as the IsContainerTest, namely check if +// something is a container is relying on only const_iterator in C++11 and +// is relying on both const_iterator and iterator otherwise +template +struct IsRecursiveContainerImpl : public false_type {}; + +template +struct IsRecursiveContainerImpl { + #if GTEST_LANG_CXX11 + typedef typename IteratorTraits::value_type + value_type; +#else + typedef typename IteratorTraits::value_type value_type; +#endif + typedef is_same type; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + // EnableIf::type is void when 'Cond' is true, and // undefined when 'Cond' is false. To use SFINAE to make a function // overload only apply when a particular expression is true, add @@ -8805,7 +9050,7 @@ class NativeArray { private: enum { kCheckTypeIsNotConstOrAReference = StaticAssertTypeEqHelper< - Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value, + Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value }; // Initializes this object with a copy of the input. @@ -8850,7 +9095,7 @@ class NativeArray { #define GTEST_SUCCESS_(message) \ GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) -// Suppresses MSVC warnings 4072 (unreachable code) for the code following +// Suppress MSVC warning 4702 (unreachable code) for the code following // statement if it returns or throws (or doesn't return or throw in some // situations). #define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ @@ -8970,7 +9215,6 @@ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ - // Copyright 2005, Google Inc. // All rights reserved. // @@ -8999,14 +9243,14 @@ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // // This header file defines the public API for death tests. It is // #included by gtest.h so a user doesn't need to include this // directly. +// GOOGLETEST_CM0001 DO NOT DELETE #ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ #define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ @@ -9040,12 +9284,11 @@ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // // This header file defines internal utilities needed for implementing // death tests. They are subject to change without notice. +// GOOGLETEST_CM0001 DO NOT DELETE #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ @@ -9065,6 +9308,9 @@ const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; #if GTEST_HAS_DEATH_TEST +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + // DeathTest is a class that hides much of the complexity of the // GTEST_DEATH_TEST_ macro. It is abstract; its static Create method // returns a concrete class that depends on the prevailing death test @@ -9148,6 +9394,8 @@ class GTEST_API_ DeathTest { GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); }; +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + // Factory interface for death tests. May be mocked out for testing. class DeathTestFactory { public: @@ -9230,14 +9478,18 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status); // can be streamed. // This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in -// NDEBUG mode. In this case we need the statements to be executed, the regex is -// ignored, and the macro must accept a streamed message even though the message -// is never printed. -# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } else \ +// NDEBUG mode. In this case we need the statements to be executed and the macro +// must accept a streamed message even though the message is never printed. +// The regex object is not evaluated, but it is used to prevent "unused" +// warnings and to avoid an expression that doesn't compile in debug mode. +#define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else if (!::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + static_cast(gtest_regex); \ + } else \ ::testing::Message() // A class representing the parsed contents of the @@ -9276,53 +9528,6 @@ class InternalRunDeathTestFlag { // the flag is specified; otherwise returns NULL. InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); -#else // GTEST_HAS_DEATH_TEST - -// This macro is used for implementing macros such as -// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where -// death tests are not supported. Those macros must compile on such systems -// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on -// systems that support death tests. This allows one to write such a macro -// on a system that does not support death tests and be sure that it will -// compile on a death-test supporting system. -// -// Parameters: -// statement - A statement that a macro such as EXPECT_DEATH would test -// for program termination. This macro has to make sure this -// statement is compiled but not executed, to ensure that -// EXPECT_DEATH_IF_SUPPORTED compiles with a certain -// parameter iff EXPECT_DEATH compiles with it. -// regex - A regex that a macro such as EXPECT_DEATH would use to test -// the output of statement. This parameter has to be -// compiled but not evaluated by this macro, to ensure that -// this macro only accepts expressions that a macro such as -// EXPECT_DEATH would accept. -// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED -// and a return statement for ASSERT_DEATH_IF_SUPPORTED. -// This ensures that ASSERT_DEATH_IF_SUPPORTED will not -// compile inside functions where ASSERT_DEATH doesn't -// compile. -// -// The branch that has an always false condition is used to ensure that -// statement and regex are compiled (and thus syntactically correct) but -// never executed. The unreachable code macro protects the terminator -// statement from generating an 'unreachable code' warning in case -// statement unconditionally returns or throws. The Message constructor at -// the end allows the syntax of streaming additional messages into the -// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. -# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - GTEST_LOG_(WARNING) \ - << "Death tests are not supported on this platform.\n" \ - << "Statement '" #statement "' cannot be verified."; \ - } else if (::testing::internal::AlwaysFalse()) { \ - ::testing::internal::RE::PartialMatch(".*", (regex)); \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - terminator; \ - } else \ - ::testing::Message() - #endif // GTEST_HAS_DEATH_TEST } // namespace internal @@ -9389,10 +9594,11 @@ GTEST_API_ bool InDeathTestChild(); // // On the regular expressions used in death tests: // +// GOOGLETEST_CM0005 DO NOT DELETE // On POSIX-compliant systems (*nix), we use the library, // which uses the POSIX extended regex syntax. // -// On other platforms (e.g. Windows), we only support a simple regex +// On other platforms (e.g. Windows or Mac), we only support a simple regex // syntax implemented as part of Google Test. This limited // implementation should be enough most of the time when writing // death tests; though it lacks many features you can find in PCRE @@ -9450,7 +9656,7 @@ GTEST_API_ bool InDeathTestChild(); // is rarely a problem as people usually don't put the test binary // directory in PATH. // -// TODO(wan@google.com): make thread-safe death tests search the PATH. +// FIXME: make thread-safe death tests search the PATH. // Asserts that a given statement causes the program to exit, with an // integer exit status that satisfies predicate, and emitting error output @@ -9488,9 +9694,10 @@ class GTEST_API_ ExitedWithCode { const int exit_code_; }; -# if !GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA // Tests that an exit code describes an exit due to termination by a // given signal. +// GOOGLETEST_CM0006 DO NOT DELETE class GTEST_API_ KilledBySignal { public: explicit KilledBySignal(int signum); @@ -9562,6 +9769,54 @@ class GTEST_API_ KilledBySignal { # endif // NDEBUG for EXPECT_DEBUG_DEATH #endif // GTEST_HAS_DEATH_TEST +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on +// systems that support death tests. This allows one to write such a macro +// on a system that does not support death tests and be sure that it will +// compile on a death-test supporting system. It is exposed publicly so that +// systems that have death-tests with stricter requirements than +// GTEST_HAS_DEATH_TEST can write their own equivalent of +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter iff EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + // EXPECT_DEATH_IF_SUPPORTED(statement, regex) and // ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if // death tests are supported; otherwise they just issue a warning. This is @@ -9574,9 +9829,9 @@ class GTEST_API_ KilledBySignal { ASSERT_DEATH(statement, regex) #else # define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ - GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, ) # define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ - GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) + GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return) #endif } // namespace testing @@ -9615,13 +9870,12 @@ class GTEST_API_ KilledBySignal { // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Authors: vladl@google.com (Vlad Losev) -// // Macros and functions for implementing parameterized tests -// in Google C++ Testing Framework (Google Test) +// in Google C++ Testing and Mocking Framework (Google Test) // // This file is generated by a SCRIPT. DO NOT EDIT BY HAND! // +// GOOGLETEST_CM0001 DO NOT DELETE #ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ #define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ @@ -9663,7 +9917,7 @@ TEST_P(FooTest, HasBlahBlah) { // Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test // case with any set of parameters you want. Google Test defines a number // of functions for generating test parameters. They return what we call -// (surprise!) parameter generators. Here is a summary of them, which +// (surprise!) parameter generators. Here is a summary of them, which // are all in the testing namespace: // // @@ -9768,9 +10022,6 @@ TEST_P(DerivedTest, DoesBlah) { # include #endif -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. // Copyright 2008 Google Inc. // All Rights Reserved. // @@ -9799,11 +10050,12 @@ TEST_P(DerivedTest, DoesBlah) { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: vladl@google.com (Vlad Losev) + // Type and function utilities for implementing parameterized tests. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ @@ -9814,9 +10066,6 @@ TEST_P(DerivedTest, DoesBlah) { #include #include -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. // Copyright 2003 Google Inc. // All rights reserved. // @@ -9846,8 +10095,6 @@ TEST_P(DerivedTest, DoesBlah) { // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Authors: Dan Egnor (egnor@google.com) -// // A "smart" pointer type with reference tracking. Every pointer to a // particular object is kept on a circular linked list. When the last pointer // to an object is destroyed or reassigned, the object is deleted. @@ -9881,9 +10128,11 @@ TEST_P(DerivedTest, DoesBlah) { // raw pointer (e.g. via get()) concurrently, and // - it's safe to write to two linked_ptrs that point to the same // shared object concurrently. -// TODO(wan@google.com): rename this to safe_linked_ptr to avoid +// FIXME: rename this to safe_linked_ptr to avoid // confusion with normal linked_ptr. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ @@ -10087,10 +10336,9 @@ linked_ptr make_linked_ptr(T* ptr) { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// Google Test - The Google C++ Testing Framework + +// Google Test - The Google C++ Testing and Mocking Framework // // This file implements a universal value printer that can print a // value of any type T: @@ -10107,6 +10355,10 @@ linked_ptr make_linked_ptr(T* ptr) { // 2. operator<<(ostream&, const T&) defined in either foo or the // global namespace. // +// However if T is an STL-style container then it is printed element-wise +// unless foo::PrintTo(const T&, ostream*) is defined. Note that +// operator<<() is ignored for container types. +// // If none of the above is defined, it will print the debug string of // the value if it is a protocol buffer, or print the raw bytes in the // value otherwise. @@ -10153,6 +10405,8 @@ linked_ptr make_linked_ptr(T* ptr) { // being defined as many user-defined container types don't have // value_type. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ #define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ @@ -10166,6 +10420,12 @@ linked_ptr make_linked_ptr(T* ptr) { # include #endif +#if GTEST_HAS_ABSL +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "absl/types/variant.h" +#endif // GTEST_HAS_ABSL + namespace testing { // Definitions in the 'internal' and 'internal2' name spaces are @@ -10184,7 +10444,11 @@ enum TypeKind { kProtobuf, // a protobuf type kConvertibleToInteger, // a type implicitly convertible to BiggestInt // (e.g. a named or unnamed enum type) - kOtherType // anything else +#if GTEST_HAS_ABSL + kConvertibleToStringView, // a type implicitly convertible to + // absl::string_view +#endif + kOtherType // anything else }; // TypeWithoutFormatter::PrintValue(value, os) is called @@ -10196,7 +10460,8 @@ class TypeWithoutFormatter { public: // This default version is called when kTypeKind is kOtherType. static void PrintValue(const T& value, ::std::ostream* os) { - PrintBytesInObjectTo(reinterpret_cast(&value), + PrintBytesInObjectTo(static_cast( + reinterpret_cast(&value)), sizeof(value), os); } }; @@ -10210,10 +10475,10 @@ template class TypeWithoutFormatter { public: static void PrintValue(const T& value, ::std::ostream* os) { - const ::testing::internal::string short_str = value.ShortDebugString(); - const ::testing::internal::string pretty_str = - short_str.length() <= kProtobufOneLinerMaxLength ? - short_str : ("\n" + value.DebugString()); + std::string pretty_str = value.ShortDebugString(); + if (pretty_str.length() > kProtobufOneLinerMaxLength) { + pretty_str = "\n" + value.DebugString(); + } *os << ("<" + pretty_str + ">"); } }; @@ -10234,6 +10499,19 @@ class TypeWithoutFormatter { } }; +#if GTEST_HAS_ABSL +template +class TypeWithoutFormatter { + public: + // Since T has neither operator<< nor PrintTo() but can be implicitly + // converted to absl::string_view, we print it as a absl::string_view. + // + // Note: the implementation is further below, as it depends on + // internal::PrintTo symbol which is defined later in the file. + static void PrintValue(const T& value, ::std::ostream* os); +}; +#endif + // Prints the given value to the given ostream. If the value is a // protocol message, its debug string is printed; if it's an enum or // of a type implicitly convertible to BiggestInt, it's printed as an @@ -10261,10 +10539,19 @@ class TypeWithoutFormatter { template ::std::basic_ostream& operator<<( ::std::basic_ostream& os, const T& x) { - TypeWithoutFormatter::value ? kProtobuf : - internal::ImplicitlyConvertible::value ? - kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); + TypeWithoutFormatter::value + ? kProtobuf + : internal::ImplicitlyConvertible< + const T&, internal::BiggestInt>::value + ? kConvertibleToInteger + : +#if GTEST_HAS_ABSL + internal::ImplicitlyConvertible< + const T&, absl::string_view>::value + ? kConvertibleToStringView + : +#endif + kOtherType)>::PrintValue(x, &os); return os; } @@ -10423,11 +10710,18 @@ class UniversalPrinter; template void UniversalPrint(const T& value, ::std::ostream* os); +enum DefaultPrinterType { + kPrintContainer, + kPrintPointer, + kPrintFunctionPointer, + kPrintOther, +}; +template struct WrapPrinterType {}; + // Used to print an STL-style container when the user doesn't define // a PrintTo() for it. template -void DefaultPrintTo(IsContainer /* dummy */, - false_type /* is not a pointer */, +void DefaultPrintTo(WrapPrinterType /* dummy */, const C& container, ::std::ostream* os) { const size_t kMaxCount = 32; // The maximum number of elements to print. *os << '{'; @@ -10460,40 +10754,34 @@ void DefaultPrintTo(IsContainer /* dummy */, // implementation-defined. Therefore they will be printed as raw // bytes.) template -void DefaultPrintTo(IsNotContainer /* dummy */, - true_type /* is a pointer */, +void DefaultPrintTo(WrapPrinterType /* dummy */, T* p, ::std::ostream* os) { if (p == NULL) { *os << "NULL"; } else { - // C++ doesn't allow casting from a function pointer to any object - // pointer. - // - // IsTrue() silences warnings: "Condition is always true", - // "unreachable code". - if (IsTrue(ImplicitlyConvertible::value)) { - // T is not a function type. We just call << to print p, - // relying on ADL to pick up user-defined << for their pointer - // types, if any. - *os << p; - } else { - // T is a function type, so '*os << p' doesn't do what we want - // (it just prints p as bool). We want to print p as a const - // void*. However, we cannot cast it to const void* directly, - // even using reinterpret_cast, as earlier versions of gcc - // (e.g. 3.4.5) cannot compile the cast when p is a function - // pointer. Casting to UInt64 first solves the problem. - *os << reinterpret_cast( - reinterpret_cast(p)); - } + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } +} +template +void DefaultPrintTo(WrapPrinterType /* dummy */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. + *os << reinterpret_cast(p); } } // Used to print a non-container, non-pointer value when the user // doesn't define PrintTo() for it. template -void DefaultPrintTo(IsNotContainer /* dummy */, - false_type /* is not a pointer */, +void DefaultPrintTo(WrapPrinterType /* dummy */, const T& value, ::std::ostream* os) { ::testing_internal::DefaultPrintNonContainerTo(value, os); } @@ -10511,11 +10799,8 @@ void DefaultPrintTo(IsNotContainer /* dummy */, // wants). template void PrintTo(const T& value, ::std::ostream* os) { - // DefaultPrintTo() is overloaded. The type of its first two - // arguments determine which version will be picked. If T is an - // STL-style container, the version for container will be called; if - // T is a pointer, the pointer version will be called; otherwise the - // generic version will be called. + // DefaultPrintTo() is overloaded. The type of its first argument + // determines which version will be picked. // // Note that we check for container types here, prior to we check // for protocol message types in our operator<<. The rationale is: @@ -10527,13 +10812,27 @@ void PrintTo(const T& value, ::std::ostream* os) { // elements; therefore we check for container types here to ensure // that our format is used. // - // The second argument of DefaultPrintTo() is needed to bypass a bug - // in Symbian's C++ compiler that prevents it from picking the right - // overload between: - // - // PrintTo(const T& x, ...); - // PrintTo(T* x, ...); - DefaultPrintTo(IsContainerTest(0), is_pointer(), value, os); + // Note that MSVC and clang-cl do allow an implicit conversion from + // pointer-to-function to pointer-to-object, but clang-cl warns on it. + // So don't use ImplicitlyConvertible if it can be helped since it will + // cause this warning, and use a separate overload of DefaultPrintTo for + // function pointers so that the `*os << p` in the object pointer overload + // doesn't cause that warning either. + DefaultPrintTo( + WrapPrinterType < + (sizeof(IsContainerTest(0)) == sizeof(IsContainer)) && + !IsRecursiveContainer::value + ? kPrintContainer + : !is_pointer::value + ? kPrintOther +#if GTEST_LANG_CXX11 + : std::is_function::type>::value +#else + : !internal::ImplicitlyConvertible::value +#endif + ? kPrintFunctionPointer + : kPrintPointer > (), + value, os); } // The following list of PrintTo() overloads tells @@ -10640,6 +10939,17 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { } #endif // GTEST_HAS_STD_WSTRING +#if GTEST_HAS_ABSL +// Overload for absl::string_view. +inline void PrintTo(absl::string_view sp, ::std::ostream* os) { + PrintTo(::std::string(sp), os); +} +#endif // GTEST_HAS_ABSL + +#if GTEST_LANG_CXX11 +inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; } +#endif // GTEST_LANG_CXX11 + #if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ // Helper function for printing a tuple. T must be instantiated with // a tuple type. @@ -10769,6 +11079,48 @@ class UniversalPrinter { GTEST_DISABLE_MSC_WARNINGS_POP_() }; +#if GTEST_HAS_ABSL + +// Printer for absl::optional + +template +class UniversalPrinter<::absl::optional> { + public: + static void Print(const ::absl::optional& value, ::std::ostream* os) { + *os << '('; + if (!value) { + *os << "nullopt"; + } else { + UniversalPrint(*value, os); + } + *os << ')'; + } +}; + +// Printer for absl::variant + +template +class UniversalPrinter<::absl::variant> { + public: + static void Print(const ::absl::variant& value, ::std::ostream* os) { + *os << '('; + absl::visit(Visitor{os}, value); + *os << ')'; + } + + private: + struct Visitor { + template + void operator()(const U& u) const { + *os << "'" << GetTypeName() << "' with value "; + UniversalPrint(u, os); + } + ::std::ostream* os; + }; +}; + +#endif // GTEST_HAS_ABSL + // UniversalPrintArray(begin, len, os) prints an array of 'len' // elements, starting at address 'begin'. template @@ -10782,7 +11134,7 @@ void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { // If the array has more than kThreshold elements, we'll have to // omit some details by printing only the first and the last // kChunkSize elements. - // TODO(wan@google.com): let the user control the threshold using a flag. + // FIXME: let the user control the threshold using a flag. if (len <= kThreshold) { PrintRawArrayTo(begin, len, os); } else { @@ -10864,7 +11216,7 @@ class UniversalTersePrinter { if (str == NULL) { *os << "NULL"; } else { - UniversalPrint(string(str), os); + UniversalPrint(std::string(str), os); } } }; @@ -10915,7 +11267,7 @@ void UniversalPrint(const T& value, ::std::ostream* os) { UniversalPrinter::Print(value, os); } -typedef ::std::vector Strings; +typedef ::std::vector< ::std::string> Strings; // TuplePolicy must provide: // - tuple_size @@ -10934,12 +11286,13 @@ struct TuplePolicy { static const size_t tuple_size = ::std::tr1::tuple_size::value; template - struct tuple_element : ::std::tr1::tuple_element {}; + struct tuple_element : ::std::tr1::tuple_element(I), Tuple> { + }; template - static typename AddReference< - const typename ::std::tr1::tuple_element::type>::type get( - const Tuple& tuple) { + static typename AddReference(I), Tuple>::type>::type + get(const Tuple& tuple) { return ::std::tr1::get(tuple); } }; @@ -11035,6 +11388,16 @@ Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { } // namespace internal +#if GTEST_HAS_ABSL +namespace internal2 { +template +void TypeWithoutFormatter::PrintValue( + const T& value, ::std::ostream* os) { + internal::PrintTo(absl::string_view(value), os); +} +} // namespace internal2 +#endif + template ::std::string PrintToString(const T& value) { ::std::stringstream ss; @@ -11080,8 +11443,8 @@ template // installation of gTest. // It will be included from gtest-printers.h and the overrides in this file // will be visible to everyone. -// See documentation at gtest/gtest-printers.h for details on how to define a -// custom printer. +// +// Injection point for custom user configurations. See README for details // // ** Custom implementation starts here ** @@ -11092,8 +11455,6 @@ template #endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ -#if GTEST_HAS_PARAM_TEST - namespace testing { // Input to a parameterized test name generator, describing a test parameter. @@ -11515,7 +11876,7 @@ class ParameterizedTestCaseInfoBase { virtual ~ParameterizedTestCaseInfoBase() {} // Base part of test case name for display purposes. - virtual const string& GetTestCaseName() const = 0; + virtual const std::string& GetTestCaseName() const = 0; // Test case id to verify identity. virtual TypeId GetTestCaseTypeId() const = 0; // UnitTest class invokes this method to register tests in this @@ -11554,7 +11915,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { : test_case_name_(name), code_location_(code_location) {} // Test case base name for display purposes. - virtual const string& GetTestCaseName() const { return test_case_name_; } + virtual const std::string& GetTestCaseName() const { return test_case_name_; } // Test case id to verify identity. virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } // TEST_P macro uses AddTestPattern() to record information @@ -11572,11 +11933,10 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { } // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information // about a generator. - int AddTestCaseInstantiation(const string& instantiation_name, + int AddTestCaseInstantiation(const std::string& instantiation_name, GeneratorCreationFunc* func, ParamNameGeneratorFunc* name_func, - const char* file, - int line) { + const char* file, int line) { instantiations_.push_back( InstantiationInfo(instantiation_name, func, name_func, file, line)); return 0; // Return value used only to run this method in namespace scope. @@ -11593,13 +11953,13 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { for (typename InstantiationContainer::iterator gen_it = instantiations_.begin(); gen_it != instantiations_.end(); ++gen_it) { - const string& instantiation_name = gen_it->name; + const std::string& instantiation_name = gen_it->name; ParamGenerator generator((*gen_it->generator)()); ParamNameGeneratorFunc* name_func = gen_it->name_func; const char* file = gen_it->file; int line = gen_it->line; - string test_case_name; + std::string test_case_name; if ( !instantiation_name.empty() ) test_case_name = instantiation_name + "/"; test_case_name += test_info->test_case_base_name; @@ -11652,8 +12012,8 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { test_base_name(a_test_base_name), test_meta_factory(a_test_meta_factory) {} - const string test_case_base_name; - const string test_base_name; + const std::string test_case_base_name; + const std::string test_base_name; const scoped_ptr > test_meta_factory; }; typedef ::std::vector > TestInfoContainer; @@ -11694,7 +12054,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { return true; } - const string test_case_name_; + const std::string test_case_name_; CodeLocation code_location_; TestInfoContainer tests_; InstantiationContainer instantiations_; @@ -11769,8 +12129,6 @@ class ParameterizedTestCaseRegistry { } // namespace internal } // namespace testing -#endif // GTEST_HAS_PARAM_TEST - #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ // This file was GENERATED by command: // pump.py gtest-param-util-generated.h.pump @@ -11804,8 +12162,7 @@ class ParameterizedTestCaseRegistry { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: vladl@google.com (Vlad Losev) + // Type and function utilities for implementing parameterized tests. // This file is generated by a SCRIPT. DO NOT EDIT BY HAND! @@ -11817,14 +12174,11 @@ class ParameterizedTestCaseRegistry { // by the maximum arity of the implementation of tuple which is // currently set at 10. +// GOOGLETEST_CM0001 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. - -#if GTEST_HAS_PARAM_TEST namespace testing { @@ -11856,6 +12210,8 @@ class ValueArray1 { return ValuesIn(array); } + ValueArray1(const ValueArray1& other) : v1_(other.v1_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray1& other); @@ -11874,6 +12230,8 @@ class ValueArray2 { return ValuesIn(array); } + ValueArray2(const ValueArray2& other) : v1_(other.v1_), v2_(other.v2_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray2& other); @@ -11894,6 +12252,9 @@ class ValueArray3 { return ValuesIn(array); } + ValueArray3(const ValueArray3& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray3& other); @@ -11916,6 +12277,9 @@ class ValueArray4 { return ValuesIn(array); } + ValueArray4(const ValueArray4& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray4& other); @@ -11939,6 +12303,9 @@ class ValueArray5 { return ValuesIn(array); } + ValueArray5(const ValueArray5& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray5& other); @@ -11965,6 +12332,9 @@ class ValueArray6 { return ValuesIn(array); } + ValueArray6(const ValueArray6& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray6& other); @@ -11992,6 +12362,10 @@ class ValueArray7 { return ValuesIn(array); } + ValueArray7(const ValueArray7& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray7& other); @@ -12021,6 +12395,10 @@ class ValueArray8 { return ValuesIn(array); } + ValueArray8(const ValueArray8& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray8& other); @@ -12052,6 +12430,10 @@ class ValueArray9 { return ValuesIn(array); } + ValueArray9(const ValueArray9& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray9& other); @@ -12084,6 +12466,10 @@ class ValueArray10 { return ValuesIn(array); } + ValueArray10(const ValueArray10& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray10& other); @@ -12118,6 +12504,11 @@ class ValueArray11 { return ValuesIn(array); } + ValueArray11(const ValueArray11& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray11& other); @@ -12154,6 +12545,11 @@ class ValueArray12 { return ValuesIn(array); } + ValueArray12(const ValueArray12& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray12& other); @@ -12192,6 +12588,11 @@ class ValueArray13 { return ValuesIn(array); } + ValueArray13(const ValueArray13& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray13& other); @@ -12231,6 +12632,11 @@ class ValueArray14 { return ValuesIn(array); } + ValueArray14(const ValueArray14& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray14& other); @@ -12272,6 +12678,12 @@ class ValueArray15 { return ValuesIn(array); } + ValueArray15(const ValueArray15& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray15& other); @@ -12316,6 +12728,12 @@ class ValueArray16 { return ValuesIn(array); } + ValueArray16(const ValueArray16& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray16& other); @@ -12361,6 +12779,12 @@ class ValueArray17 { return ValuesIn(array); } + ValueArray17(const ValueArray17& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray17& other); @@ -12408,6 +12832,12 @@ class ValueArray18 { return ValuesIn(array); } + ValueArray18(const ValueArray18& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray18& other); @@ -12456,6 +12886,13 @@ class ValueArray19 { return ValuesIn(array); } + ValueArray19(const ValueArray19& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray19& other); @@ -12506,6 +12943,13 @@ class ValueArray20 { return ValuesIn(array); } + ValueArray20(const ValueArray20& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray20& other); @@ -12559,6 +13003,13 @@ class ValueArray21 { return ValuesIn(array); } + ValueArray21(const ValueArray21& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray21& other); @@ -12613,6 +13064,13 @@ class ValueArray22 { return ValuesIn(array); } + ValueArray22(const ValueArray22& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray22& other); @@ -12669,6 +13127,14 @@ class ValueArray23 { return ValuesIn(array); } + ValueArray23(const ValueArray23& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray23& other); @@ -12727,6 +13193,14 @@ class ValueArray24 { return ValuesIn(array); } + ValueArray24(const ValueArray24& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray24& other); @@ -12786,6 +13260,14 @@ class ValueArray25 { return ValuesIn(array); } + ValueArray25(const ValueArray25& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray25& other); @@ -12847,6 +13329,14 @@ class ValueArray26 { return ValuesIn(array); } + ValueArray26(const ValueArray26& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray26& other); @@ -12911,6 +13401,15 @@ class ValueArray27 { return ValuesIn(array); } + ValueArray27(const ValueArray27& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray27& other); @@ -12976,6 +13475,15 @@ class ValueArray28 { return ValuesIn(array); } + ValueArray28(const ValueArray28& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray28& other); @@ -13042,6 +13550,15 @@ class ValueArray29 { return ValuesIn(array); } + ValueArray29(const ValueArray29& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray29& other); @@ -13111,6 +13628,15 @@ class ValueArray30 { return ValuesIn(array); } + ValueArray30(const ValueArray30& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray30& other); @@ -13182,6 +13708,16 @@ class ValueArray31 { return ValuesIn(array); } + ValueArray31(const ValueArray31& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray31& other); @@ -13254,6 +13790,16 @@ class ValueArray32 { return ValuesIn(array); } + ValueArray32(const ValueArray32& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray32& other); @@ -13329,6 +13875,16 @@ class ValueArray33 { return ValuesIn(array); } + ValueArray33(const ValueArray33& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray33& other); @@ -13405,6 +13961,16 @@ class ValueArray34 { return ValuesIn(array); } + ValueArray34(const ValueArray34& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray34& other); @@ -13482,6 +14048,17 @@ class ValueArray35 { return ValuesIn(array); } + ValueArray35(const ValueArray35& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray35& other); @@ -13562,6 +14139,17 @@ class ValueArray36 { return ValuesIn(array); } + ValueArray36(const ValueArray36& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray36& other); @@ -13644,6 +14232,17 @@ class ValueArray37 { return ValuesIn(array); } + ValueArray37(const ValueArray37& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray37& other); @@ -13727,6 +14326,17 @@ class ValueArray38 { return ValuesIn(array); } + ValueArray38(const ValueArray38& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray38& other); @@ -13812,6 +14422,18 @@ class ValueArray39 { return ValuesIn(array); } + ValueArray39(const ValueArray39& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray39& other); @@ -13899,6 +14521,18 @@ class ValueArray40 { return ValuesIn(array); } + ValueArray40(const ValueArray40& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray40& other); @@ -13988,6 +14622,18 @@ class ValueArray41 { return ValuesIn(array); } + ValueArray41(const ValueArray41& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray41& other); @@ -14079,6 +14725,18 @@ class ValueArray42 { return ValuesIn(array); } + ValueArray42(const ValueArray42& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray42& other); @@ -14171,6 +14829,19 @@ class ValueArray43 { return ValuesIn(array); } + ValueArray43(const ValueArray43& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray43& other); @@ -14265,6 +14936,19 @@ class ValueArray44 { return ValuesIn(array); } + ValueArray44(const ValueArray44& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_), v44_(other.v44_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray44& other); @@ -14361,6 +15045,19 @@ class ValueArray45 { return ValuesIn(array); } + ValueArray45(const ValueArray45& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_), v44_(other.v44_), v45_(other.v45_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray45& other); @@ -14459,6 +15156,19 @@ class ValueArray46 { return ValuesIn(array); } + ValueArray46(const ValueArray46& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray46& other); @@ -14559,6 +15269,20 @@ class ValueArray47 { return ValuesIn(array); } + ValueArray47(const ValueArray47& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_), + v47_(other.v47_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray47& other); @@ -14661,6 +15385,20 @@ class ValueArray48 { return ValuesIn(array); } + ValueArray48(const ValueArray48& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_), + v47_(other.v47_), v48_(other.v48_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray48& other); @@ -14764,6 +15502,20 @@ class ValueArray49 { return ValuesIn(array); } + ValueArray49(const ValueArray49& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_), + v47_(other.v47_), v48_(other.v48_), v49_(other.v49_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray49& other); @@ -14868,6 +15620,20 @@ class ValueArray50 { return ValuesIn(array); } + ValueArray50(const ValueArray50& other) : v1_(other.v1_), v2_(other.v2_), + v3_(other.v3_), v4_(other.v4_), v5_(other.v5_), v6_(other.v6_), + v7_(other.v7_), v8_(other.v8_), v9_(other.v9_), v10_(other.v10_), + v11_(other.v11_), v12_(other.v12_), v13_(other.v13_), v14_(other.v14_), + v15_(other.v15_), v16_(other.v16_), v17_(other.v17_), v18_(other.v18_), + v19_(other.v19_), v20_(other.v20_), v21_(other.v21_), v22_(other.v22_), + v23_(other.v23_), v24_(other.v24_), v25_(other.v25_), v26_(other.v26_), + v27_(other.v27_), v28_(other.v28_), v29_(other.v29_), v30_(other.v30_), + v31_(other.v31_), v32_(other.v32_), v33_(other.v33_), v34_(other.v34_), + v35_(other.v35_), v36_(other.v36_), v37_(other.v37_), v38_(other.v38_), + v39_(other.v39_), v40_(other.v40_), v41_(other.v41_), v42_(other.v42_), + v43_(other.v43_), v44_(other.v44_), v45_(other.v45_), v46_(other.v46_), + v47_(other.v47_), v48_(other.v48_), v49_(other.v49_), v50_(other.v50_) {} + private: // No implementation - assignment is unsupported. void operator=(const ValueArray50& other); @@ -14980,7 +15746,7 @@ class CartesianProductGenerator2 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -15012,7 +15778,7 @@ class CartesianProductGenerator2 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_); + current_value_.reset(new ParamType(*current1_, *current2_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -15034,7 +15800,7 @@ class CartesianProductGenerator2 const typename ParamGenerator::iterator begin2_; const typename ParamGenerator::iterator end2_; typename ParamGenerator::iterator current2_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator2::Iterator // No implementation - assignment is unsupported. @@ -15103,7 +15869,7 @@ class CartesianProductGenerator3 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -15139,7 +15905,7 @@ class CartesianProductGenerator3 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_); + current_value_.reset(new ParamType(*current1_, *current2_, *current3_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -15165,7 +15931,7 @@ class CartesianProductGenerator3 const typename ParamGenerator::iterator begin3_; const typename ParamGenerator::iterator end3_; typename ParamGenerator::iterator current3_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator3::Iterator // No implementation - assignment is unsupported. @@ -15244,7 +16010,7 @@ class CartesianProductGenerator4 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -15284,8 +16050,8 @@ class CartesianProductGenerator4 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_); + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -15315,7 +16081,7 @@ class CartesianProductGenerator4 const typename ParamGenerator::iterator begin4_; const typename ParamGenerator::iterator end4_; typename ParamGenerator::iterator current4_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator4::Iterator // No implementation - assignment is unsupported. @@ -15402,7 +16168,7 @@ class CartesianProductGenerator5 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -15446,8 +16212,8 @@ class CartesianProductGenerator5 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_); + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -15481,7 +16247,7 @@ class CartesianProductGenerator5 const typename ParamGenerator::iterator begin5_; const typename ParamGenerator::iterator end5_; typename ParamGenerator::iterator current5_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator5::Iterator // No implementation - assignment is unsupported. @@ -15579,7 +16345,7 @@ class CartesianProductGenerator6 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -15627,8 +16393,8 @@ class CartesianProductGenerator6 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_); + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -15666,7 +16432,7 @@ class CartesianProductGenerator6 const typename ParamGenerator::iterator begin6_; const typename ParamGenerator::iterator end6_; typename ParamGenerator::iterator current6_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator6::Iterator // No implementation - assignment is unsupported. @@ -15773,7 +16539,7 @@ class CartesianProductGenerator7 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -15825,8 +16591,8 @@ class CartesianProductGenerator7 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_); + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -15868,7 +16634,7 @@ class CartesianProductGenerator7 const typename ParamGenerator::iterator begin7_; const typename ParamGenerator::iterator end7_; typename ParamGenerator::iterator current7_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator7::Iterator // No implementation - assignment is unsupported. @@ -15986,7 +16752,7 @@ class CartesianProductGenerator8 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -16042,8 +16808,8 @@ class CartesianProductGenerator8 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_, *current8_); + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -16089,7 +16855,7 @@ class CartesianProductGenerator8 const typename ParamGenerator::iterator begin8_; const typename ParamGenerator::iterator end8_; typename ParamGenerator::iterator current8_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator8::Iterator // No implementation - assignment is unsupported. @@ -16215,7 +16981,7 @@ class CartesianProductGenerator9 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -16275,9 +17041,9 @@ class CartesianProductGenerator9 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, *current4_, *current5_, *current6_, *current7_, *current8_, - *current9_); + *current9_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -16327,7 +17093,7 @@ class CartesianProductGenerator9 const typename ParamGenerator::iterator begin9_; const typename ParamGenerator::iterator end9_; typename ParamGenerator::iterator current9_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator9::Iterator // No implementation - assignment is unsupported. @@ -16462,7 +17228,7 @@ class CartesianProductGenerator10 virtual ParamIteratorInterface* Clone() const { return new Iterator(*this); } - virtual const ParamType* Current() const { return ¤t_value_; } + virtual const ParamType* Current() const { return current_value_.get(); } virtual bool Equals(const ParamIteratorInterface& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. @@ -16526,9 +17292,9 @@ class CartesianProductGenerator10 void ComputeCurrentValue() { if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, + current_value_.reset(new ParamType(*current1_, *current2_, *current3_, *current4_, *current5_, *current6_, *current7_, *current8_, - *current9_, *current10_); + *current9_, *current10_)); } bool AtEnd() const { // We must report iterator past the end of the range when either of the @@ -16582,7 +17348,7 @@ class CartesianProductGenerator10 const typename ParamGenerator::iterator begin10_; const typename ParamGenerator::iterator end10_; typename ParamGenerator::iterator current10_; - ParamType current_value_; + linked_ptr current_value_; }; // class CartesianProductGenerator10::Iterator // No implementation - assignment is unsupported. @@ -16913,12 +17679,8 @@ CartesianProductHolder10(const Generator1& g1, const Generator2& g2, } // namespace internal } // namespace testing -#endif // GTEST_HAS_PARAM_TEST - #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ -#if GTEST_HAS_PARAM_TEST - namespace testing { // Functions producing parameter generators. @@ -16998,7 +17760,7 @@ internal::ParamGenerator Range(T start, T end) { // each with C-string values of "foo", "bar", and "baz": // // const char* strings[] = {"foo", "bar", "baz"}; -// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); +// INSTANTIATE_TEST_CASE_P(StringSequence, StringTest, ValuesIn(strings)); // // This instantiates tests from test case StlStringTest // each with STL strings with values "a" and "b": @@ -18100,8 +18862,6 @@ internal::CartesianProductHolder10AddTestPattern(\ - #test_case_name, \ - #test_name, \ + GTEST_STRINGIFY_(test_case_name), \ + GTEST_STRINGIFY_(test_name), \ new ::testing::internal::TestMetaFactory< \ GTEST_TEST_CLASS_NAME_(\ test_case_name, test_name)>()); \ @@ -18137,21 +18897,21 @@ internal::CartesianProductHolder10, and return std::string. // // testing::PrintToStringParamName is a builtin test suffix generator that -// returns the value of testing::PrintToString(GetParam()). It does not work -// for std::string or C strings. +// returns the value of testing::PrintToString(GetParam()). // // Note: test names must be non-empty, unique, and may only contain ASCII -// alphanumeric characters or underscore. +// alphanumeric characters or underscore. Because PrintToString adds quotes +// to std::string and C strings, it won't work for these types. # define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, ...) \ - ::testing::internal::ParamGenerator \ + static ::testing::internal::ParamGenerator \ gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ - ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \ + static ::std::string gtest_##prefix##test_case_name##_EvalGenerateName_( \ const ::testing::TestParamInfo& info) { \ return ::testing::internal::GetParamNameGen \ (__VA_ARGS__)(info); \ } \ - int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \ + static int gtest_##prefix##test_case_name##_dummy_ GTEST_ATTRIBUTE_UNUSED_ = \ ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ GetTestCasePatternHolder(\ #test_case_name, \ @@ -18164,8 +18924,6 @@ internal::CartesianProductHolder10 #include +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + namespace testing { // A copyable object representing the result of a test part (i.e. an @@ -18368,7 +19131,7 @@ class GTEST_API_ TestPartResultArray { }; // This interface knows how to report a test part result. -class TestPartResultReporterInterface { +class GTEST_API_ TestPartResultReporterInterface { public: virtual ~TestPartResultReporterInterface() {} @@ -18401,6 +19164,8 @@ class GTEST_API_ HasNewFatalFailureHelper } // namespace testing +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + #endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ // Copyright 2008 Google Inc. // All Rights Reserved. @@ -18430,8 +19195,9 @@ class GTEST_API_ HasNewFatalFailureHelper // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) + + +// GOOGLETEST_CM0001 DO NOT DELETE #ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ #define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ @@ -18486,6 +19252,24 @@ TYPED_TEST(FooTest, DoesBlah) { TYPED_TEST(FooTest, HasPropertyA) { ... } +// TYPED_TEST_CASE takes an optional third argument which allows to specify a +// class that generates custom test name suffixes based on the type. This should +// be a class which has a static template function GetName(int index) returning +// a string for each type. The provided integer index equals the index of the +// type in the provided type list. In many cases the index can be ignored. +// +// For example: +// class MyTypeNames { +// public: +// template +// static std::string GetName(int) { +// if (std::is_same()) return "char"; +// if (std::is_same()) return "int"; +// if (std::is_same()) return "unsignedInt"; +// } +// }; +// TYPED_TEST_CASE(FooTest, MyTypes, MyTypeNames); + #endif // 0 // Type-parameterized tests are abstract test patterns parameterized @@ -18547,6 +19331,11 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); // If the type list contains only one type, you can write that type // directly without Types<...>: // INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); +// +// Similar to the optional argument of TYPED_TEST_CASE above, +// INSTANTIATE_TEST_CASE_P takes an optional fourth argument which allows to +// generate custom names. +// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes, MyTypeNames); #endif // 0 @@ -18561,32 +19350,46 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); // given test case. # define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ +// Expands to the name of the typedef for the NameGenerator, responsible for +// creating the suffixes of the name. +#define GTEST_NAME_GENERATOR_(TestCaseName) \ + gtest_type_params_##TestCaseName##_NameGenerator + // The 'Types' template argument below must have spaces around it // since some compilers may choke on '>>' when passing a template // instance (e.g. Types) -# define TYPED_TEST_CASE(CaseName, Types) \ - typedef ::testing::internal::TypeList< Types >::type \ - GTEST_TYPE_PARAMS_(CaseName) - -# define TYPED_TEST(CaseName, TestName) \ - template \ - class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ - : public CaseName { \ - private: \ - typedef CaseName TestFixture; \ - typedef gtest_TypeParam_ TypeParam; \ - virtual void TestBody(); \ - }; \ - bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ - ::testing::internal::TypeParameterizedTest< \ - CaseName, \ - ::testing::internal::TemplateSel< \ - GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ - GTEST_TYPE_PARAMS_(CaseName)>::Register(\ - "", ::testing::internal::CodeLocation(__FILE__, __LINE__), \ - #CaseName, #TestName, 0); \ - template \ - void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() +# define TYPED_TEST_CASE(CaseName, Types, ...) \ + typedef ::testing::internal::TypeList< Types >::type GTEST_TYPE_PARAMS_( \ + CaseName); \ + typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \ + GTEST_NAME_GENERATOR_(CaseName) + +# define TYPED_TEST(CaseName, TestName) \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + static bool gtest_##CaseName##_##TestName##_registered_ \ + GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel, \ + GTEST_TYPE_PARAMS_( \ + CaseName)>::Register("", \ + ::testing::internal::CodeLocation( \ + __FILE__, __LINE__), \ + #CaseName, #TestName, 0, \ + ::testing::internal::GenerateNames< \ + GTEST_NAME_GENERATOR_(CaseName), \ + GTEST_TYPE_PARAMS_(CaseName)>()); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, \ + TestName)::TestBody() #endif // GTEST_HAS_TYPED_TEST @@ -18643,27 +19446,35 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); namespace GTEST_CASE_NAMESPACE_(CaseName) { \ typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ } \ - static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ - GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ - __FILE__, __LINE__, #__VA_ARGS__) + static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) \ + GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames( \ + __FILE__, __LINE__, #__VA_ARGS__) // The 'Types' template argument below must have spaces around it // since some compilers may choke on '>>' when passing a template // instance (e.g. Types) -# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ - bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ - ::testing::internal::TypeParameterizedTestCase::type>::Register(\ - #Prefix, \ - ::testing::internal::CodeLocation(__FILE__, __LINE__), \ - >EST_TYPED_TEST_CASE_P_STATE_(CaseName), \ - #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types, ...) \ + static bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestCase< \ + CaseName, GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \ + ::testing::internal::TypeList< Types >::type>:: \ + Register(#Prefix, \ + ::testing::internal::CodeLocation(__FILE__, __LINE__), \ + >EST_TYPED_TEST_CASE_P_STATE_(CaseName), #CaseName, \ + GTEST_REGISTERED_TEST_NAMES_(CaseName), \ + ::testing::internal::GenerateNames< \ + ::testing::internal::NameGeneratorSelector< \ + __VA_ARGS__>::type, \ + ::testing::internal::TypeList< Types >::type>()) #endif // GTEST_HAS_TYPED_TEST_P #endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + // Depending on the platform, different string classes are available. // On Linux, in addition to ::std::string, Google also makes use of // class ::string, which has the same interface as ::std::string, but @@ -18681,6 +19492,15 @@ INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); namespace testing { +// Silence C4100 (unreferenced formal parameter) and 4805 +// unsafe mix of type 'const int' and type 'const bool' +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable:4805) +# pragma warning(disable:4100) +#endif + + // Declares the flags. // This flag temporary enables the disabled tests. @@ -18702,6 +19522,10 @@ GTEST_DECLARE_string_(color); // the tests to run. If the filter is not given all tests are executed. GTEST_DECLARE_string_(filter); +// This flag controls whether Google Test installs a signal handler that dumps +// debugging information when fatal signals are raised. +GTEST_DECLARE_bool_(install_failure_signal_handler); + // This flag causes the Google Test to list tests. None of the tests listed // are actually run if the flag is provided. GTEST_DECLARE_bool_(list_tests); @@ -18714,6 +19538,9 @@ GTEST_DECLARE_string_(output); // test. GTEST_DECLARE_bool_(print_time); +// This flags control whether Google Test prints UTF8 characters as text. +GTEST_DECLARE_bool_(print_utf8); + // This flag specifies the random number seed. GTEST_DECLARE_int32_(random_seed); @@ -18734,7 +19561,7 @@ GTEST_DECLARE_int32_(stack_trace_depth); // When this flag is specified, a failed assertion will throw an // exception if exceptions are enabled, or exit the program with a -// non-zero code otherwise. +// non-zero code otherwise. For use with an external test framework. GTEST_DECLARE_bool_(throw_on_failure); // When this flag is set with a "host:port" string, on supported @@ -18742,6 +19569,10 @@ GTEST_DECLARE_bool_(throw_on_failure); // the specified host machine. GTEST_DECLARE_string_(stream_result_to); +#if GTEST_USE_OWN_FLAGFILE_FLAG_ +GTEST_DECLARE_string_(flagfile); +#endif // GTEST_USE_OWN_FLAGFILE_FLAG_ + // The upper limit for valid stack trace depths. const int kMaxStackTraceDepth = 100; @@ -18759,6 +19590,7 @@ class TestEventListenersAccessor; class TestEventRepeater; class UnitTestRecordPropertyTestHelper; class WindowsDeathTest; +class FuchsiaDeathTest; class UnitTestImpl* GetUnitTestImpl(); void ReportFailureInUnknownLocation(TestPartResult::Type result_type, const std::string& message); @@ -18858,7 +19690,9 @@ class GTEST_API_ AssertionResult { // Used in EXPECT_TRUE/FALSE(assertion_result). AssertionResult(const AssertionResult& other); +#if defined(_MSC_VER) && _MSC_VER < 1910 GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) +#endif // Used in the EXPECT_TRUE/FALSE(bool_expression). // @@ -18875,7 +19709,9 @@ class GTEST_API_ AssertionResult { /*enabler*/ = NULL) : success_(success) {} +#if defined(_MSC_VER) && _MSC_VER < 1910 GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif // Assignment operator. AssertionResult& operator=(AssertionResult other) { @@ -18896,7 +19732,7 @@ class GTEST_API_ AssertionResult { const char* message() const { return message_.get() != NULL ? message_->c_str() : ""; } - // TODO(vladl@google.com): Remove this after making sure no clients use it. + // FIXME: Remove this after making sure no clients use it. // Deprecated; please use message() instead. const char* failure_message() const { return message(); } @@ -18944,684 +19780,1067 @@ GTEST_API_ AssertionResult AssertionFailure(); // Deprecated; use AssertionFailure() << msg. GTEST_API_ AssertionResult AssertionFailure(const Message& msg); -// The abstract class that all tests inherit from. -// -// In Google Test, a unit test program contains one or many TestCases, and -// each TestCase contains one or many Tests. -// -// When you define a test using the TEST macro, you don't need to -// explicitly derive from Test - the TEST macro automatically does -// this for you. +} // namespace testing + +// Includes the auto-generated header that implements a family of generic +// predicate assertion macros. This include comes late because it relies on +// APIs declared above. +// Copyright 2006, Google Inc. +// All rights reserved. // -// The only time you derive from Test is when defining a test fixture -// to be used a TEST_F. For example: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: // -// class FooTest : public testing::Test { -// protected: -// void SetUp() override { ... } -// void TearDown() override { ... } -// ... -// }; +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. // -// TEST_F(FooTest, Bar) { ... } -// TEST_F(FooTest, Baz) { ... } +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 01/02/2018 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! // -// Test is not copyable. -class GTEST_API_ Test { - public: - friend class TestInfo; +// Implements a family of generic predicate assertion macros. - // Defines types for pointers to functions that set up and tear down - // a test case. - typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; - typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; +// GOOGLETEST_CM0001 DO NOT DELETE - // The d'tor is virtual as we intend to inherit from Test. - virtual ~Test(); +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ - // Sets up the stuff shared by all tests in this test case. - // - // Google Test will call Foo::SetUpTestCase() before running the first - // test in test case Foo. Hence a sub-class can define its own - // SetUpTestCase() method to shadow the one defined in the super - // class. - static void SetUpTestCase() {} - // Tears down the stuff shared by all tests in this test case. - // - // Google Test will call Foo::TearDownTestCase() after running the last - // test in test case Foo. Hence a sub-class can define its own - // TearDownTestCase() method to shadow the one defined in the super - // class. - static void TearDownTestCase() {} +namespace testing { - // Returns true iff the current test has a fatal failure. - static bool HasFatalFailure(); +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. - // Returns true iff the current test has a non-fatal failure. - static bool HasNonfatalFailure(); +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. - // Returns true iff the current test has a (either fatal or - // non-fatal) failure. - static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) - // Logs a property for the current test, test case, or for the entire - // invocation of the test program when used outside of the context of a - // test case. Only the last value for a given key is remembered. These - // are public static so they can be called from utility functions that are - // not members of the test fixture. Calls to RecordProperty made during - // lifespan of the test (from the moment its constructor starts to the - // moment its destructor finishes) will be output in XML as attributes of - // the element. Properties recorded from fixture's - // SetUpTestCase or TearDownTestCase are logged as attributes of the - // corresponding element. Calls to RecordProperty made in the - // global context (before or after invocation of RUN_ALL_TESTS and from - // SetUp/TearDown method of Environment objects registered with Google - // Test) will be output as attributes of the element. - static void RecordProperty(const std::string& key, const std::string& value); - static void RecordProperty(const std::string& key, int value); - protected: - // Creates a Test object. - Test(); +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); - // Sets up the test fixture. - virtual void SetUp(); + return AssertionFailure() << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; +} - // Tears down the test fixture. - virtual void TearDown(); +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) - private: - // Returns true iff the current test has the same fixture class as - // the first test in the current test case. - static bool HasSameFixtureClass(); +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) - // Runs the test after the test fixture has been set up. - // - // A sub-class must implement this to define the test logic. - // - // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. - // Instead, use the TEST or TEST_F macro. - virtual void TestBody() = 0; +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) - // Sets up, executes, and tears down the test. - void Run(); - // Deletes self. We deliberately pick an unusual name for this - // internal method to avoid clashing with names used in user TESTs. - void DeleteSelf_() { delete this; } - const internal::scoped_ptr< GTEST_FLAG_SAVER_ > gtest_flag_saver_; +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); - // Often a user misspells SetUp() as Setup() and spends a long time - // wondering why it is never called by Google Test. The declaration of - // the following method is solely for catching such an error at - // compile time: - // - // - The return type is deliberately chosen to be not void, so it - // will be a conflict if void Setup() is declared in the user's - // test fixture. - // - // - This method is private, so it will be another compiler error - // if the method is called from the user's test fixture. - // - // DO NOT OVERRIDE THIS FUNCTION. - // - // If you see an error about overriding the following function or - // about it being private, you have mis-spelled SetUp() as Setup(). - struct Setup_should_be_spelled_SetUp {}; - virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } - - // We disallow copying Tests. - GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); -}; - -typedef internal::TimeInMillis TimeInMillis; + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; +} -// A copyable object representing a user specified test property which can be -// output as a key/value string pair. -// -// Don't inherit from TestProperty as its destructor is not virtual. -class TestProperty { - public: - // C'tor. TestProperty does NOT have a default constructor. - // Always use this constructor (with parameters) to create a - // TestProperty object. - TestProperty(const std::string& a_key, const std::string& a_value) : - key_(a_key), value_(a_value) { - } +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) - // Gets the user supplied key. - const char* key() const { - return key_.c_str(); - } +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) - // Gets the user supplied value. - const char* value() const { - return value_.c_str(); - } +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) - // Sets a new value, overriding the one supplied in the constructor. - void SetValue(const std::string& new_value) { - value_ = new_value; - } - private: - // The key supplied by the user. - std::string key_; - // The value supplied by the user. - std::string value_; -}; -// The result of a single Test. This includes a list of -// TestPartResults, a list of TestProperties, a count of how many -// death tests there are in the Test, and how much time it took to run -// the Test. -// -// TestResult is not copyable. -class GTEST_API_ TestResult { - public: - // Creates an empty TestResult. - TestResult(); +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); - // D'tor. Do not inherit from TestResult. - ~TestResult(); + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; +} - // Gets the number of all test parts. This is the sum of the number - // of successful test parts and the number of failed test parts. - int total_part_count() const; +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) - // Returns the number of the test properties. - int test_property_count() const; +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) - // Returns true iff the test passed (i.e. no test part failed). - bool Passed() const { return !Failed(); } +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) - // Returns true iff the test failed. - bool Failed() const; - // Returns true iff the test fatally failed. - bool HasFatalFailure() const; - // Returns true iff the test has a non-fatal failure. - bool HasNonfatalFailure() const; +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); - // Returns the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const { return elapsed_time_; } + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; +} - // Returns the i-th test part result among all the results. i can range - // from 0 to test_property_count() - 1. If i is not in that range, aborts - // the program. - const TestPartResult& GetTestPartResult(int i) const; +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) - // Returns the i-th test property. i can range from 0 to - // test_property_count() - 1. If i is not in that range, aborts the - // program. - const TestProperty& GetTestProperty(int i) const; +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) - private: - friend class TestInfo; - friend class TestCase; - friend class UnitTest; - friend class internal::DefaultGlobalTestPartResultReporter; - friend class internal::ExecDeathTest; - friend class internal::TestResultAccessor; - friend class internal::UnitTestImpl; - friend class internal::WindowsDeathTest; +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) - // Gets the vector of TestPartResults. - const std::vector& test_part_results() const { - return test_part_results_; - } - // Gets the vector of TestProperties. - const std::vector& test_properties() const { - return test_properties_; - } - // Sets the elapsed time. - void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); - // Adds a test property to the list. The property is validated and may add - // a non-fatal failure if invalid (e.g., if it conflicts with reserved - // key names). If a property is already recorded for the same key, the - // value will be updated, rather than storing multiple values for the same - // key. xml_element specifies the element for which the property is being - // recorded and is used for validation. - void RecordProperty(const std::string& xml_element, - const TestProperty& test_property); - - // Adds a failure if the key is a reserved attribute of Google Test - // testcase tags. Returns true if the property is valid. - // TODO(russr): Validate attribute names are legal and human readable. - static bool ValidateTestProperty(const std::string& xml_element, - const TestProperty& test_property); + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; +} - // Adds a test part result to the list. - void AddTestPartResult(const TestPartResult& test_part_result); +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) - // Returns the death test count. - int death_test_count() const { return death_test_count_; } +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) - // Increments the death test count, returning the new count. - int increment_death_test_count() { return ++death_test_count_; } +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) - // Clears the test part results. - void ClearTestPartResults(); - // Clears the object. - void Clear(); - // Protects mutable state of the property vector and of owned - // properties, whose values may be updated. - internal::Mutex test_properites_mutex_; +} // namespace testing - // The vector of TestPartResults - std::vector test_part_results_; - // The vector of TestProperties - std::vector test_properties_; - // Running count of death tests. - int death_test_count_; - // The elapsed time, in milliseconds. - TimeInMillis elapsed_time_; +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ - // We disallow copying TestResult. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); -}; // class TestResult +namespace testing { -// A TestInfo object stores the following information about a test: +// The abstract class that all tests inherit from. // -// Test case name -// Test name -// Whether the test should be run -// A function pointer that creates the test object when invoked -// Test result +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. // -// The constructor of TestInfo registers itself with the UnitTest -// singleton such that the RUN_ALL_TESTS() macro knows which tests to -// run. -class GTEST_API_ TestInfo { +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used in a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// void SetUp() override { ... } +// void TearDown() override { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { public: - // Destructs a TestInfo object. This function is not virtual, so - // don't inherit from TestInfo. - ~TestInfo(); + friend class TestInfo; - // Returns the test case name. - const char* test_case_name() const { return test_case_name_.c_str(); } + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; - // Returns the test name. - const char* name() const { return name_.c_str(); } + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); - // Returns the name of the parameter type, or NULL if this is not a typed - // or a type-parameterized test. - const char* type_param() const { - if (type_param_.get() != NULL) - return type_param_->c_str(); - return NULL; - } + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} - // Returns the text representation of the value parameter, or NULL if this - // is not a value-parameterized test. - const char* value_param() const { - if (value_param_.get() != NULL) - return value_param_->c_str(); - return NULL; - } + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} - // Returns the file name where this test is defined. - const char* file() const { return location_.file.c_str(); } + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); - // Returns the line where this test is defined. - int line() const { return location_.line; } + // Returns true iff the current test has a non-fatal failure. + static bool HasNonfatalFailure(); - // Returns true if this test should run, that is if the test is not - // disabled (or it is disabled but the also_run_disabled_tests flag has - // been specified) and its full name matches the user-specified filter. - // - // Google Test allows the user to filter the tests by their full names. - // The full name of a test Bar in test case Foo is defined as - // "Foo.Bar". Only the tests that match the filter will run. - // - // A filter is a colon-separated list of glob (not regex) patterns, - // optionally followed by a '-' and a colon-separated list of - // negative patterns (tests to exclude). A test is run if it - // matches one of the positive patterns and does not match any of - // the negative patterns. - // - // For example, *A*:Foo.* is a filter that matches any string that - // contains the character 'A' or starts with "Foo.". - bool should_run() const { return should_run_; } + // Returns true iff the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } - // Returns true iff this test will appear in the XML report. - bool is_reportable() const { - // For now, the XML report includes all tests matching the filter. - // In the future, we may trim tests that are excluded because of - // sharding. - return matches_filter_; - } + // Logs a property for the current test, test case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestCase or TearDownTestCase are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); - // Returns the result of the test. - const TestResult* result() const { return &result_; } + protected: + // Creates a Test object. + Test(); - private: -#if GTEST_HAS_DEATH_TEST - friend class internal::DefaultDeathTestFactory; -#endif // GTEST_HAS_DEATH_TEST - friend class Test; - friend class TestCase; - friend class internal::UnitTestImpl; - friend class internal::StreamingListenerTest; - friend TestInfo* internal::MakeAndRegisterTestInfo( - const char* test_case_name, - const char* name, - const char* type_param, - const char* value_param, - internal::CodeLocation code_location, - internal::TypeId fixture_class_id, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc, - internal::TestFactoryBase* factory); + // Sets up the test fixture. + virtual void SetUp(); - // Constructs a TestInfo object. The newly constructed instance assumes - // ownership of the factory object. - TestInfo(const std::string& test_case_name, - const std::string& name, - const char* a_type_param, // NULL if not a type-parameterized test - const char* a_value_param, // NULL if not a value-parameterized test - internal::CodeLocation a_code_location, - internal::TypeId fixture_class_id, - internal::TestFactoryBase* factory); + // Tears down the test fixture. + virtual void TearDown(); - // Increments the number of death tests encountered in this test so - // far. - int increment_death_test_count() { - return result_.increment_death_test_count(); - } + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); - // Creates the test object, runs it, records its result, and then - // deletes it. + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. void Run(); - static void ClearTestResult(TestInfo* test_info) { - test_info->result_.Clear(); - } + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } - // These fields are immutable properties of the test. - const std::string test_case_name_; // Test case name - const std::string name_; // Test name - // Name of the parameter type, or NULL if this is not a typed or a - // type-parameterized test. - const internal::scoped_ptr type_param_; - // Text representation of the value parameter, or NULL if this is not a - // value-parameterized test. - const internal::scoped_ptr value_param_; - internal::CodeLocation location_; - const internal::TypeId fixture_class_id_; // ID of the test fixture class - bool should_run_; // True iff this test should run - bool is_disabled_; // True iff this test is disabled - bool matches_filter_; // True if this test matches the - // user-specified filter. - internal::TestFactoryBase* const factory_; // The factory that creates - // the test object - - // This field is mutable and needs to be reset before running the - // test for the second time. - TestResult result_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); -}; + const internal::scoped_ptr< GTEST_FLAG_SAVER_ > gtest_flag_saver_; -// A test case, which consists of a vector of TestInfos. -// -// TestCase is not copyable. -class GTEST_API_ TestCase { - public: - // Creates a TestCase with the given name. + // Often a user misspells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: // - // TestCase does NOT have a default constructor. Always use this - // constructor to create a TestCase object. + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if void Setup() is declared in the user's + // test fixture. // - // Arguments: + // - This method is private, so it will be another compiler error + // if the method is called from the user's test fixture. // - // name: name of the test case - // a_type_param: the name of the test's type parameter, or NULL if - // this is not a type-parameterized test. - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - TestCase(const char* name, const char* a_type_param, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc); + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } - // Destructor of TestCase. - virtual ~TestCase(); + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; - // Gets the name of the TestCase. - const char* name() const { return name_.c_str(); } +typedef internal::TimeInMillis TimeInMillis; - // Returns the name of the parameter type, or NULL if this is not a - // type-parameterized test case. - const char* type_param() const { - if (type_param_.get() != NULL) - return type_param_->c_str(); - return NULL; +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { } - // Returns true if any test in this test case should run. - bool should_run() const { return should_run_; } + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } - // Gets the number of successful tests in this test case. - int successful_test_count() const; + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } - // Gets the number of failed tests in this test case. - int failed_test_count() const; + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } - // Gets the number of disabled tests that will be reported in the XML report. - int reportable_disabled_test_count() const; + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; - // Gets the number of disabled tests in this test case. - int disabled_test_count() const; +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); - // Gets the number of tests to be printed in the XML report. - int reportable_test_count() const; + // D'tor. Do not inherit from TestResult. + ~TestResult(); - // Get the number of tests in this test case that should run. - int test_to_run_count() const; + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; - // Gets the number of all tests in this test case. - int total_test_count() const; + // Returns the number of the test properties. + int test_property_count() const; - // Returns true iff the test case passed. + // Returns true iff the test passed (i.e. no test part failed). bool Passed() const { return !Failed(); } - // Returns true iff the test case failed. - bool Failed() const { return failed_test_count() > 0; } + // Returns true iff the test failed. + bool Failed() const; + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns true iff the test has a non-fatal failure. + bool HasNonfatalFailure() const; // Returns the elapsed time, in milliseconds. TimeInMillis elapsed_time() const { return elapsed_time_; } - // Returns the i-th test among all the tests. i can range from 0 to - // total_test_count() - 1. If i is not in that range, returns NULL. - const TestInfo* GetTestInfo(int i) const; + // Returns the i-th test part result among all the results. i can range from 0 + // to total_part_count() - 1. If i is not in that range, aborts the program. + const TestPartResult& GetTestPartResult(int i) const; - // Returns the TestResult that holds test properties recorded during - // execution of SetUpTestCase and TearDownTestCase. - const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; private: - friend class Test; + friend class TestInfo; + friend class TestCase; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + friend class internal::FuchsiaDeathTest; - // Gets the (mutable) vector of TestInfos in this TestCase. - std::vector& test_info_list() { return test_info_list_; } - - // Gets the (immutable) vector of TestInfos in this TestCase. - const std::vector& test_info_list() const { - return test_info_list_; + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; } - // Returns the i-th test among all the tests. i can range from 0 to - // total_test_count() - 1. If i is not in that range, returns NULL. - TestInfo* GetMutableTestInfo(int i); + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } - // Sets the should_run member. - void set_should_run(bool should) { should_run_ = should; } + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } - // Adds a TestInfo to this test case. Will delete the TestInfo upon - // destruction of the TestCase object. - void AddTestInfo(TestInfo * test_info); + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); - // Clears the results of all tests in this test case. - void ClearResult(); + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // FIXME: Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); - // Clears the results of all tests in the given test case. - static void ClearTestCaseResult(TestCase* test_case) { - test_case->ClearResult(); - } + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); - // Runs every test in this TestCase. - void Run(); + // Returns the death test count. + int death_test_count() const { return death_test_count_; } - // Runs SetUpTestCase() for this TestCase. This wrapper is needed - // for catching exceptions thrown from SetUpTestCase(). - void RunSetUpTestCase() { (*set_up_tc_)(); } + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } - // Runs TearDownTestCase() for this TestCase. This wrapper is - // needed for catching exceptions thrown from TearDownTestCase(). - void RunTearDownTestCase() { (*tear_down_tc_)(); } + // Clears the test part results. + void ClearTestPartResults(); - // Returns true iff test passed. - static bool TestPassed(const TestInfo* test_info) { - return test_info->should_run() && test_info->result()->Passed(); - } + // Clears the object. + void Clear(); - // Returns true iff test failed. - static bool TestFailed(const TestInfo* test_info) { - return test_info->should_run() && test_info->result()->Failed(); - } + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properites_mutex_; - // Returns true iff the test is disabled and will be reported in the XML - // report. - static bool TestReportableDisabled(const TestInfo* test_info) { - return test_info->is_reportable() && test_info->is_disabled_; - } + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; - // Returns true iff test is disabled. - static bool TestDisabled(const TestInfo* test_info) { - return test_info->is_disabled_; - } + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult - // Returns true iff this test will appear in the XML report. - static bool TestReportable(const TestInfo* test_info) { - return test_info->is_reportable(); +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; } - // Returns true if the given test should run. - static bool ShouldRunTest(const TestInfo* test_info) { - return test_info->should_run(); + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != NULL) + return value_param_->c_str(); + return NULL; } - // Shuffles the tests in this test case. - void ShuffleTests(internal::Random* random); + // Returns the file name where this test is defined. + const char* file() const { return location_.file.c_str(); } - // Restores the test order to before the first shuffle. - void UnshuffleTests(); + // Returns the line where this test is defined. + int line() const { return location_.line; } - // Name of the test case. - std::string name_; + // Return true if this test should not be run because it's in another shard. + bool is_in_another_shard() const { return is_in_another_shard_; } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true iff this test will appear in the XML report. + bool is_reportable() const { + // The XML report includes tests matching the filter, excluding those + // run in other shards. + return matches_filter_ && !is_in_another_shard_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestCase; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + internal::CodeLocation code_location, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_case_name, + const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::CodeLocation a_code_location, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case name + const std::string name_; // Test name // Name of the parameter type, or NULL if this is not a typed or a // type-parameterized test. const internal::scoped_ptr type_param_; - // The vector of TestInfos in their original order. It owns the - // elements in the vector. - std::vector test_info_list_; - // Provides a level of indirection for the test list to allow easy - // shuffling and restoring the test order. The i-th element in this - // vector is the index of the i-th test in the shuffled test list. - std::vector test_indices_; - // Pointer to the function that sets up the test case. - Test::SetUpTestCaseFunc set_up_tc_; - // Pointer to the function that tears down the test case. - Test::TearDownTestCaseFunc tear_down_tc_; - // True iff any test in this test case should run. - bool should_run_; - // Elapsed time, in milliseconds. - TimeInMillis elapsed_time_; - // Holds test properties recorded during execution of SetUpTestCase and - // TearDownTestCase. - TestResult ad_hoc_test_result_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr value_param_; + internal::CodeLocation location_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + bool is_in_another_shard_; // Will be run in another shard. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object - // We disallow copying TestCases. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); }; -// An Environment object is capable of setting up and tearing down an -// environment. You should subclass this to define your own -// environment(s). -// -// An Environment object does the set-up and tear-down in virtual -// methods SetUp() and TearDown() instead of the constructor and the -// destructor, as: +// A test case, which consists of a vector of TestInfos. // -// 1. You cannot safely throw from a destructor. This is a problem -// as in some cases Google Test is used where exceptions are enabled, and -// we may want to implement ASSERT_* using exceptions where they are -// available. -// 2. You cannot use ASSERT_* directly in a constructor or -// destructor. -class Environment { +// TestCase is not copyable. +class GTEST_API_ TestCase { public: - // The d'tor is virtual as we need to subclass Environment. - virtual ~Environment() {} + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); - // Override this to define how to set up the environment. - virtual void SetUp() {} + // Destructor of TestCase. + virtual ~TestCase(); - // Override this to define how to tear down the environment. - virtual void TearDown() {} - private: - // If you see an error about overriding the following function or - // about it being private, you have mis-spelled SetUp() as Setup(). - struct Setup_should_be_spelled_SetUp {}; - virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } -}; + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } -// The interface for tracing execution of tests. The methods are organized in -// the order the corresponding events are fired. -class TestEventListener { - public: - virtual ~TestEventListener() {} + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } - // Fired before any test activity starts. - virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } - // Fired before each iteration of tests starts. There may be more than - // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration - // index, starting from 0. - virtual void OnTestIterationStart(const UnitTest& unit_test, - int iteration) = 0; + // Gets the number of successful tests in this test case. + int successful_test_count() const; - // Fired before environment set-up for each iteration of tests starts. - virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + // Gets the number of failed tests in this test case. + int failed_test_count() const; - // Fired after environment set-up for each iteration of tests ends. - virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; - // Fired before the test case starts. - virtual void OnTestCaseStart(const TestCase& test_case) = 0; + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; - // Fired before the test starts. - virtual void OnTestStart(const TestInfo& test_info) = 0; + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; - // Fired after a failed assertion or a SUCCEED() invocation. - virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + // Get the number of tests in this test case that should run. + int test_to_run_count() const; - // Fired after the test ends. - virtual void OnTestEnd(const TestInfo& test_info) = 0; + // Gets the number of all tests in this test case. + int total_test_count() const; - // Fired after the test case ends. - virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } - // Fired before environment tear-down for each iteration of tests starts. + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestCase and TearDownTestCase. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestCase. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestCase. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true iff the test is disabled and will be reported in the XML + // report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true iff this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test case. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test case. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. You should subclass this to define your own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +#if GTEST_HAS_EXCEPTIONS + +// Exception which can be thrown from TestEventListener::OnTestPartResult. +class GTEST_API_ AssertionException + : public internal::GoogleTestFailureException { + public: + explicit AssertionException(const TestPartResult& result) + : GoogleTestFailureException(result) {} +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test case starts. + virtual void OnTestCaseStart(const TestCase& test_case) = 0; + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + // If you want to throw an exception from this function to skip to the next + // TEST, it must be AssertionException defined above, or inherited from it. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test case ends. + virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + + // Fired before environment tear-down for each iteration of tests starts. virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; // Fired after environment tear-down for each iteration of tests ends. @@ -19779,14 +20998,12 @@ class GTEST_API_ UnitTest { // Returns the random seed used at the start of the current test run. int random_seed() const; -#if GTEST_HAS_PARAM_TEST // Returns the ParameterizedTestCaseRegistry object used to keep track of // value-parameterized tests and instantiate and register them. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. internal::ParameterizedTestCaseRegistry& parameterized_test_registry() GTEST_LOCK_EXCLUDED_(mutex_); -#endif // GTEST_HAS_PARAM_TEST // Gets the number of successful test cases. int successful_test_case_count() const; @@ -19886,11 +21103,11 @@ class GTEST_API_ UnitTest { internal::UnitTestImpl* impl() { return impl_; } const internal::UnitTestImpl* impl() const { return impl_; } - // These classes and funcions are friends as they need to access private + // These classes and functions are friends as they need to access private // members of UnitTest. + friend class ScopedTrace; friend class Test; friend class internal::AssertHelper; - friend class internal::ScopedTrace; friend class internal::StreamingListenerTest; friend class internal::UnitTestRecordPropertyTestHelper; friend Environment* AddGlobalTestEnvironment(Environment* env); @@ -19987,11 +21204,9 @@ AssertionResult CmpHelperEQ(const char* lhs_expression, const char* rhs_expression, const T1& lhs, const T2& rhs) { -GTEST_DISABLE_MSC_WARNINGS_PUSH_(4389 /* signed/unsigned mismatch */) if (lhs == rhs) { return AssertionSuccess(); } -GTEST_DISABLE_MSC_WARNINGS_POP_() return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); } @@ -20305,7 +21520,6 @@ class GTEST_API_ AssertHelper { } // namespace internal -#if GTEST_HAS_PARAM_TEST // The pure interface class that all value-parameterized tests inherit from. // A value-parameterized class must inherit from both ::testing::Test and // ::testing::WithParamInterface. In most cases that just means inheriting @@ -20351,483 +21565,120 @@ class WithParamInterface { // references static data, to reduce the opportunity for incorrect uses // like writing 'WithParamInterface::GetParam()' for a test that // uses a fixture whose parameter type is int. - const ParamType& GetParam() const { - GTEST_CHECK_(parameter_ != NULL) - << "GetParam() can only be called inside a value-parameterized test " - << "-- did you intend to write TEST_P instead of TEST_F?"; - return *parameter_; - } - - private: - // Sets parameter value. The caller is responsible for making sure the value - // remains alive and unchanged throughout the current test. - static void SetParam(const ParamType* parameter) { - parameter_ = parameter; - } - - // Static value used for accessing parameter during a test lifetime. - static const ParamType* parameter_; - - // TestClass must be a subclass of WithParamInterface and Test. - template friend class internal::ParameterizedTestFactory; -}; - -template -const T* WithParamInterface::parameter_ = NULL; - -// Most value-parameterized classes can ignore the existence of -// WithParamInterface, and can just inherit from ::testing::TestWithParam. - -template -class TestWithParam : public Test, public WithParamInterface { -}; - -#endif // GTEST_HAS_PARAM_TEST - -// Macros for indicating success/failure in test code. - -// ADD_FAILURE unconditionally adds a failure to the current test. -// SUCCEED generates a success - it doesn't automatically make the -// current test successful, as a test is only successful when it has -// no failure. -// -// EXPECT_* verifies that a certain condition is satisfied. If not, -// it behaves like ADD_FAILURE. In particular: -// -// EXPECT_TRUE verifies that a Boolean condition is true. -// EXPECT_FALSE verifies that a Boolean condition is false. -// -// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except -// that they will also abort the current function on failure. People -// usually want the fail-fast behavior of FAIL and ASSERT_*, but those -// writing data-driven tests often find themselves using ADD_FAILURE -// and EXPECT_* more. - -// Generates a nonfatal failure with a generic message. -#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") - -// Generates a nonfatal failure at the given source file location with -// a generic message. -#define ADD_FAILURE_AT(file, line) \ - GTEST_MESSAGE_AT_(file, line, "Failed", \ - ::testing::TestPartResult::kNonFatalFailure) - -// Generates a fatal failure with a generic message. -#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") - -// Define this macro to 1 to omit the definition of FAIL(), which is a -// generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_FAIL -# define FAIL() GTEST_FAIL() -#endif - -// Generates a success with a generic message. -#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") - -// Define this macro to 1 to omit the definition of SUCCEED(), which -// is a generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_SUCCEED -# define SUCCEED() GTEST_SUCCEED() -#endif - -// Macros for testing exceptions. -// -// * {ASSERT|EXPECT}_THROW(statement, expected_exception): -// Tests that the statement throws the expected exception. -// * {ASSERT|EXPECT}_NO_THROW(statement): -// Tests that the statement doesn't throw any exception. -// * {ASSERT|EXPECT}_ANY_THROW(statement): -// Tests that the statement throws an exception. - -#define EXPECT_THROW(statement, expected_exception) \ - GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) -#define EXPECT_NO_THROW(statement) \ - GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) -#define EXPECT_ANY_THROW(statement) \ - GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) -#define ASSERT_THROW(statement, expected_exception) \ - GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) -#define ASSERT_NO_THROW(statement) \ - GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) -#define ASSERT_ANY_THROW(statement) \ - GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) - -// Boolean assertions. Condition can be either a Boolean expression or an -// AssertionResult. For more information on how to use AssertionResult with -// these macros see comments on that class. -#define EXPECT_TRUE(condition) \ - GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \ - GTEST_NONFATAL_FAILURE_) -#define EXPECT_FALSE(condition) \ - GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ - GTEST_NONFATAL_FAILURE_) -#define ASSERT_TRUE(condition) \ - GTEST_TEST_BOOLEAN_((condition), #condition, false, true, \ - GTEST_FATAL_FAILURE_) -#define ASSERT_FALSE(condition) \ - GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ - GTEST_FATAL_FAILURE_) - -// Includes the auto-generated header that implements a family of -// generic predicate assertion macros. -// Copyright 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command -// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! -// -// Implements a family of generic predicate assertion macros. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ - -// Makes sure this header is not included before gtest.h. -#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ -# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. -#endif // GTEST_INCLUDE_GTEST_GTEST_H_ - -// This header implements a family of generic predicate assertion -// macros: -// -// ASSERT_PRED_FORMAT1(pred_format, v1) -// ASSERT_PRED_FORMAT2(pred_format, v1, v2) -// ... -// -// where pred_format is a function or functor that takes n (in the -// case of ASSERT_PRED_FORMATn) values and their source expression -// text, and returns a testing::AssertionResult. See the definition -// of ASSERT_EQ in gtest.h for an example. -// -// If you don't care about formatting, you can use the more -// restrictive version: -// -// ASSERT_PRED1(pred, v1) -// ASSERT_PRED2(pred, v1, v2) -// ... -// -// where pred is an n-ary function or functor that returns bool, -// and the values v1, v2, ..., must support the << operator for -// streaming to std::ostream. -// -// We also define the EXPECT_* variations. -// -// For now we only support predicates whose arity is at most 5. -// Please email googletestframework@googlegroups.com if you need -// support for higher arities. - -// GTEST_ASSERT_ is the basic statement to which all of the assertions -// in this file reduce. Don't use this in your code. - -#define GTEST_ASSERT_(expression, on_failure) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const ::testing::AssertionResult gtest_ar = (expression)) \ - ; \ - else \ - on_failure(gtest_ar.failure_message()) - - -// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use -// this in your code. -template -AssertionResult AssertPred1Helper(const char* pred_text, - const char* e1, - Pred pred, - const T1& v1) { - if (pred(v1)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. -// Don't use this in your code. -#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, v1), \ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use -// this in your code. -#define GTEST_PRED1_(pred, v1, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ - #v1, \ - pred, \ - v1), on_failure) - -// Unary predicate assertion macros. -#define EXPECT_PRED_FORMAT1(pred_format, v1) \ - GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED1(pred, v1) \ - GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT1(pred_format, v1) \ - GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED1(pred, v1) \ - GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use -// this in your code. -template -AssertionResult AssertPred2Helper(const char* pred_text, - const char* e1, - const char* e2, - Pred pred, - const T1& v1, - const T2& v2) { - if (pred(v1, v2)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. -// Don't use this in your code. -#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use -// this in your code. -#define GTEST_PRED2_(pred, v1, v2, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ - #v1, \ - #v2, \ - pred, \ - v1, \ - v2), on_failure) - -// Binary predicate assertion macros. -#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ - GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED2(pred, v1, v2) \ - GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ - GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED2(pred, v1, v2) \ - GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use -// this in your code. -template -AssertionResult AssertPred3Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3) { - if (pred(v1, v2, v3)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. -// Don't use this in your code. -#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use -// this in your code. -#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - pred, \ - v1, \ - v2, \ - v3), on_failure) - -// Ternary predicate assertion macros. -#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ - GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED3(pred, v1, v2, v3) \ - GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ - GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED3(pred, v1, v2, v3) \ - GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; -// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use -// this in your code. -template -AssertionResult AssertPred4Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - const char* e4, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3, - const T4& v4) { - if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + // TestClass must be a subclass of WithParamInterface and Test. + template friend class internal::ParameterizedTestFactory; +}; - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4; -} +template +const T* WithParamInterface::parameter_ = NULL; -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. -// Don't use this in your code. -#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ - on_failure) +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. -// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use -// this in your code. -#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - #v4, \ - pred, \ - v1, \ - v2, \ - v3, \ - v4), on_failure) +template +class TestWithParam : public Test, public WithParamInterface { +}; -// 4-ary predicate assertion macros. -#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ - GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ - GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ - GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ - GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +// Macros for indicating success/failure in test code. +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") -// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use -// this in your code. -template -AssertionResult AssertPred5Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - const char* e4, - const char* e5, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3, - const T4& v4, - const T5& v5) { - if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ", " - << e5 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4 - << "\n" << e5 << " evaluates to " << v5; -} +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. -// Don't use this in your code. -#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ - on_failure) +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif -// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use -// this in your code. -#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - #v4, \ - #v5, \ - pred, \ - v1, \ - v2, \ - v3, \ - v4, \ - v5), on_failure) +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") -// 5-ary predicate assertion macros. -#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ - GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ - GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ - GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ - GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) -#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) // Macros for testing equalities and inequalities. // @@ -20870,8 +21721,8 @@ AssertionResult AssertPred5Helper(const char* pred_text, // // Examples: // -// EXPECT_NE(5, Foo()); -// EXPECT_EQ(NULL, a_pointer); +// EXPECT_NE(Foo(), 5); +// EXPECT_EQ(a_pointer, NULL); // ASSERT_LT(i, array_size); // ASSERT_GT(records.size(), 0) << "There is no record left."; @@ -21057,6 +21908,57 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, #define EXPECT_NO_FATAL_FAILURE(statement) \ GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) +// Causes a trace (including the given source file path and line number, +// and the given message) to be included in every test failure message generated +// by code in the scope of the lifetime of an instance of this class. The effect +// is undone with the destruction of the instance. +// +// The message argument can be anything streamable to std::ostream. +// +// Example: +// testing::ScopedTrace trace("file.cc", 123, "message"); +// +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + + // Template version. Uses Message() to convert the values into strings. + // Slow, but flexible. + template + ScopedTrace(const char* file, int line, const T& message) { + PushTrace(file, line, (Message() << message).GetString()); + } + + // Optimize for some known types. + ScopedTrace(const char* file, int line, const char* message) { + PushTrace(file, line, message ? message : "(null)"); + } + +#if GTEST_HAS_GLOBAL_STRING + ScopedTrace(const char* file, int line, const ::string& message) { + PushTrace(file, line, message); + } +#endif + + ScopedTrace(const char* file, int line, const std::string& message) { + PushTrace(file, line, message); + } + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + void PushTrace(const char* file, int line, std::string message); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + // Causes a trace (including the source file path, the current line // number, and the given message) to be included in every test failure // message generated by code in the current scope. The effect is @@ -21068,9 +21970,14 @@ GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, // of the dummy variable name, thus allowing multiple SCOPED_TRACE()s // to appear in the same block - as long as they are on different // lines. +// +// Assuming that each thread maintains its own stack of traces. +// Therefore, a SCOPED_TRACE() would (correctly) only affect the +// assertions in its own thread. #define SCOPED_TRACE(message) \ - ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ - __FILE__, __LINE__, ::testing::Message() << (message)) + ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, (message)) + // Compile-time assertion for type equality. // StaticAssertTypeEq() compiles iff type1 and type2 are @@ -21150,7 +22057,7 @@ bool StaticAssertTypeEq() { // name of the test within the test case. // // A test fixture class must be declared earlier. The user should put -// his test code between braces after using this macro. Example: +// the test code between braces after using this macro. Example: // // class FooTest : public testing::Test { // protected: @@ -21165,14 +22072,22 @@ bool StaticAssertTypeEq() { // } // // TEST_F(FooTest, ReturnsElementCountCorrectly) { -// EXPECT_EQ(0, a_.size()); -// EXPECT_EQ(1, b_.size()); +// EXPECT_EQ(a_.size(), 0); +// EXPECT_EQ(b_.size(), 1); // } #define TEST_F(test_fixture, test_name)\ GTEST_TEST_(test_fixture, test_name, test_fixture, \ ::testing::internal::GetTypeId()) +// Returns a path to temporary directory. +// Tries to determine an appropriate directory for the platform. +GTEST_API_ std::string TempDir(); + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + } // namespace testing // Use this function in main() to run all tests. It returns 0 if all @@ -21189,4 +22104,6 @@ inline int RUN_ALL_TESTS() { return ::testing::UnitTest::GetInstance()->Run(); } +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + #endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/modules/ts/src/ts_gtest.cpp b/modules/ts/src/ts_gtest.cpp index 4f8c08aa3d..9f2b3d5653 100644 --- a/modules/ts/src/ts_gtest.cpp +++ b/modules/ts/src/ts_gtest.cpp @@ -26,10 +26,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // -// Author: mheule@google.com (Markus Heule) -// -// Google C++ Testing Framework (Google Test) +// Google C++ Testing and Mocking Framework (Google Test) // // Sometimes it's desirable to build Google Test by compiling a single file. // This file serves this purpose. @@ -67,10 +66,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + // -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) +// The Google C++ Testing and Mocking Framework (Google Test) // Copyright 2007, Google Inc. // All rights reserved. @@ -100,16 +98,20 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) + // // Utilities for testing Google Test itself and code that uses Google Test // (e.g. frameworks built on top of Google Test). +// GOOGLETEST_CM0004 DO NOT DELETE + #ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ #define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + namespace testing { // This helper class can be used to mock out Google Test failure reporting @@ -170,13 +172,12 @@ class GTEST_API_ SingleFailureChecker { public: // The constructor remembers the arguments. SingleFailureChecker(const TestPartResultArray* results, - TestPartResult::Type type, - const string& substr); + TestPartResult::Type type, const std::string& substr); ~SingleFailureChecker(); private: const TestPartResultArray* const results_; const TestPartResult::Type type_; - const string substr_; + const std::string substr_; GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); }; @@ -185,6 +186,8 @@ class GTEST_API_ SingleFailureChecker { } // namespace testing +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + // A set of macros for testing Google Test assertions or code that's expected // to generate Google Test fatal failures. It verifies that the given // statement will cause exactly one fatal Google Test failure with 'substr' @@ -324,7 +327,7 @@ class GTEST_API_ SingleFailureChecker { #if GTEST_OS_LINUX -// TODO(kenton@google.com): Use autoconf to detect availability of +// FIXME: Use autoconf to detect availability of // gettimeofday(). # define GTEST_HAS_GETTIMEOFDAY_ 1 @@ -363,9 +366,9 @@ class GTEST_API_ SingleFailureChecker { # if GTEST_OS_WINDOWS_MINGW // MinGW has gettimeofday() but not _ftime64(). -// TODO(kenton@google.com): Use autoconf to detect availability of +// FIXME: Use autoconf to detect availability of // gettimeofday(). -// TODO(kenton@google.com): There are other ways to get the time on +// FIXME: There are other ways to get the time on // Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW // supports these. consider using them instead. # define GTEST_HAS_GETTIMEOFDAY_ 1 @@ -380,7 +383,7 @@ class GTEST_API_ SingleFailureChecker { #else // Assume other platforms have gettimeofday(). -// TODO(kenton@google.com): Use autoconf to detect availability of +// FIXME: Use autoconf to detect availability of // gettimeofday(). # define GTEST_HAS_GETTIMEOFDAY_ 1 @@ -402,12 +405,6 @@ class GTEST_API_ SingleFailureChecker { # include // NOLINT #endif -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 // Copyright 2005, Google Inc. // All rights reserved. // @@ -437,24 +434,13 @@ class GTEST_API_ SingleFailureChecker { // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Utility functions and classes used by the Google C++ testing framework. -// -// Author: wan@google.com (Zhanyong Wan) -// +// Utility functions and classes used by the Google C++ testing framework.// // This file contains purely Google Test's internal implementation. Please // DO NOT #INCLUDE IT IN A USER PROGRAM. #ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ #define GTEST_SRC_GTEST_INTERNAL_INL_H_ -// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is -// part of Google Test's implementation; otherwise it's undefined. -#if !GTEST_IMPLEMENTATION_ -// If this file is included from the user's code, just say no. -# error "gtest-internal-inl.h is part of Google Test's internal implementation." -# error "It must not be included except by Google Test itself." -#endif // GTEST_IMPLEMENTATION_ - #ifndef _WIN32_WCE # include #endif // !_WIN32_WCE @@ -477,6 +463,9 @@ class GTEST_API_ SingleFailureChecker { #endif // GTEST_OS_WINDOWS +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \ +/* class A needs to have dll-interface to be used by clients of class B */) + namespace testing { // Declares the flags. @@ -501,6 +490,7 @@ const char kFilterFlag[] = "filter"; const char kListTestsFlag[] = "list_tests"; const char kOutputFlag[] = "output"; const char kPrintTimeFlag[] = "print_time"; +const char kPrintUTF8Flag[] = "print_utf8"; const char kRandomSeedFlag[] = "random_seed"; const char kRepeatFlag[] = "repeat"; const char kShuffleFlag[] = "shuffle"; @@ -581,6 +571,7 @@ class GTestFlagSaver { list_tests_ = GTEST_FLAG(list_tests); output_ = GTEST_FLAG(output); print_time_ = GTEST_FLAG(print_time); + print_utf8_ = GTEST_FLAG(print_utf8); random_seed_ = GTEST_FLAG(random_seed); repeat_ = GTEST_FLAG(repeat); shuffle_ = GTEST_FLAG(shuffle); @@ -602,6 +593,7 @@ class GTestFlagSaver { GTEST_FLAG(list_tests) = list_tests_; GTEST_FLAG(output) = output_; GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(print_utf8) = print_utf8_; GTEST_FLAG(random_seed) = random_seed_; GTEST_FLAG(repeat) = repeat_; GTEST_FLAG(shuffle) = shuffle_; @@ -623,6 +615,7 @@ class GTestFlagSaver { bool list_tests_; std::string output_; bool print_time_; + bool print_utf8_; internal::Int32 random_seed_; internal::Int32 repeat_; bool shuffle_; @@ -833,7 +826,7 @@ class OsStackTraceGetterInterface { // in the trace. // skip_count - the number of top frames to be skipped; doesn't count // against max_depth. - virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; + virtual std::string CurrentStackTrace(int max_depth, int skip_count) = 0; // UponLeavingGTest() should be called immediately before Google Test calls // user code. It saves some information about the current stack that @@ -853,10 +846,20 @@ class OsStackTraceGetter : public OsStackTraceGetterInterface { public: OsStackTraceGetter() {} - virtual string CurrentStackTrace(int max_depth, int skip_count); + virtual std::string CurrentStackTrace(int max_depth, int skip_count); virtual void UponLeavingGTest(); private: +#if GTEST_HAS_ABSL + Mutex mutex_; // Protects all internal state. + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to the stack trace code from within the user code. + void* caller_frame_ = nullptr; +#endif // GTEST_HAS_ABSL + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); }; @@ -1071,13 +1074,11 @@ class GTEST_API_ UnitTestImpl { tear_down_tc)->AddTestInfo(test_info); } -#if GTEST_HAS_PARAM_TEST // Returns ParameterizedTestCaseRegistry object used to keep track of // value-parameterized tests and instantiate and register them. internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { return parameterized_test_registry_; } -#endif // GTEST_HAS_PARAM_TEST // Sets the TestCase object for the test that's currently running. void set_current_test_case(TestCase* a_current_test_case) { @@ -1252,14 +1253,12 @@ class GTEST_API_ UnitTestImpl { // shuffled order. std::vector test_case_indices_; -#if GTEST_HAS_PARAM_TEST // ParameterizedTestRegistry object used to register value-parameterized // tests. internal::ParameterizedTestCaseRegistry parameterized_test_registry_; // Indicates whether RegisterParameterizedTests() has been called already. bool parameterized_tests_registered_; -#endif // GTEST_HAS_PARAM_TEST // Index of the last death test case registered. Initially -1. int last_death_test_case_; @@ -1399,7 +1398,7 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) { const bool parse_success = *end == '\0' && errno == 0; - // TODO(vladl@google.com): Convert this to compile time assertion when it is + // FIXME: Convert this to compile time assertion when it is // available. GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); @@ -1439,7 +1438,7 @@ class TestResultAccessor { #if GTEST_CAN_STREAM_RESULTS_ // Streams test results to the given port on the given host machine. -class GTEST_API_ StreamingListener : public EmptyTestEventListener { +class StreamingListener : public EmptyTestEventListener { public: // Abstract base class for writing strings to a socket. class AbstractSocketWriter { @@ -1447,21 +1446,19 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener { virtual ~AbstractSocketWriter() {} // Sends a string to the socket. - virtual void Send(const string& message) = 0; + virtual void Send(const std::string& message) = 0; // Closes the socket. virtual void CloseConnection() {} // Sends a string and a newline to the socket. - void SendLn(const string& message) { - Send(message + "\n"); - } + void SendLn(const std::string& message) { Send(message + "\n"); } }; // Concrete class for actually writing strings to a socket. class SocketWriter : public AbstractSocketWriter { public: - SocketWriter(const string& host, const string& port) + SocketWriter(const std::string& host, const std::string& port) : sockfd_(-1), host_name_(host), port_num_(port) { MakeConnection(); } @@ -1472,7 +1469,7 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener { } // Sends a string to the socket. - virtual void Send(const string& message) { + virtual void Send(const std::string& message) { GTEST_CHECK_(sockfd_ != -1) << "Send() can be called only when there is a connection."; @@ -1498,17 +1495,19 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener { } int sockfd_; // socket file descriptor - const string host_name_; - const string port_num_; + const std::string host_name_; + const std::string port_num_; GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); }; // class SocketWriter // Escapes '=', '&', '%', and '\n' characters in str as "%xx". - static string UrlEncode(const char* str); + static std::string UrlEncode(const char* str); - StreamingListener(const string& host, const string& port) - : socket_writer_(new SocketWriter(host, port)) { Start(); } + StreamingListener(const std::string& host, const std::string& port) + : socket_writer_(new SocketWriter(host, port)) { + Start(); + } explicit StreamingListener(AbstractSocketWriter* socket_writer) : socket_writer_(socket_writer) { Start(); } @@ -1569,13 +1568,13 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener { private: // Sends the given message and a newline to the socket. - void SendLn(const string& message) { socket_writer_->SendLn(message); } + void SendLn(const std::string& message) { socket_writer_->SendLn(message); } // Called at the start of streaming to notify the receiver what // protocol we are using. void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } - string FormatBool(bool value) { return value ? "1" : "0"; } + std::string FormatBool(bool value) { return value ? "1" : "0"; } const scoped_ptr socket_writer_; @@ -1587,13 +1586,27 @@ class GTEST_API_ StreamingListener : public EmptyTestEventListener { } // namespace internal } // namespace testing +GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 + #endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ -#undef GTEST_IMPLEMENTATION_ #if GTEST_OS_WINDOWS # define vsnprintf _vsnprintf #endif // GTEST_OS_WINDOWS +#if GTEST_OS_MAC +#ifndef GTEST_OS_IOS +#include +#endif +#endif + +#if GTEST_HAS_ABSL +#include "absl/debugging/failure_signal_handler.h" +#include "absl/debugging/stacktrace.h" +#include "absl/debugging/symbolize.h" +#include "absl/strings/str_cat.h" +#endif // GTEST_HAS_ABSL + namespace testing { using internal::CountIf; @@ -1615,8 +1628,10 @@ static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; // A test filter that matches everything. static const char kUniversalFilter[] = "*"; -// The default output file for XML output. -static const char kDefaultOutputFile[] = "test_detail.xml"; +// The default output format. +static const char kDefaultOutputFormat[] = "xml"; +// The default output file. +static const char kDefaultOutputFile[] = "test_detail"; // The environment variable name for the test shard index. static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; @@ -1635,15 +1650,31 @@ const char kStackTraceMarker[] = "\nStack trace:\n"; // specified on the command line. bool g_help_flag = false; +// Utilty function to Open File for Writing +static FILE* OpenFileForWriting(const std::string& output_file) { + FILE* fileout = NULL; + FilePath output_file_path(output_file); + FilePath output_dir(output_file_path.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + fileout = posix::FOpen(output_file.c_str(), "w"); + } + if (fileout == NULL) { + GTEST_LOG_(FATAL) << "Unable to open file \"" << output_file << "\""; + } + return fileout; +} + } // namespace internal +// Bazel passes in the argument to '--test_filter' via the TESTBRIDGE_TEST_ONLY +// environment variable. static const char* GetDefaultFilter() { -#ifdef GTEST_TEST_FILTER_ENV_VAR_ - const char* const testbridge_test_only = getenv(GTEST_TEST_FILTER_ENV_VAR_); + const char* const testbridge_test_only = + internal::posix::GetEnv("TESTBRIDGE_TEST_ONLY"); if (testbridge_test_only != NULL) { return testbridge_test_only; } -#endif // GTEST_TEST_FILTER_ENV_VAR_ return kUniversalFilter; } @@ -1680,15 +1711,28 @@ GTEST_DEFINE_string_( "exclude). A test is run if it matches one of the positive " "patterns and does not match any of the negative patterns."); +GTEST_DEFINE_bool_( + install_failure_signal_handler, + internal::BoolFromGTestEnv("install_failure_signal_handler", false), + "If true and supported on the current platform, " GTEST_NAME_ " should " + "install a signal handler that dumps debugging information when fatal " + "signals are raised."); + GTEST_DEFINE_bool_(list_tests, false, "List all tests without running them."); +// The net priority order after flag processing is thus: +// --gtest_output command line flag +// GTEST_OUTPUT environment variable +// XML_OUTPUT_FILE environment variable +// '' GTEST_DEFINE_string_( output, - internal::StringFromGTestEnv("output", ""), - "A format (currently must be \"xml\"), optionally followed " - "by a colon and an output file name or directory. A directory " - "is indicated by a trailing pathname separator. " + internal::StringFromGTestEnv("output", + internal::OutputFlagAlsoCheckEnvVar().c_str()), + "A format (defaults to \"xml\" but can be specified to be \"json\"), " + "optionally followed by a colon and an output file name or directory. " + "A directory is indicated by a trailing pathname separator. " "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " "If a directory is specified, output files will be created " "within that directory, with file-names based on the test " @@ -1701,6 +1745,12 @@ GTEST_DEFINE_bool_( "True iff " GTEST_NAME_ " should display elapsed time in text output."); +GTEST_DEFINE_bool_( + print_utf8, + internal::BoolFromGTestEnv("print_utf8", true), + "True iff " GTEST_NAME_ + " prints UTF8 characters as text."); + GTEST_DEFINE_int32_( random_seed, internal::Int32FromGTestEnv("random_seed", 0), @@ -1742,7 +1792,7 @@ GTEST_DEFINE_bool_( internal::BoolFromGTestEnv("throw_on_failure", false), "When this flag is specified, a failed assertion will throw an exception " "if exceptions are enabled or exit the program with a non-zero code " - "otherwise."); + "otherwise. For use with an external test framework."); #if GTEST_USE_OWN_FLAGFILE_FLAG_ GTEST_DEFINE_string_( @@ -1758,7 +1808,8 @@ namespace internal { // than kMaxRange. UInt32 Random::Generate(UInt32 range) { // These constants are the same as are used in glibc's rand(3). - state_ = (1103515245U*state_ + 12345U) % kMaxRange; + // Use wider types than necessary to prevent unsigned overflow diagnostics. + state_ = static_cast(1103515245ULL*state_ + 12345U) % kMaxRange; GTEST_CHECK_(range > 0) << "Cannot generate a number in the range [0, 0)."; @@ -1832,12 +1883,15 @@ void AssertHelper::operator=(const Message& message) const { GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); // A copy of all command line arguments. Set by InitGoogleTest(). -::std::vector g_argvs; +static ::std::vector g_argvs; -const ::std::vector& GetArgvs() { +::std::vector GetArgvs() { #if defined(GTEST_CUSTOM_GET_ARGVS_) - return GTEST_CUSTOM_GET_ARGVS_(); -#else // defined(GTEST_CUSTOM_GET_ARGVS_) + // GTEST_CUSTOM_GET_ARGVS_() may return a container of std::string or + // ::string. This code converts it to the appropriate type. + const auto& custom = GTEST_CUSTOM_GET_ARGVS_(); + return ::std::vector(custom.begin(), custom.end()); +#else // defined(GTEST_CUSTOM_GET_ARGVS_) return g_argvs; #endif // defined(GTEST_CUSTOM_GET_ARGVS_) } @@ -1861,8 +1915,6 @@ FilePath GetCurrentExecutableName() { // Returns the output format, or "" for normal printed output. std::string UnitTestOptions::GetOutputFormat() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) return std::string(""); - const char* const colon = strchr(gtest_output_flag, ':'); return (colon == NULL) ? std::string(gtest_output_flag) : @@ -1873,19 +1925,22 @@ std::string UnitTestOptions::GetOutputFormat() { // was explicitly specified. std::string UnitTestOptions::GetAbsolutePathToOutputFile() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) - return ""; + + std::string format = GetOutputFormat(); + if (format.empty()) + format = std::string(kDefaultOutputFormat); const char* const colon = strchr(gtest_output_flag, ':'); if (colon == NULL) - return internal::FilePath::ConcatPaths( + return internal::FilePath::MakeFileName( internal::FilePath( UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(kDefaultOutputFile)).string(); + internal::FilePath(kDefaultOutputFile), 0, + format.c_str()).string(); internal::FilePath output_name(colon + 1); if (!output_name.IsAbsolutePath()) - // TODO(wan@google.com): on Windows \some\path is not an absolute + // FIXME: on Windows \some\path is not an absolute // path (as its meaning depends on the current drive), yet the // following logic for turning it into an absolute path is wrong. // Fix it. @@ -2076,12 +2131,12 @@ extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); // This predicate-formatter checks that 'results' contains a test part // failure of the given type and that the failure message contains the // given substring. -AssertionResult HasOneFailure(const char* /* results_expr */, - const char* /* type_expr */, - const char* /* substr_expr */, - const TestPartResultArray& results, - TestPartResult::Type type, - const string& substr) { +static AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const std::string& substr) { const std::string expected(type == TestPartResult::kFatalFailure ? "1 fatal failure" : "1 non-fatal failure"); @@ -2115,13 +2170,10 @@ AssertionResult HasOneFailure(const char* /* results_expr */, // The constructor of SingleFailureChecker remembers where to look up // test part results, what type of failure we expect, and what // substring the failure message should contain. -SingleFailureChecker:: SingleFailureChecker( - const TestPartResultArray* results, - TestPartResult::Type type, - const string& substr) - : results_(results), - type_(type), - substr_(substr) {} +SingleFailureChecker::SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const std::string& substr) + : results_(results), type_(type), substr_(substr) {} // The destructor of SingleFailureChecker verifies that the given // TestPartResultArray contains exactly one failure that has the given @@ -2262,7 +2314,7 @@ TimeInMillis GetTimeInMillis() { SYSTEMTIME now_systime; FILETIME now_filetime; ULARGE_INTEGER now_int64; - // TODO(kenton@google.com): Shouldn't this just use + // FIXME: Shouldn't this just use // GetSystemTimeAsFileTime()? GetSystemTime(&now_systime); if (SystemTimeToFileTime(&now_systime, &now_filetime)) { @@ -2278,11 +2330,11 @@ TimeInMillis GetTimeInMillis() { // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 // (deprecated function) there. - // TODO(kenton@google.com): Use GetTickCount()? Or use + // FIXME: Use GetTickCount()? Or use // SystemTimeToFileTime() - GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) + GTEST_DISABLE_MSC_DEPRECATED_PUSH_() _ftime64(&now); - GTEST_DISABLE_MSC_WARNINGS_POP_() + GTEST_DISABLE_MSC_DEPRECATED_POP_() return static_cast(now.time) * 1000 + now.millitm; #elif GTEST_HAS_GETTIMEOFDAY_ @@ -2619,7 +2671,7 @@ class Hunk { // Print a unified diff header for one hunk. // The format is // "@@ -, +, @@" - // where the left/right parts are ommitted if unnecessary. + // where the left/right parts are omitted if unnecessary. void PrintHeader(std::ostream* ss) const { *ss << "@@ "; if (removes_) { @@ -2763,13 +2815,14 @@ AssertionResult EqFailure(const char* lhs_expression, const std::string& rhs_value, bool ignoring_case) { Message msg; - msg << " Expected: " << lhs_expression; + msg << "Expected equality of these values:"; + msg << "\n " << lhs_expression; if (lhs_value != lhs_expression) { - msg << "\n Which is: " << lhs_value; + msg << "\n Which is: " << lhs_value; } - msg << "\nTo be equal to: " << rhs_expression; + msg << "\n " << rhs_expression; if (rhs_value != rhs_expression) { - msg << "\n Which is: " << rhs_value; + msg << "\n Which is: " << rhs_value; } if (ignoring_case) { @@ -2816,7 +2869,7 @@ AssertionResult DoubleNearPredFormat(const char* expr1, const double diff = fabs(val1 - val2); if (diff <= abs_error) return AssertionSuccess(); - // TODO(wan): do not print the value of an expression if it's + // FIXME: do not print the value of an expression if it's // already a literal. return AssertionFailure() << "The difference between " << expr1 << " and " << expr2 @@ -3111,7 +3164,7 @@ namespace { AssertionResult HRESULTFailureHelper(const char* expr, const char* expected, long hr) { // NOLINT -# if GTEST_OS_WINDOWS_MOBILE +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_TV_TITLE // Windows CE doesn't support FormatMessage. const char error_text[] = ""; @@ -3168,7 +3221,7 @@ AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT // Utility functions for encoding Unicode text (wide strings) in // UTF-8. -// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// A Unicode code-point can have up to 21 bits, and is encoded in UTF-8 // like this: // // Code-point length Encoding @@ -3232,7 +3285,7 @@ std::string CodePointToUtf8(UInt32 code_point) { return str; } -// The following two functions only make sense if the the system +// The following two functions only make sense if the system // uses UTF-16 for wide string encoding. All supported systems // with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. @@ -3544,13 +3597,8 @@ static const char* const kReservedTestSuiteAttributes[] = { // The list of reserved attributes used in the element of XML output. static const char* const kReservedTestCaseAttributes[] = { - "classname", - "name", - "status", - "time", - "type_param", - "value_param" -}; + "classname", "name", "status", "time", + "type_param", "value_param", "file", "line"}; template std::vector ArrayAsVector(const char* const (&array)[kSize]) { @@ -3586,8 +3634,9 @@ static std::string FormatWordList(const std::vector& words) { return word_list.GetString(); } -bool ValidateTestPropertyName(const std::string& property_name, - const std::vector& reserved_names) { +static bool ValidateTestPropertyName( + const std::string& property_name, + const std::vector& reserved_names) { if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != reserved_names.end()) { ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name @@ -3884,6 +3933,8 @@ Result HandleExceptionsInMethodIfSupported( #if GTEST_HAS_EXCEPTIONS try { return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const AssertionException&) { // NOLINT + // This failure was reported already. } catch (const internal::GoogleTestFailureException&) { // NOLINT // This exception type can only be thrown by a failed Google // Test assertion with the intention of letting another testing @@ -4005,7 +4056,6 @@ TestInfo* MakeAndRegisterTestInfo( return test_info; } -#if GTEST_HAS_PARAM_TEST void ReportInvalidTestCaseType(const char* test_case_name, CodeLocation code_location) { Message errors; @@ -4019,13 +4069,10 @@ void ReportInvalidTestCaseType(const char* test_case_name, << "probably rename one of the classes to put the tests into different\n" << "test cases."; - fprintf(stderr, "%s %s", - FormatFileLocation(code_location.file.c_str(), - code_location.line).c_str(), - errors.GetString().c_str()); + GTEST_LOG_(ERROR) << FormatFileLocation(code_location.file.c_str(), + code_location.line) + << " " << errors.GetString(); } -#endif // GTEST_HAS_PARAM_TEST - } // namespace internal namespace { @@ -4063,12 +4110,10 @@ namespace internal { // and INSTANTIATE_TEST_CASE_P into regular tests and registers those. // This will be done just once during the program runtime. void UnitTestImpl::RegisterParameterizedTests() { -#if GTEST_HAS_PARAM_TEST if (!parameterized_tests_registered_) { parameterized_test_registry_.RegisterTests(); parameterized_tests_registered_ = true; } -#endif } } // namespace internal @@ -4096,18 +4141,18 @@ void TestInfo::Run() { factory_, &internal::TestFactoryBase::CreateTest, "the test fixture's constructor"); - // Runs the test only if the test object was created and its - // constructor didn't generate a fatal failure. - if ((test != NULL) && !Test::HasFatalFailure()) { + // Runs the test if the constructor didn't generate a fatal failure. + // Note that the object will not be null + if (!Test::HasFatalFailure()) { // This doesn't throw as all user code that can throw are wrapped into // exception handling code. test->Run(); } - // Deletes the test object. - impl->os_stack_trace_getter()->UponLeavingGTest(); - internal::HandleExceptionsInMethodIfSupported( - test, &Test::DeleteSelf_, "the test fixture's destructor"); + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); result_.set_elapsed_time(internal::GetTimeInMillis() - start); @@ -4333,10 +4378,10 @@ enum GTestColor { }; #if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ - !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW // Returns the character attribute for the given color. -WORD GetColorAttribute(GTestColor color) { +static WORD GetColorAttribute(GTestColor color) { switch (color) { case COLOR_RED: return FOREGROUND_RED; case COLOR_GREEN: return FOREGROUND_GREEN; @@ -4345,11 +4390,42 @@ WORD GetColorAttribute(GTestColor color) { } } +static int GetBitOffset(WORD color_mask) { + if (color_mask == 0) return 0; + + int bitOffset = 0; + while ((color_mask & 1) == 0) { + color_mask >>= 1; + ++bitOffset; + } + return bitOffset; +} + +static WORD GetNewColor(GTestColor color, WORD old_color_attrs) { + // Let's reuse the BG + static const WORD background_mask = BACKGROUND_BLUE | BACKGROUND_GREEN | + BACKGROUND_RED | BACKGROUND_INTENSITY; + static const WORD foreground_mask = FOREGROUND_BLUE | FOREGROUND_GREEN | + FOREGROUND_RED | FOREGROUND_INTENSITY; + const WORD existing_bg = old_color_attrs & background_mask; + + WORD new_color = + GetColorAttribute(color) | existing_bg | FOREGROUND_INTENSITY; + static const int bg_bitOffset = GetBitOffset(background_mask); + static const int fg_bitOffset = GetBitOffset(foreground_mask); + + if (((new_color & background_mask) >> bg_bitOffset) == + ((new_color & foreground_mask) >> fg_bitOffset)) { + new_color ^= FOREGROUND_INTENSITY; // invert intensity + } + return new_color; +} + #else // Returns the ANSI color code for the given color. COLOR_DEFAULT is // an invalid input. -const char* GetAnsiColorCode(GTestColor color) { +static const char* GetAnsiColorCode(GTestColor color) { switch (color) { case COLOR_RED: return "1"; case COLOR_GREEN: return "2"; @@ -4365,7 +4441,7 @@ bool ShouldUseColor(bool stdout_is_tty) { const char* const gtest_color = GTEST_FLAG(color).c_str(); if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { -#if GTEST_OS_WINDOWS +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW // On Windows the TERM variable is usually not set, but the // console there does support colors. return stdout_is_tty; @@ -4401,7 +4477,7 @@ bool ShouldUseColor(bool stdout_is_tty) { // cannot simply emit special characters and have the terminal change colors. // This routine must actually emit the characters rather than return a string // that would be colored when printed, as can be done on Linux. -void ColoredPrintf(GTestColor color, const char* fmt, ...) { +static void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_list args; va_start(args, fmt); @@ -4422,20 +4498,21 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { } #if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ - !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT && !GTEST_OS_WINDOWS_MINGW const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // Gets the current text color. CONSOLE_SCREEN_BUFFER_INFO buffer_info; GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); const WORD old_color_attrs = buffer_info.wAttributes; + const WORD new_color = GetNewColor(color, old_color_attrs); // We need to flush the stream buffers into the console before each // SetConsoleTextAttribute call lest it affect the text that is already // printed but has not yet reached the console. fflush(stdout); - SetConsoleTextAttribute(stdout_handle, - GetColorAttribute(color) | FOREGROUND_INTENSITY); + SetConsoleTextAttribute(stdout_handle, new_color); + vprintf(fmt, args); fflush(stdout); @@ -4449,12 +4526,12 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_end(args); } -// Text printed in Google Test's text output and --gunit_list_tests +// Text printed in Google Test's text output and --gtest_list_tests // output to label the type parameter and value parameter for a test. static const char kTypeParamLabel[] = "TypeParam"; static const char kValueParamLabel[] = "GetParam()"; -void PrintFullTestCommentIfPresent(const TestInfo& test_info) { +static void PrintFullTestCommentIfPresent(const TestInfo& test_info) { const char* const type_param = test_info.type_param(); const char* const value_param = test_info.value_param(); @@ -4725,7 +4802,7 @@ void TestEventRepeater::Append(TestEventListener *listener) { listeners_.push_back(listener); } -// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +// FIXME: Factor the search functionality into Vector::Find. TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { for (size_t i = 0; i < listeners_.size(); ++i) { if (listeners_[i] == listener) { @@ -4799,6 +4876,11 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { explicit XmlUnitTestResultPrinter(const char* output_file); virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + void ListTestsMatchingFilter(const std::vector& test_cases); + + // Prints an XML summary of all unit tests. + static void PrintXmlTestsList(std::ostream* stream, + const std::vector& test_cases); private: // Is c a whitespace character that is normalized to a space character @@ -4860,6 +4942,11 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { // to delimit this attribute from prior attributes. static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + // Streams an XML representation of the test properties of a TestResult + // object. + static void OutputXmlTestProperties(std::ostream* stream, + const TestResult& result); + // The output file. const std::string output_file_; @@ -4869,46 +4956,30 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { // Creates a new XmlUnitTestResultPrinter. XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) : output_file_(output_file) { - if (output_file_.c_str() == NULL || output_file_.empty()) { - fprintf(stderr, "XML output file may not be null\n"); - fflush(stderr); - exit(EXIT_FAILURE); + if (output_file_.empty()) { + GTEST_LOG_(FATAL) << "XML output file may not be null"; } } // Called after the unit test ends. void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, int /*iteration*/) { - FILE* xmlout = NULL; - FilePath output_file(output_file_); - FilePath output_dir(output_file.RemoveFileName()); - - if (output_dir.CreateDirectoriesRecursively()) { - xmlout = posix::FOpen(output_file_.c_str(), "w"); - } - if (xmlout == NULL) { - // TODO(wan): report the reason of the failure. - // - // We don't do it for now as: - // - // 1. There is no urgent need for it. - // 2. It's a bit involved to make the errno variable thread-safe on - // all three operating systems (Linux, Windows, and Mac OS). - // 3. To interpret the meaning of errno in a thread-safe way, - // we need the strerror_r() function, which is not available on - // Windows. - fprintf(stderr, - "Unable to open file \"%s\"\n", - output_file_.c_str()); - fflush(stderr); - exit(EXIT_FAILURE); - } + FILE* xmlout = OpenFileForWriting(output_file_); std::stringstream stream; PrintXmlUnitTest(&stream, unit_test); fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); fclose(xmlout); } +void XmlUnitTestResultPrinter::ListTestsMatchingFilter( + const std::vector& test_cases) { + FILE* xmlout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintXmlTestsList(&stream, test_cases); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + // Returns an XML-escaped copy of the input string str. If is_attribute // is true, the text is meant to appear as an attribute value, and // normalizable whitespace is preserved by replacing it with character @@ -4919,7 +4990,7 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, // module will consist of ordinary English text. // If this module is ever modified to produce version 1.1 XML output, // most invalid characters can be retained using character references. -// TODO(wan): It might be nice to have a minimally invasive, human-readable +// FIXME: It might be nice to have a minimally invasive, human-readable // escaping scheme for invalid characters, rather than dropping them. std::string XmlUnitTestResultPrinter::EscapeXml( const std::string& str, bool is_attribute) { @@ -4980,6 +5051,7 @@ std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( // The following routines generate an XML representation of a UnitTest // object. +// GOOGLETEST_CM0009 DO NOT DELETE // // This is how Google Test concepts map to the DTD: // @@ -5069,13 +5141,17 @@ void XmlUnitTestResultPrinter::OutputXmlAttribute( } // Prints an XML representation of a TestInfo object. -// TODO(wan): There is also value in printing properties with the plain printer. +// FIXME: There is also value in printing properties with the plain printer. void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, const char* test_case_name, const TestInfo& test_info) { const TestResult& result = *test_info.result(); const std::string kTestcase = "testcase"; + if (test_info.is_in_another_shard()) { + return; + } + *stream << " \n"; + return; + } OutputXmlAttribute(stream, kTestcase, "status", test_info.should_run() ? "run" : "notrun"); OutputXmlAttribute(stream, kTestcase, "time", FormatTimeInMillisAsSeconds(result.elapsed_time())); OutputXmlAttribute(stream, kTestcase, "classname", test_case_name); - *stream << TestPropertiesAsXmlAttributes(result); int failures = 0; for (int i = 0; i < result.total_part_count(); ++i) { @@ -5101,22 +5183,28 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, if (++failures == 1) { *stream << ">\n"; } - const string location = internal::FormatCompilerIndependentFileLocation( - part.file_name(), part.line_number()); - const string summary = location + "\n" + part.summary(); + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string summary = location + "\n" + part.summary(); *stream << " "; - const string detail = location + "\n" + part.message(); + const std::string detail = location + "\n" + part.message(); OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); *stream << "\n"; } } - if (failures == 0) + if (failures == 0 && result.test_property_count() == 0) { *stream << " />\n"; - else + } else { + if (failures == 0) { + *stream << ">\n"; + } + OutputXmlTestProperties(stream, result); *stream << " \n"; + } } // Prints an XML representation of a TestCase object @@ -5127,17 +5215,18 @@ void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); OutputXmlAttribute(stream, kTestsuite, "tests", StreamableToString(test_case.reportable_test_count())); - OutputXmlAttribute(stream, kTestsuite, "failures", - StreamableToString(test_case.failed_test_count())); - OutputXmlAttribute( - stream, kTestsuite, "disabled", - StreamableToString(test_case.reportable_disabled_test_count())); - OutputXmlAttribute(stream, kTestsuite, "errors", "0"); - OutputXmlAttribute(stream, kTestsuite, "time", - FormatTimeInMillisAsSeconds(test_case.elapsed_time())); - *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) - << ">\n"; - + if (!GTEST_FLAG(list_tests)) { + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()); + } + *stream << ">\n"; for (int i = 0; i < test_case.total_test_count(); ++i) { if (test_case.GetTestInfo(i)->is_reportable()) OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); @@ -5171,7 +5260,6 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, OutputXmlAttribute(stream, kTestsuites, "random_seed", StreamableToString(unit_test.random_seed())); } - *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); @@ -5184,6 +5272,28 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, *stream << "\n"; } +void XmlUnitTestResultPrinter::PrintXmlTestsList( + std::ostream* stream, const std::vector& test_cases) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + int total_tests = 0; + for (size_t i = 0; i < test_cases.size(); ++i) { + total_tests += test_cases[i]->total_test_count(); + } + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(total_tests)); + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (size_t i = 0; i < test_cases.size(); ++i) { + PrintXmlTestCase(stream, *test_cases[i]); + } + *stream << "\n"; +} + // Produces a string representing the test properties in a result as space // delimited XML attributes based on the property key="value" pairs. std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( @@ -5197,119 +5307,536 @@ std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( return attributes.GetString(); } +void XmlUnitTestResultPrinter::OutputXmlTestProperties( + std::ostream* stream, const TestResult& result) { + const std::string kProperties = "properties"; + const std::string kProperty = "property"; + + if (result.test_property_count() <= 0) { + return; + } + + *stream << "<" << kProperties << ">\n"; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + *stream << "<" << kProperty; + *stream << " name=\"" << EscapeXmlAttribute(property.key()) << "\""; + *stream << " value=\"" << EscapeXmlAttribute(property.value()) << "\""; + *stream << "/>\n"; + } + *stream << "\n"; +} + // End XmlUnitTestResultPrinter -#if GTEST_CAN_STREAM_RESULTS_ +// This class generates an JSON output file. +class JsonUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit JsonUnitTestResultPrinter(const char* output_file); -// Checks if str contains '=', '&', '%' or '\n' characters. If yes, -// replaces them by "%xx" where xx is their hexadecimal value. For -// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) -// in both time and space -- important as the input str may contain an -// arbitrarily long test failure message and stack trace. -string StreamingListener::UrlEncode(const char* str) { - string result; - result.reserve(strlen(str) + 1); - for (char ch = *str; ch != '\0'; ch = *++str) { + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + // Prints an JSON summary of all unit tests. + static void PrintJsonTestList(::std::ostream* stream, + const std::vector& test_cases); + + private: + // Returns an JSON-escaped copy of the input string str. + static std::string EscapeJson(const std::string& str); + + //// Verifies that the given attribute belongs to the given element and + //// streams the attribute as JSON. + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma = true); + static void OutputJsonKey(std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma = true); + + // Streams a JSON representation of a TestInfo object. + static void OutputJsonTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints a JSON representation of a TestCase object + static void PrintJsonTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints a JSON summary of unit_test to output stream out. + static void PrintJsonUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as + // a JSON dictionary. + static std::string TestPropertiesAsJson(const TestResult& result, + const std::string& indent); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(JsonUnitTestResultPrinter); +}; + +// Creates a new JsonUnitTestResultPrinter. +JsonUnitTestResultPrinter::JsonUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.empty()) { + GTEST_LOG_(FATAL) << "JSON output file may not be null"; + } +} + +void JsonUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* jsonout = OpenFileForWriting(output_file_); + std::stringstream stream; + PrintJsonUnitTest(&stream, unit_test); + fprintf(jsonout, "%s", StringStreamToString(&stream).c_str()); + fclose(jsonout); +} + +// Returns an JSON-escaped copy of the input string str. +std::string JsonUnitTestResultPrinter::EscapeJson(const std::string& str) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; switch (ch) { - case '%': - case '=': - case '&': + case '\\': + case '"': + case '/': + m << '\\' << ch; + break; + case '\b': + m << "\\b"; + break; + case '\t': + m << "\\t"; + break; case '\n': - result.append("%" + String::FormatByte(static_cast(ch))); + m << "\\n"; + break; + case '\f': + m << "\\f"; + break; + case '\r': + m << "\\r"; break; default: - result.push_back(ch); + if (ch < ' ') { + m << "\\u00" << String::FormatByte(static_cast(ch)); + } else { + m << ch; + } break; } } - return result; -} -void StreamingListener::SocketWriter::MakeConnection() { - GTEST_CHECK_(sockfd_ == -1) - << "MakeConnection() can't be called when there is already a connection."; - - addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. - hints.ai_socktype = SOCK_STREAM; - addrinfo* servinfo = NULL; - - // Use the getaddrinfo() to get a linked list of IP addresses for - // the given host name. - const int error_num = getaddrinfo( - host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); - if (error_num != 0) { - GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " - << gai_strerror(error_num); - } + return m.GetString(); +} - // Loop through all the results and connect to the first we can. - for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; - cur_addr = cur_addr->ai_next) { - sockfd_ = socket( - cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); - if (sockfd_ != -1) { - // Connect the client socket to the server socket. - if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { - close(sockfd_); - sockfd_ = -1; - } - } - } +// The following routines generate an JSON representation of a UnitTest +// object. - freeaddrinfo(servinfo); // all done with this structure +// Formats the given time in milliseconds as seconds. +static std::string FormatTimeInMillisAsDuration(TimeInMillis ms) { + ::std::stringstream ss; + ss << (static_cast(ms) * 1e-3) << "s"; + return ss.str(); +} - if (sockfd_ == -1) { - GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " - << host_name_ << ":" << port_num_; - } +// Converts the given epoch time in milliseconds to a date string in the +// RFC3339 format, without the timezone information. +static std::string FormatEpochTimeInMillisAsRFC3339(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec) + "Z"; } -// End of class Streaming Listener -#endif // GTEST_CAN_STREAM_RESULTS__ +static inline std::string Indent(int width) { + return std::string(width, ' '); +} -// Class ScopedTrace +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value, + const std::string& indent, + bool comma) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); -// Pushes the given source file location and message onto a per-thread -// trace stack maintained by Google Test. -ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) - GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { - TraceInfo trace; - trace.file = file; - trace.line = line; - trace.message = message.GetString(); + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; - UnitTest::GetInstance()->PushGTestTrace(trace); + *stream << indent << "\"" << name << "\": \"" << EscapeJson(value) << "\""; + if (comma) + *stream << ",\n"; } -// Pops the info pushed by the c'tor. -ScopedTrace::~ScopedTrace() - GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { - UnitTest::GetInstance()->PopGTestTrace(); -} +void JsonUnitTestResultPrinter::OutputJsonKey( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + int value, + const std::string& indent, + bool comma) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Key \"" << name << "\" is not allowed for value \"" << element_name + << "\"."; -// class OsStackTraceGetter + *stream << indent << "\"" << name << "\": " << StreamableToString(value); + if (comma) + *stream << ",\n"; +} -const char* const OsStackTraceGetterInterface::kElidedFramesMarker = - "... " GTEST_NAME_ " internal frames ..."; +// Prints a JSON representation of a TestInfo object. +void JsonUnitTestResultPrinter::OutputJsonTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + const std::string kIndent = Indent(10); -string OsStackTraceGetter::CurrentStackTrace(int /*max_depth*/, - int /*skip_count*/) { - return ""; -} + *stream << Indent(8) << "{\n"; + OutputJsonKey(stream, kTestcase, "name", test_info.name(), kIndent); -void OsStackTraceGetter::UponLeavingGTest() {} + if (test_info.value_param() != NULL) { + OutputJsonKey(stream, kTestcase, "value_param", + test_info.value_param(), kIndent); + } + if (test_info.type_param() != NULL) { + OutputJsonKey(stream, kTestcase, "type_param", test_info.type_param(), + kIndent); + } + if (GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, kTestcase, "file", test_info.file(), kIndent); + OutputJsonKey(stream, kTestcase, "line", test_info.line(), kIndent, false); + *stream << "\n" << Indent(8) << "}"; + return; + } + + OutputJsonKey(stream, kTestcase, "status", + test_info.should_run() ? "RUN" : "NOTRUN", kIndent); + OutputJsonKey(stream, kTestcase, "time", + FormatTimeInMillisAsDuration(result.elapsed_time()), kIndent); + OutputJsonKey(stream, kTestcase, "classname", test_case_name, kIndent, false); + *stream << TestPropertiesAsJson(result, kIndent); + + int failures = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + *stream << ",\n"; + if (++failures == 1) { + *stream << kIndent << "\"" << "failures" << "\": [\n"; + } + const std::string location = + internal::FormatCompilerIndependentFileLocation(part.file_name(), + part.line_number()); + const std::string message = EscapeJson(location + "\n" + part.message()); + *stream << kIndent << " {\n" + << kIndent << " \"failure\": \"" << message << "\",\n" + << kIndent << " \"type\": \"\"\n" + << kIndent << " }"; + } + } + + if (failures > 0) + *stream << "\n" << kIndent << "]"; + *stream << "\n" << Indent(8) << "}"; +} + +// Prints an JSON representation of a TestCase object +void JsonUnitTestResultPrinter::PrintJsonTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + const std::string kIndent = Indent(6); + + *stream << Indent(4) << "{\n"; + OutputJsonKey(stream, kTestsuite, "name", test_case.name(), kIndent); + OutputJsonKey(stream, kTestsuite, "tests", test_case.reportable_test_count(), + kIndent); + if (!GTEST_FLAG(list_tests)) { + OutputJsonKey(stream, kTestsuite, "failures", test_case.failed_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuite, "disabled", + test_case.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuite, "errors", 0, kIndent); + OutputJsonKey(stream, kTestsuite, "time", + FormatTimeInMillisAsDuration(test_case.elapsed_time()), + kIndent, false); + *stream << TestPropertiesAsJson(test_case.ad_hoc_test_result(), kIndent) + << ",\n"; + } + + *stream << kIndent << "\"" << kTestsuite << "\": [\n"; + + bool comma = false; + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + OutputJsonTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + } + *stream << "\n" << kIndent << "]\n" << Indent(4) << "}"; +} + +// Prints a JSON summary of unit_test to output stream out. +void JsonUnitTestResultPrinter::PrintJsonUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + const std::string kIndent = Indent(2); + *stream << "{\n"; + + OutputJsonKey(stream, kTestsuites, "tests", unit_test.reportable_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "failures", unit_test.failed_test_count(), + kIndent); + OutputJsonKey(stream, kTestsuites, "disabled", + unit_test.reportable_disabled_test_count(), kIndent); + OutputJsonKey(stream, kTestsuites, "errors", 0, kIndent); + if (GTEST_FLAG(shuffle)) { + OutputJsonKey(stream, kTestsuites, "random_seed", unit_test.random_seed(), + kIndent); + } + OutputJsonKey(stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsRFC3339(unit_test.start_timestamp()), + kIndent); + OutputJsonKey(stream, kTestsuites, "time", + FormatTimeInMillisAsDuration(unit_test.elapsed_time()), kIndent, + false); + + *stream << TestPropertiesAsJson(unit_test.ad_hoc_test_result(), kIndent) + << ",\n"; + + OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent); + *stream << kIndent << "\"" << kTestsuites << "\": [\n"; + + bool comma = false; + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) { + if (comma) { + *stream << ",\n"; + } else { + comma = true; + } + PrintJsonTestCase(stream, *unit_test.GetTestCase(i)); + } + } + + *stream << "\n" << kIndent << "]\n" << "}\n"; +} + +void JsonUnitTestResultPrinter::PrintJsonTestList( + std::ostream* stream, const std::vector& test_cases) { + const std::string kTestsuites = "testsuites"; + const std::string kIndent = Indent(2); + *stream << "{\n"; + int total_tests = 0; + for (size_t i = 0; i < test_cases.size(); ++i) { + total_tests += test_cases[i]->total_test_count(); + } + OutputJsonKey(stream, kTestsuites, "tests", total_tests, kIndent); + + OutputJsonKey(stream, kTestsuites, "name", "AllTests", kIndent); + *stream << kIndent << "\"" << kTestsuites << "\": [\n"; + + for (size_t i = 0; i < test_cases.size(); ++i) { + if (i != 0) { + *stream << ",\n"; + } + PrintJsonTestCase(stream, *test_cases[i]); + } + + *stream << "\n" + << kIndent << "]\n" + << "}\n"; +} +// Produces a string representing the test properties in a result as +// a JSON dictionary. +std::string JsonUnitTestResultPrinter::TestPropertiesAsJson( + const TestResult& result, const std::string& indent) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << ",\n" << indent << "\"" << property.key() << "\": " + << "\"" << EscapeJson(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End JsonUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +std::string StreamingListener::UrlEncode(const char* str) { + std::string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// class OsStackTraceGetter + +const char* const OsStackTraceGetterInterface::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +std::string OsStackTraceGetter::CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_) { +#if GTEST_HAS_ABSL + std::string result; + + if (max_depth <= 0) { + return result; + } + + max_depth = std::min(max_depth, kMaxStackTraceDepth); + + std::vector raw_stack(max_depth); + // Skips the frames requested by the caller, plus this function. + const int raw_stack_size = + absl::GetStackTrace(&raw_stack[0], max_depth, skip_count + 1); + + void* caller_frame = nullptr; + { + MutexLock lock(&mutex_); + caller_frame = caller_frame_; + } + + for (int i = 0; i < raw_stack_size; ++i) { + if (raw_stack[i] == caller_frame && + !GTEST_FLAG(show_internal_stack_frames)) { + // Add a marker to the trace and stop adding frames. + absl::StrAppend(&result, kElidedFramesMarker, "\n"); + break; + } + + char tmp[1024]; + const char* symbol = "(unknown)"; + if (absl::Symbolize(raw_stack[i], tmp, sizeof(tmp))) { + symbol = tmp; + } + + char line[1024]; + snprintf(line, sizeof(line), " %p: %s\n", raw_stack[i], symbol); + result += line; + } + + return result; + +#else // !GTEST_HAS_ABSL + static_cast(max_depth); + static_cast(skip_count); + return ""; +#endif // GTEST_HAS_ABSL +} + +void OsStackTraceGetter::UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_) { +#if GTEST_HAS_ABSL + void* caller_frame = nullptr; + if (absl::GetStackTrace(&caller_frame, 1, 3) <= 0) { + caller_frame = nullptr; + } + + MutexLock lock(&mutex_); + caller_frame_ = caller_frame; +#endif // GTEST_HAS_ABSL +} // A helper class that creates the premature-exit file in its // constructor and deletes the file in its destructor. class ScopedPrematureExitFile { public: explicit ScopedPrematureExitFile(const char* premature_exit_filepath) - : premature_exit_filepath_(premature_exit_filepath) { + : premature_exit_filepath_(premature_exit_filepath ? + premature_exit_filepath : "") { // If a path to the premature-exit file is specified... - if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + if (!premature_exit_filepath_.empty()) { // create the file with a single "0" character in it. I/O // errors are ignored as there's nothing better we can do and we // don't want to fail the test because of this. @@ -5320,13 +5847,18 @@ class ScopedPrematureExitFile { } ~ScopedPrematureExitFile() { - if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { - remove(premature_exit_filepath_); + if (!premature_exit_filepath_.empty()) { + int retval = remove(premature_exit_filepath_.c_str()); + if (retval) { + GTEST_LOG_(ERROR) << "Failed to remove premature exit filepath \"" + << premature_exit_filepath_ << "\" with error " + << retval; + } } } private: - const char* const premature_exit_filepath_; + const std::string premature_exit_filepath_; GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); }; @@ -5596,6 +6128,11 @@ void UnitTest::AddTestPartResult( // when a failure happens and both the --gtest_break_on_failure and // the --gtest_catch_exceptions flags are specified. DebugBreak(); +#elif (!defined(__native_client__)) && \ + ((defined(__clang__) || defined(__GNUC__)) && \ + (defined(__x86_64__) || defined(__i386__))) + // with clang/gcc we can achieve the same effect on x86 by invoking int3 + asm("int3"); #else // Dereference NULL through a volatile pointer to prevent the compiler // from removing. We use this rather than abort() or __builtin_trap() for @@ -5663,7 +6200,7 @@ int UnitTest::Run() { // used for the duration of the program. impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); -#if GTEST_HAS_SEH +#if GTEST_OS_WINDOWS // Either the user wants Google Test to catch exceptions thrown by the // tests or this is executing in the context of death test child // process. In either case the user does not want to see pop-up dialogs @@ -5692,7 +6229,7 @@ int UnitTest::Run() { // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. // Users of prior VC versions shall suffer the agony and pain of // clicking through the countless debug dialogs. - // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // FIXME: find a way to suppress the abort dialog() in the // debug mode when compiled with VC 7.1 or lower. if (!GTEST_FLAG(break_on_failure)) _set_abort_behavior( @@ -5700,7 +6237,7 @@ int UnitTest::Run() { _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. # endif } -#endif // GTEST_HAS_SEH +#endif // GTEST_OS_WINDOWS return internal::HandleExceptionsInMethodIfSupported( impl(), @@ -5733,7 +6270,6 @@ const TestInfo* UnitTest::current_test_info() const // Returns the random seed used at the start of the current test run. int UnitTest::random_seed() const { return impl_->random_seed(); } -#if GTEST_HAS_PARAM_TEST // Returns ParameterizedTestCaseRegistry object used to keep track of // value-parameterized tests and instantiate and register them. internal::ParameterizedTestCaseRegistry& @@ -5741,7 +6277,6 @@ internal::ParameterizedTestCaseRegistry& GTEST_LOCK_EXCLUDED_(mutex_) { return impl_->parameterized_test_registry(); } -#endif // GTEST_HAS_PARAM_TEST // Creates an empty UnitTest. UnitTest::UnitTest() { @@ -5780,10 +6315,8 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent) &default_global_test_part_result_reporter_), per_thread_test_part_result_reporter_( &default_per_thread_test_part_result_reporter_), -#if GTEST_HAS_PARAM_TEST parameterized_test_registry_(), parameterized_tests_registered_(false), -#endif // GTEST_HAS_PARAM_TEST last_death_test_case_(-1), current_test_case_(NULL), current_test_info_(NULL), @@ -5850,10 +6383,12 @@ void UnitTestImpl::ConfigureXmlOutput() { if (output_format == "xml") { listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format == "json") { + listeners()->SetDefaultXmlGenerator(new JsonUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); } else if (output_format != "") { - printf("WARNING: unrecognized output format \"%s\" ignored.\n", - output_format.c_str()); - fflush(stdout); + GTEST_LOG_(WARNING) << "WARNING: unrecognized output format \"" + << output_format << "\" ignored."; } } @@ -5868,9 +6403,8 @@ void UnitTestImpl::ConfigureStreamingOutput() { listeners()->Append(new StreamingListener(target.substr(0, pos), target.substr(pos+1))); } else { - printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", - target.c_str()); - fflush(stdout); + GTEST_LOG_(WARNING) << "unrecognized streaming target \"" << target + << "\" ignored."; } } } @@ -5909,6 +6443,13 @@ void UnitTestImpl::PostFlagParsingInit() { // Configures listeners for streaming test results to the specified server. ConfigureStreamingOutput(); #endif // GTEST_CAN_STREAM_RESULTS_ + +#if GTEST_HAS_ABSL + if (GTEST_FLAG(install_failure_signal_handler)) { + absl::FailureSignalHandlerOptions options; + absl::InstallFailureSignalHandler(options); + } +#endif // GTEST_HAS_ABSL } } @@ -5952,11 +6493,11 @@ TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc) { // Can we find a TestCase with the given name? - const std::vector::const_iterator test_case = - std::find_if(test_cases_.begin(), test_cases_.end(), + const std::vector::const_reverse_iterator test_case = + std::find_if(test_cases_.rbegin(), test_cases_.rend(), TestCaseNameIs(test_case_name)); - if (test_case != test_cases_.end()) + if (test_case != test_cases_.rend()) return *test_case; // No. Let's create one. @@ -5997,13 +6538,8 @@ static void TearDownEnvironment(Environment* env) { env->TearDown(); } // All other functions called from RunAllTests() may safely assume that // parameterized tests are ready to be counted and run. bool UnitTestImpl::RunAllTests() { - // Makes sure InitGoogleTest() was called. - if (!GTestIsInitialized()) { - printf("%s", - "\nThis test program did NOT call ::testing::InitGoogleTest " - "before calling RUN_ALL_TESTS(). Please fix it.\n"); - return false; - } + // True iff Google Test is initialized before RUN_ALL_TESTS() is called. + const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized(); // Do not run any test if the --help flag was specified. if (g_help_flag) @@ -6131,6 +6667,20 @@ bool UnitTestImpl::RunAllTests() { repeater->OnTestProgramEnd(*parent_); + if (!gtest_is_initialized_before_run_all_tests) { + ColoredPrintf( + COLOR_RED, + "\nIMPORTANT NOTICE - DO NOT IGNORE:\n" + "This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_ + "() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_ + " will start to enforce the valid usage. " + "Please fix it ASAP, or IT WILL START TO FAIL.\n"); // NOLINT +#if GTEST_FOR_GOOGLE_ + ColoredPrintf(COLOR_RED, + "For more details, see http://wiki/Main/ValidGUnitMain.\n"); +#endif // GTEST_FOR_GOOGLE_ + } + return !failed; } @@ -6232,8 +6782,8 @@ bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { // each TestCase and TestInfo object. // If shard_tests == true, further filters tests based on sharding // variables in the environment - see -// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. -// Returns the number of tests that should run. +// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md +// . Returns the number of tests that should run. int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? Int32FromEnvOrDie(kTestTotalShards, -1) : -1; @@ -6272,10 +6822,11 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && matches_filter; - const bool is_selected = is_runnable && - (shard_tests == IGNORE_SHARDING_PROTOCOL || - ShouldRunTestOnShard(total_shards, shard_index, - num_runnable_tests)); + const bool is_in_another_shard = + shard_tests != IGNORE_SHARDING_PROTOCOL && + !ShouldRunTestOnShard(total_shards, shard_index, num_runnable_tests); + test_info->is_in_another_shard_ = is_in_another_shard; + const bool is_selected = is_runnable && !is_in_another_shard; num_runnable_tests += is_runnable; num_selected_tests += is_selected; @@ -6345,6 +6896,23 @@ void UnitTestImpl::ListTestsMatchingFilter() { } } fflush(stdout); + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml" || output_format == "json") { + FILE* fileout = OpenFileForWriting( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()); + std::stringstream stream; + if (output_format == "xml") { + XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()) + .PrintXmlTestsList(&stream, test_cases_); + } else if (output_format == "json") { + JsonUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str()) + .PrintJsonTestList(&stream, test_cases_); + } + fprintf(fileout, "%s", StringStreamToString(&stream).c_str()); + fclose(fileout); + } } // Sets the OS stack trace getter. @@ -6375,11 +6943,15 @@ OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { return os_stack_trace_getter_; } -// Returns the TestResult for the test that's currently running, or -// the TestResult for the ad hoc test if no test is running. +// Returns the most specific TestResult currently running. TestResult* UnitTestImpl::current_test_result() { - return current_test_info_ ? - &(current_test_info_->result_) : &ad_hoc_test_result_; + if (current_test_info_ != NULL) { + return ¤t_test_info_->result_; + } + if (current_test_case_ != NULL) { + return ¤t_test_case_->ad_hoc_test_result_; + } + return &ad_hoc_test_result_; } // Shuffles all test cases, and the tests within each test case, @@ -6460,9 +7032,8 @@ bool SkipPrefix(const char* prefix, const char** pstr) { // part can be omitted. // // Returns the value of the flag, or NULL if the parsing failed. -const char* ParseFlagValue(const char* str, - const char* flag, - bool def_optional) { +static const char* ParseFlagValue(const char* str, const char* flag, + bool def_optional) { // str and flag must not be NULL. if (str == NULL || flag == NULL) return NULL; @@ -6498,7 +7069,7 @@ const char* ParseFlagValue(const char* str, // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -bool ParseBoolFlag(const char* str, const char* flag, bool* value) { +static bool ParseBoolFlag(const char* str, const char* flag, bool* value) { // Gets the value of the flag as a string. const char* const value_str = ParseFlagValue(str, flag, true); @@ -6532,7 +7103,8 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -bool ParseStringFlag(const char* str, const char* flag, std::string* value) { +template +static bool ParseStringFlag(const char* str, const char* flag, String* value) { // Gets the value of the flag as a string. const char* const value_str = ParseFlagValue(str, flag, false); @@ -6568,7 +7140,7 @@ static bool HasGoogleTestFlagPrefix(const char* str) { // @Y changes the color to yellow. // @D changes to the default terminal text color. // -// TODO(wan@google.com): Write tests for this once we add stdout +// FIXME: Write tests for this once we add stdout // capturing to Google Test. static void PrintColorEncoded(const char* str) { GTestColor color = COLOR_DEFAULT; // The current color. @@ -6634,24 +7206,25 @@ static const char kColorEncodedHelpMessage[] = " Enable/disable colored output. The default is @Gauto@D.\n" " -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" " Don't print the elapsed time of each test.\n" -" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" +" @G--" GTEST_FLAG_PREFIX_ "output=@Y(@Gjson@Y|@Gxml@Y)[@G:@YDIRECTORY_PATH@G" GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" -" Generate an XML report in the given directory or with the given file\n" -" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" -#if GTEST_CAN_STREAM_RESULTS_ +" Generate a JSON or XML report in the given directory or with the given\n" +" file name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +# if GTEST_CAN_STREAM_RESULTS_ " @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" " Stream test results to the given server.\n" -#endif // GTEST_CAN_STREAM_RESULTS_ +# endif // GTEST_CAN_STREAM_RESULTS_ "\n" "Assertion Behavior:\n" -#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +# if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS " @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" " Set the default death test style.\n" -#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +# endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS " @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" " Turn assertion failures into debugger break-points.\n" " @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" -" Turn assertion failures into C++ exceptions.\n" +" Turn assertion failures into C++ exceptions for use by an external\n" +" test framework.\n" " @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" " Do not report exceptions as test failures. Instead, allow them\n" " to crash the program or throw a pop-up (on Windows).\n" @@ -6668,7 +7241,7 @@ static const char kColorEncodedHelpMessage[] = "(not one in your own code or tests), please report it to\n" "@G<" GTEST_DEV_EMAIL_ ">@D.\n"; -bool ParseGoogleTestFlag(const char* const arg) { +static bool ParseGoogleTestFlag(const char* const arg) { return ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, >EST_FLAG(also_run_disabled_tests)) || ParseBoolFlag(arg, kBreakOnFailureFlag, @@ -6686,6 +7259,7 @@ bool ParseGoogleTestFlag(const char* const arg) { ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseBoolFlag(arg, kPrintUTF8Flag, >EST_FLAG(print_utf8)) || ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || @@ -6698,14 +7272,11 @@ bool ParseGoogleTestFlag(const char* const arg) { } #if GTEST_USE_OWN_FLAGFILE_FLAG_ -void LoadFlagsFromFile(const std::string& path) { +static void LoadFlagsFromFile(const std::string& path) { FILE* flagfile = posix::FOpen(path.c_str(), "r"); if (!flagfile) { - fprintf(stderr, - "Unable to open file \"%s\"\n", - GTEST_FLAG(flagfile).c_str()); - fflush(stderr); - exit(EXIT_FAILURE); + GTEST_LOG_(FATAL) << "Unable to open file \"" << GTEST_FLAG(flagfile) + << "\""; } std::string contents(ReadEntireFile(flagfile)); posix::FClose(flagfile); @@ -6779,6 +7350,17 @@ void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { // other parts of Google Test. void ParseGoogleTestFlagsOnly(int* argc, char** argv) { ParseGoogleTestFlagsOnlyImpl(argc, argv); + + // Fix the value of *_NSGetArgc() on macOS, but iff + // *_NSGetArgv() == argv + // Only applicable to char** version of argv +#if GTEST_OS_MAC +#ifndef GTEST_OS_IOS + if (*_NSGetArgv() == argv) { + *_NSGetArgc() = *argc; + } +#endif +#endif } void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { ParseGoogleTestFlagsOnlyImpl(argc, argv); @@ -6800,6 +7382,10 @@ void InitGoogleTestImpl(int* argc, CharType** argv) { g_argvs.push_back(StreamableToString(argv[i])); } +#if GTEST_HAS_ABSL + absl::InitializeSymbolizer(g_argvs[0].c_str()); +#endif // GTEST_HAS_ABSL + ParseGoogleTestFlagsOnly(argc, argv); GetUnitTestImpl()->PostFlagParsingInit(); } @@ -6833,6 +7419,47 @@ void InitGoogleTest(int* argc, wchar_t** argv) { #endif // defined(GTEST_CUSTOM_INIT_GOOGLE_TEST_FUNCTION_) } +std::string TempDir() { +#if defined(GTEST_CUSTOM_TEMPDIR_FUNCTION_) + return GTEST_CUSTOM_TEMPDIR_FUNCTION_(); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + return "\\temp\\"; +#elif GTEST_OS_WINDOWS + const char* temp_dir = internal::posix::GetEnv("TEMP"); + if (temp_dir == NULL || temp_dir[0] == '\0') + return "\\temp\\"; + else if (temp_dir[strlen(temp_dir) - 1] == '\\') + return temp_dir; + else + return std::string(temp_dir) + "\\"; +#elif GTEST_OS_LINUX_ANDROID + return "/sdcard/"; +#else + return "/tmp/"; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +void ScopedTrace::PushTrace(const char* file, int line, std::string message) { + internal::TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message.swap(message); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + } // namespace testing // Copyright 2005, Google Inc. // All rights reserved. @@ -6862,8 +7489,7 @@ void InitGoogleTest(int* argc, wchar_t** argv) { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) + // // This file implements death tests. @@ -6895,23 +7521,27 @@ void InitGoogleTest(int* argc, wchar_t** argv) { # include # endif // GTEST_OS_QNX -#endif // GTEST_HAS_DEATH_TEST +# if GTEST_OS_FUCHSIA +# include +# include +# include +# include +# include +# endif // GTEST_OS_FUCHSIA +#endif // GTEST_HAS_DEATH_TEST -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick exists to -// prevent the accidental inclusion of gtest-internal-inl.h in the -// user's code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ namespace testing { // Constants. // The default death test style. -static const char kDefaultDeathTestStyle[] = "fast"; +// +// This is defined in internal/gtest-port.h as "fast", but can be overridden by +// a definition in internal/custom/gtest-port.h. The recommended value, which is +// used internally at Google, is "threadsafe". +static const char kDefaultDeathTestStyle[] = GTEST_DEFAULT_DEATH_TEST_STYLE; GTEST_DEFINE_string_( death_test_style, @@ -6951,7 +7581,7 @@ namespace internal { // Valid only for fast death tests. Indicates the code is running in the // child process of a fast style death test. -# if !GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA static bool g_in_fast_death_test_child = false; # endif @@ -6961,10 +7591,10 @@ static bool g_in_fast_death_test_child = false; // tests. IMPORTANT: This is an internal utility. Using it may break the // implementation of death tests. User code MUST NOT use it. bool InDeathTestChild() { -# if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA - // On Windows, death tests are thread-safe regardless of the value of the - // death_test_style flag. + // On Windows and Fuchsia, death tests are thread-safe regardless of the value + // of the death_test_style flag. return !GTEST_FLAG(internal_run_death_test).empty(); # else @@ -6984,7 +7614,7 @@ ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { // ExitedWithCode function-call operator. bool ExitedWithCode::operator()(int exit_status) const { -# if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA return exit_status == exit_code_; @@ -6992,10 +7622,10 @@ bool ExitedWithCode::operator()(int exit_status) const { return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; -# endif // GTEST_OS_WINDOWS +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA } -# if !GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA // KilledBySignal constructor. KilledBySignal::KilledBySignal(int signum) : signum_(signum) { } @@ -7012,7 +7642,7 @@ bool KilledBySignal::operator()(int exit_status) const { # endif // defined(GTEST_KILLED_BY_SIGNAL_OVERRIDE_) return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; } -# endif // !GTEST_OS_WINDOWS +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA namespace internal { @@ -7023,7 +7653,7 @@ namespace internal { static std::string ExitSummary(int exit_code) { Message m; -# if GTEST_OS_WINDOWS +# if GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA m << "Exited with exit status " << exit_code; @@ -7039,7 +7669,7 @@ static std::string ExitSummary(int exit_code) { m << " (core dumped)"; } # endif -# endif // GTEST_OS_WINDOWS +# endif // GTEST_OS_WINDOWS || GTEST_OS_FUCHSIA return m.GetString(); } @@ -7050,7 +7680,7 @@ bool ExitedUnsuccessfully(int exit_status) { return !ExitedWithCode(0)(exit_status); } -# if !GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA // Generates a textual failure message when a death test finds more than // one thread running, or cannot determine the number of threads, prior // to executing the given statement. It is the responsibility of the @@ -7059,13 +7689,19 @@ static std::string DeathTestThreadWarning(size_t thread_count) { Message msg; msg << "Death tests use fork(), which is unsafe particularly" << " in a threaded context. For this test, " << GTEST_NAME_ << " "; - if (thread_count == 0) + if (thread_count == 0) { msg << "couldn't detect the number of threads."; - else + } else { msg << "detected " << thread_count << " threads."; + } + msg << " See " + "https://github.com/google/googletest/blob/master/googletest/docs/" + "advanced.md#death-tests-and-threads" + << " for more explanation and suggested solutions, especially if" + << " this is the last message you see before your test times out."; return msg.GetString(); } -# endif // !GTEST_OS_WINDOWS +# endif // !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA // Flag characters for reporting a death test that did not die. static const char kDeathTestLived = 'L'; @@ -7073,6 +7709,13 @@ static const char kDeathTestReturned = 'R'; static const char kDeathTestThrew = 'T'; static const char kDeathTestInternalError = 'I'; +#if GTEST_OS_FUCHSIA + +// File descriptor used for the pipe in the child process. +static const int kFuchsiaReadPipeFd = 3; + +#endif + // An enumeration describing all of the possible ways that a death test can // conclude. DIED means that the process died while executing the test // code; LIVED means that process lived beyond the end of the test code; @@ -7080,7 +7723,7 @@ static const char kDeathTestInternalError = 'I'; // statement, which is not allowed; THREW means that the test statement // returned control by throwing an exception. IN_PROGRESS means the test // has not yet concluded. -// TODO(vladl@google.com): Unify names and possibly values for +// FIXME: Unify names and possibly values for // AbortReason, DeathTestOutcome, and flag characters above. enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; @@ -7089,7 +7732,7 @@ enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; // message is propagated back to the parent process. Otherwise, the // message is simply printed to stderr. In either case, the program // then exits with status 1. -void DeathTestAbort(const std::string& message) { +static void DeathTestAbort(const std::string& message) { // On a POSIX system, this function may be called from a threadsafe-style // death test child process, which operates on a very small stack. Use // the heap for any additional non-minuscule memory requirements. @@ -7393,7 +8036,12 @@ bool DeathTestImpl::Passed(bool status_ok) { break; case DIED: if (status_ok) { +# if GTEST_USES_PCRE + // PCRE regexes support embedded NULs. + const bool matched = RE::PartialMatch(error_message, *regex()); +# else const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); +# endif // GTEST_USES_PCRE if (matched) { success = true; } else { @@ -7609,7 +8257,200 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() { set_spawned(true); return OVERSEE_TEST; } -# else // We are not on Windows. + +# elif GTEST_OS_FUCHSIA + +class FuchsiaDeathTest : public DeathTestImpl { + public: + FuchsiaDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + virtual ~FuchsiaDeathTest() { + zx_status_t status = zx_handle_close(child_process_); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + status = zx_handle_close(port_); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + } + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + + zx_handle_t child_process_ = ZX_HANDLE_INVALID; + zx_handle_t port_ = ZX_HANDLE_INVALID; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + int size() { + return args_.size() - 1; + } + + private: + std::vector args_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int FuchsiaDeathTest::Wait() { + if (!spawned()) + return 0; + + // Register to wait for the child process to terminate. + zx_status_t status_zx; + status_zx = zx_object_wait_async(child_process_, + port_, + 0 /* key */, + ZX_PROCESS_TERMINATED, + ZX_WAIT_ASYNC_ONCE); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Wait for it to terminate, or an exception to be received. + zx_port_packet_t packet; + status_zx = zx_port_wait(port_, ZX_TIME_INFINITE, &packet); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + if (ZX_PKT_IS_EXCEPTION(packet.type)) { + // Process encountered an exception. Kill it directly rather than letting + // other handlers process the event. + status_zx = zx_task_kill(child_process_); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + // Now wait for |child_process_| to terminate. + zx_signals_t signals = 0; + status_zx = zx_object_wait_one( + child_process_, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, &signals); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + GTEST_DEATH_TEST_CHECK_(signals & ZX_PROCESS_TERMINATED); + } else { + // Process terminated. + GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type)); + GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_PROCESS_TERMINATED); + } + + ReadAndInterpretStatusByte(); + + zx_info_process_t buffer; + status_zx = zx_object_get_info( + child_process_, + ZX_INFO_PROCESS, + &buffer, + sizeof(buffer), + nullptr, + nullptr); + GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); + + GTEST_DEATH_TEST_CHECK_(buffer.exited); + set_status(buffer.return_code); + return status(); +} + +// The AssumeRole process for a Fuchsia death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole FuchsiaDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(kFuchsiaReadPipeFd); + return EXECUTE_TEST; + } + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // Build the child process command line. + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + + StreamableToString(line_) + "|" + + StreamableToString(death_test_index); + Arguments args; + args.AddArguments(GetInjectableArgvs()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + // Build the pipe for communication with the child. + zx_status_t status; + zx_handle_t child_pipe_handle; + uint32_t type; + status = fdio_pipe_half(&child_pipe_handle, &type); + GTEST_DEATH_TEST_CHECK_(status >= 0); + set_read_fd(status); + + // Set the pipe handle for the child. + fdio_spawn_action_t add_handle_action = {}; + add_handle_action.action = FDIO_SPAWN_ACTION_ADD_HANDLE; + add_handle_action.h.id = PA_HND(type, kFuchsiaReadPipeFd); + add_handle_action.h.handle = child_pipe_handle; + + // Spawn the child process. + status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, + args.Argv()[0], args.Argv(), nullptr, 1, + &add_handle_action, &child_process_, nullptr); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + // Create an exception port and attach it to the |child_process_|, to allow + // us to suppress the system default exception handler from firing. + status = zx_port_create(0, &port_); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + status = zx_task_bind_exception_port( + child_process_, port_, 0 /* key */, 0 /*options */); + GTEST_DEATH_TEST_CHECK_(status == ZX_OK); + + set_spawned(true); + return OVERSEE_TEST; +} + +#else // We are neither on Windows, nor on Fuchsia. // ForkingDeathTest provides implementations for most of the abstract // methods of the DeathTest interface. Only the AssumeRole method is @@ -7713,11 +8554,10 @@ class ExecDeathTest : public ForkingDeathTest { ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } virtual TestRole AssumeRole(); private: - static ::std::vector - GetArgvsForDeathTestChildProcess() { - ::std::vector args = GetInjectableArgvs(); + static ::std::vector GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); # if defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) - ::std::vector extra_args = + ::std::vector extra_args = GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_(); args.insert(args.end(), extra_args.begin(), extra_args.end()); # endif // defined(GTEST_EXTRA_DEATH_TEST_COMMAND_LINE_ARGS_) @@ -7816,6 +8656,7 @@ static int ExecDeathTestChildMain(void* child_arg) { } # endif // !GTEST_OS_QNX +# if GTEST_HAS_CLONE // Two utility routines that together determine the direction the stack // grows. // This could be accomplished more elegantly by a single recursive @@ -7825,20 +8666,22 @@ static int ExecDeathTestChildMain(void* child_arg) { // GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining // StackLowerThanAddress into StackGrowsDown, which then doesn't give // correct answer. -void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; -void StackLowerThanAddress(const void* ptr, bool* result) { +static void StackLowerThanAddress(const void* ptr, + bool* result) GTEST_NO_INLINE_; +static void StackLowerThanAddress(const void* ptr, bool* result) { int dummy; *result = (&dummy < ptr); } // Make sure AddressSanitizer does not tamper with the stack here. GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ -bool StackGrowsDown() { +static bool StackGrowsDown() { int dummy; bool result; StackLowerThanAddress(&dummy, &result); return result; } +# endif // GTEST_HAS_CLONE // Spawns a child process with the same executable as the current process in // a thread-safe manner and instructs it to run the death test. The @@ -8030,6 +8873,13 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, *test = new WindowsDeathTest(statement, regex, file, line); } +# elif GTEST_OS_FUCHSIA + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new FuchsiaDeathTest(statement, regex, file, line); + } + # else if (GTEST_FLAG(death_test_style) == "threadsafe") { @@ -8054,7 +8904,7 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, // Recreates the pipe and event handles from the provided parameters, // signals the event, and returns a file descriptor wrapped around the pipe // handle. This function is called in the child process only. -int GetStatusFileDescriptor(unsigned int parent_process_id, +static int GetStatusFileDescriptor(unsigned int parent_process_id, size_t write_handle_as_size_t, size_t event_handle_as_size_t) { AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, @@ -8065,7 +8915,7 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, StreamableToString(parent_process_id)); } - // TODO(vladl@google.com): Replace the following check with a + // FIXME: Replace the following check with a // compile-time assertion when available. GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); @@ -8073,7 +8923,7 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, reinterpret_cast(write_handle_as_size_t); HANDLE dup_write_handle; - // The newly initialized handle is accessible only in in the parent + // The newly initialized handle is accessible only in the parent // process. To obtain one accessible within the child, we need to use // DuplicateHandle. if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, @@ -8150,6 +9000,16 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { write_fd = GetStatusFileDescriptor(parent_process_id, write_handle_as_size_t, event_handle_as_size_t); + +# elif GTEST_OS_FUCHSIA + + if (fields.size() != 3 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + # else if (fields.size() != 4 @@ -8198,8 +9058,6 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: keith.ray@gmail.com (Keith Ray) #include @@ -8217,6 +9075,7 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { # include // Some Linux distributions define PATH_MAX here. #endif // GTEST_OS_WINDOWS_MOBILE + #if GTEST_OS_WINDOWS # define GTEST_PATH_MAX_ _MAX_PATH #elif defined(PATH_MAX) @@ -8227,7 +9086,6 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { # define GTEST_PATH_MAX_ _POSIX_PATH_MAX #endif // GTEST_OS_WINDOWS - namespace testing { namespace internal { @@ -8298,7 +9156,7 @@ FilePath FilePath::RemoveExtension(const char* extension) const { return *this; } -// Returns a pointer to the last occurence of a valid path separator in +// Returns a pointer to the last occurrence of a valid path separator in // the FilePath. On Windows, for example, both '/' and '\' are valid path // separators. Returns NULL if no path separator was found. const char* FilePath::FindLastPathSeparator() const { @@ -8420,7 +9278,7 @@ bool FilePath::DirectoryExists() const { // root directory per disk drive.) bool FilePath::IsRootDirectory() const { #if GTEST_OS_WINDOWS - // TODO(wan@google.com): on Windows a network share like + // FIXME: on Windows a network share like // \\server\share can be a root directory, although it cannot be the // current directory. Handle this properly. return pathname_.length() == 3 && IsAbsolutePath(); @@ -8520,7 +9378,7 @@ FilePath FilePath::RemoveTrailingPathSeparator() const { // Removes any redundant separators that might be in the pathname. // For example, "bar///foo" becomes "bar/foo". Does not eliminate other // redundancies that might be in a pathname involving "." or "..". -// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +// FIXME: handle Windows network shares (e.g. \\server\share). void FilePath::Normalize() { if (pathname_.c_str() == NULL) { pathname_ = ""; @@ -8581,8 +9439,7 @@ void FilePath::Normalize() { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) + #include @@ -8617,14 +9474,11 @@ void FilePath::Normalize() { # include #endif // GTEST_OS_AIX +#if GTEST_OS_FUCHSIA +# include +# include +#endif // GTEST_OS_FUCHSIA -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick exists to -// prevent the accidental inclusion of gtest-internal-inl.h in the -// user's code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ namespace testing { namespace internal { @@ -8642,7 +9496,7 @@ const int kStdErrFileno = STDERR_FILENO; namespace { template -T ReadProcFileField(const string& filename, int field) { +T ReadProcFileField(const std::string& filename, int field) { std::string dummy; std::ifstream file(filename.c_str()); while (field-- > 0) { @@ -8656,7 +9510,7 @@ T ReadProcFileField(const string& filename, int field) { // Returns the number of active threads, or 0 when there is an error. size_t GetThreadCount() { - const string filename = + const std::string filename = (Message() << "/proc/" << getpid() << "/stat").GetString(); return ReadProcFileField(filename, 19); } @@ -8713,6 +9567,25 @@ size_t GetThreadCount() { } } +#elif GTEST_OS_FUCHSIA + +size_t GetThreadCount() { + int dummy_buffer; + size_t avail; + zx_status_t status = zx_object_get_info( + zx_process_self(), + ZX_INFO_PROCESS_THREADS, + &dummy_buffer, + 0, + nullptr, + &avail); + if (status == ZX_OK) { + return avail; + } else { + return 0; + } +} + #else size_t GetThreadCount() { @@ -8795,9 +9668,9 @@ Mutex::Mutex() Mutex::~Mutex() { // Static mutexes are leaked intentionally. It is not thread-safe to try // to clean them up. - // TODO(yukawa): Switch to Slim Reader/Writer (SRW) Locks, which requires + // FIXME: Switch to Slim Reader/Writer (SRW) Locks, which requires // nothing to clean it up but is available only on Vista and later. - // http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx + // https://docs.microsoft.com/en-us/windows/desktop/Sync/slim-reader-writer--srw--locks if (type_ == kDynamic) { ::DeleteCriticalSection(critical_section_); delete critical_section_; @@ -8828,6 +9701,43 @@ void Mutex::AssertHeld() { << "The current thread is not holding the mutex @" << this; } +namespace { + +// Use the RAII idiom to flag mem allocs that are intentionally never +// deallocated. The motivation is to silence the false positive mem leaks +// that are reported by the debug version of MS's CRT which can only detect +// if an alloc is missing a matching deallocation. +// Example: +// MemoryIsNotDeallocated memory_is_not_deallocated; +// critical_section_ = new CRITICAL_SECTION; +// +class MemoryIsNotDeallocated +{ + public: + MemoryIsNotDeallocated() : old_crtdbg_flag_(0) { +#ifdef _MSC_VER + old_crtdbg_flag_ = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + // Set heap allocation block type to _IGNORE_BLOCK so that MS debug CRT + // doesn't report mem leak if there's no matching deallocation. + _CrtSetDbgFlag(old_crtdbg_flag_ & ~_CRTDBG_ALLOC_MEM_DF); +#endif // _MSC_VER + } + + ~MemoryIsNotDeallocated() { +#ifdef _MSC_VER + // Restore the original _CRTDBG_ALLOC_MEM_DF flag + _CrtSetDbgFlag(old_crtdbg_flag_); +#endif // _MSC_VER + } + + private: + int old_crtdbg_flag_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MemoryIsNotDeallocated); +}; + +} // namespace + // Initializes owner_thread_id_ and critical_section_ in static mutexes. void Mutex::ThreadSafeLazyInit() { // Dynamic mutexes are initialized in the constructor. @@ -8838,7 +9748,11 @@ void Mutex::ThreadSafeLazyInit() { // If critical_section_init_phase_ was 0 before the exchange, we // are the first to test it and need to perform the initialization. owner_thread_id_ = 0; - critical_section_ = new CRITICAL_SECTION; + { + // Use RAII to flag that following mem alloc is never deallocated. + MemoryIsNotDeallocated memory_is_not_deallocated; + critical_section_ = new CRITICAL_SECTION; + } ::InitializeCriticalSection(critical_section_); // Updates the critical_section_init_phase_ to 2 to signal // initialization complete. @@ -8877,7 +9791,7 @@ class ThreadWithParamSupport : public ThreadWithParamBase { Notification* thread_can_start) { ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start); DWORD thread_id; - // TODO(yukawa): Consider to use _beginthreadex instead. + // FIXME: Consider to use _beginthreadex instead. HANDLE thread_handle = ::CreateThread( NULL, // Default security. 0, // Default stack size. @@ -9045,7 +9959,7 @@ class ThreadLocalRegistryImpl { FALSE, thread_id); GTEST_CHECK_(thread != NULL); - // We need to to pass a valid thread ID pointer into CreateThread for it + // We need to pass a valid thread ID pointer into CreateThread for it // to work correctly under Win98. DWORD watcher_thread_id; HANDLE watcher_thread = ::CreateThread( @@ -9080,7 +9994,8 @@ class ThreadLocalRegistryImpl { // Returns map of thread local instances. static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() { mutex_.AssertHeld(); - static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals; + MemoryIsNotDeallocated memory_is_not_deallocated; + static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals(); return map; } @@ -9220,7 +10135,7 @@ bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { } // Helper function used by ValidateRegex() to format error messages. -std::string FormatRegexSyntaxError(const char* regex, int index) { +static std::string FormatRegexSyntaxError(const char* regex, int index) { return (Message() << "Syntax error at index " << index << " in simple regular expression \"" << regex << "\": ").GetString(); } @@ -9229,7 +10144,7 @@ std::string FormatRegexSyntaxError(const char* regex, int index) { // otherwise returns true. bool ValidateRegex(const char* regex) { if (regex == NULL) { - // TODO(wan@google.com): fix the source file location in the + // FIXME: fix the source file location in the // assertion failures to match where the regex is used in user // code. ADD_FAILURE() << "NULL is not a valid simple regular expression."; @@ -9472,9 +10387,10 @@ GTestLog::~GTestLog() { posix::Abort(); } } + // Disable Microsoft deprecation warnings for POSIX functions called from // this class (creat, dup, dup2, and close) -GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) +GTEST_DISABLE_MSC_DEPRECATED_PUSH_() #if GTEST_HAS_STREAM_REDIRECTION @@ -9558,13 +10474,14 @@ class CapturedStream { GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); }; -GTEST_DISABLE_MSC_WARNINGS_POP_() +GTEST_DISABLE_MSC_DEPRECATED_POP_() static CapturedStream* g_captured_stderr = NULL; static CapturedStream* g_captured_stdout = NULL; // Starts capturing an output stream (stdout/stderr). -void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { +static void CaptureStream(int fd, const char* stream_name, + CapturedStream** stream) { if (*stream != NULL) { GTEST_LOG_(FATAL) << "Only one " << stream_name << " capturer can exist at a time."; @@ -9573,7 +10490,7 @@ void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { } // Stops capturing the output stream and returns the captured string. -std::string GetCapturedStream(CapturedStream** captured_stream) { +static std::string GetCapturedStream(CapturedStream** captured_stream) { const std::string content = (*captured_stream)->GetCapturedString(); delete *captured_stream; @@ -9604,23 +10521,9 @@ std::string GetCapturedStderr() { #endif // GTEST_HAS_STREAM_REDIRECTION -std::string TempDir() { -#if GTEST_OS_WINDOWS_MOBILE - return "\\temp\\"; -#elif GTEST_OS_WINDOWS - const char* temp_dir = posix::GetEnv("TEMP"); - if (temp_dir == NULL || temp_dir[0] == '\0') - return "\\temp\\"; - else if (temp_dir[strlen(temp_dir) - 1] == '\\') - return temp_dir; - else - return std::string(temp_dir) + "\\"; -#elif GTEST_OS_LINUX_ANDROID - return "/sdcard/"; -#else - return "/tmp/"; -#endif // GTEST_OS_WINDOWS_MOBILE -} + + + size_t GetFileSize(FILE* file) { fseek(file, 0, SEEK_END); @@ -9650,22 +10553,36 @@ std::string ReadEntireFile(FILE* file) { } #if GTEST_HAS_DEATH_TEST +static const std::vector* g_injected_test_argvs = NULL; // Owned. -static const ::std::vector* g_injected_test_argvs = - NULL; // Owned. - -void SetInjectableArgvs(const ::std::vector* argvs) { - if (g_injected_test_argvs != argvs) - delete g_injected_test_argvs; - g_injected_test_argvs = argvs; -} - -const ::std::vector& GetInjectableArgvs() { +std::vector GetInjectableArgvs() { if (g_injected_test_argvs != NULL) { return *g_injected_test_argvs; } return GetArgvs(); } + +void SetInjectableArgvs(const std::vector* new_argvs) { + if (g_injected_test_argvs != new_argvs) delete g_injected_test_argvs; + g_injected_test_argvs = new_argvs; +} + +void SetInjectableArgvs(const std::vector& new_argvs) { + SetInjectableArgvs( + new std::vector(new_argvs.begin(), new_argvs.end())); +} + +#if GTEST_HAS_GLOBAL_STRING +void SetInjectableArgvs(const std::vector< ::string>& new_argvs) { + SetInjectableArgvs( + new std::vector(new_argvs.begin(), new_argvs.end())); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void ClearInjectableArgvs() { + delete g_injected_test_argvs; + g_injected_test_argvs = NULL; +} #endif // GTEST_HAS_DEATH_TEST #if GTEST_OS_WINDOWS_MOBILE @@ -9740,11 +10657,12 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value) { bool BoolFromGTestEnv(const char* flag, bool default_value) { #if defined(GTEST_GET_BOOL_FROM_ENV_) return GTEST_GET_BOOL_FROM_ENV_(flag, default_value); -#endif // defined(GTEST_GET_BOOL_FROM_ENV_) +#else const std::string env_var = FlagToEnvVar(flag); const char* const string_value = posix::GetEnv(env_var.c_str()); return string_value == NULL ? default_value : strcmp(string_value, "0") != 0; +#endif // defined(GTEST_GET_BOOL_FROM_ENV_) } // Reads and returns a 32-bit integer stored in the environment @@ -9753,7 +10671,7 @@ bool BoolFromGTestEnv(const char* flag, bool default_value) { Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { #if defined(GTEST_GET_INT32_FROM_ENV_) return GTEST_GET_INT32_FROM_ENV_(flag, default_value); -#endif // defined(GTEST_GET_INT32_FROM_ENV_) +#else const std::string env_var = FlagToEnvVar(flag); const char* const string_value = posix::GetEnv(env_var.c_str()); if (string_value == NULL) { @@ -9771,37 +10689,36 @@ Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { } return result; +#endif // defined(GTEST_GET_INT32_FROM_ENV_) +} + +// As a special case for the 'output' flag, if GTEST_OUTPUT is not +// set, we look for XML_OUTPUT_FILE, which is set by the Bazel build +// system. The value of XML_OUTPUT_FILE is a filename without the +// "xml:" prefix of GTEST_OUTPUT. +// Note that this is meant to be called at the call site so it does +// not check that the flag is 'output' +// In essence this checks an env variable called XML_OUTPUT_FILE +// and if it is set we prepend "xml:" to its value, if it not set we return "" +std::string OutputFlagAlsoCheckEnvVar(){ + std::string default_value_for_output_flag = ""; + const char* xml_output_file_env = posix::GetEnv("XML_OUTPUT_FILE"); + if (NULL != xml_output_file_env) { + default_value_for_output_flag = std::string("xml:") + xml_output_file_env; + } + return default_value_for_output_flag; } // Reads and returns the string environment variable corresponding to // the given flag; if it's not set, returns default_value. -std::string StringFromGTestEnv(const char* flag, const char* default_value) { +const char* StringFromGTestEnv(const char* flag, const char* default_value) { #if defined(GTEST_GET_STRING_FROM_ENV_) return GTEST_GET_STRING_FROM_ENV_(flag, default_value); -#endif // defined(GTEST_GET_STRING_FROM_ENV_) +#else const std::string env_var = FlagToEnvVar(flag); - const char* value = posix::GetEnv(env_var.c_str()); - if (value != NULL) { - return value; - } - - // As a special case for the 'output' flag, if GTEST_OUTPUT is not - // set, we look for XML_OUTPUT_FILE, which is set by the Bazel build - // system. The value of XML_OUTPUT_FILE is a filename without the - // "xml:" prefix of GTEST_OUTPUT. - // - // The net priority order after flag processing is thus: - // --gtest_output command line flag - // GTEST_OUTPUT environment variable - // XML_OUTPUT_FILE environment variable - // 'default_value' - if (strcmp(flag, "output") == 0) { - value = posix::GetEnv("XML_OUTPUT_FILE"); - if (value != NULL) { - return std::string("xml:") + value; - } - } - return default_value; + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +#endif // defined(GTEST_GET_STRING_FROM_ENV_) } } // namespace internal @@ -9834,10 +10751,9 @@ std::string StringFromGTestEnv(const char* flag, const char* default_value) { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// Google Test - The Google C++ Testing Framework + +// Google Test - The Google C++ Testing and Mocking Framework // // This file implements a universal value printer that can print a // value of any type T: @@ -9850,8 +10766,8 @@ std::string StringFromGTestEnv(const char* flag, const char* default_value) { // or void PrintTo(const Foo&, ::std::ostream*) in the namespace that // defines Foo. -#include #include +#include #include #include // NOLINT #include @@ -9895,7 +10811,7 @@ void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, // If the object size is bigger than kThreshold, we'll have to omit // some details by printing only the first and the last kChunkSize // bytes. - // TODO(wan): let the user control the threshold using a flag. + // FIXME: let the user control the threshold using a flag. if (count < kThreshold) { PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); } else { @@ -9929,7 +10845,7 @@ namespace internal { // Depending on the value of a char (or wchar_t), we print it in one // of three formats: // - as is if it's a printable ASCII (e.g. 'a', '2', ' '), -// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a hexadecimal escape sequence (e.g. '\x7F'), or // - as a special escape sequence (e.g. '\r', '\n'). enum CharFormat { kAsIs, @@ -9986,7 +10902,10 @@ static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { *os << static_cast(c); return kAsIs; } else { - *os << "\\x" + String::FormatHexInt(static_cast(c)); + ostream::fmtflags flags = os->flags(); + *os << "\\x" << std::hex << std::uppercase + << static_cast(static_cast(c)); + os->flags(flags); return kHexEscape; } } @@ -10033,7 +10952,7 @@ void PrintCharAndCodeTo(Char c, ostream* os) { return; *os << " (" << static_cast(c); - // For more convenience, we print c's code again in hexidecimal, + // For more convenience, we print c's code again in hexadecimal, // unless c was already printed in the form '\x##' or the code is in // [1, 9]. if (format == kHexEscape || (1 <= c && c <= 9)) { @@ -10065,11 +10984,12 @@ template GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ -static void PrintCharsAsStringTo( +static CharFormat PrintCharsAsStringTo( const CharType* begin, size_t len, ostream* os) { const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; *os << kQuoteBegin; bool is_previous_hex = false; + CharFormat print_format = kAsIs; for (size_t index = 0; index < len; ++index) { const CharType cur = begin[index]; if (is_previous_hex && IsXDigit(cur)) { @@ -10079,8 +10999,13 @@ static void PrintCharsAsStringTo( *os << "\" " << kQuoteBegin; } is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + // Remember if any characters required hex escaping. + if (is_previous_hex) { + print_format = kHexEscape; + } } *os << "\""; + return print_format; } // Prints a (const) char/wchar_t array of 'len' elements, starting at address @@ -10150,15 +11075,90 @@ void PrintTo(const wchar_t* s, ostream* os) { } #endif // wchar_t is native +namespace { + +bool ContainsUnprintableControlCodes(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast(str); + + for (size_t i = 0; i < length; i++) { + unsigned char ch = *s++; + if (std::iscntrl(ch)) { + switch (ch) { + case '\t': + case '\n': + case '\r': + break; + default: + return true; + } + } + } + return false; +} + +bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t<= 0xbf; } + +bool IsValidUTF8(const char* str, size_t length) { + const unsigned char *s = reinterpret_cast(str); + + for (size_t i = 0; i < length;) { + unsigned char lead = s[i++]; + + if (lead <= 0x7f) { + continue; // single-byte character (ASCII) 0..7F + } + if (lead < 0xc2) { + return false; // trail byte or non-shortest form + } else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) { + ++i; // 2-byte character + } else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + // check for non-shortest form and surrogate + (lead != 0xe0 || s[i] >= 0xa0) && + (lead != 0xed || s[i] < 0xa0)) { + i += 2; // 3-byte character + } else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length && + IsUTF8TrailByte(s[i]) && + IsUTF8TrailByte(s[i + 1]) && + IsUTF8TrailByte(s[i + 2]) && + // check for non-shortest form + (lead != 0xf0 || s[i] >= 0x90) && + (lead != 0xf4 || s[i] < 0x90)) { + i += 3; // 4-byte character + } else { + return false; + } + } + return true; +} + +void ConditionalPrintAsText(const char* str, size_t length, ostream* os) { + if (!ContainsUnprintableControlCodes(str, length) && + IsValidUTF8(str, length)) { + *os << "\n As Text: \"" << str << "\""; + } +} + +} // anonymous namespace + // Prints a ::string object. #if GTEST_HAS_GLOBAL_STRING void PrintStringTo(const ::string& s, ostream* os) { - PrintCharsAsStringTo(s.data(), s.size(), os); + if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) { + if (GTEST_FLAG(print_utf8)) { + ConditionalPrintAsText(s.data(), s.size(), os); + } + } } #endif // GTEST_HAS_GLOBAL_STRING void PrintStringTo(const ::std::string& s, ostream* os) { - PrintCharsAsStringTo(s.data(), s.size(), os); + if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) { + if (GTEST_FLAG(print_utf8)) { + ConditionalPrintAsText(s.data(), s.size(), os); + } + } } // Prints a ::wstring object. @@ -10205,19 +11205,10 @@ void PrintWideStringTo(const ::std::wstring& s, ostream* os) { // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: mheule@google.com (Markus Heule) -// -// The Google C++ Testing Framework (Google Test) +// +// The Google C++ Testing and Mocking Framework (Google Test) -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick exists to -// prevent the accidental inclusion of gtest-internal-inl.h in the -// user's code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ namespace testing { @@ -10313,8 +11304,8 @@ void HasNewFatalFailureHelper::ReportTestPartResult( // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) + + namespace testing { From 3dddf703a963de4b76796770a39dedc3485d312d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 2 Sep 2018 12:40:49 +0000 Subject: [PATCH 31/47] ts: re-apply OpenCV-specific patch on googletest 1.8.1 --- modules/ts/include/opencv2/ts/ts_gtest.h | 170 +++++++++++++++++++---- modules/ts/src/ts_gtest.cpp | 52 ++++++- 2 files changed, 192 insertions(+), 30 deletions(-) diff --git a/modules/ts/include/opencv2/ts/ts_gtest.h b/modules/ts/include/opencv2/ts/ts_gtest.h index ebb16db7b0..9771c51d7f 100644 --- a/modules/ts/include/opencv2/ts/ts_gtest.h +++ b/modules/ts/include/opencv2/ts/ts_gtest.h @@ -422,64 +422,119 @@ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ // Determines the platform on which Google Test is compiled. +#define GTEST_OS_CYGWIN 0 +#define GTEST_OS_SYMBIAN 0 +#define GTEST_OS_WINDOWS 0 +#define GTEST_OS_WINDOWS_MOBILE 0 +#define GTEST_OS_WINDOWS_MINGW 0 +#define GTEST_OS_WINDOWS_DESKTOP 0 +#define GTEST_OS_WINDOWS_PHONE 0 +#define GTEST_OS_WINDOWS_TV_TITLE 0 +#define GTEST_OS_WINDOWS_RT 0 +#define GTEST_OS_MAC 0 +#define GTEST_OS_LINUX 0 +#define GTEST_OS_LINUX_ANDROID 0 +#define GTEST_OS_ZOS 0 +#define GTEST_OS_SOLARIS 0 +#define GTEST_OS_AIX 0 +#define GTEST_OS_HPUX 0 +#define GTEST_OS_NACL 0 +#define GTEST_OS_OPENBSD 0 +#define GTEST_OS_QNX 0 +#define GTEST_OS_IOS 0 +#define GTEST_OS_IOS_SIMULATOR 0 +#define GTEST_OS_FREEBSD 0 +#define GTEST_OS_NETBSD 0 +#define GTEST_OS_FUCHSIA 0 + #ifdef __CYGWIN__ +# undef GTEST_OS_CYGWIN # define GTEST_OS_CYGWIN 1 #elif defined __SYMBIAN32__ +# undef GTEST_OS_SYMBIAN # define GTEST_OS_SYMBIAN 1 #elif defined _WIN32 +# undef GTEST_OS_WINDOWS # define GTEST_OS_WINDOWS 1 # ifdef _WIN32_WCE +# undef GTEST_OS_WINDOWS_MOBILE # define GTEST_OS_WINDOWS_MOBILE 1 # elif defined(__MINGW__) || defined(__MINGW32__) +# undef GTEST_OS_WINDOWS_MINGW # define GTEST_OS_WINDOWS_MINGW 1 # elif defined(WINAPI_FAMILY) # include # if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# undef GTEST_OS_WINDOWS_DESKTOP # define GTEST_OS_WINDOWS_DESKTOP 1 # elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +# undef GTEST_OS_WINDOWS_PHONE # define GTEST_OS_WINDOWS_PHONE 1 # elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +# undef GTEST_OS_WINDOWS_RT # define GTEST_OS_WINDOWS_RT 1 # elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE) +# undef GTEST_OS_WINDOWS_PHONE +# undef GTEST_OS_WINDOWS_TV_TITLE # define GTEST_OS_WINDOWS_PHONE 1 # define GTEST_OS_WINDOWS_TV_TITLE 1 # else // WINAPI_FAMILY defined but no known partition matched. // Default to desktop. +# undef GTEST_OS_WINDOWS_DESKTOP # define GTEST_OS_WINDOWS_DESKTOP 1 # endif # else +# undef GTEST_OS_WINDOWS_DESKTOP # define GTEST_OS_WINDOWS_DESKTOP 1 # endif // _WIN32_WCE #elif defined __APPLE__ +# undef GTEST_OS_MAC # define GTEST_OS_MAC 1 # if TARGET_OS_IPHONE +# undef GTEST_OS_IOS # define GTEST_OS_IOS 1 +# if TARGET_IPHONE_SIMULATOR +# undef GTEST_OS_IOS_SIMULATOR +# define GTEST_OS_IOS_SIMULATOR 1 +# endif # endif #elif defined __FreeBSD__ +# undef GTEST_OS_FREEBSD # define GTEST_OS_FREEBSD 1 #elif defined __Fuchsia__ +# undef GTEST_OS_FUCHSIA # define GTEST_OS_FUCHSIA 1 #elif defined __linux__ +# undef GTEST_OS_LINUX # define GTEST_OS_LINUX 1 # if defined __ANDROID__ +# undef GTEST_OS_LINUX_ANDROID # define GTEST_OS_LINUX_ANDROID 1 # endif #elif defined __MVS__ +# undef GTEST_OS_ZOS # define GTEST_OS_ZOS 1 #elif defined(__sun) && defined(__SVR4) +# undef GTEST_OS_SOLARIS # define GTEST_OS_SOLARIS 1 #elif defined(_AIX) +# undef GTEST_OS_AIX # define GTEST_OS_AIX 1 #elif defined(__hpux) +# undef GTEST_OS_HPUX # define GTEST_OS_HPUX 1 #elif defined __native_client__ +# undef GTEST_OS_NACL # define GTEST_OS_NACL 1 #elif defined __NetBSD__ +# undef GTEST_OS_NETBSD # define GTEST_OS_NETBSD 1 #elif defined __OpenBSD__ +# undef GTEST_OS_OPENBSD # define GTEST_OS_OPENBSD 1 #elif defined __QNX__ +# undef GTEST_OS_QNX # define GTEST_OS_QNX 1 #endif // __CYGWIN__ @@ -522,6 +577,13 @@ #endif // GTEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_ +#ifndef GTEST_HAS_NOTIFICATION_ +#define GTEST_HAS_NOTIFICATION_ 0 +#endif +#ifndef GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ +#define GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ 0 +#endif + #if !defined(GTEST_DEV_EMAIL_) # define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" # define GTEST_FLAG_PREFIX_ "gtest_" @@ -547,7 +609,7 @@ // GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) // /* code that triggers warnings C4800 and C4385 */ // GTEST_DISABLE_MSC_WARNINGS_POP_() -#if _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 # define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ __pragma(warning(push)) \ __pragma(warning(disable: warnings)) @@ -580,7 +642,7 @@ // -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a // value for __cplusplus, and recent versions of clang, gcc, and // probably other compilers set that too in C++11 mode. -# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L || _MSC_VER >= 1900 +# if defined __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) // Compiling in at least C++11 mode. # define GTEST_LANG_CXX11 1 # else @@ -606,6 +668,8 @@ __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \ __GLIBCXX__ != 20120702ul)) /* GCC 4.5.4 */ # define GTEST_STDLIB_CXX11 1 +#else +# define GTEST_STDLIB_CXX11 0 #endif // Only use C++11 library features if the library provides them. @@ -622,6 +686,18 @@ # define GTEST_HAS_STD_SHARED_PTR_ 1 # define GTEST_HAS_UNORDERED_MAP_ 1 # define GTEST_HAS_UNORDERED_SET_ 1 +#else +# define GTEST_HAS_STD_BEGIN_AND_END_ 0 +# define GTEST_HAS_STD_FORWARD_LIST_ 0 +# define GTEST_HAS_STD_FUNCTION_ 0 +# define GTEST_HAS_STD_INITIALIZER_LIST_ 0 +# define GTEST_HAS_STD_MOVE_ 0 +# define GTEST_HAS_STD_SHARED_PTR_ 0 +# define GTEST_HAS_STD_TYPE_TRAITS_ 0 +# define GTEST_HAS_STD_UNIQUE_PTR_ 0 +# define GTEST_HAS_STD_SHARED_PTR_ 0 +# define GTEST_HAS_UNORDERED_MAP_ 0 +# define GTEST_HAS_UNORDERED_SET_ 0 #endif // C++11 specifies that provides std::tuple. @@ -647,6 +723,8 @@ # undef GTEST_HAS_STD_TUPLE_ # endif # endif +#else +# define GTEST_HAS_STD_TUPLE_ 0 #endif // Brings in definitions for functions used in the testing::internal::posix @@ -691,6 +769,10 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; # endif #endif +#ifndef GTEST_USES_PCRE +#define GTEST_USES_PCRE 0 +#endif + #if GTEST_USES_PCRE // The appropriate headers have already been included. @@ -703,18 +785,21 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; # include // NOLINT # define GTEST_USES_POSIX_RE 1 +# define GTEST_USES_SIMPLE_RE 0 #elif GTEST_OS_WINDOWS // is not available on Windows. Use our own simple regex // implementation instead. # define GTEST_USES_SIMPLE_RE 1 +# define GTEST_USES_POSIX_RE 0 #else // may not be available on this platform. Use our own // simple regex implementation instead. # define GTEST_USES_SIMPLE_RE 1 +# define GTEST_USES_POSIX_RE 0 #endif // GTEST_USES_PCRE @@ -907,7 +992,7 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; # define GTEST_HAS_TR1_TUPLE 0 # else // The user didn't tell us not to do it, so we assume it's OK. -# define GTEST_HAS_TR1_TUPLE 1 +# define GTEST_HAS_TR1_TUPLE 1 # endif #endif // GTEST_HAS_TR1_TUPLE @@ -932,8 +1017,10 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; // and it can be used with some compilers that define __GNUC__. # if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) \ - || (_MSC_VER >= 1600 && _MSC_VER < 1900) + || (defined(_MSC_VER) && _MSC_VER >= 1600 && _MSC_VER < 1900) # define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# else +# define GTEST_ENV_HAS_TR1_TUPLE_ 0 # endif // C++11 specifies that provides std::tuple. Use that if gtest is used @@ -941,12 +1028,16 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION; // can build with clang but need to use gcc4.2's libstdc++). # if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) # define GTEST_ENV_HAS_STD_TUPLE_ 1 +# else +# define GTEST_ENV_HAS_STD_TUPLE_ 0 # endif # if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ # define GTEST_USE_OWN_TR1_TUPLE 0 # else # define GTEST_USE_OWN_TR1_TUPLE 1 +# undef GTEST_HAS_TR1_TUPLE +# define GTEST_HAS_TR1_TUPLE 1 # endif # endif // GTEST_OS_SYMBIAN #endif // GTEST_USE_OWN_TR1_TUPLE @@ -2022,7 +2113,7 @@ inline bool operator!=(const GTEST_10_TUPLE_(T)& t, # endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 // VS 2010 now has tr1 support. -# elif _MSC_VER >= 1600 +# elif defined(_MSC_VER) && _MSC_VER >= 1600 # include // IWYU pragma: export // NOLINT # else // GTEST_USE_OWN_TR1_TUPLE @@ -2083,6 +2174,9 @@ inline bool operator!=(const GTEST_10_TUPLE_(T)& t, GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD || \ GTEST_OS_NETBSD || GTEST_OS_FUCHSIA) # define GTEST_HAS_DEATH_TEST 1 +# include // NOLINT +#else +# define GTEST_HAS_DEATH_TEST 0 #endif // Determines whether to support type-driven tests. @@ -2110,6 +2204,8 @@ inline bool operator!=(const GTEST_10_TUPLE_(T)& t, // Determines whether test results can be streamed to a socket. #if GTEST_OS_LINUX # define GTEST_CAN_STREAM_RESULTS_ 1 +#else +# define GTEST_CAN_STREAM_RESULTS_ 0 #endif // Defines some utility macros. @@ -2440,6 +2536,9 @@ typedef ::std::wstring wstring; // returns 'condition'. GTEST_API_ bool IsTrue(bool condition); +template class scoped_ptr; +template static void swap(scoped_ptr& a, scoped_ptr& b); + // Defines scoped_ptr. // This implementation of scoped_ptr is PARTIAL - it only contains @@ -2471,10 +2570,7 @@ class scoped_ptr { } } - friend void swap(scoped_ptr& a, scoped_ptr& b) { - using std::swap; - swap(a.ptr_, b.ptr_); - } + friend void swap(scoped_ptr& a, scoped_ptr& b); private: T* ptr_; @@ -2482,6 +2578,12 @@ class scoped_ptr { GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); }; +template +static void swap(scoped_ptr& a, scoped_ptr& b) { + using std::swap; + swap(a.ptr_, b.ptr_); +} + // Defines RE. #if GTEST_USES_PCRE @@ -2621,7 +2723,7 @@ inline void FlushInfoLog() { fflush(NULL); } // // GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition // is not satisfied. -// Synopsys: +// Synopsis: // GTEST_CHECK_(boolean_condition); // or // GTEST_CHECK_(boolean_condition) << "Additional message"; @@ -2711,7 +2813,7 @@ struct RvalueRef { // const Foo*). When you use ImplicitCast_, the compiler checks that // the cast is safe. Such explicit ImplicitCast_s are necessary in // surprisingly many situations where C++ demands an exact type match -// instead of an argument type convertable to a target type. +// instead of an argument type convertible to a target type. // // The syntax for using ImplicitCast_ is the same as for static_cast: // @@ -2779,7 +2881,7 @@ Derived* CheckedDowncastToActualType(Base* base) { GTEST_CHECK_(typeid(*base) == typeid(Derived)); #endif -#if GTEST_HAS_DOWNCAST_ +#if defined(GTEST_HAS_DOWNCAST_) && GTEST_HAS_DOWNCAST_ return ::down_cast(base); #elif GTEST_HAS_RTTI return dynamic_cast(base); // NOLINT @@ -3816,7 +3918,7 @@ inline void Abort() { abort(); } // MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate // function in order to achieve that. We use macro definition here because // snprintf is a variadic function. -#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE // MSVC 2005 and above support variadic macros. # define GTEST_SNPRINTF_(buffer, size, format, ...) \ _snprintf_s(buffer, size, size, format, __VA_ARGS__) @@ -4590,7 +4692,7 @@ class GTEST_API_ FilePath { void Normalize(); - // Returns a pointer to the last occurence of a valid path separator in + // Returns a pointer to the last occurrence of a valid path separator in // the FilePath. On Windows, for example, both '/' and '\' are valid path // separators. Returns NULL if no path separator was found. const char* FindLastPathSeparator() const; @@ -9162,7 +9264,7 @@ class NativeArray { // Implements Boolean test assertions such as EXPECT_TRUE. expression can be // either a boolean expression or an AssertionResult. text is a textual -// represenation of expression as it was passed into the EXPECT_TRUE. +// representation of expression as it was passed into the EXPECT_TRUE. #define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ if (const ::testing::AssertionResult gtest_ar_ = \ @@ -10420,6 +10522,10 @@ linked_ptr make_linked_ptr(T* ptr) { # include #endif +#ifndef GTEST_HAS_ABSL +#define GTEST_HAS_ABSL 0 +#endif + #if GTEST_HAS_ABSL #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -10494,7 +10600,7 @@ class TypeWithoutFormatter { // T is not an enum, printing it as an integer is the best we can do // given that it has no user-defined printer. static void PrintValue(const T& value, ::std::ostream* os) { - const internal::BiggestInt kBigInt = value; + const internal::BiggestInt kBigInt = static_cast(value); *os << kBigInt; } }; @@ -11804,12 +11910,15 @@ typename ParamNameGenFunc::Type *GetParamNameGen() { return DefaultParamName; } +} // namespace internal:: // fixes MacOS X issue with "friend class internal/*::anon*/::ParameterizedTestFactory;" +namespace { // wrap into anynomous namespace to avoid build warnings like GCC's -Wsubobject-linkage + // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // Stores a parameter value and later creates tests parameterized with that // value. template -class ParameterizedTestFactory : public TestFactoryBase { +class ParameterizedTestFactory : public internal::TestFactoryBase { public: typedef typename TestClass::ParamType ParamType; explicit ParameterizedTestFactory(ParamType parameter) : @@ -11824,6 +11933,8 @@ class ParameterizedTestFactory : public TestFactoryBase { GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); }; +} // namespace +namespace internal { // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // @@ -19526,6 +19637,9 @@ GTEST_DECLARE_string_(filter); // debugging information when fatal signals are raised. GTEST_DECLARE_bool_(install_failure_signal_handler); +// OpenCV extension: same as filter, but for the parameters string. +GTEST_DECLARE_string_(param_filter); + // This flag causes the Google Test to list tests. None of the tests listed // are actually run if the flag is provided. GTEST_DECLARE_bool_(list_tests); @@ -21520,6 +21634,11 @@ class GTEST_API_ AssertHelper { } // namespace internal +namespace internal { +// Static value used for accessing test parameter during a test lifetime. +extern void* g_parameter_; +} // namespace internal + // The pure interface class that all value-parameterized tests inherit from. // A value-parameterized class must inherit from both ::testing::Test and // ::testing::WithParamInterface. In most cases that just means inheriting @@ -21566,29 +21685,28 @@ class WithParamInterface { // like writing 'WithParamInterface::GetParam()' for a test that // uses a fixture whose parameter type is int. const ParamType& GetParam() const { - GTEST_CHECK_(parameter_ != NULL) + GTEST_CHECK_(GetParameterPtrRef_() != NULL) << "GetParam() can only be called inside a value-parameterized test " << "-- did you intend to write TEST_P instead of TEST_F?"; - return *parameter_; + return *GetParameterPtrRef_(); } private: // Sets parameter value. The caller is responsible for making sure the value // remains alive and unchanged throughout the current test. static void SetParam(const ParamType* parameter) { - parameter_ = parameter; + GetParameterPtrRef_() = parameter; } - // Static value used for accessing parameter during a test lifetime. - static const ParamType* parameter_; + static const ParamType*& GetParameterPtrRef_() + { + return (const ParamType*&)internal::g_parameter_; + } // TestClass must be a subclass of WithParamInterface and Test. - template friend class internal::ParameterizedTestFactory; + template friend class /*internal::*/ParameterizedTestFactory; }; -template -const T* WithParamInterface::parameter_ = NULL; - // Most value-parameterized classes can ignore the existence of // WithParamInterface, and can just inherit from ::testing::TestWithParam. diff --git a/modules/ts/src/ts_gtest.cpp b/modules/ts/src/ts_gtest.cpp index 9f2b3d5653..3bcd09893e 100644 --- a/modules/ts/src/ts_gtest.cpp +++ b/modules/ts/src/ts_gtest.cpp @@ -35,7 +35,15 @@ // This line ensures that gtest.h can be compiled on its own, even // when it's fused. -#include "gtest/gtest.h" +#include "precomp.hpp" + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# if __GNUC__ >= 5 +# pragma GCC diagnostic ignored "-Wsuggest-override" +# endif +#endif // The following lines pull in the real gtest *.cc files. // Copyright 2005, Google Inc. @@ -487,6 +495,7 @@ const char kBreakOnFailureFlag[] = "break_on_failure"; const char kCatchExceptionsFlag[] = "catch_exceptions"; const char kColorFlag[] = "color"; const char kFilterFlag[] = "filter"; +const char kParamFilterFlag[] = "param_filter"; const char kListTestsFlag[] = "list_tests"; const char kOutputFlag[] = "output"; const char kPrintTimeFlag[] = "print_time"; @@ -567,6 +576,7 @@ class GTestFlagSaver { death_test_style_ = GTEST_FLAG(death_test_style); death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); filter_ = GTEST_FLAG(filter); + param_filter_ = GTEST_FLAG(param_filter); internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); list_tests_ = GTEST_FLAG(list_tests); output_ = GTEST_FLAG(output); @@ -589,6 +599,7 @@ class GTestFlagSaver { GTEST_FLAG(death_test_style) = death_test_style_; GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; GTEST_FLAG(filter) = filter_; + GTEST_FLAG(param_filter) = param_filter_; GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; GTEST_FLAG(list_tests) = list_tests_; GTEST_FLAG(output) = output_; @@ -611,6 +622,7 @@ class GTestFlagSaver { std::string death_test_style_; bool death_test_use_fork_; std::string filter_; + std::string param_filter_; std::string internal_run_death_test_; bool list_tests_; std::string output_; @@ -1718,6 +1730,12 @@ GTEST_DEFINE_bool_( "install a signal handler that dumps debugging information when fatal " "signals are raised."); +GTEST_DEFINE_string_( + param_filter, + internal::StringFromGTestEnv("param_filter", GetDefaultFilter()), + "Same syntax and semantics as for param, but these patterns " + "have to match the test's parameters."); + GTEST_DEFINE_bool_(list_tests, false, "List all tests without running them."); @@ -4592,6 +4610,14 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart( "Note: %s filter = %s\n", GTEST_NAME_, filter); } + const char* const param_filter = GTEST_FLAG(param_filter).c_str(); + + // Ditto. + if (!String::CStringEquals(param_filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s parameter filter = %s\n", GTEST_NAME_, param_filter); + } + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); ColoredPrintf(COLOR_YELLOW, @@ -4636,6 +4662,7 @@ void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { ColoredPrintf(COLOR_GREEN, "[ RUN ] "); PrintTestName(test_info.test_case_name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); printf("\n"); fflush(stdout); } @@ -5961,13 +5988,13 @@ UnitTest* UnitTest::GetInstance() { // default implementation. Use this implementation to keep good OO // design with private destructor. -#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +#if (defined(_MSC_VER) && _MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) static UnitTest* const instance = new UnitTest; return instance; #else static UnitTest instance; return &instance; -#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +#endif // (defined(_MSC_VER) && _MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) } // Gets the number of successful test cases. @@ -6813,9 +6840,15 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { kDisableTestFilter); test_info->is_disabled_ = is_disabled; + const std::string value_param(test_info->value_param() == NULL ? + "" : test_info->value_param()); + const bool matches_filter = internal::UnitTestOptions::FilterMatchesTest(test_case_name, - test_name); + test_name) && + internal::UnitTestOptions::MatchesFilter(value_param, + GTEST_FLAG(param_filter).c_str()); + test_info->matches_filter_ = matches_filter; const bool is_runnable = @@ -7189,6 +7222,12 @@ static const char kColorEncodedHelpMessage[] = " Run only the tests whose name matches one of the positive patterns but\n" " none of the negative patterns. '?' matches any single character; '*'\n" " matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "param_filter=@YPOSITIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Like @G--" GTEST_FLAG_PREFIX_ + "filter@D, but applies to the test's parameter. If a\n" +" test is not parameterized, its parameter is considered to be the\n" +" empty string.\n" " @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" " Run all disabled tests too.\n" "\n" @@ -7254,6 +7293,7 @@ static bool ParseGoogleTestFlag(const char* const arg) { ParseBoolFlag(arg, kDeathTestUseFork, >EST_FLAG(death_test_use_fork)) || ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kParamFilterFlag, >EST_FLAG(param_filter)) || ParseStringFlag(arg, kInternalRunDeathTestFlag, >EST_FLAG(internal_run_death_test)) || ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || @@ -9096,6 +9136,7 @@ namespace internal { // of them. const char kPathSeparator = '\\'; const char kAlternatePathSeparator = '/'; +//const char kPathSeparatorString[] = "\\"; const char kAlternatePathSeparatorString[] = "/"; # if GTEST_OS_WINDOWS_MOBILE // Windows CE doesn't have a current directory. You should not use @@ -9109,6 +9150,7 @@ const char kCurrentDirectoryString[] = ".\\"; # endif // GTEST_OS_WINDOWS_MOBILE #else const char kPathSeparator = '/'; +//const char kPathSeparatorString[] = "/"; const char kCurrentDirectoryString[] = "./"; #endif // GTEST_OS_WINDOWS @@ -11390,5 +11432,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames( #endif // GTEST_HAS_TYPED_TEST_P +void* g_parameter_ = NULL; + } // namespace internal } // namespace testing From af883e88e16fa7a898ec5d253c3a78659a26555e Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 31 Aug 2018 17:28:27 +0300 Subject: [PATCH 32/47] ts: support QEMU launcher --- modules/ts/misc/run.py | 3 +++ modules/ts/misc/run_suite.py | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/ts/misc/run.py b/modules/ts/misc/run.py index 74b353a532..29c0aa6b90 100755 --- a/modules/ts/misc/run.py +++ b/modules/ts/misc/run.py @@ -40,6 +40,9 @@ if __name__ == "__main__": parser.add_argument("--valgrind_supp", metavar="FILE", action='append', help="Path to valgrind suppression file (example: --valgrind_supp opencv/platforms/scripts/valgrind.supp)") parser.add_argument("--valgrind_opt", metavar="OPT", action="append", default=[], help="Add command line option to valgrind (example: --valgrind_opt=--leak-check=full)") + # QEMU + parser.add_argument("--qemu", default="", help="Specify qemu binary and base parameters") + # Android parser.add_argument("--android", action="store_true", default=False, help="Android: force all tests to run on device") parser.add_argument("--android_sdk", metavar="PATH", help="Android: path to SDK to use adb and aapt tools") diff --git a/modules/ts/misc/run_suite.py b/modules/ts/misc/run_suite.py index 947db1e2b5..c7103a6263 100644 --- a/modules/ts/misc/run_suite.py +++ b/modules/ts/misc/run_suite.py @@ -77,7 +77,7 @@ class TestSuite(object): return False return os.access(fullpath, os.X_OK) - def wrapInValgrind(self, cmd=[]): + def wrapCommand(self, cmd, env): if self.options.valgrind: res = ['valgrind'] supp = self.options.valgrind_supp or [] @@ -89,6 +89,14 @@ class TestSuite(object): res.extend(self.options.valgrind_opt) has_gtest_filter = next((True for x in cmd if x.startswith('--gtest_filter=')), False) return res + cmd + ([longTestFilter(LONG_TESTS_DEBUG_VALGRIND)] if not has_gtest_filter else []) + elif self.options.qemu: + import shlex + res = shlex.split(self.options.qemu) + for (name, value) in [entry for entry in os.environ.items() if entry[0].startswith('OPENCV') and not entry[0] in env]: + res += ['-E', '"{}={}"'.format(name, value)] + for (name, value) in env.items(): + res += ['-E', '"{}={}"'.format(name, value)] + return res + ['--'] + cmd return cmd def tryCommand(self, cmd, workingDir): @@ -125,7 +133,6 @@ class TestSuite(object): else: if isColorEnabled(args): args.append("--gtest_color=yes") - cmd = self.wrapInValgrind([exe] + args) env = {} if not self.options.valgrind and self.options.trace: env['OPENCV_TRACE'] = '1' @@ -133,6 +140,7 @@ class TestSuite(object): env['OPENCV_TRACE_SYNC_OPENCL'] = '1' tempDir = TempEnvDir('OPENCV_TEMP_PATH', "__opencv_temp.") tempDir.init() + cmd = self.wrapCommand([exe] + args, env) log.warning("Run: %s" % " ".join(cmd)) ret = execute(cmd, cwd=workingDir, env=env) try: From c85e44697e29132c633493ab87d760a50baa791f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 2 Sep 2018 21:48:10 +0000 Subject: [PATCH 33/47] cmake: drop unconditional forcing of CMAKE_SKIP_RPATH=TRUE CMake "cache" entry for CMAKE_SKIP_RPATH is in the end of this file --- platforms/linux/gnu.toolchain.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/platforms/linux/gnu.toolchain.cmake b/platforms/linux/gnu.toolchain.cmake index fc53dbf9f1..cba08e7fbb 100644 --- a/platforms/linux/gnu.toolchain.cmake +++ b/platforms/linux/gnu.toolchain.cmake @@ -56,8 +56,6 @@ else() endmacro() endif() # IN_TRY_COMPILE -set(CMAKE_SKIP_RPATH TRUE) - if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) endif() From a11d944f51db5844657cf9bda8c2949dec26aabd Mon Sep 17 00:00:00 2001 From: Wu Zhiwen Date: Mon, 3 Sep 2018 08:51:26 +0800 Subject: [PATCH 34/47] dnn: Remove a duplicated code snippet for flatten layer Signed-off-by: Wu Zhiwen --- modules/dnn/src/layers/flatten_layer.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/dnn/src/layers/flatten_layer.cpp b/modules/dnn/src/layers/flatten_layer.cpp index 2fd9242ed3..41ec8dfc53 100644 --- a/modules/dnn/src/layers/flatten_layer.cpp +++ b/modules/dnn/src/layers/flatten_layer.cpp @@ -83,12 +83,6 @@ public: int startAxis = clamp(_startAxis, numAxes); int endAxis = clamp(_endAxis, numAxes); - for (size_t i = 1; i < inputs.size(); i++) - { - CV_Assert(inputs[i] == inputs[0]); - } - - CV_Assert(startAxis >= 0); CV_Assert(endAxis >= startAxis && endAxis < (int)numAxes); From 8b0e1c7fcb6814acbcddb6f9effb4a1799952488 Mon Sep 17 00:00:00 2001 From: Peter Jozsa Date: Mon, 3 Sep 2018 14:56:38 +0200 Subject: [PATCH 35/47] Fix https://github.com/opencv/opencv/issues/12301 --- modules/videoio/src/cap_msmf.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/videoio/src/cap_msmf.cpp b/modules/videoio/src/cap_msmf.cpp index 863f46bc89..a540238d23 100644 --- a/modules/videoio/src/cap_msmf.cpp +++ b/modules/videoio/src/cap_msmf.cpp @@ -616,7 +616,7 @@ class SourceReaderCB : public IMFSourceReaderCallback { public: SourceReaderCB() : - m_nRefCount(1), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_dwStreamIndex(0) + m_nRefCount(0), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_reader(NULL), m_dwStreamIndex(0) { } @@ -677,7 +677,7 @@ public: BOOL m_bEOS; HRESULT m_hrStatus; - _ComPtr m_reader; + IMFSourceReader *m_reader; DWORD m_dwStreamIndex; _ComPtr m_lastSample; }; @@ -1140,7 +1140,7 @@ bool CvCapture_MSMF::grabFrame() if (!reader->m_reader) { // Initiate capturing with async callback - reader->m_reader = videoFileSource; + reader->m_reader = videoFileSource.Get(); reader->m_dwStreamIndex = dwStreamIndex; if (FAILED(hr = videoFileSource->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL))) { From c7cf8fb35c474b9645e4a30bf33ba4ace111cd63 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Mon, 3 Sep 2018 17:08:40 +0300 Subject: [PATCH 36/47] Import SSDs from TensorFlow by training config (#12188) * Remove TensorFlow and protobuf dependencies from object detection scripts * Create text graphs for TensorFlow object detection networks from sample --- modules/dnn/include/opencv2/dnn/dnn.hpp | 8 + .../src/tensorflow/tf_graph_simplifier.cpp | 102 ++++ .../src/tensorflow/tf_graph_simplifier.hpp | 2 + modules/dnn/src/tensorflow/tf_importer.cpp | 29 ++ modules/dnn/test/test_tf_importer.cpp | 26 +- samples/dnn/object_detection.py | 28 +- samples/dnn/tf_text_graph_common.py | 210 +++++++- samples/dnn/tf_text_graph_faster_rcnn.py | 434 ++++++++-------- samples/dnn/tf_text_graph_mask_rcnn.py | 105 ++-- samples/dnn/tf_text_graph_ssd.py | 488 +++++++++--------- 10 files changed, 920 insertions(+), 512 deletions(-) diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index e99f8448fd..f1eed00852 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -885,6 +885,14 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN CV_EXPORTS_W void shrinkCaffeModel(const String& src, const String& dst, const std::vector& layersTypes = std::vector()); + /** @brief Create a text representation for a binary network stored in protocol buffer format. + * @param[in] model A path to binary network. + * @param[in] output A path to output text file to be created. + * + * @note To reduce output file size, trained weights are not included. + */ + CV_EXPORTS_W void writeTextGraph(const String& model, const String& output); + /** @brief Performs non maximum suppression given boxes and corresponding scores. * @param bboxes a set of bounding boxes to apply NMS. diff --git a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp index a766d2a024..2a34c2571d 100644 --- a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp +++ b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp @@ -782,6 +782,108 @@ void releaseTensor(tensorflow::TensorProto* tensor) } } +static void permute(google::protobuf::RepeatedPtrField* data, + const std::vector& indices) +{ + const int num = data->size(); + CV_Assert(num == indices.size()); + + std::vector elemIdToPos(num); + std::vector posToElemId(num); + for (int i = 0; i < num; ++i) + { + elemIdToPos[i] = i; + posToElemId[i] = i; + } + for (int i = 0; i < num; ++i) + { + int elemId = indices[i]; + int pos = elemIdToPos[elemId]; + if (pos != i) + { + data->SwapElements(i, pos); + const int swappedElemId = posToElemId[i]; + elemIdToPos[elemId] = i; + elemIdToPos[swappedElemId] = pos; + + posToElemId[i] = elemId; + posToElemId[pos] = swappedElemId; + } + } +} + +// Is based on tensorflow::graph_transforms::SortByExecutionOrder +void sortByExecutionOrder(tensorflow::GraphDef& net) +{ + // Maps node's name to index at net.node() list. + std::map nodesMap; + std::map::iterator nodesMapIt; + for (int i = 0; i < net.node_size(); ++i) + { + const tensorflow::NodeDef& node = net.node(i); + nodesMap.insert(std::make_pair(node.name(), i)); + } + + // Indices of nodes which use specific node as input. + std::vector > edges(nodesMap.size()); + std::vector numRefsToAdd(nodesMap.size(), 0); + std::vector nodesToAdd; + for (int i = 0; i < net.node_size(); ++i) + { + const tensorflow::NodeDef& node = net.node(i); + for (int j = 0; j < node.input_size(); ++j) + { + std::string inpName = node.input(j); + inpName = inpName.substr(0, inpName.rfind(':')); + inpName = inpName.substr(inpName.find('^') + 1); + + nodesMapIt = nodesMap.find(inpName); + CV_Assert(nodesMapIt != nodesMap.end()); + edges[nodesMapIt->second].push_back(i); + } + if (node.input_size() == 0) + nodesToAdd.push_back(i); + else + { + if (node.op() == "Merge" || node.op() == "RefMerge") + { + int numControlEdges = 0; + for (int j = 0; j < node.input_size(); ++j) + numControlEdges += node.input(j)[0] == '^'; + numRefsToAdd[i] = numControlEdges + 1; + } + else + numRefsToAdd[i] = node.input_size(); + } + } + + std::vector permIds; + permIds.reserve(net.node_size()); + while (!nodesToAdd.empty()) + { + int nodeToAdd = nodesToAdd.back(); + nodesToAdd.pop_back(); + + permIds.push_back(nodeToAdd); + // std::cout << net.node(nodeToAdd).name() << '\n'; + + for (int i = 0; i < edges[nodeToAdd].size(); ++i) + { + int consumerId = edges[nodeToAdd][i]; + if (numRefsToAdd[consumerId] > 0) + { + if (numRefsToAdd[consumerId] == 1) + nodesToAdd.push_back(consumerId); + else + CV_Assert(numRefsToAdd[consumerId] >= 0); + numRefsToAdd[consumerId] -= 1; + } + } + } + CV_Assert(permIds.size() == net.node_size()); + permute(net.mutable_node(), permIds); +} + CV__DNN_EXPERIMENTAL_NS_END }} // namespace dnn, namespace cv diff --git a/modules/dnn/src/tensorflow/tf_graph_simplifier.hpp b/modules/dnn/src/tensorflow/tf_graph_simplifier.hpp index d60ced7894..24c4bd5a70 100644 --- a/modules/dnn/src/tensorflow/tf_graph_simplifier.hpp +++ b/modules/dnn/src/tensorflow/tf_graph_simplifier.hpp @@ -25,6 +25,8 @@ Mat getTensorContent(const tensorflow::TensorProto &tensor); void releaseTensor(tensorflow::TensorProto* tensor); +void sortByExecutionOrder(tensorflow::GraphDef& net); + CV__DNN_EXPERIMENTAL_NS_END }} // namespace dnn, namespace cv diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index 264d3cbc86..6af22ead95 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -1950,5 +1950,34 @@ Net readNetFromTensorflow(const std::vector& bufferModel, const std::vect bufferConfigPtr, bufferConfig.size()); } +void writeTextGraph(const String& _model, const String& output) +{ + String model = _model; + const std::string modelExt = model.substr(model.rfind('.') + 1); + if (modelExt != "pb") + CV_Error(Error::StsNotImplemented, "Only TensorFlow models support export to text file"); + + tensorflow::GraphDef net; + ReadTFNetParamsFromBinaryFileOrDie(model.c_str(), &net); + + sortByExecutionOrder(net); + + RepeatedPtrField::iterator it; + for (it = net.mutable_node()->begin(); it != net.mutable_node()->end(); ++it) + { + if (it->op() == "Const") + { + it->mutable_attr()->at("value").mutable_tensor()->clear_tensor_content(); + } + } + + std::string content; + google::protobuf::TextFormat::PrintToString(net, &content); + + std::ofstream ofs(output.c_str()); + ofs << content; + ofs.close(); +} + CV__DNN_EXPERIMENTAL_NS_END }} // namespace diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index c1a55cd701..71aa4e7461 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -315,6 +315,29 @@ TEST_P(Test_TensorFlow_nets, Inception_v2_SSD) normAssertDetections(ref, out, "", 0.5, scoreDiff, iouDiff); } +TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD) +{ + checkBackend(); + + std::string model = findDataFile("dnn/ssd_mobilenet_v1_coco_2017_11_17.pb", false); + std::string proto = findDataFile("dnn/ssd_mobilenet_v1_coco_2017_11_17.pbtxt", false); + + Net net = readNetFromTensorflow(model, proto); + Mat img = imread(findDataFile("dnn/dog416.png", false)); + Mat blob = blobFromImage(img, 1.0f, Size(300, 300), Scalar(), true, false); + + net.setPreferableBackend(backend); + net.setPreferableTarget(target); + + net.setInput(blob); + 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; + normAssertDetections(ref, out, "", 0.3, scoreDiff, iouDiff); +} + TEST_P(Test_TensorFlow_nets, Faster_RCNN) { static std::string names[] = {"faster_rcnn_inception_v2_coco_2018_01_28", @@ -360,7 +383,8 @@ 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.008 : default_l1; + + double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.011 : default_l1; double iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.021 : default_lInf; normAssertDetections(ref, out, "", 0.4, scoreDiff, iouDiff); } diff --git a/samples/dnn/object_detection.py b/samples/dnn/object_detection.py index 7063fd2abb..76c33f8e3b 100644 --- a/samples/dnn/object_detection.py +++ b/samples/dnn/object_detection.py @@ -3,6 +3,10 @@ import argparse import sys import numpy as np +from tf_text_graph_common import readTextMessage +from tf_text_graph_ssd import createSSDGraph +from tf_text_graph_faster_rcnn import createFasterRCNNGraph + backends = (cv.dnn.DNN_BACKEND_DEFAULT, cv.dnn.DNN_BACKEND_HALIDE, cv.dnn.DNN_BACKEND_INFERENCE_ENGINE, cv.dnn.DNN_BACKEND_OPENCV) targets = (cv.dnn.DNN_TARGET_CPU, cv.dnn.DNN_TARGET_OPENCL, cv.dnn.DNN_TARGET_OPENCL_FP16, cv.dnn.DNN_TARGET_MYRIAD) @@ -11,11 +15,15 @@ parser.add_argument('--input', help='Path to input image or video file. Skip thi parser.add_argument('--model', required=True, help='Path to a binary file of model contains trained weights. ' 'It could be a file with extensions .caffemodel (Caffe), ' - '.pb (TensorFlow), .t7 or .net (Torch), .weights (Darknet)') + '.pb (TensorFlow), .t7 or .net (Torch), .weights (Darknet), .bin (OpenVINO)') parser.add_argument('--config', help='Path to a text file of model contains network configuration. ' - 'It could be a file with extensions .prototxt (Caffe), .pbtxt (TensorFlow), .cfg (Darknet)') -parser.add_argument('--framework', choices=['caffe', 'tensorflow', 'torch', 'darknet'], + 'It could be a file with extensions .prototxt (Caffe), .pbtxt or .config (TensorFlow), .cfg (Darknet), .xml (OpenVINO)') +parser.add_argument('--out_tf_graph', default='graph.pbtxt', + help='For models from TensorFlow Object Detection API, you may ' + 'pass a .config file which was used for training through --config ' + 'argument. This way an additional .pbtxt file with TensorFlow graph will be created.') +parser.add_argument('--framework', choices=['caffe', 'tensorflow', 'torch', 'darknet', 'dldt'], help='Optional name of an origin framework of the model. ' 'Detect it automatically if it does not set.') parser.add_argument('--classes', help='Optional path to a text file with names of classes to label detected objects.') @@ -46,6 +54,20 @@ parser.add_argument('--target', choices=targets, default=cv.dnn.DNN_TARGET_CPU, '%d: VPU' % targets) args = parser.parse_args() +# If config specified, try to load it as TensorFlow Object Detection API's pipeline. +config = readTextMessage(args.config) +if 'model' in config: + print('TensorFlow Object Detection API config detected') + if 'ssd' in config['model'][0]: + print('Preparing text graph representation for SSD model: ' + args.out_tf_graph) + createSSDGraph(args.model, args.config, args.out_tf_graph) + args.config = args.out_tf_graph + elif 'faster_rcnn' in config['model'][0]: + print('Preparing text graph representation for Faster-RCNN model: ' + args.out_tf_graph) + createFasterRCNNGraph(args.model, args.config, args.out_tf_graph) + args.config = args.out_tf_graph + + # Load names of classes classes = None if args.classes: diff --git a/samples/dnn/tf_text_graph_common.py b/samples/dnn/tf_text_graph_common.py index 061718e7a0..d4d202e682 100644 --- a/samples/dnn/tf_text_graph_common.py +++ b/samples/dnn/tf_text_graph_common.py @@ -1,8 +1,86 @@ -import tensorflow as tf -from tensorflow.core.framework.node_def_pb2 import NodeDef -from google.protobuf import text_format +def tokenize(s): + tokens = [] + token = "" + isString = False + isComment = False + for symbol in s: + isComment = (isComment and symbol != '\n') or (not isString and symbol == '#') + if isComment: + continue -def tensorMsg(values): + if symbol == ' ' or symbol == '\t' or symbol == '\r' or symbol == '\'' or \ + symbol == '\n' or symbol == ':' or symbol == '\"' or symbol == ';' or \ + symbol == ',': + + if (symbol == '\"' or symbol == '\'') and isString: + tokens.append(token) + token = "" + else: + if isString: + token += symbol + elif token: + tokens.append(token) + token = "" + isString = (symbol == '\"' or symbol == '\'') ^ isString; + + elif symbol == '{' or symbol == '}' or symbol == '[' or symbol == ']': + if token: + tokens.append(token) + token = "" + tokens.append(symbol) + else: + token += symbol + if token: + tokens.append(token) + return tokens + + +def parseMessage(tokens, idx): + msg = {} + assert(tokens[idx] == '{') + + isArray = False + while True: + if not isArray: + idx += 1 + if idx < len(tokens): + fieldName = tokens[idx] + else: + return None + if fieldName == '}': + break + + idx += 1 + fieldValue = tokens[idx] + + if fieldValue == '{': + embeddedMsg, idx = parseMessage(tokens, idx) + if fieldName in msg: + msg[fieldName].append(embeddedMsg) + else: + msg[fieldName] = [embeddedMsg] + elif fieldValue == '[': + isArray = True + elif fieldValue == ']': + isArray = False + else: + if fieldName in msg: + msg[fieldName].append(fieldValue) + else: + msg[fieldName] = [fieldValue] + return msg, idx + + +def readTextMessage(filePath): + with open(filePath, 'rt') as f: + content = f.read() + + tokens = tokenize('{' + content + '}') + msg = parseMessage(tokens, 0) + return msg[0] if msg else {} + + +def listToTensor(values): if all([isinstance(v, float) for v in values]): dtype = 'DT_FLOAT' field = 'float_val' @@ -12,16 +90,25 @@ def tensorMsg(values): else: raise Exception('Wrong values types') - msg = 'tensor { dtype: ' + dtype + ' tensor_shape { dim { size: %d } }' % len(values) - for value in values: - msg += '%s: %s ' % (field, str(value)) - return msg + '}' + msg = { + 'tensor': { + 'dtype': dtype, + 'tensor_shape': { + 'dim': { + 'size': len(values) + } + } + } + } + msg['tensor'][field] = values + return msg + def addConstNode(name, values, graph_def): node = NodeDef() node.name = name node.op = 'Const' - text_format.Merge(tensorMsg(values), node.attr["value"]) + node.addAttr('value', values) graph_def.node.extend([node]) @@ -29,13 +116,13 @@ def addSlice(inp, out, begins, sizes, graph_def): beginsNode = NodeDef() beginsNode.name = out + '/begins' beginsNode.op = 'Const' - text_format.Merge(tensorMsg(begins), beginsNode.attr["value"]) + beginsNode.addAttr('value', begins) graph_def.node.extend([beginsNode]) sizesNode = NodeDef() sizesNode.name = out + '/sizes' sizesNode.op = 'Const' - text_format.Merge(tensorMsg(sizes), sizesNode.attr["value"]) + sizesNode.addAttr('value', sizes) graph_def.node.extend([sizesNode]) sliced = NodeDef() @@ -51,7 +138,7 @@ def addReshape(inp, out, shape, graph_def): shapeNode = NodeDef() shapeNode.name = out + '/shape' shapeNode.op = 'Const' - text_format.Merge(tensorMsg(shape), shapeNode.attr["value"]) + shapeNode.addAttr('value', shape) graph_def.node.extend([shapeNode]) reshape = NodeDef() @@ -66,7 +153,7 @@ def addSoftMax(inp, out, graph_def): softmax = NodeDef() softmax.name = out softmax.op = 'Softmax' - text_format.Merge('i: -1', softmax.attr['axis']) + softmax.addAttr('axis', -1) softmax.input.append(inp) graph_def.node.extend([softmax]) @@ -79,6 +166,103 @@ def addFlatten(inp, out, graph_def): graph_def.node.extend([flatten]) +class NodeDef: + def __init__(self): + self.input = [] + self.name = "" + self.op = "" + self.attr = {} + + def addAttr(self, key, value): + assert(not key in self.attr) + if isinstance(value, bool): + self.attr[key] = {'b': value} + elif isinstance(value, int): + self.attr[key] = {'i': value} + elif isinstance(value, float): + self.attr[key] = {'f': value} + elif isinstance(value, str): + self.attr[key] = {'s': value} + elif isinstance(value, list): + self.attr[key] = listToTensor(value) + else: + raise Exception('Unknown type of attribute ' + key) + + def Clear(self): + self.input = [] + self.name = "" + self.op = "" + self.attr = {} + + +class GraphDef: + def __init__(self): + self.node = [] + + def save(self, filePath): + with open(filePath, 'wt') as f: + + def printAttr(d, indent): + indent = ' ' * indent + for key, value in sorted(d.items(), key=lambda x:x[0].lower()): + value = value if isinstance(value, list) else [value] + for v in value: + if isinstance(v, dict): + f.write(indent + key + ' {\n') + printAttr(v, len(indent) + 2) + f.write(indent + '}\n') + else: + isString = False + if isinstance(v, str) and not v.startswith('DT_'): + try: + float(v) + except: + isString = True + + if isinstance(v, bool): + printed = 'true' if v else 'false' + elif v == 'true' or v == 'false': + printed = 'true' if v == 'true' else 'false' + elif isString: + printed = '\"%s\"' % v + else: + printed = str(v) + f.write(indent + key + ': ' + printed + '\n') + + for node in self.node: + f.write('node {\n') + f.write(' name: \"%s\"\n' % node.name) + f.write(' op: \"%s\"\n' % node.op) + for inp in node.input: + f.write(' input: \"%s\"\n' % inp) + for key, value in sorted(node.attr.items(), key=lambda x:x[0].lower()): + f.write(' attr {\n') + f.write(' key: \"%s\"\n' % key) + f.write(' value {\n') + printAttr(value, 6) + f.write(' }\n') + f.write(' }\n') + f.write('}\n') + + +def parseTextGraph(filePath): + msg = readTextMessage(filePath) + + graph = GraphDef() + for node in msg['node']: + graphNode = NodeDef() + graphNode.name = node['name'][0] + graphNode.op = node['op'][0] + graphNode.input = node['input'] if 'input' in node else [] + + if 'attr' in node: + for attr in node['attr']: + graphNode.attr[attr['key'][0]] = attr['value'][0] + + graph.node.append(graphNode) + return graph + + # Removes Identity nodes def removeIdentity(graph_def): identities = {} diff --git a/samples/dnn/tf_text_graph_faster_rcnn.py b/samples/dnn/tf_text_graph_faster_rcnn.py index b02b0c5ca6..368edaa6fa 100644 --- a/samples/dnn/tf_text_graph_faster_rcnn.py +++ b/samples/dnn/tf_text_graph_faster_rcnn.py @@ -1,214 +1,228 @@ import argparse import numpy as np -import tensorflow as tf - -from tensorflow.core.framework.node_def_pb2 import NodeDef -from tensorflow.tools.graph_transforms import TransformGraph -from google.protobuf import text_format - +import cv2 as cv from tf_text_graph_common import * -parser = argparse.ArgumentParser(description='Run this script to get a text graph of ' - 'SSD model from TensorFlow Object Detection API. ' - 'Then pass it with .pb file to cv::dnn::readNetFromTensorflow function.') -parser.add_argument('--input', required=True, help='Path to frozen TensorFlow graph.') -parser.add_argument('--output', required=True, help='Path to output text graph.') -parser.add_argument('--num_classes', default=90, type=int, help='Number of trained classes.') -parser.add_argument('--scales', default=[0.25, 0.5, 1.0, 2.0], type=float, nargs='+', - help='Hyper-parameter of grid_anchor_generator from a config file.') -parser.add_argument('--aspect_ratios', default=[0.5, 1.0, 2.0], type=float, nargs='+', - help='Hyper-parameter of grid_anchor_generator from a config file.') -parser.add_argument('--features_stride', default=16, type=float, nargs='+', - help='Hyper-parameter from a config file.') -args = parser.parse_args() - -scopesToKeep = ('FirstStageFeatureExtractor', 'Conv', - 'FirstStageBoxPredictor/BoxEncodingPredictor', - 'FirstStageBoxPredictor/ClassPredictor', - 'CropAndResize', - 'MaxPool2D', - 'SecondStageFeatureExtractor', - 'SecondStageBoxPredictor', - 'Preprocessor/sub', - 'Preprocessor/mul', - 'image_tensor') - -scopesToIgnore = ('FirstStageFeatureExtractor/Assert', - 'FirstStageFeatureExtractor/Shape', - 'FirstStageFeatureExtractor/strided_slice', - 'FirstStageFeatureExtractor/GreaterEqual', - 'FirstStageFeatureExtractor/LogicalAnd') - -# Read the graph. -with tf.gfile.FastGFile(args.input, 'rb') as f: - graph_def = tf.GraphDef() - graph_def.ParseFromString(f.read()) - -removeIdentity(graph_def) - -def to_remove(name, op): - return name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep) - -removeUnusedNodesAndAttrs(to_remove, graph_def) - - -# Connect input node to the first layer -assert(graph_def.node[0].op == 'Placeholder') -graph_def.node[1].input.insert(0, graph_def.node[0].name) - -# Temporarily remove top nodes. -topNodes = [] -while True: - node = graph_def.node.pop() - topNodes.append(node) - if node.op == 'CropAndResize': - break - -addReshape('FirstStageBoxPredictor/ClassPredictor/BiasAdd', - 'FirstStageBoxPredictor/ClassPredictor/reshape_1', [0, -1, 2], graph_def) - -addSoftMax('FirstStageBoxPredictor/ClassPredictor/reshape_1', - 'FirstStageBoxPredictor/ClassPredictor/softmax', graph_def) # Compare with Reshape_4 - -addFlatten('FirstStageBoxPredictor/ClassPredictor/softmax', - 'FirstStageBoxPredictor/ClassPredictor/softmax/flatten', graph_def) - -# Compare with FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd -addFlatten('FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd', - 'FirstStageBoxPredictor/BoxEncodingPredictor/flatten', graph_def) - -proposals = NodeDef() -proposals.name = 'proposals' # Compare with ClipToWindow/Gather/Gather (NOTE: normalized) -proposals.op = 'PriorBox' -proposals.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd') -proposals.input.append(graph_def.node[0].name) # image_tensor - -text_format.Merge('b: false', proposals.attr["flip"]) -text_format.Merge('b: true', proposals.attr["clip"]) -text_format.Merge('f: %f' % args.features_stride, proposals.attr["step"]) -text_format.Merge('f: 0.0', proposals.attr["offset"]) -text_format.Merge(tensorMsg([0.1, 0.1, 0.2, 0.2]), proposals.attr["variance"]) - -widths = [] -heights = [] -for a in args.aspect_ratios: - for s in args.scales: - ar = np.sqrt(a) - heights.append((args.features_stride**2) * s / ar) - widths.append((args.features_stride**2) * s * ar) - -text_format.Merge(tensorMsg(widths), proposals.attr["width"]) -text_format.Merge(tensorMsg(heights), proposals.attr["height"]) - -graph_def.node.extend([proposals]) - -# Compare with Reshape_5 -detectionOut = NodeDef() -detectionOut.name = 'detection_out' -detectionOut.op = 'DetectionOutput' - -detectionOut.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/flatten') -detectionOut.input.append('FirstStageBoxPredictor/ClassPredictor/softmax/flatten') -detectionOut.input.append('proposals') - -text_format.Merge('i: 2', detectionOut.attr['num_classes']) -text_format.Merge('b: true', detectionOut.attr['share_location']) -text_format.Merge('i: 0', detectionOut.attr['background_label_id']) -text_format.Merge('f: 0.7', detectionOut.attr['nms_threshold']) -text_format.Merge('i: 6000', detectionOut.attr['top_k']) -text_format.Merge('s: "CENTER_SIZE"', detectionOut.attr['code_type']) -text_format.Merge('i: 100', detectionOut.attr['keep_top_k']) -text_format.Merge('b: false', detectionOut.attr['clip']) - -graph_def.node.extend([detectionOut]) - -addConstNode('clip_by_value/lower', [0.0], graph_def) -addConstNode('clip_by_value/upper', [1.0], graph_def) - -clipByValueNode = NodeDef() -clipByValueNode.name = 'detection_out/clip_by_value' -clipByValueNode.op = 'ClipByValue' -clipByValueNode.input.append('detection_out') -clipByValueNode.input.append('clip_by_value/lower') -clipByValueNode.input.append('clip_by_value/upper') -graph_def.node.extend([clipByValueNode]) - -# Save as text. -for node in reversed(topNodes): - graph_def.node.extend([node]) - -addSoftMax('SecondStageBoxPredictor/Reshape_1', 'SecondStageBoxPredictor/Reshape_1/softmax', graph_def) - -addSlice('SecondStageBoxPredictor/Reshape_1/softmax', - 'SecondStageBoxPredictor/Reshape_1/slice', - [0, 0, 1], [-1, -1, -1], graph_def) - -addReshape('SecondStageBoxPredictor/Reshape_1/slice', - 'SecondStageBoxPredictor/Reshape_1/Reshape', [1, -1], graph_def) - -# Replace Flatten subgraph onto a single node. -for i in reversed(range(len(graph_def.node))): - if graph_def.node[i].op == 'CropAndResize': - graph_def.node[i].input.insert(1, 'detection_out/clip_by_value') - - if graph_def.node[i].name == 'SecondStageBoxPredictor/Reshape': - addConstNode('SecondStageBoxPredictor/Reshape/shape2', [1, -1, 4], graph_def) - - graph_def.node[i].input.pop() - graph_def.node[i].input.append('SecondStageBoxPredictor/Reshape/shape2') - - if graph_def.node[i].name in ['SecondStageBoxPredictor/Flatten/flatten/Shape', - 'SecondStageBoxPredictor/Flatten/flatten/strided_slice', - 'SecondStageBoxPredictor/Flatten/flatten/Reshape/shape']: - del graph_def.node[i] - -for node in graph_def.node: - if node.name == 'SecondStageBoxPredictor/Flatten/flatten/Reshape': - node.op = 'Flatten' - node.input.pop() - - if node.name in ['FirstStageBoxPredictor/BoxEncodingPredictor/Conv2D', - 'SecondStageBoxPredictor/BoxEncodingPredictor/MatMul']: - text_format.Merge('b: true', node.attr["loc_pred_transposed"]) - -################################################################################ -### Postprocessing -################################################################################ -addSlice('detection_out/clip_by_value', 'detection_out/slice', [0, 0, 0, 3], [-1, -1, -1, 4], graph_def) - -variance = NodeDef() -variance.name = 'proposals/variance' -variance.op = 'Const' -text_format.Merge(tensorMsg([0.1, 0.1, 0.2, 0.2]), variance.attr["value"]) -graph_def.node.extend([variance]) - -varianceEncoder = NodeDef() -varianceEncoder.name = 'variance_encoded' -varianceEncoder.op = 'Mul' -varianceEncoder.input.append('SecondStageBoxPredictor/Reshape') -varianceEncoder.input.append(variance.name) -text_format.Merge('i: 2', varianceEncoder.attr["axis"]) -graph_def.node.extend([varianceEncoder]) - -addReshape('detection_out/slice', 'detection_out/slice/reshape', [1, 1, -1], graph_def) -addFlatten('variance_encoded', 'variance_encoded/flatten', graph_def) - -detectionOut = NodeDef() -detectionOut.name = 'detection_out_final' -detectionOut.op = 'DetectionOutput' - -detectionOut.input.append('variance_encoded/flatten') -detectionOut.input.append('SecondStageBoxPredictor/Reshape_1/Reshape') -detectionOut.input.append('detection_out/slice/reshape') - -text_format.Merge('i: %d' % args.num_classes, detectionOut.attr['num_classes']) -text_format.Merge('b: false', detectionOut.attr['share_location']) -text_format.Merge('i: %d' % (args.num_classes + 1), detectionOut.attr['background_label_id']) -text_format.Merge('f: 0.6', detectionOut.attr['nms_threshold']) -text_format.Merge('s: "CENTER_SIZE"', detectionOut.attr['code_type']) -text_format.Merge('i: 100', detectionOut.attr['keep_top_k']) -text_format.Merge('b: true', detectionOut.attr['clip']) -text_format.Merge('b: true', detectionOut.attr['variance_encoded_in_target']) -graph_def.node.extend([detectionOut]) - -tf.train.write_graph(graph_def, "", args.output, as_text=True) + +def createFasterRCNNGraph(modelPath, configPath, outputPath): + scopesToKeep = ('FirstStageFeatureExtractor', 'Conv', + 'FirstStageBoxPredictor/BoxEncodingPredictor', + 'FirstStageBoxPredictor/ClassPredictor', + 'CropAndResize', + 'MaxPool2D', + 'SecondStageFeatureExtractor', + 'SecondStageBoxPredictor', + 'Preprocessor/sub', + 'Preprocessor/mul', + 'image_tensor') + + scopesToIgnore = ('FirstStageFeatureExtractor/Assert', + 'FirstStageFeatureExtractor/Shape', + 'FirstStageFeatureExtractor/strided_slice', + 'FirstStageFeatureExtractor/GreaterEqual', + 'FirstStageFeatureExtractor/LogicalAnd') + + # Load a config file. + config = readTextMessage(configPath) + config = config['model'][0]['faster_rcnn'][0] + num_classes = int(config['num_classes'][0]) + + grid_anchor_generator = config['first_stage_anchor_generator'][0]['grid_anchor_generator'][0] + scales = [float(s) for s in grid_anchor_generator['scales']] + aspect_ratios = [float(ar) for ar in grid_anchor_generator['aspect_ratios']] + width_stride = float(grid_anchor_generator['width_stride'][0]) + height_stride = float(grid_anchor_generator['height_stride'][0]) + features_stride = float(config['feature_extractor'][0]['first_stage_features_stride'][0]) + + print('Number of classes: %d' % num_classes) + print('Scales: %s' % str(scales)) + print('Aspect ratios: %s' % str(aspect_ratios)) + print('Width stride: %f' % width_stride) + print('Height stride: %f' % height_stride) + print('Features stride: %f' % features_stride) + + # Read the graph. + cv.dnn.writeTextGraph(modelPath, outputPath) + graph_def = parseTextGraph(outputPath) + + removeIdentity(graph_def) + + def to_remove(name, op): + return name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep) + + removeUnusedNodesAndAttrs(to_remove, graph_def) + + + # Connect input node to the first layer + assert(graph_def.node[0].op == 'Placeholder') + graph_def.node[1].input.insert(0, graph_def.node[0].name) + + # Temporarily remove top nodes. + topNodes = [] + while True: + node = graph_def.node.pop() + topNodes.append(node) + if node.op == 'CropAndResize': + break + + addReshape('FirstStageBoxPredictor/ClassPredictor/BiasAdd', + 'FirstStageBoxPredictor/ClassPredictor/reshape_1', [0, -1, 2], graph_def) + + addSoftMax('FirstStageBoxPredictor/ClassPredictor/reshape_1', + 'FirstStageBoxPredictor/ClassPredictor/softmax', graph_def) # Compare with Reshape_4 + + addFlatten('FirstStageBoxPredictor/ClassPredictor/softmax', + 'FirstStageBoxPredictor/ClassPredictor/softmax/flatten', graph_def) + + # Compare with FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd + addFlatten('FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd', + 'FirstStageBoxPredictor/BoxEncodingPredictor/flatten', graph_def) + + proposals = NodeDef() + proposals.name = 'proposals' # Compare with ClipToWindow/Gather/Gather (NOTE: normalized) + proposals.op = 'PriorBox' + proposals.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd') + proposals.input.append(graph_def.node[0].name) # image_tensor + + proposals.addAttr('flip', False) + proposals.addAttr('clip', True) + proposals.addAttr('step', features_stride) + proposals.addAttr('offset', 0.0) + proposals.addAttr('variance', [0.1, 0.1, 0.2, 0.2]) + + widths = [] + heights = [] + for a in aspect_ratios: + for s in scales: + ar = np.sqrt(a) + heights.append((height_stride**2) * s / ar) + widths.append((width_stride**2) * s * ar) + + proposals.addAttr('width', widths) + proposals.addAttr('height', heights) + + graph_def.node.extend([proposals]) + + # Compare with Reshape_5 + detectionOut = NodeDef() + detectionOut.name = 'detection_out' + detectionOut.op = 'DetectionOutput' + + detectionOut.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/flatten') + detectionOut.input.append('FirstStageBoxPredictor/ClassPredictor/softmax/flatten') + detectionOut.input.append('proposals') + + detectionOut.addAttr('num_classes', 2) + detectionOut.addAttr('share_location', True) + detectionOut.addAttr('background_label_id', 0) + detectionOut.addAttr('nms_threshold', 0.7) + detectionOut.addAttr('top_k', 6000) + detectionOut.addAttr('code_type', "CENTER_SIZE") + detectionOut.addAttr('keep_top_k', 100) + detectionOut.addAttr('clip', False) + + graph_def.node.extend([detectionOut]) + + addConstNode('clip_by_value/lower', [0.0], graph_def) + addConstNode('clip_by_value/upper', [1.0], graph_def) + + clipByValueNode = NodeDef() + clipByValueNode.name = 'detection_out/clip_by_value' + clipByValueNode.op = 'ClipByValue' + clipByValueNode.input.append('detection_out') + clipByValueNode.input.append('clip_by_value/lower') + clipByValueNode.input.append('clip_by_value/upper') + graph_def.node.extend([clipByValueNode]) + + # Save as text. + for node in reversed(topNodes): + graph_def.node.extend([node]) + + addSoftMax('SecondStageBoxPredictor/Reshape_1', 'SecondStageBoxPredictor/Reshape_1/softmax', graph_def) + + addSlice('SecondStageBoxPredictor/Reshape_1/softmax', + 'SecondStageBoxPredictor/Reshape_1/slice', + [0, 0, 1], [-1, -1, -1], graph_def) + + addReshape('SecondStageBoxPredictor/Reshape_1/slice', + 'SecondStageBoxPredictor/Reshape_1/Reshape', [1, -1], graph_def) + + # Replace Flatten subgraph onto a single node. + for i in reversed(range(len(graph_def.node))): + if graph_def.node[i].op == 'CropAndResize': + graph_def.node[i].input.insert(1, 'detection_out/clip_by_value') + + if graph_def.node[i].name == 'SecondStageBoxPredictor/Reshape': + addConstNode('SecondStageBoxPredictor/Reshape/shape2', [1, -1, 4], graph_def) + + graph_def.node[i].input.pop() + graph_def.node[i].input.append('SecondStageBoxPredictor/Reshape/shape2') + + if graph_def.node[i].name in ['SecondStageBoxPredictor/Flatten/flatten/Shape', + 'SecondStageBoxPredictor/Flatten/flatten/strided_slice', + 'SecondStageBoxPredictor/Flatten/flatten/Reshape/shape']: + del graph_def.node[i] + + for node in graph_def.node: + if node.name == 'SecondStageBoxPredictor/Flatten/flatten/Reshape': + node.op = 'Flatten' + node.input.pop() + + if node.name in ['FirstStageBoxPredictor/BoxEncodingPredictor/Conv2D', + 'SecondStageBoxPredictor/BoxEncodingPredictor/MatMul']: + node.addAttr('loc_pred_transposed', True) + + ################################################################################ + ### Postprocessing + ################################################################################ + addSlice('detection_out/clip_by_value', 'detection_out/slice', [0, 0, 0, 3], [-1, -1, -1, 4], graph_def) + + variance = NodeDef() + variance.name = 'proposals/variance' + variance.op = 'Const' + variance.addAttr('value', [0.1, 0.1, 0.2, 0.2]) + graph_def.node.extend([variance]) + + varianceEncoder = NodeDef() + varianceEncoder.name = 'variance_encoded' + varianceEncoder.op = 'Mul' + varianceEncoder.input.append('SecondStageBoxPredictor/Reshape') + varianceEncoder.input.append(variance.name) + varianceEncoder.addAttr('axis', 2) + graph_def.node.extend([varianceEncoder]) + + addReshape('detection_out/slice', 'detection_out/slice/reshape', [1, 1, -1], graph_def) + addFlatten('variance_encoded', 'variance_encoded/flatten', graph_def) + + detectionOut = NodeDef() + detectionOut.name = 'detection_out_final' + detectionOut.op = 'DetectionOutput' + + detectionOut.input.append('variance_encoded/flatten') + detectionOut.input.append('SecondStageBoxPredictor/Reshape_1/Reshape') + detectionOut.input.append('detection_out/slice/reshape') + + detectionOut.addAttr('num_classes', num_classes) + detectionOut.addAttr('share_location', False) + detectionOut.addAttr('background_label_id', num_classes + 1) + detectionOut.addAttr('nms_threshold', 0.6) + detectionOut.addAttr('code_type', "CENTER_SIZE") + detectionOut.addAttr('keep_top_k', 100) + detectionOut.addAttr('clip', True) + detectionOut.addAttr('variance_encoded_in_target', True) + graph_def.node.extend([detectionOut]) + + # Save as text. + graph_def.save(outputPath) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Run this script to get a text graph of ' + 'Faster-RCNN model from TensorFlow Object Detection API. ' + 'Then pass it with .pb file to cv::dnn::readNetFromTensorflow function.') + parser.add_argument('--input', required=True, help='Path to frozen TensorFlow graph.') + parser.add_argument('--output', required=True, help='Path to output text graph.') + parser.add_argument('--config', required=True, help='Path to a *.config file is used for training.') + args = parser.parse_args() + + createFasterRCNNGraph(args.input, args.config, args.output) diff --git a/samples/dnn/tf_text_graph_mask_rcnn.py b/samples/dnn/tf_text_graph_mask_rcnn.py index 4c6997f930..b80a0fc4df 100644 --- a/samples/dnn/tf_text_graph_mask_rcnn.py +++ b/samples/dnn/tf_text_graph_mask_rcnn.py @@ -1,11 +1,6 @@ import argparse import numpy as np -import tensorflow as tf - -from tensorflow.core.framework.node_def_pb2 import NodeDef -from tensorflow.tools.graph_transforms import TransformGraph -from google.protobuf import text_format - +import cv2 as cv from tf_text_graph_common import * parser = argparse.ArgumentParser(description='Run this script to get a text graph of ' @@ -13,13 +8,7 @@ parser = argparse.ArgumentParser(description='Run this script to get a text grap 'Then pass it with .pb file to cv::dnn::readNetFromTensorflow function.') parser.add_argument('--input', required=True, help='Path to frozen TensorFlow graph.') parser.add_argument('--output', required=True, help='Path to output text graph.') -parser.add_argument('--num_classes', default=90, type=int, help='Number of trained classes.') -parser.add_argument('--scales', default=[0.25, 0.5, 1.0, 2.0], type=float, nargs='+', - help='Hyper-parameter of grid_anchor_generator from a config file.') -parser.add_argument('--aspect_ratios', default=[0.5, 1.0, 2.0], type=float, nargs='+', - help='Hyper-parameter of grid_anchor_generator from a config file.') -parser.add_argument('--features_stride', default=16, type=float, nargs='+', - help='Hyper-parameter from a config file.') +parser.add_argument('--config', required=True, help='Path to a *.config file is used for training.') args = parser.parse_args() scopesToKeep = ('FirstStageFeatureExtractor', 'Conv', @@ -39,11 +28,28 @@ scopesToIgnore = ('FirstStageFeatureExtractor/Assert', 'FirstStageFeatureExtractor/GreaterEqual', 'FirstStageFeatureExtractor/LogicalAnd') +# Load a config file. +config = readTextMessage(args.config) +config = config['model'][0]['faster_rcnn'][0] +num_classes = int(config['num_classes'][0]) + +grid_anchor_generator = config['first_stage_anchor_generator'][0]['grid_anchor_generator'][0] +scales = [float(s) for s in grid_anchor_generator['scales']] +aspect_ratios = [float(ar) for ar in grid_anchor_generator['aspect_ratios']] +width_stride = float(grid_anchor_generator['width_stride'][0]) +height_stride = float(grid_anchor_generator['height_stride'][0]) +features_stride = float(config['feature_extractor'][0]['first_stage_features_stride'][0]) + +print('Number of classes: %d' % num_classes) +print('Scales: %s' % str(scales)) +print('Aspect ratios: %s' % str(aspect_ratios)) +print('Width stride: %f' % width_stride) +print('Height stride: %f' % height_stride) +print('Features stride: %f' % features_stride) # Read the graph. -with tf.gfile.FastGFile(args.input, 'rb') as f: - graph_def = tf.GraphDef() - graph_def.ParseFromString(f.read()) +cv.dnn.writeTextGraph(args.input, args.output) +graph_def = parseTextGraph(args.output) removeIdentity(graph_def) @@ -87,22 +93,22 @@ proposals.op = 'PriorBox' proposals.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd') proposals.input.append(graph_def.node[0].name) # image_tensor -text_format.Merge('b: false', proposals.attr["flip"]) -text_format.Merge('b: true', proposals.attr["clip"]) -text_format.Merge('f: %f' % args.features_stride, proposals.attr["step"]) -text_format.Merge('f: 0.0', proposals.attr["offset"]) -text_format.Merge(tensorMsg([0.1, 0.1, 0.2, 0.2]), proposals.attr["variance"]) +proposals.addAttr('flip', False) +proposals.addAttr('clip', True) +proposals.addAttr('step', features_stride) +proposals.addAttr('offset', 0.0) +proposals.addAttr('variance', [0.1, 0.1, 0.2, 0.2]) widths = [] heights = [] -for a in args.aspect_ratios: - for s in args.scales: +for a in aspect_ratios: + for s in scales: ar = np.sqrt(a) - heights.append((args.features_stride**2) * s / ar) - widths.append((args.features_stride**2) * s * ar) + heights.append((features_stride**2) * s / ar) + widths.append((features_stride**2) * s * ar) -text_format.Merge(tensorMsg(widths), proposals.attr["width"]) -text_format.Merge(tensorMsg(heights), proposals.attr["height"]) +proposals.addAttr('width', widths) +proposals.addAttr('height', heights) graph_def.node.extend([proposals]) @@ -115,14 +121,14 @@ detectionOut.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/flatten') detectionOut.input.append('FirstStageBoxPredictor/ClassPredictor/softmax/flatten') detectionOut.input.append('proposals') -text_format.Merge('i: 2', detectionOut.attr['num_classes']) -text_format.Merge('b: true', detectionOut.attr['share_location']) -text_format.Merge('i: 0', detectionOut.attr['background_label_id']) -text_format.Merge('f: 0.7', detectionOut.attr['nms_threshold']) -text_format.Merge('i: 6000', detectionOut.attr['top_k']) -text_format.Merge('s: "CENTER_SIZE"', detectionOut.attr['code_type']) -text_format.Merge('i: 100', detectionOut.attr['keep_top_k']) -text_format.Merge('b: true', detectionOut.attr['clip']) +detectionOut.addAttr('num_classes', 2) +detectionOut.addAttr('share_location', True) +detectionOut.addAttr('background_label_id', 0) +detectionOut.addAttr('nms_threshold', 0.7) +detectionOut.addAttr('top_k', 6000) +detectionOut.addAttr('code_type', "CENTER_SIZE") +detectionOut.addAttr('keep_top_k', 100) +detectionOut.addAttr('clip', True) graph_def.node.extend([detectionOut]) @@ -171,7 +177,7 @@ for node in graph_def.node: if node.name in ['FirstStageBoxPredictor/BoxEncodingPredictor/Conv2D', 'SecondStageBoxPredictor/BoxEncodingPredictor/MatMul']: - text_format.Merge('b: true', node.attr["loc_pred_transposed"]) + node.addAttr('loc_pred_transposed', True) ################################################################################ ### Postprocessing @@ -181,7 +187,7 @@ addSlice('detection_out', 'detection_out/slice', [0, 0, 0, 3], [-1, -1, -1, 4], variance = NodeDef() variance.name = 'proposals/variance' variance.op = 'Const' -text_format.Merge(tensorMsg([0.1, 0.1, 0.2, 0.2]), variance.attr["value"]) +variance.addAttr('value', [0.1, 0.1, 0.2, 0.2]) graph_def.node.extend([variance]) varianceEncoder = NodeDef() @@ -189,7 +195,7 @@ varianceEncoder.name = 'variance_encoded' varianceEncoder.op = 'Mul' varianceEncoder.input.append('SecondStageBoxPredictor/Reshape') varianceEncoder.input.append(variance.name) -text_format.Merge('i: 2', varianceEncoder.attr["axis"]) +varianceEncoder.addAttr('axis', 2) graph_def.node.extend([varianceEncoder]) addReshape('detection_out/slice', 'detection_out/slice/reshape', [1, 1, -1], graph_def) @@ -203,16 +209,16 @@ detectionOut.input.append('variance_encoded/flatten') detectionOut.input.append('SecondStageBoxPredictor/Reshape_1/Reshape') detectionOut.input.append('detection_out/slice/reshape') -text_format.Merge('i: %d' % args.num_classes, detectionOut.attr['num_classes']) -text_format.Merge('b: false', detectionOut.attr['share_location']) -text_format.Merge('i: %d' % (args.num_classes + 1), detectionOut.attr['background_label_id']) -text_format.Merge('f: 0.6', detectionOut.attr['nms_threshold']) -text_format.Merge('s: "CENTER_SIZE"', detectionOut.attr['code_type']) -text_format.Merge('i: 100', detectionOut.attr['keep_top_k']) -text_format.Merge('b: true', detectionOut.attr['clip']) -text_format.Merge('b: true', detectionOut.attr['variance_encoded_in_target']) -text_format.Merge('f: 0.3', detectionOut.attr['confidence_threshold']) -text_format.Merge('b: false', detectionOut.attr['group_by_classes']) +detectionOut.addAttr('num_classes', num_classes) +detectionOut.addAttr('share_location', False) +detectionOut.addAttr('background_label_id', num_classes + 1) +detectionOut.addAttr('nms_threshold', 0.6) +detectionOut.addAttr('code_type', "CENTER_SIZE") +detectionOut.addAttr('keep_top_k',100) +detectionOut.addAttr('clip', True) +detectionOut.addAttr('variance_encoded_in_target', True) +detectionOut.addAttr('confidence_threshold', 0.3) +detectionOut.addAttr('group_by_classes', False) graph_def.node.extend([detectionOut]) for node in reversed(topNodes): @@ -227,4 +233,5 @@ graph_def.node[-1].name = 'detection_masks' graph_def.node[-1].op = 'Sigmoid' graph_def.node[-1].input.pop() -tf.train.write_graph(graph_def, "", args.output, as_text=True) +# Save as text. +graph_def.save(args.output) diff --git a/samples/dnn/tf_text_graph_ssd.py b/samples/dnn/tf_text_graph_ssd.py index c07bc760a8..eb4b33042c 100644 --- a/samples/dnn/tf_text_graph_ssd.py +++ b/samples/dnn/tf_text_graph_ssd.py @@ -9,253 +9,269 @@ # deep learning network trained in TensorFlow Object Detection API. # 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 tensorflow as tf import argparse from math import sqrt -from tensorflow.core.framework.node_def_pb2 import NodeDef -from tensorflow.tools.graph_transforms import TransformGraph -from google.protobuf import text_format +import cv2 as cv from tf_text_graph_common import * -parser = argparse.ArgumentParser(description='Run this script to get a text graph of ' - 'SSD model from TensorFlow Object Detection API. ' - 'Then pass it with .pb file to cv::dnn::readNetFromTensorflow function.') -parser.add_argument('--input', required=True, help='Path to frozen TensorFlow graph.') -parser.add_argument('--output', required=True, help='Path to output text graph.') -parser.add_argument('--num_classes', default=90, type=int, help='Number of trained classes.') -parser.add_argument('--min_scale', default=0.2, type=float, help='Hyper-parameter of ssd_anchor_generator from config file.') -parser.add_argument('--max_scale', default=0.95, type=float, help='Hyper-parameter of ssd_anchor_generator from config file.') -parser.add_argument('--num_layers', default=6, type=int, help='Hyper-parameter of ssd_anchor_generator from config file.') -parser.add_argument('--aspect_ratios', default=[1.0, 2.0, 0.5, 3.0, 0.333], type=float, nargs='+', - help='Hyper-parameter of ssd_anchor_generator from config file.') -parser.add_argument('--image_width', default=300, type=int, help='Training images width.') -parser.add_argument('--image_height', default=300, type=int, help='Training images height.') -parser.add_argument('--not_reduce_boxes_in_lowest_layer', default=False, action='store_true', - help='A boolean to indicate whether the fixed 3 boxes per ' - 'location is used in the lowest achors generation layer.') -parser.add_argument('--box_predictor', default='convolutional', type=str, - choices=['convolutional', 'weight_shared_convolutional']) -args = parser.parse_args() - -# Nodes that should be kept. -keepOps = ['Conv2D', 'BiasAdd', 'Add', 'Relu6', 'Placeholder', 'FusedBatchNorm', - 'DepthwiseConv2dNative', 'ConcatV2', 'Mul', 'MaxPool', 'AvgPool', 'Identity', - 'Sub'] - -# Node with which prefixes should be removed -prefixesToRemove = ('MultipleGridAnchorGenerator/', 'Postprocessor/', 'Preprocessor/map') - -# Read the graph. -with tf.gfile.FastGFile(args.input, 'rb') as f: - graph_def = tf.GraphDef() - graph_def.ParseFromString(f.read()) - -inpNames = ['image_tensor'] -outNames = ['num_detections', 'detection_scores', 'detection_boxes', 'detection_classes'] -graph_def = TransformGraph(graph_def, inpNames, outNames, ['sort_by_execution_order']) - -def getUnconnectedNodes(): - unconnected = [] - for node in graph_def.node: - unconnected.append(node.name) - for inp in node.input: - if inp in unconnected: - unconnected.remove(inp) - return unconnected - - -# Detect unfused batch normalization nodes and fuse them. -def fuse_batch_normalization(): - # Add_0 <-- moving_variance, add_y - # Rsqrt <-- Add_0 - # Mul_0 <-- Rsqrt, gamma - # Mul_1 <-- input, Mul_0 - # Mul_2 <-- moving_mean, Mul_0 - # Sub_0 <-- beta, Mul_2 - # Add_1 <-- Mul_1, Sub_0 - nodesMap = {node.name: node for node in graph_def.node} - subgraph = ['Add', - ['Mul', 'input', ['Mul', ['Rsqrt', ['Add', 'moving_variance', 'add_y']], 'gamma']], - ['Sub', 'beta', ['Mul', 'moving_mean', 'Mul_0']]] - def checkSubgraph(node, targetNode, inputs, fusedNodes): - op = targetNode[0] - if node.op == op and (len(node.input) >= len(targetNode) - 1): - fusedNodes.append(node) - for i, inpOp in enumerate(targetNode[1:]): - if isinstance(inpOp, list): - if not node.input[i] in nodesMap or \ - not checkSubgraph(nodesMap[node.input[i]], inpOp, inputs, fusedNodes): - return False +def createSSDGraph(modelPath, configPath, outputPath): + # Nodes that should be kept. + keepOps = ['Conv2D', 'BiasAdd', 'Add', 'Relu6', 'Placeholder', 'FusedBatchNorm', + 'DepthwiseConv2dNative', 'ConcatV2', 'Mul', 'MaxPool', 'AvgPool', 'Identity', + 'Sub'] + + # Node with which prefixes should be removed + prefixesToRemove = ('MultipleGridAnchorGenerator/', 'Postprocessor/', 'Preprocessor/map') + + # Load a config file. + config = readTextMessage(configPath) + 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' + + 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)) + + # Read the graph. + cv.dnn.writeTextGraph(modelPath, outputPath) + graph_def = parseTextGraph(outputPath) + + inpNames = ['image_tensor'] + outNames = ['num_detections', 'detection_scores', 'detection_boxes', 'detection_classes'] + + def getUnconnectedNodes(): + unconnected = [] + for node in graph_def.node: + unconnected.append(node.name) + for inp in node.input: + if inp in unconnected: + unconnected.remove(inp) + return unconnected + + + # Detect unfused batch normalization nodes and fuse them. + def fuse_batch_normalization(): + # Add_0 <-- moving_variance, add_y + # Rsqrt <-- Add_0 + # Mul_0 <-- Rsqrt, gamma + # Mul_1 <-- input, Mul_0 + # Mul_2 <-- moving_mean, Mul_0 + # Sub_0 <-- beta, Mul_2 + # Add_1 <-- Mul_1, Sub_0 + nodesMap = {node.name: node for node in graph_def.node} + subgraph = ['Add', + ['Mul', 'input', ['Mul', ['Rsqrt', ['Add', 'moving_variance', 'add_y']], 'gamma']], + ['Sub', 'beta', ['Mul', 'moving_mean', 'Mul_0']]] + def checkSubgraph(node, targetNode, inputs, fusedNodes): + op = targetNode[0] + if node.op == op and (len(node.input) >= len(targetNode) - 1): + fusedNodes.append(node) + for i, inpOp in enumerate(targetNode[1:]): + if isinstance(inpOp, list): + if not node.input[i] in nodesMap or \ + not checkSubgraph(nodesMap[node.input[i]], inpOp, inputs, fusedNodes): + return False + else: + inputs[inpOp] = node.input[i] + + return True + else: + return False + + nodesToRemove = [] + for node in graph_def.node: + inputs = {} + fusedNodes = [] + if checkSubgraph(node, subgraph, inputs, fusedNodes): + name = node.name + node.Clear() + node.name = name + node.op = 'FusedBatchNorm' + node.input.append(inputs['input']) + node.input.append(inputs['gamma']) + node.input.append(inputs['beta']) + node.input.append(inputs['moving_mean']) + node.input.append(inputs['moving_variance']) + node.addAttr('epsilon', 0.001) + nodesToRemove += fusedNodes[1:] + for node in nodesToRemove: + graph_def.node.remove(node) + + fuse_batch_normalization() + + removeIdentity(graph_def) + + def to_remove(name, op): + return (not op in keepOps) or name.startswith(prefixesToRemove) + + removeUnusedNodesAndAttrs(to_remove, graph_def) + + + # Connect input node to the first layer + assert(graph_def.node[0].op == 'Placeholder') + # assert(graph_def.node[1].op == 'Conv2D') + weights = graph_def.node[1].input[0] + for i in range(len(graph_def.node[1].input)): + graph_def.node[1].input.pop() + graph_def.node[1].input.append(graph_def.node[0].name) + graph_def.node[1].input.append(weights) + + # Create SSD postprocessing head ############################################### + + # Concatenate predictions of classes, predictions of bounding boxes and proposals. + def addConcatNode(name, inputs, axisNodeName): + concat = NodeDef() + concat.name = name + concat.op = 'ConcatV2' + for inp in inputs: + concat.input.append(inp) + concat.input.append(axisNodeName) + graph_def.node.extend([concat]) + + addConstNode('concat/axis_flatten', [-1], graph_def) + addConstNode('PriorBox/concat/axis', [-2], graph_def) + + for label in ['ClassPredictor', 'BoxEncodingPredictor' if box_predictor is 'convolutional' else 'BoxPredictor']: + concatInputs = [] + for i in range(num_layers): + # Flatten predictions + flatten = NodeDef() + if box_predictor is 'convolutional': + inpName = 'BoxPredictor_%d/%s/BiasAdd' % (i, label) + else: + if i == 0: + inpName = 'WeightSharedConvolutionalBoxPredictor/%s/BiasAdd' % label else: - inputs[inpOp] = node.input[i] + inpName = 'WeightSharedConvolutionalBoxPredictor_%d/%s/BiasAdd' % (i, label) + flatten.input.append(inpName) + flatten.name = inpName + '/Flatten' + flatten.op = 'Flatten' - return True - else: - return False + concatInputs.append(flatten.name) + graph_def.node.extend([flatten]) + addConcatNode('%s/concat' % label, concatInputs, 'concat/axis_flatten') - nodesToRemove = [] + idx = 0 for node in graph_def.node: - inputs = {} - fusedNodes = [] - if checkSubgraph(node, subgraph, inputs, fusedNodes): - name = node.name - node.Clear() - node.name = name - node.op = 'FusedBatchNorm' - node.input.append(inputs['input']) - node.input.append(inputs['gamma']) - node.input.append(inputs['beta']) - node.input.append(inputs['moving_mean']) - node.input.append(inputs['moving_variance']) - text_format.Merge('f: 0.001', node.attr["epsilon"]) - nodesToRemove += fusedNodes[1:] - for node in nodesToRemove: - graph_def.node.remove(node) - -fuse_batch_normalization() - -removeIdentity(graph_def) - -def to_remove(name, op): - return (not op in keepOps) or name.startswith(prefixesToRemove) - -removeUnusedNodesAndAttrs(to_remove, graph_def) - - -# Connect input node to the first layer -assert(graph_def.node[0].op == 'Placeholder') -# assert(graph_def.node[1].op == 'Conv2D') -weights = graph_def.node[1].input[0] -for i in range(len(graph_def.node[1].input)): - graph_def.node[1].input.pop() -graph_def.node[1].input.append(graph_def.node[0].name) -graph_def.node[1].input.append(weights) - -# Create SSD postprocessing head ############################################### - -# Concatenate predictions of classes, predictions of bounding boxes and proposals. -def addConcatNode(name, inputs, axisNodeName): - concat = NodeDef() - concat.name = name - concat.op = 'ConcatV2' - for inp in inputs: - concat.input.append(inp) - concat.input.append(axisNodeName) - graph_def.node.extend([concat]) - -addConstNode('concat/axis_flatten', [-1], graph_def) -addConstNode('PriorBox/concat/axis', [-2], graph_def) - -for label in ['ClassPredictor', 'BoxEncodingPredictor' if args.box_predictor is 'convolutional' else 'BoxPredictor']: - concatInputs = [] - for i in range(args.num_layers): - # Flatten predictions - flatten = NodeDef() - if args.box_predictor is 'convolutional': - inpName = 'BoxPredictor_%d/%s/BiasAdd' % (i, label) + if node.name == ('BoxPredictor_%d/BoxEncodingPredictor/Conv2D' % idx) or \ + node.name == ('WeightSharedConvolutionalBoxPredictor_%d/BoxPredictor/Conv2D' % idx) or \ + node.name == 'WeightSharedConvolutionalBoxPredictor/BoxPredictor/Conv2D': + node.addAttr('loc_pred_transposed', True) + idx += 1 + assert(idx == 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() + priorBox.name = 'PriorBox_%d' % i + priorBox.op = 'PriorBox' + if box_predictor is 'convolutional': + priorBox.input.append('BoxPredictor_%d/BoxEncodingPredictor/BiasAdd' % i) else: if i == 0: - inpName = 'WeightSharedConvolutionalBoxPredictor/%s/BiasAdd' % label + priorBox.input.append('WeightSharedConvolutionalBoxPredictor/BoxPredictor/Conv2D') else: - inpName = 'WeightSharedConvolutionalBoxPredictor_%d/%s/BiasAdd' % (i, label) - flatten.input.append(inpName) - flatten.name = inpName + '/Flatten' - flatten.op = 'Flatten' - - concatInputs.append(flatten.name) - graph_def.node.extend([flatten]) - addConcatNode('%s/concat' % label, concatInputs, 'concat/axis_flatten') - -idx = 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': - text_format.Merge('b: true', node.attr["loc_pred_transposed"]) - idx += 1 -assert(idx == args.num_layers) - -# Add layers that generate anchors (bounding boxes proposals). -scales = [args.min_scale + (args.max_scale - args.min_scale) * i / (args.num_layers - 1) - for i in range(args.num_layers)] + [1.0] - -priorBoxes = [] -for i in range(args.num_layers): - priorBox = NodeDef() - priorBox.name = 'PriorBox_%d' % i - priorBox.op = 'PriorBox' - if args.box_predictor is 'convolutional': - priorBox.input.append('BoxPredictor_%d/BoxEncodingPredictor/BiasAdd' % i) - else: - if i == 0: - priorBox.input.append('WeightSharedConvolutionalBoxPredictor/BoxPredictor/Conv2D') - else: - priorBox.input.append('WeightSharedConvolutionalBoxPredictor_%d/BoxPredictor/BiasAdd' % i) - priorBox.input.append(graph_def.node[0].name) # image_tensor + priorBox.input.append('WeightSharedConvolutionalBoxPredictor_%d/BoxPredictor/BiasAdd' % i) + priorBox.input.append(graph_def.node[0].name) # image_tensor - text_format.Merge('b: false', priorBox.attr["flip"]) - text_format.Merge('b: false', priorBox.attr["clip"]) + priorBox.addAttr('flip', False) + priorBox.addAttr('clip', False) - if i == 0 and not args.not_reduce_boxes_in_lowest_layer: - widths = [0.1, args.min_scale * sqrt(2.0), args.min_scale * sqrt(0.5)] - heights = [0.1, args.min_scale / sqrt(2.0), args.min_scale / sqrt(0.5)] + 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 += [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]) + + graph_def.node.extend([priorBox]) + priorBoxes.append(priorBox.name) + + addConcatNode('PriorBox/concat', priorBoxes, 'concat/axis_flatten') + + # Sigmoid for classes predictions and DetectionOutput layer + sigmoid = NodeDef() + sigmoid.name = 'ClassPredictor/concat/sigmoid' + sigmoid.op = 'Sigmoid' + sigmoid.input.append('ClassPredictor/concat') + graph_def.node.extend([sigmoid]) + + detectionOut = NodeDef() + detectionOut.name = 'detection_out' + detectionOut.op = 'DetectionOutput' + + if box_predictor == 'convolutional': + detectionOut.input.append('BoxEncodingPredictor/concat') else: - widths = [scales[i] * sqrt(ar) for ar in args.aspect_ratios] - heights = [scales[i] / sqrt(ar) for ar in args.aspect_ratios] - - widths += [sqrt(scales[i] * scales[i + 1])] - heights += [sqrt(scales[i] * scales[i + 1])] - widths = [w * args.image_width for w in widths] - heights = [h * args.image_height for h in heights] - text_format.Merge(tensorMsg(widths), priorBox.attr["width"]) - text_format.Merge(tensorMsg(heights), priorBox.attr["height"]) - text_format.Merge(tensorMsg([0.1, 0.1, 0.2, 0.2]), priorBox.attr["variance"]) - - graph_def.node.extend([priorBox]) - priorBoxes.append(priorBox.name) - -addConcatNode('PriorBox/concat', priorBoxes, 'concat/axis_flatten') - -# Sigmoid for classes predictions and DetectionOutput layer -sigmoid = NodeDef() -sigmoid.name = 'ClassPredictor/concat/sigmoid' -sigmoid.op = 'Sigmoid' -sigmoid.input.append('ClassPredictor/concat') -graph_def.node.extend([sigmoid]) - -detectionOut = NodeDef() -detectionOut.name = 'detection_out' -detectionOut.op = 'DetectionOutput' - -if args.box_predictor == 'convolutional': - detectionOut.input.append('BoxEncodingPredictor/concat') -else: - detectionOut.input.append('BoxPredictor/concat') -detectionOut.input.append(sigmoid.name) -detectionOut.input.append('PriorBox/concat') - -text_format.Merge('i: %d' % (args.num_classes + 1), detectionOut.attr['num_classes']) -text_format.Merge('b: true', detectionOut.attr['share_location']) -text_format.Merge('i: 0', detectionOut.attr['background_label_id']) -text_format.Merge('f: 0.6', detectionOut.attr['nms_threshold']) -text_format.Merge('i: 100', detectionOut.attr['top_k']) -text_format.Merge('s: "CENTER_SIZE"', detectionOut.attr['code_type']) -text_format.Merge('i: 100', detectionOut.attr['keep_top_k']) -text_format.Merge('f: 0.01', detectionOut.attr['confidence_threshold']) - -graph_def.node.extend([detectionOut]) - -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. -tf.train.write_graph(graph_def, "", args.output, as_text=True) + detectionOut.input.append('BoxPredictor/concat') + detectionOut.input.append(sigmoid.name) + detectionOut.input.append('PriorBox/concat') + + detectionOut.addAttr('num_classes', num_classes + 1) + detectionOut.addAttr('share_location', True) + detectionOut.addAttr('background_label_id', 0) + detectionOut.addAttr('nms_threshold', 0.6) + detectionOut.addAttr('top_k', 100) + detectionOut.addAttr('code_type', "CENTER_SIZE") + detectionOut.addAttr('keep_top_k', 100) + detectionOut.addAttr('confidence_threshold', 0.01) + + graph_def.node.extend([detectionOut]) + + 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) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Run this script to get a text graph of ' + 'SSD model from TensorFlow Object Detection API. ' + 'Then pass it with .pb file to cv::dnn::readNetFromTensorflow function.') + parser.add_argument('--input', required=True, help='Path to frozen TensorFlow graph.') + parser.add_argument('--output', required=True, help='Path to output text graph.') + parser.add_argument('--config', required=True, help='Path to a *.config file is used for training.') + args = parser.parse_args() + + createSSDGraph(args.input, args.config, args.output) From cb7ee27cd9a2f06cf07c91eab1970be8d9232678 Mon Sep 17 00:00:00 2001 From: yuki takehara Date: Mon, 3 Sep 2018 23:18:10 +0900 Subject: [PATCH 37/47] Fix bug in distanceTransform (#12278) * fix 12218 * Update test_distancetransform.cpp marked the test as "BIGDATA_TEST" in order to skip it on low-mem platforms * modify test * use a smaller image in the test * fix test code --- modules/imgproc/src/distransform.cpp | 56 ++++++++++--------- .../imgproc/test/test_distancetransform.cpp | 19 +++++++ 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index b573b3d552..173e79ed85 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -45,7 +45,8 @@ namespace cv { static const int DIST_SHIFT = 16; -static const int INIT_DIST0 = (INT_MAX >> 2); +static const int INIT_DIST0 = INT_MAX; +static const int DIST_MAX = (INT_MAX >> 2); #define CV_FLT_TO_FIX(x,n) cvRound((x)*(1<<(n))) static void @@ -71,8 +72,8 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met { const int BORDER = 1; int i, j; - const int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT ); - const int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT ); + const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT ); + const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT ); const float scale = 1.f/(1 << DIST_SHIFT); const uchar* src = _src.ptr(); @@ -89,7 +90,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met for( i = 0; i < size.height; i++ ) { const uchar* s = src + i*srcstep; - int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER; + unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER; for( j = 0; j < BORDER; j++ ) tmp[-j-1] = tmp[size.width + j] = INIT_DIST0; @@ -100,8 +101,8 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met tmp[j] = 0; else { - int t0 = tmp[j-step-1] + DIAG_DIST; - int t = tmp[j-step] + HV_DIST; + unsigned int t0 = tmp[j-step-1] + DIAG_DIST; + unsigned int t = tmp[j-step] + HV_DIST; if( t0 > t ) t0 = t; t = tmp[j-step+1] + DIAG_DIST; if( t0 > t ) t0 = t; @@ -116,14 +117,14 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met for( i = size.height - 1; i >= 0; i-- ) { float* d = (float*)(dist + i*dststep); - int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER; + unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER; for( j = size.width - 1; j >= 0; j-- ) { - int t0 = tmp[j]; + unsigned int t0 = tmp[j]; if( t0 > HV_DIST ) { - int t = tmp[j+step+1] + DIAG_DIST; + unsigned int t = tmp[j+step+1] + DIAG_DIST; if( t0 > t ) t0 = t; t = tmp[j+step] + HV_DIST; if( t0 > t ) t0 = t; @@ -133,6 +134,7 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met if( t0 > t ) t0 = t; tmp[j] = t0; } + t0 = (t0 > DIST_MAX) ? DIST_MAX : t0; d[j] = (float)(t0 * scale); } } @@ -144,9 +146,9 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met { const int BORDER = 2; int i, j; - const int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT ); - const int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT ); - const int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT ); + const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT ); + const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT ); + const unsigned int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT ); const float scale = 1.f/(1 << DIST_SHIFT); const uchar* src = _src.ptr(); @@ -163,7 +165,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met for( i = 0; i < size.height; i++ ) { const uchar* s = src + i*srcstep; - int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER; + unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER; for( j = 0; j < BORDER; j++ ) tmp[-j-1] = tmp[size.width + j] = INIT_DIST0; @@ -174,8 +176,8 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met tmp[j] = 0; else { - int t0 = tmp[j-step*2-1] + LONG_DIST; - int t = tmp[j-step*2+1] + LONG_DIST; + unsigned int t0 = tmp[j-step*2-1] + LONG_DIST; + unsigned int t = tmp[j-step*2+1] + LONG_DIST; if( t0 > t ) t0 = t; t = tmp[j-step-2] + LONG_DIST; if( t0 > t ) t0 = t; @@ -198,14 +200,14 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met for( i = size.height - 1; i >= 0; i-- ) { float* d = (float*)(dist + i*dststep); - int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER; + unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER; for( j = size.width - 1; j >= 0; j-- ) { - int t0 = tmp[j]; + unsigned int t0 = tmp[j]; if( t0 > HV_DIST ) { - int t = tmp[j+step*2+1] + LONG_DIST; + unsigned int t = tmp[j+step*2+1] + LONG_DIST; if( t0 > t ) t0 = t; t = tmp[j+step*2-1] + LONG_DIST; if( t0 > t ) t0 = t; @@ -223,6 +225,7 @@ distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* met if( t0 > t ) t0 = t; tmp[j] = t0; } + t0 = (t0 > DIST_MAX) ? DIST_MAX : t0; d[j] = (float)(t0 * scale); } } @@ -235,9 +238,9 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels, const int BORDER = 2; int i, j; - const int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT ); - const int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT ); - const int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT ); + const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT ); + const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT ); + const unsigned int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT ); const float scale = 1.f/(1 << DIST_SHIFT); const uchar* src = _src.ptr(); @@ -256,7 +259,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels, for( i = 0; i < size.height; i++ ) { const uchar* s = src + i*srcstep; - int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER; + unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER; int* lls = (int*)(labels + i*lstep); for( j = 0; j < BORDER; j++ ) @@ -271,7 +274,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels, } else { - int t0 = INIT_DIST0, t; + unsigned int t0 = INIT_DIST0, t; int l0 = 0; t = tmp[j-step*2-1] + LONG_DIST; @@ -333,16 +336,16 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels, for( i = size.height - 1; i >= 0; i-- ) { float* d = (float*)(dist + i*dststep); - int* tmp = (int*)(temp + (i+BORDER)*step) + BORDER; + unsigned int* tmp = (unsigned int*)(temp + (i+BORDER)*step) + BORDER; int* lls = (int*)(labels + i*lstep); for( j = size.width - 1; j >= 0; j-- ) { - int t0 = tmp[j]; + unsigned int t0 = tmp[j]; int l0 = lls[j]; if( t0 > HV_DIST ) { - int t = tmp[j+step*2+1] + LONG_DIST; + unsigned int t = tmp[j+step*2+1] + LONG_DIST; if( t0 > t ) { t0 = t; @@ -393,6 +396,7 @@ distanceTransformEx_5x5( const Mat& _src, Mat& _temp, Mat& _dist, Mat& _labels, tmp[j] = t0; lls[j] = l0; } + t0 = (t0 > DIST_MAX) ? DIST_MAX : t0; d[j] = (float)(t0 * scale); } } diff --git a/modules/imgproc/test/test_distancetransform.cpp b/modules/imgproc/test/test_distancetransform.cpp index de99a08517..75ec5ec8fa 100644 --- a/modules/imgproc/test/test_distancetransform.cpp +++ b/modules/imgproc/test/test_distancetransform.cpp @@ -283,4 +283,23 @@ void CV_DisTransTest::prepare_to_validation( int /*test_case_idx*/ ) TEST(Imgproc_DistanceTransform, accuracy) { CV_DisTransTest test; test.safe_run(); } +BIGDATA_TEST(Imgproc_DistanceTransform, large_image_12218) +{ + const int lls_maxcnt = 79992000; // labels's maximum count + const int lls_mincnt = 1; // labels's minimum count + int i, j, nz; + Mat src(8000, 20000, CV_8UC1), dst, labels; + for( i = 0; i < src.rows; i++ ) + for( j = 0; j < src.cols; j++ ) + src.at(i, j) = (j > (src.cols / 2)) ? 0 : 255; + + distanceTransform(src, dst, labels, cv::DIST_L2, cv::DIST_MASK_3, DIST_LABEL_PIXEL); + + double scale = (double)lls_mincnt / (double)lls_maxcnt; + labels.convertTo(labels, CV_32SC1, scale); + Size size = labels.size(); + nz = cv::countNonZero(labels); + EXPECT_EQ(nz, (size.height*size.width / 2)); +} + }} // namespace From 1e362ff5c36ef028ccb4740aca0177895bb4d0f2 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 3 Sep 2018 18:39:42 +0300 Subject: [PATCH 38/47] imgproc(GaussianBlur): restore processing order --- modules/imgproc/src/smooth.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index 212698bce0..1eaac74890 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -4106,20 +4106,6 @@ void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize, int sdepth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - if(sdepth == CV_8U && ((borderType & BORDER_ISOLATED) || !_src.getMat().isSubmatrix())) - { - std::vector fkx, fky; - createGaussianKernels(fkx, fky, type, ksize, sigma1, sigma2); - Mat src = _src.getMat(); - Mat dst = _dst.getMat(); - if (src.data == dst.data) - src = src.clone(); - fixedSmoothInvoker invoker(src.ptr(), src.step1(), dst.ptr(), dst.step1(), dst.cols, dst.rows, dst.channels(), &fkx[0], (int)fkx.size(), &fky[0], (int)fky.size(), borderType & ~BORDER_ISOLATED); - parallel_for_(Range(0, dst.rows), invoker, std::max(1, std::min(getNumThreads(), getNumberOfCPUs()))); - return; - } - - Mat kx, ky; createGaussianKernels(kx, ky, type, ksize, sigma1, sigma2); @@ -4145,6 +4131,17 @@ void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize, CV_IPP_RUN_FAST(ipp_GaussianBlur(src, dst, ksize, sigma1, sigma2, borderType)); + if(sdepth == CV_8U && ((borderType & BORDER_ISOLATED) || !_src.getMat().isSubmatrix())) + { + std::vector fkx, fky; + createGaussianKernels(fkx, fky, type, ksize, sigma1, sigma2); + if (src.data == dst.data) + src = src.clone(); + fixedSmoothInvoker invoker(src.ptr(), src.step1(), dst.ptr(), dst.step1(), dst.cols, dst.rows, dst.channels(), &fkx[0], (int)fkx.size(), &fky[0], (int)fky.size(), borderType & ~BORDER_ISOLATED); + parallel_for_(Range(0, dst.rows), invoker, std::max(1, std::min(getNumThreads(), getNumberOfCPUs()))); + return; + } + sepFilter2D(src, dst, sdepth, kx, ky, Point(-1, -1), 0, borderType); } From d71812425a5642785e5af6f0e1a44af3f04df809 Mon Sep 17 00:00:00 2001 From: LaurentBerger Date: Mon, 3 Sep 2018 19:30:49 +0200 Subject: [PATCH 39/47] Solves issue 12392 --- modules/core/src/rand.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/core/src/rand.cpp b/modules/core/src/rand.cpp index cc46345ecd..e791fd131b 100644 --- a/modules/core/src/rand.cpp +++ b/modules/core/src/rand.cpp @@ -584,6 +584,11 @@ void RNG::fill( InputOutputArray _mat, int disttype, } ip[j][1] = cvCeil(a); int idiff = ip[j][0] = cvFloor(b) - ip[j][1] - 1; + if (idiff < 0) + { + idiff = 0; + ip[j][0] = 0; + } double diff = b - a; fast_int_mode = fast_int_mode && diff <= 4294967296. && (idiff & (idiff+1)) == 0; From f9a5c4d181b7ab1bf64546298913305e881c3d5a Mon Sep 17 00:00:00 2001 From: Vitaly Tuzov Date: Mon, 3 Sep 2018 19:57:11 +0300 Subject: [PATCH 40/47] Fixed bit-exact resize wide intrinsics implementation for 16U --- modules/imgproc/src/resize.cpp | 40 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index 2ff84753f7..6b80415999 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -381,7 +381,7 @@ inline void v_load_indexed4(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 v_zip(v_tmp0, v_tmp1, v_tmp2, v_tmp3); v_zip(v_reinterpret_as_u16(v_tmp2), v_reinterpret_as_u16(v_tmp3), v_src0, v_src1); } -inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) +inline void v_load_indexed_deinterleave(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) { v_expand(v_reinterpret_as_u16(v_uint32( *((uint32_t*)(src + ofst[ 0])), *((uint32_t*)(src + ofst[ 1])), *((uint32_t*)(src + ofst[ 2])), *((uint32_t*)(src + ofst[ 3])), @@ -389,6 +389,11 @@ inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 *((uint32_t*)(src + ofst[ 8])), *((uint32_t*)(src + ofst[ 9])), *((uint32_t*)(src + ofst[10])), *((uint32_t*)(src + ofst[11])), *((uint32_t*)(src + ofst[12])), *((uint32_t*)(src + ofst[13])), *((uint32_t*)(src + ofst[14])), *((uint32_t*)(src + ofst[15])))), v_src0, v_src1); + v_uint32 v_tmp0, v_tmp1; + v_zip(v_src0, v_src1, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_src0, v_src1); + v_zip(v_src0, v_src1, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_src0, v_src1); } #elif CV_SIMD256 inline void v_load_indexed1(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) @@ -422,12 +427,16 @@ inline void v_load_indexed4(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 v_zip(v_tmp2, v_tmp3, v_tmp0, v_tmp1); v_zip(v_reinterpret_as_u16(v_tmp0), v_reinterpret_as_u16(v_tmp1), v_src0, v_src1); } -inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) +inline void v_load_indexed_deinterleave(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) { + v_uint32 v_tmp0, v_tmp1; v_expand(v_reinterpret_as_u16(v_uint32( *((uint32_t*)(src + ofst[0])), *((uint32_t*)(src + ofst[1])), *((uint32_t*)(src + ofst[2])), *((uint32_t*)(src + ofst[3])), *((uint32_t*)(src + ofst[4])), *((uint32_t*)(src + ofst[5])), *((uint32_t*)(src + ofst[6])), *((uint32_t*)(src + ofst[7])))), - v_src0, v_src1); + v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_src0, v_src1); + v_zip(v_src0, v_src1, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_src0, v_src1); } #elif CV_SIMD128 inline void v_load_indexed1(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 &v_src1) @@ -466,7 +475,7 @@ inline void v_load_indexed4(uint8_t* src, int *ofst, v_uint16 &v_src0, v_uint16 v_recombine(v_src0, v_src1, v_tmp0, v_tmp1); v_zip(v_tmp0, v_tmp1, v_src0, v_src1); } -inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) +inline void v_load_indexed_deinterleave(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 &v_src1) { uint32_t buf[4]; buf[0] = *((uint32_t*)(src + ofst[0])); @@ -475,6 +484,9 @@ inline void v_load_indexed1(uint16_t* src, int *ofst, v_uint32 &v_src0, v_uint32 buf[3] = *((uint32_t*)(src + ofst[3])); v_src0 = vx_load_expand((uint16_t*)buf); v_src1 = vx_load_expand((uint16_t*)buf + 4); + v_uint32 v_tmp0, v_tmp1; + v_zip(v_src0, v_src1, v_tmp0, v_tmp1); + v_zip(v_tmp0, v_tmp1, v_src0, v_src1); } #endif template <> @@ -617,7 +629,7 @@ void hlineResizeCn(uint8_t* src, int, int *o { v_uint16 v_src0, v_src1, v_src2, v_src3; v_load_indexed4(src, ofst + i, v_src0, v_src1); - v_load_indexed4(src, ofst + i + 2, v_src2, v_src3); + v_load_indexed4(src, ofst + i + VECSZ/4, v_src2, v_src3); v_uint32 v_mul0, v_mul1, v_mul2, v_mul3, v_tmp; v_mul0 = vx_load((uint32_t*)m);//AaBbCcDd @@ -680,18 +692,10 @@ void hlineResizeCn(uint16_t* src, int, int for (; i <= dst_max - VECSZ; i += VECSZ, m += 2*VECSZ, dst += VECSZ) { v_uint32 v_src0, v_src1; - v_load_indexed1(src, ofst + i, v_src0, v_src1); - - v_uint32 v_mul0 = vx_load((uint32_t*)m); - v_uint32 v_mul1 = vx_load((uint32_t*)m + 4); - - v_uint32 v_res0 = v_src0 * v_mul0;//a1a2b1b2 - v_uint32 v_res1 = v_src1 * v_mul1;//c1c2d1d2 - v_uint32 v_tmp0, v_tmp1; - v_recombine(v_res0, v_res1, v_tmp0, v_tmp1);//a1a2c1c2 b1b2d1d2 - v_zip(v_tmp0, v_tmp1, v_res0, v_res1);//a1b1a2b2 c1d1c2d2 - v_recombine(v_res0, v_res1, v_tmp0, v_tmp1);//a1b1c1d1 a2b2c2d2 - v_store((uint32_t*)dst, v_tmp0 + v_tmp1);//abcd + v_load_indexed_deinterleave(src, ofst + i, v_src0, v_src1); + v_uint32 v_mul0, v_mul1; + v_load_deinterleave((uint32_t*)m, v_mul0, v_mul1); + v_store((uint32_t*)dst, v_src0 * v_mul0 + v_src1 * v_mul1);//abcd } #endif for (; i < dst_max; i += 1, m += 2) @@ -702,7 +706,7 @@ void hlineResizeCn(uint16_t* src, int, int src_0 = (src + ofst[dst_width - 1])[0]; #if CV_SIMD v_src_0 = vx_setall_u32(*((uint32_t*)&src_0)); - for (; i < dst_width - 3; i += 4, dst += 4) + for (; i <= dst_width - VECSZ; i += VECSZ, dst += VECSZ) { v_store((uint32_t*)dst, v_src_0); } From a0f86479e0a6b7648a9296d169841719ad6292ee Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 2 Sep 2018 01:04:31 +0000 Subject: [PATCH 41/47] core: wrap custom types via _RawArray (raw() call) - support passing of `std::vector` via InputArray --- modules/core/include/opencv2/core/mat.hpp | 28 +++++++ modules/core/include/opencv2/core/mat.inl.hpp | 78 +++++++++++++++++++ modules/core/src/matrix_wrap.cpp | 21 +++++ modules/core/test/test_mat.cpp | 59 ++++++++++++++ 4 files changed, 186 insertions(+) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index a7d3021857..c0893b398d 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -148,6 +148,12 @@ synonym is needed to generate Python/Java etc. wrappers properly. At the functio level their use is similar, but _InputArray::getMat(idx) should be used to get header for the idx-th component of the outer vector and _InputArray::size().area() should be used to find the number of components (vectors/matrices) of the outer vector. + +In general, type support is limited to cv::Mat types. Other types are forbidden. +But in some cases we need to support passing of custom non-general Mat types, like arrays of cv::KeyPoint, cv::DMatch, etc. +This data is not intented to be interpreted as an image data, or processed somehow like regular cv::Mat. +To pass such custom type use rawIn() / rawOut() / rawInOut() wrappers. +Custom type is wrapped as Mat-compatible `CV_8UC` values (N = sizeof(T), N <= CV_CN_MAX). */ class CV_EXPORTS _InputArray { @@ -203,6 +209,11 @@ public: template _InputArray(const std::array& arr); #endif + template static _InputArray rawIn(const std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template static _InputArray rawIn(const std::array<_Tp, _Nm>& arr); +#endif + Mat getMat(int idx=-1) const; Mat getMat_(int idx=-1) const; UMat getUMat(int idx=-1) const; @@ -339,6 +350,11 @@ public: template _OutputArray(const std::array& arr); #endif + template static _OutputArray rawOut(std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template static _OutputArray rawOut(std::array<_Tp, _Nm>& arr); +#endif + bool fixedSize() const; bool fixedType() const; bool needed() const; @@ -408,8 +424,20 @@ public: template _InputOutputArray(const std::array& arr); #endif + template static _InputOutputArray rawInOut(std::vector<_Tp>& vec); +#ifdef CV_CXX_STD_ARRAY + template _InputOutputArray rawInOut(std::array<_Tp, _Nm>& arr); +#endif + }; +/** Helper to wrap custom types. @see InputArray */ +template static inline _InputArray rawIn(_Tp& v); +/** Helper to wrap custom types. @see InputArray */ +template static inline _OutputArray rawOut(_Tp& v); +/** Helper to wrap custom types. @see InputArray */ +template static inline _InputOutputArray rawInOut(_Tp& v); + CV__DEBUG_NS_END typedef const _InputArray& InputArray; diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index 2e9b57ecce..b729be051a 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -61,6 +61,16 @@ CV__DEBUG_NS_BEGIN //! @cond IGNORED +////////////////////////// Custom (raw) type wrapper ////////////////////////// + +template static inline +int rawType() +{ + CV_StaticAssert(sizeof(_Tp) <= CV_CN_MAX, "sizeof(_Tp) is too large"); + const int elemSize = sizeof(_Tp); + return (int)CV_MAKETYPE(CV_8U, elemSize); +} + //////////////////////// Input/Output Arrays //////////////////////// inline void _InputArray::init(int _flags, const void* _obj) @@ -140,6 +150,27 @@ inline _InputArray::_InputArray(const ogl::Buffer& buf) inline _InputArray::_InputArray(const cuda::HostMem& cuda_mem) { init(CUDA_HOST_MEM + ACCESS_READ, &cuda_mem); } +template inline +_InputArray _InputArray::rawIn(const std::vector<_Tp>& vec) +{ + _InputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_READ; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputArray _InputArray::rawIn(const std::array<_Tp, _Nm>& arr) +{ + _InputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_READ; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + inline _InputArray::~_InputArray() {} inline Mat _InputArray::getMat(int i) const @@ -279,6 +310,27 @@ inline _OutputArray::_OutputArray(const ogl::Buffer& buf) inline _OutputArray::_OutputArray(const cuda::HostMem& cuda_mem) { init(FIXED_TYPE + FIXED_SIZE + CUDA_HOST_MEM + ACCESS_WRITE, &cuda_mem); } +template inline +_OutputArray _OutputArray::rawOut(std::vector<_Tp>& vec) +{ + _OutputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_WRITE; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_OutputArray _OutputArray::rawOut(std::array<_Tp, _Nm>& arr) +{ + _OutputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_WRITE; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + /////////////////////////////////////////////////////////////////////////////////////////// inline _InputOutputArray::_InputOutputArray() { init(ACCESS_RW, 0); } @@ -395,6 +447,32 @@ inline _InputOutputArray::_InputOutputArray(const ogl::Buffer& buf) inline _InputOutputArray::_InputOutputArray(const cuda::HostMem& cuda_mem) { init(FIXED_TYPE + FIXED_SIZE + CUDA_HOST_MEM + ACCESS_RW, &cuda_mem); } +template inline +_InputOutputArray _InputOutputArray::rawInOut(std::vector<_Tp>& vec) +{ + _InputOutputArray v; + v.flags = _InputArray::FIXED_TYPE + _InputArray::STD_VECTOR + rawType<_Tp>() + ACCESS_RW; + v.obj = (void*)&vec; + return v; +} + +#ifdef CV_CXX_STD_ARRAY +template inline +_InputOutputArray _InputOutputArray::rawInOut(std::array<_Tp, _Nm>& arr) +{ + _InputOutputArray v; + v.flags = FIXED_TYPE + FIXED_SIZE + STD_ARRAY + traits::Type<_Tp>::value + ACCESS_RW; + v.obj = (void*)arr.data(); + v.sz = Size(1, _Nm); + return v; +} +#endif + + +template static inline _InputArray rawIn(_Tp& v) { return _InputArray::rawIn(v); } +template static inline _OutputArray rawOut(_Tp& v) { return _OutputArray::rawOut(v); } +template static inline _InputOutputArray rawInOut(_Tp& v) { return _InputOutputArray::rawInOut(v); } + CV__DEBUG_NS_END //////////////////////////////////////////// Mat ////////////////////////////////////////// diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index bea5c670b6..f52f301151 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -1413,18 +1413,39 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, case 16: ((std::vector*)v)->resize(len); break; + case 20: + ((std::vector >*)v)->resize(len); + break; case 24: ((std::vector*)v)->resize(len); break; + case 28: + ((std::vector >*)v)->resize(len); + break; case 32: ((std::vector*)v)->resize(len); break; case 36: ((std::vector >*)v)->resize(len); break; + case 40: + ((std::vector >*)v)->resize(len); + break; + case 44: + ((std::vector >*)v)->resize(len); + break; case 48: ((std::vector >*)v)->resize(len); break; + case 52: + ((std::vector >*)v)->resize(len); + break; + case 56: + ((std::vector >*)v)->resize(len); + break; + case 60: + ((std::vector >*)v)->resize(len); + break; case 64: ((std::vector >*)v)->resize(len); break; diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 9622fa47d4..0ef0d3fc2a 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1882,4 +1882,63 @@ TEST(Core_Split, crash_12171) EXPECT_EQ(2, dst2.ptr(1)[1]); } +struct CustomType // like cv::Keypoint +{ + Point2f pt; + float size; + float angle; + float response; + int octave; + int class_id; +}; + +static void test_CustomType(InputArray src_, OutputArray dst_) +{ + Mat src = src_.getMat(); + ASSERT_EQ(sizeof(CustomType), src.elemSize()); + CV_CheckTypeEQ(src.type(), CV_MAKETYPE(CV_8U, sizeof(CustomType)), ""); + + CustomType* kpt = NULL; + { + Mat dst = dst_.getMat(); + for (size_t i = 0; i < dst.total(); i++) + { + kpt = dst.ptr(0) + i; + kpt->octave = (int)i; + } + } + const int N = (int)src.total(); + dst_.create(1, N * 2, rawType()); + Mat dst = dst_.getMat(); + for (size_t i = N; i < dst.total(); i++) + { + kpt = dst.ptr(0) + i; + kpt->octave = -(int)i; + } +#if 0 // Compilation error + CustomType& kpt = dst.at(0, 5); +#endif +} + +TEST(Core_InputArray, support_CustomType) +{ + std::vector kp1(5); + std::vector kp2(3); + test_CustomType(rawIn(kp1), rawOut(kp2)); + ASSERT_EQ((size_t)10, kp2.size()); + for (int i = 0; i < 3; i++) + { + EXPECT_EQ(i, kp2[i].octave); + } + for (int i = 3; i < 5; i++) + { + EXPECT_EQ(0, kp2[i].octave); + } + for (int i = 5; i < 10; i++) + { + EXPECT_EQ(-i, kp2[i].octave); + } +} + + }} // namespace From 00cbb894ec1ca44bf869ea79521cb815235e6a2e Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 2 Sep 2018 03:31:05 +0000 Subject: [PATCH 42/47] CUDA: drop OPENCV_TRAITS_ENABLE_DEPRECATED requirement --- modules/core/CMakeLists.txt | 4 ---- modules/cudafeatures2d/test/test_features2d.cpp | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index fa72e71658..db13a0027b 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -77,10 +77,6 @@ ocv_target_link_libraries(${the_module} LINK_PRIVATE "${OPENCV_HAL_LINKER_LIBS}" ) -if(HAVE_CUDA) - ocv_target_compile_definitions(${the_module} PUBLIC OPENCV_TRAITS_ENABLE_DEPRECATED) -endif() - ocv_add_accuracy_tests() ocv_add_perf_tests() diff --git a/modules/cudafeatures2d/test/test_features2d.cpp b/modules/cudafeatures2d/test/test_features2d.cpp index 6ab23beaff..787b4fb345 100644 --- a/modules/cudafeatures2d/test/test_features2d.cpp +++ b/modules/cudafeatures2d/test/test_features2d.cpp @@ -222,7 +222,7 @@ CUDA_TEST_P(ORB, Accuracy) { std::vector keypoints; cv::cuda::GpuMat descriptors; - orb->detectAndComputeAsync(loadMat(image), loadMat(mask), keypoints, descriptors); + orb->detectAndComputeAsync(loadMat(image), loadMat(mask), rawOut(keypoints), descriptors); } catch (const cv::Exception& e) { From 2cf34c0fe5493b857f07d86b857a22f72acc8a29 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 3 Sep 2018 23:11:25 +0000 Subject: [PATCH 43/47] dnn: fix tests build with disabled OpenCL --- modules/dnn/test/test_common.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index e5fb5859b6..2df422b759 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -246,19 +246,23 @@ static testing::internal::ParamGenerator > dnnBackendsAnd if (withHalide) { targets.push_back(make_tuple(DNN_BACKEND_HALIDE, DNN_TARGET_CPU)); +#ifdef HAVE_OPENCL if (cv::ocl::useOpenCL()) targets.push_back(make_tuple(DNN_BACKEND_HALIDE, DNN_TARGET_OPENCL)); +#endif } #endif #ifdef HAVE_INF_ENGINE if (withInferenceEngine) { targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU)); +#ifdef HAVE_OPENCL if (cv::ocl::useOpenCL()) { targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL)); targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16)); } +#endif if (checkMyriadTarget()) targets.push_back(make_tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD)); } From afb81ba6e6ef7092bcbec33ef950b7e8bfd6fbbe Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 3 Sep 2018 18:56:04 +0000 Subject: [PATCH 44/47] samples: eliminate cvconfig.h usage - don't use TBB in samples --- samples/gpu/CMakeLists.txt | 3 ++ samples/gpu/cascadeclassifier_nvidia_api.cpp | 1 - samples/gpu/multi.cpp | 32 ++++++++------------ samples/gpu/opticalflow_nvidia_api.cpp | 1 - samples/va_intel/display.cpp.inc | 2 -- samples/va_intel/va_intel_interop.cpp | 13 -------- 6 files changed, 15 insertions(+), 37 deletions(-) diff --git a/samples/gpu/CMakeLists.txt b/samples/gpu/CMakeLists.txt index 0a3a2d0531..1c65145aca 100644 --- a/samples/gpu/CMakeLists.txt +++ b/samples/gpu/CMakeLists.txt @@ -30,6 +30,9 @@ if(NOT BUILD_EXAMPLES OR NOT OCV_DEPENDENCIES_FOUND) endif() project(gpu_samples) +if(HAVE_CUDA OR CUDA_FOUND) + add_definitions(-DHAVE_CUDA=1) +endif() if(COMMAND ocv_warnings_disable) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wsuggest-override -Winconsistent-missing-override) endif() diff --git a/samples/gpu/cascadeclassifier_nvidia_api.cpp b/samples/gpu/cascadeclassifier_nvidia_api.cpp index f3d58cae5a..c932411eda 100644 --- a/samples/gpu/cascadeclassifier_nvidia_api.cpp +++ b/samples/gpu/cascadeclassifier_nvidia_api.cpp @@ -2,7 +2,6 @@ #pragma warning( disable : 4201 4408 4127 4100) #endif -#include "opencv2/cvconfig.h" #include #include #include diff --git a/samples/gpu/multi.cpp b/samples/gpu/multi.cpp index c3aac72d50..c3ef9b9641 100644 --- a/samples/gpu/multi.cpp +++ b/samples/gpu/multi.cpp @@ -7,29 +7,14 @@ #endif #include -#include "opencv2/cvconfig.h" #include "opencv2/core.hpp" #include "opencv2/cudaarithm.hpp" -#ifdef HAVE_TBB -# include "tbb/tbb.h" -# include "tbb/task.h" -# undef min -# undef max -#endif - -#if !defined(HAVE_CUDA) || !defined(HAVE_TBB) +#if !defined(HAVE_CUDA) int main() { -#if !defined(HAVE_CUDA) - std::cout << "CUDA support is required (CMake key 'WITH_CUDA' must be true).\n"; -#endif - -#if !defined(HAVE_TBB) - std::cout << "TBB support is required (CMake key 'WITH_TBB' must be true).\n"; -#endif - + std::cout << "CUDA support is required (OpenCV CMake parameter 'WITH_CUDA' must be true)." << std::endl; return 0; } @@ -39,7 +24,14 @@ using namespace std; using namespace cv; using namespace cv::cuda; -struct Worker { void operator()(int device_id) const; }; +struct Worker : public cv::ParallelLoopBody +{ + void operator()(const Range& r) const CV_OVERRIDE + { + for (int i = r.start; i < r.end; ++i) { this->operator()(i); } + } + void operator()(int device_id) const; +}; int main() { @@ -64,8 +56,8 @@ int main() } // Execute calculation in two threads using two GPUs - int devices[] = {0, 1}; - tbb::parallel_do(devices, devices + 2, Worker()); + cv::Range devices(0, 2); + cv::parallel_for_(devices, Worker(), devices.size()); return 0; } diff --git a/samples/gpu/opticalflow_nvidia_api.cpp b/samples/gpu/opticalflow_nvidia_api.cpp index a60a7a8b46..0d924ec85e 100644 --- a/samples/gpu/opticalflow_nvidia_api.cpp +++ b/samples/gpu/opticalflow_nvidia_api.cpp @@ -9,7 +9,6 @@ #include #include -#include "cvconfig.h" #include #include #include "opencv2/core/cuda.hpp" diff --git a/samples/va_intel/display.cpp.inc b/samples/va_intel/display.cpp.inc index 56e71d26e2..6fd3e0cbf0 100644 --- a/samples/va_intel/display.cpp.inc +++ b/samples/va_intel/display.cpp.inc @@ -7,8 +7,6 @@ #include #include -#include "cvconfig.h" - #include # include diff --git a/samples/va_intel/va_intel_interop.cpp b/samples/va_intel/va_intel_interop.cpp index 8fa2c54f6f..770e9dbf41 100644 --- a/samples/va_intel/va_intel_interop.cpp +++ b/samples/va_intel/va_intel_interop.cpp @@ -45,7 +45,6 @@ #include "opencv2/imgproc.hpp" #include "opencv2/highgui.hpp" #include "opencv2/core/va_intel.hpp" -#include "cvconfig.h" #define CHECK_VASTATUS(_status,_func) \ if (_status != VA_STATUS_SUCCESS) \ @@ -65,15 +64,9 @@ public: void usage() { fprintf(stderr, -#if defined(HAVE_VA_INTEL) "Usage: va_intel_interop [-f] infile outfile1 outfile2\n\n" "Interop ON/OFF version\n\n" "where: -f option indicates interop is off (fallback mode); interop is on by default\n" -#elif defined(HAVE_VA) - "Usage: va_intel_interop infile outfile1 outfile2\n\n" - "Interop OFF only version\n\n" - "where:\n" -#endif //HAVE_VA_INTEL / HAVE_VA " infile is to be existing, contains input image data (bmp, jpg, png, tiff, etc)\n" " outfile1 is to be created, contains original surface data (NV12)\n" " outfile2 is to be created, contains processed surface data (NV12)\n"); @@ -84,20 +77,14 @@ public: int n = 0; for (int i = 0; i < _fnNumFiles; ++i) m_files[i] = 0; -#if defined(HAVE_VA_INTEL) m_interop = true; -#elif defined(HAVE_VA) - m_interop = false; -#endif //HAVE_VA_INTEL / HAVE_VA for (int i = 1; i < m_argc; ++i) { const char *arg = m_argv[i]; if (arg[0] == '-') // option { -#if defined(HAVE_VA_INTEL) if (!strcmp(arg, "-f")) m_interop = false; -#endif //HAVE_VA_INTEL } else // parameter { From 9988e1b6ee2d593c8efe5551de897c0c06d4f450 Mon Sep 17 00:00:00 2001 From: Todor Tomov Date: Tue, 4 Sep 2018 11:33:08 +0300 Subject: [PATCH 45/47] cap_v4l: Fix private control enumeration end condition Currently the private control enumeration will be stopped when QUERYCTRL returns -EINVAL only. It is possible however that other errors occur. One particular case is when the v4l2 device doesn't support any controls and doesn't implement the QUERYCTRL ioctl. In that case the v4l2 framework returns -ENOTTY. In that case the current control enumeration will go in an endless loop. To fix this change the control enumeration stop condition. If any errors occur, end the control enumeration. Signed-off-by: Todor Tomov --- modules/videoio/src/cap_v4l.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index 0416231f65..1650093e7f 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -512,9 +512,11 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) for (ctrl_id = V4L2_CID_PRIVATE_BASE;;ctrl_id++) { + errno = 0; + v4l2_control_range(capture, ctrl_id); - if (errno == EINVAL) + if (errno) break; } From acce95f446667e20912246d56d3b02a38cf53980 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 4 Sep 2018 16:44:47 +0300 Subject: [PATCH 46/47] backport fixes for static analyzer warnings Commits: - 09837928d934d104b3f327ce42122b7b8f35fff3 - 10fb88d02791b33d83a3756c62e21aa1c5a1e68d Excluded changes with std::atomic (C++98 requirement) --- modules/core/include/opencv2/core/mat.inl.hpp | 2 +- modules/core/src/arithm.cpp | 14 +++++++------- modules/core/src/convert.cpp | 4 ++-- modules/core/src/convert_scale.cpp | 2 +- modules/core/src/copy.cpp | 4 ++-- modules/core/src/count_non_zero.cpp | 2 +- modules/core/src/lut.cpp | 4 ++-- modules/core/src/mathfuncs.cpp | 16 ++++++++-------- modules/core/src/matmul.cpp | 8 ++++---- modules/core/src/matrix_wrap.cpp | 4 ++-- modules/core/src/mean.cpp | 4 ++-- modules/core/src/minmax.cpp | 2 +- modules/core/src/norm.cpp | 8 ++++---- modules/core/src/persistence_json.cpp | 4 ++-- modules/core/src/sum.cpp | 2 +- modules/cudaarithm/src/arithm.cpp | 3 +-- modules/cudaimgproc/src/canny.cpp | 1 + modules/imgcodecs/src/grfmt_jpeg2000.cpp | 2 +- modules/imgcodecs/src/grfmt_png.cpp | 2 +- modules/imgcodecs/src/grfmt_pxm.cpp | 4 ++-- modules/imgcodecs/src/grfmt_sunras.cpp | 2 +- modules/imgproc/src/accum.cpp | 8 ++++---- modules/imgproc/src/linefit.cpp | 2 +- 23 files changed, 52 insertions(+), 52 deletions(-) diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index b729be051a..a2e7923b39 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -1026,7 +1026,7 @@ _Tp* Mat::ptr(int y) template inline const _Tp* Mat::ptr(int y) const { - CV_DbgAssert( y == 0 || (data && dims >= 1 && data && (unsigned)y < (unsigned)size.p[0]) ); + CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) ); return (const _Tp*)(data + step.p[0] * y); } diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index dbfcc5c727..e751fe01bb 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -270,7 +270,7 @@ static void binary_op( InputArray _src1, InputArray _src2, OutputArray _dst, if( !haveScalar ) { const Mat* arrays[] = { &src1, &src2, &dst, &mask, 0 }; - uchar* ptrs[4]; + uchar* ptrs[4] = {}; NAryMatIterator it(arrays, ptrs); size_t total = it.size, blocksize = total; @@ -306,7 +306,7 @@ static void binary_op( InputArray _src1, InputArray _src2, OutputArray _dst, else { const Mat* arrays[] = { &src1, &dst, &mask, 0 }; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); size_t total = it.size, blocksize = std::min(total, blocksize0); @@ -745,7 +745,7 @@ static void arithm_op(InputArray _src1, InputArray _src2, OutputArray _dst, if( !haveScalar ) { const Mat* arrays[] = { &src1, &src2, &dst, &mask, 0 }; - uchar* ptrs[4]; + uchar* ptrs[4] = {}; NAryMatIterator it(arrays, ptrs); size_t total = it.size, blocksize = total; @@ -812,7 +812,7 @@ static void arithm_op(InputArray _src1, InputArray _src2, OutputArray _dst, else { const Mat* arrays[] = { &src1, &dst, &mask, 0 }; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); size_t total = it.size, blocksize = std::min(total, blocksize0); @@ -1293,7 +1293,7 @@ void cv::compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op) if( !haveScalar ) { const Mat* arrays[] = { &src1, &src2, &dst, 0 }; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); size_t total = it.size; @@ -1304,7 +1304,7 @@ void cv::compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op) else { const Mat* arrays[] = { &src1, &dst, 0 }; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); size_t total = it.size, blocksize = std::min(total, blocksize0); @@ -1801,7 +1801,7 @@ void cv::inRange(InputArray _src, InputArray _lowerb, const Mat* arrays_sc[] = { &src, &dst, 0 }; const Mat* arrays_nosc[] = { &src, &dst, &lb, &ub, 0 }; - uchar* ptrs[4]; + uchar* ptrs[4] = {}; NAryMatIterator it(lbScalar && ubScalar ? arrays_sc : arrays_nosc, ptrs); size_t total = it.size, blocksize = std::min(total, blocksize0); diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index e5fd24dfad..75b4967194 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -1347,7 +1347,7 @@ void cv::Mat::convertTo(OutputArray _dst, int _type, double alpha, double beta) else { const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); Size sz((int)(it.size*cn), 1); @@ -1496,7 +1496,7 @@ void cv::convertFp16( InputArray _src, OutputArray _dst) else { const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); Size sz((int)(it.size*cn), 1); diff --git a/modules/core/src/convert_scale.cpp b/modules/core/src/convert_scale.cpp index ff8398a27b..25f5a963b7 100644 --- a/modules/core/src/convert_scale.cpp +++ b/modules/core/src/convert_scale.cpp @@ -1775,7 +1775,7 @@ void cv::convertScaleAbs( InputArray _src, OutputArray _dst, double alpha, doubl else { const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); Size sz((int)it.size*cn, 1); diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index e89a17b323..8f93d4bb72 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -306,7 +306,7 @@ void Mat::copyTo( OutputArray _dst ) const if( total() != 0 ) { const Mat* arrays[] = { this, &dst }; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs, 2); size_t sz = it.size*elemSize(); @@ -399,7 +399,7 @@ void Mat::copyTo( OutputArray _dst, InputArray _mask ) const } const Mat* arrays[] = { this, &dst, &mask, 0 }; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); Size sz((int)(it.size*mcn), 1); diff --git a/modules/core/src/count_non_zero.cpp b/modules/core/src/count_non_zero.cpp index 142a4983c1..4a2660ec65 100644 --- a/modules/core/src/count_non_zero.cpp +++ b/modules/core/src/count_non_zero.cpp @@ -314,7 +314,7 @@ int cv::countNonZero( InputArray _src ) CV_Assert( func != 0 ); const Mat* arrays[] = {&src, 0}; - uchar* ptrs[1]; + uchar* ptrs[1] = {}; NAryMatIterator it(arrays, ptrs); int total = (int)it.size, nz = 0; diff --git a/modules/core/src/lut.cpp b/modules/core/src/lut.cpp index 71f06ea5b2..3e91bd9fcb 100644 --- a/modules/core/src/lut.cpp +++ b/modules/core/src/lut.cpp @@ -342,7 +342,7 @@ public: int lutcn = lut_.channels(); const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)it.size; @@ -408,7 +408,7 @@ void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst ) CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)it.size; diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index 5737e8d0b3..1bdac5ec98 100644 --- a/modules/core/src/mathfuncs.cpp +++ b/modules/core/src/mathfuncs.cpp @@ -158,7 +158,7 @@ void magnitude( InputArray src1, InputArray src2, OutputArray dst ) Mat Mag = dst.getMat(); const Mat* arrays[] = {&X, &Y, &Mag, 0}; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)it.size*cn; @@ -194,7 +194,7 @@ void phase( InputArray src1, InputArray src2, OutputArray dst, bool angleInDegre Mat Angle = dst.getMat(); const Mat* arrays[] = {&X, &Y, &Angle, 0}; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); int j, total = (int)(it.size*cn), blockSize = total; size_t esz1 = X.elemSize1(); @@ -280,7 +280,7 @@ void cartToPolar( InputArray src1, InputArray src2, Mat Mag = dst1.getMat(), Angle = dst2.getMat(); const Mat* arrays[] = {&X, &Y, &Mag, &Angle, 0}; - uchar* ptrs[4]; + uchar* ptrs[4] = {}; NAryMatIterator it(arrays, ptrs); int j, total = (int)(it.size*cn), blockSize = std::min(total, ((BLOCK_SIZE+cn-1)/cn)*cn); size_t esz1 = X.elemSize1(); @@ -577,7 +577,7 @@ void polarToCart( InputArray src1, InputArray src2, CV_IPP_RUN(!angleInDegrees, ipp_polarToCart(Mag, Angle, X, Y)); const Mat* arrays[] = {&Mag, &Angle, &X, &Y, 0}; - uchar* ptrs[4]; + uchar* ptrs[4] = {}; NAryMatIterator it(arrays, ptrs); cv::AutoBuffer _buf; float* buf[2] = {0, 0}; @@ -676,7 +676,7 @@ void exp( InputArray _src, OutputArray _dst ) Mat dst = _dst.getMat(); const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)(it.size*cn); @@ -709,7 +709,7 @@ void log( InputArray _src, OutputArray _dst ) Mat dst = _dst.getMat(); const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)(it.size*cn); @@ -1241,7 +1241,7 @@ void pow( InputArray _src, double power, OutputArray _dst ) Mat dst = _dst.getMat(); const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)(it.size*cn); @@ -1588,7 +1588,7 @@ void patchNaNs( InputOutputArray _a, double _val ) Mat a = _a.getMat(); const Mat* arrays[] = {&a, 0}; - int* ptrs[1]; + int* ptrs[1] = {}; NAryMatIterator it(arrays, (uchar**)ptrs); size_t len = it.size*a.channels(); Cv32suf val; diff --git a/modules/core/src/matmul.cpp b/modules/core/src/matmul.cpp index 83607c7184..676b390ce0 100644 --- a/modules/core/src/matmul.cpp +++ b/modules/core/src/matmul.cpp @@ -2144,7 +2144,7 @@ void cv::transform( InputArray _src, OutputArray _dst, InputArray _mtx ) CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); size_t i, total = it.size; @@ -2290,7 +2290,7 @@ void cv::perspectiveTransform( InputArray _src, OutputArray _dst, InputArray _mt CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &dst, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); size_t i, total = it.size; @@ -2441,7 +2441,7 @@ void cv::scaleAdd( InputArray _src1, double alpha, InputArray _src2, OutputArray } const Mat* arrays[] = {&src1, &src2, &dst, 0}; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); size_t i, len = it.size*cn; @@ -3301,7 +3301,7 @@ double Mat::dot(InputArray _mat) const } const Mat* arrays[] = {this, &mat, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)(it.size*cn); double r = 0; diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index f52f301151..b5b4514ada 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -939,7 +939,7 @@ bool _InputArray::isContinuous(int i) const if( k == STD_VECTOR_MAT ) { const std::vector& vv = *(const std::vector*)obj; - CV_Assert((size_t)i < vv.size()); + CV_Assert(i >= 0 && (size_t)i < vv.size()); return vv[i].isContinuous(); } @@ -953,7 +953,7 @@ bool _InputArray::isContinuous(int i) const if( k == STD_VECTOR_UMAT ) { const std::vector& vv = *(const std::vector*)obj; - CV_Assert((size_t)i < vv.size()); + CV_Assert(i >= 0 && (size_t)i < vv.size()); return vv[i].isContinuous(); } diff --git a/modules/core/src/mean.cpp b/modules/core/src/mean.cpp index dcf1ae206c..30959f3bf9 100644 --- a/modules/core/src/mean.cpp +++ b/modules/core/src/mean.cpp @@ -121,7 +121,7 @@ cv::Scalar cv::mean( InputArray _src, InputArray _mask ) CV_Assert( cn <= 4 && func != 0 ); const Mat* arrays[] = {&src, &mask, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int total = (int)it.size, blockSize = total, intSumBlockSize = 0; int j, count = 0; @@ -786,7 +786,7 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &mask, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int total = (int)it.size, blockSize = total, intSumBlockSize = 0; int j, count = 0, nz0 = 0; diff --git a/modules/core/src/minmax.cpp b/modules/core/src/minmax.cpp index d2b56646b8..4276b22634 100644 --- a/modules/core/src/minmax.cpp +++ b/modules/core/src/minmax.cpp @@ -770,7 +770,7 @@ void cv::minMaxIdx(InputArray _src, double* minVal, CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &mask, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); size_t minidx = 0, maxidx = 0; diff --git a/modules/core/src/norm.cpp b/modules/core/src/norm.cpp index d12dfc742d..8f262291f9 100644 --- a/modules/core/src/norm.cpp +++ b/modules/core/src/norm.cpp @@ -710,7 +710,7 @@ double cv::norm( InputArray _src, int normType, InputArray _mask ) int cellSize = normType == NORM_HAMMING ? 1 : 2; const Mat* arrays[] = {&src, 0}; - uchar* ptrs[1]; + uchar* ptrs[1] = {}; NAryMatIterator it(arrays, ptrs); int total = (int)it.size; int result = 0; @@ -727,7 +727,7 @@ double cv::norm( InputArray _src, int normType, InputArray _mask ) CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &mask, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; union { double d; @@ -1168,7 +1168,7 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m int cellSize = normType == NORM_HAMMING ? 1 : 2; const Mat* arrays[] = {&src1, &src2, 0}; - uchar* ptrs[2]; + uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int total = (int)it.size; int result = 0; @@ -1185,7 +1185,7 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m CV_Assert( func != 0 ); const Mat* arrays[] = {&src1, &src2, &mask, 0}; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; union { double d; diff --git a/modules/core/src/persistence_json.cpp b/modules/core/src/persistence_json.cpp index fe87647337..abbd292f13 100644 --- a/modules/core/src/persistence_json.cpp +++ b/modules/core/src/persistence_json.cpp @@ -238,11 +238,11 @@ static char* icvJSONParseValue( CvFileStorage* fs, char* ptr, CvFileNode* node ) CV_PARSE_ERROR("Invalid `dt` in Base64 header"); } - /* set base64_beg to beginning of base64 data */ - base64_beg = &base64_buffer.at( base64::ENCODED_HEADER_SIZE ); if ( base64_buffer.size() > base64::ENCODED_HEADER_SIZE ) { + /* set base64_beg to beginning of base64 data */ + base64_beg = &base64_buffer.at( base64::ENCODED_HEADER_SIZE ); if ( !base64::base64_valid( base64_beg, 0U, base64_end - base64_beg ) ) CV_PARSE_ERROR( "Invalid Base64 data." ); diff --git a/modules/core/src/sum.cpp b/modules/core/src/sum.cpp index f965823889..660e176777 100644 --- a/modules/core/src/sum.cpp +++ b/modules/core/src/sum.cpp @@ -602,7 +602,7 @@ cv::Scalar cv::sum( InputArray _src ) CV_Assert( cn <= 4 && func != 0 ); const Mat* arrays[] = {&src, 0}; - uchar* ptrs[1]; + uchar* ptrs[1] = {}; NAryMatIterator it(arrays, ptrs); Scalar s; int total = (int)it.size, blockSize = total, intSumBlockSize = 0; diff --git a/modules/cudaarithm/src/arithm.cpp b/modules/cudaarithm/src/arithm.cpp index 01a0169136..a8f70dd205 100644 --- a/modules/cudaarithm/src/arithm.cpp +++ b/modules/cudaarithm/src/arithm.cpp @@ -451,7 +451,6 @@ namespace Size block_size; Size user_block_size; Size dft_size; - int spect_len; GpuMat image_spect, templ_spect, result_spect; GpuMat image_block, templ_block, result_data; @@ -484,7 +483,7 @@ namespace createContinuous(dft_size, CV_32F, templ_block); createContinuous(dft_size, CV_32F, result_data); - spect_len = dft_size.height * (dft_size.width / 2 + 1); + int spect_len = dft_size.height * (dft_size.width / 2 + 1); createContinuous(1, spect_len, CV_32FC2, image_spect); createContinuous(1, spect_len, CV_32FC2, templ_spect); createContinuous(1, spect_len, CV_32FC2, result_spect); diff --git a/modules/cudaimgproc/src/canny.cpp b/modules/cudaimgproc/src/canny.cpp index 8c3fd4a2b5..9a1125d1cd 100644 --- a/modules/cudaimgproc/src/canny.cpp +++ b/modules/cudaimgproc/src/canny.cpp @@ -74,6 +74,7 @@ namespace low_thresh_(low_thresh), high_thresh_(high_thresh), apperture_size_(apperture_size), L2gradient_(L2gradient) { old_apperture_size_ = -1; + d_counter = nullptr; } void detect(InputArray image, OutputArray edges, Stream& stream); diff --git a/modules/imgcodecs/src/grfmt_jpeg2000.cpp b/modules/imgcodecs/src/grfmt_jpeg2000.cpp index b8b70fee85..be280e285a 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000.cpp @@ -179,7 +179,7 @@ bool Jpeg2KDecoder::readData( Mat& img ) { Ptr close_this(this, Jpeg2KDecoder_close); bool result = false; - int color = img.channels() > 1; + bool color = img.channels() > 1; uchar* data = img.ptr(); size_t step = img.step; jas_stream_t* stream = (jas_stream_t*)m_stream; diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 36324c2e6c..f26262282a 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -226,7 +226,7 @@ bool PngDecoder::readData( Mat& img ) volatile bool result = false; AutoBuffer _buffer(m_height); uchar** buffer = _buffer.data(); - int color = img.channels() > 1; + bool color = img.channels() > 1; png_structp png_ptr = (png_structp)m_png_ptr; png_infop info_ptr = (png_infop)m_info_ptr; diff --git a/modules/imgcodecs/src/grfmt_pxm.cpp b/modules/imgcodecs/src/grfmt_pxm.cpp index 7c5c9910be..b41fd95edc 100644 --- a/modules/imgcodecs/src/grfmt_pxm.cpp +++ b/modules/imgcodecs/src/grfmt_pxm.cpp @@ -208,7 +208,7 @@ bool PxMDecoder::readHeader() bool PxMDecoder::readData( Mat& img ) { - int color = img.channels() > 1; + bool color = img.channels() > 1; uchar* data = img.ptr(); PaletteEntry palette[256]; bool result = false; @@ -225,7 +225,7 @@ bool PxMDecoder::readData( Mat& img ) // create LUT for converting colors if( bit_depth == 8 ) { - CV_Assert(m_maxval < 256); + CV_Assert(m_maxval < 256 && m_maxval > 0); for (int i = 0; i <= m_maxval; i++) gray_palette[i] = (uchar)((i*255/m_maxval)^(m_bpp == 1 ? 255 : 0)); diff --git a/modules/imgcodecs/src/grfmt_sunras.cpp b/modules/imgcodecs/src/grfmt_sunras.cpp index ec17685850..4865edaa26 100644 --- a/modules/imgcodecs/src/grfmt_sunras.cpp +++ b/modules/imgcodecs/src/grfmt_sunras.cpp @@ -160,7 +160,7 @@ bool SunRasterDecoder::readHeader() bool SunRasterDecoder::readData( Mat& img ) { - int color = img.channels() > 1; + bool color = img.channels() > 1; uchar* data = img.ptr(); size_t step = img.step; uchar gray_palette[256] = {0}; diff --git a/modules/imgproc/src/accum.cpp b/modules/imgproc/src/accum.cpp index 2b43f4e42c..793e362bdd 100644 --- a/modules/imgproc/src/accum.cpp +++ b/modules/imgproc/src/accum.cpp @@ -332,7 +332,7 @@ void cv::accumulate( InputArray _src, InputOutputArray _dst, InputArray _mask ) CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &dst, &mask, 0}; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)it.size; @@ -430,7 +430,7 @@ void cv::accumulateSquare( InputArray _src, InputOutputArray _dst, InputArray _m CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &dst, &mask, 0}; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)it.size; @@ -533,7 +533,7 @@ void cv::accumulateProduct( InputArray _src1, InputArray _src2, CV_Assert( func != 0 ); const Mat* arrays[] = {&src1, &src2, &dst, &mask, 0}; - uchar* ptrs[4]; + uchar* ptrs[4] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)it.size; @@ -635,7 +635,7 @@ void cv::accumulateWeighted( InputArray _src, InputOutputArray _dst, CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &dst, &mask, 0}; - uchar* ptrs[3]; + uchar* ptrs[3] = {}; NAryMatIterator it(arrays, ptrs); int len = (int)it.size; diff --git a/modules/imgproc/src/linefit.cpp b/modules/imgproc/src/linefit.cpp index 103fa55950..c6e4e4a014 100644 --- a/modules/imgproc/src/linefit.cpp +++ b/modules/imgproc/src/linefit.cpp @@ -321,7 +321,7 @@ static void fitLine2D( const Point2f * points, int count, int dist, void (*calc_weights) (float *, int, float *) = 0; void (*calc_weights_param) (float *, int, float *, float) = 0; int i, j, k; - float _line[6], _lineprev[6]; + float _line[4], _lineprev[4]; float rdelta = reps != 0 ? reps : 1.0f; float adelta = aeps != 0 ? aeps : 0.01f; double min_err = DBL_MAX, err = 0; From 27a6be8763d766c7de08c9402f6d13cdf28034a5 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Tue, 4 Sep 2018 17:48:52 +0300 Subject: [PATCH 47/47] Fix #12407 --- modules/dnn/src/dnn.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 214ac9961b..fb65da2a89 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -2692,8 +2692,7 @@ void Net::setInput(InputArray blob, const String& name, double scalefactor, cons Mat Net::getParam(LayerId layer, int numParam) { LayerData &ld = impl->getLayerData(layer); - - std::vector &layerBlobs = ld.layerInstance->blobs; + std::vector &layerBlobs = ld.getLayerInstance()->blobs; CV_Assert(numParam < (int)layerBlobs.size()); return layerBlobs[numParam]; } @@ -2702,7 +2701,7 @@ void Net::setParam(LayerId layer, int numParam, const Mat &blob) { LayerData &ld = impl->getLayerData(layer); - std::vector &layerBlobs = ld.layerInstance->blobs; + std::vector &layerBlobs = ld.getLayerInstance()->blobs; CV_Assert(numParam < (int)layerBlobs.size()); //we don't make strong checks, use this function carefully layerBlobs[numParam] = blob;