From 45e0e5f8e947a9c1b4a995477c43a006ec2df43f Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Viel Date: Tue, 17 Dec 2013 12:51:58 +0100 Subject: [PATCH 001/293] Pick centers in KMeans++ with a probability proportional to their distance^2, instead of simple distance, to previous centers --- .../opencv2/flann/hierarchical_clustering_index.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h index ce2d622450..02fc278448 100644 --- a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h +++ b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h @@ -210,8 +210,11 @@ private: assert(index >=0 && index < n); centers[0] = dsindices[index]; + // Computing distance^2 will have the advantage of even higher probability further to pick new centers + // far from previous centers (and this complies to "k-means++: the advantages of careful seeding" article) for (int i = 0; i < n; i++) { closestDistSq[i] = distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols); + closestDistSq[i] *= closestDistSq[i]; currentPot += closestDistSq[i]; } @@ -237,7 +240,10 @@ private: // Compute the new potential double newPot = 0; - for (int i = 0; i < n; i++) newPot += std::min( distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols), closestDistSq[i] ); + for (int i = 0; i < n; i++) { + DistanceType dist = distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols); + newPot += std::min( dist*dist, closestDistSq[i] ); + } // Store the best result if ((bestNewPot < 0)||(newPot < bestNewPot)) { @@ -249,7 +255,10 @@ private: // Add the appropriate center centers[centerCount] = dsindices[bestNewIndex]; currentPot = bestNewPot; - for (int i = 0; i < n; i++) closestDistSq[i] = std::min( distance(dataset[dsindices[i]], dataset[dsindices[bestNewIndex]], dataset.cols), closestDistSq[i] ); + for (int i = 0; i < n; i++) { + DistanceType dist = distance(dataset[dsindices[i]], dataset[dsindices[bestNewIndex]], dataset.cols); + closestDistSq[i] = std::min( dist*dist, closestDistSq[i] ); + } } centers_length = centerCount; From 5aeeaa6fce4016fd626f31f56025cf83ff07576a Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Viel Date: Tue, 17 Dec 2013 13:04:49 +0100 Subject: [PATCH 002/293] Apply to KMeansIndex KMeanspp the same modification as in HierarchicalClusteringIndex --- modules/flann/include/opencv2/flann/kmeans_index.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/flann/include/opencv2/flann/kmeans_index.h b/modules/flann/include/opencv2/flann/kmeans_index.h index 3fea956a74..3bf12047cd 100644 --- a/modules/flann/include/opencv2/flann/kmeans_index.h +++ b/modules/flann/include/opencv2/flann/kmeans_index.h @@ -211,6 +211,7 @@ public: for (int i = 0; i < n; i++) { closestDistSq[i] = distance_(dataset_[indices[i]], dataset_[indices[index]], dataset_.cols); + closestDistSq[i] *= closestDistSq[i]; currentPot += closestDistSq[i]; } @@ -236,7 +237,10 @@ public: // Compute the new potential double newPot = 0; - for (int i = 0; i < n; i++) newPot += std::min( distance_(dataset_[indices[i]], dataset_[indices[index]], dataset_.cols), closestDistSq[i] ); + for (int i = 0; i < n; i++) { + DistanceType dist = distance_(dataset_[indices[i]], dataset_[indices[index]], dataset_.cols); + newPot += std::min( dist*dist, closestDistSq[i] ); + } // Store the best result if ((bestNewPot < 0)||(newPot < bestNewPot)) { @@ -248,7 +252,10 @@ public: // Add the appropriate center centers[centerCount] = indices[bestNewIndex]; currentPot = bestNewPot; - for (int i = 0; i < n; i++) closestDistSq[i] = std::min( distance_(dataset_[indices[i]], dataset_[indices[bestNewIndex]], dataset_.cols), closestDistSq[i] ); + for (int i = 0; i < n; i++) { + DistanceType dist = distance_(dataset_[indices[i]], dataset_[indices[bestNewIndex]], dataset_.cols); + closestDistSq[i] = std::min( dist*dist, closestDistSq[i] ); + } } centers_length = centerCount; From fa749de0dcb27d3b666eda341b43e8c13f66be8e Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Viel Date: Tue, 17 Dec 2013 13:26:55 +0100 Subject: [PATCH 003/293] As some processed distances are already ^2, use template to select whether or not we have to ^2 in KMeanspp --- .../include/opencv2/flann/kmeans_index.h | 62 ++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/modules/flann/include/opencv2/flann/kmeans_index.h b/modules/flann/include/opencv2/flann/kmeans_index.h index 3bf12047cd..460dc64be9 100644 --- a/modules/flann/include/opencv2/flann/kmeans_index.h +++ b/modules/flann/include/opencv2/flann/kmeans_index.h @@ -53,6 +53,62 @@ namespace cvflann { +template +struct squareDistance +{ + typedef typename Distance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist*dist; } +}; + + +template +struct squareDistance, ElementType> +{ + typedef typename L2_Simple::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + +template +struct squareDistance, ElementType> +{ + typedef typename L2::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + + +template +struct squareDistance, ElementType> +{ + typedef typename MinkowskiDistance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + +template +struct squareDistance, ElementType> +{ + typedef typename HellingerDistance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + +template +struct squareDistance, ElementType> +{ + typedef typename ChiSquareDistance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + + +template +typename Distance::ResultType ensureSquareDistance( typename Distance::ResultType dist ) +{ + typedef typename Distance::ElementType ElementType; + + squareDistance dummy; + return dummy( dist ); +} + + + struct KMeansIndexParams : public IndexParams { KMeansIndexParams(int branching = 32, int iterations = 11, @@ -211,7 +267,7 @@ public: for (int i = 0; i < n; i++) { closestDistSq[i] = distance_(dataset_[indices[i]], dataset_[indices[index]], dataset_.cols); - closestDistSq[i] *= closestDistSq[i]; + closestDistSq[i] = ensureSquareDistance( closestDistSq[i] ); currentPot += closestDistSq[i]; } @@ -239,7 +295,7 @@ public: double newPot = 0; for (int i = 0; i < n; i++) { DistanceType dist = distance_(dataset_[indices[i]], dataset_[indices[index]], dataset_.cols); - newPot += std::min( dist*dist, closestDistSq[i] ); + newPot += std::min( ensureSquareDistance(dist), closestDistSq[i] ); } // Store the best result @@ -254,7 +310,7 @@ public: currentPot = bestNewPot; for (int i = 0; i < n; i++) { DistanceType dist = distance_(dataset_[indices[i]], dataset_[indices[bestNewIndex]], dataset_.cols); - closestDistSq[i] = std::min( dist*dist, closestDistSq[i] ); + closestDistSq[i] = std::min( ensureSquareDistance(dist), closestDistSq[i] ); } } From 0d19685f9544ddf2668fa899ce74580fd9d1039f Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Viel Date: Tue, 17 Dec 2013 13:34:20 +0100 Subject: [PATCH 004/293] Move templates in dist.h in order to share them between KMeansIndex and HierarchicalClusteringIndex classes. --- modules/flann/include/opencv2/flann/dist.h | 60 +++++++++++++++++++ .../flann/hierarchical_clustering_index.h | 6 +- .../include/opencv2/flann/kmeans_index.h | 56 ----------------- 3 files changed, 63 insertions(+), 59 deletions(-) diff --git a/modules/flann/include/opencv2/flann/dist.h b/modules/flann/include/opencv2/flann/dist.h index 80ae2dc916..2afceb8893 100644 --- a/modules/flann/include/opencv2/flann/dist.h +++ b/modules/flann/include/opencv2/flann/dist.h @@ -812,6 +812,66 @@ struct ZeroIterator }; + +/* + * Depending on processed distances, some of them are already squared (e.g. L2) + * and some are not (e.g.Hamming). In KMeans++ for instance we want to be sure + * we are working on ^2 distances, thus following templates to ensure that. + */ +template +struct squareDistance +{ + typedef typename Distance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist*dist; } +}; + + +template +struct squareDistance, ElementType> +{ + typedef typename L2_Simple::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + +template +struct squareDistance, ElementType> +{ + typedef typename L2::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + + +template +struct squareDistance, ElementType> +{ + typedef typename MinkowskiDistance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + +template +struct squareDistance, ElementType> +{ + typedef typename HellingerDistance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + +template +struct squareDistance, ElementType> +{ + typedef typename ChiSquareDistance::ResultType ResultType; + ResultType operator()( ResultType dist ) { return dist; } +}; + + +template +typename Distance::ResultType ensureSquareDistance( typename Distance::ResultType dist ) +{ + typedef typename Distance::ElementType ElementType; + + squareDistance dummy; + return dummy( dist ); +} + } #endif //OPENCV_FLANN_DIST_H_ diff --git a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h index 02fc278448..3ccfa5534b 100644 --- a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h +++ b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h @@ -214,7 +214,7 @@ private: // far from previous centers (and this complies to "k-means++: the advantages of careful seeding" article) for (int i = 0; i < n; i++) { closestDistSq[i] = distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols); - closestDistSq[i] *= closestDistSq[i]; + closestDistSq[i] = ensureSquareDistance( closestDistSq[i] ); currentPot += closestDistSq[i]; } @@ -242,7 +242,7 @@ private: double newPot = 0; for (int i = 0; i < n; i++) { DistanceType dist = distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols); - newPot += std::min( dist*dist, closestDistSq[i] ); + newPot += std::min( ensureSquareDistance(dist), closestDistSq[i] ); } // Store the best result @@ -257,7 +257,7 @@ private: currentPot = bestNewPot; for (int i = 0; i < n; i++) { DistanceType dist = distance(dataset[dsindices[i]], dataset[dsindices[bestNewIndex]], dataset.cols); - closestDistSq[i] = std::min( dist*dist, closestDistSq[i] ); + closestDistSq[i] = std::min( ensureSquareDistance(dist), closestDistSq[i] ); } } diff --git a/modules/flann/include/opencv2/flann/kmeans_index.h b/modules/flann/include/opencv2/flann/kmeans_index.h index 460dc64be9..3cbee24404 100644 --- a/modules/flann/include/opencv2/flann/kmeans_index.h +++ b/modules/flann/include/opencv2/flann/kmeans_index.h @@ -53,62 +53,6 @@ namespace cvflann { -template -struct squareDistance -{ - typedef typename Distance::ResultType ResultType; - ResultType operator()( ResultType dist ) { return dist*dist; } -}; - - -template -struct squareDistance, ElementType> -{ - typedef typename L2_Simple::ResultType ResultType; - ResultType operator()( ResultType dist ) { return dist; } -}; - -template -struct squareDistance, ElementType> -{ - typedef typename L2::ResultType ResultType; - ResultType operator()( ResultType dist ) { return dist; } -}; - - -template -struct squareDistance, ElementType> -{ - typedef typename MinkowskiDistance::ResultType ResultType; - ResultType operator()( ResultType dist ) { return dist; } -}; - -template -struct squareDistance, ElementType> -{ - typedef typename HellingerDistance::ResultType ResultType; - ResultType operator()( ResultType dist ) { return dist; } -}; - -template -struct squareDistance, ElementType> -{ - typedef typename ChiSquareDistance::ResultType ResultType; - ResultType operator()( ResultType dist ) { return dist; } -}; - - -template -typename Distance::ResultType ensureSquareDistance( typename Distance::ResultType dist ) -{ - typedef typename Distance::ElementType ElementType; - - squareDistance dummy; - return dummy( dist ); -} - - - struct KMeansIndexParams : public IndexParams { KMeansIndexParams(int branching = 32, int iterations = 11, From d3ac1bc314136d0654fad24a9ee5b01c29e9f5af Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Viel Date: Fri, 20 Dec 2013 01:00:55 +0100 Subject: [PATCH 005/293] When a cluster is empty for KMeans, it's better to give it the point from another cluster j that is the furthest one from center j. --- modules/flann/include/opencv2/flann/kmeans_index.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/flann/include/opencv2/flann/kmeans_index.h b/modules/flann/include/opencv2/flann/kmeans_index.h index 3fea956a74..489ed80565 100644 --- a/modules/flann/include/opencv2/flann/kmeans_index.h +++ b/modules/flann/include/opencv2/flann/kmeans_index.h @@ -759,10 +759,13 @@ private: for (int k=0; k Date: Mon, 23 Dec 2013 00:21:51 +0800 Subject: [PATCH 006/293] Update system.cpp Add native C++ support --- modules/core/src/system.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index b301d95dba..09daceed53 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -87,10 +87,41 @@ #ifdef HAVE_WINRT #include +#ifndef __cplusplus_winrt +#include +#pragma comment(lib, "runtimeobject.lib") +#endif std::wstring GetTempPathWinRT() { +#ifdef __cplusplus_winrt return std::wstring(Windows::Storage::ApplicationData::Current->TemporaryFolder->Path->Data()); +#else + Microsoft::WRL::ComPtr appdataFactory; + Microsoft::WRL::ComPtr appdataRef; + Microsoft::WRL::ComPtr storagefolderRef; + Microsoft::WRL::ComPtr storageitemRef; + HSTRING str; + HSTRING_HEADER hstrHead; + std::wstring wstr; + if (FAILED(WindowsCreateStringReference(RuntimeClass_Windows_Storage_ApplicationData, + (UINT32)wcslen(RuntimeClass_Windows_Storage_ApplicationData), &hstrHead, &str))) + return wstr; + if (FAILED(RoGetActivationFactory(str, IID_PPV_ARGS(appdataFactory.ReleaseAndGetAddressOf())))) + return wstr; + if (FAILED(appdataFactory->get_Current(appdataRef.ReleaseAndGetAddressOf()))) + return wstr; + if (FAILED(appdataRef->get_TemporaryFolder(storagefolderRef.ReleaseAndGetAddressOf()))) + return wstr; + if (FAILED(storagefolderRef.As(&storageitemRef))) + return wstr; + str = NULL; + if (FAILED(storageitemRef->get_Path(&str))) + return wstr; + wstr = WindowsGetStringRawBuffer(str, NULL); + WindowsDeleteString(str); + return wstr; +#endif } std::wstring GetTempFileNameWinRT(std::wstring prefix) From 48808581190d3076b579c65498337a1fcfb97b20 Mon Sep 17 00:00:00 2001 From: GregoryMorse Date: Mon, 23 Dec 2013 00:28:50 +0800 Subject: [PATCH 007/293] Update CMakeLists.txt WinRT native C++ support allowing building of static libraries Update CMakeLists.txt Update OpenCVCRTLinkage.cmake Update OpenCVCRTLinkage.cmake --- CMakeLists.txt | 3 ++- cmake/OpenCVCRTLinkage.cmake | 12 ++++++++---- modules/core/CMakeLists.txt | 5 ++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f793f1070..daf185fbac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,6 +219,7 @@ OCV_OPTION(ENABLE_VFPV3 "Enable VFPv3-D32 instructions" OCV_OPTION(ENABLE_NOISY_WARNINGS "Show all warnings even if they are too noisy" OFF ) OCV_OPTION(OPENCV_WARNINGS_ARE_ERRORS "Treat warnings as errors" OFF ) OCV_OPTION(ENABLE_WINRT_MODE "Build with Windows Runtime support" OFF IF WIN32 ) +OCV_OPTION(ENABLE_WINRT_MODE_NATIVE "Build with Windows Runtime native C++ support" OFF IF WIN32 ) # uncategorized options # =================================================== @@ -660,7 +661,7 @@ endif() if(WIN32) status("") status(" Windows RT support:" HAVE_WINRT THEN YES ELSE NO) - if (ENABLE_WINRT_MODE) + if (ENABLE_WINRT_MODE OR ENABLE_WINRT_MODE_NATIVE) status(" Windows SDK v8.0:" ${WINDOWS_SDK_PATH}) status(" Visual Studio 2012:" ${VISUAL_STUDIO_PATH}) endif() diff --git a/cmake/OpenCVCRTLinkage.cmake b/cmake/OpenCVCRTLinkage.cmake index 8a297c6857..5265e3e8a6 100644 --- a/cmake/OpenCVCRTLinkage.cmake +++ b/cmake/OpenCVCRTLinkage.cmake @@ -9,7 +9,7 @@ set(HAVE_WINRT FALSE) # search Windows Platform SDK message(STATUS "Checking for Windows Platform SDK") GET_FILENAME_COMPONENT(WINDOWS_SDK_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v8.0;InstallationFolder]" ABSOLUTE CACHE) -if (WINDOWS_SDK_PATH STREQUAL "") +if(WINDOWS_SDK_PATH STREQUAL "") set(HAVE_MSPDK FALSE) message(STATUS "Windows Platform SDK 8.0 was not found") else() @@ -19,7 +19,7 @@ endif() #search for Visual Studio 11.0 install directory message(STATUS "Checking for Visual Studio 2012") GET_FILENAME_COMPONENT(VISUAL_STUDIO_PATH [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0\\Setup\\VS;ProductDir] REALPATH CACHE) -if (VISUAL_STUDIO_PATH STREQUAL "") +if(VISUAL_STUDIO_PATH STREQUAL "") set(HAVE_MSVC2012 FALSE) message(STATUS "Visual Studio 2012 was not found") else() @@ -30,11 +30,15 @@ try_compile(HAVE_WINRT_SDK "${OpenCV_BINARY_DIR}" "${OpenCV_SOURCE_DIR}/cmake/checks/winrttest.cpp") -if (ENABLE_WINRT_MODE AND HAVE_WINRT_SDK AND HAVE_MSVC2012 AND HAVE_MSPDK) +if(ENABLE_WINRT_MODE AND HAVE_WINRT_SDK AND HAVE_MSVC2012 AND HAVE_MSPDK) set(HAVE_WINRT TRUE) + set(HAVE_WINRT_CX TRUE) +elseif(ENABLE_WINRT_MODE_NATIVE AND HAVE_WINRT_SDK AND HAVE_MSVC2012 AND HAVE_MSPDK) + set(HAVE_WINRT TRUE) + set(HAVE_WINRT_CX FALSE) endif() -if (HAVE_WINRT) +if(HAVE_WINRT) add_definitions(/DWINVER=0x0602 /DNTDDI_VERSION=NTDDI_WIN8 /D_WIN32_WINNT=0x0602) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /appcontainer") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /appcontainer") diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 66b8ae0d2f..2adf5dbbda 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -2,8 +2,11 @@ set(the_description "The Core Functionality") ocv_add_module(core PRIVATE_REQUIRED ${ZLIB_LIBRARIES}) ocv_module_include_directories(${ZLIB_INCLUDE_DIR}) +if(HAVE_WINRT_CX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") +endif() if(HAVE_WINRT) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW /GS /Gm- /AI\"${WINDOWS_SDK_PATH}/References/CommonConfiguration/Neutral\" /AI\"${VISUAL_STUDIO_PATH}/vcpackages\"") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS /Gm- /AI\"${WINDOWS_SDK_PATH}/References/CommonConfiguration/Neutral\" /AI\"${VISUAL_STUDIO_PATH}/vcpackages\"") endif() if(HAVE_CUDA) From 0ccc903647955d632b9a9091d8ad989a2cd9b038 Mon Sep 17 00:00:00 2001 From: Peng Xiao Date: Fri, 27 Dec 2013 11:54:08 +0800 Subject: [PATCH 008/293] fixed a buffer overrun of ocl canny the `map` buffer does not have the same size with CUDA and index starts at [1, 1] instead of [0, 0]. --- modules/ocl/src/opencl/imgproc_canny.cl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/ocl/src/opencl/imgproc_canny.cl b/modules/ocl/src/opencl/imgproc_canny.cl index 0a54f1468c..2ddfdae5f9 100644 --- a/modules/ocl/src/opencl/imgproc_canny.cl +++ b/modules/ocl/src/opencl/imgproc_canny.cl @@ -381,8 +381,8 @@ struct PtrStepSz { int step; int rows, cols; }; -inline int get(struct PtrStepSz data, int y, int x) { return *((__global int *)((__global char*)data.ptr + data.step * y + sizeof(int) * x)); } -inline void set(struct PtrStepSz data, int y, int x, int value) { *((__global int *)((__global char*)data.ptr + data.step * y + sizeof(int) * x)) = value; } +inline int get(struct PtrStepSz data, int y, int x) { return *((__global int *)((__global char*)data.ptr + data.step * (y + 1) + sizeof(int) * (x + 1))); } +inline void set(struct PtrStepSz data, int y, int x, int value) { *((__global int *)((__global char*)data.ptr + data.step * (y + 1) + sizeof(int) * (x + 1))) = value; } ////////////////////////////////////////////////////////////////////////////////////////// // do Hysteresis for pixel whose edge type is 1 @@ -494,7 +494,7 @@ edgesHysteresisLocal } } #else - struct PtrStepSz map = {((__global int *)((__global char*)map_ptr + map_offset)), map_step, rows, cols}; + struct PtrStepSz map = {((__global int *)((__global char*)map_ptr + map_offset)), map_step, rows + 1, cols + 1}; __local int smem[18][18]; @@ -507,13 +507,13 @@ edgesHysteresisLocal smem[threadIdx.y + 1][threadIdx.x + 1] = x < map.cols && y < map.rows ? get(map, y, x) : 0; if (threadIdx.y == 0) - smem[0][threadIdx.x + 1] = y > 0 ? get(map, y - 1, x) : 0; + smem[0][threadIdx.x + 1] = x < map.cols ? get(map, y - 1, x) : 0; if (threadIdx.y == blockDim.y - 1) smem[blockDim.y + 1][threadIdx.x + 1] = y + 1 < map.rows ? get(map, y + 1, x) : 0; if (threadIdx.x == 0) - smem[threadIdx.y + 1][0] = x > 0 ? get(map, y, x - 1) : 0; + smem[threadIdx.y + 1][0] = y < map.rows ? get(map, y, x - 1) : 0; if (threadIdx.x == blockDim.x - 1) - smem[threadIdx.y + 1][blockDim.x + 1] = x + 1 < map.cols ? get(map, y, x + 1) : 0; + smem[threadIdx.y + 1][blockDim.x + 1] = x + 1 < map.cols && y < map.rows ? get(map, y, x + 1) : 0; if (threadIdx.x == 0 && threadIdx.y == 0) smem[0][0] = y > 0 && x > 0 ? get(map, y - 1, x - 1) : 0; if (threadIdx.x == blockDim.x - 1 && threadIdx.y == 0) @@ -525,7 +525,7 @@ edgesHysteresisLocal barrier(CLK_LOCAL_MEM_FENCE); - if (x >= map.cols || y >= map.rows) + if (x >= cols || y >= rows) return; int n; @@ -576,7 +576,7 @@ edgesHysteresisLocal if (n > 0) { const int ind = atomic_inc(counter); - st[ind] = (ushort2)(x, y); + st[ind] = (ushort2)(x + 1, y + 1); } #endif } From c48777a1c39e66dc38a809047ba8764e3be354b6 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 27 Dec 2013 11:18:10 +0400 Subject: [PATCH 009/293] CUDA dependency in nonfree nodule removed. OpenCV.mk generation fixed. --- cmake/OpenCVGenAndroidMK.cmake | 4 +++- modules/nonfree/CMakeLists.txt | 7 ++++++- modules/nonfree/include/opencv2/nonfree/gpu.hpp | 2 +- modules/nonfree/src/cuda/surf.cu | 2 +- modules/nonfree/src/precomp.hpp | 2 +- modules/nonfree/src/surf_gpu.cpp | 4 ++-- .../include/opencv2/stitching/detail/matchers.hpp | 4 ++-- 7 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index 8792d1b48a..eed47652b4 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -70,7 +70,9 @@ if(ANDROID) endif() # GPU module enabled separately - list(REMOVE_ITEM OPENCV_MODULES_CONFIGMAKE "gpu") + list(REMOVE_ITEM OPENCV_MODULES_CONFIGMAKE "opencv_gpu") + list(REMOVE_ITEM OPENCV_MODULES_CONFIGMAKE "opencv_dynamicuda") + if(HAVE_opencv_gpu) set(OPENCV_HAVE_GPU_MODULE_CONFIGMAKE "on") endif() diff --git a/modules/nonfree/CMakeLists.txt b/modules/nonfree/CMakeLists.txt index 5689a12e36..d5c5562eca 100644 --- a/modules/nonfree/CMakeLists.txt +++ b/modules/nonfree/CMakeLists.txt @@ -4,4 +4,9 @@ endif() set(the_description "Functionality with possible limitations on the use") ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) -ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_gpu opencv_ocl) +if (ENABLE_DYNAMIC_CUDA) + set(HAVE_CUDA FALSE) + ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_ocl) +else() + ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_gpu opencv_ocl) +endif() \ No newline at end of file diff --git a/modules/nonfree/include/opencv2/nonfree/gpu.hpp b/modules/nonfree/include/opencv2/nonfree/gpu.hpp index 3cb0b47621..c8730fb3b9 100644 --- a/modules/nonfree/include/opencv2/nonfree/gpu.hpp +++ b/modules/nonfree/include/opencv2/nonfree/gpu.hpp @@ -45,7 +45,7 @@ #include "opencv2/opencv_modules.hpp" -#if defined(HAVE_OPENCV_GPU) +#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) #include "opencv2/gpu/gpu.hpp" diff --git a/modules/nonfree/src/cuda/surf.cu b/modules/nonfree/src/cuda/surf.cu index 2002f534d0..df5905d31d 100644 --- a/modules/nonfree/src/cuda/surf.cu +++ b/modules/nonfree/src/cuda/surf.cu @@ -42,7 +42,7 @@ #include "opencv2/opencv_modules.hpp" -#ifdef HAVE_OPENCV_GPU +#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) #include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/limits.hpp" diff --git a/modules/nonfree/src/precomp.hpp b/modules/nonfree/src/precomp.hpp index 5fbe446af8..0d2e180fc5 100644 --- a/modules/nonfree/src/precomp.hpp +++ b/modules/nonfree/src/precomp.hpp @@ -51,7 +51,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/internal.hpp" -#if defined(HAVE_OPENCV_GPU) +#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) #include "opencv2/nonfree/gpu.hpp" #if defined(HAVE_CUDA) diff --git a/modules/nonfree/src/surf_gpu.cpp b/modules/nonfree/src/surf_gpu.cpp index bfc7e700f9..e0cf6ff517 100644 --- a/modules/nonfree/src/surf_gpu.cpp +++ b/modules/nonfree/src/surf_gpu.cpp @@ -42,7 +42,7 @@ #include "precomp.hpp" -#if defined(HAVE_OPENCV_GPU) +#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) using namespace cv; using namespace cv::gpu; @@ -422,4 +422,4 @@ void cv::gpu::SURF_GPU::releaseMemory() #endif // !defined (HAVE_CUDA) -#endif // defined(HAVE_OPENCV_GPU) +#endif // defined(HAVE_OPENCV_GPU) && !defined(ANDROID) diff --git a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp index 108cd0face..36f80f481c 100644 --- a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp @@ -48,7 +48,7 @@ #include "opencv2/opencv_modules.hpp" -#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) +#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) && !defined(ANDROID) #include "opencv2/nonfree/gpu.hpp" #endif @@ -104,7 +104,7 @@ private: }; -#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) +#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) && !defined(ANDROID) class CV_EXPORTS SurfFeaturesFinderGpu : public FeaturesFinder { public: From d014cb8fb48982ffec87dad36a40a455896ca88f Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 27 Dec 2013 14:44:58 +0400 Subject: [PATCH 010/293] fixed warning [-Wempty-body] --- modules/ocl/src/gftt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ocl/src/gftt.cpp b/modules/ocl/src/gftt.cpp index a82196d78f..4f24d13588 100644 --- a/modules/ocl/src/gftt.cpp +++ b/modules/ocl/src/gftt.cpp @@ -208,7 +208,7 @@ void cv::ocl::GoodFeaturesToTrackDetector_OCL::operator ()(const oclMat& image, if(!use_cpu_sorter) { // round to 2^n unsigned int n=1; - for(n=1;n<(unsigned int)corner_array_size;n<<=1); + for(n=1;n<(unsigned int)corner_array_size;n<<=1) ; corner_array_size = (int)n; ensureSizeIsEnough(1, corner_array_size , CV_32FC2, tmpCorners_); From 4175916b2a5b25789debdb7f79bc14abf039f5de Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 27 Dec 2013 17:19:38 +0400 Subject: [PATCH 011/293] dynamicuda became private module. --- modules/dynamicuda/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dynamicuda/CMakeLists.txt b/modules/dynamicuda/CMakeLists.txt index b523bf0fd1..75ace872a3 100644 --- a/modules/dynamicuda/CMakeLists.txt +++ b/modules/dynamicuda/CMakeLists.txt @@ -9,7 +9,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wshadow) ocv_module_include_directories("${OpenCV_SOURCE_DIR}/modules/gpu/include") set(OPENCV_MODULE_TYPE SHARED) if (BUILD_FAT_JAVA_LIB) - ocv_define_module(dynamicuda opencv_java PRIVATE_REQUIRED ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) + ocv_define_module(dynamicuda INTERNAL opencv_java PRIVATE_REQUIRED ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) else() - ocv_define_module(dynamicuda opencv_core PRIVATE_REQUIRED ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) + ocv_define_module(dynamicuda INTERNAL opencv_core PRIVATE_REQUIRED ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) endif() From df63060e4d7c132f26b9601867240eb779534f0c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 27 Dec 2013 16:49:26 +0400 Subject: [PATCH 012/293] Bugfix for DeviceInfoFuncTable in dynamicuda amd core modules. --- modules/core/src/gpumat.cpp | 21 ++- .../include/opencv2/dynamicuda/dynamicuda.hpp | 126 ++++++++---------- 2 files changed, 62 insertions(+), 85 deletions(-) diff --git a/modules/core/src/gpumat.cpp b/modules/core/src/gpumat.cpp index 5dae4697d3..ec26801ddc 100644 --- a/modules/core/src/gpumat.cpp +++ b/modules/core/src/gpumat.cpp @@ -279,20 +279,19 @@ bool cv::gpu::TargetArchs::hasEqualOrGreater(int major, int minor) { return devi bool cv::gpu::TargetArchs::hasEqualOrGreaterPtx(int major, int minor) { return deviceInfoFuncTable()->hasEqualOrGreaterPtx(major, minor); } bool cv::gpu::TargetArchs::hasEqualOrGreaterBin(int major, int minor) { return deviceInfoFuncTable()->hasEqualOrGreaterBin(major, minor); } -size_t cv::gpu::DeviceInfo::sharedMemPerBlock() const { return deviceInfoFuncTable()->sharedMemPerBlock(); } -void cv::gpu::DeviceInfo::queryMemory(size_t& total_memory, size_t& free_memory) const { deviceInfoFuncTable()->queryMemory(total_memory, free_memory); } -size_t cv::gpu::DeviceInfo::freeMemory() const { return deviceInfoFuncTable()->freeMemory(); } -size_t cv::gpu::DeviceInfo::totalMemory() const { return deviceInfoFuncTable()->totalMemory(); } -bool cv::gpu::DeviceInfo::supports(FeatureSet feature_set) const { return deviceInfoFuncTable()->supports(feature_set); } -bool cv::gpu::DeviceInfo::isCompatible() const { return deviceInfoFuncTable()->isCompatible(); } +size_t cv::gpu::DeviceInfo::sharedMemPerBlock() const { return deviceInfoFuncTable()->sharedMemPerBlock(device_id_); } +void cv::gpu::DeviceInfo::queryMemory(size_t& total_memory, size_t& free_memory) const { deviceInfoFuncTable()->queryMemory(device_id_, total_memory, free_memory); } +size_t cv::gpu::DeviceInfo::freeMemory() const { return deviceInfoFuncTable()->freeMemory(device_id_); } +size_t cv::gpu::DeviceInfo::totalMemory() const { return deviceInfoFuncTable()->totalMemory(device_id_); } +bool cv::gpu::DeviceInfo::supports(FeatureSet feature_set) const { return deviceInfoFuncTable()->supports(device_id_, feature_set); } +bool cv::gpu::DeviceInfo::isCompatible() const { return deviceInfoFuncTable()->isCompatible(device_id_); } void cv::gpu::DeviceInfo::query() { - deviceInfoFuncTable()->query(); - name_ = deviceInfoFuncTable()->name(); - multi_processor_count_ = deviceInfoFuncTable()->multiProcessorCount(); - majorVersion_ = deviceInfoFuncTable()->majorVersion(); - minorVersion_ = deviceInfoFuncTable()->minorVersion(); + name_ = deviceInfoFuncTable()->name(device_id_); + multi_processor_count_ = deviceInfoFuncTable()->multiProcessorCount(device_id_); + majorVersion_ = deviceInfoFuncTable()->majorVersion(device_id_); + minorVersion_ = deviceInfoFuncTable()->minorVersion(device_id_); } void cv::gpu::printCudaDeviceInfo(int device) { deviceInfoFuncTable()->printCudaDeviceInfo(device); } diff --git a/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp b/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp index 8973c53049..d4d0220e00 100644 --- a/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp +++ b/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp @@ -9,18 +9,17 @@ class DeviceInfoFuncTable { public: // cv::DeviceInfo - virtual size_t sharedMemPerBlock() const = 0; - virtual void queryMemory(size_t&, size_t&) const = 0; - virtual size_t freeMemory() const = 0; - virtual size_t totalMemory() const = 0; - virtual bool supports(FeatureSet) const = 0; - virtual bool isCompatible() const = 0; - virtual void query() = 0; - virtual int deviceID() const = 0; - virtual std::string name() const = 0; - virtual int majorVersion() const = 0; - virtual int minorVersion() const = 0; - virtual int multiProcessorCount() const = 0; + virtual size_t sharedMemPerBlock(int id) const = 0; + virtual void queryMemory(int id, size_t&, size_t&) const = 0; + virtual size_t freeMemory(int id) const = 0; + virtual size_t totalMemory(int id) const = 0; + virtual bool supports(int id, FeatureSet) const = 0; + virtual bool isCompatible(int id) const = 0; + virtual std::string name(int id) const = 0; + virtual int majorVersion(int id) const = 0; + virtual int minorVersion(int id) const = 0; + virtual int multiProcessorCount(int id) const = 0; + virtual int getCudaEnabledDeviceCount() const = 0; virtual void setDevice(int) const = 0; virtual int getDevice() const = 0; @@ -46,8 +45,6 @@ public: class GpuFuncTable { public: - virtual ~GpuFuncTable() {} - // GpuMat routines virtual void copy(const Mat& src, GpuMat& dst) const = 0; virtual void copy(const GpuMat& src, Mat& dst) const = 0; @@ -64,23 +61,23 @@ public: virtual void mallocPitch(void** devPtr, size_t* step, size_t width, size_t height) const = 0; virtual void free(void* devPtr) const = 0; + + virtual ~GpuFuncTable() {} }; class EmptyDeviceInfoFuncTable: public DeviceInfoFuncTable { public: - size_t sharedMemPerBlock() const { throw_nogpu; return 0; } - void queryMemory(size_t&, size_t&) const { throw_nogpu; } - size_t freeMemory() const { throw_nogpu; return 0; } - size_t totalMemory() const { throw_nogpu; return 0; } - bool supports(FeatureSet) const { throw_nogpu; return false; } - bool isCompatible() const { throw_nogpu; return false; } - void query() { throw_nogpu; } - int deviceID() const { throw_nogpu; return -1; }; - std::string name() const { throw_nogpu; return std::string(); } - int majorVersion() const { throw_nogpu; return -1; } - int minorVersion() const { throw_nogpu; return -1; } - int multiProcessorCount() const { throw_nogpu; return -1; } + size_t sharedMemPerBlock(int) const { throw_nogpu; return 0; } + void queryMemory(int, size_t&, size_t&) const { throw_nogpu; } + size_t freeMemory(int) const { throw_nogpu; return 0; } + size_t totalMemory(int) const { throw_nogpu; return 0; } + bool supports(int, FeatureSet) const { throw_nogpu; return false; } + bool isCompatible(int) const { throw_nogpu; return false; } + std::string name(int) const { throw_nogpu; return std::string(); } + int majorVersion(int) const { throw_nogpu; return -1; } + int minorVersion(int) const { throw_nogpu; return -1; } + int multiProcessorCount(int) const { throw_nogpu; return -1; } int getCudaEnabledDeviceCount() const { return 0; } @@ -538,94 +535,84 @@ private: }; DeviceProps deviceProps; +const CudaArch cudaArch; class CudaDeviceInfoFuncTable : public DeviceInfoFuncTable { public: - size_t sharedMemPerBlock() const + size_t sharedMemPerBlock(int id) const { - return deviceProps.get(device_id_)->sharedMemPerBlock; + return deviceProps.get(id)->sharedMemPerBlock; } - void queryMemory(size_t& _totalMemory, size_t& _freeMemory) const + void queryMemory(int id, size_t& _totalMemory, size_t& _freeMemory) const { int prevDeviceID = getDevice(); - if (prevDeviceID != device_id_) - setDevice(device_id_); + if (prevDeviceID != id) + setDevice(id); cudaSafeCall( cudaMemGetInfo(&_freeMemory, &_totalMemory) ); - if (prevDeviceID != device_id_) + if (prevDeviceID != id) setDevice(prevDeviceID); } - size_t freeMemory() const + size_t freeMemory(int id) const { size_t _totalMemory, _freeMemory; - queryMemory(_totalMemory, _freeMemory); + queryMemory(id, _totalMemory, _freeMemory); return _freeMemory; } - size_t totalMemory() const + size_t totalMemory(int id) const { size_t _totalMemory, _freeMemory; - queryMemory(_totalMemory, _freeMemory); + queryMemory(id, _totalMemory, _freeMemory); return _totalMemory; } - bool supports(FeatureSet feature_set) const + bool supports(int id, FeatureSet feature_set) const { - int version = majorVersion_ * 10 + minorVersion_; + int version = majorVersion(id) * 10 + minorVersion(id); return version >= feature_set; } - bool isCompatible() const + bool isCompatible(int id) const { // Check PTX compatibility - if (hasEqualOrLessPtx(majorVersion_, minorVersion_)) + if (hasEqualOrLessPtx(majorVersion(id), minorVersion(id))) return true; // Check BIN compatibility - for (int i = minorVersion_; i >= 0; --i) - if (hasBin(majorVersion_, i)) + for (int i = minorVersion(id); i >= 0; --i) + if (hasBin(majorVersion(id), i)) return true; return false; } - void query() + std::string name(int id) const { - const cudaDeviceProp* prop = deviceProps.get(device_id_); - - name_ = prop->name; - multi_processor_count_ = prop->multiProcessorCount; - majorVersion_ = prop->major; - minorVersion_ = prop->minor; + const cudaDeviceProp* prop = deviceProps.get(id); + return prop->name; } - int deviceID() const + int majorVersion(int id) const { - return device_id_; + const cudaDeviceProp* prop = deviceProps.get(id); + return prop->major; } - std::string name() const + int minorVersion(int id) const { - return name_; + const cudaDeviceProp* prop = deviceProps.get(id); + return prop->minor; } - int majorVersion() const + int multiProcessorCount(int id) const { - return majorVersion_; - } - - int minorVersion() const - { - return minorVersion_; - } - - int multiProcessorCount() const - { - return multi_processor_count_; + const cudaDeviceProp* prop = deviceProps.get(id); + return prop->multiProcessorCount; } int getCudaEnabledDeviceCount() const @@ -836,15 +823,6 @@ public: } private: - int device_id_; - - std::string name_; - int multi_processor_count_; - int majorVersion_; - int minorVersion_; - - const CudaArch cudaArch; - int convertSMVer2Cores(int major, int minor) const { // Defines for GPU Architecture types (using the SM version to determine the # of cores per SM From 8399568edfeba41912b87642def96f6e8bc4f838 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 27 Dec 2013 18:19:29 +0400 Subject: [PATCH 013/293] disabled GEMM test if library was built without CUBLAS --- modules/gpu/perf/perf_core.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/gpu/perf/perf_core.cpp b/modules/gpu/perf/perf_core.cpp index e38196b994..ae6ed865b1 100644 --- a/modules/gpu/perf/perf_core.cpp +++ b/modules/gpu/perf/perf_core.cpp @@ -1303,6 +1303,8 @@ PERF_TEST_P(Sz_3Depth, Core_AddWeighted, ////////////////////////////////////////////////////////////////////// // GEMM +#ifdef HAVE_CUBLAS + CV_FLAGS(GemmFlags, 0, GEMM_1_T, GEMM_2_T, GEMM_3_T) #define ALL_GEMM_FLAGS Values(0, CV_GEMM_A_T, CV_GEMM_B_T, CV_GEMM_C_T, CV_GEMM_A_T | CV_GEMM_B_T, CV_GEMM_A_T | CV_GEMM_C_T, CV_GEMM_A_T | CV_GEMM_B_T | CV_GEMM_C_T) @@ -1351,6 +1353,8 @@ PERF_TEST_P(Sz_Type_Flags, Core_GEMM, } } +#endif + ////////////////////////////////////////////////////////////////////// // Transpose From 15678efe847d3ec12381d3b2a7fff07bbe243830 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 27 Dec 2013 18:20:01 +0400 Subject: [PATCH 014/293] disable 2 problematic tests --- modules/gpu/perf/perf_video.cpp | 2 +- modules/gpu/test/test_objdetect.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gpu/perf/perf_video.cpp b/modules/gpu/perf/perf_video.cpp index 6e9fda605d..6c7a648221 100644 --- a/modules/gpu/perf/perf_video.cpp +++ b/modules/gpu/perf/perf_video.cpp @@ -500,7 +500,7 @@ PERF_TEST_P(ImagePair, Video_OpticalFlowBM, } } -PERF_TEST_P(ImagePair, Video_FastOpticalFlowBM, +PERF_TEST_P(ImagePair, DISABLED_Video_FastOpticalFlowBM, Values(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png"))) { declare.time(400); diff --git a/modules/gpu/test/test_objdetect.cpp b/modules/gpu/test/test_objdetect.cpp index aaeaa54e66..f5c4e16381 100644 --- a/modules/gpu/test/test_objdetect.cpp +++ b/modules/gpu/test/test_objdetect.cpp @@ -177,7 +177,7 @@ struct HOG : testing::TestWithParam, cv::gpu::HOGDescriptor }; // desabled while resize does not fixed -GPU_TEST_P(HOG, Detect) +GPU_TEST_P(HOG, DISABLED_Detect) { cv::Mat img_rgb = readImage("hog/road.png"); ASSERT_FALSE(img_rgb.empty()); From 53494ba39730cd3e5d3a22f6c3313b48e4373b31 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 27 Dec 2013 18:20:14 +0400 Subject: [PATCH 015/293] increase thresholds for some tests --- modules/gpu/test/test_color.cpp | 8 ++++---- modules/gpu/test/test_core.cpp | 6 +++--- modules/gpu/test/test_gpumat.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/gpu/test/test_color.cpp b/modules/gpu/test/test_color.cpp index 3f5a37fd03..3b4b326e4d 100644 --- a/modules/gpu/test/test_color.cpp +++ b/modules/gpu/test/test_color.cpp @@ -715,7 +715,7 @@ GPU_TEST_P(CvtColor, BGR2YCrCb) cv::Mat dst_gold; cv::cvtColor(src, dst_gold, cv::COLOR_BGR2YCrCb); - EXPECT_MAT_NEAR(dst_gold, dst, 1e-5); + EXPECT_MAT_NEAR(dst_gold, dst, 1.0); } GPU_TEST_P(CvtColor, RGB2YCrCb) @@ -728,7 +728,7 @@ GPU_TEST_P(CvtColor, RGB2YCrCb) cv::Mat dst_gold; cv::cvtColor(src, dst_gold, cv::COLOR_RGB2YCrCb); - EXPECT_MAT_NEAR(dst_gold, dst, 1e-5); + EXPECT_MAT_NEAR(dst_gold, dst, 1.0); } GPU_TEST_P(CvtColor, BGR2YCrCb4) @@ -749,7 +749,7 @@ GPU_TEST_P(CvtColor, BGR2YCrCb4) cv::split(h_dst, channels); cv::merge(channels, 3, h_dst); - EXPECT_MAT_NEAR(dst_gold, h_dst, 1e-5); + EXPECT_MAT_NEAR(dst_gold, h_dst, 1.0); } GPU_TEST_P(CvtColor, RGBA2YCrCb4) @@ -771,7 +771,7 @@ GPU_TEST_P(CvtColor, RGBA2YCrCb4) cv::split(h_dst, channels); cv::merge(channels, 3, h_dst); - EXPECT_MAT_NEAR(dst_gold, h_dst, 1e-5); + EXPECT_MAT_NEAR(dst_gold, h_dst, 1.0); } GPU_TEST_P(CvtColor, YCrCb2BGR) diff --git a/modules/gpu/test/test_core.cpp b/modules/gpu/test/test_core.cpp index b622ad8ea9..1edc69b971 100644 --- a/modules/gpu/test/test_core.cpp +++ b/modules/gpu/test/test_core.cpp @@ -2353,7 +2353,7 @@ GPU_TEST_P(AddWeighted, Accuracy) cv::Mat dst_gold; cv::addWeighted(src1, alpha, src2, beta, gamma, dst_gold, dst_depth); - EXPECT_MAT_NEAR(dst_gold, dst, dst_depth < CV_32F ? 1.0 : 1e-3); + EXPECT_MAT_NEAR(dst_gold, dst, dst_depth < CV_32F ? 2.0 : 1e-3); } } @@ -3582,7 +3582,7 @@ GPU_TEST_P(Normalize, WithOutMask) cv::Mat dst_gold; cv::normalize(src, dst_gold, alpha, beta, norm_type, type); - EXPECT_MAT_NEAR(dst_gold, dst, 1e-6); + EXPECT_MAT_NEAR(dst_gold, dst, 1.0); } GPU_TEST_P(Normalize, WithMask) @@ -3598,7 +3598,7 @@ GPU_TEST_P(Normalize, WithMask) dst_gold.setTo(cv::Scalar::all(0)); cv::normalize(src, dst_gold, alpha, beta, norm_type, type, mask); - EXPECT_MAT_NEAR(dst_gold, dst, 1e-6); + EXPECT_MAT_NEAR(dst_gold, dst, 1.0); } INSTANTIATE_TEST_CASE_P(GPU_Core, Normalize, testing::Combine( diff --git a/modules/gpu/test/test_gpumat.cpp b/modules/gpu/test/test_gpumat.cpp index c7a0cabcbc..210b6a4415 100644 --- a/modules/gpu/test/test_gpumat.cpp +++ b/modules/gpu/test/test_gpumat.cpp @@ -281,7 +281,7 @@ GPU_TEST_P(ConvertTo, WithOutScaling) cv::Mat dst_gold; src.convertTo(dst_gold, depth2); - EXPECT_MAT_NEAR(dst_gold, dst, 0.0); + EXPECT_MAT_NEAR(dst_gold, dst, 1.0); } } From 44970ddf560a531f8ce71f9856855b7e2528b4e0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 30 Dec 2013 12:31:00 +0400 Subject: [PATCH 016/293] eliminate MINGW pragma warning --- modules/core/src/system.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 09daceed53..ee0799ce05 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -982,7 +982,9 @@ public: }; #ifdef WIN32 +#ifdef _MSC_VER #pragma warning(disable:4505) // unreferenced local function has been removed +#endif #ifdef HAVE_WINRT // using C++11 thread attribute for local thread data From 09d25e11c65fa20f9ddba1d25ee4f3344ba4e655 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 28 Dec 2013 15:09:45 +0400 Subject: [PATCH 017/293] fixed bug #3341 --- modules/core/src/arithm.cpp | 4 ++-- modules/core/test/test_arithm.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index a11b64b442..0517a5fae6 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -1272,8 +1272,8 @@ static void arithm_op(InputArray _src1, InputArray _src2, OutputArray _dst, bool haveScalar = false, swapped12 = false; int depth2 = src2.depth(); if( src1.size != src2.size || src1.channels() != src2.channels() || - ((kind1 == _InputArray::MATX || kind2 == _InputArray::MATX) && - src1.cols == 1 && src2.rows == 4) ) + (kind1 == _InputArray::MATX && (src1.size() == Size(1,4) || src1.size() == Size(1,1))) || + (kind2 == _InputArray::MATX && (src2.size() == Size(1,4) || src2.size() == Size(1,1))) ) { if( checkScalar(src1, src2.type(), kind1, kind2) ) { diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index 664fa0204a..f16c801713 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -1564,3 +1564,19 @@ TEST(Core_round, CvRound) ASSERT_EQ(-2, cvRound(-2.5)); ASSERT_EQ(-4, cvRound(-3.5)); } + + +typedef testing::TestWithParam Mul1; + +TEST_P(Mul1, One) +{ + Size size = GetParam(); + cv::Mat src(size, CV_32FC1, cv::Scalar::all(2)), dst, + ref_dst(size, CV_32FC1, cv::Scalar::all(6)); + + cv::multiply(3, src, dst); + + ASSERT_EQ(0, cv::norm(dst, ref_dst, cv::NORM_INF)); +} + +INSTANTIATE_TEST_CASE_P(Arithm, Mul1, testing::Values(Size(2, 2), Size(1, 1))); From 5db1754d499d21b9dda4a6ad36f98c22db97b994 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 28 Dec 2013 16:28:17 +0400 Subject: [PATCH 018/293] SSE2 optimization of cv::remap doesn't work with big images --- modules/imgproc/src/imgwarp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 2c87efe446..4748af2cee 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -2201,15 +2201,15 @@ struct RemapVec_8u int operator()( const Mat& _src, void* _dst, const short* XY, const ushort* FXY, const void* _wtab, int width ) const { - int cn = _src.channels(); + int cn = _src.channels(), x = 0, sstep = (int)_src.step; - if( (cn != 1 && cn != 3 && cn != 4) || !checkHardwareSupport(CV_CPU_SSE2) ) + if( (cn != 1 && cn != 3 && cn != 4) || !checkHardwareSupport(CV_CPU_SSE2) || + sstep > 0x8000 ) return 0; const uchar *S0 = _src.data, *S1 = _src.data + _src.step; const short* wtab = cn == 1 ? (const short*)_wtab : &BilinearTab_iC4[0][0][0]; uchar* D = (uchar*)_dst; - int x = 0, sstep = (int)_src.step; __m128i delta = _mm_set1_epi32(INTER_REMAP_COEF_SCALE/2); __m128i xy2ofs = _mm_set1_epi32(cn + (sstep << 16)); __m128i z = _mm_setzero_si128(); From 795c108f2bf2880a81a8d0db1ddc2da71c91864e Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Mon, 30 Dec 2013 18:00:29 +0400 Subject: [PATCH 019/293] Fixed MinGW build by declaring the minimal required Windows version. Also deleted miscellaneous remaining multimon cruft. Deleted #include , because includes it already. This should have a nice side effect of preventing us from accidentally using any Windows API that's too new. --- modules/highgui/src/precomp.hpp | 8 ++++++++ modules/highgui/src/window_w32.cpp | 16 ---------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/modules/highgui/src/precomp.hpp b/modules/highgui/src/precomp.hpp index 88ba8e4b20..af5383b142 100644 --- a/modules/highgui/src/precomp.hpp +++ b/modules/highgui/src/precomp.hpp @@ -57,6 +57,14 @@ #include #if defined WIN32 || defined WINCE + #if !defined _WIN32_WINNT + #ifdef HAVE_MSMF + #define _WIN32_WINNT 0x0600 // Windows Vista + #else + #define _WIN32_WINNT 0x0500 // Windows 2000 + #endif + #endif + #include #undef small #undef min diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 7b78ebc81f..59d66b100a 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -43,27 +43,11 @@ #if defined WIN32 || defined _WIN32 -#define COMPILE_MULTIMON_STUBS // Required for multi-monitor support -#ifndef _MULTIMON_USE_SECURE_CRT -# define _MULTIMON_USE_SECURE_CRT 0 // some MinGW platforms have no strncpy_s -#endif - -#if defined SM_CMONITORS && !defined MONITOR_DEFAULTTONEAREST -# define MONITOR_DEFAULTTONULL 0x00000000 -# define MONITOR_DEFAULTTOPRIMARY 0x00000001 -# define MONITOR_DEFAULTTONEAREST 0x00000002 -# define MONITORINFOF_PRIMARY 0x00000001 -#endif -#ifndef __inout -# define __inout -#endif - #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wmissing-declarations" #endif #include -#include #include #include #include From b036fc756a65c8be5b9b0e4d77d94b6f8099fc20 Mon Sep 17 00:00:00 2001 From: Seunghoon Park Date: Sat, 28 Dec 2013 14:45:41 -0500 Subject: [PATCH 020/293] fixing bug #3345 --- modules/imgproc/src/smooth.cpp | 2 +- modules/imgproc/test/test_filter.cpp | 30 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index bbce3deedf..ae14ca9e11 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -718,7 +718,7 @@ void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth, ddepth = sdepth; _dst.create( src.size(), CV_MAKETYPE(ddepth, cn) ); Mat dst = _dst.getMat(); - if( borderType != BORDER_CONSTANT && normalize ) + if( borderType != BORDER_CONSTANT && normalize && (borderType & BORDER_ISOLATED) != 0 ) { if( src.rows == 1 ) ksize.height = 1; diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index efbad99749..d1e45b0414 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -1886,3 +1886,33 @@ protected: }; TEST(Imgproc_Filtering, supportedFormats) { CV_FilterSupportedFormatsTest test; test.safe_run(); } + +TEST(Imgproc_Blur, borderTypes) +{ + Size kernelSize(3, 3); + + /// ksize > src_roi.size() + Mat src(3, 3, CV_8UC1, cv::Scalar::all(255)), dst; + Mat src_roi = src(Rect(1, 1, 1, 1)); + src_roi.setTo(cv::Scalar::all(0)); + + // should work like !BORDER_ISOLATED + blur(src_roi, dst, kernelSize, Point(-1, -1), BORDER_REPLICATE); + EXPECT_EQ(227, dst.at(0, 0)); + + // should work like BORDER_ISOLATED + blur(src_roi, dst, kernelSize, Point(-1, -1), BORDER_ISOLATED); + EXPECT_EQ(0, dst.at(0, 0)); + + /// ksize <= src_roi.size() + src = Mat(5, 5, CV_8UC1, cv::Scalar(255)); + src_roi = src(Rect(1, 1, 3, 3)); + src_roi.setTo(0); + src.at(2, 2) = 255; + + // should work like !BORDER_ISOLATED + blur(src_roi, dst, kernelSize, Point(-1, -1), BORDER_REPLICATE); + Mat expected_dst = + (Mat_(3, 3) << 170, 113, 170, 113, 28, 113, 170, 113, 170); + EXPECT_EQ(9 * 255, cv::sum(expected_dst == dst).val[0]); +} From 4f9c081dc313f8fdfee3f0a4572779ae13e27e40 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 28 Dec 2013 01:40:21 +0400 Subject: [PATCH 021/293] fixed bug #3319 --- modules/core/src/precomp.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index c53224e0aa..ec8dcc9f23 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -129,12 +129,14 @@ template struct OpMax inline Size getContinuousSize( const Mat& m1, int widthScale=1 ) { + CV_Assert(m1.dims <= 2); return m1.isContinuous() ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } inline Size getContinuousSize( const Mat& m1, const Mat& m2, int widthScale=1 ) { + CV_Assert(m1.dims <= 2 && m1.size() == m2.size()); return (m1.flags & m2.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } @@ -142,6 +144,7 @@ inline Size getContinuousSize( const Mat& m1, const Mat& m2, int widthScale=1 ) inline Size getContinuousSize( const Mat& m1, const Mat& m2, const Mat& m3, int widthScale=1 ) { + CV_Assert(m1.dims <= 2 && m1.size() == m2.size() && m1.size() == m3.size()); return (m1.flags & m2.flags & m3.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } @@ -150,6 +153,7 @@ inline Size getContinuousSize( const Mat& m1, const Mat& m2, const Mat& m3, const Mat& m4, int widthScale=1 ) { + CV_Assert(m1.dims <= 2 && m1.size() == m2.size() && m1.size() == m3.size() && m1.size() == m4.size()); return (m1.flags & m2.flags & m3.flags & m4.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } @@ -158,6 +162,7 @@ inline Size getContinuousSize( const Mat& m1, const Mat& m2, const Mat& m3, const Mat& m4, const Mat& m5, int widthScale=1 ) { + CV_Assert(m1.dims <= 2 && m1.size() == m2.size() && m1.size() == m3.size() && m1.size() == m4.size() && m1.size() == m5.size()); return (m1.flags & m2.flags & m3.flags & m4.flags & m5.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } From e21c6e19db0183e40d12b6634aec2d1923496336 Mon Sep 17 00:00:00 2001 From: Robbert Klarenbeek Date: Thu, 2 Jan 2014 21:17:55 +0100 Subject: [PATCH 022/293] Fix algorithm setter argument validation for uchar --- modules/core/src/algorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/algorithm.cpp b/modules/core/src/algorithm.cpp index f96f243cba..5f16f95ed4 100644 --- a/modules/core/src/algorithm.cpp +++ b/modules/core/src/algorithm.cpp @@ -647,7 +647,7 @@ void AlgorithmInfo::set(Algorithm* algo, const char* parameter, int argType, con || argType == Param::FLOAT || argType == Param::UNSIGNED_INT || argType == Param::UINT64 || argType == Param::UCHAR) { if ( !( p->type == Param::INT || p->type == Param::REAL || p->type == Param::BOOLEAN - || p->type == Param::UNSIGNED_INT || p->type == Param::UINT64 || p->type == Param::FLOAT || argType == Param::UCHAR + || p->type == Param::UNSIGNED_INT || p->type == Param::UINT64 || p->type == Param::FLOAT || p->type == Param::UCHAR || (p->type == Param::SHORT && argType == Param::INT)) ) { string message = getErrorMessageForWrongArgumentInSetter(algo->name(), parameter, p->type, argType); From d3e24f3cbff8a5f1503ff45cdf8d1d8118e4c049 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Fri, 3 Jan 2014 14:18:07 +1100 Subject: [PATCH 023/293] Improved documentation for neural network --- modules/ml/doc/neural_networks.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ml/doc/neural_networks.rst b/modules/ml/doc/neural_networks.rst index 0496e2201a..776bf243bd 100644 --- a/modules/ml/doc/neural_networks.rst +++ b/modules/ml/doc/neural_networks.rst @@ -240,6 +240,7 @@ This method applies the specified training algorithm to computing/adjusting the The RPROP training algorithm is parallelized with the TBB library. +If you are using the default ``cvANN_MLP::SIGMOID_SYM`` activation function then the output should be in the range [-1,1], instead of [0,1], for optimal results. CvANN_MLP::predict ------------------ @@ -257,6 +258,8 @@ Predicts responses for input samples. The method returns a dummy value which should be ignored. +If you are using the default ``cvANN_MLP::SIGMOID_SYM`` activation function with the default parameter values fparam1=0 and fparam2=0 then the function used is y = 1.7159*tanh(2/3 * x), so the output will range from [-1.7159, 1.7159], instead of [0,1]. + CvANN_MLP::get_layer_count -------------------------- Returns the number of layers in the MLP. From 3f458c6eb114bd46ce7f08b8ca471822ea16d26e Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Viel Date: Fri, 3 Jan 2014 13:16:36 +0100 Subject: [PATCH 024/293] Fix: freeing previous elements has to be done before loading new parameters to avoid trying to delete unexisting objects if arrays size was modified --- .../opencv2/flann/hierarchical_clustering_index.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h index c27b64834e..b511ee9089 100644 --- a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h +++ b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h @@ -414,12 +414,6 @@ public: void loadIndex(FILE* stream) { - load_value(stream, branching_); - load_value(stream, trees_); - load_value(stream, centers_init_); - load_value(stream, leaf_size_); - load_value(stream, memoryCounter); - free_elements(); if (root!=NULL) { @@ -430,6 +424,12 @@ public: delete[] indices; } + load_value(stream, branching_); + load_value(stream, trees_); + load_value(stream, centers_init_); + load_value(stream, leaf_size_); + load_value(stream, memoryCounter); + indices = new int*[trees_]; root = new NodePtr[trees_]; for (int i=0; i Date: Mon, 6 Jan 2014 02:24:14 +0900 Subject: [PATCH 025/293] Fix typo of SparseMat_<_Tp>::SparseMat_(const SparseMat& m) Fix compilation erros when compiling this constructor. First argument type of "convertTo" should be instance, not a pointer of instance. First pull request was created for master branch. But it should be marged for 2.4. https://github.com/Itseez/opencv/pull/2113 --- modules/core/include/opencv2/core/mat.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 8ddc16eb1d..45c25900c3 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -2401,7 +2401,7 @@ template inline SparseMat_<_Tp>::SparseMat_(const SparseMat& m) if( m.type() == DataType<_Tp>::type ) *this = (const SparseMat_<_Tp>&)m; else - m.convertTo(this, DataType<_Tp>::type); + m.convertTo(*this, DataType<_Tp>::type); } template inline SparseMat_<_Tp>::SparseMat_(const SparseMat_<_Tp>& m) From 601b7d1dd3a3ec9e8af5df461d43632d98ed3a7a Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Mon, 6 Jan 2014 20:19:07 +1100 Subject: [PATCH 026/293] Fixed a valgrind 'Conditional jump or move depends on uninitialised value(s)' on cv::kmeans(...). The original code used points(sampleCount, 1, CV_32FC2), which confused generateCentersPP into thinking it is a 1 dimensional center, instead of 2. As a result it would set only the x variable and leave y unitialised. --- samples/cpp/kmeans.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/cpp/kmeans.cpp b/samples/cpp/kmeans.cpp index 97de6a0a48..b742112059 100644 --- a/samples/cpp/kmeans.cpp +++ b/samples/cpp/kmeans.cpp @@ -33,7 +33,7 @@ int main( int /*argc*/, char** /*argv*/ ) { int k, clusterCount = rng.uniform(2, MAX_CLUSTERS+1); int i, sampleCount = rng.uniform(1, 1001); - Mat points(sampleCount, 1, CV_32FC2), labels; + Mat points(sampleCount, 2, CV_32F), labels; clusterCount = MIN(clusterCount, sampleCount); Mat centers(clusterCount, 1, points.type()); From 6b9ebcbf3d332f6963b2ab8c37d6a14223921e15 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 7 Jan 2014 02:38:41 +0400 Subject: [PATCH 027/293] deleted extra semicolons --- apps/haartraining/cvclassifier.h | 2 +- .../calib3d/test/test_chessboardgenerator.hpp | 2 +- modules/contrib/src/adaptiveskindetector.cpp | 18 +- modules/contrib/src/ba.cpp | 8 +- modules/contrib/src/facerec.cpp | 6 +- modules/contrib/src/fuzzymeanshifttracker.cpp | 82 +++---- modules/contrib/src/retina.cpp | 2 +- modules/contrib/src/spinimages.cpp | 2 +- modules/core/src/convert.cpp | 204 +++++++++--------- modules/core/src/copy.cpp | 20 +- modules/core/test/test_arithm.cpp | 62 +++--- modules/features2d/src/features2d_init.cpp | 30 +-- modules/gpu/include/opencv2/gpu/gpu.hpp | 4 +- modules/gpu/perf/perf_imgproc.cpp | 8 +- modules/highgui/src/bitstrm.hpp | 2 +- modules/highgui/src/cap_ffmpeg_impl.hpp | 4 +- modules/imgproc/perf/perf_filter2d.cpp | 2 +- modules/imgproc/src/gcgraph.hpp | 2 +- modules/imgproc/src/generalized_hough.cpp | 8 +- modules/imgproc/test/test_cvtyuv.cpp | 2 +- modules/legacy/src/blobtrack.cpp | 2 +- modules/legacy/src/enteringblobdetection.cpp | 4 +- modules/legacy/src/vecfacetracking.cpp | 2 +- modules/ml/src/ml_init.cpp | 2 +- modules/nonfree/src/nonfree_init.cpp | 4 +- modules/ocl/include/opencv2/ocl/ocl.hpp | 2 +- .../opencv2/ocl/private/opencl_utils.hpp | 2 +- modules/ocl/test/test_canny.cpp | 4 +- modules/ocl/test/test_match_template.cpp | 2 +- modules/ocl/test/test_objdetect.cpp | 2 +- modules/ocl/test/utility.hpp | 2 +- modules/superres/src/btv_l1.cpp | 2 +- modules/superres/src/btv_l1_ocl.cpp | 2 +- modules/superres/src/optical_flow.cpp | 20 +- modules/ts/include/opencv2/ts/ts_perf.hpp | 2 +- modules/video/perf/perf_optflowpyrlk.cpp | 2 +- modules/video/src/kalman.cpp | 2 +- modules/video/src/tvl1flow.cpp | 2 +- modules/video/src/video_init.cpp | 6 +- samples/c/adaptiveskindetector.cpp | 46 ++-- samples/cpp/calibration_artificial.cpp | 2 +- .../cpp/tutorial_code/ImgProc/Threshold.cpp | 2 +- 42 files changed, 293 insertions(+), 293 deletions(-) diff --git a/apps/haartraining/cvclassifier.h b/apps/haartraining/cvclassifier.h index df644ed173..3faec500ae 100644 --- a/apps/haartraining/cvclassifier.h +++ b/apps/haartraining/cvclassifier.h @@ -338,7 +338,7 @@ typedef enum CvBoostType CV_LKCLASS = 5, /* classification (K class problem) */ CV_LSREG = 6, /* least squares regression */ CV_LADREG = 7, /* least absolute deviation regression */ - CV_MREG = 8, /* M-regression (Huber loss) */ + CV_MREG = 8 /* M-regression (Huber loss) */ } CvBoostType; /****************************************************************************************\ diff --git a/modules/calib3d/test/test_chessboardgenerator.hpp b/modules/calib3d/test/test_chessboardgenerator.hpp index 48b3f3a243..6b669d2f50 100644 --- a/modules/calib3d/test/test_chessboardgenerator.hpp +++ b/modules/calib3d/test/test_chessboardgenerator.hpp @@ -34,7 +34,7 @@ private: Mat rvec, tvec; }; -}; +} #endif diff --git a/modules/contrib/src/adaptiveskindetector.cpp b/modules/contrib/src/adaptiveskindetector.cpp index 0865ad70b9..22f729d91e 100644 --- a/modules/contrib/src/adaptiveskindetector.cpp +++ b/modules/contrib/src/adaptiveskindetector.cpp @@ -53,7 +53,7 @@ void CvAdaptiveSkinDetector::initData(IplImage *src, int widthDivider, int heigh imgGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1); imgLastGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1); imgHSVFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 3); -}; +} CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int samplingDivider, int morphingMethod) { @@ -78,7 +78,7 @@ CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int samplingDivider, int morphing imgLastGrayFrame = NULL; imgSaturationFrame = NULL; imgHSVFrame = NULL; -}; +} CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector() { @@ -91,7 +91,7 @@ CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector() cvReleaseImage(&imgGrayFrame); cvReleaseImage(&imgLastGrayFrame); cvReleaseImage(&imgHSVFrame); -}; +} void CvAdaptiveSkinDetector::process(IplImage *inputBGRImage, IplImage *outputHueMask) { @@ -188,7 +188,7 @@ void CvAdaptiveSkinDetector::process(IplImage *inputBGRImage, IplImage *outputHu if (outputHueMask != NULL) cvCopy(imgFilteredFrame, outputHueMask); -}; +} //------------------------- Histogram for Adaptive Skin Detector -------------------------// @@ -200,12 +200,12 @@ CvAdaptiveSkinDetector::Histogram::Histogram() float *ranges[] = { range }; fHistogram = cvCreateHist(1, histogramSize, CV_HIST_ARRAY, ranges, 1); cvClearHist(fHistogram); -}; +} CvAdaptiveSkinDetector::Histogram::~Histogram() { cvReleaseHist(&fHistogram); -}; +} int CvAdaptiveSkinDetector::Histogram::findCoverageIndex(double surfaceToCover, int defaultValue) { @@ -219,7 +219,7 @@ int CvAdaptiveSkinDetector::Histogram::findCoverageIndex(double surfaceToCover, } } return defaultValue; -}; +} void CvAdaptiveSkinDetector::Histogram::findCurveThresholds(int &x1, int &x2, double percent) { @@ -242,7 +242,7 @@ void CvAdaptiveSkinDetector::Histogram::findCurveThresholds(int &x1, int &x2, do x2 = GSD_HUE_UT; else x2 += GSD_HUE_LT; -}; +} void CvAdaptiveSkinDetector::Histogram::mergeWith(CvAdaptiveSkinDetector::Histogram *source, double weight) { @@ -283,4 +283,4 @@ void CvAdaptiveSkinDetector::Histogram::mergeWith(CvAdaptiveSkinDetector::Histog } } } -}; +} diff --git a/modules/contrib/src/ba.cpp b/modules/contrib/src/ba.cpp index 0e2afd95bb..ec90cb79a4 100644 --- a/modules/contrib/src/ba.cpp +++ b/modules/contrib/src/ba.cpp @@ -938,7 +938,7 @@ static void fjac(int /*i*/, int /*j*/, CvMat *point_params, CvMat* cam_params, C #endif -}; +} static void func(int /*i*/, int /*j*/, CvMat *point_params, CvMat* cam_params, CvMat* estim, void* /*data*/) { //just do projections CvMat _Mi; @@ -977,17 +977,17 @@ static void func(int /*i*/, int /*j*/, CvMat *point_params, CvMat* cam_params, C cvTranspose( _mp2, estim ); cvReleaseMat( &_mp ); cvReleaseMat( &_mp2 ); -}; +} static void fjac_new(int i, int j, Mat& point_params, Mat& cam_params, Mat& A, Mat& B, void* data) { CvMat _point_params = point_params, _cam_params = cam_params, _Al = A, _Bl = B; fjac(i,j, &_point_params, &_cam_params, &_Al, &_Bl, data); -}; +} static void func_new(int i, int j, Mat& point_params, Mat& cam_params, Mat& estim, void* data) { CvMat _point_params = point_params, _cam_params = cam_params, _estim = estim; func(i,j,&_point_params,&_cam_params,&_estim,data); -}; +} void LevMarqSparse::bundleAdjust( vector& points, //positions of points in global coordinate system (input and output) const vector >& imagePoints, //projections of 3d points for every camera diff --git a/modules/contrib/src/facerec.cpp b/modules/contrib/src/facerec.cpp index bd202d2065..a3d695ad16 100644 --- a/modules/contrib/src/facerec.cpp +++ b/modules/contrib/src/facerec.cpp @@ -873,7 +873,7 @@ CV_INIT_ALGORITHM(Eigenfaces, "FaceRecognizer.Eigenfaces", obj.info()->addParam(obj, "labels", obj._labels, true); obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true); obj.info()->addParam(obj, "eigenvalues", obj._eigenvalues, true); - obj.info()->addParam(obj, "mean", obj._mean, true)); + obj.info()->addParam(obj, "mean", obj._mean, true)) CV_INIT_ALGORITHM(Fisherfaces, "FaceRecognizer.Fisherfaces", obj.info()->addParam(obj, "ncomponents", obj._num_components); @@ -882,7 +882,7 @@ CV_INIT_ALGORITHM(Fisherfaces, "FaceRecognizer.Fisherfaces", obj.info()->addParam(obj, "labels", obj._labels, true); obj.info()->addParam(obj, "eigenvectors", obj._eigenvectors, true); obj.info()->addParam(obj, "eigenvalues", obj._eigenvalues, true); - obj.info()->addParam(obj, "mean", obj._mean, true)); + obj.info()->addParam(obj, "mean", obj._mean, true)) CV_INIT_ALGORITHM(LBPH, "FaceRecognizer.LBPH", obj.info()->addParam(obj, "radius", obj._radius); @@ -891,7 +891,7 @@ CV_INIT_ALGORITHM(LBPH, "FaceRecognizer.LBPH", obj.info()->addParam(obj, "grid_y", obj._grid_y); obj.info()->addParam(obj, "threshold", obj._threshold); obj.info()->addParam(obj, "histograms", obj._histograms, true); - obj.info()->addParam(obj, "labels", obj._labels, true)); + obj.info()->addParam(obj, "labels", obj._labels, true)) bool initModule_contrib() { diff --git a/modules/contrib/src/fuzzymeanshifttracker.cpp b/modules/contrib/src/fuzzymeanshifttracker.cpp index 0ac6d24437..1932ad595f 100644 --- a/modules/contrib/src/fuzzymeanshifttracker.cpp +++ b/modules/contrib/src/fuzzymeanshifttracker.cpp @@ -40,7 +40,7 @@ CvFuzzyPoint::CvFuzzyPoint(double _x, double _y) { x = _x; y = _y; -}; +} bool CvFuzzyCurve::between(double x, double x1, double x2) { @@ -50,37 +50,37 @@ bool CvFuzzyCurve::between(double x, double x1, double x2) return true; return false; -}; +} CvFuzzyCurve::CvFuzzyCurve() { value = 0; -}; +} CvFuzzyCurve::~CvFuzzyCurve() { // nothing to do -}; +} void CvFuzzyCurve::setCentre(double _centre) { centre = _centre; -}; +} double CvFuzzyCurve::getCentre() { return centre; -}; +} void CvFuzzyCurve::clear() { points.clear(); -}; +} void CvFuzzyCurve::addPoint(double x, double y) { points.push_back(CvFuzzyPoint(x, y)); -}; +} double CvFuzzyCurve::calcValue(double param) { @@ -101,41 +101,41 @@ double CvFuzzyCurve::calcValue(double param) } } return 0; -}; +} double CvFuzzyCurve::getValue() { return value; -}; +} void CvFuzzyCurve::setValue(double _value) { value = _value; -}; +} CvFuzzyFunction::CvFuzzyFunction() { // nothing to do -}; +} CvFuzzyFunction::~CvFuzzyFunction() { curves.clear(); -}; +} void CvFuzzyFunction::addCurve(CvFuzzyCurve *curve, double value) { curves.push_back(*curve); curve->setValue(value); -}; +} void CvFuzzyFunction::resetValues() { int numCurves = (int)curves.size(); for (int i = 0; i < numCurves; i++) curves[i].setValue(0); -}; +} double CvFuzzyFunction::calcValue() { @@ -152,7 +152,7 @@ double CvFuzzyFunction::calcValue() return s1/s2; else return 0; -}; +} CvFuzzyCurve *CvFuzzyFunction::newCurve() { @@ -160,14 +160,14 @@ CvFuzzyCurve *CvFuzzyFunction::newCurve() c = new CvFuzzyCurve(); addCurve(c); return c; -}; +} CvFuzzyRule::CvFuzzyRule() { fuzzyInput1 = NULL; fuzzyInput2 = NULL; fuzzyOutput = NULL; -}; +} CvFuzzyRule::~CvFuzzyRule() { @@ -179,14 +179,14 @@ CvFuzzyRule::~CvFuzzyRule() if (fuzzyOutput != NULL) delete fuzzyOutput; -}; +} void CvFuzzyRule::setRule(CvFuzzyCurve *c1, CvFuzzyCurve *c2, CvFuzzyCurve *o1) { fuzzyInput1 = c1; fuzzyInput2 = c2; fuzzyOutput = o1; -}; +} double CvFuzzyRule::calcValue(double param1, double param2) { @@ -202,31 +202,31 @@ double CvFuzzyRule::calcValue(double param1, double param2) } else return v1; -}; +} CvFuzzyCurve *CvFuzzyRule::getOutputCurve() { return fuzzyOutput; -}; +} CvFuzzyController::CvFuzzyController() { // nothing to do -}; +} CvFuzzyController::~CvFuzzyController() { int size = (int)rules.size(); for(int i = 0; i < size; i++) delete rules[i]; -}; +} void CvFuzzyController::addRule(CvFuzzyCurve *c1, CvFuzzyCurve *c2, CvFuzzyCurve *o1) { CvFuzzyRule *f = new CvFuzzyRule(); rules.push_back(f); f->setRule(c1, c2, o1); -}; +} double CvFuzzyController::calcOutput(double param1, double param2) { @@ -242,7 +242,7 @@ double CvFuzzyController::calcOutput(double param1, double param2) } v = list.calcValue(); return v; -}; +} CvFuzzyMeanShiftTracker::FuzzyResizer::FuzzyResizer() { @@ -298,12 +298,12 @@ CvFuzzyMeanShiftTracker::FuzzyResizer::FuzzyResizer() fuzzyController.addRule(i1L, NULL, oS); fuzzyController.addRule(i1M, NULL, oZE); fuzzyController.addRule(i1H, NULL, oE); -}; +} int CvFuzzyMeanShiftTracker::FuzzyResizer::calcOutput(double edgeDensity, double density) { return (int)fuzzyController.calcOutput(edgeDensity, density); -}; +} CvFuzzyMeanShiftTracker::SearchWindow::SearchWindow() { @@ -328,7 +328,7 @@ CvFuzzyMeanShiftTracker::SearchWindow::SearchWindow() depthLow = 0; depthHigh = 0; fuzzyResizer = NULL; -}; +} CvFuzzyMeanShiftTracker::SearchWindow::~SearchWindow() { @@ -354,7 +354,7 @@ void CvFuzzyMeanShiftTracker::SearchWindow::setSize(int _x, int _y, int _width, if (y + height > maxHeight) height = maxHeight - y; -}; +} void CvFuzzyMeanShiftTracker::SearchWindow::initDepthValues(IplImage *maskImage, IplImage *depthMap) { @@ -408,7 +408,7 @@ void CvFuzzyMeanShiftTracker::SearchWindow::initDepthValues(IplImage *maskImage, depthHigh = 32000; depthLow = 0; } -}; +} bool CvFuzzyMeanShiftTracker::SearchWindow::shift() { @@ -421,7 +421,7 @@ bool CvFuzzyMeanShiftTracker::SearchWindow::shift() { return false; } -}; +} void CvFuzzyMeanShiftTracker::SearchWindow::extractInfo(IplImage *maskImage, IplImage *depthMap, bool initDepth) { @@ -527,7 +527,7 @@ void CvFuzzyMeanShiftTracker::SearchWindow::extractInfo(IplImage *maskImage, Ipl ellipseAngle = 0; density = 0; } -}; +} void CvFuzzyMeanShiftTracker::SearchWindow::getResizeAttribsEdgeDensityLinear(int &resizeDx, int &resizeDy, int &resizeDw, int &resizeDh) { int x1 = horizontalEdgeTop; @@ -571,7 +571,7 @@ void CvFuzzyMeanShiftTracker::SearchWindow::getResizeAttribsEdgeDensityLinear(in } else { resizeDw = - resizeDx; } -}; +} void CvFuzzyMeanShiftTracker::SearchWindow::getResizeAttribsInnerDensity(int &resizeDx, int &resizeDy, int &resizeDw, int &resizeDh) { @@ -587,7 +587,7 @@ void CvFuzzyMeanShiftTracker::SearchWindow::getResizeAttribsInnerDensity(int &re resizeDy = (int)(py*dy); resizeDw = (int)((1-px)*dx); resizeDh = (int)((1-py)*dy); -}; +} void CvFuzzyMeanShiftTracker::SearchWindow::getResizeAttribsEdgeDensityFuzzy(int &resizeDx, int &resizeDy, int &resizeDw, int &resizeDh) { @@ -626,7 +626,7 @@ void CvFuzzyMeanShiftTracker::SearchWindow::getResizeAttribsEdgeDensityFuzzy(int resizeDy = int(-dy1); resizeDh = int(dy1+dy2); } -}; +} bool CvFuzzyMeanShiftTracker::SearchWindow::meanShift(IplImage *maskImage, IplImage *depthMap, int maxIteration, bool initDepth) { @@ -639,7 +639,7 @@ bool CvFuzzyMeanShiftTracker::SearchWindow::meanShift(IplImage *maskImage, IplIm } while (++numShifts < maxIteration); return false; -}; +} void CvFuzzyMeanShiftTracker::findOptimumSearchWindow(SearchWindow &searchWindow, IplImage *maskImage, IplImage *depthMap, int maxIteration, int resizeMethod, bool initDepth) { @@ -679,17 +679,17 @@ void CvFuzzyMeanShiftTracker::findOptimumSearchWindow(SearchWindow &searchWindow searchWindow.setSize(searchWindow.x + resizeDx, searchWindow.y + resizeDy, searchWindow.width + resizeDw, searchWindow.height + resizeDh); } -}; +} CvFuzzyMeanShiftTracker::CvFuzzyMeanShiftTracker() { searchMode = tsSetWindow; -}; +} CvFuzzyMeanShiftTracker::~CvFuzzyMeanShiftTracker() { // nothing to do -}; +} void CvFuzzyMeanShiftTracker::track(IplImage *maskImage, IplImage *depthMap, int resizeMethod, bool resetSearch, int minKernelMass) { @@ -717,4 +717,4 @@ void CvFuzzyMeanShiftTracker::track(IplImage *maskImage, IplImage *depthMap, int else searchMode = tsTracking; } -}; +} diff --git a/modules/contrib/src/retina.cpp b/modules/contrib/src/retina.cpp index 6f08337cc0..4a02bbcb4a 100644 --- a/modules/contrib/src/retina.cpp +++ b/modules/contrib/src/retina.cpp @@ -85,7 +85,7 @@ Retina::Retina(const cv::Size inputSz, const bool colorMode, RETINA_COLORSAMPLIN { _retinaFilter = 0; _init(inputSz, colorMode, colorSamplingMethod, useRetinaLogSampling, reductionFactor, samplingStrenght); -}; +} Retina::~Retina() { diff --git a/modules/contrib/src/spinimages.cpp b/modules/contrib/src/spinimages.cpp index b010900579..747bf3e125 100644 --- a/modules/contrib/src/spinimages.cpp +++ b/modules/contrib/src/spinimages.cpp @@ -718,7 +718,7 @@ void cv::SpinImageModel::defaultParams() T_GeometriccConsistency = 0.25f; T_GroupingCorespondances = 0.25f; -}; +} Mat cv::SpinImageModel::packRandomScaledSpins(bool separateScale, size_t xCount, size_t yCount) const { diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index 3de9f83d1a..c37208b5ca 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -839,122 +839,122 @@ stype* dst, size_t dstep, Size size, double*) \ } -DEF_CVT_SCALE_ABS_FUNC(8u, cvtScaleAbs_, uchar, uchar, float); -DEF_CVT_SCALE_ABS_FUNC(8s8u, cvtScaleAbs_, schar, uchar, float); -DEF_CVT_SCALE_ABS_FUNC(16u8u, cvtScaleAbs_, ushort, uchar, float); -DEF_CVT_SCALE_ABS_FUNC(16s8u, cvtScaleAbs_, short, uchar, float); -DEF_CVT_SCALE_ABS_FUNC(32s8u, cvtScaleAbs_, int, uchar, float); -DEF_CVT_SCALE_ABS_FUNC(32f8u, cvtScaleAbs_, float, uchar, float); -DEF_CVT_SCALE_ABS_FUNC(64f8u, cvtScaleAbs_, double, uchar, float); +DEF_CVT_SCALE_ABS_FUNC(8u, cvtScaleAbs_, uchar, uchar, float) +DEF_CVT_SCALE_ABS_FUNC(8s8u, cvtScaleAbs_, schar, uchar, float) +DEF_CVT_SCALE_ABS_FUNC(16u8u, cvtScaleAbs_, ushort, uchar, float) +DEF_CVT_SCALE_ABS_FUNC(16s8u, cvtScaleAbs_, short, uchar, float) +DEF_CVT_SCALE_ABS_FUNC(32s8u, cvtScaleAbs_, int, uchar, float) +DEF_CVT_SCALE_ABS_FUNC(32f8u, cvtScaleAbs_, float, uchar, float) +DEF_CVT_SCALE_ABS_FUNC(64f8u, cvtScaleAbs_, double, uchar, float) -DEF_CVT_SCALE_FUNC(8u, uchar, uchar, float); -DEF_CVT_SCALE_FUNC(8s8u, schar, uchar, float); -DEF_CVT_SCALE_FUNC(16u8u, ushort, uchar, float); -DEF_CVT_SCALE_FUNC(16s8u, short, uchar, float); -DEF_CVT_SCALE_FUNC(32s8u, int, uchar, float); -DEF_CVT_SCALE_FUNC(32f8u, float, uchar, float); -DEF_CVT_SCALE_FUNC(64f8u, double, uchar, float); +DEF_CVT_SCALE_FUNC(8u, uchar, uchar, float) +DEF_CVT_SCALE_FUNC(8s8u, schar, uchar, float) +DEF_CVT_SCALE_FUNC(16u8u, ushort, uchar, float) +DEF_CVT_SCALE_FUNC(16s8u, short, uchar, float) +DEF_CVT_SCALE_FUNC(32s8u, int, uchar, float) +DEF_CVT_SCALE_FUNC(32f8u, float, uchar, float) +DEF_CVT_SCALE_FUNC(64f8u, double, uchar, float) -DEF_CVT_SCALE_FUNC(8u8s, uchar, schar, float); -DEF_CVT_SCALE_FUNC(8s, schar, schar, float); -DEF_CVT_SCALE_FUNC(16u8s, ushort, schar, float); -DEF_CVT_SCALE_FUNC(16s8s, short, schar, float); -DEF_CVT_SCALE_FUNC(32s8s, int, schar, float); -DEF_CVT_SCALE_FUNC(32f8s, float, schar, float); -DEF_CVT_SCALE_FUNC(64f8s, double, schar, float); +DEF_CVT_SCALE_FUNC(8u8s, uchar, schar, float) +DEF_CVT_SCALE_FUNC(8s, schar, schar, float) +DEF_CVT_SCALE_FUNC(16u8s, ushort, schar, float) +DEF_CVT_SCALE_FUNC(16s8s, short, schar, float) +DEF_CVT_SCALE_FUNC(32s8s, int, schar, float) +DEF_CVT_SCALE_FUNC(32f8s, float, schar, float) +DEF_CVT_SCALE_FUNC(64f8s, double, schar, float) -DEF_CVT_SCALE_FUNC(8u16u, uchar, ushort, float); -DEF_CVT_SCALE_FUNC(8s16u, schar, ushort, float); -DEF_CVT_SCALE_FUNC(16u, ushort, ushort, float); -DEF_CVT_SCALE_FUNC(16s16u, short, ushort, float); -DEF_CVT_SCALE_FUNC(32s16u, int, ushort, float); -DEF_CVT_SCALE_FUNC(32f16u, float, ushort, float); -DEF_CVT_SCALE_FUNC(64f16u, double, ushort, float); +DEF_CVT_SCALE_FUNC(8u16u, uchar, ushort, float) +DEF_CVT_SCALE_FUNC(8s16u, schar, ushort, float) +DEF_CVT_SCALE_FUNC(16u, ushort, ushort, float) +DEF_CVT_SCALE_FUNC(16s16u, short, ushort, float) +DEF_CVT_SCALE_FUNC(32s16u, int, ushort, float) +DEF_CVT_SCALE_FUNC(32f16u, float, ushort, float) +DEF_CVT_SCALE_FUNC(64f16u, double, ushort, float) -DEF_CVT_SCALE_FUNC(8u16s, uchar, short, float); -DEF_CVT_SCALE_FUNC(8s16s, schar, short, float); -DEF_CVT_SCALE_FUNC(16u16s, ushort, short, float); -DEF_CVT_SCALE_FUNC(16s, short, short, float); -DEF_CVT_SCALE_FUNC(32s16s, int, short, float); -DEF_CVT_SCALE_FUNC(32f16s, float, short, float); -DEF_CVT_SCALE_FUNC(64f16s, double, short, float); +DEF_CVT_SCALE_FUNC(8u16s, uchar, short, float) +DEF_CVT_SCALE_FUNC(8s16s, schar, short, float) +DEF_CVT_SCALE_FUNC(16u16s, ushort, short, float) +DEF_CVT_SCALE_FUNC(16s, short, short, float) +DEF_CVT_SCALE_FUNC(32s16s, int, short, float) +DEF_CVT_SCALE_FUNC(32f16s, float, short, float) +DEF_CVT_SCALE_FUNC(64f16s, double, short, float) -DEF_CVT_SCALE_FUNC(8u32s, uchar, int, float); -DEF_CVT_SCALE_FUNC(8s32s, schar, int, float); -DEF_CVT_SCALE_FUNC(16u32s, ushort, int, float); -DEF_CVT_SCALE_FUNC(16s32s, short, int, float); -DEF_CVT_SCALE_FUNC(32s, int, int, double); -DEF_CVT_SCALE_FUNC(32f32s, float, int, float); -DEF_CVT_SCALE_FUNC(64f32s, double, int, double); +DEF_CVT_SCALE_FUNC(8u32s, uchar, int, float) +DEF_CVT_SCALE_FUNC(8s32s, schar, int, float) +DEF_CVT_SCALE_FUNC(16u32s, ushort, int, float) +DEF_CVT_SCALE_FUNC(16s32s, short, int, float) +DEF_CVT_SCALE_FUNC(32s, int, int, double) +DEF_CVT_SCALE_FUNC(32f32s, float, int, float) +DEF_CVT_SCALE_FUNC(64f32s, double, int, double) -DEF_CVT_SCALE_FUNC(8u32f, uchar, float, float); -DEF_CVT_SCALE_FUNC(8s32f, schar, float, float); -DEF_CVT_SCALE_FUNC(16u32f, ushort, float, float); -DEF_CVT_SCALE_FUNC(16s32f, short, float, float); -DEF_CVT_SCALE_FUNC(32s32f, int, float, double); -DEF_CVT_SCALE_FUNC(32f, float, float, float); -DEF_CVT_SCALE_FUNC(64f32f, double, float, double); +DEF_CVT_SCALE_FUNC(8u32f, uchar, float, float) +DEF_CVT_SCALE_FUNC(8s32f, schar, float, float) +DEF_CVT_SCALE_FUNC(16u32f, ushort, float, float) +DEF_CVT_SCALE_FUNC(16s32f, short, float, float) +DEF_CVT_SCALE_FUNC(32s32f, int, float, double) +DEF_CVT_SCALE_FUNC(32f, float, float, float) +DEF_CVT_SCALE_FUNC(64f32f, double, float, double) -DEF_CVT_SCALE_FUNC(8u64f, uchar, double, double); -DEF_CVT_SCALE_FUNC(8s64f, schar, double, double); -DEF_CVT_SCALE_FUNC(16u64f, ushort, double, double); -DEF_CVT_SCALE_FUNC(16s64f, short, double, double); -DEF_CVT_SCALE_FUNC(32s64f, int, double, double); -DEF_CVT_SCALE_FUNC(32f64f, float, double, double); -DEF_CVT_SCALE_FUNC(64f, double, double, double); +DEF_CVT_SCALE_FUNC(8u64f, uchar, double, double) +DEF_CVT_SCALE_FUNC(8s64f, schar, double, double) +DEF_CVT_SCALE_FUNC(16u64f, ushort, double, double) +DEF_CVT_SCALE_FUNC(16s64f, short, double, double) +DEF_CVT_SCALE_FUNC(32s64f, int, double, double) +DEF_CVT_SCALE_FUNC(32f64f, float, double, double) +DEF_CVT_SCALE_FUNC(64f, double, double, double) -DEF_CPY_FUNC(8u, uchar); -DEF_CVT_FUNC(8s8u, schar, uchar); -DEF_CVT_FUNC(16u8u, ushort, uchar); -DEF_CVT_FUNC(16s8u, short, uchar); -DEF_CVT_FUNC(32s8u, int, uchar); -DEF_CVT_FUNC(32f8u, float, uchar); -DEF_CVT_FUNC(64f8u, double, uchar); +DEF_CPY_FUNC(8u, uchar) +DEF_CVT_FUNC(8s8u, schar, uchar) +DEF_CVT_FUNC(16u8u, ushort, uchar) +DEF_CVT_FUNC(16s8u, short, uchar) +DEF_CVT_FUNC(32s8u, int, uchar) +DEF_CVT_FUNC(32f8u, float, uchar) +DEF_CVT_FUNC(64f8u, double, uchar) -DEF_CVT_FUNC(8u8s, uchar, schar); -DEF_CVT_FUNC(16u8s, ushort, schar); -DEF_CVT_FUNC(16s8s, short, schar); -DEF_CVT_FUNC(32s8s, int, schar); -DEF_CVT_FUNC(32f8s, float, schar); -DEF_CVT_FUNC(64f8s, double, schar); +DEF_CVT_FUNC(8u8s, uchar, schar) +DEF_CVT_FUNC(16u8s, ushort, schar) +DEF_CVT_FUNC(16s8s, short, schar) +DEF_CVT_FUNC(32s8s, int, schar) +DEF_CVT_FUNC(32f8s, float, schar) +DEF_CVT_FUNC(64f8s, double, schar) -DEF_CVT_FUNC(8u16u, uchar, ushort); -DEF_CVT_FUNC(8s16u, schar, ushort); -DEF_CPY_FUNC(16u, ushort); -DEF_CVT_FUNC(16s16u, short, ushort); -DEF_CVT_FUNC(32s16u, int, ushort); -DEF_CVT_FUNC(32f16u, float, ushort); -DEF_CVT_FUNC(64f16u, double, ushort); +DEF_CVT_FUNC(8u16u, uchar, ushort) +DEF_CVT_FUNC(8s16u, schar, ushort) +DEF_CPY_FUNC(16u, ushort) +DEF_CVT_FUNC(16s16u, short, ushort) +DEF_CVT_FUNC(32s16u, int, ushort) +DEF_CVT_FUNC(32f16u, float, ushort) +DEF_CVT_FUNC(64f16u, double, ushort) -DEF_CVT_FUNC(8u16s, uchar, short); -DEF_CVT_FUNC(8s16s, schar, short); -DEF_CVT_FUNC(16u16s, ushort, short); -DEF_CVT_FUNC(32s16s, int, short); -DEF_CVT_FUNC(32f16s, float, short); -DEF_CVT_FUNC(64f16s, double, short); +DEF_CVT_FUNC(8u16s, uchar, short) +DEF_CVT_FUNC(8s16s, schar, short) +DEF_CVT_FUNC(16u16s, ushort, short) +DEF_CVT_FUNC(32s16s, int, short) +DEF_CVT_FUNC(32f16s, float, short) +DEF_CVT_FUNC(64f16s, double, short) -DEF_CVT_FUNC(8u32s, uchar, int); -DEF_CVT_FUNC(8s32s, schar, int); -DEF_CVT_FUNC(16u32s, ushort, int); -DEF_CVT_FUNC(16s32s, short, int); -DEF_CPY_FUNC(32s, int); -DEF_CVT_FUNC(32f32s, float, int); -DEF_CVT_FUNC(64f32s, double, int); +DEF_CVT_FUNC(8u32s, uchar, int) +DEF_CVT_FUNC(8s32s, schar, int) +DEF_CVT_FUNC(16u32s, ushort, int) +DEF_CVT_FUNC(16s32s, short, int) +DEF_CPY_FUNC(32s, int) +DEF_CVT_FUNC(32f32s, float, int) +DEF_CVT_FUNC(64f32s, double, int) -DEF_CVT_FUNC(8u32f, uchar, float); -DEF_CVT_FUNC(8s32f, schar, float); -DEF_CVT_FUNC(16u32f, ushort, float); -DEF_CVT_FUNC(16s32f, short, float); -DEF_CVT_FUNC(32s32f, int, float); -DEF_CVT_FUNC(64f32f, double, float); +DEF_CVT_FUNC(8u32f, uchar, float) +DEF_CVT_FUNC(8s32f, schar, float) +DEF_CVT_FUNC(16u32f, ushort, float) +DEF_CVT_FUNC(16s32f, short, float) +DEF_CVT_FUNC(32s32f, int, float) +DEF_CVT_FUNC(64f32f, double, float) -DEF_CVT_FUNC(8u64f, uchar, double); -DEF_CVT_FUNC(8s64f, schar, double); -DEF_CVT_FUNC(16u64f, ushort, double); -DEF_CVT_FUNC(16s64f, short, double); -DEF_CVT_FUNC(32s64f, int, double); -DEF_CVT_FUNC(32f64f, float, double); -DEF_CPY_FUNC(64s, int64); +DEF_CVT_FUNC(8u64f, uchar, double) +DEF_CVT_FUNC(8s64f, schar, double) +DEF_CVT_FUNC(16u64f, ushort, double) +DEF_CVT_FUNC(16s64f, short, double) +DEF_CVT_FUNC(32s64f, int, double) +DEF_CVT_FUNC(32f64f, float, double) +DEF_CPY_FUNC(64s, int64) static BinaryFunc getCvtScaleAbsFunc(int depth) { diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index cc26b3eb35..894776bb32 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -166,16 +166,16 @@ static void copyMask##suffix(const uchar* src, size_t sstep, const uchar* mask, } -DEF_COPY_MASK(8u, uchar); -DEF_COPY_MASK(16u, ushort); -DEF_COPY_MASK(8uC3, Vec3b); -DEF_COPY_MASK(32s, int); -DEF_COPY_MASK(16uC3, Vec3s); -DEF_COPY_MASK(32sC2, Vec2i); -DEF_COPY_MASK(32sC3, Vec3i); -DEF_COPY_MASK(32sC4, Vec4i); -DEF_COPY_MASK(32sC6, Vec6i); -DEF_COPY_MASK(32sC8, Vec8i); +DEF_COPY_MASK(8u, uchar) +DEF_COPY_MASK(16u, ushort) +DEF_COPY_MASK(8uC3, Vec3b) +DEF_COPY_MASK(32s, int) +DEF_COPY_MASK(16uC3, Vec3s) +DEF_COPY_MASK(32sC2, Vec2i) +DEF_COPY_MASK(32sC3, Vec3i) +DEF_COPY_MASK(32sC4, Vec4i) +DEF_COPY_MASK(32sC6, Vec6i) +DEF_COPY_MASK(32sC8, Vec8i) BinaryFunc copyMaskTab[] = { diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index f16c801713..8fec388df8 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -115,7 +115,7 @@ struct BaseAddOp : public BaseElemWiseOp struct AddOp : public BaseAddOp { - AddOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}; + AddOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { if( mask.empty() ) @@ -128,7 +128,7 @@ struct AddOp : public BaseAddOp struct SubOp : public BaseAddOp { - SubOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, -1, Scalar::all(0)) {}; + SubOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, -1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { if( mask.empty() ) @@ -141,7 +141,7 @@ struct SubOp : public BaseAddOp struct AddSOp : public BaseAddOp { - AddSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 0, Scalar::all(0)) {}; + AddSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 0, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { if( mask.empty() ) @@ -154,7 +154,7 @@ struct AddSOp : public BaseAddOp struct SubRSOp : public BaseAddOp { - SubRSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, -1, 0, Scalar::all(0)) {}; + SubRSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, -1, 0, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { if( mask.empty() ) @@ -167,7 +167,7 @@ struct SubRSOp : public BaseAddOp struct ScaleAddOp : public BaseAddOp { - ScaleAddOp() : BaseAddOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + ScaleAddOp() : BaseAddOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { scaleAdd(src[0], alpha, src[1], dst); @@ -181,7 +181,7 @@ struct ScaleAddOp : public BaseAddOp struct AddWeightedOp : public BaseAddOp { - AddWeightedOp() : BaseAddOp(2, REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + AddWeightedOp() : BaseAddOp(2, REAL_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { addWeighted(src[0], alpha, src[1], beta, gamma[0], dst); @@ -194,7 +194,7 @@ struct AddWeightedOp : public BaseAddOp struct MulOp : public BaseElemWiseOp { - MulOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + MulOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void getValueRange(int depth, double& minval, double& maxval) { minval = depth < CV_32S ? cvtest::getMinVal(depth) : depth == CV_32S ? -1000000 : -1000.; @@ -218,7 +218,7 @@ struct MulOp : public BaseElemWiseOp struct DivOp : public BaseElemWiseOp { - DivOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + DivOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::divide(src[0], src[1], dst, alpha); @@ -235,7 +235,7 @@ struct DivOp : public BaseElemWiseOp struct RecipOp : public BaseElemWiseOp { - RecipOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + RecipOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::divide(alpha, src[0], dst); @@ -252,7 +252,7 @@ struct RecipOp : public BaseElemWiseOp struct AbsDiffOp : public BaseAddOp { - AbsDiffOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, -1, Scalar::all(0)) {}; + AbsDiffOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, -1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { absdiff(src[0], src[1], dst); @@ -265,7 +265,7 @@ struct AbsDiffOp : public BaseAddOp struct AbsDiffSOp : public BaseAddOp { - AbsDiffSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA, 1, 0, Scalar::all(0)) {}; + AbsDiffSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA, 1, 0, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { absdiff(src[0], gamma, dst); @@ -278,7 +278,7 @@ struct AbsDiffSOp : public BaseAddOp struct LogicOp : public BaseElemWiseOp { - LogicOp(char _opcode) : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)), opcode(_opcode) {}; + LogicOp(char _opcode) : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)), opcode(_opcode) {} void op(const vector& src, Mat& dst, const Mat& mask) { if( opcode == '&' ) @@ -309,7 +309,7 @@ struct LogicOp : public BaseElemWiseOp struct LogicSOp : public BaseElemWiseOp { LogicSOp(char _opcode) - : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+(_opcode != '~' ? SUPPORT_MASK : 0), 1, 1, Scalar::all(0)), opcode(_opcode) {}; + : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+(_opcode != '~' ? SUPPORT_MASK : 0), 1, 1, Scalar::all(0)), opcode(_opcode) {} void op(const vector& src, Mat& dst, const Mat& mask) { if( opcode == '&' ) @@ -341,7 +341,7 @@ struct LogicSOp : public BaseElemWiseOp struct MinOp : public BaseElemWiseOp { - MinOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + MinOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::min(src[0], src[1], dst); @@ -358,7 +358,7 @@ struct MinOp : public BaseElemWiseOp struct MaxOp : public BaseElemWiseOp { - MaxOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + MaxOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::max(src[0], src[1], dst); @@ -375,7 +375,7 @@ struct MaxOp : public BaseElemWiseOp struct MinSOp : public BaseElemWiseOp { - MinSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + MinSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::min(src[0], gamma[0], dst); @@ -392,7 +392,7 @@ struct MinSOp : public BaseElemWiseOp struct MaxSOp : public BaseElemWiseOp { - MaxSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + MaxSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::max(src[0], gamma[0], dst); @@ -409,7 +409,7 @@ struct MaxSOp : public BaseElemWiseOp struct CmpOp : public BaseElemWiseOp { - CmpOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + CmpOp() : BaseElemWiseOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void generateScalars(int depth, RNG& rng) { BaseElemWiseOp::generateScalars(depth, rng); @@ -437,7 +437,7 @@ struct CmpOp : public BaseElemWiseOp struct CmpSOp : public BaseElemWiseOp { - CmpSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + CmpSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {} void generateScalars(int depth, RNG& rng) { BaseElemWiseOp::generateScalars(depth, rng); @@ -467,7 +467,7 @@ struct CmpSOp : public BaseElemWiseOp struct CopyOp : public BaseElemWiseOp { - CopyOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}; + CopyOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { src[0].copyTo(dst, mask); @@ -490,7 +490,7 @@ struct CopyOp : public BaseElemWiseOp struct SetOp : public BaseElemWiseOp { - SetOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}; + SetOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {} void op(const vector&, Mat& dst, const Mat& mask) { dst.setTo(gamma, mask); @@ -651,7 +651,7 @@ static void inRangeS(const Mat& src, const Scalar& lb, const Scalar& rb, Mat& ds struct InRangeSOp : public BaseElemWiseOp { - InRangeSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {}; + InRangeSOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::inRange(src[0], gamma, gamma1, dst); @@ -681,7 +681,7 @@ struct InRangeSOp : public BaseElemWiseOp struct InRangeOp : public BaseElemWiseOp { - InRangeOp() : BaseElemWiseOp(3, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + InRangeOp() : BaseElemWiseOp(3, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { Mat lb, rb; @@ -707,7 +707,7 @@ struct InRangeOp : public BaseElemWiseOp struct ConvertScaleOp : public BaseElemWiseOp { - ConvertScaleOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)), ddepth(0) { }; + ConvertScaleOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)), ddepth(0) { } void op(const vector& src, Mat& dst, const Mat&) { src[0].convertTo(dst, ddepth, alpha, gamma[0]); @@ -742,7 +742,7 @@ struct ConvertScaleOp : public BaseElemWiseOp struct ConvertScaleAbsOp : public BaseElemWiseOp { - ConvertScaleAbsOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {}; + ConvertScaleAbsOp() : BaseElemWiseOp(1, FIX_BETA+REAL_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { cv::convertScaleAbs(src[0], dst, alpha, gamma[0]); @@ -810,7 +810,7 @@ static void setIdentity(Mat& dst, const Scalar& s) struct FlipOp : public BaseElemWiseOp { - FlipOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + FlipOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void getRandomSize(RNG& rng, vector& size) { cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size); @@ -836,7 +836,7 @@ struct FlipOp : public BaseElemWiseOp struct TransposeOp : public BaseElemWiseOp { - TransposeOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + TransposeOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void getRandomSize(RNG& rng, vector& size) { cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size); @@ -857,7 +857,7 @@ struct TransposeOp : public BaseElemWiseOp struct SetIdentityOp : public BaseElemWiseOp { - SetIdentityOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {}; + SetIdentityOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA, 1, 1, Scalar::all(0)) {} void getRandomSize(RNG& rng, vector& size) { cvtest::randomSize(rng, 2, 2, cvtest::ARITHM_MAX_SIZE_LOG, size); @@ -878,7 +878,7 @@ struct SetIdentityOp : public BaseElemWiseOp struct SetZeroOp : public BaseElemWiseOp { - SetZeroOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + SetZeroOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector&, Mat& dst, const Mat&) { dst = Scalar::all(0); @@ -954,7 +954,7 @@ static void log(const Mat& src, Mat& dst) struct ExpOp : public BaseElemWiseOp { - ExpOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + ExpOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} int getRandomType(RNG& rng) { return cvtest::randomType(rng, DEPTH_MASK_FLT, 1, ARITHM_MAX_CHANNELS); @@ -981,7 +981,7 @@ struct ExpOp : public BaseElemWiseOp struct LogOp : public BaseElemWiseOp { - LogOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}; + LogOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} int getRandomType(RNG& rng) { return cvtest::randomType(rng, DEPTH_MASK_FLT, 1, ARITHM_MAX_CHANNELS); diff --git a/modules/features2d/src/features2d_init.cpp b/modules/features2d/src/features2d_init.cpp index 13ef0f8ae5..e12310a3b4 100644 --- a/modules/features2d/src/features2d_init.cpp +++ b/modules/features2d/src/features2d_init.cpp @@ -59,23 +59,23 @@ Ptr Feature2D::create( const string& feature2DType ) CV_INIT_ALGORITHM(BRISK, "Feature2D.BRISK", obj.info()->addParam(obj, "thres", obj.threshold); - obj.info()->addParam(obj, "octaves", obj.octaves)); + obj.info()->addParam(obj, "octaves", obj.octaves)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// CV_INIT_ALGORITHM(BriefDescriptorExtractor, "Feature2D.BRIEF", - obj.info()->addParam(obj, "bytes", obj.bytes_)); + obj.info()->addParam(obj, "bytes", obj.bytes_)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// CV_INIT_ALGORITHM(FastFeatureDetector, "Feature2D.FAST", obj.info()->addParam(obj, "threshold", obj.threshold); - obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression)); + obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression)) CV_INIT_ALGORITHM(FastFeatureDetector2, "Feature2D.FASTX", obj.info()->addParam(obj, "threshold", obj.threshold); obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression); - obj.info()->addParam(obj, "type", obj.type)); + obj.info()->addParam(obj, "type", obj.type)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -84,7 +84,7 @@ CV_INIT_ALGORITHM(StarDetector, "Feature2D.STAR", obj.info()->addParam(obj, "responseThreshold", obj.responseThreshold); obj.info()->addParam(obj, "lineThresholdProjected", obj.lineThresholdProjected); obj.info()->addParam(obj, "lineThresholdBinarized", obj.lineThresholdBinarized); - obj.info()->addParam(obj, "suppressNonmaxSize", obj.suppressNonmaxSize)); + obj.info()->addParam(obj, "suppressNonmaxSize", obj.suppressNonmaxSize)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -97,7 +97,7 @@ CV_INIT_ALGORITHM(MSER, "Feature2D.MSER", obj.info()->addParam(obj, "maxEvolution", obj.maxEvolution); obj.info()->addParam(obj, "areaThreshold", obj.areaThreshold); obj.info()->addParam(obj, "minMargin", obj.minMargin); - obj.info()->addParam(obj, "edgeBlurSize", obj.edgeBlurSize)); + obj.info()->addParam(obj, "edgeBlurSize", obj.edgeBlurSize)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -109,7 +109,7 @@ CV_INIT_ALGORITHM(ORB, "Feature2D.ORB", obj.info()->addParam(obj, "edgeThreshold", obj.edgeThreshold); obj.info()->addParam(obj, "patchSize", obj.patchSize); obj.info()->addParam(obj, "WTA_K", obj.WTA_K); - obj.info()->addParam(obj, "scoreType", obj.scoreType)); + obj.info()->addParam(obj, "scoreType", obj.scoreType)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -117,7 +117,7 @@ CV_INIT_ALGORITHM(FREAK, "Feature2D.FREAK", obj.info()->addParam(obj, "orientationNormalized", obj.orientationNormalized); obj.info()->addParam(obj, "scaleNormalized", obj.scaleNormalized); obj.info()->addParam(obj, "patternScale", obj.patternScale); - obj.info()->addParam(obj, "nbOctave", obj.nOctaves)); + obj.info()->addParam(obj, "nbOctave", obj.nOctaves)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -126,7 +126,7 @@ CV_INIT_ALGORITHM(GFTTDetector, "Feature2D.GFTT", obj.info()->addParam(obj, "qualityLevel", obj.qualityLevel); obj.info()->addParam(obj, "minDistance", obj.minDistance); obj.info()->addParam(obj, "useHarrisDetector", obj.useHarrisDetector); - obj.info()->addParam(obj, "k", obj.k)); + obj.info()->addParam(obj, "k", obj.k)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -146,7 +146,7 @@ CV_INIT_ALGORITHM(SimpleBlobDetector, "Feature2D.SimpleBlob", obj.info()->addParam(obj, "maxInertiaRatio", obj.params.maxInertiaRatio); obj.info()->addParam(obj, "filterByConvexity", obj.params.filterByConvexity); obj.info()->addParam(obj, "maxConvexity", obj.params.maxConvexity); - ); + ) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -167,7 +167,7 @@ CV_INIT_ALGORITHM(HarrisDetector, "Feature2D.HARRIS", obj.info()->addParam(obj, "qualityLevel", obj.qualityLevel); obj.info()->addParam(obj, "minDistance", obj.minDistance); obj.info()->addParam(obj, "useHarrisDetector", obj.useHarrisDetector); - obj.info()->addParam(obj, "k", obj.k)); + obj.info()->addParam(obj, "k", obj.k)) //////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -178,21 +178,21 @@ CV_INIT_ALGORITHM(DenseFeatureDetector, "Feature2D.Dense", obj.info()->addParam(obj, "initXyStep", obj.initXyStep); obj.info()->addParam(obj, "initImgBound", obj.initImgBound); obj.info()->addParam(obj, "varyXyStepWithScale", obj.varyXyStepWithScale); - obj.info()->addParam(obj, "varyImgBoundWithScale", obj.varyImgBoundWithScale)); + obj.info()->addParam(obj, "varyImgBoundWithScale", obj.varyImgBoundWithScale)) CV_INIT_ALGORITHM(GridAdaptedFeatureDetector, "Feature2D.Grid", obj.info()->addParam(obj, "detector", obj.detector, false, 0, 0); // Extra params added to avoid VS2013 fatal error in opencv2/core.hpp (decl. of addParam) obj.info()->addParam(obj, "maxTotalKeypoints", obj.maxTotalKeypoints); obj.info()->addParam(obj, "gridRows", obj.gridRows); - obj.info()->addParam(obj, "gridCols", obj.gridCols)); + obj.info()->addParam(obj, "gridCols", obj.gridCols)) //////////////////////////////////////////////////////////////////////////////////////////////////////////// CV_INIT_ALGORITHM(BFMatcher, "DescriptorMatcher.BFMatcher", obj.info()->addParam(obj, "normType", obj.normType); - obj.info()->addParam(obj, "crossCheck", obj.crossCheck)); + obj.info()->addParam(obj, "crossCheck", obj.crossCheck)) -CV_INIT_ALGORITHM(FlannBasedMatcher, "DescriptorMatcher.FlannBasedMatcher",); +CV_INIT_ALGORITHM(FlannBasedMatcher, "DescriptorMatcher.FlannBasedMatcher",) /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index e2fc99b90f..e040ccfddb 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -2443,7 +2443,7 @@ public: Uncompressed_YV12 = (('Y'<<24)|('V'<<16)|('1'<<8)|('2')), // Y,V,U (4:2:0) Uncompressed_NV12 = (('N'<<24)|('V'<<16)|('1'<<8)|('2')), // Y,UV (4:2:0) Uncompressed_YUYV = (('Y'<<24)|('U'<<16)|('Y'<<8)|('V')), // YUYV/YUY2 (4:2:2) - Uncompressed_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')), // UYVY (4:2:2) + Uncompressed_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')) // UYVY (4:2:2) }; enum ChromaFormat @@ -2451,7 +2451,7 @@ public: Monochrome=0, YUV420, YUV422, - YUV444, + YUV444 }; struct FormatInfo diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index d942bed612..4093b16e73 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -50,7 +50,7 @@ using namespace perf; // Remap enum { HALF_SIZE=0, UPSIDE_DOWN, REFLECTION_X, REFLECTION_BOTH }; -CV_ENUM(RemapMode, HALF_SIZE, UPSIDE_DOWN, REFLECTION_X, REFLECTION_BOTH); +CV_ENUM(RemapMode, HALF_SIZE, UPSIDE_DOWN, REFLECTION_X, REFLECTION_BOTH) void generateMap(cv::Mat& map_x, cv::Mat& map_y, int remapMode) { @@ -941,7 +941,7 @@ PERF_TEST_P(Sz_TemplateSz_Cn_Method, ImgProc_MatchTemplate8U, CPU_SANITY_CHECK(dst); } -}; +} //////////////////////////////////////////////////////////////////////////////// // MatchTemplate32F @@ -981,7 +981,7 @@ PERF_TEST_P(Sz_TemplateSz_Cn_Method, ImgProc_MatchTemplate32F, CPU_SANITY_CHECK(dst); } -}; +} ////////////////////////////////////////////////////////////////////// // MulSpectrums @@ -1821,7 +1821,7 @@ PERF_TEST_P(Sz_Dp_MinDist, ImgProc_HoughCircles, ////////////////////////////////////////////////////////////////////// // GeneralizedHough -CV_FLAGS(GHMethod, GHT_POSITION, GHT_SCALE, GHT_ROTATION); +CV_FLAGS(GHMethod, GHT_POSITION, GHT_SCALE, GHT_ROTATION) DEF_PARAM_TEST(Method_Sz, GHMethod, cv::Size); diff --git a/modules/highgui/src/bitstrm.hpp b/modules/highgui/src/bitstrm.hpp index e476d9c569..57956beb53 100644 --- a/modules/highgui/src/bitstrm.hpp +++ b/modules/highgui/src/bitstrm.hpp @@ -53,7 +53,7 @@ enum RBS_THROW_EOS=-123, // exception code RBS_THROW_FORB=-124, // exception code RBS_HUFF_FORB=2047, // forrbidden huffman code "value" - RBS_BAD_HEADER=-125, // invalid header + RBS_BAD_HEADER=-125 // invalid header }; typedef unsigned long ulong; diff --git a/modules/highgui/src/cap_ffmpeg_impl.hpp b/modules/highgui/src/cap_ffmpeg_impl.hpp index 151a0cac24..2b185595d9 100644 --- a/modules/highgui/src/cap_ffmpeg_impl.hpp +++ b/modules/highgui/src/cap_ffmpeg_impl.hpp @@ -2066,7 +2066,7 @@ enum VideoCodec_YV12 = (('Y'<<24)|('V'<<16)|('1'<<8)|('2')), // Y,V,U (4:2:0) VideoCodec_NV12 = (('N'<<24)|('V'<<16)|('1'<<8)|('2')), // Y,UV (4:2:0) VideoCodec_YUYV = (('Y'<<24)|('U'<<16)|('Y'<<8)|('V')), // YUYV/YUY2 (4:2:2) - VideoCodec_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')), // UYVY (4:2:2) + VideoCodec_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')) // UYVY (4:2:2) }; enum @@ -2074,7 +2074,7 @@ enum VideoChromaFormat_Monochrome = 0, VideoChromaFormat_YUV420, VideoChromaFormat_YUV422, - VideoChromaFormat_YUV444, + VideoChromaFormat_YUV444 }; struct InputMediaStream_FFMPEG diff --git a/modules/imgproc/perf/perf_filter2d.cpp b/modules/imgproc/perf/perf_filter2d.cpp index b897d6ac01..98992e98e5 100644 --- a/modules/imgproc/perf/perf_filter2d.cpp +++ b/modules/imgproc/perf/perf_filter2d.cpp @@ -8,7 +8,7 @@ using std::tr1::make_tuple; using std::tr1::get; -CV_ENUM(BorderMode, BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101); +CV_ENUM(BorderMode, BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT_101) typedef TestBaseWithParam< tr1::tuple > TestFilter2d; typedef TestBaseWithParam< tr1::tuple > Image_KernelSize; diff --git a/modules/imgproc/src/gcgraph.hpp b/modules/imgproc/src/gcgraph.hpp index 59c9744e7b..92b9e124b4 100644 --- a/modules/imgproc/src/gcgraph.hpp +++ b/modules/imgproc/src/gcgraph.hpp @@ -380,6 +380,6 @@ bool GCGraph::inSourceSegment( int i ) { CV_Assert( i>=0 && i<(int)vtcs.size() ); return vtcs[i].t == 0; -}; +} #endif diff --git a/modules/imgproc/src/generalized_hough.cpp b/modules/imgproc/src/generalized_hough.cpp index 846a55fe9a..8d0ac753f4 100644 --- a/modules/imgproc/src/generalized_hough.cpp +++ b/modules/imgproc/src/generalized_hough.cpp @@ -300,7 +300,7 @@ namespace obj.info()->addParam(obj, "votesThreshold", obj.votesThreshold, false, 0, 0, "The accumulator threshold for the template centers at the detection stage. The smaller it is, the more false positions may be detected."); obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, - "Inverse ratio of the accumulator resolution to the image resolution.")); + "Inverse ratio of the accumulator resolution to the image resolution.")) GHT_Ballard_Pos::GHT_Ballard_Pos() { @@ -466,7 +466,7 @@ namespace obj.info()->addParam(obj, "maxScale", obj.maxScale, false, 0, 0, "Maximal scale to detect."); obj.info()->addParam(obj, "scaleStep", obj.scaleStep, false, 0, 0, - "Scale step.")); + "Scale step.")) GHT_Ballard_PosScale::GHT_Ballard_PosScale() { @@ -631,7 +631,7 @@ namespace obj.info()->addParam(obj, "maxAngle", obj.maxAngle, false, 0, 0, "Maximal rotation angle to detect in degrees."); obj.info()->addParam(obj, "angleStep", obj.angleStep, false, 0, 0, - "Angle step in degrees.")); + "Angle step in degrees.")) GHT_Ballard_PosRotation::GHT_Ballard_PosRotation() { @@ -878,7 +878,7 @@ namespace obj.info()->addParam(obj, "dp", obj.dp, false, 0, 0, "Inverse ratio of the accumulator resolution to the image resolution."); obj.info()->addParam(obj, "posThresh", obj.posThresh, false, 0, 0, - "Position threshold.")); + "Position threshold.")) GHT_Guil_Full::GHT_Guil_Full() { diff --git a/modules/imgproc/test/test_cvtyuv.cpp b/modules/imgproc/test/test_cvtyuv.cpp index bd8d95dc76..0cce64cdba 100644 --- a/modules/imgproc/test/test_cvtyuv.cpp +++ b/modules/imgproc/test/test_cvtyuv.cpp @@ -603,7 +603,7 @@ CV_ENUM(YUVCVTS, CV_YUV2RGB_NV12, CV_YUV2BGR_NV12, CV_YUV2RGB_NV21, CV_YUV2BGR_N CV_YUV2RGBA_YUY2, CV_YUV2BGRA_YUY2, CV_YUV2RGBA_YVYU, CV_YUV2BGRA_YVYU, CV_YUV2GRAY_420, CV_YUV2GRAY_UYVY, CV_YUV2GRAY_YUY2, CV_YUV2BGR, CV_YUV2RGB, CV_RGB2YUV_YV12, CV_BGR2YUV_YV12, CV_RGBA2YUV_YV12, - CV_BGRA2YUV_YV12, CV_RGB2YUV_I420, CV_BGR2YUV_I420, CV_RGBA2YUV_I420, CV_BGRA2YUV_I420); + CV_BGRA2YUV_YV12, CV_RGB2YUV_I420, CV_BGR2YUV_I420, CV_RGBA2YUV_I420, CV_BGRA2YUV_I420) typedef ::testing::TestWithParam Imgproc_ColorYUV; diff --git a/modules/legacy/src/blobtrack.cpp b/modules/legacy/src/blobtrack.cpp index 48b83ef914..00e4905cca 100644 --- a/modules/legacy/src/blobtrack.cpp +++ b/modules/legacy/src/blobtrack.cpp @@ -205,7 +205,7 @@ double CvVSModule::GetParam(const char* name) if(p->pInt) return p->pInt[0]; } return 0; -}; +} const char* CvVSModule::GetParamStr(const char* name) { diff --git a/modules/legacy/src/enteringblobdetection.cpp b/modules/legacy/src/enteringblobdetection.cpp index d66a997a70..a383bcf677 100644 --- a/modules/legacy/src/enteringblobdetection.cpp +++ b/modules/legacy/src/enteringblobdetection.cpp @@ -209,7 +209,7 @@ public: CvBlobDetectorSimple(); ~CvBlobDetectorSimple(); int DetectNewBlob(IplImage* pImg, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList); - void Release(){delete this;}; + void Release(){delete this;} protected: IplImage* m_pMaskBlobNew; @@ -219,7 +219,7 @@ protected: }; /* Blob detector creator (sole interface function for this file) */ -CvBlobDetector* cvCreateBlobDetectorSimple(){return new CvBlobDetectorSimple;}; +CvBlobDetector* cvCreateBlobDetectorSimple(){return new CvBlobDetectorSimple;} /* Constructor of BlobDetector: */ CvBlobDetectorSimple::CvBlobDetectorSimple() diff --git a/modules/legacy/src/vecfacetracking.cpp b/modules/legacy/src/vecfacetracking.cpp index bcf309f706..b29b6b27f2 100644 --- a/modules/legacy/src/vecfacetracking.cpp +++ b/modules/legacy/src/vecfacetracking.cpp @@ -52,7 +52,7 @@ enum { MOUTH = 0, LEYE = 1, - REYE = 2, + REYE = 2 }; #define MAX_LAYERS 64 diff --git a/modules/ml/src/ml_init.cpp b/modules/ml/src/ml_init.cpp index 276f6f5d47..c6cb594efd 100644 --- a/modules/ml/src/ml_init.cpp +++ b/modules/ml/src/ml_init.cpp @@ -52,7 +52,7 @@ CV_INIT_ALGORITHM(EM, "StatModel.EM", obj.info()->addParam(obj, "epsilon", obj.epsilon); obj.info()->addParam(obj, "weights", obj.weights, true); obj.info()->addParam(obj, "means", obj.means, true); - obj.info()->addParam(obj, "covs", obj.covs, true)); + obj.info()->addParam(obj, "covs", obj.covs, true)) bool initModule_ml(void) { diff --git a/modules/nonfree/src/nonfree_init.cpp b/modules/nonfree/src/nonfree_init.cpp index 3c530e3257..f18e7d81d9 100644 --- a/modules/nonfree/src/nonfree_init.cpp +++ b/modules/nonfree/src/nonfree_init.cpp @@ -52,7 +52,7 @@ CV_INIT_ALGORITHM(SURF, "Feature2D.SURF", obj.info()->addParam(obj, "nOctaves", obj.nOctaves); obj.info()->addParam(obj, "nOctaveLayers", obj.nOctaveLayers); obj.info()->addParam(obj, "extended", obj.extended); - obj.info()->addParam(obj, "upright", obj.upright)); + obj.info()->addParam(obj, "upright", obj.upright)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -61,7 +61,7 @@ CV_INIT_ALGORITHM(SIFT, "Feature2D.SIFT", obj.info()->addParam(obj, "nOctaveLayers", obj.nOctaveLayers); obj.info()->addParam(obj, "contrastThreshold", obj.contrastThreshold); obj.info()->addParam(obj, "edgeThreshold", obj.edgeThreshold); - obj.info()->addParam(obj, "sigma", obj.sigma)); + obj.info()->addParam(obj, "sigma", obj.sigma)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index ff35d14e70..9ea5f66524 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -204,7 +204,7 @@ namespace cv CACHE_NONE = 0, // do not cache OpenCL binary CACHE_DEBUG = 0x1 << 0, // cache OpenCL binary when built in debug mode CACHE_RELEASE = 0x1 << 1, // default behavior, only cache when built in release mode - CACHE_ALL = CACHE_DEBUG | CACHE_RELEASE, // cache opencl binary + CACHE_ALL = CACHE_DEBUG | CACHE_RELEASE // cache opencl binary }; //! Enable or disable OpenCL program binary caching onto local disk // After a program (*.cl files in opencl/ folder) is built at runtime, we allow the diff --git a/modules/ocl/include/opencv2/ocl/private/opencl_utils.hpp b/modules/ocl/include/opencv2/ocl/private/opencl_utils.hpp index 70c45d3dde..a737f75a5a 100644 --- a/modules/ocl/include/opencv2/ocl/private/opencl_utils.hpp +++ b/modules/ocl/include/opencv2/ocl/private/opencl_utils.hpp @@ -108,7 +108,7 @@ inline cl_int getStringInfo(Functor f, ObjectType obj, cl_uint name, std::string } return CL_SUCCESS; -}; +} } // namespace cl_utils diff --git a/modules/ocl/test/test_canny.cpp b/modules/ocl/test/test_canny.cpp index 82286031f5..6bd7f26ad1 100644 --- a/modules/ocl/test/test_canny.cpp +++ b/modules/ocl/test/test_canny.cpp @@ -48,8 +48,8 @@ //////////////////////////////////////////////////////// // Canny -IMPLEMENT_PARAM_CLASS(AppertureSize, int); -IMPLEMENT_PARAM_CLASS(L2gradient, bool); +IMPLEMENT_PARAM_CLASS(AppertureSize, int) +IMPLEMENT_PARAM_CLASS(L2gradient, bool) PARAM_TEST_CASE(Canny, AppertureSize, L2gradient) { diff --git a/modules/ocl/test/test_match_template.cpp b/modules/ocl/test/test_match_template.cpp index edbc36a3f2..aa63f3d9e6 100644 --- a/modules/ocl/test/test_match_template.cpp +++ b/modules/ocl/test/test_match_template.cpp @@ -50,7 +50,7 @@ // MatchTemplate #define ALL_TEMPLATE_METHODS testing::Values(TemplateMethod(cv::TM_SQDIFF), TemplateMethod(cv::TM_CCORR), TemplateMethod(cv::TM_CCOEFF), TemplateMethod(cv::TM_SQDIFF_NORMED), TemplateMethod(cv::TM_CCORR_NORMED), TemplateMethod(cv::TM_CCOEFF_NORMED)) -IMPLEMENT_PARAM_CLASS(TemplateSize, cv::Size); +IMPLEMENT_PARAM_CLASS(TemplateSize, cv::Size) #define MTEMP_SIZES testing::Values(cv::Size(128, 256), cv::Size(1024, 768)) diff --git a/modules/ocl/test/test_objdetect.cpp b/modules/ocl/test/test_objdetect.cpp index 89f45b07c9..604a96a431 100644 --- a/modules/ocl/test/test_objdetect.cpp +++ b/modules/ocl/test/test_objdetect.cpp @@ -181,7 +181,7 @@ INSTANTIATE_TEST_CASE_P(OCL_ObjDetect, HOG, testing::Combine( testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)))); ///////////////////////////// Haar ////////////////////////////// -IMPLEMENT_PARAM_CLASS(CascadeName, std::string); +IMPLEMENT_PARAM_CLASS(CascadeName, std::string) CascadeName cascade_frontalface_alt(std::string("haarcascade_frontalface_alt.xml")); CascadeName cascade_frontalface_alt2(std::string("haarcascade_frontalface_alt2.xml")); struct getRect diff --git a/modules/ocl/test/utility.hpp b/modules/ocl/test/utility.hpp index a1fe3ffb75..6591456ee0 100644 --- a/modules/ocl/test/utility.hpp +++ b/modules/ocl/test/utility.hpp @@ -266,7 +266,7 @@ CV_ENUM(Interpolation, INTER_NEAREST, INTER_LINEAR, INTER_CUBIC, INTER_AREA) CV_ENUM(Border, BORDER_REFLECT101, BORDER_REPLICATE, BORDER_CONSTANT, BORDER_REFLECT, BORDER_WRAP) CV_ENUM(TemplateMethod, TM_SQDIFF, TM_SQDIFF_NORMED, TM_CCORR, TM_CCORR_NORMED, TM_CCOEFF, TM_CCOEFF_NORMED) -CV_FLAGS(GemmFlags, GEMM_1_T, GEMM_2_T, GEMM_3_T); +CV_FLAGS(GemmFlags, GEMM_1_T, GEMM_2_T, GEMM_3_T) CV_FLAGS(WarpFlags, INTER_NEAREST, INTER_LINEAR, INTER_CUBIC, WARP_INVERSE_MAP) CV_FLAGS(DftFlags, DFT_INVERSE, DFT_SCALE, DFT_ROWS, DFT_COMPLEX_OUTPUT, DFT_REAL_OUTPUT) diff --git a/modules/superres/src/btv_l1.cpp b/modules/superres/src/btv_l1.cpp index 0d96a9d17f..d49bcf32b8 100644 --- a/modules/superres/src/btv_l1.cpp +++ b/modules/superres/src/btv_l1.cpp @@ -488,7 +488,7 @@ namespace obj.info()->addParam(obj, "blurKernelSize", obj.blurKernelSize_, false, 0, 0, "Gaussian blur kernel size."); obj.info()->addParam(obj, "blurSigma", obj.blurSigma_, false, 0, 0, "Gaussian blur sigma."); obj.info()->addParam(obj, "temporalAreaRadius", obj.temporalAreaRadius_, false, 0, 0, "Radius of the temporal search area."); - obj.info()->addParam(obj, "opticalFlow", obj.opticalFlow_, false, 0, 0, "Dense optical flow algorithm.")); + obj.info()->addParam(obj, "opticalFlow", obj.opticalFlow_, false, 0, 0, "Dense optical flow algorithm.")) BTVL1::BTVL1() { diff --git a/modules/superres/src/btv_l1_ocl.cpp b/modules/superres/src/btv_l1_ocl.cpp index b4f4acdaf7..69564ef0dd 100644 --- a/modules/superres/src/btv_l1_ocl.cpp +++ b/modules/superres/src/btv_l1_ocl.cpp @@ -580,7 +580,7 @@ namespace obj.info()->addParam(obj, "blurKernelSize", obj.blurKernelSize_, false, 0, 0, "Gaussian blur kernel size."); obj.info()->addParam(obj, "blurSigma", obj.blurSigma_, false, 0, 0, "Gaussian blur sigma."); obj.info()->addParam(obj, "temporalAreaRadius", obj.temporalAreaRadius_, false, 0, 0, "Radius of the temporal search area."); - obj.info()->addParam(obj, "opticalFlow", obj.opticalFlow_, false, 0, 0, "Dense optical flow algorithm.")); + obj.info()->addParam(obj, "opticalFlow", obj.opticalFlow_, false, 0, 0, "Dense optical flow algorithm.")) BTVL1_OCL::BTVL1_OCL() { diff --git a/modules/superres/src/optical_flow.cpp b/modules/superres/src/optical_flow.cpp index 9df4c3b79d..e1e8a10275 100644 --- a/modules/superres/src/optical_flow.cpp +++ b/modules/superres/src/optical_flow.cpp @@ -149,7 +149,7 @@ namespace obj.info()->addParam(obj, "numIters", obj.numIters_); obj.info()->addParam(obj, "polyN", obj.polyN_); obj.info()->addParam(obj, "polySigma", obj.polySigma_); - obj.info()->addParam(obj, "flags", obj.flags_)); + obj.info()->addParam(obj, "flags", obj.flags_)) Farneback::Farneback() : CpuOpticalFlow(CV_8UC1) { @@ -217,7 +217,7 @@ namespace obj.info()->addParam(obj, "upscaleAveragingRadius", obj.upscaleAveragingRadius_); obj.info()->addParam(obj, "upscaleSigmaDist", obj.upscaleSigmaDist_); obj.info()->addParam(obj, "upscaleSigmaColor", obj.upscaleSigmaColor_); - obj.info()->addParam(obj, "speedUpThr", obj.speedUpThr_)); + obj.info()->addParam(obj, "speedUpThr", obj.speedUpThr_)) Simple::Simple() : CpuOpticalFlow(CV_8UC3) { @@ -300,7 +300,7 @@ namespace obj.info()->addParam(obj, "warps", obj.warps_); obj.info()->addParam(obj, "epsilon", obj.epsilon_); obj.info()->addParam(obj, "iterations", obj.iterations_); - obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow_)); + obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow_)) DualTVL1::DualTVL1() : CpuOpticalFlow(CV_8UC1) { @@ -471,7 +471,7 @@ namespace obj.info()->addParam(obj, "scaleFactor", obj.scaleFactor_, false, 0, 0, "Pyramid scale factor"); obj.info()->addParam(obj, "innerIterations", obj.innerIterations_, false, 0, 0, "Number of lagged non-linearity iterations (inner loop)"); obj.info()->addParam(obj, "outerIterations", obj.outerIterations_, false, 0, 0, "Number of warping iterations (number of pyramid levels)"); - obj.info()->addParam(obj, "solverIterations", obj.solverIterations_, false, 0, 0, "Number of linear system solver iterations")); + obj.info()->addParam(obj, "solverIterations", obj.solverIterations_, false, 0, 0, "Number of linear system solver iterations")) Brox_GPU::Brox_GPU() : GpuOpticalFlow(CV_32FC1), alg_(0.197f, 50.0f, 0.8f, 10, 77, 10) { @@ -535,7 +535,7 @@ namespace CV_INIT_ALGORITHM(PyrLK_GPU, "DenseOpticalFlowExt.PyrLK_GPU", obj.info()->addParam(obj, "winSize", obj.winSize_); obj.info()->addParam(obj, "maxLevel", obj.maxLevel_); - obj.info()->addParam(obj, "iterations", obj.iterations_)); + obj.info()->addParam(obj, "iterations", obj.iterations_)) PyrLK_GPU::PyrLK_GPU() : GpuOpticalFlow(CV_8UC1) { @@ -602,7 +602,7 @@ namespace obj.info()->addParam(obj, "numIters", obj.numIters_); obj.info()->addParam(obj, "polyN", obj.polyN_); obj.info()->addParam(obj, "polySigma", obj.polySigma_); - obj.info()->addParam(obj, "flags", obj.flags_)); + obj.info()->addParam(obj, "flags", obj.flags_)) Farneback_GPU::Farneback_GPU() : GpuOpticalFlow(CV_8UC1) { @@ -678,7 +678,7 @@ namespace obj.info()->addParam(obj, "warps", obj.warps_); obj.info()->addParam(obj, "epsilon", obj.epsilon_); obj.info()->addParam(obj, "iterations", obj.iterations_); - obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow_)); + obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow_)) DualTVL1_GPU::DualTVL1_GPU() : GpuOpticalFlow(CV_8UC1) { @@ -800,7 +800,7 @@ namespace CV_INIT_ALGORITHM(PyrLK_OCL, "DenseOpticalFlowExt.PyrLK_OCL", obj.info()->addParam(obj, "winSize", obj.winSize_); obj.info()->addParam(obj, "maxLevel", obj.maxLevel_); - obj.info()->addParam(obj, "iterations", obj.iterations_)); + obj.info()->addParam(obj, "iterations", obj.iterations_)) PyrLK_OCL::PyrLK_OCL() : oclOpticalFlow(CV_8UC1) { @@ -869,7 +869,7 @@ namespace obj.info()->addParam(obj, "warps", obj.warps_); obj.info()->addParam(obj, "epsilon", obj.epsilon_); obj.info()->addParam(obj, "iterations", obj.iterations_); - obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow_)); + obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow_)) DualTVL1_OCL::DualTVL1_OCL() : oclOpticalFlow(CV_8UC1) { @@ -946,7 +946,7 @@ namespace obj.info()->addParam(obj, "numIters", obj.numIters_); obj.info()->addParam(obj, "polyN", obj.polyN_); obj.info()->addParam(obj, "polySigma", obj.polySigma_); - obj.info()->addParam(obj, "flags", obj.flags_)); + obj.info()->addParam(obj, "flags", obj.flags_)) FarneBack_OCL::FarneBack_OCL() : oclOpticalFlow(CV_8UC1) { diff --git a/modules/ts/include/opencv2/ts/ts_perf.hpp b/modules/ts/include/opencv2/ts/ts_perf.hpp index 9238b3e342..e32057de23 100644 --- a/modules/ts/include/opencv2/ts/ts_perf.hpp +++ b/modules/ts/include/opencv2/ts/ts_perf.hpp @@ -258,7 +258,7 @@ typedef struct CV_EXPORTS performance_metrics enum PERF_STRATEGY { PERF_STRATEGY_BASE = 0, - PERF_STRATEGY_SIMPLE = 1, + PERF_STRATEGY_SIMPLE = 1 }; diff --git a/modules/video/perf/perf_optflowpyrlk.cpp b/modules/video/perf/perf_optflowpyrlk.cpp index 8c53db03ae..25310af0e7 100644 --- a/modules/video/perf/perf_optflowpyrlk.cpp +++ b/modules/video/perf/perf_optflowpyrlk.cpp @@ -178,7 +178,7 @@ PERF_TEST_P(Path_Idx_Cn_NPoints_WSize_Deriv, OpticalFlowPyrLK_self, testing::Com SANITY_CHECK(err, 2); } -CV_ENUM(PyrBorderMode, BORDER_DEFAULT, BORDER_TRANSPARENT); +CV_ENUM(PyrBorderMode, BORDER_DEFAULT, BORDER_TRANSPARENT) typedef tr1::tuple Path_Win_Deriv_Border_Reuse_t; typedef TestBaseWithParam Path_Win_Deriv_Border_Reuse; diff --git a/modules/video/src/kalman.cpp b/modules/video/src/kalman.cpp index b4b4c7435f..16ab7745f8 100644 --- a/modules/video/src/kalman.cpp +++ b/modules/video/src/kalman.cpp @@ -297,4 +297,4 @@ const Mat& KalmanFilter::correct(const Mat& measurement) return statePost; } -}; +} diff --git a/modules/video/src/tvl1flow.cpp b/modules/video/src/tvl1flow.cpp index ddcdabdd3b..eb0ff23510 100644 --- a/modules/video/src/tvl1flow.cpp +++ b/modules/video/src/tvl1flow.cpp @@ -928,7 +928,7 @@ CV_INIT_ALGORITHM(OpticalFlowDual_TVL1, "DenseOpticalFlow.DualTVL1", "Stopping criterion threshold used in the numerical scheme, which is a trade-off between precision and running time"); obj.info()->addParam(obj, "iterations", obj.iterations, false, 0, 0, "Stopping criterion iterations number used in the numerical scheme"); - obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow)); + obj.info()->addParam(obj, "useInitialFlow", obj.useInitialFlow)) } // namespace diff --git a/modules/video/src/video_init.cpp b/modules/video/src/video_init.cpp index 7ec860fbd3..69928c398f 100644 --- a/modules/video/src/video_init.cpp +++ b/modules/video/src/video_init.cpp @@ -52,7 +52,7 @@ CV_INIT_ALGORITHM(BackgroundSubtractorMOG, "BackgroundSubtractor.MOG", obj.info()->addParam(obj, "history", obj.history); obj.info()->addParam(obj, "nmixtures", obj.nmixtures); obj.info()->addParam(obj, "backgroundRatio", obj.backgroundRatio); - obj.info()->addParam(obj, "noiseSigma", obj.noiseSigma)); + obj.info()->addParam(obj, "noiseSigma", obj.noiseSigma)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -68,7 +68,7 @@ CV_INIT_ALGORITHM(BackgroundSubtractorMOG2, "BackgroundSubtractor.MOG2", obj.info()->addParam(obj, "fVarMax", obj.fVarMax); obj.info()->addParam(obj, "fCT", obj.fCT); obj.info()->addParam(obj, "nShadowDetection", obj.nShadowDetection); - obj.info()->addParam(obj, "fTau", obj.fTau)); + obj.info()->addParam(obj, "fTau", obj.fTau)) /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -88,7 +88,7 @@ CV_INIT_ALGORITHM(BackgroundSubtractorGMG, "BackgroundSubtractor.GMG", obj.info()->addParam(obj, "decisionThreshold", obj.decisionThreshold,false,0,0, "Threshold for FG decision rule. Pixel is FG if posterior probability exceeds threshold."); obj.info()->addParam(obj, "updateBackgroundModel", obj.updateBackgroundModel,false,0,0, - "Perform background model update.")); + "Perform background model update.")) bool initModule_video(void) { diff --git a/samples/c/adaptiveskindetector.cpp b/samples/c/adaptiveskindetector.cpp index 37856bd4e7..3ae10017d5 100644 --- a/samples/c/adaptiveskindetector.cpp +++ b/samples/c/adaptiveskindetector.cpp @@ -126,12 +126,12 @@ ASDFrameHolder::ASDFrameHolder( ) { image = NULL; timeStamp = 0; -}; +} ASDFrameHolder::~ASDFrameHolder( ) { cvReleaseImage(&image); -}; +} void ASDFrameHolder::assignFrame(IplImage *sourceImage, double frameTime) { @@ -143,22 +143,22 @@ void ASDFrameHolder::assignFrame(IplImage *sourceImage, double frameTime) image = cvCloneImage(sourceImage); timeStamp = frameTime; -}; +} IplImage *ASDFrameHolder::getImage() { return image; -}; +} double ASDFrameHolder::getTimeStamp() { return timeStamp; -}; +} void ASDFrameHolder::setImage(IplImage *sourceImage) { image = sourceImage; -}; +} //-------------------- ASDFrameSequencer -----------------------// @@ -166,26 +166,26 @@ void ASDFrameHolder::setImage(IplImage *sourceImage) ASDFrameSequencer::~ASDFrameSequencer() { close(); -}; +} IplImage *ASDFrameSequencer::getNextImage() { return NULL; -}; +} void ASDFrameSequencer::close() { -}; +} bool ASDFrameSequencer::isOpen() { return false; -}; +} void ASDFrameSequencer::getFrameCaption(char* /*caption*/) { return; -}; +} IplImage* ASDCVFrameSequencer::getNextImage() { @@ -201,7 +201,7 @@ IplImage* ASDCVFrameSequencer::getNextImage() { return NULL; } -}; +} void ASDCVFrameSequencer::close() { @@ -209,12 +209,12 @@ void ASDCVFrameSequencer::close() { cvReleaseCapture(&capture); } -}; +} bool ASDCVFrameSequencer::isOpen() { return (capture != NULL); -}; +} //-------------------- ASDFrameSequencerWebCam -----------------------// @@ -233,7 +233,7 @@ bool ASDFrameSequencerWebCam::open(int cameraIndex) { return true; } -}; +} //-------------------- ASDFrameSequencerVideoFile -----------------------// @@ -251,7 +251,7 @@ bool ASDFrameSequencerVideoFile::open(const char *fileName) { return true; } -}; +} //-------------------- ASDFrameSequencerImageFile -----------------------// @@ -263,11 +263,11 @@ void ASDFrameSequencerImageFile::open(const char *fileNameMask, int startIndex, nEndIndex = endIndex; std::sprintf(sFileNameMask, "%s", fileNameMask); -}; +} void ASDFrameSequencerImageFile::getFrameCaption(char *caption) { std::sprintf(caption, sFileNameMask, nCurrentIndex); -}; +} IplImage* ASDFrameSequencerImageFile::getNextImage() { @@ -283,23 +283,23 @@ IplImage* ASDFrameSequencerImageFile::getNextImage() IplImage* img = cvLoadImage(fileName); return img; -}; +} void ASDFrameSequencerImageFile::close() { nCurrentIndex = nEndIndex+1; -}; +} bool ASDFrameSequencerImageFile::isOpen() { return (nCurrentIndex <= nEndIndex); -}; +} static void putTextWithShadow(IplImage *img, const char *str, CvPoint point, CvFont *font, CvScalar color = CV_RGB(255, 255, 128)) { cvPutText(img, str, cvPoint(point.x-1,point.y-1), font, CV_RGB(0, 0, 0)); cvPutText(img, str, point, font, color); -}; +} #define ASD_RGB_SET_PIXEL(pointer, r, g, b) { (*pointer) = (unsigned char)b; (*(pointer+1)) = (unsigned char)g; (*(pointer+2)) = (unsigned char)r; } @@ -336,7 +336,7 @@ static void displayBuffer(IplImage *rgbDestImage, IplImage *buffer, int rValue, destY = 0; destX += dx; } -}; +} int main(int argc, char** argv ) { diff --git a/samples/cpp/calibration_artificial.cpp b/samples/cpp/calibration_artificial.cpp index c22cb528f8..7d443c1db3 100644 --- a/samples/cpp/calibration_artificial.cpp +++ b/samples/cpp/calibration_artificial.cpp @@ -46,7 +46,7 @@ private: Point3f generateChessBoardCenter(const Mat& camMat, const Size& imgSize) const; Mat rvec, tvec; }; -}; +} diff --git a/samples/cpp/tutorial_code/ImgProc/Threshold.cpp b/samples/cpp/tutorial_code/ImgProc/Threshold.cpp index d98cc1182c..96d5686a8d 100644 --- a/samples/cpp/tutorial_code/ImgProc/Threshold.cpp +++ b/samples/cpp/tutorial_code/ImgProc/Threshold.cpp @@ -14,7 +14,7 @@ using namespace cv; /// Global variables int threshold_value = 0; -int threshold_type = 3;; +int threshold_type = 3; int const max_value = 255; int const max_type = 4; int const max_BINARY_value = 255; From bf4994554df10e9c070da5490b5c274fa152fe84 Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Thu, 9 Jan 2014 21:04:17 +1100 Subject: [PATCH 028/293] Removed unecessary initialisation of Mat centers. --- samples/cpp/kmeans.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/cpp/kmeans.cpp b/samples/cpp/kmeans.cpp index b742112059..19e998379d 100644 --- a/samples/cpp/kmeans.cpp +++ b/samples/cpp/kmeans.cpp @@ -36,7 +36,7 @@ int main( int /*argc*/, char** /*argv*/ ) Mat points(sampleCount, 2, CV_32F), labels; clusterCount = MIN(clusterCount, sampleCount); - Mat centers(clusterCount, 1, points.type()); + Mat centers; /* generate random sample from multigaussian distribution */ for( k = 0; k < clusterCount; k++ ) From ae795e5797ba3b85812d56edc7fe497d05cc2d77 Mon Sep 17 00:00:00 2001 From: ComFreek Date: Thu, 9 Jan 2014 17:24:20 +0100 Subject: [PATCH 029/293] Corrected package name in tutorial See also #2101 --- doc/tutorials/introduction/linux_install/linux_install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/introduction/linux_install/linux_install.rst b/doc/tutorials/introduction/linux_install/linux_install.rst index 1e02b64c9d..8e1409650e 100644 --- a/doc/tutorials/introduction/linux_install/linux_install.rst +++ b/doc/tutorials/introduction/linux_install/linux_install.rst @@ -16,7 +16,7 @@ Required Packages * CMake 2.6 or higher; * Git; * GTK+2.x or higher, including headers (libgtk2.0-dev); - * pkgconfig; + * pkg-config; * Python 2.6 or later and Numpy 1.5 or later with developer packages (python-dev, python-numpy); * ffmpeg or libav development packages: libavcodec-dev, libavformat-dev, libswscale-dev; * [optional] libdc1394 2.x; From 4d28e8243c6fe78eeeef0a4f59d72c6812578f29 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Fri, 10 Jan 2014 00:14:48 +0400 Subject: [PATCH 030/293] 'master'-like Haar perf test --- modules/ocl/perf/perf_haar.cpp | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/modules/ocl/perf/perf_haar.cpp b/modules/ocl/perf/perf_haar.cpp index b7a1dd1a43..a6c107fe4d 100644 --- a/modules/ocl/perf/perf_haar.cpp +++ b/modules/ocl/perf/perf_haar.cpp @@ -83,3 +83,53 @@ PERF_TEST(HaarFixture, Haar) else OCL_PERF_ELSE } + +using namespace std; +using namespace cv; +using namespace perf; +using std::tr1::make_tuple; +using std::tr1::get; + +typedef std::tr1::tuple Cascade_Image_MinSize_t; +typedef perf::TestBaseWithParam Cascade_Image_MinSize; + +PERF_TEST_P( Cascade_Image_MinSize, CascadeClassifier_UMat, + testing::Combine( + testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml") ), + testing::Values( string("cv/shared/lena.png"), + string("cv/cascadeandhog/images/bttf301.png"), + string("cv/cascadeandhog/images/class57.png") ), + testing::Values(30, 64, 90) ) ) +{ + const string cascasePath = get<0>(GetParam()); + const string imagePath = get<1>(GetParam()); + const int min_size = get<2>(GetParam()); + Size minSize(min_size, min_size); + + ocl::OclCascadeClassifier cc; + if (!cc.load( getDataPath(cascasePath) )) + FAIL() << "Can't load cascade file: " << getDataPath(cascasePath); + + Mat img = imread(getDataPath(imagePath), IMREAD_GRAYSCALE); + if (img.empty()) + FAIL() << "Can't load source image: " << getDataPath(imagePath); + + vector faces; + + equalizeHist(img, img); + declare.in(img).time(60); + + ocl::oclMat uimg(img); + + while(next()) + { + faces.clear(); + + startTimer(); + cc.detectMultiScale(uimg, faces, 1.1, 3, 0, minSize); + stopTimer(); + } + + //sort(faces.begin(), faces.end(), comparators::RectLess()); + SANITY_CHECK_NOTHING();//(faces, min_size/5); +} From f197d8b91c1b89037aaf81bfeb8217c0a7aa0f9c Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 10 Jan 2014 18:59:06 +0400 Subject: [PATCH 031/293] updated .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 46d3499e56..0d0dcf8b0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.autosave *.pyc *.user +*~ .*.swp .DS_Store .sw[a-z] From a7821c60e55be5968aec41f31349f58db789671e Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Mon, 13 Jan 2014 11:20:17 +0400 Subject: [PATCH 032/293] refactoring the test as it should be in 2.4 --- modules/ocl/perf/perf_haar.cpp | 52 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/modules/ocl/perf/perf_haar.cpp b/modules/ocl/perf/perf_haar.cpp index a6c107fe4d..2fe01ee6ee 100644 --- a/modules/ocl/perf/perf_haar.cpp +++ b/modules/ocl/perf/perf_haar.cpp @@ -105,31 +105,45 @@ PERF_TEST_P( Cascade_Image_MinSize, CascadeClassifier_UMat, const string imagePath = get<1>(GetParam()); const int min_size = get<2>(GetParam()); Size minSize(min_size, min_size); - - ocl::OclCascadeClassifier cc; - if (!cc.load( getDataPath(cascasePath) )) - FAIL() << "Can't load cascade file: " << getDataPath(cascasePath); - - Mat img = imread(getDataPath(imagePath), IMREAD_GRAYSCALE); - if (img.empty()) - FAIL() << "Can't load source image: " << getDataPath(imagePath); - vector faces; + Mat img = imread(getDataPath(imagePath), IMREAD_GRAYSCALE); + ASSERT_TRUE(!img.empty()) << "Can't load source image: " << getDataPath(imagePath); equalizeHist(img, img); - declare.in(img).time(60); + declare.in(img); - ocl::oclMat uimg(img); - - while(next()) + if (RUN_PLAIN_IMPL) { - faces.clear(); + CascadeClassifier cc; + ASSERT_TRUE(cc.load(getDataPath(cascasePath))) << "Can't load cascade file: " << getDataPath(cascasePath); - startTimer(); - cc.detectMultiScale(uimg, faces, 1.1, 3, 0, minSize); - stopTimer(); + while (next()) + { + faces.clear(); + + startTimer(); + cc.detectMultiScale(img, faces, 1.1, 3, 0, minSize); + stopTimer(); + } } + else if (RUN_OCL_IMPL) + { + ocl::oclMat uimg(img); + ocl::OclCascadeClassifier cc; + ASSERT_TRUE(cc.load(getDataPath(cascasePath))) << "Can't load cascade file: " << getDataPath(cascasePath); - //sort(faces.begin(), faces.end(), comparators::RectLess()); - SANITY_CHECK_NOTHING();//(faces, min_size/5); + while (next()) + { + faces.clear(); + + startTimer(); + cc.detectMultiScale(uimg, faces, 1.1, 3, 0, minSize); + stopTimer(); + } + } + else + OCL_PERF_ELSE + + //sort(faces.begin(), faces.end(), comparators::RectLess()); + SANITY_CHECK_NOTHING();//(faces, min_size/5); } From 5d2edced2670e4d2305b5799da9378f21c18d24e Mon Sep 17 00:00:00 2001 From: Daniil Osokin Date: Mon, 13 Jan 2014 11:41:54 +0400 Subject: [PATCH 033/293] Added throwing exception when saving untrained SVM model --- modules/ml/src/svm.cpp | 18 ++++++++++++------ modules/ml/test/test_save_load.cpp | 8 ++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/modules/ml/src/svm.cpp b/modules/ml/src/svm.cpp index 674365bb39..f158805219 100644 --- a/modules/ml/src/svm.cpp +++ b/modules/ml/src/svm.cpp @@ -2298,14 +2298,24 @@ void CvSVM::write_params( CvFileStorage* fs ) const } +static bool isSvmModelApplicable(int sv_total, int var_all, int var_count, int class_count) +{ + return (sv_total > 0 && var_count > 0 && var_count <= var_all && class_count >= 0); +} + + void CvSVM::write( CvFileStorage* fs, const char* name ) const { CV_FUNCNAME( "CvSVM::write" ); __BEGIN__; - int i, var_count = get_var_count(), df_count, class_count; + int i, var_count = get_var_count(), df_count; + int class_count = class_labels ? class_labels->cols : + params.svm_type == CvSVM::ONE_CLASS ? 1 : 0; const CvSVMDecisionFunc* df = decision_func; + if( !isSvmModelApplicable(sv_total, var_all, var_count, class_count) ) + CV_ERROR( CV_StsParseError, "SVM model data is invalid, check sv_count, var_* and class_count tags" ); cvStartWriteStruct( fs, name, CV_NODE_MAP, CV_TYPE_NAME_ML_SVM ); @@ -2314,9 +2324,6 @@ void CvSVM::write( CvFileStorage* fs, const char* name ) const cvWriteInt( fs, "var_all", var_all ); cvWriteInt( fs, "var_count", var_count ); - class_count = class_labels ? class_labels->cols : - params.svm_type == CvSVM::ONE_CLASS ? 1 : 0; - if( class_count ) { cvWriteInt( fs, "class_count", class_count ); @@ -2454,7 +2461,6 @@ void CvSVM::read_params( CvFileStorage* fs, CvFileNode* svm_node ) __END__; } - void CvSVM::read( CvFileStorage* fs, CvFileNode* svm_node ) { const double not_found_dbl = DBL_MAX; @@ -2483,7 +2489,7 @@ void CvSVM::read( CvFileStorage* fs, CvFileNode* svm_node ) var_count = cvReadIntByName( fs, svm_node, "var_count", var_all ); class_count = cvReadIntByName( fs, svm_node, "class_count", 0 ); - if( sv_total <= 0 || var_all <= 0 || var_count <= 0 || var_count > var_all || class_count < 0 ) + if( !isSvmModelApplicable(sv_total, var_all, var_count, class_count) ) CV_ERROR( CV_StsParseError, "SVM model data is invalid, check sv_count, var_* and class_count tags" ); CV_CALL( class_labels = (CvMat*)cvReadByName( fs, svm_node, "class_labels" )); diff --git a/modules/ml/test/test_save_load.cpp b/modules/ml/test/test_save_load.cpp index 9fd31b9f24..7300185b4d 100644 --- a/modules/ml/test/test_save_load.cpp +++ b/modules/ml/test/test_save_load.cpp @@ -155,6 +155,14 @@ TEST(ML_RTrees, save_load) { CV_SLMLTest test( CV_RTREES ); test.safe_run(); } TEST(ML_ERTrees, save_load) { CV_SLMLTest test( CV_ERTREES ); test.safe_run(); } +TEST(ML_SVM, throw_exception_when_save_untrained_model) +{ + SVM svm; + string filename = tempfile("svm.xml"); + ASSERT_THROW(svm.save(filename.c_str()), Exception); + remove(filename.c_str()); +} + TEST(DISABLED_ML_SVM, linear_save_load) { CvSVM svm1, svm2, svm3; From 4c99196399288d2a97af37366c9a938d9092f0f1 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Mon, 13 Jan 2014 18:12:30 +0400 Subject: [PATCH 034/293] adding `finish()` to flush CL queue, renaming the test to match 'master' branch --- modules/ocl/perf/perf_haar.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/ocl/perf/perf_haar.cpp b/modules/ocl/perf/perf_haar.cpp index 2fe01ee6ee..86890a8911 100644 --- a/modules/ocl/perf/perf_haar.cpp +++ b/modules/ocl/perf/perf_haar.cpp @@ -90,10 +90,10 @@ using namespace perf; using std::tr1::make_tuple; using std::tr1::get; -typedef std::tr1::tuple Cascade_Image_MinSize_t; -typedef perf::TestBaseWithParam Cascade_Image_MinSize; +typedef std::tr1::tuple OCL_Cascade_Image_MinSize_t; +typedef perf::TestBaseWithParam OCL_Cascade_Image_MinSize; -PERF_TEST_P( Cascade_Image_MinSize, CascadeClassifier_UMat, +PERF_TEST_P( OCL_Cascade_Image_MinSize, CascadeClassifier, testing::Combine( testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml") ), testing::Values( string("cv/shared/lena.png"), @@ -135,6 +135,7 @@ PERF_TEST_P( Cascade_Image_MinSize, CascadeClassifier_UMat, while (next()) { faces.clear(); + ocl::finish(); startTimer(); cc.detectMultiScale(uimg, faces, 1.1, 3, 0, minSize); @@ -146,4 +147,5 @@ PERF_TEST_P( Cascade_Image_MinSize, CascadeClassifier_UMat, //sort(faces.begin(), faces.end(), comparators::RectLess()); SANITY_CHECK_NOTHING();//(faces, min_size/5); + // using SANITY_CHECK_NOTHING() since OCL and PLAIN version may find different faces number } From 49dfa5a17f00a82f0c160e20395c193b992d286e Mon Sep 17 00:00:00 2001 From: ahb Date: Mon, 13 Jan 2014 16:09:42 +0100 Subject: [PATCH 035/293] Fix the following error for ocl::getOpenCLPlatforms() on Ubuntu 12.04 with gcc 4.8 OpenCV Error: Unknown error code -6 (OpenCL function is not available: [clGetPlatformIDs]) in opencl_check_fn, file /home/ahb/software/opencv/modules/ocl/src/cl_runtime/cl_runtime.cpp, line 83 The issue results from modules/ocl/src/cl_runtime/cl_runtime.cpp checking for "linux" instead of "__linux__" (cp. http://sourceforge.net/p/predef/wiki/OperatingSystems/) Adjust all other occurrences of "defined(linux)" as well. --- 3rdparty/include/opencl/1.2/CL/cl.hpp | 2 +- modules/ocl/src/cl_runtime/cl_runtime.cpp | 2 +- modules/ocl/src/cl_runtime/clamdblas_runtime.cpp | 2 +- modules/ocl/src/cl_runtime/clamdfft_runtime.cpp | 2 +- .../src/cl_runtime/generator/template/clamdblas_runtime.cpp.in | 2 +- .../src/cl_runtime/generator/template/clamdfft_runtime.cpp.in | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/3rdparty/include/opencl/1.2/CL/cl.hpp b/3rdparty/include/opencl/1.2/CL/cl.hpp index 0480e31163..2502d4c52c 100644 --- a/3rdparty/include/opencl/1.2/CL/cl.hpp +++ b/3rdparty/include/opencl/1.2/CL/cl.hpp @@ -210,7 +210,7 @@ #include #endif -#if defined(linux) || defined(__APPLE__) || defined(__MACOSX) +#if defined(__linux__) || defined(__APPLE__) || defined(__MACOSX) #include #include diff --git a/modules/ocl/src/cl_runtime/cl_runtime.cpp b/modules/ocl/src/cl_runtime/cl_runtime.cpp index a0d967c0b9..c3a31ccc33 100644 --- a/modules/ocl/src/cl_runtime/cl_runtime.cpp +++ b/modules/ocl/src/cl_runtime/cl_runtime.cpp @@ -45,7 +45,7 @@ #define CV_CL_GET_PROC_ADDRESS(name) WinGetProcAddress(name) #endif // _WIN32 -#if defined(linux) +#if defined(__linux__) #include #include diff --git a/modules/ocl/src/cl_runtime/clamdblas_runtime.cpp b/modules/ocl/src/cl_runtime/clamdblas_runtime.cpp index 0a077db691..1256000c8c 100644 --- a/modules/ocl/src/cl_runtime/clamdblas_runtime.cpp +++ b/modules/ocl/src/cl_runtime/clamdblas_runtime.cpp @@ -27,7 +27,7 @@ #define CV_CL_GET_PROC_ADDRESS(name) WinGetProcAddress(name) #endif // _WIN32 -#if defined(linux) +#if defined(__linux__) #include #include diff --git a/modules/ocl/src/cl_runtime/clamdfft_runtime.cpp b/modules/ocl/src/cl_runtime/clamdfft_runtime.cpp index 60cbecef2a..f0fa769b7d 100644 --- a/modules/ocl/src/cl_runtime/clamdfft_runtime.cpp +++ b/modules/ocl/src/cl_runtime/clamdfft_runtime.cpp @@ -27,7 +27,7 @@ #define CV_CL_GET_PROC_ADDRESS(name) WinGetProcAddress(name) #endif // _WIN32 -#if defined(linux) +#if defined(__linux__) #include #include diff --git a/modules/ocl/src/cl_runtime/generator/template/clamdblas_runtime.cpp.in b/modules/ocl/src/cl_runtime/generator/template/clamdblas_runtime.cpp.in index 8492edda9e..0cf33bb7ef 100644 --- a/modules/ocl/src/cl_runtime/generator/template/clamdblas_runtime.cpp.in +++ b/modules/ocl/src/cl_runtime/generator/template/clamdblas_runtime.cpp.in @@ -24,7 +24,7 @@ #define CV_CL_GET_PROC_ADDRESS(name) WinGetProcAddress(name) #endif // _WIN32 -#if defined(linux) +#if defined(__linux__) #include #include diff --git a/modules/ocl/src/cl_runtime/generator/template/clamdfft_runtime.cpp.in b/modules/ocl/src/cl_runtime/generator/template/clamdfft_runtime.cpp.in index aee6bd8ab6..8e8fb42c4f 100644 --- a/modules/ocl/src/cl_runtime/generator/template/clamdfft_runtime.cpp.in +++ b/modules/ocl/src/cl_runtime/generator/template/clamdfft_runtime.cpp.in @@ -24,7 +24,7 @@ #define CV_CL_GET_PROC_ADDRESS(name) WinGetProcAddress(name) #endif // _WIN32 -#if defined(linux) +#if defined(__linux__) #include #include From 2272a5876972f74684c43f2069c3046bd2888d01 Mon Sep 17 00:00:00 2001 From: Seunghoon Park Date: Tue, 14 Jan 2014 20:47:23 -0500 Subject: [PATCH 036/293] fixing bug #3345. don't use BORDER_ISOLATED alone. it should be combined with some border type --- modules/imgproc/test/test_filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index d1e45b0414..ac678e83a6 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -1901,7 +1901,7 @@ TEST(Imgproc_Blur, borderTypes) EXPECT_EQ(227, dst.at(0, 0)); // should work like BORDER_ISOLATED - blur(src_roi, dst, kernelSize, Point(-1, -1), BORDER_ISOLATED); + blur(src_roi, dst, kernelSize, Point(-1, -1), BORDER_REPLICATE | BORDER_ISOLATED); EXPECT_EQ(0, dst.at(0, 0)); /// ksize <= src_roi.size() From 8ce691e67966954cbab1e61f123f8206a42b1e90 Mon Sep 17 00:00:00 2001 From: Daniil Osokin Date: Mon, 13 Jan 2014 14:20:42 +0400 Subject: [PATCH 037/293] Fixed cvtColor alpha channel docs --- modules/imgproc/doc/miscellaneous_transformations.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/doc/miscellaneous_transformations.rst b/modules/imgproc/doc/miscellaneous_transformations.rst index e3c459d68c..515d6128f8 100644 --- a/modules/imgproc/doc/miscellaneous_transformations.rst +++ b/modules/imgproc/doc/miscellaneous_transformations.rst @@ -113,6 +113,8 @@ But in case of a non-linear transformation, an input RGB image should be normali If you use ``cvtColor`` with 8-bit images, the conversion will have some information lost. For many applications, this will not be noticeable but it is recommended to use 32-bit images in applications that need the full range of colors or that convert an image before an operation and then convert back. +If conversion adds the alpha channel, its value will set to the maximum of corresponding channel range: 255 for ``CV_8U``, 65535 for ``CV_16U``, 1 for ``CV_32F``. + The function can do the following transformations: * @@ -127,7 +129,7 @@ The function can do the following transformations: .. math:: - \text{Gray to RGB[A]:} \quad R \leftarrow Y, G \leftarrow Y, B \leftarrow Y, A \leftarrow 0 + \text{Gray to RGB[A]:} \quad R \leftarrow Y, G \leftarrow Y, B \leftarrow Y, A \leftarrow \max (ChannelRange) The conversion from a RGB image to gray is done with: From f02204847af64656a8bff7a4b5ec1f55df11643b Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Fri, 17 Jan 2014 13:02:35 +0400 Subject: [PATCH 038/293] Revert "fixed bug #3319" See 092f916 for explanation. This reverts commit 4f9c081dc313f8fdfee3f0a4572779ae13e27e40. --- modules/core/src/precomp.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index ec8dcc9f23..c53224e0aa 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -129,14 +129,12 @@ template struct OpMax inline Size getContinuousSize( const Mat& m1, int widthScale=1 ) { - CV_Assert(m1.dims <= 2); return m1.isContinuous() ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } inline Size getContinuousSize( const Mat& m1, const Mat& m2, int widthScale=1 ) { - CV_Assert(m1.dims <= 2 && m1.size() == m2.size()); return (m1.flags & m2.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } @@ -144,7 +142,6 @@ inline Size getContinuousSize( const Mat& m1, const Mat& m2, int widthScale=1 ) inline Size getContinuousSize( const Mat& m1, const Mat& m2, const Mat& m3, int widthScale=1 ) { - CV_Assert(m1.dims <= 2 && m1.size() == m2.size() && m1.size() == m3.size()); return (m1.flags & m2.flags & m3.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } @@ -153,7 +150,6 @@ inline Size getContinuousSize( const Mat& m1, const Mat& m2, const Mat& m3, const Mat& m4, int widthScale=1 ) { - CV_Assert(m1.dims <= 2 && m1.size() == m2.size() && m1.size() == m3.size() && m1.size() == m4.size()); return (m1.flags & m2.flags & m3.flags & m4.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } @@ -162,7 +158,6 @@ inline Size getContinuousSize( const Mat& m1, const Mat& m2, const Mat& m3, const Mat& m4, const Mat& m5, int widthScale=1 ) { - CV_Assert(m1.dims <= 2 && m1.size() == m2.size() && m1.size() == m3.size() && m1.size() == m4.size() && m1.size() == m5.size()); return (m1.flags & m2.flags & m3.flags & m4.flags & m5.flags & Mat::CONTINUOUS_FLAG) != 0 ? Size(m1.cols*m1.rows*widthScale, 1) : Size(m1.cols*widthScale, m1.rows); } From ee97a5e7573e292ea0d029a257fea0542c38e4a5 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Fri, 17 Jan 2014 14:13:21 +0400 Subject: [PATCH 039/293] Re-fix bug #3319 with less side effects. --- modules/core/src/copy.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 894776bb32..b87d080de5 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -286,6 +286,7 @@ void Mat::copyTo( OutputArray _dst, InputArray _mask ) const if( dims <= 2 ) { + CV_Assert( size() == mask.size() ); Size sz = getContinuousSize(*this, dst, mask, mcn); copymask(data, step, mask.data, mask.step, dst.data, dst.step, sz, &esz); return; From 4e4a7d035335b8db373f479335abd095fc582446 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Fri, 17 Jan 2014 14:16:22 +0400 Subject: [PATCH 040/293] Removed an unnecessary workaround for matrix-to-vector copyTo. --- modules/core/src/copy.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index b87d080de5..b8d87fc224 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -232,10 +232,7 @@ void Mat::copyTo( OutputArray _dst ) const const uchar* sptr = data; uchar* dptr = dst.data; - // to handle the copying 1xn matrix => nx1 std vector. - Size sz = size() == dst.size() ? - getContinuousSize(*this, dst) : - getContinuousSize(*this); + Size sz = getContinuousSize(*this, dst); size_t len = sz.width*elemSize(); for( ; sz.height--; sptr += step, dptr += dst.step ) From 5f8d8c0069a715dd3dfbbcd6a01a6715e74571b2 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Fri, 17 Jan 2014 14:18:31 +0400 Subject: [PATCH 041/293] Added a test for matrix-to-vector copy and convert. --- modules/core/test/test_mat.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 514b587d75..a6ebe152d9 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -897,3 +897,24 @@ TEST(Core_Mat, reshape_1942) ); ASSERT_EQ(1, cn); } + +TEST(Core_Mat, copyNx1ToVector) +{ + cv::Mat_ src(5, 1); + cv::Mat_ ref_dst8; + cv::Mat_ ref_dst16; + std::vector dst8; + std::vector dst16; + + src << 1, 2, 3, 4, 5; + + src.copyTo(ref_dst8); + src.copyTo(dst8); + + ASSERT_PRED_FORMAT2(cvtest::MatComparator(0, 0), ref_dst8, cv::Mat_(dst8)); + + src.convertTo(ref_dst16, CV_16U); + src.convertTo(dst16, CV_16U); + + ASSERT_PRED_FORMAT2(cvtest::MatComparator(0, 0), ref_dst16, cv::Mat_(dst16)); +} From 6bf599b1bca8a58c7a656ddc169f7be0fc3094c6 Mon Sep 17 00:00:00 2001 From: Drew Jetter Date: Sat, 18 Jan 2014 16:39:50 -0700 Subject: [PATCH 042/293] Fixed bug #3489: The code assumed that two global variables would be constructed in a particular order, but global variable initialization order is compiler-dependent. --- modules/core/src/system.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index ee0799ce05..ef45b9b5b6 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -1131,17 +1131,24 @@ public: } } }; -static TLSContainerStorage tlsContainerStorage; + +// This is a wrapper function that will ensure 'tlsContainerStorage' is constructed on first use. +// For more information: http://www.parashift.com/c++-faq/static-init-order-on-first-use.html +static TLSContainerStorage& getTLSContainerStorage() +{ + static TLSContainerStorage *tlsContainerStorage = new TLSContainerStorage(); + return *tlsContainerStorage; +} TLSDataContainer::TLSDataContainer() : key_(-1) { - key_ = tlsContainerStorage.allocateKey(this); + key_ = getTLSContainerStorage().allocateKey(this); } TLSDataContainer::~TLSDataContainer() { - tlsContainerStorage.releaseKey(key_, this); + getTLSContainerStorage().releaseKey(key_, this); key_ = -1; } @@ -1166,7 +1173,7 @@ TLSStorage::~TLSStorage() void*& data = tlsData_[i]; if (data) { - tlsContainerStorage.destroyData(i, data); + getTLSContainerStorage().destroyData(i, data); data = NULL; } } From 02ebc4368c34df1904f1a6f3b5eef96a496aaf9f Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 19 Jan 2014 03:16:06 +0400 Subject: [PATCH 043/293] Viz initial backport, compilation fixes, moved viz/viz.hpp header according to 2.4 style --- CMakeLists.txt | 5 + cmake/OpenCVDetectVTK.cmake | 21 + doc/tutorials/images/viz.jpg | Bin 0 -> 31804 bytes doc/tutorials/tutorials.rst | 16 + .../viz/creating_widgets/creating_widgets.rst | 159 +++ .../creating_widgets/images/red_triangle.png | Bin 0 -> 10489 bytes .../viz/launching_viz/images/window_demo.png | Bin 0 -> 7519 bytes .../viz/launching_viz/launching_viz.rst | 118 ++ .../images/facedetect.jpg | Bin 0 -> 17902 bytes .../images/image_effects.png | Bin 0 -> 29966 bytes .../viz/table_of_content_viz/images/intro.png | Bin 0 -> 2584 bytes .../table_of_content_viz.rst | 94 ++ .../images/camera_view_point.png | Bin 0 -> 18439 bytes .../images/global_view_point.png | Bin 0 -> 13767 bytes .../viz/transformations/transformations.rst | 202 +++ .../viz/widget_pose/images/widgetpose.png | Bin 0 -> 40892 bytes doc/tutorials/viz/widget_pose/widget_pose.rst | 162 +++ modules/core/include/opencv2/core/affine.hpp | 509 ++++++++ modules/core/include/opencv2/core/core.hpp | 1 + modules/viz/CMakeLists.txt | 11 + modules/viz/doc/images/cpw1.png | Bin 0 -> 4321 bytes modules/viz/doc/images/cpw2.png | Bin 0 -> 3548 bytes modules/viz/doc/images/cpw3.png | Bin 0 -> 17724 bytes modules/viz/doc/images/cube_widget.png | Bin 0 -> 6507 bytes modules/viz/doc/viz.rst | 9 + modules/viz/doc/viz3d.rst | 637 ++++++++++ modules/viz/doc/widget.rst | 1019 +++++++++++++++ modules/viz/include/opencv2/viz/types.hpp | 236 ++++ modules/viz/include/opencv2/viz/viz3d.hpp | 131 ++ modules/viz/include/opencv2/viz/vizcore.hpp | 127 ++ .../include/opencv2/viz/widget_accessor.hpp | 69 ++ modules/viz/include/opencv2/viz/widgets.hpp | 396 ++++++ modules/viz/src/clouds.cpp | 441 +++++++ modules/viz/src/interactor_style.cpp | 639 ++++++++++ modules/viz/src/interactor_style.hpp | 119 ++ modules/viz/src/precomp.hpp | 324 +++++ modules/viz/src/shapes.cpp | 1088 +++++++++++++++++ modules/viz/src/types.cpp | 206 ++++ modules/viz/src/viz3d.cpp | 148 +++ modules/viz/src/vizcore.cpp | 312 +++++ modules/viz/src/vizimpl.cpp | 542 ++++++++ modules/viz/src/vizimpl.hpp | 138 +++ modules/viz/src/vtk/vtkCloudMatSink.cpp | 158 +++ modules/viz/src/vtk/vtkCloudMatSink.h | 79 ++ modules/viz/src/vtk/vtkCloudMatSource.cpp | 286 +++++ modules/viz/src/vtk/vtkCloudMatSource.h | 96 ++ modules/viz/src/vtk/vtkImageMatSource.cpp | 143 +++ modules/viz/src/vtk/vtkImageMatSource.h | 82 ++ modules/viz/src/vtk/vtkOBJWriter.cpp | 241 ++++ modules/viz/src/vtk/vtkOBJWriter.h | 79 ++ modules/viz/src/vtk/vtkTrajectorySource.cpp | 110 ++ modules/viz/src/vtk/vtkTrajectorySource.h | 84 ++ modules/viz/src/vtk/vtkXYZWriter.cpp | 93 ++ modules/viz/src/vtk/vtkXYZWriter.h | 78 ++ modules/viz/src/widget.cpp | 327 +++++ modules/viz/test/test_main.cpp | 3 + modules/viz/test/test_precomp.cpp | 24 + modules/viz/test/test_precomp.hpp | 104 ++ modules/viz/test/test_tutorial2.cpp | 54 + modules/viz/test/test_tutorial3.cpp | 64 + modules/viz/test/test_viz3d.cpp | 64 + modules/viz/test/tests_simple.cpp | 407 ++++++ 62 files changed, 10455 insertions(+) create mode 100644 cmake/OpenCVDetectVTK.cmake create mode 100644 doc/tutorials/images/viz.jpg create mode 100644 doc/tutorials/viz/creating_widgets/creating_widgets.rst create mode 100644 doc/tutorials/viz/creating_widgets/images/red_triangle.png create mode 100644 doc/tutorials/viz/launching_viz/images/window_demo.png create mode 100644 doc/tutorials/viz/launching_viz/launching_viz.rst create mode 100644 doc/tutorials/viz/table_of_content_viz/images/facedetect.jpg create mode 100644 doc/tutorials/viz/table_of_content_viz/images/image_effects.png create mode 100644 doc/tutorials/viz/table_of_content_viz/images/intro.png create mode 100644 doc/tutorials/viz/table_of_content_viz/table_of_content_viz.rst create mode 100644 doc/tutorials/viz/transformations/images/camera_view_point.png create mode 100644 doc/tutorials/viz/transformations/images/global_view_point.png create mode 100644 doc/tutorials/viz/transformations/transformations.rst create mode 100644 doc/tutorials/viz/widget_pose/images/widgetpose.png create mode 100644 doc/tutorials/viz/widget_pose/widget_pose.rst create mode 100644 modules/core/include/opencv2/core/affine.hpp create mode 100644 modules/viz/CMakeLists.txt create mode 100644 modules/viz/doc/images/cpw1.png create mode 100644 modules/viz/doc/images/cpw2.png create mode 100644 modules/viz/doc/images/cpw3.png create mode 100644 modules/viz/doc/images/cube_widget.png create mode 100644 modules/viz/doc/viz.rst create mode 100644 modules/viz/doc/viz3d.rst create mode 100644 modules/viz/doc/widget.rst create mode 100644 modules/viz/include/opencv2/viz/types.hpp create mode 100644 modules/viz/include/opencv2/viz/viz3d.hpp create mode 100644 modules/viz/include/opencv2/viz/vizcore.hpp create mode 100644 modules/viz/include/opencv2/viz/widget_accessor.hpp create mode 100644 modules/viz/include/opencv2/viz/widgets.hpp create mode 100644 modules/viz/src/clouds.cpp create mode 100644 modules/viz/src/interactor_style.cpp create mode 100644 modules/viz/src/interactor_style.hpp create mode 100644 modules/viz/src/precomp.hpp create mode 100644 modules/viz/src/shapes.cpp create mode 100644 modules/viz/src/types.cpp create mode 100644 modules/viz/src/viz3d.cpp create mode 100644 modules/viz/src/vizcore.cpp create mode 100644 modules/viz/src/vizimpl.cpp create mode 100644 modules/viz/src/vizimpl.hpp create mode 100644 modules/viz/src/vtk/vtkCloudMatSink.cpp create mode 100644 modules/viz/src/vtk/vtkCloudMatSink.h create mode 100644 modules/viz/src/vtk/vtkCloudMatSource.cpp create mode 100644 modules/viz/src/vtk/vtkCloudMatSource.h create mode 100644 modules/viz/src/vtk/vtkImageMatSource.cpp create mode 100644 modules/viz/src/vtk/vtkImageMatSource.h create mode 100644 modules/viz/src/vtk/vtkOBJWriter.cpp create mode 100644 modules/viz/src/vtk/vtkOBJWriter.h create mode 100644 modules/viz/src/vtk/vtkTrajectorySource.cpp create mode 100644 modules/viz/src/vtk/vtkTrajectorySource.h create mode 100644 modules/viz/src/vtk/vtkXYZWriter.cpp create mode 100644 modules/viz/src/vtk/vtkXYZWriter.h create mode 100644 modules/viz/src/widget.cpp create mode 100644 modules/viz/test/test_main.cpp create mode 100644 modules/viz/test/test_precomp.cpp create mode 100644 modules/viz/test/test_precomp.hpp create mode 100644 modules/viz/test/test_tutorial2.cpp create mode 100644 modules/viz/test/test_tutorial3.cpp create mode 100644 modules/viz/test/test_viz3d.cpp create mode 100644 modules/viz/test/tests_simple.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d7db8fd130..aa4a2e28f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,7 @@ OCV_OPTION(WITH_1394 "Include IEEE1394 support" ON OCV_OPTION(WITH_AVFOUNDATION "Use AVFoundation for Video I/O" ON IF IOS) OCV_OPTION(WITH_CARBON "Use Carbon for UI instead of Cocoa" OFF IF APPLE ) OCV_OPTION(WITH_CUDA "Include NVidia Cuda Runtime support" ON IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT IOS) ) +OCV_OPTION(WITH_VTK "Include VTK library support (and build opencv_viz module eiher)" ON IF (NOT ANDROID AND NOT IOS) ) OCV_OPTION(WITH_CUFFT "Include NVidia Cuda Fast Fourier Transform (FFT) library support" ON IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT IOS) ) OCV_OPTION(WITH_CUBLAS "Include NVidia Cuda Basic Linear Algebra Subprograms (BLAS) library support" OFF IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT IOS) ) OCV_OPTION(WITH_NVCUVID "Include NVidia Video Decoding library support" OFF IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT ANDROID AND NOT IOS AND NOT APPLE) ) @@ -471,6 +472,9 @@ if(WITH_OPENCL) include(cmake/OpenCVDetectOpenCL.cmake) endif() +# --- VTK support --- +include(cmake/OpenCVDetectVTK.cmake) + # ---------------------------------------------------------------------------- # Add CUDA libraries (needed for apps/tools, samples) # ---------------------------------------------------------------------------- @@ -705,6 +709,7 @@ else() endif() status(" OpenGL support:" HAVE_OPENGL THEN "YES (${OPENGL_LIBRARIES})" ELSE NO) +status(" VTK support:" HAVE_VTK THEN "YES (ver ${VTK_VERSION})" ELSE NO) # ========================== MEDIA IO ========================== status("") diff --git a/cmake/OpenCVDetectVTK.cmake b/cmake/OpenCVDetectVTK.cmake new file mode 100644 index 0000000000..ef9aa8043c --- /dev/null +++ b/cmake/OpenCVDetectVTK.cmake @@ -0,0 +1,21 @@ +if(NOT WITH_VTK OR ANDROID OR IOS) + return() +endif() + +find_package(VTK 6.0 QUIET COMPONENTS vtkRenderingCore vtkInteractionWidgets vtkInteractionStyle vtkIOLegacy vtkIOPLY vtkRenderingFreeType vtkRenderingLOD vtkFiltersTexture vtkIOExport NO_MODULE) + +if(NOT DEFINED VTK_FOUND OR NOT VTK_FOUND) + find_package(VTK 5.10 QUIET COMPONENTS vtkCommon vtkFiltering vtkRendering vtkWidgets vtkImaging NO_MODULE) +endif() + +if(NOT DEFINED VTK_FOUND OR NOT VTK_FOUND) + find_package(VTK 5.8 QUIET COMPONENTS vtkCommon vtkFiltering vtkRendering vtkWidgets vtkImaging NO_MODULE) +endif() + +if(VTK_FOUND) + set(HAVE_VTK ON) + message(STATUS "Found VTK ver. ${VTK_VERSION} (usefile: ${VTK_USE_FILE})") +else() + set(HAVE_VTK OFF) + message(STATUS "VTK is not found. Please set -DVTK_DIR in CMake to VTK build directory, or set $VTK_DIR enviroment variable to VTK install subdirectory with VTKConfig.cmake file (for windows)") +endif() diff --git a/doc/tutorials/images/viz.jpg b/doc/tutorials/images/viz.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7ac8f3ed8d641d2332755e8c9196517f1438b616 GIT binary patch literal 31804 zcmcF~Wl$VZ)9x;dC0KBmun-a~!F3^o;OV% zR5Y|S|62+0-=i4l7`T|2xHLrgL^S{7@wXj-j|n^gZh?UG02F*62p{;j3qS<`0)S}$ zy#2oe9SA~2!$86OC)Ile0D?d$|6E7I1Yu!fy!dxF%0DSS!E1Cv4h(uR6=Nb!2VY|F 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 Za{J9|C-wgpr~fA2NBO^g0R8Xc{{oslWW)df literal 0 HcmV?d00001 diff --git a/doc/tutorials/tutorials.rst b/doc/tutorials/tutorials.rst index cbc51c1956..54cc91acb8 100644 --- a/doc/tutorials/tutorials.rst +++ b/doc/tutorials/tutorials.rst @@ -186,6 +186,21 @@ As always, we would be happy to hear your comments and receive your contribution :width: 80pt :alt: gpu icon +* :ref:`Table-Of-Content-Viz` + + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + =========== ======================================================= + |Viz| These tutorials show how to use Viz module effectively. + + =========== ======================================================= + + .. |Viz| image:: images/viz.jpg + :height: 80pt + :width: 80pt + :alt: viz icon + * :ref:`Table-Of-Content-General` .. tabularcolumns:: m{100pt} m{300pt} @@ -221,4 +236,5 @@ As always, we would be happy to hear your comments and receive your contribution gpu/table_of_content_gpu/table_of_content_gpu contrib/table_of_content_contrib/table_of_content_contrib ios/table_of_content_ios/table_of_content_ios + viz/table_of_content_viz/table_of_content_viz general/table_of_content_general/table_of_content_general diff --git a/doc/tutorials/viz/creating_widgets/creating_widgets.rst b/doc/tutorials/viz/creating_widgets/creating_widgets.rst new file mode 100644 index 0000000000..8858035c34 --- /dev/null +++ b/doc/tutorials/viz/creating_widgets/creating_widgets.rst @@ -0,0 +1,159 @@ +.. _creating_widgets: + +Creating Widgets +**************** + +Goal +==== + +In this tutorial you will learn how to + +.. container:: enumeratevisibleitemswithsquare + + * Create your own widgets using WidgetAccessor and VTK. + * Show your widget in the visualization window. + +Code +==== + +You can download the code from :download:`here <../../../../samples/cpp/tutorial_code/viz/creating_widgets.cpp>`. + +.. code-block:: cpp + + #include + #include + #include + + #include + #include + #include + #include + #include + #include + #include + #include + + using namespace cv; + using namespace std; + + /** + * @class WTriangle + * @brief Defining our own 3D Triangle widget + */ + class WTriangle : public viz::Widget3D + { + public: + WTriangle(const Point3f &pt1, const Point3f &pt2, const Point3f &pt3, const viz::Color & color = viz::Color::white()); + }; + + /** + * @function WTriangle::WTriangle + */ + WTriangle::WTriangle(const Point3f &pt1, const Point3f &pt2, const Point3f &pt3, const viz::Color & color) + { + // Create a triangle + vtkSmartPointer points = vtkSmartPointer::New(); + points->InsertNextPoint(pt1.x, pt1.y, pt1.z); + points->InsertNextPoint(pt2.x, pt2.y, pt2.z); + points->InsertNextPoint(pt3.x, pt3.y, pt3.z); + + vtkSmartPointer triangle = vtkSmartPointer::New(); + triangle->GetPointIds()->SetId(0,0); + triangle->GetPointIds()->SetId(1,1); + triangle->GetPointIds()->SetId(2,2); + + vtkSmartPointer cells = vtkSmartPointer::New(); + cells->InsertNextCell(triangle); + + // Create a polydata object + vtkSmartPointer polyData = vtkSmartPointer::New(); + + // Add the geometry and topology to the polydata + polyData->SetPoints(points); + polyData->SetPolys(cells); + + // Create mapper and actor + vtkSmartPointer mapper = vtkSmartPointer::New(); + #if VTK_MAJOR_VERSION <= 5 + mapper->SetInput(polyData); + #else + mapper->SetInputData(polyData); + #endif + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + // Store this actor in the widget in order that visualizer can access it + viz::WidgetAccessor::setProp(*this, actor); + + // Set the color of the widget. This has to be called after WidgetAccessor. + setColor(color); + } + + /** + * @function main + */ + int main() + { + /// Create a window + viz::Viz3d myWindow("Creating Widgets"); + + /// Create a triangle widget + WTriangle tw(Point3f(0.0,0.0,0.0), Point3f(1.0,1.0,1.0), Point3f(0.0,1.0,0.0), viz::Color::red()); + + /// Show widget in the visualizer window + myWindow.showWidget("TRIANGLE", tw); + + /// Start event loop + myWindow.spin(); + + return 0; + } + +Explanation +=========== + +Here is the general structure of the program: + +* Extend Widget3D class to create a new 3D widget. + +.. code-block:: cpp + + class WTriangle : public viz::Widget3D + { + public: + WTriangle(const Point3f &pt1, const Point3f &pt2, const Point3f &pt3, const viz::Color & color = viz::Color::white()); + }; + +* Assign a VTK actor to the widget. + +.. code-block:: cpp + + // Store this actor in the widget in order that visualizer can access it + viz::WidgetAccessor::setProp(*this, actor); + +* Set color of the widget. + +.. code-block:: cpp + + // Set the color of the widget. This has to be called after WidgetAccessor. + setColor(color); + +* Construct a triangle widget and display it in the window. + +.. code-block:: cpp + + /// Create a triangle widget + WTriangle tw(Point3f(0.0,0.0,0.0), Point3f(1.0,1.0,1.0), Point3f(0.0,1.0,0.0), viz::Color::red()); + + /// Show widget in the visualizer window + myWindow.showWidget("TRIANGLE", tw); + +Results +======= + +Here is the result of the program. + +.. image:: images/red_triangle.png + :alt: Creating Widgets + :align: center diff --git a/doc/tutorials/viz/creating_widgets/images/red_triangle.png b/doc/tutorials/viz/creating_widgets/images/red_triangle.png new file mode 100644 index 0000000000000000000000000000000000000000..7da6ad0602a974e00695f9c0aa698c5dc474b2be GIT binary patch literal 10489 zcmeI2_g_;_(C?#&1rRJm5fLo3&{Y)apdg0OL8>&7VrbHdlwd^>f`qO#kepKsMOFRC17Z|dwjn4U${Tq*TXNIGxM4E?#|AcO*p*P)6qD}!p#DM!Hy#Ds2RXu z`wL*OeaVLp0?UnV&G+Ef0k4}#qr>1Q@bH6I;Qoxax~aFJyMwo%t*1TA(aqh}Ufj#h z)85|A%gNoF%G9U?gPn&V)l`lAla@%BB$B-?b%p9K-+oS}iPIe15NY^oo>NfnU*t*B`})R+^;O8Cvx?|>i#vzSzVRm} ze)p^(`!a4g3SLd5@s}#hCiQI7!p=B!Q)PSTU0(xNlV*2tK7(C;t*3?d46S!&Sk2cP zbmtUTmTD+MMbJ7E*p~uor(2_>XYVA}c*fObYcIPm2j0utkwLKBs>h5Stw9<%FEn31 zRC;^&ceeGp-5;tlT#q|f_~h54?Re!oBJzvfq&nh5mG1}2@C+wyyV)Pz9$6`E-4)hI zx=H_%8_7$#5RMhR#H)<+XjSkHFV1JWVa?QfW-_vK?vm-3?^*8x_&lB9${rsXsZBqu z&uo@y{H@MkwY7MLy_2Wl+sKf6MKx=zVqm5JenjJ_+n+i)G`^Gb6h8SV0l6tSGd|Dw+y{h+Suzpf2+)%k>~Ew%rz3p>D`DMKU-LT zr}~~w)OM0X7dnc6dhLbCOD2##K^^O8-^Gc1gMph%<5AJ?JEzy_Jwdm{#jnEozALNl zgTX?M5c{tfy1QUd0bU;#CnhE+DI6eBDm{>=)7)=s)PLstnM2YlJZ)jW?|2wZ)EX>l zy7I(tPSMVp>?s;!zL?VeI_JkW9ckp?dy_^=56$ob7C5`k60wnrf*ZK)HfAuy$j0&# zUX%j6UwR_XmY=){fBvCaYB^H1t$0cfr!ahO<*uLyPyB#%-0vHBYG_!qo<1ic%x)yM zsqCCa%Iuvu%iyJ3`Kq7Ic-!#5{dp#Ul$ADFp=aF7vnA&W^fP@8xT0#0p>1%BU~F09joq|A5U5 zjBEV!kVu@Sd5w3@DM`1%Bw4R(&c<)#dDlFg2xEo)Idb?0P4L6|`)Uk63 z9X1k+BaV*d!bRiq?1#jzIhM{-DEf}GR<7e!`WBULcY*vw3Qt>9M*GoWVLd;`O3$b~ z7gXt$yQxdu$2C}*P2(oNo$;5`#_rg4NwVMV7duyjPKUSU6itJvi9S-K_Z zWO6ws{*F=WTcZ(8z11L0N>*u)9IDi@Qkd=13)>7~mIq^nJXt$s!2tnT!Mi`7tp-^i z>B)PZIj9UO*ptU1E44v~_0)G0&ZElR%cOwyKVKrVr?xc(PI%tSs&R_2gV${2=JB^9 zA8srX)8-?eJsp*2VX;TZenjHf2e*~iJS?Rbe1fP{26EGvASf{Y`-ahF6TJp5;lrJ$ z&lI22c>Ta2G4pH2L46*0d1c!`l6vX%#-hGXEbPsG+Fw-W&y_Y2AD^63E|iWzsRLm; zV%>jIkF-SU(7;E#8T8A$5b5d!UbwN&DJ~X>{?j?zx~~_uV#M=<{p6I;xuB)RkFmHQdNS$KM6l{bf_t4^?B4v|?BJ<`o|YzY0_stm&p+T_ zXLQTE*YgY;=<4cXtX+sQ@p!Q@yZ|cE#r$*TvLMdv=A10Q(W`Y^Kv zCKo9=m6Zy(-wkc$?nyIe%#!^kFg4rD0Y)r=0%9oK)o!2C8^*k)6tRWOQMFY;)#jaWI4*Fp~o6AjXY+LQ=R*+bs&fv7Ba z9MSS!Ryno7^mIyvX6G!yUeuV{9kJE*adWQCuRc4l`F+g!ut|K))05u2;wE!} z-|Uo$>c@;>_kMXtl&jxfav{cPoqlw)?IJF(;o^*xU3ef=mVa{1H^VL={-A31V&I+u zV|T@7G?ddSQC7LgR7Kn3LjQH2>e#F)xcVweAlYX|uEirQP(rw^Da1M`Ku5gO;Mm{Q zy5~Cbt`W<%)M3x1Ro-FYFe8yuG@SAciwf~+FZ$)yBU6}^p#pgVHpxRH&%PZswLTVL z9fSRj#;gs^hY!hnTZG2=*u5Z_zR{7t%gUeQ_-q+-O|QSfnz5A}Gt@Pn9*`<7Vox8f zm*yWl_BD1LX7NT)LH+ZA&zNEp2%Cta)o*rG} z$uz%osepJ-m{A9u_@0CFYq8jvehH$$r9a0-19x|xa@X&&5nT_3o6SK)8~013o^~WZ zk6BnW=+=Jdi!#W;eU4O4eq=P)EovRc*&4L=C`OB;P8sQuVbG8^ag!V97wM&A2yYUwL%7Ppz4ckgxp4cyWq~of?c5&E<@9ca~2nxhgWt z0}DAQy&OBVnwwd~dfGu+xx1aZ7{gyatvQ;!>9d}s#c;Kzd_yeAqPqm)GqvSXW9h7? z?P+<60K18aJUldk#UIEUr$v^i%ZkIagT@IKvveVuy{Z2ze94vq=ZS17df)#m0!PR zvCwUv&&T+rt#2&y{EhcYji;=y(ngw9q>K8H?c*N%*#)&q)P~pDb<%eY+DGTeGsi^X zwZVmXMfBrY#d0I(t9hu{NZf(1u-0QnBqDxu-ER;+8qn8=doic+6VzbsDB5 zy+0#0oG5kSJV<)MCWUpFoU5%`imUnirDn1Dq>t&4@xz|H$A7cP!+v~Z!h;U|xAE+? zQ|{{~juL}Of))mANKEq^IG**tjGp&v z-oIk`dbd8Fa{l!_Lktqkot9gVpoTk>14I?!Ch(z|-1a_@k+y9c2vyI#I> zVLG%S4_jNSJ<;^YCu2O5?3`un^yCzJ@TNs%<8fi1W`ucS3BULNCC_ngIdr2#Y5lM` zo`0#=y#fv&>pb+GYDQW$MjF?d&IbOyQa?-lB0V61*4<7!dOqxnO8txC8KLQb6Xx{p zUp>zDS>F%OE~&y#RN(U_3Y;Z_LU9%e`1EwSN`;*FjXOcRN?Hijs{Jr-JpuDJKRe~a zK0|e7PbVbQpGYD{%?{Hd&rT2r+a&w@O5d+*I$Bu=yVVxTt>}qs#k!wd_oXai9+Jwh zN{;!$4@R9TmoQLDHQ{uWb{r`ur$R?#1CV zZVly(=hxDH23cfe}i4zgfrF zy@b%yjW1eEy5!vr#}=3o8i!0$#1Zh@EbnK~aVAEa6L)eF&vYrTx>(Y4@Yk`wuh%C| z$Otsn8j_wEjV2pPe-D1Hch%>6d#H1iC_*GB?YTi|JSAVb(R^q_mqNDYm}V}-?RECd z4LE*(%jrz*PG)hC>`i*nSvKX|UBrM@u3 z_)W&7&B#M{!%B1qO#Bz$fZ_hRoiK7NJHon+S86t1eS3PXGga?R5!Jp$Z zZm`6*KQI!BJs1(yGkYjvK#1*G2lc}{kx8TNm*1>Jdh;3swF0{3biEu>@@b)rJLJH@ z{xGj0bOpa_kjkcOWlV=={kFPu86}`*CRZiuo%i=cN_ma)*h1oNhFc9qpLU%Pvz&?& zYpm6<+-s88arP~(b#Jui>M*O)S`$FJKA!PAx7vRs)=%ElQ?YVY6944tTz~yA)_}1W zCMloz_NrI1Qb{l_!?lJs+Ox1BbF)i%b6{(hqoDUSRRMW5)n6^R^kbI{_FjD8fT4p! z>6KD)VI%(RRR$gzr9TJ(%G=1GHJVw? z+?veXvrtFx{>0FZ#}7ZUcy;#@KxGi~+V)oDf|hdX;&$2@{SnmVF-EVSIGLr>;L3Pr zmW;ZbS9yVY9*6$wFmr*t+y$Sy>9b#bcHP2a~ir%7u+#Dt6oq*5XA^wd*axSQkwLVl_(ieNeq$7dmJ* ziab{`fE&!swp2CQZm<(a<$R-BBO`vBmJk`z4fbY_7mI~7>J|ocEXJ@dSEhw?>$n7S z)pDrYyOWCy|AWEztRlo2epk%SWR2R3wndjt?<3J>Ff}enW9MPM4^)@eK6|4M$YQG< z`sCmEU`C=|*|t@!>!e)@!FBcU2}( z9lN4igoyRoE_^TLG`G4j;6Eoa{#Msi^SWaUj#WfL>2|0l{?r0qGRQEVt^D9RzJZyi?iV@zukMZSi`6Vn=960uT@I$=S@>}*)!uO=` zB&nG4Kf>jj2EWuv+qG5%gIm`P+2|=FI;v+=M??ygjF$WD$`%isv^i!}24h0qEUL6p zm1u|S?FMZ}%ftlQhx2sZ4d;#qsV0>EbTt=7R+AS##IK?|ez2gJ_%!(4^XV;79YluY#Q9XZ=s z;t-R$X>S&L`pb{)be}4e8Ti^FEK)>-ziHX? z8_C6q_lyxjv3n(ZQysCIseujM{)ql};TbyzcNZyc0(HbcMG722wCkxk3xx_uGdOL$ zkx{z*!vPV^tq^%Ui<9H8l^O2ohRmQuj@g<|bM7h9MxF%bp+?1BD-!!nizDs{w!b^% z6U=n%QS-ctn9YGvSsVKs%b2?Xlcp2J@e3bM=j&Y@Pv3T{*KL&KP4^3WRL@?JWaX@) zG!{F!B3#j08RBZTo!%?bcr0(#_&EL|Lc;pt{VF;KcWe0|y}5Ms!>#h{Y5(26fC02| zEw4Yi)Cx_nXUR&*wj&39IMoxMA+!G1nYkJ#ift@h#)`!*WNy^UjHPY!_UyFYD6)(b z56D;()OZ+B>Q=qX`gk&Og5Z}N5FS^E$x$3RWHREC&KN;UIxobYRnFLCAGi9MNyPg+pmhyb zO#AM{^9D{D?2g#>|BA<|nZz_?$;;H`u#*SAtGf^AulZ3DXMK-?kIB|uybZLH3g_}T z;k?Xb-MNi`3gOep&G#Xu3h&O8tZmFJBxhABzgN&zV^vc`e|~~cX<&a)5Noy zo(>CthB7oQYK=S%(K=LY>ZDvL%b3-mPJyvAx?JL(yorpWK5=}cUDb=s2yl?C$R{xf zj$@2Fr@K-Jlg4+@SkuVmS4J)Yrah;1iS3~U67B^5$%pknTsxh^Le=sZL#WXYcIEt- z{E48^d!$DU3r)5LA!fVo9^SROQ}--ZUd?vt&+!lPxD3@-2C6sK_#{qjw=B>a)LDHx zkY2P&`AuQp#Uu4D+%QU@pes9tPcrAZ0`*1!Ukv%G=518lCSg zyu0XG!Xp((kGGc+-fC-Fw43O#=`o6GVpjDrQKM|9F*StguxziGt;9bV0Up zP8&p&%WzSHN3G6osPfS9<@?EAE#-2vVf!j2U3yO!ZoLeyWuR^DS=sw+Dx~u%`aUU? z70(;w`2GP$-~ zfsLS+A$pKJbf;My>FLlB zala*s)1>sdLxAVla^Gy>NbZ}xzc-VEnpwmn-E{X%Yd)C}@TEHAjbRoRwp)H0_$$d$FwmWnAyHK5xj_$;|8Ogt|wrsDerQ@aL zd~+7uP_Zmy=at&3D9u zE0rqKOs?Ku|Qo?fkJXSzHbK)8x^yc{;Xe7j0qSLZJ;?o6w*B8TxFg zEX*=EW?^s)6&KIt`759$>Ty6A%YD@A%nZD(KB>iaxyLiMi}!1-v3gXPx^?g|L_VAQ zNCnTZz6hnGuL+Y8!|P4yGYxJx>Ejq2rpCF7Mrn#rG>yex5b&!^4HBr5b0+O&S(fBEzmZ-~A%Y9+8O5ppL{3~JspG%(sjM9PjAt) z)oU0+8yXJ08h+R{?KxUJDK3jEcp!3ZPTg3aGtWp&gzZv!g@ln@@-q2LP2}C`2gcb2 z7L|ywiVDs_i%j@OE3+(l$W=!w(#Jbk9 z1X?#xMZ<2Y>x+$5JQ%|%^hD2ME)_o5*lpo$o6cHCJt^wt=m;?s=AQIOmHG_m5)D#I!0+2{+Fxhl}X; zSd{RfUO&%LqbX-bH?7n6!St|C&*v|@-aE94dLkwoGg+mXHNa%`S6XRTlu$wVQ4(7g z9ai7K%C=N{(}B!ddT=@5+`}Yc&ls&6nvW+MHsitsTB4fotfKZp`Z%Vz3lak3>N2u^ zUC&Qg*}W1J47znzZsj31qXrxrx%aK8<;tlb@R~$VzLhbvAG{c8l9v6s1PrhyBiZc# zE*=R`qBK&ImNCE#`vTsYm`~Uo0+%zF%z@HIAsr5GTK|8$DLvPgxGW1T`I0A3sMEg} zJ+F+xEmw~R&xk}H^!Dyqb$%JhT9fzm(!N4@(i)_`Jyj(k3SL{nq|OHXPkuK3Y5ec4 zLLvV*PyZPu;_Cn)giZX4~W_v1d&n_O%%rUR4Q2s=#+pi^O)}J z`%oKZG!pjoh^-Nx}8os{43>F}mw3dxb?>L479 z!p)VJnJsicRFC}1IIa75rqcm%&48{gMJ~c=5jM=+&lE`~VX&Vn$MaPt&x3Scdku`N zV1?PT2Sj`QL8+@~Uod^rss?r*4K_$x0;EFkBv@SWaZn(W$Ma!86Vi9s7cIN&3T};;du&g+JiLuK=TFK(N?fO;m^Ud#P!66S&AEoq?Uls?%!^Q0TVAs zq|NBDd{`@31Wb$dDO}|mAT-5XGTmqN&x8QbC(wKiU)nG~O8e&oR30`6Mgg!x|70RS zrUuEPflNvtl0CUqKM#W)#zOxKkO4xp_@R(rAmn5OG?*|5c>sl+ghI@qkmryLDwRjQ zKo$+jctG(s89}l`;Xrm5lI^aA$*wtS&*QmjJ~Lp{@Z`?>7jw51?)XR0y<7Am>d^2Si~p zp!{sWX2CW~Tg7k-h{tymwf+w`;GZ@+i4`7e} zKr%HT(}F5c5Ipd(lMn+5$-aVh**t~DT!CcJT(#IC8AOYQWJe&`6p;Nq56S32cH=N4 zI|^j%$AHWbk}*LtB9tXf$f*dNxsYicB*iA_FkmaGffmFB^aG&&LoP!(*aa{y&}d)| zkP37FSOV0vY=Q4zU)%tVF9ZXLLTs2}S3z3!K*mCkavOedA$6>erRfG3!v+g!Kv2jpFh)2?6YMs4rlt0S!MKk0vDl1)_>f}Y%Y;KF zaF{iffx?1Ks+a9gI0Xo4++ZdM4G@?MnGAsGrwXKJ1Ns|!kQ1V^9|W*|2=)w;89`%T z1Tw*+K$aj3WJpLR2wJX9GbDQrYCk~_l3jsh5G_Fr$d3Gzo$O=znFh()f$WAbBohHL zcJM&L5=4NE3FHQr3213($e9a@AorgMoOL$iAcIZbpwz=zA-xXJUxr{50ONwzDYXx% zpyF@h1U_5fgGB@UOVBo9nIMi0LTP~$WDZ)m7Ff|+@Z7_`gLp^+J_|iL2zk(eBMoI< zPZXR{s?ZqbAiYOlfx0AuW5Nj>*|4-z#|u=Lprn?A$DI2B7(d%yO|%yVW~ox(l@$Qe zGEC*)wW$%>2WwKjPk?88L1DaLM&Q#JFX$}LvGu<<|C@vVhjmbm_uh_dQ2y_`!8&8# Y2MLWci`MvfP>e97x{g}mO`E6x2cG-67XSbN literal 0 HcmV?d00001 diff --git a/doc/tutorials/viz/launching_viz/images/window_demo.png b/doc/tutorials/viz/launching_viz/images/window_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..b853fe29da9b03ce7e606dae6aa743e3822df2df GIT binary patch literal 7519 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 A?*IS* literal 0 HcmV?d00001 diff --git a/doc/tutorials/viz/launching_viz/launching_viz.rst b/doc/tutorials/viz/launching_viz/launching_viz.rst new file mode 100644 index 0000000000..a3dd5d93c6 --- /dev/null +++ b/doc/tutorials/viz/launching_viz/launching_viz.rst @@ -0,0 +1,118 @@ +.. _launching_viz: + +Launching Viz +************* + +Goal +==== + +In this tutorial you will learn how to + +.. container:: enumeratevisibleitemswithsquare + + * Open a visualization window. + * Access a window by its name. + * Start event loop. + * Start event loop for a given amount of time. + +Code +==== + +You can download the code from :download:`here <../../../../samples/cpp/tutorial_code/viz/launching_viz.cpp>`. + +.. code-block:: cpp + + #include + #include + + using namespace cv; + using namespace std; + + /** + * @function main + */ + int main() + { + /// Create a window + viz::Viz3d myWindow("Viz Demo"); + + /// Start event loop + myWindow.spin(); + + /// Event loop is over when pressed q, Q, e, E + cout << "First event loop is over" << endl; + + /// Access window via its name + viz::Viz3d sameWindow = viz::getWindowByName("Viz Demo"); + + /// Start event loop + sameWindow.spin(); + + /// Event loop is over when pressed q, Q, e, E + cout << "Second event loop is over" << endl; + + /// Event loop is over when pressed q, Q, e, E + /// Start event loop once for 1 millisecond + sameWindow.spinOnce(1, true); + while(!sameWindow.wasStopped()) + { + /// Interact with window + + /// Event loop for 1 millisecond + sameWindow.spinOnce(1, true); + } + + /// Once more event loop is stopped + cout << "Last event loop is over" << endl; + return 0; + } + +Explanation +=========== + +Here is the general structure of the program: + +* Create a window. + +.. code-block:: cpp + + /// Create a window + viz::Viz3d myWindow("Viz Demo"); + +* Start event loop. This event loop will run until user terminates it by pressing **e**, **E**, **q**, **Q**. + +.. code-block:: cpp + + /// Start event loop + myWindow.spin(); + +* Access same window via its name. Since windows are implicitly shared, **sameWindow** is exactly the same with **myWindow**. If the name does not exist, a new window is created. + +.. code-block:: cpp + + /// Access window via its name + viz::Viz3d sameWindow = viz::get("Viz Demo"); + +* Start a controlled event loop. Once it starts, **wasStopped** is set to false. Inside the while loop, in each iteration, **spinOnce** is called to prevent event loop from completely stopping. Inside the while loop, user can execute other statements including those which interact with the window. + +.. code-block:: cpp + + /// Event loop is over when pressed q, Q, e, E + /// Start event loop once for 1 millisecond + sameWindow.spinOnce(1, true); + while(!sameWindow.wasStopped()) + { + /// Interact with window + + /// Event loop for 1 millisecond + sameWindow.spinOnce(1, true); + } + +Results +======= + +Here is the result of the program. + +.. image:: images/window_demo.png + :alt: Launching Viz + :align: center diff --git a/doc/tutorials/viz/table_of_content_viz/images/facedetect.jpg b/doc/tutorials/viz/table_of_content_viz/images/facedetect.jpg new file mode 100644 index 0000000000000000000000000000000000000000..788b7d8262b75bc416edc75495663d592fa1d4e2 GIT binary patch literal 17902 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

x5ax<_qW0y#dAIh~p33!OG=L*8bsD9JfGY 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 Syng=>03dFf8`QyFV*UrqR@d7A literal 0 HcmV?d00001 diff --git a/doc/tutorials/viz/table_of_content_viz/table_of_content_viz.rst b/doc/tutorials/viz/table_of_content_viz/table_of_content_viz.rst new file mode 100644 index 0000000000..c3d08fe17b --- /dev/null +++ b/doc/tutorials/viz/table_of_content_viz/table_of_content_viz.rst @@ -0,0 +1,94 @@ +.. _Table-Of-Content-Viz: + +**OpenCV Viz** +----------------------------------------------------------- + +.. include:: ../../definitions/tocDefinitions.rst + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ================== =============================================================================== + |VizLaunchingViz| **Title:** :ref:`launching_viz` + + *Compatibility:* > OpenCV 3.0.0 + + *Author:* Ozan Tonkal + + You will learn how to launch a viz window. + + ================== =============================================================================== + + .. |VizLaunchingViz| image:: ../launching_viz/images/window_demo.png + :height: 120pt + :width: 90pt + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ================ ============================================================================ + |WidgetPose| **Title:** :ref:`widget_pose` + + *Compatibility:* > OpenCV 3.0.0 + + *Author:* Ozan Tonkal + + You will learn how to change pose of a widget. + + ================ ============================================================================ + + .. |WidgetPose| image:: ../widget_pose/images/widgetpose.png + :height: 90pt + :width: 90pt + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ================== ============================================================================ + |Transformations| **Title:** :ref:`transformations` + + *Compatibility:* > OpenCV 3.0.0 + + *Author:* Ozan Tonkal + + You will learn how to transform between global and camera frames. + + ================== ============================================================================ + + .. |Transformations| image:: ../transformations/images/global_view_point.png + :height: 120pt + :width: 90pt + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ================== ============================================================================ + |CreatingWidgets| **Title:** :ref:`creating_widgets` + + *Compatibility:* > OpenCV 3.0.0 + + *Author:* Ozan Tonkal + + You will learn how to create your own widgets. + + ================== ============================================================================ + + .. |CreatingWidgets| image:: ../creating_widgets/images/red_triangle.png + :height: 120pt + :width: 90pt + +.. raw:: latex + + \pagebreak + +.. toctree:: + :hidden: + + ../launching_viz/launching_viz + ../widget_pose/widget_pose + ../transformations/transformations + ../creating_widgets/creating_widgets diff --git a/doc/tutorials/viz/transformations/images/camera_view_point.png b/doc/tutorials/viz/transformations/images/camera_view_point.png new file mode 100644 index 0000000000000000000000000000000000000000..e2ac5b0f0de84aa88561e44b635c0b73fd68dab0 GIT binary patch literal 18439 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/viz/transformations/images/global_view_point.png b/doc/tutorials/viz/transformations/images/global_view_point.png new file mode 100644 index 0000000000000000000000000000000000000000..fc6de2c1a1fa36ad56ad6ffb679eb20d5bf77609 GIT binary patch literal 13767 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 QpoJpyQt?HJgi-MS0}6qlQ~&?~ literal 0 HcmV?d00001 diff --git a/doc/tutorials/viz/transformations/transformations.rst b/doc/tutorials/viz/transformations/transformations.rst new file mode 100644 index 0000000000..d1f2d0c2e0 --- /dev/null +++ b/doc/tutorials/viz/transformations/transformations.rst @@ -0,0 +1,202 @@ +.. _transformations: + +Transformations +*************** + +Goal +==== + +In this tutorial you will learn how to + +.. container:: enumeratevisibleitemswithsquare + + * How to use makeTransformToGlobal to compute pose + * How to use makeCameraPose and Viz3d::setViewerPose + * How to visualize camera position by axes and by viewing frustum + +Code +==== + +You can download the code from :download:`here <../../../../samples/cpp/tutorial_code/viz/transformations.cpp>`. + +.. code-block:: cpp + + #include + #include + #include + + using namespace cv; + using namespace std; + + /** + * @function cvcloud_load + * @brief load bunny.ply + */ + Mat cvcloud_load() + { + Mat cloud(1, 1889, CV_32FC3); + ifstream ifs("bunny.ply"); + + string str; + for(size_t i = 0; i < 12; ++i) + getline(ifs, str); + + Point3f* data = cloud.ptr(); + float dummy1, dummy2; + for(size_t i = 0; i < 1889; ++i) + ifs >> data[i].x >> data[i].y >> data[i].z >> dummy1 >> dummy2; + + cloud *= 5.0f; + return cloud; + } + + /** + * @function main + */ + int main(int argn, char **argv) + { + if (argn < 2) + { + cout << "Usage: " << endl << "./transformations [ G | C ]" << endl; + return 1; + } + + bool camera_pov = (argv[1][0] == 'C'); + + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Let's assume camera has the following properties + Point3f cam_pos(3.0f,3.0f,3.0f), cam_focal_point(3.0f,3.0f,2.0f), cam_y_dir(-1.0f,0.0f,0.0f); + + /// We can get the pose of the cam using makeCameraPose + Affine3f cam_pose = viz::makeCameraPose(cam_pos, cam_focal_point, cam_y_dir); + + /// We can get the transformation matrix from camera coordinate system to global using + /// - makeTransformToGlobal. We need the axes of the camera + Affine3f transform = viz::makeTransformToGlobal(Vec3f(0.0f,-1.0f,0.0f), Vec3f(-1.0f,0.0f,0.0f), Vec3f(0.0f,0.0f,-1.0f), cam_pos); + + /// Create a cloud widget. + Mat bunny_cloud = cvcloud_load(); + viz::WCloud cloud_widget(bunny_cloud, viz::Color::green()); + + /// Pose of the widget in camera frame + Affine3f cloud_pose = Affine3f().translate(Vec3f(0.0f,0.0f,3.0f)); + /// Pose of the widget in global frame + Affine3f cloud_pose_global = transform * cloud_pose; + + /// Visualize camera frame + if (!camera_pov) + { + viz::WCameraPosition cpw(0.5); // Coordinate axes + viz::WCameraPosition cpw_frustum(Vec2f(0.889484, 0.523599)); // Camera frustum + myWindow.showWidget("CPW", cpw, cam_pose); + myWindow.showWidget("CPW_FRUSTUM", cpw_frustum, cam_pose); + } + + /// Visualize widget + myWindow.showWidget("bunny", cloud_widget, cloud_pose_global); + + /// Set the viewer pose to that of camera + if (camera_pov) + myWindow.setViewerPose(cam_pose); + + /// Start event loop. + myWindow.spin(); + + return 0; + } + + +Explanation +=========== + +Here is the general structure of the program: + +* Create a visualization window. + +.. code-block:: cpp + + /// Create a window + viz::Viz3d myWindow("Transformations"); + +* Get camera pose from camera position, camera focal point and y direction. + +.. code-block:: cpp + + /// Let's assume camera has the following properties + Point3f cam_pos(3.0f,3.0f,3.0f), cam_focal_point(3.0f,3.0f,2.0f), cam_y_dir(-1.0f,0.0f,0.0f); + + /// We can get the pose of the cam using makeCameraPose + Affine3f cam_pose = viz::makeCameraPose(cam_pos, cam_focal_point, cam_y_dir); + +* Obtain transform matrix knowing the axes of camera coordinate system. + +.. code-block:: cpp + + /// We can get the transformation matrix from camera coordinate system to global using + /// - makeTransformToGlobal. We need the axes of the camera + Affine3f transform = viz::makeTransformToGlobal(Vec3f(0.0f,-1.0f,0.0f), Vec3f(-1.0f,0.0f,0.0f), Vec3f(0.0f,0.0f,-1.0f), cam_pos); + +* Create a cloud widget from bunny.ply file + +.. code-block:: cpp + + /// Create a cloud widget. + Mat bunny_cloud = cvcloud_load(); + viz::WCloud cloud_widget(bunny_cloud, viz::Color::green()); + +* Given the pose in camera coordinate system, estimate the global pose. + +.. code-block:: cpp + + /// Pose of the widget in camera frame + Affine3f cloud_pose = Affine3f().translate(Vec3f(0.0f,0.0f,3.0f)); + /// Pose of the widget in global frame + Affine3f cloud_pose_global = transform * cloud_pose; + +* If the view point is set to be global, visualize camera coordinate frame and viewing frustum. + +.. code-block:: cpp + + /// Visualize camera frame + if (!camera_pov) + { + viz::WCameraPosition cpw(0.5); // Coordinate axes + viz::WCameraPosition cpw_frustum(Vec2f(0.889484, 0.523599)); // Camera frustum + myWindow.showWidget("CPW", cpw, cam_pose); + myWindow.showWidget("CPW_FRUSTUM", cpw_frustum, cam_pose); + } + +* Visualize the cloud widget with the estimated global pose + +.. code-block:: cpp + + /// Visualize widget + myWindow.showWidget("bunny", cloud_widget, cloud_pose_global); + +* If the view point is set to be camera's, set viewer pose to **cam_pose**. + +.. code-block:: cpp + + /// Set the viewer pose to that of camera + if (camera_pov) + myWindow.setViewerPose(cam_pose); + +Results +======= + +#. Here is the result from the camera point of view. + + .. image:: images/camera_view_point.png + :alt: Camera Viewpoint + :align: center + +#. Here is the result from global point of view. + + .. image:: images/global_view_point.png + :alt: Global Viewpoint + :align: center diff --git a/doc/tutorials/viz/widget_pose/images/widgetpose.png b/doc/tutorials/viz/widget_pose/images/widgetpose.png new file mode 100644 index 0000000000000000000000000000000000000000..ef8a5937f9c3e18507acdefc758102919c2efdb8 GIT binary patch literal 40892 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 LUA9izJp6wE?|M7Z literal 0 HcmV?d00001 diff --git a/doc/tutorials/viz/widget_pose/widget_pose.rst b/doc/tutorials/viz/widget_pose/widget_pose.rst new file mode 100644 index 0000000000..a4466bdedf --- /dev/null +++ b/doc/tutorials/viz/widget_pose/widget_pose.rst @@ -0,0 +1,162 @@ +.. _widget_pose: + +Pose of a widget +**************** + +Goal +==== + +In this tutorial you will learn how to + +.. container:: enumeratevisibleitemswithsquare + + * Add widgets to the visualization window + * Use Affine3 to set pose of a widget + * Rotating and translating a widget along an axis + +Code +==== + +You can download the code from :download:`here <../../../../samples/cpp/tutorial_code/viz/widget_pose.cpp>`. + +.. code-block:: cpp + + #include + #include + #include + + using namespace cv; + using namespace std; + + /** + * @function main + */ + int main() + { + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Add line to represent (1,1,1) axis + viz::WLine axis(Point3f(-1.0f,-1.0f,-1.0f), Point3f(1.0f,1.0f,1.0f)); + axis.setRenderingProperty(viz::LINE_WIDTH, 4.0); + myWindow.showWidget("Line Widget", axis); + + /// Construct a cube widget + viz::WCube cube_widget(Point3f(0.5,0.5,0.0), Point3f(0.0,0.0,-0.5), true, viz::Color::blue()); + cube_widget.setRenderingProperty(viz::LINE_WIDTH, 4.0); + + /// Display widget (update if already displayed) + myWindow.showWidget("Cube Widget", cube_widget); + + /// Rodrigues vector + Mat rot_vec = Mat::zeros(1,3,CV_32F); + float translation_phase = 0.0, translation = 0.0; + while(!myWindow.wasStopped()) + { + /* Rotation using rodrigues */ + /// Rotate around (1,1,1) + rot_vec.at(0,0) += CV_PI * 0.01f; + rot_vec.at(0,1) += CV_PI * 0.01f; + rot_vec.at(0,2) += CV_PI * 0.01f; + + /// Shift on (1,1,1) + translation_phase += CV_PI * 0.01f; + translation = sin(translation_phase); + + Mat rot_mat; + Rodrigues(rot_vec, rot_mat); + + /// Construct pose + Affine3f pose(rot_mat, Vec3f(translation, translation, translation)); + + myWindow.setWidgetPose("Cube Widget", pose); + + myWindow.spinOnce(1, true); + } + + return 0; + } + +Explanation +=========== + +Here is the general structure of the program: + +* Create a visualization window. + +.. code-block:: cpp + + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + +* Show coordinate axes in the window using CoordinateSystemWidget. + +.. code-block:: cpp + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + +* Display a line representing the axis (1,1,1). + +.. code-block:: cpp + + /// Add line to represent (1,1,1) axis + viz::WLine axis(Point3f(-1.0f,-1.0f,-1.0f), Point3f(1.0f,1.0f,1.0f)); + axis.setRenderingProperty(viz::LINE_WIDTH, 4.0); + myWindow.showWidget("Line Widget", axis); + +* Construct a cube. + +.. code-block:: cpp + + /// Construct a cube widget + viz::WCube cube_widget(Point3f(0.5,0.5,0.0), Point3f(0.0,0.0,-0.5), true, viz::Color::blue()); + cube_widget.setRenderingProperty(viz::LINE_WIDTH, 4.0); + myWindow.showWidget("Cube Widget", cube_widget); + +* Create rotation matrix from rodrigues vector + +.. code-block:: cpp + + /// Rotate around (1,1,1) + rot_vec.at(0,0) += CV_PI * 0.01f; + rot_vec.at(0,1) += CV_PI * 0.01f; + rot_vec.at(0,2) += CV_PI * 0.01f; + + ... + + Mat rot_mat; + Rodrigues(rot_vec, rot_mat); + +* Use Affine3f to set pose of the cube. + +.. code-block:: cpp + + /// Construct pose + Affine3f pose(rot_mat, Vec3f(translation, translation, translation)); + myWindow.setWidgetPose("Cube Widget", pose); + +* Animate the rotation using wasStopped and spinOnce + +.. code-block:: cpp + + while(!myWindow.wasStopped()) + { + ... + + myWindow.spinOnce(1, true); + } + +Results +======= + +Here is the result of the program. + +.. raw:: html + +

diff --git a/modules/core/include/opencv2/core/affine.hpp b/modules/core/include/opencv2/core/affine.hpp new file mode 100644 index 0000000000..827d044b87 --- /dev/null +++ b/modules/core/include/opencv2/core/affine.hpp @@ -0,0 +1,509 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#ifndef __OPENCV_CORE_AFFINE3_HPP__ +#define __OPENCV_CORE_AFFINE3_HPP__ + +#ifdef __cplusplus + +#include + +namespace cv +{ + template + class Affine3 + { + public: + typedef T float_type; + typedef Matx Mat3; + typedef Matx Mat4; + typedef Vec Vec3; + + Affine3(); + + //Augmented affine matrix + Affine3(const Mat4& affine); + + //Rotation matrix + Affine3(const Mat3& R, const Vec3& t = Vec3::all(0)); + + //Rodrigues vector + Affine3(const Vec3& rvec, const Vec3& t = Vec3::all(0)); + + //Combines all contructors above. Supports 4x4, 4x3, 3x3, 1x3, 3x1 sizes of data matrix + explicit Affine3(const Mat& data, const Vec3& t = Vec3::all(0)); + + //From 16th element array + explicit Affine3(const float_type* vals); + + static Affine3 Identity(); + + //Rotation matrix + void rotation(const Mat3& R); + + //Rodrigues vector + void rotation(const Vec3& rvec); + + //Combines rotation methods above. Suports 3x3, 1x3, 3x1 sizes of data matrix; + void rotation(const Mat& data); + + void linear(const Mat3& L); + void translation(const Vec3& t); + + Mat3 rotation() const; + Mat3 linear() const; + Vec3 translation() const; + + //Rodrigues vector + Vec3 rvec() const; + + Affine3 inv(int method = cv::DECOMP_SVD) const; + + // a.rotate(R) is equivalent to Affine(R, 0) * a; + Affine3 rotate(const Mat3& R) const; + + // a.rotate(R) is equivalent to Affine(rvec, 0) * a; + Affine3 rotate(const Vec3& rvec) const; + + // a.translate(t) is equivalent to Affine(E, t) * a; + Affine3 translate(const Vec3& t) const; + + // a.concatenate(affine) is equivalent to affine * a; + Affine3 concatenate(const Affine3& affine) const; + + template operator Affine3() const; + + template Affine3 cast() const; + + Mat4 matrix; + +#if defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H + Affine3(const Eigen::Transform& affine); + Affine3(const Eigen::Transform& affine); + operator Eigen::Transform() const; + operator Eigen::Transform() const; +#endif + }; + + template static + Affine3 operator*(const Affine3& affine1, const Affine3& affine2); + + template static + V operator*(const Affine3& affine, const V& vector); + + typedef Affine3 Affine3f; + typedef Affine3 Affine3d; + + static Vec3f operator*(const Affine3f& affine, const Vec3f& vector); + static Vec3d operator*(const Affine3d& affine, const Vec3d& vector); + + template class DataType< Affine3<_Tp> > + { + public: + typedef Affine3<_Tp> value_type; + typedef Affine3::work_type> work_type; + typedef _Tp channel_type; + + enum { generic_type = 0, + depth = DataType::depth, + channels = 16, + fmt = DataType::fmt + ((channels - 1) << 8), + type = CV_MAKETYPE(depth, channels) + }; + + typedef Vec vec_type; + }; +} + + +/////////////////////////////////////////////////////////////////////////////////// +/// Implementaiton + +template inline +cv::Affine3::Affine3() + : matrix(Mat4::eye()) +{} + +template inline +cv::Affine3::Affine3(const Mat4& affine) + : matrix(affine) +{} + +template inline +cv::Affine3::Affine3(const Mat3& R, const Vec3& t) +{ + rotation(R); + translation(t); + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const Vec3& _rvec, const Vec3& t) +{ + rotation(_rvec); + translation(t); + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const cv::Mat& data, const Vec3& t) +{ + CV_Assert(data.type() == cv::DataType::type); + + if (data.cols == 4 && data.rows == 4) + { + data.copyTo(matrix); + return; + } + else if (data.cols == 4 && data.rows == 3) + { + rotation(data(Rect(0, 0, 3, 3))); + translation(data(Rect(3, 0, 1, 3))); + return; + } + + rotation(data); + translation(t); + matrix.val[12] = matrix.val[13] = matrix.val[14] = 0; + matrix.val[15] = 1; +} + +template inline +cv::Affine3::Affine3(const float_type* vals) : matrix(vals) +{} + +template inline +cv::Affine3 cv::Affine3::Identity() +{ + return Affine3(cv::Affine3::Mat4::eye()); +} + +template inline +void cv::Affine3::rotation(const Mat3& R) +{ + linear(R); +} + +template inline +void cv::Affine3::rotation(const Vec3& _rvec) +{ + double rx = _rvec[0], ry = _rvec[1], rz = _rvec[2]; + double theta = std::sqrt(rx*rx + ry*ry + rz*rz); + + if (theta < DBL_EPSILON) + rotation(Mat3::eye()); + else + { + const double I[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; + + double c = std::cos(theta); + double s = std::sin(theta); + double c1 = 1. - c; + double itheta = theta ? 1./theta : 0.; + + rx *= itheta; ry *= itheta; rz *= itheta; + + double rrt[] = { rx*rx, rx*ry, rx*rz, rx*ry, ry*ry, ry*rz, rx*rz, ry*rz, rz*rz }; + double _r_x_[] = { 0, -rz, ry, rz, 0, -rx, -ry, rx, 0 }; + Mat3 R; + + // R = cos(theta)*I + (1 - cos(theta))*r*rT + sin(theta)*[r_x] + // where [r_x] is [0 -rz ry; rz 0 -rx; -ry rx 0] + for(int k = 0; k < 9; ++k) + R.val[k] = static_cast(c*I[k] + c1*rrt[k] + s*_r_x_[k]); + + rotation(R); + } +} + +//Combines rotation methods above. Suports 3x3, 1x3, 3x1 sizes of data matrix; +template inline +void cv::Affine3::rotation(const cv::Mat& data) +{ + CV_Assert(data.type() == cv::DataType::type); + + if (data.cols == 3 && data.rows == 3) + { + Mat3 R; + data.copyTo(R); + rotation(R); + } + else if ((data.cols == 3 && data.rows == 1) || (data.cols == 1 && data.rows == 3)) + { + Vec3 _rvec; + data.reshape(1, 3).copyTo(_rvec); + rotation(_rvec); + } + else + CV_Assert(!"Input marix can be 3x3, 1x3 or 3x1"); +} + +template inline +void cv::Affine3::linear(const Mat3& L) +{ + matrix.val[0] = L.val[0]; matrix.val[1] = L.val[1]; matrix.val[ 2] = L.val[2]; + matrix.val[4] = L.val[3]; matrix.val[5] = L.val[4]; matrix.val[ 6] = L.val[5]; + matrix.val[8] = L.val[6]; matrix.val[9] = L.val[7]; matrix.val[10] = L.val[8]; +} + +template inline +void cv::Affine3::translation(const Vec3& t) +{ + matrix.val[3] = t[0]; matrix.val[7] = t[1]; matrix.val[11] = t[2]; +} + +template inline +typename cv::Affine3::Mat3 cv::Affine3::rotation() const +{ + return linear(); +} + +template inline +typename cv::Affine3::Mat3 cv::Affine3::linear() const +{ + typename cv::Affine3::Mat3 R; + R.val[0] = matrix.val[0]; R.val[1] = matrix.val[1]; R.val[2] = matrix.val[ 2]; + R.val[3] = matrix.val[4]; R.val[4] = matrix.val[5]; R.val[5] = matrix.val[ 6]; + R.val[6] = matrix.val[8]; R.val[7] = matrix.val[9]; R.val[8] = matrix.val[10]; + return R; +} + +template inline +typename cv::Affine3::Vec3 cv::Affine3::translation() const +{ + return Vec3(matrix.val[3], matrix.val[7], matrix.val[11]); +} + +template inline +typename cv::Affine3::Vec3 cv::Affine3::rvec() const +{ + cv::Vec3d w; + cv::Matx33d u, vt, R = rotation(); + cv::SVD::compute(R, w, u, vt, cv::SVD::FULL_UV + cv::SVD::MODIFY_A); + R = u * vt; + + double rx = R.val[7] - R.val[5]; + double ry = R.val[2] - R.val[6]; + double rz = R.val[3] - R.val[1]; + + double s = std::sqrt((rx*rx + ry*ry + rz*rz)*0.25); + double c = (R.val[0] + R.val[4] + R.val[8] - 1) * 0.5; + c = c > 1.0 ? 1.0 : c < -1.0 ? -1.0 : c; + double theta = acos(c); + + if( s < 1e-5 ) + { + if( c > 0 ) + rx = ry = rz = 0; + else + { + double t; + t = (R.val[0] + 1) * 0.5; + rx = std::sqrt(std::max(t, 0.0)); + t = (R.val[4] + 1) * 0.5; + ry = std::sqrt(std::max(t, 0.0)) * (R.val[1] < 0 ? -1.0 : 1.0); + t = (R.val[8] + 1) * 0.5; + rz = std::sqrt(std::max(t, 0.0)) * (R.val[2] < 0 ? -1.0 : 1.0); + + if( fabs(rx) < fabs(ry) && fabs(rx) < fabs(rz) && (R.val[5] > 0) != (ry*rz > 0) ) + rz = -rz; + theta /= std::sqrt(rx*rx + ry*ry + rz*rz); + rx *= theta; + ry *= theta; + rz *= theta; + } + } + else + { + double vth = 1/(2*s); + vth *= theta; + rx *= vth; ry *= vth; rz *= vth; + } + + return cv::Vec3d(rx, ry, rz); +} + +template inline +cv::Affine3 cv::Affine3::inv(int method) const +{ + return matrix.inv(method); +} + +template inline +cv::Affine3 cv::Affine3::rotate(const Mat3& R) const +{ + Mat3 Lc = linear(); + Vec3 tc = translation(); + Mat4 result; + result.val[12] = result.val[13] = result.val[14] = 0; + result.val[15] = 1; + + for(int j = 0; j < 3; ++j) + { + for(int i = 0; i < 3; ++i) + { + float_type value = 0; + for(int k = 0; k < 3; ++k) + value += R(j, k) * Lc(k, i); + result(j, i) = value; + } + + result(j, 3) = R.row(j).dot(tc.t()); + } + return result; +} + +template inline +cv::Affine3 cv::Affine3::rotate(const Vec3& _rvec) const +{ + return rotate(Affine3f(_rvec).rotation()); +} + +template inline +cv::Affine3 cv::Affine3::translate(const Vec3& t) const +{ + Mat4 m = matrix; + m.val[ 3] += t[0]; + m.val[ 7] += t[1]; + m.val[11] += t[2]; + return m; +} + +template inline +cv::Affine3 cv::Affine3::concatenate(const Affine3& affine) const +{ + return (*this).rotate(affine.rotation()).translate(affine.translation()); +} + +template template inline +cv::Affine3::operator Affine3() const +{ + return Affine3(matrix); +} + +template template inline +cv::Affine3 cv::Affine3::cast() const +{ + return Affine3(matrix); +} + +template inline +cv::Affine3 cv::operator*(const cv::Affine3& affine1, const cv::Affine3& affine2) +{ + return affine2.concatenate(affine1); +} + +template inline +V cv::operator*(const cv::Affine3& affine, const V& v) +{ + const typename Affine3::Mat4& m = affine.matrix; + + V r; + r.x = m.val[0] * v.x + m.val[1] * v.y + m.val[ 2] * v.z + m.val[ 3]; + r.y = m.val[4] * v.x + m.val[5] * v.y + m.val[ 6] * v.z + m.val[ 7]; + r.z = m.val[8] * v.x + m.val[9] * v.y + m.val[10] * v.z + m.val[11]; + return r; +} + +static inline +cv::Vec3f cv::operator*(const cv::Affine3f& affine, const cv::Vec3f& v) +{ + const cv::Matx44f& m = affine.matrix; + cv::Vec3f r; + r.val[0] = m.val[0] * v[0] + m.val[1] * v[1] + m.val[ 2] * v[2] + m.val[ 3]; + r.val[1] = m.val[4] * v[0] + m.val[5] * v[1] + m.val[ 6] * v[2] + m.val[ 7]; + r.val[2] = m.val[8] * v[0] + m.val[9] * v[1] + m.val[10] * v[2] + m.val[11]; + return r; +} + +static inline +cv::Vec3d cv::operator*(const cv::Affine3d& affine, const cv::Vec3d& v) +{ + const cv::Matx44d& m = affine.matrix; + cv::Vec3d r; + r.val[0] = m.val[0] * v[0] + m.val[1] * v[1] + m.val[ 2] * v[2] + m.val[ 3]; + r.val[1] = m.val[4] * v[0] + m.val[5] * v[1] + m.val[ 6] * v[2] + m.val[ 7]; + r.val[2] = m.val[8] * v[0] + m.val[9] * v[1] + m.val[10] * v[2] + m.val[11]; + return r; +} + + + +#if defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H + +template inline +cv::Affine3::Affine3(const Eigen::Transform& affine) +{ + cv::Mat(4, 4, cv::DataType::type, affine.matrix().data()).copyTo(matrix); +} + +template inline +cv::Affine3::Affine3(const Eigen::Transform& affine) +{ + Eigen::Transform a = affine; + cv::Mat(4, 4, cv::DataType::type, a.matrix().data()).copyTo(matrix); +} + +template inline +cv::Affine3::operator Eigen::Transform() const +{ + Eigen::Transform r; + cv::Mat hdr(4, 4, cv::DataType::type, r.matrix().data()); + cv::Mat(matrix, false).copyTo(hdr); + return r; +} + +template inline +cv::Affine3::operator Eigen::Transform() const +{ + return this->operator Eigen::Transform(); +} + +#endif /* defined EIGEN_WORLD_VERSION && defined EIGEN_GEOMETRY_MODULE_H */ + + +#endif /* __cplusplus */ + +#endif /* __OPENCV_CORE_AFFINE3_HPP__ */ diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index cafba0f8f3..2ecb70c713 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -892,6 +892,7 @@ public: typedef Point_ Point2i; typedef Point2i Point; typedef Size_ Size2i; +typedef Size_ Size2d; typedef Size2i Size; typedef Rect_ Rect; typedef Point_ Point2f; diff --git a/modules/viz/CMakeLists.txt b/modules/viz/CMakeLists.txt new file mode 100644 index 0000000000..7ccd079216 --- /dev/null +++ b/modules/viz/CMakeLists.txt @@ -0,0 +1,11 @@ +if(NOT WITH_VTK OR NOT DEFINED HAVE_VTK OR NOT HAVE_VTK) + ocv_module_disable(viz) +endif() + +include(${VTK_USE_FILE}) +set(the_description "Viz") +ocv_define_module(viz opencv_core ${VTK_LIBRARIES}) + +if(APPLE AND BUILD_opencv_viz) + target_link_libraries(opencv_viz "-framework Cocoa") +endif() diff --git a/modules/viz/doc/images/cpw1.png b/modules/viz/doc/images/cpw1.png new file mode 100644 index 0000000000000000000000000000000000000000..985b1eeaffbed1e207128eaa8167cde04d3dcf04 GIT binary patch literal 4321 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 literal 0 HcmV?d00001 diff --git a/modules/viz/doc/images/cpw2.png b/modules/viz/doc/images/cpw2.png new file mode 100644 index 0000000000000000000000000000000000000000..5733a6af6579373d6b9512b70cd5ada26b62db3d GIT binary patch literal 3548 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 literal 0 HcmV?d00001 diff --git a/modules/viz/doc/images/cpw3.png b/modules/viz/doc/images/cpw3.png new file mode 100644 index 0000000000000000000000000000000000000000..585b836b66ddfdbfa34219972f9c766eccb5f9d4 GIT binary patch literal 17724 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

gB=@dpOOp^?a9R9TDL$j)- zMXOuI1HZR%h@Qv%*6b@~Dzl3CHF6(_6hN@Pn5ESv@|UTNF@KR5V80 zeqQbSQC<-?VBq<9-g?orw&{7rquy$+mTtD{C~19`EHgAs3SMqCYfQ?quZO7#yGC)7v?)rzgR4l89MG2c zc&Yb1DNMd>9g-_Y4kOg4EhZj_ktSHNGYy~%Vy^kmRIhj(d z&buf!6zpinUc1qYuI4r+@U!IR$M)Kd32JEdzh=ywF6X~bnW6`%e<6b`H(9T}zuXz) zD$q|T>}Yg@sd+_BeP|SoT4NLA{cZCBrG4h@vc;=W_NJne>CAocLr%)#wx;ti_o!yLg0JF`UWOn$p=}DJU z%?I;CSdrj-t=YEw;}JYjK{1#Ka$D_L1^jzffBZ!U&v+(cb=>@Xeij+s!k#krUdGj$ zm~9$2TcjPL-o06qQP`G>{3q+!`5_L;+oS$}qLg~$)X%bVZYCX>?4((H@i|`c63jom zh0V&tZxPB-v*-&m#~Ca22gdsPL?smnHGF&@_j3x&VJLc+jL{3HsN%zr@Fq_AlKJ@UVEZVh9RXW1!)b9 zM0~m{XqT=B2mZ%z|p7*2`oMvA$r?AU+-ujC=p<7XAng1 z0YpJV{s-pzEytS|-Bvp|SBW%@Xzg%6YYLKysE)mfq^%z~+Xgx>|Ag-#$hu4VDlxy4 zl0Ds+=hN=vTCF*nrDc9wh?kUwEXw`kkhv!1+5eUnzS>kBu2e_5v#hXFF-FQzg2xO5?Aa> z@y*024CT%xeU`U0?%yyqxL+!2dhpHTl1Ga0)olCFQwY1HE5iR;{qAp%7Xk>( z-T>_vs2fT9Xtqhm?<_y^+maaLL@?GH7(+n zLK35fg@W_1t*yL6RH$WcGRxSadQE>|m`qki|=%l-| zbE8`l1FP|9LdDo|Of5Mb9ab1`2m-E4Al4$W;Zh4Ffu@GWasBRM01_{|@8`SqTGPy{ zor$C&uh|P*0aWFO0Mx%hBmSjv(3<8=V*gSLKBC9&6wAX&1OrCpCdcxa*8Gk&q9%X^k`+j=Go`4zw znStr%je*87etcKBikbN<5?9~R{$EpQaDLS8AkOBhC3vQFJem40lvDBIaj2dq>wtOl z{Sc$jV^KK?TO$7*hV|7Ri4z}zz#kx^9DMiFLRZ~ee4*`mc?Mfu-Z6hI#X1`s8^e;~ zl5Ue0GB4HKjjNgj{L0|vgu&Q%p})P&zK8UGc|TypNB51%$#Nk%Hk~wA=r|b{h=`2V z;25HjNe?5>MB>M83Jf+m`xeH}dc&t)V~c-y-QC^MwH@a{;k)tAPoyvyj>P@U7mGg3 zkot$1Q&1p`j_-9XpnwhNfOiMNk$9iCX*8RL6RgIU!6dYgXV(#6js0(OzFL;f0fA8j z`~$6by4QzChBKx+_eBt{P#IGrfBvG;&$XtB=0_Qpf~}d2T(ccBaQ!hSTN1%Zq!W|9 zx;j8v_!v4bl}3ykKJfW|#5XnuWb`wk5Jd002{v*bB<7Ltxl4C_KBVz!i`Y zD~RDe01)zX>gzqaq_m`j{i0!x->nzn#pss7TNW_ELA) z=yfcK{ZZ!Uj~QKc^*__j2U^dA+aFz)78V8N(uDg<>$>9tro(eYj^Ae48eYffQvs|; zQJfLiMyOB-yipo_GV+=@_RKiw58wA2648hh7R$xb;;c5e8v~Hq-_l;nA^MaW4P=>! zdapawqz!eemsatf%rZP%un*2@uLw#8b8J3O?W6KcL#1TXA|7yRIMCJKn&(K3Ch31s z$*ZYBZ6_$nGWcF%U|;|p(rE5mXlp*8ch6ur7&_Q%inz_S^)d+x^@DJ94?ypSDTbe) zPu<{d2Z?I3+&97swmYoqH$}0ee<^In!HO{LuK%6yMeB1CORLNuz~{Yk!|#wqF&kERi-7H7e#Are81hHLx(zjBAQJTqTiH_0#w2 z&3~+M=bU%jjhWcuO=@E2QyJZAHt2hH z_jZ@9S#pKcgS~8;9mGD~i1l)?YcR<%l>uM}o*#~gg!s{7`3i$6d+sKJ^c}#G&UpNS zWvP~+7OP4?yvQ-TdpXLs{korv4niA`_<+ZcE25c;rLVSI){k3IElxwV2HUQms&TJB zXg5>mTywcfillM;^L;w6qq!!gkbq&vSrC0Q^4;SU5kpdc3Dt;va( z7G`QCF`mv5pi--vrv&1Z>0}1Q<6k9TT&v9o<^UabG2@>H-Ix8W*~p2#H&}IAU9NN{ z6R@^$qHJ96s#W;(WGPYdFNp%E~y{pobm8+Xu`fYy|))|lWAFQ`J@;z?{gy4G|18t9lC^irJ=lN14I5;?6&kGi&B>lU?2`<;m z0w!pZoJ1#y^}r72S&53=f7OqlEO>eipdg=*Yv-*;HHJ`8JeVMB?a2&~gUc#vwSYP^ zaJgu*`k|;{r2*#Pz-zU^90%8B1`F7Rgri+W>M{=Z&8N_zJ)jL)z_H9+yt+RUFXFcv z$+r1C&NS5%v%lLDu)3)DGWlJc|&2a@6SVgyf6FlmX?+WT{kM_s$n?(=AL{bzD{2< zq$RS^+7KXKZ)>VJQkmATtBH#(jTnRIdN0R*p8_dUh&X+Wjy%$N*(WqRZkydDWn~`k zH$x)ouj5CPnN<5Hz-CW~0XQ@mfdJ7@v@+XyQGJ(hx4HYn8fSvla1dSz9g1R3;D#RT z>m~b;vVp>!;@qB2_&ZePkb3rmwq;Nd&Uc`&rMOSBZ{mg6fIi{g&2w|tLuLFgIaPF%LG0^Q%W9c4W(igsOG`9vD0y97 zAQ?#On}-+PdcQqNX!4MLfU}v=omHNhT7OD5`ZX1a7}F_pSizcB+TN+BC}*=pFD(L< zK-US0V_`GQucN3lYu?hD*%!IXNINFJyc&_3PAqG0V3~dYXLAurAW+Nhhk!{H!yYy`zrJ z>XlFeY}sb=#N&R+Ivx^QI?f9!dHguEolZv?j#1}3xgq#*=7}HH_*uFeTBym%PXDYH zKF%BF%%tI*6N8@nQ0$U%AQ1kF^XH#Hm&O*rBwyFw?*b9Nqoh zf1A*LLX&GP>CAnFBAh={Rz~^HjzatAl*q(%u8@dfS}+P^-_x2c-wolz_!vFABY0|8 zYJ|8mMc>_>l724aFLl2hMEfR&#$e0@wFsQPSuSJ_k{FWz8J>FRw3DxAs&X+;9iGqg z&aJAD5I8i`QiZzEhWV~QX;m~HS5sl3@h4D1A5F1s;i7vXvK$}u!YOFNCH|d<674)r zI9;rx7}p=Y774G@@UI)`4mJeq9040hY z%o~1ffcgLe z!xJEC0f}JP$ulf7;dMHe|GuT6t^kS~Iq}n8cHGvV;N4Fq?c4DwbH^BBrnSb?YNawj zx!Z61uaLGeAQv=4HotUm0j=qUx*|BcZ8g`ek5Q`Xl;#qjstY2lpyo{vsdEi~YwPtk znjgYtj&!Uho7_!?Vw|(sCotCTFn*EW2a;(FQyY1Msh=qM6$$e*Xlerc-R0%3ub*p_ zwSd9qbUfYG#u-k4h^&8+?LAml786`QQPB1W0?zJ!rqn?c4?l?vGQ$BMEZKn>wz^H*KC4-dS}OsrTI>_0kb?EYPxrza6%dQyJfXm|ZOH|0Z1 zto=RSCYY6+?0VJtQe)xy#CJayZ? z3<6@Ve*sXYRse6y?%|3Ubwmc#s_yQs&>jr`I$5ki%Uw54o#&468hRl04n`9LAQ2=x zX(Q)?*lhY|#PUm^X=unPKzp7p3^ym824u)hO01m9VAu~j9PK>X!+?3s>1lfZZ&k3v z*ov*|jTQ@(cQ@MYE+e-9;zDx6(SQaf)CB1c(pr0dCTj_9nIQ_TAvxZTc5|zy}FAE`sA;{-0fGQb2`(RO64H zRMRXNzf$!%&7d@JD(EkE-XEUo)HVhUwhK*g>u zdvCM|ooNNlDcHn&{e*ae=V-o5qiyiLgn8}XP4Oi^2&$&P_4vN;5kzA%2&%Hi{5Y;7 zsFWV5Hg?Ttsk2uNw*howgg>Sr`uIqWaOBGfJdShecY8~Zc@0;Y6@E9EpvOSVAPk4vlDvRT>XL{} zfGwVW5EcMcaBv43$+|x3zNPwZbDHwTEg2r9SM?fBeKYN940`b+$~+(Uk1UosG~=Q1 z!J*^pGR$|`ZfP8b5u7_$-Cv*At{0n~nn_y#w+wP|ap8dEzT9PB_xG^;VAgBfOEBqw z?|}fR7Z+CM=GB+RGCNxo6J;;L#J2~p5TlL>aeI5GKde&*aSq(8?o*8K73Jt1(4hm; z+;lDO{IZrqzWV2#Pz!lQt&EG}Ee?+-3P|*+v)!A>?hhJhec?_m_c%BczLya>(utCt zPO}-}3w5{TyrRy|OwOa*yE`01-?z1`{z9V`b1cI7l|g@Cdhvf6JT51Q?^BM>N){V) za<p4yo>a;{xIoJbE34^K@ikkJEx+ta!u(j6+3e5i2vA#syx5)m!1H+hH2 z-S34~OjgVEE3;cW)-5a89MBT)=@-u({1&l8ofz~iz~>+z{u{2Xu7OTzt6#)%WYzUMyAh4pVd5=^lE{oSixQL{^O zW1(df`^kt`yz`tALr#{nnRAUyHX{YNf@5EJ*n3};5hqrul_HkD(aG&w{z`KHr+mz| zon>p1)A;S%zGHMdd_sOnIkkxcgqx&ny@V{MY^`|~)9Z##G z31g~T7s0L8=}dE^-5VhQt_4Ke8fW!|g(67}MFo0oi~3_!P(hsdqA42*?iC znyk_81_Ebie`UE5$A776`{ldZ5Qn<~C0V0>AEe`582MB>TQs9qK+F{MZTZ@xb2x<_ zKtcYDDEWD{#L$p#9Lp0Q%op{afT5e=`un1K4Mc z@IyU;&Q7%My8;Nvx90yo7hwMp7?w^klx6Ynh)QSQ$K1VfFH^;_l0ExsNs2K^XBWA z?k6!VK$R9+%Ir3+*)XB8ZaGfz92^|1qyWf+{e;IZ)<1n}V%iDx^5U5LVP46w7Legm z4MpLlw~3_jyq(wAQ8Q&r^&8aw7gjY6fND~f=V3kr9Z(ay@CV;$1s8=&(RBYkq{$D7 zu*)eem3VZ#Xk41LA@F(DpYs4Z9cyBd-xCdiq5~Lb=B?nqO4bjpVO@5hRHAS60t|I@ z%zkfhWrt!7zclI4Y|l5R47`L6|QI7@^S<|mOvw$ZQZVKMvTCE z#VIKqc#z|7uR%w|b_;1r>L&KvJ%Hg#+5))1`et`PLk$2CxO_ex*L}(8ShibTJRvMV zS#BFrr9YNLV{qejwpeL66lHB?WfUPo9IG?>;~n7mM2TaGghDhP>}rYLPs^(}+g+X!3y?NBKcDw@M!tZ}snl*~T~opfAz8}1{_*;M#$7A$#R@n!adP>bLp@&& z7#{4|y7lWNZ++gXGT`{u{onV#uSlBj4IBx406K%9f$cEk3*O3qpHAyfpE5_+0V zPZG&G`ugveO!nK%-`fj^Uxd!{ZaXa%Kf}1}K!>2( z;q$iN?--x6a9-vh*7hA30Yyf@5G-D6`9-WMGdL?Yb}lfzUkfM&6)*+=em?*IfZd+K zcg`H=%rn+4ptBkJ(tDn2yDYT$ z-T&2B-p{i9g+K80jsD}0^Y{Hs1FnUh`Yt63t50!3OH1o%4>QewNjyF zQvmR^f(Z*>giPX8JiL8!vEUTl)SgaZ54ICkGmbFKIw`YB_T`Dsw{G21khHttVm{|f z*$Jg3lYN%0TJ`GVarx=bCwSQLI{+8x=C|j#R2a={YisM3Hg~!!Iqxv%RVU!e%?U?2 zZq?{*T6Q{(Ik@ET&3k-1TY%dtJ6%?-Uj6vvjuY$E4&7o~aP{qj37dgqRu?lubV}-f zJZxtU&=oHTp5?6Ptha=Bjzb7L-9Y=9qyO1!m5)w)^J=p%@ca`7Pgg&ebxsLQ0QrLz AnE(I) literal 0 HcmV?d00001 diff --git a/modules/viz/doc/images/cube_widget.png b/modules/viz/doc/images/cube_widget.png new file mode 100644 index 0000000000000000000000000000000000000000..ffe56811aedb6fc57e77097e6b2840cdd6bb64e4 GIT binary patch literal 6507 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 V6?X0&0K6`tFw`^Ct<-jn{U22ce2xGB literal 0 HcmV?d00001 diff --git a/modules/viz/doc/viz.rst b/modules/viz/doc/viz.rst new file mode 100644 index 0000000000..1d8c743ad2 --- /dev/null +++ b/modules/viz/doc/viz.rst @@ -0,0 +1,9 @@ +*********************** +viz. 3D Visualizer +*********************** + +.. toctree:: + :maxdepth: 2 + + viz3d.rst + widget.rst diff --git a/modules/viz/doc/viz3d.rst b/modules/viz/doc/viz3d.rst new file mode 100644 index 0000000000..d0e24ae8e7 --- /dev/null +++ b/modules/viz/doc/viz3d.rst @@ -0,0 +1,637 @@ +Viz +=== + +.. highlight:: cpp + +This section describes 3D visualization window as well as classes and methods +that are used to interact with it. + +3D visualization window (see :ocv:class:`Viz3d`) is used to display widgets (see :ocv:class:`Widget`), and it provides +several methods to interact with scene and widgets. + +viz::makeTransformToGlobal +-------------------------- +Takes coordinate frame data and builds transform to global coordinate frame. + +.. ocv:function:: Affine3d viz::makeTransformToGlobal(const Vec3f& axis_x, const Vec3f& axis_y, const Vec3f& axis_z, const Vec3f& origin = Vec3f::all(0)) + + :param axis_x: X axis vector in global coordinate frame. + :param axis_y: Y axis vector in global coordinate frame. + :param axis_z: Z axis vector in global coordinate frame. + :param origin: Origin of the coordinate frame in global coordinate frame. + +This function returns affine transform that describes transformation between global coordinate frame and a given coordinate frame. + +viz::makeCameraPose +------------------- +Constructs camera pose from position, focal_point and up_vector (see gluLookAt() for more infromation). + +.. ocv:function:: Affine3d makeCameraPose(const Vec3f& position, const Vec3f& focal_point, const Vec3f& y_dir) + + :param position: Position of the camera in global coordinate frame. + :param focal_point: Focal point of the camera in global coordinate frame. + :param y_dir: Up vector of the camera in global coordinate frame. + +This function returns pose of the camera in global coordinate frame. + +viz::getWindowByName +-------------------- +Retrieves a window by its name. + +.. ocv:function:: Viz3d getWindowByName(const String &window_name) + + :param window_name: Name of the window that is to be retrieved. + +This function returns a :ocv:class:`Viz3d` object with the given name. + +.. note:: If the window with that name already exists, that window is returned. Otherwise, new window is created with the given name, and it is returned. + +.. note:: Window names are automatically prefixed by "Viz - " if it is not done by the user. + + .. code-block:: cpp + + /// window and window_2 are the same windows. + viz::Viz3d window = viz::getWindowByName("myWindow"); + viz::Viz3d window_2 = viz::getWindowByName("Viz - myWindow"); + +viz::isNan +---------- +Checks **float/double** value for nan. + + .. ocv:function:: bool isNan(float x) + + .. ocv:function:: bool isNan(double x) + + :param x: return true if nan. + +Checks **vector** for nan. + + .. ocv:function:: bool isNan(const Vec<_Tp, cn>& v) + + :param v: return true if **any** of the elements of the vector is *nan*. + +Checks **point** for nan + + .. ocv:function:: bool isNan(const Point3_<_Tp>& p) + + :param p: return true if **any** of the elements of the point is *nan*. + +viz::Viz3d +---------- +.. ocv:class:: Viz3d + +The Viz3d class represents a 3D visualizer window. This class is implicitly shared. :: + + class CV_EXPORTS Viz3d + { + public: + typedef cv::Ptr Ptr; + typedef void (*KeyboardCallback)(const KeyboardEvent&, void*); + typedef void (*MouseCallback)(const MouseEvent&, void*); + + Viz3d(const String& window_name = String()); + Viz3d(const Viz3d&); + Viz3d& operator=(const Viz3d&); + ~Viz3d(); + + void showWidget(const String &id, const Widget &widget, const Affine3d &pose = Affine3d::Identity()); + void removeWidget(const String &id); + Widget getWidget(const String &id) const; + void removeAllWidgets(); + + void setWidgetPose(const String &id, const Affine3d &pose); + void updateWidgetPose(const String &id, const Affine3d &pose); + Affine3d getWidgetPose(const String &id) const; + + void showImage(InputArray image, const Size& window_size = Size(-1, -1)); + + void setCamera(const Camera &camera); + Camera getCamera() const; + Affine3d getViewerPose(); + void setViewerPose(const Affine3d &pose); + + void resetCameraViewpoint (const String &id); + void resetCamera(); + + void convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord); + void converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction); + + Size getWindowSize() const; + void setWindowSize(const Size &window_size); + String getWindowName() const; + void saveScreenshot (const String &file); + void setWindowPosition (int x, int y); + void setFullScreen (bool mode); + void setBackgroundColor(const Color& color = Color::black()); + + void spin(); + void spinOnce(int time = 1, bool force_redraw = false); + bool wasStopped() const; + + void registerKeyboardCallback(KeyboardCallback callback, void* cookie = 0); + void registerMouseCallback(MouseCallback callback, void* cookie = 0); + + void setRenderingProperty(const String &id, int property, double value); + double getRenderingProperty(const String &id, int property); + + + void setRepresentation(int representation); + private: + /* hidden */ + }; + +viz::Viz3d::Viz3d +----------------- +The constructors. + +.. ocv:function:: Viz3d::Viz3d(const String& window_name = String()) + + :param window_name: Name of the window. + +viz::Viz3d::showWidget +---------------------- +Shows a widget in the window. + +.. ocv:function:: void Viz3d::showWidget(const String &id, const Widget &widget, const Affine3d &pose = Affine3d::Identity()) + + :param id: A unique id for the widget. + :param widget: The widget to be displayed in the window. + :param pose: Pose of the widget. + +viz::Viz3d::removeWidget +------------------------ +Removes a widget from the window. + +.. ocv:function:: void removeWidget(const String &id) + + :param id: The id of the widget that will be removed. + +viz::Viz3d::getWidget +--------------------- +Retrieves a widget from the window. A widget is implicitly shared; +that is, if the returned widget is modified, the changes will be +immediately visible in the window. + +.. ocv:function:: Widget getWidget(const String &id) const + + :param id: The id of the widget that will be returned. + +viz::Viz3d::removeAllWidgets +---------------------------- +Removes all widgets from the window. + +.. ocv:function:: void removeAllWidgets() + +viz::Viz3d::showImage +--------------------- +Removed all widgets and displays image scaled to whole window area. + +.. ocv:function:: void showImage(InputArray image, const Size& window_size = Size(-1, -1)) + + :param image: Image to be displayed. + :param size: Size of Viz3d window. Default value means no change. + +viz::Viz3d::setWidgetPose +------------------------- +Sets pose of a widget in the window. + +.. ocv:function:: void setWidgetPose(const String &id, const Affine3d &pose) + + :param id: The id of the widget whose pose will be set. + :param pose: The new pose of the widget. + +viz::Viz3d::updateWidgetPose +---------------------------- +Updates pose of a widget in the window by pre-multiplying its current pose. + +.. ocv:function:: void updateWidgetPose(const String &id, const Affine3d &pose) + + :param id: The id of the widget whose pose will be updated. + :param pose: The pose that the current pose of the widget will be pre-multiplied by. + +viz::Viz3d::getWidgetPose +------------------------- +Returns the current pose of a widget in the window. + +.. ocv:function:: Affine3d getWidgetPose(const String &id) const + + :param id: The id of the widget whose pose will be returned. + +viz::Viz3d::setCamera +--------------------- +Sets the intrinsic parameters of the viewer using Camera. + +.. ocv:function:: void setCamera(const Camera &camera) + + :param camera: Camera object wrapping intrinsinc parameters. + +viz::Viz3d::getCamera +--------------------- +Returns a camera object that contains intrinsic parameters of the current viewer. + +.. ocv:function:: Camera getCamera() const + +viz::Viz3d::getViewerPose +------------------------- +Returns the current pose of the viewer. + +..ocv:function:: Affine3d getViewerPose() + +viz::Viz3d::setViewerPose +------------------------- +Sets pose of the viewer. + +.. ocv:function:: void setViewerPose(const Affine3d &pose) + + :param pose: The new pose of the viewer. + +viz::Viz3d::resetCameraViewpoint +-------------------------------- +Resets camera viewpoint to a 3D widget in the scene. + +.. ocv:function:: void resetCameraViewpoint (const String &id) + + :param pose: Id of a 3D widget. + +viz::Viz3d::resetCamera +----------------------- +Resets camera. + +.. ocv:function:: void resetCamera() + +viz::Viz3d::convertToWindowCoordinates +-------------------------------------- +Transforms a point in world coordinate system to window coordinate system. + +.. ocv:function:: void convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord) + + :param pt: Point in world coordinate system. + :param window_coord: Output point in window coordinate system. + +viz::Viz3d::converTo3DRay +------------------------- +Transforms a point in window coordinate system to a 3D ray in world coordinate system. + +.. ocv:function:: void converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction) + + :param window_coord: Point in window coordinate system. + :param origin: Output origin of the ray. + :param direction: Output direction of the ray. + +viz::Viz3d::getWindowSize +------------------------- +Returns the current size of the window. + +.. ocv:function:: Size getWindowSize() const + +viz::Viz3d::setWindowSize +------------------------- +Sets the size of the window. + +.. ocv:function:: void setWindowSize(const Size &window_size) + + :param window_size: New size of the window. + +viz::Viz3d::getWindowName +------------------------- +Returns the name of the window which has been set in the constructor. + +.. ocv:function:: String getWindowName() const + +viz::Viz3d::saveScreenshot +-------------------------- +Saves screenshot of the current scene. + +.. ocv:function:: void saveScreenshot(const String &file) + + :param file: Name of the file. + +viz::Viz3d::setWindowPosition +----------------------------- +Sets the position of the window in the screen. + +.. ocv:function:: void setWindowPosition(int x, int y) + + :param x: x coordinate of the window + :param y: y coordinate of the window + +viz::Viz3d::setFullScreen +------------------------- +Sets or unsets full-screen rendering mode. + +.. ocv:function:: void setFullScreen(bool mode) + + :param mode: If true, window will use full-screen mode. + +viz::Viz3d::setBackgroundColor +------------------------------ +Sets background color. + +.. ocv:function:: void setBackgroundColor(const Color& color = Color::black()) + +viz::Viz3d::spin +---------------- +The window renders and starts the event loop. + +.. ocv:function:: void spin() + +viz::Viz3d::spinOnce +-------------------- +Starts the event loop for a given time. + +.. ocv:function:: void spinOnce(int time = 1, bool force_redraw = false) + + :param time: Amount of time in milliseconds for the event loop to keep running. + :param force_draw: If true, window renders. + +viz::Viz3d::wasStopped +---------------------- +Returns whether the event loop has been stopped. + +.. ocv:function:: bool wasStopped() + +viz::Viz3d::registerKeyboardCallback +------------------------------------ +Sets keyboard handler. + +.. ocv:function:: void registerKeyboardCallback(KeyboardCallback callback, void* cookie = 0) + + :param callback: Keyboard callback ``(void (*KeyboardCallbackFunction(const KeyboardEvent&, void*))``. + :param cookie: The optional parameter passed to the callback. + +viz::Viz3d::registerMouseCallback +--------------------------------- +Sets mouse handler. + +.. ocv:function:: void registerMouseCallback(MouseCallback callback, void* cookie = 0) + + :param callback: Mouse callback ``(void (*MouseCallback)(const MouseEvent&, void*))``. + :param cookie: The optional parameter passed to the callback. + +viz::Viz3d::setRenderingProperty +-------------------------------- +Sets rendering property of a widget. + +.. ocv:function:: void setRenderingProperty(const String &id, int property, double value) + + :param id: Id of the widget. + :param property: Property that will be modified. + :param value: The new value of the property. + + **Rendering property** can be one of the following: + + * **POINT_SIZE** + * **OPACITY** + * **LINE_WIDTH** + * **FONT_SIZE** + * **REPRESENTATION**: Expected values are + * **REPRESENTATION_POINTS** + * **REPRESENTATION_WIREFRAME** + * **REPRESENTATION_SURFACE** + * **IMMEDIATE_RENDERING**: + * Turn on immediate rendering by setting the value to ``1``. + * Turn off immediate rendering by setting the value to ``0``. + * **SHADING**: Expected values are + * **SHADING_FLAT** + * **SHADING_GOURAUD** + * **SHADING_PHONG** + +viz::Viz3d::getRenderingProperty +-------------------------------- +Returns rendering property of a widget. + +.. ocv:function:: double getRenderingProperty(const String &id, int property) + + :param id: Id of the widget. + :param property: Property. + + **Rendering property** can be one of the following: + + * **POINT_SIZE** + * **OPACITY** + * **LINE_WIDTH** + * **FONT_SIZE** + * **REPRESENTATION**: Expected values are + * **REPRESENTATION_POINTS** + * **REPRESENTATION_WIREFRAME** + * **REPRESENTATION_SURFACE** + * **IMMEDIATE_RENDERING**: + * Turn on immediate rendering by setting the value to ``1``. + * Turn off immediate rendering by setting the value to ``0``. + * **SHADING**: Expected values are + * **SHADING_FLAT** + * **SHADING_GOURAUD** + * **SHADING_PHONG** + +viz::Viz3d::setRepresentation +----------------------------- +Sets geometry representation of the widgets to surface, wireframe or points. + +.. ocv:function:: void setRepresentation(int representation) + + :param representation: Geometry representation which can be one of the following: + + * **REPRESENTATION_POINTS** + * **REPRESENTATION_WIREFRAME** + * **REPRESENTATION_SURFACE** + +viz::Color +---------- +.. ocv:class:: Color + +This class a represents BGR color. :: + + class CV_EXPORTS Color : public Scalar + { + public: + Color(); + Color(double gray); + Color(double blue, double green, double red); + + Color(const Scalar& color); + + static Color black(); + static Color blue(); + static Color green(); + static Color cyan(); + + static Color red(); + static Color magenta(); + static Color yellow(); + static Color white(); + + static Color gray(); + }; + +viz::Mesh +----------- +.. ocv:class:: Mesh + +This class wraps mesh attributes, and it can load a mesh from a ``ply`` file. :: + + class CV_EXPORTS Mesh + { + public: + + Mat cloud, colors, normals; + + //! Raw integer list of the form: (n,id1,id2,...,idn, n,id1,id2,...,idn, ...) + //! where n is the number of points in the poligon, and id is a zero-offset index into an associated cloud. + Mat polygons; + + //! Loads mesh from a given ply file + static Mesh load(const String& file); + }; + +viz::Mesh::load +--------------------- +Loads a mesh from a ``ply`` file. + +.. ocv:function:: static Mesh load(const String& file) + + :param file: File name (for no only PLY is supported) + + +viz::KeyboardEvent +------------------ +.. ocv:class:: KeyboardEvent + +This class represents a keyboard event. :: + + class CV_EXPORTS KeyboardEvent + { + public: + enum { ALT = 1, CTRL = 2, SHIFT = 4 }; + enum Action { KEY_UP = 0, KEY_DOWN = 1 }; + + KeyboardEvent(Action action, const String& symbol, unsigned char code, int modifiers); + + Action action; + String symbol; + unsigned char code; + int modifiers; + }; + +viz::KeyboardEvent::KeyboardEvent +--------------------------------- +Constructs a KeyboardEvent. + +.. ocv:function:: KeyboardEvent (Action action, const String& symbol, unsigned char code, Modifiers modifiers) + + :param action: Signals if key is pressed or released. + :param symbol: Name of the key. + :param code: Code of the key. + :param modifiers: Signals if ``alt``, ``ctrl`` or ``shift`` are pressed or their combination. + + +viz::MouseEvent +--------------- +.. ocv:class:: MouseEvent + +This class represents a mouse event. :: + + class CV_EXPORTS MouseEvent + { + public: + enum Type { MouseMove = 1, MouseButtonPress, MouseButtonRelease, MouseScrollDown, MouseScrollUp, MouseDblClick } ; + enum MouseButton { NoButton = 0, LeftButton, MiddleButton, RightButton, VScroll } ; + + MouseEvent(const Type& type, const MouseButton& button, const Point& pointer, int modifiers); + + Type type; + MouseButton button; + Point pointer; + int modifiers; + }; + +viz::MouseEvent::MouseEvent +--------------------------- +Constructs a MouseEvent. + +.. ocv:function:: MouseEvent (const Type& type, const MouseButton& button, const Point& p, Modifiers modifiers) + + :param type: Type of the event. This can be **MouseMove**, **MouseButtonPress**, **MouseButtonRelease**, **MouseScrollDown**, **MouseScrollUp**, **MouseDblClick**. + :param button: Mouse button. This can be **NoButton**, **LeftButton**, **MiddleButton**, **RightButton**, **VScroll**. + :param p: Position of the event. + :param modifiers: Signals if ``alt``, ``ctrl`` or ``shift`` are pressed or their combination. + +viz::Camera +----------- +.. ocv:class:: Camera + +This class wraps intrinsic parameters of a camera. It provides several constructors +that can extract the intrinsic parameters from ``field of view``, ``intrinsic matrix`` and +``projection matrix``. :: + + class CV_EXPORTS Camera + { + public: + Camera(double f_x, double f_y, double c_x, double c_y, const Size &window_size); + Camera(const Vec2d &fov, const Size &window_size); + Camera(const Matx33d &K, const Size &window_size); + Camera(const Matx44d &proj, const Size &window_size); + + inline const Vec2d & getClip() const; + inline void setClip(const Vec2d &clip); + + inline const Size & getWindowSize() const; + void setWindowSize(const Size &window_size); + + inline const Vec2d & getFov() const; + inline void setFov(const Vec2d & fov); + + inline const Vec2d & getPrincipalPoint() const; + inline const Vec2d & getFocalLength() const; + + void computeProjectionMatrix(Matx44d &proj) const; + + static Camera KinectCamera(const Size &window_size); + + private: + /* hidden */ + }; + +viz::Camera::Camera +------------------- +Constructs a Camera. + +.. ocv:function:: Camera(double f_x, double f_y, double c_x, double c_y, const Size &window_size) + + :param f_x: Horizontal focal length. + :param f_y: Vertical focal length. + :param c_x: x coordinate of the principal point. + :param c_y: y coordinate of the principal point. + :param window_size: Size of the window. This together with focal length and principal point determines the field of view. + +.. ocv:function:: Camera(const Vec2d &fov, const Size &window_size) + + :param fov: Field of view (horizontal, vertical) + :param window_size: Size of the window. + + Principal point is at the center of the window by default. + +.. ocv:function:: Camera(const Matx33d &K, const Size &window_size) + + :param K: Intrinsic matrix of the camera. + :param window_size: Size of the window. This together with intrinsic matrix determines the field of view. + +.. ocv:function:: Camera(const Matx44d &proj, const Size &window_size) + + :param proj: Projection matrix of the camera. + :param window_size: Size of the window. This together with projection matrix determines the field of view. + +viz::Camera::computeProjectionMatrix +------------------------------------ +Computes projection matrix using intrinsic parameters of the camera. + +.. ocv:function:: void computeProjectionMatrix(Matx44d &proj) const + + :param proj: Output projection matrix. + +viz::Camera::KinectCamera +------------------------- +Creates a Kinect Camera. + +.. ocv:function:: static Camera KinectCamera(const Size &window_size) + + :param window_size: Size of the window. This together with intrinsic matrix of a Kinect Camera determines the field of view. diff --git a/modules/viz/doc/widget.rst b/modules/viz/doc/widget.rst new file mode 100644 index 0000000000..008e0e68a5 --- /dev/null +++ b/modules/viz/doc/widget.rst @@ -0,0 +1,1019 @@ +Widget +====== + +.. highlight:: cpp + +In this section, the widget framework is explained. Widgets represent +2D or 3D objects, varying from simple ones such as lines to complex one such as +point clouds and meshes. + +Widgets are **implicitly shared**. Therefore, one can add a widget to the scene, +and modify the widget without re-adding the widget. + +.. code-block:: cpp + + ... + /// Create a cloud widget + viz::WCloud cw(cloud, viz::Color::red()); + /// Display it in a window + myWindow.showWidget("CloudWidget1", cw); + /// Modify it, and it will be modified in the window. + cw.setColor(viz::Color::yellow()); + ... + +viz::Widget +----------- +.. ocv:class:: Widget + +Base class of all widgets. Widget is implicitly shared. :: + + class CV_EXPORTS Widget + { + public: + Widget(); + Widget(const Widget& other); + Widget& operator=(const Widget& other); + ~Widget(); + + //! Create a widget directly from ply file + static Widget fromPlyFile(const String &file_name); + + //! Rendering properties of this particular widget + void setRenderingProperty(int property, double value); + double getRenderingProperty(int property) const; + + //! Casting between widgets + template _W cast(); + private: + /* hidden */ + }; + +viz::Widget::fromPlyFile +------------------------ +Creates a widget from ply file. + +.. ocv:function:: static Widget fromPlyFile(const String &file_name) + + :param file_name: Ply file name. + +viz::Widget::setRenderingProperty +--------------------------------- +Sets rendering property of the widget. + +.. ocv:function:: void setRenderingProperty(int property, double value) + + :param property: Property that will be modified. + :param value: The new value of the property. + + **Rendering property** can be one of the following: + + * **POINT_SIZE** + * **OPACITY** + * **LINE_WIDTH** + * **FONT_SIZE** + * **REPRESENTATION**: Expected values are + * **REPRESENTATION_POINTS** + * **REPRESENTATION_WIREFRAME** + * **REPRESENTATION_SURFACE** + * **IMMEDIATE_RENDERING**: + * Turn on immediate rendering by setting the value to ``1``. + * Turn off immediate rendering by setting the value to ``0``. + * **SHADING**: Expected values are + * **SHADING_FLAT** + * **SHADING_GOURAUD** + * **SHADING_PHONG** + +viz::Widget::getRenderingProperty +--------------------------------- +Returns rendering property of the widget. + +.. ocv:function:: double getRenderingProperty(int property) const + + :param property: Property. + + **Rendering property** can be one of the following: + + * **POINT_SIZE** + * **OPACITY** + * **LINE_WIDTH** + * **FONT_SIZE** + * **REPRESENTATION**: Expected values are + * **REPRESENTATION_POINTS** + * **REPRESENTATION_WIREFRAME** + * **REPRESENTATION_SURFACE** + * **IMMEDIATE_RENDERING**: + * Turn on immediate rendering by setting the value to ``1``. + * Turn off immediate rendering by setting the value to ``0``. + * **SHADING**: Expected values are + * **SHADING_FLAT** + * **SHADING_GOURAUD** + * **SHADING_PHONG** + +viz::Widget::cast +----------------- +Casts a widget to another. + +.. ocv:function:: template _W cast() + +.. code-block:: cpp + + // Create a sphere widget + viz::WSphere sw(Point3f(0.0f,0.0f,0.0f), 0.5f); + // Cast sphere widget to cloud widget + viz::WCloud cw = sw.cast(); + +.. note:: 3D Widgets can only be cast to 3D Widgets. 2D Widgets can only be cast to 2D Widgets. + +viz::WidgetAccessor +------------------- +.. ocv:class:: WidgetAccessor + +This class is for users who want to develop their own widgets using VTK library API. :: + + struct CV_EXPORTS WidgetAccessor + { + static vtkSmartPointer getProp(const Widget &widget); + static void setProp(Widget &widget, vtkSmartPointer prop); + }; + +viz::WidgetAccessor::getProp +---------------------------- +Returns ``vtkProp`` of a given widget. + +.. ocv:function:: static vtkSmartPointer getProp(const Widget &widget) + + :param widget: Widget whose ``vtkProp`` is to be returned. + +.. note:: vtkProp has to be down cast appropriately to be modified. + + .. code-block:: cpp + + vtkActor * actor = vtkActor::SafeDownCast(viz::WidgetAccessor::getProp(widget)); + +viz::WidgetAccessor::setProp +---------------------------- +Sets ``vtkProp`` of a given widget. + +.. ocv:function:: static void setProp(Widget &widget, vtkSmartPointer prop) + + :param widget: Widget whose ``vtkProp`` is to be set. + :param prop: A ``vtkProp``. + +viz::Widget3D +------------- +.. ocv:class:: Widget3D + +Base class of all 3D widgets. :: + + class CV_EXPORTS Widget3D : public Widget + { + public: + Widget3D() {} + + //! widget position manipulation, i.e. place where it is rendered. + void setPose(const Affine3d &pose); + void updatePose(const Affine3d &pose); + Affine3d getPose() const; + + //! updates internal widget data, i.e. points, normals, etc. + void applyTransform(const Affine3d &transform); + + void setColor(const Color &color); + + }; + +viz::Widget3D::setPose +---------------------- +Sets pose of the widget. + +.. ocv:function:: void setPose(const Affine3d &pose) + + :param pose: The new pose of the widget. + +viz::Widget3D::updateWidgetPose +------------------------------- +Updates pose of the widget by pre-multiplying its current pose. + +.. ocv:function:: void updateWidgetPose(const Affine3d &pose) + + :param pose: The pose that the current pose of the widget will be pre-multiplied by. + +viz::Widget3D::getPose +---------------------- +Returns the current pose of the widget. + +.. ocv:function:: Affine3d getWidgetPose() const + + +viz::Widget3D::applyTransform +------------------------------- +Transforms internal widget data (i.e. points, normals) using the given transform. + +.. ocv:function:: void applyTransform(const Affine3d &transform) + + :param transform: Specified transformation to apply. + +viz::Widget3D::setColor +----------------------- +Sets the color of the widget. + +.. ocv:function:: void setColor(const Color &color) + + :param color: color of type :ocv:class:`Color` + +viz::Widget2D +------------- +.. ocv:class:: Widget2D + +Base class of all 2D widgets. :: + + class CV_EXPORTS Widget2D : public Widget + { + public: + Widget2D() {} + + void setColor(const Color &color); + }; + +viz::Widget2D::setColor +----------------------- +Sets the color of the widget. + +.. ocv:function:: void setColor(const Color &color) + + :param color: color of type :ocv:class:`Color` + +viz::WLine +---------- +.. ocv:class:: WLine + +This 3D Widget defines a finite line. :: + + class CV_EXPORTS WLine : public Widget3D + { + public: + WLine(const Point3f &pt1, const Point3f &pt2, const Color &color = Color::white()); + }; + +viz::WLine::WLine +----------------- +Constructs a WLine. + +.. ocv:function:: WLine(const Point3f &pt1, const Point3f &pt2, const Color &color = Color::white()) + + :param pt1: Start point of the line. + :param pt2: End point of the line. + :param color: :ocv:class:`Color` of the line. + +viz::WPlane +----------- +.. ocv:class:: WPlane + +This 3D Widget defines a finite plane. :: + + class CV_EXPORTS WPlane : public Widget3D + { + public: + //! created default plane with center point at origin and normal oriented along z-axis + WPlane(const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()); + + //! repositioned plane + WPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_plane_yaxis,const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()); + }; + +viz::WPlane::WPlane +------------------- +Constructs a default plane with center point at origin and normal oriented along z-axis. + +.. ocv:function:: WPlane(const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()) + + :param size: Size of the plane + :param color: :ocv:class:`Color` of the plane. + +viz::WPlane::WPlane +------------------- +Constructs a repositioned plane + +.. ocv:function:: WPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis,const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()) + + :param center: Center of the plane + :param normal: Plane normal orientation + :param new_yaxis: Up-vector. New orientation of plane y-axis. + :param color: :ocv:class:`Color` of the plane. + +viz::WSphere +------------ +.. ocv:class:: WSphere + +This 3D Widget defines a sphere. :: + + class CV_EXPORTS WSphere : public Widget3D + { + public: + WSphere(const cv::Point3f ¢er, double radius, int sphere_resolution = 10, const Color &color = Color::white()) + }; + +viz::WSphere::WSphere +--------------------- +Constructs a WSphere. + +.. ocv:function:: WSphere(const cv::Point3f ¢er, double radius, int sphere_resolution = 10, const Color &color = Color::white()) + + :param center: Center of the sphere. + :param radius: Radius of the sphere. + :param sphere_resolution: Resolution of the sphere. + :param color: :ocv:class:`Color` of the sphere. + +viz::WArrow +---------------- +.. ocv:class:: WArrow + +This 3D Widget defines an arrow. :: + + class CV_EXPORTS WArrow : public Widget3D + { + public: + WArrow(const Point3f& pt1, const Point3f& pt2, double thickness = 0.03, const Color &color = Color::white()); + }; + +viz::WArrow::WArrow +----------------------------- +Constructs an WArrow. + +.. ocv:function:: WArrow(const Point3f& pt1, const Point3f& pt2, double thickness = 0.03, const Color &color = Color::white()) + + :param pt1: Start point of the arrow. + :param pt2: End point of the arrow. + :param thickness: Thickness of the arrow. Thickness of arrow head is also adjusted accordingly. + :param color: :ocv:class:`Color` of the arrow. + +Arrow head is located at the end point of the arrow. + +viz::WCircle +----------------- +.. ocv:class:: WCircle + +This 3D Widget defines a circle. :: + + class CV_EXPORTS WCircle : public Widget3D + { + public: + //! creates default planar circle centred at origin with plane normal along z-axis + WCircle(double radius, double thickness = 0.01, const Color &color = Color::white()); + + //! creates repositioned circle + WCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness = 0.01, const Color &color = Color::white()); + }; + +viz::WCircle::WCircle +------------------------------- +Constructs default planar circle centred at origin with plane normal along z-axis + +.. ocv:function:: WCircle(double radius, double thickness = 0.01, const Color &color = Color::white()) + + :param radius: Radius of the circle. + :param thickness: Thickness of the circle. + :param color: :ocv:class:`Color` of the circle. + +viz::WCircle::WCircle +------------------------------- +Constructs repositioned planar circle. + +.. ocv:function:: WCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness = 0.01, const Color &color = Color::white()) + + :param radius: Radius of the circle. + :param center: Center of the circle. + :param normal: Normal of the plane in which the circle lies. + :param thickness: Thickness of the circle. + :param color: :ocv:class:`Color` of the circle. + +viz::WCone +------------------------------- +.. ocv:class:: WCone + +This 3D Widget defines a cone. :: + + class CV_EXPORTS WCone : public Widget3D + { + public: + //! create default cone, oriented along x-axis with center of its base located at origin + WCone(double lenght, double radius, int resolution = 6.0, const Color &color = Color::white()); + + //! creates repositioned cone + WCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6.0, const Color &color = Color::white()); + }; + +viz::WCone::WCone +------------------------------- +Constructs default cone oriented along x-axis with center of its base located at origin + +.. ocv:function:: WCone(double length, double radius, int resolution = 6.0, const Color &color = Color::white()) + + :param length: Length of the cone. + :param radius: Radius of the cone. + :param resolution: Resolution of the cone. + :param color: :ocv:class:`Color` of the cone. + +viz::WCone::WCone +------------------------------- +Constructs repositioned planar cone. + +.. ocv:function:: WCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6.0, const Color &color = Color::white()) + + :param radius: Radius of the cone. + :param center: Center of the cone base. + :param tip: Tip of the cone. + :param resolution: Resolution of the cone. + :param color: :ocv:class:`Color` of the cone. + +viz::WCylinder +-------------- +.. ocv:class:: WCylinder + +This 3D Widget defines a cylinder. :: + + class CV_EXPORTS WCylinder : public Widget3D + { + public: + WCylinder(const Point3d& axis_point1, const Point3d& axis_point2, double radius, int numsides = 30, const Color &color = Color::white()); + }; + +viz::WCylinder::WCylinder +----------------------------------- +Constructs a WCylinder. + +.. ocv:function:: WCylinder(const Point3f& pt_on_axis, const Point3f& axis_direction, double radius, int numsides = 30, const Color &color = Color::white()) + + :param axis_point1: A point1 on the axis of the cylinder. + :param axis_point2: A point2 on the axis of the cylinder. + :param radius: Radius of the cylinder. + :param numsides: Resolution of the cylinder. + :param color: :ocv:class:`Color` of the cylinder. + +viz::WCube +---------- +.. ocv:class:: WCube + +This 3D Widget defines a cube. :: + + class CV_EXPORTS WCube : public Widget3D + { + public: + WCube(const Point3f& pt_min, const Point3f& pt_max, bool wire_frame = true, const Color &color = Color::white()); + }; + +viz::WCube::WCube +--------------------------- +Constructs a WCube. + +.. ocv:function:: WCube(const Point3f& pt_min, const Point3f& pt_max, bool wire_frame = true, const Color &color = Color::white()) + + :param pt_min: Specifies minimum point of the bounding box. + :param pt_max: Specifies maximum point of the bounding box. + :param wire_frame: If true, cube is represented as wireframe. + :param color: :ocv:class:`Color` of the cube. + +.. image:: images/cube_widget.png + :alt: Cube Widget + :align: center + +viz::WCoordinateSystem +---------------------- +.. ocv:class:: WCoordinateSystem + +This 3D Widget represents a coordinate system. :: + + class CV_EXPORTS WCoordinateSystem : public Widget3D + { + public: + WCoordinateSystem(double scale = 1.0); + }; + +viz::WCoordinateSystem::WCoordinateSystem +--------------------------------------------------- +Constructs a WCoordinateSystem. + +.. ocv:function:: WCoordinateSystem(double scale = 1.0) + + :param scale: Determines the size of the axes. + +viz::WPolyLine +-------------- +.. ocv:class:: WPolyLine + +This 3D Widget defines a poly line. :: + + class CV_EXPORTS WPolyLine : public Widget3D + { + public: + WPolyLine(InputArray points, const Color &color = Color::white()); + }; + +viz::WPolyLine::WPolyLine +----------------------------------- +Constructs a WPolyLine. + +.. ocv:function:: WPolyLine(InputArray points, const Color &color = Color::white()) + + :param points: Point set. + :param color: :ocv:class:`Color` of the poly line. + +viz::WGrid +---------- +.. ocv:class:: WGrid + +This 3D Widget defines a grid. :: + + class CV_EXPORTS WGrid : public Widget3D + { + public: + //! Creates grid at the origin and normal oriented along z-axis + WGrid(const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const Color &color = Color::white()); + + //! Creates repositioned grid + WGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, + const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const Color &color = Color::white()); + }; + +viz::WGrid::WGrid +--------------------------- +Constructs a WGrid. + +.. ocv:function:: WGrid(const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const Color &color = Color::white()) + + :param cells: Number of cell columns and rows, respectively. + :param cells_spacing: Size of each cell, respectively. + :param color: :ocv:class:`Color` of the grid. + +.. ocv:function: WGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, Vec2i &cells, const Vec2d &cells_spacing, const Color &color; + + :param center: Center of the grid + :param normal: Grid normal orientation + :param new_yaxis: Up-vector. New orientation of grid y-axis. + :param cells: Number of cell columns and rows, respectively. + :param cells_spacing: Size of each cell, respectively. + :param color: :ocv:class:`Color` of the grid.. + +viz::WText3D +------------ +.. ocv:class:: WText3D + +This 3D Widget represents 3D text. The text always faces the camera. :: + + class CV_EXPORTS WText3D : public Widget3D + { + public: + WText3D(const String &text, const Point3f &position, double text_scale = 1.0, bool face_camera = true, const Color &color = Color::white()); + + void setText(const String &text); + String getText() const; + }; + +viz::WText3D::WText3D +------------------------------- +Constructs a WText3D. + +.. ocv:function:: WText3D(const String &text, const Point3f &position, double text_scale = 1.0, bool face_camera = true, const Color &color = Color::white()) + + :param text: Text content of the widget. + :param position: Position of the text. + :param text_scale: Size of the text. + :param face_camera: If true, text always faces the camera. + :param color: :ocv:class:`Color` of the text. + +viz::WText3D::setText +--------------------- +Sets the text content of the widget. + +.. ocv:function:: void setText(const String &text) + + :param text: Text content of the widget. + +viz::WText3D::getText +--------------------- +Returns the current text content of the widget. + +.. ocv:function:: String getText() const + +viz::WText +---------- +.. ocv:class:: WText + +This 2D Widget represents text overlay. :: + + class CV_EXPORTS WText : public Widget2D + { + public: + WText(const String &text, const Point2i &pos, int font_size = 10, const Color &color = Color::white()); + + void setText(const String &text); + String getText() const; + }; + +viz::WText::WText +----------------- +Constructs a WText. + +.. ocv:function:: WText(const String &text, const Point2i &pos, int font_size = 10, const Color &color = Color::white()) + + :param text: Text content of the widget. + :param pos: Position of the text. + :param font_size: Font size. + :param color: :ocv:class:`Color` of the text. + +viz::WText::setText +------------------- +Sets the text content of the widget. + +.. ocv:function:: void setText(const String &text) + + :param text: Text content of the widget. + +viz::WText::getText +------------------- +Returns the current text content of the widget. + +.. ocv:function:: String getText() const + +viz::WImageOverlay +------------------ +.. ocv:class:: WImageOverlay + +This 2D Widget represents an image overlay. :: + + class CV_EXPORTS WImageOverlay : public Widget2D + { + public: + WImageOverlay(InputArray image, const Rect &rect); + + void setImage(InputArray image); + }; + +viz::WImageOverlay::WImageOverlay +--------------------------------- +Constructs an WImageOverlay. + +.. ocv:function:: WImageOverlay(InputArray image, const Rect &rect) + + :param image: BGR or Gray-Scale image. + :param rect: Image is scaled and positioned based on rect. + +viz::WImageOverlay::setImage +---------------------------- +Sets the image content of the widget. + +.. ocv:function:: void setImage(InputArray image) + + :param image: BGR or Gray-Scale image. + +viz::WImage3D +------------- +.. ocv:class:: WImage3D + +This 3D Widget represents an image in 3D space. :: + + class CV_EXPORTS WImage3D : public Widget3D + { + public: + //! Creates 3D image at the origin + WImage3D(InputArray image, const Size2d &size); + //! Creates 3D image at a given position, pointing in the direction of the normal, and having the up_vector orientation + WImage3D(InputArray image, const Size2d &size, const Vec3d &position, const Vec3d &normal, const Vec3d &up_vector); + + void setImage(InputArray image); + }; + +viz::WImage3D::WImage3D +----------------------- +Constructs an WImage3D. + +.. ocv:function:: WImage3D(InputArray image, const Size2d &size) + + :param image: BGR or Gray-Scale image. + :param size: Size of the image. + +.. ocv:function:: WImage3D(InputArray image, const Size2d &size, const Vec3d &position, const Vec3d &normal, const Vec3d &up_vector) + + :param position: Position of the image. + :param normal: Normal of the plane that represents the image. + :param up_vector: Determines orientation of the image. + :param image: BGR or Gray-Scale image. + :param size: Size of the image. + +viz::WImage3D::setImage +----------------------- +Sets the image content of the widget. + +.. ocv:function:: void setImage(InputArray image) + + :param image: BGR or Gray-Scale image. + +viz::WCameraPosition +-------------------- +.. ocv:class:: WCameraPosition + +This 3D Widget represents camera position in a scene by its axes or viewing frustum. :: + + class CV_EXPORTS WCameraPosition : public Widget3D + { + public: + //! Creates camera coordinate frame (axes) at the origin + WCameraPosition(double scale = 1.0); + //! Creates frustum based on the intrinsic marix K at the origin + WCameraPosition(const Matx33d &K, double scale = 1.0, const Color &color = Color::white()); + //! Creates frustum based on the field of view at the origin + WCameraPosition(const Vec2d &fov, double scale = 1.0, const Color &color = Color::white()); + //! Creates frustum and display given image at the far plane + WCameraPosition(const Matx33d &K, InputArray image, double scale = 1.0, const Color &color = Color::white()); + //! Creates frustum and display given image at the far plane + WCameraPosition(const Vec2d &fov, InputArray image, double scale = 1.0, const Color &color = Color::white()); + }; + +viz::WCameraPosition::WCameraPosition +------------------------------------- +Constructs a WCameraPosition. + +- **Display camera coordinate frame.** + + .. ocv:function:: WCameraPosition(double scale = 1.0) + + Creates camera coordinate frame at the origin. + + .. image:: images/cpw1.png + :alt: Camera coordinate frame + :align: center + +- **Display the viewing frustum.** + + .. ocv:function:: WCameraPosition(const Matx33d &K, double scale = 1.0, const Color &color = Color::white()) + + :param K: Intrinsic matrix of the camera. + :param scale: Scale of the frustum. + :param color: :ocv:class:`Color` of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K. + + .. ocv:function:: WCameraPosition(const Vec2d &fov, double scale = 1.0, const Color &color = Color::white()) + + :param fov: Field of view of the camera (horizontal, vertical). + :param scale: Scale of the frustum. + :param color: :ocv:class:`Color` of the frustum. + + Creates viewing frustum of the camera based on its field of view fov. + + .. image:: images/cpw2.png + :alt: Camera viewing frustum + :align: center + +- **Display image on the far plane of the viewing frustum.** + + .. ocv:function:: WCameraPosition(const Matx33d &K, InputArray image, double scale = 1.0, const Color &color = Color::white()) + + :param K: Intrinsic matrix of the camera. + :param img: BGR or Gray-Scale image that is going to be displayed on the far plane of the frustum. + :param scale: Scale of the frustum and image. + :param color: :ocv:class:`Color` of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K, and displays image on the far end plane. + + .. ocv:function:: WCameraPosition(const Vec2d &fov, InputArray image, double scale = 1.0, const Color &color = Color::white()) + + :param fov: Field of view of the camera (horizontal, vertical). + :param img: BGR or Gray-Scale image that is going to be displayed on the far plane of the frustum. + :param scale: Scale of the frustum and image. + :param color: :ocv:class:`Color` of the frustum. + + Creates viewing frustum of the camera based on its intrinsic matrix K, and displays image on the far end plane. + + .. image:: images/cpw3.png + :alt: Camera viewing frustum with image + :align: center + +viz::WTrajectory +---------------- +.. ocv:class:: WTrajectory + +This 3D Widget represents a trajectory. :: + + class CV_EXPORTS WTrajectory : public Widget3D + { + public: + enum {FRAMES = 1, PATH = 2, BOTH = FRAMES + PATH}; + + //! Displays trajectory of the given path either by coordinate frames or polyline + WTrajectory(InputArray path, int display_mode = WTrajectory::PATH, double scale = 1.0, const Color &color = Color::white(),; + }; + +viz::WTrajectory::WTrajectory +----------------------------- +Constructs a WTrajectory. + +.. ocv:function:: WTrajectory(InputArray path, int display_mode = WTrajectory::PATH, double scale = 1.0, const Color &color = Color::white()) + + :param path: List of poses on a trajectory. Takes std::vector> with T == [float | double] + :param display_mode: Display mode. This can be PATH, FRAMES, and BOTH. + :param scale: Scale of the frames. Polyline is not affected. + :param color: :ocv:class:`Color` of the polyline that represents path. Frames are not affected. + + Displays trajectory of the given path as follows: + + * PATH : Displays a poly line that represents the path. + * FRAMES : Displays coordinate frames at each pose. + * PATH & FRAMES : Displays both poly line and coordinate frames. + +viz::WTrajectoryFrustums +------------------------ +.. ocv:class:: WTrajectoryFrustums + +This 3D Widget represents a trajectory. :: + + class CV_EXPORTS WTrajectoryFrustums : public Widget3D + { + public: + //! Displays trajectory of the given path by frustums + WTrajectoryFrustums(InputArray path, const Matx33d &K, double scale = 1.0, const Color &color = Color::white()); + //! Displays trajectory of the given path by frustums + WTrajectoryFrustums(InputArray path, const Vec2d &fov, double scale = 1.0, const Color &color = Color::white()); + }; + +viz::WTrajectoryFrustums::WTrajectoryFrustums +--------------------------------------------- +Constructs a WTrajectoryFrustums. + +.. ocv:function:: WTrajectoryFrustums(const std::vector &path, const Matx33d &K, double scale = 1.0, const Color &color = Color::white()) + + :param path: List of poses on a trajectory. Takes std::vector> with T == [float | double] + :param K: Intrinsic matrix of the camera. + :param scale: Scale of the frustums. + :param color: :ocv:class:`Color` of the frustums. + + Displays frustums at each pose of the trajectory. + +.. ocv:function:: WTrajectoryFrustums(const std::vector &path, const Vec2d &fov, double scale = 1.0, const Color &color = Color::white()) + + :param path: List of poses on a trajectory. Takes std::vector> with T == [float | double] + :param fov: Field of view of the camera (horizontal, vertical). + :param scale: Scale of the frustums. + :param color: :ocv:class:`Color` of the frustums. + + Displays frustums at each pose of the trajectory. + +viz::WTrajectorySpheres +----------------------- +.. ocv:class:: WTrajectorySpheres + +This 3D Widget represents a trajectory using spheres and lines, where spheres represent the positions of the camera, and lines +represent the direction from previous position to the current. :: + + class CV_EXPORTS WTrajectorySpheres : public Widget3D + { + public: + WTrajectorySpheres(InputArray path, double line_length = 0.05, double radius = 0.007, + const Color &from = Color::red(), const Color &to = Color::white()); + }; + +viz::WTrajectorySpheres::WTrajectorySpheres +------------------------------------------- +Constructs a WTrajectorySpheres. + +.. ocv:function:: WTrajectorySpheres(InputArray path, double line_length = 0.05, double radius = 0.007, const Color &from = Color::red(), const Color &to = Color::white()) + + :param path: List of poses on a trajectory. Takes std::vector> with T == [float | double] + :param line_length: Max length of the lines which point to previous position + :param sphere_radius: Radius of the spheres. + :param from: :ocv:class:`Color` for first sphere. + :param to: :ocv:class:`Color` for last sphere. Intermediate spheres will have interpolated color. + +viz::WCloud +----------- +.. ocv:class:: WCloud + +This 3D Widget defines a point cloud. :: + + class CV_EXPORTS WCloud : public Widget3D + { + public: + //! Each point in cloud is mapped to a color in colors + WCloud(InputArray cloud, InputArray colors); + //! All points in cloud have the same color + WCloud(InputArray cloud, const Color &color = Color::white()); + }; + +viz::WCloud::WCloud +------------------- +Constructs a WCloud. + +.. ocv:function:: WCloud(InputArray cloud, InputArray colors) + + :param cloud: Set of points which can be of type: ``CV_32FC3``, ``CV_32FC4``, ``CV_64FC3``, ``CV_64FC4``. + :param colors: Set of colors. It has to be of the same size with cloud. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + +.. ocv:function:: WCloud(InputArray cloud, const Color &color = Color::white()) + + :param cloud: Set of points which can be of type: ``CV_32FC3``, ``CV_32FC4``, ``CV_64FC3``, ``CV_64FC4``. + :param color: A single :ocv:class:`Color` for the whole cloud. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + +.. note:: In case there are four channels in the cloud, fourth channel is ignored. + +viz::WCloudCollection +--------------------- +.. ocv:class:: WCloudCollection + +This 3D Widget defines a collection of clouds. :: + + class CV_EXPORTS WCloudCollection : public Widget3D + { + public: + WCloudCollection(); + + //! Each point in cloud is mapped to a color in colors + void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()); + //! All points in cloud have the same color + void addCloud(InputArray cloud, const Color &color = Color::white(), Affine3d &pose = Affine3d::Identity()); + }; + +viz::WCloudCollection::WCloudCollection +--------------------------------------- +Constructs a WCloudCollection. + +.. ocv:function:: WCloudCollection() + +viz::WCloudCollection::addCloud +------------------------------- +Adds a cloud to the collection. + +.. ocv:function:: void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()) + + :param cloud: Point set which can be of type: ``CV_32FC3``, ``CV_32FC4``, ``CV_64FC3``, ``CV_64FC4``. + :param colors: Set of colors. It has to be of the same size with cloud. + :param pose: Pose of the cloud. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + +.. ocv:function:: void addCloud(InputArray cloud, const Color &color = Color::white(), const Affine3d &pose = Affine3d::Identity()) + + :param cloud: Point set which can be of type: ``CV_32FC3``, ``CV_32FC4``, ``CV_64FC3``, ``CV_64FC4``. + :param colors: A single :ocv:class:`Color` for the whole cloud. + :param pose: Pose of the cloud. + + Points in the cloud belong to mask when they are set to (NaN, NaN, NaN). + +.. note:: In case there are four channels in the cloud, fourth channel is ignored. + +viz::WCloudNormals +------------------ +.. ocv:class:: WCloudNormals + +This 3D Widget represents normals of a point cloud. :: + + class CV_EXPORTS WCloudNormals : public Widget3D + { + public: + WCloudNormals(InputArray cloud, InputArray normals, int level = 100, double scale = 0.02f, const Color &color = Color::white()); + }; + +viz::WCloudNormals::WCloudNormals +--------------------------------- +Constructs a WCloudNormals. + +.. ocv:function:: WCloudNormals(InputArray cloud, InputArray normals, int level = 100, double scale = 0.02f, const Color &color = Color::white()) + + :param cloud: Point set which can be of type: ``CV_32FC3``, ``CV_32FC4``, ``CV_64FC3``, ``CV_64FC4``. + :param normals: A set of normals that has to be of same type with cloud. + :param level: Display only every ``level`` th normal. + :param scale: Scale of the arrows that represent normals. + :param color: :ocv:class:`Color` of the arrows that represent normals. + +.. note:: In case there are four channels in the cloud, fourth channel is ignored. + +viz::WMesh +---------- +.. ocv:class:: WMesh + +This 3D Widget defines a mesh. :: + + class CV_EXPORTS WMesh : public Widget3D + { + public: + WMesh(const Mesh &mesh); + WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()); + }; + +viz::WMesh::WMesh +----------------- +Constructs a WMesh. + +.. ocv:function:: WMesh(const Mesh &mesh) + + :param mesh: :ocv:class:`Mesh` object that will be displayed. + +.. ocv:function:: WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()) + + :param cloud: Points of the mesh object. + :param polygons: Points of the mesh object. + :param colors: Point colors. + :param normals: Point normals. diff --git a/modules/viz/include/opencv2/viz/types.hpp b/modules/viz/include/opencv2/viz/types.hpp new file mode 100644 index 0000000000..acbece2edf --- /dev/null +++ b/modules/viz/include/opencv2/viz/types.hpp @@ -0,0 +1,236 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_TYPES_HPP__ +#define __OPENCV_VIZ_TYPES_HPP__ + +#include +#include +#include + +namespace cv +{ + namespace viz + { + class Color : public Scalar + { + public: + Color(); + Color(double gray); + Color(double blue, double green, double red); + + Color(const Scalar& color); + + static Color black(); + static Color blue(); + static Color green(); + static Color cyan(); + + static Color red(); + static Color magenta(); + static Color yellow(); + static Color white(); + + static Color gray(); + + static Color mlab(); + + static Color navy(); + static Color olive(); + static Color maroon(); + static Color teal(); + static Color rose(); + static Color azure(); + static Color lime(); + static Color gold(); + static Color brown(); + static Color orange(); + static Color chartreuse(); + static Color orange_red(); + static Color purple(); + static Color indigo(); + + static Color pink(); + static Color cherry(); + static Color bluberry(); + static Color raspberry(); + static Color silver(); + static Color violet(); + static Color apricot(); + static Color turquoise(); + static Color celestial_blue(); + static Color amethyst(); + + static Color not_set(); + }; + + class CV_EXPORTS Mesh + { + public: + Mat cloud, colors, normals; + + //! Raw integer list of the form: (n,id1,id2,...,idn, n,id1,id2,...,idn, ...) + //! where n is the number of points in the poligon, and id is a zero-offset index into an associated cloud. + Mat polygons; + + Mat texture, tcoords; + + //! Loads mesh from a given ply file (no texture load support for now) + static Mesh load(const String& file); + }; + + class CV_EXPORTS Camera + { + public: + Camera(double fx, double fy, double cx, double cy, const Size &window_size); + explicit Camera(const Vec2d &fov, const Size &window_size); + explicit Camera(const Matx33d &K, const Size &window_size); + explicit Camera(const Matx44d &proj, const Size &window_size); + + const Vec2d & getClip() const { return clip_; } + void setClip(const Vec2d &clip) { clip_ = clip; } + + const Size & getWindowSize() const { return window_size_; } + void setWindowSize(const Size &window_size); + + const Vec2d& getFov() const { return fov_; } + void setFov(const Vec2d& fov) { fov_ = fov; } + + const Vec2d& getPrincipalPoint() const { return principal_point_; } + const Vec2d& getFocalLength() const { return focal_; } + + void computeProjectionMatrix(Matx44d &proj) const; + + static Camera KinectCamera(const Size &window_size); + + private: + void init(double fx, double fy, double cx, double cy, const Size &window_size); + + Vec2d clip_; + Vec2d fov_; + Size window_size_; + Vec2d principal_point_; + Vec2d focal_; + }; + + class CV_EXPORTS KeyboardEvent + { + public: + enum { NONE = 0, ALT = 1, CTRL = 2, SHIFT = 4 }; + enum Action { KEY_UP = 0, KEY_DOWN = 1 }; + + KeyboardEvent(Action action, const String& symbol, unsigned char code, int modifiers); + + Action action; + String symbol; + unsigned char code; + int modifiers; + }; + + class CV_EXPORTS MouseEvent + { + public: + enum Type { MouseMove = 1, MouseButtonPress, MouseButtonRelease, MouseScrollDown, MouseScrollUp, MouseDblClick } ; + enum MouseButton { NoButton = 0, LeftButton, MiddleButton, RightButton, VScroll } ; + + MouseEvent(const Type& type, const MouseButton& button, const Point& pointer, int modifiers); + + Type type; + MouseButton button; + Point pointer; + int modifiers; + }; + } /* namespace viz */ +} /* namespace cv */ + +////////////////////////////////////////////////////////////////////////////////////////////////////// +/// cv::viz::Color + +inline cv::viz::Color::Color() : Scalar(0, 0, 0) {} +inline cv::viz::Color::Color(double _gray) : Scalar(_gray, _gray, _gray) {} +inline cv::viz::Color::Color(double _blue, double _green, double _red) : Scalar(_blue, _green, _red) {} +inline cv::viz::Color::Color(const Scalar& color) : Scalar(color) {} + +inline cv::viz::Color cv::viz::Color::black() { return Color( 0, 0, 0); } +inline cv::viz::Color cv::viz::Color::green() { return Color( 0, 255, 0); } +inline cv::viz::Color cv::viz::Color::blue() { return Color(255, 0, 0); } +inline cv::viz::Color cv::viz::Color::cyan() { return Color(255, 255, 0); } +inline cv::viz::Color cv::viz::Color::red() { return Color( 0, 0, 255); } +inline cv::viz::Color cv::viz::Color::yellow() { return Color( 0, 255, 255); } +inline cv::viz::Color cv::viz::Color::magenta() { return Color(255, 0, 255); } +inline cv::viz::Color cv::viz::Color::white() { return Color(255, 255, 255); } +inline cv::viz::Color cv::viz::Color::gray() { return Color(128, 128, 128); } + +inline cv::viz::Color cv::viz::Color::mlab() { return Color(255, 128, 128); } + +inline cv::viz::Color cv::viz::Color::navy() { return Color(0, 0, 128); } +inline cv::viz::Color cv::viz::Color::olive() { return Color(0, 128, 128); } +inline cv::viz::Color cv::viz::Color::maroon() { return Color(0, 0, 128); } +inline cv::viz::Color cv::viz::Color::teal() { return Color(128, 128, 0); } +inline cv::viz::Color cv::viz::Color::rose() { return Color(128, 0, 255); } +inline cv::viz::Color cv::viz::Color::azure() { return Color(255, 128, 0); } +inline cv::viz::Color cv::viz::Color::lime() { return Color(0, 255, 191); } +inline cv::viz::Color cv::viz::Color::gold() { return Color(0, 215, 255); } +inline cv::viz::Color cv::viz::Color::brown() { return Color(0, 75, 150); } +inline cv::viz::Color cv::viz::Color::orange() { return Color(0, 165, 255); } +inline cv::viz::Color cv::viz::Color::chartreuse() { return Color(0, 255, 128); } +inline cv::viz::Color cv::viz::Color::orange_red() { return Color(0, 69, 255); } +inline cv::viz::Color cv::viz::Color::purple() { return Color(128, 0, 128); } +inline cv::viz::Color cv::viz::Color::indigo() { return Color(130, 0, 75); } + +inline cv::viz::Color cv::viz::Color::pink() { return Color(203, 192, 255); } +inline cv::viz::Color cv::viz::Color::cherry() { return Color( 99, 29, 222); } +inline cv::viz::Color cv::viz::Color::bluberry() { return Color(247, 134, 79); } +inline cv::viz::Color cv::viz::Color::raspberry() { return Color( 92, 11, 227); } +inline cv::viz::Color cv::viz::Color::silver() { return Color(192, 192, 192); } +inline cv::viz::Color cv::viz::Color::violet() { return Color(226, 43, 138); } +inline cv::viz::Color cv::viz::Color::apricot() { return Color(177, 206, 251); } +inline cv::viz::Color cv::viz::Color::turquoise() { return Color(208, 224, 64); } +inline cv::viz::Color cv::viz::Color::celestial_blue() { return Color(208, 151, 73); } +inline cv::viz::Color cv::viz::Color::amethyst() { return Color(204, 102, 153); } + +inline cv::viz::Color cv::viz::Color::not_set() { return Color(-1, -1, -1); } + +#endif diff --git a/modules/viz/include/opencv2/viz/viz3d.hpp b/modules/viz/include/opencv2/viz/viz3d.hpp new file mode 100644 index 0000000000..1a137bcfb3 --- /dev/null +++ b/modules/viz/include/opencv2/viz/viz3d.hpp @@ -0,0 +1,131 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_VIZ3D_HPP__ +#define __OPENCV_VIZ_VIZ3D_HPP__ + +#if !defined YES_I_AGREE_THAT_VIZ_API_IS_NOT_STABLE_NOW_AND_BINARY_COMPARTIBILITY_WONT_BE_SUPPORTED && !defined CVAPI_EXPORTS + //#error "Viz is in beta state now. Please define macro above to use it" +#endif + +#include +#include +#include + +namespace cv +{ + namespace viz + { + class CV_EXPORTS Viz3d + { + public: + typedef cv::viz::Color Color; + typedef void (*KeyboardCallback)(const KeyboardEvent&, void*); + typedef void (*MouseCallback)(const MouseEvent&, void*); + + Viz3d(const String& window_name = String()); + Viz3d(const Viz3d&); + Viz3d& operator=(const Viz3d&); + ~Viz3d(); + + void showWidget(const String &id, const Widget &widget, const Affine3d &pose = Affine3d::Identity()); + void removeWidget(const String &id); + Widget getWidget(const String &id) const; + void removeAllWidgets(); + + void showImage(InputArray image, const Size& window_size = Size(-1, -1)); + + void setWidgetPose(const String &id, const Affine3d &pose); + void updateWidgetPose(const String &id, const Affine3d &pose); + Affine3d getWidgetPose(const String &id) const; + + void setCamera(const Camera &camera); + Camera getCamera() const; + Affine3d getViewerPose(); + void setViewerPose(const Affine3d &pose); + + void resetCameraViewpoint(const String &id); + void resetCamera(); + + void convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord); + void converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction); + + Size getWindowSize() const; + void setWindowSize(const Size &window_size); + String getWindowName() const; + void saveScreenshot(const String &file); + void setWindowPosition(const Point& window_position); + void setFullScreen(bool mode = true); + void setBackgroundColor(const Color& color = Color::black(), const Color& color2 = Color::not_set()); + void setBackgroundTexture(InputArray image = noArray()); + void setBackgroundMeshLab(); + + void spin(); + void spinOnce(int time = 1, bool force_redraw = false); + bool wasStopped() const; + void close(); + + void registerKeyboardCallback(KeyboardCallback callback, void* cookie = 0); + void registerMouseCallback(MouseCallback callback, void* cookie = 0); + + void setRenderingProperty(const String &id, int property, double value); + double getRenderingProperty(const String &id, int property); + + void setRepresentation(int representation); + private: + + struct VizImpl; + VizImpl* impl_; + + void create(const String &window_name); + void release(); + + friend class VizStorage; + }; + + } /* namespace viz */ +} /* namespace cv */ + +#endif diff --git a/modules/viz/include/opencv2/viz/vizcore.hpp b/modules/viz/include/opencv2/viz/vizcore.hpp new file mode 100644 index 0000000000..bf44a2c039 --- /dev/null +++ b/modules/viz/include/opencv2/viz/vizcore.hpp @@ -0,0 +1,127 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_HPP__ +#define __OPENCV_VIZ_HPP__ + +#include +#include +#include + +namespace cv +{ + namespace viz + { + //! takes coordiante frame data and builds transfrom to global coordinate frame + CV_EXPORTS Affine3d makeTransformToGlobal(const Vec3d& axis_x, const Vec3d& axis_y, const Vec3d& axis_z, const Vec3d& origin = Vec3d::all(0)); + + //! constructs camera pose from position, focal_point and up_vector (see gluLookAt() for more infromation) + CV_EXPORTS Affine3d makeCameraPose(const Vec3d& position, const Vec3d& focal_point, const Vec3d& y_dir); + + //! retrieves a window by its name. If no window with such name, then it creates new. + CV_EXPORTS Viz3d getWindowByName(const String &window_name); + + //! Unregisters all Viz windows from internal database. After it 'getWindowByName()' will create new windows instead getting existing from the database. + CV_EXPORTS void unregisterAllWindows(); + + //! Displays image in specified window + CV_EXPORTS Viz3d imshow(const String& window_name, InputArray image, const Size& window_size = Size(-1, -1)); + + //! checks float value for Nan + inline bool isNan(float x) + { + unsigned int *u = reinterpret_cast(&x); + return ((u[0] & 0x7f800000) == 0x7f800000) && (u[0] & 0x007fffff); + } + + //! checks double value for Nan + inline bool isNan(double x) + { + unsigned int *u = reinterpret_cast(&x); + return (u[1] & 0x7ff00000) == 0x7ff00000 && (u[0] != 0 || (u[1] & 0x000fffff) != 0); + } + + //! checks vectors for Nans + template inline bool isNan(const Vec<_Tp, cn>& v) + { return isNan(v.val[0]) || isNan(v.val[1]) || isNan(v.val[2]); } + + //! checks point for Nans + template inline bool isNan(const Point3_<_Tp>& p) + { return isNan(p.x) || isNan(p.y) || isNan(p.z); } + + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Read/write clouds. Supported formats: ply, xyz, obj and stl (readonly) + + CV_EXPORTS void writeCloud(const String& file, InputArray cloud, InputArray colors = noArray(), InputArray normals = noArray(), bool binary = false); + CV_EXPORTS Mat readCloud (const String& file, OutputArray colors = noArray(), OutputArray normals = noArray()); + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Reads mesh. Only ply format is supported now and no texture load support + + CV_EXPORTS Mesh readMesh(const String& file); + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Read/write poses and trajectories + + CV_EXPORTS bool readPose(const String& file, Affine3d& pose, const String& tag = "pose"); + CV_EXPORTS void writePose(const String& file, const Affine3d& pose, const String& tag = "pose"); + + //! takes vector> with T = float/dobule and writes to a sequence of files with given filename format + CV_EXPORTS void writeTrajectory(InputArray traj, const String& files_format = "pose%05d.xml", int start = 0, const String& tag = "pose"); + + //! takes vector> with T = float/dobule and loads poses from sequence of files + CV_EXPORTS void readTrajectory(OutputArray traj, const String& files_format = "pose%05d.xml", int start = 0, int end = INT_MAX, const String& tag = "pose"); + + + /////////////////////////////////////////////////////////////////////////////////////////////// + /// Computing normals for mesh + + CV_EXPORTS void computeNormals(const Mesh& mesh, OutputArray normals); + + } /* namespace viz */ +} /* namespace cv */ + +#endif /* __OPENCV_VIZ_HPP__ */ diff --git a/modules/viz/include/opencv2/viz/widget_accessor.hpp b/modules/viz/include/opencv2/viz/widget_accessor.hpp new file mode 100644 index 0000000000..734f6ce559 --- /dev/null +++ b/modules/viz/include/opencv2/viz/widget_accessor.hpp @@ -0,0 +1,69 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_WIDGET_ACCESSOR_HPP__ +#define __OPENCV_VIZ_WIDGET_ACCESSOR_HPP__ + +#include +#include +#include + +namespace cv +{ + namespace viz + { + class Widget; + + //The class is only that depends on VTK in its interface. + //It is indended for those users who want to develop own widgets system using VTK library API. + struct CV_EXPORTS WidgetAccessor + { + static vtkSmartPointer getProp(const Widget &widget); + static void setProp(Widget &widget, vtkSmartPointer prop); + }; + } +} + +#endif diff --git a/modules/viz/include/opencv2/viz/widgets.hpp b/modules/viz/include/opencv2/viz/widgets.hpp new file mode 100644 index 0000000000..2c49b9d0e2 --- /dev/null +++ b/modules/viz/include/opencv2/viz/widgets.hpp @@ -0,0 +1,396 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_WIDGETS_HPP__ +#define __OPENCV_VIZ_WIDGETS_HPP__ + +#include + +namespace cv +{ + namespace viz + { + ///////////////////////////////////////////////////////////////////////////// + /// Widget rendering properties + enum RenderingProperties + { + POINT_SIZE, + OPACITY, + LINE_WIDTH, + FONT_SIZE, + REPRESENTATION, + IMMEDIATE_RENDERING, + SHADING + }; + + enum RepresentationValues + { + REPRESENTATION_POINTS, + REPRESENTATION_WIREFRAME, + REPRESENTATION_SURFACE + }; + + enum ShadingValues + { + SHADING_FLAT, + SHADING_GOURAUD, + SHADING_PHONG + }; + + ///////////////////////////////////////////////////////////////////////////// + /// The base class for all widgets + class CV_EXPORTS Widget + { + public: + Widget(); + Widget(const Widget& other); + Widget& operator=(const Widget& other); + ~Widget(); + + //! Create a widget directly from ply file + static Widget fromPlyFile(const String &file_name); + + //! Rendering properties of this particular widget + void setRenderingProperty(int property, double value); + double getRenderingProperty(int property) const; + + //! Casting between widgets + template _W cast(); + private: + class Impl; + Impl *impl_; + friend struct WidgetAccessor; + }; + + ///////////////////////////////////////////////////////////////////////////// + /// The base class for all 3D widgets + class CV_EXPORTS Widget3D : public Widget + { + public: + Widget3D() {} + + //! widget position manipulation, i.e. place where it is rendered + void setPose(const Affine3d &pose); + void updatePose(const Affine3d &pose); + Affine3d getPose() const; + + //! update internal widget data, i.e. points, normals, etc. + void applyTransform(const Affine3d &transform); + + void setColor(const Color &color); + + }; + + ///////////////////////////////////////////////////////////////////////////// + /// The base class for all 2D widgets + class CV_EXPORTS Widget2D : public Widget + { + public: + Widget2D() {} + + void setColor(const Color &color); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Simple widgets + + class CV_EXPORTS WLine : public Widget3D + { + public: + WLine(const Point3d &pt1, const Point3d &pt2, const Color &color = Color::white()); + }; + + class CV_EXPORTS WPlane : public Widget3D + { + public: + //! created default plane with center point at origin and normal oriented along z-axis + WPlane(const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()); + + //! repositioned plane + WPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, + const Size2d& size = Size2d(1.0, 1.0), const Color &color = Color::white()); + }; + + class CV_EXPORTS WSphere : public Widget3D + { + public: + WSphere(const cv::Point3d ¢er, double radius, int sphere_resolution = 10, const Color &color = Color::white()); + }; + + class CV_EXPORTS WArrow : public Widget3D + { + public: + WArrow(const Point3d& pt1, const Point3d& pt2, double thickness = 0.03, const Color &color = Color::white()); + }; + + class CV_EXPORTS WCircle : public Widget3D + { + public: + //! creates default planar circle centred at origin with plane normal along z-axis + WCircle(double radius, double thickness = 0.01, const Color &color = Color::white()); + + //! creates repositioned circle + WCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness = 0.01, const Color &color = Color::white()); + }; + + class CV_EXPORTS WCone : public Widget3D + { + public: + //! create default cone, oriented along x-axis with center of its base located at origin + WCone(double length, double radius, int resolution = 6.0, const Color &color = Color::white()); + + //! creates repositioned cone + WCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6.0, const Color &color = Color::white()); + }; + + class CV_EXPORTS WCylinder : public Widget3D + { + public: + WCylinder(const Point3d& axis_point1, const Point3d& axis_point2, double radius, int numsides = 30, const Color &color = Color::white()); + }; + + class CV_EXPORTS WCube : public Widget3D + { + public: + WCube(const Point3d& min_point = Vec3d::all(-0.5), const Point3d& max_point = Vec3d::all(0.5), + bool wire_frame = true, const Color &color = Color::white()); + }; + + class CV_EXPORTS WPolyLine : public Widget3D + { + public: + WPolyLine(InputArray points, const Color &color = Color::white()); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Text and image widgets + + class CV_EXPORTS WText : public Widget2D + { + public: + WText(const String &text, const Point &pos, int font_size = 20, const Color &color = Color::white()); + + void setText(const String &text); + String getText() const; + }; + + class CV_EXPORTS WText3D : public Widget3D + { + public: + //! creates text label in 3D. If face_camera = false, text plane normal is oriented along z-axis. Use widget pose to orient it properly + WText3D(const String &text, const Point3d &position, double text_scale = 1., bool face_camera = true, const Color &color = Color::white()); + + void setText(const String &text); + String getText() const; + }; + + class CV_EXPORTS WImageOverlay : public Widget2D + { + public: + WImageOverlay(InputArray image, const Rect &rect); + void setImage(InputArray image); + }; + + class CV_EXPORTS WImage3D : public Widget3D + { + public: + //! Creates 3D image in a plane centered at the origin with normal orientaion along z-axis, + //! image x- and y-axes are oriented along x- and y-axes of 3d world + WImage3D(InputArray image, const Size2d &size); + + //! Creates 3D image at a given position, pointing in the direction of the normal, and having the up_vector orientation + WImage3D(InputArray image, const Size2d &size, const Vec3d ¢er, const Vec3d &normal, const Vec3d &up_vector); + + void setImage(InputArray image); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Compond widgets + + class CV_EXPORTS WCoordinateSystem : public Widget3D + { + public: + WCoordinateSystem(double scale = 1.0); + }; + + class CV_EXPORTS WGrid : public Widget3D + { + public: + //! Creates grid at the origin and normal oriented along z-axis + WGrid(const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const Color &color = Color::white()); + + //! Creates repositioned grid + WGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, + const Vec2i &cells = Vec2i::all(10), const Vec2d &cells_spacing = Vec2d::all(1.0), const Color &color = Color::white()); + }; + + class CV_EXPORTS WCameraPosition : public Widget3D + { + public: + //! Creates camera coordinate frame (axes) at the origin + WCameraPosition(double scale = 1.0); + //! Creates frustum based on the intrinsic marix K at the origin + WCameraPosition(const Matx33d &K, double scale = 1.0, const Color &color = Color::white()); + //! Creates frustum based on the field of view at the origin + WCameraPosition(const Vec2d &fov, double scale = 1.0, const Color &color = Color::white()); + //! Creates frustum and display given image at the far plane + WCameraPosition(const Matx33d &K, InputArray image, double scale = 1.0, const Color &color = Color::white()); + //! Creates frustum and display given image at the far plane + WCameraPosition(const Vec2d &fov, InputArray image, double scale = 1.0, const Color &color = Color::white()); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Trajectories + + class CV_EXPORTS WTrajectory : public Widget3D + { + public: + enum {FRAMES = 1, PATH = 2, BOTH = FRAMES + PATH }; + + //! Takes vector> and displays trajectory of the given path either by coordinate frames or polyline + WTrajectory(InputArray path, int display_mode = WTrajectory::PATH, double scale = 1.0, const Color &color = Color::white()); + }; + + class CV_EXPORTS WTrajectoryFrustums : public Widget3D + { + public: + //! Takes vector> and displays trajectory of the given path by frustums + WTrajectoryFrustums(InputArray path, const Matx33d &K, double scale = 1., const Color &color = Color::white()); + + //! Takes vector> and displays trajectory of the given path by frustums + WTrajectoryFrustums(InputArray path, const Vec2d &fov, double scale = 1., const Color &color = Color::white()); + }; + + class CV_EXPORTS WTrajectorySpheres: public Widget3D + { + public: + //! Takes vector> and displays trajectory of the given path + WTrajectorySpheres(InputArray path, double line_length = 0.05, double radius = 0.007, + const Color &from = Color::red(), const Color &to = Color::white()); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Clouds + + class CV_EXPORTS WCloud: public Widget3D + { + public: + //! Each point in cloud is mapped to a color in colors + WCloud(InputArray cloud, InputArray colors); + //! All points in cloud have the same color + WCloud(InputArray cloud, const Color &color = Color::white()); + }; + + class CV_EXPORTS WPaintedCloud: public Widget3D + { + public: + //! Paint cloud with default gradient between cloud bounds points + WPaintedCloud(InputArray cloud); + + //! Paint cloud with default gradient between given points + WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2); + + //! Paint cloud with gradient specified by given colors between given points + WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2, const Color& c1, const Color c2); + }; + + class CV_EXPORTS WCloudCollection : public Widget3D + { + public: + WCloudCollection(); + + //! Each point in cloud is mapped to a color in colors + void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()); + //! All points in cloud have the same color + void addCloud(InputArray cloud, const Color &color = Color::white(), const Affine3d &pose = Affine3d::Identity()); + }; + + class CV_EXPORTS WCloudNormals : public Widget3D + { + public: + WCloudNormals(InputArray cloud, InputArray normals, int level = 64, double scale = 0.1, const Color &color = Color::white()); + }; + + class CV_EXPORTS WMesh : public Widget3D + { + public: + WMesh(const Mesh &mesh); + WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()); + }; + + ///////////////////////////////////////////////////////////////////////////// + /// Utility exports + + template<> CV_EXPORTS Widget2D Widget::cast(); + template<> CV_EXPORTS Widget3D Widget::cast(); + template<> CV_EXPORTS WLine Widget::cast(); + template<> CV_EXPORTS WPlane Widget::cast(); + template<> CV_EXPORTS WSphere Widget::cast(); + template<> CV_EXPORTS WCylinder Widget::cast(); + template<> CV_EXPORTS WArrow Widget::cast(); + template<> CV_EXPORTS WCircle Widget::cast(); + template<> CV_EXPORTS WCone Widget::cast(); + template<> CV_EXPORTS WCube Widget::cast(); + template<> CV_EXPORTS WCoordinateSystem Widget::cast(); + template<> CV_EXPORTS WPolyLine Widget::cast(); + template<> CV_EXPORTS WGrid Widget::cast(); + template<> CV_EXPORTS WText3D Widget::cast(); + template<> CV_EXPORTS WText Widget::cast(); + template<> CV_EXPORTS WImageOverlay Widget::cast(); + template<> CV_EXPORTS WImage3D Widget::cast(); + template<> CV_EXPORTS WCameraPosition Widget::cast(); + template<> CV_EXPORTS WTrajectory Widget::cast(); + template<> CV_EXPORTS WTrajectoryFrustums Widget::cast(); + template<> CV_EXPORTS WTrajectorySpheres Widget::cast(); + template<> CV_EXPORTS WCloud Widget::cast(); + template<> CV_EXPORTS WPaintedCloud Widget::cast(); + template<> CV_EXPORTS WCloudCollection Widget::cast(); + template<> CV_EXPORTS WCloudNormals Widget::cast(); + template<> CV_EXPORTS WMesh Widget::cast(); + + } /* namespace viz */ +} /* namespace cv */ + +#endif diff --git a/modules/viz/src/clouds.cpp b/modules/viz/src/clouds.cpp new file mode 100644 index 0000000000..4b84e8e9e1 --- /dev/null +++ b/modules/viz/src/clouds.cpp @@ -0,0 +1,441 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Point Cloud Widget implementation + +cv::viz::WCloud::WCloud(InputArray cloud, InputArray colors) +{ + CV_Assert(!cloud.empty() && !colors.empty()); + + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetColorCloud(cloud, colors); + cloud_source->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, cloud_source->GetOutput()); + mapper->SetScalarModeToUsePointData(); + mapper->ImmediateModeRenderingOff(); + mapper->SetScalarRange(0, 255); + mapper->ScalarVisibilityOn(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCloud::WCloud(InputArray cloud, const Color &color) +{ + WCloud cloud_widget(cloud, Mat(cloud.size(), CV_8UC3, color)); + *this = cloud_widget; +} + + +template<> cv::viz::WCloud cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Painted Cloud Widget implementation + +cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud) +{ + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetCloud(cloud); + cloud_source->Update(); + + Vec6d bounds(cloud_source->GetOutput()->GetPoints()->GetBounds()); + + vtkSmartPointer elevation = vtkSmartPointer::New(); + elevation->SetInputConnection(cloud_source->GetOutputPort()); + elevation->SetLowPoint(bounds[0], bounds[2], bounds[4]); + elevation->SetHighPoint(bounds[1], bounds[3], bounds[5]); + elevation->SetScalarRange(0.0, 1.0); + elevation->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, vtkPolyData::SafeDownCast(elevation->GetOutput())); + mapper->ImmediateModeRenderingOff(); + mapper->ScalarVisibilityOn(); + mapper->SetColorModeToMapScalars(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2) +{ + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetCloud(cloud); + + vtkSmartPointer elevation = vtkSmartPointer::New(); + elevation->SetInputConnection(cloud_source->GetOutputPort()); + elevation->SetLowPoint(p1.x, p1.y, p1.z); + elevation->SetHighPoint(p2.x, p2.y, p2.z); + elevation->SetScalarRange(0.0, 1.0); + elevation->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, vtkPolyData::SafeDownCast(elevation->GetOutput())); + mapper->ImmediateModeRenderingOff(); + mapper->ScalarVisibilityOn(); + mapper->SetColorModeToMapScalars(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WPaintedCloud::WPaintedCloud(InputArray cloud, const Point3d& p1, const Point3d& p2, const Color& c1, const Color c2) +{ + vtkSmartPointer cloud_source = vtkSmartPointer::New(); + cloud_source->SetCloud(cloud); + + vtkSmartPointer elevation = vtkSmartPointer::New(); + elevation->SetInputConnection(cloud_source->GetOutputPort()); + elevation->SetLowPoint(p1.x, p1.y, p1.z); + elevation->SetHighPoint(p2.x, p2.y, p2.z); + elevation->SetScalarRange(0.0, 1.0); + elevation->Update(); + + Color vc1 = vtkcolor(c1), vc2 = vtkcolor(c2); + vtkSmartPointer color_transfer = vtkSmartPointer::New(); + color_transfer->SetColorSpaceToRGB(); + color_transfer->AddRGBPoint(0.0, vc1[0], vc1[1], vc1[2]); + color_transfer->AddRGBPoint(1.0, vc2[0], vc2[1], vc2[2]); + color_transfer->SetScaleToLinear(); + color_transfer->Build(); + + //if in future some need to replace color table with real scalars, then this can be done usine next calls: + //vtkDataArray *float_scalars = vtkPolyData::SafeDownCast(elevation->GetOutput())->GetPointData()->GetArray("Elevation"); + //vtkSmartPointer polydata = cloud_source->GetOutput(); + //polydata->GetPointData()->SetScalars(color_transfer->MapScalars(float_scalars, VTK_COLOR_MODE_DEFAULT, 0)); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, vtkPolyData::SafeDownCast(elevation->GetOutput())); + mapper->ImmediateModeRenderingOff(); + mapper->ScalarVisibilityOn(); + mapper->SetColorModeToMapScalars(); + mapper->SetLookupTable(color_transfer); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WPaintedCloud cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Cloud Collection Widget implementation + +cv::viz::WCloudCollection::WCloudCollection() +{ + // Just create the actor + vtkSmartPointer actor = vtkSmartPointer::New(); + WidgetAccessor::setProp(*this, actor); +} + +void cv::viz::WCloudCollection::addCloud(InputArray cloud, InputArray colors, const Affine3d &pose) +{ + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetColorCloud(cloud, colors); + + vtkSmartPointer polydata = VtkUtils::TransformPolydata(source->GetOutputPort(), pose); + + vtkSmartPointer actor = vtkLODActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Incompatible widget type." && actor); + + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + if (!mapper) + { + // This is the first cloud + mapper = vtkSmartPointer::New(); + mapper->SetScalarRange(0, 255); + mapper->SetScalarModeToUsePointData(); + mapper->ScalarVisibilityOn(); + mapper->ImmediateModeRenderingOff(); + VtkUtils::SetInputData(mapper, polydata); + + actor->SetNumberOfCloudPoints(std::max(1, polydata->GetNumberOfPoints()/10)); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + return; + } + + vtkPolyData *currdata = vtkPolyData::SafeDownCast(mapper->GetInput()); + CV_Assert("Cloud Widget without data" && currdata); + + vtkSmartPointer append_filter = vtkSmartPointer::New(); + VtkUtils::AddInputData(append_filter, currdata); + VtkUtils::AddInputData(append_filter, polydata); + append_filter->Update(); + + VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + + actor->SetNumberOfCloudPoints(std::max(1, actor->GetNumberOfCloudPoints() + polydata->GetNumberOfPoints()/10)); +} + +void cv::viz::WCloudCollection::addCloud(InputArray cloud, const Color &color, const Affine3d &pose) +{ + addCloud(cloud, Mat(cloud.size(), CV_8UC3, color), pose); +} + +template<> cv::viz::WCloudCollection cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Cloud Normals Widget implementation + +cv::viz::WCloudNormals::WCloudNormals(InputArray _cloud, InputArray _normals, int level, double scale, const Color &color) +{ + Mat cloud = _cloud.getMat(); + Mat normals = _normals.getMat(); + + CV_Assert(cloud.type() == CV_32FC3 || cloud.type() == CV_64FC3 || cloud.type() == CV_32FC4 || cloud.type() == CV_64FC4); + CV_Assert(cloud.size() == normals.size() && cloud.type() == normals.type()); + + int sqlevel = (int)std::sqrt((double)level); + int ystep = (cloud.cols > 1 && cloud.rows > 1) ? sqlevel : 1; + int xstep = (cloud.cols > 1 && cloud.rows > 1) ? sqlevel : level; + + vtkSmartPointer points = vtkSmartPointer::New(); + points->SetDataType(cloud.depth() == CV_32F ? VTK_FLOAT : VTK_DOUBLE); + + vtkSmartPointer lines = vtkSmartPointer::New(); + + int s_chs = cloud.channels(); + int n_chs = normals.channels(); + int total = 0; + + for(int y = 0; y < cloud.rows; y += ystep) + { + if (cloud.depth() == CV_32F) + { + const float *srow = cloud.ptr(y); + const float *send = srow + cloud.cols * s_chs; + const float *nrow = normals.ptr(y); + + for (; srow < send; srow += xstep * s_chs, nrow += xstep * n_chs) + if (!isNan(srow) && !isNan(nrow)) + { + Vec3f endp = Vec3f(srow) + Vec3f(nrow) * (float)scale; + + points->InsertNextPoint(srow); + points->InsertNextPoint(endp.val); + + lines->InsertNextCell(2); + lines->InsertCellPoint(total++); + lines->InsertCellPoint(total++); + } + } + else + { + const double *srow = cloud.ptr(y); + const double *send = srow + cloud.cols * s_chs; + const double *nrow = normals.ptr(y); + + for (; srow < send; srow += xstep * s_chs, nrow += xstep * n_chs) + if (!isNan(srow) && !isNan(nrow)) + { + Vec3d endp = Vec3d(srow) + Vec3d(nrow) * (double)scale; + + points->InsertNextPoint(srow); + points->InsertNextPoint(endp.val); + + lines->InsertNextCell(2); + lines->InsertCellPoint(total++); + lines->InsertCellPoint(total++); + } + } + } + + vtkSmartPointer polyData = vtkSmartPointer::New(); + polyData->SetPoints(points); + polyData->SetLines(lines); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetColorModeToMapScalars(); + mapper->SetScalarModeToUsePointData(); + VtkUtils::SetInputData(mapper, polyData); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +template<> cv::viz::WCloudNormals cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Mesh Widget implementation + +cv::viz::WMesh::WMesh(const Mesh &mesh) +{ + CV_Assert(mesh.cloud.rows == 1 && mesh.polygons.type() == CV_32SC1); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetColorCloudNormalsTCoords(mesh.cloud, mesh.colors, mesh.normals, mesh.tcoords); + source->Update(); + + Mat lookup_buffer(1, mesh.cloud.total(), CV_32SC1); + int *lookup = lookup_buffer.ptr(); + for(int y = 0, index = 0; y < mesh.cloud.rows; ++y) + { + int s_chs = mesh.cloud.channels(); + + if (mesh.cloud.depth() == CV_32F) + { + const float* srow = mesh.cloud.ptr(y); + const float* send = srow + mesh.cloud.cols * s_chs; + + for (; srow != send; srow += s_chs, ++lookup) + if (!isNan(srow[0]) && !isNan(srow[1]) && !isNan(srow[2])) + *lookup = index++; + } + + if (mesh.cloud.depth() == CV_64F) + { + const double* srow = mesh.cloud.ptr(y); + const double* send = srow + mesh.cloud.cols * s_chs; + + for (; srow != send; srow += s_chs, ++lookup) + if (!isNan(srow[0]) && !isNan(srow[1]) && !isNan(srow[2])) + *lookup = index++; + } + } + lookup = lookup_buffer.ptr(); + + vtkSmartPointer polydata = source->GetOutput(); + polydata->SetVerts(0); + + const int * polygons = mesh.polygons.ptr(); + vtkSmartPointer cell_array = vtkSmartPointer::New(); + + int idx = 0; + size_t polygons_size = mesh.polygons.total(); + for (size_t i = 0; i < polygons_size; ++idx) + { + int n_points = polygons[i++]; + + cell_array->InsertNextCell(n_points); + for (int j = 0; j < n_points; ++j, ++idx) + cell_array->InsertCellPoint(lookup[polygons[i++]]); + } + cell_array->GetData()->SetNumberOfValues(idx); + cell_array->Squeeze(); + polydata->SetStrips(cell_array); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetScalarModeToUsePointData(); + mapper->ImmediateModeRenderingOff(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + //actor->SetNumberOfCloudPoints(std::max(1, polydata->GetNumberOfPoints() / 10)); + actor->GetProperty()->SetRepresentationToSurface(); + actor->GetProperty()->BackfaceCullingOff(); // Backface culling is off for higher efficiency + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->EdgeVisibilityOff(); + actor->GetProperty()->ShadingOff(); + actor->SetMapper(mapper); + + if (!mesh.texture.empty()) + { + vtkSmartPointer image_source = vtkSmartPointer::New(); + image_source->SetImage(mesh.texture); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(image_source->GetOutputPort()); + actor->SetTexture(texture); + } + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WMesh::WMesh(InputArray cloud, InputArray polygons, InputArray colors, InputArray normals) +{ + Mesh mesh; + mesh.cloud = cloud.getMat(); + mesh.colors = colors.getMat(); + mesh.normals = normals.getMat(); + mesh.polygons = polygons.getMat(); + *this = WMesh(mesh); +} + +template<> CV_EXPORTS cv::viz::WMesh cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} diff --git a/modules/viz/src/interactor_style.cpp b/modules/viz/src/interactor_style.cpp new file mode 100644 index 0000000000..75003a2b66 --- /dev/null +++ b/modules/viz/src/interactor_style.cpp @@ -0,0 +1,639 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +// OpenCV Viz module is complete rewrite of +// PCL visualization module (www.pointclouds.org) +// +//M*/ + +#include "precomp.hpp" + + +namespace cv { namespace viz +{ + vtkStandardNewMacro(InteractorStyle) +}} + + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::Initialize() +{ + // Set windows size (width, height) to unknown (-1) + win_size_ = Vec2i(-1, -1); + win_pos_ = Vec2i(0, 0); + max_win_size_ = Vec2i(-1, -1); + + init_ = true; + stereo_anaglyph_mask_default_ = true; + + // Initialize the keyboard event callback as none + keyboardCallback_ = 0; + keyboard_callback_cookie_ = 0; + + // Initialize the mouse event callback as none + mouseCallback_ = 0; + mouse_callback_cookie_ = 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::saveScreenshot(const String &file) +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + vtkSmartPointer wif = vtkSmartPointer::New(); + wif->SetInput(Interactor->GetRenderWindow()); + + vtkSmartPointer snapshot_writer = vtkSmartPointer::New(); + snapshot_writer->SetInputConnection(wif->GetOutputPort()); + snapshot_writer->SetFileName(file.c_str()); + snapshot_writer->Write(); + + cout << "Screenshot successfully captured (" << file.c_str() << ")" << endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::exportScene(const String &file) +{ + vtkSmartPointer exporter; + if (file.size() > 5 && file.substr(file.size() - 5) == ".vrml") + { + exporter = vtkSmartPointer::New(); + vtkVRMLExporter::SafeDownCast(exporter)->SetFileName(file.c_str()); + } + else + { + exporter = vtkSmartPointer::New(); + vtkOBJExporter::SafeDownCast(exporter)->SetFilePrefix(file.c_str()); + } + + exporter->SetInput(Interactor->GetRenderWindow()); + exporter->Write(); + + cout << "Scene successfully exported (" << file.c_str() << ")" << endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::zoomIn() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + // Zoom in + StartDolly(); + double factor = 10.0 * 0.2 * .5; + Dolly(std::pow(1.1, factor)); + EndDolly(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::zoomOut() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + // Zoom out + StartDolly(); + double factor = 10.0 * -0.2 * .5; + Dolly(std::pow(1.1, factor)); + EndDolly(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnChar() +{ + // Make sure we ignore the same events we handle in OnKeyDown to avoid calling things twice + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + if (Interactor->GetKeyCode() >= '0' && Interactor->GetKeyCode() <= '9') + return; + + String key(Interactor->GetKeySym()); + if (key.find("XF86ZoomIn") != String::npos) + zoomIn(); + else if (key.find("XF86ZoomOut") != String::npos) + zoomOut(); + + int keymod = Interactor->GetAltKey(); + + switch (Interactor->GetKeyCode()) + { + // All of the options below simply exit + case 'h': case 'H': + case 'l': case 'L': + case 'p': case 'P': + case 'j': case 'J': + case 'c': case 'C': + case 43: // KEY_PLUS + case 45: // KEY_MINUS + case 'f': case 'F': + case 'g': case 'G': + case 'o': case 'O': + case 'u': case 'U': + case 'q': case 'Q': + { + break; + } + // S and R have a special !ALT case + case 'r': case 'R': + case 's': case 'S': + { + if (!keymod) + Superclass::OnChar(); + break; + } + default: + { + Superclass::OnChar(); + break; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie) +{ + // Register the callback function and store the user data + mouseCallback_ = callback; + mouse_callback_cookie_ = cookie; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void *cookie) +{ + // Register the callback function and store the user data + keyboardCallback_ = callback; + keyboard_callback_cookie_ = cookie; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +int cv::viz::InteractorStyle::getModifiers() +{ + int modifiers = KeyboardEvent::NONE; + + if (Interactor->GetAltKey()) + modifiers |= KeyboardEvent::ALT; + + if (Interactor->GetControlKey()) + modifiers |= KeyboardEvent::CTRL; + + if (Interactor->GetShiftKey()) + modifiers |= KeyboardEvent::SHIFT; + return modifiers; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnKeyDown() +{ + CV_Assert("Interactor style not initialized. Please call Initialize() before continuing" && init_); + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + // Save the initial windows width/height + if (win_size_[0] == -1 || win_size_[1] == -1) + win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); + + bool alt = Interactor->GetAltKey() != 0; + + std::string key(Interactor->GetKeySym()); + if (key.find("XF86ZoomIn") != std::string::npos) + zoomIn(); + else if (key.find("XF86ZoomOut") != std::string::npos) + zoomOut(); + + switch (Interactor->GetKeyCode()) + { + case 'h': case 'H': + { + std::cout << "| Help:\n" + "-------\n" + " p, P : switch to a point-based representation\n" + " w, W : switch to a wireframe-based representation (where available)\n" + " s, S : switch to a surface-based representation (where available)\n" + "\n" + " j, J : take a .PNG snapshot of the current window view\n" + " k, K : export scene to Wavefront .obj format\n" + " ALT + k, K : export scene to VRML format\n" + " c, C : display current camera/window parameters\n" + " f, F : fly to point mode, hold the key and move mouse where to fly\n" + "\n" + " e, E : exit the interactor\n" + " q, Q : stop and call VTK's TerminateApp\n" + "\n" + " +/- : increment/decrement overall point size\n" + " +/- [+ ALT] : zoom in/out \n" + "\n" + " r, R [+ ALT] : reset camera [to viewpoint = {0, 0, 0} -> center_{x, y, z}]\n" + "\n" + " ALT + s, S : turn stereo mode on/off\n" + " ALT + f, F : switch between maximized window mode and original size\n" + "\n" + << std::endl; + break; + } + + // Switch representation to points + case 'p': case 'P': + { + vtkSmartPointer ac = CurrentRenderer->GetActors(); + vtkCollectionSimpleIterator ait; + for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) + for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) + { + vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); + apart->GetProperty()->SetRepresentationToPoints(); + } + break; + } + + // Save a PNG snapshot + case 'j': case 'J': + saveScreenshot(cv::format("screenshot-%d.png", (unsigned int)time(0))); break; + + // Export scene as in obj or vrml format + case 'k': case 'K': + { + String format = alt ? "scene-%d.vrml" : "scene-%d"; + exportScene(cv::format(format.c_str(), (unsigned int)time(0))); + break; + } + + // display current camera settings/parameters + case 'c': case 'C': + { + vtkSmartPointer cam = Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->GetActiveCamera(); + + Vec2d clip(cam->GetClippingRange()); + Vec3d focal(cam->GetFocalPoint()), pos(cam->GetPosition()), view(cam->GetViewUp()); + Vec2i win_pos(Interactor->GetRenderWindow()->GetPosition()); + Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); + double angle = cam->GetViewAngle () / 180.0 * CV_PI; + + String data = cv::format("clip(%f,%f) focal(%f,%f,%f) pos(%f,%f,%f) view(%f,%f,%f) angle(%f) winsz(%d,%d) winpos(%d,%d)", + clip[0], clip[1], focal[0], focal[1], focal[2], pos[0], pos[1], pos[2], view[0], view[1], view[2], + angle, win_size[0], win_size[1], win_pos[0], win_pos[1]); + + std::cout << data.c_str() << std::endl; + + break; + } + case '=': + { + zoomIn(); + break; + } + case 43: // KEY_PLUS + { + if (alt) + zoomIn(); + else + { + vtkSmartPointer ac = CurrentRenderer->GetActors(); + vtkCollectionSimpleIterator ait; + for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) + for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) + { + vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); + float psize = apart->GetProperty()->GetPointSize(); + if (psize < 63.0f) + apart->GetProperty()->SetPointSize(psize + 1.0f); + } + } + break; + } + case 45: // KEY_MINUS + { + if (alt) + zoomOut(); + else + { + vtkSmartPointer ac = CurrentRenderer->GetActors(); + vtkCollectionSimpleIterator ait; + for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) + for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) + { + vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); + float psize = apart->GetProperty()->GetPointSize(); + if (psize > 1.0f) + apart->GetProperty()->SetPointSize(psize - 1.0f); + } + } + break; + } + // Switch between maximize and original window size + case 'f': case 'F': + { + if (alt) + { + Vec2i screen_size(Interactor->GetRenderWindow()->GetScreenSize()); + Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); + + // Is window size = max? + if (win_size == max_win_size_) + { + Interactor->GetRenderWindow()->SetSize(win_size_.val); + Interactor->GetRenderWindow()->SetPosition(win_pos_.val); + Interactor->GetRenderWindow()->Render(); + Interactor->Render(); + } + // Set to max + else + { + win_pos_ = Vec2i(Interactor->GetRenderWindow()->GetPosition()); + win_size_ = win_size; + + Interactor->GetRenderWindow()->SetSize(screen_size.val); + Interactor->GetRenderWindow()->Render(); + Interactor->Render(); + max_win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); + } + } + else + { + AnimState = VTKIS_ANIM_ON; + Interactor->GetPicker()->Pick(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1], 0.0, CurrentRenderer); + vtkSmartPointer picker = vtkAbstractPropPicker::SafeDownCast(Interactor->GetPicker()); + if (picker) + if (picker->GetPath()) + Interactor->FlyTo(CurrentRenderer, picker->GetPickPosition()); + AnimState = VTKIS_ANIM_OFF; + } + break; + } + // 's'/'S' w/out ALT + case 's': case 'S': + { + if (alt) + { + vtkSmartPointer window = Interactor->GetRenderWindow(); + if (!window->GetStereoRender()) + { + static Vec2i red_blue(4, 3), magenta_green(2, 5); + window->SetAnaglyphColorMask (stereo_anaglyph_mask_default_ ? red_blue.val : magenta_green.val); + stereo_anaglyph_mask_default_ = !stereo_anaglyph_mask_default_; + } + window->SetStereoRender(!window->GetStereoRender()); + Interactor->Render(); + } + else + Superclass::OnKeyDown(); + break; + } + + case 'o': case 'O': + { + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + cam->SetParallelProjection(!cam->GetParallelProjection()); + CurrentRenderer->Render(); + break; + } + + // Overwrite the camera reset + case 'r': case 'R': + { + if (!alt) + { + Superclass::OnKeyDown(); + break; + } + + WidgetActorMap::iterator it = widget_actor_map_->begin(); + // it might be that some actors don't have a valid transformation set -> we skip them to avoid a seg fault. + for (; it != widget_actor_map_->end(); ++it) + { + vtkProp3D * actor = vtkProp3D::SafeDownCast(it->second); + if (actor && actor->GetUserMatrix()) + break; + } + + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + + // if a valid transformation was found, use it otherwise fall back to default view point. + if (it != widget_actor_map_->end()) + { + vtkMatrix4x4* m = vtkProp3D::SafeDownCast(it->second)->GetUserMatrix(); + + cam->SetFocalPoint(m->GetElement(0, 3) - m->GetElement(0, 2), + m->GetElement(1, 3) - m->GetElement(1, 2), + m->GetElement(2, 3) - m->GetElement(2, 2)); + + cam->SetViewUp (m->GetElement(0, 1), m->GetElement(1, 1), m->GetElement(2, 1)); + cam->SetPosition(m->GetElement(0, 3), m->GetElement(1, 3), m->GetElement(2, 3)); + } + else + { + cam->SetPosition(0, 0, 0); + cam->SetFocalPoint(0, 0, 1); + cam->SetViewUp(0, -1, 0); + } + + // go to the next actor for the next key-press event. + if (it != widget_actor_map_->end()) + ++it; + else + it = widget_actor_map_->begin(); + + CurrentRenderer->SetActiveCamera(cam); + CurrentRenderer->ResetCameraClippingRange(); + CurrentRenderer->Render(); + break; + } + + case 'q': case 'Q': + { + Interactor->ExitCallback(); + return; + } + default: + { + Superclass::OnKeyDown(); + break; + } + } + + KeyboardEvent event(KeyboardEvent::KEY_DOWN, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); + if (keyboardCallback_) + keyboardCallback_(event, keyboard_callback_cookie_); + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnKeyUp() +{ + KeyboardEvent event(KeyboardEvent::KEY_UP, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); + if (keyboardCallback_) + keyboardCallback_(event, keyboard_callback_cookie_); + Superclass::OnKeyUp(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnMouseMove() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseMove, MouseEvent::NoButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + Superclass::OnMouseMove(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnLeftButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::LeftButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + Superclass::OnLeftButtonDown(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnLeftButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::LeftButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + Superclass::OnLeftButtonUp(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnMiddleButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::MiddleButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + Superclass::OnMiddleButtonDown(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnMiddleButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::MiddleButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + Superclass::OnMiddleButtonUp(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnRightButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::RightButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + Superclass::OnRightButtonDown(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnRightButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::RightButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + Superclass::OnRightButtonUp(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnMouseWheelForward() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseScrollUp, MouseEvent::VScroll, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + if (Interactor->GetRepeatCount() && mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetAltKey()) + { + // zoom + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + double opening_angle = cam->GetViewAngle(); + if (opening_angle > 15.0) + opening_angle -= 1.0; + + cam->SetViewAngle(opening_angle); + cam->Modified(); + CurrentRenderer->ResetCameraClippingRange(); + CurrentRenderer->Modified(); + CurrentRenderer->Render(); + Interactor->Render(); + } + else + Superclass::OnMouseWheelForward(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnMouseWheelBackward() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseScrollDown, MouseEvent::VScroll, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetRepeatCount() && mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetAltKey()) + { + // zoom + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + double opening_angle = cam->GetViewAngle(); + if (opening_angle < 170.0) + opening_angle += 1.0; + + cam->SetViewAngle(opening_angle); + cam->Modified(); + CurrentRenderer->ResetCameraClippingRange(); + CurrentRenderer->Modified(); + CurrentRenderer->Render(); + Interactor->Render(); + } + else + Superclass::OnMouseWheelBackward(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::InteractorStyle::OnTimer() +{ + CV_Assert("Interactor style not initialized." && init_); + Interactor->Render(); +} diff --git a/modules/viz/src/interactor_style.hpp b/modules/viz/src/interactor_style.hpp new file mode 100644 index 0000000000..8d01697a87 --- /dev/null +++ b/modules/viz/src/interactor_style.hpp @@ -0,0 +1,119 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_INTERACTOR_STYLE_H__ +#define __OPENCV_VIZ_INTERACTOR_STYLE_H__ + +namespace cv +{ + namespace viz + { + class InteractorStyle : public vtkInteractorStyleTrackballCamera + { + public: + static InteractorStyle *New(); + virtual ~InteractorStyle() {} + + // this macro defines Superclass, the isA functionality and the safe downcast method + vtkTypeMacro(InteractorStyle, vtkInteractorStyleTrackballCamera) + + /** \brief Initialization routine. Must be called before anything else. */ + virtual void Initialize(); + + void setWidgetActorMap(const Ptr& actors) { widget_actor_map_ = actors; } + void registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie = 0); + void registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void * cookie = 0); + void saveScreenshot(const String &file); + void exportScene(const String &file); + + private: + /** \brief Set to true after initialization is complete. */ + bool init_; + + Ptr widget_actor_map_; + + Vec2i win_size_; + Vec2i win_pos_; + Vec2i max_win_size_; + + /** \brief Interactor style internal method. Gets called whenever a key is pressed. */ + virtual void OnChar(); + + // Keyboard events + virtual void OnKeyDown(); + virtual void OnKeyUp(); + + // mouse button events + virtual void OnMouseMove(); + virtual void OnLeftButtonDown(); + virtual void OnLeftButtonUp(); + virtual void OnMiddleButtonDown(); + virtual void OnMiddleButtonUp(); + virtual void OnRightButtonDown(); + virtual void OnRightButtonUp(); + virtual void OnMouseWheelForward(); + virtual void OnMouseWheelBackward(); + + /** \brief Interactor style internal method. Gets called periodically if a timer is set. */ + virtual void OnTimer(); + + void zoomIn(); + void zoomOut(); + + /** \brief True if we're using red-blue colors for anaglyphic stereo, false if magenta-green. */ + bool stereo_anaglyph_mask_default_; + + void (*keyboardCallback_)(const KeyboardEvent&, void*); + void *keyboard_callback_cookie_; + + void (*mouseCallback_)(const MouseEvent&, void*); + void *mouse_callback_cookie_; + + int getModifiers(); + }; + } +} + +#endif diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp new file mode 100644 index 0000000000..d9681ce83b --- /dev/null +++ b/modules/viz/src/precomp.hpp @@ -0,0 +1,324 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_PRECOMP_HPP__ +#define __OPENCV_VIZ_PRECOMP_HPP__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(_WIN32) || defined(__CYGWIN__) +# include /* unlink */ +#else +# include /* unlink */ +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +namespace cv +{ + namespace viz + { + typedef std::map > WidgetActorMap; + typedef std::map VizMap; + + class VizStorage + { + public: + static void unregisterAll(); + + //! window names automatically have Viz - prefix even though not provided by the users + static String generateWindowName(const String &window_name); + + private: + VizStorage(); // Static + ~VizStorage(); + + static void add(const Viz3d& window); + static Viz3d& get(const String &window_name); + static void remove(const String &window_name); + static bool windowExists(const String &window_name); + static void removeUnreferenced(); + + static VizMap storage; + friend class Viz3d; + }; + + template inline _Tp normalized(const _Tp& v) { return v * 1/norm(v); } + + template inline bool isNan(const _Tp* data) + { + return isNan(data[0]) || isNan(data[1]) || isNan(data[2]); + } + + inline vtkSmartPointer getActor(const Widget3D& widget) + { + return vtkActor::SafeDownCast(WidgetAccessor::getProp(widget)); + } + + inline vtkSmartPointer getPolyData(const Widget3D& widget) + { + vtkSmartPointer mapper = getActor(widget)->GetMapper(); + return vtkPolyData::SafeDownCast(mapper->GetInput()); + } + + inline vtkSmartPointer vtkmatrix(const cv::Matx44d &matrix) + { + vtkSmartPointer vtk_matrix = vtkSmartPointer::New(); + vtk_matrix->DeepCopy(matrix.val); + return vtk_matrix; + } + + inline Color vtkcolor(const Color& color) + { + Color scaled_color = color * (1.0/255.0); + std::swap(scaled_color[0], scaled_color[2]); + return scaled_color; + } + + inline Vec3d get_random_vec(double from = -10.0, double to = 10.0) + { + RNG& rng = theRNG(); + return Vec3d(rng.uniform(from, to), rng.uniform(from, to), rng.uniform(from, to)); + } + + struct VtkUtils + { + template + static void SetInputData(vtkSmartPointer filter, vtkPolyData* polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->SetInput(polydata); + #else + filter->SetInputData(polydata); + #endif + } + template + static void SetSourceData(vtkSmartPointer filter, vtkPolyData* polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->SetSource(polydata); + #else + filter->SetSourceData(polydata); + #endif + } + + template + static void SetInputData(vtkSmartPointer filter, vtkImageData* polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->SetInput(polydata); + #else + filter->SetInputData(polydata); + #endif + } + + template + static void AddInputData(vtkSmartPointer filter, vtkPolyData *polydata) + { + #if VTK_MAJOR_VERSION <= 5 + filter->AddInput(polydata); + #else + filter->AddInputData(polydata); + #endif + } + + static vtkSmartPointer FillScalars(size_t size, const Color& color) + { + Vec3b rgb = Vec3d(color[2], color[1], color[0]); + Vec3b* color_data = new Vec3b[size]; + std::fill(color_data, color_data + size, rgb); + + vtkSmartPointer scalars = vtkSmartPointer::New(); + scalars->SetName("Colors"); + scalars->SetNumberOfComponents(3); + scalars->SetNumberOfTuples(size); + scalars->SetArray(color_data->val, size * 3, 0); + return scalars; + } + + static vtkSmartPointer ComputeNormals(vtkSmartPointer polydata) + { + vtkSmartPointer normals_generator = vtkSmartPointer::New(); + normals_generator->ComputePointNormalsOn(); + normals_generator->ComputeCellNormalsOff(); + normals_generator->SetFeatureAngle(0.1); + normals_generator->SetSplitting(0); + normals_generator->SetConsistency(1); + normals_generator->SetAutoOrientNormals(0); + normals_generator->SetFlipNormals(0); + normals_generator->SetNonManifoldTraversal(1); + VtkUtils::SetInputData(normals_generator, polydata); + normals_generator->Update(); + return normals_generator->GetOutput(); + } + + static vtkSmartPointer TransformPolydata(vtkSmartPointer algorithm_output_port, const Affine3d& pose) + { + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->SetMatrix(vtkmatrix(pose.matrix)); + + vtkSmartPointer transform_filter = vtkSmartPointer::New(); + transform_filter->SetTransform(transform); + transform_filter->SetInputConnection(algorithm_output_port); + transform_filter->Update(); + return transform_filter->GetOutput(); + } + + static vtkSmartPointer TransformPolydata(vtkSmartPointer polydata, const Affine3d& pose) + { + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->SetMatrix(vtkmatrix(pose.matrix)); + + vtkSmartPointer transform_filter = vtkSmartPointer::New(); + VtkUtils::SetInputData(transform_filter, polydata); + transform_filter->SetTransform(transform); + transform_filter->Update(); + return transform_filter->GetOutput(); + } + }; + } +} + +#include "interactor_style.hpp" +#include "vizimpl.hpp" + + +#endif diff --git a/modules/viz/src/shapes.cpp b/modules/viz/src/shapes.cpp new file mode 100644 index 0000000000..f3d24f757c --- /dev/null +++ b/modules/viz/src/shapes.cpp @@ -0,0 +1,1088 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// line widget implementation +cv::viz::WLine::WLine(const Point3d &pt1, const Point3d &pt2, const Color &color) +{ + vtkSmartPointer line = vtkSmartPointer::New(); + line->SetPoint1(pt1.x, pt1.y, pt1.z); + line->SetPoint2(pt2.x, pt2.y, pt2.z); + line->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, line->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +template<> cv::viz::WLine cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// sphere widget implementation + +cv::viz::WSphere::WSphere(const Point3d ¢er, double radius, int sphere_resolution, const Color &color) +{ + vtkSmartPointer sphere = vtkSmartPointer::New(); + sphere->SetRadius(radius); + sphere->SetCenter(center.x, center.y, center.z); + sphere->SetPhiResolution(sphere_resolution); + sphere->SetThetaResolution(sphere_resolution); + sphere->LatLongTessellationOff(); + sphere->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, sphere->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +template<> cv::viz::WSphere cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// plane widget implementation + +cv::viz::WPlane::WPlane(const Size2d& size, const Color &color) +{ + vtkSmartPointer plane = vtkSmartPointer::New(); + plane->SetOrigin(-0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint1( 0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint2(-0.5 * size.width, 0.5 * size.height, 0.0); + plane->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, plane->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->GetProperty()->LightingOff(); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +cv::viz::WPlane::WPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, const Size2d& size, const Color &color) +{ + Vec3d zvec = normalize(normal); + Vec3d xvec = normalize(new_yaxis.cross(zvec)); + Vec3d yvec = zvec.cross(xvec); + + WPlane plane(size, color); + plane.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = plane; +} + +template<> cv::viz::WPlane cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// arrow widget implementation + +cv::viz::WArrow::WArrow(const Point3d& pt1, const Point3d& pt2, double thickness, const Color &color) +{ + vtkSmartPointer arrow_source = vtkSmartPointer::New(); + arrow_source->SetShaftRadius(thickness); + arrow_source->SetTipRadius(thickness * 3.0); + arrow_source->SetTipLength(thickness * 10.0); + + Vec3d arbitrary = get_random_vec(); + Vec3d start_point(pt1.x, pt1.y, pt1.z), end_point(pt2.x, pt2.y, pt2.z); + + double length = norm(end_point - start_point); + + Vec3d xvec = normalized(end_point - start_point); + Vec3d zvec = normalized(xvec.cross(arbitrary)); + Vec3d yvec = zvec.cross(xvec); + + Matx33d R = makeTransformToGlobal(xvec, yvec, zvec).rotation(); + Affine3d transform_with_scale(R * length, start_point); + + vtkSmartPointer polydata = VtkUtils::TransformPolydata(arrow_source->GetOutputPort(), transform_with_scale); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +template<> cv::viz::WArrow cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// circle widget implementation + +cv::viz::WCircle::WCircle(double radius, double thickness, const Color &color) +{ + vtkSmartPointer disk = vtkSmartPointer::New(); + disk->SetCircumferentialResolution(30); + disk->SetInnerRadius(radius - thickness); + disk->SetOuterRadius(radius + thickness); + disk->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, disk->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->LightingOff(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); + +} + +cv::viz::WCircle::WCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness, const Color &color) +{ + Vec3d arbitrary = get_random_vec(); + Vec3d zvec = normalized(normal); + Vec3d xvec = normalized(zvec.cross(arbitrary)); + Vec3d yvec = zvec.cross(xvec); + + WCircle circle(radius, thickness, color); + circle.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = circle; +} + +template<> cv::viz::WCircle cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// WCone widget implementation + +cv::viz::WCone::WCone(double length, double radius, int resolution, const Color &color) +{ + vtkSmartPointer cone_source = vtkSmartPointer::New(); + cone_source->SetCenter(length*0.5, 0.0, 0.0); + cone_source->SetHeight(length); + cone_source->SetRadius(radius); + cone_source->SetResolution(resolution); + cone_source->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, cone_source->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +cv::viz::WCone::WCone(double radius, const Point3d& center, const Point3d& tip, int resolution, const Color &color) +{ + Vec3d arbitrary = get_random_vec(); + Vec3d xvec = normalized(Vec3d(tip - center)); + Vec3d zvec = normalized(xvec.cross(arbitrary)); + Vec3d yvec = zvec.cross(xvec); + + WCone circle(norm(tip - center), radius, resolution, color); + circle.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = circle; +} + +template<> cv::viz::WCone cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// cylinder widget implementation + +cv::viz::WCylinder::WCylinder(const Point3d& axis_point1, const Point3d& axis_point2, double radius, int numsides, const Color &color) +{ + vtkSmartPointer line = vtkSmartPointer::New(); + line->SetPoint1(axis_point1.x, axis_point1.y, axis_point1.z); + line->SetPoint2(axis_point2.x, axis_point2.y, axis_point2.z); + + vtkSmartPointer tuber = vtkSmartPointer::New(); + tuber->SetInputConnection(line->GetOutputPort()); + tuber->SetNumberOfSides(numsides); + tuber->SetRadius(radius); + tuber->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, tuber->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +template<> cv::viz::WCylinder cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// cylinder widget implementation + +cv::viz::WCube::WCube(const Point3d& min_point, const Point3d& max_point, bool wire_frame, const Color &color) +{ + double bounds[6]; + bounds[0] = std::min(min_point.x, max_point.x); + bounds[1] = std::max(min_point.x, max_point.x); + bounds[2] = std::min(min_point.y, max_point.y); + bounds[3] = std::max(min_point.y, max_point.y); + bounds[4] = std::min(min_point.z, max_point.z); + bounds[5] = std::max(min_point.z, max_point.z); + + vtkSmartPointer cube; + if (wire_frame) + { + cube = vtkSmartPointer::New(); + vtkOutlineSource::SafeDownCast(cube)->SetBounds(bounds); + } + else + { + cube = vtkSmartPointer::New(); + vtkCubeSource::SafeDownCast(cube)->SetBounds(bounds); + } + cube->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, cube->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +template<> cv::viz::WCube cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// coordinate system widget implementation + +cv::viz::WCoordinateSystem::WCoordinateSystem(double scale) +{ + vtkSmartPointer axes = vtkSmartPointer::New(); + axes->SetOrigin(0, 0, 0); + axes->SetScaleFactor(scale); + axes->Update(); + + vtkSmartPointer colors = vtkSmartPointer::New(); + colors->SetNumberOfComponents(3); + colors->InsertNextTuple3(255, 0, 0); + colors->InsertNextTuple3(255, 0, 0); + colors->InsertNextTuple3(0, 255, 0); + colors->InsertNextTuple3(0, 255, 0); + colors->InsertNextTuple3(0, 0, 255); + colors->InsertNextTuple3(0, 0, 255); + + vtkSmartPointer polydata = axes->GetOutput(); + polydata->GetPointData()->SetScalars(colors); + + vtkSmartPointer tube_filter = vtkSmartPointer::New(); + VtkUtils::SetInputData(tube_filter, polydata); + tube_filter->SetRadius(axes->GetScaleFactor() / 50.0); + tube_filter->SetNumberOfSides(6); + tube_filter->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetScalarModeToUsePointData(); + VtkUtils::SetInputData(mapper, tube_filter->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCoordinateSystem cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// polyline widget implementation + +cv::viz::WPolyLine::WPolyLine(InputArray _points, const Color &color) +{ + CV_Assert(_points.type() == CV_32FC3 || _points.type() == CV_32FC4 || _points.type() == CV_64FC3 || _points.type() == CV_64FC4); + + const float *fpoints = _points.getMat().ptr(); + const double *dpoints = _points.getMat().ptr(); + size_t total = _points.total(); + int s_chs = _points.channels(); + + vtkSmartPointer points = vtkSmartPointer::New(); + points->SetDataType(_points.depth() == CV_32F ? VTK_FLOAT : VTK_DOUBLE); + points->SetNumberOfPoints(total); + + if (_points.depth() == CV_32F) + for(size_t i = 0; i < total; ++i, fpoints += s_chs) + points->SetPoint(i, fpoints); + + if (_points.depth() == CV_64F) + for(size_t i = 0; i < total; ++i, dpoints += s_chs) + points->SetPoint(i, dpoints); + + vtkSmartPointer cell_array = vtkSmartPointer::New(); + cell_array->Allocate(cell_array->EstimateSize(1, total)); + cell_array->InsertNextCell(total); + for(size_t i = 0; i < total; ++i) + cell_array->InsertCellPoint(i); + + vtkSmartPointer scalars = VtkUtils::FillScalars(total, color); + + vtkSmartPointer polydata = vtkSmartPointer::New(); + polydata->SetPoints(points); + polydata->SetLines(cell_array); + polydata->GetPointData()->SetScalars(scalars); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + mapper->SetScalarRange(0, 255); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WPolyLine cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// grid widget implementation + + +cv::viz::WGrid::WGrid(const Vec2i &cells, const Vec2d &cells_spacing, const Color &color) +{ + vtkSmartPointer grid_data = vtkSmartPointer::New(); + + // Add 1 to dimensions because in ImageData dimensions is the number of lines + // - however here it means number of cells + grid_data->SetDimensions(cells[0]+1, cells[1]+1, 1); + grid_data->SetSpacing(cells_spacing[0], cells_spacing[1], 0.); + + // Set origin of the grid to be the middle of the grid + grid_data->SetOrigin(cells[0] * cells_spacing[0] * (-0.5), cells[1] * cells_spacing[1] * (-0.5), 0); + + // Extract the edges so we have the grid + vtkSmartPointer extract_edges = vtkSmartPointer::New(); + VtkUtils::SetInputData(extract_edges, grid_data); + extract_edges->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, extract_edges->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +cv::viz::WGrid::WGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, const Vec2i &cells, const Vec2d &cells_spacing, const Color &color) +{ + Vec3d zvec = normalize(normal); + Vec3d xvec = normalize(new_yaxis.cross(zvec)); + Vec3d yvec = zvec.cross(xvec); + + WGrid grid(cells, cells_spacing, color); + grid.applyTransform(makeTransformToGlobal(xvec, yvec, zvec, center)); + *this = grid; +} + +template<> cv::viz::WGrid cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// text3D widget implementation + +cv::viz::WText3D::WText3D(const String &text, const Point3d &position, double text_scale, bool face_camera, const Color &color) +{ + vtkSmartPointer textSource = vtkSmartPointer::New(); + textSource->SetText(text.c_str()); + textSource->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(textSource->GetOutputPort()); + + if (face_camera) + { + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->SetPosition(position.x, position.y, position.z); + actor->SetScale(text_scale); + WidgetAccessor::setProp(*this, actor); + } + else + { + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->SetPosition(position.x, position.y, position.z); + actor->SetScale(text_scale); + actor->GetProperty()->LightingOff(); + WidgetAccessor::setProp(*this, actor); + } + + setColor(color); +} + +void cv::viz::WText3D::setText(const String &text) +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + + // Update text source + vtkPolyDataMapper *mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + vtkVectorText * textSource = vtkVectorText::SafeDownCast(mapper->GetInputConnection(0,0)->GetProducer()); + CV_Assert("This widget does not support text." && textSource); + + textSource->SetText(text.c_str()); + textSource->Modified(); + textSource->Update(); +} + +cv::String cv::viz::WText3D::getText() const +{ + vtkFollower *actor = vtkFollower::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + + vtkPolyDataMapper *mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + vtkVectorText * textSource = vtkVectorText::SafeDownCast(mapper->GetInputConnection(0,0)->GetProducer()); + CV_Assert("This widget does not support text." && textSource); + + return textSource->GetText(); +} + +template<> cv::viz::WText3D cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// text widget implementation + +cv::viz::WText::WText(const String &text, const Point &pos, int font_size, const Color &color) +{ + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetDisplayPosition(pos.x, pos.y); + actor->SetInput(text.c_str()); + + actor->GetProperty()->SetDisplayLocationToForeground(); + + vtkSmartPointer tprop = actor->GetTextProperty(); + tprop->SetFontSize(font_size); + tprop->SetFontFamilyToCourier(); + tprop->SetJustificationToLeft(); + tprop->BoldOn(); + + Color c = vtkcolor(color); + tprop->SetColor(c.val); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WText cv::viz::Widget::cast() +{ + Widget2D widget = this->cast(); + return static_cast(widget); +} + +void cv::viz::WText::setText(const String &text) +{ + vtkTextActor *actor = vtkTextActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + actor->SetInput(text.c_str()); +} + +cv::String cv::viz::WText::getText() const +{ + vtkTextActor *actor = vtkTextActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support text." && actor); + return actor->GetInput(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// image overlay widget implementation + +cv::viz::WImageOverlay::WImageOverlay(InputArray image, const Rect &rect) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + Size sz = image.size(); + + // Scale the image based on the Rect, and flip to match y-ais orientation + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->Scale(sz.width/(double)rect.width, sz.height/(double)rect.height, 1.0); + transform->RotateX(180); + + vtkSmartPointer image_reslice = vtkSmartPointer::New(); + image_reslice->SetResliceTransform(transform); + image_reslice->SetInputConnection(source->GetOutputPort()); + image_reslice->SetOutputDimensionality(2); + image_reslice->InterpolateOn(); + image_reslice->AutoCropOutputOn(); + image_reslice->Update(); + + vtkSmartPointer image_mapper = vtkSmartPointer::New(); + image_mapper->SetInputConnection(image_reslice->GetOutputPort()); + image_mapper->SetColorWindow(255); // OpenCV color + image_mapper->SetColorLevel(127.5); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(image_mapper); + actor->SetPosition(rect.x, rect.y); + actor->GetProperty()->SetDisplayLocationToForeground(); + + WidgetAccessor::setProp(*this, actor); +} + +void cv::viz::WImageOverlay::setImage(InputArray image) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + vtkActor2D *actor = vtkActor2D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support overlay image." && actor); + + vtkImageMapper *mapper = vtkImageMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("This widget does not support overlay image." && mapper); + \ + Vec6i extent; + mapper->GetInput()->GetExtent(extent.val); + Size size(extent[1], extent[3]); + + // Create the vtk image and set its parameters based on input image + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + Size sz = image.size(); + + // Scale the image based on the Rect, and flip to match y-ais orientation + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->Scale(sz.width/(double)size.width, sz.height/(double)size.height, 1.0); + transform->RotateX(180); + + vtkSmartPointer image_reslice = vtkSmartPointer::New(); + image_reslice->SetResliceTransform(transform); + image_reslice->SetInputConnection(source->GetOutputPort()); + image_reslice->SetOutputDimensionality(2); + image_reslice->InterpolateOn(); + image_reslice->AutoCropOutputOn(); + image_reslice->Update(); + + mapper->SetInputConnection(image_reslice->GetOutputPort()); +} + +template<> cv::viz::WImageOverlay cv::viz::Widget::cast() +{ + Widget2D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// image 3D widget implementation + +cv::viz::WImage3D::WImage3D(InputArray image, const Size2d &size) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(source->GetOutputPort()); + + vtkSmartPointer plane = vtkSmartPointer::New(); + plane->SetOrigin(-0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint1( 0.5 * size.width, -0.5 * size.height, 0.0); + plane->SetPoint2(-0.5 * size.width, 0.5 * size.height, 0.0); + + vtkSmartPointer textured_plane = vtkSmartPointer::New(); + textured_plane->SetInputConnection(plane->GetOutputPort()); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(textured_plane->GetOutputPort()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + actor->SetTexture(texture); + actor->GetProperty()->ShadingOff(); + actor->GetProperty()->LightingOff(); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WImage3D::WImage3D(InputArray image, const Size2d &size, const Vec3d ¢er, const Vec3d &normal, const Vec3d &up_vector) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + // Compute the transformation matrix for drawing the camera frame in a scene + Vec3d n = normalize(normal); + Vec3d u = normalize(up_vector.cross(n)); + Vec3d v = n.cross(u); + Affine3d pose = makeTransformToGlobal(u, v, n, center); + + WImage3D image3d(image, size); + image3d.applyTransform(pose); + *this = image3d; +} + +void cv::viz::WImage3D::setImage(InputArray image) +{ + CV_Assert(!image.empty() && image.depth() == CV_8U); + + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("This widget does not support 3D image." && actor); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(source->GetOutputPort()); + + actor->SetTexture(texture); +} + +template<> cv::viz::WImage3D cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// camera position widget implementation + +namespace cv { namespace viz { namespace +{ + struct CameraPositionUtils + { + static vtkSmartPointer createFrustum(double aspect_ratio, double fovy, double scale) + { + vtkSmartPointer camera = vtkSmartPointer::New(); + camera->SetViewAngle(fovy); + camera->SetPosition(0.0, 0.0, 0.0); + camera->SetViewUp(0.0, 1.0, 0.0); + camera->SetFocalPoint(0.0, 0.0, 1.0); + camera->SetClippingRange(1e-9, scale); + + double planes_array[24]; + camera->GetFrustumPlanes(aspect_ratio, planes_array); + + vtkSmartPointer planes = vtkSmartPointer::New(); + planes->SetFrustumPlanes(planes_array); + + vtkSmartPointer frustumSource = vtkSmartPointer::New(); + frustumSource->SetPlanes(planes); + + vtkSmartPointer extract_edges = vtkSmartPointer::New(); + extract_edges->SetInputConnection(frustumSource->GetOutputPort()); + extract_edges->Update(); + + return extract_edges->GetOutput(); + } + + static Mat ensureColorImage(InputArray image) + { + Mat color(image.size(), CV_8UC3); + if (image.channels() == 1) + { + Vec3b *drow = color.ptr(); + for(int y = 0; y < color.rows; ++y) + { + const unsigned char *srow = image.getMat().ptr(y); + const unsigned char *send = srow + color.cols; + for(;srow < send;) + *drow++ = Vec3b::all(*srow++); + } + } + else + image.getMat().copyTo(color); + return color; + } + }; +}}} + +cv::viz::WCameraPosition::WCameraPosition(double scale) +{ + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, getPolyData(WCoordinateSystem(scale))); + mapper->SetScalarModeToUsePointData(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCameraPosition::WCameraPosition(const Matx33d &K, double scale, const Color &color) +{ + double f_x = K(0,0), f_y = K(1,1), c_y = K(1,2); + + // Assuming that this is an ideal camera (c_y and c_x are at the center of the image) + double fovy = 2.0 * atan2(c_y, f_y) * 180 / CV_PI; + double aspect_ratio = f_y / f_x; + + vtkSmartPointer polydata = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +cv::viz::WCameraPosition::WCameraPosition(const Vec2d &fov, double scale, const Color &color) +{ + double aspect_ratio = tan(fov[0] * 0.5) / tan(fov[1] * 0.5); + double fovy = fov[1] * 180 / CV_PI; + + vtkSmartPointer polydata = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, polydata); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +cv::viz::WCameraPosition::WCameraPosition(const Matx33d &K, InputArray _image, double scale, const Color &color) +{ + CV_Assert(!_image.empty() && _image.depth() == CV_8U); + Mat image = CameraPositionUtils::ensureColorImage(_image); + image.at(0, 0) = Vec3d(color.val); //workaround of VTK limitation + + double f_y = K(1,1), c_y = K(1,2); + // Assuming that this is an ideal camera (c_y and c_x are at the center of the image) + double fovy = 2.0 * atan2(c_y, f_y) * 180.0 / CV_PI; + double far_end_height = 2.00 * c_y * scale / f_y; + double aspect_ratio = image.cols/(double)image.rows; + double image_scale = far_end_height/image.rows; + + WImage3D image_widget(image, Size2d(image.cols, image.rows) * image_scale); + image_widget.applyTransform(Affine3d().translate(Vec3d(0, 0, scale))); + vtkSmartPointer plane = getPolyData(image_widget); + + vtkSmartPointer frustum = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + + // Frustum needs to be textured or else it can't be combined with image + vtkSmartPointer frustum_texture = vtkSmartPointer::New(); + VtkUtils::SetInputData(frustum_texture, frustum); + frustum_texture->SetSRange(0.0, 0.0); // Texture mapping with only one pixel + frustum_texture->SetTRange(0.0, 0.0); // from the image to have constant color + + vtkSmartPointer append_filter = vtkSmartPointer::New(); + append_filter->AddInputConnection(frustum_texture->GetOutputPort()); + VtkUtils::AddInputData(append_filter, plane); + + vtkSmartPointer actor = getActor(image_widget); + actor->GetMapper()->SetInputConnection(append_filter->GetOutputPort()); + WidgetAccessor::setProp(*this, actor); +} + +cv::viz::WCameraPosition::WCameraPosition(const Vec2d &fov, InputArray _image, double scale, const Color &color) +{ + CV_Assert(!_image.empty() && _image.depth() == CV_8U); + Mat image = CameraPositionUtils::ensureColorImage(_image); + image.at(0, 0) = Vec3d(color.val); //workaround of VTK limitation + + double fovy = fov[1] * 180.0 / CV_PI; + double far_end_height = 2.0 * scale * tan(fov[1] * 0.5); + double aspect_ratio = image.cols/(double)image.rows; + double image_scale = far_end_height/image.rows; + + WImage3D image_widget(image, Size2d(image.cols, image.rows) * image_scale); + image_widget.applyTransform(Affine3d().translate(Vec3d(0, 0, scale))); + vtkSmartPointer plane = getPolyData(image_widget); + + vtkSmartPointer frustum = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + + // Frustum needs to be textured or else it can't be combined with image + vtkSmartPointer frustum_texture = vtkSmartPointer::New(); + VtkUtils::SetInputData(frustum_texture, frustum); + frustum_texture->SetSRange(0.0, 0.0); // Texture mapping with only one pixel + frustum_texture->SetTRange(0.0, 0.0); // from the image to have constant color + + vtkSmartPointer append_filter = vtkSmartPointer::New(); + append_filter->AddInputConnection(frustum_texture->GetOutputPort()); + VtkUtils::AddInputData(append_filter, plane); + + vtkSmartPointer actor = getActor(image_widget); + actor->GetMapper()->SetInputConnection(append_filter->GetOutputPort()); + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WCameraPosition cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// trajectory widget implementation + +cv::viz::WTrajectory::WTrajectory(InputArray _path, int display_mode, double scale, const Color &color) +{ + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + // Bitwise and with 3 in order to limit the domain to 2 bits + if (display_mode & WTrajectory::PATH) + { + Mat points = vtkTrajectorySource::ExtractPoints(_path); + vtkSmartPointer polydata = getPolyData(WPolyLine(points, color)); + VtkUtils::AddInputData(append_filter, polydata); + } + + if (display_mode & WTrajectory::FRAMES) + { + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetTrajectory(_path); + + vtkSmartPointer glyph = getPolyData(WCoordinateSystem(scale)); + + vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); + tensor_glyph->SetInputConnection(source->GetOutputPort()); + VtkUtils::SetSourceData(tensor_glyph, glyph); + tensor_glyph->ExtractEigenvaluesOff(); // Treat as a rotation matrix, not as something with eigenvalues + tensor_glyph->ThreeGlyphsOff(); + tensor_glyph->SymmetricOff(); + tensor_glyph->ColorGlyphsOff(); + + append_filter->AddInputConnection(tensor_glyph->GetOutputPort()); + } + append_filter->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + mapper->SetScalarModeToUsePointData(); + mapper->SetScalarRange(0, 255); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WTrajectory cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// WTrajectoryFrustums widget implementation + +cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Matx33d &K, double scale, const Color &color) +{ + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetTrajectory(_path); + + vtkSmartPointer glyph = getPolyData(WCameraPosition(K, scale)); + + vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); + tensor_glyph->SetInputConnection(source->GetOutputPort()); + VtkUtils::SetSourceData(tensor_glyph, glyph); + tensor_glyph->ExtractEigenvaluesOff(); // Treat as a rotation matrix, not as something with eigenvalues + tensor_glyph->ThreeGlyphsOff(); + tensor_glyph->SymmetricOff(); + tensor_glyph->ColorGlyphsOff(); + tensor_glyph->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, tensor_glyph->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Vec2d &fov, double scale, const Color &color) +{ + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetTrajectory(_path); + + vtkSmartPointer glyph = getPolyData(WCameraPosition(fov, scale)); + + vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); + tensor_glyph->SetInputConnection(source->GetOutputPort()); + VtkUtils::SetSourceData(tensor_glyph, glyph); + tensor_glyph->ExtractEigenvaluesOff(); // Treat as a rotation matrix, not as something with eigenvalues + tensor_glyph->ThreeGlyphsOff(); + tensor_glyph->SymmetricOff(); + tensor_glyph->ColorGlyphsOff(); + tensor_glyph->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + VtkUtils::SetInputData(mapper, tensor_glyph->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); + setColor(color); +} + +template<> cv::viz::WTrajectoryFrustums cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// WTrajectorySpheres widget implementation + +cv::viz::WTrajectorySpheres::WTrajectorySpheres(InputArray _path, double line_length, double radius, const Color &from, const Color &to) +{ + CV_Assert(_path.kind() == _InputArray::STD_VECTOR || _path.kind() == _InputArray::MAT); + CV_Assert(_path.type() == CV_32FC(16) || _path.type() == CV_64FC(16)); + + Mat path64; + _path.getMat().convertTo(path64, CV_64F); + Affine3d *traj = path64.ptr(); + size_t total = path64.total(); + + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + for(size_t i = 0; i < total; ++i) + { + Vec3d curr = traj[i].translation(); + + vtkSmartPointer sphere_source = vtkSmartPointer::New(); + sphere_source->SetCenter(curr.val); + sphere_source->SetRadius( (i == 0) ? 2 * radius : radius ); + sphere_source->Update(); + + double alpha = static_cast(i)/total; + Color c = from * (1 - alpha) + to * alpha; + + vtkSmartPointer polydata = sphere_source->GetOutput(); + polydata->GetCellData()->SetScalars(VtkUtils::FillScalars(polydata->GetNumberOfCells(), c)); + VtkUtils::AddInputData(append_filter, polydata); + + if (i > 0) + { + Vec3d prev = traj[i-1].translation(); + Vec3d lvec = prev - curr; + + if(norm(lvec) > line_length) + lvec = normalize(lvec) * line_length; + + Vec3d lend = curr + lvec; + + vtkSmartPointer line_source = vtkSmartPointer::New(); + line_source->SetPoint1(curr.val); + line_source->SetPoint2(lend.val); + line_source->Update(); + vtkSmartPointer polydata_ = line_source->GetOutput(); + polydata_->GetCellData()->SetScalars(VtkUtils::FillScalars(polydata_->GetNumberOfCells(), c)); + VtkUtils::AddInputData(append_filter, polydata_); + } + } + append_filter->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetScalarModeToUseCellData(); + VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +template<> cv::viz::WTrajectorySpheres cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} diff --git a/modules/viz/src/types.cpp b/modules/viz/src/types.cpp new file mode 100644 index 0000000000..2e32a63279 --- /dev/null +++ b/modules/viz/src/types.cpp @@ -0,0 +1,206 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +//////////////////////////////////////////////////////////////////// +/// Events + +cv::viz::KeyboardEvent::KeyboardEvent(Action _action, const String& _symbol, unsigned char _code, int _modifiers) + : action(_action), symbol(_symbol), code(_code), modifiers(_modifiers) {} + +cv::viz::MouseEvent::MouseEvent(const Type& _type, const MouseButton& _button, const Point& _pointer, int _modifiers) + : type(_type), button(_button), pointer(_pointer), modifiers(_modifiers) {} + +//////////////////////////////////////////////////////////////////// +/// cv::viz::Mesh3d + +cv::viz::Mesh cv::viz::Mesh::load(const String& file) +{ + vtkSmartPointer reader = vtkSmartPointer::New(); + reader->SetFileName(file.c_str()); + reader->Update(); + + vtkSmartPointer polydata = reader->GetOutput(); + CV_Assert("File does not exist or file format is not supported." && polydata); + + Mesh mesh; + vtkSmartPointer sink = vtkSmartPointer::New(); + sink->SetOutput(mesh.cloud, mesh.colors, mesh.normals, mesh.tcoords); + sink->SetInputConnection(reader->GetOutputPort()); + sink->Write(); + + // Now handle the polygons + vtkSmartPointer polygons = polydata->GetPolys(); + mesh.polygons.create(1, polygons->GetSize(), CV_32SC1); + int* poly_ptr = mesh.polygons.ptr(); + + polygons->InitTraversal(); + vtkIdType nr_cell_points, *cell_points; + while (polygons->GetNextCell(nr_cell_points, cell_points)) + { + *poly_ptr++ = nr_cell_points; + for (vtkIdType i = 0; i < nr_cell_points; ++i) + *poly_ptr++ = (int)cell_points[i]; + } + + return mesh; +} + +//////////////////////////////////////////////////////////////////// +/// Camera implementation + +cv::viz::Camera::Camera(double fx, double fy, double cx, double cy, const Size &window_size) +{ + init(fx, fy, cx, cy, window_size); +} + +cv::viz::Camera::Camera(const Vec2d &fov, const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + setClip(Vec2d(0.01, 1000.01)); // Default clipping + setFov(fov); + window_size_ = window_size; + // Principal point at the center + principal_point_ = Vec2f(static_cast(window_size.width)*0.5f, static_cast(window_size.height)*0.5f); + focal_ = Vec2f(principal_point_[0] / tan(fov_[0]*0.5f), principal_point_[1] / tan(fov_[1]*0.5f)); +} + +cv::viz::Camera::Camera(const cv::Matx33d & K, const Size &window_size) +{ + double f_x = K(0,0); + double f_y = K(1,1); + double c_x = K(0,2); + double c_y = K(1,2); + init(f_x, f_y, c_x, c_y, window_size); +} + +cv::viz::Camera::Camera(const Matx44d &proj, const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + + double near = proj(2,3) / (proj(2,2) - 1.0); + double far = near * (proj(2,2) - 1.0) / (proj(2,2) + 1.0); + double left = near * (proj(0,2)-1) / proj(0,0); + double right = 2.0 * near / proj(0,0) + left; + double bottom = near * (proj(1,2)-1) / proj(1,1); + double top = 2.0 * near / proj(1,1) + bottom; + + double epsilon = 2.2204460492503131e-16; + + principal_point_[0] = fabs(left-right) < epsilon ? window_size.width * 0.5 : (left * window_size.width) / (left - right); + principal_point_[1] = fabs(top-bottom) < epsilon ? window_size.height * 0.5 : (top * window_size.height) / (top - bottom); + + focal_[0] = -near * principal_point_[0] / left; + focal_[1] = near * principal_point_[1] / top; + + setClip(Vec2d(near, far)); + fov_[0] = atan2(principal_point_[0], focal_[0]) + atan2(window_size.width-principal_point_[0], focal_[0]); + fov_[1] = atan2(principal_point_[1], focal_[1]) + atan2(window_size.height-principal_point_[1], focal_[1]); + + window_size_ = window_size; +} + +void cv::viz::Camera::init(double fx, double fy, double cx, double cy, const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + setClip(Vec2d(0.01, 1000.01));// Default clipping + + fov_[0] = atan2(cx, fx) + atan2(window_size.width - cx, fx); + fov_[1] = atan2(cy, fy) + atan2(window_size.height - cy, fy); + + principal_point_[0] = cx; + principal_point_[1] = cy; + + focal_[0] = fx; + focal_[1] = fy; + + window_size_ = window_size; +} + +void cv::viz::Camera::setWindowSize(const Size &window_size) +{ + CV_Assert(window_size.width > 0 && window_size.height > 0); + + // Get the scale factor and update the principal points + float scalex = static_cast(window_size.width) / static_cast(window_size_.width); + float scaley = static_cast(window_size.height) / static_cast(window_size_.height); + + principal_point_[0] *= scalex; + principal_point_[1] *= scaley; + focal_ *= scaley; + // Vertical field of view is fixed! Update horizontal field of view + fov_[0] = (atan2(principal_point_[0],focal_[0]) + atan2(window_size.width-principal_point_[0],focal_[0])); + + window_size_ = window_size; +} + +void cv::viz::Camera::computeProjectionMatrix(Matx44d &proj) const +{ + double top = clip_[0] * principal_point_[1] / focal_[1]; + double left = -clip_[0] * principal_point_[0] / focal_[0]; + double right = clip_[0] * (window_size_.width - principal_point_[0]) / focal_[0]; + double bottom = -clip_[0] * (window_size_.height - principal_point_[1]) / focal_[1]; + + double temp1 = 2.0 * clip_[0]; + double temp2 = 1.0 / (right - left); + double temp3 = 1.0 / (top - bottom); + double temp4 = 1.0 / (clip_[0] - clip_[1]); + + proj = Matx44d::zeros(); + proj(0,0) = temp1 * temp2; + proj(1,1) = temp1 * temp3; + proj(0,2) = (right + left) * temp2; + proj(1,2) = (top + bottom) * temp3; + proj(2,2) = (clip_[1]+clip_[0]) * temp4; + proj(3,2) = -1.0; + proj(2,3) = (temp1 * clip_[1]) * temp4; +} + +cv::viz::Camera cv::viz::Camera::KinectCamera(const Size &window_size) +{ + Matx33d K(525.0, 0.0, 320.0, 0.0, 525.0, 240.0, 0.0, 0.0, 1.0); + return Camera(K, window_size); +} diff --git a/modules/viz/src/viz3d.cpp b/modules/viz/src/viz3d.cpp new file mode 100644 index 0000000000..56f978c0ea --- /dev/null +++ b/modules/viz/src/viz3d.cpp @@ -0,0 +1,148 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +cv::viz::Viz3d::Viz3d(const String& window_name) : impl_(0) { create(window_name); } + +cv::viz::Viz3d::Viz3d(const Viz3d& other) : impl_(other.impl_) +{ + if (impl_) + CV_XADD(&impl_->ref_counter, 1); +} + +cv::viz::Viz3d& cv::viz::Viz3d::operator=(const Viz3d& other) +{ + if (this != &other) + { + release(); + impl_ = other.impl_; + if (impl_) + CV_XADD(&impl_->ref_counter, 1); + } + return *this; +} + +cv::viz::Viz3d::~Viz3d() { release(); } + +void cv::viz::Viz3d::create(const String &window_name) +{ + if (impl_) + release(); + + if (VizStorage::windowExists(window_name)) + *this = VizStorage::get(window_name); + else + { + impl_ = new VizImpl(window_name); + impl_->ref_counter = 1; + + // Register the window + VizStorage::add(*this); + } +} + +void cv::viz::Viz3d::release() +{ + if (impl_ && CV_XADD(&impl_->ref_counter, -1) == 1) + { + delete impl_; + impl_ = 0; + } + + if (impl_ && impl_->ref_counter == 1) + VizStorage::removeUnreferenced(); + + impl_ = 0; +} + +void cv::viz::Viz3d::spin() { impl_->spin(); } +void cv::viz::Viz3d::spinOnce(int time, bool force_redraw) { impl_->spinOnce(time, force_redraw); } +bool cv::viz::Viz3d::wasStopped() const { return impl_->wasStopped(); } +void cv::viz::Viz3d::close() { impl_->close(); } + +void cv::viz::Viz3d::registerKeyboardCallback(KeyboardCallback callback, void* cookie) +{ impl_->registerKeyboardCallback(callback, cookie); } + +void cv::viz::Viz3d::registerMouseCallback(MouseCallback callback, void* cookie) +{ impl_->registerMouseCallback(callback, cookie); } + +void cv::viz::Viz3d::showWidget(const String &id, const Widget &widget, const Affine3d &pose) { impl_->showWidget(id, widget, pose); } +void cv::viz::Viz3d::removeWidget(const String &id) { impl_->removeWidget(id); } +cv::viz::Widget cv::viz::Viz3d::getWidget(const String &id) const { return impl_->getWidget(id); } +void cv::viz::Viz3d::removeAllWidgets() { impl_->removeAllWidgets(); } + +void cv::viz::Viz3d::showImage(InputArray image, const Size& window_size) { impl_->showImage(image, window_size); } + +void cv::viz::Viz3d::setWidgetPose(const String &id, const Affine3d &pose) { impl_->setWidgetPose(id, pose); } +void cv::viz::Viz3d::updateWidgetPose(const String &id, const Affine3d &pose) { impl_->updateWidgetPose(id, pose); } +cv::Affine3d cv::viz::Viz3d::getWidgetPose(const String &id) const { return impl_->getWidgetPose(id); } + +void cv::viz::Viz3d::setCamera(const Camera &camera) { impl_->setCamera(camera); } +cv::viz::Camera cv::viz::Viz3d::getCamera() const { return impl_->getCamera(); } +void cv::viz::Viz3d::setViewerPose(const Affine3d &pose) { impl_->setViewerPose(pose); } +cv::Affine3d cv::viz::Viz3d::getViewerPose() { return impl_->getViewerPose(); } + +void cv::viz::Viz3d::resetCameraViewpoint(const String &id) { impl_->resetCameraViewpoint(id); } +void cv::viz::Viz3d::resetCamera() { impl_->resetCamera(); } + +void cv::viz::Viz3d::convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord) { impl_->convertToWindowCoordinates(pt, window_coord); } +void cv::viz::Viz3d::converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction) { impl_->converTo3DRay(window_coord, origin, direction); } + +cv::Size cv::viz::Viz3d::getWindowSize() const { return impl_->getWindowSize(); } +void cv::viz::Viz3d::setWindowSize(const Size &window_size) { impl_->setWindowSize(window_size); } +cv::String cv::viz::Viz3d::getWindowName() const { return impl_->getWindowName(); } +void cv::viz::Viz3d::saveScreenshot(const String &file) { impl_->saveScreenshot(file); } +void cv::viz::Viz3d::setWindowPosition(const Point& window_position) { impl_->setWindowPosition(window_position); } +void cv::viz::Viz3d::setFullScreen(bool mode) { impl_->setFullScreen(mode); } +void cv::viz::Viz3d::setBackgroundColor(const Color& color, const Color& color2) { impl_->setBackgroundColor(color, color2); } + +void cv::viz::Viz3d::setBackgroundTexture(InputArray image) { impl_->setBackgroundTexture(image); } +void cv::viz::Viz3d::setBackgroundMeshLab() {impl_->setBackgroundMeshLab(); } + +void cv::viz::Viz3d::setRenderingProperty(const String &id, int property, double value) { getWidget(id).setRenderingProperty(property, value); } +double cv::viz::Viz3d::getRenderingProperty(const String &id, int property) { return getWidget(id).getRenderingProperty(property); } + +void cv::viz::Viz3d::setRepresentation(int representation) { impl_->setRepresentation(representation); } diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp new file mode 100644 index 0000000000..b4ec83bd44 --- /dev/null +++ b/modules/viz/src/vizcore.cpp @@ -0,0 +1,312 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +cv::Affine3d cv::viz::makeTransformToGlobal(const Vec3d& axis_x, const Vec3d& axis_y, const Vec3d& axis_z, const Vec3d& origin) +{ + Affine3d::Mat3 R(axis_x[0], axis_y[0], axis_z[0], + axis_x[1], axis_y[1], axis_z[1], + axis_x[2], axis_y[2], axis_z[2]); + + return Affine3d(R, origin); +} + +cv::Affine3d cv::viz::makeCameraPose(const Vec3d& position, const Vec3d& focal_point, const Vec3d& y_dir) +{ + // Compute the transformation matrix for drawing the camera frame in a scene + Vec3d n = normalize(focal_point - position); + Vec3d u = normalize(y_dir.cross(n)); + Vec3d v = n.cross(u); + + return makeTransformToGlobal(u, v, n, position); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// VizStorage implementation + +cv::viz::VizMap cv::viz::VizStorage::storage; +void cv::viz::VizStorage::unregisterAll() { storage.clear(); } + +cv::viz::Viz3d& cv::viz::VizStorage::get(const String &window_name) +{ + String name = generateWindowName(window_name); + VizMap::iterator vm_itr = storage.find(name); + CV_Assert(vm_itr != storage.end()); + return vm_itr->second; +} + +void cv::viz::VizStorage::add(const Viz3d& window) +{ + String window_name = window.getWindowName(); + VizMap::iterator vm_itr = storage.find(window_name); + CV_Assert(vm_itr == storage.end()); + storage.insert(std::make_pair(window_name, window)); +} + +bool cv::viz::VizStorage::windowExists(const String &window_name) +{ + String name = generateWindowName(window_name); + return storage.find(name) != storage.end(); +} + +void cv::viz::VizStorage::removeUnreferenced() +{ + for(VizMap::iterator pos = storage.begin(); pos != storage.end();) + if(pos->second.impl_->ref_counter == 1) + storage.erase(pos++); + else + ++pos; +} + +cv::String cv::viz::VizStorage::generateWindowName(const String &window_name) +{ + String output = "Viz"; + // Already is Viz + if (window_name == output) + return output; + + String prefixed = output + " - "; + if (window_name.substr(0, prefixed.length()) == prefixed) + output = window_name; // Already has "Viz - " + else if (window_name.substr(0, output.length()) == output) + output = prefixed + window_name; // Doesn't have prefix + else + output = (window_name == "" ? output : prefixed + window_name); + + return output; +} + +cv::viz::Viz3d cv::viz::getWindowByName(const String &window_name) { return Viz3d (window_name); } +void cv::viz::unregisterAllWindows() { VizStorage::unregisterAll(); } + +cv::viz::Viz3d cv::viz::imshow(const String& window_name, InputArray image, const Size& window_size) +{ + Viz3d viz = getWindowByName(window_name); + viz.showImage(image, window_size); + return viz; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Read/write clouds. Supported formats: ply, stl, xyz, obj + +void cv::viz::writeCloud(const String& file, InputArray cloud, InputArray colors, InputArray normals, bool binary) +{ + CV_Assert(file.size() > 4 && "Extention is required"); + String extention = file.substr(file.size()-4); + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetColorCloudNormals(cloud, colors, normals); + + vtkSmartPointer writer; + if (extention == ".xyz") + { + writer = vtkSmartPointer::New(); + vtkXYZWriter::SafeDownCast(writer)->SetFileName(file.c_str()); + } + else if (extention == ".ply") + { + writer = vtkSmartPointer::New(); + vtkPLYWriter::SafeDownCast(writer)->SetFileName(file.c_str()); + vtkPLYWriter::SafeDownCast(writer)->SetFileType(binary ? VTK_BINARY : VTK_ASCII); + vtkPLYWriter::SafeDownCast(writer)->SetArrayName("Colors"); + } + else if (extention == ".obj") + { + writer = vtkSmartPointer::New(); + vtkOBJWriter::SafeDownCast(writer)->SetFileName(file.c_str()); + } + else + CV_Assert(!"Unsupported format"); + + writer->SetInputConnection(source->GetOutputPort()); + writer->Write(); +} + +cv::Mat cv::viz::readCloud(const String& file, OutputArray colors, OutputArray normals) +{ + CV_Assert(file.size() > 4 && "Extention is required"); + String extention = file.substr(file.size()-4); + + vtkSmartPointer reader; + if (extention == ".xyz") + { + reader = vtkSmartPointer::New(); + vtkSimplePointsReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else if (extention == ".ply") + { + reader = vtkSmartPointer::New(); + CV_Assert(vtkPLYReader::CanReadFile(file.c_str())); + vtkPLYReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else if (extention == ".obj") + { + reader = vtkSmartPointer::New(); + vtkOBJReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else if (extention == ".stl") + { + reader = vtkSmartPointer::New(); + vtkSTLReader::SafeDownCast(reader)->SetFileName(file.c_str()); + } + else + CV_Assert(!"Unsupported format"); + + cv::Mat cloud; + + vtkSmartPointer sink = vtkSmartPointer::New(); + sink->SetInputConnection(reader->GetOutputPort()); + sink->SetOutput(cloud, colors, normals); + sink->Write(); + + return cloud; +} + +cv::viz::Mesh cv::viz::readMesh(const String& file) { return Mesh::load(file); } + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Read/write poses and trajectories + +bool cv::viz::readPose(const String& file, Affine3d& pose, const String& tag) +{ + FileStorage fs(file, FileStorage::READ); + if (!fs.isOpened()) + return false; + + Mat hdr(pose.matrix, false); + fs[tag] >> hdr; + if (hdr.empty() || hdr.cols != pose.matrix.cols || hdr.rows != pose.matrix.rows) + return false; + + hdr.convertTo(pose.matrix, CV_64F); + return true; +} + +void cv::viz::writePose(const String& file, const Affine3d& pose, const String& tag) +{ + FileStorage fs(file, FileStorage::WRITE); + fs << tag << Mat(pose.matrix, false); +} + +void cv::viz::readTrajectory(OutputArray _traj, const String& files_format, int start, int end, const String& tag) +{ + CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT); + + start = max(0, std::min(start, end)); + end = std::max(start, end); + + std::vector traj; + + for(int i = start; i < end; ++i) + { + Affine3d affine; + bool ok = readPose(cv::format(files_format.c_str(), i), affine, tag); + if (!ok) + break; + + traj.push_back(affine); + } + + Mat(traj).convertTo(_traj, _traj.depth()); +} + +void cv::viz::writeTrajectory(InputArray _traj, const String& files_format, int start, const String& tag) +{ + if (_traj.kind() == _InputArray::STD_VECTOR_MAT) + { + std::vector& v = *(std::vector*)_traj.obj; + + for(size_t i = 0, index = max(0, start); i < v.size(); ++i, ++index) + { + Affine3d affine; + Mat pose = v[i]; + CV_Assert(pose.type() == CV_32FC(16) || pose.type() == CV_64FC(16)); + pose.copyTo(affine.matrix); + writePose(cv::format(files_format.c_str(), index), affine, tag); + } + return; + } + + if (_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT) + { + CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16)); + + Mat traj = _traj.getMat(); + + if (traj.depth() == CV_32F) + for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index) + writePose(cv::format(files_format.c_str(), index), traj.at(i), tag); + + if (traj.depth() == CV_64F) + for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index) + writePose(cv::format(files_format.c_str(), index), traj.at(i), tag); + } + + CV_Assert(!"Unsupported array kind"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Computing normals for mesh + +void cv::viz::computeNormals(const Mesh& mesh, OutputArray _normals) +{ + vtkSmartPointer polydata = getPolyData(WMesh(mesh)); + vtkSmartPointer with_normals = VtkUtils::ComputeNormals(polydata); + + vtkSmartPointer generic_normals = with_normals->GetPointData()->GetNormals(); + if(generic_normals) + { + Mat normals(1, generic_normals->GetNumberOfTuples(), CV_64FC3); + Vec3d *optr = normals.ptr(); + + for(int i = 0; i < generic_normals->GetNumberOfTuples(); ++i, ++optr) + generic_normals->GetTuple(i, optr->val); + + normals.convertTo(_normals, mesh.cloud.type()); + } + else + _normals.release(); +} diff --git a/modules/viz/src/vizimpl.cpp b/modules/viz/src/vizimpl.cpp new file mode 100644 index 0000000000..5fa49e2f96 --- /dev/null +++ b/modules/viz/src/vizimpl.cpp @@ -0,0 +1,542 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::viz::Viz3d::VizImpl::VizImpl(const String &name) : spin_once_state_(false), + window_position_(Vec2i(std::numeric_limits::min())), widget_actor_map_(new WidgetActorMap) +{ + renderer_ = vtkSmartPointer::New(); + window_name_ = VizStorage::generateWindowName(name); + + // Create render window + window_ = vtkSmartPointer::New(); + cv::Vec2i window_size = cv::Vec2i(window_->GetScreenSize()) / 2; + window_->SetSize(window_size.val); + window_->AddRenderer(renderer_); + + // Create the interactor style + style_ = vtkSmartPointer::New(); + style_->setWidgetActorMap(widget_actor_map_); + style_->UseTimersOn(); + style_->Initialize(); + + timer_callback_ = vtkSmartPointer::New(); + exit_callback_ = vtkSmartPointer::New(); + exit_callback_->viz = this; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::TimerCallback::Execute(vtkObject* caller, unsigned long event_id, void* cookie) +{ + if (event_id == vtkCommand::TimerEvent && timer_id == *reinterpret_cast(cookie)) + { + vtkSmartPointer interactor = vtkRenderWindowInteractor::SafeDownCast(caller); + interactor->TerminateApp(); + } +} + +void cv::viz::Viz3d::VizImpl::ExitCallback::Execute(vtkObject*, unsigned long event_id, void*) +{ + if (event_id == vtkCommand::ExitEvent) + { + viz->interactor_->TerminateApp(); + viz->interactor_ = 0; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +bool cv::viz::Viz3d::VizImpl::wasStopped() const +{ + bool stopped = spin_once_state_ ? interactor_ == 0 : false; + spin_once_state_ &= !stopped; + return stopped; +} + +void cv::viz::Viz3d::VizImpl::close() +{ + if (!interactor_) + return; + interactor_->GetRenderWindow()->Finalize(); + interactor_->TerminateApp(); // This tends to close the window... + interactor_ = 0; +} + +void cv::viz::Viz3d::VizImpl::recreateRenderWindow() +{ +#if !defined _MSC_VER + //recreating is workaround for Ubuntu -- a crash in x-server + Vec2i window_size(window_->GetSize()); + int fullscreen = window_->GetFullScreen(); + + window_ = vtkSmartPointer::New(); + if (window_position_[0] != std::numeric_limits::min()) //also workaround + window_->SetPosition(window_position_.val); + + window_->SetSize(window_size.val); + window_->SetFullScreen(fullscreen); + window_->AddRenderer(renderer_); +#endif +} + + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::spin() +{ + recreateRenderWindow(); + interactor_ = vtkSmartPointer::New(); + interactor_->SetRenderWindow(window_); + interactor_->SetInteractorStyle(style_); + window_->AlphaBitPlanesOff(); + window_->PointSmoothingOff(); + window_->LineSmoothingOff(); + window_->PolygonSmoothingOff(); + window_->SwapBuffersOn(); + window_->SetStereoTypeToAnaglyph(); + window_->Render(); + window_->SetWindowName(window_name_.c_str()); + interactor_->Start(); + interactor_ = 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::spinOnce(int time, bool force_redraw) +{ + if (interactor_ == 0) + { + spin_once_state_ = true; + recreateRenderWindow(); + interactor_ = vtkSmartPointer::New(); + interactor_->SetRenderWindow(window_); + interactor_->SetInteractorStyle(style_); + interactor_->AddObserver(vtkCommand::TimerEvent, timer_callback_); + interactor_->AddObserver(vtkCommand::ExitEvent, exit_callback_); + window_->AlphaBitPlanesOff(); + window_->PointSmoothingOff(); + window_->LineSmoothingOff(); + window_->PolygonSmoothingOff(); + window_->SwapBuffersOn(); + window_->SetStereoTypeToAnaglyph(); + window_->Render(); + window_->SetWindowName(window_name_.c_str()); + } + + vtkSmartPointer local = interactor_; + + if (force_redraw) + local->Render(); + + timer_callback_->timer_id = local->CreateRepeatingTimer(std::max(1, time)); + local->Start(); + local->DestroyTimer(timer_callback_->timer_id); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::showWidget(const String &id, const Widget &widget, const Affine3d &pose) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + if (exists) + { + // Remove it if it exists and add it again + removeActorFromRenderer(wam_itr->second); + } + // Get the actor and set the user matrix + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(widget)); + if (actor) + { + // If the actor is 3D, apply pose + vtkSmartPointer matrix = vtkmatrix(pose.matrix); + actor->SetUserMatrix(matrix); + actor->Modified(); + } + // If the actor is a vtkFollower, then it should always face the camera + vtkFollower *follower = vtkFollower::SafeDownCast(actor); + if (follower) + { + follower->SetCamera(renderer_->GetActiveCamera()); + } + + renderer_->AddActor(WidgetAccessor::getProp(widget)); + (*widget_actor_map_)[id] = WidgetAccessor::getProp(widget); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::removeWidget(const String &id) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + CV_Assert("Widget could not be removed." && removeActorFromRenderer(wam_itr->second)); + widget_actor_map_->erase(wam_itr); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::viz::Widget cv::viz::Viz3d::VizImpl::getWidget(const String &id) const +{ + WidgetActorMap::const_iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + Widget widget; + WidgetAccessor::setProp(widget, wam_itr->second); + return widget; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setWidgetPose(const String &id, const Affine3d &pose) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + vtkProp3D *actor = vtkProp3D::SafeDownCast(wam_itr->second); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = vtkmatrix(pose.matrix); + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::updateWidgetPose(const String &id, const Affine3d &pose) +{ + WidgetActorMap::iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + vtkProp3D *actor = vtkProp3D::SafeDownCast(wam_itr->second); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = actor->GetUserMatrix(); + if (!matrix) + { + setWidgetPose(id, pose); + return ; + } + Affine3d updated_pose = pose * Affine3d(*matrix->Element); + matrix = vtkmatrix(updated_pose.matrix); + + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::Affine3d cv::viz::Viz3d::VizImpl::getWidgetPose(const String &id) const +{ + WidgetActorMap::const_iterator wam_itr = widget_actor_map_->find(id); + bool exists = wam_itr != widget_actor_map_->end(); + CV_Assert("Widget does not exist." && exists); + + vtkProp3D *actor = vtkProp3D::SafeDownCast(wam_itr->second); + CV_Assert("Widget is not 3D." && actor); + + return Affine3d(*actor->GetUserMatrix()->Element); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::saveScreenshot(const String &file) { style_->saveScreenshot(file.c_str()); } + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::registerMouseCallback(MouseCallback callback, void* cookie) +{ style_->registerMouseCallback(callback, cookie); } + +void cv::viz::Viz3d::VizImpl::registerKeyboardCallback(KeyboardCallback callback, void* cookie) +{ style_->registerKeyboardCallback(callback, cookie); } + + +////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::removeAllWidgets() +{ + widget_actor_map_->clear(); + renderer_->RemoveAllViewProps(); +} +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::showImage(InputArray image, const Size& window_size) +{ + removeAllWidgets(); + if (window_size.width > 0 && window_size.height > 0) + setWindowSize(window_size); + + showWidget("showImage", WImageOverlay(image, Rect(Point(0,0), getWindowSize()))); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +bool cv::viz::Viz3d::VizImpl::removeActorFromRenderer(vtkSmartPointer actor) +{ + vtkPropCollection* actors = renderer_->GetViewProps(); + actors->InitTraversal(); + vtkProp* current_actor = NULL; + while ((current_actor = actors->GetNextProp()) != NULL) + if (current_actor == actor) + { + renderer_->RemoveActor(actor); + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setBackgroundColor(const Color& color, const Color& color2) +{ + Color c = vtkcolor(color), c2 = vtkcolor(color2); + bool gradient = color2[0] >= 0 && color2[1] >= 0 && color2[2] >= 0; + + if (gradient) + { + renderer_->SetBackground(c2.val); + renderer_->SetBackground2(c.val); + renderer_->GradientBackgroundOn(); + } + else + { + renderer_->SetBackground(c.val); + renderer_->GradientBackgroundOff(); + } +} + +void cv::viz::Viz3d::VizImpl::setBackgroundMeshLab() +{ setBackgroundColor(Color(2, 1, 1), Color(240, 120, 120)); } + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setBackgroundTexture(InputArray image) +{ + if (image.empty()) + { + renderer_->SetBackgroundTexture(0); + renderer_->TexturedBackgroundOff(); + return; + } + + vtkSmartPointer source = vtkSmartPointer::New(); + source->SetImage(image); + + vtkSmartPointer image_flip = vtkSmartPointer::New(); + image_flip->SetFilteredAxis(1); // Vertical flip + image_flip->SetInputConnection(source->GetOutputPort()); + + vtkSmartPointer texture = vtkSmartPointer::New(); + texture->SetInputConnection(image_flip->GetOutputPort()); + //texture->Update(); + + renderer_->SetBackgroundTexture(texture); + renderer_->TexturedBackgroundOn(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setCamera(const Camera &camera) +{ + vtkSmartPointer active_camera = renderer_->GetActiveCamera(); + + // Set the intrinsic parameters of the camera + window_->SetSize(camera.getWindowSize().width, camera.getWindowSize().height); + double aspect_ratio = static_cast(camera.getWindowSize().width)/static_cast(camera.getWindowSize().height); + + Matx44d proj_mat; + camera.computeProjectionMatrix(proj_mat); + + // Use the intrinsic parameters of the camera to simulate more realistically + vtkSmartPointer vtk_matrix = active_camera->GetProjectionTransformMatrix(aspect_ratio, -1.0, 1.0); + Matx44d old_proj_mat(*vtk_matrix->Element); + + // This is a hack around not being able to set Projection Matrix + vtkSmartPointer transform = vtkSmartPointer::New(); + transform->SetMatrix(vtkmatrix(proj_mat * old_proj_mat.inv())); + active_camera->SetUserTransform(transform); + + renderer_->ResetCameraClippingRange(); + renderer_->Render(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::viz::Camera cv::viz::Viz3d::VizImpl::getCamera() const +{ + vtkSmartPointer active_camera = renderer_->GetActiveCamera(); + + Size window_size(renderer_->GetRenderWindow()->GetSize()[0], + renderer_->GetRenderWindow()->GetSize()[1]); + double aspect_ratio = window_size.width / (double)window_size.height; + + vtkSmartPointer proj_matrix = active_camera->GetProjectionTransformMatrix(aspect_ratio, -1.0f, 1.0f); + return Camera(Matx44d(*proj_matrix->Element), window_size); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setViewerPose(const Affine3d &pose) +{ + vtkCamera& camera = *renderer_->GetActiveCamera(); + + // Position = extrinsic translation + cv::Vec3d pos_vec = pose.translation(); + + // Rotate the view vector + cv::Matx33d rotation = pose.rotation(); + cv::Vec3d y_axis(0.0, 1.0, 0.0); + cv::Vec3d up_vec(rotation * y_axis); + + // Compute the new focal point + cv::Vec3d z_axis(0.0, 0.0, 1.0); + cv::Vec3d focal_vec = pos_vec + rotation * z_axis; + + camera.SetPosition(pos_vec.val); + camera.SetFocalPoint(focal_vec.val); + camera.SetViewUp(up_vec.val); + + renderer_->ResetCameraClippingRange(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +cv::Affine3d cv::viz::Viz3d::VizImpl::getViewerPose() +{ + vtkCamera& camera = *renderer_->GetActiveCamera(); + + Vec3d pos(camera.GetPosition()); + Vec3d view_up(camera.GetViewUp()); + Vec3d focal(camera.GetFocalPoint()); + + Vec3d y_axis = normalized(view_up); + Vec3d z_axis = normalized(focal - pos); + Vec3d x_axis = normalized(y_axis.cross(z_axis)); + + return makeTransformToGlobal(x_axis, y_axis, z_axis, pos); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord) +{ + Vec3d window_pt; + vtkInteractorObserver::ComputeWorldToDisplay(renderer_, pt.x, pt.y, pt.z, window_pt.val); + window_coord = window_pt; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction) +{ + Vec4d world_pt; + vtkInteractorObserver::ComputeDisplayToWorld(renderer_, window_coord.x, window_coord.y, window_coord.z, world_pt.val); + Vec3d cam_pos(renderer_->GetActiveCamera()->GetPosition()); + origin = cam_pos; + direction = normalize(Vec3d(world_pt.val) - cam_pos); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::resetCameraViewpoint(const String &id) +{ + vtkSmartPointer camera_pose; + static WidgetActorMap::iterator it = widget_actor_map_->find(id); + if (it != widget_actor_map_->end()) + { + vtkProp3D *actor = vtkProp3D::SafeDownCast(it->second); + CV_Assert("Widget is not 3D." && actor); + camera_pose = actor->GetUserMatrix(); + } + else + return; + + // Prevent a segfault + if (!camera_pose) return; + + vtkSmartPointer cam = renderer_->GetActiveCamera(); + cam->SetPosition(camera_pose->GetElement(0, 3), + camera_pose->GetElement(1, 3), + camera_pose->GetElement(2, 3)); + + cam->SetFocalPoint(camera_pose->GetElement(0, 3) - camera_pose->GetElement(0, 2), + camera_pose->GetElement(1, 3) - camera_pose->GetElement(1, 2), + camera_pose->GetElement(2, 3) - camera_pose->GetElement(2, 2)); + + cam->SetViewUp(camera_pose->GetElement(0, 1), + camera_pose->GetElement(1, 1), + camera_pose->GetElement(2, 1)); + + renderer_->SetActiveCamera(cam); + renderer_->ResetCameraClippingRange(); + renderer_->Render(); +} + +/////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::resetCamera() +{ + renderer_->ResetCamera(); +} + +/////////////////////////////////////////////////////////////////////////////////// +void cv::viz::Viz3d::VizImpl::setRepresentation(int representation) +{ + vtkActorCollection * actors = renderer_->GetActors(); + actors->InitTraversal(); + vtkActor * actor; + switch (representation) + { + case REPRESENTATION_POINTS: + { + while ((actor = actors->GetNextActor()) != NULL) + actor->GetProperty()->SetRepresentationToPoints(); + break; + } + case REPRESENTATION_SURFACE: + { + while ((actor = actors->GetNextActor()) != NULL) + actor->GetProperty()->SetRepresentationToSurface(); + break; + } + case REPRESENTATION_WIREFRAME: + { + while ((actor = actors->GetNextActor()) != NULL) + actor->GetProperty()->SetRepresentationToWireframe(); + break; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +cv::String cv::viz::Viz3d::VizImpl::getWindowName() const { return window_name_; } +void cv::viz::Viz3d::VizImpl::setFullScreen(bool mode) { window_->SetFullScreen(mode); } +void cv::viz::Viz3d::VizImpl::setWindowPosition(const Point& position) { window_position_ = position; window_->SetPosition(position.x, position.y); } +void cv::viz::Viz3d::VizImpl::setWindowSize(const Size& window_size) { window_->SetSize(window_size.width, window_size.height); } +cv::Size cv::viz::Viz3d::VizImpl::getWindowSize() const { return Size(Point(Vec2i(window_->GetSize()))); } diff --git a/modules/viz/src/vizimpl.hpp b/modules/viz/src/vizimpl.hpp new file mode 100644 index 0000000000..9eb918af68 --- /dev/null +++ b/modules/viz/src/vizimpl.hpp @@ -0,0 +1,138 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __OPENCV_VIZ_VIZ3D_IMPL_HPP__ +#define __OPENCV_VIZ_VIZ3D_IMPL_HPP__ + +struct cv::viz::Viz3d::VizImpl +{ +public: + typedef Viz3d::KeyboardCallback KeyboardCallback; + typedef Viz3d::MouseCallback MouseCallback; + + int ref_counter; + + VizImpl(const String &name); + virtual ~VizImpl() {}; + + bool wasStopped() const; + void close(); + + void spin(); + void spinOnce(int time = 1, bool force_redraw = false); + + void showWidget(const String &id, const Widget &widget, const Affine3d &pose = Affine3d::Identity()); + void removeWidget(const String &id); + Widget getWidget(const String &id) const; + void removeAllWidgets(); + + void showImage(InputArray image, const Size& window_size); + + void setWidgetPose(const String &id, const Affine3d &pose); + void updateWidgetPose(const String &id, const Affine3d &pose); + Affine3d getWidgetPose(const String &id) const; + + void setRepresentation(int representation); + + void setCamera(const Camera &camera); + Camera getCamera() const; + + /** \brief Reset the camera to a given widget */ + void resetCameraViewpoint(const String& id); + void resetCamera(); + + void setViewerPose(const Affine3d &pose); + Affine3d getViewerPose(); + + void convertToWindowCoordinates(const Point3d &pt, Point3d &window_coord); + void converTo3DRay(const Point3d &window_coord, Point3d &origin, Vec3d &direction); + + void saveScreenshot(const String &file); + void setWindowPosition(const Point& position); + Size getWindowSize() const; + void setWindowSize(const Size& window_size); + void setFullScreen(bool mode); + String getWindowName() const; + void setBackgroundColor(const Color& color, const Color& color2); + void setBackgroundTexture(InputArray image); + void setBackgroundMeshLab(); + + void registerKeyboardCallback(KeyboardCallback callback, void* cookie = 0); + void registerMouseCallback(MouseCallback callback, void* cookie = 0); + +private: + struct TimerCallback : public vtkCommand + { + static TimerCallback* New() { return new TimerCallback; } + virtual void Execute(vtkObject* caller, unsigned long event_id, void* cookie); + int timer_id; + }; + + struct ExitCallback : public vtkCommand + { + static ExitCallback* New() { return new ExitCallback; } + virtual void Execute(vtkObject*, unsigned long event_id, void*); + VizImpl* viz; + }; + + mutable bool spin_once_state_; + vtkSmartPointer interactor_; + + vtkSmartPointer window_; + String window_name_; + Vec2i window_position_; + + vtkSmartPointer timer_callback_; + vtkSmartPointer exit_callback_; + + vtkSmartPointer renderer_; + vtkSmartPointer style_; + Ptr widget_actor_map_; + + bool removeActorFromRenderer(vtkSmartPointer actor); + void recreateRenderWindow(); +}; + +#endif diff --git a/modules/viz/src/vtk/vtkCloudMatSink.cpp b/modules/viz/src/vtk/vtkCloudMatSink.cpp new file mode 100644 index 0000000000..09ef0cca99 --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSink.cpp @@ -0,0 +1,158 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkCloudMatSink); +}} + +cv::viz::vtkCloudMatSink::vtkCloudMatSink() {} +cv::viz::vtkCloudMatSink::~vtkCloudMatSink() {} + +void cv::viz::vtkCloudMatSink::SetOutput(OutputArray _cloud, OutputArray _colors, OutputArray _normals, OutputArray _tcoords) +{ + cloud = _cloud; + colors = _colors; + normals = _normals; + tcoords = _tcoords; +} + +void cv::viz::vtkCloudMatSink::WriteData() +{ + vtkPolyData *input = this->GetInput(); + if (!input) + return; + + vtkSmartPointer points_Data = input->GetPoints(); + + if (cloud.needed() && points_Data) + { + int vtktype = points_Data->GetDataType(); + CV_Assert(vtktype == VTK_FLOAT || vtktype == VTK_DOUBLE); + + cloud.create(1, points_Data->GetNumberOfPoints(), vtktype == VTK_FLOAT ? CV_32FC3 : CV_64FC3); + Vec3d *ddata = cloud.getMat().ptr(); + Vec3f *fdata = cloud.getMat().ptr(); + + if (cloud.depth() == CV_32F) + for(size_t i = 0; i < cloud.total(); ++i) + *fdata++ = Vec3d(points_Data->GetPoint(i)); + + if (cloud.depth() == CV_64F) + for(size_t i = 0; i < cloud.total(); ++i) + *ddata++ = Vec3d(points_Data->GetPoint(i)); + } + else + cloud.release(); + + vtkSmartPointer scalars_data = input->GetPointData() ? input->GetPointData()->GetScalars() : 0; + + if (colors.needed() && scalars_data) + { + int channels = scalars_data->GetNumberOfComponents(); + int vtktype = scalars_data->GetDataType(); + + CV_Assert((channels == 3 || channels == 4) && "Only 3- or 4-channel color data support is implemented"); + CV_Assert(cloud.total() == (size_t)scalars_data->GetNumberOfTuples()); + + Mat buffer(cloud.size(), CV_64FC(channels)); + Vec3d *cptr = buffer.ptr(); + for(size_t i = 0; i < buffer.total(); ++i) + *cptr++ = Vec3d(scalars_data->GetTuple(i)); + + buffer.convertTo(colors, CV_8U, vtktype == VTK_FLOAT || VTK_FLOAT == VTK_DOUBLE ? 255.0 : 1.0); + } + else + colors.release(); + + vtkSmartPointer normals_data = input->GetPointData() ? input->GetPointData()->GetNormals() : 0; + + if (normals.needed() && normals_data) + { + int channels = normals_data->GetNumberOfComponents(); + int vtktype = normals_data->GetDataType(); + + CV_Assert((vtktype == VTK_FLOAT || VTK_FLOAT == VTK_DOUBLE) && (channels == 3 || channels == 4)); + CV_Assert(cloud.total() == (size_t)normals_data->GetNumberOfTuples()); + + Mat buffer(cloud.size(), CV_64FC(channels)); + Vec3d *cptr = buffer.ptr(); + for(size_t i = 0; i < buffer.total(); ++i) + *cptr++ = Vec3d(normals_data->GetTuple(i)); + + buffer.convertTo(normals, vtktype == VTK_FLOAT ? CV_32F : CV_64F); + } + else + normals.release(); + + vtkSmartPointer coords_data = input->GetPointData() ? input->GetPointData()->GetTCoords() : 0; + + if (tcoords.needed() && coords_data) + { + int vtktype = coords_data->GetDataType(); + + CV_Assert(vtktype == VTK_FLOAT || VTK_FLOAT == VTK_DOUBLE); + CV_Assert(cloud.total() == (size_t)coords_data->GetNumberOfTuples()); + + Mat buffer(cloud.size(), CV_64FC2); + Vec2d *cptr = buffer.ptr(); + for(size_t i = 0; i < buffer.total(); ++i) + *cptr++ = Vec2d(coords_data->GetTuple(i)); + + buffer.convertTo(tcoords, vtktype == VTK_FLOAT ? CV_32F : CV_64F); + + } + else + tcoords.release(); +} + +void cv::viz::vtkCloudMatSink::PrintSelf(ostream& os, vtkIndent indent) +{ + Superclass::PrintSelf(os, indent); + os << indent << "Cloud: " << cloud.needed() << "\n"; + os << indent << "Colors: " << colors.needed() << "\n"; + os << indent << "Normals: " << normals.needed() << "\n"; +} diff --git a/modules/viz/src/vtk/vtkCloudMatSink.h b/modules/viz/src/vtk/vtkCloudMatSink.h new file mode 100644 index 0000000000..44d7e52a5a --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSink.h @@ -0,0 +1,79 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkCloudMatSink_h +#define __vtkCloudMatSink_h + +#include +#include + +namespace cv +{ + namespace viz + { + class vtkCloudMatSink : public vtkPolyDataWriter + { + public: + static vtkCloudMatSink *New(); + vtkTypeMacro(vtkCloudMatSink,vtkPolyDataWriter) + void PrintSelf(ostream& os, vtkIndent indent); + + void SetOutput(OutputArray cloud, OutputArray colors = noArray(), OutputArray normals = noArray(), OutputArray tcoords = noArray()); + + protected: + vtkCloudMatSink(); + ~vtkCloudMatSink(); + + void WriteData(); + + _OutputArray cloud, colors, normals, tcoords; + + private: + vtkCloudMatSink(const vtkCloudMatSink&); // Not implemented. + void operator=(const vtkCloudMatSink&); // Not implemented. + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkCloudMatSource.cpp b/modules/viz/src/vtk/vtkCloudMatSource.cpp new file mode 100644 index 0000000000..74d01bbd01 --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSource.cpp @@ -0,0 +1,286 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkCloudMatSource); + + template struct VtkDepthTraits; + + template<> struct VtkDepthTraits + { + const static int data_type = VTK_FLOAT; + typedef vtkFloatArray array_type; + }; + + template<> struct VtkDepthTraits + { + const static int data_type = VTK_DOUBLE; + typedef vtkDoubleArray array_type; + }; +}} + +cv::viz::vtkCloudMatSource::vtkCloudMatSource() { SetNumberOfInputPorts(0); } +cv::viz::vtkCloudMatSource::~vtkCloudMatSource() {} + +int cv::viz::vtkCloudMatSource::SetCloud(InputArray _cloud) +{ + CV_Assert(_cloud.depth() == CV_32F || _cloud.depth() == CV_64F); + CV_Assert(_cloud.channels() == 3 || _cloud.channels() == 4); + + Mat cloud = _cloud.getMat(); + + int total = _cloud.depth() == CV_32F ? filterNanCopy(cloud) : filterNanCopy(cloud); + + vertices = vtkSmartPointer::New(); + vertices->Allocate(vertices->EstimateSize(1, total)); + vertices->InsertNextCell(total); + for(int i = 0; i < total; ++i) + vertices->InsertCellPoint(i); + + return total; +} + +int cv::viz::vtkCloudMatSource::SetColorCloud(InputArray _cloud, InputArray _colors) +{ + int total = SetCloud(_cloud); + + if (_colors.empty()) + return total; + + CV_Assert(_colors.depth() == CV_8U && _colors.channels() <= 4 && _colors.channels() != 2); + CV_Assert(_colors.size() == _cloud.size()); + + Mat cloud = _cloud.getMat(); + Mat colors = _colors.getMat(); + + if (cloud.depth() == CV_32F) + filterNanColorsCopy(colors, cloud, total); + else if (cloud.depth() == CV_64F) + filterNanColorsCopy(colors, cloud, total); + + return total; +} + +int cv::viz::vtkCloudMatSource::SetColorCloudNormals(InputArray _cloud, InputArray _colors, InputArray _normals) +{ + int total = SetColorCloud(_cloud, _colors); + + if (_normals.empty()) + return total; + + CV_Assert(_normals.depth() == CV_32F || _normals.depth() == CV_64F); + CV_Assert(_normals.channels() == 3 || _normals.channels() == 4); + CV_Assert(_normals.size() == _cloud.size()); + + Mat c = _cloud.getMat(); + Mat n = _normals.getMat(); + + if (n.depth() == CV_32F && c.depth() == CV_32F) + filterNanNormalsCopy(n, c, total); + else if (n.depth() == CV_32F && c.depth() == CV_64F) + filterNanNormalsCopy(n, c, total); + else if (n.depth() == CV_64F && c.depth() == CV_32F) + filterNanNormalsCopy(n, c, total); + else if (n.depth() == CV_64F && c.depth() == CV_64F) + filterNanNormalsCopy(n, c, total); + else + CV_Assert(!"Unsupported normals/cloud type"); + + return total; +} + +int cv::viz::vtkCloudMatSource::SetColorCloudNormalsTCoords(InputArray _cloud, InputArray _colors, InputArray _normals, InputArray _tcoords) +{ + int total = SetColorCloudNormals(_cloud, _colors, _normals); + + if (_tcoords.empty()) + return total; + + CV_Assert(_tcoords.depth() == CV_32F || _tcoords.depth() == CV_64F); + CV_Assert(_tcoords.channels() == 2 && _tcoords.size() == _cloud.size()); + + Mat cl = _cloud.getMat(); + Mat tc = _tcoords.getMat(); + + if (tc.depth() == CV_32F && cl.depth() == CV_32F) + filterNanTCoordsCopy(tc, cl, total); + else if (tc.depth() == CV_32F && cl.depth() == CV_64F) + filterNanTCoordsCopy(tc, cl, total); + else if (tc.depth() == CV_64F && cl.depth() == CV_32F) + filterNanTCoordsCopy(tc, cl, total); + else if (tc.depth() == CV_64F && cl.depth() == CV_64F) + filterNanTCoordsCopy(tc, cl, total); + else + CV_Assert(!"Unsupported tcoords/cloud type"); + + return total; +} + +int cv::viz::vtkCloudMatSource::RequestData(vtkInformation *vtkNotUsed(request), vtkInformationVector **vtkNotUsed(inputVector), vtkInformationVector *outputVector) +{ + vtkInformation *outInfo = outputVector->GetInformationObject(0); + vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + + output->SetPoints(points); + output->SetVerts(vertices); + if (scalars) + output->GetPointData()->SetScalars(scalars); + + if (normals) + output->GetPointData()->SetNormals(normals); + + if (tcoords) + output->GetPointData()->SetTCoords(tcoords); + + return 1; +} + +template +int cv::viz::vtkCloudMatSource::filterNanCopy(const Mat& cloud) +{ + CV_DbgAssert(DataType<_Tp>::depth == cloud.depth()); + points = vtkSmartPointer::New(); + points->SetDataType(VtkDepthTraits<_Tp>::data_type); + points->Allocate(cloud.total()); + points->SetNumberOfPoints(cloud.total()); + + int s_chs = cloud.channels(); + int total = 0; + for (int y = 0; y < cloud.rows; ++y) + { + const _Tp* srow = cloud.ptr<_Tp>(y); + const _Tp* send = srow + cloud.cols * s_chs; + + for (; srow != send; srow += s_chs) + if (!isNan(srow)) + points->SetPoint(total++, srow); + } + points->SetNumberOfPoints(total); + points->Squeeze(); + return total; +} + +template +void cv::viz::vtkCloudMatSource::filterNanColorsCopy(const Mat& cloud_colors, const Mat& mask, int total) +{ + Vec3b* array = new Vec3b[total]; + Vec3b* pos = array; + + int s_chs = cloud_colors.channels(); + int m_chs = mask.channels(); + for (int y = 0; y < cloud_colors.rows; ++y) + { + const unsigned char* srow = cloud_colors.ptr(y); + const unsigned char* send = srow + cloud_colors.cols * s_chs; + const _Msk* mrow = mask.ptr<_Msk>(y); + + if (cloud_colors.channels() == 1) + { + for (; srow != send; srow += s_chs, mrow += m_chs) + if (!isNan(mrow)) + *pos++ = Vec3b(srow[0], srow[0], srow[0]); + } + else + for (; srow != send; srow += s_chs, mrow += m_chs) + if (!isNan(mrow)) + *pos++ = Vec3b(srow[2], srow[1], srow[0]); + + } + + scalars = vtkSmartPointer::New(); + scalars->SetName("Colors"); + scalars->SetNumberOfComponents(3); + scalars->SetNumberOfTuples(total); + scalars->SetArray(array->val, total * 3, 0); +} + +template +void cv::viz::vtkCloudMatSource::filterNanNormalsCopy(const Mat& cloud_normals, const Mat& mask, int total) +{ + normals = vtkSmartPointer< typename VtkDepthTraits<_Tn>::array_type >::New(); + normals->SetName("Normals"); + normals->SetNumberOfComponents(3); + normals->SetNumberOfTuples(total); + + int s_chs = cloud_normals.channels(); + int m_chs = mask.channels(); + + int pos = 0; + for (int y = 0; y < cloud_normals.rows; ++y) + { + const _Tn* srow = cloud_normals.ptr<_Tn>(y); + const _Tn* send = srow + cloud_normals.cols * s_chs; + + const _Msk* mrow = mask.ptr<_Msk>(y); + + for (; srow != send; srow += s_chs, mrow += m_chs) + if (!isNan(mrow)) + normals->SetTuple(pos++, srow); + } +} + +template +void cv::viz::vtkCloudMatSource::filterNanTCoordsCopy(const Mat& _tcoords, const Mat& mask, int total) +{ + typedef Vec<_Tn, 2> Vec2; + tcoords = vtkSmartPointer< typename VtkDepthTraits<_Tn>::array_type >::New(); + tcoords->SetName("TextureCoordinates"); + tcoords->SetNumberOfComponents(2); + tcoords->SetNumberOfTuples(total); + + int pos = 0; + for (int y = 0; y < mask.rows; ++y) + { + const Vec2* srow = _tcoords.ptr(y); + const Vec2* send = srow + _tcoords.cols; + const _Msk* mrow = mask.ptr<_Msk>(y); + + for (; srow != send; ++srow, mrow += mask.channels()) + if (!isNan(mrow)) + tcoords->SetTuple(pos++, srow->val); + } +} diff --git a/modules/viz/src/vtk/vtkCloudMatSource.h b/modules/viz/src/vtk/vtkCloudMatSource.h new file mode 100644 index 0000000000..4097f9cc87 --- /dev/null +++ b/modules/viz/src/vtk/vtkCloudMatSource.h @@ -0,0 +1,96 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkCloudMatSource_h +#define __vtkCloudMatSource_h + +#include +#include +#include +#include +#include + +namespace cv +{ + namespace viz + { + class vtkCloudMatSource : public vtkPolyDataAlgorithm + { + public: + static vtkCloudMatSource *New(); + vtkTypeMacro(vtkCloudMatSource,vtkPolyDataAlgorithm) + + virtual int SetCloud(InputArray cloud); + virtual int SetColorCloud(InputArray cloud, InputArray colors); + virtual int SetColorCloudNormals(InputArray cloud, InputArray colors, InputArray normals); + virtual int SetColorCloudNormalsTCoords(InputArray cloud, InputArray colors, InputArray normals, InputArray tcoords); + + protected: + vtkCloudMatSource(); + ~vtkCloudMatSource(); + + int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *); + + vtkSmartPointer points; + vtkSmartPointer vertices; + vtkSmartPointer scalars; + vtkSmartPointer normals; + vtkSmartPointer tcoords; + private: + vtkCloudMatSource(const vtkCloudMatSource&); // Not implemented. + void operator=(const vtkCloudMatSource&); // Not implemented. + + template int filterNanCopy(const Mat& cloud); + template void filterNanColorsCopy(const Mat& cloud_colors, const Mat& mask, int total); + + template + void filterNanNormalsCopy(const Mat& cloud_normals, const Mat& mask, int total); + + template + void filterNanTCoordsCopy(const Mat& tcoords, const Mat& mask, int total); + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkImageMatSource.cpp b/modules/viz/src/vtk/vtkImageMatSource.cpp new file mode 100644 index 0000000000..58a5642d46 --- /dev/null +++ b/modules/viz/src/vtk/vtkImageMatSource.cpp @@ -0,0 +1,143 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkImageMatSource); +}} + +cv::viz::vtkImageMatSource::vtkImageMatSource() +{ + this->SetNumberOfInputPorts(0); + this->ImageData = vtkImageData::New(); +} + +int cv::viz::vtkImageMatSource::RequestInformation(vtkInformation *, vtkInformationVector**, vtkInformationVector *outputVector) +{ + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), this->ImageData->GetExtent(), 6); + outInfo->Set(vtkDataObject::SPACING(), 1.0, 1.0, 1.0); + outInfo->Set(vtkDataObject::ORIGIN(), 0.0, 0.0, 0.0); + + vtkDataObject::SetPointDataActiveScalarInfo(outInfo, this->ImageData->GetScalarType(), this->ImageData->GetNumberOfScalarComponents()); + return 1; +} + +int cv::viz::vtkImageMatSource::RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector *outputVector) +{ + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + vtkImageData *output = vtkImageData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()) ); + output->ShallowCopy(this->ImageData); + return 1; +} + +void cv::viz::vtkImageMatSource::SetImage(InputArray _image) +{ + CV_Assert(_image.depth() == CV_8U && (_image.channels() == 1 || _image.channels() == 3 || _image.channels() == 4)); + + Mat image = _image.getMat(); + + this->ImageData->SetDimensions(image.cols, image.rows, 1); +#if VTK_MAJOR_VERSION <= 5 + this->ImageData->SetNumberOfScalarComponents(image.channels()); + this->ImageData->SetScalarTypeToUnsignedChar(); + this->ImageData->AllocateScalars(); +#else + this->ImageData->AllocateScalars(VTK_UNSIGNED_CHAR, image.channels()); +#endif + + switch(image.channels()) + { + case 1: copyGrayImage(image, this->ImageData); break; + case 3: copyRGBImage (image, this->ImageData); break; + case 4: copyRGBAImage(image, this->ImageData); break; + } + this->ImageData->Modified(); +} + +void cv::viz::vtkImageMatSource::copyGrayImage(const Mat &source, vtkSmartPointer output) +{ + unsigned char* dptr = reinterpret_cast(output->GetScalarPointer()); + size_t elem_step = output->GetIncrements()[1]/sizeof(unsigned char); + + for (int y = 0; y < source.rows; ++y) + { + unsigned char* drow = dptr + elem_step * y; + const unsigned char *srow = source.ptr(y); + for (int x = 0; x < source.cols; ++x) + drow[x] = *srow++; + } +} + +void cv::viz::vtkImageMatSource::copyRGBImage(const Mat &source, vtkSmartPointer output) +{ + Vec3b* dptr = reinterpret_cast(output->GetScalarPointer()); + size_t elem_step = output->GetIncrements()[1]/sizeof(Vec3b); + + for (int y = 0; y < source.rows; ++y) + { + Vec3b* drow = dptr + elem_step * y; + const unsigned char *srow = source.ptr(y); + for (int x = 0; x < source.cols; ++x, srow += source.channels()) + drow[x] = Vec3b(srow[2], srow[1], srow[0]); + } +} + +void cv::viz::vtkImageMatSource::copyRGBAImage(const Mat &source, vtkSmartPointer output) +{ + Vec4b* dptr = reinterpret_cast(output->GetScalarPointer()); + size_t elem_step = output->GetIncrements()[1]/sizeof(Vec4b); + + for (int y = 0; y < source.rows; ++y) + { + Vec4b* drow = dptr + elem_step * y; + const unsigned char *srow = source.ptr(y); + for (int x = 0; x < source.cols; ++x, srow += source.channels()) + drow[x] = Vec4b(srow[2], srow[1], srow[0], srow[3]); + } +} diff --git a/modules/viz/src/vtk/vtkImageMatSource.h b/modules/viz/src/vtk/vtkImageMatSource.h new file mode 100644 index 0000000000..db0c093ed8 --- /dev/null +++ b/modules/viz/src/vtk/vtkImageMatSource.h @@ -0,0 +1,82 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +#ifndef __vtkImageMatSource_h +#define __vtkImageMatSource_h + +namespace cv +{ + namespace viz + { + class vtkImageMatSource : public vtkImageAlgorithm + { + public: + static vtkImageMatSource *New(); + vtkTypeMacro(vtkImageMatSource,vtkImageAlgorithm); + + void SetImage(InputArray image); + + protected: + vtkImageMatSource(); + ~vtkImageMatSource() {} + + vtkSmartPointer ImageData; + + int RequestInformation(vtkInformation*, vtkInformationVector**, vtkInformationVector*); + int RequestData (vtkInformation*, vtkInformationVector**, vtkInformationVector*); + private: + vtkImageMatSource(const vtkImageMatSource&); // Not implemented. + void operator=(const vtkImageMatSource&); // Not implemented. + + static void copyGrayImage(const Mat &source, vtkSmartPointer output); + static void copyRGBImage (const Mat &source, vtkSmartPointer output); + static void copyRGBAImage(const Mat &source, vtkSmartPointer output); + }; + } +} + + +#endif diff --git a/modules/viz/src/vtk/vtkOBJWriter.cpp b/modules/viz/src/vtk/vtkOBJWriter.cpp new file mode 100644 index 0000000000..452ad19a7a --- /dev/null +++ b/modules/viz/src/vtk/vtkOBJWriter.cpp @@ -0,0 +1,241 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkOBJWriter); +}} + +cv::viz::vtkOBJWriter::vtkOBJWriter() +{ + std::ofstream fout; // only used to extract the default precision + this->DecimalPrecision = fout.precision(); + this->FileName = NULL; + this->FileType = VTK_ASCII; +} + +cv::viz::vtkOBJWriter::~vtkOBJWriter(){} + +void cv::viz::vtkOBJWriter::WriteData() +{ + vtkPolyData *input = this->GetInput(); + if (!input) + return; + + std::ostream *outfilep = this->OpenVTKFile(); + if (!outfilep) + return; + + std::ostream& outfile = *outfilep; + + //write header + outfile << "# wavefront obj file written by the visualization toolkit" << std::endl << std::endl; + outfile << "mtllib NONE" << std::endl << std::endl; + + // write out the points + for (int i = 0; i < input->GetNumberOfPoints(); i++) + { + Vec3d p; + input->GetPoint(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << "v " << p[0] << " " << p[1] << " " << p[2] << std::endl; + } + + const int idStart = 1; + + // write out the point data + vtkSmartPointer normals = input->GetPointData()->GetNormals(); + if(normals) + { + for (int i = 0; i < normals->GetNumberOfTuples(); i++) + { + Vec3d p; + normals->GetTuple(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << "vn " << p[0] << " " << p[1] << " " << p[2] << std::endl; + } + } + + vtkSmartPointer tcoords = input->GetPointData()->GetTCoords(); + if (tcoords) + { + for (int i = 0; i < tcoords->GetNumberOfTuples(); i++) + { + Vec2d p; + tcoords->GetTuple(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << "vt " << p[0] << " " << p[1] << std::endl; + } + } + + // write out a group name and material + outfile << std::endl << "g grp" << idStart << std::endl; + outfile << "usemtl mtlNONE" << std::endl; + + // write out verts if any + if (input->GetNumberOfVerts() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetVerts(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + outfile << "p "; + for (int i = 0; i < npts; i++) + outfile << index[i] + idStart << " "; + outfile << std::endl; + } + } + + // write out lines if any + if (input->GetNumberOfLines() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetLines(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + outfile << "l "; + if (tcoords) + { + for (int i = 0; i < npts; i++) + outfile << index[i] + idStart << "/" << index[i] + idStart << " "; + } + else + for (int i = 0; i < npts; i++) + outfile << index[i] + idStart << " "; + + outfile << std::endl; + } + } + + // write out polys if any + if (input->GetNumberOfPolys() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetPolys(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + outfile << "f "; + for (int i = 0; i < npts; i++) + { + if (normals) + { + if (tcoords) + outfile << index[i] + idStart << "/" << index[i] + idStart << "/" << index[i] + idStart << " "; + else + outfile << index[i] + idStart << "//" << index[i] + idStart << " "; + } + else + { + if (tcoords) + outfile << index[i] + idStart << " " << index[i] + idStart << " "; + else + outfile << index[i] + idStart << " "; + } + } + outfile << std::endl; + } + } + + // write out tstrips if any + if (input->GetNumberOfStrips() > 0) + { + vtkIdType npts = 0, *index = 0; + vtkCellArray *cells = input->GetStrips(); + for (cells->InitTraversal(); cells->GetNextCell(npts, index); ) + { + for (int i = 2, i1, i2; i < npts; ++i) + { + if (i % 2) + { + i1 = i - 1; + i2 = i - 2; + } + else + { + i1 = i - 1; + i2 = i - 2; + } + + if(normals) + { + if (tcoords) + { + outfile << "f " << index[i1] + idStart << "/" << index[i1] + idStart << "/" << index[i1] + idStart << " " + << index[i2]+ idStart << "/" << index[i2] + idStart << "/" << index[i2] + idStart << " " + << index[i] + idStart << "/" << index[i] + idStart << "/" << index[i] + idStart << std::endl; + } + else + { + outfile << "f " << index[i1] + idStart << "//" << index[i1] + idStart << " " << index[i2] + idStart + << "//" << index[i2] + idStart << " " << index[i] + idStart << "//" << index[i] + idStart << std::endl; + } + } + else + { + if (tcoords) + { + outfile << "f " << index[i1] + idStart << "/" << index[i1] + idStart << " " << index[i2] + idStart + << "/" << index[i2] + idStart << " " << index[i] + idStart << "/" << index[i] + idStart << std::endl; + } + else + outfile << "f " << index[i1] + idStart << " " << index[i2] + idStart << " " << index[i] + idStart << std::endl; + } + } /* for (int i = 2; i < npts; ++i) */ + } + } /* if (input->GetNumberOfStrips() > 0) */ + + this->CloseVTKFile(outfilep); + + // Delete the file if an error occurred + if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) + { + vtkErrorMacro("Ran out of disk space; deleting file: " << this->FileName); + unlink(this->FileName); + } +} + +void cv::viz::vtkOBJWriter::PrintSelf(ostream& os, vtkIndent indent) +{ + Superclass::PrintSelf(os, indent); + os << indent << "DecimalPrecision: " << DecimalPrecision << "\n"; +} diff --git a/modules/viz/src/vtk/vtkOBJWriter.h b/modules/viz/src/vtk/vtkOBJWriter.h new file mode 100644 index 0000000000..f8889884d7 --- /dev/null +++ b/modules/viz/src/vtk/vtkOBJWriter.h @@ -0,0 +1,79 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkOBJWriter_h +#define __vtkOBJWriter_h + +#include + +namespace cv +{ + namespace viz + { + class vtkOBJWriter : public vtkPolyDataWriter + { + public: + static vtkOBJWriter *New(); + vtkTypeMacro(vtkOBJWriter,vtkPolyDataWriter) + void PrintSelf(ostream& os, vtkIndent indent); + + vtkGetMacro(DecimalPrecision, int); + vtkSetMacro(DecimalPrecision, int); + + protected: + vtkOBJWriter(); + ~vtkOBJWriter(); + + void WriteData(); + + int DecimalPrecision; + + private: + vtkOBJWriter(const vtkOBJWriter&); // Not implemented. + void operator=(const vtkOBJWriter&); // Not implemented. + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkTrajectorySource.cpp b/modules/viz/src/vtk/vtkTrajectorySource.cpp new file mode 100644 index 0000000000..e098a1d553 --- /dev/null +++ b/modules/viz/src/vtk/vtkTrajectorySource.cpp @@ -0,0 +1,110 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkTrajectorySource); +}} + +cv::viz::vtkTrajectorySource::vtkTrajectorySource() { SetNumberOfInputPorts(0); } +cv::viz::vtkTrajectorySource::~vtkTrajectorySource() {} + +void cv::viz::vtkTrajectorySource::SetTrajectory(InputArray _traj) +{ + CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT); + CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16)); + + Mat traj; + _traj.getMat().convertTo(traj, CV_64F); + const Affine3d* dpath = traj.ptr(); + size_t total = traj.total(); + + points = vtkSmartPointer::New(); + points->SetDataType(VTK_DOUBLE); + points->SetNumberOfPoints(total); + + tensors = vtkSmartPointer::New(); + tensors->SetNumberOfComponents(9); + tensors->SetNumberOfTuples(total); + + for(size_t i = 0; i < total; ++i, ++dpath) + { + Matx33d R = dpath->rotation().t(); // transposed because of + tensors->SetTuple(i, R.val); // column major order + + Vec3d p = dpath->translation(); + points->SetPoint(i, p.val); + } +} + +cv::Mat cv::viz::vtkTrajectorySource::ExtractPoints(InputArray _traj) +{ + CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT); + CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16)); + + Mat points(1, _traj.total(), CV_MAKETYPE(_traj.depth(), 3)); + const Affine3d* dpath = _traj.getMat().ptr(); + const Affine3f* fpath = _traj.getMat().ptr(); + + if (_traj.depth() == CV_32F) + for(int i = 0; i < points.cols; ++i) + points.at(i) = fpath[i].translation(); + + if (_traj.depth() == CV_64F) + for(int i = 0; i < points.cols; ++i) + points.at(i) = dpath[i].translation(); + + return points; +} + +int cv::viz::vtkTrajectorySource::RequestData(vtkInformation *vtkNotUsed(request), vtkInformationVector **vtkNotUsed(inputVector), vtkInformationVector *outputVector) +{ + vtkInformation *outInfo = outputVector->GetInformationObject(0); + vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + output->SetPoints(points); + output->GetPointData()->SetTensors(tensors); + return 1; +} diff --git a/modules/viz/src/vtk/vtkTrajectorySource.h b/modules/viz/src/vtk/vtkTrajectorySource.h new file mode 100644 index 0000000000..f6c9c77b9c --- /dev/null +++ b/modules/viz/src/vtk/vtkTrajectorySource.h @@ -0,0 +1,84 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkTrajectorySource_h +#define __vtkTrajectorySource_h + +#include +#include +#include +#include +#include + +namespace cv +{ + namespace viz + { + class vtkTrajectorySource : public vtkPolyDataAlgorithm + { + public: + static vtkTrajectorySource *New(); + vtkTypeMacro(vtkTrajectorySource,vtkPolyDataAlgorithm) + + virtual void SetTrajectory(InputArray trajectory); + + static Mat ExtractPoints(InputArray trajectory); + + protected: + vtkTrajectorySource(); + ~vtkTrajectorySource(); + + vtkSmartPointer points; + vtkSmartPointer tensors; + + int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *); + private: + vtkTrajectorySource(const vtkTrajectorySource&); // Not implemented. + void operator=(const vtkTrajectorySource&); // Not implemented. + + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkXYZWriter.cpp b/modules/viz/src/vtk/vtkXYZWriter.cpp new file mode 100644 index 0000000000..4518a0103a --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZWriter.cpp @@ -0,0 +1,93 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkXYZWriter); +}} + +cv::viz::vtkXYZWriter::vtkXYZWriter() +{ + std::ofstream fout; // only used to extract the default precision + this->DecimalPrecision = fout.precision(); +} + +void cv::viz::vtkXYZWriter::WriteData() +{ + vtkPolyData *input = this->GetInput(); + if (!input) + return; + + // OpenVTKFile() will report any errors that happen + ostream *outfilep = this->OpenVTKFile(); + if (!outfilep) + return; + + ostream &outfile = *outfilep; + + for(vtkIdType i = 0; i < input->GetNumberOfPoints(); ++i) + { + Vec3d p; + input->GetPoint(i, p.val); + outfile << std::setprecision(this->DecimalPrecision) << p[0] << " " << p[1] << " " << p[2] << std::endl; + } + + // Close the file + this->CloseVTKFile(outfilep); + + // Delete the file if an error occurred + if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) + { + vtkErrorMacro("Ran out of disk space; deleting file: " << this->FileName); + unlink(this->FileName); + } +} + +void cv::viz::vtkXYZWriter::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + os << indent << "DecimalPrecision: " << this->DecimalPrecision << "\n"; +} diff --git a/modules/viz/src/vtk/vtkXYZWriter.h b/modules/viz/src/vtk/vtkXYZWriter.h new file mode 100644 index 0000000000..3db18b793b --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZWriter.h @@ -0,0 +1,78 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkXYZWriter_h +#define __vtkXYZWriter_h + +#include "vtkPolyDataWriter.h" + +namespace cv +{ + namespace viz + { + class vtkXYZWriter : public vtkPolyDataWriter + { + public: + static vtkXYZWriter *New(); + vtkTypeMacro(vtkXYZWriter,vtkPolyDataWriter) + void PrintSelf(ostream& os, vtkIndent indent); + + vtkGetMacro(DecimalPrecision, int) + vtkSetMacro(DecimalPrecision, int) + + protected: + vtkXYZWriter(); + ~vtkXYZWriter(){} + + void WriteData(); + + int DecimalPrecision; + + private: + vtkXYZWriter(const vtkXYZWriter&); // Not implemented. + void operator=(const vtkXYZWriter&); // Not implemented. + }; + } +} +#endif diff --git a/modules/viz/src/widget.cpp b/modules/viz/src/widget.cpp new file mode 100644 index 0000000000..33b467ebc7 --- /dev/null +++ b/modules/viz/src/widget.cpp @@ -0,0 +1,327 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget implementation + +class cv::viz::Widget::Impl +{ +public: + vtkSmartPointer prop; + Impl() : prop(0) {} +}; + +cv::viz::Widget::Widget() : impl_( new Impl() ) { } + +cv::viz::Widget::Widget(const Widget& other) : impl_( new Impl() ) +{ + if (other.impl_ && other.impl_->prop) + impl_->prop = other.impl_->prop; +} + +cv::viz::Widget& cv::viz::Widget::operator=(const Widget& other) +{ + if (!impl_) + impl_ = new Impl(); + + if (other.impl_) + impl_->prop = other.impl_->prop; + return *this; +} + +cv::viz::Widget::~Widget() +{ + if (impl_) + { + delete impl_; + impl_ = 0; + } +} + +cv::viz::Widget cv::viz::Widget::fromPlyFile(const String &file_name) +{ + CV_Assert(vtkPLYReader::CanReadFile(file_name.c_str())); + + vtkSmartPointer reader = vtkSmartPointer::New(); + reader->SetFileName(file_name.c_str()); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection( reader->GetOutputPort() ); + mapper->ImmediateModeRenderingOff(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + Widget widget; + WidgetAccessor::setProp(widget, actor); + return widget; +} + +void cv::viz::Widget::setRenderingProperty(int property, double value) +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + + switch (property) + { + case POINT_SIZE: actor->GetProperty()->SetPointSize(float(value)); break; + case OPACITY: actor->GetProperty()->SetOpacity(value); break; + case LINE_WIDTH: actor->GetProperty()->SetLineWidth(float(value)); break; + case IMMEDIATE_RENDERING: actor->GetMapper()->SetImmediateModeRendering(int(value)); break; + case FONT_SIZE: + { + vtkTextActor* text_actor = vtkTextActor::SafeDownCast(actor); + CV_Assert("Widget does not have text content." && text_actor); + text_actor->GetTextProperty()->SetFontSize(int(value)); + break; + } + case REPRESENTATION: + { + switch (int(value)) + { + case REPRESENTATION_POINTS: actor->GetProperty()->SetRepresentationToPoints(); break; + case REPRESENTATION_WIREFRAME: actor->GetProperty()->SetRepresentationToWireframe(); break; + case REPRESENTATION_SURFACE: actor->GetProperty()->SetRepresentationToSurface(); break; + } + break; + } + case SHADING: + { + switch (int(value)) + { + case SHADING_FLAT: actor->GetProperty()->SetInterpolationToFlat(); break; + case SHADING_GOURAUD: + { + if (!actor->GetMapper()->GetInput()->GetPointData()->GetNormals()) + { + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Can't set shading property for such type of widget" && mapper); + + vtkSmartPointer with_normals = VtkUtils::ComputeNormals(mapper->GetInput()); + VtkUtils::SetInputData(mapper, with_normals); + } + actor->GetProperty()->SetInterpolationToGouraud(); + break; + } + case SHADING_PHONG: + { + if (!actor->GetMapper()->GetInput()->GetPointData()->GetNormals()) + { + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Can't set shading property for such type of widget" && mapper); + + vtkSmartPointer with_normals = VtkUtils::ComputeNormals(mapper->GetInput()); + VtkUtils::SetInputData(mapper, with_normals); + } + actor->GetProperty()->SetInterpolationToPhong(); + break; + } + } + break; + } + default: + CV_Assert("setPointCloudRenderingProperties: Unknown property"); + } + actor->Modified(); +} + +double cv::viz::Widget::getRenderingProperty(int property) const +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + + double value = 0.0; + switch (property) + { + case POINT_SIZE: value = actor->GetProperty()->GetPointSize(); break; + case OPACITY: value = actor->GetProperty()->GetOpacity(); break; + case LINE_WIDTH: value = actor->GetProperty()->GetLineWidth(); break; + case IMMEDIATE_RENDERING: value = actor->GetMapper()->GetImmediateModeRendering(); break; + + case FONT_SIZE: + { + vtkTextActor* text_actor = vtkTextActor::SafeDownCast(actor); + CV_Assert("Widget does not have text content." && text_actor); + value = text_actor->GetTextProperty()->GetFontSize();; + break; + } + case REPRESENTATION: + { + switch (actor->GetProperty()->GetRepresentation()) + { + case VTK_POINTS: value = REPRESENTATION_POINTS; break; + case VTK_WIREFRAME: value = REPRESENTATION_WIREFRAME; break; + case VTK_SURFACE: value = REPRESENTATION_SURFACE; break; + } + break; + } + case SHADING: + { + switch (actor->GetProperty()->GetInterpolation()) + { + case VTK_FLAT: value = SHADING_FLAT; break; + case VTK_GOURAUD: value = SHADING_GOURAUD; break; + case VTK_PHONG: value = SHADING_PHONG; break; + } + break; + } + default: + CV_Assert("getPointCloudRenderingProperties: Unknown property"); + } + return value; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget accessor implementaion + +vtkSmartPointer cv::viz::WidgetAccessor::getProp(const Widget& widget) +{ + return widget.impl_->prop; +} + +void cv::viz::WidgetAccessor::setProp(Widget& widget, vtkSmartPointer prop) +{ + widget.impl_->prop = prop; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget3D implementation + +void cv::viz::Widget3D::setPose(const Affine3d &pose) +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = vtkmatrix(pose.matrix); + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +void cv::viz::Widget3D::updatePose(const Affine3d &pose) +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D." && actor); + + vtkSmartPointer matrix = actor->GetUserMatrix(); + if (!matrix) + { + setPose(pose); + return; + } + + Affine3d updated_pose = pose * Affine3d(*matrix->Element); + matrix = vtkmatrix(updated_pose.matrix); + + actor->SetUserMatrix(matrix); + actor->Modified(); +} + +cv::Affine3d cv::viz::Widget3D::getPose() const +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D." && actor); + return Affine3d(*actor->GetUserMatrix()->Element); +} + +void cv::viz::Widget3D::applyTransform(const Affine3d &transform) +{ + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget is not 3D actor." && actor); + + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Widget doesn't have a polydata mapper" && mapper); + mapper->Update(); + + VtkUtils::SetInputData(mapper, VtkUtils::TransformPolydata(mapper->GetInput(), transform)); +} + +void cv::viz::Widget3D::setColor(const Color &color) +{ + // Cast to actor instead of prop3d since prop3d doesn't provide getproperty + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + + Color c = vtkcolor(color); + actor->GetMapper()->ScalarVisibilityOff(); + actor->GetProperty()->SetColor(c.val); + actor->GetProperty()->SetEdgeColor(c.val); + actor->Modified(); +} + +template<> cv::viz::Widget3D cv::viz::Widget::cast() +{ + vtkProp3D *actor = vtkProp3D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget cannot be cast." && actor); + + Widget3D widget; + WidgetAccessor::setProp(widget, actor); + return widget; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// widget2D implementation + +void cv::viz::Widget2D::setColor(const Color &color) +{ + vtkActor2D *actor = vtkActor2D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget type is not supported." && actor); + Color c = vtkcolor(color); + actor->GetProperty()->SetColor(c.val); + actor->Modified(); +} + +template<> cv::viz::Widget2D cv::viz::Widget::cast() +{ + vtkActor2D *actor = vtkActor2D::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Widget cannot be cast." && actor); + + Widget2D widget; + WidgetAccessor::setProp(widget, actor); + return widget; +} diff --git a/modules/viz/test/test_main.cpp b/modules/viz/test/test_main.cpp new file mode 100644 index 0000000000..e737d2db31 --- /dev/null +++ b/modules/viz/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("viz") diff --git a/modules/viz/test/test_precomp.cpp b/modules/viz/test/test_precomp.cpp new file mode 100644 index 0000000000..c2673fee69 --- /dev/null +++ b/modules/viz/test/test_precomp.cpp @@ -0,0 +1,24 @@ +#include "test_precomp.hpp" + +cv::String cv::Path::combine(const String& item1, const String& item2) +{ + if (item1.empty()) + return item2; + + if (item2.empty()) + return item1; + + char last = item1[item1.size()-1]; + + bool need_append = last != '/' && last != '\\'; + return item1 + (need_append ? "/" : "") + item2; +} + +cv::String cv::Path::combine(const String& item1, const String& item2, const String& item3) +{ return combine(combine(item1, item2), item3); } + +cv::String cv::Path::change_extension(const String& file, const String& ext) +{ + String::size_type pos = file.find_last_of('.'); + return pos == String::npos ? file : file.substr(0, pos+1) + ext; +} diff --git a/modules/viz/test/test_precomp.hpp b/modules/viz/test/test_precomp.hpp new file mode 100644 index 0000000000..cd00b6e73b --- /dev/null +++ b/modules/viz/test/test_precomp.hpp @@ -0,0 +1,104 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts/ts.hpp" +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace cv +{ + struct Path + { + static String combine(const String& item1, const String& item2); + static String combine(const String& item1, const String& item2, const String& item3); + static String change_extension(const String& file, const String& ext); + }; + + inline cv::String get_dragon_ply_file_path() + { + return Path::combine(cvtest::TS::ptr()->get_data_path(), "dragon.ply"); + } + + template + inline std::vector< Affine3<_Tp> > generate_test_trajectory() + { + std::vector< Affine3<_Tp> > result; + + for (int i = 0, j = 0; i <= 270; i += 3, j += 10) + { + double x = 2 * cos(i * 3 * CV_PI/180.0) * (1.0 + 0.5 * cos(1.2 + i * 1.2 * CV_PI/180.0)); + double y = 0.25 + i/270.0 + sin(j * CV_PI/180.0) * 0.2 * sin(0.6 + j * 1.5 * CV_PI/180.0); + double z = 2 * sin(i * 3 * CV_PI/180.0) * (1.0 + 0.5 * cos(1.2 + i * CV_PI/180.0)); + result.push_back(viz::makeCameraPose(Vec3d(x, y, z), Vec3d::all(0.0), Vec3d(0.0, 1.0, 0.0))); + } + return result; + } + + inline Mat make_gray(const Mat& image) + { + Mat chs[3]; split(image, chs); + return 0.114 * chs[0] + 0.58 * chs[1] + 0.3 * chs[2]; + } +} + +#endif diff --git a/modules/viz/test/test_tutorial2.cpp b/modules/viz/test/test_tutorial2.cpp new file mode 100644 index 0000000000..a901adc2c7 --- /dev/null +++ b/modules/viz/test/test_tutorial2.cpp @@ -0,0 +1,54 @@ +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +void tutorial2() +{ + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Add line to represent (1,1,1) axis + viz::WLine axis(Point3f(-1.0, -1.0, -1.0), Point3d(1.0, 1.0, 1.0)); + axis.setRenderingProperty(viz::LINE_WIDTH, 4.0); + myWindow.showWidget("Line Widget", axis); + + /// Construct a cube widget + viz::WCube cube_widget(Point3d(0.5, 0.5, 0.0), Point3d(0.0, 0.0, -0.5), true, viz::Color::blue()); + cube_widget.setRenderingProperty(viz::LINE_WIDTH, 4.0); + + /// Display widget (update if already displayed) + myWindow.showWidget("Cube Widget", cube_widget); + + /// Rodrigues vector + Vec3d rot_vec = Vec3d::all(0); + double translation_phase = 0.0, translation = 0.0; + while(!myWindow.wasStopped()) + { + /* Rotation using rodrigues */ + /// Rotate around (1,1,1) + rot_vec[0] += CV_PI * 0.01; + rot_vec[1] += CV_PI * 0.01; + rot_vec[2] += CV_PI * 0.01; + + /// Shift on (1,1,1) + translation_phase += CV_PI * 0.01; + translation = sin(translation_phase); + + /// Construct pose + Affine3d pose(rot_vec, Vec3d(translation, translation, translation)); + + myWindow.setWidgetPose("Cube Widget", pose); + + myWindow.spinOnce(1, true); + } +} + + +TEST(Viz, DISABLED_tutorial2_pose_of_widget) +{ + tutorial2(); +} diff --git a/modules/viz/test/test_tutorial3.cpp b/modules/viz/test/test_tutorial3.cpp new file mode 100644 index 0000000000..590e29ebfd --- /dev/null +++ b/modules/viz/test/test_tutorial3.cpp @@ -0,0 +1,64 @@ +#include "test_precomp.hpp" + +using namespace cv; +using namespace std; + +/** + * @function main + */ +void tutorial3(bool camera_pov) +{ + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Let's assume camera has the following properties + Point3d cam_pos(3.0, 3.0, 3.0), cam_focal_point(3.0, 3.0, 2.0), cam_y_dir(-1.0, 0.0, 0.0); + + /// We can get the pose of the cam using makeCameraPose + Affine3d cam_pose = viz::makeCameraPose(cam_pos, cam_focal_point, cam_y_dir); + + /// We can get the transformation matrix from camera coordinate system to global using + /// - makeTransformToGlobal. We need the axes of the camera + Affine3d transform = viz::makeTransformToGlobal(Vec3d(0.0, -1.0, 0.0), Vec3d(-1.0, 0.0, 0.0), Vec3d(0.0, 0.0, -1.0), cam_pos); + + /// Create a cloud widget. + Mat dragon_cloud = viz::readCloud(get_dragon_ply_file_path()); + viz::WCloud cloud_widget(dragon_cloud, viz::Color::green()); + + /// Pose of the widget in camera frame + Affine3d cloud_pose = Affine3d().translate(Vec3d(0.0, 0.0, 3.0)); + /// Pose of the widget in global frame + Affine3d cloud_pose_global = transform * cloud_pose; + + /// Visualize camera frame + if (!camera_pov) + { + viz::WCameraPosition cpw(0.5); // Coordinate axes + viz::WCameraPosition cpw_frustum(Vec2f(0.889484f, 0.523599f)); // Camera frustum + myWindow.showWidget("CPW", cpw, cam_pose); + myWindow.showWidget("CPW_FRUSTUM", cpw_frustum, cam_pose); + } + + /// Visualize widget + myWindow.showWidget("bunny", cloud_widget, cloud_pose_global); + + /// Set the viewer pose to that of camera + if (camera_pov) + myWindow.setViewerPose(cam_pose); + + /// Start event loop. + myWindow.spin(); +} + +TEST(Viz, DISABLED_tutorial3_global_view) +{ + tutorial3(false); +} + +TEST(Viz, DISABLED_tutorial3_camera_view) +{ + tutorial3(true); +} diff --git a/modules/viz/test/test_viz3d.cpp b/modules/viz/test/test_viz3d.cpp new file mode 100644 index 0000000000..45d3cdc3cf --- /dev/null +++ b/modules/viz/test/test_viz3d.cpp @@ -0,0 +1,64 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// + // + // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + // + // By downloading, copying, installing or using the software you agree to this license. + // If you do not agree to this license, do not download, install, + // copy or use the software. + // + // + // License Agreement + // For Open Source Computer Vision Library + // + // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. + // Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's 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. + // + // * The name of the copyright holders may not 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 Intel Corporation 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. + // + //M*/ +#include "test_precomp.hpp" + +using namespace cv; + +TEST(Viz_viz3d, DISABLED_develop) +{ + cv::Mat cloud = cv::viz::readCloud(get_dragon_ply_file_path()); + + cv::viz::Viz3d viz("abc"); + viz.setBackgroundMeshLab(); + viz.showWidget("coo", cv::viz::WCoordinateSystem(1)); + viz.showWidget("cloud", cv::viz::WPaintedCloud(cloud)); + + //---->>>>> + //std::vector gt, es; + //cv::viz::readTrajectory(gt, "d:/Datasets/trajs/gt%05d.xml"); + //cv::viz::readTrajectory(es, "d:/Datasets/trajs/es%05d.xml"); + //cv::Mat cloud = cv::viz::readCloud(get_dragon_ply_file_path()); + //---->>>>> + + + viz.spin(); +} diff --git a/modules/viz/test/tests_simple.cpp b/modules/viz/test/tests_simple.cpp new file mode 100644 index 0000000000..f84b60a475 --- /dev/null +++ b/modules/viz/test/tests_simple.cpp @@ -0,0 +1,407 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// + // + // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + // + // By downloading, copying, installing or using the software you agree to this license. + // If you do not agree to this license, do not download, install, + // copy or use the software. + // + // + // License Agreement + // For Open Source Computer Vision Library + // + // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. + // Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's 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. + // + // * The name of the copyright holders may not 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 Intel Corporation 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. + // + //M*/ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace cv::viz; + +TEST(Viz, show_cloud_bluberry) +{ + Mat dragon_cloud = readCloud(get_dragon_ply_file_path()); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_cloud_bluberry"); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("dragon", WCloud(dragon_cloud, Color::bluberry()), pose); + + viz.showWidget("text2d", WText("Bluberry cloud", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_cloud_random_color) +{ + Mat dragon_cloud = readCloud(get_dragon_ply_file_path()); + + Mat colors(dragon_cloud.size(), CV_8UC3); + theRNG().fill(colors, RNG::UNIFORM, 0, 255); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_cloud_random_color"); + viz.setBackgroundMeshLab(); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("dragon", WCloud(dragon_cloud, colors), pose); + viz.showWidget("text2d", WText("Random color cloud", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_cloud_masked) +{ + Mat dragon_cloud = readCloud(get_dragon_ply_file_path()); + + Vec3f qnan = Vec3f::all(std::numeric_limits::quiet_NaN()); + for(size_t i = 0; i < dragon_cloud.total(); ++i) + if (i % 15 != 0) + dragon_cloud.at(i) = qnan; + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_cloud_masked"); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("dragon", WCloud(dragon_cloud), pose); + viz.showWidget("text2d", WText("Nan masked cloud", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_cloud_collection) +{ + Mat cloud = readCloud(get_dragon_ply_file_path()); + + WCloudCollection ccol; + ccol.addCloud(cloud, Color::white(), Affine3d().translate(Vec3d(0, 0, 0)).rotate(Vec3d(CV_PI/2, 0, 0))); + ccol.addCloud(cloud, Color::blue(), Affine3d().translate(Vec3d(1, 0, 0))); + ccol.addCloud(cloud, Color::red(), Affine3d().translate(Vec3d(2, 0, 0))); + + Viz3d viz("show_cloud_collection"); + viz.setBackgroundColor(Color::mlab()); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("ccol", ccol); + viz.showWidget("text2d", WText("Cloud collection", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_painted_clouds) +{ + Mat cloud = readCloud(get_dragon_ply_file_path()); + + Viz3d viz("show_painted_clouds"); + viz.setBackgroundMeshLab(); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("cloud1", WPaintedCloud(cloud), Affine3d(Vec3d(0.0, -CV_PI/2, 0.0), Vec3d(-1.5, 0.0, 0.0))); + viz.showWidget("cloud2", WPaintedCloud(cloud, Vec3d(0.0, -0.75, -1.0), Vec3d(0.0, 0.75, 0.0)), Affine3d(Vec3d(0.0, CV_PI/2, 0.0), Vec3d(1.5, 0.0, 0.0))); + viz.showWidget("cloud3", WPaintedCloud(cloud, Vec3d(0.0, 0.0, -1.0), Vec3d(0.0, 0.0, 1.0), Color::blue(), Color::red())); + viz.showWidget("arrow", WArrow(Vec3d(0.0, 1.0, -1.0), Vec3d(0.0, 1.0, 1.0), 0.009, Color::raspberry())); + viz.showWidget("text2d", WText("Painted clouds", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_mesh) +{ + Mesh mesh = Mesh::load(get_dragon_ply_file_path()); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_mesh"); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("mesh", WMesh(mesh), pose); + viz.showWidget("text2d", WText("Just mesh", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_mesh_random_colors) +{ + Mesh mesh = Mesh::load(get_dragon_ply_file_path()); + theRNG().fill(mesh.colors, RNG::UNIFORM, 0, 255); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_mesh_random_color"); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("mesh", WMesh(mesh), pose); + viz.setRenderingProperty("mesh", SHADING, SHADING_PHONG); + viz.showWidget("text2d", WText("Random color mesh", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_textured_mesh) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + + std::vector points; + std::vector tcoords; + std::vector polygons; + for(size_t i = 0; i < 64; ++i) + { + double angle = CV_PI/2 * i/64.0; + points.push_back(Vec3d(0.00, cos(angle), sin(angle))*0.75); + points.push_back(Vec3d(1.57, cos(angle), sin(angle))*0.75); + tcoords.push_back(Vec2d(0.0, i/64.0)); + tcoords.push_back(Vec2d(1.0, i/64.0)); + } + + for(size_t i = 0; i < points.size()/2-1; ++i) + { + int polys[] = {3, 2*i, 2*i+1, 2*i+2, 3, 2*i+1, 2*i+2, 2*i+3}; + polygons.insert(polygons.end(), polys, polys + sizeof(polys)/sizeof(polys[0])); + } + + cv::viz::Mesh mesh; + mesh.cloud = Mat(points, true).reshape(3, 1); + mesh.tcoords = Mat(tcoords, true).reshape(2, 1); + mesh.polygons = Mat(polygons, true).reshape(1, 1); + mesh.texture = lena; + + Viz3d viz("show_textured_mesh"); + viz.setBackgroundMeshLab(); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("mesh", WMesh(mesh)); + viz.setRenderingProperty("mesh", SHADING, SHADING_PHONG); + viz.showWidget("text2d", WText("Textured mesh", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_polyline) +{ + Mat polyline(1, 32, CV_64FC3); + for(size_t i = 0; i < polyline.total(); ++i) + polyline.at(i) = Vec3d(i/16.0, cos(i * CV_PI/6), sin(i * CV_PI/6)); + + Viz3d viz("show_polyline"); + viz.showWidget("polyline", WPolyLine(Mat(polyline), Color::apricot())); + viz.showWidget("coosys", WCoordinateSystem()); + viz.showWidget("text2d", WText("Polyline", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_sampled_normals) +{ + Mesh mesh = Mesh::load(get_dragon_ply_file_path()); + computeNormals(mesh, mesh.normals); + + Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); + + Viz3d viz("show_sampled_normals"); + viz.showWidget("mesh", WMesh(mesh), pose); + viz.showWidget("normals", WCloudNormals(mesh.cloud, mesh.normals, 30, 0.1f, Color::green()), pose); + viz.setRenderingProperty("normals", LINE_WIDTH, 2.0); + viz.showWidget("text2d", WText("Cloud or mesh normals", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_trajectories) +{ + std::vector path = generate_test_trajectory(), sub0, sub1, sub2, sub3, sub4, sub5; + + Mat(path).rowRange(0, path.size()/10+1).copyTo(sub0); + Mat(path).rowRange(path.size()/10, path.size()/5+1).copyTo(sub1); + Mat(path).rowRange(path.size()/5, 11*path.size()/12).copyTo(sub2); + Mat(path).rowRange(11*path.size()/12, path.size()).copyTo(sub3); + Mat(path).rowRange(3*path.size()/4, 33*path.size()/40).copyTo(sub4); + Mat(path).rowRange(33*path.size()/40, 9*path.size()/10).copyTo(sub5); + Matx33d K(1024.0, 0.0, 320.0, 0.0, 1024.0, 240.0, 0.0, 0.0, 1.0); + + Viz3d viz("show_trajectories"); + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("sub0", WTrajectorySpheres(sub0, 0.25, 0.07)); + viz.showWidget("sub1", WTrajectory(sub1, WTrajectory::PATH, 0.2, Color::brown())); + viz.showWidget("sub2", WTrajectory(sub2, WTrajectory::FRAMES, 0.2)); + viz.showWidget("sub3", WTrajectory(sub3, WTrajectory::BOTH, 0.2, Color::green())); + viz.showWidget("sub4", WTrajectoryFrustums(sub4, K, 0.3, Color::yellow())); + viz.showWidget("sub5", WTrajectoryFrustums(sub5, Vec2d(0.78, 0.78), 0.15)); + viz.showWidget("text2d", WText("Different kinds of supported trajectories", Point(20, 20), 20, Color::green())); + + int i = 0; + while(!viz.wasStopped()) + { + double a = --i % 360; + Vec3d pose(sin(a * CV_PI/180), 0.7, cos(a * CV_PI/180)); + viz.setViewerPose(makeCameraPose(pose * 7.5, Vec3d(0.0, 0.5, 0.0), Vec3d(0.0, 0.1, 0.0))); + viz.spinOnce(20, true); + } + viz.resetCamera(); + viz.spin(); +} + +TEST(Viz, show_trajectory_reposition) +{ + std::vector path = generate_test_trajectory(); + + Viz3d viz("show_trajectory_reposition_to_origin"); + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("sub3", WTrajectory(Mat(path).rowRange(0, path.size()/3), WTrajectory::BOTH, 0.2, Color::brown()), path.front().inv()); + viz.showWidget("text2d", WText("Trajectory resposition to origin", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_camera_positions) +{ + Matx33d K(1024.0, 0.0, 320.0, 0.0, 1024.0, 240.0, 0.0, 0.0, 1.0); + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + Mat gray = make_gray(lena); + + Affine3d poses[2]; + for(int i = 0; i < 2; ++i) + { + Vec3d pose = 5 * Vec3d(sin(3.14 + 2.7 + i*60 * CV_PI/180), 0.4 - i*0.3, cos(3.14 + 2.7 + i*60 * CV_PI/180)); + poses[i] = makeCameraPose(pose, Vec3d(0.0, 0.0, 0.0), Vec3d(0.0, -0.1, 0.0)); + } + + Viz3d viz("show_camera_positions"); + viz.showWidget("sphe", WSphere(Point3d(0,0,0), 1.0, 10, Color::orange_red())); + viz.showWidget("coos", WCoordinateSystem(1.5)); + viz.showWidget("pos1", WCameraPosition(0.75), poses[0]); + viz.showWidget("pos2", WCameraPosition(Vec2d(0.78, 0.78), lena, 2.2, Color::green()), poses[0]); + viz.showWidget("pos3", WCameraPosition(0.75), poses[1]); + viz.showWidget("pos4", WCameraPosition(K, gray, 3, Color::indigo()), poses[1]); + viz.showWidget("text2d", WText("Camera positions with images", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_overlay_image) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + Mat gray = make_gray(lena); + + Size2d half_lsize = Size2d(lena.cols, lena.rows) * 0.5; + + Viz3d viz("show_overlay_image"); + viz.setBackgroundMeshLab(); + Size vsz = viz.getWindowSize(); + + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("img1", WImageOverlay(lena, Rect(Point(10, 10), half_lsize))); + viz.showWidget("img2", WImageOverlay(gray, Rect(Point(vsz.width-10-lena.cols/2, 10), half_lsize))); + viz.showWidget("img3", WImageOverlay(gray, Rect(Point(10, vsz.height-10-lena.rows/2), half_lsize))); + viz.showWidget("img5", WImageOverlay(lena, Rect(Point(vsz.width-10-lena.cols/2, vsz.height-10-lena.rows/2), half_lsize))); + viz.showWidget("text2d", WText("Overlay images", Point(20, 20), 20, Color::green())); + + int i = 0; + while(!viz.wasStopped()) + { + double a = ++i % 360; + Vec3d pose(sin(a * CV_PI/180), 0.7, cos(a * CV_PI/180)); + viz.setViewerPose(makeCameraPose(pose * 3, Vec3d(0.0, 0.5, 0.0), Vec3d(0.0, 0.1, 0.0))); + viz.getWidget("img1").cast().setImage(lena * pow(sin(i*10*CV_PI/180) * 0.5 + 0.5, 1.0)); + viz.spinOnce(1, true); + } + viz.showWidget("text2d", WText("Overlay images (stopped)", Point(20, 20), 20, Color::green())); + viz.spin(); +} + + +TEST(Viz, show_image_method) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + + Viz3d viz("show_image_method"); + viz.showImage(lena); + viz.spinOnce(1500, true); + viz.showImage(lena, lena.size()); + viz.spinOnce(1500, true); + + cv::viz::imshow("show_image_method", make_gray(lena)).spin(); +} + +TEST(Viz, show_image_3d) +{ + Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); + Mat gray = make_gray(lena); + + Viz3d viz("show_image_3d"); + viz.setBackgroundMeshLab(); + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("arr0", WArrow(Vec3d(0.5, 0.0, 0.0), Vec3d(1.5, 0.0, 0.0), 0.009, Color::raspberry())); + viz.showWidget("img0", WImage3D(lena, Size2d(1.0, 1.0)), Affine3d(Vec3d(0.0, CV_PI/2, 0.0), Vec3d(.5, 0.0, 0.0))); + viz.showWidget("arr1", WArrow(Vec3d(-0.5, -0.5, 0.0), Vec3d(0.2, 0.2, 0.0), 0.009, Color::raspberry())); + viz.showWidget("img1", WImage3D(gray, Size2d(1.0, 1.0), Vec3d(-0.5, -0.5, 0.0), Vec3d(1.0, 1.0, 0.0), Vec3d(0.0, 1.0, 0.0))); + + viz.showWidget("arr3", WArrow(Vec3d::all(-0.5), Vec3d::all(0.5), 0.009, Color::raspberry())); + + viz.showWidget("text2d", WText("Images in 3D", Point(20, 20), 20, Color::green())); + + int i = 0; + while(!viz.wasStopped()) + { + viz.getWidget("img0").cast().setImage(lena * pow(sin(i++*7.5*CV_PI/180) * 0.5 + 0.5, 1.0)); + viz.spinOnce(1, true); + } + viz.showWidget("text2d", WText("Images in 3D (stopped)", Point(20, 20), 20, Color::green())); + viz.spin(); +} + +TEST(Viz, show_simple_widgets) +{ + Viz3d viz("show_simple_widgets"); + viz.setBackgroundMeshLab(); + + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("cub0", WCube(Vec3d::all(-1.0), Vec3d::all(-0.5), false, Color::indigo())); + viz.showWidget("arro", WArrow(Vec3d::all(-0.5), Vec3d::all(0.5), 0.009, Color::raspberry())); + viz.showWidget("cir1", WCircle(0.5, 0.01, Color::bluberry())); + viz.showWidget("cir2", WCircle(0.5, Point3d(0.5, 0.0, 0.0), Vec3d(1.0, 0.0, 0.0), 0.01, Color::apricot())); + + viz.showWidget("cyl0", WCylinder(Vec3d(-0.5, 0.5, -0.5), Vec3d(0.5, 0.5, -0.5), 0.125, 30, Color::brown())); + viz.showWidget("con0", WCone(0.25, 0.125, 6, Color::azure())); + viz.showWidget("con1", WCone(0.125, Point3d(0.5, -0.5, 0.5), Point3d(0.5, -1.0, 0.5), 6, Color::turquoise())); + + viz.showWidget("text2d", WText("Different simple widgets", Point(20, 20), 20, Color::green())); + viz.showWidget("text3d", WText3D("Simple 3D text", Point3d( 0.5, 0.5, 0.5), 0.125, false, Color::green())); + + viz.showWidget("plane1", WPlane(Size2d(0.25, 0.75))); + viz.showWidget("plane2", WPlane(Vec3d(0.5, -0.5, -0.5), Vec3d(0.0, 1.0, 1.0), Vec3d(1.0, 1.0, 0.0), Size2d(1.0, 0.5), Color::gold())); + + viz.showWidget("grid1", WGrid(Vec2i(7,7), Vec2d::all(0.75), Color::gray()), Affine3d().translate(Vec3d(0.0, 0.0, -1.0))); + + viz.spin(); + viz.getWidget("text2d").cast().setText("Different simple widgets (updated)"); + viz.getWidget("text3d").cast().setText("Updated text 3D"); + viz.spin(); +} + +TEST(Viz, show_follower) +{ + Viz3d viz("show_follower"); + + viz.showWidget("coos", WCoordinateSystem()); + viz.showWidget("cube", WCube()); + viz.showWidget("t3d_2", WText3D("Simple 3D follower", Point3d(-0.5, -0.5, 0.5), 0.125, true, Color::green())); + viz.showWidget("text2d", WText("Follower: text always facing camera", Point(20, 20), 20, Color::green())); + viz.setBackgroundMeshLab(); + viz.spin(); + viz.getWidget("t3d_2").cast().setText("Updated follower 3D"); + viz.spin(); +} From 130914d9f4a96e3e6da8b548b9c3a897085fee63 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Mon, 20 Jan 2014 23:51:33 +0400 Subject: [PATCH 044/293] fixed documentation warnings --- samples/cpp/CMakeLists.txt | 2 + samples/cpp/tutorial_code/viz/bunny.ply | 5752 +++++++++++++++++ .../tutorial_code/viz/creating_widgets.cpp | 113 + .../cpp/tutorial_code/viz/launching_viz.cpp | 66 + .../cpp/tutorial_code/viz/transformations.cpp | 112 + samples/cpp/tutorial_code/viz/widget_pose.cpp | 79 + 6 files changed, 6124 insertions(+) create mode 100644 samples/cpp/tutorial_code/viz/bunny.ply create mode 100644 samples/cpp/tutorial_code/viz/creating_widgets.cpp create mode 100644 samples/cpp/tutorial_code/viz/launching_viz.cpp create mode 100644 samples/cpp/tutorial_code/viz/transformations.cpp create mode 100644 samples/cpp/tutorial_code/viz/widget_pose.cpp diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index ebee5bd0a8..21c781b6ad 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -82,6 +82,8 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) ocv_list_filterout(cpp_samples "/gpu/") endif() + ocv_list_filterout(cpp_samples "viz") + foreach(sample_filename ${cpp_samples}) get_filename_component(sample ${sample_filename} NAME_WE) OPENCV_DEFINE_CPP_EXAMPLE(${sample} ${sample_filename}) diff --git a/samples/cpp/tutorial_code/viz/bunny.ply b/samples/cpp/tutorial_code/viz/bunny.ply new file mode 100644 index 0000000000..7d34233395 --- /dev/null +++ b/samples/cpp/tutorial_code/viz/bunny.ply @@ -0,0 +1,5752 @@ +ply +format ascii 1.0 +comment zipper output +element vertex 1889 +property float x +property float y +property float z +property float confidence +property float intensity +element face 3851 +property list uchar int vertex_indices +end_header +-0.0369122 0.127512 0.00276757 0.850855 0.5 +-0.0457707 0.130327 0.00306785 0.900159 0.5 +-0.0708847 0.149834 0.0388672 0.398443 0.5 +-0.00331557 0.130403 0.0212208 0.85268 0.5 +-0.0211979 0.1272 0.00915278 0.675938 0.5 +-0.0265255 0.12592 0.00874866 0.711533 0.5 +0.0339261 0.112038 0.0269672 0.652757 0.5 +0.0376485 0.110455 0.0145481 0.708171 0.5 +-0.0259368 0.111118 0.0379115 0.454541 0.437538 +0.027952 0.120939 0.0215377 0.533079 0.5 +-0.0628308 0.155987 -0.0150105 0.404517 0.5 +0.0390029 0.106711 0.0215202 0.535542 0.5 +0.0447976 0.0950477 0.00866471 0.579563 0.425995 +-0.0330636 0.173619 -0.0031267 0.365607 0.5 +-0.0808069 0.136243 0.0495014 0.499575 0.5 +-0.0705086 0.12445 0.0526685 0.564827 0.5 +0.00874873 0.131225 0.0145345 0.748371 0.5 +0.0401015 0.106711 0.00874166 0.680399 0.5 +0.0379483 0.100145 -0.00827134 0.600054 0.5 +-0.0906538 0.137201 0.0207305 0.824561 0.5 +-0.0841655 0.110667 0.0275273 0.690889 0.5 +-0.0705214 0.156214 0.0144536 0.807492 0.5 +-0.083872 0.15212 0.0282652 0.248168 0.41865 +0.00305028 0.12432 0.0332425 0.555044 0.354559 +0.00870828 0.124165 0.0330348 0.653433 0.5 +-0.0328896 0.12613 0.00300653 0.898771 0.5 +-0.0506302 0.143065 0.0150611 0.691477 0.5 +-0.0757863 0.13637 0.050172 0.566256 0.5 +-0.0027191 0.128962 0.0264678 0.271491 0.462815 +-0.0460961 0.125118 0.0263142 0.539149 0.5 +-0.0785104 0.0942728 -0.0109192 0.710999 0.5 +0.0216915 0.125373 0.0211452 0.530957 0.5 +-0.0888469 0.124305 0.00237041 0.635593 0.5 +0.040386 0.100825 -0.00303043 0.574857 0.5 +-0.0884145 0.117791 0.00268555 0.487167 0.430737 +-0.0319074 0.177421 -0.00491879 0.269231 0.447035 +-0.0765825 0.143224 0.0455148 0.414139 0.5 +-0.0209748 0.112544 0.0388613 0.482541 0.5 +-0.020836 0.179425 -0.0221622 0.341071 0.440034 +-0.0377039 0.167987 -0.0130391 0.396317 0.473039 +-0.0331765 0.12681 0.00839958 0.896274 0.5 +0.00893926 0.127114 0.0292916 0.350014 0.41288 +-0.044944 0.131083 0.0147963 0.599596 0.5 +-0.0266041 0.12515 0.00282384 0.73687 0.5 +0.0144285 0.12328 0.0319185 0.625269 0.5 +0.019244 0.122284 0.0308314 0.611204 0.34486 +-0.0390225 0.167317 0.00215527 0.413994 0.469929 +-0.08808 0.129976 0.00206377 0.625486 0.5 +-0.0537203 0.142608 0.0266058 0.696873 0.5 +0.043095 0.0980072 0.0191617 0.665192 0.5 +0.0432138 0.100117 0.00866473 0.691828 0.5 +0.0415448 0.0944954 0.0275695 0.671611 0.5 +-0.0578726 0.155337 0.0149245 0.394763 0.437313 +-0.0231577 0.157375 -0.0046304 0.136389 0.380194 +-0.0683123 0.145735 0.0420568 0.751812 0.5 +-0.0708351 0.142847 0.0451248 0.627973 0.5 +-0.070664 0.0642894 0.0209789 0.413051 0.5 +-0.0761519 0.130581 0.0525324 0.629117 0.5 +-0.0640036 0.161784 -0.0208118 0.449093 0.5 +-0.0706461 0.155711 0.00252406 0.855717 0.5 +-0.0924366 0.118434 0.0399838 0.673877 0.5 +-0.0635349 0.156052 0.0148814 0.798496 0.5 +0.0282675 0.118192 0.0274382 0.635485 0.5 +0.0392736 0.0938857 -0.00915453 0.585857 0.459742 +-0.0695973 0.164844 -0.0174846 0.548789 0.5 +-0.00892354 0.123904 0.0330319 0.602316 0.374044 +0.0269099 0.0942476 0.0444911 0.649753 0.5 +-0.0146258 0.162377 -0.0144398 0.338176 0.5 +-0.0450983 0.167072 0.00289327 0.449091 0.5 +-0.0761536 0.172742 -0.0384391 0.256591 0.4298 +-0.0858274 0.105458 0.00472318 0.523819 0.297125 +0.0370431 0.110443 0.0207229 0.52623 0.448558 +0.0321593 0.0994027 0.0380657 0.733041 0.5 +-0.075287 0.146433 0.0428582 0.424358 0.5 +-0.0395145 0.171107 0.000531747 0.452893 0.5 +-0.0839586 0.11215 0.00148754 0.436727 0.419097 +0.0446848 0.0883378 0.0216285 0.487783 0.481728 +0.0161783 0.127819 0.0220535 0.481793 0.5 +-0.00251635 0.0397232 0.0474087 0.280725 0.5 +0.00303163 0.0406968 0.0460422 0.331809 0.5 +-0.0143059 0.128197 0.00333856 0.693854 0.5 +-0.0526117 0.155596 0.0109972 0.561042 0.5 +-0.0332043 0.17776 -0.00906223 0.212789 0.5 +0.0394391 0.106654 0.00306577 0.522321 0.489889 +-0.0923799 0.1249 0.0327641 0.848517 0.5 +0.0454681 0.0882959 0.0146642 0.575503 0.5 +-0.0274495 0.179802 -0.00925837 0.258799 0.457369 +-0.072504 0.146297 0.0429682 0.549207 0.5 +-0.0579959 0.129793 0.0383118 0.658867 0.444043 +0.043117 0.0923689 0.0251649 0.622686 0.5 +-0.00865718 0.130323 0.0149721 0.633691 0.5 +-0.0141304 0.129188 0.0147431 0.547632 0.5 +-0.0707877 0.15583 0.00921954 0.739059 0.5 +-0.00952731 0.127041 0.0281475 0.375412 0.377874 +-0.0646289 0.153404 0.0329146 0.855321 0.5 +-0.0706939 0.15347 0.0328596 0.444959 0.455263 +0.0208126 0.118434 0.0336393 0.519282 0.5 +-0.0396566 0.173008 -0.00299705 0.274377 0.177706 +-0.0442176 0.170815 -0.00391429 0.245926 0.5 +-0.0582565 0.0395149 0.0457796 0.417977 0.459314 +-0.0523033 0.0401501 0.04623 0.454776 0.456044 +-0.0760211 0.161274 -0.0145891 0.267801 0.372187 +-0.0693983 0.163016 -0.0140293 0.403228 0.45768 +0.0399663 0.106491 0.014952 0.713602 0.5 +0.041536 0.0950084 -0.00475737 0.490139 0.464008 +-0.0470079 0.163779 0.00528295 0.432857 0.486946 +-0.0402546 0.161678 0.00298655 0.447592 0.5 +-0.0386569 0.0389805 0.0441153 0.509262 0.5 +-0.0704175 0.166991 -0.0216976 0.332592 0.447054 +-0.0254201 0.0886622 0.0503827 0.608282 0.5 +-0.0886334 0.137429 0.00876953 0.549009 0.5 +-0.014179 0.12627 0.0266417 0.420759 0.5 +-0.0360017 0.17408 -0.0118959 0.409753 0.289042 +-0.0886251 0.0937834 0.00823534 0.753697 0.5 +-0.0648672 0.155874 -0.00891497 0.595216 0.5 +-0.0704508 0.137752 -0.00774011 0.446131 0.5 +-0.0750154 0.166247 -0.0219558 0.263106 0.5 +0.0299465 0.114869 0.0300239 0.642356 0.5 +0.0398138 0.0998788 0.0273101 0.51725 0.5 +-0.015242 0.111698 0.0407424 0.605597 0.5 +-0.0700387 0.118219 0.0524379 0.585543 0.5 +0.0149973 0.112399 0.0386082 0.669811 0.5 +-0.036487 0.171225 0.000545037 0.438578 0.5 +-0.0641664 0.118551 -0.00968333 0.569796 0.5 +-0.071817 0.166979 -0.0463822 0.381568 0.451091 +-0.0913559 0.14534 0.0246937 0.648478 0.5 +0.00903703 0.112569 0.0396571 0.549283 0.408623 +0.0324674 0.0997396 -0.0141603 0.732658 0.5 +0.0417911 0.101845 0.00188609 0.547756 0.5 +0.00302992 0.112517 0.0415434 0.592572 0.5 +-0.0650368 0.148485 0.0382561 0.62562 0.5 +-0.0706519 0.13063 0.0502497 0.563116 0.5 +-0.0144471 0.128935 0.00903509 0.682121 0.5 +0.00292575 0.131541 0.00912318 0.795238 0.5 +-0.0625682 0.151125 0.035875 0.463512 0.5 +0.0349829 0.113328 0.0214487 0.620597 0.5 +0.021327 0.0385664 0.0392992 0.259499 0.426724 +0.0145125 0.093771 0.0501571 0.654705 0.5 +-0.00923752 0.112849 0.0413907 0.615633 0.5 +0.0415329 0.100906 0.0210277 0.662312 0.5 +0.0422859 0.101486 0.0146614 0.569693 0.490777 +-0.0773783 0.112839 -0.00448759 0.505277 0.5 +-0.078035 0.137641 -0.00517379 0.466714 0.5 +0.00873437 0.106347 -0.0202193 0.792948 0.5 +0.0090324 0.13035 0.0211569 0.465873 0.5 +0.00301322 0.130902 0.0206741 0.592486 0.5 +-0.00286342 0.13115 0.0147367 0.587804 0.5 +-0.0391578 0.12569 0.0207996 0.438744 0.464814 +-0.0205725 0.123523 0.0265579 0.445477 0.415699 +-0.0644194 0.155634 0.00928477 0.611624 0.331941 +-0.0463385 0.131411 0.0207671 0.674928 0.5 +-0.0532034 0.0439067 0.044658 0.417403 0.440199 +-0.00297651 0.131046 0.00884967 0.738924 0.5 +-0.089664 0.137755 0.0263925 0.80362 0.5 +-0.00888731 0.124273 -0.00880284 0.767738 0.284429 +-0.0460971 0.0385107 0.0446891 0.654962 0.5 +-0.0649255 0.178874 -0.0579325 0.245129 0.411885 +-0.0329347 0.124601 0.0211235 0.32811 0.5 +-0.0831301 0.149901 0.0334123 0.331963 0.314683 +-0.0895652 0.093948 0.0149303 0.603378 0.5 +-0.0328901 0.124518 -0.00282055 0.63839 0.5 +-0.0845271 0.106161 0.00204328 0.338681 0.43162 +-0.0469341 0.155816 0.00872921 0.470367 0.484595 +0.0206202 0.123943 0.0267275 0.477255 0.5 +-0.026256 0.117499 0.0321672 0.543293 0.5 +-0.021392 0.118632 0.0336445 0.468887 0.429556 +-0.0195069 0.116132 0.0368525 0.534732 0.411301 +-0.0761618 0.118382 0.0520923 0.490413 0.5 +0.00889281 0.0395765 0.0451727 0.476347 0.38769 +-0.0534736 0.159548 0.00753828 0.476667 0.5 +-0.0469464 0.161226 0.00680216 0.495992 0.483766 +-0.0574886 0.154862 0.0204748 0.677314 0.5 +0.0317199 0.117635 0.0202007 0.579556 0.5 +0.0378683 0.105514 -0.00259159 0.588286 0.5 +-0.0811847 0.137693 -0.00253994 0.641736 0.5 +-0.0764348 0.124515 0.0528345 0.65366 0.5 +0.0343816 0.106104 -0.00900254 0.534403 0.5 +0.0457922 0.088316 0.00867097 0.586292 0.439394 +-0.0703288 0.0944195 -0.0159143 0.511499 0.5 +-0.0756048 0.0937947 -0.0135536 0.429902 0.5 +-0.058657 0.156369 0.0093256 0.31374 0.5 +-0.0637335 0.153848 0.00222718 0.478676 0.5 +-0.0777278 0.0960024 0.0363437 0.678588 0.5 +-0.0868519 0.136556 0.00309926 0.517441 0.5 +-0.0455299 0.0432404 0.0432162 0.712662 0.5 +-0.0402011 0.045749 0.0408051 0.669165 0.320516 +-0.0654123 0.160403 -0.0149066 0.335302 0.5 +-0.0318898 0.0387174 0.0510004 0.553401 0.5 +-0.0267997 0.0453977 0.0509311 0.501112 0.5 +-0.0271043 0.0396972 0.0535379 0.487956 0.5 +-0.0215575 0.0460868 0.0517209 0.709553 0.5 +-0.0143078 0.0445295 0.0504368 0.575852 0.5 +-0.00981594 0.043264 0.0493162 0.448927 0.393067 +-0.00348436 0.044054 0.0472086 0.598081 0.5 +0.009577 0.0458139 0.0465877 0.519814 0.433928 +0.02048 0.111086 0.0379569 0.681163 0.5 +-0.0141831 0.128547 0.0200007 0.293349 0.5 +-0.0526702 0.144108 0.0210347 0.639643 0.5 +-0.0634838 0.17384 -0.0527131 0.549906 0.5 +-0.0366553 0.171999 -0.0125745 0.436075 0.5 +-0.0525548 0.131228 0.0328277 0.727547 0.5 +-0.0659567 0.132023 0.0442925 0.724494 0.5 +-0.0921726 0.11832 0.0267606 0.794672 0.5 +0.0452792 0.0882737 0.00268175 0.507794 0.5 +-0.00305651 0.112889 0.0417789 0.635396 0.5 +-0.0451955 0.161396 -0.00871567 0.424682 0.5 +-0.0402914 0.160933 -0.0115368 0.411895 0.405943 +-0.0521414 0.0701165 0.0389584 0.682177 0.456916 +-0.0383315 0.093604 -0.0232581 0.72469 0.5 +-0.0690556 0.137374 0.046352 0.61723 0.5 +-0.0695996 0.167401 -0.0516299 0.518552 0.5 +-0.00246047 0.124102 0.0337609 0.444043 0.5 +-0.0398624 0.128204 0.00299348 0.864483 0.5 +-0.0753331 0.149032 0.0395625 0.432149 0.5 +-0.0701432 0.160618 -0.00917801 0.464361 0.5 +-0.0589378 0.0440425 0.0434222 0.437887 0.447715 +-0.0207164 0.126445 0.00312493 0.710427 0.5 +-0.00850666 0.0467286 0.0481052 0.613173 0.5 +0.00300323 0.0450308 0.0469911 0.464978 0.5 +-0.0802174 0.148665 0.0379438 0.47939 0.5 +-0.0819961 0.130698 0.0513437 0.54405 0.5 +0.00273088 0.106333 -0.0209927 0.733954 0.5 +-0.0757273 0.0885687 -0.0138399 0.397424 0.5 +-0.0698477 0.0882875 -0.0167823 0.420617 0.5 +-0.0668508 0.159243 -0.0102161 0.42216 0.440727 +-0.0226988 0.0885773 0.0536309 0.546444 0.5 +-0.00281419 0.0990077 0.0505614 0.455087 0.5 +0.0452902 0.0696213 0.0253974 0.33948 0.5 +-0.0525629 0.0472823 0.040482 0.279548 0.5 +-0.046959 0.0466581 0.0408127 0.43714 0.5 +-0.0691348 0.156682 -0.00276369 0.629099 0.5 +-0.0897599 0.150073 0.0220744 0.276354 0.5 +-0.0702883 0.155637 0.0263654 0.47565 0.441038 +-0.0765031 0.154893 0.0266005 0.799832 0.5 +-0.00804843 0.0987379 0.0505998 0.327523 0.438474 +0.0300791 0.11567 -0.00430465 0.66246 0.5 +-0.0923054 0.117757 0.0334441 0.476916 0.5 +-0.0331192 0.0449511 0.0462474 0.432059 0.466683 +-0.0337794 0.113308 0.034612 0.683562 0.5 +-0.0521291 0.113769 0.0349566 0.515399 0.5 +0.0437636 0.0825382 -0.0027974 0.568535 0.5 +-0.0202819 0.126016 0.0210507 0.374818 0.437592 +0.0327872 0.043925 0.0295904 0.650152 0.5 +-0.0453372 0.155266 -0.0075525 0.386286 0.5 +-0.0284609 0.173987 -0.0175958 0.379432 0.418735 +0.0268448 0.0881755 -0.0223077 0.715629 0.5 +-0.0308231 0.0923023 -0.0246377 0.474586 0.431409 +-0.0899732 0.149975 0.0141115 0.257143 0.5 +0.0381804 0.105121 0.0266947 0.534482 0.490368 +0.00842001 0.12907 0.0258154 0.374593 0.448613 +-0.0266549 0.0942999 -0.0265555 0.294426 0.332222 +-0.0279896 0.0475815 0.0485532 0.381268 0.5 +-0.0150037 0.048073 0.0483203 0.576068 0.5 +-0.00298993 0.0473817 0.0491102 0.446744 0.431743 +0.00376754 0.0477551 0.0502037 0.495901 0.44823 +0.00748504 0.0473851 0.0493363 0.494952 0.5 +-0.0581651 0.149751 0.032858 0.470966 0.5 +-0.0720688 0.136456 0.0490662 0.625357 0.5 +-0.0810638 0.0939541 -0.0082617 0.685573 0.5 +0.0380863 0.0458646 0.0307423 0.807573 0.5 +-0.0253234 0.182998 -0.0108168 0.245054 0.5 +-0.0230508 0.183235 -0.0110157 0.246322 0.458572 +0.00323317 0.129146 0.0263855 0.347796 0.441746 +-0.0626125 0.149788 -0.00343342 0.691705 0.5 +-0.0591471 0.0466998 0.0395843 0.0883466 0.213805 +-0.0353862 0.0471292 0.0414241 0.656538 0.5 +-0.0194948 0.0486404 0.0485565 0.373069 0.5 +-0.00849455 0.0521633 0.0517688 0.61481 0.5 +-0.00296485 0.051429 0.0527827 0.53012 0.5 +0.00279019 0.0517664 0.0528352 0.560812 0.423049 +0.00904034 0.0517165 0.051222 0.558244 0.5 +0.0443839 0.0943042 0.00268377 0.582116 0.455816 +-0.0886145 0.111113 0.0148415 0.604102 0.5 +-0.0885219 0.144027 0.0329221 0.623335 0.5 +0.0440719 0.0937787 0.0206165 0.493368 0.454688 +0.0436531 0.0980341 0.0146596 0.668233 0.5 +-0.0650976 0.153799 -0.00285808 0.715743 0.5 +-0.0517297 0.0490759 0.0371355 0 0 +-0.0331222 0.0518259 0.0385377 0.676102 0.5 +-0.0377352 0.127448 0.0152358 0.612182 0.5 +-0.00906608 0.100701 0.0460122 0.338462 0.5 +-0.0410683 0.128416 0.0134054 0.417331 0.5 +-0.0712056 0.158724 -0.00521868 0.246338 0.5 +-0.0266313 0.0501544 0.044695 0.182016 0.5 +-0.0211065 0.0519946 0.0455753 0.195646 0.404388 +-0.0168667 0.0505241 0.0476889 0.520032 0.5 +-0.0147601 0.0527687 0.050103 0.451613 0.5 +-0.0626395 0.149972 -0.00897733 0.363787 0.461156 +-0.090861 0.124732 0.00627835 0.587249 0.5 +-0.0255786 0.0923499 -0.0315595 0.294527 0.5 +-0.0709738 0.172947 -0.052768 0.460427 0.5 +-0.0588974 0.143232 -0.00327646 0.48145 0.5 +-0.0943643 0.12436 0.0216467 0.570519 0.5 +0.0337044 0.112449 -0.00269877 0.532211 0.5 +-0.0515051 0.136557 0.0263185 0.72719 0.5 +-0.00886593 0.121199 0.0360577 0.614897 0.5 +-0.061729 0.155665 -0.0259512 0.690546 0.5 +-0.0862637 0.10567 0.0206042 0.519516 0.5 +-0.0895584 0.138606 0.032689 0.685876 0.5 +-0.0268168 0.123904 0.0208113 0.428255 0.5 +0.0341937 0.0515433 0.033081 0.609925 0.5 +0.0401268 0.0512743 0.0322702 0.669803 0.5 +0.0449306 0.0526595 0.0319582 0.655209 0.5 +-0.0405348 0.117168 0.0319438 0.657986 0.5 +-0.0636902 0.155546 -0.0390642 0.523327 0.5 +0.0278663 0.100401 0.0410064 0.689793 0.5 +-0.0275828 0.179275 -0.0157605 0.314049 0.5 +-0.0758871 0.0942302 0.0383961 0.647987 0.457049 +0.0138371 0.129201 0.0203961 0.412341 0.5 +-0.0152723 0.0998429 0.0451638 0.271215 0.427554 +-0.00916763 0.129718 0.0206646 0.438679 0.430152 +-0.0512444 0.0516901 0.0334801 0.192432 0.5 +-0.0461563 0.0523184 0.0379981 0.311543 0.5 +-0.0410001 0.05272 0.0393793 0.629588 0.477809 +-0.0270993 0.0526642 0.0393104 0.155274 0.5 +0.0434924 0.0931097 -0.00154028 0.576953 0.480183 +-0.0823819 0.112683 0.045427 0.438131 0.5 +-0.092066 0.118055 0.00909937 0.325678 0.5 +-0.00448884 0.121713 0.0362976 0.591545 0.5 +0.0147346 0.129423 0.0143146 0.840212 0.5 +-0.0158113 0.161888 -0.00973584 0.202865 0.5 +-0.0778838 0.149704 -0.00337488 0.403345 0.5 +-0.0865357 0.12477 -0.00166991 0.677311 0.5 +0.0153656 0.126058 0.0275381 0.479299 0.429147 +-0.0388913 0.123761 0.0249778 0.514489 0.5 +-0.0390351 0.121238 0.0283673 0.510424 0.470651 +-0.0324963 0.120237 0.0283344 0.568849 0.348087 +-0.0149052 0.12311 0.0316417 0.446842 0.5 +-0.0582873 0.117688 0.0386719 0.634635 0.5 +-0.0626536 0.161861 -0.0264031 0.685413 0.5 +-0.0818147 0.141639 0.0444825 0.392929 0.5 +0.0350734 0.100071 0.0345975 0.716199 0.5 +0.0311856 0.11215 0.0310216 0.689434 0.5 +-0.0335778 0.11743 0.031458 0.525408 0.5 +-0.059637 0.153475 0.031348 0.93076 0.5 +-0.0481256 0.0536625 0.0362191 0.58186 0.5 +-0.059026 0.156388 0.00269852 0.133166 0.5 +-0.0211187 0.0578754 0.0461125 0.660553 0.5 +-0.082738 0.124721 0.050554 0.665202 0.5 +-0.0466997 0.11363 0.0348133 0.568902 0.5 +-0.0107262 0.179662 -0.0277472 0.400699 0.458536 +0.0347725 0.0894441 -0.0170339 0.702331 0.5 +-0.0891825 0.100351 0.0148945 0.574286 0.477791 +0.0257275 0.122894 0.0207337 0.498278 0.5 +-0.0883949 0.100277 0.00841226 0.477822 0.5 +-0.0649858 0.155518 0.0263367 0.864791 0.5 +-0.0768402 0.154073 0.00257877 0.57436 0.5 +-0.0576877 0.154146 0.0262123 0.402162 0.5 +-0.0266966 0.125729 0.0145923 0.393422 0.5 +-0.076376 0.155782 0.0208875 0.505065 0.5 +-0.0763295 0.167188 -0.039594 0.405226 0.426366 +-0.0771877 0.100229 -0.0103313 0.528684 0.5 +-0.0153681 0.0590839 0.0519909 0.652683 0.5 +-0.010206 0.0576345 0.0535443 0.781548 0.413019 +-0.00350044 0.0578672 0.0543757 0.774384 0.5 +0.00300818 0.0568916 0.0538692 0.704357 0.5 +0.0088308 0.0580497 0.0529859 0.692645 0.5 +0.0410915 0.0820775 -0.00893411 0.500391 0.430286 +0.0395449 0.0576373 0.0318985 0.612032 0.4505 +-0.0762443 0.139336 0.0484763 0.588653 0.42756 +-0.0324306 0.120379 -0.00955344 0.656019 0.5 +-0.0194451 0.0881559 0.0557639 0.449983 0.473992 +-0.074787 0.159471 -0.00898201 0.281303 0.5 +-0.0639935 0.15611 0.0210031 0.687157 0.5 +-0.0762438 0.153101 0.0322442 0.323875 0.45561 +-0.00876679 0.128727 0.025102 0.735708 0.5 +0.0282216 0.112237 -0.00983067 0.567922 0.385391 +-0.0451341 0.0593225 0.0387559 0.511245 0.5 +-0.0405005 0.0579499 0.040202 0.540369 0.5 +-0.033993 0.0584028 0.038704 0.646744 0.5 +-0.0272756 0.0585468 0.0382285 0.571263 0.5 +-0.0248608 0.122913 0.0245429 0.379391 0.5 +-0.0825276 0.154355 0.0206132 0.385494 0.444119 +-0.00884271 0.129403 0.00305159 0.702319 0.5 +0.0207587 0.126654 0.0147646 0.624434 0.5 +-0.0394868 0.173351 -0.00839443 0.199648 0.251821 +-0.028421 0.114019 0.0347746 0.603313 0.5 +-0.0193575 0.122009 0.0306737 0.55532 0.5 +-0.0691626 0.161675 -0.0514614 0.38665 0.5 +-0.0516736 0.15006 0.0148119 0.716684 0.5 +-0.0156325 0.120151 0.0349054 0.470635 0.336572 +0.0467454 0.0582319 0.0314404 0.576429 0.5 +-0.0770165 0.0685425 0.0147863 0.703257 0.5 +-0.00967101 0.173225 -0.0264945 0.379771 0.5 +-0.0213141 0.184813 -0.0151112 0.186313 0.403961 +-0.0766524 0.0882188 0.0382876 0.650646 0.5 +-0.0540219 0.0521463 0.0110698 0.270787 0.5 +-0.0219451 0.126821 0.0155536 0.534695 0.5 +-0.0820391 0.153392 0.0264506 0.292051 0.4047 +-0.0213183 0.124468 -0.00290836 0.782181 0.5 +-0.0268364 0.123465 -0.00321538 0.727949 0.5 +-0.0312035 0.177796 -0.0133521 0.371348 0.5 +-0.00749945 0.0598042 0.0553302 0.778631 0.5 +-0.00108951 0.0601245 0.0554892 0.776353 0.5 +0.00280202 0.0599746 0.0555283 0.768603 0.5 +-0.051797 0.118119 0.033678 0.677092 0.438456 +0.00302464 0.131618 0.0149353 0.692956 0.5 +0.0446005 0.0942619 0.0151198 0.554026 0.5 +-0.0880636 0.111855 0.00852285 0.304511 0.3924 +-0.0704321 0.144096 -0.0148369 0.130446 0.5 +-0.0820967 0.0943634 0.0322765 0.629357 0.5 +-0.0269642 0.120812 0.0275676 0.345323 0.386314 +-0.0540164 0.149968 0.0253393 0.49489 0.5 +-0.0800337 0.0995053 -0.00770139 0.499264 0.5 +0.00922138 0.12038 0.0360924 0.562107 0.5 +0.00286056 0.117968 0.0387331 0.649494 0.5 +-0.0936229 0.118494 0.0206524 0.664933 0.5 +-0.0409923 0.113229 0.035109 0.667726 0.5 +-0.0822185 0.154488 0.0146661 0.500539 0.5 +-0.0625956 0.155202 -0.0329876 0.814083 0.5 +-0.0462511 0.124621 -0.00898124 0.590842 0.5 +-0.0220336 0.160676 -0.00426008 0.309766 0.47069 +-0.065621 0.172767 -0.0466049 0.613718 0.5 +-0.0762614 0.155884 0.0148687 0.717415 0.5 +-0.0644988 0.149044 -0.0265159 0.690046 0.5 +-0.0581979 0.0593456 0.0210895 0.079935 0 +-0.0335439 0.122618 0.0254024 0.514037 0.5 +-0.0826578 0.153434 0.00921403 0.601617 0.5 +-0.049999 0.132417 0.0286961 0.650903 0.5 +0.0088217 0.131096 0.00864908 0.834131 0.5 +-0.0154842 0.0644282 0.0533754 0.608033 0.445048 +-0.00871951 0.065015 0.0556827 0.650491 0.470895 +-0.00324815 0.0640003 0.0562816 0.762387 0.5 +0.00292601 0.0643094 0.0563956 0.748671 0.5 +0.00738462 0.0651614 0.0553402 0.488299 0.46872 +-0.0143174 0.116971 0.037836 0.441459 0.5 +-0.00299223 0.118083 0.0390751 0.65526 0.5 +-0.00864301 0.117816 0.0385662 0.681198 0.5 +-0.0532884 0.0571719 0.0206631 0.106703 0 +-0.0882588 0.100387 0.0210097 0.535268 0.5 +-0.0324377 0.099703 -0.0227313 0.620611 0.5 +0.0425072 0.0603725 0.0302275 0.744481 0.5 +0.0523383 0.0580401 0.0290457 0.405493 0.41666 +0.0413612 0.0877503 -0.00929235 0.635782 0.5 +-0.0581547 0.0620148 0.0270981 0.448705 0.5 +-0.0530328 0.0590503 0.0266933 0.136202 0.5 +-0.0477227 0.135526 0.0148654 0.740469 0.5 +0.00323512 0.0983053 0.0504424 0.395048 0.366076 +0.0150627 0.119642 0.034806 0.696033 0.374342 +-0.0453373 0.0643061 0.0391142 0.587502 0.5 +-0.0394097 0.0644278 0.0414133 0.715885 0.5 +-0.033068 0.0642666 0.0396407 0.650585 0.5 +-0.0270237 0.0644489 0.0395335 0.617817 0.5 +-0.0881604 0.149479 0.0268507 0.265855 0.5 +-0.0640727 0.143434 -0.00894036 0.668887 0.5 +0.00286033 0.121151 0.036139 0.623932 0.5 +-0.0827306 0.138152 0.0466993 0.412428 0.5 +-0.00261511 0.127006 0.030132 0.335862 0.5 +0.0355841 0.108498 -0.00452523 0.461807 0.466834 +0.0219203 0.114136 0.0356941 0.554683 0.5 +-0.0379555 0.161954 -0.0128021 0.499753 0.5 +-0.0526362 0.0643632 0.0340621 0.277414 0.5 +0.025874 0.123374 0.0143811 0.506732 0.5 +-0.0451406 0.131184 0.00901599 0.493237 0.5 +-0.075778 0.155361 -0.00310678 0.708579 0.5 +-0.0739145 0.156437 -0.0274945 0.645327 0.5 +-0.0833056 0.100778 -0.00354288 0.490806 0.397415 +-0.0767099 0.173942 -0.0452732 0.259897 0.5 +0.00846106 0.116985 0.038033 0.66824 0.5 +-0.0200899 0.184788 -0.020546 0.237973 0.106197 +-0.046571 0.120413 0.0285524 0.752764 0.5 +-0.0515313 0.123718 -0.0088569 0.538005 0.5 +0.0212116 0.105804 -0.0171101 0.576137 0.468722 +-0.0938613 0.124487 0.0151416 0.737559 0.5 +0.0414591 0.064577 0.0290352 0.617794 0.5 +0.0466725 0.0643471 0.0285539 0.486488 0.5 +0.0526423 0.0634018 0.0283831 0.501229 0.5 +-0.0468141 0.168322 -0.00285433 0.371444 0.5 +-0.0869152 0.0944156 0.00293118 0.494536 0.346642 +-0.0773713 0.161559 -0.0267238 0.476378 0.5 +-0.0396095 0.126677 -0.00334699 0.853498 0.5 +-0.0271315 0.0764239 0.0455715 0.693464 0.5 +-0.0587953 0.107012 -0.0177177 0.484023 0.5 +-0.0748314 0.11156 -0.00720996 0.44421 0.5 +-0.0642623 0.0888181 -0.018733 0.676741 0.5 +-0.0325172 0.0881157 -0.0255424 0.370176 0.330832 +0.00325654 0.0700086 0.0561047 0.731659 0.5 +0.0103151 0.0636713 0.0537558 0.477793 0.458716 +0.0432701 0.0979967 0.00267804 0.544182 0.465461 +-0.0708223 0.156244 0.021207 0.768676 0.5 +-0.0584176 0.0702277 0.0384322 0.673529 0.5 +-0.0703207 0.112305 -0.00963846 0.530989 0.5 +-0.0581653 0.0881983 -0.0208369 0.619673 0.5 +-0.0443038 0.0877156 -0.0218942 0.693083 0.5 +-0.0488091 0.0660127 0.0373959 0.829801 0.5 +0.00269411 0.126911 0.030114 0.419275 0.5 +0.0239692 0.12105 0.0288706 0.523768 0.5 +-0.0469203 0.117468 0.0314407 0.649888 0.5 +-0.091552 0.143361 0.0201623 0.515231 0.5 +-0.0907563 0.143859 0.0263089 0.504684 0.469425 +-0.0495713 0.144022 0.00976642 0.636632 0.45621 +-0.0770934 0.15583 -0.0147903 0.519503 0.5 +-0.0868322 0.105634 0.00887573 0.731519 0.5 +-0.082848 0.131648 -0.00299747 0.386393 0.5 +-0.0384249 0.106407 -0.0201393 0.79815 0.5 +-0.0823953 0.118841 -0.00336022 0.540306 0.5 +-0.0102333 0.0876697 -0.0375101 0.564234 0.5 +-0.00789361 0.089842 -0.0363492 0.755212 0.5 +-0.0579097 0.111769 -0.0161856 0.463258 0.5 +0.0140074 0.105793 -0.0193841 0.554632 0.5 +-0.00328561 0.105435 -0.0225198 0.740261 0.5 +-0.0409613 0.070972 0.0419904 0.795206 0.5 +-0.033501 0.0710512 0.0409793 0.706864 0.5 +-0.0272732 0.0701361 0.0410332 0.726443 0.5 +-0.0161963 0.127121 0.0228897 0.305628 0.5 +-0.0190644 0.127936 0.0133818 0.519435 0.5 +-0.0149926 0.0694778 0.0545159 0.595577 0.5 +-0.00932719 0.0707313 0.0562936 0.785998 0.5 +-0.002994 0.0710941 0.0575426 0.779773 0.5 +0.00838831 0.0714267 0.0556585 0.671976 0.5 +0.0102531 0.0693533 0.0547665 0.525573 0.5 +-0.0323939 0.153399 -0.00240332 0.209483 0.5 +0.0435981 0.0881514 0.0254203 0.603121 0.478265 +-0.0586529 0.124882 -0.00781093 0.700525 0.5 +-0.0204287 0.107045 -0.022046 0.723165 0.5 +-0.0382961 0.0879422 -0.0229335 0.629507 0.5 +-0.081573 0.113394 -0.00173083 0.508624 0.426711 +-0.0380811 0.154778 -0.00889149 0.748063 0.5 +-0.00212588 0.0889926 -0.0354677 0.782073 0.5 +0.00904065 0.100193 -0.0222794 0.54652 0.5 +-0.0467068 0.0700493 0.0405769 0.710023 0.5 +-0.0779974 0.151244 0.0352264 0.347296 0.5 +0.0149019 0.116126 0.0367849 0.635361 0.5 +-0.07603 0.106301 -0.0087688 0.520423 0.5 +-0.0885261 0.137839 0.0393964 0.651389 0.5 +-0.0703112 0.131278 -0.00857724 0.737784 0.5 +0.0419377 0.0703605 0.0288832 0.54196 0.5 +0.0514194 0.0684326 0.0256968 0.512602 0.5 +-0.0922548 0.124813 0.0393757 0.806636 0.5 +0.0135035 0.128105 0.0250558 0.487288 0.424656 +-0.0704618 0.125421 -0.00881334 0.801453 0.5 +-0.0703931 0.118731 -0.00840961 0.381625 0.5 +-0.0719685 0.106305 -0.0114493 0.499561 0.5 +-0.0646972 0.161498 -0.0573125 0.41682 0.5 +0.0463693 0.0715128 0.0216754 0.461473 0.448797 +-0.0538246 0.153497 0.0152346 0.602795 0.402362 +-0.0142869 0.0724666 0.0554243 0.617853 0.5 +-0.0394057 0.118512 -0.01336 0.602235 0.5 +-0.0280509 0.0880065 -0.0330858 0.33771 0.5 +-0.00957701 0.168254 -0.0212321 0.359593 0.5 +-0.0445856 0.167324 -0.00782662 0.413138 0.327414 +-0.0513101 0.161594 -0.00355965 0.292939 0.5 +-0.0702356 0.179304 -0.0569867 0.253404 0.5 +-0.0644695 0.168402 -0.0398946 0.676128 0.5 +-0.0089459 0.130139 0.00911776 0.703889 0.5 +0.00219503 0.0880369 -0.0342201 0.75972 0.5 +-0.0268891 0.16726 -0.0174204 0.847505 0.5 +-0.0525985 0.155054 -0.00368706 0.37123 0.419006 +-0.0761618 0.131736 -0.00696723 0.42394 0.44361 +-0.0759576 0.07099 0.0265672 0.757943 0.5 +-0.00875341 0.10588 -0.02285 0.71177 0.5 +-0.0519242 0.1493 -0.00277595 0.483301 0.5 +-0.016371 0.18465 -0.0214272 0.271878 0.5 +-0.020548 0.0705632 0.0520411 0.601639 0.5 +-0.0813371 0.120073 0.049533 0.662828 0.5 +-0.0625087 0.149934 -0.0150319 0.415531 0.480025 +-0.0831098 0.10651 0.0273461 0.515033 0.5 +-0.011119 0.163582 -0.018751 0.17813 0.5 +-0.00291057 0.101147 0.0456419 0.307462 0.5 +-0.0635467 0.0660523 0.0318653 0.49936 0.45677 +-0.0511979 0.0873878 -0.0217212 0.75515 0.5 +-0.0530335 0.0740367 0.0417219 0.727079 0.5 +-0.0465007 0.0756701 0.0421325 0.696934 0.5 +-0.022314 0.0760359 0.0530306 0.607912 0.5 +-0.0151351 0.0764056 0.0563566 0.616605 0.5 +-0.00900601 0.0766621 0.0575852 0.791265 0.5 +-0.00299732 0.0767339 0.0584651 0.678647 0.450838 +0.00347424 0.0769755 0.0565905 0.523043 0.5 +0.00860763 0.0767538 0.0557293 0.612782 0.5 +-0.0271239 0.156216 -0.00302734 0.139755 0.329034 +-0.0633091 0.16738 -0.0580906 0.358909 0.45373 +-0.0873943 0.144225 0.00902371 0.583528 0.5 +-0.0626891 0.162297 -0.0470925 0.70746 0.5 +0.0370111 0.110397 0.00265294 0.516602 0.481774 +-0.0744006 0.144062 -0.00864565 0.417075 0.5 +-0.0244124 0.183841 -0.0135068 0.166659 0.5 +-0.0803381 0.0715473 0.0150483 0.5669 0.5 +-0.0644528 0.0761561 0.040638 0.610448 0.476331 +-0.0588413 0.0753794 0.0421022 0.634349 0.5 +-0.0524294 0.077372 0.0433357 0.774603 0.5 +-0.0484981 0.0769334 0.043281 0.674446 0.5 +-0.0414954 0.0773856 0.0429005 0.752035 0.5 +-0.0395008 0.0754808 0.0425134 0.72256 0.5 +-0.033488 0.0764759 0.0414605 0.748994 0.5 +-0.0627838 0.162163 -0.0530538 0.691143 0.5 +0.0381456 0.0881056 -0.0138675 0.676152 0.5 +-0.0642837 0.0396418 0.039624 0.532543 0.5 +-0.0526672 0.121335 -0.010917 0.523608 0.5 +-0.0738104 0.162942 -0.037093 0.458525 0.324439 +-0.0490869 0.13938 0.00889895 0.657159 0.5 +-0.0495771 0.166027 -0.00171113 0.322064 0.5 +-0.0709736 0.161609 -0.0450808 0.365011 0.420984 +0.0251847 0.12195 0.0254854 0.524179 0.5 +-0.0193615 0.0781018 0.0558163 0.595703 0.4544 +-0.0265458 0.120645 -0.00911332 0.52669 0.5 +-0.061796 0.155741 -0.0207923 0.443336 0.5 +-0.082476 0.110295 0.0324103 0.745977 0.5 +-0.0691674 0.156314 -0.050857 0.360984 0.5 +-0.0622848 0.16236 -0.0396288 0.427869 0.464762 +-0.088248 0.113803 0.0264606 0.595923 0.5 +-0.0575392 0.0787026 0.0436363 0.801201 0.5 +-0.0298439 0.0782596 0.0421168 0.771067 0.5 +-0.0677617 0.0876701 0.0434928 0.59211 0.5 +-0.0921939 0.131884 0.015227 0.781723 0.5 +-0.0878987 0.111742 0.0209206 0.698028 0.5 +-0.049353 0.139298 0.0147955 0.761861 0.5 +-0.0327071 0.173321 -0.0149209 0.384317 0.5 +-0.0866298 0.152851 0.0149144 0.267781 0.5 +-0.0779646 0.100025 0.035185 0.697079 0.5 +-0.0935537 0.118404 0.0151524 0.667612 0.5 +-0.084908 0.10801 0.0228537 0.694681 0.5 +-0.0210677 0.0821213 0.0562096 0.557699 0.5 +-0.0149957 0.082187 0.0572635 0.665194 0.5 +-0.00899671 0.0822178 0.0576875 0.71377 0.5 +-0.00299966 0.0822055 0.0574653 0.668024 0.472979 +0.0034748 0.0817533 0.0567544 0.69456 0.5 +0.00824833 0.082992 0.0556315 0.615627 0.5 +0.0102414 0.0812949 0.0546523 0.424956 0.485927 +-0.0398496 0.123966 -0.00878898 0.60318 0.5 +-0.092257 0.124769 0.00902091 0.309094 0.468872 +-0.0436728 0.126191 0.0209533 0.472028 0.413108 +-0.0820425 0.105873 -0.00271871 0.341089 0.347157 +-0.0663016 0.0807623 0.0424437 0.632223 0.5 +-0.0639939 0.0836688 0.0439754 0.778832 0.5 +-0.058539 0.0825906 0.0439671 0.770991 0.5 +-0.0521209 0.0822523 0.0446262 0.782751 0.5 +-0.0467559 0.0828569 0.0439458 0.699516 0.399968 +-0.0424962 0.0810729 0.0423266 0.617938 0.5 +-0.0404903 0.0830123 0.0430984 0.712874 0.5 +-0.0365108 0.0825773 0.0434355 0.675696 0.5 +-0.032204 0.0824171 0.0421121 0.529763 0.5 +-0.0864005 0.152981 0.0204492 0.250247 0.416029 +-0.0235661 0.115415 0.0353667 0.518805 0.471584 +-0.0764871 0.111685 0.0461598 0.498936 0.5 +-0.0763895 0.14977 -0.00829972 0.604451 0.5 +-0.0754801 0.161855 -0.0327796 0.39691 0.5 +-0.0285733 0.0828247 0.0462702 0.636794 0.5 +-0.0862819 0.100797 0.0028483 0.65379 0.5 +0.021088 0.08242 0.0504086 0.491924 0.475524 +-0.0801892 0.143128 -0.00230055 0.641961 0.5 +0.00844098 0.124407 -0.00878569 0.555015 0.5 +0.0147552 0.0825883 0.0529115 0.480476 0.5 +-0.061995 0.161169 -0.032654 0.499509 0.5 +-0.0807571 0.1525 0.0307996 0.295115 0.454522 +-0.00295953 0.130272 0.00279699 0.742188 0.5 +-0.0153619 0.0884791 0.0565599 0.818561 0.5 +-0.00899729 0.0878977 0.0570287 0.818958 0.5 +-0.00299611 0.0880658 0.0568489 0.695384 0.5 +0.00301457 0.0885291 0.0562756 0.81087 0.5 +0.00834267 0.0873808 0.0555541 0.577038 0.479545 +-0.00897481 0.0941651 -0.0338408 0.678465 0.5 +0.0314278 0.11673 0.0250113 0.597807 0.5 +-0.0760602 0.155337 0.0093949 0.68566 0.5 +0.0257808 0.116776 -0.00728909 0.54747 0.36626 +-0.0646577 0.0882843 0.0447113 0.69894 0.5 +-0.058996 0.0882997 0.0449149 0.778337 0.5 +-0.0529958 0.0883132 0.0451395 0.696869 0.45083 +-0.0465421 0.0881579 0.0443187 0.605881 0.5 +-0.0404961 0.0876863 0.0430941 0.556958 0.5 +-0.0331792 0.0885648 0.04366 0.668172 0.5 +-0.0280482 0.0879652 0.046363 0.699915 0.5 +0.0150626 0.0881784 0.0517745 0.702815 0.5 +0.0205955 0.087113 0.0492325 0.678548 0.5 +-0.0702712 0.0823874 0.0409431 0.628092 0.5 +-0.0296926 0.0896882 -0.0286839 0.317989 0.390463 +-0.0236137 0.179242 -0.0115629 0.264741 0.5 +-0.0809391 0.100029 0.0323433 0.683272 0.5 +-0.0928336 0.130683 0.0207107 0.62518 0.472282 +-0.0761771 0.156201 -0.0204165 0.612769 0.5 +0.0146577 0.129396 0.00843576 0.595962 0.5 +0.0104845 0.089766 0.0542005 0.46622 0.5 +-0.072579 0.161253 -0.0389447 0.482103 0.5 +-0.0322741 0.110391 -0.0184574 0.809584 0.5 +-0.0550172 0.150108 0.027792 0.412797 0.5 +-0.071635 0.0883254 0.0414652 0.604622 0.463567 +-0.0424904 0.0895336 0.0426086 0.959715 0.5 +0.0207945 0.0897491 0.0484315 0.669841 0.5 +0.0273189 0.118845 -0.00265658 0.615055 0.5 +0.0285218 0.121112 0.0162366 0.593248 0.434231 +-0.00899735 0.0930598 0.0559298 0.639163 0.5 +-0.00291176 0.118727 -0.0144021 0.826286 0.5 +-0.0885191 0.113233 0.0327948 0.447552 0.461926 +-0.0713744 0.0938304 0.0415269 0.544171 0.444972 +-0.0641029 0.0935514 0.0439488 0.597795 0.395518 +-0.0584965 0.0944146 0.0446213 0.678752 0.5 +-0.0515853 0.0939836 0.0442383 0.634435 0.477778 +-0.0465591 0.0937901 0.0436103 0.714507 0.5 +-0.0414914 0.0942416 0.0425268 0.490492 0.46307 +-0.0377723 0.0933327 0.0434889 0.620752 0.5 +-0.0332864 0.0945766 0.0443868 0.723538 0.5 +-0.0263807 0.094318 0.0450568 0.620324 0.5 +-0.0141606 0.0929618 0.0553898 0.503825 0.5 +-0.00319641 0.0930898 0.0557853 0.624082 0.5 +0.00150357 0.0931879 0.0551544 0.492015 0.5 +0.00367616 0.0950752 0.0535295 0.508462 0.5 +0.00915739 0.0941794 0.0519212 0.597357 0.452723 +0.0216553 0.0937794 0.0473202 0.671835 0.5 +-0.0702968 0.174481 -0.045888 0.43732 0.455145 +-0.0305889 0.168899 -0.00702359 0.59106 0.5 +-0.0528191 0.162649 0.00296711 0.343566 0.5 +-0.0890968 0.0940104 0.0208024 0.539357 0.478012 +-0.0626249 0.173112 -0.0586131 0.353011 0.447085 +-0.0443835 0.105923 -0.0201903 0.683228 0.5 +-0.0664958 0.0951776 0.0424531 0.672396 0.5 +-0.0324384 0.126415 0.0146752 0.445893 0.463327 +-0.0152469 0.0961657 0.0518098 0.323594 0.5 +-0.0097537 0.0960506 0.0535818 0.446732 0.426556 +-0.00304601 0.0963367 0.0537791 0.579525 0.5 +0.01642 0.0957081 0.0480381 0.687032 0.5 +-0.0876548 0.105191 0.0148253 0.774556 0.5 +-0.0699417 0.0763232 0.0381496 0.596573 0.5 +0.0358078 0.0958594 -0.0120328 0.738943 0.5 +0.0374966 0.100154 0.031249 0.720944 0.5 +-0.0530195 0.150059 0.0207323 0.696139 0.5 +-0.0905911 0.131765 0.0328667 0.816274 0.5 +-0.0709717 0.147309 -0.0268389 0.224341 0.389051 +-0.0443321 0.0935075 -0.0222668 0.709831 0.5 +-0.0400911 0.128618 0.00909496 0.81345 0.5 +-0.0710054 0.100275 0.0398128 0.481571 0.5 +-0.0653063 0.100124 0.0417262 0.670525 0.470095 +-0.0589969 0.0980495 0.0430328 0.779482 0.5 +-0.0529938 0.0980631 0.0432952 0.836255 0.5 +-0.0469951 0.0980659 0.043235 0.637806 0.5 +-0.0408476 0.100401 0.0414668 0.648927 0.395789 +-0.0323344 0.0988071 0.0435216 0.652032 0.5 +-0.0259464 0.0998425 0.0438947 0.737424 0.5 +-0.0212066 0.0999849 0.0444194 0.576924 0.5 +0.00749586 0.09835 0.0488255 0.46146 0.5 +0.0090271 0.101109 0.0469975 0.470012 0.5 +0.0153076 0.100008 0.0472449 0.600016 0.5 +0.0208175 0.100067 0.0453866 0.595024 0.46889 +-0.0648326 0.131509 -0.00838673 0.790869 0.5 +-0.0740297 0.150832 -0.0323367 0.406089 0.5 +-0.0932444 0.124885 0.026841 0.802537 0.5 +-0.0633239 0.169093 -0.0610358 0.362406 0.5 +-0.0771158 0.162488 -0.0202679 0.465605 0.5 +-0.0585669 0.0647555 0.0323611 0.494963 0.328305 +0.0377689 0.110383 0.00969065 0.710008 0.5 +-0.0503559 0.0935892 -0.0218956 0.807094 0.5 +-0.0589961 0.101543 0.042437 0.529374 0.5 +-0.0516647 0.101981 0.0417488 0.647378 0.5 +-0.0469248 0.101325 0.0421166 0.608323 0.5 +-0.0352173 0.101965 0.0413638 0.751982 0.5 +0.00285015 0.100935 0.0464433 0.395489 0.5 +-0.075479 0.150312 -0.0143808 0.730394 0.5 +-0.078936 0.108126 -0.00525459 0.540251 0.381971 +-0.0251472 0.168981 -0.0187156 0.757996 0.5 +-0.071457 0.113692 0.0499983 0.429195 0.5 +-0.0747771 0.0997536 0.0377868 0.551123 0.5 +-0.0902919 0.137212 0.0146286 0.495279 0.5 +-0.0264568 0.105883 0.0411765 0.58994 0.471484 +-0.0209966 0.1044 0.0429589 0.797197 0.5 +-0.0145208 0.105597 0.0430511 0.780555 0.5 +-0.00899316 0.10622 0.0435541 0.510194 0.5 +-0.00289533 0.105882 0.0438861 0.384284 0.5 +0.00245231 0.105621 0.0429868 0.332307 0.5 +0.00945613 0.104903 0.0439002 0.435482 0.5 +0.0149913 0.104769 0.0443348 0.548532 0.5 +-0.0772186 0.106139 0.0350601 0.430274 0.367589 +-0.0708601 0.106945 0.0381598 0.402417 0.5 +-0.0652985 0.106577 0.0390805 0.558067 0.398761 +-0.0583896 0.105623 0.0405326 0.594554 0.5 +-0.0529341 0.106445 0.0398435 0.644542 0.398207 +-0.0461638 0.105797 0.0404843 0.759883 0.5 +-0.0400204 0.106789 0.0388993 0.653599 0.5 +-0.03311 0.106322 0.0394461 0.532024 0.5 +0.0193026 0.10477 0.0431964 0.486674 0.480281 +-0.0501412 0.13774 0.00286739 0.569746 0.5 +0.0266104 0.105911 0.0384052 0.650339 0.5 +0.0438719 0.088439 -0.0031027 0.506353 0.478726 +-0.0590381 0.113203 0.0362299 0.87726 0.5 +-0.021499 0.107851 0.0414162 0.584043 0.5 +-0.0164951 0.107881 0.0420289 0.633836 0.5 +0.00450524 0.107918 0.0419336 0.79888 0.5 +0.00856234 0.108229 0.0410531 0.820786 0.5 +0.0149994 0.10779 0.0412845 0.598409 0.5 +0.0213049 0.106041 0.0409433 0.561561 0.479574 +-0.0336665 0.167843 -0.00338268 0.478764 0.5 +-0.0587789 0.131705 -0.00671001 0.673026 0.5 +-0.0443517 0.100306 -0.0215281 0.825942 0.5 +-0.0147306 0.179604 -0.0266222 0.40888 0.5 +0.0159582 0.108177 -0.0177822 0.564672 0.468958 +-0.0638447 0.138119 -0.00733006 0.633194 0.5 +-0.0330953 0.167861 -0.0155539 0.527374 0.428366 +-0.0643684 0.125359 -0.00876153 0.813046 0.5 +-0.032583 0.161992 -0.0142418 0.852313 0.5 +-0.068568 0.110392 0.0392194 0.353622 0.364353 +-0.0643494 0.112195 0.0388907 0.34696 0.5 +-0.0593722 0.112082 0.0373875 0.588374 0.5 +-0.0529986 0.110472 0.0373551 0.513233 0.408461 +-0.0468613 0.11028 0.0378862 0.569336 0.5 +-0.040984 0.110496 0.0370883 0.553647 0.5 +-0.0320055 0.110468 0.0370438 0.565129 0.5 +-0.0074871 0.110717 0.042649 0.617568 0.5 +-0.00449218 0.110714 0.0426582 0.621679 0.5 +0.0250033 0.110611 0.0368459 0.631257 0.5 +0.025919 0.0995286 -0.0189206 0.684181 0.5 +-0.06973 0.112153 0.0457184 0.746569 0.5 +-0.045604 0.148834 -0.00329924 0.521986 0.5 +-0.0653006 0.0947889 -0.0177657 0.582853 0.5 +-0.0906677 0.13318 0.0277848 0.773217 0.5 +-0.0331508 0.094474 -0.0237799 0.742 0.5 +-0.0575764 0.0941613 -0.0208023 0.703326 0.5 +-0.0200586 0.0397198 0.0532237 0.447203 0.5 +-0.0203685 0.0352888 0.051184 0.291685 0.457265 +-0.0764163 0.125947 -0.00745144 0.524375 0.5 +-0.0205906 0.167551 -0.0139677 0.809186 0.5 +0.025858 0.116851 0.0315289 0.660225 0.5 +-0.0139279 0.167191 -0.021044 0.669958 0.5 +-0.0587481 0.149802 -0.00133886 0.562881 0.5 +0.0144191 0.0395247 0.0443396 0.266796 0.5 +0.0332953 0.105473 0.0329627 0.721815 0.5 +-0.0647461 0.114313 -0.0115219 0.592211 0.5 +-0.0520818 0.0353771 0.0449331 0.341981 0.5 +-0.015004 0.0392095 0.0513548 0.312679 0.5 +-0.0094925 0.0384962 0.049554 0.302651 0.5 +-0.0638496 0.117631 0.0454477 0.559641 0.5 +-0.0573025 0.136864 0.033162 0.554568 0.5 +0.0189101 0.0400942 0.0428502 0.270107 0.5 +-0.0508192 0.124393 0.0332635 0.581555 0.5 +-0.0182623 0.180885 -0.017743 0.594618 0.5 +-0.0651271 0.150343 -0.0325707 0.505808 0.5 +0.0332966 0.0936886 0.0400216 0.637373 0.5 +-0.0463011 0.149493 0.00833001 0.611316 0.5 +0.00260773 0.0354887 0.0450013 0.261253 0.345588 +-0.0780807 0.10971 0.0423535 0.916894 0.5 +-0.0542262 0.124756 0.0369858 0.64506 0.5 +-0.0402584 0.0361447 0.0436625 0.193197 0.5 +-0.00317483 0.0942874 -0.0331049 0.71511 0.325502 +-0.0151032 0.179716 -0.0207621 0.731902 0.5 +0.026141 0.0403246 0.0327265 0.294647 0.339561 +-0.0640247 0.111376 -0.0136272 0.608847 0.5 +0.027817 0.112309 0.0339118 0.692282 0.5 +-0.0586332 0.142774 0.0334953 0.761767 0.5 +-0.0146622 0.167501 -0.0154455 0.61604 0.5 +-0.0270893 0.167298 -0.00866399 0.642638 0.5 +0.0152056 0.045813 0.0442638 0.487785 0.5 +0.0190988 0.0442996 0.0429 0.362689 0.463942 +0.0215694 0.0456112 0.041209 0.479281 0.5 +0.0257452 0.0459137 0.0381185 0.444171 0.5 +0.0387365 0.0944447 0.0327088 0.718127 0.5 +0.0287308 0.0456722 0.0347466 0.335561 0.431941 +-0.0151805 0.173809 -0.0213305 0.730436 0.5 +-0.0658903 0.118253 0.0498126 0.307185 0.5 +-0.0628345 0.093206 -0.0188544 0.659442 0.5 +-0.0643065 0.142451 0.0394123 0.621016 0.5 +-0.040079 0.150283 0.00280951 0.491474 0.5 +-0.026851 0.173268 -0.00983852 0.620534 0.5 +-0.0207913 0.173767 -0.0147826 0.653794 0.5 +-0.0582334 0.124238 0.0403406 0.70004 0.5 +-0.0683337 0.131545 0.0479709 0.732904 0.5 +-0.0693547 0.10637 -0.012803 0.472443 0.5 +-0.0428668 0.157627 0.0050419 0.670804 0.5 +-0.0476449 0.130368 0.0258834 0.623828 0.5 +0.0379451 0.0817167 -0.0141547 0.644934 0.5 +0.0312298 0.0470286 0.0324465 0.426433 0.5 +-0.0662284 0.138149 0.042896 0.72515 0.5 +-0.0644094 0.105575 -0.0158634 0.566501 0.5 +0.0411271 0.0443713 0.0285474 0.466284 0.5 +-0.0830031 0.0762361 0.0150296 0.67606 0.5 +-0.0660167 0.123488 0.0501643 0.718404 0.5 +-0.0416352 0.155329 0.00636435 0.466436 0.5 +-0.0388456 0.155994 0.00477206 0.438555 0.402124 +-0.0551732 0.116538 0.0359195 0.457649 0.5 +-0.0600069 0.134082 0.0369434 0.682472 0.5 +0.0452816 0.0453284 0.0263124 0.471094 0.5 +0.0513161 0.0463154 0.0204963 0.342211 0.398387 +-0.0106687 0.172847 -0.0215627 0.69267 0.5 +-0.0147735 0.18419 -0.0259341 0.309641 0.5 +0.0301064 0.106776 0.0358091 0.72383 0.5 +-0.063709 0.125122 0.0457451 0.712215 0.420475 +0.0473431 0.0499217 0.0295077 0.554948 0.5 +0.0497106 0.0482066 0.0259506 0.48379 0.5 +0.0518484 0.0518415 0.0267161 0.416499 0.5 +-0.0162732 0.172938 -0.0174582 0.719256 0.5 +0.0355097 0.107304 0.0291151 0.718782 0.5 +-0.0552656 0.143077 0.0300537 0.622521 0.5 +-0.0637191 0.136482 0.0388176 0.603354 0.5 +-0.0199086 0.161072 -0.00863325 0.350317 0.5 +-0.0209172 0.179282 -0.0148523 0.455842 0.5 +0.014511 0.0513519 0.0474271 0.589102 0.5 +-0.0610259 0.126912 0.0416133 0.698375 0.5 +0.0539905 0.0494141 0.0219114 0.418448 0.5 +0.00925922 0.118865 -0.0148674 0.54369 0.457314 +-0.0268384 0.162091 -0.00836699 0.546076 0.486591 +-0.0367024 0.163198 -0.00107067 0.680811 0.5 +-0.0336432 0.155948 0.00188963 0.445666 0.44081 +-0.0280966 0.159587 0.000483069 0.431301 0.5 +-0.026491 0.16163 -0.00321758 0.537982 0.323001 +0.0206613 0.0528733 0.0451655 0.647628 0.324331 +0.0231576 0.0513069 0.0414753 0.507052 0.5 +0.0266044 0.0526516 0.039853 0.635463 0.446542 +0.0309772 0.0527823 0.0371348 0.671735 0.5 +0.0239371 0.103424 0.0418106 0.654526 0.5 +0.0568895 0.0527484 0.0209204 0.474964 0.5 +-0.0664209 0.11329 0.0441331 0.212624 0.5 +-0.0326789 0.162384 -0.00243762 0.543585 0.5 +0.0145199 0.0932586 -0.026363 0.546403 0.5 +-0.0543983 0.119186 0.0365781 0.502204 0.44785 +-0.0564272 0.132376 0.0357966 0.720059 0.5 +-0.0501636 0.142911 0.00230897 0.376445 0.5 +-0.043714 0.147707 0.0038501 0.245798 0.5 +-0.0291346 0.177171 -0.00534178 0.371295 0.5 +0.0357304 0.100363 -0.0111604 0.61591 0.5 +0.0133943 0.0541536 0.0499521 0.532724 0.5 +0.0551089 0.0545007 0.0253961 0.545646 0.5 +0.0291033 0.0572886 0.0407089 0.633826 0.5 +0.0585723 0.0583402 0.0214893 0.549998 0.477428 +-0.0740322 0.0656952 0.0144875 0.594594 0.5 +-0.0749844 0.179305 -0.0518221 0.216638 0.5 +0.0145778 0.0585769 0.0501691 0.387785 0.5 +0.0214876 0.058332 0.0470549 0.596242 0.5 +0.0259507 0.0590004 0.0437762 0.663038 0.5 +0.032833 0.0585633 0.0387158 0.630786 0.5 +0.0358218 0.0578374 0.0350365 0.591179 0.5 +0.0360585 0.0951301 0.0364902 0.726421 0.5 +-0.0886806 0.118283 0.0459142 0.444358 0.5 +0.0562736 0.0586365 0.0253398 0.57284 0.5 +0.0303311 0.0951295 0.0419589 0.717458 0.5 +-0.0222315 0.167389 -0.0110472 0.688671 0.5 +-0.0543257 0.136577 0.0307959 0.688078 0.5 +-0.0500074 0.150447 0.0117579 0.563476 0.5 +-0.0616289 0.137406 0.0354744 0.592141 0.5 +-0.0319367 0.159507 0.00191749 0.44862 0.5 +-0.0634458 0.132148 0.0406867 0.731705 0.5 +0.0368678 0.0921989 0.0367449 0.708135 0.5 +-0.0728433 0.156137 -0.0339112 0.713518 0.5 +0.0389872 0.0640689 0.0330299 0.521361 0.5 +-0.0636611 0.1488 -0.0205996 0.618447 0.5 +0.0153938 0.0648444 0.0513036 0.554385 0.463079 +0.0213958 0.0645506 0.0473078 0.414803 0.412252 +0.0265105 0.0649235 0.0439721 0.611901 0.5 +0.0302364 0.0650657 0.0415975 0.600683 0.487653 +0.0331295 0.0642221 0.0397381 0.500385 0.490901 +0.0367885 0.065027 0.0366867 0.593561 0.5 +0.0563131 0.0650782 0.0252208 0.639437 0.5 +0.0591364 0.0644742 0.0211357 0.550839 0.448044 +-0.0110683 0.167098 -0.0167807 0.360187 0.5 +-0.0605202 0.146205 0.0366666 0.591479 0.5 +0.0194528 0.0665736 0.0491642 0.603282 0.5 +-0.0286777 0.158132 0.000508817 0.402765 0.431383 +0.0253025 0.0989569 0.0434277 0.623394 0.5 +-0.0349979 0.152158 8.20736e-05 0.217633 0.5 +0.014665 0.070627 0.0528306 0.52613 0.5 +0.0202908 0.071041 0.0498828 0.634288 0.435356 +0.0230702 0.0702991 0.0473835 0.571849 0.5 +0.0263693 0.0706238 0.0441789 0.622852 0.474797 +0.0328306 0.0707606 0.0401362 0.612279 0.409693 +0.0368832 0.070672 0.0365953 0.662199 0.5 +0.0398878 0.0705632 0.0325808 0.656566 0.5 +0.0579544 0.0694794 0.0198345 0.6125 0.5 +-0.0641704 0.063724 0.0268682 0.425507 0.418571 +-0.0919499 0.114216 0.0149265 0.530043 0.5 +0.0351624 0.0819076 -0.0172502 0.760295 0.5 +-0.0862408 0.119271 -0.00117534 0.455279 0.5 +-0.0294401 0.174958 -0.00579982 0.562984 0.5 +-0.0175288 0.165418 -0.0114925 0.675539 0.5 +-0.0617869 0.117789 0.0409144 0.40334 0.5 +0.0301891 0.0723658 0.0418804 0.606777 0.5 +-0.0822099 0.149486 0.00288044 0.385889 0.468811 +-0.0760271 0.175704 -0.0506937 0.340571 0.5 +-0.0652343 0.0614738 0.0211346 0.414933 0.425841 +-0.0266574 0.110394 -0.019007 0.783101 0.5 +-0.0813538 0.0779161 0.0268055 0.756683 0.5 +0.021417 0.118723 -0.00893569 0.549 0.5 +0.0149346 0.0759297 0.0536191 0.48671 0.476705 +0.0209886 0.0761609 0.0506055 0.575091 0.5 +0.0268396 0.0762089 0.0459193 0.572664 0.5 +0.0336785 0.0760737 0.0405166 0.630563 0.5 +0.0373422 0.0760306 0.0366776 0.505468 0.5 +0.0400324 0.0763062 0.0328345 0.645662 0.5 +0.0419048 0.076876 0.0296092 0.673034 0.5 +0.0438094 0.0763805 0.0258638 0.624347 0.5 +-0.0452412 0.118472 -0.0142046 0.833781 0.5 +0.0456773 0.0768089 0.0208187 0.458504 0.467907 +-0.050165 0.137714 0.0207618 0.606401 0.481088 +-0.00327054 0.111563 -0.0203549 0.551699 0.482404 +-0.0483236 0.145111 0.00757835 0.59165 0.5 +0.0310833 0.0775315 0.0432282 0.624343 0.5 +-0.046855 0.145222 0.00288431 0.195425 0.432502 +-0.0141716 0.10541 -0.0225802 0.672132 0.5 +0.0470348 0.0753979 0.0148736 0.455861 0.5 +-0.0611433 0.140542 0.0356184 0.646306 0.5 +0.0272779 0.0823714 0.0459243 0.61663 0.478488 +0.0309212 0.08255 0.0430252 0.611382 0.5 +0.0343037 0.0825412 0.0402907 0.613309 0.465282 +0.0370354 0.0824663 0.0369099 0.642585 0.5 +-0.0799946 0.147989 -0.000835337 0.484293 0.5 +-0.0774435 0.0690153 0.00961977 0.704234 0.277826 +0.0404363 0.0826995 0.0326021 0.686672 0.5 +0.0417479 0.0827335 0.0302524 0.63553 0.5 +0.0436887 0.0825508 0.0263844 0.61829 0.5 +0.0454407 0.0825465 0.0207137 0.601475 0.480065 +-0.0822812 0.116295 0.0482855 0.66926 0.5 +-0.0844726 0.0947391 -0.00345192 0.592061 0.5 +-0.020271 0.168003 -0.0193935 0.821267 0.5 +-0.0742716 0.0668501 0.0190414 0.706894 0.5 +0.026747 0.0882417 0.0458314 0.539865 0.389736 +0.0308722 0.0882572 0.0430146 0.948814 0.5 +0.0344922 0.0883047 0.0403697 0.638338 0.5 +0.0372481 0.0881263 0.0366393 0.643327 0.5 +0.039927 0.088094 0.0326668 0.711283 0.5 +0.0419027 0.0877782 0.0290815 0.667656 0.5 +0.00264738 0.112302 -0.019871 0.766242 0.5 +-0.0703315 0.1455 -0.0205576 0.136819 0.239158 +-0.0749446 0.137879 -0.00653312 0.459033 0.397283 +-0.0266967 0.114299 -0.0159903 0.856895 0.5 +-0.0869924 0.113518 0.00410409 0.344807 0.5 +-0.0142186 0.174013 -0.0259807 0.439072 0.5 +-0.0221564 0.157852 -0.00861651 0.254248 0.5 +-0.011587 0.164129 -0.0163045 0.228563 0.367524 +-0.00997381 0.169338 -0.0247765 0.42189 0.5 +-0.082875 0.143405 0.00186692 0.494272 0.5 +0.0203757 0.0354405 -0.00287175 0 0 +0.0191274 0.0363337 -0.00917714 0.174536 0.5 +0.0184456 0.036388 -0.013479 0.173751 0.5 +0.0149535 0.0347732 -0.0154937 0.144529 0.253209 +0.0221204 0.0372026 0.0342324 0.156956 0.287305 +0.039271 0.0382866 0.00854708 0.245023 0.5 +0.0397549 0.0398545 0.002614 0.276002 0.5 +0.0221892 0.0380614 -0.00446361 0.173629 0.5 +0.0179901 0.0369066 -0.0161835 0.336518 0.5 +0.0154148 0.0392444 -0.0212861 0.367832 0.5 +0.0208023 0.100118 -0.0213392 0.648293 0.46589 +0.0446004 0.0409064 0.00927401 0.208963 0.5 +0.0435625 0.0411355 0.00427044 0.357471 0.452104 +0.0381381 0.0411139 -0.00147908 0.514406 0.5 +-0.0478807 0.135207 0.00885778 0.482359 0.5 +0.0217274 0.0404287 -0.00964433 0.311593 0.5 +0.0206744 0.0405956 -0.0144437 0.473825 0.5 +0.0192578 0.0411681 -0.0195074 0.414351 0.5 +-0.0885736 0.112913 0.0395856 0.488806 0.5 +-0.026793 0.106457 -0.0218501 0.617481 0.5 +0.0481487 0.0428585 0.0145594 0.265572 0.5 +0.0521212 0.0461655 0.0089655 0.199267 0.5 +0.0480438 0.0430647 0.00724585 0.412258 0.5 +0.0460936 0.0434131 0.00284357 0.566688 0.5 +0.0285003 0.100485 -0.0168103 0.728425 0.5 +0.0269462 0.0395833 -0.00334578 0.464947 0.5 +-0.0907856 0.117838 0.00647331 0.421552 0.5 +-0.062721 0.167567 -0.0470628 0.645866 0.5 +-0.0799532 0.106813 0.0316838 0.420249 0.5 +0.0527437 0.0462125 0.0139554 0.286197 0.5 +0.0504533 0.0466263 0.00264513 0.57721 0.5 +-0.0322581 0.117324 -0.0133273 0.811815 0.5 +0.0272475 0.0455966 -0.00927071 0.533119 0.5 +-0.0146455 0.0942084 -0.0337341 0.520871 0.5 +-0.0411545 0.16722 -0.010818 0.48116 0.5 +-0.0721385 0.156112 -0.0384102 0.511983 0.468875 +0.0456803 0.0474217 -0.00311192 0.412576 0.5 +0.0239407 0.0433254 -0.00969837 0.651864 0.5 +0.021084 0.0462585 -0.0205303 0.476548 0.5 +-0.0348527 0.0351549 -0.0307351 0.16856 0.5 +-0.0699867 0.0663066 0.0259153 0.590849 0.43032 +-0.0747071 0.149891 -0.0201453 0.5851 0.5 +-0.0845448 0.13725 0.000743181 0.580039 0.5 +0.0549514 0.0484178 0.0163982 0.295573 0.5 +0.0264565 0.0466261 -0.0141039 0.515417 0.5 +0.0225276 0.0444655 -0.0157683 0.505631 0.5 +0.0330538 0.0938135 -0.0160538 0.699679 0.5 +0.0526476 0.0694992 0.00297306 0.629664 0.372945 +0.0528544 0.0581339 -0.00277966 0.592036 0.5 +-0.0571464 0.0671799 0.0361705 0.503626 0.472266 +-0.0651544 0.157167 -0.0515491 0.708429 0.5 +-0.0493189 0.133682 0.00119868 0.355836 0.438333 +-0.032962 0.10595 -0.0206729 0.810434 0.5 +-0.0649538 0.155656 -0.045631 0.820472 0.5 +-0.0390456 0.150445 -0.00354536 0.204281 0.5 +0.0574365 0.051618 0.0145183 0.351624 0.5 +0.0574129 0.0522531 0.00903377 0.511629 0.5 +0.0536112 0.0500965 0.00204174 0.768402 0.5 +0.0512204 0.0520121 -0.00218354 0.534755 0.5 +0.0471226 0.0515811 -0.00481298 0.434179 0.5 +0.033443 0.047576 -0.0063817 0.557462 0.465257 +0.00280933 0.118297 -0.0158208 0.570337 0.473222 +-0.0147841 0.10125 -0.0238408 0.771507 0.5 +-0.0620037 0.167422 -0.0527165 0.538383 0.466596 +0.0559147 0.0528382 0.00339683 0.824166 0.5 +0.0334801 0.0518506 -0.00825293 0.591066 0.5 +0.0287814 0.0501171 -0.0157926 0.574224 0.5 +0.0256197 0.0485542 -0.0190548 0.421586 0.5 +-0.00863537 0.118406 -0.0146114 0.827086 0.5 +-0.0148322 0.117675 -0.014701 0.559736 0.5 +-0.0615138 0.145712 -0.00481276 0.466074 0.5 +0.0232531 0.12083 -0.00456186 0.617393 0.5 +-0.0401535 0.0342718 -0.0275149 0.0979878 0.5 +0.0302657 0.0496868 -0.0107289 0.647285 0.5 +0.0320066 0.111334 -0.00737407 0.536101 0.5 +-0.0211003 0.120417 -0.0102482 0.732965 0.5 +-0.0204991 0.117125 -0.0140803 0.767014 0.5 +-0.00910263 0.0383602 -0.025776 0.274297 0.5 +-0.0525144 0.11229 -0.0171034 0.442719 0.484227 +0.0202353 0.123713 -0.00247094 0.59012 0.5 +-0.0701749 0.0347541 -0.0017891 0.135623 0.5 +-0.00340266 0.114844 -0.0176928 0.826111 0.5 +0.0310248 0.053713 -0.0140522 0.572913 0.5 +0.0268191 0.0528482 -0.020339 0.412387 0.455219 +-0.0147458 0.120673 -0.0105853 0.653192 0.5 +0.0270905 0.106214 -0.0146756 0.603346 0.5 +0.0465541 0.0697991 0.00228503 0.590477 0.5 +-0.00300122 0.100676 -0.0235814 0.77298 0.5 +-0.0755874 0.076212 0.033468 0.651011 0.5 +0.059738 0.0572998 0.0151736 0.624329 0.5 +0.0595394 0.0578717 0.00861672 0.650231 0.5 +0.0572091 0.0580526 0.00253507 0.577167 0.5 +-0.0142907 0.123147 -0.00746744 0.689207 0.5 +0.0211831 0.112303 -0.0140834 0.636933 0.5 +0.0347455 0.0565046 -0.010714 0.517615 0.5 +0.0249138 0.0825163 -0.0245877 0.759593 0.5 +-0.0382227 0.114521 -0.016178 0.845616 0.5 +-0.0819485 0.0761672 0.0208322 0.76776 0.5 +-0.0269557 0.0392251 -0.0293943 0.537642 0.5 +0.0377037 0.0593401 -0.00852013 0.537798 0.5 +0.0330295 0.0586306 -0.014729 0.60439 0.5 +0.0218121 0.0515865 -0.0236492 0.56032 0.5 +-0.0204953 0.0935908 -0.0331675 0.485557 0.5 +-0.0872217 0.113521 0.0440666 0.448078 0.427651 +-0.0271537 0.0351608 0.0509267 0.96808 0.5 +-0.0503825 0.106302 -0.0194598 0.649024 0.5 +0.0266611 0.0585067 -0.0219134 0.622435 0.5 +0.00975018 0.0945932 -0.0280451 0.504262 0.457756 +-0.0205524 0.122391 -0.00754739 0.498583 0.5 +-0.0668021 0.0909191 -0.0174744 0.566525 0.5 +-0.0856155 0.0942099 -0.00109094 0.420789 0.436678 +-0.0915274 0.11444 0.0204492 0.759207 0.5 +-0.0909048 0.131701 0.00809159 0.558083 0.5 +0.0404851 0.0578886 -0.0051698 0.425865 0.437223 +0.0295964 0.0580473 -0.0178274 0.608291 0.460655 +0.0266986 0.0941359 -0.0205949 0.662934 0.5 +-0.0677104 0.172869 -0.0572602 0.695141 0.5 +0.0142001 0.118043 -0.013917 0.45799 0.403894 +-0.0698171 0.0699687 0.0326375 0.529959 0.5 +0.0607097 0.0648802 0.0151632 0.434757 0.451533 +0.0609346 0.0630505 0.0131585 0.526971 0.5 +0.0602205 0.0643718 0.00864139 0.443146 0.456896 +0.0574055 0.0638877 0.00271573 0.413274 0.5 +-0.0797793 0.103858 -0.00660016 0.553637 0.5 +-0.0563867 0.137359 -0.00421998 0.659682 0.5 +0.0344512 0.0638263 -0.0152012 0.581486 0.5 +0.0307139 0.0605317 -0.0184589 0.617611 0.449874 +0.0185684 0.121789 -0.00725624 0.61441 0.349043 +-0.0456617 0.112414 -0.0169658 0.70381 0.5 +0.0456177 0.0644845 -0.00162168 0.572144 0.5 +-0.0584268 0.0349015 0.0441202 0.767369 0.5 +-0.0747982 0.0723674 0.0308514 0.656357 0.5 +-0.0699373 0.0621854 0.0151778 0.587415 0.5 +-0.052889 0.136519 -0.00170821 0.593683 0.5 +0.0410205 0.0644886 -0.00476733 0.363401 0.5 +0.0388712 0.0646166 -0.00976797 0.384344 0.5 +0.0514871 0.0637279 -0.00174794 0.518067 0.5 +-0.0787297 0.0744551 0.0267421 0.809934 0.5 +-0.0850281 0.144269 0.00618082 0.578063 0.5 +0.0313094 0.064487 -0.0188936 0.672704 0.5 +0.0267274 0.0646171 -0.0220842 0.752591 0.5 +0.0318737 0.0877439 -0.0192705 0.740422 0.5 +-0.0772455 0.143995 -0.00470939 0.452269 0.5 +0.0132576 0.110443 -0.0183541 0.539267 0.5 +-0.00289343 0.124723 -0.00863032 0.516883 0.5 +-0.0342868 0.038582 0.0485461 0.546061 0.5 +0.0200397 0.0876233 -0.0261205 0.735721 0.5 +0.0585453 0.0705354 0.0146976 0.608535 0.5 +0.0581405 0.0699819 0.00856199 0.483528 0.5 +0.056099 0.069436 0.00424359 0.385578 0.5 +0.0370479 0.0665186 -0.0132637 0.645736 0.5 +-0.062561 0.172971 -0.0616721 0.43069 0.5 +-0.0702718 0.15494 -0.0455472 0.29179 0.457421 +-0.0916259 0.130499 0.00930481 0.432982 0.472725 +-0.070021 0.148229 -0.0328231 0.322588 0.195946 +-0.0721274 0.0680183 0.0267753 0.656727 0.5 +-0.0745337 0.15067 -0.0264303 0.331486 0.5 +0.0431087 0.0713461 -0.002764 0.390428 0.45538 +0.0421659 0.0692525 -0.00466106 0.55545 0.5 +0.0345404 0.0699378 -0.0160391 0.727409 0.5 +-0.0342368 0.122912 -0.00708584 0.432969 0.5 +0.0401518 0.070932 -0.00951127 0.706551 0.5 +0.0370706 0.0707408 -0.013301 0.722628 0.5 +0.0310856 0.0702175 -0.0192905 0.761897 0.5 +0.0283004 0.0705453 -0.0222447 0.701199 0.5 +-0.00859023 0.101699 -0.0237897 0.731824 0.5 +-0.0328234 0.0400139 -0.029875 0.413461 0.5 +-0.0830588 0.11047 0.0397334 0.931001 0.5 +0.0142724 0.123237 -0.00806485 0.479991 0.484444 +-0.0760443 0.108637 0.0389078 0.769887 0.5 +-0.0732762 0.154939 -0.0321392 0.640327 0.5 +0.0160324 0.0889232 -0.0282477 0.595959 0.5 +-0.0901677 0.131361 0.0394374 0.633972 0.457764 +0.0455828 0.0768365 0.00270178 0.323813 0.5 +-0.0516717 0.0553965 0.014906 0.168077 0.5 +-0.0376545 0.121002 -0.0109724 0.599451 0.451266 +0.0466318 0.0762885 0.00910629 0.334003 0.5 +0.0437303 0.0769241 -0.00295564 0.541016 0.5 +0.0405043 0.0766784 -0.0084913 0.540094 0.5 +0.0369463 0.0762836 -0.0128837 0.716695 0.5 +0.0349351 0.0766648 -0.0155944 0.687304 0.5 +0.0319237 0.0763904 -0.0194186 0.722365 0.5 +0.0285208 0.0758075 -0.0225233 0.729644 0.5 +-0.0646857 0.068809 0.0348219 0.518098 0.396839 +-0.00335573 0.0986136 -0.0269283 0.762285 0.5 +-0.0383606 0.100112 -0.0217661 0.633523 0.5 +-0.0705433 0.149897 -0.0387319 0.143598 0.5 +-0.0247871 0.179215 -0.0188356 0.466421 0.5 +0.00339058 0.0937023 -0.0318365 0.697748 0.5 +-0.09099 0.142689 0.0226645 0.743514 0.5 +-0.0851088 0.102115 0.000391121 0.420019 0.403283 +0.00299202 0.124707 -0.00864775 0.631346 0.5 +-0.0649459 0.167336 -0.0329944 0.692397 0.5 +0.045975 0.0827243 0.0146716 0.494123 0.463874 +0.0461931 0.0827376 0.00867911 0.540283 0.443947 +0.0453461 0.0826602 0.00269811 0.520808 0.5 +0.032594 0.082231 -0.0190597 0.700575 0.5 +-0.0707752 0.142011 -0.00901143 0.440829 0.5 +-0.0396694 0.045239 -0.0210351 0.371561 0.5 +-0.0736488 0.145787 -0.0131048 0.298566 0.5 +-0.0661855 0.1779 -0.0529018 0.456268 0.5 +-0.0698006 0.179227 -0.0517285 0.330383 0.5 +-0.0719677 0.177848 -0.0474604 0.498199 0.393806 +-0.0131817 0.0974247 0.0509808 0.29677 0.5 +-0.0760529 0.177651 -0.0471457 0.200482 0.341482 +-0.0875274 0.149451 0.00937476 0.260452 0.5 +-0.0847504 0.149536 0.00652369 0.220089 0.5 +-0.0853843 0.0980412 -0.000554198 0.453316 0.5 +-0.070162 0.172945 -0.0393132 0.377002 0.42015 +-0.0669053 0.17136 -0.0404187 0.587367 0.5 +-0.0915765 0.114644 0.0108349 0.335405 0.476851 +0.0311175 0.116345 -0.00142056 0.524001 0.485056 +-0.09039 0.144074 0.0142555 0.571623 0.5 +0.0533752 0.0724173 0.00805773 0.504643 0.5 +0.0348115 0.113636 0.00289967 0.517745 0.5 +0.0321047 0.117128 0.00373672 0.512637 0.481334 +-0.0558554 0.16013 0.00226313 0.176407 0.35978 +0.0284127 0.12005 0.00266093 0.800124 0.5 +-0.0693417 0.151526 -0.0443255 0.162625 0.220555 +0.0509143 0.0733396 0.0112131 0.81315 0.5 +0.0485286 0.0726358 0.00856732 0.779683 0.5 +0.0251471 0.122517 0.00254898 0.804299 0.5 +-0.0684168 0.170157 -0.0319531 0.535557 0.5 +-0.071028 0.171274 -0.0325886 0.712016 0.5 +-0.0765634 0.155757 -0.00874762 0.256295 0.5 +0.0525206 0.0734678 0.0148876 0.468908 0.45355 +0.035521 0.113454 0.00908801 0.654915 0.5 +0.0208324 0.125627 0.00327965 0.76886 0.5 +-0.0476722 0.134348 0.0194434 0.579216 0.488505 +-0.0746083 0.171229 -0.0326516 0.439107 0.422901 +0.0322027 0.117616 0.0093642 0.646061 0.5 +0.0162523 0.127588 0.00132734 0.679655 0.445027 +-0.0914669 0.142805 0.0167223 0.344959 0.5 +0.0290775 0.120474 0.00686894 0.798143 0.5 +0.0135909 0.12914 0.00336546 0.632038 0.474565 +-0.0861635 0.100458 0.025719 0.514874 0.431291 +-0.0653051 0.165945 -0.0269849 0.665887 0.5 +-0.0698492 0.16889 -0.0268648 0.536219 0.5 +-0.07827 0.167473 -0.032496 0.259817 0.452429 +0.0215557 0.0945234 -0.0226594 0.630702 0.48336 +0.0260612 0.123082 0.00873766 0.803075 0.5 +0.00920342 0.130081 0.00248247 0.641161 0.5 +-0.0709934 0.170517 -0.0295248 0.566905 0.409383 +-0.0760202 0.167938 -0.0272636 0.242234 0.5 +0.0525229 0.0716654 0.0211203 0.349876 0.431389 +0.0207167 0.126566 0.00922145 0.763786 0.5 +-0.0746025 0.0998033 -0.0126456 0.503102 0.5 +-0.0864333 0.0890874 0.0257055 0.752441 0.5 +0.0354941 0.113435 0.0150848 0.708057 0.5 +0.0320737 0.117698 0.0146262 0.694886 0.5 +0.00294754 0.130714 0.00292443 0.849802 0.5 +-0.0256391 0.0823957 0.0519489 0.764034 0.5 +-0.0666258 0.165416 -0.0221631 0.534987 0.5 +-0.0804177 0.153092 0.00488677 0.321879 0.39417 +-0.0645623 0.0350017 0.0151892 0.352362 0.5 +-0.0627936 0.0352479 0.02012 0.616295 0.5 +-0.0642932 0.0349381 0.0264604 0.161121 0.384305 +-0.0642421 0.0397497 0.0267659 0.206373 0.5 +-0.0652419 0.0352202 0.0324357 0.167045 0.5 +-0.06432 0.0352261 0.0387914 0.349097 0.5 +-0.0869014 0.0944088 0.0260869 0.722262 0.5 +-0.026376 0.100403 -0.0237519 0.527518 0.47737 +-0.0704394 0.0348288 0.00888692 0.228898 0.5 +-0.0696375 0.039673 0.0091864 0.30841 0.5 +-0.0678064 0.035728 0.013362 0.509091 0.5 +-0.0778433 0.0819732 0.0354617 0.774608 0.5 +-0.0809318 0.0827942 0.0325 0.767831 0.5 +-0.0712316 0.038974 0.00275642 0.155719 0.237906 +-0.0616101 0.0379618 0.0219344 0 0 +-0.0653778 0.0407054 0.0323415 0.379158 0.5 +-0.0612949 0.040108 0.0438783 0.388361 0.5 +-0.0748891 0.0826916 0.0381154 0.772848 0.5 +-0.0841641 0.133769 0.0486564 0.546232 0.467433 +-0.0849106 0.0945271 0.0290479 0.754258 0.5 +-0.082994 0.144712 0.0404065 0.382972 0.420138 +-0.0265479 0.117619 -0.0132781 0.755106 0.5 +-0.0679678 0.0383221 0.0123903 0.271535 0.306541 +-0.0639259 0.0401146 0.0151101 0.258252 0.450399 +-0.0588527 0.0407802 0.0202136 0.51937 0.5 +-0.0869621 0.135589 0.0440584 0.520567 0.5 +-0.038827 0.0398484 0.042564 0.570175 0.5 +-0.0253238 0.0773437 0.0501603 0.646885 0.5 +0.00864855 0.111878 -0.0192252 0.821439 0.5 +-0.0625014 0.04424 0.0388616 0.455153 0.47063 +-0.088493 0.125258 0.0461673 0.674925 0.5 +0.0150785 0.10107 -0.0220372 0.749486 0.5 +-0.0810533 0.0876325 0.0334622 0.750019 0.5 +-0.0636602 0.0439221 0.0322355 0.437404 0.5 +-0.0823757 0.12585 -0.00459555 0.376136 0.464207 +-0.0374554 0.042873 0.0429512 0.492581 0.5 +-0.031328 0.0432863 0.0501185 0.483275 0.5 +-0.0841802 0.0875016 0.0285815 0.671149 0.464325 +-0.0690099 0.0427216 0.00298087 0.372436 0.5 +-0.0690323 0.0427133 0.00739115 0.277083 0.5 +-0.0642007 0.0449178 0.00895163 0.562755 0.5 +-0.0630005 0.0427497 0.0133004 0.520064 0.348086 +-0.0580777 0.0444032 0.0143596 0.493924 0.5 +-0.087476 0.130712 0.0458544 0.531379 0.477045 +-0.0837712 0.0999337 0.029339 0.668895 0.5 +-0.083719 0.0822846 0.0270932 0.660348 0.5 +-0.0209183 0.0934772 0.0512134 0.479975 0.5 +-0.0868983 0.142651 0.0383505 0.486766 0.469754 +-0.0588984 0.0467651 0.00989959 0.460736 0.319245 +-0.0529144 0.0464475 0.0158024 0.381525 0.5 +-0.0881654 0.0882094 0.0209192 0.624947 0.5 +-0.0494075 0.165901 0.000731671 0.369742 0.391777 +-0.0586114 0.0473978 0.0337061 0.152377 0.410418 +-0.05614 0.0517476 0.00835186 0.396733 0.5 +-0.0865231 0.148073 0.0321271 0.367072 0.452379 +-0.0308497 0.0493297 0.0429654 0.330168 0.454747 +-0.0769102 0.114994 0.0501188 0.653806 0.5 +-0.0209065 0.0959579 0.0474195 0.622864 0.5 +-0.0509947 0.0509637 0.0150799 0.759028 0.5 +0.00842415 0.0889657 -0.0320537 0.627702 0.5 +-0.0240561 0.0544386 0.0416973 0.433194 0.5 +-0.0510392 0.0524223 0.0203213 0.262945 0.5 +-0.0526208 0.0518271 0.027021 0.695325 0.5 +-0.0504022 0.0591186 0.0326891 0.768296 0.5 +-0.0478821 0.0590694 0.0363134 0.800191 0.5 +-0.0239128 0.0586553 0.0421308 0.768223 0.5 +-0.0759314 0.119228 -0.00697007 0.568703 0.5 +-0.0183181 0.0604564 0.0506182 0.70539 0.5 +-0.0298441 0.0972531 -0.0235715 0.830462 0.5 +-0.0241926 0.0628773 0.0422936 0.709715 0.5 +-0.0223998 0.06467 0.045979 0.606456 0.5 +-0.0192899 0.0641483 0.0503928 0.754401 0.5 +-0.0260109 0.172925 -0.0191453 0.51739 0.5 +-0.0265331 0.161574 -0.0144318 0.84044 0.5 +-0.0558556 0.15572 -0.00121016 0.41523 0.5 +-0.0599028 0.136466 -0.0064456 0.660892 0.5 +-0.063538 0.071665 0.0379463 0.556494 0.5 +-0.0200417 0.0869862 -0.0378876 0.500126 0.449734 +-0.0557176 0.105745 -0.0186241 0.707273 0.5 +-0.0530691 0.143914 -0.00100898 0.728895 0.5 +-0.0256688 0.0704637 0.0438935 0.717372 0.393932 +-0.0235577 0.0693774 0.0470203 0.657726 0.5 +-0.0525759 0.127247 -0.00521525 0.567734 0.5 +-0.0787859 0.131858 -0.00545913 0.44224 0.460808 +-0.0580212 0.120088 -0.0102747 0.564344 0.455328 +-0.0396294 0.110441 -0.0186258 0.62346 0.5 +-0.0210282 0.173113 -0.0214922 0.42389 0.352327 +-0.0547593 0.0563289 0.0107147 0.179388 0.5 +-0.0435534 0.0345758 -0.024752 0.176398 0.205782 +-0.0449833 0.0346921 -0.0207483 0.159962 0.261208 +-0.0443576 0.0390403 -0.0217491 0.178142 0.5 +-0.0462855 0.0345037 -0.0153112 0.189574 0.5 +-0.046619 0.0396457 -0.0141457 0.194812 0.5 +-0.00904923 0.0343826 -0.0246429 0.15305 0.5 +0.00311748 0.100303 -0.0227929 0.684313 0.5 +-0.0690809 0.0392217 -0.00181724 0.169982 0.409113 +-0.0920289 0.131041 0.0262349 0.856795 0.5 +-0.043414 0.0372487 -0.0253064 0.219927 0.5 +0.0280974 0.0818294 -0.0220931 0.752623 0.5 +-0.067702 0.169446 -0.0560134 0.487347 0.455218 +-0.0915377 0.129674 0.0312365 0.601516 0.48259 +-0.0663086 0.0411162 -0.00443149 0.346306 0.5 +-0.0731255 0.151935 -0.0368879 0.40925 0.5 +-0.0390145 0.0394889 -0.027598 0.3765 0.5 +-0.0637372 0.0437827 -0.00264533 0.37233 0.5 +-0.0605427 0.0425565 0.0246975 0.23689 0.5 +-0.0857603 0.130763 -0.000714461 0.66754 0.5 +-0.0520472 0.0403573 -0.0107411 0.62257 0.5 +-0.0568522 0.0434504 0.0224413 0.404188 0.5 +-0.043239 0.0429342 -0.0193166 0.339314 0.38382 +-0.0438787 0.0441322 -0.0144222 0.427488 0.468839 +-0.0457505 0.046486 -0.0105694 0.340556 0.5 +-0.0645938 0.0456897 0.00313082 0.3549 0.5 +-0.0525978 0.0464843 0.0207116 0.3335 0.5 +-0.0572578 0.0459489 0.026887 0.439332 0.5 +-0.0618962 0.0443648 0.0286813 0.302557 0.45843 +-0.0331467 0.0453179 -0.0267282 0.481653 0.5 +-0.0377669 0.0443547 -0.0252099 0.392631 0.5 +-0.0320922 0.114425 -0.0162304 0.853943 0.5 +-0.0578027 0.0470669 -0.0032674 0.530144 0.5 +-0.0914954 0.147994 0.0205137 0.478387 0.480384 +-0.0400067 0.0471536 -0.0151042 0.224844 0.33752 +0.00454895 0.121869 -0.0124797 0.622385 0.5 +0.0151282 0.112708 -0.0165496 0.634759 0.463552 +-0.0525787 0.0463291 -0.00775444 0.598118 0.5 +-0.0599276 0.0475112 0.00267117 0.286734 0.429608 +-0.0726458 0.147126 -0.0218625 0.235551 0.5 +-0.0740924 0.168686 -0.0440312 0.451963 0.347747 +-0.057494 0.0515426 0.00319413 0.311918 0.5 +-0.0536918 0.0483048 0.0264945 0.447469 0.5 +-0.0147156 0.114453 -0.0172255 0.634887 0.5 +-0.0335191 0.0480424 -0.021246 0.299501 0.5 +0.019461 0.0924333 -0.0244344 0.636237 0.5 +0.0169402 0.0952065 -0.0238278 0.793707 0.5 +0.0201047 0.104156 -0.0188197 0.859301 0.5 +-0.0319642 0.0516657 -0.0152509 0.265727 0.5 +-0.0368448 0.0488256 -0.0131071 0.109826 0.5 +-0.0391265 0.0518909 -0.0109467 0.555432 0.5 +-0.00892221 0.111576 -0.0202733 0.785262 0.5 +-0.0515659 0.0515158 -0.00751393 0.527245 0.5 +-0.0557028 0.05294 -0.00268598 0.514955 0.5 +-0.0293421 0.0526398 -0.0213991 0.356317 0.5 +-0.0314453 0.0496351 -0.0193539 0.306544 0.5 +0.0322381 0.10409 -0.0128482 0.653044 0.5 +-0.0261025 0.0525801 -0.0264669 0.366688 0.5 +-0.0583031 0.116733 -0.0130038 0.568329 0.5 +-0.014851 0.111599 -0.0191484 0.630253 0.463696 +-0.0521348 0.118189 -0.0137451 0.464136 0.474515 +-0.0517493 0.0582798 -0.00896954 0.683087 0.5 +-0.0561982 0.0582462 -0.00310645 0.618759 0.5 +-0.0587989 0.0586119 0.00276734 0.328771 0.427166 +-0.0585564 0.0578416 0.00857596 0.293131 0.5 +0.019026 0.11614 -0.0131686 0.497701 0.5 +-0.0211893 0.111662 -0.0190883 0.650648 0.5 +-0.0239176 0.0561149 -0.030057 0.484351 0.5 +-0.0272603 0.058548 -0.027478 0.457773 0.5 +-0.0295766 0.0582799 -0.0217551 0.550969 0.5 +-0.0320928 0.0589382 -0.0147618 0.534177 0.453646 +0.0073938 0.121789 -0.0126555 0.654152 0.5 +-0.0251946 0.0595227 -0.0308632 0.509396 0.5 +-0.0307167 0.06013 -0.0194181 0.549851 0.422118 +-0.0650113 0.0632174 -0.00293095 0.168435 0.5 +-0.0696479 0.065751 -0.00198101 0.165663 0.5 +-0.0699926 0.0635013 0.00374106 0.275779 0.5 +-0.0799435 0.0724812 0.0191514 0.599916 0.5 +-0.0676844 0.160922 -0.0559942 0.35716 0.5 +-0.0215435 0.0636559 -0.0350431 0.45692 0.5 +-0.0258325 0.0648252 -0.0322087 0.452259 0.5 +-0.028982 0.0636438 -0.0274997 0.410415 0.5 +-0.0304226 0.0629368 -0.0224261 0.908229 0.5 +-0.0319042 0.0651819 -0.0201942 0.518875 0.434998 +-0.0332741 0.0636337 -0.0160032 0.40837 0.447765 +-0.0205547 0.034111 -0.026401 0.174612 0.215481 +-0.0743367 0.0658286 0.00833126 0.649876 0.5 +0.016103 0.120745 -0.0103843 0.509865 0.5 +-0.0770212 0.0700544 0.00316631 0.305775 0.384345 +-0.0748219 0.06693 0.00451345 0.433069 0.463791 +-0.0306317 0.0657524 -0.025453 0.517895 0.5 +-0.0711433 0.0687078 -0.00390291 0.256016 0.135401 +-0.0762625 0.0716316 -0.00295918 0.293636 0.296358 +-0.0802204 0.0713935 0.00991267 0.507181 0.5 +-0.0913413 0.148143 0.0161458 0.474933 0.5 +-0.0273736 0.0700052 -0.0335323 0.445714 0.5 +-0.0300274 0.0692073 -0.0289677 0.511122 0.5 +-0.0316277 0.0711218 -0.0266514 0.502235 0.5 +-0.0330629 0.0699765 -0.0212743 0.929225 0.5 +-0.0353642 0.0705896 -0.0177097 0.263666 0.5 +-0.0587004 0.0391044 -0.0090027 0.295521 0.5 +-0.0697696 0.0703857 -0.00808666 0.238472 0.5 +-0.0804832 0.0726462 0.00472466 0.630221 0.5 +0.0151616 0.126104 -0.00266395 0.542796 0.5 +-0.0745721 0.072883 -0.00757069 0.303203 0.5 +-0.0823908 0.076277 0.00270117 0.615888 0.5 +-0.0912831 0.133698 0.0142161 0.68945 0.5 +0.00371049 0.0968817 -0.0280931 0.670854 0.5 +-0.0761392 0.0766258 -0.00859487 0.260107 0.5 +-0.0784749 0.0748827 -0.00523624 0.238143 0.440892 +-0.0806781 0.0771902 -0.00290803 0.36458 0.43512 +-0.0834622 0.0765209 0.00927112 0.562933 0.5 +0.00983826 0.11402 -0.0178612 0.519736 0.475688 +0.00210649 0.0981565 -0.0261244 0.689185 0.5 +-0.0285085 0.0757575 -0.0348118 0.64535 0.304239 +-0.0330874 0.0761249 -0.0270661 0.564742 0.5 +-0.0346568 0.0757906 -0.0215029 0.930953 0.5 +0.0231104 0.0892807 -0.0240236 0.697809 0.45449 +-0.0312132 0.0771357 -0.0320416 0.687582 0.5 +-0.0700425 0.0763633 -0.0141464 0.485274 0.5 +-0.0861137 0.0814707 0.00908143 0.590509 0.5 +-0.086319 0.08152 0.0149936 0.698173 0.5 +-0.0208042 0.0963182 -0.0270563 0.75553 0.5 +-0.0211078 0.114391 -0.0171285 0.793027 0.5 +-0.0746162 0.0828529 -0.0139325 0.683447 0.5 +-0.077295 0.081216 -0.0100568 0.47673 0.5 +-0.0800127 0.0821605 -0.00722237 0.637376 0.5 +-0.0826334 0.0820868 -0.00324616 0.569954 0.5 +-0.0844667 0.0817669 0.00249573 0.601403 0.5 +-0.0860445 0.0832591 0.0203255 0.630527 0.5 +-0.084816 0.0816746 0.0219849 0.638209 0.5 +0.0545549 0.0661692 0.000765649 0.628404 0.43579 +-0.0331604 0.0828369 -0.0270493 0.417784 0.5 +-0.0358028 0.0829047 -0.0227723 0.112354 0 +-0.0861942 0.0842505 0.00298565 0.418742 0.5 +-0.0287072 0.0827267 -0.0349537 0.48086 0.471486 +-0.0311601 0.0822387 -0.0315627 0.627475 0.5 +-0.085403 0.141865 0.00516647 0.463398 0.5 +-0.0785169 0.0885628 -0.0107607 0.69884 0.5 +-0.0807046 0.0887676 -0.00826584 0.689404 0.5 +-0.0843972 0.0878743 -0.00349923 0.402052 0.5 +-0.0855708 0.0882073 -0.00109946 0.425364 0.422235 +-0.0876157 0.0881286 0.00369184 0.414972 0.435161 +-0.0885339 0.0876942 0.00897158 0.630733 0.5 +-0.0885791 0.0877213 0.0149616 0.665472 0.5 +-0.0643854 0.0348576 -0.00775085 0.279509 0.5 +-0.0512932 0.034227 -0.0129013 0.159841 0.5 +-0.0266839 0.0458556 -0.027274 0.610127 0.5 +-0.0146368 0.0981541 -0.0264318 0.44201 0.5 +-0.0213468 0.10077 -0.0239588 0.58675 0.5 +0.020932 0.0825954 -0.0267347 0.750174 0.5 +0.00759225 0.0928541 -0.0309237 0.580726 0.5 +-0.0144478 0.0879274 -0.0380297 0.689122 0.5 +-0.00859724 0.11451 -0.0173132 0.77831 0.5 +0.0264818 0.109935 -0.0126182 0.652634 0.5 +-0.0145855 0.0385179 -0.0267991 0.230538 0.5 +-0.0330054 0.0337044 -0.0272991 0.262513 0.5 +-0.0267872 0.0340475 -0.0271901 0.244173 0 +-0.00849157 0.0985859 -0.0270535 0.53889 0.411612 +-0.0110954 0.120824 -0.0120135 0.770076 0.5 +0.0367379 0.0925992 -0.0129888 0.684003 0.5 +-0.0571635 0.0435755 -0.00717607 0.581004 0.404197 +-0.0193328 0.0979251 -0.024792 0.661276 0.5 +-0.0203798 0.0385467 -0.0283088 0.392689 0.5 +-0.0587681 0.0337133 -0.00871891 0.1361 0.5 +-0.0517919 0.100655 -0.0213258 0.798237 0.5 +0.00702627 0.0978418 -0.0246055 0.732067 0.326346 +-0.0148892 0.126068 -0.00252126 0.467449 0.5 +0.0307578 0.092446 -0.0188519 0.704525 0.5 +0.0211049 0.0578126 -0.0266116 0.685576 0.5 +-0.0169237 0.0970481 -0.0278718 0.775366 0.5 +0.0460004 0.0581866 -0.00508589 0.612698 0.5 +-0.00944331 0.0822271 -0.0381067 0.670336 0.467319 +-0.0635881 0.0392124 -0.00717766 0.572252 0.5 +0.00864227 0.0386371 -0.0233053 0.540697 0.5 +0.0252935 0.0769557 -0.0248407 0.75695 0.5 +-0.0229653 0.0895159 -0.036199 0.454072 0.467569 +-0.0523791 0.0341193 -0.00994653 0.132813 0.5 +0.0211693 0.0643935 -0.0268578 0.690366 0.5 +-0.0515867 0.13164 -0.0028092 0.545448 0.5 +-0.0149669 0.0345529 -0.0254273 0.17846 0.5 +-0.0161167 0.127288 0.00169291 0.694465 0.5 +-0.0469232 0.128515 -0.00163965 0.389857 0.5 +-0.00961381 0.127158 -0.00378809 0.714685 0.5 +-0.0074566 0.128562 -0.00130751 0.72817 0.5 +-0.00304493 0.128909 -0.00174857 0.778769 0.5 +0.0028379 0.129022 -0.00194723 0.574275 0.5 +0.00903363 0.128674 -0.00165013 0.617309 0.5 +-0.0561607 0.131588 -0.00571429 0.687735 0.5 +-0.0457551 0.127167 -0.00484962 0.645893 0.5 +-0.00304746 0.127678 -0.00456004 0.562309 0.5 +0.00303811 0.12768 -0.00442 0.624596 0.5 +0.0101526 0.126812 -0.00466464 0.64326 0.5 +-0.0553259 0.126836 -0.00601308 0.517644 0.5 +0.00799473 0.034846 -0.0206913 0.278473 0.5 +0.0027179 0.0342191 -0.0204737 0.322372 0.5 +-0.00295804 0.0342418 -0.0216222 0.194059 0.5 +0.0134674 0.0353221 -0.0196961 0.466171 0.5 +0.00440963 0.0383063 -0.0240776 0.3469 0.5 +0.00140752 0.0383474 -0.0246147 0.361099 0.5 +-0.00309177 0.0383877 -0.0251866 0.314174 0.5 +-0.0575023 0.100661 -0.0195211 0.459895 0.452391 +-0.0485739 0.15316 -0.00547278 0.691758 0.5 +-0.0646573 0.0334831 -0.00296009 0.187639 0.5 +-0.0640796 0.100426 -0.0173936 0.44544 0.466101 +-0.0704415 0.100139 -0.0146037 0.499781 0.478548 +-0.0326376 0.155806 -0.00949884 0.828995 0.5 +0.0336094 0.0373624 0.00273412 0.290019 0.5 +0.0320943 0.0397885 -0.00195136 0.323719 0.5 +0.0158502 0.0449602 -0.0237212 0.910511 0.5 +0.00889467 0.0426449 -0.0242659 0.891863 0.5 +0.00312499 0.0452721 -0.026588 0.665265 0.460024 +-0.00298345 0.044686 -0.0272222 0.905955 0.5 +-0.00912346 0.0448524 -0.0280671 0.895801 0.5 +-0.0145351 0.0443266 -0.0277771 0.887903 0.5 +-0.0209223 0.0460913 -0.0281918 0.705844 0.5 +0.034052 0.0448434 -0.00540113 0.626363 0.5 +-0.0312646 0.158257 -0.01223 0.732334 0.5 +0.0401509 0.0448981 -0.00354586 0.446696 0.5 +0.0143253 0.0473484 -0.0251513 0.546545 0.456757 +0.00937888 0.0466526 -0.0261685 0.907397 0.5 +-0.0766531 0.0695423 0.0207982 0.774152 0.5 +0.0087246 0.0517916 -0.0291615 0.840924 0.5 +0.00299372 0.0506927 -0.0298557 0.901259 0.5 +-0.00164566 0.0489436 -0.0304144 0.872257 0.5 +-0.00321397 0.0522596 -0.0314075 0.634884 0.475184 +-0.00915341 0.0509217 -0.0318681 0.650022 0.5 +-0.0146018 0.0513752 -0.0319045 0.891033 0.5 +-0.0161558 0.0488543 -0.0303763 0.808351 0.5 +-0.0205843 0.0508011 -0.0296435 0.813106 0.5 +0.0405252 0.0518855 -0.00654453 0.65569 0.5 +0.0149309 0.0520772 -0.0273859 0.655547 0.5 +0.041884 0.0490868 -0.00604367 0.898378 0.5 +0.019962 0.0529908 -0.0261219 0.592286 0.5 +-0.0198501 0.0534234 -0.0312267 0.768335 0.5 +-0.0336273 0.0527187 -0.0106243 0.102172 0.5 +-0.0461112 0.0529158 -0.0101664 0.636429 0.372142 +-0.0204 0.161875 -0.014658 0.822907 0.5 +0.0449924 0.0530898 -0.00614891 0.575737 0.5 +0.00733679 0.0546532 -0.0305038 0.688621 0.5 +0.00283568 0.0546532 -0.0307468 0.611749 0.5 +-0.00302245 0.0577 -0.0331477 0.67582 0.5 +-0.00914668 0.0576676 -0.0341165 0.698389 0.5 +-0.01517 0.058199 -0.0349877 0.856637 0.5 +-0.0202707 0.0581031 -0.0333681 0.552506 0.5 +0.0140844 0.057965 -0.028983 0.564173 0.483714 +0.0103301 0.0588553 -0.0299472 0.602031 0.489059 +0.00732823 0.0588898 -0.0306117 0.710141 0.5 +0.0027369 0.0590151 -0.0321928 0.690932 0.5 +-0.0337187 0.0579742 -0.0115824 0.143826 0.5 +-0.0390711 0.0582467 -0.0115033 0.780735 0.5 +-0.0460474 0.0579124 -0.0115174 0.472305 0.5 +-0.00961439 0.0642168 -0.0358564 0.670518 0.457134 +-0.044157 0.0599825 -0.0123877 0.830365 0.5 +0.015251 0.0645803 -0.029567 0.626368 0.396114 +0.00839294 0.0649214 -0.0316957 0.79033 0.385997 +0.00325858 0.0643529 -0.0332439 0.728322 0.418376 +-0.00361257 0.0645861 -0.034907 0.670644 0.5 +-0.0144709 0.065006 -0.0371603 0.712311 0.5 +-0.0366623 0.060679 -0.0122791 0.525705 0.5 +-0.0526404 0.0636402 -0.0101297 0.452904 0.5 +-0.0381866 0.0648919 -0.0142073 0.543504 0.5 +-0.0452495 0.0647856 -0.0139819 0.883769 0.5 +-0.0599262 0.0622966 -0.00429285 0.195385 0.344922 +-0.0778641 0.117463 -0.00576778 0.523105 0.5 +-0.0187447 0.0664151 -0.0374779 0.820087 0.5 +-0.0577616 0.0644884 -0.00779097 0.472929 0.5 +-0.0625778 0.0655353 -0.00741131 0.379588 0.453283 +0.0251088 0.0710945 -0.0248604 0.704567 0.5 +0.021457 0.0702729 -0.0273415 0.740248 0.5 +0.0166747 0.0701586 -0.0297203 0.658948 0.5 +0.0132745 0.0702643 -0.0312074 0.651019 0.5 +0.00867525 0.0703509 -0.0324278 0.818076 0.5 +0.00229643 0.0708694 -0.0343123 0.73028 0.5 +-0.0030646 0.070381 -0.0353565 0.764349 0.5 +-0.00773679 0.0691749 -0.0362051 0.757441 0.5 +-0.0101988 0.0715122 -0.0373778 0.76291 0.5 +-0.0147454 0.0704429 -0.0382943 0.581028 0.470136 +-0.0203984 0.0706516 -0.038158 0.645161 0.5 +-0.0240967 0.0693418 -0.0362521 0.4533 0.5 +-0.0605175 0.0673597 -0.0108259 0.635082 0.5 +-0.0387293 0.0706355 -0.0168457 0.666323 0.5 +-0.0451347 0.0705064 -0.0164504 0.875899 0.5 +-0.0523435 0.0697862 -0.0145984 0.401473 0.5 +-0.0591515 0.0702891 -0.0147203 0.639534 0.5 +-0.0652515 0.0688492 -0.00993982 0.422384 0.422462 +-0.0247614 0.0719777 -0.0368317 0.497524 0.5 +-0.0637884 0.0712697 -0.0138535 0.437166 0.5 +0.0211454 0.0769268 -0.0268772 0.737516 0.5 +0.0162128 0.0765268 -0.0293784 0.712202 0.5 +0.0133247 0.0760196 -0.0306715 0.679361 0.5 +0.00907695 0.076038 -0.0330382 0.719764 0.5 +0.00245085 0.0760857 -0.0351615 0.721395 0.5 +-0.00176321 0.0762288 -0.0360688 0.799862 0.5 +-0.00476487 0.076286 -0.0369742 0.814155 0.377247 +-0.00962992 0.0765936 -0.0378651 0.627364 0.487104 +-0.0144481 0.0764118 -0.0385775 0.832444 0.5 +-0.021453 0.0763574 -0.038668 0.751656 0.5 +-0.024977 0.0762484 -0.0374518 0.39297 0.457854 +-0.0377453 0.0766164 -0.0189124 0.273711 0.5 +-0.0397395 0.0746623 -0.0180255 0.973337 0.5 +-0.0437423 0.0765905 -0.0187922 0.595439 0.440211 +-0.0466377 0.0744845 -0.0173668 0.538698 0.468502 +-0.0518623 0.0745812 -0.0175084 0.855826 0.5 +-0.0589866 0.0745368 -0.01766 0.879682 0.5 +-0.0644081 0.0756279 -0.0167529 0.562234 0.481782 +-0.0721295 0.0740256 -0.0105719 0.341576 0.432723 +-0.0615233 0.0354132 0.043881 0.499903 0.5 +-0.0524971 0.0769872 -0.0189536 0.873913 0.5 +-0.0587482 0.0767445 -0.0187462 0.882838 0.5 +0.013102 0.0809953 -0.0307917 0.69611 0.5 +0.00892296 0.0820652 -0.0325478 0.700162 0.5 +0.0022917 0.0820297 -0.0349279 0.829579 0.5 +-0.00177837 0.0804805 -0.0364471 0.647084 0.379833 +-0.00379684 0.0824193 -0.037328 0.824332 0.5 +-0.0142988 0.0820384 -0.0390211 0.832022 0.373406 +-0.0207708 0.0823862 -0.0387335 0.805306 0.5 +-0.0248089 0.0818968 -0.0377031 0.470046 0.439483 +-0.0735819 0.0777026 -0.0122023 0.445372 0.5 +0.015425 0.0831288 -0.0295207 0.735694 0.5 +-0.0383994 0.0817919 -0.0209596 0.290803 0.5 +-0.0451184 0.0815526 -0.020434 0.548777 0.45056 +-0.051814 0.0818472 -0.0211348 0.886135 0.5 +-0.0583689 0.0812724 -0.0202975 0.847354 0.5 +-0.063949 0.082768 -0.0188935 0.606874 0.5 +-0.0662709 0.080065 -0.0177832 0.593549 0.449354 +-0.0695594 0.0830593 -0.0170582 0.4495 0.5 +-0.00481814 0.086841 -0.0367951 0.827149 0.5 +-0.0248206 0.0867524 -0.0367639 0.487957 0.5 +0.0132046 0.0871602 -0.0305473 0.663835 0.5 +-0.0360837 0.0867076 -0.023791 0.366486 0.5 +-0.00877843 0.0340556 -0.0204927 0 0 +-0.0207128 0.0342382 -0.0208728 0.319674 0.5 +-0.0147915 0.0341096 -0.0207616 0.312592 0.5 +-0.0265767 0.0342963 -0.0210989 0.482378 0.5 +0.00282685 0.0351053 -0.0158136 0.508357 0.5 +0.00885967 0.034471 -0.0147487 0.490133 0.5 +-0.0390848 0.0337228 -0.0202617 0.628543 0.5 +-0.0326656 0.0345334 -0.0201874 0.788348 0.5 +-0.00224535 0.0351539 -0.0166234 0.756398 0.5 +-0.0149096 0.0357313 -0.0180956 0.933106 0.5 +-0.0114808 0.0353662 -0.0177045 0.933613 0.5 +-0.00921575 0.0380183 -0.0149732 0.909294 0.5 +-0.00282494 0.0382292 -0.0140636 0.920543 0.5 +0.00285919 0.0377324 -0.0134715 0.965028 0.5 +0.0159109 0.0347098 -0.00882204 0.420938 0.5 +-0.0306839 0.036693 -0.0184598 0.875112 0.5 +-0.0265216 0.0367471 -0.0188177 0.84266 0.5 +-0.0218341 0.0369718 -0.0184303 0.873319 0.5 +-0.0203027 0.0382765 -0.0152577 0.887215 0.5 +-0.0152596 0.0382328 -0.0156428 0.873575 0.5 +0.00738356 0.0366172 -0.0125003 0.962688 0.5 +0.00992361 0.0351979 -0.00924624 0.9642 0.5 +0.00702596 0.0378387 -0.00879015 0.927286 0.5 +-0.0396958 0.0342843 -0.014578 0.76643 0.5 +-0.0329517 0.0382154 -0.014678 0.654319 0.5 +-0.0263862 0.0385778 -0.0153644 0.924592 0.5 +0.00320835 0.0389424 -0.00953857 0.945732 0.5 +-0.0364387 0.0357946 -0.0155844 0.543249 0.5 +-0.00301526 0.0391061 -0.00886496 0.816802 0.5 +0.00831664 0.0348156 -0.00321961 0.671683 0.5 +0.0145039 0.0343685 -0.0028433 0.748562 0.5 +-0.0365752 0.0370276 -0.0136534 0.498247 0.5 +-0.0146234 0.0388055 -0.00887465 0.759701 0.5 +-0.00886749 0.0389394 -0.00890173 0.761944 0.5 +-0.0451032 0.0336721 -0.00848668 0.772585 0.5 +-0.040313 0.0350801 -0.00861758 0.462745 0.5 +-0.0206235 0.0386 -0.00878063 0.747754 0.5 +0.00267879 0.038424 -0.00319748 0.448355 0.5 +0.015044 0.0350517 0.00289039 0.579133 0.5 +0.0201479 0.0347806 0.00348327 0.403273 0.5 +0.027119 0.0353514 0.00366834 0.0909279 0.5 +0.0280785 0.0365531 0.000826759 0 0 +-0.0376066 0.0375692 -0.00942418 0.22755 0.5 +-0.0332748 0.0384549 -0.00855692 0.752109 0.5 +-0.0264541 0.0384497 -0.00886193 0.729343 0.5 +-0.00299262 0.0389582 -0.00292437 0.746846 0.5 +0.00451408 0.0356078 -0.00103635 0.413486 0.5 +0.00881079 0.0350428 0.00356828 0.588251 0.5 +0.0314184 0.0360255 0.00457907 0.187967 0.5 +-0.00888202 0.0387884 -0.00299409 0.73859 0.5 +0.00271787 0.0349091 0.00339755 0.645421 0.5 +-0.041199 0.0341471 -0.00327644 0 0 +-0.0205479 0.0384259 -0.00283766 0.741413 0.5 +-0.0146618 0.0385908 -0.00288739 0.718901 0.5 +0.00103528 0.0375917 0.000952222 0.441385 0.5 +0.0215747 0.0354906 0.0086194 0.395945 0.5 +0.0264794 0.0346514 0.00870654 0.414057 0.5 +0.0322391 0.0355412 0.00882378 0.515667 0.5 +-0.0521057 0.0334794 -0.00318207 0.614129 0.5 +-0.0455078 0.0336572 -0.00225818 0.757211 0.5 +-0.0334104 0.0383259 -0.00292317 0.611245 0.5 +-0.0265122 0.0383343 -0.00296504 0.763748 0.5 +-0.00224847 0.0383354 0.00320971 0.728422 0.5 +-0.0589386 0.0334143 -0.00291301 0.444064 0.5 +-0.00874044 0.0385976 0.00291227 0.735039 0.5 +0.00273457 0.0342734 0.0088248 0.796819 0.5 +0.00621941 0.0351341 0.00654928 0 0 +-0.080018 0.109279 0.0373655 0.503151 0.426569 +-0.0393178 0.0336443 0.00354096 0.266658 0.5 +-0.0213111 0.0382973 0.00334866 0.753895 0.5 +-0.0146196 0.0384265 0.00290922 0.762157 0.5 +-0.00353554 0.0379644 0.00874752 0.658939 0.5 +0.0276681 0.0349662 0.0149532 0.360666 0.5 +0.03282 0.0359255 0.0147037 0.719837 0.5 +0.0389763 0.0383079 0.0145025 0.635106 0.5 +-0.0523961 0.0335249 0.00326874 0.742717 0.5 +-0.0462346 0.0335696 0.00267776 0.743661 0.5 +-0.0277984 0.0382296 0.00286126 0.456211 0.5 +-0.000947006 0.0357374 0.0103469 0.779853 0.5 +0.0222276 0.0358262 0.0160256 0.180494 0.5 +0.0448051 0.0411192 0.0150961 0.294679 0.5 +-0.0581064 0.033504 0.00272997 0.775526 0.5 +-0.0352323 0.0337248 0.00491425 0.152905 0 +-0.0312985 0.0381858 0.00167702 0 0 +-0.0088641 0.03847 0.00876261 0.73345 0.5 +0.0028919 0.0342894 0.0147059 0.676527 0.5 +-0.0703332 0.0340583 0.00286723 0.639535 0.5 +-0.0648245 0.0334924 0.00301734 0.793089 0.5 +-0.0387963 0.034763 0.00935652 0.458758 0.5 +-0.0332327 0.0337932 0.00943608 0.151116 0.5 +-0.0203456 0.0382265 0.00836296 0.759992 0.5 +-0.0152156 0.0383161 0.00935801 0.755179 0.5 +-0.000385714 0.0351459 0.0134171 0.848157 0.5 +0.00663645 0.0342324 0.0159688 0 0 +0.0268074 0.0356469 0.0204126 0.619176 0.5 +0.0309391 0.0362152 0.0189937 0.762661 0.5 +0.0334119 0.0376179 0.0210082 0.70177 0.5 +-0.0515734 0.0338904 0.00817232 0.493124 0.5 +-0.0454999 0.0352808 0.00804865 0.53914 0.5 +-0.0263229 0.0380313 0.00871732 0.143858 0.5 +-0.0031858 0.0377098 0.014513 0.797449 0.5 +0.0211051 0.0351552 0.0207004 0.432057 0.5 +0.0391983 0.0395969 0.0205879 0.670001 0.5 +0.0441778 0.0418755 0.0204802 0.609797 0.5 +-0.0580282 0.0335624 0.00918162 0.776077 0.5 +-0.00922404 0.0383488 0.0150261 0.754001 0.5 +0.00313746 0.0352426 0.0204176 0.692001 0.5 +0.00877508 0.0346179 0.020856 0.290121 0.5 +0.0468489 0.0434226 0.0210936 0.239557 0.5 +-0.0648031 0.0337402 0.00884817 0.802283 0.5 +-0.0338156 0.0345063 0.0150293 0.572312 0.5 +-0.0149173 0.0382498 0.0147214 0.753708 0.5 +0.0146344 0.0345628 0.0222588 0.157065 0.5 +-0.0365655 0.0357926 0.0130139 0.391807 0.5 +-0.0262153 0.0376693 0.0148666 0.146481 0.5 +-0.0205165 0.0381248 0.0146779 0.715632 0.5 +-0.00229335 0.0382456 0.020565 0.923102 0.5 +0.014723 0.0347707 0.0263935 0.310145 0.5 +0.0210245 0.0353476 0.0265418 0.313898 0.5 +0.0250756 0.0364517 0.0246847 0.678097 0.5 +0.0273584 0.0381522 0.0267127 0.778478 0.5 +0.0321164 0.0401984 0.026762 0.778536 0.5 +-0.053829 0.0335431 0.0139547 0.458851 0.5 +0.00114114 0.037661 0.0223414 0.978558 0.5 +0.00915473 0.0353589 0.0262457 0.701449 0.5 +0.0380552 0.0412819 0.02589 0.374179 0.417844 +-0.0588034 0.0336951 0.0146283 0.798139 0.5 +-0.0339319 0.0346253 0.0202274 0.513983 0.5 +-0.0152545 0.0382629 0.0204704 0.75125 0.5 +-0.00888844 0.0384087 0.0207206 0.746481 0.5 +0.00307272 0.0384964 0.0264151 0.996029 0.5 +-0.0261643 0.0378491 0.0205422 0.603577 0.5 +-0.0205429 0.0381473 0.0213758 0.772551 0.5 +-0.0538188 0.0335608 0.0210581 0 0 +-0.00301594 0.03875 0.0263901 0.805634 0.5 +0.00756209 0.0380712 0.0285007 0.978659 0.5 +0.0143741 0.0348327 0.0331833 0.915728 0.5 +0.0198279 0.03555 0.0321213 0.749506 0.5 +0.0236875 0.0373106 0.0299772 0.517201 0.5 +-0.0588476 0.033906 0.020465 0.657735 0.5 +-0.00882687 0.0386047 0.0265705 0.756827 0.5 +0.00847025 0.0383344 0.0315598 0.739987 0.5 +0.0108958 0.035647 0.0330663 0.649316 0.5 +-0.0366651 0.0353042 0.023032 0.153172 0.5 +-0.0340084 0.0344659 0.0266224 0.263742 0.5 +-0.0270447 0.0379104 0.0270529 0.074682 0.5 +-0.0210471 0.0383013 0.026282 0.782021 0.5 +-0.0147317 0.0384888 0.0265233 0.791552 0.5 +-0.0712786 0.0733348 0.0355839 0.683322 0.427231 +-0.0388887 0.0346255 0.0265538 0.109729 0 +0.00290004 0.0393205 0.032168 0.626516 0.5 +0.0155389 0.0350901 0.0393977 0.759188 0.5 +0.0195159 0.0358111 0.0367948 0.405286 0.5 +-0.0589139 0.0341314 0.0264586 0.808252 0.5 +-0.052234 0.0340737 0.0268887 0.497915 0.5 +-0.0447866 0.0339274 0.0274346 0.154159 0.5 +-0.0310127 0.0369382 0.02848 0.240675 0.5 +-0.00908756 0.0390146 0.0330901 0.79352 0.5 +-0.00293287 0.039209 0.03365 0.804769 0.5 +0.00861952 0.0346654 0.0391536 0.125418 0.5 +-0.0149144 0.0388312 0.0324344 0.795183 0.5 +0.00392423 0.0347398 0.0399064 0.146347 0.5 +-0.0657827 0.0618455 0.00187562 0.442355 0.5 +-0.0640051 0.0606097 0.00361345 0.333039 0.5 +-0.0455164 0.0345095 0.0326748 0.510388 0.5 +-0.0385699 0.0344168 0.033204 0.485482 0.5 +-0.0342024 0.0351611 0.0325685 0.248514 0.5 +-0.0270303 0.0384799 0.0326469 0.783767 0.5 +-0.0209433 0.0387397 0.0332273 0.806699 0.5 +-0.0520994 0.0344582 0.0326775 0.466807 0.5 +-0.0313489 0.0377268 0.0321213 0.178238 0.5 +-0.00219023 0.0348305 0.0410082 0.139343 0 +0.00818206 0.0355366 0.0443043 0.642932 0.5 +0.014947 0.0361331 0.0431407 0.796588 0.5 +-0.0642564 0.0597236 0.0092932 0.716255 0.5 +-0.0584732 0.0343588 0.0331559 0.775713 0.5 +-0.0145859 0.0393004 0.0380317 0.483641 0.5 +-0.00937548 0.0394517 0.037871 0.328321 0.5 +-0.0588297 0.0579582 0.0145443 0 0 +-0.038732 0.0346956 0.0400227 0.628019 0.5 +-0.0331487 0.034492 0.0390527 0.154826 0.5 +-0.0201914 0.0391628 0.0381696 0.483919 0.5 +-0.00878985 0.0348233 0.0452949 0.139305 0.5 +-0.0031441 0.0351515 0.045825 0.295611 0.5 +-0.0701619 0.0622789 0.00863964 0.42197 0.408074 +-0.0451191 0.034688 0.0396457 0.766116 0.5 +-0.0256628 0.0389081 0.0373249 0 0 +-0.0146115 0.0348173 0.0458198 0.143796 0.5 +-0.0636462 0.0593677 0.014889 0.807508 0.5 +-0.0531671 0.0345191 0.0391729 0.74918 0.5 +-0.0595372 0.034497 0.0397515 0.783724 0.5 +-0.0329555 0.0349777 0.045552 0.474674 0.5 +-0.0262436 0.034809 0.0452831 0.162616 0.5 +-0.0215554 0.0348112 0.0459347 0.152356 0 +-0.0633407 0.0601272 0.0190813 0.939061 0.5 +-0.0471 0.0351015 0.0434178 0.627709 0.5 +-0.0120723 0.0353434 0.0494553 0.877126 0.5 +-0.016313 0.0351836 0.0504037 0.67915 0.5 +-0.0483699 0.146034 -0.00115148 0.583019 0.5 +-0.0264335 0.156562 -0.00835956 0.469485 0.437523 +-0.065003 0.144791 -0.0142909 0.400803 0.470167 +-0.066228 0.151547 -0.0394609 0.538048 0.5 +-0.0663323 0.145309 -0.018858 0.764025 0.5 +-0.0412403 0.152108 -0.00674014 0.633348 0.5 +3 4 132 80 +3 80 132 544 +3 373 80 544 +3 387 299 241 +3 859 1475 1474 +3 371 299 401 +3 401 326 333 +3 347 673 402 +3 1187 1354 386 +3 1221 457 69 +3 186 224 114 +3 1250 1256 116 +3 164 333 376 +3 19 488 1245 +3 749 19 1245 +3 667 19 749 +3 1040 412 543 +3 1359 1358 1500 +3 216 4 80 +3 152 544 146 +3 4 387 505 +3 543 1235 1205 +3 610 604 297 +3 250 801 1274 +3 504 148 111 +3 387 348 299 +3 401 333 164 +3 1484 1483 1110 +3 91 196 310 +3 90 91 310 +3 952 406 609 +3 1244 1247 1240 +3 93 327 65 +3 373 544 152 +3 373 152 644 +3 1321 158 22 +3 401 416 326 +3 644 152 1263 +3 276 59 181 +3 294 853 150 +3 308 249 529 +3 406 1124 604 +3 609 406 463 +3 146 3 145 +3 90 310 3 +3 58 186 10 +3 575 261 384 +3 25 40 43 +3 379 535 713 +3 348 704 157 +3 388 443 22 +3 396 146 145 +3 152 133 1263 +3 1830 1829 1812 +3 214 114 224 +3 157 147 324 +3 1335 430 1274 +3 282 230 214 +3 92 346 652 +3 1151 1012 1491 +3 571 1151 1491 +3 571 1491 183 +3 310 196 111 +3 91 4 505 +3 1250 116 108 +3 110 183 47 +3 1209 854 953 +3 132 4 91 +3 111 148 327 +3 93 111 327 +3 110 571 183 +3 713 171 402 +3 294 920 200 +3 81 180 52 +3 525 731 784 +3 347 256 673 +3 175 57 220 +3 338 175 220 +3 27 14 220 +3 57 27 220 +3 359 446 27 +3 359 36 446 +3 145 28 262 +3 133 16 419 +3 1447 576 1465 +3 1885 287 444 +3 133 396 16 +3 598 543 1205 +3 447 93 65 +3 73 213 36 +3 1236 1255 1250 +3 1235 1236 1250 +3 115 782 731 +3 28 93 447 +3 525 548 115 +3 299 416 401 +3 667 603 463 +3 292 667 463 +3 492 70 637 +3 133 146 396 +3 1166 1125 619 +3 1151 1219 959 +3 821 304 409 +3 1486 1487 1684 +3 15 175 167 +3 120 15 167 +3 15 131 57 +3 175 15 57 +3 57 131 27 +3 257 209 359 +3 27 257 359 +3 209 55 36 +3 359 209 36 +3 55 87 73 +3 36 55 73 +3 101 108 735 +3 108 101 64 +3 310 365 3 +3 576 859 1465 +3 262 28 447 +3 102 64 101 +3 544 91 90 +3 262 447 485 +3 485 447 211 +3 1443 1440 1442 +3 697 457 1221 +3 1008 383 1011 +3 451 435 1330 +3 129 405 426 +3 70 75 161 +3 648 693 692 +3 204 129 426 +3 812 481 123 +3 406 292 463 +3 878 1591 1009 +3 478 128 50 +3 900 979 977 +3 490 900 977 +3 241 299 371 +3 1164 701 734 +3 683 703 682 +3 719 718 682 +3 703 719 682 +3 760 759 718 +3 719 760 718 +3 137 729 728 +3 54 130 2 +3 302 358 301 +3 566 567 614 +3 1069 1103 1068 +3 1186 1190 1208 +3 4 348 387 +3 277 311 228 +3 707 226 706 +3 355 394 393 +3 773 129 755 +3 646 647 679 +3 356 355 269 +3 270 356 269 +3 424 394 356 +3 623 654 602 +3 654 683 602 +3 193 217 192 +3 1677 1676 1662 +3 1018 1019 1025 +3 597 1231 1165 +3 490 26 605 +3 299 157 416 +3 504 241 148 +3 84 528 714 +3 1247 669 1240 +3 683 719 703 +3 1886 1231 1066 +3 79 168 218 +3 211 318 426 +3 165 377 148 +3 91 505 387 +3 577 623 622 +3 692 693 707 +3 255 254 218 +3 194 270 255 +3 695 137 728 +3 1475 1498 1474 +3 67 808 1010 +3 1190 240 1208 +3 242 259 300 +3 476 509 567 +3 743 755 558 +3 1025 1024 1018 +3 194 255 218 +3 270 269 254 +3 203 271 12 +3 603 667 749 +3 1379 1395 1392 +3 783 546 1340 +3 578 600 577 +3 624 623 577 +3 600 624 577 +3 655 654 623 +3 684 683 654 +3 655 684 654 +3 720 719 683 +3 684 720 683 +3 720 739 719 +3 761 760 719 +3 739 761 719 +3 218 254 253 +3 694 695 437 +3 255 270 254 +3 1202 488 19 +3 412 1222 543 +3 60 528 84 +3 1352 494 702 +3 624 655 623 +3 1361 221 143 +3 755 129 204 +3 132 91 544 +3 543 1221 1235 +3 216 5 4 +3 1221 1236 1235 +3 754 755 204 +3 1169 732 715 +3 756 755 743 +3 1036 1035 1024 +3 728 756 743 +3 476 567 508 +3 4 5 348 +3 244 1339 546 +3 405 445 211 +3 254 269 268 +3 253 254 268 +3 381 358 302 +3 346 92 59 +3 517 450 1560 +3 1618 1333 141 +3 1498 1497 1474 +3 1231 597 1165 +3 228 264 215 +3 100 151 99 +3 151 215 99 +3 151 228 215 +3 1864 827 1870 +3 561 578 480 +3 207 561 480 +3 579 600 578 +3 561 579 578 +3 600 625 624 +3 656 655 624 +3 625 656 624 +3 685 684 655 +3 656 685 655 +3 685 721 720 +3 684 685 720 +3 721 740 739 +3 720 721 739 +3 739 740 761 +3 762 789 788 +3 761 762 788 +3 789 239 770 +3 788 789 770 +3 328 770 239 +3 423 424 476 +3 121 195 522 +3 423 476 422 +3 381 431 358 +3 148 371 401 +3 579 625 600 +3 465 464 431 +3 381 465 431 +3 464 465 227 +3 248 11 71 +3 548 142 1005 +3 740 762 761 +3 767 900 490 +3 728 743 437 +3 776 195 121 +3 1177 1176 1153 +3 1043 1034 1035 +3 137 708 729 +3 91 387 196 +3 1721 1729 1703 +3 728 729 756 +3 727 728 437 +3 196 387 241 +3 404 458 522 +3 355 354 268 +3 647 648 692 +3 979 846 901 +3 241 371 148 +3 142 1155 574 +3 269 355 268 +3 358 301 300 +3 301 358 300 +3 753 754 793 +3 184 229 228 +3 229 277 228 +3 312 311 277 +3 1845 1853 1831 +3 1523 1532 1153 +3 580 579 561 +3 1276 1280 1771 +3 580 626 625 +3 579 580 625 +3 626 657 656 +3 625 626 656 +3 656 657 685 +3 722 721 685 +3 741 740 721 +3 722 741 721 +3 740 763 762 +3 790 789 762 +3 763 790 762 +3 790 339 239 +3 789 790 239 +3 377 165 327 +3 476 508 422 +3 259 301 300 +3 162 170 169 +3 81 162 169 +3 580 561 562 +3 657 686 685 +3 229 312 277 +3 28 365 93 +3 1263 419 1254 +3 396 145 144 +3 685 686 722 +3 741 763 740 +3 133 152 146 +3 1263 133 419 +3 207 520 562 +3 520 562 580 +3 562 520 580 +3 562 626 580 +3 239 339 487 +3 597 1063 1066 +3 3 365 28 +3 649 648 615 +3 108 64 116 +3 571 1225 1218 +3 184 185 229 +3 313 312 229 +3 185 313 229 +3 439 501 520 +3 501 581 562 +3 520 501 562 +3 627 626 562 +3 581 627 562 +3 627 628 626 +3 658 657 626 +3 628 658 626 +3 658 675 657 +3 687 686 657 +3 675 687 657 +3 723 722 686 +3 687 723 686 +3 722 723 741 +3 741 723 763 +3 764 791 790 +3 763 764 790 +3 791 407 339 +3 790 791 339 +3 407 303 339 +3 303 487 339 +3 303 460 487 +3 303 325 460 +3 170 106 105 +3 105 106 68 +3 439 440 501 +3 723 764 763 +3 1 1027 453 +3 1067 511 942 +3 775 121 774 +3 1281 1270 1291 +3 368 440 439 +3 367 368 439 +3 582 581 501 +3 628 627 581 +3 658 688 687 +3 675 658 687 +3 1733 1562 1561 +3 757 775 756 +3 74 68 46 +3 398 1223 317 +3 631 607 231 +3 1465 859 1474 +3 1775 1784 1754 +3 204 138 793 +3 74 122 97 +3 584 533 570 +3 278 313 185 +3 265 278 185 +3 369 368 313 +3 278 369 313 +3 369 440 368 +3 502 501 440 +3 583 582 501 +3 502 583 501 +3 583 581 582 +3 629 628 581 +3 583 629 581 +3 629 659 658 +3 628 629 658 +3 658 659 688 +3 724 723 687 +3 688 724 687 +3 724 742 723 +3 742 765 764 +3 723 742 764 +3 764 238 791 +3 791 238 407 +3 407 238 303 +3 238 333 303 +3 333 325 303 +3 614 615 647 +3 46 122 74 +3 606 199 112 +3 441 440 369 +3 83 173 573 +3 775 776 121 +3 846 979 901 +3 441 502 440 +3 659 689 688 +3 84 714 1367 +3 535 52 171 +3 551 798 1883 +3 630 629 583 +3 629 630 659 +3 689 724 688 +3 792 238 764 +3 765 792 764 +3 1207 1208 177 +3 195 96 522 +3 122 13 97 +3 344 492 637 +3 1025 1036 1024 +3 775 774 756 +3 1012 1151 959 +3 1270 1372 1291 +3 145 3 28 +3 649 670 695 +3 517 1888 243 +3 444 399 1885 +3 370 369 278 +3 724 765 742 +3 376 333 238 +3 1372 1375 1291 +3 1060 1161 1162 +3 16 396 144 +3 369 442 441 +3 583 601 630 +3 690 689 659 +3 318 295 427 +3 138 204 427 +3 693 694 707 +3 310 111 365 +3 365 111 93 +3 636 660 659 +3 567 566 508 +3 426 405 211 +3 121 126 774 +3 471 601 583 +3 251 237 188 +3 1303 188 237 +3 278 314 370 +3 370 442 369 +3 442 503 502 +3 441 442 502 +3 503 471 583 +3 502 503 583 +3 858 302 259 +3 16 144 319 +3 660 690 659 +3 690 725 724 +3 689 690 724 +3 750 765 724 +3 725 750 724 +3 8 792 765 +3 750 8 765 +3 376 238 792 +3 8 376 792 +3 164 376 238 +3 376 164 238 +3 1381 1380 1375 +3 1135 1134 1103 +3 1104 1135 1103 +3 794 204 793 +3 447 65 211 +3 442 1347 503 +3 249 262 485 +3 1036 1043 1035 +3 522 96 438 +3 204 426 427 +3 188 283 251 +3 1235 1250 1205 +3 485 262 23 +3 597 1066 1165 +3 144 308 319 +3 1027 767 589 +3 648 649 694 +3 567 615 614 +3 821 409 304 +3 63 711 903 +3 8 164 376 +3 12 478 50 +3 171 347 402 +3 284 1327 314 +3 1447 1465 1459 +3 1456 1447 1459 +3 1329 1328 1380 +3 755 756 773 +3 756 774 773 +3 193 218 253 +3 648 694 693 +3 168 194 218 +3 190 188 189 +3 284 283 188 +3 190 284 188 +3 283 284 314 +3 262 485 23 +3 108 116 64 +3 751 750 725 +3 726 751 725 +3 751 771 750 +3 37 8 750 +3 771 37 750 +3 632 164 8 +3 569 53 411 +3 511 1560 1884 +3 386 1354 1320 +3 165 632 8 +3 37 165 8 +3 165 164 632 +3 662 661 638 +3 354 393 422 +3 401 165 148 +3 979 1883 798 +3 144 145 262 +3 413 408 349 +3 16 319 669 +3 318 211 295 +3 156 1213 198 +3 1153 1152 1119 +3 1225 1448 247 +3 190 266 284 +3 419 669 1247 +3 479 233 232 +3 166 165 37 +3 709 492 344 +3 567 568 615 +3 107 827 1864 +3 695 727 437 +3 485 211 23 +3 1254 419 1247 +3 419 16 669 +3 1884 1591 1009 +3 249 485 24 +3 41 249 24 +3 1103 1134 1133 +3 272 398 492 +3 754 204 794 +3 1498 159 113 +3 24 485 23 +3 1102 1103 1133 +3 308 144 249 +3 164 165 401 +3 692 707 706 +3 509 568 567 +3 191 252 190 +3 190 252 266 +3 252 285 284 +3 266 252 284 +3 285 286 284 +3 284 286 337 +3 144 262 249 +3 536 564 563 +3 563 564 593 +3 564 612 611 +3 593 564 611 +3 645 361 611 +3 612 645 611 +3 645 691 1313 +3 309 752 751 +3 726 309 751 +3 752 772 771 +3 751 752 771 +3 119 37 771 +3 772 119 771 +3 425 166 37 +3 119 425 37 +3 380 165 166 +3 425 380 166 +3 128 83 17 +3 50 128 17 +3 729 757 756 +3 394 423 422 +3 589 767 490 +3 424 509 476 +3 1374 1359 1531 +3 408 372 349 +3 679 692 706 +3 855 242 300 +3 766 757 730 +3 354 355 393 +3 79 218 193 +3 129 126 405 +3 126 458 405 +3 647 692 679 +3 757 766 775 +3 766 776 775 +3 1699 1014 1013 +3 393 394 422 +3 252 286 285 +3 752 119 772 +3 425 327 380 +3 696 730 729 +3 708 696 729 +3 649 695 694 +3 78 79 193 +3 1497 1498 113 +3 901 979 798 +3 404 24 445 +3 24 23 445 +3 776 795 195 +3 1340 1591 1884 +3 1035 1034 1024 +3 177 203 12 +3 380 327 425 +3 510 509 424 +3 477 510 424 +3 458 404 405 +3 192 217 252 +3 191 192 252 +3 217 267 286 +3 252 217 286 +3 286 267 352 +3 353 421 420 +3 352 353 420 +3 421 507 506 +3 506 507 536 +3 507 565 564 +3 536 507 564 +3 565 613 612 +3 564 565 612 +3 646 645 612 +3 613 646 612 +3 646 679 691 +3 645 646 691 +3 706 705 691 +3 679 706 691 +3 753 309 280 +3 138 119 752 +3 753 138 752 +3 427 425 119 +3 138 427 119 +3 295 380 425 +3 427 295 425 +3 65 327 380 +3 295 65 380 +3 769 104 315 +3 426 318 427 +3 568 616 615 +3 695 728 727 +3 404 445 405 +3 1635 1653 1453 +3 271 478 12 +3 839 136 830 +3 615 648 647 +3 311 277 228 +3 749 1245 1225 +3 353 392 421 +3 793 138 753 +3 315 104 33 +3 432 466 465 +3 381 432 465 +3 465 466 527 +3 1170 1190 1099 +3 754 794 793 +3 558 754 280 +3 193 253 217 +3 253 268 267 +3 217 253 267 +3 268 354 353 +3 267 268 353 +3 354 392 353 +3 422 421 392 +3 354 422 392 +3 422 508 507 +3 421 422 507 +3 508 566 565 +3 507 508 565 +3 614 613 565 +3 566 614 565 +3 614 647 646 +3 613 614 646 +3 168 810 194 +3 886 940 923 +3 946 945 930 +3 929 939 944 +3 940 569 887 +3 661 649 616 +3 320 919 878 +3 227 526 464 +3 882 873 866 +3 552 384 820 +3 464 927 358 +3 917 432 905 +3 879 829 820 +3 194 836 880 +3 935 466 432 +3 917 935 432 +3 1038 1725 1013 +3 1378 1391 1406 +3 173 448 293 +3 477 943 510 +3 616 568 617 +3 1405 550 980 +3 665 86 847 +3 891 906 912 +3 845 130 54 +3 999 925 822 +3 1885 928 555 +3 904 910 270 +3 315 33 478 +3 1033 1034 1042 +3 490 921 26 +3 850 257 131 +3 1070 1077 1034 +3 843 860 15 +3 120 843 15 +3 850 209 257 +3 914 913 300 +3 880 911 910 +3 641 661 616 +3 843 120 797 +3 860 870 15 +3 870 131 15 +3 870 850 131 +3 894 873 882 +3 811 248 875 +3 974 981 992 +3 850 201 131 +3 131 201 850 +3 850 201 209 +3 907 917 905 +3 694 437 226 +3 895 843 797 +3 870 860 843 +3 816 870 843 +3 870 201 850 +3 913 933 932 +3 968 969 986 +3 840 118 712 +3 816 843 895 +3 201 856 209 +3 856 845 55 +3 209 856 55 +3 931 930 911 +3 228 151 184 +3 1340 884 1884 +3 553 506 536 +3 539 867 842 +3 870 924 201 +3 977 823 490 +3 868 829 780 +3 999 1000 925 +3 198 701 156 +3 787 816 895 +3 924 877 856 +3 201 924 856 +3 877 845 856 +3 66 305 941 +3 769 203 1208 +3 848 847 919 +3 880 889 911 +3 1027 589 605 +3 957 816 787 +3 849 870 816 +3 957 849 816 +3 414 821 409 +3 1887 1004 928 +3 569 888 887 +3 459 384 552 +3 891 889 890 +3 839 892 891 +3 1080 1057 1051 +3 957 328 816 +3 328 957 816 +3 849 881 870 +3 881 849 870 +3 870 849 924 +3 481 531 123 +3 777 835 698 +3 891 892 906 +3 912 911 889 +3 891 912 889 +3 546 1339 746 +3 328 849 957 +3 849 88 924 +3 1043 1070 1034 +3 777 122 46 +3 477 929 943 +3 617 641 616 +3 822 915 72 +3 915 331 72 +3 834 806 956 +3 788 957 787 +3 770 788 787 +3 788 328 957 +3 864 877 924 +3 833 938 130 +3 845 833 130 +3 938 256 130 +3 1005 142 574 +3 661 676 137 +3 730 305 776 +3 1186 1208 1207 +3 1189 1186 1207 +3 798 1888 1067 +3 864 924 88 +3 864 922 877 +3 982 845 877 +3 922 982 877 +3 982 833 845 +3 894 905 873 +3 879 665 86 +3 879 665 847 +3 817 922 864 +3 833 982 922 +3 817 833 922 +3 894 907 905 +3 1562 1561 1038 +3 305 893 776 +3 899 864 88 +3 1071 1049 1072 +3 788 770 328 +3 776 768 795 +3 835 919 847 +3 817 864 899 +3 833 256 938 +3 1177 1195 1176 +3 1276 1771 1275 +3 155 100 813 +3 832 96 449 +3 879 384 665 +3 879 86 665 +3 834 956 320 +3 863 898 328 +3 826 849 328 +3 898 826 328 +3 849 826 88 +3 826 899 88 +3 1346 1883 900 +3 930 945 944 +3 939 930 944 +3 810 818 836 +3 838 836 837 +3 1077 1069 1034 +3 891 890 838 +3 1473 1680 1679 +3 44 24 438 +3 899 200 817 +3 1374 1391 1378 +3 466 935 527 +3 66 941 730 +3 913 906 912 +3 956 919 320 +3 662 676 661 +3 239 395 863 +3 395 898 863 +3 819 826 898 +3 826 200 899 +3 35 86 82 +3 880 270 194 +3 935 950 527 +3 670 661 695 +3 134 256 94 +3 818 837 836 +3 848 879 847 +3 395 819 898 +3 826 819 200 +3 200 920 817 +3 920 876 833 +3 817 920 833 +3 833 876 256 +3 1034 1069 1068 +3 932 933 947 +3 997 676 662 +3 836 889 880 +3 757 729 730 +3 956 806 919 +3 603 749 1460 +3 876 48 256 +3 827 107 155 +3 107 184 155 +3 830 855 841 +3 1042 1034 1068 +3 832 449 795 +3 997 662 638 +3 384 261 665 +3 997 696 676 +3 294 48 876 +3 920 294 876 +3 925 915 822 +3 1199 1231 1886 +3 941 305 730 +3 200 418 294 +3 569 940 886 +3 100 155 184 +3 840 712 331 +3 921 379 26 +3 1016 1014 1699 +3 776 766 730 +3 983 997 638 +3 676 696 137 +3 487 395 239 +3 487 819 395 +3 569 886 511 +3 940 887 923 +3 986 1000 985 +3 1125 110 47 +3 947 968 958 +3 842 874 834 +3 822 918 66 +3 985 999 998 +3 984 985 998 +3 999 822 998 +3 983 984 997 +3 984 998 997 +3 819 418 200 +3 177 85 1206 +3 12 275 397 +3 1231 1165 1066 +3 240 769 1208 +3 1000 999 985 +3 943 965 568 +3 906 913 932 +3 300 913 892 +3 997 998 66 +3 998 822 66 +3 478 33 128 +3 570 701 1076 +3 305 72 768 +3 72 811 768 +3 878 884 411 +3 878 835 884 +3 930 939 929 +3 968 978 967 +3 958 968 967 +3 946 958 967 +3 819 853 418 +3 510 943 509 +3 509 943 568 +3 151 100 184 +3 978 984 983 +3 967 978 983 +3 474 1122 799 +3 932 931 912 +3 487 460 819 +3 460 29 819 +3 819 29 853 +3 340 867 383 +3 1134 1135 1161 +3 947 946 931 +3 1411 1501 1408 +3 300 892 855 +3 356 910 929 +3 136 838 837 +3 1259 351 523 +3 887 896 923 +3 260 86 665 +3 774 129 773 +3 872 873 871 +3 906 932 912 +3 661 137 695 +3 511 886 942 +3 985 984 978 +3 968 985 978 +3 818 136 837 +3 1559 851 857 +3 872 871 865 +3 1222 1221 543 +3 548 1005 115 +3 430 1198 1065 +3 768 811 832 +3 945 967 944 +3 1132 1134 1160 +3 1019 1036 1025 +3 1134 1161 1160 +3 615 616 649 +3 1560 884 1884 +3 884 835 888 +3 214 230 114 +3 811 332 832 +3 878 411 53 +3 848 842 879 +3 842 829 879 +3 48 673 256 +3 869 811 768 +3 912 931 911 +3 935 936 950 +3 871 302 381 +3 972 991 971 +3 708 137 696 +3 1225 571 110 +3 847 955 13 +3 803 190 189 +3 865 871 858 +3 986 985 968 +3 929 944 943 +3 227 972 526 +3 888 835 896 +3 1001 1002 840 +3 1830 1841 1829 +3 50 140 275 +3 394 424 423 +3 411 884 888 +3 936 935 917 +3 907 936 917 +3 835 847 698 +3 811 6 332 +3 842 867 829 +3 1161 1060 1226 +3 1885 399 1887 +3 808 834 995 +3 1659 1658 1638 +3 65 295 211 +3 918 822 305 +3 302 871 381 +3 847 86 955 +3 1001 840 925 +3 1010 937 834 +3 1208 203 177 +3 1135 1162 1161 +3 921 81 379 +3 271 315 478 +3 948 969 947 +3 464 526 927 +3 834 848 806 +3 409 296 414 +3 302 873 432 +3 885 896 777 +3 841 892 839 +3 811 875 6 +3 1077 1104 1069 +3 1104 1103 1069 +3 68 106 46 +3 823 921 490 +3 162 81 921 +3 823 162 921 +3 989 1001 1000 +3 986 989 1000 +3 1000 1001 925 +3 888 896 887 +3 929 477 356 +3 974 972 534 +3 87 2 213 +3 915 840 331 +3 970 969 948 +3 965 641 568 +3 1207 177 1206 +3 1726 1725 1038 +3 1002 51 840 +3 814 191 803 +3 191 190 803 +3 855 892 841 +3 302 432 381 +3 173 293 573 +3 880 904 270 +3 871 873 302 +3 358 914 300 +3 239 863 328 +3 910 911 929 +3 331 712 811 +3 438 24 404 +3 892 913 906 +3 991 1002 990 +3 128 33 83 +3 810 836 194 +3 788 770 787 +3 814 803 804 +3 774 126 129 +3 242 855 830 +3 981 1189 1206 +3 927 934 914 +3 847 13 777 +3 301 358 300 +3 822 72 305 +3 641 617 568 +3 839 838 136 +3 904 880 910 +3 1850 1864 1870 +3 118 248 811 +3 949 970 948 +3 970 989 986 +3 1328 1325 1316 +3 358 927 914 +3 867 340 829 +3 943 944 966 +3 1100 221 1361 +3 530 805 525 +3 327 148 377 +3 1259 179 351 +3 1029 1028 1014 +3 969 968 947 +3 970 986 969 +3 832 795 768 +3 888 569 411 +3 342 344 113 +3 458 126 121 +3 943 966 965 +3 979 901 823 +3 823 861 162 +3 701 198 1076 +3 966 638 641 +3 769 315 271 +3 760 761 787 +3 965 966 641 +3 927 949 934 +3 949 948 934 +3 558 755 754 +3 919 835 878 +3 270 910 356 +3 852 162 861 +3 106 170 162 +3 852 106 162 +3 947 958 946 +3 815 192 191 +3 814 815 191 +3 820 384 879 +3 305 768 893 +3 698 847 777 +3 829 340 780 +3 534 972 227 +3 121 522 458 +3 1071 1077 1070 +3 846 823 901 +3 846 861 823 +3 918 305 66 +3 893 768 776 +3 1190 1186 1099 +3 67 1010 937 +3 925 840 915 +3 862 861 846 +3 862 852 861 +3 835 777 896 +3 946 945 944 +3 862 106 852 +3 1885 1887 928 +3 464 358 431 +3 526 949 927 +3 946 944 945 +3 890 889 838 +3 66 696 997 +3 1019 1561 1026 +3 1375 1380 1291 +3 1071 1061 1077 +3 712 118 811 +3 806 848 919 +3 971 990 970 +3 661 670 649 +3 971 970 949 +3 749 1225 110 +3 122 777 13 +3 35 13 955 +3 734 701 1164 +3 795 449 195 +3 874 842 848 +3 990 1002 989 +3 977 979 823 +3 526 971 949 +3 78 193 192 +3 815 78 192 +3 990 989 970 +3 834 539 842 +3 839 891 838 +3 1146 767 1064 +3 1002 1001 989 +3 840 51 118 +3 886 862 846 +3 280 754 753 +3 811 869 768 +3 906 913 912 +3 967 966 944 +3 931 946 930 +3 829 552 820 +3 886 106 862 +3 885 46 106 +3 1061 1104 1077 +3 320 67 834 +3 905 432 873 +3 874 848 834 +3 911 930 929 +3 1026 1572 1019 +3 972 974 992 +3 934 933 913 +3 914 934 913 +3 923 106 886 +3 777 46 885 +3 355 356 394 +3 449 96 195 +3 66 730 696 +3 807 96 832 +3 72 331 811 +3 896 106 923 +3 896 885 106 +3 1071 1070 1043 +3 932 947 931 +3 1049 1071 1043 +3 450 39 785 +3 946 967 945 +3 836 838 889 +3 787 761 788 +3 967 983 638 +3 966 967 638 +3 991 990 971 +3 597 1165 1231 +3 937 539 834 +3 934 948 947 +3 933 934 947 +3 886 846 942 +3 972 971 526 +3 1737 1762 1746 +3 1841 1851 1829 +3 417 1219 1218 +3 1166 110 1125 +3 159 342 113 +3 1065 1032 1274 +3 430 1065 1274 +3 1307 1320 1395 +3 767 1027 1 +3 846 798 1067 +3 735 1256 469 +3 1829 1850 1834 +3 398 317 1039 +3 288 32 34 +3 1051 1057 1058 +3 515 1684 1674 +3 1080 1079 1057 +3 1051 1058 1029 +3 1039 288 34 +3 1561 1726 1038 +3 1379 1307 1395 +3 304 642 409 +3 1396 1380 1381 +3 1030 1051 1029 +3 1219 1218 959 +3 598 1205 642 +3 1604 1615 1613 +3 1209 953 1193 +3 389 1521 1121 +3 398 75 70 +3 1314 273 524 +3 1022 1030 1021 +3 1022 1021 1016 +3 1030 1029 1021 +3 598 642 304 +3 528 1185 714 +3 1194 1209 1193 +3 177 12 397 +3 878 67 320 +3 1057 1045 1028 +3 1096 1095 1079 +3 1296 264 1319 +3 101 491 1237 +3 834 808 67 +3 1312 1484 1110 +3 963 1312 1110 +3 113 344 637 +3 1497 113 1496 +3 1119 1127 1113 +3 1312 1483 1484 +3 1799 1816 1812 +3 1079 1095 1057 +3 854 357 1191 +3 399 444 1210 +3 539 1011 383 +3 246 250 664 +3 1028 1038 1020 +3 1058 1057 1029 +3 311 1329 1396 +3 1260 1483 1312 +3 1187 1328 428 +3 1851 1864 1850 +3 317 609 619 +3 609 463 619 +3 1223 952 317 +3 603 1166 619 +3 1003 976 1094 +3 1248 556 297 +3 287 1885 555 +3 1138 1172 1163 +3 297 556 610 +3 1591 878 1009 +3 463 603 619 +3 749 110 1166 +3 1157 680 1081 +3 1886 304 409 +3 436 1027 605 +3 1015 1029 1014 +3 556 20 610 +3 20 604 610 +3 1099 1186 1189 +3 20 599 604 +3 1209 854 953 +3 1360 1550 1685 +3 492 398 70 +3 1172 1193 1192 +3 1175 1172 1192 +3 733 292 406 +3 202 733 406 +3 1010 834 67 +3 468 113 637 +3 154 1157 1081 +3 1172 1175 1163 +3 1193 854 1192 +3 953 854 1193 +3 1055 174 493 +3 1502 650 1046 +3 236 60 84 +3 1195 1194 1176 +3 85 177 397 +3 1163 1175 1148 +3 585 433 357 +3 1050 1045 1028 +3 1138 1148 1112 +3 1603 1402 1589 +3 1174 1192 1191 +3 1170 1174 1190 +3 1416 1417 1617 +3 398 1039 34 +3 75 398 1007 +3 1095 1107 1078 +3 1133 1134 1132 +3 1528 1022 1551 +3 1066 598 304 +3 292 733 667 +3 63 903 18 +3 619 1125 288 +3 1357 1355 1356 +3 733 1363 667 +3 470 1 212 +3 1017 830 136 +3 1119 1113 1095 +3 1387 231 247 +3 1107 1112 1078 +3 113 468 1496 +3 1028 1045 1050 +3 1014 1028 1013 +3 808 1011 539 +3 830 841 839 +3 12 50 275 +3 1344 1530 1115 +3 1363 153 19 +3 667 1363 19 +3 1103 1102 1068 +3 952 609 317 +3 1175 1174 1148 +3 1031 236 681 +3 595 10 555 +3 1119 1114 1587 +3 1114 1119 1096 +3 709 272 492 +3 451 736 434 +3 1174 1175 1192 +3 1380 1328 1316 +3 928 595 555 +3 153 489 1202 +3 572 598 1066 +3 19 153 1202 +3 1737 1746 1721 +3 1418 1417 1395 +3 1148 1147 1126 +3 488 1387 1448 +3 1245 488 1448 +3 1040 543 598 +3 572 1040 598 +3 1021 1029 1015 +3 1654 1653 1635 +3 329 58 595 +3 489 125 488 +3 1163 1148 1138 +3 1534 1687 1439 +3 342 709 344 +3 1112 1148 1126 +3 1202 489 488 +3 125 231 1387 +3 488 125 1387 +3 398 272 1223 +3 383 867 539 +3 414 296 928 +3 1176 1194 1172 +3 1028 1020 1013 +3 173 176 448 +3 1591 878 1009 +3 444 287 263 +3 1083 444 263 +3 272 952 1223 +3 1192 854 1191 +3 854 585 357 +3 1119 1152 1139 +3 1547 461 513 +3 296 329 595 +3 296 595 928 +3 603 749 1166 +3 1319 1329 1381 +3 1138 1152 1172 +3 63 18 33 +3 433 63 104 +3 769 433 104 +3 1174 1171 1147 +3 1372 1381 1375 +3 1613 1424 1603 +3 1113 1138 1107 +3 571 1218 1219 +3 1528 1551 1548 +3 1007 398 34 +3 1738 1737 1717 +3 1396 1329 1380 +3 1063 572 1066 +3 1153 1176 1152 +3 1139 1113 1127 +3 1119 1139 1127 +3 1191 357 1190 +3 357 240 1190 +3 1148 1174 1147 +3 29 460 325 +3 317 619 1039 +3 1754 1762 1737 +3 1329 311 1396 +3 1309 1380 1316 +3 1225 247 1218 +3 1448 1387 247 +3 1028 1045 1038 +3 1635 1453 1452 +3 116 1256 735 +3 514 1032 962 +3 1095 1078 1086 +3 1079 1095 1086 +3 357 433 240 +3 1174 1170 1171 +3 1218 1219 959 +3 1067 942 846 +3 1057 1095 1079 +3 10 114 287 +3 150 853 620 +3 555 10 287 +3 1152 1138 1139 +3 1052 1085 1370 +3 1704 1721 1703 +3 89 51 1002 +3 512 89 1002 +3 1152 1176 1172 +3 981 1206 992 +3 991 512 1002 +3 402 673 48 +3 1016 1551 1022 +3 1151 571 1219 +3 433 769 240 +3 1291 1380 1309 +3 1571 785 884 +3 589 490 605 +3 584 572 1063 +3 1057 1079 1045 +3 1138 1112 1107 +3 1045 1086 1078 +3 1095 1113 1107 +3 76 512 991 +3 1549 1552 1548 +3 203 769 271 +3 992 76 991 +3 274 89 512 +3 76 274 512 +3 274 51 89 +3 139 118 51 +3 274 139 51 +3 11 248 118 +3 139 11 118 +3 1056 1042 1068 +3 737 103 17 +3 871 302 858 +3 273 489 153 +3 1826 1835 1820 +3 197 48 294 +3 975 197 294 +3 197 713 402 +3 48 197 402 +3 584 1076 1040 +3 1079 1086 1045 +3 1029 1057 1028 +3 1139 1138 1113 +3 572 584 1040 +3 198 412 1040 +3 1076 198 1040 +3 298 273 153 +3 1500 1531 1359 +3 1096 1119 1095 +3 1194 1193 1172 +3 1560 785 1571 +3 882 866 894 +3 49 139 274 +3 1189 1207 1206 +3 1102 1133 1132 +3 1717 1721 1704 +3 1674 1487 1653 +3 584 570 1076 +3 894 1102 907 +3 821 1167 1199 +3 17 103 140 +3 50 17 140 +3 1042 1056 866 +3 1056 1068 894 +3 866 1056 894 +3 894 1068 1102 +3 1102 1132 936 +3 907 1102 936 +3 1160 950 936 +3 1132 1160 936 +3 1174 1191 1190 +3 1206 85 76 +3 992 1206 76 +3 397 274 76 +3 85 397 76 +3 275 49 274 +3 397 275 274 +3 140 139 49 +3 275 140 49 +3 103 11 139 +3 140 103 139 +3 409 642 329 +3 296 409 329 +3 436 975 1241 +3 436 605 975 +3 605 26 975 +3 26 197 975 +3 26 379 713 +3 197 26 713 +3 1010 539 937 +3 59 454 346 +3 652 408 413 +3 21 61 149 +3 171 345 347 +3 94 2 130 +3 130 256 134 +3 1004 1393 715 +3 313 368 367 +3 544 90 146 +3 81 535 379 +3 1257 527 950 +3 1257 950 1160 +3 302 301 259 +3 1004 414 928 +3 1160 1238 1257 +3 102 214 186 +3 1238 1160 1161 +3 1226 1238 1161 +3 1257 227 527 +3 95 233 364 +3 620 853 29 +3 1257 534 227 +3 282 454 230 +3 1453 1653 1452 +3 232 233 95 +3 821 1199 1886 +3 1232 1238 1226 +3 1238 981 1257 +3 1257 981 534 +3 417 408 652 +3 1233 1238 1232 +3 1027 436 42 +3 196 504 111 +3 169 180 81 +3 61 21 479 +3 631 231 388 +3 372 631 388 +3 1300 1382 1270 +3 1558 1559 857 +3 714 298 800 +3 298 153 800 +3 981 974 534 +3 704 348 5 +3 706 226 234 +3 388 231 443 +3 311 1330 1329 +3 1282 1300 1270 +3 1189 981 1238 +3 1233 1189 1238 +3 334 94 256 +3 1462 1672 1473 +3 895 786 787 +3 595 58 10 +3 1242 1251 1256 +3 489 231 125 +3 1236 1256 1250 +3 0 717 40 +3 470 212 0 +3 717 279 40 +3 276 230 59 +3 454 282 1237 +3 521 219 213 +3 417 652 346 +3 1266 417 346 +3 364 521 213 +3 171 363 345 +3 279 704 40 +3 470 0 160 +3 94 95 2 +3 42 281 717 +3 97 375 540 +3 61 479 363 +3 1216 705 706 +3 349 372 233 +3 453 42 717 +3 1241 975 150 +3 150 975 294 +3 214 362 282 +3 959 417 1266 +3 959 1219 417 +3 281 42 279 +3 408 607 372 +3 372 607 631 +3 0 40 25 +3 1221 69 1242 +3 287 114 263 +3 279 147 157 +3 704 279 157 +3 134 94 130 +3 81 52 535 +3 1265 58 329 +3 1249 1265 329 +3 82 97 13 +3 364 643 158 +3 82 375 97 +3 156 542 1214 +3 479 232 345 +3 35 82 13 +3 147 620 29 +3 102 186 58 +3 64 102 58 +3 363 479 345 +3 21 413 479 +3 652 413 21 +3 372 388 233 +3 216 43 5 +3 61 171 52 +3 413 349 479 +3 186 114 10 +3 619 288 1039 +3 697 1221 412 +3 171 61 363 +3 212 717 0 +3 1236 1242 1256 +3 607 408 417 +3 92 21 149 +3 279 42 147 +3 1221 1222 412 +3 697 1217 457 +3 156 1214 1213 +3 453 717 1 +3 552 829 868 +3 114 276 263 +3 570 734 701 +3 324 29 325 +3 1 717 212 +3 214 102 101 +3 2 364 213 +3 95 364 2 +3 74 97 68 +3 108 58 1265 +3 196 241 504 +3 416 325 326 +3 1346 900 767 +3 642 1205 1249 +3 71 135 6 +3 665 261 260 +3 389 43 216 +3 108 64 58 +3 1255 1236 1250 +3 7 1261 71 +3 1261 135 71 +3 83 737 17 +3 165 380 327 +3 147 29 324 +3 279 717 281 +3 417 1218 607 +3 1218 247 607 +3 83 573 737 +3 737 1239 7 +3 1239 1261 7 +3 42 620 147 +3 1215 697 1214 +3 1221 1242 1236 +3 1261 172 135 +3 651 62 6 +3 117 332 6 +3 62 117 6 +3 416 324 325 +3 157 324 416 +3 40 5 43 +3 1227 1239 737 +3 573 1227 737 +3 1261 1262 172 +3 172 651 6 +3 135 172 6 +3 62 807 117 +3 0 25 160 +3 364 388 643 +3 345 95 94 +3 1214 909 1215 +3 336 149 180 +3 233 388 364 +3 807 832 117 +3 1243 1262 1261 +3 1239 1243 1261 +3 42 150 620 +3 1215 1217 697 +3 1214 412 198 +3 1213 1214 198 +3 293 1227 573 +3 172 62 651 +3 878 1591 67 +3 1214 697 412 +3 1228 1243 1239 +3 1227 1228 1239 +3 96 45 438 +3 40 704 5 +3 59 92 181 +3 172 9 62 +3 643 22 158 +3 388 22 643 +3 92 149 181 +3 345 94 334 +3 652 21 92 +3 345 232 95 +3 214 101 362 +3 535 171 713 +3 1262 678 172 +3 678 9 172 +3 9 592 62 +3 479 349 233 +3 326 325 333 +3 117 832 332 +3 347 345 334 +3 234 1216 706 +3 486 62 592 +3 486 807 62 +3 884 1340 1884 +3 1270 1381 1372 +3 348 157 299 +3 1320 1418 1395 +3 1243 452 1262 +3 1262 452 678 +3 343 592 9 +3 149 61 52 +3 1224 1230 1228 +3 1246 1253 1243 +3 1243 1253 452 +3 163 486 592 +3 163 96 486 +3 2 87 54 +3 1474 1497 1496 +3 1488 1474 1496 +3 525 115 731 +3 1230 1246 1243 +3 1228 1230 1243 +3 452 343 9 +3 678 452 9 +3 31 592 343 +3 31 163 592 +3 743 226 437 +3 334 256 347 +3 149 52 180 +3 6 875 248 +3 1482 1474 1488 +3 1246 1230 1253 +3 452 31 343 +3 45 96 163 +3 364 158 521 +3 737 7 103 +3 213 73 87 +3 1063 533 584 +3 45 44 438 +3 42 436 150 +3 1244 1240 1092 +3 211 445 23 +3 1459 1465 1482 +3 1440 988 1442 +3 163 44 45 +3 1418 1354 1863 +3 436 1241 150 +3 453 1027 42 +3 108 1265 1249 +3 230 454 59 +3 1465 1474 1482 +3 311 1329 1319 +3 677 1234 1230 +3 1230 1234 1253 +3 452 374 31 +3 163 323 44 +3 282 214 230 +3 214 282 230 +3 1258 374 452 +3 1253 1258 452 +3 1215 909 1217 +3 1354 1418 1320 +3 1234 1240 1253 +3 294 418 853 +3 558 234 226 +3 11 103 71 +3 1240 1258 1253 +3 31 77 163 +3 77 323 163 +3 558 280 234 +3 214 224 186 +3 1205 1250 1249 +3 586 1296 1282 +3 1240 1234 677 +3 114 230 276 +3 1125 47 32 +3 308 77 31 +3 868 780 340 +3 1250 108 1249 +3 694 226 707 +3 288 1125 32 +3 319 31 374 +3 31 319 308 +3 529 323 77 +3 323 24 44 +3 280 309 234 +3 234 309 1216 +3 1491 1012 183 +3 77 308 529 +3 323 41 24 +3 225 361 1313 +3 6 248 71 +3 1258 669 374 +3 669 319 374 +3 249 41 323 +3 529 249 323 +3 115 444 782 +3 146 90 3 +3 309 705 1216 +3 669 1258 1240 +3 1264 109 636 +3 1302 185 1293 +3 1217 909 960 +3 237 265 1302 +3 337 1337 1336 +3 547 541 205 +3 1313 691 705 +3 286 352 337 +3 1327 1332 370 +3 798 846 901 +3 337 1338 1337 +3 361 225 611 +3 451 439 484 +3 677 1092 1240 +3 225 1313 109 +3 264 228 277 +3 352 1334 337 +3 785 783 1340 +3 309 1313 705 +3 674 683 682 +3 663 623 602 +3 622 663 710 +3 995 1591 806 +3 450 206 1047 +3 1283 99 215 +3 611 563 593 +3 475 246 664 +3 1294 1264 636 +3 442 1337 1347 +3 465 527 227 +3 659 630 636 +3 1454 1499 1527 +3 602 674 663 +3 107 1293 185 +3 1829 1851 1850 +3 109 690 660 +3 1313 690 109 +3 563 611 1264 +3 362 101 1237 +3 337 1334 1338 +3 206 450 517 +3 1347 471 503 +3 167 554 1323 +3 1468 1472 1489 +3 1091 1141 702 +3 471 563 1294 +3 715 1167 821 +3 1264 611 225 +3 1332 337 1336 +3 1004 1887 399 +3 586 1283 215 +3 1023 1252 1400 +3 1179 1370 1383 +3 1313 726 1324 +3 471 636 630 +3 352 420 1334 +3 1047 39 450 +3 99 1283 586 +3 237 1302 107 +3 14 446 330 +3 1313 361 645 +3 530 525 784 +3 1338 553 1348 +3 1337 1338 1348 +3 370 1332 442 +3 1332 1336 442 +3 715 821 414 +3 237 283 1322 +3 362 1237 282 +3 187 1303 237 +3 257 27 131 +3 801 430 1335 +3 1287 36 213 +3 554 167 338 +3 335 1331 1330 +3 311 335 1330 +3 1331 439 451 +3 506 420 421 +3 1330 1331 451 +3 663 674 1284 +3 674 385 1284 +3 184 107 185 +3 1322 283 314 +3 14 27 446 +3 439 520 484 +3 265 185 1302 +3 420 553 1338 +3 554 338 916 +3 1400 1298 1023 +3 553 563 471 +3 1324 726 1313 +3 1285 14 446 +3 434 435 451 +3 338 167 175 +3 277 311 1319 +3 546 783 244 +3 801 1335 1274 +3 338 1297 916 +3 1294 563 1264 +3 420 1338 1334 +3 783 606 244 +3 1337 1348 1347 +3 1313 1324 690 +3 311 312 335 +3 220 1285 1310 +3 1284 385 1278 +3 1128 1023 1252 +3 1285 220 14 +3 622 623 663 +3 109 660 636 +3 524 446 330 +3 1589 1402 1403 +3 338 220 1310 +3 674 682 385 +3 284 337 1332 +3 107 1302 1293 +3 663 1284 710 +3 1888 517 1067 +3 350 1251 69 +3 435 434 415 +3 435 415 428 +3 1297 338 1310 +3 682 307 385 +3 1306 1305 1280 +3 1276 1306 1280 +3 313 367 312 +3 1327 284 1332 +3 1336 1337 442 +3 1264 225 109 +3 180 169 1229 +3 475 801 246 +3 352 267 353 +3 1403 1404 1589 +3 1285 1292 1310 +3 307 682 748 +3 682 718 748 +3 1277 1276 1275 +3 1158 237 107 +3 1067 517 511 +3 1271 1282 1270 +3 489 125 443 +3 446 14 330 +3 586 1282 1271 +3 1292 1285 446 +3 446 330 1287 +3 443 125 489 +3 108 116 735 +3 813 100 99 +3 1276 1307 1306 +3 1483 1260 1317 +3 1272 586 1271 +3 1348 553 471 +3 1287 213 219 +3 330 446 1287 +3 443 231 489 +3 330 36 1287 +3 86 35 955 +3 450 785 1560 +3 1312 1304 1260 +3 1329 435 428 +3 1289 1276 1277 +3 1289 1290 1276 +3 1158 187 237 +3 1311 556 1248 +3 558 226 743 +3 1323 554 993 +3 1292 446 524 +3 273 443 489 +3 1290 1289 1277 +3 1290 1307 1276 +3 215 264 1296 +3 1304 1286 1273 +3 1260 1304 1273 +3 1311 1248 1273 +3 1286 1311 1273 +3 246 801 250 +3 1322 314 278 +3 1019 1572 1036 +3 307 748 608 +3 182 307 608 +3 1321 443 273 +3 471 1294 636 +3 215 1296 586 +3 1322 278 265 +3 542 701 1129 +3 1101 1284 1278 +3 39 783 785 +3 1304 400 1286 +3 400 1311 1286 +3 1339 244 1200 +3 1324 309 726 +3 674 602 683 +3 265 237 1322 +3 1653 1487 1470 +3 446 36 330 +3 1321 22 443 +3 1277 1267 1290 +3 1290 1308 1307 +3 1313 309 1324 +3 467 68 97 +3 1312 1279 1304 +3 367 1331 335 +3 524 330 1287 +3 1347 1348 471 +3 1308 1315 1307 +3 1365 1529 1108 +3 531 530 784 +3 123 531 784 +3 1041 556 1311 +3 666 1041 1311 +3 312 367 335 +3 1705 1707 1711 +3 690 1324 725 +3 1331 367 439 +3 710 1284 1101 +3 608 748 758 +3 170 699 169 +3 1308 1309 1315 +3 1368 1362 1371 +3 306 1200 244 +3 1279 1299 1304 +3 1304 1299 400 +3 666 1311 400 +3 251 283 237 +3 1853 1866 1871 +3 1312 963 1279 +3 1101 1279 963 +3 1314 1321 273 +3 699 1229 169 +3 1268 1290 1267 +3 1290 1309 1308 +3 386 1320 1315 +3 1320 386 1315 +3 314 1327 370 +3 542 156 701 +3 475 1486 1684 +3 1297 1185 528 +3 1031 916 60 +3 1297 1310 1185 +3 158 1321 1314 +3 1379 1305 1306 +3 553 420 506 +3 291 1342 782 +3 608 758 1041 +3 666 608 1041 +3 1635 1452 1453 +3 60 916 528 +3 916 1297 528 +3 1314 1287 158 +3 601 471 630 +3 1291 1290 1268 +3 1316 1315 1309 +3 1316 1320 1315 +3 400 608 666 +3 1292 524 1185 +3 187 189 188 +3 68 467 1318 +3 187 188 1303 +3 1281 1291 1268 +3 1290 1291 1309 +3 1320 1316 386 +3 1278 385 1299 +3 1279 1278 1299 +3 385 307 400 +3 1299 385 400 +3 307 182 400 +3 400 182 608 +3 801 208 430 +3 243 547 205 +3 1292 1185 1310 +3 1324 726 725 +3 699 170 105 +3 105 68 1318 +3 699 105 1318 +3 1316 1325 386 +3 1325 1187 386 +3 1096 1587 1114 +3 515 208 801 +3 1287 1314 524 +3 1287 219 158 +3 1541 1546 1545 +3 1540 1541 1545 +3 1361 519 1520 +3 570 1129 701 +3 785 1340 884 +3 176 903 127 +3 1232 1226 1233 +3 570 1366 1129 +3 1571 884 1560 +3 378 533 1432 +3 210 570 533 +3 378 210 533 +3 570 210 1366 +3 210 290 1129 +3 1366 210 1129 +3 1129 290 542 +3 290 909 542 +3 290 960 909 +3 124 697 290 +3 210 124 290 +3 697 457 290 +3 290 457 960 +3 960 457 1217 +3 378 124 210 +3 1477 1420 1006 +3 591 378 597 +3 1165 591 597 +3 378 591 124 +3 124 457 697 +3 124 1394 457 +3 1653 1470 1452 +3 1377 1378 1388 +3 1199 1048 1231 +3 124 350 1394 +3 1394 350 457 +3 1048 1165 1231 +3 1048 671 591 +3 1165 1048 591 +3 671 350 124 +3 591 671 124 +3 350 69 457 +3 98 467 97 +3 827 155 1880 +3 1544 1545 1157 +3 176 173 18 +3 1273 700 1260 +3 1369 1048 1199 +3 671 588 350 +3 1545 1546 1204 +3 1546 640 1204 +3 607 247 231 +3 1278 1279 1101 +3 154 1081 1513 +3 1777 1770 1785 +3 1295 499 143 +3 1850 1849 1834 +3 1199 732 1369 +3 732 1183 1369 +3 926 1048 1369 +3 1183 926 1369 +3 926 635 671 +3 1048 926 671 +3 671 635 588 +3 635 1251 350 +3 588 635 350 +3 1449 1472 1468 +3 1490 1489 1472 +3 1425 1074 1389 +3 1198 494 1065 +3 758 718 759 +3 1449 1468 1472 +3 1260 700 1317 +3 732 926 1183 +3 1449 1451 1468 +3 1468 1451 1472 +3 1490 538 1489 +3 272 406 952 +3 1183 926 732 +3 926 1183 635 +3 1426 1435 1434 +3 1449 1450 1451 +3 1421 1422 1426 +3 1426 1422 1435 +3 1469 1486 1490 +3 1472 1469 1490 +3 1183 455 635 +3 1435 1450 1449 +3 1434 1435 1449 +3 1451 1469 1472 +3 475 538 1490 +3 1486 475 1490 +3 475 664 538 +3 250 289 538 +3 664 250 538 +3 306 260 575 +3 455 1183 732 +3 1169 455 732 +3 455 469 635 +3 469 1256 1251 +3 635 469 1251 +3 1204 1389 1074 +3 1319 1381 1382 +3 1364 1370 1085 +3 1329 1396 1381 +3 1330 435 1329 +3 1444 1451 1450 +3 1435 1444 1450 +3 1520 519 1120 +3 1505 1120 1326 +3 640 883 1425 +3 1357 1364 1355 +3 1357 1370 1364 +3 1357 1211 1370 +3 1225 1245 1448 +3 1408 1423 1422 +3 1411 1408 1422 +3 1423 1436 1435 +3 1422 1423 1435 +3 1436 1437 1444 +3 1435 1436 1444 +3 1437 1452 1451 +3 1444 1437 1451 +3 1452 1470 1469 +3 1451 1452 1469 +3 1469 1470 1486 +3 1486 1674 1487 +3 1413 980 1420 +3 1470 1487 1486 +3 1537 154 1105 +3 1393 1054 1169 +3 668 455 1169 +3 1054 668 1169 +3 668 735 469 +3 455 668 469 +3 1455 1445 1446 +3 1185 524 298 +3 459 552 38 +3 1731 1739 1722 +3 1376 1211 1357 +3 1409 1408 1398 +3 1427 1437 1436 +3 1423 1427 1436 +3 1403 1388 1404 +3 84 1367 1363 +3 596 556 1041 +3 714 1185 298 +3 1004 1054 1393 +3 1210 574 399 +3 1320 1307 1315 +3 1635 1453 1438 +3 1359 1357 1356 +3 1358 1359 1356 +3 1377 1376 1357 +3 1359 1377 1357 +3 1388 1211 1376 +3 1377 1388 1376 +3 1402 1409 1398 +3 1388 1398 1211 +3 1402 1408 1409 +3 1402 1424 1423 +3 1408 1402 1423 +3 1423 1424 1427 +3 1424 1438 1437 +3 1427 1424 1437 +3 1438 1453 1452 +3 1437 1438 1452 +3 1197 828 650 +3 1111 1511 1510 +3 1300 1319 1382 +3 1010 808 539 +3 208 1198 430 +3 1200 459 38 +3 1539 1540 1544 +3 1507 1094 1405 +3 1094 976 1405 +3 475 515 801 +3 533 378 1432 +3 744 491 668 +3 1054 744 668 +3 491 101 735 +3 668 491 735 +3 384 459 1200 +3 306 384 1200 +3 1512 1197 650 +3 7 71 103 +3 1466 1295 1074 +3 1325 1328 1187 +3 1329 428 1328 +3 1507 1405 1397 +3 1120 897 1184 +3 1388 1403 1402 +3 1110 859 576 +3 1431 1110 576 +3 399 1212 744 +3 1363 800 153 +3 575 384 306 +3 1178 1100 1512 +3 1425 883 1074 +3 733 84 1363 +3 55 54 87 +3 1296 1300 1282 +3 1378 1377 1359 +3 1004 715 414 +3 1462 1479 1478 +3 1479 1492 222 +3 1478 1479 222 +3 1492 30 179 +3 222 1492 179 +3 30 351 179 +3 1544 1157 154 +3 429 297 709 +3 1110 1483 1475 +3 86 306 391 +3 700 159 1498 +3 1317 700 1498 +3 205 467 540 +3 828 518 650 +3 1672 1462 1473 +3 399 574 1212 +3 574 634 744 +3 1212 574 744 +3 634 1237 491 +3 744 634 491 +3 1367 714 1363 +3 375 606 112 +3 375 82 606 +3 82 86 391 +3 1457 1181 640 +3 1546 1457 640 +3 1479 1493 1492 +3 1493 258 30 +3 1492 1493 30 +3 429 1248 297 +3 39 375 199 +3 336 181 149 +3 1439 1511 1111 +3 1684 515 475 +3 1483 1317 1475 +3 1317 1498 1475 +3 429 342 159 +3 700 429 159 +3 1510 1052 1179 +3 1181 1130 883 +3 640 1181 883 +3 1405 980 1413 +3 1140 964 1181 +3 1509 1439 1517 +3 1479 1480 1493 +3 403 351 30 +3 258 403 30 +3 390 389 1121 +3 1400 897 1298 +3 604 272 709 +3 1460 749 603 +3 403 523 351 +3 1249 329 642 +3 1390 1466 883 +3 1382 1381 1270 +3 1363 714 800 +3 342 429 709 +3 540 375 1047 +3 297 604 709 +3 1467 1461 1201 +3 1130 1390 883 +3 1374 1454 1515 +3 1462 1480 1479 +3 1136 523 403 +3 964 1441 1181 +3 1059 1522 1128 +3 1003 221 500 +3 976 1003 500 +3 1100 1197 1512 +3 390 25 43 +3 1407 1416 1415 +3 1406 1407 1415 +3 1455 1446 1458 +3 1446 1463 1462 +3 1458 1446 1462 +3 1463 1464 1462 +3 1464 1481 1480 +3 1462 1464 1480 +3 1481 1494 1493 +3 1480 1481 1493 +3 1494 994 258 +3 1493 1494 258 +3 456 403 258 +3 994 456 258 +3 621 1136 403 +3 456 621 403 +3 621 523 1136 +3 621 745 523 +3 141 473 523 +3 745 141 523 +3 1516 1524 1476 +3 1128 245 1471 +3 1155 321 634 +3 574 1155 634 +3 599 681 202 +3 298 524 273 +3 681 236 202 +3 1368 1371 1527 +3 1263 1254 1540 +3 1502 1524 1476 +3 1476 1046 1115 +3 321 454 1237 +3 634 321 1237 +3 541 467 205 +3 467 97 540 +3 98 97 467 +3 236 84 733 +3 1446 1464 1463 +3 621 141 745 +3 1516 1476 1503 +3 1467 1201 828 +3 174 639 142 +3 202 236 733 +3 1686 1703 1702 +3 1494 1495 994 +3 1495 1123 994 +3 516 141 621 +3 1037 1059 1128 +3 590 467 541 +3 701 156 1164 +3 1398 1408 1383 +3 493 1301 322 +3 639 321 1155 +3 1429 1446 1445 +3 1481 1495 1494 +3 1220 456 994 +3 1123 1220 994 +3 954 322 495 +3 1373 493 322 +3 987 321 639 +3 260 306 86 +3 1370 1384 1383 +3 1280 1305 1362 +3 1305 1371 1362 +3 1305 1379 1371 +3 1392 1386 1371 +3 1392 1395 1386 +3 1395 1407 1386 +3 1395 1417 1416 +3 1407 1395 1416 +3 1703 1694 1704 +3 1428 1430 1429 +3 1430 1442 1446 +3 1429 1430 1446 +3 1442 1459 1464 +3 1446 1442 1464 +3 1459 1482 1481 +3 1464 1459 1481 +3 1482 1488 1481 +3 1488 1496 1495 +3 1481 1488 1495 +3 1496 468 1123 +3 1495 1496 1123 +3 637 1220 1123 +3 468 637 1123 +3 637 456 1220 +3 637 1203 456 +3 161 621 456 +3 1203 161 456 +3 75 516 621 +3 161 75 621 +3 1687 1694 1703 +3 75 34 495 +3 954 495 34 +3 34 32 322 +3 954 34 322 +3 32 47 322 +3 47 1373 322 +3 47 493 1373 +3 47 183 493 +3 183 1055 493 +3 1055 1012 174 +3 1012 639 174 +3 959 987 639 +3 1012 959 639 +3 959 321 987 +3 959 346 321 +3 346 454 321 +3 1341 1229 541 +3 1229 699 541 +3 699 1318 590 +3 541 699 590 +3 637 161 1203 +3 1615 1635 1438 +3 1300 1296 1319 +3 1379 1395 1392 +3 1615 1438 1424 +3 1613 1615 1424 +3 75 1007 34 +3 1229 1341 336 +3 700 1273 1248 +3 1392 1395 1379 +3 70 161 637 +3 183 1012 1055 +3 1673 1184 1504 +3 1159 1504 1184 +3 1266 346 959 +3 1604 1613 1603 +3 1459 1442 1456 +3 1075 1502 1503 +3 221 1100 500 +3 677 1084 1092 +3 1230 1224 677 +3 1537 1544 154 +3 1105 1097 1088 +3 1121 1105 1088 +3 1146 1346 767 +3 1087 293 176 +3 1100 1467 1197 +3 1154 585 1209 +3 962 1032 1065 +3 1765 1759 1024 +3 1408 1501 1383 +3 1516 1502 1524 +3 143 499 519 +3 1147 1142 1126 +3 1074 1094 680 +3 1399 897 1400 +3 1087 176 366 +3 235 1087 653 +3 897 1399 1184 +3 1149 1135 1061 +3 1685 1687 1360 +3 1379 1392 1371 +3 1810 259 242 +3 293 448 176 +3 1521 1537 1105 +3 235 653 677 +3 1100 1361 1467 +3 373 1538 1537 +3 1514 585 1059 +3 1059 585 341 +3 462 796 1023 +3 964 1106 1419 +3 1505 1326 1201 +3 711 63 1514 +3 964 1140 1092 +3 340 780 868 +3 796 1037 1128 +3 1037 127 796 +3 127 1037 796 +3 18 903 127 +3 1059 341 1522 +3 1508 366 1098 +3 1410 176 127 +3 293 1087 235 +3 1117 187 1876 +3 366 1508 1106 +3 1399 1159 1184 +3 1399 1400 1252 +3 1170 1099 1142 +3 1410 1037 1098 +3 903 18 127 +3 1383 1384 1211 +3 1522 341 1154 +3 1120 1505 1201 +3 1087 366 653 +3 1060 1135 1485 +3 964 1419 1130 +3 293 235 1224 +3 1244 1092 1457 +3 1545 1204 1157 +3 1252 1471 1399 +3 366 964 653 +3 1525 1126 1142 +3 160 360 1173 +3 366 176 1098 +3 499 1401 1298 +3 1539 1544 1537 +3 677 653 1084 +3 176 18 903 +3 199 375 112 +3 1520 1120 1461 +3 644 1539 1538 +3 80 1535 216 +3 176 1410 1098 +3 1140 1181 1457 +3 462 1098 796 +3 1541 1457 1546 +3 1361 1520 1467 +3 1467 1520 1461 +3 373 644 1538 +3 1106 462 781 +3 1591 834 67 +3 390 594 360 +3 160 390 360 +3 1254 1247 1541 +3 1075 1503 980 +3 903 711 127 +3 181 263 276 +3 444 1083 291 +3 1538 1539 1537 +3 1419 1106 1390 +3 519 1361 143 +3 216 1535 1521 +3 1094 976 680 +3 1441 1130 1181 +3 1006 672 1385 +3 1060 1485 1149 +3 43 389 390 +3 181 809 263 +3 1120 1201 1461 +3 1130 1419 1390 +3 1081 680 1507 +3 1231 1165 597 +3 809 1083 263 +3 964 366 1106 +3 809 181 336 +3 809 291 1083 +3 496 1506 1046 +3 650 496 1046 +3 390 1121 594 +3 1295 1003 1074 +3 1832 1845 1831 +3 1204 640 1389 +3 341 585 1154 +3 1081 1507 1082 +3 511 1884 569 +3 1197 1467 828 +3 1341 809 336 +3 1094 680 976 +3 1540 1545 1544 +3 1398 1388 1402 +3 594 1121 1088 +3 1233 1099 1189 +3 1502 1516 1503 +3 1506 1344 1115 +3 291 1137 1342 +3 1097 1513 1082 +3 1412 812 123 +3 498 831 812 +3 1412 498 812 +3 883 1466 1074 +3 1006 1420 962 +3 1399 1471 1159 +3 962 672 1006 +3 1118 1091 702 +3 1686 1687 1703 +3 653 964 1084 +3 1735 1756 1747 +3 1 470 1536 +3 127 1059 1037 +3 1046 1506 1115 +3 1288 1006 1385 +3 1044 1288 1385 +3 1147 1171 1170 +3 1170 1142 1147 +3 1137 291 1346 +3 808 1008 1011 +3 1159 245 1504 +3 672 962 1065 +3 1390 1106 781 +3 1390 1156 1466 +3 1466 1156 1295 +3 1015 1016 1021 +3 644 1540 1539 +3 1137 1533 1542 +3 1088 1089 1288 +3 594 1088 1288 +3 1721 1746 1729 +3 360 594 1044 +3 594 1288 1044 +3 245 1108 1504 +3 216 1521 389 +3 1370 1211 1384 +3 1089 1006 1288 +3 461 587 513 +3 513 587 1351 +3 1351 1414 1412 +3 1091 498 1412 +3 1414 1091 1412 +3 1115 1530 538 +3 1092 1140 1457 +3 1509 1090 1534 +3 1137 1146 1533 +3 1349 461 1547 +3 767 1 1064 +3 1529 1365 1195 +3 964 1130 1441 +3 1351 587 1414 +3 1115 538 289 +3 1405 1413 1397 +3 1513 1081 1082 +3 980 514 1420 +3 680 1094 1507 +3 461 1414 587 +3 1106 1508 462 +3 1052 1370 1179 +3 1457 1541 1247 +3 587 1414 461 +3 1420 514 962 +3 545 1201 1326 +3 63 585 1514 +3 1105 154 1097 +3 1064 1533 1146 +3 1414 587 461 +3 973 1414 587 +3 1414 973 587 +3 250 1476 289 +3 1157 1204 680 +3 1398 1383 1211 +3 1507 1397 1082 +3 1543 1349 1533 +3 1536 1543 1533 +3 1543 410 461 +3 1349 1543 461 +3 973 1141 1414 +3 1141 1091 1414 +3 1476 1115 289 +3 1084 964 1092 +3 373 1537 1521 +3 1521 1105 1121 +3 1476 250 1274 +3 1149 1525 1142 +3 1254 1541 1540 +3 1064 1 1533 +3 1082 1397 1089 +3 1088 1082 1089 +3 80 373 1521 +3 1535 80 1521 +3 1341 541 547 +3 39 199 783 +3 1536 1533 1 +3 973 1414 461 +3 410 973 461 +3 1397 1413 1420 +3 1089 1397 1420 +3 1111 1383 1501 +3 1503 1476 1274 +3 606 82 391 +3 780 38 868 +3 1135 1104 1061 +3 711 1514 1059 +3 1365 1194 1195 +3 1111 1510 1179 +3 1485 1135 1149 +3 796 1128 1252 +3 1228 293 1224 +3 644 1263 1540 +3 390 160 25 +3 63 33 104 +3 391 306 606 +3 306 244 606 +3 1097 154 1513 +3 494 672 1065 +3 1543 618 410 +3 537 973 410 +3 618 537 410 +3 1141 1352 702 +3 972 992 991 +3 1509 1534 1439 +3 1088 1097 1082 +3 1401 1023 1298 +3 53 569 1884 +3 1536 470 1543 +3 1410 127 1037 +3 1227 293 1228 +3 1128 1522 245 +3 1522 1154 245 +3 1850 1870 1849 +3 796 1252 1023 +3 173 83 33 +3 1543 470 618 +3 1242 69 1251 +3 1515 1527 1371 +3 1162 1135 1060 +3 1188 537 618 +3 1109 973 537 +3 1109 1141 973 +3 1109 1352 1141 +3 545 518 828 +3 1201 545 828 +3 1244 1457 1247 +3 1386 1515 1371 +3 1454 1518 1499 +3 1089 1477 1006 +3 462 1401 781 +3 1401 499 781 +3 63 433 585 +3 1886 1066 304 +3 1204 1074 680 +3 127 711 1059 +3 640 1425 1389 +3 1188 618 537 +3 499 1298 519 +3 1226 1060 1099 +3 1233 1226 1099 +3 1515 1454 1386 +3 1298 897 1120 +3 519 1298 1120 +3 1188 537 618 +3 1524 1502 1046 +3 1178 980 550 +3 1178 1512 1075 +3 1515 1386 1391 +3 1386 1454 1515 +3 1149 1142 1099 +3 1060 1149 1099 +3 1508 1098 462 +3 1401 462 1023 +3 1178 1075 980 +3 552 868 459 +3 1061 1525 1149 +3 1098 1037 796 +3 1524 1502 1476 +3 1476 1524 1046 +3 1828 1829 1834 +3 1089 1420 1477 +3 1352 672 494 +3 1502 1512 650 +3 1252 1128 1471 +3 1531 1518 1454 +3 1374 1531 1454 +3 1512 1502 1075 +3 1173 618 470 +3 160 1173 470 +3 1173 1188 618 +3 1173 360 1188 +3 1188 360 537 +3 360 1044 537 +3 537 1044 1109 +3 1044 1385 1109 +3 672 1352 1109 +3 1385 672 1109 +3 511 517 1560 +3 1390 781 1156 +3 223 222 178 +3 831 123 812 +3 141 1333 1618 +3 1812 1829 1828 +3 115 1005 1210 +3 1636 1637 1656 +3 1515 1454 1527 +3 1682 538 1530 +3 1637 1657 1656 +3 1072 1525 1061 +3 1071 1072 1061 +3 548 1350 142 +3 1072 1592 1525 +3 1487 1674 1684 +3 67 937 1010 +3 1754 1784 1762 +3 1462 1478 1473 +3 1137 1542 778 +3 1614 1637 1616 +3 1036 1572 1049 +3 1049 1584 1072 +3 1072 1584 1592 +3 1126 1525 1592 +3 1584 1126 1592 +3 199 606 783 +3 1499 1362 1368 +3 1680 1678 1679 +3 1049 1586 1584 +3 799 178 1558 +3 702 494 1198 +3 1499 1368 1527 +3 868 552 459 +3 1486 1487 1674 +3 1572 1584 1586 +3 1049 1572 1586 +3 779 702 1198 +3 1799 1812 1795 +3 1618 1333 141 +3 1662 1676 1675 +3 805 1350 548 +3 1026 1570 1572 +3 1572 1073 1584 +3 1073 1078 1584 +3 1112 1126 1584 +3 1078 1112 1584 +3 1617 1620 1416 +3 1676 560 483 +3 1675 1676 483 +3 1659 1678 1663 +3 1572 1570 1073 +3 1054 1004 399 +3 827 1880 1870 +3 1404 1590 1604 +3 1374 1515 1391 +3 1345 472 498 +3 802 1555 1519 +3 1655 1675 1674 +3 1416 1614 1415 +3 1407 1406 1386 +3 482 560 1676 +3 1678 1677 1663 +3 1146 1137 1346 +3 1026 1562 1570 +3 1562 1570 1572 +3 1570 1562 1572 +3 1677 482 1676 +3 547 1556 798 +3 701 734 570 +3 1589 1404 1603 +3 1641 1638 1634 +3 1639 1641 1634 +3 1657 1658 1662 +3 260 261 575 +3 156 701 1164 +3 1639 1634 1621 +3 1638 1658 1657 +3 1637 1638 1657 +3 1519 1118 702 +3 1415 1605 1590 +3 779 1519 702 +3 1406 1415 1590 +3 1663 1677 1662 +3 1641 1659 1638 +3 1658 1663 1662 +3 1635 1636 1654 +3 1562 1073 1570 +3 1687 1686 1439 +3 1047 375 39 +3 1621 1620 1617 +3 738 1519 779 +3 483 515 1674 +3 473 1333 531 +3 481 473 531 +3 1155 142 639 +3 1662 1675 1655 +3 1656 1662 1655 +3 1038 1045 1073 +3 1562 1038 1073 +3 222 179 178 +3 1428 1621 1617 +3 1675 483 1674 +3 1159 1471 245 +3 1365 245 1154 +3 1209 1365 1154 +3 245 1365 1108 +3 1637 1636 1616 +3 1416 1620 1614 +3 1638 1637 1614 +3 884 1340 1884 +3 1049 1043 1036 +3 1605 1616 1607 +3 1620 1638 1614 +3 1455 1428 1445 +3 1622 1529 1195 +3 1177 1622 1195 +3 550 976 500 +3 1587 1523 1119 +3 1428 1455 1639 +3 1096 1079 1080 +3 553 536 563 +3 1404 1604 1603 +3 1153 1622 1177 +3 641 638 661 +3 1013 1020 1038 +3 1051 1114 1096 +3 1080 1051 1096 +3 1114 1587 1096 +3 1556 243 798 +3 1532 1623 1622 +3 1153 1532 1622 +3 1642 1529 1622 +3 1623 1642 1622 +3 1642 1504 1108 +3 1529 1642 1108 +3 1378 1404 1388 +3 178 179 1559 +3 1455 1473 1641 +3 1653 1655 1674 +3 805 530 531 +3 33 18 173 +3 1605 1604 1590 +3 179 1259 1559 +3 1051 1587 1114 +3 1118 1345 1091 +3 851 523 532 +3 715 732 1167 +3 1636 1655 1654 +3 1621 1634 1620 +3 1093 1280 1362 +3 1051 1114 1587 +3 1604 1605 1607 +3 1022 1563 1051 +3 1030 1022 1051 +3 1563 1573 1114 +3 1051 1563 1114 +3 1573 1585 1587 +3 1114 1573 1587 +3 1585 1599 1523 +3 1587 1585 1523 +3 1599 1608 1532 +3 1523 1599 1532 +3 1608 1624 1623 +3 1532 1608 1623 +3 1643 1642 1623 +3 1624 1643 1623 +3 1643 1673 1504 +3 1642 1643 1504 +3 551 547 798 +3 1014 1016 1015 +3 1306 1307 1379 +3 805 548 525 +3 1656 1655 1636 +3 1333 805 531 +3 1683 1184 1673 +3 1608 1625 1624 +3 1644 1643 1624 +3 1625 1644 1624 +3 1664 1673 1643 +3 1644 1664 1643 +3 1023 1128 1252 +3 585 854 1209 +3 1351 1412 123 +3 322 1301 495 +3 799 844 474 +3 516 495 141 +3 1555 1345 1118 +3 495 1618 141 +3 1047 205 540 +3 141 1333 473 +3 1433 1426 1434 +3 597 378 1063 +3 1528 1564 1563 +3 1022 1528 1563 +3 1564 1574 1573 +3 1563 1564 1573 +3 1573 1574 1585 +3 1574 1576 1585 +3 1585 1576 1599 +3 1576 1600 1599 +3 1600 1609 1608 +3 1599 1600 1608 +3 1609 1626 1625 +3 1608 1609 1625 +3 1645 1644 1625 +3 1626 1645 1625 +3 1645 1665 1664 +3 1644 1645 1664 +3 1665 1673 1664 +3 1665 1326 1683 +3 1673 1665 1683 +3 1326 1184 1683 +3 1590 1404 1378 +3 1614 1616 1605 +3 1415 1614 1605 +3 495 1333 1618 +3 738 802 1519 +3 1473 1659 1641 +3 1301 805 1333 +3 205 206 517 +3 1405 976 550 +3 495 1301 1333 +3 1593 1601 1600 +3 1576 1593 1600 +3 1600 1601 1609 +3 716 208 515 +3 500 1178 550 +3 1259 523 851 +3 1559 1259 851 +3 831 481 812 +3 513 1349 1547 +3 1616 1636 1635 +3 222 223 1680 +3 1301 493 1350 +3 805 1301 1350 +3 1548 1552 1528 +3 1552 1565 1564 +3 1528 1552 1564 +3 1564 1565 1574 +3 1574 1565 1576 +3 1565 1577 1576 +3 1594 1593 1576 +3 1577 1594 1576 +3 1602 1601 1593 +3 1594 1602 1593 +3 1602 1610 1609 +3 1601 1602 1609 +3 1610 1627 1626 +3 1609 1610 1626 +3 1627 1646 1645 +3 1626 1627 1645 +3 1666 1665 1645 +3 1646 1666 1645 +3 1666 545 1326 +3 1665 1666 1326 +3 483 716 515 +3 547 243 1556 +3 221 1003 1295 +3 143 221 1295 +3 809 1341 547 +3 551 809 547 +3 782 1342 778 +3 731 782 778 +3 1458 1660 1455 +3 752 309 753 +3 1424 1402 1603 +3 1519 1555 1118 +3 1549 1553 1552 +3 1552 1553 1565 +3 1775 1799 1784 +3 1406 1590 1378 +3 493 174 142 +3 1350 493 142 +3 1656 1657 1662 +3 291 809 551 +3 731 778 513 +3 523 473 481 +3 1365 1209 1194 +3 1346 291 551 +3 784 731 513 +3 1550 1554 1553 +3 1549 1550 1553 +3 1566 1565 1553 +3 1554 1566 1553 +3 1566 1578 1577 +3 1565 1566 1577 +3 1578 1579 1577 +3 1577 1579 1594 +3 1579 1595 1602 +3 1594 1579 1602 +3 1595 1611 1610 +3 1602 1595 1610 +3 1628 1627 1610 +3 1611 1628 1610 +3 1628 1647 1646 +3 1627 1628 1646 +3 1647 1667 1666 +3 1646 1647 1666 +3 1667 1668 1666 +3 518 545 1666 +3 1668 518 1666 +3 1074 1003 1094 +3 1607 1616 1615 +3 123 784 513 +3 1499 1527 1454 +3 1604 1607 1615 +3 1566 1579 1578 +3 1628 1648 1647 +3 1648 1668 1667 +3 1647 1648 1667 +3 1681 518 1668 +3 778 1533 1349 +3 513 778 1349 +3 496 650 497 +3 1615 1616 1635 +3 1429 1445 1428 +3 718 758 748 +3 1678 1680 474 +3 1680 223 474 +3 778 1542 1533 +3 206 205 1047 +3 1478 222 1680 +3 1679 1678 1659 +3 857 831 498 +3 1386 1406 1391 +3 1360 1090 1554 +3 1550 1360 1554 +3 1567 1566 1554 +3 1090 1567 1554 +3 1567 1580 1579 +3 1566 1567 1579 +3 1580 1596 1595 +3 1579 1580 1595 +3 1596 1606 1611 +3 1595 1596 1611 +3 1606 1629 1628 +3 1611 1606 1628 +3 1629 1630 1628 +3 1630 1649 1648 +3 1628 1630 1648 +3 1649 1526 1668 +3 1648 1649 1668 +3 496 1681 1668 +3 1526 496 1668 +3 497 518 1681 +3 496 497 1681 +3 518 497 650 +3 1005 574 1210 +3 123 513 1351 +3 1877 1876 1865 +3 1558 1555 802 +3 799 1558 802 +3 472 857 498 +3 1455 1641 1639 +3 1634 1638 1620 +3 482 802 738 +3 1248 429 700 +3 1654 1655 1653 +3 1606 1630 1629 +3 1473 1478 1680 +3 1362 1499 1557 +3 1558 857 472 +3 1120 1184 1326 +3 857 851 831 +3 844 802 482 +3 474 844 482 +3 1555 1558 472 +3 844 799 802 +3 1342 1137 778 +3 1649 1669 1526 +3 1669 1506 496 +3 1526 1669 496 +3 1421 1411 1422 +3 851 532 481 +3 780 340 383 +3 1008 780 383 +3 1568 1567 1090 +3 1509 1568 1090 +3 1568 1581 1580 +3 1567 1568 1580 +3 1597 1596 1580 +3 1581 1597 1580 +3 1596 1597 1606 +3 1597 1612 1606 +3 1612 1631 1630 +3 1606 1612 1630 +3 1631 1650 1649 +3 1630 1631 1649 +3 1649 1650 1669 +3 1678 482 1677 +3 560 738 716 +3 1345 498 1091 +3 500 1100 1178 +3 1678 474 482 +3 1009 878 53 +3 1841 1855 1851 +3 1454 1527 1499 +3 827 107 1864 +3 495 516 75 +3 208 716 1198 +3 1428 1639 1621 +3 1771 1280 1093 +3 842 1008 808 +3 378 533 1063 +3 1582 1581 1568 +3 1659 1663 1658 +3 1865 1864 1851 +3 1503 514 980 +3 1583 1582 1568 +3 1588 1581 1582 +3 1583 1588 1582 +3 1598 1597 1581 +3 1588 1598 1581 +3 1433 1612 1597 +3 1433 1619 1612 +3 1632 1631 1612 +3 1619 1632 1612 +3 1651 1650 1631 +3 1632 1651 1631 +3 1651 1670 1669 +3 1650 1651 1669 +3 1670 1344 1506 +3 1669 1670 1506 +3 1660 1473 1455 +3 1353 842 808 +3 995 1353 808 +3 1353 1008 842 +3 1353 842 1008 +3 38 780 1008 +3 842 38 1008 +3 1517 1569 1568 +3 1509 1517 1568 +3 1569 1583 1568 +3 716 779 1198 +3 716 738 779 +3 560 482 738 +3 1360 1687 1534 +3 1353 38 842 +3 1111 1179 1383 +3 1598 1433 1597 +3 868 38 552 +3 1045 1078 1073 +3 1124 406 272 +3 1433 1632 1619 +3 474 223 1122 +3 1033 1765 1024 +3 1378 1359 1374 +3 1269 1270 1281 +3 291 782 444 +3 483 560 716 +3 1462 1473 1660 +3 499 1156 781 +3 1214 542 909 +3 1555 472 1345 +3 178 1559 1558 +3 851 481 831 +3 1269 1281 1268 +3 824 1856 1846 +3 53 1884 1009 +3 1439 1111 1517 +3 1111 1501 1569 +3 1517 1111 1569 +3 1411 1583 1569 +3 1411 1588 1583 +3 1411 1421 1588 +3 1421 1598 1588 +3 1421 1426 1598 +3 1426 1433 1598 +3 1434 1433 1598 +3 1433 1434 1598 +3 1633 1632 1433 +3 1434 1633 1433 +3 1633 1640 1632 +3 1652 1651 1632 +3 1640 1652 1632 +3 1671 1670 1651 +3 1652 1671 1651 +3 1671 1682 1344 +3 1670 1671 1344 +3 1274 1032 514 +3 1503 1274 514 +3 532 523 481 +3 1319 264 277 +3 546 806 1591 +3 546 995 806 +3 546 746 995 +3 1339 1353 995 +3 746 1339 995 +3 1339 1200 38 +3 1353 1339 38 +3 223 178 799 +3 1530 1344 1682 +3 1449 1633 1434 +3 399 744 1054 +3 831 812 123 +3 1122 223 799 +3 243 205 517 +3 1473 1679 1659 +3 1024 1034 1033 +3 1591 1340 546 +3 1501 1411 1569 +3 1156 499 1295 +3 1449 1640 1633 +3 1209 953 854 +3 1458 1462 1660 +3 1224 235 677 +3 1523 1153 1119 +3 1085 1355 1364 +3 1468 1652 1640 +3 1449 1468 1640 +3 1489 1671 1652 +3 1468 1489 1652 +3 1671 1489 1682 +3 1489 538 1682 +3 1836 1857 1844 +3 803 189 1117 +3 1685 1696 1695 +3 1785 1791 1801 +3 1808 1801 1791 +3 1791 1792 1815 +3 1751 1750 1735 +3 1843 1862 1842 +3 1210 444 115 +3 1800 1816 1799 +3 1803 1821 1802 +3 995 834 1591 +3 758 759 1182 +3 1836 1858 1857 +3 810 168 1857 +3 1858 810 1857 +3 1740 1757 1764 +3 873 872 865 +3 1808 1815 1801 +3 1815 1819 1801 +3 804 803 1117 +3 1741 1757 1740 +3 1793 865 1788 +3 1812 1816 1830 +3 804 1117 1878 +3 1561 1725 1726 +3 1857 168 824 +3 1757 1779 1764 +3 1764 1779 1786 +3 78 815 1868 +3 1822 1821 1803 +3 1717 1737 1721 +3 1844 1857 1846 +3 1725 1724 1013 +3 824 168 79 +3 1713 1718 1696 +3 1715 1714 1699 +3 1722 1730 1711 +3 1843 1842 1825 +3 1819 1843 1825 +3 1857 824 1846 +3 1821 1827 1826 +3 1809 1821 1826 +3 1815 1843 1819 +3 1821 1809 1802 +3 1548 1689 1549 +3 1733 1561 1562 +3 1733 1725 1561 +3 604 599 406 +3 406 599 202 +3 1822 1836 1821 +3 815 1881 1867 +3 1852 1855 1830 +3 1699 1706 1016 +3 54 55 845 +3 158 219 521 +3 1742 1741 1725 +3 1733 1742 1725 +3 1763 1777 1785 +3 1033 1788 1765 +3 1769 1776 1749 +3 1749 1755 1734 +3 1756 1769 1747 +3 1732 1751 1735 +3 1707 1714 1722 +3 1738 1754 1737 +3 1876 1864 1865 +3 1722 1714 1731 +3 865 1810 1787 +3 1788 865 1787 +3 1715 1732 1714 +3 1742 1757 1741 +3 1804 1803 1786 +3 1779 1804 1786 +3 1770 1791 1785 +3 1806 1810 242 +3 1722 1747 1730 +3 1705 1706 1707 +3 1804 1805 1803 +3 1835 1843 1815 +3 1346 551 1883 +3 1802 1809 1792 +3 1837 1836 1822 +3 1017 1837 1822 +3 1878 1117 1877 +3 1711 1730 1713 +3 1690 1706 1705 +3 1837 136 1836 +3 1830 1855 1841 +3 1868 1867 1856 +3 599 20 681 +3 136 1858 1836 +3 1791 1815 1808 +3 1024 1759 1018 +3 1730 1734 1713 +3 1785 1790 1756 +3 20 596 681 +3 1831 1830 1817 +3 1797 1802 1792 +3 1864 1876 827 +3 1881 815 814 +3 1787 1806 1781 +3 1116 916 1031 +3 814 1882 1872 +3 1711 1713 1697 +3 1110 1475 859 +3 1690 1705 1698 +3 1785 1801 1790 +3 1765 1787 1759 +3 596 20 556 +3 596 1180 681 +3 681 1180 1031 +3 1882 804 1872 +3 1698 1711 1697 +3 1759 1781 1758 +3 900 1883 979 +3 1696 1717 1704 +3 1026 1561 1562 +3 1769 1796 1776 +3 1013 1723 1715 +3 1734 1717 1718 +3 1832 1831 1813 +3 1180 316 1116 +3 1031 1180 1116 +3 1561 1742 1733 +3 1882 814 804 +3 1820 1835 1815 +3 1442 988 1447 +3 710 577 622 +3 1810 1806 1787 +3 596 1752 1180 +3 1180 316 1116 +3 316 1180 1116 +3 316 993 916 +3 1116 316 916 +3 1759 1787 1781 +3 1776 1800 1775 +3 1732 1735 1714 +3 1886 409 821 +3 1703 1729 1710 +3 1693 1685 1550 +3 1016 1706 1690 +3 1693 1689 1697 +3 1842 1862 1861 +3 1694 1695 1704 +3 1687 1685 1694 +3 988 382 1447 +3 1447 382 576 +3 747 797 120 +3 1776 1775 1755 +3 1199 1167 732 +3 1041 758 596 +3 596 758 1752 +3 1876 1158 107 +3 1685 1695 1694 +3 1858 818 810 +3 382 1431 576 +3 1693 1697 1696 +3 243 1888 798 +3 1117 189 187 +3 1823 1822 1803 +3 1805 1823 1803 +3 1442 1430 1443 +3 818 1858 136 +3 356 477 424 +3 382 1575 1431 +3 916 993 554 +3 467 590 1318 +3 1685 1693 1696 +3 1845 1866 1853 +3 1431 1575 1110 +3 1182 1752 758 +3 1182 1180 1752 +3 1182 825 1180 +3 1180 825 316 +3 1017 136 1837 +3 1430 1440 1443 +3 1440 908 988 +3 988 908 382 +3 1575 549 1110 +3 549 1150 963 +3 1110 549 963 +3 1825 1813 1814 +3 1796 1817 1800 +3 825 633 316 +3 1699 1714 1706 +3 633 993 316 +3 633 1323 993 +3 1430 1869 1440 +3 908 1575 382 +3 1101 963 1150 +3 1734 1738 1717 +3 1819 1825 1814 +3 1801 1819 1814 +3 633 825 1182 +3 1440 1869 908 +3 908 996 1575 +3 759 787 786 +3 1809 1826 1820 +3 1869 1145 908 +3 549 1101 1150 +3 1723 1732 1715 +3 1428 1847 1430 +3 1145 56 908 +3 908 56 996 +3 1144 1101 549 +3 1809 1820 1815 +3 1793 1788 1033 +3 797 633 1182 +3 759 797 1182 +3 1735 1750 1756 +3 996 56 1575 +3 56 549 1575 +3 1750 1763 1756 +3 1847 1848 1430 +3 1848 1859 1430 +3 1430 1859 1869 +3 56 1168 549 +3 1690 1689 1548 +3 1758 1779 1757 +3 786 797 759 +3 747 633 797 +3 633 747 1323 +3 1323 747 167 +3 865 866 873 +3 1778 1791 1770 +3 78 1868 824 +3 1805 1804 1779 +3 1428 1848 1847 +3 56 1053 1168 +3 1168 1131 1144 +3 549 1168 1144 +3 1877 1117 1876 +3 1707 1722 1711 +3 1731 1735 1739 +3 830 1823 1805 +3 830 1822 1823 +3 1859 1873 1145 +3 1869 1859 1145 +3 1131 1101 1144 +3 1814 1813 1790 +3 830 1017 1822 +3 207 562 561 +3 1772 1771 1093 +3 1557 1772 1093 +3 866 1033 1042 +3 1617 1417 1428 +3 1428 1417 1848 +3 1734 1755 1738 +3 1821 1836 1844 +3 1801 1814 1790 +3 895 797 786 +3 787 759 760 +3 1873 961 1145 +3 1145 961 56 +3 1053 1131 1168 +3 1833 1101 1131 +3 1833 710 1101 +3 1853 1871 1852 +3 1689 1690 1698 +3 1772 1275 1771 +3 1848 1417 1859 +3 242 830 1806 +3 1713 1734 1718 +3 1800 1799 1775 +3 1695 1696 1704 +3 1518 1557 1499 +3 1794 1275 1772 +3 1809 1815 1792 +3 1417 1418 1859 +3 961 951 56 +3 56 951 1053 +3 1833 1131 710 +3 1019 1742 1561 +3 1758 1757 1742 +3 1780 1779 1758 +3 1748 1557 1518 +3 1794 1277 1275 +3 1873 1879 961 +3 951 559 1053 +3 1053 559 1131 +3 1876 107 827 +3 1747 1769 1749 +3 1267 1277 1794 +3 1418 1863 1859 +3 1859 1863 1873 +3 1868 815 1867 +3 1766 1772 1557 +3 1748 1766 1557 +3 866 1793 1033 +3 559 1196 1131 +3 1196 1343 1131 +3 1131 1343 710 +3 1766 1794 1772 +3 180 1229 336 +3 415 1879 1873 +3 415 961 1879 +3 1343 577 710 +3 866 865 1793 +3 1362 1557 1093 +3 1789 1794 1766 +3 1873 1863 415 +3 1456 1442 1447 +3 1018 1742 1019 +3 1743 1748 1518 +3 1531 1743 1518 +3 1789 1267 1794 +3 434 961 415 +3 434 951 961 +3 1781 1779 1780 +3 1811 1267 1789 +3 1781 1805 1779 +3 404 522 438 +3 434 736 951 +3 736 559 951 +3 1743 1766 1748 +3 559 736 1196 +3 1722 1739 1747 +3 715 1393 1169 +3 1534 1090 1360 +3 1269 1271 1270 +3 604 1124 272 +3 1358 1531 1500 +3 1824 1268 1267 +3 1811 1824 1267 +3 155 813 1880 +3 736 480 1196 +3 1196 480 1343 +3 1343 480 577 +3 1790 1813 1796 +3 1782 1789 1766 +3 1760 1782 1766 +3 1803 1802 1797 +3 1777 1770 1785 +3 1719 1743 1531 +3 1760 1766 1743 +3 1838 1269 1268 +3 1824 1838 1268 +3 1750 1770 1777 +3 480 578 577 +3 1689 1693 1550 +3 1763 1750 1777 +3 1770 1777 1785 +3 1691 1358 1356 +3 1719 1531 1358 +3 736 1062 480 +3 1813 1831 1817 +3 1549 1689 1550 +3 1756 1763 1785 +3 1691 1356 1355 +3 1085 1691 1355 +3 1761 1760 1743 +3 1744 1761 1743 +3 1807 1811 1789 +3 1838 1271 1269 +3 78 824 79 +3 1876 187 1158 +3 1821 1844 1827 +3 1708 1719 1358 +3 1744 1743 1719 +3 1782 1807 1789 +3 1807 1824 1811 +3 1860 1271 1838 +3 736 451 1062 +3 1756 1790 1769 +3 1725 1740 1724 +3 1698 1705 1711 +3 1510 1691 1085 +3 1708 1358 1691 +3 1761 1782 1760 +3 1818 1824 1807 +3 1860 1272 1271 +3 1718 1717 1696 +3 451 207 480 +3 1062 451 480 +3 1817 1830 1816 +3 1796 1800 1776 +3 1510 1085 1052 +3 1818 1838 1824 +3 1875 1272 1860 +3 1813 1817 1796 +3 1720 1719 1708 +3 1736 1744 1719 +3 1839 1838 1818 +3 96 807 486 +3 1018 1758 1742 +3 1842 1861 1845 +3 1881 814 1872 +3 1781 1780 1758 +3 1867 1881 1872 +3 1692 1691 1510 +3 1720 1736 1719 +3 1783 1782 1761 +3 1839 1860 1838 +3 1875 1661 586 +3 1272 1875 586 +3 1806 1805 1781 +3 1712 1716 1708 +3 1727 1720 1708 +3 1716 1727 1708 +3 1854 1860 1839 +3 1143 1661 1875 +3 1661 99 586 +3 1143 99 1661 +3 1031 60 236 +3 1709 1708 1691 +3 1692 1709 1691 +3 1709 1712 1708 +3 1709 1716 1712 +3 1709 1727 1716 +3 1727 1728 1720 +3 1786 1803 1797 +3 747 120 167 +3 484 207 451 +3 1736 1753 1744 +3 1753 1761 1744 +3 1753 1783 1761 +3 1874 1860 1854 +3 1874 1875 1860 +3 1792 1791 1778 +3 824 1868 1856 +3 1817 1816 1800 +3 520 207 484 +3 1692 1700 1709 +3 1728 1727 1709 +3 1728 1745 1727 +3 1511 1688 1510 +3 1688 1692 1510 +3 1874 1143 1875 +3 1013 1715 1699 +3 1749 1776 1755 +3 1759 1758 1018 +3 1688 1701 1700 +3 1692 1688 1700 +3 1773 1783 1753 +3 1849 1854 1839 +3 813 1143 1874 +3 813 99 1143 +3 1842 1845 1832 +3 1710 1709 1700 +3 1701 1710 1700 +3 1872 804 1878 +3 1825 1842 1832 +3 1853 1852 1830 +3 1730 1749 1734 +3 1689 1698 1697 +3 1805 1806 830 +3 1710 1728 1709 +3 1840 1849 1839 +3 1831 1853 1830 +3 1724 1723 1013 +3 1729 1728 1710 +3 1849 1874 1854 +3 1849 1870 1874 +3 1741 1740 1725 +3 1755 1775 1754 +3 1731 1714 1735 +3 1747 1749 1730 +3 1706 1714 1707 +3 1729 1745 1728 +3 1767 1773 1753 +3 1880 813 1874 +3 1870 1880 1874 +3 865 858 1810 +3 1810 858 259 +3 1861 1866 1845 +3 1790 1796 1769 +3 1755 1754 1738 +3 1739 1735 1747 +3 1686 1688 1511 +3 1439 1686 1511 +3 1746 1745 1729 +3 1774 1773 1767 +3 1834 1849 1840 +3 1016 1690 1548 +3 1788 1787 1765 +3 1551 1016 1548 +3 1686 1702 1701 +3 1688 1686 1701 +3 1703 1710 1701 +3 1702 1703 1701 +3 1795 1798 1773 +3 1825 1832 1813 +3 1697 1713 1696 +3 1762 1745 1746 +3 1762 1768 1745 +3 1795 1773 1774 diff --git a/samples/cpp/tutorial_code/viz/creating_widgets.cpp b/samples/cpp/tutorial_code/viz/creating_widgets.cpp new file mode 100644 index 0000000000..9ee6c54364 --- /dev/null +++ b/samples/cpp/tutorial_code/viz/creating_widgets.cpp @@ -0,0 +1,113 @@ +/** + * @file creating_widgets.cpp + * @brief Creating custom widgets using VTK + * @author Ozan Cagri Tonkal + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; +using namespace std; + +/** + * @function help + * @brief Display instructions to use this tutorial program + */ +void help() +{ + cout + << "--------------------------------------------------------------------------" << endl + << "This program shows how to create a custom widget. You can create your own " + << "widgets by extending Widget2D/Widget3D, and with the help of WidgetAccessor." << endl + << "Usage:" << endl + << "./creating_widgets" << endl + << endl; +} + +/** + * @class TriangleWidget + * @brief Defining our own 3D Triangle widget + */ +class WTriangle : public viz::Widget3D +{ + public: + WTriangle(const Point3f &pt1, const Point3f &pt2, const Point3f &pt3, const viz::Color & color = viz::Color::white()); +}; + +/** + * @function TriangleWidget::TriangleWidget + * @brief Constructor + */ +WTriangle::WTriangle(const Point3f &pt1, const Point3f &pt2, const Point3f &pt3, const viz::Color & color) +{ + // Create a triangle + vtkSmartPointer points = vtkSmartPointer::New(); + points->InsertNextPoint(pt1.x, pt1.y, pt1.z); + points->InsertNextPoint(pt2.x, pt2.y, pt2.z); + points->InsertNextPoint(pt3.x, pt3.y, pt3.z); + + vtkSmartPointer triangle = vtkSmartPointer::New(); + triangle->GetPointIds()->SetId(0,0); + triangle->GetPointIds()->SetId(1,1); + triangle->GetPointIds()->SetId(2,2); + + vtkSmartPointer cells = vtkSmartPointer::New(); + cells->InsertNextCell(triangle); + + // Create a polydata object + vtkSmartPointer polyData = vtkSmartPointer::New(); + + // Add the geometry and topology to the polydata + polyData->SetPoints(points); + polyData->SetPolys(cells); + + // Create mapper and actor + vtkSmartPointer mapper = vtkSmartPointer::New(); +#if VTK_MAJOR_VERSION <= 5 + mapper->SetInput(polyData); +#else + mapper->SetInputData(polyData); +#endif + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + // Store this actor in the widget in order that visualizer can access it + viz::WidgetAccessor::setProp(*this, actor); + + // Set the color of the widget. This has to be called after WidgetAccessor. + setColor(color); +} + +/** + * @function main + */ +int main() +{ + help(); + + /// Create a window + viz::Viz3d myWindow("Creating Widgets"); + + /// Create a triangle widget + WTriangle tw(Point3f(0.0,0.0,0.0), Point3f(1.0,1.0,1.0), Point3f(0.0,1.0,0.0), viz::Color::red()); + + /// Show widget in the visualizer window + myWindow.showWidget("TRIANGLE", tw); + + /// Start event loop + myWindow.spin(); + + return 0; +} diff --git a/samples/cpp/tutorial_code/viz/launching_viz.cpp b/samples/cpp/tutorial_code/viz/launching_viz.cpp new file mode 100644 index 0000000000..55a3ddee2c --- /dev/null +++ b/samples/cpp/tutorial_code/viz/launching_viz.cpp @@ -0,0 +1,66 @@ +/** + * @file launching_viz.cpp + * @brief Launching visualization window + * @author Ozan Cagri Tonkal + */ + +#include +#include + +using namespace cv; +using namespace std; + +/** + * @function help + * @brief Display instructions to use this tutorial program + */ +void help() +{ + cout + << "--------------------------------------------------------------------------" << endl + << "This program shows how to launch a 3D visualization window. You can stop event loop to continue executing. " + << "You can access the same window via its name. You can run event loop for a given period of time. " << endl + << "Usage:" << endl + << "./launching_viz" << endl + << endl; +} + +/** + * @function main + */ +int main() +{ + help(); + /// Create a window + viz::Viz3d myWindow("Viz Demo"); + + /// Start event loop + myWindow.spin(); + + /// Event loop is over when pressed q, Q, e, E + cout << "First event loop is over" << endl; + + /// Access window via its name + viz::Viz3d sameWindow = viz::getWindowByName("Viz Demo"); + + /// Start event loop + sameWindow.spin(); + + /// Event loop is over when pressed q, Q, e, E + cout << "Second event loop is over" << endl; + + /// Event loop is over when pressed q, Q, e, E + /// Start event loop once for 1 millisecond + sameWindow.spinOnce(1, true); + while(!sameWindow.wasStopped()) + { + /// Interact with window + + /// Event loop for 1 millisecond + sameWindow.spinOnce(1, true); + } + + /// Once more event loop is stopped + cout << "Last event loop is over" << endl; + return 0; +} diff --git a/samples/cpp/tutorial_code/viz/transformations.cpp b/samples/cpp/tutorial_code/viz/transformations.cpp new file mode 100644 index 0000000000..d8713fddc1 --- /dev/null +++ b/samples/cpp/tutorial_code/viz/transformations.cpp @@ -0,0 +1,112 @@ +/** + * @file transformations.cpp + * @brief Visualizing cloud in different positions, coordinate frames, camera frustums + * @author Ozan Cagri Tonkal + */ + +#include +#include +#include + +using namespace cv; +using namespace std; + +/** + * @function help + * @brief Display instructions to use this tutorial program + */ +void help() +{ + cout + << "--------------------------------------------------------------------------" << endl + << "This program shows how to use makeTransformToGlobal() to compute required pose," + << "how to use makeCameraPose and Viz3d::setViewerPose. You can observe the scene " + << "from camera point of view (C) or global point of view (G)" << endl + << "Usage:" << endl + << "./transformations [ G | C ]" << endl + << endl; +} + +/** + * @function cvcloud_load + * @brief load bunny.ply + */ +Mat cvcloud_load() +{ + Mat cloud(1, 1889, CV_32FC3); + ifstream ifs("bunny.ply"); + + string str; + for(size_t i = 0; i < 12; ++i) + getline(ifs, str); + + Point3f* data = cloud.ptr(); + float dummy1, dummy2; + for(size_t i = 0; i < 1889; ++i) + ifs >> data[i].x >> data[i].y >> data[i].z >> dummy1 >> dummy2; + + cloud *= 5.0f; + return cloud; +} + +/** + * @function main + */ +int main(int argn, char **argv) +{ + help(); + + if (argn < 2) + { + cout << "Missing arguments." << endl; + return 1; + } + + bool camera_pov = (argv[1][0] == 'C'); + + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Let's assume camera has the following properties + Point3f cam_pos(3.0f,3.0f,3.0f), cam_focal_point(3.0f,3.0f,2.0f), cam_y_dir(-1.0f,0.0f,0.0f); + + /// We can get the pose of the cam using makeCameraPose + Affine3f cam_pose = viz::makeCameraPose(cam_pos, cam_focal_point, cam_y_dir); + + /// We can get the transformation matrix from camera coordinate system to global using + /// - makeTransformToGlobal. We need the axes of the camera + Affine3f transform = viz::makeTransformToGlobal(Vec3f(0.0f,-1.0f,0.0f), Vec3f(-1.0f,0.0f,0.0f), Vec3f(0.0f,0.0f,-1.0f), cam_pos); + + /// Create a cloud widget. + Mat bunny_cloud = cvcloud_load(); + viz::WCloud cloud_widget(bunny_cloud, viz::Color::green()); + + /// Pose of the widget in camera frame + Affine3f cloud_pose = Affine3f().translate(Vec3f(0.0f,0.0f,3.0f)); + /// Pose of the widget in global frame + Affine3f cloud_pose_global = transform * cloud_pose; + + /// Visualize camera frame + if (!camera_pov) + { + viz::WCameraPosition cpw(0.5); // Coordinate axes + viz::WCameraPosition cpw_frustum(Vec2f(0.889484, 0.523599)); // Camera frustum + myWindow.showWidget("CPW", cpw, cam_pose); + myWindow.showWidget("CPW_FRUSTUM", cpw_frustum, cam_pose); + } + + /// Visualize widget + myWindow.showWidget("bunny", cloud_widget, cloud_pose_global); + + /// Set the viewer pose to that of camera + if (camera_pov) + myWindow.setViewerPose(cam_pose); + + /// Start event loop. + myWindow.spin(); + + return 0; +} diff --git a/samples/cpp/tutorial_code/viz/widget_pose.cpp b/samples/cpp/tutorial_code/viz/widget_pose.cpp new file mode 100644 index 0000000000..3ecead8d93 --- /dev/null +++ b/samples/cpp/tutorial_code/viz/widget_pose.cpp @@ -0,0 +1,79 @@ +/** + * @file widget_pose.cpp + * @brief Setting pose of a widget + * @author Ozan Cagri Tonkal + */ + +#include +#include +#include + +using namespace cv; +using namespace std; + +/** + * @function help + * @brief Display instructions to use this tutorial program + */ +void help() +{ + cout + << "--------------------------------------------------------------------------" << endl + << "This program shows how to visualize a cube rotated around (1,1,1) and shifted " + << "using Rodrigues vector." << endl + << "Usage:" << endl + << "./widget_pose" << endl + << endl; +} + +/** + * @function main + */ +int main() +{ + help(); + + /// Create a window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + /// Add line to represent (1,1,1) axis + viz::WLine axis(Point3f(-1.0f,-1.0f,-1.0f), Point3f(1.0f,1.0f,1.0f)); + axis.setRenderingProperty(viz::LINE_WIDTH, 4.0); + myWindow.showWidget("Line Widget", axis); + + /// Construct a cube widget + viz::WCube cube_widget(Point3f(0.5,0.5,0.0), Point3f(0.0,0.0,-0.5), true, viz::Color::blue()); + cube_widget.setRenderingProperty(viz::LINE_WIDTH, 4.0); + myWindow.showWidget("Cube Widget", cube_widget); + + /// Rodrigues vector + Mat rot_vec = Mat::zeros(1,3,CV_32F); + float translation_phase = 0.0, translation = 0.0; + while(!myWindow.wasStopped()) + { + /* Rotation using rodrigues */ + /// Rotate around (1,1,1) + rot_vec.at(0,0) += CV_PI * 0.01f; + rot_vec.at(0,1) += CV_PI * 0.01f; + rot_vec.at(0,2) += CV_PI * 0.01f; + + /// Shift on (1,1,1) + translation_phase += CV_PI * 0.01f; + translation = sin(translation_phase); + + Mat rot_mat; + Rodrigues(rot_vec, rot_mat); + + /// Construct pose + Affine3f pose(rot_mat, Vec3f(translation, translation, translation)); + + myWindow.setWidgetPose("Cube Widget", pose); + + myWindow.spinOnce(1, true); + } + + return 0; +} From 6cb90c0e976ff24af85167e71d88c9d9b2ed06c7 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 20 Jan 2014 10:23:46 +0100 Subject: [PATCH 045/293] fix cross-compilation issue with Numpy --- cmake/OpenCVDetectPython.cmake | 37 ++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/cmake/OpenCVDetectPython.cmake b/cmake/OpenCVDetectPython.cmake index 3326bcd98e..8c488e6561 100644 --- a/cmake/OpenCVDetectPython.cmake +++ b/cmake/OpenCVDetectPython.cmake @@ -80,14 +80,29 @@ if(PYTHON_EXECUTABLE) endif() SET(PYTHON_PACKAGES_PATH "${_PYTHON_PACKAGES_PATH}" CACHE PATH "Where to install the python packages.") - if(NOT PYTHON_NUMPY_INCLUDE_DIR) - # Attempt to discover the NumPy include directory. If this succeeds, then build python API with NumPy - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import os; os.environ['DISTUTILS_USE_SDK']='1'; import numpy.distutils; print numpy.distutils.misc_util.get_numpy_include_dirs()[0]" - RESULT_VARIABLE PYTHON_NUMPY_PROCESS - OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT CMAKE_CROSSCOMPILING) + if(NOT PYTHON_NUMPY_INCLUDE_DIR) + # Attempt to discover the NumPy include directory. If this succeeds, then build python API with NumPy + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import os; os.environ['DISTUTILS_USE_SDK']='1'; import numpy.distutils; print numpy.distutils.misc_util.get_numpy_include_dirs()[0]" + RESULT_VARIABLE PYTHON_NUMPY_PROCESS + OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) - if(PYTHON_NUMPY_PROCESS EQUAL 0) + if(PYTHON_NUMPY_PROCESS EQUAL 0) + file(TO_CMAKE_PATH "${PYTHON_NUMPY_INCLUDE_DIR}" _PYTHON_NUMPY_INCLUDE_DIR) + set(PYTHON_NUMPY_INCLUDE_DIR ${_PYTHON_NUMPY_INCLUDE_DIR} CACHE PATH "Path to numpy headers") + endif() + endif() + else() + if(NOT PYTHON_NUMPY_INCLUDE_DIR) + message(STATUS "Cannot probe for Python/Numpy support (because we are cross-compiling OpenCV)") + message(STATUS "If you want to enable Python/Numpy support, set the following variables:") + message(STATUS " PYTHON_EXECUTABLE") + message(STATUS " PYTHON_INCLUDE_DIR") + message(STATUS " PYTHON_LIBRARY") + message(STATUS " PYTHON_NUMPY_INCLUDE_DIR") + message(STATUS " PYTHON_NUMPY_VERSION") + else() file(TO_CMAKE_PATH "${PYTHON_NUMPY_INCLUDE_DIR}" _PYTHON_NUMPY_INCLUDE_DIR) set(PYTHON_NUMPY_INCLUDE_DIR ${_PYTHON_NUMPY_INCLUDE_DIR} CACHE PATH "Path to numpy headers") endif() @@ -95,10 +110,16 @@ if(PYTHON_EXECUTABLE) if(PYTHON_NUMPY_INCLUDE_DIR) set(PYTHON_USE_NUMPY TRUE) - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import numpy; print numpy.version.version" + if(NOT CMAKE_CROSSCOMPILING) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import numpy; print numpy.version.version" RESULT_VARIABLE PYTHON_NUMPY_PROCESS OUTPUT_VARIABLE PYTHON_NUMPY_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + else() + if(NOT PYTHON_NUMPY_VERSION) + set(PYTHON_NUMPY_VERSION "undefined - cannot be probed because of the cross-compilation") + endif() + endif() endif() endif(NOT ANDROID AND NOT IOS) From eabcfa56527de1117f09665f9c6c18b8d4212d9d Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Tue, 21 Jan 2014 10:01:52 +0100 Subject: [PATCH 046/293] reorder the if's for clarity --- cmake/OpenCVDetectPython.cmake | 40 +++++++++++++++------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/cmake/OpenCVDetectPython.cmake b/cmake/OpenCVDetectPython.cmake index 8c488e6561..618b0fd222 100644 --- a/cmake/OpenCVDetectPython.cmake +++ b/cmake/OpenCVDetectPython.cmake @@ -80,21 +80,8 @@ if(PYTHON_EXECUTABLE) endif() SET(PYTHON_PACKAGES_PATH "${_PYTHON_PACKAGES_PATH}" CACHE PATH "Where to install the python packages.") - if(NOT CMAKE_CROSSCOMPILING) - if(NOT PYTHON_NUMPY_INCLUDE_DIR) - # Attempt to discover the NumPy include directory. If this succeeds, then build python API with NumPy - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import os; os.environ['DISTUTILS_USE_SDK']='1'; import numpy.distutils; print numpy.distutils.misc_util.get_numpy_include_dirs()[0]" - RESULT_VARIABLE PYTHON_NUMPY_PROCESS - OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(PYTHON_NUMPY_PROCESS EQUAL 0) - file(TO_CMAKE_PATH "${PYTHON_NUMPY_INCLUDE_DIR}" _PYTHON_NUMPY_INCLUDE_DIR) - set(PYTHON_NUMPY_INCLUDE_DIR ${_PYTHON_NUMPY_INCLUDE_DIR} CACHE PATH "Path to numpy headers") - endif() - endif() - else() - if(NOT PYTHON_NUMPY_INCLUDE_DIR) + if(NOT PYTHON_NUMPY_INCLUDE_DIR) + if(CMAKE_CROSSCOMPILING) message(STATUS "Cannot probe for Python/Numpy support (because we are cross-compiling OpenCV)") message(STATUS "If you want to enable Python/Numpy support, set the following variables:") message(STATUS " PYTHON_EXECUTABLE") @@ -103,22 +90,31 @@ if(PYTHON_EXECUTABLE) message(STATUS " PYTHON_NUMPY_INCLUDE_DIR") message(STATUS " PYTHON_NUMPY_VERSION") else() - file(TO_CMAKE_PATH "${PYTHON_NUMPY_INCLUDE_DIR}" _PYTHON_NUMPY_INCLUDE_DIR) - set(PYTHON_NUMPY_INCLUDE_DIR ${_PYTHON_NUMPY_INCLUDE_DIR} CACHE PATH "Path to numpy headers") + # Attempt to discover the NumPy include directory. If this succeeds, then build python API with NumPy + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import os; os.environ['DISTUTILS_USE_SDK']='1'; import numpy.distutils; print numpy.distutils.misc_util.get_numpy_include_dirs()[0]" + RESULT_VARIABLE PYTHON_NUMPY_PROCESS + OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NOT PYTHON_NUMPY_PROCESS EQUAL 0) + unset(PYTHON_NUMPY_INCLUDE_DIR) + endif() endif() endif() if(PYTHON_NUMPY_INCLUDE_DIR) + file(TO_CMAKE_PATH "${PYTHON_NUMPY_INCLUDE_DIR}" _PYTHON_NUMPY_INCLUDE_DIR) + set(PYTHON_NUMPY_INCLUDE_DIR ${_PYTHON_NUMPY_INCLUDE_DIR} CACHE PATH "Path to numpy headers") set(PYTHON_USE_NUMPY TRUE) - if(NOT CMAKE_CROSSCOMPILING) + if(CMAKE_CROSSCOMPILING) + if(NOT PYTHON_NUMPY_VERSION) + set(PYTHON_NUMPY_VERSION "undefined - cannot be probed because of the cross-compilation") + endif() + else() execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import numpy; print numpy.version.version" RESULT_VARIABLE PYTHON_NUMPY_PROCESS OUTPUT_VARIABLE PYTHON_NUMPY_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) - else() - if(NOT PYTHON_NUMPY_VERSION) - set(PYTHON_NUMPY_VERSION "undefined - cannot be probed because of the cross-compilation") - endif() endif() endif() endif(NOT ANDROID AND NOT IOS) From d84738769422aad33038d90681c47486e47a0380 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 21 Jan 2014 14:35:34 +0400 Subject: [PATCH 047/293] split CUDA Hough sources --- modules/gpu/src/cuda/build_point_list.cu | 138 ++++ .../cuda/{hough.cu => generalized_hough.cu} | 638 +----------------- modules/gpu/src/cuda/hough_circles.cu | 254 +++++++ modules/gpu/src/cuda/hough_lines.cu | 212 ++++++ modules/gpu/src/cuda/hough_segments.cu | 249 +++++++ .../src/{hough.cpp => generalized_hough.cpp} | 303 --------- modules/gpu/src/hough_circles.cpp | 223 ++++++ modules/gpu/src/hough_lines.cpp | 142 ++++ modules/gpu/src/hough_segments.cpp | 110 +++ 9 files changed, 1330 insertions(+), 939 deletions(-) create mode 100644 modules/gpu/src/cuda/build_point_list.cu rename modules/gpu/src/cuda/{hough.cu => generalized_hough.cu} (65%) create mode 100644 modules/gpu/src/cuda/hough_circles.cu create mode 100644 modules/gpu/src/cuda/hough_lines.cu create mode 100644 modules/gpu/src/cuda/hough_segments.cu rename modules/gpu/src/{hough.cpp => generalized_hough.cpp} (79%) create mode 100644 modules/gpu/src/hough_circles.cpp create mode 100644 modules/gpu/src/hough_lines.cpp create mode 100644 modules/gpu/src/hough_segments.cpp diff --git a/modules/gpu/src/cuda/build_point_list.cu b/modules/gpu/src/cuda/build_point_list.cu new file mode 100644 index 0000000000..8a9c73b3f1 --- /dev/null +++ b/modules/gpu/src/cuda/build_point_list.cu @@ -0,0 +1,138 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#if !defined CUDA_DISABLER + +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/emulation.hpp" + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + __device__ static int g_counter; + + template + __global__ void buildPointList(const PtrStepSzb src, unsigned int* list) + { + __shared__ unsigned int s_queues[4][32 * PIXELS_PER_THREAD]; + __shared__ int s_qsize[4]; + __shared__ int s_globStart[4]; + + const int x = blockIdx.x * blockDim.x * PIXELS_PER_THREAD + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (threadIdx.x == 0) + s_qsize[threadIdx.y] = 0; + __syncthreads(); + + if (y < src.rows) + { + // fill the queue + const uchar* srcRow = src.ptr(y); + for (int i = 0, xx = x; i < PIXELS_PER_THREAD && xx < src.cols; ++i, xx += blockDim.x) + { + if (srcRow[xx]) + { + const unsigned int val = (y << 16) | xx; + const int qidx = Emulation::smem::atomicAdd(&s_qsize[threadIdx.y], 1); + s_queues[threadIdx.y][qidx] = val; + } + } + } + + __syncthreads(); + + // let one thread reserve the space required in the global list + if (threadIdx.x == 0 && threadIdx.y == 0) + { + // find how many items are stored in each list + int totalSize = 0; + for (int i = 0; i < blockDim.y; ++i) + { + s_globStart[i] = totalSize; + totalSize += s_qsize[i]; + } + + // calculate the offset in the global list + const int globalOffset = atomicAdd(&g_counter, totalSize); + for (int i = 0; i < blockDim.y; ++i) + s_globStart[i] += globalOffset; + } + + __syncthreads(); + + // copy local queues to global queue + const int qsize = s_qsize[threadIdx.y]; + int gidx = s_globStart[threadIdx.y] + threadIdx.x; + for(int i = threadIdx.x; i < qsize; i += blockDim.x, gidx += blockDim.x) + list[gidx] = s_queues[threadIdx.y][i]; + } + + int buildPointList_gpu(PtrStepSzb src, unsigned int* list) + { + const int PIXELS_PER_THREAD = 16; + + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 4); + const dim3 grid(divUp(src.cols, block.x * PIXELS_PER_THREAD), divUp(src.rows, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(buildPointList, cudaFuncCachePreferShared) ); + + buildPointList<<>>(src, list); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + return totalCount; + } + } +}}} + +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/hough.cu b/modules/gpu/src/cuda/generalized_hough.cu similarity index 65% rename from modules/gpu/src/cuda/hough.cu rename to modules/gpu/src/cuda/generalized_hough.cu index 59eba26081..5e2041eae4 100644 --- a/modules/gpu/src/cuda/hough.cu +++ b/modules/gpu/src/cuda/generalized_hough.cu @@ -43,651 +43,18 @@ #if !defined CUDA_DISABLER #include -#include +#include #include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/emulation.hpp" #include "opencv2/gpu/device/vec_math.hpp" #include "opencv2/gpu/device/functional.hpp" -#include "opencv2/gpu/device/limits.hpp" -#include "opencv2/gpu/device/dynamic_smem.hpp" namespace cv { namespace gpu { namespace device { namespace hough { - __device__ int g_counter; - - //////////////////////////////////////////////////////////////////////// - // buildPointList - - template - __global__ void buildPointList(const PtrStepSzb src, unsigned int* list) - { - __shared__ unsigned int s_queues[4][32 * PIXELS_PER_THREAD]; - __shared__ int s_qsize[4]; - __shared__ int s_globStart[4]; - - const int x = blockIdx.x * blockDim.x * PIXELS_PER_THREAD + threadIdx.x; - const int y = blockIdx.y * blockDim.y + threadIdx.y; - - if (threadIdx.x == 0) - s_qsize[threadIdx.y] = 0; - __syncthreads(); - - if (y < src.rows) - { - // fill the queue - const uchar* srcRow = src.ptr(y); - for (int i = 0, xx = x; i < PIXELS_PER_THREAD && xx < src.cols; ++i, xx += blockDim.x) - { - if (srcRow[xx]) - { - const unsigned int val = (y << 16) | xx; - const int qidx = Emulation::smem::atomicAdd(&s_qsize[threadIdx.y], 1); - s_queues[threadIdx.y][qidx] = val; - } - } - } - - __syncthreads(); - - // let one thread reserve the space required in the global list - if (threadIdx.x == 0 && threadIdx.y == 0) - { - // find how many items are stored in each list - int totalSize = 0; - for (int i = 0; i < blockDim.y; ++i) - { - s_globStart[i] = totalSize; - totalSize += s_qsize[i]; - } - - // calculate the offset in the global list - const int globalOffset = atomicAdd(&g_counter, totalSize); - for (int i = 0; i < blockDim.y; ++i) - s_globStart[i] += globalOffset; - } - - __syncthreads(); - - // copy local queues to global queue - const int qsize = s_qsize[threadIdx.y]; - int gidx = s_globStart[threadIdx.y] + threadIdx.x; - for(int i = threadIdx.x; i < qsize; i += blockDim.x, gidx += blockDim.x) - list[gidx] = s_queues[threadIdx.y][i]; - } - - int buildPointList_gpu(PtrStepSzb src, unsigned int* list) - { - const int PIXELS_PER_THREAD = 16; - - void* counterPtr; - cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - - cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); - - const dim3 block(32, 4); - const dim3 grid(divUp(src.cols, block.x * PIXELS_PER_THREAD), divUp(src.rows, block.y)); - - cudaSafeCall( cudaFuncSetCacheConfig(buildPointList, cudaFuncCachePreferShared) ); - - buildPointList<<>>(src, list); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - int totalCount; - cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - - return totalCount; - } - - //////////////////////////////////////////////////////////////////////// - // linesAccum - - __global__ void linesAccumGlobal(const unsigned int* list, const int count, PtrStepi accum, const float irho, const float theta, const int numrho) - { - const int n = blockIdx.x; - const float ang = n * theta; - - float sinVal; - float cosVal; - sincosf(ang, &sinVal, &cosVal); - sinVal *= irho; - cosVal *= irho; - - const int shift = (numrho - 1) / 2; - - int* accumRow = accum.ptr(n + 1); - for (int i = threadIdx.x; i < count; i += blockDim.x) - { - const unsigned int val = list[i]; - - const int x = (val & 0xFFFF); - const int y = (val >> 16) & 0xFFFF; - - int r = __float2int_rn(x * cosVal + y * sinVal); - r += shift; - - ::atomicAdd(accumRow + r + 1, 1); - } - } - - __global__ void linesAccumShared(const unsigned int* list, const int count, PtrStepi accum, const float irho, const float theta, const int numrho) - { - int* smem = DynamicSharedMem(); - - for (int i = threadIdx.x; i < numrho + 1; i += blockDim.x) - smem[i] = 0; - - __syncthreads(); - - const int n = blockIdx.x; - const float ang = n * theta; - - float sinVal; - float cosVal; - sincosf(ang, &sinVal, &cosVal); - sinVal *= irho; - cosVal *= irho; - - const int shift = (numrho - 1) / 2; - - for (int i = threadIdx.x; i < count; i += blockDim.x) - { - const unsigned int val = list[i]; - - const int x = (val & 0xFFFF); - const int y = (val >> 16) & 0xFFFF; - - int r = __float2int_rn(x * cosVal + y * sinVal); - r += shift; - - Emulation::smem::atomicAdd(&smem[r + 1], 1); - } - - __syncthreads(); - - int* accumRow = accum.ptr(n + 1); - for (int i = threadIdx.x; i < numrho + 1; i += blockDim.x) - accumRow[i] = smem[i]; - } - - void linesAccum_gpu(const unsigned int* list, int count, PtrStepSzi accum, float rho, float theta, size_t sharedMemPerBlock, bool has20) - { - const dim3 block(has20 ? 1024 : 512); - const dim3 grid(accum.rows - 2); - - size_t smemSize = (accum.cols - 1) * sizeof(int); - - if (smemSize < sharedMemPerBlock - 1000) - linesAccumShared<<>>(list, count, accum, 1.0f / rho, theta, accum.cols - 2); - else - linesAccumGlobal<<>>(list, count, accum, 1.0f / rho, theta, accum.cols - 2); - - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - } - - //////////////////////////////////////////////////////////////////////// - // linesGetResult - - __global__ void linesGetResult(const PtrStepSzi accum, float2* out, int* votes, const int maxSize, const float rho, const float theta, const int threshold, const int numrho) - { - const int r = blockIdx.x * blockDim.x + threadIdx.x; - const int n = blockIdx.y * blockDim.y + threadIdx.y; - - if (r >= accum.cols - 2 || n >= accum.rows - 2) - return; - - const int curVotes = accum(n + 1, r + 1); - - if (curVotes > threshold && - curVotes > accum(n + 1, r) && - curVotes >= accum(n + 1, r + 2) && - curVotes > accum(n, r + 1) && - curVotes >= accum(n + 2, r + 1)) - { - const float radius = (r - (numrho - 1) * 0.5f) * rho; - const float angle = n * theta; - - const int ind = ::atomicAdd(&g_counter, 1); - if (ind < maxSize) - { - out[ind] = make_float2(radius, angle); - votes[ind] = curVotes; - } - } - } - - int linesGetResult_gpu(PtrStepSzi accum, float2* out, int* votes, int maxSize, float rho, float theta, int threshold, bool doSort) - { - void* counterPtr; - cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - - cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); - - const dim3 block(32, 8); - const dim3 grid(divUp(accum.cols - 2, block.x), divUp(accum.rows - 2, block.y)); - - cudaSafeCall( cudaFuncSetCacheConfig(linesGetResult, cudaFuncCachePreferL1) ); - - linesGetResult<<>>(accum, out, votes, maxSize, rho, theta, threshold, accum.cols - 2); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - int totalCount; - cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - - totalCount = ::min(totalCount, maxSize); - - if (doSort && totalCount > 0) - { - thrust::device_ptr outPtr(out); - thrust::device_ptr votesPtr(votes); - thrust::sort_by_key(votesPtr, votesPtr + totalCount, outPtr, thrust::greater()); - } - - return totalCount; - } - - //////////////////////////////////////////////////////////////////////// - // houghLinesProbabilistic - - texture tex_mask(false, cudaFilterModePoint, cudaAddressModeClamp); - - __global__ void houghLinesProbabilistic(const PtrStepSzi accum, - int4* out, const int maxSize, - const float rho, const float theta, - const int lineGap, const int lineLength, - const int rows, const int cols) - { - const int r = blockIdx.x * blockDim.x + threadIdx.x; - const int n = blockIdx.y * blockDim.y + threadIdx.y; - - if (r >= accum.cols - 2 || n >= accum.rows - 2) - return; - - const int curVotes = accum(n + 1, r + 1); - - if (curVotes >= lineLength && - curVotes > accum(n, r) && - curVotes > accum(n, r + 1) && - curVotes > accum(n, r + 2) && - curVotes > accum(n + 1, r) && - curVotes > accum(n + 1, r + 2) && - curVotes > accum(n + 2, r) && - curVotes > accum(n + 2, r + 1) && - curVotes > accum(n + 2, r + 2)) - { - const float radius = (r - (accum.cols - 2 - 1) * 0.5f) * rho; - const float angle = n * theta; - - float cosa; - float sina; - sincosf(angle, &sina, &cosa); - - float2 p0 = make_float2(cosa * radius, sina * radius); - float2 dir = make_float2(-sina, cosa); - - float2 pb[4] = {make_float2(-1, -1), make_float2(-1, -1), make_float2(-1, -1), make_float2(-1, -1)}; - float a; - - if (dir.x != 0) - { - a = -p0.x / dir.x; - pb[0].x = 0; - pb[0].y = p0.y + a * dir.y; - - a = (cols - 1 - p0.x) / dir.x; - pb[1].x = cols - 1; - pb[1].y = p0.y + a * dir.y; - } - if (dir.y != 0) - { - a = -p0.y / dir.y; - pb[2].x = p0.x + a * dir.x; - pb[2].y = 0; - - a = (rows - 1 - p0.y) / dir.y; - pb[3].x = p0.x + a * dir.x; - pb[3].y = rows - 1; - } - - if (pb[0].x == 0 && (pb[0].y >= 0 && pb[0].y < rows)) - { - p0 = pb[0]; - if (dir.x < 0) - dir = -dir; - } - else if (pb[1].x == cols - 1 && (pb[0].y >= 0 && pb[0].y < rows)) - { - p0 = pb[1]; - if (dir.x > 0) - dir = -dir; - } - else if (pb[2].y == 0 && (pb[2].x >= 0 && pb[2].x < cols)) - { - p0 = pb[2]; - if (dir.y < 0) - dir = -dir; - } - else if (pb[3].y == rows - 1 && (pb[3].x >= 0 && pb[3].x < cols)) - { - p0 = pb[3]; - if (dir.y > 0) - dir = -dir; - } - - float2 d; - if (::fabsf(dir.x) > ::fabsf(dir.y)) - { - d.x = dir.x > 0 ? 1 : -1; - d.y = dir.y / ::fabsf(dir.x); - } - else - { - d.x = dir.x / ::fabsf(dir.y); - d.y = dir.y > 0 ? 1 : -1; - } - - float2 line_end[2]; - int gap; - bool inLine = false; - - float2 p1 = p0; - if (p1.x < 0 || p1.x >= cols || p1.y < 0 || p1.y >= rows) - return; - - for (;;) - { - if (tex2D(tex_mask, p1.x, p1.y)) - { - gap = 0; - - if (!inLine) - { - line_end[0] = p1; - line_end[1] = p1; - inLine = true; - } - else - { - line_end[1] = p1; - } - } - else if (inLine) - { - if (++gap > lineGap) - { - bool good_line = ::abs(line_end[1].x - line_end[0].x) >= lineLength || - ::abs(line_end[1].y - line_end[0].y) >= lineLength; - - if (good_line) - { - const int ind = ::atomicAdd(&g_counter, 1); - if (ind < maxSize) - out[ind] = make_int4(line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y); - } - - gap = 0; - inLine = false; - } - } - - p1 = p1 + d; - if (p1.x < 0 || p1.x >= cols || p1.y < 0 || p1.y >= rows) - { - if (inLine) - { - bool good_line = ::abs(line_end[1].x - line_end[0].x) >= lineLength || - ::abs(line_end[1].y - line_end[0].y) >= lineLength; - - if (good_line) - { - const int ind = ::atomicAdd(&g_counter, 1); - if (ind < maxSize) - out[ind] = make_int4(line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y); - } - - } - break; - } - } - } - } - - int houghLinesProbabilistic_gpu(PtrStepSzb mask, PtrStepSzi accum, int4* out, int maxSize, float rho, float theta, int lineGap, int lineLength) - { - void* counterPtr; - cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - - cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); - - const dim3 block(32, 8); - const dim3 grid(divUp(accum.cols - 2, block.x), divUp(accum.rows - 2, block.y)); - - bindTexture(&tex_mask, mask); - - houghLinesProbabilistic<<>>(accum, - out, maxSize, - rho, theta, - lineGap, lineLength, - mask.rows, mask.cols); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - int totalCount; - cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - - totalCount = ::min(totalCount, maxSize); - - return totalCount; - } - - //////////////////////////////////////////////////////////////////////// - // circlesAccumCenters - - __global__ void circlesAccumCenters(const unsigned int* list, const int count, const PtrStepi dx, const PtrStepi dy, - PtrStepi accum, const int width, const int height, const int minRadius, const int maxRadius, const float idp) - { - const int SHIFT = 10; - const int ONE = 1 << SHIFT; - - const int tid = blockIdx.x * blockDim.x + threadIdx.x; - - if (tid >= count) - return; - - const unsigned int val = list[tid]; - - const int x = (val & 0xFFFF); - const int y = (val >> 16) & 0xFFFF; - - const int vx = dx(y, x); - const int vy = dy(y, x); - - if (vx == 0 && vy == 0) - return; - - const float mag = ::sqrtf(vx * vx + vy * vy); - - const int x0 = __float2int_rn((x * idp) * ONE); - const int y0 = __float2int_rn((y * idp) * ONE); - - int sx = __float2int_rn((vx * idp) * ONE / mag); - int sy = __float2int_rn((vy * idp) * ONE / mag); - - // Step from minRadius to maxRadius in both directions of the gradient - for (int k1 = 0; k1 < 2; ++k1) - { - int x1 = x0 + minRadius * sx; - int y1 = y0 + minRadius * sy; - - for (int r = minRadius; r <= maxRadius; x1 += sx, y1 += sy, ++r) - { - const int x2 = x1 >> SHIFT; - const int y2 = y1 >> SHIFT; - - if (x2 < 0 || x2 >= width || y2 < 0 || y2 >= height) - break; - - ::atomicAdd(accum.ptr(y2 + 1) + x2 + 1, 1); - } - - sx = -sx; - sy = -sy; - } - } - - void circlesAccumCenters_gpu(const unsigned int* list, int count, PtrStepi dx, PtrStepi dy, PtrStepSzi accum, int minRadius, int maxRadius, float idp) - { - const dim3 block(256); - const dim3 grid(divUp(count, block.x)); - - cudaSafeCall( cudaFuncSetCacheConfig(circlesAccumCenters, cudaFuncCachePreferL1) ); - - circlesAccumCenters<<>>(list, count, dx, dy, accum, accum.cols - 2, accum.rows - 2, minRadius, maxRadius, idp); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - } - - //////////////////////////////////////////////////////////////////////// - // buildCentersList - - __global__ void buildCentersList(const PtrStepSzi accum, unsigned int* centers, const int threshold) - { - const int x = blockIdx.x * blockDim.x + threadIdx.x; - const int y = blockIdx.y * blockDim.y + threadIdx.y; - - if (x < accum.cols - 2 && y < accum.rows - 2) - { - const int top = accum(y, x + 1); - - const int left = accum(y + 1, x); - const int cur = accum(y + 1, x + 1); - const int right = accum(y + 1, x + 2); - - const int bottom = accum(y + 2, x + 1); - - if (cur > threshold && cur > top && cur >= bottom && cur > left && cur >= right) - { - const unsigned int val = (y << 16) | x; - const int idx = ::atomicAdd(&g_counter, 1); - centers[idx] = val; - } - } - } - - int buildCentersList_gpu(PtrStepSzi accum, unsigned int* centers, int threshold) - { - void* counterPtr; - cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - - cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); - - const dim3 block(32, 8); - const dim3 grid(divUp(accum.cols - 2, block.x), divUp(accum.rows - 2, block.y)); - - cudaSafeCall( cudaFuncSetCacheConfig(buildCentersList, cudaFuncCachePreferL1) ); - - buildCentersList<<>>(accum, centers, threshold); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - int totalCount; - cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - - return totalCount; - } - - //////////////////////////////////////////////////////////////////////// - // circlesAccumRadius - - __global__ void circlesAccumRadius(const unsigned int* centers, const unsigned int* list, const int count, - float3* circles, const int maxCircles, const float dp, - const int minRadius, const int maxRadius, const int histSize, const int threshold) - { - int* smem = DynamicSharedMem(); - - for (int i = threadIdx.x; i < histSize + 2; i += blockDim.x) - smem[i] = 0; - __syncthreads(); - - unsigned int val = centers[blockIdx.x]; - - float cx = (val & 0xFFFF); - float cy = (val >> 16) & 0xFFFF; - - cx = (cx + 0.5f) * dp; - cy = (cy + 0.5f) * dp; - - for (int i = threadIdx.x; i < count; i += blockDim.x) - { - val = list[i]; - - const int x = (val & 0xFFFF); - const int y = (val >> 16) & 0xFFFF; - - const float rad = ::sqrtf((cx - x) * (cx - x) + (cy - y) * (cy - y)); - if (rad >= minRadius && rad <= maxRadius) - { - const int r = __float2int_rn(rad - minRadius); - - Emulation::smem::atomicAdd(&smem[r + 1], 1); - } - } - - __syncthreads(); - - for (int i = threadIdx.x; i < histSize; i += blockDim.x) - { - const int curVotes = smem[i + 1]; - - if (curVotes >= threshold && curVotes > smem[i] && curVotes >= smem[i + 2]) - { - const int ind = ::atomicAdd(&g_counter, 1); - if (ind < maxCircles) - circles[ind] = make_float3(cx, cy, i + minRadius); - } - } - } - - int circlesAccumRadius_gpu(const unsigned int* centers, int centersCount, const unsigned int* list, int count, - float3* circles, int maxCircles, float dp, int minRadius, int maxRadius, int threshold, bool has20) - { - void* counterPtr; - cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - - cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); - - const dim3 block(has20 ? 1024 : 512); - const dim3 grid(centersCount); - - const int histSize = maxRadius - minRadius + 1; - size_t smemSize = (histSize + 2) * sizeof(int); - - circlesAccumRadius<<>>(centers, list, count, circles, maxCircles, dp, minRadius, maxRadius, histSize, threshold); - cudaSafeCall( cudaGetLastError() ); - - cudaSafeCall( cudaDeviceSynchronize() ); - - int totalCount; - cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - - totalCount = ::min(totalCount, maxCircles); - - return totalCount; - } - - //////////////////////////////////////////////////////////////////////// - // Generalized Hough + __device__ static int g_counter; template __global__ void buildEdgePointList(const PtrStepSzb edges, const PtrStep dx, const PtrStep dy, unsigned int* coordList, float* thetaList) @@ -1706,5 +1073,4 @@ namespace cv { namespace gpu { namespace device } }}} - #endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/hough_circles.cu b/modules/gpu/src/cuda/hough_circles.cu new file mode 100644 index 0000000000..5df7887b0b --- /dev/null +++ b/modules/gpu/src/cuda/hough_circles.cu @@ -0,0 +1,254 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#if !defined CUDA_DISABLER + +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/emulation.hpp" +#include "opencv2/gpu/device/dynamic_smem.hpp" + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + __device__ static int g_counter; + + //////////////////////////////////////////////////////////////////////// + // circlesAccumCenters + + __global__ void circlesAccumCenters(const unsigned int* list, const int count, const PtrStepi dx, const PtrStepi dy, + PtrStepi accum, const int width, const int height, const int minRadius, const int maxRadius, const float idp) + { + const int SHIFT = 10; + const int ONE = 1 << SHIFT; + + const int tid = blockIdx.x * blockDim.x + threadIdx.x; + + if (tid >= count) + return; + + const unsigned int val = list[tid]; + + const int x = (val & 0xFFFF); + const int y = (val >> 16) & 0xFFFF; + + const int vx = dx(y, x); + const int vy = dy(y, x); + + if (vx == 0 && vy == 0) + return; + + const float mag = ::sqrtf(vx * vx + vy * vy); + + const int x0 = __float2int_rn((x * idp) * ONE); + const int y0 = __float2int_rn((y * idp) * ONE); + + int sx = __float2int_rn((vx * idp) * ONE / mag); + int sy = __float2int_rn((vy * idp) * ONE / mag); + + // Step from minRadius to maxRadius in both directions of the gradient + for (int k1 = 0; k1 < 2; ++k1) + { + int x1 = x0 + minRadius * sx; + int y1 = y0 + minRadius * sy; + + for (int r = minRadius; r <= maxRadius; x1 += sx, y1 += sy, ++r) + { + const int x2 = x1 >> SHIFT; + const int y2 = y1 >> SHIFT; + + if (x2 < 0 || x2 >= width || y2 < 0 || y2 >= height) + break; + + ::atomicAdd(accum.ptr(y2 + 1) + x2 + 1, 1); + } + + sx = -sx; + sy = -sy; + } + } + + void circlesAccumCenters_gpu(const unsigned int* list, int count, PtrStepi dx, PtrStepi dy, PtrStepSzi accum, int minRadius, int maxRadius, float idp) + { + const dim3 block(256); + const dim3 grid(divUp(count, block.x)); + + cudaSafeCall( cudaFuncSetCacheConfig(circlesAccumCenters, cudaFuncCachePreferL1) ); + + circlesAccumCenters<<>>(list, count, dx, dy, accum, accum.cols - 2, accum.rows - 2, minRadius, maxRadius, idp); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + //////////////////////////////////////////////////////////////////////// + // buildCentersList + + __global__ void buildCentersList(const PtrStepSzi accum, unsigned int* centers, const int threshold) + { + const int x = blockIdx.x * blockDim.x + threadIdx.x; + const int y = blockIdx.y * blockDim.y + threadIdx.y; + + if (x < accum.cols - 2 && y < accum.rows - 2) + { + const int top = accum(y, x + 1); + + const int left = accum(y + 1, x); + const int cur = accum(y + 1, x + 1); + const int right = accum(y + 1, x + 2); + + const int bottom = accum(y + 2, x + 1); + + if (cur > threshold && cur > top && cur >= bottom && cur > left && cur >= right) + { + const unsigned int val = (y << 16) | x; + const int idx = ::atomicAdd(&g_counter, 1); + centers[idx] = val; + } + } + } + + int buildCentersList_gpu(PtrStepSzi accum, unsigned int* centers, int threshold) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 8); + const dim3 grid(divUp(accum.cols - 2, block.x), divUp(accum.rows - 2, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(buildCentersList, cudaFuncCachePreferL1) ); + + buildCentersList<<>>(accum, centers, threshold); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + return totalCount; + } + + //////////////////////////////////////////////////////////////////////// + // circlesAccumRadius + + __global__ void circlesAccumRadius(const unsigned int* centers, const unsigned int* list, const int count, + float3* circles, const int maxCircles, const float dp, + const int minRadius, const int maxRadius, const int histSize, const int threshold) + { + int* smem = DynamicSharedMem(); + + for (int i = threadIdx.x; i < histSize + 2; i += blockDim.x) + smem[i] = 0; + __syncthreads(); + + unsigned int val = centers[blockIdx.x]; + + float cx = (val & 0xFFFF); + float cy = (val >> 16) & 0xFFFF; + + cx = (cx + 0.5f) * dp; + cy = (cy + 0.5f) * dp; + + for (int i = threadIdx.x; i < count; i += blockDim.x) + { + val = list[i]; + + const int x = (val & 0xFFFF); + const int y = (val >> 16) & 0xFFFF; + + const float rad = ::sqrtf((cx - x) * (cx - x) + (cy - y) * (cy - y)); + if (rad >= minRadius && rad <= maxRadius) + { + const int r = __float2int_rn(rad - minRadius); + + Emulation::smem::atomicAdd(&smem[r + 1], 1); + } + } + + __syncthreads(); + + for (int i = threadIdx.x; i < histSize; i += blockDim.x) + { + const int curVotes = smem[i + 1]; + + if (curVotes >= threshold && curVotes > smem[i] && curVotes >= smem[i + 2]) + { + const int ind = ::atomicAdd(&g_counter, 1); + if (ind < maxCircles) + circles[ind] = make_float3(cx, cy, i + minRadius); + } + } + } + + int circlesAccumRadius_gpu(const unsigned int* centers, int centersCount, const unsigned int* list, int count, + float3* circles, int maxCircles, float dp, int minRadius, int maxRadius, int threshold, bool has20) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(has20 ? 1024 : 512); + const dim3 grid(centersCount); + + const int histSize = maxRadius - minRadius + 1; + size_t smemSize = (histSize + 2) * sizeof(int); + + circlesAccumRadius<<>>(centers, list, count, circles, maxCircles, dp, minRadius, maxRadius, histSize, threshold); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + totalCount = ::min(totalCount, maxCircles); + + return totalCount; + } + } +}}} + +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/hough_lines.cu b/modules/gpu/src/cuda/hough_lines.cu new file mode 100644 index 0000000000..2f2fe9a7e2 --- /dev/null +++ b/modules/gpu/src/cuda/hough_lines.cu @@ -0,0 +1,212 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#if !defined CUDA_DISABLER + +#include +#include + +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/emulation.hpp" +#include "opencv2/gpu/device/dynamic_smem.hpp" + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + __device__ static int g_counter; + + //////////////////////////////////////////////////////////////////////// + // linesAccum + + __global__ void linesAccumGlobal(const unsigned int* list, const int count, PtrStepi accum, const float irho, const float theta, const int numrho) + { + const int n = blockIdx.x; + const float ang = n * theta; + + float sinVal; + float cosVal; + sincosf(ang, &sinVal, &cosVal); + sinVal *= irho; + cosVal *= irho; + + const int shift = (numrho - 1) / 2; + + int* accumRow = accum.ptr(n + 1); + for (int i = threadIdx.x; i < count; i += blockDim.x) + { + const unsigned int val = list[i]; + + const int x = (val & 0xFFFF); + const int y = (val >> 16) & 0xFFFF; + + int r = __float2int_rn(x * cosVal + y * sinVal); + r += shift; + + ::atomicAdd(accumRow + r + 1, 1); + } + } + + __global__ void linesAccumShared(const unsigned int* list, const int count, PtrStepi accum, const float irho, const float theta, const int numrho) + { + int* smem = DynamicSharedMem(); + + for (int i = threadIdx.x; i < numrho + 1; i += blockDim.x) + smem[i] = 0; + + __syncthreads(); + + const int n = blockIdx.x; + const float ang = n * theta; + + float sinVal; + float cosVal; + sincosf(ang, &sinVal, &cosVal); + sinVal *= irho; + cosVal *= irho; + + const int shift = (numrho - 1) / 2; + + for (int i = threadIdx.x; i < count; i += blockDim.x) + { + const unsigned int val = list[i]; + + const int x = (val & 0xFFFF); + const int y = (val >> 16) & 0xFFFF; + + int r = __float2int_rn(x * cosVal + y * sinVal); + r += shift; + + Emulation::smem::atomicAdd(&smem[r + 1], 1); + } + + __syncthreads(); + + int* accumRow = accum.ptr(n + 1); + for (int i = threadIdx.x; i < numrho + 1; i += blockDim.x) + accumRow[i] = smem[i]; + } + + void linesAccum_gpu(const unsigned int* list, int count, PtrStepSzi accum, float rho, float theta, size_t sharedMemPerBlock, bool has20) + { + const dim3 block(has20 ? 1024 : 512); + const dim3 grid(accum.rows - 2); + + size_t smemSize = (accum.cols - 1) * sizeof(int); + + if (smemSize < sharedMemPerBlock - 1000) + linesAccumShared<<>>(list, count, accum, 1.0f / rho, theta, accum.cols - 2); + else + linesAccumGlobal<<>>(list, count, accum, 1.0f / rho, theta, accum.cols - 2); + + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + } + + //////////////////////////////////////////////////////////////////////// + // linesGetResult + + __global__ void linesGetResult(const PtrStepSzi accum, float2* out, int* votes, const int maxSize, const float rho, const float theta, const int threshold, const int numrho) + { + const int r = blockIdx.x * blockDim.x + threadIdx.x; + const int n = blockIdx.y * blockDim.y + threadIdx.y; + + if (r >= accum.cols - 2 || n >= accum.rows - 2) + return; + + const int curVotes = accum(n + 1, r + 1); + + if (curVotes > threshold && + curVotes > accum(n + 1, r) && + curVotes >= accum(n + 1, r + 2) && + curVotes > accum(n, r + 1) && + curVotes >= accum(n + 2, r + 1)) + { + const float radius = (r - (numrho - 1) * 0.5f) * rho; + const float angle = n * theta; + + const int ind = ::atomicAdd(&g_counter, 1); + if (ind < maxSize) + { + out[ind] = make_float2(radius, angle); + votes[ind] = curVotes; + } + } + } + + int linesGetResult_gpu(PtrStepSzi accum, float2* out, int* votes, int maxSize, float rho, float theta, int threshold, bool doSort) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 8); + const dim3 grid(divUp(accum.cols - 2, block.x), divUp(accum.rows - 2, block.y)); + + cudaSafeCall( cudaFuncSetCacheConfig(linesGetResult, cudaFuncCachePreferL1) ); + + linesGetResult<<>>(accum, out, votes, maxSize, rho, theta, threshold, accum.cols - 2); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + totalCount = ::min(totalCount, maxSize); + + if (doSort && totalCount > 0) + { + thrust::device_ptr outPtr(out); + thrust::device_ptr votesPtr(votes); + thrust::sort_by_key(votesPtr, votesPtr + totalCount, outPtr, thrust::greater()); + } + + return totalCount; + } + } +}}} + + +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/cuda/hough_segments.cu b/modules/gpu/src/cuda/hough_segments.cu new file mode 100644 index 0000000000..f55bb4de9f --- /dev/null +++ b/modules/gpu/src/cuda/hough_segments.cu @@ -0,0 +1,249 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#if !defined CUDA_DISABLER + +#include "opencv2/gpu/device/common.hpp" +#include "opencv2/gpu/device/vec_math.hpp" + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + __device__ int g_counter; + + texture tex_mask(false, cudaFilterModePoint, cudaAddressModeClamp); + + __global__ void houghLinesProbabilistic(const PtrStepSzi accum, + int4* out, const int maxSize, + const float rho, const float theta, + const int lineGap, const int lineLength, + const int rows, const int cols) + { + const int r = blockIdx.x * blockDim.x + threadIdx.x; + const int n = blockIdx.y * blockDim.y + threadIdx.y; + + if (r >= accum.cols - 2 || n >= accum.rows - 2) + return; + + const int curVotes = accum(n + 1, r + 1); + + if (curVotes >= lineLength && + curVotes > accum(n, r) && + curVotes > accum(n, r + 1) && + curVotes > accum(n, r + 2) && + curVotes > accum(n + 1, r) && + curVotes > accum(n + 1, r + 2) && + curVotes > accum(n + 2, r) && + curVotes > accum(n + 2, r + 1) && + curVotes > accum(n + 2, r + 2)) + { + const float radius = (r - (accum.cols - 2 - 1) * 0.5f) * rho; + const float angle = n * theta; + + float cosa; + float sina; + sincosf(angle, &sina, &cosa); + + float2 p0 = make_float2(cosa * radius, sina * radius); + float2 dir = make_float2(-sina, cosa); + + float2 pb[4] = {make_float2(-1, -1), make_float2(-1, -1), make_float2(-1, -1), make_float2(-1, -1)}; + float a; + + if (dir.x != 0) + { + a = -p0.x / dir.x; + pb[0].x = 0; + pb[0].y = p0.y + a * dir.y; + + a = (cols - 1 - p0.x) / dir.x; + pb[1].x = cols - 1; + pb[1].y = p0.y + a * dir.y; + } + if (dir.y != 0) + { + a = -p0.y / dir.y; + pb[2].x = p0.x + a * dir.x; + pb[2].y = 0; + + a = (rows - 1 - p0.y) / dir.y; + pb[3].x = p0.x + a * dir.x; + pb[3].y = rows - 1; + } + + if (pb[0].x == 0 && (pb[0].y >= 0 && pb[0].y < rows)) + { + p0 = pb[0]; + if (dir.x < 0) + dir = -dir; + } + else if (pb[1].x == cols - 1 && (pb[0].y >= 0 && pb[0].y < rows)) + { + p0 = pb[1]; + if (dir.x > 0) + dir = -dir; + } + else if (pb[2].y == 0 && (pb[2].x >= 0 && pb[2].x < cols)) + { + p0 = pb[2]; + if (dir.y < 0) + dir = -dir; + } + else if (pb[3].y == rows - 1 && (pb[3].x >= 0 && pb[3].x < cols)) + { + p0 = pb[3]; + if (dir.y > 0) + dir = -dir; + } + + float2 d; + if (::fabsf(dir.x) > ::fabsf(dir.y)) + { + d.x = dir.x > 0 ? 1 : -1; + d.y = dir.y / ::fabsf(dir.x); + } + else + { + d.x = dir.x / ::fabsf(dir.y); + d.y = dir.y > 0 ? 1 : -1; + } + + float2 line_end[2]; + int gap; + bool inLine = false; + + float2 p1 = p0; + if (p1.x < 0 || p1.x >= cols || p1.y < 0 || p1.y >= rows) + return; + + for (;;) + { + if (tex2D(tex_mask, p1.x, p1.y)) + { + gap = 0; + + if (!inLine) + { + line_end[0] = p1; + line_end[1] = p1; + inLine = true; + } + else + { + line_end[1] = p1; + } + } + else if (inLine) + { + if (++gap > lineGap) + { + bool good_line = ::abs(line_end[1].x - line_end[0].x) >= lineLength || + ::abs(line_end[1].y - line_end[0].y) >= lineLength; + + if (good_line) + { + const int ind = ::atomicAdd(&g_counter, 1); + if (ind < maxSize) + out[ind] = make_int4(line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y); + } + + gap = 0; + inLine = false; + } + } + + p1 = p1 + d; + if (p1.x < 0 || p1.x >= cols || p1.y < 0 || p1.y >= rows) + { + if (inLine) + { + bool good_line = ::abs(line_end[1].x - line_end[0].x) >= lineLength || + ::abs(line_end[1].y - line_end[0].y) >= lineLength; + + if (good_line) + { + const int ind = ::atomicAdd(&g_counter, 1); + if (ind < maxSize) + out[ind] = make_int4(line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y); + } + + } + break; + } + } + } + } + + int houghLinesProbabilistic_gpu(PtrStepSzb mask, PtrStepSzi accum, int4* out, int maxSize, float rho, float theta, int lineGap, int lineLength) + { + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); + + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); + + const dim3 block(32, 8); + const dim3 grid(divUp(accum.cols - 2, block.x), divUp(accum.rows - 2, block.y)); + + bindTexture(&tex_mask, mask); + + houghLinesProbabilistic<<>>(accum, + out, maxSize, + rho, theta, + lineGap, lineLength, + mask.rows, mask.cols); + cudaSafeCall( cudaGetLastError() ); + + cudaSafeCall( cudaDeviceSynchronize() ); + + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); + + totalCount = ::min(totalCount, maxSize); + + return totalCount; + } + } +}}} + + +#endif /* CUDA_DISABLER */ diff --git a/modules/gpu/src/hough.cpp b/modules/gpu/src/generalized_hough.cpp similarity index 79% rename from modules/gpu/src/hough.cpp rename to modules/gpu/src/generalized_hough.cpp index 09cf01850e..a92c37d1a5 100644 --- a/modules/gpu/src/hough.cpp +++ b/modules/gpu/src/generalized_hough.cpp @@ -48,16 +48,6 @@ using namespace cv::gpu; #if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) -void cv::gpu::HoughLines(const GpuMat&, GpuMat&, float, float, int, bool, int) { throw_nogpu(); } -void cv::gpu::HoughLines(const GpuMat&, GpuMat&, HoughLinesBuf&, float, float, int, bool, int) { throw_nogpu(); } -void cv::gpu::HoughLinesDownload(const GpuMat&, OutputArray, OutputArray) { throw_nogpu(); } - -void cv::gpu::HoughLinesP(const GpuMat&, GpuMat&, HoughLinesBuf&, float, float, int, int, int) { throw_nogpu(); } - -void cv::gpu::HoughCircles(const GpuMat&, GpuMat&, int, float, float, int, int, int, int, int) { throw_nogpu(); } -void cv::gpu::HoughCircles(const GpuMat&, GpuMat&, HoughCirclesBuf&, int, float, float, int, int, int, int, int) { throw_nogpu(); } -void cv::gpu::HoughCirclesDownload(const GpuMat&, OutputArray) { throw_nogpu(); } - Ptr cv::gpu::GeneralizedHough_GPU::create(int) { throw_nogpu(); return Ptr(); } cv::gpu::GeneralizedHough_GPU::~GeneralizedHough_GPU() {} void cv::gpu::GeneralizedHough_GPU::setTemplate(const GpuMat&, int, Point) { throw_nogpu(); } @@ -77,299 +67,6 @@ namespace cv { namespace gpu { namespace device } }}} -////////////////////////////////////////////////////////// -// HoughLines - -namespace cv { namespace gpu { namespace device -{ - namespace hough - { - void linesAccum_gpu(const unsigned int* list, int count, PtrStepSzi accum, float rho, float theta, size_t sharedMemPerBlock, bool has20); - int linesGetResult_gpu(PtrStepSzi accum, float2* out, int* votes, int maxSize, float rho, float theta, int threshold, bool doSort); - } -}}} - -void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, float rho, float theta, int threshold, bool doSort, int maxLines) -{ - HoughLinesBuf buf; - HoughLines(src, lines, buf, rho, theta, threshold, doSort, maxLines); -} - -void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, HoughLinesBuf& buf, float rho, float theta, int threshold, bool doSort, int maxLines) -{ - using namespace cv::gpu::device::hough; - - CV_Assert(src.type() == CV_8UC1); - CV_Assert(src.cols < std::numeric_limits::max()); - CV_Assert(src.rows < std::numeric_limits::max()); - - ensureSizeIsEnough(1, src.size().area(), CV_32SC1, buf.list); - unsigned int* srcPoints = buf.list.ptr(); - - const int pointsCount = buildPointList_gpu(src, srcPoints); - if (pointsCount == 0) - { - lines.release(); - return; - } - - const int numangle = cvRound(CV_PI / theta); - const int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho); - CV_Assert(numangle > 0 && numrho > 0); - - ensureSizeIsEnough(numangle + 2, numrho + 2, CV_32SC1, buf.accum); - buf.accum.setTo(Scalar::all(0)); - - DeviceInfo devInfo; - linesAccum_gpu(srcPoints, pointsCount, buf.accum, rho, theta, devInfo.sharedMemPerBlock(), devInfo.supports(FEATURE_SET_COMPUTE_20)); - - ensureSizeIsEnough(2, maxLines, CV_32FC2, lines); - - int linesCount = linesGetResult_gpu(buf.accum, lines.ptr(0), lines.ptr(1), maxLines, rho, theta, threshold, doSort); - if (linesCount > 0) - lines.cols = linesCount; - else - lines.release(); -} - -void cv::gpu::HoughLinesDownload(const GpuMat& d_lines, OutputArray h_lines_, OutputArray h_votes_) -{ - if (d_lines.empty()) - { - h_lines_.release(); - if (h_votes_.needed()) - h_votes_.release(); - return; - } - - CV_Assert(d_lines.rows == 2 && d_lines.type() == CV_32FC2); - - h_lines_.create(1, d_lines.cols, CV_32FC2); - Mat h_lines = h_lines_.getMat(); - d_lines.row(0).download(h_lines); - - if (h_votes_.needed()) - { - h_votes_.create(1, d_lines.cols, CV_32SC1); - Mat h_votes = h_votes_.getMat(); - GpuMat d_votes(1, d_lines.cols, CV_32SC1, const_cast(d_lines.ptr(1))); - d_votes.download(h_votes); - } -} - -////////////////////////////////////////////////////////// -// HoughLinesP - -namespace cv { namespace gpu { namespace device -{ - namespace hough - { - int houghLinesProbabilistic_gpu(PtrStepSzb mask, PtrStepSzi accum, int4* out, int maxSize, float rho, float theta, int lineGap, int lineLength); - } -}}} - -void cv::gpu::HoughLinesP(const GpuMat& src, GpuMat& lines, HoughLinesBuf& buf, float rho, float theta, int minLineLength, int maxLineGap, int maxLines) -{ - using namespace cv::gpu::device::hough; - - CV_Assert( src.type() == CV_8UC1 ); - CV_Assert( src.cols < std::numeric_limits::max() ); - CV_Assert( src.rows < std::numeric_limits::max() ); - - ensureSizeIsEnough(1, src.size().area(), CV_32SC1, buf.list); - unsigned int* srcPoints = buf.list.ptr(); - - const int pointsCount = buildPointList_gpu(src, srcPoints); - if (pointsCount == 0) - { - lines.release(); - return; - } - - const int numangle = cvRound(CV_PI / theta); - const int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho); - CV_Assert( numangle > 0 && numrho > 0 ); - - ensureSizeIsEnough(numangle + 2, numrho + 2, CV_32SC1, buf.accum); - buf.accum.setTo(Scalar::all(0)); - - DeviceInfo devInfo; - linesAccum_gpu(srcPoints, pointsCount, buf.accum, rho, theta, devInfo.sharedMemPerBlock(), devInfo.supports(FEATURE_SET_COMPUTE_20)); - - ensureSizeIsEnough(1, maxLines, CV_32SC4, lines); - - int linesCount = houghLinesProbabilistic_gpu(src, buf.accum, lines.ptr(), maxLines, rho, theta, maxLineGap, minLineLength); - - if (linesCount > 0) - lines.cols = linesCount; - else - lines.release(); -} - -////////////////////////////////////////////////////////// -// HoughCircles - -namespace cv { namespace gpu { namespace device -{ - namespace hough - { - void circlesAccumCenters_gpu(const unsigned int* list, int count, PtrStepi dx, PtrStepi dy, PtrStepSzi accum, int minRadius, int maxRadius, float idp); - int buildCentersList_gpu(PtrStepSzi accum, unsigned int* centers, int threshold); - int circlesAccumRadius_gpu(const unsigned int* centers, int centersCount, const unsigned int* list, int count, - float3* circles, int maxCircles, float dp, int minRadius, int maxRadius, int threshold, bool has20); - } -}}} - -void cv::gpu::HoughCircles(const GpuMat& src, GpuMat& circles, int method, float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles) -{ - HoughCirclesBuf buf; - HoughCircles(src, circles, buf, method, dp, minDist, cannyThreshold, votesThreshold, minRadius, maxRadius, maxCircles); -} - -void cv::gpu::HoughCircles(const GpuMat& src, GpuMat& circles, HoughCirclesBuf& buf, int method, - float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles) -{ - using namespace cv::gpu::device::hough; - - CV_Assert(src.type() == CV_8UC1); - CV_Assert(src.cols < std::numeric_limits::max()); - CV_Assert(src.rows < std::numeric_limits::max()); - CV_Assert(method == CV_HOUGH_GRADIENT); - CV_Assert(dp > 0); - CV_Assert(minRadius > 0 && maxRadius > minRadius); - CV_Assert(cannyThreshold > 0); - CV_Assert(votesThreshold > 0); - CV_Assert(maxCircles > 0); - - const float idp = 1.0f / dp; - - cv::gpu::Canny(src, buf.cannyBuf, buf.edges, std::max(cannyThreshold / 2, 1), cannyThreshold); - - ensureSizeIsEnough(2, src.size().area(), CV_32SC1, buf.list); - unsigned int* srcPoints = buf.list.ptr(0); - unsigned int* centers = buf.list.ptr(1); - - const int pointsCount = buildPointList_gpu(buf.edges, srcPoints); - if (pointsCount == 0) - { - circles.release(); - return; - } - - ensureSizeIsEnough(cvCeil(src.rows * idp) + 2, cvCeil(src.cols * idp) + 2, CV_32SC1, buf.accum); - buf.accum.setTo(Scalar::all(0)); - - circlesAccumCenters_gpu(srcPoints, pointsCount, buf.cannyBuf.dx, buf.cannyBuf.dy, buf.accum, minRadius, maxRadius, idp); - - int centersCount = buildCentersList_gpu(buf.accum, centers, votesThreshold); - if (centersCount == 0) - { - circles.release(); - return; - } - - if (minDist > 1) - { - cv::AutoBuffer oldBuf_(centersCount); - cv::AutoBuffer newBuf_(centersCount); - int newCount = 0; - - ushort2* oldBuf = oldBuf_; - ushort2* newBuf = newBuf_; - - cudaSafeCall( cudaMemcpy(oldBuf, centers, centersCount * sizeof(ushort2), cudaMemcpyDeviceToHost) ); - - const int cellSize = cvRound(minDist); - const int gridWidth = (src.cols + cellSize - 1) / cellSize; - const int gridHeight = (src.rows + cellSize - 1) / cellSize; - - std::vector< std::vector > grid(gridWidth * gridHeight); - - const float minDist2 = minDist * minDist; - - for (int i = 0; i < centersCount; ++i) - { - ushort2 p = oldBuf[i]; - - bool good = true; - - int xCell = static_cast(p.x / cellSize); - int yCell = static_cast(p.y / cellSize); - - int x1 = xCell - 1; - int y1 = yCell - 1; - int x2 = xCell + 1; - int y2 = yCell + 1; - - // boundary check - x1 = std::max(0, x1); - y1 = std::max(0, y1); - x2 = std::min(gridWidth - 1, x2); - y2 = std::min(gridHeight - 1, y2); - - for (int yy = y1; yy <= y2; ++yy) - { - for (int xx = x1; xx <= x2; ++xx) - { - vector& m = grid[yy * gridWidth + xx]; - - for(size_t j = 0; j < m.size(); ++j) - { - float dx = (float)(p.x - m[j].x); - float dy = (float)(p.y - m[j].y); - - if (dx * dx + dy * dy < minDist2) - { - good = false; - goto break_out; - } - } - } - } - - break_out: - - if(good) - { - grid[yCell * gridWidth + xCell].push_back(p); - - newBuf[newCount++] = p; - } - } - - cudaSafeCall( cudaMemcpy(centers, newBuf, newCount * sizeof(unsigned int), cudaMemcpyHostToDevice) ); - centersCount = newCount; - } - - ensureSizeIsEnough(1, maxCircles, CV_32FC3, circles); - - const int circlesCount = circlesAccumRadius_gpu(centers, centersCount, srcPoints, pointsCount, circles.ptr(), maxCircles, - dp, minRadius, maxRadius, votesThreshold, deviceSupports(FEATURE_SET_COMPUTE_20)); - - if (circlesCount > 0) - circles.cols = circlesCount; - else - circles.release(); -} - -void cv::gpu::HoughCirclesDownload(const GpuMat& d_circles, cv::OutputArray h_circles_) -{ - if (d_circles.empty()) - { - h_circles_.release(); - return; - } - - CV_Assert(d_circles.rows == 1 && d_circles.type() == CV_32FC3); - - h_circles_.create(1, d_circles.cols, CV_32FC3); - Mat h_circles = h_circles_.getMat(); - d_circles.download(h_circles); -} - -////////////////////////////////////////////////////////// -// GeneralizedHough - namespace cv { namespace gpu { namespace device { namespace hough diff --git a/modules/gpu/src/hough_circles.cpp b/modules/gpu/src/hough_circles.cpp new file mode 100644 index 0000000000..74aa7394f2 --- /dev/null +++ b/modules/gpu/src/hough_circles.cpp @@ -0,0 +1,223 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#include "precomp.hpp" + +using namespace std; +using namespace cv; +using namespace cv::gpu; + +#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) + +void cv::gpu::HoughCircles(const GpuMat&, GpuMat&, int, float, float, int, int, int, int, int) { throw_nogpu(); } +void cv::gpu::HoughCircles(const GpuMat&, GpuMat&, HoughCirclesBuf&, int, float, float, int, int, int, int, int) { throw_nogpu(); } +void cv::gpu::HoughCirclesDownload(const GpuMat&, OutputArray) { throw_nogpu(); } + +#else /* !defined (HAVE_CUDA) */ + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + int buildPointList_gpu(PtrStepSzb src, unsigned int* list); + } +}}} + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + void circlesAccumCenters_gpu(const unsigned int* list, int count, PtrStepi dx, PtrStepi dy, PtrStepSzi accum, int minRadius, int maxRadius, float idp); + int buildCentersList_gpu(PtrStepSzi accum, unsigned int* centers, int threshold); + int circlesAccumRadius_gpu(const unsigned int* centers, int centersCount, const unsigned int* list, int count, + float3* circles, int maxCircles, float dp, int minRadius, int maxRadius, int threshold, bool has20); + } +}}} + +void cv::gpu::HoughCircles(const GpuMat& src, GpuMat& circles, int method, float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles) +{ + HoughCirclesBuf buf; + HoughCircles(src, circles, buf, method, dp, minDist, cannyThreshold, votesThreshold, minRadius, maxRadius, maxCircles); +} + +void cv::gpu::HoughCircles(const GpuMat& src, GpuMat& circles, HoughCirclesBuf& buf, int method, + float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles) +{ + using namespace cv::gpu::device::hough; + + CV_Assert(src.type() == CV_8UC1); + CV_Assert(src.cols < std::numeric_limits::max()); + CV_Assert(src.rows < std::numeric_limits::max()); + CV_Assert(method == CV_HOUGH_GRADIENT); + CV_Assert(dp > 0); + CV_Assert(minRadius > 0 && maxRadius > minRadius); + CV_Assert(cannyThreshold > 0); + CV_Assert(votesThreshold > 0); + CV_Assert(maxCircles > 0); + + const float idp = 1.0f / dp; + + cv::gpu::Canny(src, buf.cannyBuf, buf.edges, std::max(cannyThreshold / 2, 1), cannyThreshold); + + ensureSizeIsEnough(2, src.size().area(), CV_32SC1, buf.list); + unsigned int* srcPoints = buf.list.ptr(0); + unsigned int* centers = buf.list.ptr(1); + + const int pointsCount = buildPointList_gpu(buf.edges, srcPoints); + if (pointsCount == 0) + { + circles.release(); + return; + } + + ensureSizeIsEnough(cvCeil(src.rows * idp) + 2, cvCeil(src.cols * idp) + 2, CV_32SC1, buf.accum); + buf.accum.setTo(Scalar::all(0)); + + circlesAccumCenters_gpu(srcPoints, pointsCount, buf.cannyBuf.dx, buf.cannyBuf.dy, buf.accum, minRadius, maxRadius, idp); + + int centersCount = buildCentersList_gpu(buf.accum, centers, votesThreshold); + if (centersCount == 0) + { + circles.release(); + return; + } + + if (minDist > 1) + { + cv::AutoBuffer oldBuf_(centersCount); + cv::AutoBuffer newBuf_(centersCount); + int newCount = 0; + + ushort2* oldBuf = oldBuf_; + ushort2* newBuf = newBuf_; + + cudaSafeCall( cudaMemcpy(oldBuf, centers, centersCount * sizeof(ushort2), cudaMemcpyDeviceToHost) ); + + const int cellSize = cvRound(minDist); + const int gridWidth = (src.cols + cellSize - 1) / cellSize; + const int gridHeight = (src.rows + cellSize - 1) / cellSize; + + std::vector< std::vector > grid(gridWidth * gridHeight); + + const float minDist2 = minDist * minDist; + + for (int i = 0; i < centersCount; ++i) + { + ushort2 p = oldBuf[i]; + + bool good = true; + + int xCell = static_cast(p.x / cellSize); + int yCell = static_cast(p.y / cellSize); + + int x1 = xCell - 1; + int y1 = yCell - 1; + int x2 = xCell + 1; + int y2 = yCell + 1; + + // boundary check + x1 = std::max(0, x1); + y1 = std::max(0, y1); + x2 = std::min(gridWidth - 1, x2); + y2 = std::min(gridHeight - 1, y2); + + for (int yy = y1; yy <= y2; ++yy) + { + for (int xx = x1; xx <= x2; ++xx) + { + vector& m = grid[yy * gridWidth + xx]; + + for(size_t j = 0; j < m.size(); ++j) + { + float dx = (float)(p.x - m[j].x); + float dy = (float)(p.y - m[j].y); + + if (dx * dx + dy * dy < minDist2) + { + good = false; + goto break_out; + } + } + } + } + + break_out: + + if(good) + { + grid[yCell * gridWidth + xCell].push_back(p); + + newBuf[newCount++] = p; + } + } + + cudaSafeCall( cudaMemcpy(centers, newBuf, newCount * sizeof(unsigned int), cudaMemcpyHostToDevice) ); + centersCount = newCount; + } + + ensureSizeIsEnough(1, maxCircles, CV_32FC3, circles); + + const int circlesCount = circlesAccumRadius_gpu(centers, centersCount, srcPoints, pointsCount, circles.ptr(), maxCircles, + dp, minRadius, maxRadius, votesThreshold, deviceSupports(FEATURE_SET_COMPUTE_20)); + + if (circlesCount > 0) + circles.cols = circlesCount; + else + circles.release(); +} + +void cv::gpu::HoughCirclesDownload(const GpuMat& d_circles, cv::OutputArray h_circles_) +{ + if (d_circles.empty()) + { + h_circles_.release(); + return; + } + + CV_Assert(d_circles.rows == 1 && d_circles.type() == CV_32FC3); + + h_circles_.create(1, d_circles.cols, CV_32FC3); + Mat h_circles = h_circles_.getMat(); + d_circles.download(h_circles); +} + +#endif /* !defined (HAVE_CUDA) */ diff --git a/modules/gpu/src/hough_lines.cpp b/modules/gpu/src/hough_lines.cpp new file mode 100644 index 0000000000..4cc4067b20 --- /dev/null +++ b/modules/gpu/src/hough_lines.cpp @@ -0,0 +1,142 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#include "precomp.hpp" + +using namespace std; +using namespace cv; +using namespace cv::gpu; + +#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) + +void cv::gpu::HoughLines(const GpuMat&, GpuMat&, float, float, int, bool, int) { throw_nogpu(); } +void cv::gpu::HoughLines(const GpuMat&, GpuMat&, HoughLinesBuf&, float, float, int, bool, int) { throw_nogpu(); } +void cv::gpu::HoughLinesDownload(const GpuMat&, OutputArray, OutputArray) { throw_nogpu(); } + +#else /* !defined (HAVE_CUDA) */ + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + int buildPointList_gpu(PtrStepSzb src, unsigned int* list); + } +}}} + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + void linesAccum_gpu(const unsigned int* list, int count, PtrStepSzi accum, float rho, float theta, size_t sharedMemPerBlock, bool has20); + int linesGetResult_gpu(PtrStepSzi accum, float2* out, int* votes, int maxSize, float rho, float theta, int threshold, bool doSort); + } +}}} + +void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, float rho, float theta, int threshold, bool doSort, int maxLines) +{ + HoughLinesBuf buf; + HoughLines(src, lines, buf, rho, theta, threshold, doSort, maxLines); +} + +void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, HoughLinesBuf& buf, float rho, float theta, int threshold, bool doSort, int maxLines) +{ + using namespace cv::gpu::device::hough; + + CV_Assert(src.type() == CV_8UC1); + CV_Assert(src.cols < std::numeric_limits::max()); + CV_Assert(src.rows < std::numeric_limits::max()); + + ensureSizeIsEnough(1, src.size().area(), CV_32SC1, buf.list); + unsigned int* srcPoints = buf.list.ptr(); + + const int pointsCount = buildPointList_gpu(src, srcPoints); + if (pointsCount == 0) + { + lines.release(); + return; + } + + const int numangle = cvRound(CV_PI / theta); + const int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho); + CV_Assert(numangle > 0 && numrho > 0); + + ensureSizeIsEnough(numangle + 2, numrho + 2, CV_32SC1, buf.accum); + buf.accum.setTo(Scalar::all(0)); + + DeviceInfo devInfo; + linesAccum_gpu(srcPoints, pointsCount, buf.accum, rho, theta, devInfo.sharedMemPerBlock(), devInfo.supports(FEATURE_SET_COMPUTE_20)); + + ensureSizeIsEnough(2, maxLines, CV_32FC2, lines); + + int linesCount = linesGetResult_gpu(buf.accum, lines.ptr(0), lines.ptr(1), maxLines, rho, theta, threshold, doSort); + if (linesCount > 0) + lines.cols = linesCount; + else + lines.release(); +} + +void cv::gpu::HoughLinesDownload(const GpuMat& d_lines, OutputArray h_lines_, OutputArray h_votes_) +{ + if (d_lines.empty()) + { + h_lines_.release(); + if (h_votes_.needed()) + h_votes_.release(); + return; + } + + CV_Assert(d_lines.rows == 2 && d_lines.type() == CV_32FC2); + + h_lines_.create(1, d_lines.cols, CV_32FC2); + Mat h_lines = h_lines_.getMat(); + d_lines.row(0).download(h_lines); + + if (h_votes_.needed()) + { + h_votes_.create(1, d_lines.cols, CV_32SC1); + Mat h_votes = h_votes_.getMat(); + GpuMat d_votes(1, d_lines.cols, CV_32SC1, const_cast(d_lines.ptr(1))); + d_votes.download(h_votes); + } +} + +#endif /* !defined (HAVE_CUDA) */ diff --git a/modules/gpu/src/hough_segments.cpp b/modules/gpu/src/hough_segments.cpp new file mode 100644 index 0000000000..c34f33a626 --- /dev/null +++ b/modules/gpu/src/hough_segments.cpp @@ -0,0 +1,110 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ + +#include "precomp.hpp" + +using namespace std; +using namespace cv; +using namespace cv::gpu; + +#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) + +void cv::gpu::HoughLinesP(const GpuMat&, GpuMat&, HoughLinesBuf&, float, float, int, int, int) { throw_nogpu(); } + +#else /* !defined (HAVE_CUDA) */ + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + int buildPointList_gpu(PtrStepSzb src, unsigned int* list); + } +}}} + +namespace cv { namespace gpu { namespace device +{ + namespace hough + { + void linesAccum_gpu(const unsigned int* list, int count, PtrStepSzi accum, float rho, float theta, size_t sharedMemPerBlock, bool has20); + int houghLinesProbabilistic_gpu(PtrStepSzb mask, PtrStepSzi accum, int4* out, int maxSize, float rho, float theta, int lineGap, int lineLength); + } +}}} + +void cv::gpu::HoughLinesP(const GpuMat& src, GpuMat& lines, HoughLinesBuf& buf, float rho, float theta, int minLineLength, int maxLineGap, int maxLines) +{ + using namespace cv::gpu::device::hough; + + CV_Assert( src.type() == CV_8UC1 ); + CV_Assert( src.cols < std::numeric_limits::max() ); + CV_Assert( src.rows < std::numeric_limits::max() ); + + ensureSizeIsEnough(1, src.size().area(), CV_32SC1, buf.list); + unsigned int* srcPoints = buf.list.ptr(); + + const int pointsCount = buildPointList_gpu(src, srcPoints); + if (pointsCount == 0) + { + lines.release(); + return; + } + + const int numangle = cvRound(CV_PI / theta); + const int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho); + CV_Assert( numangle > 0 && numrho > 0 ); + + ensureSizeIsEnough(numangle + 2, numrho + 2, CV_32SC1, buf.accum); + buf.accum.setTo(Scalar::all(0)); + + DeviceInfo devInfo; + linesAccum_gpu(srcPoints, pointsCount, buf.accum, rho, theta, devInfo.sharedMemPerBlock(), devInfo.supports(FEATURE_SET_COMPUTE_20)); + + ensureSizeIsEnough(1, maxLines, CV_32SC4, lines); + + int linesCount = houghLinesProbabilistic_gpu(src, buf.accum, lines.ptr(), maxLines, rho, theta, maxLineGap, minLineLength); + + if (linesCount > 0) + lines.cols = linesCount; + else + lines.release(); +} + +#endif /* !defined (HAVE_CUDA) */ From 33d42b740c6fe938b63a0b25c9ad51741aba48c3 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 21 Jan 2014 14:35:51 +0400 Subject: [PATCH 048/293] disable CUDA generalized Hough Transform --- modules/gpu/src/cuda/generalized_hough.cu | 2 ++ modules/gpu/src/generalized_hough.cpp | 2 ++ modules/gpu/test/test_hough.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/gpu/src/cuda/generalized_hough.cu b/modules/gpu/src/cuda/generalized_hough.cu index 5e2041eae4..35451e7e23 100644 --- a/modules/gpu/src/cuda/generalized_hough.cu +++ b/modules/gpu/src/cuda/generalized_hough.cu @@ -40,6 +40,8 @@ // //M*/ +#define CUDA_DISABLER + #if !defined CUDA_DISABLER #include diff --git a/modules/gpu/src/generalized_hough.cpp b/modules/gpu/src/generalized_hough.cpp index a92c37d1a5..867ba7ee71 100644 --- a/modules/gpu/src/generalized_hough.cpp +++ b/modules/gpu/src/generalized_hough.cpp @@ -40,6 +40,8 @@ // //M*/ +#define CUDA_DISABLER + #include "precomp.hpp" using namespace std; diff --git a/modules/gpu/test/test_hough.cpp b/modules/gpu/test/test_hough.cpp index f876a7a2b0..6441fc69ab 100644 --- a/modules/gpu/test/test_hough.cpp +++ b/modules/gpu/test/test_hough.cpp @@ -189,7 +189,7 @@ PARAM_TEST_CASE(GeneralizedHough, cv::gpu::DeviceInfo, UseRoi) { }; -GPU_TEST_P(GeneralizedHough, POSITION) +GPU_TEST_P(GeneralizedHough, DISABLED_POSITION) { const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); cv::gpu::setDevice(devInfo.deviceID()); From b75cbfde45c00fc956f033d0af7fe6d63312fd33 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 17 Jan 2014 16:30:31 +0400 Subject: [PATCH 049/293] All installed files marked with component names for install customization. --- 3rdparty/libjasper/CMakeLists.txt | 2 +- 3rdparty/libjpeg/CMakeLists.txt | 2 +- 3rdparty/libpng/CMakeLists.txt | 2 +- 3rdparty/libtiff/CMakeLists.txt | 2 +- 3rdparty/openexr/CMakeLists.txt | 2 +- 3rdparty/tbb/CMakeLists.txt | 6 ++--- 3rdparty/zlib/CMakeLists.txt | 2 +- apps/haartraining/CMakeLists.txt | 12 ++++----- apps/traincascade/CMakeLists.txt | 4 +-- cmake/OpenCVDetectAndroidSDK.cmake | 10 +++---- cmake/OpenCVGenAndroidMK.cmake | 2 +- cmake/OpenCVGenConfig.cmake | 26 +++++++++---------- cmake/OpenCVGenHeaders.cmake | 2 +- cmake/OpenCVGenPkgconfig.cmake | 2 +- cmake/OpenCVModule.cmake | 12 ++++----- data/CMakeLists.txt | 8 +++--- doc/CMakeLists.txt | 4 +-- include/CMakeLists.txt | 4 +-- modules/androidcamera/CMakeLists.txt | 2 +- .../camera_wrapper/CMakeLists.txt | 2 +- modules/gpu/CMakeLists.txt | 2 +- modules/highgui/CMakeLists.txt | 2 +- modules/java/CMakeLists.txt | 26 +++++++++---------- modules/python/CMakeLists.txt | 12 ++++----- platforms/android/libinfo/CMakeLists.txt | 2 +- platforms/android/package/CMakeLists.txt | 2 +- platforms/android/service/CMakeLists.txt | 2 +- samples/c/CMakeLists.txt | 4 +-- samples/cpp/CMakeLists.txt | 4 +-- samples/gpu/CMakeLists.txt | 4 +-- samples/gpu/performance/CMakeLists.txt | 2 +- samples/ocl/CMakeLists.txt | 4 +-- 32 files changed, 87 insertions(+), 87 deletions(-) diff --git a/3rdparty/libjasper/CMakeLists.txt b/3rdparty/libjasper/CMakeLists.txt index dda9cd2556..7b3dcb08a5 100644 --- a/3rdparty/libjasper/CMakeLists.txt +++ b/3rdparty/libjasper/CMakeLists.txt @@ -46,5 +46,5 @@ if(ENABLE_SOLUTION_FOLDERS) endif() if(NOT BUILD_SHARED_LIBS) - ocv_install_target(${JASPER_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main) + ocv_install_target(${JASPER_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) endif() diff --git a/3rdparty/libjpeg/CMakeLists.txt b/3rdparty/libjpeg/CMakeLists.txt index 8d622f24ff..02d71ade2b 100644 --- a/3rdparty/libjpeg/CMakeLists.txt +++ b/3rdparty/libjpeg/CMakeLists.txt @@ -39,5 +39,5 @@ if(ENABLE_SOLUTION_FOLDERS) endif() if(NOT BUILD_SHARED_LIBS) - ocv_install_target(${JPEG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main) + ocv_install_target(${JPEG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) endif() diff --git a/3rdparty/libpng/CMakeLists.txt b/3rdparty/libpng/CMakeLists.txt index 42b6263e52..8d3d5f4976 100644 --- a/3rdparty/libpng/CMakeLists.txt +++ b/3rdparty/libpng/CMakeLists.txt @@ -55,5 +55,5 @@ if(ENABLE_SOLUTION_FOLDERS) endif() if(NOT BUILD_SHARED_LIBS) - ocv_install_target(${PNG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main) + ocv_install_target(${PNG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) endif() diff --git a/3rdparty/libtiff/CMakeLists.txt b/3rdparty/libtiff/CMakeLists.txt index 6c34fb5e34..addbb5551c 100644 --- a/3rdparty/libtiff/CMakeLists.txt +++ b/3rdparty/libtiff/CMakeLists.txt @@ -115,5 +115,5 @@ if(ENABLE_SOLUTION_FOLDERS) endif() if(NOT BUILD_SHARED_LIBS) - ocv_install_target(${TIFF_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main) + ocv_install_target(${TIFF_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) endif() diff --git a/3rdparty/openexr/CMakeLists.txt b/3rdparty/openexr/CMakeLists.txt index 2b11436e1d..c4facad2fc 100644 --- a/3rdparty/openexr/CMakeLists.txt +++ b/3rdparty/openexr/CMakeLists.txt @@ -62,7 +62,7 @@ if(ENABLE_SOLUTION_FOLDERS) endif() if(NOT BUILD_SHARED_LIBS) - ocv_install_target(IlmImf EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main) + ocv_install_target(IlmImf EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) endif() set(OPENEXR_INCLUDE_PATHS ${OPENEXR_INCLUDE_PATHS} PARENT_SCOPE) diff --git a/3rdparty/tbb/CMakeLists.txt b/3rdparty/tbb/CMakeLists.txt index 272c195b36..e16f6cd38d 100644 --- a/3rdparty/tbb/CMakeLists.txt +++ b/3rdparty/tbb/CMakeLists.txt @@ -248,9 +248,9 @@ if(ENABLE_SOLUTION_FOLDERS) endif() ocv_install_target(tbb EXPORT OpenCVModules - RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT main - LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main - ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main + RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs + LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs + ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev ) # get TBB version diff --git a/3rdparty/zlib/CMakeLists.txt b/3rdparty/zlib/CMakeLists.txt index f1b28fd396..410f2420bf 100644 --- a/3rdparty/zlib/CMakeLists.txt +++ b/3rdparty/zlib/CMakeLists.txt @@ -95,5 +95,5 @@ if(ENABLE_SOLUTION_FOLDERS) endif() if(NOT BUILD_SHARED_LIBS) - ocv_install_target(${ZLIB_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main) + ocv_install_target(${ZLIB_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev) endif() diff --git a/apps/haartraining/CMakeLists.txt b/apps/haartraining/CMakeLists.txt index cdc280556e..d8a3c55c8d 100644 --- a/apps/haartraining/CMakeLists.txt +++ b/apps/haartraining/CMakeLists.txt @@ -71,14 +71,14 @@ set_target_properties(opencv_performance PROPERTIES if(INSTALL_CREATE_DISTRIB) if(BUILD_SHARED_LIBS) - install(TARGETS opencv_haartraining RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT main) - install(TARGETS opencv_createsamples RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT main) - install(TARGETS opencv_performance RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT main) + install(TARGETS opencv_haartraining RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev) + install(TARGETS opencv_createsamples RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev) + install(TARGETS opencv_performance RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev) endif() else() - install(TARGETS opencv_haartraining RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT main) - install(TARGETS opencv_createsamples RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT main) - install(TARGETS opencv_performance RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT main) + install(TARGETS opencv_haartraining RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev) + install(TARGETS opencv_createsamples RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev) + install(TARGETS opencv_performance RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev) endif() if(ENABLE_SOLUTION_FOLDERS) diff --git a/apps/traincascade/CMakeLists.txt b/apps/traincascade/CMakeLists.txt index 8f6fbe0348..941c0ec711 100644 --- a/apps/traincascade/CMakeLists.txt +++ b/apps/traincascade/CMakeLists.txt @@ -35,8 +35,8 @@ endif() if(INSTALL_CREATE_DISTRIB) if(BUILD_SHARED_LIBS) - install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT main) + install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev) endif() else() - install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT main) + install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev) endif() diff --git a/cmake/OpenCVDetectAndroidSDK.cmake b/cmake/OpenCVDetectAndroidSDK.cmake index 393dbb62d2..7a37051e49 100644 --- a/cmake/OpenCVDetectAndroidSDK.cmake +++ b/cmake/OpenCVDetectAndroidSDK.cmake @@ -344,20 +344,20 @@ macro(add_android_project target path) add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${android_proj_bin_dir}/bin/${target}-debug.apk" "${OpenCV_BINARY_DIR}/bin/${target}.apk") if(INSTALL_ANDROID_EXAMPLES AND "${target}" MATCHES "^example-") #apk - install(FILES "${OpenCV_BINARY_DIR}/bin/${target}.apk" DESTINATION "samples" COMPONENT main) + install(FILES "${OpenCV_BINARY_DIR}/bin/${target}.apk" DESTINATION "samples" COMPONENT samples) get_filename_component(sample_dir "${path}" NAME) #java part list(REMOVE_ITEM android_proj_files ${ANDROID_MANIFEST_FILE}) foreach(f ${android_proj_files} ${ANDROID_MANIFEST_FILE}) get_filename_component(install_subdir "${f}" PATH) - install(FILES "${android_proj_bin_dir}/${f}" DESTINATION "samples/${sample_dir}/${install_subdir}" COMPONENT main) + install(FILES "${android_proj_bin_dir}/${f}" DESTINATION "samples/${sample_dir}/${install_subdir}" COMPONENT samples) endforeach() #jni part + eclipse files file(GLOB_RECURSE jni_files RELATIVE "${path}" "${path}/jni/*" "${path}/.cproject") ocv_list_filterout(jni_files "\\\\.svn") foreach(f ${jni_files} ".classpath" ".project" ".settings/org.eclipse.jdt.core.prefs") get_filename_component(install_subdir "${f}" PATH) - install(FILES "${path}/${f}" DESTINATION "samples/${sample_dir}/${install_subdir}" COMPONENT main) + install(FILES "${path}/${f}" DESTINATION "samples/${sample_dir}/${install_subdir}" COMPONENT samples) endforeach() #update proj if(android_proj_lib_deps_commands) @@ -365,9 +365,9 @@ macro(add_android_project target path) endif() install(CODE "EXECUTE_PROCESS(COMMAND ${ANDROID_EXECUTABLE} --silent update project --path . --target \"${android_proj_sdk_target}\" --name \"${target}\" ${inst_lib_opt} WORKING_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/samples/${sample_dir}\" - )" COMPONENT main) + )" COMPONENT dev) #empty 'gen' - install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/samples/${sample_dir}/gen\")" COMPONENT main) + install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/samples/${sample_dir}/gen\")" COMPONENT samples) endif() endif() endmacro() diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index eed47652b4..45193a2732 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -116,5 +116,5 @@ if(ANDROID) set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../3rdparty/libs/\$(OPENCV_TARGET_ARCH_ABI)") configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk" IMMEDIATE @ONLY) - install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}) + install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk DESTINATION ${OPENCV_CONFIG_INSTALL_PATH} COMPONENT dev) endif(ANDROID) diff --git a/cmake/OpenCVGenConfig.cmake b/cmake/OpenCVGenConfig.cmake index 411d22582d..18411e8786 100644 --- a/cmake/OpenCVGenConfig.cmake +++ b/cmake/OpenCVGenConfig.cmake @@ -109,18 +109,18 @@ if(UNIX) # ANDROID configuration is created here also # /(share|lib)/*/ (U) # /(share|lib)/*/(cmake|CMake)/ (U) if(INSTALL_TO_MANGLED_PATHS) - install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig.cmake DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}-${OPENCV_VERSION}/) - install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig-version.cmake DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}-${OPENCV_VERSION}/) - install(EXPORT OpenCVModules DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}-${OPENCV_VERSION}/ FILE OpenCVModules${modules_file_suffix}.cmake) + install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig.cmake DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}-${OPENCV_VERSION}/ COMPONENT dev) + install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig-version.cmake DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}-${OPENCV_VERSION}/ COMPONENT dev) + install(EXPORT OpenCVModules DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}-${OPENCV_VERSION}/ FILE OpenCVModules${modules_file_suffix}.cmake COMPONENT dev) else() - install(FILES "${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig.cmake" DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/) - install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig-version.cmake DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/) - install(EXPORT OpenCVModules DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/ FILE OpenCVModules${modules_file_suffix}.cmake) + install(FILES "${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig.cmake" DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/ COMPONENT dev) + install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig-version.cmake DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/ COMPONENT dev) + install(EXPORT OpenCVModules DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/ FILE OpenCVModules${modules_file_suffix}.cmake COMPONENT dev) endif() endif() if(ANDROID) - install(FILES "${OpenCV_SOURCE_DIR}/platforms/android/android.toolchain.cmake" DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/) + install(FILES "${OpenCV_SOURCE_DIR}/platforms/android/android.toolchain.cmake" DESTINATION ${OPENCV_CONFIG_INSTALL_PATH}/ COMPONENT dev) endif() # -------------------------------------------------------------------------------------------- @@ -134,12 +134,12 @@ if(WIN32) configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" IMMEDIATE @ONLY) configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig-version.cmake.in" "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig-version.cmake" IMMEDIATE @ONLY) if(BUILD_SHARED_LIBS) - install(FILES "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}lib") - install(EXPORT OpenCVModules DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}lib" FILE OpenCVModules${modules_file_suffix}.cmake) + install(FILES "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}lib" COMPONENT dev) + install(EXPORT OpenCVModules DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}lib" FILE OpenCVModules${modules_file_suffix}.cmake COMPONENT dev) else() - install(FILES "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}staticlib") - install(EXPORT OpenCVModules DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}staticlib" FILE OpenCVModules${modules_file_suffix}.cmake) + install(FILES "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}staticlib" COMPONENT dev) + install(EXPORT OpenCVModules DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}staticlib" FILE OpenCVModules${modules_file_suffix}.cmake COMPONENT dev) endif() - install(FILES "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig-version.cmake" DESTINATION "${CMAKE_INSTALL_PREFIX}") - install(FILES "${OpenCV_SOURCE_DIR}/cmake/OpenCVConfig.cmake" DESTINATION "${CMAKE_INSTALL_PREFIX}/") + install(FILES "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig-version.cmake" DESTINATION "${CMAKE_INSTALL_PREFIX}" COMPONENT dev) + install(FILES "${OpenCV_SOURCE_DIR}/cmake/OpenCVConfig.cmake" DESTINATION "${CMAKE_INSTALL_PREFIX}/" COMPONENT dev) endif() diff --git a/cmake/OpenCVGenHeaders.cmake b/cmake/OpenCVGenHeaders.cmake index 35da0fb4be..c892a929c2 100644 --- a/cmake/OpenCVGenHeaders.cmake +++ b/cmake/OpenCVGenHeaders.cmake @@ -23,4 +23,4 @@ set(OPENCV_MODULE_DEFINITIONS_CONFIGMAKE "${OPENCV_MODULE_DEFINITIONS_CONFIGMAKE #endforeach() configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/opencv_modules.hpp.in" "${OPENCV_CONFIG_FILE_INCLUDE_DIR}/opencv2/opencv_modules.hpp") -install(FILES "${OPENCV_CONFIG_FILE_INCLUDE_DIR}/opencv2/opencv_modules.hpp" DESTINATION ${OPENCV_INCLUDE_INSTALL_PATH}/opencv2 COMPONENT main) +install(FILES "${OPENCV_CONFIG_FILE_INCLUDE_DIR}/opencv2/opencv_modules.hpp" DESTINATION ${OPENCV_INCLUDE_INSTALL_PATH}/opencv2 COMPONENT dev) diff --git a/cmake/OpenCVGenPkgconfig.cmake b/cmake/OpenCVGenPkgconfig.cmake index cd54f11bf3..13b1e44970 100644 --- a/cmake/OpenCVGenPkgconfig.cmake +++ b/cmake/OpenCVGenPkgconfig.cmake @@ -81,5 +81,5 @@ configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/opencv-XXX.pc.in" @ONLY IMMEDIATE) if(UNIX AND NOT ANDROID) - install(FILES ${CMAKE_BINARY_DIR}/unix-install/${OPENCV_PC_FILE_NAME} DESTINATION ${OPENCV_LIB_INSTALL_PATH}/pkgconfig) + install(FILES ${CMAKE_BINARY_DIR}/unix-install/${OPENCV_PC_FILE_NAME} DESTINATION ${OPENCV_LIB_INSTALL_PATH}/pkgconfig COMPONENT dev) endif() diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 3dd749b053..2f90a97f36 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -577,9 +577,9 @@ macro(ocv_create_module) endif() ocv_install_target(${the_module} EXPORT OpenCVModules - RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT main - LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main - ARCHIVE DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main + RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev + LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs + ARCHIVE DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT dev ) # only "public" headers need to be installed @@ -587,7 +587,7 @@ macro(ocv_create_module) foreach(hdr ${OPENCV_MODULE_${the_module}_HEADERS}) string(REGEX REPLACE "^.*opencv2/" "opencv2/" hdr2 "${hdr}") if(hdr2 MATCHES "^(opencv2/.*)/[^/]+.h(..)?$") - install(FILES ${hdr} DESTINATION "${OPENCV_INCLUDE_INSTALL_PATH}/${CMAKE_MATCH_1}" COMPONENT main) + install(FILES ${hdr} DESTINATION "${OPENCV_INCLUDE_INSTALL_PATH}/${CMAKE_MATCH_1}" COMPONENT dev) endif() endforeach() endif() @@ -795,7 +795,7 @@ function(ocv_add_samples) endif() if(WIN32) - install(TARGETS ${the_target} RUNTIME DESTINATION "samples/${module_id}" COMPONENT main) + install(TARGETS ${the_target} RUNTIME DESTINATION "samples/${module_id}" COMPONENT samples) endif() endforeach() endif() @@ -805,7 +805,7 @@ function(ocv_add_samples) file(GLOB sample_files "${samples_path}/*") install(FILES ${sample_files} DESTINATION share/OpenCV/samples/${module_id} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() endfunction() diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 70efd6fd03..48094df402 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -2,9 +2,9 @@ file(GLOB HAAR_CASCADES haarcascades/*.xml) file(GLOB LBP_CASCADES lbpcascades/*.xml) if(ANDROID) - install(FILES ${HAAR_CASCADES} DESTINATION sdk/etc/haarcascades COMPONENT main) - install(FILES ${LBP_CASCADES} DESTINATION sdk/etc/lbpcascades COMPONENT main) + install(FILES ${HAAR_CASCADES} DESTINATION sdk/etc/haarcascades COMPONENT libs) + install(FILES ${LBP_CASCADES} DESTINATION sdk/etc/lbpcascades COMPONENT libs) elseif(NOT WIN32) - install(FILES ${HAAR_CASCADES} DESTINATION share/OpenCV/haarcascades COMPONENT main) - install(FILES ${LBP_CASCADES} DESTINATION share/OpenCV/lbpcascades COMPONENT main) + install(FILES ${HAAR_CASCADES} DESTINATION share/OpenCV/haarcascades COMPONENT libs) + install(FILES ${LBP_CASCADES} DESTINATION share/OpenCV/lbpcascades COMPONENT libs) endif() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 5793f7298b..3557a2bd2c 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -143,11 +143,11 @@ if(BUILD_DOCS AND HAVE_SPHINX) endif() foreach(f ${DOC_LIST}) - install(FILES "${f}" DESTINATION "${OPENCV_DOC_INSTALL_PATH}" COMPONENT main) + install(FILES "${f}" DESTINATION "${OPENCV_DOC_INSTALL_PATH}" COMPONENT docs) endforeach() foreach(f ${OPTIONAL_DOC_LIST}) - install(FILES "${f}" DESTINATION "${OPENCV_DOC_INSTALL_PATH}" OPTIONAL) + install(FILES "${f}" DESTINATION "${OPENCV_DOC_INSTALL_PATH}" OPTIONAL COMPONENT docs) endforeach() endif() diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index ed3b85a8fc..b4e48e6fa7 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,7 +1,7 @@ file(GLOB old_hdrs "opencv/*.h*") install(FILES ${old_hdrs} DESTINATION ${OPENCV_INCLUDE_INSTALL_PATH}/opencv - COMPONENT main) + COMPONENT dev) install(FILES "opencv2/opencv.hpp" DESTINATION ${OPENCV_INCLUDE_INSTALL_PATH}/opencv2 - COMPONENT main) + COMPONENT dev) diff --git a/modules/androidcamera/CMakeLists.txt b/modules/androidcamera/CMakeLists.txt index 8ac8ced88e..3858ba9f6d 100644 --- a/modules/androidcamera/CMakeLists.txt +++ b/modules/androidcamera/CMakeLists.txt @@ -40,6 +40,6 @@ else() get_filename_component(wrapper_name "${wrapper}" NAME) install(FILES "${LIBRARY_OUTPUT_PATH}/${wrapper_name}" DESTINATION ${OPENCV_LIB_INSTALL_PATH} - COMPONENT main) + COMPONENT libs) endforeach() endif() diff --git a/modules/androidcamera/camera_wrapper/CMakeLists.txt b/modules/androidcamera/camera_wrapper/CMakeLists.txt index 21b9ee1ad2..bc5585a7a8 100644 --- a/modules/androidcamera/camera_wrapper/CMakeLists.txt +++ b/modules/androidcamera/camera_wrapper/CMakeLists.txt @@ -63,4 +63,4 @@ if (NOT (CMAKE_BUILD_TYPE MATCHES "debug")) endif() -install(TARGETS ${the_target} LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main) +install(TARGETS ${the_target} LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs) diff --git a/modules/gpu/CMakeLists.txt b/modules/gpu/CMakeLists.txt index 9171febc74..6de3e0efab 100644 --- a/modules/gpu/CMakeLists.txt +++ b/modules/gpu/CMakeLists.txt @@ -82,7 +82,7 @@ ocv_create_module(${cuda_link_libs}) if(HAVE_CUDA) install(FILES src/nvidia/NPP_staging/NPP_staging.hpp src/nvidia/core/NCV.hpp DESTINATION ${OPENCV_INCLUDE_INSTALL_PATH}/opencv2/${name} - COMPONENT main) + COMPONENT dev) endif() ocv_add_precompiled_headers(${the_module}) diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index fd2eec6a1e..7d2547bcff 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -315,7 +315,7 @@ if(WIN32 AND WITH_FFMPEG) COMMENT "Copying ${ffmpeg_path} to the output directory") endif() - install(FILES "${ffmpeg_path}" DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT main RENAME "${ffmpeg_bare_name_ver}") + install(FILES "${ffmpeg_path}" DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs RENAME "${ffmpeg_bare_name_ver}") endif() ocv_add_accuracy_tests() diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt index 3a6ebe8362..198048ebe2 100644 --- a/modules/java/CMakeLists.txt +++ b/modules/java/CMakeLists.txt @@ -175,7 +175,7 @@ foreach(java_file ${step3_input_files}) if(ANDROID) get_filename_component(install_subdir "${java_file_name}" PATH) - install(FILES "${output_name}" DESTINATION "${JAVA_INSTALL_ROOT}/src/org/opencv/${install_subdir}" COMPONENT main) + install(FILES "${output_name}" DESTINATION "${JAVA_INSTALL_ROOT}/src/org/opencv/${install_subdir}" COMPONENT java) endif() endforeach() @@ -189,7 +189,7 @@ if(ANDROID) if(NOT file MATCHES "jni/.+") get_filename_component(install_subdir "${file}" PATH) - install(FILES "${OpenCV_BINARY_DIR}/${file}" DESTINATION "${JAVA_INSTALL_ROOT}/${install_subdir}" COMPONENT main) + install(FILES "${OpenCV_BINARY_DIR}/${file}" DESTINATION "${JAVA_INSTALL_ROOT}/${install_subdir}" COMPONENT java) endif() endforeach() @@ -225,11 +225,11 @@ if(ANDROID AND ANDROID_EXECUTABLE) list(APPEND copied_files ${lib_target_files} "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}") list(APPEND step3_input_files "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}") - install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_PROJECT_PROPERTIES_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT main) - install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT main) + install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_PROJECT_PROPERTIES_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java) + install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java) # creating empty 'gen' and 'res' folders - install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/gen\")" COMPONENT main) - install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/res\")" COMPONENT main) + install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/gen\")" COMPONENT java) + install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/res\")" COMPONENT java) endif(ANDROID AND ANDROID_EXECUTABLE) set(step3_depends ${step2_depends} ${step3_input_files} ${copied_files}) @@ -282,7 +282,7 @@ else(ANDROID) else(WIN32) set(JAR_INSTALL_DIR share/OpenCV/java) endif(WIN32) - install(FILES ${JAR_FILE} DESTINATION ${JAR_INSTALL_DIR} COMPONENT main) + install(FILES ${JAR_FILE} DESTINATION ${JAR_INSTALL_DIR} COMPONENT java) endif(ANDROID) # step 5: build native part @@ -353,17 +353,17 @@ endif() if(ANDROID) ocv_install_target(${the_module} EXPORT OpenCVModules - LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main - ARCHIVE DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main) + LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT java + ARCHIVE DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT java) else() if(NOT INSTALL_CREATE_DISTRIB) ocv_install_target(${the_module} EXPORT OpenCVModules - RUNTIME DESTINATION ${JAR_INSTALL_DIR} COMPONENT main - LIBRARY DESTINATION ${JAR_INSTALL_DIR} COMPONENT main) + RUNTIME DESTINATION ${JAR_INSTALL_DIR} COMPONENT java + LIBRARY DESTINATION ${JAR_INSTALL_DIR} COMPONENT java) else() ocv_install_target(${the_module} EXPORT OpenCVModules - RUNTIME DESTINATION ${JAR_INSTALL_DIR}/${OpenCV_ARCH} COMPONENT main - LIBRARY DESTINATION ${JAR_INSTALL_DIR}/${OpenCV_ARCH} COMPONENT main) + RUNTIME DESTINATION ${JAR_INSTALL_DIR}/${OpenCV_ARCH} COMPONENT java + LIBRARY DESTINATION ${JAR_INSTALL_DIR}/${OpenCV_ARCH} COMPONENT java) endif() endif() diff --git a/modules/python/CMakeLists.txt b/modules/python/CMakeLists.txt index 2c44a3906d..bab8b061b3 100644 --- a/modules/python/CMakeLists.txt +++ b/modules/python/CMakeLists.txt @@ -108,17 +108,17 @@ endif() if(WIN32) set(PYTHON_INSTALL_ARCHIVE "") else() - set(PYTHON_INSTALL_ARCHIVE ARCHIVE DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT main) + set(PYTHON_INSTALL_ARCHIVE ARCHIVE DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT python) endif() if(NOT INSTALL_CREATE_DISTRIB) install(TARGETS ${the_module} ${PYTHON_INSTALL_CONFIGURATIONS} - RUNTIME DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT main - LIBRARY DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT main + RUNTIME DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT python + LIBRARY DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT python ${PYTHON_INSTALL_ARCHIVE} ) - install(FILES src2/cv.py ${PYTHON_INSTALL_CONFIGURATIONS} DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT main) + install(FILES src2/cv.py ${PYTHON_INSTALL_CONFIGURATIONS} DESTINATION ${PYTHON_PACKAGES_PATH} COMPONENT python) else() if(DEFINED PYTHON_VERSION_MAJOR) set(__ver "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") @@ -127,7 +127,7 @@ else() endif() install(TARGETS ${the_module} CONFIGURATIONS Release - RUNTIME DESTINATION python/${__ver}/${OpenCV_ARCH} COMPONENT main - LIBRARY DESTINATION python/${__ver}/${OpenCV_ARCH} COMPONENT main + RUNTIME DESTINATION python/${__ver}/${OpenCV_ARCH} COMPONENT python + LIBRARY DESTINATION python/${__ver}/${OpenCV_ARCH} COMPONENT python ) endif() diff --git a/platforms/android/libinfo/CMakeLists.txt b/platforms/android/libinfo/CMakeLists.txt index 028413ec6e..55dd278594 100644 --- a/platforms/android/libinfo/CMakeLists.txt +++ b/platforms/android/libinfo/CMakeLists.txt @@ -36,4 +36,4 @@ set_target_properties(${the_module} PROPERTIES ) get_filename_component(lib_name "libopencv_info.so" NAME) -install(FILES "${LIBRARY_OUTPUT_PATH}/${lib_name}" DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main) +install(FILES "${LIBRARY_OUTPUT_PATH}/${lib_name}" DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs) diff --git a/platforms/android/package/CMakeLists.txt b/platforms/android/package/CMakeLists.txt index 1382a078cf..b48a55a6a1 100644 --- a/platforms/android/package/CMakeLists.txt +++ b/platforms/android/package/CMakeLists.txt @@ -89,6 +89,6 @@ add_custom_command( DEPENDS "${OpenCV_BINARY_DIR}/bin/classes.jar.dephelper" "${PACKAGE_DIR}/res/values/strings.xml" "${PACKAGE_DIR}/res/drawable/icon.png" ${camera_wrappers} opencv_java ) -install(FILES "${APK_NAME}" DESTINATION "apk/" COMPONENT main) +install(FILES "${APK_NAME}" DESTINATION "apk/" COMPONENT libs) add_custom_target(android_package ALL SOURCES "${APK_NAME}" ) add_dependencies(android_package opencv_java) diff --git a/platforms/android/service/CMakeLists.txt b/platforms/android/service/CMakeLists.txt index dde1455138..c99b71392f 100644 --- a/platforms/android/service/CMakeLists.txt +++ b/platforms/android/service/CMakeLists.txt @@ -3,4 +3,4 @@ if(BUILD_ANDROID_SERVICE) #add_subdirectory(engine_test) endif() -install(FILES "readme.txt" DESTINATION "apk/" COMPONENT main) +install(FILES "readme.txt" DESTINATION "apk/" COMPONENT libs) diff --git a/samples/c/CMakeLists.txt b/samples/c/CMakeLists.txt index 77a42949d0..ab6e15dbf2 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/c/CMakeLists.txt @@ -39,7 +39,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) set_target_properties(${the_target} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:atlsd.lib /DEBUG") endif() install(TARGETS ${the_target} - RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/c" COMPONENT main) + RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/c" COMPONENT samples) endif() ENDMACRO() @@ -55,5 +55,5 @@ if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) install(FILES ${C_SAMPLES} DESTINATION share/OpenCV/samples/c - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif () diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index ebee5bd0a8..e0842d9e4a 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -68,7 +68,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) set_target_properties(${the_target} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:atlsd.lib /DEBUG") endif() install(TARGETS ${the_target} - RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${sample_subfolder}" COMPONENT main) + RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${sample_subfolder}" COMPONENT samples) endif() ENDMACRO() @@ -92,5 +92,5 @@ if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) install(FILES ${C_SAMPLES} DESTINATION share/OpenCV/samples/cpp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/gpu/CMakeLists.txt b/samples/gpu/CMakeLists.txt index 732a9172a5..7093cf5d19 100644 --- a/samples/gpu/CMakeLists.txt +++ b/samples/gpu/CMakeLists.txt @@ -65,7 +65,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) if(MSVC AND NOT BUILD_SHARED_LIBS) set_target_properties(${the_target} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:atlsd.lib /DEBUG") endif() - install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${project}" COMPONENT main) + install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${project}" COMPONENT samples) endif() ENDMACRO() @@ -84,5 +84,5 @@ if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) install(FILES ${install_list} DESTINATION share/OpenCV/samples/${project} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/gpu/performance/CMakeLists.txt b/samples/gpu/performance/CMakeLists.txt index 8f3caac5b3..0b2346f91f 100644 --- a/samples/gpu/performance/CMakeLists.txt +++ b/samples/gpu/performance/CMakeLists.txt @@ -23,7 +23,7 @@ if(ENABLE_SOLUTION_FOLDERS) endif() if(WIN32) - install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/gpu" COMPONENT main) + install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/gpu" COMPONENT samples) endif() if(INSTALL_C_EXAMPLES AND NOT WIN32) diff --git a/samples/ocl/CMakeLists.txt b/samples/ocl/CMakeLists.txt index 8db77d52c8..4139211000 100644 --- a/samples/ocl/CMakeLists.txt +++ b/samples/ocl/CMakeLists.txt @@ -38,7 +38,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) if(MSVC AND NOT BUILD_SHARED_LIBS) set_target_properties(${the_target} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:atlsd.lib /DEBUG") endif() - install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${project}" COMPONENT main) + install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${project}" COMPONENT samples) endif() ENDMACRO() @@ -55,5 +55,5 @@ if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) install(FILES ${install_list} DESTINATION share/OpenCV/samples/${project} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() From 7821fe2bde331c6b1abd612315ca9fc59da58619 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Sat, 18 Jan 2014 23:50:07 +0400 Subject: [PATCH 050/293] Initial Linux packages build rools for CPack. --- CMakeLists.txt | 6 +++ LICENSE | 33 ++++++++++++++ cmake/OpenCVModule.cmake | 2 +- cmake/OpenCVPackaging.cmake | 89 +++++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 LICENSE create mode 100644 cmake/OpenCVPackaging.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index d7db8fd130..eb25cd3474 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -975,3 +975,9 @@ ocv_finalize_status() if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") message(WARNING "The source directory is the same as binary directory. \"make clean\" may damage the source tree") endif() + +# ---------------------------------------------------------------------------- +# CPack stuff +# ---------------------------------------------------------------------------- + +include(cmake/OpenCVPackaging.cmake) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..5e32d88b47 --- /dev/null +++ b/LICENSE @@ -0,0 +1,33 @@ +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +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 names of the copyright holders nor the names of the 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 copyright holders 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. diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 2f90a97f36..6734462fc2 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -577,7 +577,7 @@ macro(ocv_create_module) endif() ocv_install_target(${the_module} EXPORT OpenCVModules - RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev + RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs ARCHIVE DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT dev ) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake new file mode 100644 index 0000000000..4a81c255bf --- /dev/null +++ b/cmake/OpenCVPackaging.cmake @@ -0,0 +1,89 @@ +if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") +set(CPACK_set_DESTDIR "on") + +if(NOT OPENCV_CUSTOM_PACKAGE_INFO) + set(CPACK_PACKAGE_DESCRIPTION "Open Computer Vision Library") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenCV") + set(CPACK_PACKAGE_VENDOR "OpenCV Foundation") + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + set(CPACK_PACKAGE_CONTACT "admin@opencv.org") +endif(NOT OPENCV_CUSTOM_PACKAGE_INFO) + +set(CPACK_PACKAGE_VERSION_MAJOR "${OPENCV_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${OPENCV_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${OPENCV_VERSION_PATCH}") + +#arch +if(X86) + set(CPACK_DEBIAN_ARCHITECTURE "i386") + set(CPACK_RPM_PACKAGE_ARCHITECTURE "i686") +elseif(X86_64) + set(CPACK_DEBIAN_ARCHITECTURE "amd64") + set(CPACK_RPM_PACKAGE_ARCHITECTURE "amd64") +elseif(ARM) + set(CPACK_DEBIAN_ARCHITECTURE "armhf") + set(CPACK_RPM_PACKAGE_ARCHITECTURE "armhf") +else() + set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) + set(CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +endif() + +if(CPACK_GENERATOR STREQUAL "DEB") + set(OPENCV_PACKAGE_ARCH_SUFFIX ${CPACK_DEBIAN_ARCHITECTURE}) +elseif(CPACK_GENERATOR STREQUAL "RPM") + set(OPENCV_PACKAGE_ARCH_SUFFIX ${CPACK_RPM_PACKAGE_ARCHITECTURE}) +else() + set(OPENCV_PACKAGE_ARCH_SUFFIX ${CMAKE_SYSTEM_PROCESSOR}) +endif() + +set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${OPENCV_VCSVERSION}-${OPENCV_PACKAGE_ARCH_SUFFIX}") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${OPENCV_VCSVERSION}-${OPENCV_PACKAGE_ARCH_SUFFIX}") + +#rpm options +set(CPACK_RPM_COMPONENT_INSTALL TRUE) +set(CPACK_RPM_PACKAGE_LICENSE ${CPACK_RESOURCE_FILE_LICENSE}) + +#deb options +set(CPACK_DEB_COMPONENT_INSTALL TRUE) +set(CPACK_DEBIAN_PACKAGE_PRIORITY "extra") + +#depencencies +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS TRUE) +set(CPACK_COMPONENT_samples_DEPENDS libs) +set(CPACK_COMPONENT_dev_DEPENDS libs) +set(CPACK_COMPONENT_docs_DEPENDS libs) +set(CPACK_COMPONENT_java_DEPENDS libs) +set(CPACK_COMPONENT_python_DEPENDS libs) + +if(NOT OPENCV_CUSTOM_PACKAGE_INFO) + set(CPACK_COMPONENT_libs_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}") + set(CPACK_COMPONENT_libs_DESCRIPTION "Open Computer Vision Library") + + set(CPACK_COMPONENT_python_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-python") + set(CPACK_COMPONENT_python_DESCRIPTION "Python bindings for Open Computer Vision Library") + + set(CPACK_COMPONENT_java_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-java") + set(CPACK_COMPONENT_java_DESCRIPTION "Java bindings for Open Computer Vision Library") + + set(CPACK_COMPONENT_dev_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-dev") + set(CPACK_COMPONENT_dev_DESCRIPTION "Development files for Open Computer Vision Library") + + set(CPACK_COMPONENT_docs_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-docs") + set(CPACK_COMPONENT_docs_DESCRIPTION "Documentation for Open Computer Vision Library") + + set(CPACK_COMPONENT_samples_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-samples") + set(CPACK_COMPONENT_samples_DESCRIPTION "Samples for Open Computer Vision Library") +endif(NOT OPENCV_CUSTOM_PACKAGE_INFO) + +if(NOT OPENCV_CUSTOM_PACKAGE_LAYOUT) + set(CPACK_libs_COMPONENT_INSTALL TRUE) + set(CPACK_dev_COMPONENT_INSTALL TRUE) + set(CPACK_docs_COMPONENT_INSTALL TRUE) + set(CPACK_python_COMPONENT_INSTALL TRUE) + set(CPACK_java_COMPONENT_INSTALL TRUE) + set(CPACK_samples_COMPONENT_INSTALL TRUE) +endif(NOT OPENCV_CUSTOM_PACKAGE_LAYOUT) + +include(CPack) + +ENDif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") \ No newline at end of file From dda999545c9dd1cca56081a4b2d56755210b840a Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Wed, 22 Jan 2014 10:40:14 +0400 Subject: [PATCH 051/293] fix GpuMat::copyTo method with mask: fill destination matrix with zeros if it was reallocated --- modules/core/src/gpumat.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/core/src/gpumat.cpp b/modules/core/src/gpumat.cpp index ec26801ddc..96691919fd 100644 --- a/modules/core/src/gpumat.cpp +++ b/modules/core/src/gpumat.cpp @@ -620,11 +620,19 @@ void cv::gpu::GpuMat::copyTo(GpuMat& m) const void cv::gpu::GpuMat::copyTo(GpuMat& mat, const GpuMat& mask) const { if (mask.empty()) + { copyTo(mat); + } else { + uchar* data0 = mat.data; + mat.create(size(), type()); + // do not leave dst uninitialized + if (mat.data != data0) + mat.setTo(Scalar::all(0)); + gpuFuncTable()->copyWithMask(*this, mat, mask); } } From 167a26642e58486a06517bdf6c1b3cdeae7152ac Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 22 Jan 2014 15:26:14 +0100 Subject: [PATCH 052/293] fix message sent to user during cross_compilation --- cmake/OpenCVDetectPython.cmake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmake/OpenCVDetectPython.cmake b/cmake/OpenCVDetectPython.cmake index 618b0fd222..d02b7596a6 100644 --- a/cmake/OpenCVDetectPython.cmake +++ b/cmake/OpenCVDetectPython.cmake @@ -84,11 +84,9 @@ if(PYTHON_EXECUTABLE) if(CMAKE_CROSSCOMPILING) message(STATUS "Cannot probe for Python/Numpy support (because we are cross-compiling OpenCV)") message(STATUS "If you want to enable Python/Numpy support, set the following variables:") - message(STATUS " PYTHON_EXECUTABLE") - message(STATUS " PYTHON_INCLUDE_DIR") - message(STATUS " PYTHON_LIBRARY") + message(STATUS " PYTHON_INCLUDE_PATH") + message(STATUS " PYTHON_LIBRARIES") message(STATUS " PYTHON_NUMPY_INCLUDE_DIR") - message(STATUS " PYTHON_NUMPY_VERSION") else() # Attempt to discover the NumPy include directory. If this succeeds, then build python API with NumPy execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import os; os.environ['DISTUTILS_USE_SDK']='1'; import numpy.distutils; print numpy.distutils.misc_util.get_numpy_include_dirs()[0]" From 9145985ff4ac7089d5082f49a0cc9dab14769b55 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Thu, 23 Jan 2014 14:41:21 +0400 Subject: [PATCH 053/293] added guard to prevent linking VTK with Qt5 --- cmake/OpenCVDetectVTK.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/OpenCVDetectVTK.cmake b/cmake/OpenCVDetectVTK.cmake index ef9aa8043c..78d1a73b69 100644 --- a/cmake/OpenCVDetectVTK.cmake +++ b/cmake/OpenCVDetectVTK.cmake @@ -2,6 +2,11 @@ if(NOT WITH_VTK OR ANDROID OR IOS) return() endif() +if (HAVE_QT5) + message(STATUS "VTK is disabled because OpenCV is linked with Q5. Some VTK disributives are compiled with Q4 and therefore can't be linked together Qt5.") + return() +endif() + find_package(VTK 6.0 QUIET COMPONENTS vtkRenderingCore vtkInteractionWidgets vtkInteractionStyle vtkIOLegacy vtkIOPLY vtkRenderingFreeType vtkRenderingLOD vtkFiltersTexture vtkIOExport NO_MODULE) if(NOT DEFINED VTK_FOUND OR NOT VTK_FOUND) From dca56841459dadfa01ac74eb1ac01cd7e05d8522 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Wed, 22 Jan 2014 21:47:50 +0400 Subject: [PATCH 054/293] removing duplicated legacy license, the actual instance is in 'opencv/LICENSE' --- doc/license.txt | 37 ------------------- .../android_binary_package/O4A_SDK.rst | 2 +- 2 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 doc/license.txt diff --git a/doc/license.txt b/doc/license.txt deleted file mode 100644 index 8824228d03..0000000000 --- a/doc/license.txt +++ /dev/null @@ -1,37 +0,0 @@ -IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. - - By downloading, copying, installing or using the software you agree to this license. - If you do not agree to this license, do not download, install, - copy or use the software. - - - License Agreement - For Open Source Computer Vision Library - -Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. -Third party copyrights are property of their respective owners. - -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. - - * The name of the copyright holders may not 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 Intel Corporation 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. diff --git a/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst b/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst index 9a683ea496..ef9337aae2 100644 --- a/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst +++ b/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst @@ -66,7 +66,7 @@ The structure of package contents looks as follows: | |_ armeabi-v7a | |_ x86 | - |_ license.txt + |_ LICENSE |_ README.android * :file:`sdk` folder contains OpenCV API and libraries for Android: From 0a4a1d7526ba893e818365875cd5aa916fc9f92b Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Fri, 24 Jan 2014 10:07:21 +0400 Subject: [PATCH 055/293] temporary disabling hanging test --- modules/ocl/perf/perf_haar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ocl/perf/perf_haar.cpp b/modules/ocl/perf/perf_haar.cpp index 86890a8911..3aedd88868 100644 --- a/modules/ocl/perf/perf_haar.cpp +++ b/modules/ocl/perf/perf_haar.cpp @@ -97,8 +97,8 @@ PERF_TEST_P( OCL_Cascade_Image_MinSize, CascadeClassifier, testing::Combine( testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml") ), testing::Values( string("cv/shared/lena.png"), - string("cv/cascadeandhog/images/bttf301.png"), - string("cv/cascadeandhog/images/class57.png") ), + string("cv/cascadeandhog/images/bttf301.png")/*, + string("cv/cascadeandhog/images/class57.png")*/ ), testing::Values(30, 64, 90) ) ) { const string cascasePath = get<0>(GetParam()); From 086792ec06ff78a45fdd5e1b2fa7f72fe3b7c9a2 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 23 Jan 2014 20:57:30 +0400 Subject: [PATCH 056/293] Improvements in package build. --- cmake/OpenCVPackaging.cmake | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index 4a81c255bf..3b8d4db547 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -2,24 +2,29 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") set(CPACK_set_DESTDIR "on") if(NOT OPENCV_CUSTOM_PACKAGE_INFO) - set(CPACK_PACKAGE_DESCRIPTION "Open Computer Vision Library") - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenCV") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Open Computer Vision Library") + set(CPACK_PACKAGE_DESCRIPTION +"OpenCV (Open Source Computer Vision Library) is an open source computer vision +and machine learning software library. OpenCV was built to provide a common +infrastructure for computer vision applications and to accelerate the use of +machine perception in the commercial products. Being a BSD-licensed product, +OpenCV makes it easy for businesses to utilize and modify the code.") set(CPACK_PACKAGE_VENDOR "OpenCV Foundation") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_CONTACT "admin@opencv.org") + set(CPACK_PACKAGE_VERSION_MAJOR "${OPENCV_VERSION_MAJOR}") + set(CPACK_PACKAGE_VERSION_MINOR "${OPENCV_VERSION_MINOR}") + set(CPACK_PACKAGE_VERSION_PATCH "${OPENCV_VERSION_PATCH}") + set(CPACK_PACKAGE_VERSION "${OPENCV_VCSVERSION}") endif(NOT OPENCV_CUSTOM_PACKAGE_INFO) -set(CPACK_PACKAGE_VERSION_MAJOR "${OPENCV_VERSION_MAJOR}") -set(CPACK_PACKAGE_VERSION_MINOR "${OPENCV_VERSION_MINOR}") -set(CPACK_PACKAGE_VERSION_PATCH "${OPENCV_VERSION_PATCH}") - #arch if(X86) set(CPACK_DEBIAN_ARCHITECTURE "i386") set(CPACK_RPM_PACKAGE_ARCHITECTURE "i686") elseif(X86_64) set(CPACK_DEBIAN_ARCHITECTURE "amd64") - set(CPACK_RPM_PACKAGE_ARCHITECTURE "amd64") + set(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") elseif(ARM) set(CPACK_DEBIAN_ARCHITECTURE "armhf") set(CPACK_RPM_PACKAGE_ARCHITECTURE "armhf") @@ -41,11 +46,16 @@ set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${OPENCV_VCSVERSION}-$ #rpm options set(CPACK_RPM_COMPONENT_INSTALL TRUE) -set(CPACK_RPM_PACKAGE_LICENSE ${CPACK_RESOURCE_FILE_LICENSE}) +set(CPACK_RPM_PACKAGE_SUMMARY ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}) +set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) +set(CPACK_RPM_PACKAGE_URL "http://opencv.org") +set(CPACK_RPM_PACKAGE_LICENSE "BSD") #deb options set(CPACK_DEB_COMPONENT_INSTALL TRUE) -set(CPACK_DEBIAN_PACKAGE_PRIORITY "extra") +set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") +set(CPACK_DEBIAN_PACKAGE_SECTION "libs") +set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://opencv.org") #depencencies set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS TRUE) @@ -55,6 +65,13 @@ set(CPACK_COMPONENT_docs_DEPENDS libs) set(CPACK_COMPONENT_java_DEPENDS libs) set(CPACK_COMPONENT_python_DEPENDS libs) +if(HAVE_CUDA) + string(REPLACE "." "-" cuda_version_suffix ${CUDA_VERSION}) + set(CPACK_DEB_libs_PACKAGE_DEPENDS "cuda-core-libs-${cuda_version_suffix}, cuda-extra-libs-${cuda_version_suffix}") + set(CPACK_COMPONENT_dev_DEPENDS libs) + set(CPACK_DEB_dev_PACKAGE_DEPENDS "cuda-headers-${cuda_version_suffix}") +endif() + if(NOT OPENCV_CUSTOM_PACKAGE_INFO) set(CPACK_COMPONENT_libs_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}") set(CPACK_COMPONENT_libs_DESCRIPTION "Open Computer Vision Library") From 64fd54ffb4994103d82a34b4039d4938fdb87e46 Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Fri, 24 Jan 2014 13:03:47 +0200 Subject: [PATCH 057/293] fix tiled read for reading tiles into partial blocks in image --- modules/highgui/src/grfmt_tiff.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/highgui/src/grfmt_tiff.cpp index 5179531f50..4b4d8011e8 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/highgui/src/grfmt_tiff.cpp @@ -246,13 +246,15 @@ bool TiffDecoder::readData( Mat& img ) return false; } + uchar * bstart = buffer + (tile_height0 - tile_height) * tile_width0 * 4; + for( i = 0; i < tile_height; i++ ) if( color ) - icvCvt_BGRA2BGR_8u_C4C3R( buffer + i*tile_width*4, 0, + icvCvt_BGRA2BGR_8u_C4C3R( bstart + i*tile_width0*4, 0, data + x*3 + img.step*(tile_height - i - 1), 0, cvSize(tile_width,1), 2 ); else - icvCvt_BGRA2Gray_8u_C4C1R( buffer + i*tile_width*4, 0, + icvCvt_BGRA2Gray_8u_C4C1R( bstart + i*tile_width0*4, 0, data + x + img.step*(tile_height - i - 1), 0, cvSize(tile_width,1), 2 ); break; @@ -271,25 +273,27 @@ bool TiffDecoder::readData( Mat& img ) return false; } + uint16 * bstart = buffer16 + (tile_height0 - tile_height) * tile_width0 * ncn; + for( i = 0; i < tile_height; i++ ) { if( color ) { if( ncn == 1 ) { - icvCvt_Gray2BGR_16u_C1C3R(buffer16 + i*tile_width*ncn, 0, + icvCvt_Gray2BGR_16u_C1C3R(bstart + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x*3, 0, cvSize(tile_width,1) ); } else if( ncn == 3 ) { - icvCvt_RGB2BGR_16u_C3R(buffer16 + i*tile_width*ncn, 0, + icvCvt_RGB2BGR_16u_C3R(bstart + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x*3, 0, cvSize(tile_width,1) ); } else { - icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width*ncn, 0, + icvCvt_BGRA2BGR_16u_C4C3R(bstart + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x*3, 0, cvSize(tile_width,1), 2 ); } @@ -299,12 +303,12 @@ bool TiffDecoder::readData( Mat& img ) if( ncn == 1 ) { memcpy((ushort*)(data + img.step*i)+x, - buffer16 + i*tile_width*ncn, + bstart + i*tile_width0*ncn, tile_width*sizeof(buffer16[0])); } else { - icvCvt_BGRA2Gray_16u_CnC1R(buffer16 + i*tile_width*ncn, 0, + icvCvt_BGRA2Gray_16u_CnC1R(bstart + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x, 0, cvSize(tile_width,1), ncn, 2 ); } @@ -327,18 +331,21 @@ bool TiffDecoder::readData( Mat& img ) return false; } + float * fstart = buffer32 + (tile_height0 - tile_height) * tile_width0 * sizeof(buffer32[0]); + double * dstart = buffer64 + (tile_height0 - tile_height) * tile_width0 * sizeof(buffer64[0]); + for( i = 0; i < tile_height; i++ ) { if(dst_bpp == 32) { memcpy((float*)(data + img.step*i)+x, - buffer32 + i*tile_width*ncn, + fstart + i*tile_width0*ncn, tile_width*sizeof(buffer32[0])); } else { memcpy((double*)(data + img.step*i)+x, - buffer64 + i*tile_width*ncn, + dstart + i*tile_width0*ncn, tile_width*sizeof(buffer64[0])); } } From 3dc2dbc17e7bf6d6001fd84d45c595bf02ea7a8c Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Fri, 24 Jan 2014 21:44:59 +0400 Subject: [PATCH 058/293] VTK off by default --- CMakeLists.txt | 2 +- modules/viz/include/opencv2/viz/vizcore.hpp | 6 +++--- modules/viz/src/vizimpl.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa4a2e28f3..c11a9f71d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,7 +128,7 @@ OCV_OPTION(WITH_1394 "Include IEEE1394 support" ON OCV_OPTION(WITH_AVFOUNDATION "Use AVFoundation for Video I/O" ON IF IOS) OCV_OPTION(WITH_CARBON "Use Carbon for UI instead of Cocoa" OFF IF APPLE ) OCV_OPTION(WITH_CUDA "Include NVidia Cuda Runtime support" ON IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT IOS) ) -OCV_OPTION(WITH_VTK "Include VTK library support (and build opencv_viz module eiher)" ON IF (NOT ANDROID AND NOT IOS) ) +OCV_OPTION(WITH_VTK "Include VTK library support (and build opencv_viz module eiher)" OFF IF (NOT ANDROID AND NOT IOS) ) OCV_OPTION(WITH_CUFFT "Include NVidia Cuda Fast Fourier Transform (FFT) library support" ON IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT IOS) ) OCV_OPTION(WITH_CUBLAS "Include NVidia Cuda Basic Linear Algebra Subprograms (BLAS) library support" OFF IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT IOS) ) OCV_OPTION(WITH_NVCUVID "Include NVidia Video Decoding library support" OFF IF (CMAKE_VERSION VERSION_GREATER "2.8" AND NOT ANDROID AND NOT IOS AND NOT APPLE) ) diff --git a/modules/viz/include/opencv2/viz/vizcore.hpp b/modules/viz/include/opencv2/viz/vizcore.hpp index bf44a2c039..0fde95b2f1 100644 --- a/modules/viz/include/opencv2/viz/vizcore.hpp +++ b/modules/viz/include/opencv2/viz/vizcore.hpp @@ -43,8 +43,8 @@ // //M*/ -#ifndef __OPENCV_VIZ_HPP__ -#define __OPENCV_VIZ_HPP__ +#ifndef __OPENCV_VIZCORE_HPP__ +#define __OPENCV_VIZCORE_HPP__ #include #include @@ -124,4 +124,4 @@ namespace cv } /* namespace viz */ } /* namespace cv */ -#endif /* __OPENCV_VIZ_HPP__ */ +#endif /* __OPENCV_VIZCORE_HPP__ */ diff --git a/modules/viz/src/vizimpl.hpp b/modules/viz/src/vizimpl.hpp index 9eb918af68..02675e0a5c 100644 --- a/modules/viz/src/vizimpl.hpp +++ b/modules/viz/src/vizimpl.hpp @@ -55,7 +55,7 @@ public: int ref_counter; VizImpl(const String &name); - virtual ~VizImpl() {}; + virtual ~VizImpl() {} bool wasStopped() const; void close(); From f332cba14b2a86017e1d2081130db110c2048c00 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 27 Jan 2014 14:20:30 +0400 Subject: [PATCH 059/293] OpenCV C/C++/OCL/CUDA samples install path fixed. Install rools for tests added. --- CMakeLists.txt | 2 +- cmake/OpenCVModule.cmake | 15 +++++++++++++++ cmake/OpenCVPackaging.cmake | 22 +++++++++++++++++----- cmake/templates/postinst | 3 +++ data/CMakeLists.txt | 8 ++++++++ samples/c/CMakeLists.txt | 12 +++++++++--- samples/cpp/CMakeLists.txt | 12 +++++++++--- samples/gpu/CMakeLists.txt | 12 +++++++++--- samples/gpu/performance/CMakeLists.txt | 3 ++- samples/ocl/CMakeLists.txt | 12 +++++++++--- 10 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 cmake/templates/postinst diff --git a/CMakeLists.txt b/CMakeLists.txt index eb25cd3474..2adc320435 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,7 +197,7 @@ OCV_OPTION(INSTALL_C_EXAMPLES "Install C examples" OFF ) OCV_OPTION(INSTALL_PYTHON_EXAMPLES "Install Python examples" OFF ) OCV_OPTION(INSTALL_ANDROID_EXAMPLES "Install Android examples" OFF IF ANDROID ) OCV_OPTION(INSTALL_TO_MANGLED_PATHS "Enables mangled install paths, that help with side by side installs." OFF IF (UNIX AND NOT ANDROID AND NOT IOS AND BUILD_SHARED_LIBS) ) - +OCV_OPTION(INSTALL_TESTS "Install accuracy and performance test binaries and test data" OFF) # OpenCV build options # =================================================== diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 6734462fc2..0e1ee250e7 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -711,6 +711,13 @@ function(ocv_add_perf_tests) else(OCV_DEPENDENCIES_FOUND) # TODO: warn about unsatisfied dependencies endif(OCV_DEPENDENCIES_FOUND) + if(INSTALL_TESTS) + if(ANDROID) + install(TARGETS ${the_target} RUNTIME DESTINATION sdk/etc/bin COMPONENT tests) + elseif(NOT WIN32) + install(TARGETS ${the_target} RUNTIME DESTINATION share/OpenCV/bin COMPONENT tests) + endif() + endif() endif() endfunction() @@ -764,6 +771,14 @@ function(ocv_add_accuracy_tests) else(OCV_DEPENDENCIES_FOUND) # TODO: warn about unsatisfied dependencies endif(OCV_DEPENDENCIES_FOUND) + + if(INSTALL_TESTS) + if(ANDROID) + install(TARGETS ${the_target} RUNTIME DESTINATION sdk/etc/bin COMPONENT tests) + elseif(NOT WIN32) + install(TARGETS ${the_target} RUNTIME DESTINATION share/OpenCV/bin COMPONENT tests) + endif() + endif() endif() endfunction() diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index 3b8d4db547..32d5c5da7f 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -56,6 +56,12 @@ set(CPACK_DEB_COMPONENT_INSTALL TRUE) set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") set(CPACK_DEBIAN_PACKAGE_SECTION "libs") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://opencv.org") +if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH) + set(prefix "${CMAKE_INSTALL_PREFIX}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/postinst" + "${CMAKE_BINARY_DIR}/junk/postinst" @ONLY IMMEDIATE) + set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/junk/postinst") +endif() #depencencies set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS TRUE) @@ -64,6 +70,9 @@ set(CPACK_COMPONENT_dev_DEPENDS libs) set(CPACK_COMPONENT_docs_DEPENDS libs) set(CPACK_COMPONENT_java_DEPENDS libs) set(CPACK_COMPONENT_python_DEPENDS libs) +if(INSTALL_TESTS) +set(CPACK_COMPONENT_tests_DEPENDS libs) +endif() if(HAVE_CUDA) string(REPLACE "." "-" cuda_version_suffix ${CUDA_VERSION}) @@ -77,19 +86,22 @@ if(NOT OPENCV_CUSTOM_PACKAGE_INFO) set(CPACK_COMPONENT_libs_DESCRIPTION "Open Computer Vision Library") set(CPACK_COMPONENT_python_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-python") - set(CPACK_COMPONENT_python_DESCRIPTION "Python bindings for Open Computer Vision Library") + set(CPACK_COMPONENT_python_DESCRIPTION "Python bindings for Open Source Computer Vision Library") set(CPACK_COMPONENT_java_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-java") - set(CPACK_COMPONENT_java_DESCRIPTION "Java bindings for Open Computer Vision Library") + set(CPACK_COMPONENT_java_DESCRIPTION "Java bindings for Open Source Computer Vision Library") set(CPACK_COMPONENT_dev_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-dev") - set(CPACK_COMPONENT_dev_DESCRIPTION "Development files for Open Computer Vision Library") + set(CPACK_COMPONENT_dev_DESCRIPTION "Development files for Open Source Computer Vision Library") set(CPACK_COMPONENT_docs_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-docs") - set(CPACK_COMPONENT_docs_DESCRIPTION "Documentation for Open Computer Vision Library") + set(CPACK_COMPONENT_docs_DESCRIPTION "Documentation for Open Source Computer Vision Library") set(CPACK_COMPONENT_samples_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-samples") - set(CPACK_COMPONENT_samples_DESCRIPTION "Samples for Open Computer Vision Library") + set(CPACK_COMPONENT_samples_DESCRIPTION "Samples for Open Source Computer Vision Library") + + set(CPACK_COMPONENT_tests_DISPLAY_NAME "lib${CMAKE_PROJECT_NAME}-tests") + set(CPACK_COMPONENT_tests_DESCRIPTION "Accuracy and performance tests for Open Source Computer Vision Library") endif(NOT OPENCV_CUSTOM_PACKAGE_INFO) if(NOT OPENCV_CUSTOM_PACKAGE_LAYOUT) diff --git a/cmake/templates/postinst b/cmake/templates/postinst new file mode 100644 index 0000000000..f6763781b6 --- /dev/null +++ b/cmake/templates/postinst @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "export OPENCV_TEST_DATA_PATH=@prefix@/share/OpenCV/testdata" >> /etc/profile \ No newline at end of file diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 48094df402..726fc0d10d 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -8,3 +8,11 @@ elseif(NOT WIN32) install(FILES ${HAAR_CASCADES} DESTINATION share/OpenCV/haarcascades COMPONENT libs) install(FILES ${LBP_CASCADES} DESTINATION share/OpenCV/lbpcascades COMPONENT libs) endif() + +if (OPENCV_TEST_DATA_PATH) + if(ANDROID) + install(FILES ${OPENCV_TEST_DATA_PATH} DESTINATION sdk/etc/testdata COMPONENT tests) + elseif(NOT WIN32) + install(FILES ${OPENCV_TEST_DATA_PATH} DESTINATION share/OpenCV/testdata COMPONENT tests) + endif() +endif() \ No newline at end of file diff --git a/samples/c/CMakeLists.txt b/samples/c/CMakeLists.txt index ab6e15dbf2..aca8886f09 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/c/CMakeLists.txt @@ -53,7 +53,13 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - install(FILES ${C_SAMPLES} - DESTINATION share/OpenCV/samples/c - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + if (ANDROID) + install(FILES ${C_SAMPLES} + DESTINATION samples/native/c + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + else() + install(FILES ${C_SAMPLES} + DESTINATION share/OpenCV/samples/c + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + endif() endif () diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index e0842d9e4a..6ccc75a747 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -90,7 +90,13 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - install(FILES ${C_SAMPLES} - DESTINATION share/OpenCV/samples/cpp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + if (ANDROID) + install(FILES ${C_SAMPLES} + DESTINATION samples/native/cpp + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + else() + install(FILES ${C_SAMPLES} + DESTINATION share/OpenCV/samples/cpp + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + endif() endif() diff --git a/samples/gpu/CMakeLists.txt b/samples/gpu/CMakeLists.txt index 7093cf5d19..226869aaa1 100644 --- a/samples/gpu/CMakeLists.txt +++ b/samples/gpu/CMakeLists.txt @@ -82,7 +82,13 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - install(FILES ${install_list} - DESTINATION share/OpenCV/samples/${project} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + if(ANDROID) + install(FILES ${install_list} + DESTINATION samples/native/gpu + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + else() + install(FILES ${install_list} + DESTINATION share/OpenCV/samples/gpu + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + endif() endif() diff --git a/samples/gpu/performance/CMakeLists.txt b/samples/gpu/performance/CMakeLists.txt index 0b2346f91f..32dc002aec 100644 --- a/samples/gpu/performance/CMakeLists.txt +++ b/samples/gpu/performance/CMakeLists.txt @@ -30,5 +30,6 @@ if(INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB GPU_FILES performance/*.cpp performance/*.h) install(FILES ${GPU_FILES} DESTINATION share/OpenCV/samples/gpu/performance - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + COMPONENT samples) endif() diff --git a/samples/ocl/CMakeLists.txt b/samples/ocl/CMakeLists.txt index 4139211000..8889452a01 100644 --- a/samples/ocl/CMakeLists.txt +++ b/samples/ocl/CMakeLists.txt @@ -53,7 +53,13 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - install(FILES ${install_list} - DESTINATION share/OpenCV/samples/${project} - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + if(ANDROID) + install(FILES ${install_list} + DESTINATION samples/native/ocl + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + else() + install(FILES ${install_list} + DESTINATION share/OpenCV/samples/ocl + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) + endif() endif() From 39201e68e2649955f40936a1d07f2d1c64c560f5 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 27 Jan 2014 17:57:11 +0400 Subject: [PATCH 060/293] Code review notes fixed. Env setup for testing package implemented using /etc/profile.d; Variable with path for all native samples added; Path for test binaries and test data updated. --- CMakeLists.txt | 3 +++ cmake/OpenCVModule.cmake | 14 +++----------- cmake/OpenCVPackaging.cmake | 8 +++----- cmake/templates/postinst | 3 --- samples/c/CMakeLists.txt | 12 +++--------- samples/cpp/CMakeLists.txt | 12 +++--------- samples/gpu/CMakeLists.txt | 12 +++--------- samples/gpu/performance/CMakeLists.txt | 2 +- samples/ocl/CMakeLists.txt | 12 +++--------- 9 files changed, 22 insertions(+), 56 deletions(-) delete mode 100644 cmake/templates/postinst diff --git a/CMakeLists.txt b/CMakeLists.txt index 2adc320435..752c991b0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,6 +283,7 @@ if(ANDROID) set(OPENCV_3P_LIB_INSTALL_PATH sdk/native/3rdparty/libs/${ANDROID_NDK_ABI_NAME}) set(OPENCV_CONFIG_INSTALL_PATH sdk/native/jni) set(OPENCV_INCLUDE_INSTALL_PATH sdk/native/jni/include) + set(OPENCV_SAMPLES_SRC_INSTALL_PATH samples/native) else() set(LIBRARY_OUTPUT_PATH "${OpenCV_BINARY_DIR}/lib") set(3P_LIBRARY_OUTPUT_PATH "${OpenCV_BINARY_DIR}/3rdparty/lib${LIB_SUFFIX}") @@ -293,9 +294,11 @@ else() set(OPENCV_LIB_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}lib${LIB_SUFFIX}") endif() set(OPENCV_3P_LIB_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}staticlib${LIB_SUFFIX}") + set(OPENCV_SAMPLES_SRC_INSTALL_PATH samples/native) else() set(OPENCV_LIB_INSTALL_PATH lib${LIB_SUFFIX}) set(OPENCV_3P_LIB_INSTALL_PATH share/OpenCV/3rdparty/${OPENCV_LIB_INSTALL_PATH}) + set(OPENCV_SAMPLES_SRC_INSTALL_PATH share/OpenCV/samples) endif() set(OPENCV_INCLUDE_INSTALL_PATH "include") diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 0e1ee250e7..2328d89bda 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -712,11 +712,7 @@ function(ocv_add_perf_tests) # TODO: warn about unsatisfied dependencies endif(OCV_DEPENDENCIES_FOUND) if(INSTALL_TESTS) - if(ANDROID) - install(TARGETS ${the_target} RUNTIME DESTINATION sdk/etc/bin COMPONENT tests) - elseif(NOT WIN32) - install(TARGETS ${the_target} RUNTIME DESTINATION share/OpenCV/bin COMPONENT tests) - endif() + install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT tests) endif() endif() endfunction() @@ -773,11 +769,7 @@ function(ocv_add_accuracy_tests) endif(OCV_DEPENDENCIES_FOUND) if(INSTALL_TESTS) - if(ANDROID) - install(TARGETS ${the_target} RUNTIME DESTINATION sdk/etc/bin COMPONENT tests) - elseif(NOT WIN32) - install(TARGETS ${the_target} RUNTIME DESTINATION share/OpenCV/bin COMPONENT tests) - endif() + install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT tests) endif() endif() endfunction() @@ -819,7 +811,7 @@ function(ocv_add_samples) if(INSTALL_C_EXAMPLES AND NOT WIN32 AND EXISTS "${samples_path}") file(GLOB sample_files "${samples_path}/*") install(FILES ${sample_files} - DESTINATION share/OpenCV/samples/${module_id} + DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/${module_id} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() endfunction() diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index 32d5c5da7f..0117873776 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -58,9 +58,9 @@ set(CPACK_DEBIAN_PACKAGE_SECTION "libs") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://opencv.org") if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH) set(prefix "${CMAKE_INSTALL_PREFIX}") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/postinst" - "${CMAKE_BINARY_DIR}/junk/postinst" @ONLY IMMEDIATE) - set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/junk/postinst") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_testing.sh.in" + "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" @ONLY IMMEDIATE) + install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" DESTINATION /etc/profile.d/ COMPONENT tests) endif() #depencencies @@ -70,9 +70,7 @@ set(CPACK_COMPONENT_dev_DEPENDS libs) set(CPACK_COMPONENT_docs_DEPENDS libs) set(CPACK_COMPONENT_java_DEPENDS libs) set(CPACK_COMPONENT_python_DEPENDS libs) -if(INSTALL_TESTS) set(CPACK_COMPONENT_tests_DEPENDS libs) -endif() if(HAVE_CUDA) string(REPLACE "." "-" cuda_version_suffix ${CUDA_VERSION}) diff --git a/cmake/templates/postinst b/cmake/templates/postinst deleted file mode 100644 index f6763781b6..0000000000 --- a/cmake/templates/postinst +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "export OPENCV_TEST_DATA_PATH=@prefix@/share/OpenCV/testdata" >> /etc/profile \ No newline at end of file diff --git a/samples/c/CMakeLists.txt b/samples/c/CMakeLists.txt index aca8886f09..6d374e7443 100644 --- a/samples/c/CMakeLists.txt +++ b/samples/c/CMakeLists.txt @@ -53,13 +53,7 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - if (ANDROID) - install(FILES ${C_SAMPLES} - DESTINATION samples/native/c - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - else() - install(FILES ${C_SAMPLES} - DESTINATION share/OpenCV/samples/c - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - endif() + install(FILES ${C_SAMPLES} + DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/c + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif () diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 6ccc75a747..b21fe86996 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -90,13 +90,7 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB C_SAMPLES *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - if (ANDROID) - install(FILES ${C_SAMPLES} - DESTINATION samples/native/cpp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - else() - install(FILES ${C_SAMPLES} - DESTINATION share/OpenCV/samples/cpp - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - endif() + install(FILES ${C_SAMPLES} + DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/cpp + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/gpu/CMakeLists.txt b/samples/gpu/CMakeLists.txt index 226869aaa1..8fa539473b 100644 --- a/samples/gpu/CMakeLists.txt +++ b/samples/gpu/CMakeLists.txt @@ -82,13 +82,7 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - if(ANDROID) - install(FILES ${install_list} - DESTINATION samples/native/gpu - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - else() - install(FILES ${install_list} - DESTINATION share/OpenCV/samples/gpu - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - endif() + install(FILES ${install_list} + DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/gpu + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/gpu/performance/CMakeLists.txt b/samples/gpu/performance/CMakeLists.txt index 32dc002aec..de0feadd26 100644 --- a/samples/gpu/performance/CMakeLists.txt +++ b/samples/gpu/performance/CMakeLists.txt @@ -29,7 +29,7 @@ endif() if(INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB GPU_FILES performance/*.cpp performance/*.h) install(FILES ${GPU_FILES} - DESTINATION share/OpenCV/samples/gpu/performance + DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/gpu/performance PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() diff --git a/samples/ocl/CMakeLists.txt b/samples/ocl/CMakeLists.txt index 8889452a01..7fc20fd356 100644 --- a/samples/ocl/CMakeLists.txt +++ b/samples/ocl/CMakeLists.txt @@ -53,13 +53,7 @@ endif() if (INSTALL_C_EXAMPLES AND NOT WIN32) file(GLOB install_list *.c *.cpp *.jpg *.png *.data makefile.* build_all.sh *.dsp *.cmd ) - if(ANDROID) - install(FILES ${install_list} - DESTINATION samples/native/ocl - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - else() - install(FILES ${install_list} - DESTINATION share/OpenCV/samples/ocl - PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) - endif() + install(FILES ${install_list} + DESTINATION ${OPENCV_SAMPLES_SRC_INSTALL_PATH}/ocl + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT samples) endif() From eb9d7c4dd54eea87950d98b843b349db7a95c951 Mon Sep 17 00:00:00 2001 From: Seunghoon Park Date: Mon, 27 Jan 2014 20:57:40 -0500 Subject: [PATCH 061/293] fixing bug #3345. use norm to make sure two matrices are the same. --- modules/imgproc/test/test_filter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index ac678e83a6..c860a6f114 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -1914,5 +1914,7 @@ TEST(Imgproc_Blur, borderTypes) blur(src_roi, dst, kernelSize, Point(-1, -1), BORDER_REPLICATE); Mat expected_dst = (Mat_(3, 3) << 170, 113, 170, 113, 28, 113, 170, 113, 170); - EXPECT_EQ(9 * 255, cv::sum(expected_dst == dst).val[0]); + EXPECT_EQ(expected_dst.type(), dst.type()); + EXPECT_EQ(expected_dst.size(), dst.size()); + EXPECT_DOUBLE_EQ(0.0, cvtest::norm(expected_dst, dst, NORM_INF)); } From c41e8006c7e9a3e796b5f78d3bfc5a97a9e87c4c Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 28 Jan 2014 10:28:00 +0400 Subject: [PATCH 062/293] fix #3477: CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING property is not supported by all VideoCapture backends. Some backends can return 0.0 or -1.0. --- modules/java/generator/src/cpp/VideoCapture.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/java/generator/src/cpp/VideoCapture.cpp b/modules/java/generator/src/cpp/VideoCapture.cpp index a9d0a56c1c..e4a8bc25a9 100644 --- a/modules/java/generator/src/cpp/VideoCapture.cpp +++ b/modules/java/generator/src/cpp/VideoCapture.cpp @@ -329,7 +329,10 @@ JNIEXPORT jstring JNICALL Java_org_opencv_highgui_VideoCapture_n_1getSupportedPr VideoCapture* me = (VideoCapture*) self; //TODO: check for NULL union {double prop; const char* name;} u; u.prop = me->get(CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING); - return env->NewStringUTF(u.name); + // VideoCapture::get can return 0.0 or -1.0 if it doesn't support + // CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING + if (u.prop != 0.0 && u.prop != -1.0) + return env->NewStringUTF(u.name); } catch(const std::exception &e) { throwJavaException(env, &e, method_name); } catch (...) { From d9dc5ffa918639e5d8be76644aef4385def13688 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 28 Jan 2014 14:05:26 +0400 Subject: [PATCH 063/293] Multiple fixes for tests deb package build. Added opencv_testing.sh.in file; opencv_testing.sh installation guarded by OS check. --- CMakeLists.txt | 7 +++++++ cmake/OpenCVPackaging.cmake | 6 ------ cmake/templates/opencv_testing.sh.in | 2 ++ data/CMakeLists.txt | 6 +++--- 4 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 cmake/templates/opencv_testing.sh.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 752c991b0d..4e576ea4fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -561,6 +561,13 @@ include(cmake/OpenCVGenConfig.cmake) # Generate Info.plist for the IOS framework include(cmake/OpenCVGenInfoPlist.cmake) +# Generate environment setup file +if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH AND UNIX AND NOT ANDROID) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_testing.sh.in" + "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" @ONLY IMMEDIATE) + install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" DESTINATION /etc/profile.d/ COMPONENT tests) +endif() + # ---------------------------------------------------------------------------- # Summary: # ---------------------------------------------------------------------------- diff --git a/cmake/OpenCVPackaging.cmake b/cmake/OpenCVPackaging.cmake index 0117873776..91f5940960 100644 --- a/cmake/OpenCVPackaging.cmake +++ b/cmake/OpenCVPackaging.cmake @@ -56,12 +56,6 @@ set(CPACK_DEB_COMPONENT_INSTALL TRUE) set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") set(CPACK_DEBIAN_PACKAGE_SECTION "libs") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://opencv.org") -if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH) - set(prefix "${CMAKE_INSTALL_PREFIX}") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_testing.sh.in" - "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" @ONLY IMMEDIATE) - install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" DESTINATION /etc/profile.d/ COMPONENT tests) -endif() #depencencies set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS TRUE) diff --git a/cmake/templates/opencv_testing.sh.in b/cmake/templates/opencv_testing.sh.in new file mode 100644 index 0000000000..3140136eb2 --- /dev/null +++ b/cmake/templates/opencv_testing.sh.in @@ -0,0 +1,2 @@ +# Environment setup for OpenCV testing +export OPENCV_TEST_DATA_PATH=@CMAKE_INSTALL_PREFIX@/share/OpenCV/testdata \ No newline at end of file diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 726fc0d10d..2f10c82f64 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -9,10 +9,10 @@ elseif(NOT WIN32) install(FILES ${LBP_CASCADES} DESTINATION share/OpenCV/lbpcascades COMPONENT libs) endif() -if (OPENCV_TEST_DATA_PATH) +if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH) if(ANDROID) - install(FILES ${OPENCV_TEST_DATA_PATH} DESTINATION sdk/etc/testdata COMPONENT tests) + install(DIRECTORY ${OPENCV_TEST_DATA_PATH} DESTINATION sdk/etc/testdata COMPONENT tests) elseif(NOT WIN32) - install(FILES ${OPENCV_TEST_DATA_PATH} DESTINATION share/OpenCV/testdata COMPONENT tests) + install(DIRECTORY ${OPENCV_TEST_DATA_PATH} DESTINATION share/OpenCV/testdata COMPONENT tests) endif() endif() \ No newline at end of file From d45350a06a287a8ab8a812659d5afea898ffe95a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 29 Jan 2014 12:01:06 +0400 Subject: [PATCH 064/293] opencv_run_all_tests.sh script added to -tests package. --- CMakeLists.txt | 13 ++++++++++-- cmake/OpenCVModule.cmake | 4 ++-- cmake/templates/opencv_run_all_tests.sh.in | 24 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 cmake/templates/opencv_run_all_tests.sh.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e576ea4fb..0d342cfac9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,6 +275,9 @@ endif() set(OPENCV_SAMPLES_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}samples") set(OPENCV_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}bin") +if(NOT OPENCV_TEST_INSTALL_PATH) + set(OPENCV_TEST_INSTALL_PATH "${OPENCV_BIN_INSTALL_PATH}") +endif() if(ANDROID) set(LIBRARY_OUTPUT_PATH "${OpenCV_BINARY_DIR}/lib/${ANDROID_NDK_ABI_NAME}") @@ -564,8 +567,14 @@ include(cmake/OpenCVGenInfoPlist.cmake) # Generate environment setup file if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH AND UNIX AND NOT ANDROID) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_testing.sh.in" - "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" @ONLY IMMEDIATE) - install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" DESTINATION /etc/profile.d/ COMPONENT tests) + "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" @ONLY) + install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" + DESTINATION /etc/profile.d/ COMPONENT tests) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_run_all_tests.sh.in" + "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" @ONLY) + install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE + DESTINATION ${OPENCV_TEST_INSTALL_PATH} COMPONENT tests) endif() # ---------------------------------------------------------------------------- diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 2328d89bda..86a9d0c83c 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -712,7 +712,7 @@ function(ocv_add_perf_tests) # TODO: warn about unsatisfied dependencies endif(OCV_DEPENDENCIES_FOUND) if(INSTALL_TESTS) - install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT tests) + install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_TEST_INSTALL_PATH} COMPONENT tests) endif() endif() endfunction() @@ -769,7 +769,7 @@ function(ocv_add_accuracy_tests) endif(OCV_DEPENDENCIES_FOUND) if(INSTALL_TESTS) - install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT tests) + install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_TEST_INSTALL_PATH} COMPONENT tests) endif() endif() endfunction() diff --git a/cmake/templates/opencv_run_all_tests.sh.in b/cmake/templates/opencv_run_all_tests.sh.in new file mode 100644 index 0000000000..c8bb0297a1 --- /dev/null +++ b/cmake/templates/opencv_run_all_tests.sh.in @@ -0,0 +1,24 @@ +#!/bin/sh + +OPENCV_TEST_PATH=@OPENCV_TEST_INSTALL_PATH@ +export OPENCV_TEST_DATA_PATH=@CMAKE_INSTALL_PREFIX@/share/OpenCV/testdata + +SUMMARY_STATUS=0 +for t in "$OPENCV_TEST_PATH/"opencv_test_* "$OPENCV_TEST_PATH/"opencv_perf_*; +do + "$t" --perf_min_samples=1 --perf_force_samples=1 --gtest_output=xml:$t-`date --rfc-3339=date`.xml + TEST_STATUS=$? + if [ $TEST_STATUS -ne 0 ]; then + SUMMARY_STATUS=$TEST_STATUS + fi +done + +rm -f /tmp/__opencv_temp.* + +if [ $SUMMARY_STATUS -eq 0 ]; then + echo "All OpenCV tests finished successfully" +else + echo "OpenCV tests finished with status $SUMMARY_STATUS" +fi + +return $SUMMARY_STATUS \ No newline at end of file From 0dad2876e29e2173505bdb36bc5adbeb9fea56f0 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Wed, 29 Jan 2014 19:34:02 +0400 Subject: [PATCH 065/293] Removed all use of the obsolete IMMEDIATE parameter to configure_file. It's not documented, and it does nothing unless CMake 2.0 compatibility is enabled (and it isn't): https://github.com/Kitware/CMake/blob/v2.6.0/Source/cmConfigureFileCommand.cxx --- cmake/OpenCVExtraTargets.cmake | 2 +- cmake/OpenCVGenAndroidMK.cmake | 4 ++-- cmake/OpenCVGenConfig.cmake | 12 ++++++------ cmake/OpenCVGenPkgconfig.cmake | 2 +- modules/java/CMakeLists.txt | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cmake/OpenCVExtraTargets.cmake b/cmake/OpenCVExtraTargets.cmake index b4d339155a..ecb2a3b36a 100644 --- a/cmake/OpenCVExtraTargets.cmake +++ b/cmake/OpenCVExtraTargets.cmake @@ -4,7 +4,7 @@ CONFIGURE_FILE( "${OpenCV_SOURCE_DIR}/cmake/templates/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) + @ONLY) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") if(ENABLE_SOLUTION_FOLDERS) diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index 45193a2732..447aea2436 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -105,7 +105,7 @@ if(ANDROID) set(OPENCV_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/lib/\$(OPENCV_TARGET_ARCH_ABI)") set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/3rdparty/lib/\$(OPENCV_TARGET_ARCH_ABI)") - configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/OpenCV.mk" IMMEDIATE @ONLY) + configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/OpenCV.mk" @ONLY) # ------------------------------------------------------------------------------------------- # Part 2/2: ${BIN_DIR}/unix-install/OpenCV.mk -> For use with "make install" @@ -115,6 +115,6 @@ if(ANDROID) set(OPENCV_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../libs/\$(OPENCV_TARGET_ARCH_ABI)") set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../3rdparty/libs/\$(OPENCV_TARGET_ARCH_ABI)") - configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk" IMMEDIATE @ONLY) + configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk" @ONLY) install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk DESTINATION ${OPENCV_CONFIG_INSTALL_PATH} COMPONENT dev) endif(ANDROID) diff --git a/cmake/OpenCVGenConfig.cmake b/cmake/OpenCVGenConfig.cmake index 18411e8786..cdf418ec82 100644 --- a/cmake/OpenCVGenConfig.cmake +++ b/cmake/OpenCVGenConfig.cmake @@ -83,9 +83,9 @@ endif() export(TARGETS ${OpenCVModules_TARGETS} FILE "${CMAKE_BINARY_DIR}/OpenCVModules${modules_file_suffix}.cmake") -configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/OpenCVConfig.cmake" IMMEDIATE @ONLY) +configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/OpenCVConfig.cmake" @ONLY) #support for version checking when finding opencv. find_package(OpenCV 2.3.1 EXACT) should now work. -configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig-version.cmake.in" "${CMAKE_BINARY_DIR}/OpenCVConfig-version.cmake" IMMEDIATE @ONLY) +configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig-version.cmake.in" "${CMAKE_BINARY_DIR}/OpenCVConfig-version.cmake" @ONLY) # -------------------------------------------------------------------------------------------- # Part 2/3: ${BIN_DIR}/unix-install/OpenCVConfig.cmake -> For use *with* "make install" @@ -98,8 +98,8 @@ if(INSTALL_TO_MANGLED_PATHS) set(OpenCV_3RDPARTY_LIB_DIRS_CONFIGCMAKE "\"\${OpenCV_INSTALL_PATH}/${OpenCV_3RDPARTY_LIB_DIRS_CONFIGCMAKE}\"") endif() -configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig.cmake" IMMEDIATE @ONLY) -configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig-version.cmake.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig-version.cmake" IMMEDIATE @ONLY) +configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig.cmake" @ONLY) +configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig-version.cmake.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCVConfig-version.cmake" @ONLY) if(UNIX) # ANDROID configuration is created here also #http://www.vtk.org/Wiki/CMake/Tutorials/Packaging reference @@ -131,8 +131,8 @@ if(WIN32) set(OpenCV2_INCLUDE_DIRS_CONFIGCMAKE "\"\"") exec_program(mkdir ARGS "-p \"${CMAKE_BINARY_DIR}/win-install/\"" OUTPUT_VARIABLE RET_VAL) - configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" IMMEDIATE @ONLY) - configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig-version.cmake.in" "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig-version.cmake" IMMEDIATE @ONLY) + configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" @ONLY) + configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCVConfig-version.cmake.in" "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig-version.cmake" @ONLY) if(BUILD_SHARED_LIBS) install(FILES "${CMAKE_BINARY_DIR}/win-install/OpenCVConfig.cmake" DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}lib" COMPONENT dev) install(EXPORT OpenCVModules DESTINATION "${OpenCV_INSTALL_BINARIES_PREFIX}lib" FILE OpenCVModules${modules_file_suffix}.cmake COMPONENT dev) diff --git a/cmake/OpenCVGenPkgconfig.cmake b/cmake/OpenCVGenPkgconfig.cmake index 13b1e44970..fa57db9d3c 100644 --- a/cmake/OpenCVGenPkgconfig.cmake +++ b/cmake/OpenCVGenPkgconfig.cmake @@ -78,7 +78,7 @@ else() endif() configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/opencv-XXX.pc.in" "${CMAKE_BINARY_DIR}/unix-install/${OPENCV_PC_FILE_NAME}" - @ONLY IMMEDIATE) + @ONLY) if(UNIX AND NOT ANDROID) install(FILES ${CMAKE_BINARY_DIR}/unix-install/${OPENCV_PC_FILE_NAME} DESTINATION ${OPENCV_LIB_INSTALL_PATH}/pkgconfig COMPONENT dev) diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt index 198048ebe2..1ef2a1208e 100644 --- a/modules/java/CMakeLists.txt +++ b/modules/java/CMakeLists.txt @@ -266,7 +266,7 @@ if(ANDROID) else(ANDROID) set(JAR_NAME opencv-${LIB_NAME_SUFIX}.jar) set(JAR_FILE "${OpenCV_BINARY_DIR}/bin/${JAR_NAME}") - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build.xml.in" "${OpenCV_BINARY_DIR}/build.xml" IMMEDIATE @ONLY) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build.xml.in" "${OpenCV_BINARY_DIR}/build.xml" @ONLY) list(APPEND step3_depends "${OpenCV_BINARY_DIR}/build.xml") add_custom_command(OUTPUT "${JAR_FILE}" "${JAR_FILE}.dephelper" From e7e63fac6c3eaa65a8eb0926c7c9557f0614ab03 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 30 Jan 2014 01:14:02 +0400 Subject: [PATCH 066/293] eliminated possible memory leak --- modules/core/test/test_math.cpp | 36 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/modules/core/test/test_math.cpp b/modules/core/test/test_math.cpp index 3847afce6e..a572cd0d92 100644 --- a/modules/core/test/test_math.cpp +++ b/modules/core/test/test_math.cpp @@ -2392,16 +2392,14 @@ TYPED_TEST_P(Core_CheckRange, Negative) double min_bound = 4.5; double max_bound = 16.0; - TypeParam data[] = {5, 10, 15, 4, 10 ,2, 8, 12, 14}; + TypeParam data[] = {5, 10, 15, 4, 10, 2, 8, 12, 14}; cv::Mat src = cv::Mat(3,3, cv::DataDepth::value, data); - cv::Point* bad_pt = new cv::Point(0, 0); + cv::Point bad_pt(0, 0); - ASSERT_FALSE(checkRange(src, true, bad_pt, min_bound, max_bound)); - ASSERT_EQ(bad_pt->x,0); - ASSERT_EQ(bad_pt->y,1); - - delete bad_pt; + ASSERT_FALSE(checkRange(src, true, &bad_pt, min_bound, max_bound)); + ASSERT_EQ(bad_pt.x, 0); + ASSERT_EQ(bad_pt.y, 1); } TYPED_TEST_P(Core_CheckRange, Positive) @@ -2409,16 +2407,14 @@ TYPED_TEST_P(Core_CheckRange, Positive) double min_bound = -1; double max_bound = 16.0; - TypeParam data[] = {5, 10, 15, 4, 10 ,2, 8, 12, 14}; + TypeParam data[] = {5, 10, 15, 4, 10, 2, 8, 12, 14}; cv::Mat src = cv::Mat(3,3, cv::DataDepth::value, data); - cv::Point* bad_pt = new cv::Point(0, 0); + cv::Point bad_pt(0, 0); - ASSERT_TRUE(checkRange(src, true, bad_pt, min_bound, max_bound)); - ASSERT_EQ(bad_pt->x,0); - ASSERT_EQ(bad_pt->y,0); - - delete bad_pt; + ASSERT_TRUE(checkRange(src, true, &bad_pt, min_bound, max_bound)); + ASSERT_EQ(bad_pt.x, 0); + ASSERT_EQ(bad_pt.y, 0); } TYPED_TEST_P(Core_CheckRange, Bounds) @@ -2426,16 +2422,14 @@ TYPED_TEST_P(Core_CheckRange, Bounds) double min_bound = 24.5; double max_bound = 1.0; - TypeParam data[] = {5, 10, 15, 4, 10 ,2, 8, 12, 14}; + TypeParam data[] = {5, 10, 15, 4, 10, 2, 8, 12, 14}; cv::Mat src = cv::Mat(3,3, cv::DataDepth::value, data); - cv::Point* bad_pt = new cv::Point(0, 0); + cv::Point bad_pt(0, 0); - ASSERT_FALSE(checkRange(src, true, bad_pt, min_bound, max_bound)); - ASSERT_EQ(bad_pt->x,0); - ASSERT_EQ(bad_pt->y,0); - - delete bad_pt; + ASSERT_FALSE(checkRange(src, true, &bad_pt, min_bound, max_bound)); + ASSERT_EQ(bad_pt.x, 0); + ASSERT_EQ(bad_pt.y, 0); } TYPED_TEST_P(Core_CheckRange, Zero) From c8150436073a4c25ec4f4273b80c1b76201b8be1 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 30 Jan 2014 11:08:49 +0400 Subject: [PATCH 067/293] Android toolchain file sync with original project. Original project: https://github.com/taka-no-me/android-cmake/ Revision: 5db45cfb87fec180b74963d3680dd60d4d8d8c3a --- platforms/android/android.toolchain.cmake | 123 +++++++++++++--------- 1 file changed, 75 insertions(+), 48 deletions(-) diff --git a/platforms/android/android.toolchain.cmake b/platforms/android/android.toolchain.cmake index 68b256fbd0..457164a1ee 100644 --- a/platforms/android/android.toolchain.cmake +++ b/platforms/android/android.toolchain.cmake @@ -1,5 +1,5 @@ # Copyright (c) 2010-2011, Ethan Rublee -# Copyright (c) 2011-2013, Andrey Kamaev +# Copyright (c) 2011-2014, Andrey Kamaev # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -12,9 +12,9 @@ # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # -# 3. The name of the copyright holders may be used to endorse or promote -# products derived from this software without specific prior written -# permission. +# 3. Neither the name of the copyright holder 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 @@ -29,12 +29,12 @@ # POSSIBILITY OF SUCH DAMAGE. # ------------------------------------------------------------------------------ -# Android CMake toolchain file, for use with the Android NDK r5-r8 +# Android CMake toolchain file, for use with the Android NDK r5-r9 # Requires cmake 2.6.3 or newer (2.8.5 or newer is recommended). # See home page: https://github.com/taka-no-me/android-cmake # # The file is mantained by the OpenCV project. The latest version can be get at -# https://github.com/Itseez/opencv/tree/master/platforms/android/android.toolchain.cmake +# http://code.opencv.org/projects/opencv/repository/revisions/master/changes/android/android.toolchain.cmake # # Usage Linux: # $ export ANDROID_NDK=/absolute/path/to/the/android-ndk @@ -87,8 +87,7 @@ # "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP. # "x86" - matches to the NDK ABI with the same name. # See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. -# "mips" - matches to the NDK ABI with the same name -# (It is not tested on real devices by the authos of this toolchain) +# "mips" - matches to the NDK ABI with the same name. # See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. # # ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. @@ -292,6 +291,16 @@ # - April 2013 # [+] support non-release NDK layouts (from Linaro git and Android git) # [~] automatically detect if explicit link to crtbegin_*.o is needed +# - June 2013 +# [~] fixed stl include path for standalone toolchain made by NDK >= r8c +# - July 2013 +# [+] updated for NDK r9 +# - November 2013 +# [+] updated for NDK r9b +# - December 2013 +# [+] updated for NDK r9c +# - January 2014 +# [~] fix copying of shared STL # ------------------------------------------------------------------------------ cmake_minimum_required( VERSION 2.6.3 ) @@ -318,7 +327,7 @@ set( CMAKE_SYSTEM_VERSION 1 ) # rpath makes low sence for Android set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) -set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) +set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) if(NOT DEFINED ANDROID_NDK_SEARCH_PATHS) if( CMAKE_HOST_WIN32 ) file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) @@ -464,7 +473,7 @@ endif() # detect current host platform -if( NOT DEFINED ANDROID_NDK_HOST_X64 AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64") +if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) ) set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) mark_as_advanced( ANDROID_NDK_HOST_X64 ) endif() @@ -484,9 +493,7 @@ else() message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) endif() -# CMAKE_HOST_SYSTEM_PROCESSOR on MacOS X always says i386 on Intel platform -# So we do not trust ANDROID_NDK_HOST_X64 on Apple hosts -if( NOT ANDROID_NDK_HOST_X64 AND NOT CMAKE_HOST_APPLE) +if( NOT ANDROID_NDK_HOST_X64 ) set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) endif() @@ -634,30 +641,27 @@ endif() macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath ) foreach( __toolchain ${${__availableToolchainsLst}} ) - # Skip renderscript folder. It's not C++ toolchain - if (NOT ${__toolchain} STREQUAL "renderscript") - if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) - string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) - else() - set( __gcc_toolchain "${__toolchain}" ) - endif() - __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) - if( __machine ) - string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) - if( __machine MATCHES i686 ) - set( __arch "x86" ) - elseif( __machine MATCHES arm ) - set( __arch "arm" ) - elseif( __machine MATCHES mipsel ) - set( __arch "mipsel" ) - endif() - list( APPEND __availableToolchainMachines "${__machine}" ) - list( APPEND __availableToolchainArchs "${__arch}" ) - list( APPEND __availableToolchainCompilerVersions "${__version}" ) - list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) - endif() - unset( __gcc_toolchain ) + if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) + string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) + else() + set( __gcc_toolchain "${__toolchain}" ) endif() + __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) + if( __machine ) + string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) + if( __machine MATCHES i686 ) + set( __arch "x86" ) + elseif( __machine MATCHES arm ) + set( __arch "arm" ) + elseif( __machine MATCHES mipsel ) + set( __arch "mipsel" ) + endif() + list( APPEND __availableToolchainMachines "${__machine}" ) + list( APPEND __availableToolchainArchs "${__arch}" ) + list( APPEND __availableToolchainCompilerVersions "${__version}" ) + list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) + endif() + unset( __gcc_toolchain ) endforeach() endmacro() @@ -687,6 +691,7 @@ if( BUILD_WITH_ANDROID_NDK ) endif() __LIST_FILTER( __availableToolchainsLst "^[.]" ) __LIST_FILTER( __availableToolchainsLst "llvm" ) + __LIST_FILTER( __availableToolchainsLst "renderscript" ) __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) @@ -975,7 +980,11 @@ if( BUILD_WITH_STANDALONE_TOOLCHAIN ) set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) if( NOT ANDROID_STL STREQUAL "none" ) - set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" ) + if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" ) + # old location ( pre r8c ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) + endif() if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) @@ -1130,15 +1139,7 @@ endif() # case of shared STL linkage if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl ) string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" ) - if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" ) - get_filename_component( __libstlname "${__libstl}" NAME ) - execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) - if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") - message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) - endif() - unset( __fileCopyProcess ) - unset( __libstlname ) - endif() + # TODO: check if .so file exists before the renaming endif() @@ -1503,7 +1504,8 @@ endif() # global includes and link directories include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} ) -link_directories( "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ) +get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning +link_directories( "${__android_install_path}" ) # detect if need link crtbegin_so.o explicitly if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK ) @@ -1555,6 +1557,18 @@ if(NOT _CMAKE_IN_TRY_COMPILE) set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" ) endif() +# copy shaed stl library to build directory +if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" ) + get_filename_component( __libstlname "${__libstl}" NAME ) + execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) + if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") + message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) + endif() + unset( __fileCopyProcess ) + unset( __libstlname ) +endif() + + # set these global flags for cmake client scripts to change behavior set( ANDROID True ) set( BUILD_ANDROID True ) @@ -1663,6 +1677,19 @@ if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) endif() +# force cmake to produce / instead of \ in build commands for Ninja generator +if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) + # it is a bad hack after all + # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW + set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW + set( CMAKE_CROSSCOMPILING TRUE ) # stop recursion + enable_language( C ) + enable_language( CXX ) + # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it + unset( MINGW ) +endif() + + # set some obsolete variables for backward compatibility set( ANDROID_SET_OBSOLETE_VARIABLES ON CACHE BOOL "Define obsolete Andrid-specific cmake variables" ) mark_as_advanced( ANDROID_SET_OBSOLETE_VARIABLES ) @@ -1717,7 +1744,7 @@ endif() # BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used # ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform # ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86" or "mips" depending on ANDROID_ABI -# ANDROID_NDK_RELEASE : one of r5, r5b, r5c, r6, r6b, r7, r7b, r7c, r8, r8b, r8c, r8d, r8e; set only for NDK +# ANDROID_NDK_RELEASE : one of r5, r5b, r5c, r6, r6b, r7, r7b, r7c, r8, r8b, r8c, r8d, r8e, r9, r9b, r9c; set only for NDK # ANDROID_ARCH_NAME : "arm" or "x86" or "mips" depending on ANDROID_ABI # ANDROID_SYSROOT : path to the compiler sysroot # TOOL_OS_SUFFIX : "" or ".exe" depending on host platform From 063d8b421136b2ed0f537c663b89828f6a2b263c Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 31 Jan 2014 12:40:40 +0400 Subject: [PATCH 068/293] disable performance test for gpu generalized hough --- modules/gpu/perf/perf_imgproc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 4093b16e73..74426d7765 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -1825,7 +1825,7 @@ CV_FLAGS(GHMethod, GHT_POSITION, GHT_SCALE, GHT_ROTATION) DEF_PARAM_TEST(Method_Sz, GHMethod, cv::Size); -PERF_TEST_P(Method_Sz, ImgProc_GeneralizedHough, +PERF_TEST_P(Method_Sz, DISABLED_ImgProc_GeneralizedHough, Combine(Values(GHMethod(cv::GHT_POSITION), GHMethod(cv::GHT_POSITION | cv::GHT_SCALE), GHMethod(cv::GHT_POSITION | cv::GHT_ROTATION), GHMethod(cv::GHT_POSITION | cv::GHT_SCALE | cv::GHT_ROTATION)), GPU_TYPICAL_MAT_SIZES)) { From 87935f35600228a746d8a29cb1a5c108e710429d Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 31 Jan 2014 12:49:10 +0400 Subject: [PATCH 069/293] Highgui test output fixes. Useless output to console fixed; Test output files moved from cwd to temp folder. --- modules/highgui/test/test_ffmpeg.cpp | 2 +- modules/highgui/test/test_video_io.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/highgui/test/test_ffmpeg.cpp b/modules/highgui/test/test_ffmpeg.cpp index 30410eaab8..55bf952213 100644 --- a/modules/highgui/test/test_ffmpeg.cpp +++ b/modules/highgui/test/test_ffmpeg.cpp @@ -88,7 +88,7 @@ public: stringstream s; s << tag; - const string filename = "output_"+s.str()+".avi"; + const string filename = tempfile((s.str()+".avi").c_str()); try { diff --git a/modules/highgui/test/test_video_io.cpp b/modules/highgui/test/test_video_io.cpp index cf47b73a6f..755bcd0677 100644 --- a/modules/highgui/test/test_video_io.cpp +++ b/modules/highgui/test/test_video_io.cpp @@ -332,9 +332,7 @@ void CV_HighGuiTest::VideoTest(const string& dir, const cvtest::VideoFormat& fmt } } - printf("Before saved release for %s\n", tmp_name.c_str()); cvReleaseCapture( &saved ); - printf("After release\n"); ts->printf(ts->LOG, "end test function : ImagesVideo \n"); } From 3d261e8a010eda45908592d3b2caa76d939d342c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 31 Jan 2014 10:47:01 +0400 Subject: [PATCH 070/293] Reports path fix for opencv_run_all_tests.sh.in script. --- cmake/templates/opencv_run_all_tests.sh.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/templates/opencv_run_all_tests.sh.in b/cmake/templates/opencv_run_all_tests.sh.in index c8bb0297a1..b614900026 100644 --- a/cmake/templates/opencv_run_all_tests.sh.in +++ b/cmake/templates/opencv_run_all_tests.sh.in @@ -6,7 +6,8 @@ export OPENCV_TEST_DATA_PATH=@CMAKE_INSTALL_PREFIX@/share/OpenCV/testdata SUMMARY_STATUS=0 for t in "$OPENCV_TEST_PATH/"opencv_test_* "$OPENCV_TEST_PATH/"opencv_perf_*; do - "$t" --perf_min_samples=1 --perf_force_samples=1 --gtest_output=xml:$t-`date --rfc-3339=date`.xml + report="`basename "$t"`-`date --rfc-3339=date`.xml" + "$t" --perf_min_samples=1 --perf_force_samples=1 --gtest_output=xml:"$report" TEST_STATUS=$? if [ $TEST_STATUS -ne 0 ]; then SUMMARY_STATUS=$TEST_STATUS From 49731ad5303a714302ff053aaeb32900845304bf Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 31 Jan 2014 13:19:15 +0400 Subject: [PATCH 071/293] gpu test output files moved from cwd to temp folder --- modules/gpu/test/nvidia/TestHaarCascadeLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gpu/test/nvidia/TestHaarCascadeLoader.cpp b/modules/gpu/test/nvidia/TestHaarCascadeLoader.cpp index 42552295d2..1c0e691ba2 100644 --- a/modules/gpu/test/nvidia/TestHaarCascadeLoader.cpp +++ b/modules/gpu/test/nvidia/TestHaarCascadeLoader.cpp @@ -98,7 +98,7 @@ bool TestHaarCascadeLoader::process() NCV_SET_SKIP_COND(this->allocatorGPU.get()->isCounting()); NCV_SKIP_COND_BEGIN - const std::string testNvbinName = "test.nvbin"; + const std::string testNvbinName = cv::tempfile("test.nvbin"); ncvStat = ncvHaarLoadFromFile_host(this->cascadeName, haar, h_HaarStages, h_HaarNodes, h_HaarFeatures); ncvAssertReturn(ncvStat == NCV_SUCCESS, false); From 8401ed939524f2f65c1bbcb81ba5181851719d7c Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 31 Jan 2014 15:40:59 +0400 Subject: [PATCH 072/293] disable some gpu tests if library was built without CUFFT --- modules/gpu/perf/perf_imgproc.cpp | 4 ++++ modules/gpu/test/test_imgproc.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 74426d7765..1e598297a7 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -851,6 +851,8 @@ PERF_TEST_P(Sz_Depth_Cn, ImgProc_BlendLinear, } } +#ifdef HAVE_CUFFT + ////////////////////////////////////////////////////////////////////// // Convolve @@ -1085,6 +1087,8 @@ PERF_TEST_P(Sz_Flags, ImgProc_Dft, } } +#endif + ////////////////////////////////////////////////////////////////////// // CornerHarris diff --git a/modules/gpu/test/test_imgproc.cpp b/modules/gpu/test/test_imgproc.cpp index 811d1294cf..9ce32d12b8 100644 --- a/modules/gpu/test/test_imgproc.cpp +++ b/modules/gpu/test/test_imgproc.cpp @@ -563,6 +563,8 @@ INSTANTIATE_TEST_CASE_P(GPU_ImgProc, Blend, testing::Combine( testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), WHOLE_SUBMAT)); +#ifdef HAVE_CUFFT + //////////////////////////////////////////////////////// // Convolve @@ -1090,6 +1092,8 @@ GPU_TEST_P(Dft, R2CThenC2R) INSTANTIATE_TEST_CASE_P(GPU_ImgProc, Dft, ALL_DEVICES); +#endif + /////////////////////////////////////////////////////////////////////////////////////////////////////// // CornerHarris From e630be3890d91f84d0f2d825c755bd0c1d070918 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 31 Jan 2014 15:52:06 +0400 Subject: [PATCH 073/293] disable NPP for GpuMat methods and for copyMakeBorder --- .../include/opencv2/dynamicuda/dynamicuda.hpp | 61 +++++++++++++++++-- modules/gpu/src/imgproc.cpp | 6 ++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp b/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp index d4d0220e00..00f0873030 100644 --- a/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp +++ b/modules/dynamicuda/include/opencv2/dynamicuda/dynamicuda.hpp @@ -129,15 +129,20 @@ public: #if defined(USE_CUDA) -#define cudaSafeCall(expr) ___cudaSafeCall(expr, __FILE__, __LINE__, CV_Func) -#define nppSafeCall(expr) ___nppSafeCall(expr, __FILE__, __LINE__, CV_Func) +// Disable NPP for this file +//#define USE_NPP +#undef USE_NPP +#define cudaSafeCall(expr) ___cudaSafeCall(expr, __FILE__, __LINE__, CV_Func) inline void ___cudaSafeCall(cudaError_t err, const char *file, const int line, const char *func = "") { if (cudaSuccess != err) cv::gpu::error(cudaGetErrorString(err), file, line, func); } +#ifdef USE_NPP + +#define nppSafeCall(expr) ___nppSafeCall(expr, __FILE__, __LINE__, CV_Func) inline void ___nppSafeCall(int err, const char *file, const int line, const char *func = "") { if (err < 0) @@ -148,6 +153,8 @@ inline void ___nppSafeCall(int err, const char *file, const int line, const char } } +#endif + namespace cv { namespace gpu { namespace device { void copyToWithMask_gpu(PtrStepSzb src, PtrStepSzb dst, size_t elemSize1, int cn, PtrStepSzb mask, bool colorMask, cudaStream_t stream); @@ -173,6 +180,8 @@ template void kernelSetCaller(GpuMat& src, Scalar s, const GpuMat& cv::gpu::device::set_to_gpu(src, sf.val, mask, src.channels(), stream); } +#ifdef USE_NPP + template struct NPPTypeTraits; template<> struct NPPTypeTraits { typedef Npp8u npp_type; }; template<> struct NPPTypeTraits { typedef Npp8s npp_type; }; @@ -182,9 +191,13 @@ template<> struct NPPTypeTraits { typedef Npp32s npp_type; }; template<> struct NPPTypeTraits { typedef Npp32f npp_type; }; template<> struct NPPTypeTraits { typedef Npp64f npp_type; }; +#endif + ////////////////////////////////////////////////////////////////////////// // Convert +#ifdef USE_NPP + template struct NppConvertFunc { typedef typename NPPTypeTraits::npp_type src_t; @@ -232,9 +245,13 @@ template::func_ptr func> str } }; +#endif + ////////////////////////////////////////////////////////////////////////// // Set +#ifdef USE_NPP + template struct NppSetFunc { typedef typename NPPTypeTraits::npp_type src_t; @@ -339,9 +356,13 @@ template::func_ptr func> struct N } }; +#endif + ////////////////////////////////////////////////////////////////////////// // CopyMasked +#ifdef USE_NPP + template struct NppCopyMaskedFunc { typedef typename NPPTypeTraits::npp_type src_t; @@ -365,6 +386,8 @@ template::func_ptr func> struct N } }; +#endif + template static inline bool isAligned(const T* ptr, size_t size) { return reinterpret_cast(ptr) % size == 0; @@ -877,6 +900,8 @@ public: } typedef void (*func_t)(const GpuMat& src, GpuMat& dst, const GpuMat& mask, cudaStream_t stream); + +#ifdef USE_NPP static const func_t funcs[7][4] = { /* 8U */ {NppCopyMasked::call, cv::gpu::device::copyWithMask, NppCopyMasked::call, NppCopyMasked::call}, @@ -889,6 +914,9 @@ public: }; const func_t func = mask.channels() == src.channels() ? funcs[src.depth()][src.channels() - 1] : cv::gpu::device::copyWithMask; +#else + const func_t func = cv::gpu::device::copyWithMask; +#endif func(src, dst, mask, 0); } @@ -896,6 +924,8 @@ public: void convert(const GpuMat& src, GpuMat& dst) const { typedef void (*func_t)(const GpuMat& src, GpuMat& dst); + +#ifdef USE_NPP static const func_t funcs[7][7][4] = { { @@ -962,6 +992,7 @@ public: /* 64F -> 64F */ {0,0,0,0} } }; +#endif CV_Assert(src.depth() <= CV_64F && src.channels() <= 4); CV_Assert(dst.depth() <= CV_64F); @@ -980,8 +1011,12 @@ public: return; } +#ifdef USE_NPP const func_t func = funcs[src.depth()][dst.depth()][src.channels() - 1]; CV_DbgAssert(func != 0); +#else + const func_t func = cv::gpu::device::convertTo; +#endif func(src, dst); } @@ -1023,6 +1058,8 @@ public: } typedef void (*func_t)(GpuMat& src, Scalar s); + +#ifdef USE_NPP static const func_t funcs[7][4] = { {NppSet::call, cv::gpu::device::setTo , cv::gpu::device::setTo , NppSet::call}, @@ -1033,6 +1070,7 @@ public: {NppSet::call, cv::gpu::device::setTo , cv::gpu::device::setTo , NppSet::call}, {cv::gpu::device::setTo , cv::gpu::device::setTo , cv::gpu::device::setTo , cv::gpu::device::setTo } }; +#endif CV_Assert(m.depth() <= CV_64F && m.channels() <= 4); @@ -1042,14 +1080,22 @@ public: CV_Error(CV_StsUnsupportedFormat, "The device doesn't support double"); } +#ifdef USE_NPP + const func_t func = funcs[m.depth()][m.channels() - 1]; +#else + const func_t func = cv::gpu::device::setTo; +#endif + if (stream) cv::gpu::device::setTo(m, s, stream); else - funcs[m.depth()][m.channels() - 1](m, s); + func(m, s); } else { typedef void (*func_t)(GpuMat& src, Scalar s, const GpuMat& mask); + +#ifdef USE_NPP static const func_t funcs[7][4] = { {NppSetMask::call, cv::gpu::device::setTo, cv::gpu::device::setTo, NppSetMask::call}, @@ -1060,6 +1106,7 @@ public: {NppSetMask::call, cv::gpu::device::setTo, cv::gpu::device::setTo, NppSetMask::call}, {cv::gpu::device::setTo , cv::gpu::device::setTo, cv::gpu::device::setTo, cv::gpu::device::setTo } }; +#endif CV_Assert(m.depth() <= CV_64F && m.channels() <= 4); @@ -1069,10 +1116,16 @@ public: CV_Error(CV_StsUnsupportedFormat, "The device doesn't support double"); } +#ifdef USE_NPP + const func_t func = funcs[m.depth()][m.channels() - 1]; +#else + const func_t func = cv::gpu::device::setTo; +#endif + if (stream) cv::gpu::device::setTo(m, s, mask, stream); else - funcs[m.depth()][m.channels() - 1](m, s, mask); + func(m, s, mask); } } diff --git a/modules/gpu/src/imgproc.cpp b/modules/gpu/src/imgproc.cpp index 1904b6aad6..97adb685ff 100644 --- a/modules/gpu/src/imgproc.cpp +++ b/modules/gpu/src/imgproc.cpp @@ -244,6 +244,10 @@ void cv::gpu::reprojectImageTo3D(const GpuMat& disp, GpuMat& xyz, const Mat& Q, //////////////////////////////////////////////////////////////////////// // copyMakeBorder +// Disable NPP for this file +//#define USE_NPP +#undef USE_NPP + namespace cv { namespace gpu { namespace device { namespace imgproc @@ -279,6 +283,7 @@ void cv::gpu::copyMakeBorder(const GpuMat& src, GpuMat& dst, int top, int bottom cudaStream_t stream = StreamAccessor::getStream(s); +#ifdef USE_NPP if (borderType == BORDER_CONSTANT && (src.type() == CV_8UC1 || src.type() == CV_8UC4 || src.type() == CV_32SC1 || src.type() == CV_32FC1)) { NppiSize srcsz; @@ -328,6 +333,7 @@ void cv::gpu::copyMakeBorder(const GpuMat& src, GpuMat& dst, int top, int bottom cudaSafeCall( cudaDeviceSynchronize() ); } else +#endif { typedef void (*caller_t)(const PtrStepSzb& src, const PtrStepSzb& dst, int top, int left, int borderType, const Scalar& value, cudaStream_t stream); static const caller_t callers[6][4] = From dbce90692acd84fbf46bde4da4b1726049f42857 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 31 Jan 2014 16:10:37 +0400 Subject: [PATCH 074/293] disable gpu Canny and HoughCircles perf tests: it fails because driver terminates CUDA kernels after time out --- modules/gpu/perf/perf_imgproc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 74426d7765..f44b579ad1 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -672,7 +672,7 @@ PERF_TEST_P(Sz, ImgProc_ColumnSum, DEF_PARAM_TEST(Image_AppertureSz_L2gradient, string, int, bool); -PERF_TEST_P(Image_AppertureSz_L2gradient, ImgProc_Canny, +PERF_TEST_P(Image_AppertureSz_L2gradient, DISABLED_ImgProc_Canny, Combine(Values("perf/800x600.png", "perf/1280x1024.png", "perf/1680x1050.png"), Values(3, 5), Bool())) @@ -1773,7 +1773,7 @@ PERF_TEST_P(Image, ImgProc_HoughLinesP, DEF_PARAM_TEST(Sz_Dp_MinDist, cv::Size, float, float); -PERF_TEST_P(Sz_Dp_MinDist, ImgProc_HoughCircles, +PERF_TEST_P(Sz_Dp_MinDist, DISABLED_ImgProc_HoughCircles, Combine(GPU_TYPICAL_MAT_SIZES, Values(1.0f, 2.0f, 4.0f), Values(1.0f))) From e91bf95d5832e87aa70240c50f0bf7fcc587e8c8 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 31 Jan 2014 16:15:11 +0400 Subject: [PATCH 075/293] disable gpu Subtract_Array test: possible bug in CPU version --- modules/gpu/test/test_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gpu/test/test_core.cpp b/modules/gpu/test/test_core.cpp index 1edc69b971..2f1bbd7b2c 100644 --- a/modules/gpu/test/test_core.cpp +++ b/modules/gpu/test/test_core.cpp @@ -422,7 +422,7 @@ PARAM_TEST_CASE(Subtract_Array, cv::gpu::DeviceInfo, cv::Size, std::pair Date: Fri, 31 Jan 2014 16:20:45 +0400 Subject: [PATCH 076/293] disable gpu CvtColor.*2HSV tests: possible bug in CPU version --- modules/gpu/test/test_color.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/gpu/test/test_color.cpp b/modules/gpu/test/test_color.cpp index 3b4b326e4d..16f5fc84ea 100644 --- a/modules/gpu/test/test_color.cpp +++ b/modules/gpu/test/test_color.cpp @@ -840,7 +840,7 @@ GPU_TEST_P(CvtColor, YCrCb42RGBA) EXPECT_MAT_NEAR(dst_gold, dst, 1e-5); } -GPU_TEST_P(CvtColor, BGR2HSV) +GPU_TEST_P(CvtColor, DISABLED_BGR2HSV) { if (depth == CV_16U) return; @@ -856,7 +856,7 @@ GPU_TEST_P(CvtColor, BGR2HSV) EXPECT_MAT_NEAR(dst_gold, dst, depth == CV_32F ? 1e-2 : 1); } -GPU_TEST_P(CvtColor, RGB2HSV) +GPU_TEST_P(CvtColor, DISABLED_RGB2HSV) { if (depth == CV_16U) return; @@ -872,7 +872,7 @@ GPU_TEST_P(CvtColor, RGB2HSV) EXPECT_MAT_NEAR(dst_gold, dst, depth == CV_32F ? 1e-2 : 1); } -GPU_TEST_P(CvtColor, RGB2HSV4) +GPU_TEST_P(CvtColor, DISABLED_RGB2HSV4) { if (depth == CV_16U) return; @@ -896,7 +896,7 @@ GPU_TEST_P(CvtColor, RGB2HSV4) EXPECT_MAT_NEAR(dst_gold, h_dst, depth == CV_32F ? 1e-2 : 1); } -GPU_TEST_P(CvtColor, RGBA2HSV4) +GPU_TEST_P(CvtColor, DISABLED_RGBA2HSV4) { if (depth == CV_16U) return; From ede5b23753f9946ba107237a3eaae006afd82b4f Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 2 Feb 2014 18:24:21 +0400 Subject: [PATCH 077/293] rewrote perf tests for SURF --- modules/nonfree/perf/perf_main.cpp | 3 + .../{perf_surf.ocl.cpp => perf_surf_ocl.cpp} | 75 +++++++++++-------- .../src/{surf.ocl.cpp => surf_ocl.cpp} | 0 .../{test_surf.ocl.cpp => test_surf_ocl.cpp} | 0 4 files changed, 47 insertions(+), 31 deletions(-) rename modules/nonfree/perf/{perf_surf.ocl.cpp => perf_surf_ocl.cpp} (66%) rename modules/nonfree/src/{surf.ocl.cpp => surf_ocl.cpp} (100%) rename modules/nonfree/test/{test_surf.ocl.cpp => test_surf_ocl.cpp} (100%) diff --git a/modules/nonfree/perf/perf_main.cpp b/modules/nonfree/perf/perf_main.cpp index d5f4a1a512..03f9b71852 100644 --- a/modules/nonfree/perf/perf_main.cpp +++ b/modules/nonfree/perf/perf_main.cpp @@ -4,6 +4,9 @@ static const char * impls[] = { #ifdef HAVE_CUDA "cuda", +#endif +#ifdef HAVE_OPENCL + "ocl", #endif "plain" }; diff --git a/modules/nonfree/perf/perf_surf.ocl.cpp b/modules/nonfree/perf/perf_surf_ocl.cpp similarity index 66% rename from modules/nonfree/perf/perf_surf.ocl.cpp rename to modules/nonfree/perf/perf_surf_ocl.cpp index cc48aa28c5..4528b79268 100644 --- a/modules/nonfree/perf/perf_surf.ocl.cpp +++ b/modules/nonfree/perf/perf_surf_ocl.cpp @@ -57,55 +57,68 @@ typedef perf::TestBaseWithParam OCL_SURF; "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\ "stitching/a3.png" -PERF_TEST_P(OCL_SURF, DISABLED_with_data_transfer, testing::Values(SURF_IMAGES)) +#define OCL_TEST_CYCLE() for( ; startTimer(), next(); cv::ocl::finish(), stopTimer()) + +PERF_TEST_P(OCL_SURF, with_data_transfer, testing::Values(SURF_IMAGES)) { string filename = getDataPath(GetParam()); - Mat img = imread(filename, IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - SURF_OCL d_surf; - oclMat d_keypoints; - oclMat d_descriptors; - Mat cpu_kp; - Mat cpu_dp; + Mat src = imread(filename, IMREAD_GRAYSCALE); + ASSERT_FALSE(src.empty()); + Mat cpu_kp, cpu_dp; declare.time(60); - TEST_CYCLE() + if (getSelectedImpl() == "ocl") { - oclMat d_src(img); + SURF_OCL d_surf; + oclMat d_keypoints, d_descriptors; - d_surf(d_src, oclMat(), d_keypoints, d_descriptors); + OCL_TEST_CYCLE() + { + oclMat d_src(src); - d_keypoints.download(cpu_kp); - d_descriptors.download(cpu_dp); + d_surf(d_src, oclMat(), d_keypoints, d_descriptors); + + d_keypoints.download(cpu_kp); + d_descriptors.download(cpu_dp); + } + } + else if (getSelectedImpl() == "plain") + { + cv::SURF surf; + std::vector kp; + + TEST_CYCLE() surf(src, Mat(), kp, cpu_dp); } - SANITY_CHECK(cpu_kp, 1); - SANITY_CHECK(cpu_dp, 1); + SANITY_CHECK_NOTHING(); } -PERF_TEST_P(OCL_SURF, DISABLED_without_data_transfer, testing::Values(SURF_IMAGES)) +PERF_TEST_P(OCL_SURF, without_data_transfer, testing::Values(SURF_IMAGES)) { string filename = getDataPath(GetParam()); - Mat img = imread(filename, IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - SURF_OCL d_surf; - oclMat d_keypoints; - oclMat d_descriptors; - oclMat d_src(img); + Mat src = imread(filename, IMREAD_GRAYSCALE); + ASSERT_FALSE(src.empty()); + Mat cpu_kp, cpu_dp; declare.time(60); - TEST_CYCLE() d_surf(d_src, oclMat(), d_keypoints, d_descriptors); + if (getSelectedImpl() == "ocl") + { + SURF_OCL d_surf; + oclMat d_keypoints, d_descriptors, d_src(src); - Mat cpu_kp; - Mat cpu_dp; - d_keypoints.download(cpu_kp); - d_descriptors.download(cpu_dp); - SANITY_CHECK(cpu_kp, 1); - SANITY_CHECK(cpu_dp, 1); + OCL_TEST_CYCLE() d_surf(d_src, oclMat(), d_keypoints, d_descriptors); + } + else if (getSelectedImpl() == "plain") + { + cv::SURF surf; + std::vector kp; + + TEST_CYCLE() surf(src, Mat(), kp, cpu_dp); + } + + SANITY_CHECK_NOTHING(); } #endif // HAVE_OPENCV_OCL diff --git a/modules/nonfree/src/surf.ocl.cpp b/modules/nonfree/src/surf_ocl.cpp similarity index 100% rename from modules/nonfree/src/surf.ocl.cpp rename to modules/nonfree/src/surf_ocl.cpp diff --git a/modules/nonfree/test/test_surf.ocl.cpp b/modules/nonfree/test/test_surf_ocl.cpp similarity index 100% rename from modules/nonfree/test/test_surf.ocl.cpp rename to modules/nonfree/test/test_surf_ocl.cpp From d8f7377122a65512db2f443535a9d01ea336470c Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 3 Feb 2014 11:52:43 +0400 Subject: [PATCH 078/293] turn on CUDA part of nonfree module on Android for non-dynamic build --- modules/nonfree/CMakeLists.txt | 6 +++--- modules/nonfree/include/opencv2/nonfree/gpu.hpp | 8 +------- modules/nonfree/perf/perf_gpu.cpp | 4 +++- modules/nonfree/src/cuda/surf.cu | 2 +- modules/nonfree/src/precomp.hpp | 2 +- modules/nonfree/src/surf_gpu.cpp | 6 +----- modules/nonfree/test/test_gpu.cpp | 4 +++- 7 files changed, 13 insertions(+), 19 deletions(-) diff --git a/modules/nonfree/CMakeLists.txt b/modules/nonfree/CMakeLists.txt index d5c5562eca..53fb2f73e9 100644 --- a/modules/nonfree/CMakeLists.txt +++ b/modules/nonfree/CMakeLists.txt @@ -4,9 +4,9 @@ endif() set(the_description "Functionality with possible limitations on the use") ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) -if (ENABLE_DYNAMIC_CUDA) - set(HAVE_CUDA FALSE) +if(ENABLE_DYNAMIC_CUDA) + add_definitions(-DDYNAMIC_CUDA_SUPPORT) ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_ocl) else() ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_gpu opencv_ocl) -endif() \ No newline at end of file +endif() diff --git a/modules/nonfree/include/opencv2/nonfree/gpu.hpp b/modules/nonfree/include/opencv2/nonfree/gpu.hpp index c8730fb3b9..722ef26a25 100644 --- a/modules/nonfree/include/opencv2/nonfree/gpu.hpp +++ b/modules/nonfree/include/opencv2/nonfree/gpu.hpp @@ -43,11 +43,7 @@ #ifndef __OPENCV_NONFREE_GPU_HPP__ #define __OPENCV_NONFREE_GPU_HPP__ -#include "opencv2/opencv_modules.hpp" - -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) - -#include "opencv2/gpu/gpu.hpp" +#include "opencv2/core/gpumat.hpp" namespace cv { namespace gpu { @@ -129,6 +125,4 @@ public: } // namespace cv -#endif // defined(HAVE_OPENCV_GPU) - #endif // __OPENCV_NONFREE_GPU_HPP__ diff --git a/modules/nonfree/perf/perf_gpu.cpp b/modules/nonfree/perf/perf_gpu.cpp index 9f451deaba..e29eeb2fa8 100644 --- a/modules/nonfree/perf/perf_gpu.cpp +++ b/modules/nonfree/perf/perf_gpu.cpp @@ -42,7 +42,9 @@ #include "perf_precomp.hpp" -#if defined(HAVE_OPENCV_GPU) && defined(HAVE_CUDA) +#include "cvconfig.h" + +#if defined(HAVE_OPENCV_GPU) && defined(HAVE_CUDA) && !defined(DYNAMIC_CUDA_SUPPORT) #include "opencv2/ts/gpu_perf.hpp" diff --git a/modules/nonfree/src/cuda/surf.cu b/modules/nonfree/src/cuda/surf.cu index df5905d31d..65345a363d 100644 --- a/modules/nonfree/src/cuda/surf.cu +++ b/modules/nonfree/src/cuda/surf.cu @@ -42,7 +42,7 @@ #include "opencv2/opencv_modules.hpp" -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) #include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/limits.hpp" diff --git a/modules/nonfree/src/precomp.hpp b/modules/nonfree/src/precomp.hpp index 0d2e180fc5..28531390d6 100644 --- a/modules/nonfree/src/precomp.hpp +++ b/modules/nonfree/src/precomp.hpp @@ -51,7 +51,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/internal.hpp" -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) #include "opencv2/nonfree/gpu.hpp" #if defined(HAVE_CUDA) diff --git a/modules/nonfree/src/surf_gpu.cpp b/modules/nonfree/src/surf_gpu.cpp index e0cf6ff517..b3c2ce22b2 100644 --- a/modules/nonfree/src/surf_gpu.cpp +++ b/modules/nonfree/src/surf_gpu.cpp @@ -42,12 +42,10 @@ #include "precomp.hpp" -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) - using namespace cv; using namespace cv::gpu; -#if !defined (HAVE_CUDA) +#if !defined (HAVE_CUDA) || !defined(HAVE_OPENCV_GPU) || defined(DYNAMIC_CUDA_SUPPORT) cv::gpu::SURF_GPU::SURF_GPU() { throw_nogpu(); } cv::gpu::SURF_GPU::SURF_GPU(double, int, int, bool, float, bool) { throw_nogpu(); } @@ -421,5 +419,3 @@ void cv::gpu::SURF_GPU::releaseMemory() } #endif // !defined (HAVE_CUDA) - -#endif // defined(HAVE_OPENCV_GPU) && !defined(ANDROID) diff --git a/modules/nonfree/test/test_gpu.cpp b/modules/nonfree/test/test_gpu.cpp index 3f63eeddf2..938bc1d328 100644 --- a/modules/nonfree/test/test_gpu.cpp +++ b/modules/nonfree/test/test_gpu.cpp @@ -42,7 +42,9 @@ #include "test_precomp.hpp" -#if defined(HAVE_OPENCV_GPU) && defined(HAVE_CUDA) +#include "cvconfig.h" + +#if defined(HAVE_OPENCV_GPU) && defined(HAVE_CUDA) && !defined(DYNAMIC_CUDA_SUPPORT) using namespace cvtest; From a138e5a6a585e9dfc686d76b9769adeff02672b3 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 3 Feb 2014 12:35:24 +0400 Subject: [PATCH 079/293] turn on CUDA part of stitching module on Android for non-dynamic build --- modules/stitching/CMakeLists.txt | 5 +- .../opencv2/stitching/detail/matchers.hpp | 5 +- .../opencv2/stitching/detail/seam_finders.hpp | 4 +- .../opencv2/stitching/detail/warpers.hpp | 7 +- .../include/opencv2/stitching/warpers.hpp | 2 - modules/stitching/src/blenders.cpp | 8 +- modules/stitching/src/matchers.cpp | 34 +++++-- modules/stitching/src/precomp.hpp | 2 +- modules/stitching/src/seam_finders.cpp | 58 +++++++++++- modules/stitching/src/stitcher.cpp | 2 +- modules/stitching/src/warpers.cpp | 92 ++++++++++++++++++- 11 files changed, 190 insertions(+), 29 deletions(-) diff --git a/modules/stitching/CMakeLists.txt b/modules/stitching/CMakeLists.txt index 6e9a35ba73..fc8b2fcf96 100644 --- a/modules/stitching/CMakeLists.txt +++ b/modules/stitching/CMakeLists.txt @@ -1,6 +1,7 @@ set(the_description "Images stitching") -if (ENABLE_DYNAMIC_CUDA) +if(ENABLE_DYNAMIC_CUDA) + add_definitions(-DDYNAMIC_CUDA_SUPPORT) ocv_define_module(stitching opencv_imgproc opencv_features2d opencv_calib3d opencv_objdetect OPTIONAL opencv_nonfree) else() ocv_define_module(stitching opencv_imgproc opencv_features2d opencv_calib3d opencv_objdetect OPTIONAL opencv_gpu opencv_nonfree) -endif() \ No newline at end of file +endif() diff --git a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp index 36f80f481c..af7439fba2 100644 --- a/modules/stitching/include/opencv2/stitching/detail/matchers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/matchers.hpp @@ -44,11 +44,12 @@ #define __OPENCV_STITCHING_MATCHERS_HPP__ #include "opencv2/core/core.hpp" +#include "opencv2/core/gpumat.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/opencv_modules.hpp" -#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_NONFREE) #include "opencv2/nonfree/gpu.hpp" #endif @@ -104,7 +105,7 @@ private: }; -#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_NONFREE) class CV_EXPORTS SurfFeaturesFinderGpu : public FeaturesFinder { public: diff --git a/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp b/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp index 9301dc5ebe..5034c80043 100644 --- a/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp @@ -45,7 +45,7 @@ #include #include "opencv2/core/core.hpp" -#include "opencv2/opencv_modules.hpp" +#include "opencv2/core/gpumat.hpp" namespace cv { namespace detail { @@ -227,7 +227,6 @@ private: }; -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) class CV_EXPORTS GraphCutSeamFinderGpu : public GraphCutSeamFinderBase, public PairwiseSeamFinder { public: @@ -251,7 +250,6 @@ private: float terminal_cost_; float bad_region_penalty_; }; -#endif } // namespace detail } // namespace cv diff --git a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp index d44bfe69eb..60d5e54188 100644 --- a/modules/stitching/include/opencv2/stitching/detail/warpers.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/warpers.hpp @@ -44,11 +44,8 @@ #define __OPENCV_STITCHING_WARPERS_HPP__ #include "opencv2/core/core.hpp" +#include "opencv2/core/gpumat.hpp" #include "opencv2/imgproc/imgproc.hpp" -#include "opencv2/opencv_modules.hpp" -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) -# include "opencv2/gpu/gpu.hpp" -#endif namespace cv { namespace detail { @@ -331,7 +328,6 @@ public: }; -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) class CV_EXPORTS PlaneWarperGpu : public PlaneWarper { public: @@ -448,7 +444,6 @@ public: private: gpu::GpuMat d_xmap_, d_ymap_, d_src_, d_dst_; }; -#endif struct SphericalPortraitProjector : ProjectorBase diff --git a/modules/stitching/include/opencv2/stitching/warpers.hpp b/modules/stitching/include/opencv2/stitching/warpers.hpp index 87efa7e80a..11e012ff09 100644 --- a/modules/stitching/include/opencv2/stitching/warpers.hpp +++ b/modules/stitching/include/opencv2/stitching/warpers.hpp @@ -145,7 +145,6 @@ public: -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) class PlaneWarperGpu: public WarperCreator { public: @@ -165,7 +164,6 @@ class SphericalWarperGpu: public WarperCreator public: Ptr create(float scale) const { return new detail::SphericalWarperGpu(scale); } }; -#endif } // namespace cv diff --git a/modules/stitching/src/blenders.cpp b/modules/stitching/src/blenders.cpp index fb3c0d666b..316263e998 100644 --- a/modules/stitching/src/blenders.cpp +++ b/modules/stitching/src/blenders.cpp @@ -189,7 +189,7 @@ Rect FeatherBlender::createWeightMaps(const vector &masks, const vector &pyr) void createLaplacePyrGpu(const Mat &img, int num_levels, vector &pyr) { -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) pyr.resize(num_levels + 1); vector gpu_pyr(num_levels + 1); @@ -512,6 +512,7 @@ void createLaplacePyrGpu(const Mat &img, int num_levels, vector &pyr) (void)img; (void)num_levels; (void)pyr; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); #endif } @@ -531,7 +532,7 @@ void restoreImageFromLaplacePyr(vector &pyr) void restoreImageFromLaplacePyrGpu(vector &pyr) { -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) if (pyr.empty()) return; @@ -549,6 +550,7 @@ void restoreImageFromLaplacePyrGpu(vector &pyr) gpu_pyr[0].download(pyr[0]); #else (void)pyr; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); #endif } diff --git a/modules/stitching/src/matchers.cpp b/modules/stitching/src/matchers.cpp index d86206233f..ac29d7ca2b 100644 --- a/modules/stitching/src/matchers.cpp +++ b/modules/stitching/src/matchers.cpp @@ -45,10 +45,7 @@ using namespace std; using namespace cv; using namespace cv::detail; - -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) using namespace cv::gpu; -#endif #ifdef HAVE_OPENCV_NONFREE #include "opencv2/nonfree/nonfree.hpp" @@ -129,7 +126,7 @@ private: float match_conf_; }; -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) class GpuMatcher : public FeaturesMatcher { public: @@ -204,7 +201,7 @@ void CpuMatcher::match(const ImageFeatures &features1, const ImageFeatures &feat LOG("1->2 & 2->1 matches: " << matches_info.matches.size() << endl); } -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) void GpuMatcher::match(const ImageFeatures &features1, const ImageFeatures &features2, MatchesInfo& matches_info) { matches_info.matches.clear(); @@ -432,7 +429,7 @@ void OrbFeaturesFinder::find(const Mat &image, ImageFeatures &features) } } -#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) SurfFeaturesFinderGpu::SurfFeaturesFinderGpu(double hess_thresh, int num_octaves, int num_layers, int num_octaves_descr, int num_layers_descr) { @@ -478,6 +475,29 @@ void SurfFeaturesFinderGpu::collectGarbage() keypoints_.release(); descriptors_.release(); } +#elif defined(HAVE_OPENCV_NONFREE) +SurfFeaturesFinderGpu::SurfFeaturesFinderGpu(double hess_thresh, int num_octaves, int num_layers, + int num_octaves_descr, int num_layers_descr) +{ + (void)hess_thresh; + (void)num_octaves; + (void)num_layers; + (void)num_octaves_descr; + (void)num_layers_descr; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); +} + + +void SurfFeaturesFinderGpu::find(const Mat &image, ImageFeatures &features) +{ + (void)image; + (void)features; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); +} + +void SurfFeaturesFinderGpu::collectGarbage() +{ +} #endif @@ -533,7 +553,7 @@ void FeaturesMatcher::operator ()(const vector &features, vector< BestOf2NearestMatcher::BestOf2NearestMatcher(bool try_use_gpu, float match_conf, int num_matches_thresh1, int num_matches_thresh2) { -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) if (try_use_gpu && getCudaEnabledDeviceCount() > 0) impl_ = new GpuMatcher(match_conf); else diff --git a/modules/stitching/src/precomp.hpp b/modules/stitching/src/precomp.hpp index 54b6721437..699f19c38c 100644 --- a/modules/stitching/src/precomp.hpp +++ b/modules/stitching/src/precomp.hpp @@ -68,7 +68,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/calib3d/calib3d.hpp" -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) #include "opencv2/gpu/gpu.hpp" #ifdef HAVE_OPENCV_NONFREE diff --git a/modules/stitching/src/seam_finders.cpp b/modules/stitching/src/seam_finders.cpp index a198c1ebb4..234f2047e6 100644 --- a/modules/stitching/src/seam_finders.cpp +++ b/modules/stitching/src/seam_finders.cpp @@ -1318,7 +1318,7 @@ void GraphCutSeamFinder::find(const vector &src, const vector &corne } -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) void GraphCutSeamFinderGpu::find(const vector &src, const vector &corners, vector &masks) { @@ -1642,6 +1642,62 @@ void GraphCutSeamFinderGpu::setGraphWeightsColorGrad( } } } +#else +void GraphCutSeamFinderGpu::find(const vector &src, const vector &corners, + vector &masks) +{ + (void)src; + (void)corners; + (void)masks; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); +} + + +void GraphCutSeamFinderGpu::findInPair(size_t first, size_t second, Rect roi) +{ + (void)first; + (void)second; + (void)roi; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); +} + + +void GraphCutSeamFinderGpu::setGraphWeightsColor(const Mat &img1, const Mat &img2, const Mat &mask1, const Mat &mask2, + Mat &terminals, Mat &leftT, Mat &rightT, Mat &top, Mat &bottom) +{ + (void)img1; + (void)img2; + (void)mask1; + (void)mask2; + (void)terminals; + (void)leftT; + (void)rightT; + (void)top; + (void)bottom; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); +} + + +void GraphCutSeamFinderGpu::setGraphWeightsColorGrad( + const Mat &img1, const Mat &img2, const Mat &dx1, const Mat &dx2, + const Mat &dy1, const Mat &dy2, const Mat &mask1, const Mat &mask2, + Mat &terminals, Mat &leftT, Mat &rightT, Mat &top, Mat &bottom) +{ + (void)img1; + (void)img2; + (void)dx1; + (void)dx2; + (void)dy1; + (void)dy2; + (void)mask1; + (void)mask2; + (void)terminals; + (void)leftT; + (void)rightT; + (void)top; + (void)bottom; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); +} #endif } // namespace detail diff --git a/modules/stitching/src/stitcher.cpp b/modules/stitching/src/stitcher.cpp index 4a36ab0a45..83ea60954e 100644 --- a/modules/stitching/src/stitcher.cpp +++ b/modules/stitching/src/stitcher.cpp @@ -58,7 +58,7 @@ Stitcher Stitcher::createDefault(bool try_use_gpu) stitcher.setFeaturesMatcher(new detail::BestOf2NearestMatcher(try_use_gpu)); stitcher.setBundleAdjuster(new detail::BundleAdjusterRay()); -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) if (try_use_gpu && gpu::getCudaEnabledDeviceCount() > 0) { #if defined(HAVE_OPENCV_NONFREE) diff --git a/modules/stitching/src/warpers.cpp b/modules/stitching/src/warpers.cpp index 935831950f..3b1d9c2287 100644 --- a/modules/stitching/src/warpers.cpp +++ b/modules/stitching/src/warpers.cpp @@ -212,7 +212,7 @@ void SphericalWarper::detectResultRoi(Size src_size, Point &dst_tl, Point &dst_b } -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) Rect PlaneWarperGpu::buildMaps(Size src_size, const Mat &K, const Mat &R, gpu::GpuMat &xmap, gpu::GpuMat &ymap) { return buildMaps(src_size, K, R, Mat::zeros(3, 1, CV_32F), xmap, ymap); @@ -294,6 +294,96 @@ Point CylindricalWarperGpu::warp(const gpu::GpuMat &src, const Mat &K, const Mat gpu::remap(src, dst, d_xmap_, d_ymap_, interp_mode, border_mode); return dst_roi.tl(); } +#else +Rect PlaneWarperGpu::buildMaps(Size src_size, const Mat &K, const Mat &R, gpu::GpuMat &xmap, gpu::GpuMat &ymap) +{ + return buildMaps(src_size, K, R, Mat::zeros(3, 1, CV_32F), xmap, ymap); +} + +Rect PlaneWarperGpu::buildMaps(Size src_size, const Mat &K, const Mat &R, const Mat &T, gpu::GpuMat &xmap, gpu::GpuMat &ymap) +{ + (void)src_size; + (void)K; + (void)R; + (void)T; + (void)xmap; + (void)ymap; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); + return Rect(); +} + +Point PlaneWarperGpu::warp(const gpu::GpuMat &src, const Mat &K, const Mat &R, int interp_mode, int border_mode, + gpu::GpuMat &dst) +{ + return warp(src, K, R, Mat::zeros(3, 1, CV_32F), interp_mode, border_mode, dst); +} + + +Point PlaneWarperGpu::warp(const gpu::GpuMat &src, const Mat &K, const Mat &R, const Mat &T, int interp_mode, int border_mode, + gpu::GpuMat &dst) +{ + (void)src; + (void)K; + (void)R; + (void)T; + (void)interp_mode; + (void)border_mode; + (void)dst; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); + return Point(); +} + + +Rect SphericalWarperGpu::buildMaps(Size src_size, const Mat &K, const Mat &R, gpu::GpuMat &xmap, gpu::GpuMat &ymap) +{ + (void)src_size; + (void)K; + (void)R; + (void)xmap; + (void)ymap; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); + return Rect(); +} + + +Point SphericalWarperGpu::warp(const gpu::GpuMat &src, const Mat &K, const Mat &R, int interp_mode, int border_mode, + gpu::GpuMat &dst) +{ + (void)src; + (void)K; + (void)R; + (void)interp_mode; + (void)border_mode; + (void)dst; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); + return Point(); +} + + +Rect CylindricalWarperGpu::buildMaps(Size src_size, const Mat &K, const Mat &R, gpu::GpuMat &xmap, gpu::GpuMat &ymap) +{ + (void)src_size; + (void)K; + (void)R; + (void)xmap; + (void)ymap; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); + return Rect(); +} + + +Point CylindricalWarperGpu::warp(const gpu::GpuMat &src, const Mat &K, const Mat &R, int interp_mode, int border_mode, + gpu::GpuMat &dst) +{ + (void)src; + (void)K; + (void)R; + (void)interp_mode; + (void)border_mode; + (void)dst; + CV_Error(CV_StsNotImplemented, "CUDA optimization is unavailable"); + return Point(); +} #endif void SphericalPortraitWarper::detectResultRoi(Size src_size, Point &dst_tl, Point &dst_br) From 214cbabc4073c17413c2982ce06266e777e73654 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 3 Feb 2014 12:55:03 +0400 Subject: [PATCH 080/293] update stitching sample --- samples/cpp/stitching_detailed.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/cpp/stitching_detailed.cpp b/samples/cpp/stitching_detailed.cpp index 7394a72821..4162addb38 100644 --- a/samples/cpp/stitching_detailed.cpp +++ b/samples/cpp/stitching_detailed.cpp @@ -355,7 +355,7 @@ int main(int argc, char* argv[]) Ptr finder; if (features_type == "surf") { -#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_NONFREE) && defined(HAVE_OPENCV_GPU) if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0) finder = new SurfFeaturesFinderGpu(); else @@ -543,7 +543,7 @@ int main(int argc, char* argv[]) // Warp images and their masks Ptr warper_creator; -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0) { if (warp_type == "plane") warper_creator = new cv::PlaneWarperGpu(); @@ -608,7 +608,7 @@ int main(int argc, char* argv[]) seam_finder = new detail::VoronoiSeamFinder(); else if (seam_find_type == "gc_color") { -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0) seam_finder = new detail::GraphCutSeamFinderGpu(GraphCutSeamFinderBase::COST_COLOR); else @@ -617,7 +617,7 @@ int main(int argc, char* argv[]) } else if (seam_find_type == "gc_colorgrad") { -#if defined(HAVE_OPENCV_GPU) && !defined(ANDROID) +#if defined(HAVE_OPENCV_GPU) if (try_gpu && gpu::getCudaEnabledDeviceCount() > 0) seam_finder = new detail::GraphCutSeamFinderGpu(GraphCutSeamFinderBase::COST_COLOR_GRAD); else From a098fb1803a6bd4d6ebe58c4a1dd0f2dfea464ab Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 4 Feb 2014 10:29:15 +0400 Subject: [PATCH 081/293] fix path to CUDA libraries (use targets/armv7-linux-androideabi/lib) --- cmake/templates/OpenCV.mk.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/OpenCV.mk.in b/cmake/templates/OpenCV.mk.in index 0fd7b9e058..690d2ef62b 100644 --- a/cmake/templates/OpenCV.mk.in +++ b/cmake/templates/OpenCV.mk.in @@ -157,7 +157,7 @@ LOCAL_LDLIBS += $(foreach lib,$(OPENCV_EXTRA_COMPONENTS), -l$(lib)) ifeq ($(OPENCV_USE_GPU_MODULE),on) LOCAL_STATIC_LIBRARIES+=libopencv_gpu - LOCAL_LDLIBS += -L$(CUDA_TOOLKIT_DIR)/lib $(foreach lib, $(CUDA_RUNTIME_LIBS), -l$(lib)) + LOCAL_LDLIBS += -L$(CUDA_TOOLKIT_DIR)/targets/armv7-linux-androideabi/lib $(foreach lib, $(CUDA_RUNTIME_LIBS), -l$(lib)) endif #restore the LOCAL_PATH From 286fe261d07a7faf481ac907c6f09b3fece4aa64 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 4 Feb 2014 10:29:43 +0400 Subject: [PATCH 082/293] save previous values of LOCAL_* variables and restore them at the end --- cmake/templates/OpenCV.mk.in | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmake/templates/OpenCV.mk.in b/cmake/templates/OpenCV.mk.in index 690d2ef62b..104ddb6dd2 100644 --- a/cmake/templates/OpenCV.mk.in +++ b/cmake/templates/OpenCV.mk.in @@ -2,6 +2,13 @@ # you might need to define NDK_USE_CYGPATH=1 before calling the ndk-build USER_LOCAL_PATH:=$(LOCAL_PATH) + +USER_LOCAL_C_INCLUDES:=$(LOCAL_C_INCLUDES) +USER_LOCAL_CFLAGS:=$(LOCAL_CFLAGS) +USER_LOCAL_STATIC_LIBRARIES:=$(LOCAL_STATIC_LIBRARIES) +USER_LOCAL_SHARED_LIBRARIES:=$(LOCAL_SHARED_LIBRARIES) +USER_LOCAL_LDLIBS:=$(LOCAL_LDLIBS) + LOCAL_PATH:=$(subst ?,,$(firstword ?$(subst \, ,$(subst /, ,$(call my-dir))))) OPENCV_TARGET_ARCH_ABI:=$(TARGET_ARCH_ABI) @@ -136,6 +143,13 @@ ifeq ($(OPENCV_LOCAL_CFLAGS),) endif include $(CLEAR_VARS) + +LOCAL_C_INCLUDES:=$(USER_LOCAL_C_INCLUDES) +LOCAL_CFLAGS:=$(USER_LOCAL_CFLAGS) +LOCAL_STATIC_LIBRARIES:=$(USER_LOCAL_STATIC_LIBRARIES) +LOCAL_SHARED_LIBRARIES:=$(USER_LOCAL_SHARED_LIBRARIES) +LOCAL_LDLIBS:=$(USER_LOCAL_LDLIBS) + LOCAL_C_INCLUDES += $(OPENCV_LOCAL_C_INCLUDES) LOCAL_CFLAGS += $(OPENCV_LOCAL_CFLAGS) From 0cd0e4749bd74c2050b3e3cf082ced6bdc489376 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 4 Feb 2014 16:23:01 +0400 Subject: [PATCH 083/293] Tests install path fix for Android SDK. --- CMakeLists.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d342cfac9..c3dc8028ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,13 +268,24 @@ if(WIN32) message(STATUS "Can't detect runtime and/or arch") set(OpenCV_INSTALL_BINARIES_PREFIX "") endif() +elseif(ANDROID) + set(OpenCV_INSTALL_BINARIES_PREFIX "sdk/native/") else() set(OpenCV_INSTALL_BINARIES_PREFIX "") endif() -set(OPENCV_SAMPLES_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}samples") +if(ANDROID) + set(OPENCV_SAMPLES_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}samples/${ANDROID_NDK_ABI_NAME}") +else() + set(OPENCV_SAMPLES_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}samples") +endif() + +if(ANDROID) + set(OPENCV_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}bin/${ANDROID_NDK_ABI_NAME}") +else() + set(OPENCV_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}bin") +endif() -set(OPENCV_BIN_INSTALL_PATH "${OpenCV_INSTALL_BINARIES_PREFIX}bin") if(NOT OPENCV_TEST_INSTALL_PATH) set(OPENCV_TEST_INSTALL_PATH "${OPENCV_BIN_INSTALL_PATH}") endif() From 435615ba2e14b19e5fe2d3bb3036273eee7979bc Mon Sep 17 00:00:00 2001 From: Konstantin Matskevich Date: Wed, 5 Feb 2014 12:23:36 +0400 Subject: [PATCH 084/293] update test CascadeClassifier --- modules/ocl/perf/perf_haar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ocl/perf/perf_haar.cpp b/modules/ocl/perf/perf_haar.cpp index 3aedd88868..1a97e908c1 100644 --- a/modules/ocl/perf/perf_haar.cpp +++ b/modules/ocl/perf/perf_haar.cpp @@ -95,7 +95,8 @@ typedef perf::TestBaseWithParam OCL_Cascade_Image_M PERF_TEST_P( OCL_Cascade_Image_MinSize, CascadeClassifier, testing::Combine( - testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml") ), + testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml"), + string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt2.xml") ), testing::Values( string("cv/shared/lena.png"), string("cv/cascadeandhog/images/bttf301.png")/*, string("cv/cascadeandhog/images/class57.png")*/ ), From 65b4d779597ffdfe909cb01f6163b1c404b040c2 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 5 Feb 2014 17:34:19 +0400 Subject: [PATCH 085/293] project.properties file generation fixed for per-component installation. --- cmake/OpenCVDetectAndroidSDK.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/OpenCVDetectAndroidSDK.cmake b/cmake/OpenCVDetectAndroidSDK.cmake index 7a37051e49..d9e10213ae 100644 --- a/cmake/OpenCVDetectAndroidSDK.cmake +++ b/cmake/OpenCVDetectAndroidSDK.cmake @@ -365,7 +365,7 @@ macro(add_android_project target path) endif() install(CODE "EXECUTE_PROCESS(COMMAND ${ANDROID_EXECUTABLE} --silent update project --path . --target \"${android_proj_sdk_target}\" --name \"${target}\" ${inst_lib_opt} WORKING_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/samples/${sample_dir}\" - )" COMPONENT dev) + )" COMPONENT samples) #empty 'gen' install(CODE "MAKE_DIRECTORY(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/samples/${sample_dir}/gen\")" COMPONENT samples) endif() From 530702c5f2350c58f0e9078b5fde8185af6a3c77 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 6 Feb 2014 12:35:14 +0400 Subject: [PATCH 086/293] Absolute path to tests in opencv_run_app_tests.sh fixed. --- cmake/templates/opencv_run_all_tests.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/opencv_run_all_tests.sh.in b/cmake/templates/opencv_run_all_tests.sh.in index b614900026..77dc1191a9 100644 --- a/cmake/templates/opencv_run_all_tests.sh.in +++ b/cmake/templates/opencv_run_all_tests.sh.in @@ -1,6 +1,6 @@ #!/bin/sh -OPENCV_TEST_PATH=@OPENCV_TEST_INSTALL_PATH@ +OPENCV_TEST_PATH=@CMAKE_INSTALL_PREFIX@/@OPENCV_TEST_INSTALL_PATH@ export OPENCV_TEST_DATA_PATH=@CMAKE_INSTALL_PREFIX@/share/OpenCV/testdata SUMMARY_STATUS=0 From 6ae4a9b09bf4b3572b2d31136528f1faa809a065 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 4 Feb 2014 13:25:47 +0400 Subject: [PATCH 087/293] Multiple improvements in OpenCV examples build. EMBED_CUDA and FORCE_EMBED_OPENCV flags added to cmake macro add_android_project; INSTALL_CUDA_LIBRARIES option added to OpenCV.mk opencv_dynamicuda library installation with enabled OPENCV_INSTALL_MODULES flag fixed; CUDA initialization apportunity added to OpenCVLoader.initDebug(); Tutorial-4-CUDA sample reimplemented with static OpenCV and CUDA initialization. --- cmake/OpenCVDetectAndroidSDK.cmake | 42 +++++++++++++++- cmake/OpenCVGenAndroidMK.cmake | 23 +++++++++ cmake/OpenCVUtils.cmake | 14 ++++++ cmake/templates/OpenCV.mk.in | 34 +++++++++++-- .../src/java/android+OpenCVLoader.java | 12 ++++- .../src/java/android+StaticHelper.java | 14 +++++- .../android/tutorial-4-cuda/CMakeLists.txt | 7 +-- .../android/tutorial-4-cuda/jni/Android.mk | 2 + .../samples/tutorial4/Tutorial4Activity.java | 50 ++++++++++--------- 9 files changed, 163 insertions(+), 35 deletions(-) diff --git a/cmake/OpenCVDetectAndroidSDK.cmake b/cmake/OpenCVDetectAndroidSDK.cmake index d9e10213ae..af7427194e 100644 --- a/cmake/OpenCVDetectAndroidSDK.cmake +++ b/cmake/OpenCVDetectAndroidSDK.cmake @@ -180,7 +180,7 @@ unset(__android_project_chain CACHE) # add_android_project(target_name ${path} NATIVE_DEPS opencv_core LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11) macro(add_android_project target path) # parse arguments - set(android_proj_arglist NATIVE_DEPS LIBRARY_DEPS SDK_TARGET IGNORE_JAVA IGNORE_MANIFEST) + set(android_proj_arglist NATIVE_DEPS LIBRARY_DEPS SDK_TARGET IGNORE_JAVA IGNORE_MANIFEST EMBED_CUDA FORCE_EMBED_OPENCV) set(__varname "android_proj_") foreach(v ${android_proj_arglist}) set(${__varname}${v} "") @@ -303,6 +303,46 @@ macro(add_android_project target path) add_custom_command(TARGET ${JNI_LIB_NAME} POST_BUILD COMMAND ${CMAKE_STRIP} --strip-unneeded "${android_proj_jni_location}") endif() endif() + + # copy opencv_java, tbb if it is shared and dynamicuda if present if FORCE_EMBED_OPENCV flag is set + if(android_proj_FORCE_EMBED_OPENCV) + set(native_deps ${android_proj_NATIVE_DEPS}) + # filter out gpu module as it is always static library on Android + list(REMOVE_ITEM native_deps "opencv_gpu") + if(ENABLE_DYNAMIC_CUDA) + list(APPEND native_deps "opencv_dynamicuda") + endif() + foreach(lib ${native_deps}) + get_property(f TARGET ${lib} PROPERTY LOCATION) + get_filename_component(f_name ${f} NAME) + add_custom_command( + OUTPUT "${android_proj_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}/${f_name}" + COMMAND ${CMAKE_COMMAND} -E copy "${f}" "${android_proj_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}/${f_name}" + DEPENDS "${lib}" VERBATIM + COMMENT "Embedding ${f}") + list(APPEND android_proj_file_deps "${android_proj_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}/${f_name}") + endforeach() + endif() + + # copy all needed CUDA libs to project if EMBED_CUDA flag is present + if(android_proj_EMBED_CUDA) + set(android_proj_culibs ${CUDA_npp_LIBRARY} ${CUDA_LIBRARIES}) + if(HAVE_CUFFT) + list(INSERT android_proj_culibs 0 ${CUDA_cufft_LIBRARY}) + endif() + if(HAVE_CUBLAS) + list(INSERT android_proj_culibs 0 ${CUDA_cublas_LIBRARY}) + endif() + foreach(lib ${android_proj_culibs}) + get_filename_component(f "${lib}" NAME) + add_custom_command( + OUTPUT "${android_proj_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}/${f}" + COMMAND ${CMAKE_COMMAND} -E copy "${lib}" "${android_proj_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}/${f}" + DEPENDS "${lib}" VERBATIM + COMMENT "Embedding ${f}") + list(APPEND android_proj_file_deps "${android_proj_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}/${f}") + endforeach() + endif() endif() # build java part diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index 447aea2436..ee52fa6886 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -59,6 +59,24 @@ if(ANDROID) ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "libcu") ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "libnpp") + if(HAVE_CUDA) + # CUDA runtime libraries and are required always + set(culibs ${CUDA_LIBRARIES}) + + # right now NPP is requared always too + list(INSERT culibs 0 ${CUDA_npp_LIBRARY}) + + if(HAVE_CUFFT) + list(INSERT culibs 0 ${CUDA_cufft_LIBRARY}) + endif() + + if(HAVE_CUBLAS) + list(INSERT culibs 0 ${CUDA_cublas_LIBRARY}) + endif() + endif() + + ocv_convert_to_lib_name(CUDA_RUNTIME_LIBS_CONFIGMAKE ${culibs}) + # split 3rdparty libs and modules foreach(mod ${OPENCV_MODULES_CONFIGMAKE}) if(NOT mod MATCHES "^opencv_.+$") @@ -69,6 +87,10 @@ if(ANDROID) list(REMOVE_ITEM OPENCV_MODULES_CONFIGMAKE ${OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE}) endif() + if(ENABLE_DYNAMIC_CUDA) + set(OPENCV_DYNAMICUDA_MODULE_CONFIGMAKE "dynamicuda") + endif() + # GPU module enabled separately list(REMOVE_ITEM OPENCV_MODULES_CONFIGMAKE "opencv_gpu") list(REMOVE_ITEM OPENCV_MODULES_CONFIGMAKE "opencv_dynamicuda") @@ -84,6 +106,7 @@ if(ANDROID) string(REPLACE ";" " " ${lst} "${${lst}}") endforeach() string(REPLACE "opencv_" "" OPENCV_MODULES_CONFIGMAKE "${OPENCV_MODULES_CONFIGMAKE}") + string(REPLACE ";" " " CUDA_RUNTIME_LIBS_CONFIGMAKE "${CUDA_RUNTIME_LIBS_CONFIGMAKE}") # prepare 3rd-party component list without TBB for armeabi and mips platforms. TBB is useless there. set(OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE_NO_TBB ${OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE}) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 13461e82c1..9fa94bb8bd 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -448,6 +448,20 @@ macro(ocv_convert_to_full_paths VAR) endmacro() +# convert list of paths to libraries names without lib prefix +macro(ocv_convert_to_lib_name var) + set(__tmp "") + foreach(path ${ARGN}) + get_filename_component(__tmp_name "${path}" NAME_WE) + string(REGEX REPLACE "^lib" "" __tmp_name ${__tmp_name}) + list(APPEND __tmp "${__tmp_name}") + endforeach() + set(${var} ${__tmp}) + unset(__tmp) + unset(__tmp_name) +endmacro() + + # add install command function(ocv_install_target) install(TARGETS ${ARGN}) diff --git a/cmake/templates/OpenCV.mk.in b/cmake/templates/OpenCV.mk.in index 104ddb6dd2..97330e8545 100644 --- a/cmake/templates/OpenCV.mk.in +++ b/cmake/templates/OpenCV.mk.in @@ -19,8 +19,9 @@ OPENCV_3RDPARTY_LIBS_DIR:=@OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE@ OPENCV_BASEDIR:=@OPENCV_BASE_INCLUDE_DIR_CONFIGCMAKE@ OPENCV_LOCAL_C_INCLUDES:=@OPENCV_INCLUDE_DIRS_CONFIGCMAKE@ OPENCV_MODULES:=@OPENCV_MODULES_CONFIGMAKE@ +OPENCV_DYNAMICUDA_MODULE:=@OPENCV_DYNAMICUDA_MODULE_CONFIGMAKE@ -OPENCV_HAVE_GPU_MODULE=@OPENCV_HAVE_GPU_MODULE_CONFIGMAKE@ +OPENCV_HAVE_GPU_MODULE:=@OPENCV_HAVE_GPU_MODULE_CONFIGMAKE@ OPENCV_USE_GPU_MODULE:= ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) @@ -31,7 +32,7 @@ ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) endif endif -CUDA_RUNTIME_LIBS:=cufft npps nppi nppc cudart +CUDA_RUNTIME_LIBS:=@CUDA_RUNTIME_LIBS_CONFIGMAKE@ ifeq ($(OPENCV_LIB_TYPE),) OPENCV_LIB_TYPE:=@OPENCV_LIBTYPE_CONFIGMAKE@ @@ -67,7 +68,7 @@ else endif endif -ifeq (${OPENCV_CAMERA_MODULES},on) +ifeq ($(OPENCV_CAMERA_MODULES),on) ifeq ($(TARGET_ARCH_ABI),armeabi) OPENCV_CAMERA_MODULES:=@OPENCV_CAMERA_LIBS_ARMEABI_CONFIGCMAKE@ endif @@ -98,6 +99,13 @@ define add_opencv_module include $(PREBUILT_$(OPENCV_LIB_TYPE)_LIBRARY) endef +define add_cuda_module + include $(CLEAR_VARS) + LOCAL_MODULE:=$1 + LOCAL_SRC_FILES:=$(CUDA_TOOLKIT_DIR)/targets/armv7-linux-androideabi/lib/lib$1.so + include $(PREBUILT_SHARED_LIBRARY) +endef + define add_opencv_3rdparty_component include $(CLEAR_VARS) LOCAL_MODULE:=$1 @@ -115,6 +123,15 @@ endef ifeq ($(OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_ALREADY_INCLUDED),) ifeq ($(OPENCV_INSTALL_MODULES),on) $(foreach module,$(OPENCV_LIBS),$(eval $(call add_opencv_module,$(module)))) + ifneq ($(OPENCV_DYNAMICUDA_MODULE),) + $(eval $(call add_opencv_module,$(OPENCV_DYNAMICUDA_MODULE))) + endif + endif + + ifeq ($(OPENCV_USE_GPU_MODULE),on) + ifeq ($(INSTALL_CUDA_LIBRARIES),on) + $(foreach module,$(CUDA_RUNTIME_LIBS),$(eval $(call add_cuda_module,$(module)))) + endif endif $(foreach module,$(OPENCV_3RDPARTY_COMPONENTS),$(eval $(call add_opencv_3rdparty_component,$(module)))) @@ -159,6 +176,11 @@ endif ifeq ($(OPENCV_INSTALL_MODULES),on) LOCAL_$(OPENCV_LIB_TYPE)_LIBRARIES += $(foreach mod, $(OPENCV_LIBS), opencv_$(mod)) + ifeq ($(OPENCV_LIB_TYPE),SHARED) + ifneq ($(OPENCV_DYNAMICUDA_MODULE),) + LOCAL_$(OPENCV_LIB_TYPE)_LIBRARIES += $(OPENCV_DYNAMICUDA_MODULE) + endif + endif else LOCAL_LDLIBS += -L$(call host-path,$(LOCAL_PATH)/$(OPENCV_LIBS_DIR)) $(foreach lib, $(OPENCV_LIBS), -lopencv_$(lib)) endif @@ -170,8 +192,12 @@ endif LOCAL_LDLIBS += $(foreach lib,$(OPENCV_EXTRA_COMPONENTS), -l$(lib)) ifeq ($(OPENCV_USE_GPU_MODULE),on) + ifeq ($(INSTALL_CUDA_LIBRARIES),on) + LOCAL_SHARED_LIBRARIES += $(foreach mod, $(CUDA_RUNTIME_LIBS), $(mod)) + else + LOCAL_LDLIBS += -L$(CUDA_TOOLKIT_DIR)/targets/armv7-linux-androideabi/lib $(foreach lib, $(CUDA_RUNTIME_LIBS), -l$(lib)) + endif LOCAL_STATIC_LIBRARIES+=libopencv_gpu - LOCAL_LDLIBS += -L$(CUDA_TOOLKIT_DIR)/targets/armv7-linux-androideabi/lib $(foreach lib, $(CUDA_RUNTIME_LIBS), -l$(lib)) endif #restore the LOCAL_PATH diff --git a/modules/java/generator/src/java/android+OpenCVLoader.java b/modules/java/generator/src/java/android+OpenCVLoader.java index 46e62eb347..0892e3af3c 100644 --- a/modules/java/generator/src/java/android+OpenCVLoader.java +++ b/modules/java/generator/src/java/android+OpenCVLoader.java @@ -48,7 +48,17 @@ public class OpenCVLoader */ public static boolean initDebug() { - return StaticHelper.initOpenCV(); + return StaticHelper.initOpenCV(false); + } + + /** + * Loads and initializes OpenCV library from current application package. Roughly, it's an analog of system.loadLibrary("opencv_java"). + * @param InitCuda load and initialize CUDA runtime libraries. + * @return Returns true is initialization of OpenCV was successful. + */ + public static boolean initDebug(boolean InitCuda) + { + return StaticHelper.initOpenCV(InitCuda); } /** diff --git a/modules/java/generator/src/java/android+StaticHelper.java b/modules/java/generator/src/java/android+StaticHelper.java index 8d0629c8d3..10442c904d 100644 --- a/modules/java/generator/src/java/android+StaticHelper.java +++ b/modules/java/generator/src/java/android+StaticHelper.java @@ -7,11 +7,21 @@ import android.util.Log; class StaticHelper { - public static boolean initOpenCV() + public static boolean initOpenCV(boolean InitCuda) { boolean result; String libs = ""; + if(InitCuda) + { + loadLibrary("cudart"); + loadLibrary("nppc"); + loadLibrary("nppi"); + loadLibrary("npps"); + loadLibrary("cufft"); + loadLibrary("cublas"); + } + Log.d(TAG, "Trying to get library list"); try @@ -52,7 +62,7 @@ class StaticHelper { try { System.loadLibrary(Name); - Log.d(TAG, "OpenCV libs init was ok!"); + Log.d(TAG, "Library " + Name + " loaded"); } catch(UnsatisfiedLinkError e) { diff --git a/samples/android/tutorial-4-cuda/CMakeLists.txt b/samples/android/tutorial-4-cuda/CMakeLists.txt index a011b33492..da9fe9871d 100644 --- a/samples/android/tutorial-4-cuda/CMakeLists.txt +++ b/samples/android/tutorial-4-cuda/CMakeLists.txt @@ -1,15 +1,16 @@ set(sample example-tutorial-4-cuda) -ocv_check_dependencies(opencv_core opencv_java opencv_gpu) +ocv_check_dependencies(opencv_core opencv_features2d opencv_java opencv_gpu) if (OCV_DEPENDENCIES_FOUND) if(BUILD_FAT_JAVA_LIB) set(native_deps opencv_java opencv_gpu) else() - set(native_deps opencv_gpu) + set(native_deps opencv_core opencv_features2d opencv_java opencv_gpu) endif() - add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET} NATIVE_DEPS ${native_deps}) + add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET} NATIVE_DEPS ${native_deps} EMBED_CUDA ON FORCE_EMBED_OPENCV ON) + if(TARGET ${sample}) add_dependencies(opencv_android_examples ${sample}) endif() diff --git a/samples/android/tutorial-4-cuda/jni/Android.mk b/samples/android/tutorial-4-cuda/jni/Android.mk index 3d709dff3b..e14b1990f2 100644 --- a/samples/android/tutorial-4-cuda/jni/Android.mk +++ b/samples/android/tutorial-4-cuda/jni/Android.mk @@ -2,6 +2,8 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +INSTALL_CUDA_LIBRARIES:=on +OPENCV_INSTALL_MODULES:=on CUDA_TOOLKIT_DIR=$(CUDA_TOOLKIT_ROOT) include ../../sdk/native/jni/OpenCV.mk diff --git a/samples/android/tutorial-4-cuda/src/org/opencv/samples/tutorial4/Tutorial4Activity.java b/samples/android/tutorial-4-cuda/src/org/opencv/samples/tutorial4/Tutorial4Activity.java index c1753b68cc..6a8cb5ec04 100644 --- a/samples/android/tutorial-4-cuda/src/org/opencv/samples/tutorial4/Tutorial4Activity.java +++ b/samples/android/tutorial-4-cuda/src/org/opencv/samples/tutorial4/Tutorial4Activity.java @@ -49,29 +49,6 @@ public class Tutorial4Activity extends Activity implements CvCameraViewListener2 { Log.i(TAG, "OpenCV loaded successfully"); - // Check CUDA support - if (Gpu.getCudaEnabledDeviceCount() <= 0) - { - Log.e(TAG, "No CUDA capable device found!"); - AlertDialog InitFailedDialog = new AlertDialog.Builder(Tutorial4Activity.this).create(); - InitFailedDialog.setTitle("OpenCV CUDA error"); - InitFailedDialog.setMessage("CUDA compatible device was not found!"); - InitFailedDialog.setCancelable(false); // This blocks the 'BACK' button - InitFailedDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - Tutorial4Activity.this.finish(); - } - }); - InitFailedDialog.show(); - } - else - { - // Load native library after(!) OpenCV initialization - Log.i(TAG, "Found CUDA capable device!"); - System.loadLibrary("cuda_sample"); - mOpenCvCameraView.enableView(); - } } break; default: { @@ -120,7 +97,32 @@ public class Tutorial4Activity extends Activity implements CvCameraViewListener2 public void onResume() { super.onResume(); - OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_8, this, mLoaderCallback); + if (OpenCVLoader.initDebug(true)) + { + // Check CUDA support + if (Gpu.getCudaEnabledDeviceCount() <= 0) + { + Log.e(TAG, "No CUDA capable device found!"); + AlertDialog InitFailedDialog = new AlertDialog.Builder(Tutorial4Activity.this).create(); + InitFailedDialog.setTitle("OpenCV CUDA error"); + InitFailedDialog.setMessage("CUDA compatible device was not found!"); + InitFailedDialog.setCancelable(false); // This blocks the 'BACK' button + InitFailedDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Tutorial4Activity.this.finish(); + } + }); + InitFailedDialog.show(); + } + else + { + // Load native library after(!) OpenCV initialization + Log.i(TAG, "Found CUDA capable device!"); + System.loadLibrary("cuda_sample"); + mOpenCvCameraView.enableView(); + } + + } } public void onDestroy() { From da44a2fac1c0be45d2a987c165298a9629757723 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Wed, 5 Feb 2014 15:59:13 +0400 Subject: [PATCH 088/293] Revert "disable gpu Subtract_Array test:" This reverts commit e91bf95d5832e87aa70240c50f0bf7fcc587e8c8. --- modules/gpu/test/test_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gpu/test/test_core.cpp b/modules/gpu/test/test_core.cpp index 2f1bbd7b2c..1edc69b971 100644 --- a/modules/gpu/test/test_core.cpp +++ b/modules/gpu/test/test_core.cpp @@ -422,7 +422,7 @@ PARAM_TEST_CASE(Subtract_Array, cv::gpu::DeviceInfo, cv::Size, std::pair Date: Thu, 6 Feb 2014 10:12:20 +0400 Subject: [PATCH 089/293] Revert "disable gpu CvtColor.*2HSV tests:" This reverts commit 952027a8536215a5e4308c79a59308c6b3426354. --- modules/gpu/test/test_color.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/gpu/test/test_color.cpp b/modules/gpu/test/test_color.cpp index 16f5fc84ea..3b4b326e4d 100644 --- a/modules/gpu/test/test_color.cpp +++ b/modules/gpu/test/test_color.cpp @@ -840,7 +840,7 @@ GPU_TEST_P(CvtColor, YCrCb42RGBA) EXPECT_MAT_NEAR(dst_gold, dst, 1e-5); } -GPU_TEST_P(CvtColor, DISABLED_BGR2HSV) +GPU_TEST_P(CvtColor, BGR2HSV) { if (depth == CV_16U) return; @@ -856,7 +856,7 @@ GPU_TEST_P(CvtColor, DISABLED_BGR2HSV) EXPECT_MAT_NEAR(dst_gold, dst, depth == CV_32F ? 1e-2 : 1); } -GPU_TEST_P(CvtColor, DISABLED_RGB2HSV) +GPU_TEST_P(CvtColor, RGB2HSV) { if (depth == CV_16U) return; @@ -872,7 +872,7 @@ GPU_TEST_P(CvtColor, DISABLED_RGB2HSV) EXPECT_MAT_NEAR(dst_gold, dst, depth == CV_32F ? 1e-2 : 1); } -GPU_TEST_P(CvtColor, DISABLED_RGB2HSV4) +GPU_TEST_P(CvtColor, RGB2HSV4) { if (depth == CV_16U) return; @@ -896,7 +896,7 @@ GPU_TEST_P(CvtColor, DISABLED_RGB2HSV4) EXPECT_MAT_NEAR(dst_gold, h_dst, depth == CV_32F ? 1e-2 : 1); } -GPU_TEST_P(CvtColor, DISABLED_RGBA2HSV4) +GPU_TEST_P(CvtColor, RGBA2HSV4) { if (depth == CV_16U) return; From 3e4bb371c8a364315dec18df14674d9164b7523d Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 6 Feb 2014 12:41:19 +0400 Subject: [PATCH 090/293] fix epsilons for several gpu tests --- modules/gpu/test/test_color.cpp | 8 ++++---- modules/gpu/test/test_core.cpp | 4 ++-- modules/gpu/test/test_gpumat.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/gpu/test/test_color.cpp b/modules/gpu/test/test_color.cpp index 3b4b326e4d..321785ffe0 100644 --- a/modules/gpu/test/test_color.cpp +++ b/modules/gpu/test/test_color.cpp @@ -715,7 +715,7 @@ GPU_TEST_P(CvtColor, BGR2YCrCb) cv::Mat dst_gold; cv::cvtColor(src, dst_gold, cv::COLOR_BGR2YCrCb); - EXPECT_MAT_NEAR(dst_gold, dst, 1.0); + EXPECT_MAT_NEAR(dst_gold, dst, depth == CV_32F ? 1e-2 : 1); } GPU_TEST_P(CvtColor, RGB2YCrCb) @@ -728,7 +728,7 @@ GPU_TEST_P(CvtColor, RGB2YCrCb) cv::Mat dst_gold; cv::cvtColor(src, dst_gold, cv::COLOR_RGB2YCrCb); - EXPECT_MAT_NEAR(dst_gold, dst, 1.0); + EXPECT_MAT_NEAR(dst_gold, dst, depth == CV_32F ? 1e-2 : 1); } GPU_TEST_P(CvtColor, BGR2YCrCb4) @@ -749,7 +749,7 @@ GPU_TEST_P(CvtColor, BGR2YCrCb4) cv::split(h_dst, channels); cv::merge(channels, 3, h_dst); - EXPECT_MAT_NEAR(dst_gold, h_dst, 1.0); + EXPECT_MAT_NEAR(dst_gold, h_dst, depth == CV_32F ? 1e-2 : 1); } GPU_TEST_P(CvtColor, RGBA2YCrCb4) @@ -771,7 +771,7 @@ GPU_TEST_P(CvtColor, RGBA2YCrCb4) cv::split(h_dst, channels); cv::merge(channels, 3, h_dst); - EXPECT_MAT_NEAR(dst_gold, h_dst, 1.0); + EXPECT_MAT_NEAR(dst_gold, h_dst, depth == CV_32F ? 1e-2 : 1); } GPU_TEST_P(CvtColor, YCrCb2BGR) diff --git a/modules/gpu/test/test_core.cpp b/modules/gpu/test/test_core.cpp index 1edc69b971..7ceeaedf08 100644 --- a/modules/gpu/test/test_core.cpp +++ b/modules/gpu/test/test_core.cpp @@ -3582,7 +3582,7 @@ GPU_TEST_P(Normalize, WithOutMask) cv::Mat dst_gold; cv::normalize(src, dst_gold, alpha, beta, norm_type, type); - EXPECT_MAT_NEAR(dst_gold, dst, 1.0); + EXPECT_MAT_NEAR(dst_gold, dst, type < CV_32F ? 1.0 : 1e-4); } GPU_TEST_P(Normalize, WithMask) @@ -3598,7 +3598,7 @@ GPU_TEST_P(Normalize, WithMask) dst_gold.setTo(cv::Scalar::all(0)); cv::normalize(src, dst_gold, alpha, beta, norm_type, type, mask); - EXPECT_MAT_NEAR(dst_gold, dst, 1.0); + EXPECT_MAT_NEAR(dst_gold, dst, type < CV_32F ? 1.0 : 1e-4); } INSTANTIATE_TEST_CASE_P(GPU_Core, Normalize, testing::Combine( diff --git a/modules/gpu/test/test_gpumat.cpp b/modules/gpu/test/test_gpumat.cpp index 210b6a4415..fee264341b 100644 --- a/modules/gpu/test/test_gpumat.cpp +++ b/modules/gpu/test/test_gpumat.cpp @@ -281,7 +281,7 @@ GPU_TEST_P(ConvertTo, WithOutScaling) cv::Mat dst_gold; src.convertTo(dst_gold, depth2); - EXPECT_MAT_NEAR(dst_gold, dst, 1.0); + EXPECT_MAT_NEAR(dst_gold, dst, depth2 < CV_32F ? 1.0 : 1e-4); } } From 5d099df57864d083881f026ffe32637afac6ba2e Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 6 Feb 2014 16:58:40 +0400 Subject: [PATCH 091/293] Revert "disable CUDA generalized Hough Transform" This reverts commit 33d42b740c6fe938b63a0b25c9ad51741aba48c3. --- modules/gpu/src/cuda/generalized_hough.cu | 2 -- modules/gpu/src/generalized_hough.cpp | 2 -- modules/gpu/test/test_hough.cpp | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/gpu/src/cuda/generalized_hough.cu b/modules/gpu/src/cuda/generalized_hough.cu index 35451e7e23..5e2041eae4 100644 --- a/modules/gpu/src/cuda/generalized_hough.cu +++ b/modules/gpu/src/cuda/generalized_hough.cu @@ -40,8 +40,6 @@ // //M*/ -#define CUDA_DISABLER - #if !defined CUDA_DISABLER #include diff --git a/modules/gpu/src/generalized_hough.cpp b/modules/gpu/src/generalized_hough.cpp index 867ba7ee71..a92c37d1a5 100644 --- a/modules/gpu/src/generalized_hough.cpp +++ b/modules/gpu/src/generalized_hough.cpp @@ -40,8 +40,6 @@ // //M*/ -#define CUDA_DISABLER - #include "precomp.hpp" using namespace std; diff --git a/modules/gpu/test/test_hough.cpp b/modules/gpu/test/test_hough.cpp index 6441fc69ab..f876a7a2b0 100644 --- a/modules/gpu/test/test_hough.cpp +++ b/modules/gpu/test/test_hough.cpp @@ -189,7 +189,7 @@ PARAM_TEST_CASE(GeneralizedHough, cv::gpu::DeviceInfo, UseRoi) { }; -GPU_TEST_P(GeneralizedHough, DISABLED_POSITION) +GPU_TEST_P(GeneralizedHough, POSITION) { const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); cv::gpu::setDevice(devInfo.deviceID()); From b10d4b05ed3eb9947bcb3dd183da69e44be5e30a Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 7 Feb 2014 12:46:24 +0400 Subject: [PATCH 092/293] dynamicuda module disabled in OpenCv.mk for all arches except armeabi-v7a. --- cmake/templates/OpenCV.mk.in | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmake/templates/OpenCV.mk.in b/cmake/templates/OpenCV.mk.in index 97330e8545..16fc4c9ccb 100644 --- a/cmake/templates/OpenCV.mk.in +++ b/cmake/templates/OpenCV.mk.in @@ -19,7 +19,6 @@ OPENCV_3RDPARTY_LIBS_DIR:=@OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE@ OPENCV_BASEDIR:=@OPENCV_BASE_INCLUDE_DIR_CONFIGCMAKE@ OPENCV_LOCAL_C_INCLUDES:=@OPENCV_INCLUDE_DIRS_CONFIGCMAKE@ OPENCV_MODULES:=@OPENCV_MODULES_CONFIGMAKE@ -OPENCV_DYNAMICUDA_MODULE:=@OPENCV_DYNAMICUDA_MODULE_CONFIGMAKE@ OPENCV_HAVE_GPU_MODULE:=@OPENCV_HAVE_GPU_MODULE_CONFIGMAKE@ OPENCV_USE_GPU_MODULE:= @@ -30,6 +29,9 @@ ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) OPENCV_USE_GPU_MODULE:=on endif endif + OPENCV_DYNAMICUDA_MODULE:=@OPENCV_DYNAMICUDA_MODULE_CONFIGMAKE@ +else + OPENCV_DYNAMICUDA_MODULE:= endif CUDA_RUNTIME_LIBS:=@CUDA_RUNTIME_LIBS_CONFIGMAKE@ @@ -124,7 +126,9 @@ ifeq ($(OPENCV_MK_$(OPENCV_TARGET_ARCH_ABI)_ALREADY_INCLUDED),) ifeq ($(OPENCV_INSTALL_MODULES),on) $(foreach module,$(OPENCV_LIBS),$(eval $(call add_opencv_module,$(module)))) ifneq ($(OPENCV_DYNAMICUDA_MODULE),) - $(eval $(call add_opencv_module,$(OPENCV_DYNAMICUDA_MODULE))) + ifeq ($(OPENCV_LIB_TYPE),SHARED) + $(eval $(call add_opencv_module,$(OPENCV_DYNAMICUDA_MODULE))) + endif endif endif From 3e1f74f2cafc5c38d0e64928149edb87d9b28d28 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 7 Feb 2014 13:40:37 +0400 Subject: [PATCH 093/293] fix nonfree module compilation without CUDA --- modules/nonfree/src/precomp.hpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/modules/nonfree/src/precomp.hpp b/modules/nonfree/src/precomp.hpp index 28531390d6..6311ee2aa9 100644 --- a/modules/nonfree/src/precomp.hpp +++ b/modules/nonfree/src/precomp.hpp @@ -51,17 +51,14 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/core/internal.hpp" -#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) - #include "opencv2/nonfree/gpu.hpp" +#include "opencv2/nonfree/gpu.hpp" - #if defined(HAVE_CUDA) - #include "opencv2/gpu/stream_accessor.hpp" - #include "opencv2/gpu/device/common.hpp" - - static inline void throw_nogpu() { CV_Error(CV_StsNotImplemented, "The called functionality is disabled for current build or platform"); } - #else - static inline void throw_nogpu() { CV_Error(CV_GpuNotSupported, "The library is compiled without GPU support"); } - #endif +#if defined(HAVE_CUDA) && defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) + #include "opencv2/gpu/stream_accessor.hpp" + #include "opencv2/gpu/device/common.hpp" + static inline void throw_nogpu() { CV_Error(CV_StsNotImplemented, "The called functionality is disabled for current build or platform"); } +#else + static inline void throw_nogpu() { CV_Error(CV_GpuNotSupported, "The library is compiled without GPU support"); } #endif #ifdef HAVE_OPENCV_OCL From b86088b89c43ba1b7db2d8435f222489722e62c9 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 6 Feb 2014 23:36:23 +0400 Subject: [PATCH 094/293] Implicit testdata directory permissions setup added. --- data/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 2f10c82f64..998e78520c 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -13,6 +13,10 @@ if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH) if(ANDROID) install(DIRECTORY ${OPENCV_TEST_DATA_PATH} DESTINATION sdk/etc/testdata COMPONENT tests) elseif(NOT WIN32) - install(DIRECTORY ${OPENCV_TEST_DATA_PATH} DESTINATION share/OpenCV/testdata COMPONENT tests) + # CPack does not set correct permissions by default, so we do it explicitly. + install(DIRECTORY ${OPENCV_TEST_DATA_PATH} + DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + DESTINATION share/OpenCV/testdata COMPONENT tests) endif() endif() \ No newline at end of file From bfc27271e2543bb0807f6dc000f770993a740581 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 7 Feb 2014 14:55:06 +0400 Subject: [PATCH 095/293] Revert "disable gpu Canny and HoughCircles perf tests:" This reverts commit dbce90692acd84fbf46bde4da4b1726049f42857. --- modules/gpu/perf/perf_imgproc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 8ad020d105..1e598297a7 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -672,7 +672,7 @@ PERF_TEST_P(Sz, ImgProc_ColumnSum, DEF_PARAM_TEST(Image_AppertureSz_L2gradient, string, int, bool); -PERF_TEST_P(Image_AppertureSz_L2gradient, DISABLED_ImgProc_Canny, +PERF_TEST_P(Image_AppertureSz_L2gradient, ImgProc_Canny, Combine(Values("perf/800x600.png", "perf/1280x1024.png", "perf/1680x1050.png"), Values(3, 5), Bool())) @@ -1777,7 +1777,7 @@ PERF_TEST_P(Image, ImgProc_HoughLinesP, DEF_PARAM_TEST(Sz_Dp_MinDist, cv::Size, float, float); -PERF_TEST_P(Sz_Dp_MinDist, DISABLED_ImgProc_HoughCircles, +PERF_TEST_P(Sz_Dp_MinDist, ImgProc_HoughCircles, Combine(GPU_TYPICAL_MAT_SIZES, Values(1.0f, 2.0f, 4.0f), Values(1.0f))) From 5dbdadb769e97f47b64655b5b3144787c57e2740 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 7 Feb 2014 16:04:29 +0400 Subject: [PATCH 096/293] fixed several bugs in CUDA Canny implementation: * out of border access in edgesHysteresisLocalKernel * incorrect usage of atomicAdd --- modules/gpu/src/cuda/canny.cu | 49 +++++++++++++++++++++-------------- modules/gpu/src/imgproc.cpp | 14 +++++----- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/modules/gpu/src/cuda/canny.cu b/modules/gpu/src/cuda/canny.cu index aab922f22c..2ab260ec26 100644 --- a/modules/gpu/src/cuda/canny.cu +++ b/modules/gpu/src/cuda/canny.cu @@ -239,30 +239,35 @@ namespace canny { __device__ int counter = 0; - __global__ void edgesHysteresisLocalKernel(PtrStepSzi map, ushort2* st) + __device__ __forceinline__ bool checkIdx(int y, int x, int rows, int cols) + { + return (y >= 0) && (y < rows) && (x >= 0) && (x < cols); + } + + __global__ void edgesHysteresisLocalKernel(PtrStepSzi map, short2* st) { __shared__ volatile int smem[18][18]; const int x = blockIdx.x * blockDim.x + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; - smem[threadIdx.y + 1][threadIdx.x + 1] = x < map.cols && y < map.rows ? map(y, x) : 0; + smem[threadIdx.y + 1][threadIdx.x + 1] = checkIdx(y, x, map.rows, map.cols) ? map(y, x) : 0; if (threadIdx.y == 0) - smem[0][threadIdx.x + 1] = y > 0 ? map(y - 1, x) : 0; + smem[0][threadIdx.x + 1] = checkIdx(y - 1, x, map.rows, map.cols) ? map(y - 1, x) : 0; if (threadIdx.y == blockDim.y - 1) - smem[blockDim.y + 1][threadIdx.x + 1] = y + 1 < map.rows ? map(y + 1, x) : 0; + smem[blockDim.y + 1][threadIdx.x + 1] = checkIdx(y + 1, x, map.rows, map.cols) ? map(y + 1, x) : 0; if (threadIdx.x == 0) - smem[threadIdx.y + 1][0] = x > 0 ? map(y, x - 1) : 0; + smem[threadIdx.y + 1][0] = checkIdx(y, x - 1, map.rows, map.cols) ? map(y, x - 1) : 0; if (threadIdx.x == blockDim.x - 1) - smem[threadIdx.y + 1][blockDim.x + 1] = x + 1 < map.cols ? map(y, x + 1) : 0; + smem[threadIdx.y + 1][blockDim.x + 1] = checkIdx(y, x + 1, map.rows, map.cols) ? map(y, x + 1) : 0; if (threadIdx.x == 0 && threadIdx.y == 0) - smem[0][0] = y > 0 && x > 0 ? map(y - 1, x - 1) : 0; + smem[0][0] = checkIdx(y - 1, x - 1, map.rows, map.cols) ? map(y - 1, x - 1) : 0; if (threadIdx.x == blockDim.x - 1 && threadIdx.y == 0) - smem[0][blockDim.x + 1] = y > 0 && x + 1 < map.cols ? map(y - 1, x + 1) : 0; + smem[0][blockDim.x + 1] = checkIdx(y - 1, x + 1, map.rows, map.cols) ? map(y - 1, x + 1) : 0; if (threadIdx.x == 0 && threadIdx.y == blockDim.y - 1) - smem[blockDim.y + 1][0] = y + 1 < map.rows && x > 0 ? map(y + 1, x - 1) : 0; + smem[blockDim.y + 1][0] = checkIdx(y + 1, x - 1, map.rows, map.cols) ? map(y + 1, x - 1) : 0; if (threadIdx.x == blockDim.x - 1 && threadIdx.y == blockDim.y - 1) - smem[blockDim.y + 1][blockDim.x + 1] = y + 1 < map.rows && x + 1 < map.cols ? map(y + 1, x + 1) : 0; + smem[blockDim.y + 1][blockDim.x + 1] = checkIdx(y + 1, x + 1, map.rows, map.cols) ? map(y + 1, x + 1) : 0; __syncthreads(); @@ -317,11 +322,11 @@ namespace canny if (n > 0) { const int ind = ::atomicAdd(&counter, 1); - st[ind] = make_ushort2(x, y); + st[ind] = make_short2(x, y); } } - void edgesHysteresisLocal(PtrStepSzi map, ushort2* st1) + void edgesHysteresisLocal(PtrStepSzi map, short2* st1) { void* counter_ptr; cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, counter) ); @@ -345,13 +350,13 @@ namespace canny __constant__ int c_dx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; __constant__ int c_dy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; - __global__ void edgesHysteresisGlobalKernel(PtrStepSzi map, ushort2* st1, ushort2* st2, const int count) + __global__ void edgesHysteresisGlobalKernel(PtrStepSzi map, short2* st1, short2* st2, const int count) { const int stack_size = 512; __shared__ int s_counter; __shared__ int s_ind; - __shared__ ushort2 s_st[stack_size]; + __shared__ short2 s_st[stack_size]; if (threadIdx.x == 0) s_counter = 0; @@ -363,14 +368,14 @@ namespace canny if (ind >= count) return; - ushort2 pos = st1[ind]; + short2 pos = st1[ind]; if (threadIdx.x < 8) { pos.x += c_dx[threadIdx.x]; pos.y += c_dy[threadIdx.x]; - if (pos.x > 0 && pos.x < map.cols && pos.y > 0 && pos.y < map.rows && map(pos.y, pos.x) == 1) + if (pos.x > 0 && pos.x < map.cols - 1 && pos.y > 0 && pos.y < map.rows - 1 && map(pos.y, pos.x) == 1) { map(pos.y, pos.x) = 2; @@ -402,7 +407,7 @@ namespace canny pos.x += c_dx[threadIdx.x & 7]; pos.y += c_dy[threadIdx.x & 7]; - if (pos.x > 0 && pos.x < map.cols && pos.y > 0 && pos.y < map.rows && map(pos.y, pos.x) == 1) + if (pos.x > 0 && pos.x < map.cols - 1 && pos.y > 0 && pos.y < map.rows - 1 && map(pos.y, pos.x) == 1) { map(pos.y, pos.x) = 2; @@ -419,8 +424,10 @@ namespace canny { if (threadIdx.x == 0) { - ind = ::atomicAdd(&counter, s_counter); - s_ind = ind - s_counter; + s_ind = ::atomicAdd(&counter, s_counter); + + if (s_ind + s_counter > map.cols * map.rows) + s_counter = 0; } __syncthreads(); @@ -432,7 +439,7 @@ namespace canny } } - void edgesHysteresisGlobal(PtrStepSzi map, ushort2* st1, ushort2* st2) + void edgesHysteresisGlobal(PtrStepSzi map, short2* st1, short2* st2) { void* counter_ptr; cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, canny::counter) ); @@ -454,6 +461,8 @@ namespace canny cudaSafeCall( cudaMemcpy(&count, counter_ptr, sizeof(int), cudaMemcpyDeviceToHost) ); + count = min(count, map.cols * map.rows); + std::swap(st1, st2); } } diff --git a/modules/gpu/src/imgproc.cpp b/modules/gpu/src/imgproc.cpp index 97adb685ff..66f838f77a 100644 --- a/modules/gpu/src/imgproc.cpp +++ b/modules/gpu/src/imgproc.cpp @@ -1491,6 +1491,8 @@ void cv::gpu::convolve(const GpuMat& image, const GpuMat& templ, GpuMat& result, void cv::gpu::CannyBuf::create(const Size& image_size, int apperture_size) { + CV_Assert(image_size.width < std::numeric_limits::max() && image_size.height < std::numeric_limits::max()); + if (apperture_size > 0) { ensureSizeIsEnough(image_size, CV_32SC1, dx); @@ -1506,8 +1508,8 @@ void cv::gpu::CannyBuf::create(const Size& image_size, int apperture_size) ensureSizeIsEnough(image_size, CV_32FC1, mag); ensureSizeIsEnough(image_size, CV_32SC1, map); - ensureSizeIsEnough(1, image_size.area(), CV_16UC2, st1); - ensureSizeIsEnough(1, image_size.area(), CV_16UC2, st2); + ensureSizeIsEnough(1, image_size.area(), CV_16SC2, st1); + ensureSizeIsEnough(1, image_size.area(), CV_16SC2, st2); } void cv::gpu::CannyBuf::release() @@ -1527,9 +1529,9 @@ namespace canny void calcMap(PtrStepSzi dx, PtrStepSzi dy, PtrStepSzf mag, PtrStepSzi map, float low_thresh, float high_thresh); - void edgesHysteresisLocal(PtrStepSzi map, ushort2* st1); + void edgesHysteresisLocal(PtrStepSzi map, short2* st1); - void edgesHysteresisGlobal(PtrStepSzi map, ushort2* st1, ushort2* st2); + void edgesHysteresisGlobal(PtrStepSzi map, short2* st1, short2* st2); void getEdges(PtrStepSzi map, PtrStepSzb dst); } @@ -1543,9 +1545,9 @@ namespace buf.map.setTo(Scalar::all(0)); calcMap(dx, dy, buf.mag, buf.map, low_thresh, high_thresh); - edgesHysteresisLocal(buf.map, buf.st1.ptr()); + edgesHysteresisLocal(buf.map, buf.st1.ptr()); - edgesHysteresisGlobal(buf.map, buf.st1.ptr(), buf.st2.ptr()); + edgesHysteresisGlobal(buf.map, buf.st1.ptr(), buf.st2.ptr()); getEdges(buf.map, dst); } From 8b44a42a403548c244aaea6852fb09935a0741e9 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 10 Feb 2014 11:50:14 +0400 Subject: [PATCH 097/293] decrease input size for several gpu tests to fix "timed out" error: * BruteForceNonLocalMeans * OpticalFlowBM --- modules/gpu/test/test_denoising.cpp | 3 +++ modules/gpu/test/test_optflow.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/gpu/test/test_denoising.cpp b/modules/gpu/test/test_denoising.cpp index e480cf4680..e416e92597 100644 --- a/modules/gpu/test/test_denoising.cpp +++ b/modules/gpu/test/test_denoising.cpp @@ -114,6 +114,7 @@ GPU_TEST_P(BruteForceNonLocalMeans, Regression) cv::Mat bgr = readImage("denoising/lena_noised_gaussian_sigma=20_multi_0.png", cv::IMREAD_COLOR); ASSERT_FALSE(bgr.empty()); + cv::resize(bgr, bgr, cv::Size(256, 256)); cv::Mat gray; cv::cvtColor(bgr, gray, CV_BGR2GRAY); @@ -130,6 +131,8 @@ GPU_TEST_P(BruteForceNonLocalMeans, Regression) cv::Mat bgr_gold = readImage("denoising/nlm_denoised_lena_bgr.png", cv::IMREAD_COLOR); cv::Mat gray_gold = readImage("denoising/nlm_denoised_lena_gray.png", cv::IMREAD_GRAYSCALE); ASSERT_FALSE(bgr_gold.empty() || gray_gold.empty()); + cv::resize(bgr_gold, bgr_gold, cv::Size(256, 256)); + cv::resize(gray_gold, gray_gold, cv::Size(256, 256)); EXPECT_MAT_NEAR(bgr_gold, dbgr, 1e-4); EXPECT_MAT_NEAR(gray_gold, dgray, 1e-4); diff --git a/modules/gpu/test/test_optflow.cpp b/modules/gpu/test/test_optflow.cpp index 53b93a096b..571403d2a7 100644 --- a/modules/gpu/test/test_optflow.cpp +++ b/modules/gpu/test/test_optflow.cpp @@ -483,13 +483,15 @@ GPU_TEST_P(OpticalFlowBM, Accuracy) cv::Mat frame0 = readImage("opticalflow/rubberwhale1.png", cv::IMREAD_GRAYSCALE); ASSERT_FALSE(frame0.empty()); + cv::resize(frame0, frame0, cv::Size(), 0.5, 0.5); cv::Mat frame1 = readImage("opticalflow/rubberwhale2.png", cv::IMREAD_GRAYSCALE); ASSERT_FALSE(frame1.empty()); + cv::resize(frame1, frame1, cv::Size(), 0.5, 0.5); - cv::Size block_size(16, 16); + cv::Size block_size(8, 8); cv::Size shift_size(1, 1); - cv::Size max_range(16, 16); + cv::Size max_range(8, 8); cv::gpu::GpuMat d_velx, d_vely, buf; cv::gpu::calcOpticalFlowBM(loadMat(frame0), loadMat(frame1), From becbfa62812710cf2ef5becd3322374deb777769 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Mon, 10 Feb 2014 13:41:27 +0400 Subject: [PATCH 098/293] Fixed Cl.exe hang for VS2008 x64 with Eigen::exp() function --- modules/contrib/src/rgbdodometry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/contrib/src/rgbdodometry.cpp b/modules/contrib/src/rgbdodometry.cpp index 4e9d8c4df9..0b2b9518c4 100644 --- a/modules/contrib/src/rgbdodometry.cpp +++ b/modules/contrib/src/rgbdodometry.cpp @@ -115,7 +115,7 @@ void computeProjectiveMatrix( const Mat& ksi, Mat& Rt ) { CV_Assert( ksi.size() == Size(1,6) && ksi.type() == CV_64FC1 ); -#if defined(HAVE_EIGEN) && EIGEN_WORLD_VERSION == 3 +#if defined(HAVE_EIGEN) && EIGEN_WORLD_VERSION == 3 && (!defined _MSC_VER || !defined _M_X64 || _MSC_VER > 1500) const double* ksi_ptr = reinterpret_cast(ksi.ptr(0)); Eigen::Matrix twist, g; twist << 0., -ksi_ptr[2], ksi_ptr[1], ksi_ptr[3], From eb8728d72751d78342d2dbb9fb11110380cf6c50 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 2 Feb 2014 19:00:54 +0400 Subject: [PATCH 099/293] Updated cloud io functionality - removed dependency from VTK legacy --- modules/viz/src/precomp.hpp | 4 +- modules/viz/src/vizcore.cpp | 4 +- modules/viz/src/vtk/vtkCloudMatSink.cpp | 16 ++++ modules/viz/src/vtk/vtkCloudMatSink.h | 12 ++- modules/viz/src/vtk/vtkOBJWriter.cpp | 37 +++++++- modules/viz/src/vtk/vtkOBJWriter.h | 22 +++-- modules/viz/src/vtk/vtkXYZReader.cpp | 107 ++++++++++++++++++++++++ modules/viz/src/vtk/vtkXYZReader.h | 80 ++++++++++++++++++ modules/viz/src/vtk/vtkXYZWriter.cpp | 37 +++++++- modules/viz/src/vtk/vtkXYZWriter.h | 18 +++- 10 files changed, 313 insertions(+), 24 deletions(-) create mode 100644 modules/viz/src/vtk/vtkXYZReader.cpp create mode 100644 modules/viz/src/vtk/vtkXYZReader.h diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp index d9681ce83b..2b34f56389 100644 --- a/modules/viz/src/precomp.hpp +++ b/modules/viz/src/precomp.hpp @@ -76,7 +76,6 @@ #include #include #include -#include #include #include #include @@ -115,11 +114,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -142,6 +139,7 @@ #include #include +#include #include #include #include diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp index b4ec83bd44..b8cad1ca68 100644 --- a/modules/viz/src/vizcore.cpp +++ b/modules/viz/src/vizcore.cpp @@ -173,8 +173,8 @@ cv::Mat cv::viz::readCloud(const String& file, OutputArray colors, OutputArray n vtkSmartPointer reader; if (extention == ".xyz") { - reader = vtkSmartPointer::New(); - vtkSimplePointsReader::SafeDownCast(reader)->SetFileName(file.c_str()); + reader = vtkSmartPointer::New(); + vtkXYZReader::SafeDownCast(reader)->SetFileName(file.c_str()); } else if (extention == ".ply") { diff --git a/modules/viz/src/vtk/vtkCloudMatSink.cpp b/modules/viz/src/vtk/vtkCloudMatSink.cpp index 09ef0cca99..7f47022c34 100644 --- a/modules/viz/src/vtk/vtkCloudMatSink.cpp +++ b/modules/viz/src/vtk/vtkCloudMatSink.cpp @@ -156,3 +156,19 @@ void cv::viz::vtkCloudMatSink::PrintSelf(ostream& os, vtkIndent indent) os << indent << "Colors: " << colors.needed() << "\n"; os << indent << "Normals: " << normals.needed() << "\n"; } + +int cv::viz::vtkCloudMatSink::FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + return 1; +} + +vtkPolyData* cv::viz::vtkCloudMatSink::GetInput() +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput()); +} + +vtkPolyData* cv::viz::vtkCloudMatSink::GetInput(int port) +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput(port)); +} diff --git a/modules/viz/src/vtk/vtkCloudMatSink.h b/modules/viz/src/vtk/vtkCloudMatSink.h index 44d7e52a5a..b3fd3eb719 100644 --- a/modules/viz/src/vtk/vtkCloudMatSink.h +++ b/modules/viz/src/vtk/vtkCloudMatSink.h @@ -46,26 +46,32 @@ #define __vtkCloudMatSink_h #include -#include +#include namespace cv { namespace viz { - class vtkCloudMatSink : public vtkPolyDataWriter + class vtkCloudMatSink : public vtkWriter { public: static vtkCloudMatSink *New(); - vtkTypeMacro(vtkCloudMatSink,vtkPolyDataWriter) + vtkTypeMacro(vtkCloudMatSink,vtkWriter) void PrintSelf(ostream& os, vtkIndent indent); void SetOutput(OutputArray cloud, OutputArray colors = noArray(), OutputArray normals = noArray(), OutputArray tcoords = noArray()); + // Description: + // Get the input to this writer. + vtkPolyData* GetInput(); + vtkPolyData* GetInput(int port); + protected: vtkCloudMatSink(); ~vtkCloudMatSink(); void WriteData(); + int FillInputPortInformation(int port, vtkInformation *info); _OutputArray cloud, colors, normals, tcoords; diff --git a/modules/viz/src/vtk/vtkOBJWriter.cpp b/modules/viz/src/vtk/vtkOBJWriter.cpp index 452ad19a7a..e92424c665 100644 --- a/modules/viz/src/vtk/vtkOBJWriter.cpp +++ b/modules/viz/src/vtk/vtkOBJWriter.cpp @@ -54,7 +54,6 @@ cv::viz::vtkOBJWriter::vtkOBJWriter() std::ofstream fout; // only used to extract the default precision this->DecimalPrecision = fout.precision(); this->FileName = NULL; - this->FileType = VTK_ASCII; } cv::viz::vtkOBJWriter::~vtkOBJWriter(){} @@ -65,9 +64,22 @@ void cv::viz::vtkOBJWriter::WriteData() if (!input) return; - std::ostream *outfilep = this->OpenVTKFile(); - if (!outfilep) + if (!this->FileName ) + { + vtkErrorMacro(<< "No FileName specified! Can't write!"); + this->SetErrorCode(vtkErrorCode::NoFileNameError); return; + } + + vtkDebugMacro(<<"Opening vtk file for writing..."); + ostream *outfilep = new ofstream(this->FileName, ios::out); + if (outfilep->fail()) + { + vtkErrorMacro(<< "Unable to open file: "<< this->FileName); + this->SetErrorCode(vtkErrorCode::CannotOpenFileError); + delete outfilep; + return; + } std::ostream& outfile = *outfilep; @@ -224,7 +236,8 @@ void cv::viz::vtkOBJWriter::WriteData() } } /* if (input->GetNumberOfStrips() > 0) */ - this->CloseVTKFile(outfilep); + vtkDebugMacro(<<"Closing vtk file\n"); + delete outfilep; // Delete the file if an error occurred if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) @@ -239,3 +252,19 @@ void cv::viz::vtkOBJWriter::PrintSelf(ostream& os, vtkIndent indent) Superclass::PrintSelf(os, indent); os << indent << "DecimalPrecision: " << DecimalPrecision << "\n"; } + +int cv::viz::vtkOBJWriter::FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + return 1; +} + +vtkPolyData* cv::viz::vtkOBJWriter::GetInput() +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput()); +} + +vtkPolyData* cv::viz::vtkOBJWriter::GetInput(int port) +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput(port)); +} diff --git a/modules/viz/src/vtk/vtkOBJWriter.h b/modules/viz/src/vtk/vtkOBJWriter.h index f8889884d7..7ad0f17b15 100644 --- a/modules/viz/src/vtk/vtkOBJWriter.h +++ b/modules/viz/src/vtk/vtkOBJWriter.h @@ -45,29 +45,41 @@ #ifndef __vtkOBJWriter_h #define __vtkOBJWriter_h -#include +#include namespace cv { namespace viz { - class vtkOBJWriter : public vtkPolyDataWriter + class vtkOBJWriter : public vtkWriter { public: static vtkOBJWriter *New(); - vtkTypeMacro(vtkOBJWriter,vtkPolyDataWriter) + vtkTypeMacro(vtkOBJWriter,vtkWriter) void PrintSelf(ostream& os, vtkIndent indent); - vtkGetMacro(DecimalPrecision, int); - vtkSetMacro(DecimalPrecision, int); + vtkGetMacro(DecimalPrecision, int) + vtkSetMacro(DecimalPrecision, int) + + // Description: + // Specify file name of data file to write. + vtkSetStringMacro(FileName) + vtkGetStringMacro(FileName) + + // Description: + // Get the input to this writer. + vtkPolyData* GetInput(); + vtkPolyData* GetInput(int port); protected: vtkOBJWriter(); ~vtkOBJWriter(); void WriteData(); + int FillInputPortInformation(int port, vtkInformation *info); int DecimalPrecision; + char *FileName; private: vtkOBJWriter(const vtkOBJWriter&); // Not implemented. diff --git a/modules/viz/src/vtk/vtkXYZReader.cpp b/modules/viz/src/vtk/vtkXYZReader.cpp new file mode 100644 index 0000000000..283a592489 --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZReader.cpp @@ -0,0 +1,107 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkXYZReader); +}} + + +cv::viz::vtkXYZReader::vtkXYZReader() +{ + this->FileName = 0; + this->SetNumberOfInputPorts(0); +} + +cv::viz::vtkXYZReader::~vtkXYZReader() +{ + this->SetFileName(0); +} + +void cv::viz::vtkXYZReader::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + os << indent << "FileName: " << (this->FileName ? this->FileName : "(none)") << "\n"; +} + +int cv::viz::vtkXYZReader::RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector* outputVector) +{ + // Make sure we have a file to read. + if(!this->FileName) + { + vtkErrorMacro("A FileName must be specified."); + return 0; + } + + // Open the input file. + ifstream fin(this->FileName); + if(!fin) + { + vtkErrorMacro("Error opening file " << this->FileName); + return 0; + } + + // Allocate objects to hold points and vertex cells. + vtkSmartPointer points = vtkSmartPointer::New(); + vtkSmartPointer verts = vtkSmartPointer::New(); + + // Read points from the file. + vtkDebugMacro("Reading points from file " << this->FileName); + double x[3]; + while(fin >> x[0] >> x[1] >> x[2]) + { + vtkIdType id = points->InsertNextPoint(x); + verts->InsertNextCell(1, &id); + } + vtkDebugMacro("Read " << points->GetNumberOfPoints() << " points."); + + // Store the points and cells in the output data object. + vtkPolyData* output = vtkPolyData::GetData(outputVector); + output->SetPoints(points); + output->SetVerts(verts); + + return 1; +} diff --git a/modules/viz/src/vtk/vtkXYZReader.h b/modules/viz/src/vtk/vtkXYZReader.h new file mode 100644 index 0000000000..13ae048ec6 --- /dev/null +++ b/modules/viz/src/vtk/vtkXYZReader.h @@ -0,0 +1,80 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#ifndef __vtkXYZReader_h +#define __vtkXYZReader_h + +#include "vtkPolyDataAlgorithm.h" + +namespace cv +{ + namespace viz + { + class vtkXYZReader : public vtkPolyDataAlgorithm + { + public: + static vtkXYZReader* New(); + vtkTypeMacro(vtkXYZReader,vtkPolyDataAlgorithm) + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Set/Get the name of the file from which to read points. + vtkSetStringMacro(FileName) + vtkGetStringMacro(FileName) + + protected: + vtkXYZReader(); + ~vtkXYZReader(); + + char* FileName; + + int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*); + private: + vtkXYZReader(const vtkXYZReader&); // Not implemented. + void operator=(const vtkXYZReader&); // Not implemented. + }; + } +} + +#endif diff --git a/modules/viz/src/vtk/vtkXYZWriter.cpp b/modules/viz/src/vtk/vtkXYZWriter.cpp index 4518a0103a..5a3d7d5c83 100644 --- a/modules/viz/src/vtk/vtkXYZWriter.cpp +++ b/modules/viz/src/vtk/vtkXYZWriter.cpp @@ -61,10 +61,22 @@ void cv::viz::vtkXYZWriter::WriteData() if (!input) return; - // OpenVTKFile() will report any errors that happen - ostream *outfilep = this->OpenVTKFile(); - if (!outfilep) + if (!this->FileName ) + { + vtkErrorMacro(<< "No FileName specified! Can't write!"); + this->SetErrorCode(vtkErrorCode::NoFileNameError); return; + } + + vtkDebugMacro(<<"Opening vtk file for writing..."); + ostream *outfilep = new ofstream(this->FileName, ios::out); + if (outfilep->fail()) + { + vtkErrorMacro(<< "Unable to open file: "<< this->FileName); + this->SetErrorCode(vtkErrorCode::CannotOpenFileError); + delete outfilep; + return; + } ostream &outfile = *outfilep; @@ -76,7 +88,8 @@ void cv::viz::vtkXYZWriter::WriteData() } // Close the file - this->CloseVTKFile(outfilep); + vtkDebugMacro(<<"Closing vtk file\n"); + delete outfilep; // Delete the file if an error occurred if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError) @@ -86,8 +99,24 @@ void cv::viz::vtkXYZWriter::WriteData() } } +int cv::viz::vtkXYZWriter::FillInputPortInformation(int, vtkInformation *info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + return 1; +} + void cv::viz::vtkXYZWriter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "DecimalPrecision: " << this->DecimalPrecision << "\n"; } + +vtkPolyData* cv::viz::vtkXYZWriter::GetInput() +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput()); +} + +vtkPolyData* cv::viz::vtkXYZWriter::GetInput(int port) +{ + return vtkPolyData::SafeDownCast(this->Superclass::GetInput(port)); +} diff --git a/modules/viz/src/vtk/vtkXYZWriter.h b/modules/viz/src/vtk/vtkXYZWriter.h index 3db18b793b..91d0c8f6b4 100644 --- a/modules/viz/src/vtk/vtkXYZWriter.h +++ b/modules/viz/src/vtk/vtkXYZWriter.h @@ -45,29 +45,41 @@ #ifndef __vtkXYZWriter_h #define __vtkXYZWriter_h -#include "vtkPolyDataWriter.h" +#include "vtkWriter.h" namespace cv { namespace viz { - class vtkXYZWriter : public vtkPolyDataWriter + class vtkXYZWriter : public vtkWriter { public: static vtkXYZWriter *New(); - vtkTypeMacro(vtkXYZWriter,vtkPolyDataWriter) + vtkTypeMacro(vtkXYZWriter,vtkWriter) void PrintSelf(ostream& os, vtkIndent indent); vtkGetMacro(DecimalPrecision, int) vtkSetMacro(DecimalPrecision, int) + // Description: + // Specify file name of data file to write. + vtkSetStringMacro(FileName) + vtkGetStringMacro(FileName) + + // Description: + // Get the input to this writer. + vtkPolyData* GetInput(); + vtkPolyData* GetInput(int port); + protected: vtkXYZWriter(); ~vtkXYZWriter(){} void WriteData(); + int FillInputPortInformation(int port, vtkInformation *info); int DecimalPrecision; + char *FileName; private: vtkXYZWriter(const vtkXYZWriter&); // Not implemented. From 975a6299fa1b1fee9454567280fbe251347512dd Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 2 Feb 2014 18:56:55 +0400 Subject: [PATCH 100/293] More proper OpneCv+Qt+Vtk issues handling (http://code.opencv.org/issues/3492) --- cmake/OpenCVDetectVTK.cmake | 55 +++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/cmake/OpenCVDetectVTK.cmake b/cmake/OpenCVDetectVTK.cmake index 78d1a73b69..2b55a9c3f1 100644 --- a/cmake/OpenCVDetectVTK.cmake +++ b/cmake/OpenCVDetectVTK.cmake @@ -2,25 +2,52 @@ if(NOT WITH_VTK OR ANDROID OR IOS) return() endif() -if (HAVE_QT5) - message(STATUS "VTK is disabled because OpenCV is linked with Q5. Some VTK disributives are compiled with Q4 and therefore can't be linked together Qt5.") +# VTK 6.x components +find_package(VTK QUIET COMPONENTS vtkRenderingOpenGL vtkInteractionStyle vtkRenderingLOD vtkIOPLY vtkFiltersTexture vtkRenderingFreeType vtkIOExport NO_MODULE) + +# VTK 5.x components +if(NOT VTK_FOUND) + find_package(VTK QUIET COMPONENTS vtkCommon NO_MODULE) +endif() + +if(NOT VTK_FOUND) + set(HAVE_VTK OFF) + message(STATUS "VTK is not found. Please set -DVTK_DIR in CMake to VTK build directory, or to VTK install subdirectory with VTKConfig.cmake file") return() endif() -find_package(VTK 6.0 QUIET COMPONENTS vtkRenderingCore vtkInteractionWidgets vtkInteractionStyle vtkIOLegacy vtkIOPLY vtkRenderingFreeType vtkRenderingLOD vtkFiltersTexture vtkIOExport NO_MODULE) - -if(NOT DEFINED VTK_FOUND OR NOT VTK_FOUND) - find_package(VTK 5.10 QUIET COMPONENTS vtkCommon vtkFiltering vtkRendering vtkWidgets vtkImaging NO_MODULE) +# Don't support ealier VTKs +if(${VTK_VERSION} VERSION_LESS "5.8.0") + message(STATUS "VTK support is disabled. VTK ver. 5.8.0 is minimum required, but found VTK ver. ${VTK_VERSION}") + return() endif() -if(NOT DEFINED VTK_FOUND OR NOT VTK_FOUND) - find_package(VTK 5.8 QUIET COMPONENTS vtkCommon vtkFiltering vtkRendering vtkWidgets vtkImaging NO_MODULE) +# Different Qt versions can't be linked together +if(HAVE_QT5 AND ${VTK_VERSION} VERSION_LESS "6.0.0") + if(VTK_USE_QT) + message(STATUS "VTK support is disabled. Incompatible combination: OpenCV + Qt5 and VTK ver.${VTK_VERSION} + Qt4") + endif() endif() -if(VTK_FOUND) - set(HAVE_VTK ON) - message(STATUS "Found VTK ver. ${VTK_VERSION} (usefile: ${VTK_USE_FILE})") -else() - set(HAVE_VTK OFF) - message(STATUS "VTK is not found. Please set -DVTK_DIR in CMake to VTK build directory, or set $VTK_DIR enviroment variable to VTK install subdirectory with VTKConfig.cmake file (for windows)") +# Different Qt versions can't be linked together. VTK 6.0.0 doesn't provide a way to get Qt version it was linked with +if(HAVE_QT5 AND ${VTK_VERSION} VERSION_EQUAL "6.0.0" AND NOT DEFINED FORCE_VTK) + message(STATUS "VTK support is disabled. Possible incompatible combination: OpenCV+Qt5, and VTK ver.${VTK_VERSION} with Qt4") + message(STATUS "If it is known that VTK was compiled without Qt4, please define '-DFORCE_VTK=TRUE' flag in CMake") + return() endif() + +# Different Qt versions can't be linked together +if(HAVE_QT AND ${VTK_VERSION} VERSION_GREATER "6.0.0" AND NOT ${VTK_QT_VERSION} STREQUAL "") + if(HAVE_QT5 AND ${VTK_QT_VERSION} EQUAL "4") + message(STATUS "VTK support is disabled. Incompatible combination: OpenCV + Qt5 and VTK ver.${VTK_VERSION} + Qt4") + return() + endif() + + if(NOT HAVE_QT5 AND ${VTK_QT_VERSION} EQUAL "5") + message(STATUS "VTK support is disabled. Incompatible combination: OpenCV + Qt4 and VTK ver.${VTK_VERSION} + Qt5") + return() + endif() +endif() + +set(HAVE_VTK ON) +message(STATUS "Found VTK ver. ${VTK_VERSION} (usefile: ${VTK_USE_FILE})") From 998aefd1d10a5cce794cf4c81248796719b9b0e6 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 2 Feb 2014 20:43:43 +0400 Subject: [PATCH 101/293] cloud collection merges data only on rendering --- modules/viz/src/clouds.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/modules/viz/src/clouds.cpp b/modules/viz/src/clouds.cpp index 4b84e8e9e1..b7c8bbb212 100644 --- a/modules/viz/src/clouds.cpp +++ b/modules/viz/src/clouds.cpp @@ -211,13 +211,16 @@ void cv::viz::WCloudCollection::addCloud(InputArray cloud, InputArray colors, co vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); if (!mapper) { + vtkSmartPointer append_filter = vtkSmartPointer::New(); + VtkUtils::AddInputData(append_filter, polydata); + // This is the first cloud mapper = vtkSmartPointer::New(); mapper->SetScalarRange(0, 255); mapper->SetScalarModeToUsePointData(); mapper->ScalarVisibilityOn(); mapper->ImmediateModeRenderingOff(); - VtkUtils::SetInputData(mapper, polydata); + mapper->SetInputConnection(append_filter->GetOutputPort()); actor->SetNumberOfCloudPoints(std::max(1, polydata->GetNumberOfPoints()/10)); actor->GetProperty()->SetInterpolationToFlat(); @@ -226,15 +229,10 @@ void cv::viz::WCloudCollection::addCloud(InputArray cloud, InputArray colors, co return; } - vtkPolyData *currdata = vtkPolyData::SafeDownCast(mapper->GetInput()); - CV_Assert("Cloud Widget without data" && currdata); - - vtkSmartPointer append_filter = vtkSmartPointer::New(); - VtkUtils::AddInputData(append_filter, currdata); + vtkSmartPointer producer = mapper->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); VtkUtils::AddInputData(append_filter, polydata); - append_filter->Update(); - - VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + append_filter->Modified(); actor->SetNumberOfCloudPoints(std::max(1, actor->GetNumberOfCloudPoints() + polydata->GetNumberOfPoints()/10)); } From 17fea0dd0ad30f06d4676eb1d4e9320b4e6e9aa4 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 2 Feb 2014 21:29:33 +0400 Subject: [PATCH 102/293] fixed size_t/int conversion warnings --- modules/viz/src/clouds.cpp | 2 +- modules/viz/src/precomp.hpp | 4 ++-- modules/viz/src/shapes.cpp | 12 ++++++------ modules/viz/src/vizcore.cpp | 4 ++-- modules/viz/src/vtk/vtkCloudMatSink.cpp | 10 +++++----- modules/viz/src/vtk/vtkCloudMatSource.cpp | 4 ++-- modules/viz/src/vtk/vtkTrajectorySource.cpp | 10 +++++----- modules/viz/test/tests_simple.cpp | 21 +++++++++++---------- 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/modules/viz/src/clouds.cpp b/modules/viz/src/clouds.cpp index b7c8bbb212..c2c78d5523 100644 --- a/modules/viz/src/clouds.cpp +++ b/modules/viz/src/clouds.cpp @@ -347,7 +347,7 @@ cv::viz::WMesh::WMesh(const Mesh &mesh) source->SetColorCloudNormalsTCoords(mesh.cloud, mesh.colors, mesh.normals, mesh.tcoords); source->Update(); - Mat lookup_buffer(1, mesh.cloud.total(), CV_32SC1); + Mat lookup_buffer(1, (int)mesh.cloud.total(), CV_32SC1); int *lookup = lookup_buffer.ptr(); for(int y = 0, index = 0; y < mesh.cloud.rows; ++y) { diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp index 2b34f56389..8329f52f12 100644 --- a/modules/viz/src/precomp.hpp +++ b/modules/viz/src/precomp.hpp @@ -267,8 +267,8 @@ namespace cv vtkSmartPointer scalars = vtkSmartPointer::New(); scalars->SetName("Colors"); scalars->SetNumberOfComponents(3); - scalars->SetNumberOfTuples(size); - scalars->SetArray(color_data->val, size * 3, 0); + scalars->SetNumberOfTuples((vtkIdType)size); + scalars->SetArray(color_data->val, (vtkIdType)(size * 3), 0); return scalars; } diff --git a/modules/viz/src/shapes.cpp b/modules/viz/src/shapes.cpp index f3d24f757c..171470d812 100644 --- a/modules/viz/src/shapes.cpp +++ b/modules/viz/src/shapes.cpp @@ -390,21 +390,21 @@ cv::viz::WPolyLine::WPolyLine(InputArray _points, const Color &color) vtkSmartPointer points = vtkSmartPointer::New(); points->SetDataType(_points.depth() == CV_32F ? VTK_FLOAT : VTK_DOUBLE); - points->SetNumberOfPoints(total); + points->SetNumberOfPoints((vtkIdType)total); if (_points.depth() == CV_32F) for(size_t i = 0; i < total; ++i, fpoints += s_chs) - points->SetPoint(i, fpoints); + points->SetPoint((vtkIdType)i, fpoints); if (_points.depth() == CV_64F) for(size_t i = 0; i < total; ++i, dpoints += s_chs) - points->SetPoint(i, dpoints); + points->SetPoint((vtkIdType)i, dpoints); vtkSmartPointer cell_array = vtkSmartPointer::New(); - cell_array->Allocate(cell_array->EstimateSize(1, total)); - cell_array->InsertNextCell(total); + cell_array->Allocate(cell_array->EstimateSize(1, (int)total)); + cell_array->InsertNextCell((int)total); for(size_t i = 0; i < total; ++i) - cell_array->InsertCellPoint(i); + cell_array->InsertCellPoint((vtkIdType)i); vtkSmartPointer scalars = VtkUtils::FillScalars(total, color); diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp index b8cad1ca68..b4332a860a 100644 --- a/modules/viz/src/vizcore.cpp +++ b/modules/viz/src/vizcore.cpp @@ -278,11 +278,11 @@ void cv::viz::writeTrajectory(InputArray _traj, const String& files_format, int if (traj.depth() == CV_32F) for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index) - writePose(cv::format(files_format.c_str(), index), traj.at(i), tag); + writePose(cv::format(files_format.c_str(), index), traj.at((int)i), tag); if (traj.depth() == CV_64F) for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index) - writePose(cv::format(files_format.c_str(), index), traj.at(i), tag); + writePose(cv::format(files_format.c_str(), index), traj.at((int)i), tag); } CV_Assert(!"Unsupported array kind"); diff --git a/modules/viz/src/vtk/vtkCloudMatSink.cpp b/modules/viz/src/vtk/vtkCloudMatSink.cpp index 7f47022c34..8bd1011323 100644 --- a/modules/viz/src/vtk/vtkCloudMatSink.cpp +++ b/modules/viz/src/vtk/vtkCloudMatSink.cpp @@ -79,11 +79,11 @@ void cv::viz::vtkCloudMatSink::WriteData() if (cloud.depth() == CV_32F) for(size_t i = 0; i < cloud.total(); ++i) - *fdata++ = Vec3d(points_Data->GetPoint(i)); + *fdata++ = Vec3d(points_Data->GetPoint((vtkIdType)i)); if (cloud.depth() == CV_64F) for(size_t i = 0; i < cloud.total(); ++i) - *ddata++ = Vec3d(points_Data->GetPoint(i)); + *ddata++ = Vec3d(points_Data->GetPoint((vtkIdType)i)); } else cloud.release(); @@ -101,7 +101,7 @@ void cv::viz::vtkCloudMatSink::WriteData() Mat buffer(cloud.size(), CV_64FC(channels)); Vec3d *cptr = buffer.ptr(); for(size_t i = 0; i < buffer.total(); ++i) - *cptr++ = Vec3d(scalars_data->GetTuple(i)); + *cptr++ = Vec3d(scalars_data->GetTuple((vtkIdType)i)); buffer.convertTo(colors, CV_8U, vtktype == VTK_FLOAT || VTK_FLOAT == VTK_DOUBLE ? 255.0 : 1.0); } @@ -121,7 +121,7 @@ void cv::viz::vtkCloudMatSink::WriteData() Mat buffer(cloud.size(), CV_64FC(channels)); Vec3d *cptr = buffer.ptr(); for(size_t i = 0; i < buffer.total(); ++i) - *cptr++ = Vec3d(normals_data->GetTuple(i)); + *cptr++ = Vec3d(normals_data->GetTuple((vtkIdType)i)); buffer.convertTo(normals, vtktype == VTK_FLOAT ? CV_32F : CV_64F); } @@ -140,7 +140,7 @@ void cv::viz::vtkCloudMatSink::WriteData() Mat buffer(cloud.size(), CV_64FC2); Vec2d *cptr = buffer.ptr(); for(size_t i = 0; i < buffer.total(); ++i) - *cptr++ = Vec2d(coords_data->GetTuple(i)); + *cptr++ = Vec2d(coords_data->GetTuple((vtkIdType)i)); buffer.convertTo(tcoords, vtktype == VTK_FLOAT ? CV_32F : CV_64F); diff --git a/modules/viz/src/vtk/vtkCloudMatSource.cpp b/modules/viz/src/vtk/vtkCloudMatSource.cpp index 74d01bbd01..1d8ab7894c 100644 --- a/modules/viz/src/vtk/vtkCloudMatSource.cpp +++ b/modules/viz/src/vtk/vtkCloudMatSource.cpp @@ -185,8 +185,8 @@ int cv::viz::vtkCloudMatSource::filterNanCopy(const Mat& cloud) CV_DbgAssert(DataType<_Tp>::depth == cloud.depth()); points = vtkSmartPointer::New(); points->SetDataType(VtkDepthTraits<_Tp>::data_type); - points->Allocate(cloud.total()); - points->SetNumberOfPoints(cloud.total()); + points->Allocate((vtkIdType)cloud.total()); + points->SetNumberOfPoints((vtkIdType)cloud.total()); int s_chs = cloud.channels(); int total = 0; diff --git a/modules/viz/src/vtk/vtkTrajectorySource.cpp b/modules/viz/src/vtk/vtkTrajectorySource.cpp index e098a1d553..2036e09af3 100644 --- a/modules/viz/src/vtk/vtkTrajectorySource.cpp +++ b/modules/viz/src/vtk/vtkTrajectorySource.cpp @@ -64,19 +64,19 @@ void cv::viz::vtkTrajectorySource::SetTrajectory(InputArray _traj) points = vtkSmartPointer::New(); points->SetDataType(VTK_DOUBLE); - points->SetNumberOfPoints(total); + points->SetNumberOfPoints((vtkIdType)total); tensors = vtkSmartPointer::New(); tensors->SetNumberOfComponents(9); - tensors->SetNumberOfTuples(total); + tensors->SetNumberOfTuples((vtkIdType)total); for(size_t i = 0; i < total; ++i, ++dpath) { Matx33d R = dpath->rotation().t(); // transposed because of - tensors->SetTuple(i, R.val); // column major order + tensors->SetTuple((vtkIdType)i, R.val); // column major order Vec3d p = dpath->translation(); - points->SetPoint(i, p.val); + points->SetPoint((vtkIdType)i, p.val); } } @@ -85,7 +85,7 @@ cv::Mat cv::viz::vtkTrajectorySource::ExtractPoints(InputArray _traj) CV_Assert(_traj.kind() == _InputArray::STD_VECTOR || _traj.kind() == _InputArray::MAT); CV_Assert(_traj.type() == CV_32FC(16) || _traj.type() == CV_64FC(16)); - Mat points(1, _traj.total(), CV_MAKETYPE(_traj.depth(), 3)); + Mat points(1, (int)_traj.total(), CV_MAKETYPE(_traj.depth(), 3)); const Affine3d* dpath = _traj.getMat().ptr(); const Affine3f* fpath = _traj.getMat().ptr(); diff --git a/modules/viz/test/tests_simple.cpp b/modules/viz/test/tests_simple.cpp index f84b60a475..c595affba7 100644 --- a/modules/viz/test/tests_simple.cpp +++ b/modules/viz/test/tests_simple.cpp @@ -81,7 +81,7 @@ TEST(Viz, show_cloud_masked) Mat dragon_cloud = readCloud(get_dragon_ply_file_path()); Vec3f qnan = Vec3f::all(std::numeric_limits::quiet_NaN()); - for(size_t i = 0; i < dragon_cloud.total(); ++i) + for(int i = 0; i < (int)dragon_cloud.total(); ++i) if (i % 15 != 0) dragon_cloud.at(i) = qnan; @@ -170,7 +170,7 @@ TEST(Viz, show_textured_mesh) tcoords.push_back(Vec2d(1.0, i/64.0)); } - for(size_t i = 0; i < points.size()/2-1; ++i) + for(int i = 0; i < (int)points.size()/2-1; ++i) { int polys[] = {3, 2*i, 2*i+1, 2*i+2, 3, 2*i+1, 2*i+2, 2*i+3}; polygons.insert(polygons.end(), polys, polys + sizeof(polys)/sizeof(polys[0])); @@ -194,7 +194,7 @@ TEST(Viz, show_textured_mesh) TEST(Viz, show_polyline) { Mat polyline(1, 32, CV_64FC3); - for(size_t i = 0; i < polyline.total(); ++i) + for(int i = 0; i < (int)polyline.total(); ++i) polyline.at(i) = Vec3d(i/16.0, cos(i * CV_PI/6), sin(i * CV_PI/6)); Viz3d viz("show_polyline"); @@ -222,13 +222,14 @@ TEST(Viz, show_sampled_normals) TEST(Viz, show_trajectories) { std::vector path = generate_test_trajectory(), sub0, sub1, sub2, sub3, sub4, sub5; + int size =(int)path.size(); - Mat(path).rowRange(0, path.size()/10+1).copyTo(sub0); - Mat(path).rowRange(path.size()/10, path.size()/5+1).copyTo(sub1); - Mat(path).rowRange(path.size()/5, 11*path.size()/12).copyTo(sub2); - Mat(path).rowRange(11*path.size()/12, path.size()).copyTo(sub3); - Mat(path).rowRange(3*path.size()/4, 33*path.size()/40).copyTo(sub4); - Mat(path).rowRange(33*path.size()/40, 9*path.size()/10).copyTo(sub5); + Mat(path).rowRange(0, size/10+1).copyTo(sub0); + Mat(path).rowRange(size/10, size/5+1).copyTo(sub1); + Mat(path).rowRange(size/5, 11*size/12).copyTo(sub2); + Mat(path).rowRange(11*size/12, size).copyTo(sub3); + Mat(path).rowRange(3*size/4, 33*size/40).copyTo(sub4); + Mat(path).rowRange(33*size/40, 9*size/10).copyTo(sub5); Matx33d K(1024.0, 0.0, 320.0, 0.0, 1024.0, 240.0, 0.0, 0.0, 1.0); Viz3d viz("show_trajectories"); @@ -259,7 +260,7 @@ TEST(Viz, show_trajectory_reposition) Viz3d viz("show_trajectory_reposition_to_origin"); viz.showWidget("coos", WCoordinateSystem()); - viz.showWidget("sub3", WTrajectory(Mat(path).rowRange(0, path.size()/3), WTrajectory::BOTH, 0.2, Color::brown()), path.front().inv()); + viz.showWidget("sub3", WTrajectory(Mat(path).rowRange(0, (int)path.size()/3), WTrajectory::BOTH, 0.2, Color::brown()), path.front().inv()); viz.showWidget("text2d", WText("Trajectory resposition to origin", Point(20, 20), 20, Color::green())); viz.spin(); } From f8ccc115ef3f593b4b44f5efbc7c79a276e8619d Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 2 Feb 2014 22:08:18 +0400 Subject: [PATCH 103/293] proper render call order (fixes some issues with VTK6.1) --- modules/viz/src/interactor_style.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/viz/src/interactor_style.cpp b/modules/viz/src/interactor_style.cpp index 75003a2b66..7fa6714480 100644 --- a/modules/viz/src/interactor_style.cpp +++ b/modules/viz/src/interactor_style.cpp @@ -366,7 +366,6 @@ void cv::viz::InteractorStyle::OnKeyDown() { Interactor->GetRenderWindow()->SetSize(win_size_.val); Interactor->GetRenderWindow()->SetPosition(win_pos_.val); - Interactor->GetRenderWindow()->Render(); Interactor->Render(); } // Set to max @@ -376,7 +375,6 @@ void cv::viz::InteractorStyle::OnKeyDown() win_size_ = win_size; Interactor->GetRenderWindow()->SetSize(screen_size.val); - Interactor->GetRenderWindow()->Render(); Interactor->Render(); max_win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); } @@ -417,7 +415,7 @@ void cv::viz::InteractorStyle::OnKeyDown() { vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); cam->SetParallelProjection(!cam->GetParallelProjection()); - CurrentRenderer->Render(); + Interactor->Render(); break; } @@ -468,7 +466,7 @@ void cv::viz::InteractorStyle::OnKeyDown() CurrentRenderer->SetActiveCamera(cam); CurrentRenderer->ResetCameraClippingRange(); - CurrentRenderer->Render(); + Interactor->Render(); break; } @@ -594,7 +592,6 @@ void cv::viz::InteractorStyle::OnMouseWheelForward() cam->Modified(); CurrentRenderer->ResetCameraClippingRange(); CurrentRenderer->Modified(); - CurrentRenderer->Render(); Interactor->Render(); } else @@ -624,7 +621,6 @@ void cv::viz::InteractorStyle::OnMouseWheelBackward() cam->Modified(); CurrentRenderer->ResetCameraClippingRange(); CurrentRenderer->Modified(); - CurrentRenderer->Render(); Interactor->Render(); } else From 069dd8a068263f6b0cfce647d11637baaf48b745 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sat, 8 Feb 2014 16:11:20 +0400 Subject: [PATCH 104/293] default meshlab background --- modules/viz/src/vizimpl.cpp | 3 +++ modules/viz/test/tests_simple.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/modules/viz/src/vizimpl.cpp b/modules/viz/src/vizimpl.cpp index 5fa49e2f96..cf7a251202 100644 --- a/modules/viz/src/vizimpl.cpp +++ b/modules/viz/src/vizimpl.cpp @@ -68,6 +68,8 @@ cv::viz::Viz3d::VizImpl::VizImpl(const String &name) : spin_once_state_(false), timer_callback_ = vtkSmartPointer::New(); exit_callback_ = vtkSmartPointer::New(); exit_callback_->viz = this; + + setBackgroundMeshLab(); } ///////////////////////////////////////////////////////////////////////////////////////////// @@ -114,6 +116,7 @@ void cv::viz::Viz3d::VizImpl::recreateRenderWindow() Vec2i window_size(window_->GetSize()); int fullscreen = window_->GetFullScreen(); + window_->Finalize(); window_ = vtkSmartPointer::New(); if (window_position_[0] != std::numeric_limits::min()) //also workaround window_->SetPosition(window_position_.val); diff --git a/modules/viz/test/tests_simple.cpp b/modules/viz/test/tests_simple.cpp index c595affba7..4edb324f4f 100644 --- a/modules/viz/test/tests_simple.cpp +++ b/modules/viz/test/tests_simple.cpp @@ -52,6 +52,7 @@ TEST(Viz, show_cloud_bluberry) Affine3d pose = Affine3d().rotate(Vec3d(0, 0.8, 0)); Viz3d viz("show_cloud_bluberry"); + viz.setBackgroundColor(Color::black()); viz.showWidget("coosys", WCoordinateSystem()); viz.showWidget("dragon", WCloud(dragon_cloud, Color::bluberry()), pose); From 5dc17f5d587b866aaee676c723a2bd30e1f710e2 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sat, 8 Feb 2014 19:31:24 +0400 Subject: [PATCH 105/293] Fixed all OpenGL issues for Macos (via objective-C++ layer) --- cmake/OpenCVModule.cmake | 10 +- modules/viz/src/precomp.hpp | 2 + modules/viz/src/vizimpl.cpp | 11 +- modules/viz/src/vtk/vtkCocoaInteractorFix.mm | 213 +++++++++++++++++++ 4 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 modules/viz/src/vtk/vtkCocoaInteractorFix.mm diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 86a9d0c83c..6f727af8d9 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -484,6 +484,10 @@ macro(ocv_glob_module_sources) file(GLOB_RECURSE lib_int_hdrs "src/*.hpp" "src/*.h") file(GLOB lib_hdrs "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") file(GLOB lib_hdrs_detail "include/opencv2/${name}/detail/*.hpp" "include/opencv2/${name}/detail/*.h") + file(GLOB_RECURSE lib_srcs_apple "src/*.m*") + if (APPLE) + list(APPEND lib_srcs ${lib_srcs_apple}) + endif() file(GLOB lib_cuda_srcs "src/cuda/*.cu") set(cuda_objs "") @@ -745,7 +749,11 @@ function(ocv_add_accuracy_tests) get_native_precompiled_header(${the_target} test_precomp.hpp) - add_executable(${the_target} ${OPENCV_TEST_${the_module}_SOURCES} ${${the_target}_pch}) + if(APPLE AND ${the_target} STREQUAL "opencv_test_viz") + add_executable(${the_target} MACOSX_BUNDLE ${OPENCV_TEST_${the_module}_SOURCES} ${${the_target}_pch}) + else() + add_executable(${the_target} ${OPENCV_TEST_${the_module}_SOURCES} ${${the_target}_pch}) + endif() target_link_libraries(${the_target} ${OPENCV_MODULE_${the_module}_DEPS} ${test_deps} ${OPENCV_LINKER_LIBS}) add_dependencies(opencv_tests ${the_target}) diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp index 8329f52f12..f77da5a5b0 100644 --- a/modules/viz/src/precomp.hpp +++ b/modules/viz/src/precomp.hpp @@ -312,6 +312,8 @@ namespace cv return transform_filter->GetOutput(); } }; + + vtkSmartPointer vtkCocoaRenderWindowInteractorNew(); } } diff --git a/modules/viz/src/vizimpl.cpp b/modules/viz/src/vizimpl.cpp index cf7a251202..3e852dd718 100644 --- a/modules/viz/src/vizimpl.cpp +++ b/modules/viz/src/vizimpl.cpp @@ -111,7 +111,7 @@ void cv::viz::Viz3d::VizImpl::close() void cv::viz::Viz3d::VizImpl::recreateRenderWindow() { -#if !defined _MSC_VER +#if !defined _MSC_VER && !defined __APPLE__ //recreating is workaround for Ubuntu -- a crash in x-server Vec2i window_size(window_->GetSize()); int fullscreen = window_->GetFullScreen(); @@ -127,12 +127,15 @@ void cv::viz::Viz3d::VizImpl::recreateRenderWindow() #endif } - ///////////////////////////////////////////////////////////////////////////////////////////// void cv::viz::Viz3d::VizImpl::spin() { recreateRenderWindow(); +#if defined __APPLE__ + interactor_ = vtkCocoaRenderWindowInteractorNew(); +#else interactor_ = vtkSmartPointer::New(); +#endif interactor_->SetRenderWindow(window_); interactor_->SetInteractorStyle(style_); window_->AlphaBitPlanesOff(); @@ -154,7 +157,11 @@ void cv::viz::Viz3d::VizImpl::spinOnce(int time, bool force_redraw) { spin_once_state_ = true; recreateRenderWindow(); +#if defined __APPLE__ + interactor_ = vtkCocoaRenderWindowInteractorNew(); +#else interactor_ = vtkSmartPointer::New(); +#endif interactor_->SetRenderWindow(window_); interactor_->SetInteractorStyle(style_); interactor_->AddObserver(vtkCommand::TimerEvent, timer_callback_); diff --git a/modules/viz/src/vtk/vtkCocoaInteractorFix.mm b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm new file mode 100644 index 0000000000..0e55aebb08 --- /dev/null +++ b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm @@ -0,0 +1,213 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +// OpenCV Viz module is complete rewrite of +// PCL visualization module (www.pointclouds.org) +// +//M*/ + +#import +#include +#include +#include +#include + +//---------------------------------------------------------------------------- +@interface vtkCocoaServerFix : NSObject +{ + vtkCocoaRenderWindow* renWin; +} + ++ (id)cocoaServerWithRenderWindow:(vtkCocoaRenderWindow*)inRenderWindow; + +- (void)start; +- (void)stop; +- (void)breakEventLoop; + +@end + +//---------------------------------------------------------------------------- +@implementation vtkCocoaServerFix + +//---------------------------------------------------------------------------- +- (id)initWithRenderWindow:(vtkCocoaRenderWindow *)inRenderWindow +{ + self = [super init]; + if (self) + renWin = inRenderWindow; + return self; +} + +//---------------------------------------------------------------------------- ++ (id)cocoaServerWithRenderWindow:(vtkCocoaRenderWindow *)inRenderWindow +{ + vtkCocoaServerFix *server = [[[vtkCocoaServerFix alloc] initWithRenderWindow:inRenderWindow] autorelease]; + return server; +} + +//---------------------------------------------------------------------------- +- (void)start +{ + // Retrieve the NSWindow. + NSWindow *win = nil; + if (renWin) + { + win = reinterpret_cast (renWin->GetRootWindow ()); + + // We don't want to be informed of every window closing, so check for nil. + if (win != nil) + { + // Register for the windowWillClose notification in order to stop the run loop if the window closes. + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:win]; + } + } + // Start the NSApplication's run loop + NSApplication* application = [NSApplication sharedApplication]; + [application run]; +} + +//---------------------------------------------------------------------------- +- (void)stop +{ + [self breakEventLoop]; +} + +//---------------------------------------------------------------------------- +- (void)breakEventLoop +{ + NSApplication* application = [NSApplication sharedApplication]; + [application stop:application]; + + NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0.0,0.0) + modifierFlags:0 + timestamp:0 + windowNumber:-1 + context:nil + subtype:0 + data1:0 + data2:0]; + [application postEvent:event atStart:YES]; +} + +//---------------------------------------------------------------------------- +- (void)windowWillClose:(NSNotification*)aNotification +{ + (void)aNotification; + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name:NSWindowWillCloseNotification object:nil]; + + if (renWin) + { + int windowCreated = renWin->GetWindowCreated (); + if (windowCreated) + { + [self breakEventLoop]; + + // The NSWindow is closing, so prevent anyone from accidently using it + renWin->SetRootWindow(NULL); + } + } +} + +@end + +//---------------------------------------------------------------------------- + +namespace cv { namespace viz +{ + class vtkCocoaRenderWindowInteractorFix : public vtkCocoaRenderWindowInteractor + { + public: + static vtkCocoaRenderWindowInteractorFix *New (); + vtkTypeMacro (vtkCocoaRenderWindowInteractorFix, vtkCocoaRenderWindowInteractor) + + virtual void Start (); + virtual void TerminateApp (); + + protected: + vtkCocoaRenderWindowInteractorFix () {} + ~vtkCocoaRenderWindowInteractorFix () {} + + private: + vtkCocoaRenderWindowInteractorFix (const vtkCocoaRenderWindowInteractorFix&); // Not implemented. + void operator = (const vtkCocoaRenderWindowInteractorFix&); // Not implemented. + }; + + vtkStandardNewMacro (vtkCocoaRenderWindowInteractorFix) + + vtkSmartPointer vtkCocoaRenderWindowInteractorNew(); +}} + +void cv::viz::vtkCocoaRenderWindowInteractorFix::Start () +{ + vtkCocoaRenderWindow* renWin = vtkCocoaRenderWindow::SafeDownCast(this->GetRenderWindow ()); + if (renWin != NULL) + { + vtkCocoaServerFix *server = reinterpret_cast (this->GetCocoaServer ()); + if (!this->GetCocoaServer ()) + { + server = [vtkCocoaServerFix cocoaServerWithRenderWindow:renWin]; + this->SetCocoaServer (reinterpret_cast (server)); + } + + [server start]; + } +} + +void cv::viz::vtkCocoaRenderWindowInteractorFix::TerminateApp () +{ + vtkCocoaRenderWindow *renWin = vtkCocoaRenderWindow::SafeDownCast (this->RenderWindow); + if (renWin) + { + vtkCocoaServerFix *server = reinterpret_cast (this->GetCocoaServer ()); + [server stop]; + } +} + +vtkSmartPointer cv::viz::vtkCocoaRenderWindowInteractorNew() +{ + return vtkSmartPointer::New(); +} + From f1c062c30ab9eb02ea1b1bd8d221dc85ad7b2d27 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Mon, 10 Feb 2014 13:27:08 +0400 Subject: [PATCH 106/293] cloud collection finalize --- modules/viz/doc/widget.rst | 8 ++++++++ modules/viz/include/opencv2/viz/widgets.hpp | 2 ++ modules/viz/src/clouds.cpp | 18 ++++++++++++++++++ modules/viz/test/tests_simple.cpp | 1 + 4 files changed, 29 insertions(+) diff --git a/modules/viz/doc/widget.rst b/modules/viz/doc/widget.rst index 008e0e68a5..2802edff79 100644 --- a/modules/viz/doc/widget.rst +++ b/modules/viz/doc/widget.rst @@ -934,6 +934,8 @@ This 3D Widget defines a collection of clouds. :: void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()); //! All points in cloud have the same color void addCloud(InputArray cloud, const Color &color = Color::white(), Affine3d &pose = Affine3d::Identity()); + //! Repacks internal structure to sinle cloud + void finalize(); }; viz::WCloudCollection::WCloudCollection @@ -964,6 +966,12 @@ Adds a cloud to the collection. .. note:: In case there are four channels in the cloud, fourth channel is ignored. +viz::WCloudCollection::finalize +------------------------------- +Finalizes cloud data by repacking to single cloud. Useful for large cloud collections to reduce memory usage + +.. ocv:function:: void finalize() + viz::WCloudNormals ------------------ .. ocv:class:: WCloudNormals diff --git a/modules/viz/include/opencv2/viz/widgets.hpp b/modules/viz/include/opencv2/viz/widgets.hpp index 2c49b9d0e2..6910196c3d 100644 --- a/modules/viz/include/opencv2/viz/widgets.hpp +++ b/modules/viz/include/opencv2/viz/widgets.hpp @@ -345,6 +345,8 @@ namespace cv void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()); //! All points in cloud have the same color void addCloud(InputArray cloud, const Color &color = Color::white(), const Affine3d &pose = Affine3d::Identity()); + //! Repacks internal structure to sinle cloud + void finalize(); }; class CV_EXPORTS WCloudNormals : public Widget3D diff --git a/modules/viz/src/clouds.cpp b/modules/viz/src/clouds.cpp index c2c78d5523..349de2f5f2 100644 --- a/modules/viz/src/clouds.cpp +++ b/modules/viz/src/clouds.cpp @@ -242,6 +242,24 @@ void cv::viz::WCloudCollection::addCloud(InputArray cloud, const Color &color, c addCloud(cloud, Mat(cloud.size(), CV_8UC3, color), pose); } +void cv::viz::WCloudCollection::finalize() +{ + vtkSmartPointer actor = vtkLODActor::SafeDownCast(WidgetAccessor::getProp(*this)); + CV_Assert("Incompatible widget type." && actor); + + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + CV_Assert("Need to add at least one cloud." && mapper); + + vtkSmartPointer producer = mapper->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); + append_filter->Update(); + + vtkSmartPointer polydata = append_filter->GetOutput(); + mapper->RemoveInputConnection(0, 0); + VtkUtils::SetInputData(mapper, polydata); + mapper->Modified(); +} + template<> cv::viz::WCloudCollection cv::viz::Widget::cast() { Widget3D widget = this->cast(); diff --git a/modules/viz/test/tests_simple.cpp b/modules/viz/test/tests_simple.cpp index 4edb324f4f..10c1d81808 100644 --- a/modules/viz/test/tests_simple.cpp +++ b/modules/viz/test/tests_simple.cpp @@ -103,6 +103,7 @@ TEST(Viz, show_cloud_collection) ccol.addCloud(cloud, Color::white(), Affine3d().translate(Vec3d(0, 0, 0)).rotate(Vec3d(CV_PI/2, 0, 0))); ccol.addCloud(cloud, Color::blue(), Affine3d().translate(Vec3d(1, 0, 0))); ccol.addCloud(cloud, Color::red(), Affine3d().translate(Vec3d(2, 0, 0))); + ccol.finalize(); Viz3d viz("show_cloud_collection"); viz.setBackgroundColor(Color::mlab()); From 281666887e8233c4dd2a8ed289e30ef0b9c71300 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 11 Feb 2014 14:25:37 +0400 Subject: [PATCH 107/293] typo --- cmake/OpenCVModule.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 86a9d0c83c..03818018d9 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -135,13 +135,13 @@ macro(ocv_add_module _name) # parse list of dependencies if("${ARGV1}" STREQUAL "INTERNAL" OR "${ARGV1}" STREQUAL "BINDINGS") - set(OPENCV_MODULE_${the_module}_CLASS "${ARGV1}" CACHE INTERNAL "The cathegory of the module") + set(OPENCV_MODULE_${the_module}_CLASS "${ARGV1}" CACHE INTERNAL "The category of the module") set(__ocv_argn__ ${ARGN}) list(REMOVE_AT __ocv_argn__ 0) ocv_add_dependencies(${the_module} ${__ocv_argn__}) unset(__ocv_argn__) else() - set(OPENCV_MODULE_${the_module}_CLASS "PUBLIC" CACHE INTERNAL "The cathegory of the module") + set(OPENCV_MODULE_${the_module}_CLASS "PUBLIC" CACHE INTERNAL "The category of the module") ocv_add_dependencies(${the_module} ${ARGN}) if(BUILD_${the_module}) set(OPENCV_MODULES_PUBLIC ${OPENCV_MODULES_PUBLIC} "${the_module}" CACHE INTERNAL "List of OpenCV modules marked for export") From 261546f6f619bb5c77cad529e8114bdc16b5f508 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Tue, 11 Feb 2014 23:27:19 +0400 Subject: [PATCH 108/293] casting Vec3b operator for viz::Color --- modules/viz/include/opencv2/viz/types.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/viz/include/opencv2/viz/types.hpp b/modules/viz/include/opencv2/viz/types.hpp index acbece2edf..3e8e87c65e 100644 --- a/modules/viz/include/opencv2/viz/types.hpp +++ b/modules/viz/include/opencv2/viz/types.hpp @@ -63,6 +63,8 @@ namespace cv Color(const Scalar& color); + operator Vec3b() const; + static Color black(); static Color blue(); static Color green(); @@ -193,6 +195,8 @@ inline cv::viz::Color::Color(double _gray) : Scalar(_gray, _gray, _gray) {} inline cv::viz::Color::Color(double _blue, double _green, double _red) : Scalar(_blue, _green, _red) {} inline cv::viz::Color::Color(const Scalar& color) : Scalar(color) {} +inline cv::viz::Color::operator cv::Vec3b() const { return cv::Vec3d(val); } + inline cv::viz::Color cv::viz::Color::black() { return Color( 0, 0, 0); } inline cv::viz::Color cv::viz::Color::green() { return Color( 0, 255, 0); } inline cv::viz::Color cv::viz::Color::blue() { return Color(255, 0, 0); } From 9aef26e2ad05dea8a5203e01a2de148ba86cfdf2 Mon Sep 17 00:00:00 2001 From: Zhigang Gong Date: Wed, 12 Feb 2014 11:23:27 +0800 Subject: [PATCH 109/293] Workaround a LLVM/Clang 3.3 bug. The LLVM/Clang 3.3 has a bug when compile a cl kernel with assignment of a scalar to a vector data type. This patch could work around this bug. Signed-off-by: Zhigang Gong --- modules/ocl/src/opencl/arithm_pow.cl | 2 +- modules/ocl/src/opencl/imgproc_resize.cl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/ocl/src/opencl/arithm_pow.cl b/modules/ocl/src/opencl/arithm_pow.cl index 385e4cc15a..fe24553e97 100644 --- a/modules/ocl/src/opencl/arithm_pow.cl +++ b/modules/ocl/src/opencl/arithm_pow.cl @@ -66,7 +66,7 @@ __kernel void arithm_pow(__global VT * src, int src_step, int src_offset, int dst_index = mad24(y, dst_step, x + dst_offset); VT src_data = src[src_index]; - VT tmp = src_data > 0 ? exp(p * log(src_data)) : (src_data == 0 ? 0 : exp(p * log(fabs(src_data)))); + VT tmp = src_data > (VT)0 ? (VT)exp(p * log(src_data)) : (src_data == (VT)0 ? (VT)0 : (VT)exp(p * log(fabs(src_data)))); dst[dst_index] = tmp; } diff --git a/modules/ocl/src/opencl/imgproc_resize.cl b/modules/ocl/src/opencl/imgproc_resize.cl index ebf8c712b7..100d687739 100644 --- a/modules/ocl/src/opencl/imgproc_resize.cl +++ b/modules/ocl/src/opencl/imgproc_resize.cl @@ -83,10 +83,10 @@ __kernel void resizeLN_C1_D0(__global uchar * dst, __global uchar const * restri int y = floor(sy); float v = sy - y; - u = x < 0 ? 0 : u; - u = (x >= src_cols) ? 0 : u; - x = x < 0 ? 0 : x; - x = (x >= src_cols) ? src_cols-1 : x; + u = x < (int4)0 ? (float4)0 : u; + u = (x >= (int4)src_cols) ? (float4)0 : u; + x = x < (int4)0 ? (int4)0 : x; + x = (x >= (int4)src_cols) ? (int4)(src_cols-1) : x; y<0 ? y=0,v=0 : y; y>=src_rows ? y=src_rows-1,v=0 : y; From e55f2b26028a5261806fb8e972b6165c40593357 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 12 Feb 2014 10:29:53 +0400 Subject: [PATCH 110/293] LICENSE and README files installation rules added. --- CMakeLists.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6518cfc0e..050a5ead7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -592,6 +592,28 @@ if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH AND UNIX AND NOT ANDROID) DESTINATION ${OPENCV_TEST_INSTALL_PATH} COMPONENT tests) endif() +if(NOT OPENCV_README_FILE) + if(ANDROID) + set(OPENCV_README_FILE ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/README.android) + endif() +endif() + +if(NOT OPENCV_LICENSE_FILE) + set(OPENCV_LICENSE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE) +endif() + +# for UNIX it does not make sense as LICENSE and readme will be part of the package automatically +if(ANDROID OR NOT UNIX) + install(FILES ${OPENCV_LICENSE_FILE} + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT libs) + if(OPENCV_README_FILE) + install(FILES ${OPENCV_README_FILE} + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ + DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT libs) + endif() +endif() + # ---------------------------------------------------------------------------- # Summary: # ---------------------------------------------------------------------------- From 8bbce0a2a2039fbb253eee3b87453b4351989601 Mon Sep 17 00:00:00 2001 From: Stuart Cunningham Date: Wed, 12 Feb 2014 21:21:06 +1100 Subject: [PATCH 111/293] Fix reading of 16-bit TIFF images on big endian host. Use correct integer types for arguments to TIFFGetField to avoid corruption of values and failed loads of TIFF file when using cv::imread(). Added test where both big and little endian TIFF files are read using imread(). Fixed build of 3rdparty libtiff on big endian hosts. Reduced memory required during decode_tile16384x16384 test by not converting large grayscale test image to color image during read. --- 3rdparty/libtiff/CMakeLists.txt | 2 ++ 3rdparty/libtiff/tif_config.h.cmakein | 2 +- modules/highgui/src/grfmt_tiff.cpp | 15 ++++---- modules/highgui/test/test_grfmt.cpp | 52 +++++++++++++++++++++++++-- 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/3rdparty/libtiff/CMakeLists.txt b/3rdparty/libtiff/CMakeLists.txt index addbb5551c..5793021b78 100644 --- a/3rdparty/libtiff/CMakeLists.txt +++ b/3rdparty/libtiff/CMakeLists.txt @@ -6,6 +6,7 @@ project(${TIFF_LIBRARY}) include(CheckFunctionExists) include(CheckIncludeFile) +include(TestBigEndian) check_include_file(assert.h HAVE_ASSERT_H) check_include_file(fcntl.h HAVE_FCNTL_H) @@ -16,6 +17,7 @@ check_include_file(search.h HAVE_SEARCH_H) check_include_file(string.h HAVE_STRING_H) check_include_file(sys/types.h HAVE_SYS_TYPES_H) check_include_file(unistd.h HAVE_UNISTD_H) +test_big_endian(HOST_BIGENDIAN) if(WIN32 AND NOT HAVE_WINRT) set(USE_WIN32_FILEIO 1) diff --git a/3rdparty/libtiff/tif_config.h.cmakein b/3rdparty/libtiff/tif_config.h.cmakein index 182f2833d1..55c6cb26a0 100644 --- a/3rdparty/libtiff/tif_config.h.cmakein +++ b/3rdparty/libtiff/tif_config.h.cmakein @@ -54,7 +54,7 @@ /* Native cpu byte order: 1 if big-endian (Motorola) or 0 if little-endian (Intel) */ -#define HOST_BIGENDIAN 0 +#define HOST_BIGENDIAN @HOST_BIGENDIAN@ /* Set the native cpu bit order (FILLORDER_LSB2MSB or FILLORDER_MSB2LSB) */ #define HOST_FILLORDER FILLORDER_LSB2MSB diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/highgui/src/grfmt_tiff.cpp index 5179531f50..c86b4e3654 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/highgui/src/grfmt_tiff.cpp @@ -111,18 +111,21 @@ bool TiffDecoder::readHeader() bool result = false; close(); - TIFF* tif = TIFFOpen( m_filename.c_str(), "rb" ); + // TIFFOpen() mode flags are different to fopen(). A 'b' in mode "rb" has no effect when reading. + // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html + TIFF* tif = TIFFOpen( m_filename.c_str(), "r" ); if( tif ) { - int wdth = 0, hght = 0, photometric = 0; + uint wdth = 0, hght = 0; + ushort photometric = 0; m_tif = tif; if( TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &wdth ) && TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &hght ) && TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric )) { - int bpp=8, ncn = photometric > 1 ? 3 : 1; + ushort bpp=8, ncn = photometric > 1 ? 3 : 1; TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); @@ -175,12 +178,12 @@ bool TiffDecoder::readData( Mat& img ) if( m_tif && m_width && m_height ) { TIFF* tif = (TIFF*)m_tif; - int tile_width0 = m_width, tile_height0 = 0; + uint tile_width0 = m_width, tile_height0 = 0; int x, y, i; int is_tiled = TIFFIsTiled(tif); - int photometric; + ushort photometric; TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ); - int bpp = 8, ncn = photometric > 1 ? 3 : 1; + ushort bpp = 8, ncn = photometric > 1 ? 3 : 1; TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); const int bitsPerByte = 8; diff --git a/modules/highgui/test/test_grfmt.cpp b/modules/highgui/test/test_grfmt.cpp index 86954e3e10..edccc0280a 100644 --- a/modules/highgui/test/test_grfmt.cpp +++ b/modules/highgui/test/test_grfmt.cpp @@ -408,8 +408,8 @@ TEST(Highgui_Tiff, decode_tile16384x16384) try { - cv::imread(file3); - EXPECT_NO_THROW(cv::imread(file4)); + cv::imread(file3, CV_LOAD_IMAGE_UNCHANGED); + EXPECT_NO_THROW(cv::imread(file4, CV_LOAD_IMAGE_UNCHANGED)); } catch(const std::bad_alloc&) { @@ -419,4 +419,52 @@ TEST(Highgui_Tiff, decode_tile16384x16384) remove(file3.c_str()); remove(file4.c_str()); } + +TEST(Highgui_Tiff, write_read_16bit_big_little_endian) +{ + // see issue #2601 "16-bit Grayscale TIFF Load Failures Due to Buffer Underflow and Endianness" + + // Setup data for two minimal 16-bit grayscale TIFF files in both endian formats + uchar tiff_sample_data[2][86] = { { + // Little endian + 0x49, 0x49, 0x2a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xad, 0xde, 0xef, 0xbe, 0x06, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x01, + 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x01, 0x04, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }, { + // Big endian + 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x0c, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x06, 0x01, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, + 0x00, 0x00, 0x01, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x11, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x01, 0x17, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x04 } + }; + + // Test imread() for both a little endian TIFF and big endian TIFF + for (int i = 0; i < 2; i++) + { + string filename = cv::tempfile(".tiff"); + + // Write sample TIFF file + FILE* fp = fopen(filename.c_str(), "wb"); + ASSERT_TRUE(fp != NULL); + ASSERT_EQ((size_t)1, fwrite(tiff_sample_data, 86, 1, fp)); + fclose(fp); + + Mat img = imread(filename, CV_LOAD_IMAGE_UNCHANGED); + + EXPECT_EQ(1, img.rows); + EXPECT_EQ(2, img.cols); + EXPECT_EQ(CV_16U, img.type()); + EXPECT_EQ(sizeof(ushort), img.elemSize()); + EXPECT_EQ(1, img.channels()); + EXPECT_EQ(0xDEAD, img.at(0,0)); + EXPECT_EQ(0xBEEF, img.at(0,1)); + + remove(filename.c_str()); + } +} + #endif From d02c2911607b199e18988c29c3fb9df141555974 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 12 Feb 2014 10:56:57 +0400 Subject: [PATCH 112/293] opencv_run_all_tests.sh implemented for Android SDK. --- CMakeLists.txt | 28 ++++++---- .../opencv_run_all_tests_android.sh.in | 51 +++++++++++++++++++ ....sh.in => opencv_run_all_tests_unix.sh.in} | 0 3 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 cmake/templates/opencv_run_all_tests_android.sh.in rename cmake/templates/{opencv_run_all_tests.sh.in => opencv_run_all_tests_unix.sh.in} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6518cfc0e..92bee258e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -580,16 +580,24 @@ include(cmake/OpenCVGenConfig.cmake) include(cmake/OpenCVGenInfoPlist.cmake) # Generate environment setup file -if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH AND UNIX AND NOT ANDROID) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_testing.sh.in" - "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" @ONLY) - install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" - DESTINATION /etc/profile.d/ COMPONENT tests) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_run_all_tests.sh.in" - "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" @ONLY) - install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE - DESTINATION ${OPENCV_TEST_INSTALL_PATH} COMPONENT tests) +if(INSTALL_TESTS AND OPENCV_TEST_DATA_PATH AND UNIX) + if(ANDROID) + get_filename_component(TEST_PATH ${OPENCV_TEST_INSTALL_PATH} DIRECTORY) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_run_all_tests_android.sh.in" + "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" @ONLY) + install(PROGRAMS "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" + DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT tests) + else() + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_testing.sh.in" + "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" @ONLY) + install(FILES "${CMAKE_BINARY_DIR}/unix-install/opencv_testing.sh" + DESTINATION /etc/profile.d/ COMPONENT tests) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/opencv_run_all_tests_unix.sh.in" + "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" @ONLY) + install(PROGRAMS "${CMAKE_BINARY_DIR}/unix-install/opencv_run_all_tests.sh" + DESTINATION ${OPENCV_TEST_INSTALL_PATH} COMPONENT tests) + + endif() endif() # ---------------------------------------------------------------------------- diff --git a/cmake/templates/opencv_run_all_tests_android.sh.in b/cmake/templates/opencv_run_all_tests_android.sh.in new file mode 100644 index 0000000000..93373fa964 --- /dev/null +++ b/cmake/templates/opencv_run_all_tests_android.sh.in @@ -0,0 +1,51 @@ +#!/bin/sh + +BASE_DIR=`dirname $0` +OPENCV_TEST_PATH=$BASE_DIR/@TEST_PATH@ +OPENCV_TEST_DATA_PATH=$BASE_DIR/sdk/etc/testdata/ + +if [ $# -ne 1 ]; then + echo "Device architecture is not preset in command line" + echo "Tests are available for architectures: `ls -m ${OPENCV_TEST_PATH}`" + echo "Usage: $0 " + return 1 +else + TARGET_ARCH=$1 +fi + +if [ -z `which adb` ]; then + echo "adb command was not found in PATH" + return 1 +fi + +adb push $OPENCV_TEST_DATA_PATH /sdcard/opencv_testdata + +adb shell "mkdir -p /data/local/tmp/opencv_test" +SUMMARY_STATUS=0 +for t in "$OPENCV_TEST_PATH/$TARGET_ARCH/"opencv_test_* "$OPENCV_TEST_PATH/$TARGET_ARCH/"opencv_perf_*; +do + test_name=`basename "$t"` + report="$test_name-`date --rfc-3339=date`.xml" + adb push $t /data/local/tmp/opencv_test/ + adb shell "export OPENCV_TEST_DATA_PATH=/sdcard/opencv_testdata && /data/local/tmp/opencv_test/$test_name --perf_min_samples=1 --perf_force_samples=1 --gtest_output=xml:/data/local/tmp/opencv_test/$report" + adb pull "/data/local/tmp/opencv_test/$report" $report + TEST_STATUS=0 + if [ -e $report ]; then + if [ `grep -c " Date: Wed, 12 Feb 2014 23:41:38 +1100 Subject: [PATCH 113/293] Fix Windows build of grfmt_tiff.cpp by using libtiff's integer types. --- modules/highgui/src/grfmt_tiff.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/highgui/src/grfmt_tiff.cpp index c86b4e3654..2d976c2d0c 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/highgui/src/grfmt_tiff.cpp @@ -117,15 +117,15 @@ bool TiffDecoder::readHeader() if( tif ) { - uint wdth = 0, hght = 0; - ushort photometric = 0; + uint32 wdth = 0, hght = 0; + uint16 photometric = 0; m_tif = tif; if( TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &wdth ) && TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &hght ) && TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric )) { - ushort bpp=8, ncn = photometric > 1 ? 3 : 1; + uint16 bpp=8, ncn = photometric > 1 ? 3 : 1; TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); @@ -178,12 +178,12 @@ bool TiffDecoder::readData( Mat& img ) if( m_tif && m_width && m_height ) { TIFF* tif = (TIFF*)m_tif; - uint tile_width0 = m_width, tile_height0 = 0; + uint32 tile_width0 = m_width, tile_height0 = 0; int x, y, i; int is_tiled = TIFFIsTiled(tif); - ushort photometric; + uint16 photometric; TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ); - ushort bpp = 8, ncn = photometric > 1 ? 3 : 1; + uint16 bpp = 8, ncn = photometric > 1 ? 3 : 1; TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); const int bitsPerByte = 8; From 879c0196d44dc62ac394f76b13cf64047c40c16d Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Feb 2014 00:17:15 +0400 Subject: [PATCH 114/293] enabled OpenGL on MacOSX --- CMakeLists.txt | 2 +- modules/core/src/gl_core_3_1.cpp | 29 +++++---- samples/cpp/Qt_sample/CMakeLists.txt | 2 +- .../cpp/Qt_sample/{main.cpp => qt_opengl.cpp} | 65 ++++++++++--------- 4 files changed, 52 insertions(+), 46 deletions(-) rename samples/cpp/Qt_sample/{main.cpp => qt_opengl.cpp} (82%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f515682f4a..ba239a55c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,7 +142,7 @@ OCV_OPTION(WITH_IPP "Include Intel IPP support" OFF OCV_OPTION(WITH_JASPER "Include JPEG2K support" ON IF (NOT IOS) ) OCV_OPTION(WITH_JPEG "Include JPEG support" ON) OCV_OPTION(WITH_OPENEXR "Include ILM support via OpenEXR" ON IF (NOT IOS) ) -OCV_OPTION(WITH_OPENGL "Include OpenGL support" OFF IF (NOT ANDROID AND NOT APPLE) ) +OCV_OPTION(WITH_OPENGL "Include OpenGL support" OFF IF (NOT ANDROID) ) OCV_OPTION(WITH_OPENNI "Include OpenNI support" OFF IF (NOT ANDROID AND NOT IOS) ) OCV_OPTION(WITH_PNG "Include PNG support" ON) OCV_OPTION(WITH_PVAPI "Include Prosilica GigE support" ON IF (NOT ANDROID AND NOT IOS) ) diff --git a/modules/core/src/gl_core_3_1.cpp b/modules/core/src/gl_core_3_1.cpp index bd1eb7ddd3..4a83ba0ca0 100644 --- a/modules/core/src/gl_core_3_1.cpp +++ b/modules/core/src/gl_core_3_1.cpp @@ -47,22 +47,27 @@ #include "gl_core_3_1.hpp" #ifdef HAVE_OPENGL - #if defined(__APPLE__) - #include + + #ifdef __APPLE__ + #include static void* AppleGLGetProcAddress (const char* name) { - static const struct mach_header* image = 0; - if (!image) - image = NSAddImage("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", NSADDIMAGE_OPTION_RETURN_ON_ERROR); + static bool initialized = false; + static void * handle = NULL; + if (!handle) + { + if (!initialized) + { + initialized = true; + const char * const path = "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL"; - // prepend a '_' for the Unix C symbol mangling convention - std::string symbolName = "_"; - symbolName += std::string(name); - - NSSymbol symbol = image ? NSLookupSymbolInImage(image, &symbolName[0], NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) : 0; - - return symbol ? NSAddressOfSymbol(symbol) : 0; + handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL); + } + if (!handle) + return NULL; + } + return dlsym(handle, name); } #endif // __APPLE__ diff --git a/samples/cpp/Qt_sample/CMakeLists.txt b/samples/cpp/Qt_sample/CMakeLists.txt index e831f752f6..f465947dbc 100644 --- a/samples/cpp/Qt_sample/CMakeLists.txt +++ b/samples/cpp/Qt_sample/CMakeLists.txt @@ -7,6 +7,6 @@ FIND_PACKAGE( OpenCV REQUIRED ) find_package (OpenGL REQUIRED) -ADD_EXECUTABLE(OpenGL_Qt_Binding main.cpp) +ADD_EXECUTABLE(OpenGL_Qt_Binding qt_opengl.cpp) TARGET_LINK_LIBRARIES(OpenGL_Qt_Binding ${OpenCV_LIBS} ${OPENGL_LIBRARIES} ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cube4.avi ${CMAKE_CURRENT_BINARY_DIR}/cube4.avi COPYONLY) diff --git a/samples/cpp/Qt_sample/main.cpp b/samples/cpp/Qt_sample/qt_opengl.cpp similarity index 82% rename from samples/cpp/Qt_sample/main.cpp rename to samples/cpp/Qt_sample/qt_opengl.cpp index 6969544a0c..91f8a76f88 100644 --- a/samples/cpp/Qt_sample/main.cpp +++ b/samples/cpp/Qt_sample/qt_opengl.cpp @@ -33,14 +33,14 @@ static void help() cout << "\nThis demo demonstrates the use of the Qt enhanced version of the highgui GUI interface\n" " and dang if it doesn't throw in the use of of the POSIT 3D tracking algorithm too\n" "It works off of the video: cube4.avi\n" - "Using OpenCV version %s\n" << CV_VERSION << "\n\n" -" 1). This demo is mainly based on work from Javier Barandiaran Martirena\n" -" See this page http://code.opencv.org/projects/opencv/wiki/Posit.\n" -" 2). This is a demo to illustrate how to use **OpenGL Callback**.\n" -" 3). You need Qt binding to compile this sample with OpenGL support enabled.\n" -" 4). The features' detection is very basic and could highly be improved \n" -" (basic thresholding tuned for the specific video) but 2).\n" -" 5) THANKS TO Google Summer of Code 2010 for supporting this work!\n" << endl; + "Using OpenCV version " << CV_VERSION << "\n\n" + " 1). This demo is mainly based on work from Javier Barandiaran Martirena\n" + " See this page http://code.opencv.org/projects/opencv/wiki/Posit.\n" + " 2). This is a demo to illustrate how to use **OpenGL Callback**.\n" + " 3). You need Qt binding to compile this sample with OpenGL support enabled.\n" + " 4). The features' detection is very basic and could highly be improved \n" + " (basic thresholding tuned for the specific video) but 2).\n" + " 5) THANKS TO Google Summer of Code 2010 for supporting this work!\n" << endl; } #define FOCAL_LENGTH 600 @@ -88,7 +88,6 @@ static void renderCube(float size) glEnd(); } - static void on_opengl(void* param) { //Draw the object with the estimated pose @@ -121,8 +120,6 @@ static void foundCorners(vector *srcImagePoints, const Mat& source threshold(grayImage, grayImage, 26, 255, THRESH_BINARY_INV); //25 Mat MgrayImage = grayImage; - //For debug - //MgrayImage = MgrayImage.clone();//deep copy vector > contours; vector hierarchy; findContours(MgrayImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); @@ -199,21 +196,15 @@ static void foundCorners(vector *srcImagePoints, const Mat& source srcImagePoints->at(i) = cvPoint2D32f(srcImagePoints_temp.at(i).x-source.cols/2,source.rows/2-srcImagePoints_temp.at(i).y); } } - } static void createOpenGLMatrixFrom(float *posePOSIT,const CvMatr32f &rotationMatrix, const CvVect32f &translationVector) { - - //coordinate system returned is relative to the first 3D input point for (int f=0; f<3; f++) - { for (int c=0; c<3; c++) - { posePOSIT[c*4+f] = rotationMatrix[f*3+c]; //transposed - } - } + posePOSIT[3] = 0.0; posePOSIT[7] = 0.0; posePOSIT[11] = 0.0; @@ -226,19 +217,27 @@ static void createOpenGLMatrixFrom(float *posePOSIT,const CvMatr32f &rotationMat int main(void) { help(); - VideoCapture video("cube4.avi"); - CV_Assert(video.isOpened()); + + string fileName = "cube4.avi"; + VideoCapture video(fileName); + if (!video.isOpened()) + { + cerr << "Video file " << fileName << " could not be opened" << endl; + return EXIT_FAILURE; + } Mat source, grayImage; - video >> source; namedWindow("original", WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO); - namedWindow("POSIT", WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO); - displayOverlay("POSIT", "We lost the 4 corners' detection quite often (the red circles disappear). This demo is only to illustrate how to use OpenGL callback.\n -- Press ESC to exit.", 10000); + namedWindow("POSIT", WINDOW_OPENGL | CV_WINDOW_FREERATIO); + resizeWindow("POSIT", source.cols, source.rows); - float OpenGLMatrix[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - setOpenGlDrawCallback("POSIT",on_opengl,OpenGLMatrix); + displayOverlay("POSIT", "We lost the 4 corners' detection quite often (the red circles disappear)." + "This demo is only to illustrate how to use OpenGL callback.\n -- Press ESC to exit.", 10000); + + float OpenGLMatrix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + setOpenGlDrawCallback("POSIT", on_opengl, OpenGLMatrix); vector modelPoints; initPOSIT(&modelPoints); @@ -248,19 +247,20 @@ int main(void) CvMatr32f rotation_matrix = new float[9]; CvVect32f translation_vector = new float[3]; - CvTermCriteria criteria = cvTermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 100, 1.0e-4f); + CvTermCriteria criteria = cvTermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 100, 1e-4f); + vector srcImagePoints(4, cvPoint2D32f(0, 0)); - vector srcImagePoints(4,cvPoint2D32f(0,0)); - - - while(waitKey(33) != 27) + while (waitKey(33) != 27) { video >> source; - imshow("original",source); + if (source.empty()) + break; + + imshow("original", source); foundCorners(&srcImagePoints,source,grayImage); cvPOSIT( positObject, &srcImagePoints[0], FOCAL_LENGTH, criteria, rotation_matrix, translation_vector ); - createOpenGLMatrixFrom(OpenGLMatrix,rotation_matrix,translation_vector); + createOpenGLMatrixFrom(OpenGLMatrix, rotation_matrix, translation_vector); imshow("POSIT",source); @@ -268,6 +268,7 @@ int main(void) video.set(CV_CAP_PROP_POS_AVI_RATIO, 0); } + setOpenGlDrawCallback("POSIT", 0, 0); destroyAllWindows(); cvReleasePOSITObject(&positObject); From 55b9c0374c943bd5d7224190bed8d3228dd0d792 Mon Sep 17 00:00:00 2001 From: Stuart Cunningham Date: Thu, 13 Feb 2014 22:59:30 +1100 Subject: [PATCH 115/293] Fix cmake detection of build platform endianness Improve comments to indicate actual usage of WORDS_BIGENDIAN where it is tested with #ifdef rather than #if --- 3rdparty/libtiff/CMakeLists.txt | 2 -- 3rdparty/libtiff/tif_config.h.cmakein | 14 +++----------- CMakeLists.txt | 6 ++++++ cmake/templates/cvconfig.h.in | 2 +- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/3rdparty/libtiff/CMakeLists.txt b/3rdparty/libtiff/CMakeLists.txt index 5793021b78..addbb5551c 100644 --- a/3rdparty/libtiff/CMakeLists.txt +++ b/3rdparty/libtiff/CMakeLists.txt @@ -6,7 +6,6 @@ project(${TIFF_LIBRARY}) include(CheckFunctionExists) include(CheckIncludeFile) -include(TestBigEndian) check_include_file(assert.h HAVE_ASSERT_H) check_include_file(fcntl.h HAVE_FCNTL_H) @@ -17,7 +16,6 @@ check_include_file(search.h HAVE_SEARCH_H) check_include_file(string.h HAVE_STRING_H) check_include_file(sys/types.h HAVE_SYS_TYPES_H) check_include_file(unistd.h HAVE_UNISTD_H) -test_big_endian(HOST_BIGENDIAN) if(WIN32 AND NOT HAVE_WINRT) set(USE_WIN32_FILEIO 1) diff --git a/3rdparty/libtiff/tif_config.h.cmakein b/3rdparty/libtiff/tif_config.h.cmakein index 55c6cb26a0..d46761b525 100644 --- a/3rdparty/libtiff/tif_config.h.cmakein +++ b/3rdparty/libtiff/tif_config.h.cmakein @@ -54,7 +54,7 @@ /* Native cpu byte order: 1 if big-endian (Motorola) or 0 if little-endian (Intel) */ -#define HOST_BIGENDIAN @HOST_BIGENDIAN@ +#define HOST_BIGENDIAN @WORDS_BIGENDIAN@ /* Set the native cpu bit order (FILLORDER_LSB2MSB or FILLORDER_MSB2LSB) */ #define HOST_FILLORDER FILLORDER_LSB2MSB @@ -154,17 +154,9 @@ /* define to use win32 IO system */ #cmakedefine USE_WIN32_FILEIO -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most +/* Define WORDS_BIGENDIAN if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -/* # undef WORDS_BIGENDIAN */ -# endif -#endif +#cmakedefine WORDS_BIGENDIAN /* Support Deflate compression */ #define ZIP_SUPPORT 1 diff --git a/CMakeLists.txt b/CMakeLists.txt index c6518cfc0e..6489517659 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,6 +444,12 @@ endif() include(cmake/OpenCVPCHSupport.cmake) include(cmake/OpenCVModule.cmake) +# ---------------------------------------------------------------------------- +# Detect endianness of build platform +# ---------------------------------------------------------------------------- +include(TestBigEndian) +test_big_endian(WORDS_BIGENDIAN) + # ---------------------------------------------------------------------------- # Detect 3rd-party libraries # ---------------------------------------------------------------------------- diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index a6cee63684..d1c9e65d3d 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -161,6 +161,6 @@ /* Xine video library */ #cmakedefine HAVE_XINE -/* Define to 1 if your processor stores words with the most significant byte +/* Define if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #cmakedefine WORDS_BIGENDIAN From 7da3e98dfd1539c027b26fd67f9225e93af8d144 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 13 Feb 2014 16:47:02 +0400 Subject: [PATCH 116/293] Application pause/resume fix for Android sample NativeActivity. --- .../org/opencv/samples/NativeActivity/CvNativeActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/android/native-activity/src/org/opencv/samples/NativeActivity/CvNativeActivity.java b/samples/android/native-activity/src/org/opencv/samples/NativeActivity/CvNativeActivity.java index 04da9a9496..b9db22de1f 100644 --- a/samples/android/native-activity/src/org/opencv/samples/NativeActivity/CvNativeActivity.java +++ b/samples/android/native-activity/src/org/opencv/samples/NativeActivity/CvNativeActivity.java @@ -21,6 +21,7 @@ public class CvNativeActivity extends Activity { System.loadLibrary("native_activity"); Intent intent = new Intent(CvNativeActivity.this, android.app.NativeActivity.class); CvNativeActivity.this.startActivity(intent); + CvNativeActivity.this.finish(); } break; default: { @@ -34,7 +35,7 @@ public class CvNativeActivity extends Activity { Log.i(TAG, "Instantiated new " + this.getClass()); } - @Override + @Override public void onResume() { super.onResume(); From 9e69e2a07a9798d75a0949ab2b4ad063dd84e8f2 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 13 Feb 2014 17:16:43 +0400 Subject: [PATCH 117/293] increase epsilon for AlphaComp sanity test for integer input --- modules/gpu/perf/perf_imgproc.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 1e598297a7..23db16e0aa 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -1563,7 +1563,14 @@ PERF_TEST_P(Sz_Type_Op, ImgProc_AlphaComp, TEST_CYCLE() cv::gpu::alphaComp(d_img1, d_img2, dst, alpha_op); - GPU_SANITY_CHECK(dst, 1e-3, ERROR_RELATIVE); + if (CV_MAT_DEPTH(type) < CV_32F) + { + GPU_SANITY_CHECK(dst, 1); + } + else + { + GPU_SANITY_CHECK(dst, 1e-3, ERROR_RELATIVE); + } } else { From eb247d826f04673a23e4d050ee5cf0395bde82c2 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 13 Feb 2014 17:25:59 +0400 Subject: [PATCH 118/293] temporary disable perf test for StereoBeliefPropagation --- modules/gpu/perf/perf_calib3d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gpu/perf/perf_calib3d.cpp b/modules/gpu/perf/perf_calib3d.cpp index 91800649c2..4a67405980 100644 --- a/modules/gpu/perf/perf_calib3d.cpp +++ b/modules/gpu/perf/perf_calib3d.cpp @@ -93,7 +93,7 @@ PERF_TEST_P(ImagePair, Calib3D_StereoBM, ////////////////////////////////////////////////////////////////////// // StereoBeliefPropagation -PERF_TEST_P(ImagePair, Calib3D_StereoBeliefPropagation, +PERF_TEST_P(ImagePair, DISABLED_Calib3D_StereoBeliefPropagation, Values(pair_string("gpu/stereobp/aloe-L.png", "gpu/stereobp/aloe-R.png"))) { declare.time(300.0); From dbe7634286d405161adb30677aa4d07cc17e0de2 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 13 Feb 2014 18:17:47 +0400 Subject: [PATCH 119/293] Dead code removed as this cannot be null in Java. --- modules/java/generator/src/java/core+TermCriteria.java | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/java/generator/src/java/core+TermCriteria.java b/modules/java/generator/src/java/core+TermCriteria.java index 98a5e3c394..c67e51ea8d 100644 --- a/modules/java/generator/src/java/core+TermCriteria.java +++ b/modules/java/generator/src/java/core+TermCriteria.java @@ -87,7 +87,6 @@ public class TermCriteria { @Override public String toString() { - if (this == null) return "null"; return "{ type: " + type + ", maxCount: " + maxCount + ", epsilon: " + epsilon + "}"; } } From b92a46c130a0f34f07af64525fb57fd27c841654 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Feb 2014 23:47:18 +0400 Subject: [PATCH 120/293] some more fixes --- samples/cpp/Qt_sample/qt_opengl.cpp | 164 +++++++++++++--------------- 1 file changed, 78 insertions(+), 86 deletions(-) diff --git a/samples/cpp/Qt_sample/qt_opengl.cpp b/samples/cpp/Qt_sample/qt_opengl.cpp index 91f8a76f88..2878da4c00 100644 --- a/samples/cpp/Qt_sample/qt_opengl.cpp +++ b/samples/cpp/Qt_sample/qt_opengl.cpp @@ -1,6 +1,5 @@ -//Yannick Verdie 2010 - -//--- Please read help() below: --- +// Yannick Verdie 2010 +// --- Please read help() below: --- #include #include @@ -11,18 +10,10 @@ #include #include -#if defined WIN32 || defined _WIN32 || defined WINCE - #include - #undef small - #undef min - #undef max - #undef abs -#endif - #ifdef __APPLE__ - #include +#include #else - #include +#include #endif using namespace std; @@ -30,21 +21,22 @@ using namespace cv; static void help() { - cout << "\nThis demo demonstrates the use of the Qt enhanced version of the highgui GUI interface\n" - " and dang if it doesn't throw in the use of of the POSIT 3D tracking algorithm too\n" + cout << "This demo demonstrates the use of the Qt enhanced version of the highgui GUI interface\n" + "and dang if it doesn't throw in the use of of the POSIT 3D tracking algorithm too\n" "It works off of the video: cube4.avi\n" "Using OpenCV version " << CV_VERSION << "\n\n" - " 1). This demo is mainly based on work from Javier Barandiaran Martirena\n" - " See this page http://code.opencv.org/projects/opencv/wiki/Posit.\n" - " 2). This is a demo to illustrate how to use **OpenGL Callback**.\n" - " 3). You need Qt binding to compile this sample with OpenGL support enabled.\n" - " 4). The features' detection is very basic and could highly be improved \n" - " (basic thresholding tuned for the specific video) but 2).\n" - " 5) THANKS TO Google Summer of Code 2010 for supporting this work!\n" << endl; + + " 1) This demo is mainly based on work from Javier Barandiaran Martirena\n" + " See this page http://code.opencv.org/projects/opencv/wiki/Posit.\n" + " 2) This is a demo to illustrate how to use **OpenGL Callback**.\n" + " 3) You need Qt binding to compile this sample with OpenGL support enabled.\n" + " 4) The features' detection is very basic and could highly be improved\n" + " (basic thresholding tuned for the specific video) but 2).\n" + " 5) Thanks to Google Summer of Code 2010 for supporting this work!\n" << endl; } #define FOCAL_LENGTH 600 -#define CUBE_SIZE 10 +#define CUBE_SIZE 0.5 static void renderCube(float size) { @@ -103,19 +95,19 @@ static void on_opengl(void* param) glDisable( GL_LIGHTING ); } -static void initPOSIT(std::vector *modelPoints) +static void initPOSIT(std::vector * modelPoints) { - //Create the model pointss - modelPoints->push_back(cvPoint3D32f(0.0f, 0.0f, 0.0f)); //The first must be (0,0,0) + // Create the model pointss + modelPoints->push_back(cvPoint3D32f(0.0f, 0.0f, 0.0f)); // The first must be (0, 0, 0) modelPoints->push_back(cvPoint3D32f(0.0f, 0.0f, CUBE_SIZE)); modelPoints->push_back(cvPoint3D32f(CUBE_SIZE, 0.0f, 0.0f)); modelPoints->push_back(cvPoint3D32f(0.0f, CUBE_SIZE, 0.0f)); } -static void foundCorners(vector *srcImagePoints, const Mat& source, Mat& grayImage) +static void foundCorners(vector * srcImagePoints, const Mat & source, Mat & grayImage) { cvtColor(source, grayImage, COLOR_RGB2GRAY); - GaussianBlur(grayImage, grayImage, Size(11,11), 0, 0); + GaussianBlur(grayImage, grayImage, Size(11, 11), 0, 0); normalize(grayImage, grayImage, 0, 255, NORM_MINMAX); threshold(grayImage, grayImage, 26, 255, THRESH_BINARY_INV); //25 @@ -125,93 +117,85 @@ static void foundCorners(vector *srcImagePoints, const Mat& source findContours(MgrayImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); Point p; - vector srcImagePoints_temp(4,cvPoint2D32f(0,0)); + vector srcImagePoints_temp(4, cvPoint2D32f(0, 0)); if (contours.size() == srcImagePoints_temp.size()) { - - for(size_t i = 0 ; i y = 0 - //> x = 1 - //< x = 2 - //< y = 3 + // Need to keep the same order + // > y = 0 + // > x = 1 + // < x = 2 + // < y = 3 - //get point 0; + // get point 0; size_t index = 0; - for(size_t i = 1 ; i srcImagePoints_temp.at(index).y) index = i; - } srcImagePoints->at(0) = srcImagePoints_temp.at(index); - //get point 1; + // get point 1; index = 0; - for(size_t i = 1 ; i srcImagePoints_temp.at(index).x) index = i; - } srcImagePoints->at(1) = srcImagePoints_temp.at(index); - //get point 2; + // get point 2; index = 0; - for(size_t i = 1 ; iat(2) = srcImagePoints_temp.at(index); - //get point 3; + // get point 3; index = 0; - for(size_t i = 1 ; iat(3) = srcImagePoints_temp.at(index); Mat Msource = source; stringstream ss; - for(size_t i = 0 ; iat(i),5,Scalar(0,0,255)); - putText(Msource,ss.str(),srcImagePoints->at(i),FONT_HERSHEY_SIMPLEX,1,Scalar(0,0,255)); + ss << i; + circle(Msource, srcImagePoints->at(i), 5, Scalar(0, 0, 255)); + putText(Msource, ss.str(), srcImagePoints->at(i), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255)); ss.str(""); - //new coordinate system in the middle of the frame and reversed (camera coordinate system) - srcImagePoints->at(i) = cvPoint2D32f(srcImagePoints_temp.at(i).x-source.cols/2,source.rows/2-srcImagePoints_temp.at(i).y); + // new coordinate system in the middle of the frame and reversed (camera coordinate system) + srcImagePoints->at(i) = cvPoint2D32f(srcImagePoints_temp.at(i).x - source.cols / 2, + source.rows / 2 - srcImagePoints_temp.at(i).y); } } } -static void createOpenGLMatrixFrom(float *posePOSIT,const CvMatr32f &rotationMatrix, const CvVect32f &translationVector) +static void createOpenGLMatrixFrom(float * posePOSIT, const CvMatr32f & rotationMatrix, + const CvVect32f & translationVector) { - //coordinate system returned is relative to the first 3D input point - for (int f=0; f<3; f++) - for (int c=0; c<3; c++) - posePOSIT[c*4+f] = rotationMatrix[f*3+c]; //transposed + // coordinate system returned is relative to the first 3D input point + for (int f = 0; f < 3; f++) + for (int c = 0; c < 3; c++) + posePOSIT[c * 4 + f] = rotationMatrix[f * 3 + c]; // transposed - posePOSIT[3] = 0.0; - posePOSIT[7] = 0.0; - posePOSIT[11] = 0.0; - posePOSIT[12] = translationVector[0]; - posePOSIT[13] = translationVector[1]; - posePOSIT[14] = translationVector[2]; - posePOSIT[15] = 1.0; + posePOSIT[3] = translationVector[0]; + posePOSIT[7] = translationVector[1]; + posePOSIT[11] = translationVector[2]; + posePOSIT[12] = 0.0f; + posePOSIT[13] = 0.0f; + posePOSIT[14] = 0.0f; + posePOSIT[15] = 1.0f; } int main(void) @@ -229,21 +213,26 @@ int main(void) Mat source, grayImage; video >> source; - namedWindow("original", WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO); + namedWindow("Original", WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO); namedWindow("POSIT", WINDOW_OPENGL | CV_WINDOW_FREERATIO); resizeWindow("POSIT", source.cols, source.rows); - displayOverlay("POSIT", "We lost the 4 corners' detection quite often (the red circles disappear)." - "This demo is only to illustrate how to use OpenGL callback.\n -- Press ESC to exit.", 10000); + displayOverlay("POSIT", "We lost the 4 corners' detection quite often (the red circles disappear).\n" + "This demo is only to illustrate how to use OpenGL callback.\n" + " -- Press ESC to exit.", 10000); - float OpenGLMatrix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + float OpenGLMatrix[] = { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 }; + setOpenGlContext("POSIT"); setOpenGlDrawCallback("POSIT", on_opengl, OpenGLMatrix); vector modelPoints; initPOSIT(&modelPoints); - //Create the POSIT object with the model points - CvPOSITObject* positObject = cvCreatePOSITObject( &modelPoints[0], (int)modelPoints.size() ); + // Create the POSIT object with the model points + CvPOSITObject* positObject = cvCreatePOSITObject( &modelPoints[0], (int)modelPoints.size()); CvMatr32f rotation_matrix = new float[9]; CvVect32f translation_vector = new float[3]; @@ -256,21 +245,24 @@ int main(void) if (source.empty()) break; - imshow("original", source); + imshow("Original", source); - foundCorners(&srcImagePoints,source,grayImage); - cvPOSIT( positObject, &srcImagePoints[0], FOCAL_LENGTH, criteria, rotation_matrix, translation_vector ); + foundCorners(&srcImagePoints, source, grayImage); + cvPOSIT(positObject, &srcImagePoints[0], FOCAL_LENGTH, criteria, rotation_matrix, translation_vector); createOpenGLMatrixFrom(OpenGLMatrix, rotation_matrix, translation_vector); - imshow("POSIT",source); + updateWindow("POSIT"); if (video.get(CV_CAP_PROP_POS_AVI_RATIO) > 0.99) video.set(CV_CAP_PROP_POS_AVI_RATIO, 0); } - setOpenGlDrawCallback("POSIT", 0, 0); + setOpenGlDrawCallback("POSIT", NULL, NULL); destroyAllWindows(); cvReleasePOSITObject(&positObject); - return 0; + delete[]rotation_matrix; + delete[]translation_vector; + + return EXIT_SUCCESS; } From 1454843b81f642ca25716997522c6630f6bb624f Mon Sep 17 00:00:00 2001 From: Stuart Cunningham Date: Fri, 14 Feb 2014 16:16:17 +1100 Subject: [PATCH 121/293] Fix build of libtiff on big endian host due to defined but empty WORDS_BIGENDIAN macro --- 3rdparty/libtiff/tif_config.h.cmakein | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3rdparty/libtiff/tif_config.h.cmakein b/3rdparty/libtiff/tif_config.h.cmakein index d46761b525..24f58119ba 100644 --- a/3rdparty/libtiff/tif_config.h.cmakein +++ b/3rdparty/libtiff/tif_config.h.cmakein @@ -154,9 +154,9 @@ /* define to use win32 IO system */ #cmakedefine USE_WIN32_FILEIO -/* Define WORDS_BIGENDIAN if your processor stores words with the most +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ -#cmakedefine WORDS_BIGENDIAN +#cmakedefine WORDS_BIGENDIAN 1 /* Support Deflate compression */ #define ZIP_SUPPORT 1 From 1ce5165cb7ccabdd0280970e3f1b6bc180055a3d Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 14 Feb 2014 14:27:43 +0400 Subject: [PATCH 122/293] temporary disable performance test for alphaComp function --- modules/gpu/perf/perf_imgproc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 23db16e0aa..be0e312a02 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -1542,7 +1542,7 @@ CV_ENUM(AlphaOp, ALPHA_OVER, ALPHA_IN, ALPHA_OUT, ALPHA_ATOP, ALPHA_XOR, ALPHA_P DEF_PARAM_TEST(Sz_Type_Op, cv::Size, MatType, AlphaOp); -PERF_TEST_P(Sz_Type_Op, ImgProc_AlphaComp, +PERF_TEST_P(Sz_Type_Op, DISABLED_ImgProc_AlphaComp, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC4, CV_16UC4, CV_32SC4, CV_32FC4), AlphaOp::all())) From d1606b4aa37f6d1d6daeebe039f593a37cbf607e Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 30 Jan 2014 17:01:48 +0400 Subject: [PATCH 123/293] ocl: added SVM perf test --- modules/ocl/perf/perf_ml.cpp | 106 ++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/modules/ocl/perf/perf_ml.cpp b/modules/ocl/perf/perf_ml.cpp index db45eceb8f..13dcaa18f0 100644 --- a/modules/ocl/perf/perf_ml.cpp +++ b/modules/ocl/perf/perf_ml.cpp @@ -106,4 +106,108 @@ PERF_TEST_P(KNNFixture, KNN, }else OCL_PERF_ELSE SANITY_CHECK(best_label); -} \ No newline at end of file +} + + +typedef TestBaseWithParam > SVMFixture; + +// code is based on: samples\cpp\tutorial_code\ml\non_linear_svms\non_linear_svms.cpp +PERF_TEST_P(SVMFixture, DISABLED_SVM, + testing::Values(50, 100)) +{ + + const int NTRAINING_SAMPLES = get<0>(GetParam()); // Number of training samples per class + + #define FRAC_LINEAR_SEP 0.9f // Fraction of samples which compose the linear separable part + + const int WIDTH = 512, HEIGHT = 512; + + Mat trainData(2*NTRAINING_SAMPLES, 2, CV_32FC1); + Mat labels (2*NTRAINING_SAMPLES, 1, CV_32FC1); + + RNG rng(100); // Random value generation class + + // Set up the linearly separable part of the training data + int nLinearSamples = (int) (FRAC_LINEAR_SEP * NTRAINING_SAMPLES); + + // Generate random points for the class 1 + Mat trainClass = trainData.rowRange(0, nLinearSamples); + // The x coordinate of the points is in [0, 0.4) + Mat c = trainClass.colRange(0, 1); + rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(0.4 * WIDTH)); + // The y coordinate of the points is in [0, 1) + c = trainClass.colRange(1,2); + rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); + + // Generate random points for the class 2 + trainClass = trainData.rowRange(2*NTRAINING_SAMPLES-nLinearSamples, 2*NTRAINING_SAMPLES); + // The x coordinate of the points is in [0.6, 1] + c = trainClass.colRange(0 , 1); + rng.fill(c, RNG::UNIFORM, Scalar(0.6*WIDTH), Scalar(WIDTH)); + // The y coordinate of the points is in [0, 1) + c = trainClass.colRange(1,2); + rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); + + //------------------ Set up the non-linearly separable part of the training data --------------- + + // Generate random points for the classes 1 and 2 + trainClass = trainData.rowRange( nLinearSamples, 2*NTRAINING_SAMPLES-nLinearSamples); + // The x coordinate of the points is in [0.4, 0.6) + c = trainClass.colRange(0,1); + rng.fill(c, RNG::UNIFORM, Scalar(0.4*WIDTH), Scalar(0.6*WIDTH)); + // The y coordinate of the points is in [0, 1) + c = trainClass.colRange(1,2); + rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); + + //------------------------- Set up the labels for the classes --------------------------------- + labels.rowRange( 0, NTRAINING_SAMPLES).setTo(1); // Class 1 + labels.rowRange(NTRAINING_SAMPLES, 2*NTRAINING_SAMPLES).setTo(2); // Class 2 + + //------------------------ Set up the support vector machines parameters -------------------- + CvSVMParams params; + params.svm_type = SVM::C_SVC; + params.C = 0.1; + params.kernel_type = SVM::LINEAR; + params.term_crit = TermCriteria(CV_TERMCRIT_ITER, (int)1e7, 1e-6); + + Mat dst = Mat::zeros(HEIGHT, WIDTH, CV_8UC1); + + Mat samples(WIDTH*HEIGHT, 2, CV_32FC1); + int k = 0; + for (int i = 0; i < HEIGHT; ++i) + { + for (int j = 0; j < WIDTH; ++j) + { + samples.at(k, 0) = (float)i; + samples.at(k, 0) = (float)j; + k++; + } + } + Mat results(WIDTH*HEIGHT, 1, CV_32FC1); + + CvMat samples_ = samples; + CvMat results_ = results; + + if(RUN_PLAIN_IMPL) + { + CvSVM svm; + svm.train(trainData, labels, Mat(), Mat(), params); + TEST_CYCLE() + { + svm.predict(&samples_, &results_); + } + } + else if(RUN_OCL_IMPL) + { + CvSVM_OCL svm; + svm.train(trainData, labels, Mat(), Mat(), params); + OCL_TEST_CYCLE() + { + svm.predict(&samples_, &results_); + } + } + else + OCL_PERF_ELSE + + SANITY_CHECK_NOTHING(); +} From 8b8c3681484536fdc28d5fd0e8b7dd06ca2970f5 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 14 Feb 2014 17:56:03 +0400 Subject: [PATCH 124/293] fixed two warnings in gpu sources (-Wshadow, -Wno-sign-promo) --- cmake/OpenCVDetectCUDA.cmake | 3 +++ modules/core/CMakeLists.txt | 2 +- modules/nonfree/CMakeLists.txt | 2 +- modules/superres/CMakeLists.txt | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index b35a7977c3..56b142970e 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -180,6 +180,9 @@ if(CUDA_FOUND) # we remove -Wsign-promo as it generates warnings under linux string(REPLACE "-Wsign-promo" "" ${var} "${${var}}") + # we remove -Wno-sign-promo as it generates warnings under linux + string(REPLACE "-Wno-sign-promo" "" ${var} "${${var}}") + # we remove -Wno-delete-non-virtual-dtor because it's used for C++ compiler # but NVCC uses C compiler by default string(REPLACE "-Wno-delete-non-virtual-dtor" "" ${var} "${${var}}") diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index fe3eff33ab..d9de52da2f 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -25,7 +25,7 @@ endif() if(HAVE_CUDA) ocv_include_directories("${OpenCV_SOURCE_DIR}/modules/gpu/include") - ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wshadow) endif() file(GLOB lib_cuda_hdrs "include/opencv2/${name}/cuda/*.hpp" "include/opencv2/${name}/cuda/*.h") diff --git a/modules/nonfree/CMakeLists.txt b/modules/nonfree/CMakeLists.txt index 53fb2f73e9..b43273bc80 100644 --- a/modules/nonfree/CMakeLists.txt +++ b/modules/nonfree/CMakeLists.txt @@ -3,7 +3,7 @@ if(BUILD_ANDROID_PACKAGE) endif() set(the_description "Functionality with possible limitations on the use") -ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) +ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wshadow) if(ENABLE_DYNAMIC_CUDA) add_definitions(-DDYNAMIC_CUDA_SUPPORT) ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_ocl) diff --git a/modules/superres/CMakeLists.txt b/modules/superres/CMakeLists.txt index 3da8dc2c6e..82c61cccb4 100644 --- a/modules/superres/CMakeLists.txt +++ b/modules/superres/CMakeLists.txt @@ -3,5 +3,5 @@ if(ANDROID OR IOS) endif() set(the_description "Super Resolution") -ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 -Wundef) +ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 -Wundef -Wshadow) ocv_define_module(superres opencv_imgproc opencv_video OPTIONAL opencv_gpu opencv_highgui opencv_ocl ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) From e2f5001b1147829af7c75d22e811cad0150caacb Mon Sep 17 00:00:00 2001 From: yash Date: Fri, 14 Feb 2014 23:04:03 +0530 Subject: [PATCH 125/293] fixed doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst --- .../android_binary_package/dev_with_OCV_on_Android.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst index 3d7268c809..bc9ff7a4a8 100644 --- a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst +++ b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst @@ -382,7 +382,7 @@ result. OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback); } -#. Defines that your activity implements ``CvViewFrameListener2`` interface and fix activity related +#. Defines that your activity implements ``CvCameraViewListener2`` interface and fix activity related errors by defining missed methods. For this activity define ``onCreate``, ``onDestroy`` and ``onPause`` and implement them according code snippet bellow. Fix errors by adding requited imports. @@ -432,7 +432,7 @@ result. Lets discuss some most important steps. Every Android application with UI must implement Activity and View. By the first steps we create blank activity and default view layout. The simplest OpenCV-centric application must implement OpenCV initialization, create its own view to show -preview from camera and implements ``CvViewFrameListener2`` interface to get frames from camera and +preview from camera and implements ``CvCameraViewListener2`` interface to get frames from camera and process it. First of all we create our application view using xml layout. Our layout consists of the only From cce225f6e9cb18d5e6432e90faf8ca94f0267374 Mon Sep 17 00:00:00 2001 From: yash Date: Sat, 15 Feb 2014 12:48:42 +0530 Subject: [PATCH 126/293] fixed doc/tutorials/imgproc/histograms/template_matching/template_matching.rst --- .../imgproc/histograms/template_matching/template_matching.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/imgproc/histograms/template_matching/template_matching.rst b/doc/tutorials/imgproc/histograms/template_matching/template_matching.rst index cb7ece86f1..db6838a125 100644 --- a/doc/tutorials/imgproc/histograms/template_matching/template_matching.rst +++ b/doc/tutorials/imgproc/histograms/template_matching/template_matching.rst @@ -85,7 +85,7 @@ d. **method=CV\_TM\_CCORR\_NORMED** .. math:: - R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I'(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}} + R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}} e. **method=CV\_TM\_CCOEFF** From ed1aa73dd3c5b43a50bac920fea7c37f0606a328 Mon Sep 17 00:00:00 2001 From: Kirill Kornyakov Date: Sat, 15 Feb 2014 13:08:52 +0400 Subject: [PATCH 127/293] Replaced Gittip button --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 403f118eed..3a26ad8555 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ### OpenCV: Open Source Computer Vision Library +[![Gittip](http://img.shields.io/gittip/OpenCV.png)](https://www.gittip.com/OpenCV/) + #### Resources * Homepage: @@ -18,6 +20,3 @@ Summary of guidelines: * Include tests and documentation; * Clean up "oops" commits before submitting; * Follow the coding style guide. - -[![Donate OpenCV project](http://opencv.org/wp-content/uploads/2013/07/gittip1.png)](https://www.gittip.com/OpenCV/) -[![Donate OpenCV project](http://opencv.org/wp-content/uploads/2013/07/paypal-donate-button.png)](https://www.paypal.com/cgi-bin/webscr?item_name=Donation+to+OpenCV&cmd=_donations&business=accountant%40opencv.org) \ No newline at end of file From 1a5dfe421dcbc5c2d831fff61ad7d5bc69c2c59c Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sat, 15 Feb 2014 17:02:17 +0400 Subject: [PATCH 128/293] Added WidgetMerger, Polyline - colors support for each point independently, simple widgets now compute color array instead of setting global color --- modules/viz/doc/widget.rst | 41 +++++++ modules/viz/include/opencv2/viz/widgets.hpp | 15 +++ modules/viz/src/clouds.cpp | 114 ++++++++++++++------ modules/viz/src/precomp.hpp | 5 + modules/viz/src/shapes.cpp | 101 ++++++++--------- modules/viz/test/tests_simple.cpp | 31 +++++- 6 files changed, 222 insertions(+), 85 deletions(-) diff --git a/modules/viz/doc/widget.rst b/modules/viz/doc/widget.rst index 2802edff79..31546ae8e2 100644 --- a/modules/viz/doc/widget.rst +++ b/modules/viz/doc/widget.rst @@ -1025,3 +1025,44 @@ Constructs a WMesh. :param polygons: Points of the mesh object. :param colors: Point colors. :param normals: Point normals. + +viz::WWidgetMerger +--------------------- +.. ocv:class:: WWidgetMerger + +This class allos to merge several widgets to single one. It has quite limited functionality and can't merge widgets with different attributes. For instance, +if widgetA has color array and widgetB has only global color defined, then result of merge won't have color at all. The class is suitable for merging large amount of similar widgets. + + class CV_EXPORTS WWidgetMerger : public Widget3D + { + public: + WWidgetMerger(); + + //! Add widget to merge with optional position change + void addWidget(const Widget3D& widget, const Affine3d &pose = Affine3d::Identity()); + + //! Repacks internal structure to sinle widget + void finalize(); + }; + + +viz::WWidgetMerger::WWidgetMerger +--------------------------------------- +Constructs a WWidgetMerger. + +.. ocv:WWidgetMerger:: WWidgetMerger() + +viz::WWidgetMerger::addCloud +------------------------------- +Adds a cloud to the collection. + +.. ocv:function:: void addWidget(const Widget3D& widget, const Affine3d &pose = Affine3d::Identity()) + + :param widget: Widget to merge. + :param pose: Pose of the widget. + +viz::WWidgetMerger::finalize +------------------------------- +Finalizes merger data and constructs final merged widget + +.. ocv:function:: void finalize() \ No newline at end of file diff --git a/modules/viz/include/opencv2/viz/widgets.hpp b/modules/viz/include/opencv2/viz/widgets.hpp index 6910196c3d..0c2a4f7050 100644 --- a/modules/viz/include/opencv2/viz/widgets.hpp +++ b/modules/viz/include/opencv2/viz/widgets.hpp @@ -201,6 +201,7 @@ namespace cv class CV_EXPORTS WPolyLine : public Widget3D { public: + WPolyLine(InputArray points, InputArray colors); WPolyLine(InputArray points, const Color &color = Color::white()); }; @@ -362,6 +363,19 @@ namespace cv WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()); }; + + class CV_EXPORTS WWidgetMerger : public Widget3D + { + public: + WWidgetMerger(); + + //! Add widget to merge with optional position change + void addWidget(const Widget3D& widget, const Affine3d &pose = Affine3d::Identity()); + + //! Repacks internal structure to sinle widget + void finalize(); + }; + ///////////////////////////////////////////////////////////////////////////// /// Utility exports @@ -391,6 +405,7 @@ namespace cv template<> CV_EXPORTS WCloudCollection Widget::cast(); template<> CV_EXPORTS WCloudNormals Widget::cast(); template<> CV_EXPORTS WMesh Widget::cast(); + template<> CV_EXPORTS WWidgetMerger Widget::cast(); } /* namespace viz */ } /* namespace cv */ diff --git a/modules/viz/src/clouds.cpp b/modules/viz/src/clouds.cpp index 349de2f5f2..eec02639e5 100644 --- a/modules/viz/src/clouds.cpp +++ b/modules/viz/src/clouds.cpp @@ -193,8 +193,21 @@ template<> cv::viz::WPaintedCloud cv::viz::Widget::cast( cv::viz::WCloudCollection::WCloudCollection() { - // Just create the actor + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(append_filter->GetOutputPort()); + mapper->SetScalarModeToUsePointData(); + mapper->ImmediateModeRenderingOff(); + mapper->SetScalarRange(0, 255); + mapper->ScalarVisibilityOn(); + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->SetNumberOfCloudPoints(1); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + WidgetAccessor::setProp(*this, actor); } @@ -206,33 +219,11 @@ void cv::viz::WCloudCollection::addCloud(InputArray cloud, InputArray colors, co vtkSmartPointer polydata = VtkUtils::TransformPolydata(source->GetOutputPort(), pose); vtkSmartPointer actor = vtkLODActor::SafeDownCast(WidgetAccessor::getProp(*this)); - CV_Assert("Incompatible widget type." && actor); + CV_Assert("Correctness check." && actor); - vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); - if (!mapper) - { - vtkSmartPointer append_filter = vtkSmartPointer::New(); - VtkUtils::AddInputData(append_filter, polydata); - - // This is the first cloud - mapper = vtkSmartPointer::New(); - mapper->SetScalarRange(0, 255); - mapper->SetScalarModeToUsePointData(); - mapper->ScalarVisibilityOn(); - mapper->ImmediateModeRenderingOff(); - mapper->SetInputConnection(append_filter->GetOutputPort()); - - actor->SetNumberOfCloudPoints(std::max(1, polydata->GetNumberOfPoints()/10)); - actor->GetProperty()->SetInterpolationToFlat(); - actor->GetProperty()->BackfaceCullingOn(); - actor->SetMapper(mapper); - return; - } - - vtkSmartPointer producer = mapper->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer producer = actor->GetMapper()->GetInputConnection(0, 0)->GetProducer(); vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); VtkUtils::AddInputData(append_filter, polydata); - append_filter->Modified(); actor->SetNumberOfCloudPoints(std::max(1, actor->GetNumberOfCloudPoints() + polydata->GetNumberOfPoints()/10)); } @@ -257,7 +248,6 @@ void cv::viz::WCloudCollection::finalize() vtkSmartPointer polydata = append_filter->GetOutput(); mapper->RemoveInputConnection(0, 0); VtkUtils::SetInputData(mapper, polydata); - mapper->Modified(); } template<> cv::viz::WCloudCollection cv::viz::Widget::cast() @@ -332,20 +322,18 @@ cv::viz::WCloudNormals::WCloudNormals(InputArray _cloud, InputArray _normals, in } } - vtkSmartPointer polyData = vtkSmartPointer::New(); - polyData->SetPoints(points); - polyData->SetLines(lines); + vtkSmartPointer polydata = vtkSmartPointer::New(); + polydata->SetPoints(points); + polydata->SetLines(lines); + VtkUtils::FillScalars(polydata, color); vtkSmartPointer mapper = vtkSmartPointer::New(); - mapper->SetColorModeToMapScalars(); - mapper->SetScalarModeToUsePointData(); - VtkUtils::SetInputData(mapper, polyData); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } template<> cv::viz::WCloudNormals cv::viz::Widget::cast() @@ -455,3 +443,63 @@ template<> CV_EXPORTS cv::viz::WMesh cv::viz::Widget::cast() Widget3D widget = this->cast(); return static_cast(widget); } + + +/////////////////////////////////////////////////////////////////////////////////////////////// +/// Widget Merger implementation + +cv::viz::WWidgetMerger::WWidgetMerger() +{ + vtkSmartPointer append_filter = vtkSmartPointer::New(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(append_filter->GetOutputPort()); + mapper->SetScalarModeToUsePointData(); + mapper->ImmediateModeRenderingOff(); + mapper->SetScalarRange(0, 255); + mapper->ScalarVisibilityOn(); + + vtkSmartPointer actor = vtkSmartPointer::New(); + actor->GetProperty()->SetInterpolationToFlat(); + actor->GetProperty()->BackfaceCullingOn(); + actor->SetMapper(mapper); + + WidgetAccessor::setProp(*this, actor); +} + +void cv::viz::WWidgetMerger::addWidget(const Widget3D& widget, const Affine3d &pose) +{ + vtkActor *widget_actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(widget)); + CV_Assert("Widget is not 3D actor." && widget_actor); + + vtkSmartPointer widget_mapper = vtkPolyDataMapper::SafeDownCast(widget_actor->GetMapper()); + CV_Assert("Widget doesn't have a polydata mapper" && widget_mapper); + widget_mapper->Update(); + + vtkSmartPointer actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + vtkSmartPointer producer = actor->GetMapper()->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); + CV_Assert("Correctness check" && append_filter); + + VtkUtils::AddInputData(append_filter, VtkUtils::TransformPolydata(widget_mapper->GetInput(), pose)); +} + +void cv::viz::WWidgetMerger::finalize() +{ + vtkSmartPointer actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); + vtkSmartPointer producer = actor->GetMapper()->GetInputConnection(0, 0)->GetProducer(); + vtkSmartPointer append_filter = vtkAppendPolyData::SafeDownCast(producer); + CV_Assert("Correctness check" && append_filter); + append_filter->Update(); + + vtkSmartPointer mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + mapper->RemoveInputConnection(0, 0); + VtkUtils::SetInputData(mapper, append_filter->GetOutput()); + mapper->Modified(); +} + +template<> CV_EXPORTS cv::viz::WWidgetMerger cv::viz::Widget::cast() +{ + Widget3D widget = this->cast(); + return static_cast(widget); +} diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp index f77da5a5b0..c594962059 100644 --- a/modules/viz/src/precomp.hpp +++ b/modules/viz/src/precomp.hpp @@ -272,6 +272,11 @@ namespace cv return scalars; } + static vtkSmartPointer FillScalars(vtkSmartPointer polydata, const Color& color) + { + return polydata->GetPointData()->SetScalars(FillScalars(polydata->GetNumberOfPoints(), color)), polydata; + } + static vtkSmartPointer ComputeNormals(vtkSmartPointer polydata) { vtkSmartPointer normals_generator = vtkSmartPointer::New(); diff --git a/modules/viz/src/shapes.cpp b/modules/viz/src/shapes.cpp index 171470d812..4dd77038ec 100644 --- a/modules/viz/src/shapes.cpp +++ b/modules/viz/src/shapes.cpp @@ -54,14 +54,16 @@ cv::viz::WLine::WLine(const Point3d &pt1, const Point3d &pt2, const Color &color line->SetPoint2(pt2.x, pt2.y, pt2.z); line->Update(); + vtkSmartPointer polydata = line->GetOutput(); + VtkUtils::FillScalars(polydata, color); + vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, line->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } template<> cv::viz::WLine cv::viz::Widget::cast() @@ -83,14 +85,16 @@ cv::viz::WSphere::WSphere(const Point3d ¢er, double radius, int sphere_resol sphere->LatLongTessellationOff(); sphere->Update(); + vtkSmartPointer polydata = sphere->GetOutput(); + VtkUtils::FillScalars(polydata, color); + vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, sphere->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } template<> cv::viz::WSphere cv::viz::Widget::cast() @@ -110,15 +114,17 @@ cv::viz::WPlane::WPlane(const Size2d& size, const Color &color) plane->SetPoint2(-0.5 * size.width, 0.5 * size.height, 0.0); plane->Update(); + vtkSmartPointer polydata = plane->GetOutput(); + VtkUtils::FillScalars(polydata, color); + vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, plane->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); actor->GetProperty()->LightingOff(); WidgetAccessor::setProp(*this, actor); - setColor(color); } cv::viz::WPlane::WPlane(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, const Size2d& size, const Color &color) @@ -161,6 +167,7 @@ cv::viz::WArrow::WArrow(const Point3d& pt1, const Point3d& pt2, double thickness Affine3d transform_with_scale(R * length, start_point); vtkSmartPointer polydata = VtkUtils::TransformPolydata(arrow_source->GetOutputPort(), transform_with_scale); + VtkUtils::FillScalars(polydata, color); vtkSmartPointer mapper = vtkSmartPointer::New(); VtkUtils::SetInputData(mapper, polydata); @@ -169,7 +176,6 @@ cv::viz::WArrow::WArrow(const Point3d& pt1, const Point3d& pt2, double thickness actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } template<> cv::viz::WArrow cv::viz::Widget::cast() @@ -189,16 +195,17 @@ cv::viz::WCircle::WCircle(double radius, double thickness, const Color &color) disk->SetOuterRadius(radius + thickness); disk->Update(); + vtkSmartPointer polydata = disk->GetOutput(); + VtkUtils::FillScalars(polydata, color); + vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, disk->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->GetProperty()->LightingOff(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); - } cv::viz::WCircle::WCircle(double radius, const Point3d& center, const Vec3d& normal, double thickness, const Color &color) @@ -231,14 +238,16 @@ cv::viz::WCone::WCone(double length, double radius, int resolution, const Color cone_source->SetResolution(resolution); cone_source->Update(); + vtkSmartPointer polydata = cone_source->GetOutput(); + VtkUtils::FillScalars(polydata, color); + vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, cone_source->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } cv::viz::WCone::WCone(double radius, const Point3d& center, const Point3d& tip, int resolution, const Color &color) @@ -274,14 +283,16 @@ cv::viz::WCylinder::WCylinder(const Point3d& axis_point1, const Point3d& axis_po tuber->SetRadius(radius); tuber->Update(); + vtkSmartPointer polydata = tuber->GetOutput(); + VtkUtils::FillScalars(polydata, color); + vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, tuber->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } template<> cv::viz::WCylinder cv::viz::Widget::cast() @@ -315,15 +326,16 @@ cv::viz::WCube::WCube(const Point3d& min_point, const Point3d& max_point, bool w vtkCubeSource::SafeDownCast(cube)->SetBounds(bounds); } cube->Update(); + vtkSmartPointer polydata =cube->GetOutput(); + VtkUtils::FillScalars(polydata, color); vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, cube->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } template<> cv::viz::WCube cv::viz::Widget::cast() @@ -379,40 +391,21 @@ template<> cv::viz::WCoordinateSystem cv::viz::Widget::cast cloud_source = vtkSmartPointer::New(); + cloud_source->SetColorCloud(points, colors); + cloud_source->Update(); - const float *fpoints = _points.getMat().ptr(); - const double *dpoints = _points.getMat().ptr(); - size_t total = _points.total(); - int s_chs = _points.channels(); - - vtkSmartPointer points = vtkSmartPointer::New(); - points->SetDataType(_points.depth() == CV_32F ? VTK_FLOAT : VTK_DOUBLE); - points->SetNumberOfPoints((vtkIdType)total); - - if (_points.depth() == CV_32F) - for(size_t i = 0; i < total; ++i, fpoints += s_chs) - points->SetPoint((vtkIdType)i, fpoints); - - if (_points.depth() == CV_64F) - for(size_t i = 0; i < total; ++i, dpoints += s_chs) - points->SetPoint((vtkIdType)i, dpoints); + vtkSmartPointer polydata = cloud_source->GetOutput(); vtkSmartPointer cell_array = vtkSmartPointer::New(); - cell_array->Allocate(cell_array->EstimateSize(1, (int)total)); - cell_array->InsertNextCell((int)total); - for(size_t i = 0; i < total; ++i) - cell_array->InsertCellPoint((vtkIdType)i); + cell_array->Allocate(cell_array->EstimateSize(1, polydata->GetNumberOfPoints())); + cell_array->InsertNextCell(polydata->GetNumberOfPoints()); + for(vtkIdType i = 0; i < polydata->GetNumberOfPoints(); ++i) + cell_array->InsertCellPoint(i); - vtkSmartPointer scalars = VtkUtils::FillScalars(total, color); - - vtkSmartPointer polydata = vtkSmartPointer::New(); - polydata->SetPoints(points); polydata->SetLines(cell_array); - polydata->GetPointData()->SetScalars(scalars); - vtkSmartPointer mapper = vtkSmartPointer::New(); VtkUtils::SetInputData(mapper, polydata); mapper->SetScalarRange(0, 255); @@ -423,6 +416,12 @@ cv::viz::WPolyLine::WPolyLine(InputArray _points, const Color &color) WidgetAccessor::setProp(*this, actor); } +cv::viz::WPolyLine::WPolyLine(InputArray points, const Color &color) +{ + WPolyLine polyline(points, Mat(points.size(), CV_8UC3, color)); + *this = polyline; +} + template<> cv::viz::WPolyLine cv::viz::Widget::cast() { Widget3D widget = this->cast(); @@ -450,14 +449,16 @@ cv::viz::WGrid::WGrid(const Vec2i &cells, const Vec2d &cells_spacing, const Colo VtkUtils::SetInputData(extract_edges, grid_data); extract_edges->Update(); + vtkSmartPointer polydata = extract_edges->GetOutput(); + VtkUtils::FillScalars(polydata, color); + vtkSmartPointer mapper = vtkSmartPointer::New(); - VtkUtils::SetInputData(mapper, extract_edges->GetOutput()); + VtkUtils::SetInputData(mapper, polydata); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } cv::viz::WGrid::WGrid(const Point3d& center, const Vec3d& normal, const Vec3d& new_yaxis, const Vec2i &cells, const Vec2d &cells_spacing, const Color &color) @@ -807,6 +808,7 @@ cv::viz::WCameraPosition::WCameraPosition(const Matx33d &K, double scale, const double aspect_ratio = f_y / f_x; vtkSmartPointer polydata = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + VtkUtils::FillScalars(polydata, color); vtkSmartPointer mapper = vtkSmartPointer::New(); VtkUtils::SetInputData(mapper, polydata); @@ -815,7 +817,6 @@ cv::viz::WCameraPosition::WCameraPosition(const Matx33d &K, double scale, const actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } cv::viz::WCameraPosition::WCameraPosition(const Vec2d &fov, double scale, const Color &color) @@ -824,6 +825,7 @@ cv::viz::WCameraPosition::WCameraPosition(const Vec2d &fov, double scale, const double fovy = fov[1] * 180 / CV_PI; vtkSmartPointer polydata = CameraPositionUtils::createFrustum(aspect_ratio, fovy, scale); + VtkUtils::FillScalars(polydata, color); vtkSmartPointer mapper = vtkSmartPointer::New(); VtkUtils::SetInputData(mapper, polydata); @@ -832,7 +834,6 @@ cv::viz::WCameraPosition::WCameraPosition(const Vec2d &fov, double scale, const actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } cv::viz::WCameraPosition::WCameraPosition(const Matx33d &K, InputArray _image, double scale, const Color &color) @@ -967,6 +968,7 @@ cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Matx33 source->SetTrajectory(_path); vtkSmartPointer glyph = getPolyData(WCameraPosition(K, scale)); + VtkUtils::FillScalars(glyph, color); vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); tensor_glyph->SetInputConnection(source->GetOutputPort()); @@ -984,7 +986,6 @@ cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Matx33 actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Vec2d &fov, double scale, const Color &color) @@ -993,6 +994,7 @@ cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Vec2d source->SetTrajectory(_path); vtkSmartPointer glyph = getPolyData(WCameraPosition(fov, scale)); + VtkUtils::FillScalars(glyph, color); vtkSmartPointer tensor_glyph = vtkSmartPointer::New(); tensor_glyph->SetInputConnection(source->GetOutputPort()); @@ -1010,7 +1012,6 @@ cv::viz::WTrajectoryFrustums::WTrajectoryFrustums(InputArray _path, const Vec2d actor->SetMapper(mapper); WidgetAccessor::setProp(*this, actor); - setColor(color); } template<> cv::viz::WTrajectoryFrustums cv::viz::Widget::cast() diff --git a/modules/viz/test/tests_simple.cpp b/modules/viz/test/tests_simple.cpp index 10c1d81808..d1b059cf67 100644 --- a/modules/viz/test/tests_simple.cpp +++ b/modules/viz/test/tests_simple.cpp @@ -156,6 +156,27 @@ TEST(Viz, show_mesh_random_colors) viz.spin(); } +TEST(Viz, show_widget_merger) +{ + WWidgetMerger merger; + merger.addWidget(WCube(Vec3d::all(0.0), Vec3d::all(1.0), true, Color::gold())); + + RNG& rng = theRNG(); + for(int i = 0; i < 77; ++i) + { + Vec3b c; + rng.fill(c, RNG::NORMAL, Scalar::all(128), Scalar::all(48), true); + merger.addWidget(WSphere(Vec3d(c)*(1.0/255.0), 7.0/255.0, 10, Color(c[2], c[1], c[0]))); + } + merger.finalize(); + + Viz3d viz("show_mesh_random_color"); + viz.showWidget("coo", WCoordinateSystem()); + viz.showWidget("merger", merger); + viz.showWidget("text2d", WText("Widget merger", Point(20, 20), 20, Color::green())); + viz.spin(); +} + TEST(Viz, show_textured_mesh) { Mat lena = imread(Path::combine(cvtest::TS::ptr()->get_data_path(), "lena.png")); @@ -195,12 +216,18 @@ TEST(Viz, show_textured_mesh) TEST(Viz, show_polyline) { - Mat polyline(1, 32, CV_64FC3); + const Color palette[] = { Color::red(), Color::green(), Color::blue(), Color::gold(), Color::raspberry(), Color::bluberry(), Color::lime() }; + size_t palette_size = sizeof(palette)/sizeof(palette[0]); + + Mat polyline(1, 32, CV_64FC3), colors(1, 32, CV_8UC3); for(int i = 0; i < (int)polyline.total(); ++i) + { polyline.at(i) = Vec3d(i/16.0, cos(i * CV_PI/6), sin(i * CV_PI/6)); + colors.at(i) = palette[i & palette_size]; + } Viz3d viz("show_polyline"); - viz.showWidget("polyline", WPolyLine(Mat(polyline), Color::apricot())); + viz.showWidget("polyline", WPolyLine(polyline, colors)); viz.showWidget("coosys", WCoordinateSystem()); viz.showWidget("text2d", WText("Polyline", Point(20, 20), 20, Color::green())); viz.spin(); From 56754e907dcc40f5caa9bcae242a1d0ae5eb1932 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sat, 15 Feb 2014 20:08:04 +0400 Subject: [PATCH 129/293] fixed crash for windows if console widow is closed or CTRL_C/CTRL_BRAK keys in console are pressed. --- modules/viz/include/opencv2/viz/viz3d.hpp | 2 + modules/viz/src/precomp.hpp | 14 +++++- modules/viz/src/viz3d.cpp | 2 + modules/viz/src/vizcore.cpp | 52 +++++++++++++++++++---- modules/viz/src/vizimpl.cpp | 2 + modules/viz/src/vizimpl.hpp | 2 +- 6 files changed, 62 insertions(+), 12 deletions(-) diff --git a/modules/viz/include/opencv2/viz/viz3d.hpp b/modules/viz/include/opencv2/viz/viz3d.hpp index 1a137bcfb3..1be748c8cd 100644 --- a/modules/viz/include/opencv2/viz/viz3d.hpp +++ b/modules/viz/include/opencv2/viz/viz3d.hpp @@ -114,6 +114,8 @@ namespace cv double getRenderingProperty(const String &id, int property); void setRepresentation(int representation); + + void setGlobalWarnings(bool enabled = false); private: struct VizImpl; diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp index c594962059..795ac16b07 100644 --- a/modules/viz/src/precomp.hpp +++ b/modules/viz/src/precomp.hpp @@ -155,7 +155,16 @@ namespace cv namespace viz { typedef std::map > WidgetActorMap; - typedef std::map VizMap; + + struct VizMap + { + typedef std::map type; + typedef type::iterator iterator; + + type m; + ~VizMap(); + void replace_clear(); + }; class VizStorage { @@ -167,7 +176,6 @@ namespace cv private: VizStorage(); // Static - ~VizStorage(); static void add(const Viz3d& window); static Viz3d& get(const String &window_name); @@ -177,6 +185,8 @@ namespace cv static VizMap storage; friend class Viz3d; + + static VizStorage init; }; template inline _Tp normalized(const _Tp& v) { return v * 1/norm(v); } diff --git a/modules/viz/src/viz3d.cpp b/modules/viz/src/viz3d.cpp index 56f978c0ea..6e7dfcae78 100644 --- a/modules/viz/src/viz3d.cpp +++ b/modules/viz/src/viz3d.cpp @@ -146,3 +146,5 @@ void cv::viz::Viz3d::setRenderingProperty(const String &id, int property, double double cv::viz::Viz3d::getRenderingProperty(const String &id, int property) { return getWidget(id).getRenderingProperty(property); } void cv::viz::Viz3d::setRepresentation(int representation) { impl_->setRepresentation(representation); } + +void cv::viz::Viz3d::setGlobalWarnings(bool enabled) { vtkObject::SetGlobalWarningDisplay(enabled ? 1 : 0); } diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp index b4332a860a..1166c08741 100644 --- a/modules/viz/src/vizcore.cpp +++ b/modules/viz/src/vizcore.cpp @@ -67,36 +67,70 @@ cv::Affine3d cv::viz::makeCameraPose(const Vec3d& position, const Vec3d& focal_p /////////////////////////////////////////////////////////////////////////////////////////////// /// VizStorage implementation +#if defined(_WIN32) && !defined(__CYGWIN__) + + #include + + static BOOL WINAPI ConsoleHandlerRoutine(DWORD /*dwCtrlType*/) + { + vtkObject::GlobalWarningDisplayOff(); + return FALSE; + } + + static void register_console_handler() + { + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO hOutInfo; + if (GetConsoleScreenBufferInfo(hOut, &hOutInfo)) + SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE); + } + +#else + + void register_console_handler() {} + +#endif + + +cv::viz::VizStorage cv::viz::VizStorage::init; cv::viz::VizMap cv::viz::VizStorage::storage; -void cv::viz::VizStorage::unregisterAll() { storage.clear(); } + +void cv::viz::VizMap::replace_clear() { type().swap(m); } +cv::viz::VizMap::~VizMap() { replace_clear(); } + +cv::viz::VizStorage::VizStorage() +{ + register_console_handler(); +} +void cv::viz::VizStorage::unregisterAll() { storage.replace_clear(); } cv::viz::Viz3d& cv::viz::VizStorage::get(const String &window_name) { String name = generateWindowName(window_name); - VizMap::iterator vm_itr = storage.find(name); - CV_Assert(vm_itr != storage.end()); + VizMap::iterator vm_itr = storage.m.find(name); + CV_Assert(vm_itr != storage.m.end()); return vm_itr->second; } void cv::viz::VizStorage::add(const Viz3d& window) { String window_name = window.getWindowName(); - VizMap::iterator vm_itr = storage.find(window_name); - CV_Assert(vm_itr == storage.end()); - storage.insert(std::make_pair(window_name, window)); + VizMap::iterator vm_itr = storage.m.find(window_name); + CV_Assert(vm_itr == storage.m.end()); + storage.m.insert(std::make_pair(window_name, window)); } bool cv::viz::VizStorage::windowExists(const String &window_name) { String name = generateWindowName(window_name); - return storage.find(name) != storage.end(); + return storage.m.find(name) != storage.m.end(); } void cv::viz::VizStorage::removeUnreferenced() { - for(VizMap::iterator pos = storage.begin(); pos != storage.end();) + for(VizMap::iterator pos = storage.m.begin(); pos != storage.m.end();) if(pos->second.impl_->ref_counter == 1) - storage.erase(pos++); + storage.m.erase(pos++); else ++pos; } diff --git a/modules/viz/src/vizimpl.cpp b/modules/viz/src/vizimpl.cpp index 3e852dd718..37d008bc82 100644 --- a/modules/viz/src/vizimpl.cpp +++ b/modules/viz/src/vizimpl.cpp @@ -72,6 +72,8 @@ cv::viz::Viz3d::VizImpl::VizImpl(const String &name) : spin_once_state_(false), setBackgroundMeshLab(); } +cv::viz::Viz3d::VizImpl::~VizImpl() { close(); } + ///////////////////////////////////////////////////////////////////////////////////////////// void cv::viz::Viz3d::VizImpl::TimerCallback::Execute(vtkObject* caller, unsigned long event_id, void* cookie) { diff --git a/modules/viz/src/vizimpl.hpp b/modules/viz/src/vizimpl.hpp index 02675e0a5c..67cab3674b 100644 --- a/modules/viz/src/vizimpl.hpp +++ b/modules/viz/src/vizimpl.hpp @@ -55,7 +55,7 @@ public: int ref_counter; VizImpl(const String &name); - virtual ~VizImpl() {} + virtual ~VizImpl(); bool wasStopped() const; void close(); From 03bd82796de2278f0fac1424a255c339d214ec10 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sat, 15 Feb 2014 23:40:09 +0400 Subject: [PATCH 130/293] source compatibility with master --- modules/viz/src/vizcore.cpp | 5 +++++ modules/viz/test/test_precomp.hpp | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp index 1166c08741..4544f9b3cd 100644 --- a/modules/viz/src/vizcore.cpp +++ b/modules/viz/src/vizcore.cpp @@ -291,7 +291,11 @@ void cv::viz::writeTrajectory(InputArray _traj, const String& files_format, int { if (_traj.kind() == _InputArray::STD_VECTOR_MAT) { +#if CV_MAJOR_VERSION < 3 std::vector& v = *(std::vector*)_traj.obj; +#else + std::vector& v = *(std::vector*)_traj.getObj(); +#endif for(size_t i = 0, index = max(0, start); i < v.size(); ++i, ++index) { @@ -317,6 +321,7 @@ void cv::viz::writeTrajectory(InputArray _traj, const String& files_format, int if (traj.depth() == CV_64F) for(size_t i = 0, index = max(0, start); i < traj.total(); ++i, ++index) writePose(cv::format(files_format.c_str(), index), traj.at((int)i), tag); + return; } CV_Assert(!"Unsupported array kind"); diff --git a/modules/viz/test/test_precomp.hpp b/modules/viz/test/test_precomp.hpp index cd00b6e73b..05914e2848 100644 --- a/modules/viz/test/test_precomp.hpp +++ b/modules/viz/test/test_precomp.hpp @@ -54,12 +54,20 @@ #ifndef __OPENCV_TEST_PRECOMP_HPP__ #define __OPENCV_TEST_PRECOMP_HPP__ -#include "opencv2/ts/ts.hpp" -#include -#include -#include +#include #include +namespace cv +{ + Mat imread(const String& filename, int flags = 1); +} + +#if CV_MAJOR_VERSION < 3 + #include "opencv2/ts/ts.hpp" +#else + #include "opencv2/ts.hpp" +#endif + #include #include #include From 510680a5df77f520449e0e74680486e00e88fb96 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 16 Feb 2014 01:42:02 +0400 Subject: [PATCH 131/293] typo --- modules/core/include/opencv2/core/types_c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/types_c.h b/modules/core/include/opencv2/core/types_c.h index 3e5d5b0334..99ac0d2575 100644 --- a/modules/core/include/opencv2/core/types_c.h +++ b/modules/core/include/opencv2/core/types_c.h @@ -245,7 +245,7 @@ enum { CV_StsVecLengthErr= -28, /* incorrect vector length */ CV_StsFilterStructContentErr= -29, /* incorr. filter structure content */ CV_StsKernelStructContentErr= -30, /* incorr. transform kernel content */ - CV_StsFilterOffsetErr= -31, /* incorrect filter ofset value */ + CV_StsFilterOffsetErr= -31, /* incorrect filter offset value */ CV_StsBadSize= -201, /* the input/output structure size is incorrect */ CV_StsDivByZero= -202, /* division by zero */ CV_StsInplaceNotSupported= -203, /* in-place operation is not supported */ From 150e522beea4807c7d6342e050a1eb6f9fb71199 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Sun, 16 Feb 2014 12:17:17 +0400 Subject: [PATCH 132/293] fix bug #3552: replace std::swap with own code --- modules/gpu/src/cuda/canny.cu | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/gpu/src/cuda/canny.cu b/modules/gpu/src/cuda/canny.cu index 2ab260ec26..1dc179e340 100644 --- a/modules/gpu/src/cuda/canny.cu +++ b/modules/gpu/src/cuda/canny.cu @@ -42,8 +42,6 @@ #if !defined CUDA_DISABLER -#include -#include //std::swap #include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/emulation.hpp" #include "opencv2/gpu/device/transform.hpp" @@ -463,7 +461,10 @@ namespace canny count = min(count, map.cols * map.rows); - std::swap(st1, st2); + //std::swap(st1, st2); + short2* tmp = st1; + st1 = st2; + st2 = tmp; } } } From 9a98cd6e6520d348b0be067360236c50181e9983 Mon Sep 17 00:00:00 2001 From: Marijan Vukcevich Date: Tue, 4 Feb 2014 10:10:50 -0800 Subject: [PATCH 133/293] Update cap_ios_abstract_camera.mm AVCaptureVideoPreviewLayer setOrientation is depricated. This fixes the warning and provides backward compatibility. --- modules/highgui/src/cap_ios_abstract_camera.mm | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/modules/highgui/src/cap_ios_abstract_camera.mm b/modules/highgui/src/cap_ios_abstract_camera.mm index a77e200a82..6675a9db63 100644 --- a/modules/highgui/src/cap_ios_abstract_camera.mm +++ b/modules/highgui/src/cap_ios_abstract_camera.mm @@ -278,9 +278,21 @@ { self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; - if ([self.captureVideoPreviewLayer isOrientationSupported]) { - [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation]; - } + if ([self.captureVideoPreviewLayer respondsToSelector:@selector(connection)]) + { + if ([self.captureVideoPreviewLayer.connection isVideoOrientationSupported]) + { + [self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation]; + } + } + else + { + // Deprecated in 6.0; here for backward compatibility + if ([self.captureVideoPreviewLayer isOrientationSupported]) + { + [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation]; + } + } if (parentView != nil) { self.captureVideoPreviewLayer.frame = self.parentView.bounds; From 01a980aa9e2293c079012ead3976ed51223dbcbb Mon Sep 17 00:00:00 2001 From: Alexander Karsakov Date: Mon, 17 Feb 2014 15:28:21 +0400 Subject: [PATCH 134/293] Fixed compilation with IPP on Linux. Added linking with Intel compiler runtime libraries. --- cmake/OpenCVFindIPP.cmake | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/cmake/OpenCVFindIPP.cmake b/cmake/OpenCVFindIPP.cmake index 772cae886f..db02e6acbf 100644 --- a/cmake/OpenCVFindIPP.cmake +++ b/cmake/OpenCVFindIPP.cmake @@ -163,9 +163,16 @@ function(set_ipp_new_libraries _LATEST_VERSION) ${IPP_LIB_PREFIX}${IPP_PREFIX}${IPPCV}${IPP_SUFFIX}${IPP_LIB_SUFFIX} ${IPP_LIB_PREFIX}${IPP_PREFIX}${IPPI}${IPP_SUFFIX}${IPP_LIB_SUFFIX} ${IPP_LIB_PREFIX}${IPP_PREFIX}${IPPS}${IPP_SUFFIX}${IPP_LIB_SUFFIX} - ${IPP_LIB_PREFIX}${IPP_PREFIX}${IPPCORE}${IPP_SUFFIX}${IPP_LIB_SUFFIX} - PARENT_SCOPE) + ${IPP_LIB_PREFIX}${IPP_PREFIX}${IPPCORE}${IPP_SUFFIX}${IPP_LIB_SUFFIX}) + if (UNIX) + set(IPP_LIBRARIES + ${IPP_LIBRARIES} + ${IPP_LIB_PREFIX}irc${CMAKE_SHARED_LIBRARY_SUFFIX} + ${IPP_LIB_PREFIX}imf${CMAKE_SHARED_LIBRARY_SUFFIX} + ${IPP_LIB_PREFIX}svml${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() + set(IPP_LIBRARIES ${IPP_LIBRARIES} PARENT_SCOPE) return() endfunction() @@ -208,19 +215,39 @@ function(set_ipp_variables _LATEST_VERSION) set(IPP_INCLUDE_DIRS ${IPP_ROOT_DIR}/include PARENT_SCOPE) if (APPLE) - set(IPP_LIBRARY_DIRS ${IPP_ROOT_DIR}/lib PARENT_SCOPE) + set(IPP_LIBRARY_DIRS ${IPP_ROOT_DIR}/lib) elseif (IPP_X64) if(NOT EXISTS ${IPP_ROOT_DIR}/lib/intel64) message(SEND_ERROR "IPP EM64T libraries not found") endif() - set(IPP_LIBRARY_DIRS ${IPP_ROOT_DIR}/lib/intel64 PARENT_SCOPE) + set(IPP_LIBRARY_DIRS ${IPP_ROOT_DIR}/lib/intel64) else() if(NOT EXISTS ${IPP_ROOT_DIR}/lib/ia32) message(SEND_ERROR "IPP IA32 libraries not found") endif() - set(IPP_LIBRARY_DIRS ${IPP_ROOT_DIR}/lib/ia32 PARENT_SCOPE) + set(IPP_LIBRARY_DIRS ${IPP_ROOT_DIR}/lib/ia32) endif() + if (UNIX) + get_filename_component(INTEL_COMPILER_LIBRARY_DIR ${IPP_ROOT_DIR}/../lib REALPATH) + if (IPP_X64) + if(NOT EXISTS ${INTEL_COMPILER_LIBRARY_DIR}/intel64) + message(SEND_ERROR "Intel compiler EM64T libraries not found") + endif() + set(IPP_LIBRARY_DIRS + ${IPP_LIBRARY_DIRS} + ${INTEL_COMPILER_LIBRARY_DIR}/intel64) + else() + if(NOT EXISTS ${INTEL_COMPILER_LIBRARY_DIR}/ia32) + message(SEND_ERROR "Intel compiler IA32 libraries not found") + endif() + set(IPP_LIBRARY_DIRS + ${IPP_LIBRARY_DIRS} + ${INTEL_COMPILER_LIBRARY_DIR}/ia32) + endif() + endif() + set(IPP_LIBRARY_DIRS ${IPP_LIBRARY_DIRS} PARENT_SCOPE) + # set IPP_LIBRARIES variable (7.x or 8.x lib names) set_ipp_new_libraries(${_LATEST_VERSION}) set(IPP_LIBRARIES ${IPP_LIBRARIES} PARENT_SCOPE) From 8236181c627448bf5e662648ab78ffc60d7d835e Mon Sep 17 00:00:00 2001 From: Evgeniy Date: Mon, 17 Feb 2014 17:01:23 +0400 Subject: [PATCH 135/293] Added more colorizing options to XLS generating script --- modules/ts/misc/xls-report.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ts/misc/xls-report.py b/modules/ts/misc/xls-report.py index e71a7f66c7..6b90b59247 100755 --- a/modules/ts/misc/xls-report.py +++ b/modules/ts/misc/xls-report.py @@ -97,6 +97,9 @@ re_data_type = re.compile(r'^ (?: 8 | 16 | 32 | 64 ) [USF] C [1234] $', re.VERBO time_style = xlwt.easyxf(num_format_str='#0.00') no_time_style = xlwt.easyxf('pattern: pattern solid, fore_color gray25') +failed_style = xlwt.easyxf('pattern: pattern solid, fore_color red') +noimpl_style = xlwt.easyxf('pattern: pattern solid, fore_color orange') +style_dict = {"failed": failed_style, "noimpl":noimpl_style} speedup_style = time_style good_speedup_style = xlwt.easyxf('font: color green', num_format_str='#0.00') @@ -328,7 +331,7 @@ def main(): for c in config_names: if c in configs: - sheet.write(row, col, configs[c], time_style) + sheet.write(row, col, configs[c], style_dict.get(configs[c], time_style)) else: sheet.write(row, col, None, no_time_style) col += 1 From cc529d971b7fb6b459c9cc5acde44cf1afded5d9 Mon Sep 17 00:00:00 2001 From: Alexander Shishkov Date: Mon, 17 Feb 2014 17:37:02 +0400 Subject: [PATCH 136/293] removed tabs --- .../highgui/src/cap_ios_abstract_camera.mm | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/modules/highgui/src/cap_ios_abstract_camera.mm b/modules/highgui/src/cap_ios_abstract_camera.mm index 6675a9db63..e5c70724b1 100644 --- a/modules/highgui/src/cap_ios_abstract_camera.mm +++ b/modules/highgui/src/cap_ios_abstract_camera.mm @@ -279,20 +279,20 @@ self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; if ([self.captureVideoPreviewLayer respondsToSelector:@selector(connection)]) - { - if ([self.captureVideoPreviewLayer.connection isVideoOrientationSupported]) - { - [self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation]; - } - } - else - { - // Deprecated in 6.0; here for backward compatibility - if ([self.captureVideoPreviewLayer isOrientationSupported]) - { - [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation]; - } - } + { + if ([self.captureVideoPreviewLayer.connection isVideoOrientationSupported]) + { + [self.captureVideoPreviewLayer.connection setVideoOrientation:self.defaultAVCaptureVideoOrientation]; + } + } + else + { + // Deprecated in 6.0; here for backward compatibility + if ([self.captureVideoPreviewLayer isOrientationSupported]) + { + [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation]; + } + } if (parentView != nil) { self.captureVideoPreviewLayer.frame = self.parentView.bounds; @@ -302,9 +302,6 @@ NSLog(@"[Camera] created AVCaptureVideoPreviewLayer"); } - - - - (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition; { for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { From 63235f51f06f15b0eddeb3a212808f91f574400b Mon Sep 17 00:00:00 2001 From: Alexander Shishkov Date: Mon, 17 Feb 2014 17:56:12 +0400 Subject: [PATCH 137/293] fixed year in copyright --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 312a1c2b9e..d815924727 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -44,7 +44,7 @@ master_doc = 'index' # General information about the project. project = u'OpenCV' -copyright = u'2011-2013, opencv dev team' +copyright = u'2011-2014, opencv dev team' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From a8eab26bae0bc7f7f38e9e72235896311c65de72 Mon Sep 17 00:00:00 2001 From: Alexander Shishkov Date: Mon, 17 Feb 2014 18:10:08 +0400 Subject: [PATCH 138/293] fixed incorrect code in introduction tutorial --- .../linux_gcc_cmake/linux_gcc_cmake.rst | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst b/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst index f582d32086..9aa1f6289b 100644 --- a/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst +++ b/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst @@ -25,29 +25,34 @@ Let's use a simple program such as DisplayImage.cpp shown below. .. code-block:: cpp - #include - #include + #include + #include - using namespace cv; + using namespace cv; - int main( int argc, char** argv ) - { - Mat image; - image = imread( argv[1], 1 ); + int main(int argc, char** argv ) + { + if ( argc != 2 ) + { + printf("usage: DisplayImage.out \n"); + return -1; + } - if( argc != 2 || !image.data ) - { - printf( "No image data \n" ); - return -1; - } + Mat image; + image = imread( argv[1], 1 ); - namedWindow( "Display Image", CV_WINDOW_AUTOSIZE ); - imshow( "Display Image", image ); + if ( !image.data ) + { + printf("No image data \n"); + return -1; + } + namedWindow("Display Image", CV_WINDOW_AUTOSIZE ); + imshow("Display Image", image); - waitKey(0); + waitKey(0); - return 0; - } + return 0; + } Create a CMake file --------------------- From 01527c44fd23c6a5956557910710fae2a7880a95 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 17 Feb 2014 21:50:00 +0400 Subject: [PATCH 139/293] removed unused field --- modules/imgproc/src/clahe.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/src/clahe.cpp b/modules/imgproc/src/clahe.cpp index 4ce479713e..9ecf792daf 100644 --- a/modules/imgproc/src/clahe.cpp +++ b/modules/imgproc/src/clahe.cpp @@ -49,8 +49,8 @@ namespace class CLAHE_CalcLut_Body : public cv::ParallelLoopBody { public: - CLAHE_CalcLut_Body(const cv::Mat& src, cv::Mat& lut, cv::Size tileSize, int tilesX, int tilesY, int clipLimit, float lutScale) : - src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY), clipLimit_(clipLimit), lutScale_(lutScale) + CLAHE_CalcLut_Body(const cv::Mat& src, cv::Mat& lut, cv::Size tileSize, int tilesX, int clipLimit, float lutScale) : + src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale) { } @@ -62,7 +62,6 @@ namespace cv::Size tileSize_; int tilesX_; - int tilesY_; int clipLimit_; float lutScale_; }; @@ -293,7 +292,7 @@ namespace clipLimit = std::max(clipLimit, 1); } - CLAHE_CalcLut_Body calcLutBody(srcForLut, lut_, tileSize, tilesX_, tilesY_, clipLimit, lutScale); + CLAHE_CalcLut_Body calcLutBody(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale); cv::parallel_for_(cv::Range(0, tilesX_ * tilesY_), calcLutBody); CLAHE_Interpolation_Body interpolationBody(src, dst, lut_, tileSize, tilesX_, tilesY_); From 017a282c7ab2a83922107ba5c425fb35a7ef43dc Mon Sep 17 00:00:00 2001 From: yash Date: Tue, 18 Feb 2014 10:34:58 +0530 Subject: [PATCH 140/293] Minor error in the documentation Load and Save Image --- doc/tutorials/introduction/load_save_image/load_save_image.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/introduction/load_save_image/load_save_image.rst b/doc/tutorials/introduction/load_save_image/load_save_image.rst index 50fb9ea37f..675387efb1 100644 --- a/doc/tutorials/introduction/load_save_image/load_save_image.rst +++ b/doc/tutorials/introduction/load_save_image/load_save_image.rst @@ -100,7 +100,7 @@ Explanation imshow( imageName, image ); imshow( "Gray image", gray_image ); -#. Add add the *waitKey(0)* function call for the program to wait forever for an user key press. +#. Add the *waitKey(0)* function call for the program to wait forever for an user key press. Result From 38ef8894b7f5e85217faa6e44cf7d10fc4bc7020 Mon Sep 17 00:00:00 2001 From: yash Date: Tue, 18 Feb 2014 10:51:05 +0530 Subject: [PATCH 141/293] Made a sentence in the doc for Mat::copyTo more clearer --- modules/core/doc/basic_structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/doc/basic_structures.rst b/modules/core/doc/basic_structures.rst index 6f9c10e1db..d23da8de19 100644 --- a/modules/core/doc/basic_structures.rst +++ b/modules/core/doc/basic_structures.rst @@ -1355,7 +1355,7 @@ The method copies the matrix data to another matrix. Before copying the data, th so that the destination matrix is reallocated if needed. While ``m.copyTo(m);`` works flawlessly, the function does not handle the case of a partial overlap between the source and the destination matrices. -When the operation mask is specified, and the ``Mat::create`` call shown above reallocated the matrix, the newly allocated matrix is initialized with all zeros before copying the data. +When the operation mask is specified and the ``Mat::create`` call shown above is invoked to reallocate the matrix, the newly allocated matrix is initialized with all zeros before copying the data. .. _Mat::convertTo: From de6d13088fc435e1cdc4c6ae4221a3912b80b2ca Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 14 Feb 2014 17:43:33 +0400 Subject: [PATCH 142/293] gcov tool support added. --- CMakeLists.txt | 1 + cmake/OpenCVCompilerOptions.cmake | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14033b390f..b5fbb9f2b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,6 +206,7 @@ OCV_OPTION(ENABLE_DYNAMIC_CUDA "Enabled dynamic CUDA linkage" OCV_OPTION(ENABLE_PRECOMPILED_HEADERS "Use precompiled headers" ON IF (NOT IOS) ) OCV_OPTION(ENABLE_SOLUTION_FOLDERS "Solution folder in Visual Studio or in other IDEs" (MSVC_IDE OR CMAKE_GENERATOR MATCHES Xcode) IF (CMAKE_VERSION VERSION_GREATER "2.8.0") ) OCV_OPTION(ENABLE_PROFILING "Enable profiling in the GCC compiler (Add flags: -g -pg)" OFF IF CMAKE_COMPILER_IS_GNUCXX ) +OCV_OPTION(ENABLE_COVERAGE "Enable coverage collection with GCov" OFF IF CMAKE_COMPILER_IS_GNUCXX ) OCV_OPTION(ENABLE_OMIT_FRAME_POINTER "Enable -fomit-frame-pointer for GCC" ON IF CMAKE_COMPILER_IS_GNUCXX AND NOT (APPLE AND CMAKE_COMPILER_IS_CLANGCXX) ) OCV_OPTION(ENABLE_POWERPC "Enable PowerPC for GCC" ON IF (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_PROCESSOR MATCHES powerpc.*) ) OCV_OPTION(ENABLE_FAST_MATH "Enable -ffast-math (not recommended for GCC 4.6.x)" OFF IF (CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index a4b039280f..d525609d18 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -187,6 +187,11 @@ if(CMAKE_COMPILER_IS_GNUCXX) add_extra_compiler_option(-ffunction-sections) endif() + if(ENABLE_COVERAGE) + set(OPENCV_EXTRA_C_FLAGS "${OPENCV_EXTRA_C_FLAGS} --coverage") + set(OPENCV_EXTRA_CXX_FLAGS "${OPENCV_EXTRA_CXX_FLAGS} --coverage") + endif() + set(OPENCV_EXTRA_FLAGS_RELEASE "${OPENCV_EXTRA_FLAGS_RELEASE} -DNDEBUG") set(OPENCV_EXTRA_FLAGS_DEBUG "${OPENCV_EXTRA_FLAGS_DEBUG} -O0 -DDEBUG -D_DEBUG") endif() From 394c74b349d1d0e912324b9e2136700be0a39e8b Mon Sep 17 00:00:00 2001 From: yash Date: Tue, 18 Feb 2014 15:04:27 +0530 Subject: [PATCH 143/293] edited the doc for mat::copyto and clarified the part regarding reallocation by .create --- modules/core/doc/basic_structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/doc/basic_structures.rst b/modules/core/doc/basic_structures.rst index d23da8de19..54a1f4261d 100644 --- a/modules/core/doc/basic_structures.rst +++ b/modules/core/doc/basic_structures.rst @@ -1355,7 +1355,7 @@ The method copies the matrix data to another matrix. Before copying the data, th so that the destination matrix is reallocated if needed. While ``m.copyTo(m);`` works flawlessly, the function does not handle the case of a partial overlap between the source and the destination matrices. -When the operation mask is specified and the ``Mat::create`` call shown above is invoked to reallocate the matrix, the newly allocated matrix is initialized with all zeros before copying the data. +When the operation mask is specified, if the ``Mat::create`` call shown above reallocates the matrix, the newly allocated matrix is initialized with all zeros before copying the data. .. _Mat::convertTo: From e35d98e566b628d52660854bf9a6e56c0251654e Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Mon, 17 Feb 2014 16:08:28 +0400 Subject: [PATCH 144/293] Worked around an apparent GCC bug in moments. --- modules/imgproc/src/moments.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/imgproc/src/moments.cpp b/modules/imgproc/src/moments.cpp index 784a61b8df..2fca862bca 100644 --- a/modules/imgproc/src/moments.cpp +++ b/modules/imgproc/src/moments.cpp @@ -197,6 +197,10 @@ static void icvContourMoments( CvSeq* contour, CvMoments* moments ) \****************************************************************************************/ template +#if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9 +// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60196 +__attribute__((optimize("no-tree-vectorize"))) +#endif static void momentsInTile( const cv::Mat& img, double* moments ) { cv::Size size = img.size(); From 77df5948e78cc630c8f6478f276b386c8affaa61 Mon Sep 17 00:00:00 2001 From: Alexander Shishkov Date: Tue, 18 Feb 2014 13:56:26 +0400 Subject: [PATCH 145/293] removed trailing spaces --- modules/highgui/src/cap_ios_abstract_camera.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highgui/src/cap_ios_abstract_camera.mm b/modules/highgui/src/cap_ios_abstract_camera.mm index e5c70724b1..b40b3648de 100644 --- a/modules/highgui/src/cap_ios_abstract_camera.mm +++ b/modules/highgui/src/cap_ios_abstract_camera.mm @@ -291,7 +291,7 @@ if ([self.captureVideoPreviewLayer isOrientationSupported]) { [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation]; - } + } } if (parentView != nil) { From da9bdf9c15e435364d52cc317d3e119ca4aaae44 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 18 Feb 2014 14:16:54 +0400 Subject: [PATCH 146/293] fixes typo and unused variables --- modules/gpu/src/cuda/nlm.cu | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/gpu/src/cuda/nlm.cu b/modules/gpu/src/cuda/nlm.cu index 922cba7e5a..8789a4cf6b 100644 --- a/modules/gpu/src/cuda/nlm.cu +++ b/modules/gpu/src/cuda/nlm.cu @@ -266,7 +266,7 @@ namespace cv { namespace gpu { namespace device __device__ __forceinline__ int calcDist(const uchar2& a, const uchar2& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); } __device__ __forceinline__ int calcDist(const uchar3& a, const uchar3& b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z); } - template struct FastNonLocalMenas + template struct FastNonLocalMeans { enum { @@ -290,7 +290,7 @@ namespace cv { namespace gpu { namespace device int block_window; float minus_h2_inv; - FastNonLocalMenas(int search_window_, int block_window_, float h) : search_radius(search_window_/2), block_radius(block_window_/2), + FastNonLocalMeans(int search_window_, int block_window_, float h) : search_radius(search_window_/2), block_radius(block_window_/2), search_window(search_window_), block_window(block_window_), minus_h2_inv(-1.f/(h * h * VecTraits::cn)) {} PtrStep src; @@ -394,7 +394,7 @@ namespace cv { namespace gpu { namespace device } } - __device__ __forceinline__ void convolve_window(int i, int j, const int* dist_sums, PtrStepi& col_sums, PtrStepi& up_col_sums, T& dst) const + __device__ __forceinline__ void convolve_window(int i, int j, const int* dist_sums, T& dst) const { typedef typename TypeVec::cn>::vec_type sum_type; @@ -471,18 +471,18 @@ namespace cv { namespace gpu { namespace device __syncthreads(); - convolve_window(i, j, dist_sums, col_sums, up_col_sums, dst(i, j)); + convolve_window(i, j, dist_sums, dst(i, j)); } } }; template - __global__ void fast_nlm_kernel(const FastNonLocalMenas fnlm, PtrStepSz dst) { fnlm(dst); } + __global__ void fast_nlm_kernel(const FastNonLocalMeans fnlm, PtrStepSz dst) { fnlm(dst); } void nln_fast_get_buffer_size(const PtrStepSzb& src, int search_window, int block_window, int& buffer_cols, int& buffer_rows) { - typedef FastNonLocalMenas FNLM; + typedef FastNonLocalMeans FNLM; dim3 grid(divUp(src.cols, FNLM::TILE_COLS), divUp(src.rows, FNLM::TILE_ROWS)); buffer_cols = search_window * search_window * grid.y; @@ -493,7 +493,7 @@ namespace cv { namespace gpu { namespace device void nlm_fast_gpu(const PtrStepSzb& src, PtrStepSzb dst, PtrStepi buffer, int search_window, int block_window, float h, cudaStream_t stream) { - typedef FastNonLocalMenas FNLM; + typedef FastNonLocalMeans FNLM; FNLM fnlm(search_window, block_window, h); fnlm.src = (PtrStepSz)src; From e460a29cab9d929de3d12632718f7c677a20d5c7 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 18 Feb 2014 14:24:17 +0400 Subject: [PATCH 147/293] typo --- .../android/service/engine/jni/Tests/HardwareDetectionTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/android/service/engine/jni/Tests/HardwareDetectionTest.cpp b/platforms/android/service/engine/jni/Tests/HardwareDetectionTest.cpp index 83dd9c27e1..8e7dfab006 100644 --- a/platforms/android/service/engine/jni/Tests/HardwareDetectionTest.cpp +++ b/platforms/android/service/engine/jni/Tests/HardwareDetectionTest.cpp @@ -55,7 +55,7 @@ TEST(Parse, ParseEmptyString) EXPECT_FALSE(ParseString(a, key, value)); } -TEST(Parse, ParseStringWithoutSeporator) +TEST(Parse, ParseStringWithoutSeparator) { string a = "qqqwww"; string key; From 4c1ed138461400a8d74801a92c9cc048d8539723 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 18 Feb 2014 12:01:01 +0400 Subject: [PATCH 148/293] Warning fixes for GCC 4.8. --- modules/imgproc/src/color.cpp | 4 ++-- modules/imgproc/src/floodfill.cpp | 2 +- modules/imgproc/src/imgwarp.cpp | 12 +++++++++++- modules/imgproc/test/test_convhull.cpp | 8 ++++---- modules/legacy/src/bgfg_gaussmix.cpp | 4 ++-- modules/legacy/src/face.cpp | 1 + modules/legacy/src/lmeds.cpp | 4 ++-- modules/ts/src/ts_arrtest.cpp | 2 +- 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index 15c214ef91..08f27aef97 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -3214,7 +3214,7 @@ struct YUV420p2RGB888Invoker : ParallelLoopBody const int rangeBegin = range.start * 2; const int rangeEnd = range.end * 2; - size_t uvsteps[2] = {width/2, stride - width/2}; + int uvsteps[2] = {width/2, stride - width/2}; int usIdx = ustepIdx, vsIdx = vstepIdx; const uchar* y1 = my1 + rangeBegin * stride; @@ -3282,7 +3282,7 @@ struct YUV420p2RGBA8888Invoker : ParallelLoopBody int rangeBegin = range.start * 2; int rangeEnd = range.end * 2; - size_t uvsteps[2] = {width/2, stride - width/2}; + int uvsteps[2] = {width/2, stride - width/2}; int usIdx = ustepIdx, vsIdx = vstepIdx; const uchar* y1 = my1 + rangeBegin * stride; diff --git a/modules/imgproc/src/floodfill.cpp b/modules/imgproc/src/floodfill.cpp index 74047676e4..db2563ddea 100644 --- a/modules/imgproc/src/floodfill.cpp +++ b/modules/imgproc/src/floodfill.cpp @@ -41,7 +41,7 @@ #include "precomp.hpp" -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic ignored "-Warray-bounds" #endif diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 4748af2cee..6cbb416c9e 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -1217,8 +1217,13 @@ public: alpha(_alpha), _beta(__beta), ssize(_ssize), dsize(_dsize), ksize(_ksize), xmin(_xmin), xmax(_xmax) { + CV_Assert(ksize <= MAX_ESIZE); } +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Warray-bounds" +#endif virtual void operator() (const Range& range) const { int dy, cn = src.channels(); @@ -1267,6 +1272,9 @@ public: vresize( (const WT**)rows, (T*)(dst.data + dst.step*dy), beta, dsize.width ); } } +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +# pragma GCC diagnostic pop +#endif private: Mat src; @@ -1274,7 +1282,9 @@ private: const int* xofs, *yofs; const AT* alpha, *_beta; Size ssize, dsize; - int ksize, xmin, xmax; + const int ksize, xmin, xmax; + + resizeGeneric_Invoker& operator = (const resizeGeneric_Invoker&); }; template diff --git a/modules/imgproc/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index e20b1a2076..75fddfd04f 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -1225,7 +1225,7 @@ CV_FitLineTest::CV_FitLineTest() max_noise = 0.05; } -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Warray-bounds" #endif @@ -1301,7 +1301,7 @@ void CV_FitLineTest::generate_point_set( void* pointsSet ) } } -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic pop #endif @@ -1329,7 +1329,7 @@ void CV_FitLineTest::run_func() cv::fitLine(cv::cvarrToMat(points), (cv::Vec6f&)line[0], dist_type, 0, reps, aeps); } -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Warray-bounds" #endif @@ -1412,7 +1412,7 @@ _exit_: return code; } -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic pop #endif diff --git a/modules/legacy/src/bgfg_gaussmix.cpp b/modules/legacy/src/bgfg_gaussmix.cpp index 3cb7a5af9f..829e4fa14c 100644 --- a/modules/legacy/src/bgfg_gaussmix.cpp +++ b/modules/legacy/src/bgfg_gaussmix.cpp @@ -415,7 +415,7 @@ CV_INLINE int _icvRemoveShadowGMM(float* data, int nD, //IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004 //http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif @@ -608,7 +608,7 @@ CV_INLINE int _icvUpdateGMM(float* data, int nD, return bBackground; } -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic pop #endif diff --git a/modules/legacy/src/face.cpp b/modules/legacy/src/face.cpp index b188a10def..2132ea8f95 100644 --- a/modules/legacy/src/face.cpp +++ b/modules/legacy/src/face.cpp @@ -200,6 +200,7 @@ void RFace::CalculateError(FaceData * lpFaceData) void RFace::CreateFace(void * lpData) { FaceData Data; + memset(&Data, 0, sizeof(FaceData)); double Error = MAX_ERROR; double CurError = MAX_ERROR; diff --git a/modules/legacy/src/lmeds.cpp b/modules/legacy/src/lmeds.cpp index 33b57a7597..d1bb298ec1 100644 --- a/modules/legacy/src/lmeds.cpp +++ b/modules/legacy/src/lmeds.cpp @@ -163,7 +163,7 @@ icvLMedS( int *points1, int *points2, int numPoints, CvMatrix3 * fundamentalMatr /*===========================================================================*/ /*===========================================================================*/ -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Warray-bounds" #endif @@ -328,7 +328,7 @@ icvCubic( double a2, double a1, double a0, double *squares ) return CV_NO_ERR; } /* icvCubic */ -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) # pragma GCC diagnostic pop #endif diff --git a/modules/ts/src/ts_arrtest.cpp b/modules/ts/src/ts_arrtest.cpp index ec3f18330d..cdfbe23b37 100644 --- a/modules/ts/src/ts_arrtest.cpp +++ b/modules/ts/src/ts_arrtest.cpp @@ -122,7 +122,7 @@ void ArrayTest::get_test_array_types_and_sizes( int /*test_case_idx*/, vector Date: Thu, 20 Feb 2014 10:24:52 +0400 Subject: [PATCH 149/293] fix bug #3562: add missing __syncthreads to edgesHysteresisLocalKernel --- modules/gpu/src/cuda/canny.cu | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/gpu/src/cuda/canny.cu b/modules/gpu/src/cuda/canny.cu index 1dc179e340..468eec4172 100644 --- a/modules/gpu/src/cuda/canny.cu +++ b/modules/gpu/src/cuda/canny.cu @@ -293,8 +293,12 @@ namespace canny n += smem[threadIdx.y + 2][threadIdx.x + 2] == 2; } + __syncthreads(); + if (n > 0) smem[threadIdx.y + 1][threadIdx.x + 1] = 2; + + __syncthreads(); } const int e = smem[threadIdx.y + 1][threadIdx.x + 1]; From aa5311ea9fdc21f4b76df555aa0d1a9d529bb325 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 20 Feb 2014 18:55:08 +0400 Subject: [PATCH 150/293] backport of master-based perf tests for matchTemplate to 2.4 --- modules/ocl/perf/perf_match_template.cpp | 57 +++++++++++++++++++++++ modules/ts/include/opencv2/ts/ts_perf.hpp | 2 +- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/modules/ocl/perf/perf_match_template.cpp b/modules/ocl/perf/perf_match_template.cpp index 3ee038a840..561a978ae3 100644 --- a/modules/ocl/perf/perf_match_template.cpp +++ b/modules/ocl/perf/perf_match_template.cpp @@ -119,3 +119,60 @@ PERF_TEST_P(CV_TM_CCORR_NORMEDFixture, matchTemplate, OCL_TYPICAL_MAT_SIZES) else OCL_PERF_ELSE } + +CV_ENUM(MethodType, TM_SQDIFF, TM_SQDIFF_NORMED, TM_CCORR, TM_CCORR_NORMED, TM_CCOEFF, TM_CCOEFF_NORMED) + +typedef std::tr1::tuple ImgSize_TmplSize_Method_t; +typedef TestBaseWithParam ImgSize_TmplSize_Method; + +PERF_TEST_P(ImgSize_TmplSize_Method, MatchTemplate, + ::testing::Combine( + testing::Values(szSmall128, cv::Size(320, 240), + cv::Size(640, 480), cv::Size(800, 600), + cv::Size(1024, 768), cv::Size(1280, 1024)), + testing::Values(cv::Size(12, 12), cv::Size(28, 9), + cv::Size(8, 30), cv::Size(16, 16)), + MethodType::all() + ) + ) +{ + Size imgSz = get<0>(GetParam()); + Size tmplSz = get<1>(GetParam()); + int method = get<2>(GetParam()); + + Mat img(imgSz, CV_8UC1); + Mat tmpl(tmplSz, CV_8UC1); + Mat result(imgSz - tmplSz + Size(1, 1), CV_32F); + + bool isNormed = + method == TM_CCORR_NORMED || + method == TM_SQDIFF_NORMED || + method == TM_CCOEFF_NORMED; + + declare.in(img, tmpl, WARMUP_RNG).out(result).time(30); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclImg(img), oclTmpl(tmpl), oclResult(result.size(), CV_32F); + + OCL_TEST_CYCLE() matchTemplate(oclImg, oclTmpl, oclResult, method); + + oclResult.download(result); + } + else if (RUN_PLAIN_IMPL) + { + OCL_TEST_CYCLE() matchTemplate(img, tmpl, result, method); + } + else + OCL_PERF_ELSE + + double eps = isNormed ? 3e-2 + : 255 * 255 * tmpl.total() * 1e-4; + + if (method == TM_SQDIFF_NORMED) + SANITY_CHECK_NOTHING(); + else if (isNormed) + SANITY_CHECK(result, eps, ERROR_RELATIVE); + else + SANITY_CHECK(result, eps); +} diff --git a/modules/ts/include/opencv2/ts/ts_perf.hpp b/modules/ts/include/opencv2/ts/ts_perf.hpp index e32057de23..088479aa8f 100644 --- a/modules/ts/include/opencv2/ts/ts_perf.hpp +++ b/modules/ts/include/opencv2/ts/ts_perf.hpp @@ -209,7 +209,7 @@ private: #define SANITY_CHECK(array, ...) ::perf::Regression::add(this, #array, array , ## __VA_ARGS__) #define SANITY_CHECK_KEYPOINTS(array, ...) ::perf::Regression::addKeypoints(this, #array, array , ## __VA_ARGS__) #define SANITY_CHECK_MATCHES(array, ...) ::perf::Regression::addMatches(this, #array, array , ## __VA_ARGS__) -#define SANITY_CHECK_NOTHING() this->setVerified(); +#define SANITY_CHECK_NOTHING() this->setVerified() class CV_EXPORTS GpuPerf { From 46d128e073a0736f93301f98446f3e721a3e3fd1 Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Thu, 20 Feb 2014 22:10:22 +0200 Subject: [PATCH 151/293] add test (needs updated opencv_extra repo) --- modules/highgui/test/test_grfmt.cpp | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/modules/highgui/test/test_grfmt.cpp b/modules/highgui/test/test_grfmt.cpp index 86954e3e10..1fdf2c515a 100644 --- a/modules/highgui/test/test_grfmt.cpp +++ b/modules/highgui/test/test_grfmt.cpp @@ -419,4 +419,46 @@ TEST(Highgui_Tiff, decode_tile16384x16384) remove(file3.c_str()); remove(file4.c_str()); } + +class CV_GrfmtReadTifTiledWithNotFullTiles: public cvtest::BaseTest +{ +public: + void run(int) + { + try + { + /* see issue #3472 - dealing with tiled images where the tile size is + * not a multiple of image size. + * The tiled images were created with 'convert' from ImageMagick, + * using the command 'convert -define tiff:tile-geometry=128x128 -depth [8|16] + * Note that the conversion to 16 bits expands the range from 0-255 to 0-255*255, + * so the test converts back but rounding errors cause small differences. + */ + cv::Mat img = imread(string(ts->get_data_path()) + "readwrite/non_tiled.tif",-1); + if (img.empty()) ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + ASSERT_TRUE(img.channels() == 3); + cv::Mat tiled8 = imread(string(ts->get_data_path()) + "readwrite/tiled_8.tif", -1); + if (tiled8.empty()) ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + ASSERT_PRED_FORMAT2(cvtest::MatComparator(0, 0), img, tiled8); + + cv::Mat tiled16 = imread(string(ts->get_data_path()) + "readwrite/tiled_16.tif", -1); + if (tiled16.empty()) ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + ASSERT_TRUE(tiled16.elemSize() == 6); + tiled16.convertTo(tiled8, CV_8UC3, 1./256.); + ASSERT_PRED_FORMAT2(cvtest::MatComparator(2, 0), img, tiled8); + // What about 32, 64 bit? + } + catch(...) + { + ts->set_failed_test_info(cvtest::TS::FAIL_EXCEPTION); + } + ts->set_failed_test_info(cvtest::TS::OK); + } +}; + +TEST(Highgui_Tiff, decode_tile_remainder) +{ + CV_GrfmtReadTifTiledWithNotFullTiles test; test.safe_run(); +} + #endif From 31c8f2ec57138aa2708aa2b59f930d4102c0d7e8 Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Thu, 20 Feb 2014 22:12:22 +0200 Subject: [PATCH 152/293] fix code, it turns out only TIFFReadRGBATile needs buffer pointer mangling --- modules/highgui/src/grfmt_tiff.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/highgui/src/grfmt_tiff.cpp index 4b4d8011e8..614d001231 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/highgui/src/grfmt_tiff.cpp @@ -235,19 +235,21 @@ bool TiffDecoder::readData( Mat& img ) { case 8: { + uchar * bstart = buffer; if( !is_tiled ) ok = TIFFReadRGBAStrip( tif, y, (uint32*)buffer ); else + { ok = TIFFReadRGBATile( tif, x, y, (uint32*)buffer ); - + //Tiles fill the buffer from the bottom up + bstart += (tile_height0 - tile_height) * tile_width0 * 4; + } if( !ok ) { close(); return false; } - uchar * bstart = buffer + (tile_height0 - tile_height) * tile_width0 * 4; - for( i = 0; i < tile_height; i++ ) if( color ) icvCvt_BGRA2BGR_8u_C4C3R( bstart + i*tile_width0*4, 0, @@ -273,27 +275,25 @@ bool TiffDecoder::readData( Mat& img ) return false; } - uint16 * bstart = buffer16 + (tile_height0 - tile_height) * tile_width0 * ncn; - for( i = 0; i < tile_height; i++ ) { if( color ) { if( ncn == 1 ) { - icvCvt_Gray2BGR_16u_C1C3R(bstart + i*tile_width0*ncn, 0, + icvCvt_Gray2BGR_16u_C1C3R(buffer16 + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x*3, 0, cvSize(tile_width,1) ); } else if( ncn == 3 ) { - icvCvt_RGB2BGR_16u_C3R(bstart + i*tile_width0*ncn, 0, + icvCvt_RGB2BGR_16u_C3R(buffer16 + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x*3, 0, cvSize(tile_width,1) ); } else { - icvCvt_BGRA2BGR_16u_C4C3R(bstart + i*tile_width0*ncn, 0, + icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x*3, 0, cvSize(tile_width,1), 2 ); } @@ -303,12 +303,12 @@ bool TiffDecoder::readData( Mat& img ) if( ncn == 1 ) { memcpy((ushort*)(data + img.step*i)+x, - bstart + i*tile_width0*ncn, + buffer16 + i*tile_width0*ncn, tile_width*sizeof(buffer16[0])); } else { - icvCvt_BGRA2Gray_16u_CnC1R(bstart + i*tile_width0*ncn, 0, + icvCvt_BGRA2Gray_16u_CnC1R(buffer16 + i*tile_width0*ncn, 0, (ushort*)(data + img.step*i) + x, 0, cvSize(tile_width,1), ncn, 2 ); } @@ -331,21 +331,18 @@ bool TiffDecoder::readData( Mat& img ) return false; } - float * fstart = buffer32 + (tile_height0 - tile_height) * tile_width0 * sizeof(buffer32[0]); - double * dstart = buffer64 + (tile_height0 - tile_height) * tile_width0 * sizeof(buffer64[0]); - for( i = 0; i < tile_height; i++ ) { if(dst_bpp == 32) { memcpy((float*)(data + img.step*i)+x, - fstart + i*tile_width0*ncn, + buffer32 + i*tile_width0*ncn, tile_width*sizeof(buffer32[0])); } else { memcpy((double*)(data + img.step*i)+x, - dstart + i*tile_width0*ncn, + buffer64 + i*tile_width0*ncn, tile_width*sizeof(buffer64[0])); } } From dc50dc72548a9b7c970610ee95a7797e644040be Mon Sep 17 00:00:00 2001 From: Matti Picus Date: Fri, 21 Feb 2014 13:54:58 +0200 Subject: [PATCH 153/293] fix merge --- modules/highgui/test/test_grfmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highgui/test/test_grfmt.cpp b/modules/highgui/test/test_grfmt.cpp index bd9e79ad02..f451036ab9 100644 --- a/modules/highgui/test/test_grfmt.cpp +++ b/modules/highgui/test/test_grfmt.cpp @@ -420,7 +420,7 @@ TEST(Highgui_Tiff, decode_tile16384x16384) remove(file4.c_str()); } -class CV_GrfmtReadTifTiledWithNotFullTiles: public cvtest::BaseTest +TEST(Highgui_Tiff, write_read_16bit_big_little_endian) { // see issue #2601 "16-bit Grayscale TIFF Load Failures Due to Buffer Underflow and Endianness" From 1cf75c0cabff9699ab80d078629f9af0f71f1ec1 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Fri, 21 Feb 2014 16:08:49 +0400 Subject: [PATCH 154/293] Hardcode iOS's endianness, since the TestBigEndian module doesn't work there --- CMakeLists.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5fbb9f2b2..fb49497412 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,8 +448,15 @@ include(cmake/OpenCVModule.cmake) # ---------------------------------------------------------------------------- # Detect endianness of build platform # ---------------------------------------------------------------------------- -include(TestBigEndian) -test_big_endian(WORDS_BIGENDIAN) + +if(CMAKE_SYSTEM_NAME STREQUAL iOS) + # test_big_endian needs try_compile, which doesn't work for iOS + # http://public.kitware.com/Bug/view.php?id=12288 + set(WORDS_BIGENDIAN 0) +else() + include(TestBigEndian) + test_big_endian(WORDS_BIGENDIAN) +endif() # ---------------------------------------------------------------------------- # Detect 3rd-party libraries From ced8e7a8131c327cd4a00e72c1fb192fa2e3dd0f Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Mon, 24 Feb 2014 20:06:20 +1100 Subject: [PATCH 155/293] Typo --- modules/imgproc/doc/miscellaneous_transformations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/doc/miscellaneous_transformations.rst b/modules/imgproc/doc/miscellaneous_transformations.rst index 515d6128f8..e525f726da 100644 --- a/modules/imgproc/doc/miscellaneous_transformations.rst +++ b/modules/imgproc/doc/miscellaneous_transformations.rst @@ -642,7 +642,7 @@ The functions calculate one or more integral images for the source image as foll \texttt{tilted} (X,Y) = \sum _{y Date: Mon, 24 Feb 2014 23:26:01 +0400 Subject: [PATCH 156/293] disabled ABF tests --- modules/ocl/perf/perf_filters.cpp | 2 +- modules/ocl/test/test_filters.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ocl/perf/perf_filters.cpp b/modules/ocl/perf/perf_filters.cpp index 7e5389df6a..c625caa474 100644 --- a/modules/ocl/perf/perf_filters.cpp +++ b/modules/ocl/perf/perf_filters.cpp @@ -381,7 +381,7 @@ PERF_TEST_P(BilateralFixture, Bilateral, typedef Size_MatType adaptiveBilateralFixture; -PERF_TEST_P(adaptiveBilateralFixture, adaptiveBilateral, +PERF_TEST_P(adaptiveBilateralFixture, DISABLED_adaptiveBilateral, ::testing::Combine(::testing::Values(OCL_SIZE_1000), OCL_PERF_ENUM(CV_8UC1, CV_8UC3))) { const Size_MatType_t params = GetParam(); diff --git a/modules/ocl/test/test_filters.cpp b/modules/ocl/test/test_filters.cpp index b2caeaf6fc..d39c272048 100644 --- a/modules/ocl/test/test_filters.cpp +++ b/modules/ocl/test/test_filters.cpp @@ -334,7 +334,7 @@ OCL_TEST_P(Bilateral, Mat) typedef FilterTestBase AdaptiveBilateral; -OCL_TEST_P(AdaptiveBilateral, Mat) +OCL_TEST_P(AdaptiveBilateral, DISABLED_Mat) { const Size kernelSize(ksize, ksize); From dbb7aa7856282a09eec452ffa25205fcd5bd5659 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 25 Feb 2014 10:36:20 +0400 Subject: [PATCH 157/293] Highgui_Tiff.decode_tile16384x16384 disabled for Android. Last changes in test leads to SIGKILL on Android. SIGKILL is called by out of memory killer. --- modules/highgui/test/test_grfmt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/highgui/test/test_grfmt.cpp b/modules/highgui/test/test_grfmt.cpp index f451036ab9..aa1a84506b 100644 --- a/modules/highgui/test/test_grfmt.cpp +++ b/modules/highgui/test/test_grfmt.cpp @@ -392,7 +392,13 @@ TEST(Highgui_Jpeg, encode_empty) #define int64 int64_hack_ #include "tiff.h" +#ifdef ANDROID +// Test disabled as it uses a lot of memory. +// It is killed with SIGKILL by out of memory killer. +TEST(Highgui_Tiff, DISABLED_decode_tile16384x16384) +#else TEST(Highgui_Tiff, decode_tile16384x16384) +#endif { // see issue #2161 cv::Mat big(16384, 16384, CV_8UC1, cv::Scalar::all(0)); From c78142acb016a11385bb0a82c66bdc66fd7b7be8 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 25 Feb 2014 11:50:55 +0400 Subject: [PATCH 158/293] Bug #3391 org.opencv.android.NativeCameraView crashes after latest OpenCV Manager update fixed. The crash was cased by massive Mat objects leak in NativeCameraView class. --- .../src/java/android+NativeCameraView.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/java/generator/src/java/android+NativeCameraView.java b/modules/java/generator/src/java/android+NativeCameraView.java index 62d0775801..8035d04377 100644 --- a/modules/java/generator/src/java/android+NativeCameraView.java +++ b/modules/java/generator/src/java/android+NativeCameraView.java @@ -22,6 +22,7 @@ public class NativeCameraView extends CameraBridgeViewBase { private Thread mThread; protected VideoCapture mCamera; + protected NativeCameraFrame mFrame; public NativeCameraView(Context context, int cameraId) { super(context, cameraId); @@ -97,6 +98,8 @@ public class NativeCameraView extends CameraBridgeViewBase { if (mCamera.isOpened() == false) return false; + mFrame = new NativeCameraFrame(mCamera); + java.util.List sizes = mCamera.getSupportedPreviewSizes(); /* Select the size that fits surface considering maximum size allowed */ @@ -127,9 +130,8 @@ public class NativeCameraView extends CameraBridgeViewBase { private void releaseCamera() { synchronized (this) { - if (mCamera != null) { - mCamera.release(); - } + if (mFrame != null) mFrame.release(); + if (mCamera != null) mCamera.release(); } } @@ -153,6 +155,11 @@ public class NativeCameraView extends CameraBridgeViewBase { mRgba = new Mat(); } + public void release() { + if (mGray != null) mGray.release(); + if (mRgba != null) mRgba.release(); + } + private VideoCapture mCapture; private Mat mRgba; private Mat mGray; @@ -167,7 +174,7 @@ public class NativeCameraView extends CameraBridgeViewBase { break; } - deliverAndDrawFrame(new NativeCameraFrame(mCamera)); + deliverAndDrawFrame(mFrame); } while (!mStopThread); } From a228633d319447029de361446c09b027f93c2c98 Mon Sep 17 00:00:00 2001 From: Linquize Date: Tue, 25 Feb 2014 23:34:41 +0800 Subject: [PATCH 159/293] Add tgit.icon project config --- .tgitconfig | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .tgitconfig diff --git a/.tgitconfig b/.tgitconfig new file mode 100644 index 0000000000..5fa522d236 --- /dev/null +++ b/.tgitconfig @@ -0,0 +1,2 @@ +[tgit] + icon = doc/opencv.ico From e6f6707558f7237dd74d654aa4460c6befe80472 Mon Sep 17 00:00:00 2001 From: Alexander Karsakov Date: Wed, 26 Feb 2014 18:11:11 +0400 Subject: [PATCH 160/293] OCL: Fix for Haar classifier (thanks to Konstantin Rodyushkin). --- modules/ocl/src/haar.cpp | 4 ++-- modules/ocl/src/opencl/haarobjectdetect.cl | 4 ++-- modules/ocl/test/test_objdetect.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/ocl/src/haar.cpp b/modules/ocl/src/haar.cpp index e334ad913b..7da3d3d317 100644 --- a/modules/ocl/src/haar.cpp +++ b/modules/ocl/src/haar.cpp @@ -888,12 +888,12 @@ CvSeq *cv::ocl::OclCascadeClassifier::oclHaarDetectObjects( oclMat &gimg, CvMemS for(int y=0;y=(Height-cascade->orig_window_size.height)) + if(gy>=Height) continue; // no data to process for(int x=0;x=(Width-cascade->orig_window_size.width)) + if(gx>=Width) continue; // no data to process if(scaleinfo[z].factor<=2) diff --git a/modules/ocl/src/opencl/haarobjectdetect.cl b/modules/ocl/src/opencl/haarobjectdetect.cl index d6e5fb9ba3..8464a580bf 100644 --- a/modules/ocl/src/opencl/haarobjectdetect.cl +++ b/modules/ocl/src/opencl/haarobjectdetect.cl @@ -150,8 +150,8 @@ __kernel void gpuRunHaarClassifierCascadePacked( int index = i+lid; // index in shared local memory if(index Date: Fri, 28 Feb 2014 14:15:56 +0400 Subject: [PATCH 161/293] master-like performance tests --- modules/ocl/perf/perf_arithm.cpp | 291 +++++++++++------- modules/ocl/perf/perf_bgfg.cpp | 24 +- modules/ocl/perf/perf_blend.cpp | 6 +- modules/ocl/perf/perf_brute_force_matcher.cpp | 35 +-- modules/ocl/perf/perf_canny.cpp | 19 +- modules/ocl/perf/perf_color.cpp | 50 +-- modules/ocl/perf/perf_fft.cpp | 17 +- modules/ocl/perf/perf_filters.cpp | 231 ++++++++------ modules/ocl/perf/perf_gemm.cpp | 15 +- modules/ocl/perf/perf_gftt.cpp | 28 +- modules/ocl/perf/perf_haar.cpp | 38 ++- modules/ocl/perf/perf_hog.cpp | 4 +- modules/ocl/perf/perf_imgproc.cpp | 79 +++-- modules/ocl/perf/perf_imgwarp.cpp | 97 +++--- modules/ocl/perf/perf_match_template.cpp | 12 +- modules/ocl/perf/perf_matrix_operation.cpp | 30 +- modules/ocl/perf/perf_moments.cpp | 20 +- modules/ocl/perf/perf_norm.cpp | 27 +- modules/ocl/perf/perf_opticalflow.cpp | 60 +--- modules/ocl/perf/perf_precomp.hpp | 41 ++- modules/ocl/perf/perf_pyramid.cpp | 18 +- modules/ocl/perf/perf_split_merge.cpp | 81 +++-- 22 files changed, 667 insertions(+), 556 deletions(-) diff --git a/modules/ocl/perf/perf_arithm.cpp b/modules/ocl/perf/perf_arithm.cpp index 2699b44a78..7c194ae169 100644 --- a/modules/ocl/perf/perf_arithm.cpp +++ b/modules/ocl/perf/perf_arithm.cpp @@ -54,9 +54,8 @@ using std::tr1::tuple; typedef Size_MatType LUTFixture; -PERF_TEST_P(LUTFixture, LUT, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC3))) +OCL_PERF_TEST_P(LUTFixture, LUT, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) { // getting params const Size_MatType_t params = GetParam(); @@ -64,7 +63,7 @@ PERF_TEST_P(LUTFixture, LUT, const int type = get<1>(params); // creating src data - Mat src(srcSize, type), lut(1, 256, CV_8UC1); + Mat src(srcSize, CV_8UC1), lut(1, 256, type); int dstType = CV_MAKETYPE(lut.depth(), src.channels()); Mat dst(srcSize, dstType); @@ -93,16 +92,19 @@ PERF_TEST_P(LUTFixture, LUT, ///////////// Exp //////////////////////// -typedef TestBaseWithParam ExpFixture; +typedef Size_MatType ExpFixture; -PERF_TEST_P(ExpFixture, Exp, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(ExpFixture, Exp, ::testing::Combine( + OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { // getting params - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); const double eps = 1e-6; // creating src data - Mat src(srcSize, CV_32FC1), dst(srcSize, CV_32FC1); + Mat src(srcSize, type), dst(srcSize, type); declare.in(src).out(dst); randu(src, 5, 16); @@ -125,18 +127,21 @@ PERF_TEST_P(ExpFixture, Exp, OCL_TYPICAL_MAT_SIZES) SANITY_CHECK(dst, eps, ERROR_RELATIVE); } -///////////// LOG //////////////////////// +///////////// Log //////////////////////// -typedef TestBaseWithParam LogFixture; +typedef Size_MatType LogFixture; -PERF_TEST_P(LogFixture, Log, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(LogFixture, Log, ::testing::Combine( + OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { // getting params - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); const double eps = 1e-6; // creating src data - Mat src(srcSize, CV_32F), dst(srcSize, src.type()); + Mat src(srcSize, type), dst(srcSize, type); randu(src, 1, 10); declare.in(src).out(dst); @@ -166,9 +171,8 @@ PERF_TEST_P(LogFixture, Log, OCL_TYPICAL_MAT_SIZES) typedef Size_MatType AddFixture; -PERF_TEST_P(AddFixture, Add, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +OCL_PERF_TEST_P(AddFixture, Add, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { // getting params const Size_MatType_t params = GetParam(); @@ -202,12 +206,51 @@ PERF_TEST_P(AddFixture, Add, OCL_PERF_ELSE } +///////////// Subtract //////////////////////// + +typedef Size_MatType SubtractFixture; + +OCL_PERF_TEST_P(SubtractFixture, Subtract, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) +{ + // getting params + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + // creating src data + Mat src1(srcSize, type), src2(srcSize, type), dst(srcSize, type); + randu(src1, 0, 1); + randu(src2, 0, 1); + declare.in(src1, src2).out(dst); + + // select implementation + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc1(src1), oclSrc2(src2), oclDst(srcSize, type); + + OCL_TEST_CYCLE() cv::ocl::subtract(oclSrc1, oclSrc2, oclDst); + + oclDst.download(dst); + + SANITY_CHECK(dst); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() cv::subtract(src1, src2, dst); + + SANITY_CHECK(dst); + } + else + OCL_PERF_ELSE +} + + ///////////// Mul //////////////////////// typedef Size_MatType MulFixture; -PERF_TEST_P(MulFixture, Mul, ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(MulFixture, Multiply, ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { // getting params const Size_MatType_t params = GetParam(); @@ -245,9 +288,8 @@ PERF_TEST_P(MulFixture, Mul, ::testing::Combine(OCL_TYPICAL_MAT_SIZES, typedef Size_MatType DivFixture; -PERF_TEST_P(DivFixture, Div, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(DivFixture, Divide, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { // getting params const Size_MatType_t params = GetParam(); @@ -260,12 +302,6 @@ PERF_TEST_P(DivFixture, Div, randu(src1, 0, 256); randu(src2, 0, 256); - if ((srcSize == OCL_SIZE_4000 && type == CV_8UC1) || - (srcSize == OCL_SIZE_2000 && type == CV_8UC4)) - declare.time(4.2); - else if (srcSize == OCL_SIZE_4000 && type == CV_8UC4) - declare.time(16.6); - // select implementation if (RUN_OCL_IMPL) { @@ -275,13 +311,13 @@ PERF_TEST_P(DivFixture, Div, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::divide(src1, src2, dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); } else OCL_PERF_ELSE @@ -291,9 +327,8 @@ PERF_TEST_P(DivFixture, Div, typedef Size_MatType AbsDiffFixture; -PERF_TEST_P(AbsDiffFixture, Absdiff, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(AbsDiffFixture, Absdiff, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -326,15 +361,18 @@ PERF_TEST_P(AbsDiffFixture, Absdiff, ///////////// CartToPolar //////////////////////// -typedef TestBaseWithParam CartToPolarFixture; +typedef Size_MatType CartToPolarFixture; -PERF_TEST_P(CartToPolarFixture, CartToPolar, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(CartToPolarFixture, CartToPolar, ::testing::Combine( + OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); const double eps = 8e-3; - Mat src1(srcSize, CV_32FC1), src2(srcSize, CV_32FC1), - dst1(srcSize, CV_32FC1), dst2(srcSize, CV_32FC1); + Mat src1(srcSize, type), src2(srcSize, type), + dst1(srcSize, type), dst2(srcSize, type); declare.in(src1, src2).out(dst1, dst2); randu(src1, 0, 256); randu(src2, 0, 256); @@ -368,14 +406,17 @@ PERF_TEST_P(CartToPolarFixture, CartToPolar, OCL_TYPICAL_MAT_SIZES) ///////////// PolarToCart //////////////////////// -typedef TestBaseWithParam PolarToCartFixture; +typedef Size_MatType PolarToCartFixture; -PERF_TEST_P(PolarToCartFixture, PolarToCart, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(PolarToCartFixture, PolarToCart, ::testing::Combine( + OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); - Mat src1(srcSize, CV_32FC1), src2(srcSize, CV_32FC1), - dst1(srcSize, CV_32FC1), dst2(srcSize, CV_32FC1); + Mat src1(srcSize, type), src2(srcSize, type), + dst1(srcSize, type), dst2(srcSize, type); declare.in(src1, src2).out(dst1, dst2); randu(src1, 0, 256); randu(src2, 0, 256); @@ -409,14 +450,17 @@ PERF_TEST_P(PolarToCartFixture, PolarToCart, OCL_TYPICAL_MAT_SIZES) ///////////// Magnitude //////////////////////// -typedef TestBaseWithParam MagnitudeFixture; +typedef Size_MatType MagnitudeFixture; -PERF_TEST_P(MagnitudeFixture, Magnitude, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(MagnitudeFixture, Magnitude, ::testing::Combine( + OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); - Mat src1(srcSize, CV_32FC1), src2(srcSize, CV_32FC1), - dst(srcSize, CV_32FC1); + Mat src1(srcSize, type), src2(srcSize, type), + dst(srcSize, type); randu(src1, 0, 1); randu(src2, 0, 1); declare.in(src1, src2).out(dst); @@ -446,9 +490,8 @@ PERF_TEST_P(MagnitudeFixture, Magnitude, OCL_TYPICAL_MAT_SIZES) typedef Size_MatType TransposeFixture; -PERF_TEST_P(TransposeFixture, Transpose, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(TransposeFixture, Transpose, ::testing::Combine( + OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -479,15 +522,24 @@ PERF_TEST_P(TransposeFixture, Transpose, ///////////// Flip //////////////////////// -typedef Size_MatType FlipFixture; - -PERF_TEST_P(FlipFixture, Flip, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +enum { - const Size_MatType_t params = GetParam(); + FLIP_BOTH = 0, FLIP_ROWS, FLIP_COLS +}; + +CV_ENUM(FlipType, FLIP_BOTH, FLIP_ROWS, FLIP_COLS) + +typedef std::tr1::tuple FlipParams; +typedef TestBaseWithParam FlipFixture; + +OCL_PERF_TEST_P(FlipFixture, Flip, + ::testing::Combine(OCL_TEST_SIZES, + OCL_TEST_TYPES, FlipType::all())) +{ + const FlipParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params); + const int flipType = get<2>(params); Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); @@ -496,7 +548,7 @@ PERF_TEST_P(FlipFixture, Flip, { ocl::oclMat oclSrc(src), oclDst(srcSize, type); - OCL_TEST_CYCLE() cv::ocl::flip(oclSrc, oclDst, 0); + OCL_TEST_CYCLE() cv::ocl::flip(oclSrc, oclDst, flipType - 1); oclDst.download(dst); @@ -504,7 +556,7 @@ PERF_TEST_P(FlipFixture, Flip, } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() cv::flip(src, dst, 0); + TEST_CYCLE() cv::flip(src, dst, flipType - 1); SANITY_CHECK(dst); } @@ -512,11 +564,11 @@ PERF_TEST_P(FlipFixture, Flip, OCL_PERF_ELSE } -///////////// minMax //////////////////////// +///////////// MinMax //////////////////////// -typedef Size_MatType minMaxFixture; +typedef Size_MatType MinMaxFixture; -PERF_TEST_P(minMaxFixture, minMax, +PERF_TEST_P(MinMaxFixture, MinMax, ::testing::Combine(OCL_TYPICAL_MAT_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) { @@ -554,13 +606,12 @@ PERF_TEST_P(minMaxFixture, minMax, OCL_PERF_ELSE } -///////////// minMaxLoc //////////////////////// +///////////// MinMaxLoc //////////////////////// -typedef Size_MatType minMaxLocFixture; +typedef Size_MatType MinMaxLocFixture; -PERF_TEST_P(minMaxLocFixture, minMaxLoc, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +OCL_PERF_TEST_P(MinMaxLocFixture, MinMaxLoc, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -599,9 +650,9 @@ PERF_TEST_P(minMaxLocFixture, minMaxLoc, typedef Size_MatType SumFixture; -PERF_TEST_P(SumFixture, Sum, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32SC1))) +OCL_PERF_TEST_P(SumFixture, Sum, + ::testing::Combine(OCL_TEST_SIZES, + OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -618,13 +669,13 @@ PERF_TEST_P(SumFixture, Sum, OCL_TEST_CYCLE() result = cv::ocl::sum(oclSrc); - SANITY_CHECK(result); + SANITY_CHECK(result, 1e-6, ERROR_RELATIVE); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() result = cv::sum(src); - SANITY_CHECK(result); + SANITY_CHECK(result, 1e-6, ERROR_RELATIVE); } else OCL_PERF_ELSE @@ -632,10 +683,10 @@ PERF_TEST_P(SumFixture, Sum, ///////////// countNonZero //////////////////////// -typedef Size_MatType countNonZeroFixture; +typedef Size_MatType CountNonZeroFixture; -PERF_TEST_P(countNonZeroFixture, countNonZero, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, +OCL_PERF_TEST_P(CountNonZeroFixture, CountNonZero, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) { const Size_MatType_t params = GetParam(); @@ -667,14 +718,16 @@ PERF_TEST_P(countNonZeroFixture, countNonZero, ///////////// Phase //////////////////////// -typedef TestBaseWithParam PhaseFixture; +typedef Size_MatType PhaseFixture; -PERF_TEST_P(PhaseFixture, Phase, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(PhaseFixture, Phase, ::testing::Combine( + OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); - Mat src1(srcSize, CV_32FC1), src2(srcSize, CV_32FC1), - dst(srcSize, CV_32FC1); + Mat src1(srcSize, type), src2(srcSize, type), dst(srcSize, type); declare.in(src1, src2).out(dst); randu(src1, 0, 256); randu(src2, 0, 256); @@ -704,9 +757,8 @@ PERF_TEST_P(PhaseFixture, Phase, OCL_TYPICAL_MAT_SIZES) typedef Size_MatType BitwiseAndFixture; -PERF_TEST_P(BitwiseAndFixture, bitwise_and, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32SC1))) +OCL_PERF_TEST_P(BitwiseAndFixture, Bitwise_and, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -741,9 +793,8 @@ PERF_TEST_P(BitwiseAndFixture, bitwise_and, typedef Size_MatType BitwiseXorFixture; -PERF_TEST_P(BitwiseXorFixture, bitwise_xor, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32SC1))) +OCL_PERF_TEST_P(BitwiseXorFixture, Bitwise_xor, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -778,9 +829,8 @@ PERF_TEST_P(BitwiseXorFixture, bitwise_xor, typedef Size_MatType BitwiseOrFixture; -PERF_TEST_P(BitwiseOrFixture, bitwise_or, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32SC1))) +OCL_PERF_TEST_P(BitwiseOrFixture, Bitwise_or, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -798,26 +848,25 @@ PERF_TEST_P(BitwiseOrFixture, bitwise_or, OCL_TEST_CYCLE() cv::ocl::bitwise_or(oclSrc1, oclSrc2, oclDst); oclDst.download(dst); - - SANITY_CHECK(dst); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::bitwise_or(src1, src2, dst); - - SANITY_CHECK(dst); } else OCL_PERF_ELSE + + if (CV_MAT_DEPTH(type) >= CV_32F) + cv::patchNaNs(dst, 17); + SANITY_CHECK(dst); } ///////////// bitwise_not//////////////////////// typedef Size_MatType BitwiseNotFixture; -PERF_TEST_P(BitwiseAndFixture, bitwise_not, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32SC1))) +OCL_PERF_TEST_P(BitwiseNotFixture, Bitwise_not, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -848,15 +897,19 @@ PERF_TEST_P(BitwiseAndFixture, bitwise_not, ///////////// compare//////////////////////// -typedef Size_MatType CompareFixture; +CV_ENUM(CmpCode, CMP_LT, CMP_LE, CMP_EQ, CMP_NE, CMP_GE, CMP_GT) -PERF_TEST_P(CompareFixture, compare, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +typedef std::tr1::tuple CompareParams; +typedef TestBaseWithParam CompareFixture; + +OCL_PERF_TEST_P(CompareFixture, Compare, + ::testing::Combine(OCL_TEST_SIZES, + OCL_PERF_ENUM(CV_8UC1, CV_32FC1), CmpCode::all())) { - const Size_MatType_t params = GetParam(); + const CompareParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params); + const int cmpCode = get<2>(params); Mat src1(srcSize, type), src2(srcSize, type), dst(srcSize, CV_8UC1); declare.in(src1, src2, WARMUP_RNG).out(dst); @@ -865,7 +918,7 @@ PERF_TEST_P(CompareFixture, compare, { ocl::oclMat oclSrc1(src1), oclSrc2(src2), oclDst(srcSize, CV_8UC1); - OCL_TEST_CYCLE() cv::ocl::compare(oclSrc1, oclSrc2, oclDst, CMP_EQ); + OCL_TEST_CYCLE() cv::ocl::compare(oclSrc1, oclSrc2, oclDst, cmpCode); oclDst.download(dst); @@ -873,7 +926,7 @@ PERF_TEST_P(CompareFixture, compare, } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() cv::compare(src1, src2, dst, CMP_EQ); + TEST_CYCLE() cv::compare(src1, src2, dst, cmpCode); SANITY_CHECK(dst); } @@ -883,14 +936,17 @@ PERF_TEST_P(CompareFixture, compare, ///////////// pow //////////////////////// -typedef TestBaseWithParam PowFixture; +typedef Size_MatType PowFixture; -PERF_TEST_P(PowFixture, pow, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(PowFixture, Pow, ::testing::Combine( + OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); const double eps = 1e-6; - Mat src(srcSize, CV_32F), dst(srcSize, CV_32F); + Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); if (RUN_OCL_IMPL) @@ -915,9 +971,8 @@ PERF_TEST_P(PowFixture, pow, OCL_TYPICAL_MAT_SIZES) typedef Size_MatType AddWeightedFixture; -PERF_TEST_P(AddWeightedFixture, AddWeighted, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +OCL_PERF_TEST_P(AddWeightedFixture, AddWeighted, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -951,9 +1006,8 @@ PERF_TEST_P(AddWeightedFixture, AddWeighted, typedef Size_MatType MinFixture; -PERF_TEST_P(MinFixture, Min, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +OCL_PERF_TEST_P(MinFixture, Min, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -986,9 +1040,8 @@ PERF_TEST_P(MinFixture, Min, typedef Size_MatType MaxFixture; -PERF_TEST_P(MaxFixture, Max, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +OCL_PERF_TEST_P(MaxFixture, Max, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -1017,7 +1070,7 @@ PERF_TEST_P(MaxFixture, Max, OCL_PERF_ELSE } -///////////// Max //////////////////////// +///////////// Abs //////////////////////// typedef Size_MatType AbsFixture; @@ -1056,9 +1109,9 @@ PERF_TEST_P(AbsFixture, Abs, typedef Size_MatType RepeatFixture; -PERF_TEST_P(RepeatFixture, Repeat, - ::testing::Combine(::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000), - OCL_PERF_ENUM(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4))) +OCL_PERF_TEST_P(RepeatFixture, Repeat, + ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), + OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); diff --git a/modules/ocl/perf/perf_bgfg.cpp b/modules/ocl/perf/perf_bgfg.cpp index b361a9232a..c2a1941e9b 100644 --- a/modules/ocl/perf/perf_bgfg.cpp +++ b/modules/ocl/perf/perf_bgfg.cpp @@ -165,11 +165,11 @@ PERF_TEST_P(VideoMOGFixture, MOG, ///////////// MOG2 //////////////////////// typedef tuple VideoMOG2ParamType; -typedef TestBaseWithParam VideoMOG2Fixture; +typedef TestBaseWithParam MOG2_Apply; -PERF_TEST_P(VideoMOG2Fixture, DISABLED_MOG2, // TODO Disabled: random hungs on buildslave - ::testing::Combine(::testing::Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), - ::testing::Values(1, 3))) +OCL_PERF_TEST_P(MOG2_Apply, Mog2, + testing::Combine(testing::Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), + testing::Values(1, 3))) { VideoMOG2ParamType params = GetParam(); @@ -195,9 +195,7 @@ PERF_TEST_P(VideoMOG2Fixture, DISABLED_MOG2, // TODO Disabled: random hungs on b foreground.release(); for (int i = 0; i < nFrame; i++) - { mog2(frame_buffer[i], foreground); - } } SANITY_CHECK(foreground); } @@ -210,9 +208,7 @@ PERF_TEST_P(VideoMOG2Fixture, DISABLED_MOG2, // TODO Disabled: random hungs on b cv::ocl::MOG2 d_mog2; foreground_d.release(); for (int i = 0; i < nFrame; i++) - { d_mog2(frame_buffer_ocl[i], foreground_d); - } } foreground_d.download(foreground); SANITY_CHECK(foreground); @@ -223,11 +219,11 @@ PERF_TEST_P(VideoMOG2Fixture, DISABLED_MOG2, // TODO Disabled: random hungs on b ///////////// MOG2_GetBackgroundImage ////////////////// -typedef TestBaseWithParam Video_MOG2GetBackgroundImage; +typedef TestBaseWithParam MOG2_GetBackgroundImage; -PERF_TEST_P(Video_MOG2GetBackgroundImage, MOG2, - ::testing::Combine(::testing::Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), - ::testing::Values(3))) +OCL_PERF_TEST_P(MOG2_GetBackgroundImage, Mog2, + testing::Combine(testing::Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), + testing::Values(3))) { VideoMOG2ParamType params = GetParam(); @@ -248,7 +244,7 @@ PERF_TEST_P(Video_MOG2GetBackgroundImage, MOG2, cv::ocl::oclMat foreground_d; cv::ocl::oclMat background_d; - if(RUN_PLAIN_IMPL) + if (RUN_PLAIN_IMPL) { TEST_CYCLE() { @@ -264,7 +260,7 @@ PERF_TEST_P(Video_MOG2GetBackgroundImage, MOG2, } SANITY_CHECK(background); } - else if(RUN_OCL_IMPL) + else if (RUN_OCL_IMPL) { prepareData(frame_buffer, frame_buffer_ocl); CV_Assert((int)(frame_buffer_ocl.size()) == nFrame); diff --git a/modules/ocl/perf/perf_blend.cpp b/modules/ocl/perf/perf_blend.cpp index 6f611bbc34..5471dee2e2 100644 --- a/modules/ocl/perf/perf_blend.cpp +++ b/modules/ocl/perf/perf_blend.cpp @@ -88,10 +88,10 @@ typedef void (*blendFunction)(const Mat &img1, const Mat &img2, const Mat &weights1, const Mat &weights2, Mat &result_gold); -typedef Size_MatType blendLinearFixture; +typedef Size_MatType BlendLinearFixture; -PERF_TEST_P(blendLinearFixture, blendLinear, ::testing::Combine( - OCL_TYPICAL_MAT_SIZES, testing::Values(CV_8UC1, CV_8UC3, CV_32FC1))) +OCL_PERF_TEST_P(BlendLinearFixture, BlendLinear, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); diff --git a/modules/ocl/perf/perf_brute_force_matcher.cpp b/modules/ocl/perf/perf_brute_force_matcher.cpp index d124428e9d..54e829f4bc 100644 --- a/modules/ocl/perf/perf_brute_force_matcher.cpp +++ b/modules/ocl/perf/perf_brute_force_matcher.cpp @@ -47,20 +47,17 @@ using namespace perf; -#define OCL_BFMATCHER_TYPICAL_MAT_SIZES ::testing::Values(cv::Size(128, 500), cv::Size(128, 1000), cv::Size(128, 2000)) - //////////////////// BruteForceMatch ///////////////// typedef TestBaseWithParam BruteForceMatcherFixture; -PERF_TEST_P(BruteForceMatcherFixture, match, - OCL_BFMATCHER_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(BruteForceMatcherFixture, Match, OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3)) { const Size srcSize = GetParam(); vector matches; - Mat query(srcSize, CV_32F), train(srcSize, CV_32F); - declare.in(query, train).time(srcSize.height == 2000 ? 9 : 4 ); + Mat query(srcSize, CV_32FC1), train(srcSize, CV_32FC1); + declare.in(query, train); randu(query, 0.0f, 1.0f); randu(train, 0.0f, 1.0f); @@ -75,12 +72,9 @@ PERF_TEST_P(BruteForceMatcherFixture, match, { ocl::BruteForceMatcher_OCL_base oclMatcher(ocl::BruteForceMatcher_OCL_base::L2Dist); ocl::oclMat oclQuery(query), oclTrain(train); - ocl::oclMat oclTrainIdx, oclDistance; OCL_TEST_CYCLE() - oclMatcher.matchSingle(oclQuery, oclTrain, oclTrainIdx, oclDistance); - - oclMatcher.matchDownload(oclTrainIdx, oclDistance, matches); + oclMatcher.match(oclQuery, oclTrain, matches); SANITY_CHECK_MATCHES(matches, 1e-5); } @@ -88,8 +82,7 @@ PERF_TEST_P(BruteForceMatcherFixture, match, OCL_PERF_ELSE } -PERF_TEST_P(BruteForceMatcherFixture, knnMatch, - OCL_BFMATCHER_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(BruteForceMatcherFixture, KnnMatch, OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3)) { const Size srcSize = GetParam(); @@ -99,8 +92,6 @@ PERF_TEST_P(BruteForceMatcherFixture, knnMatch, randu(train, 0.0f, 1.0f); declare.in(query, train); - if (srcSize.height == 2000) - declare.time(9); if (RUN_PLAIN_IMPL) { @@ -115,10 +106,10 @@ PERF_TEST_P(BruteForceMatcherFixture, knnMatch, { ocl::BruteForceMatcher_OCL_base oclMatcher(ocl::BruteForceMatcher_OCL_base::L2Dist); ocl::oclMat oclQuery(query), oclTrain(train); - ocl::oclMat oclTrainIdx, oclDistance, oclAllDist; + ocl::oclMat oclTrainIdx, oclDistance; OCL_TEST_CYCLE() - oclMatcher.knnMatchSingle(oclQuery, oclTrain, oclTrainIdx, oclDistance, oclAllDist, 2); + oclMatcher.knnMatch(oclQuery, oclTrain, matches, 2); oclMatcher.knnMatchDownload(oclTrainIdx, oclDistance, matches); @@ -130,22 +121,18 @@ PERF_TEST_P(BruteForceMatcherFixture, knnMatch, OCL_PERF_ELSE } -PERF_TEST_P(BruteForceMatcherFixture, radiusMatch, - OCL_BFMATCHER_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(BruteForceMatcherFixture, RadiusMatch, OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3)) { const Size srcSize = GetParam(); const float max_distance = 2.0f; vector > matches(2); - Mat query(srcSize, CV_32F), train(srcSize, CV_32F); + Mat query(srcSize, CV_32FC1), train(srcSize, CV_32FC1); declare.in(query, train); randu(query, 0.0f, 1.0f); randu(train, 0.0f, 1.0f); - if (srcSize.height == 2000) - declare.time(9.15); - if (RUN_PLAIN_IMPL) { cv::BFMatcher matcher(NORM_L2); @@ -162,7 +149,7 @@ PERF_TEST_P(BruteForceMatcherFixture, radiusMatch, ocl::oclMat oclTrainIdx, oclDistance, oclNMatches; OCL_TEST_CYCLE() - oclMatcher.radiusMatchSingle(oclQuery, oclTrain, oclTrainIdx, oclDistance, oclNMatches, max_distance); + oclMatcher.radiusMatch(oclQuery, oclTrain, matches, max_distance); oclMatcher.radiusMatchDownload(oclTrainIdx, oclDistance, oclNMatches, matches); @@ -173,5 +160,3 @@ PERF_TEST_P(BruteForceMatcherFixture, radiusMatch, else OCL_PERF_ELSE } - -#undef OCL_BFMATCHER_TYPICAL_MAT_SIZES diff --git a/modules/ocl/perf/perf_canny.cpp b/modules/ocl/perf/perf_canny.cpp index 33723daa32..96c4441513 100644 --- a/modules/ocl/perf/perf_canny.cpp +++ b/modules/ocl/perf/perf_canny.cpp @@ -46,11 +46,21 @@ #include "perf_precomp.hpp" using namespace perf; +using std::tr1::tuple; +using std::tr1::get; ///////////// Canny //////////////////////// -PERF_TEST(CannyFixture, Canny) +typedef tuple CannyParams; +typedef TestBaseWithParam CannyFixture; + +OCL_PERF_TEST_P(CannyFixture, Canny, + ::testing::Combine(OCL_PERF_ENUM(3, 5), testing::Bool())) { + const CannyParams params = GetParam(); + int apertureSize = get<0>(params); + bool L2Grad = get<1>(params); + Mat img = imread(getDataPath("gpu/stereobm/aloe-L.png"), cv::IMREAD_GRAYSCALE), edges(img.size(), CV_8UC1); ASSERT_TRUE(!img.empty()) << "can't open aloe-L.png"; @@ -61,16 +71,15 @@ PERF_TEST(CannyFixture, Canny) { ocl::oclMat oclImg(img), oclEdges(img.size(), CV_8UC1); - OCL_TEST_CYCLE() ocl::Canny(oclImg, oclEdges, 50.0, 100.0); + OCL_TEST_CYCLE() ocl::Canny(oclImg, oclEdges, 50.0, 100.0, apertureSize, L2Grad); oclEdges.download(edges); } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() Canny(img, edges, 50.0, 100.0); + TEST_CYCLE() Canny(img, edges, 50.0, 100.0, apertureSize, L2Grad); } else OCL_PERF_ELSE - int value = 0; - SANITY_CHECK(value); + SANITY_CHECK_NOTHING(); } diff --git a/modules/ocl/perf/perf_color.cpp b/modules/ocl/perf/perf_color.cpp index 8433315189..0797a87fdf 100644 --- a/modules/ocl/perf/perf_color.cpp +++ b/modules/ocl/perf/perf_color.cpp @@ -52,36 +52,36 @@ using std::tr1::make_tuple; ///////////// cvtColor//////////////////////// -CV_ENUM(ConversionTypes, CV_RGB2GRAY, CV_RGB2BGR, CV_RGB2YUV, CV_YUV2RGB, CV_RGB2YCrCb, - CV_YCrCb2RGB, CV_RGB2XYZ, CV_XYZ2RGB, CV_RGB2HSV, CV_HSV2RGB, CV_RGB2HLS, - CV_HLS2RGB, CV_BGR5652BGR, CV_BGR2BGR565, CV_RGBA2mRGBA, CV_mRGBA2RGBA, CV_YUV2RGB_NV12) +CV_ENUM(ConversionTypes, COLOR_RGB2GRAY, COLOR_RGB2BGR, COLOR_RGB2YUV, COLOR_YUV2RGB, COLOR_RGB2YCrCb, + COLOR_YCrCb2RGB, COLOR_RGB2XYZ, COLOR_XYZ2RGB, COLOR_RGB2HSV, COLOR_HSV2RGB, COLOR_RGB2HLS, + COLOR_HLS2RGB, COLOR_BGR5652BGR, COLOR_BGR2BGR565, COLOR_RGBA2mRGBA, COLOR_mRGBA2RGBA, COLOR_YUV2RGB_NV12) -typedef tuple > cvtColorParams; -typedef TestBaseWithParam cvtColorFixture; +typedef tuple > CvtColorParams; +typedef TestBaseWithParam CvtColorFixture; -PERF_TEST_P(cvtColorFixture, cvtColor, testing::Combine( - testing::Values(Size(1000, 1002), Size(2000, 2004), Size(4000, 4008)), +OCL_PERF_TEST_P(CvtColorFixture, CvtColor, testing::Combine( + OCL_TEST_SIZES, testing::Values( - make_tuple(ConversionTypes(CV_RGB2GRAY), 3, 1), - make_tuple(ConversionTypes(CV_RGB2BGR), 3, 3), - make_tuple(ConversionTypes(CV_RGB2YUV), 3, 3), - make_tuple(ConversionTypes(CV_YUV2RGB), 3, 3), - make_tuple(ConversionTypes(CV_RGB2YCrCb), 3, 3), - make_tuple(ConversionTypes(CV_YCrCb2RGB), 3, 3), - make_tuple(ConversionTypes(CV_RGB2XYZ), 3, 3), - make_tuple(ConversionTypes(CV_XYZ2RGB), 3, 3), - make_tuple(ConversionTypes(CV_RGB2HSV), 3, 3), - make_tuple(ConversionTypes(CV_HSV2RGB), 3, 3), - make_tuple(ConversionTypes(CV_RGB2HLS), 3, 3), - make_tuple(ConversionTypes(CV_HLS2RGB), 3, 3), - make_tuple(ConversionTypes(CV_BGR5652BGR), 2, 3), - make_tuple(ConversionTypes(CV_BGR2BGR565), 3, 2), - make_tuple(ConversionTypes(CV_RGBA2mRGBA), 4, 4), - make_tuple(ConversionTypes(CV_mRGBA2RGBA), 4, 4), - make_tuple(ConversionTypes(CV_YUV2RGB_NV12), 1, 3) + make_tuple(ConversionTypes(COLOR_RGB2GRAY), 3, 1), + make_tuple(ConversionTypes(COLOR_RGB2BGR), 3, 3), + make_tuple(ConversionTypes(COLOR_RGB2YUV), 3, 3), + make_tuple(ConversionTypes(COLOR_YUV2RGB), 3, 3), + make_tuple(ConversionTypes(COLOR_RGB2YCrCb), 3, 3), + make_tuple(ConversionTypes(COLOR_YCrCb2RGB), 3, 3), + make_tuple(ConversionTypes(COLOR_RGB2XYZ), 3, 3), + make_tuple(ConversionTypes(COLOR_XYZ2RGB), 3, 3), + make_tuple(ConversionTypes(COLOR_RGB2HSV), 3, 3), + make_tuple(ConversionTypes(COLOR_HSV2RGB), 3, 3), + make_tuple(ConversionTypes(COLOR_RGB2HLS), 3, 3), + make_tuple(ConversionTypes(COLOR_HLS2RGB), 3, 3), + make_tuple(ConversionTypes(COLOR_BGR5652BGR), 2, 3), + make_tuple(ConversionTypes(COLOR_BGR2BGR565), 3, 2), + make_tuple(ConversionTypes(COLOR_RGBA2mRGBA), 4, 4), + make_tuple(ConversionTypes(COLOR_mRGBA2RGBA), 4, 4), + make_tuple(ConversionTypes(COLOR_YUV2RGB_NV12), 1, 3) ))) { - cvtColorParams params = GetParam(); + CvtColorParams params = GetParam(); const Size srcSize = get<0>(params); const tuple conversionParams = get<1>(params); const int code = get<0>(conversionParams), scn = get<1>(conversionParams), diff --git a/modules/ocl/perf/perf_fft.cpp b/modules/ocl/perf/perf_fft.cpp index 49da659361..29e042ccde 100644 --- a/modules/ocl/perf/perf_fft.cpp +++ b/modules/ocl/perf/perf_fft.cpp @@ -47,6 +47,8 @@ #include "perf_precomp.hpp" using namespace perf; +using std::tr1::tuple; +using std::tr1::get; ///////////// dft //////////////////////// @@ -54,9 +56,16 @@ typedef TestBaseWithParam dftFixture; #ifdef HAVE_CLAMDFFT -PERF_TEST_P(dftFixture, dft, OCL_TYPICAL_MAT_SIZES) +typedef tuple DftParams; +typedef TestBaseWithParam DftFixture; + +OCL_PERF_TEST_P(DftFixture, Dft, ::testing::Combine(testing::Values(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), + ::testing::Values((int)DFT_ROWS, (int)DFT_SCALE, (int)DFT_INVERSE, + (int)DFT_INVERSE | DFT_SCALE, (int)DFT_ROWS | DFT_INVERSE))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int flags = get<1>(params); Mat src(srcSize, CV_32FC2), dst; randu(src, 0.0f, 1.0f); @@ -69,7 +78,7 @@ PERF_TEST_P(dftFixture, dft, OCL_TYPICAL_MAT_SIZES) { ocl::oclMat oclSrc(src), oclDst; - OCL_TEST_CYCLE() cv::ocl::dft(oclSrc, oclDst); + OCL_TEST_CYCLE() cv::ocl::dft(oclSrc, oclDst, Size(), flags | DFT_COMPLEX_OUTPUT); oclDst.download(dst); @@ -77,7 +86,7 @@ PERF_TEST_P(dftFixture, dft, OCL_TYPICAL_MAT_SIZES) } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() cv::dft(src, dst); + TEST_CYCLE() cv::dft(src, dst, flags | DFT_COMPLEX_OUTPUT); SANITY_CHECK(dst); } diff --git a/modules/ocl/perf/perf_filters.cpp b/modules/ocl/perf/perf_filters.cpp index c625caa474..70e51b4662 100644 --- a/modules/ocl/perf/perf_filters.cpp +++ b/modules/ocl/perf/perf_filters.cpp @@ -49,31 +49,30 @@ using namespace perf; using std::tr1::get; using std::tr1::tuple; +typedef tuple FilterParams; +typedef TestBaseWithParam FilterFixture; + ///////////// Blur//////////////////////// -typedef Size_MatType BlurFixture; +typedef FilterFixture BlurFixture; -PERF_TEST_P(BlurFixture, Blur, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(BlurFixture, Blur, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, OCL_PERF_ENUM(3, 5))) { - const Size_MatType_t params = GetParam(); - const Size srcSize = get<0>(params), ksize(3, 3); - const int type = get<1>(params), bordertype = BORDER_CONSTANT; + const FilterParams params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params), ksize = get<2>(params), bordertype = BORDER_CONSTANT; checkDeviceMaxMemoryAllocSize(srcSize, type); Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); - if (srcSize == OCL_SIZE_4000 && type == CV_8UC4) - declare.time(5); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst(srcSize, type); - OCL_TEST_CYCLE() cv::ocl::blur(oclSrc, oclDst, ksize, Point(-1, -1), bordertype); + OCL_TEST_CYCLE() cv::ocl::blur(oclSrc, oclDst, Size(ksize, ksize), Point(-1, -1), bordertype); oclDst.download(dst); @@ -81,7 +80,7 @@ PERF_TEST_P(BlurFixture, Blur, } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() cv::blur(src, dst, ksize, Point(-1, -1), bordertype); + TEST_CYCLE() cv::blur(src, dst, Size(ksize, ksize), Point(-1, -1), bordertype); SANITY_CHECK(dst, 1 + DBL_EPSILON); } @@ -91,24 +90,20 @@ PERF_TEST_P(BlurFixture, Blur, ///////////// Laplacian//////////////////////// -typedef Size_MatType LaplacianFixture; +typedef FilterFixture LaplacianFixture; -PERF_TEST_P(LaplacianFixture, Laplacian, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(LaplacianFixture, Laplacian, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, OCL_PERF_ENUM(1, 3))) { - const Size_MatType_t params = GetParam(); + const FilterParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params), ksize = 3; + const int type = get<1>(params), ksize = get<2>(params); checkDeviceMaxMemoryAllocSize(srcSize, type); Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); - if (srcSize == OCL_SIZE_4000 && type == CV_8UC4) - declare.time(6); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst(srcSize, type); @@ -117,13 +112,13 @@ PERF_TEST_P(LaplacianFixture, Laplacian, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-3); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::Laplacian(src, dst, -1, ksize, 1); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-3); } else OCL_PERF_ELSE @@ -131,15 +126,14 @@ PERF_TEST_P(LaplacianFixture, Laplacian, ///////////// Erode //////////////////// -typedef Size_MatType ErodeFixture; +typedef FilterFixture ErodeFixture; -PERF_TEST_P(ErodeFixture, Erode, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4))) +OCL_PERF_TEST_P(ErodeFixture, Erode, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, OCL_PERF_ENUM(3, 5))) { - const Size_MatType_t params = GetParam(); + const FilterParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params), ksize = 3; + const int type = get<1>(params), ksize = get<2>(params); const Mat ker = getStructuringElement(MORPH_RECT, Size(ksize, ksize)); checkDeviceMaxMemoryAllocSize(srcSize, type); @@ -147,9 +141,6 @@ PERF_TEST_P(ErodeFixture, Erode, Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst).in(ker); - if (srcSize == OCL_SIZE_4000 && type == CV_8UC4) - declare.time(5); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst(srcSize, type), oclKer(ker); @@ -170,13 +161,89 @@ PERF_TEST_P(ErodeFixture, Erode, OCL_PERF_ELSE } +///////////// Dilate //////////////////// + +typedef FilterFixture DilateFixture; + +OCL_PERF_TEST_P(DilateFixture, Dilate, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, OCL_PERF_ENUM(3, 5))) +{ + const FilterParams params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params), ksize = get<2>(params); + const Mat ker = getStructuringElement(MORPH_RECT, Size(ksize, ksize)); + + checkDeviceMaxMemoryAllocSize(srcSize, type); + + Mat src(srcSize, type), dst(srcSize, type); + declare.in(src, WARMUP_RNG).out(dst).in(ker); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src), oclDst(srcSize, type), oclKer(ker); + + OCL_TEST_CYCLE() cv::ocl::dilate(oclSrc, oclDst, oclKer); + + oclDst.download(dst); + + SANITY_CHECK(dst); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() cv::dilate(src, dst, ker); + + SANITY_CHECK(dst); + } + else + OCL_PERF_ELSE +} + +///////////// MorphologyEx //////////////////// + +CV_ENUM(MorphOp, MORPH_OPEN, MORPH_CLOSE, MORPH_GRADIENT, MORPH_TOPHAT, MORPH_BLACKHAT) + +typedef tuple MorphologyExParams; +typedef TestBaseWithParam MorphologyExFixture; + +OCL_PERF_TEST_P(MorphologyExFixture, MorphologyEx, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, MorphOp::all(), OCL_PERF_ENUM(3, 5))) +{ + const MorphologyExParams params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params), op = get<2>(params), ksize = get<3>(params); + const Mat ker = getStructuringElement(MORPH_RECT, Size(ksize, ksize)); + + checkDeviceMaxMemoryAllocSize(srcSize, type); + + Mat src(srcSize, type), dst(srcSize, type); + declare.in(src, WARMUP_RNG).out(dst).in(ker); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src), oclDst(srcSize, type), oclKer(ker); + + OCL_TEST_CYCLE() cv::ocl::morphologyEx(oclSrc, oclDst, op, oclKer); + + oclDst.download(dst); + + SANITY_CHECK(dst); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() cv::morphologyEx(src, dst, op, ker); + + SANITY_CHECK(dst); + } + else + OCL_PERF_ELSE +} + ///////////// Sobel //////////////////////// typedef Size_MatType SobelFixture; -PERF_TEST_P(SobelFixture, Sobel, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(SobelFixture, Sobel, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -187,12 +254,6 @@ PERF_TEST_P(SobelFixture, Sobel, Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); - if ((srcSize == OCL_SIZE_2000 && type == CV_8UC4) || - (srcSize == OCL_SIZE_4000 && type == CV_8UC1)) - declare.time(5.5); - else if (srcSize == OCL_SIZE_4000 && type == CV_8UC4) - declare.time(20); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst(srcSize, type); @@ -217,9 +278,8 @@ PERF_TEST_P(SobelFixture, Sobel, typedef Size_MatType ScharrFixture; -PERF_TEST_P(ScharrFixture, Scharr, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(ScharrFixture, Scharr, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -230,12 +290,6 @@ PERF_TEST_P(ScharrFixture, Scharr, Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); - if ((srcSize == OCL_SIZE_2000 && type == CV_8UC4) || - (srcSize == OCL_SIZE_4000 && type == CV_8UC1)) - declare.time(5.5); - else if (srcSize == OCL_SIZE_4000 && type == CV_8UC4) - declare.time(21); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst(srcSize, type); @@ -244,7 +298,7 @@ PERF_TEST_P(ScharrFixture, Scharr, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 3e-3); } else if (RUN_PLAIN_IMPL) { @@ -258,15 +312,14 @@ PERF_TEST_P(ScharrFixture, Scharr, ///////////// GaussianBlur //////////////////////// -typedef Size_MatType GaussianBlurFixture; +typedef FilterFixture GaussianBlurFixture; -PERF_TEST_P(GaussianBlurFixture, GaussianBlur, - ::testing::Combine(::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000), - OCL_PERF_ENUM(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4))) +OCL_PERF_TEST_P(GaussianBlurFixture, GaussianBlur, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, OCL_PERF_ENUM(3, 5, 7))) { - const Size_MatType_t params = GetParam(); + const FilterParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params), ksize = 7; + const int type = get<1>(params), ksize = get<2>(params); checkDeviceMaxMemoryAllocSize(srcSize, type); @@ -297,15 +350,14 @@ PERF_TEST_P(GaussianBlurFixture, GaussianBlur, ///////////// filter2D//////////////////////// -typedef Size_MatType filter2DFixture; +typedef FilterFixture Filter2DFixture; -PERF_TEST_P(filter2DFixture, filter2D, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(Filter2DFixture, Filter2D, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, OCL_PERF_ENUM(3, 5))) { - const Size_MatType_t params = GetParam(); + const FilterParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params), ksize = 3; + const int type = get<1>(params), ksize = get<2>(params); checkDeviceMaxMemoryAllocSize(srcSize, type); @@ -313,9 +365,6 @@ PERF_TEST_P(filter2DFixture, filter2D, declare.in(src, WARMUP_RNG).in(kernel).out(dst); randu(kernel, -3.0, 3.0); - if (srcSize == OCL_SIZE_4000 && type == CV_8UC4) - declare.time(8); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst(srcSize, type), oclKernel(kernel); @@ -324,13 +373,13 @@ PERF_TEST_P(filter2DFixture, filter2D, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 3e-2); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::filter2D(src, dst, -1, kernel); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 1e-2); } else OCL_PERF_ELSE @@ -338,28 +387,22 @@ PERF_TEST_P(filter2DFixture, filter2D, ///////////// Bilateral//////////////////////// -typedef Size_MatType BilateralFixture; +typedef TestBaseWithParam BilateralFixture; -PERF_TEST_P(BilateralFixture, Bilateral, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC3))) +OCL_PERF_TEST_P(BilateralFixture, Bilateral, OCL_TEST_SIZES) { - const Size_MatType_t params = GetParam(); - const Size srcSize = get<0>(params); - const int type = get<1>(params), d = 7; + const Size srcSize = GetParam(); + const int d = 7; const double sigmacolor = 50.0, sigmaspace = 50.0; - checkDeviceMaxMemoryAllocSize(srcSize, type); + checkDeviceMaxMemoryAllocSize(srcSize, CV_8UC1); - Mat src(srcSize, type), dst(srcSize, type); + Mat src(srcSize, CV_8UC1), dst(srcSize, CV_8UC1); declare.in(src, WARMUP_RNG).out(dst); - if (srcSize == OCL_SIZE_4000) - declare.time(type == CV_8UC3 ? 8 : 4.5); - if (RUN_OCL_IMPL) { - ocl::oclMat oclSrc(src), oclDst(srcSize, type); + ocl::oclMat oclSrc(src), oclDst(srcSize, CV_8UC1); OCL_TEST_CYCLE() cv::ocl::bilateralFilter(oclSrc, oclDst, d, sigmacolor, sigmaspace); @@ -377,37 +420,35 @@ PERF_TEST_P(BilateralFixture, Bilateral, OCL_PERF_ELSE } -///////////// adaptiveBilateral//////////////////////// +///////////// MedianBlur//////////////////////// -typedef Size_MatType adaptiveBilateralFixture; +typedef tuple MedianBlurParams; +typedef TestBaseWithParam MedianBlurFixture; -PERF_TEST_P(adaptiveBilateralFixture, DISABLED_adaptiveBilateral, - ::testing::Combine(::testing::Values(OCL_SIZE_1000), OCL_PERF_ENUM(CV_8UC1, CV_8UC3))) +OCL_PERF_TEST_P(MedianBlurFixture, Bilateral, ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(3, 5))) { - const Size_MatType_t params = GetParam(); + MedianBlurParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params); - const double sigmaspace = 10.0; - Size ksize(9, 9); + const int ksize = get<1>(params); - checkDeviceMaxMemoryAllocSize(srcSize, type); + checkDeviceMaxMemoryAllocSize(srcSize, CV_8UC1); - Mat src(srcSize, type), dst(srcSize, type); + Mat src(srcSize, CV_8UC1), dst(srcSize, CV_8UC1); declare.in(src, WARMUP_RNG).out(dst); if (RUN_OCL_IMPL) { - ocl::oclMat oclSrc(src), oclDst(srcSize, type); + ocl::oclMat oclSrc(src), oclDst(srcSize, CV_8UC1); - OCL_TEST_CYCLE() cv::ocl::adaptiveBilateralFilter(oclSrc, oclDst, ksize, sigmaspace); + OCL_TEST_CYCLE() cv::ocl::medianFilter(oclSrc, oclDst, ksize); oclDst.download(dst); - SANITY_CHECK(dst, 1.0); + SANITY_CHECK(dst); } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() cv::adaptiveBilateralFilter(src, dst, ksize, sigmaspace); + TEST_CYCLE() cv::medianBlur(src, dst, ksize); SANITY_CHECK(dst); } diff --git a/modules/ocl/perf/perf_gemm.cpp b/modules/ocl/perf/perf_gemm.cpp index 4dcd5d4d6a..32492ad978 100644 --- a/modules/ocl/perf/perf_gemm.cpp +++ b/modules/ocl/perf/perf_gemm.cpp @@ -46,16 +46,21 @@ #include "perf_precomp.hpp" using namespace perf; +using std::tr1::get; ///////////// gemm //////////////////////// -typedef TestBaseWithParam gemmFixture; +typedef Size_MatType GemmFixture; #ifdef HAVE_CLAMDBLAS -PERF_TEST_P(gemmFixture, gemm, ::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000)) +OCL_PERF_TEST_P(GemmFixture, Gemm, ::testing::Combine( + ::testing::Values(Size(1000, 1000), Size(1500, 1500)), + ::testing::Values((int)cv::GEMM_1_T, (int)cv::GEMM_1_T | (int)cv::GEMM_2_T))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); Mat src1(srcSize, CV_32FC1), src2(srcSize, CV_32FC1), src3(srcSize, CV_32FC1), dst(srcSize, CV_32FC1); @@ -69,7 +74,7 @@ PERF_TEST_P(gemmFixture, gemm, ::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000)) ocl::oclMat oclSrc1(src1), oclSrc2(src2), oclSrc3(src3), oclDst(srcSize, CV_32FC1); - OCL_TEST_CYCLE() cv::ocl::gemm(oclSrc1, oclSrc2, 1.0, oclSrc3, 1.0, oclDst); + OCL_TEST_CYCLE() cv::ocl::gemm(oclSrc1, oclSrc2, 1.0, oclSrc3, 1.0, oclDst, type); oclDst.download(dst); @@ -77,7 +82,7 @@ PERF_TEST_P(gemmFixture, gemm, ::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000)) } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() cv::gemm(src1, src2, 1.0, src3, 1.0, dst); + TEST_CYCLE() cv::gemm(src1, src2, 1.0, src3, 1.0, dst, type); SANITY_CHECK(dst, 0.01); } diff --git a/modules/ocl/perf/perf_gftt.cpp b/modules/ocl/perf/perf_gftt.cpp index af24c3489a..688d24316b 100644 --- a/modules/ocl/perf/perf_gftt.cpp +++ b/modules/ocl/perf/perf_gftt.cpp @@ -52,22 +52,21 @@ using std::tr1::get; ///////////// GoodFeaturesToTrack //////////////////////// -typedef tuple GoodFeaturesToTrackParams; +typedef tuple GoodFeaturesToTrackParams; typedef TestBaseWithParam GoodFeaturesToTrackFixture; -PERF_TEST_P(GoodFeaturesToTrackFixture, GoodFeaturesToTrack, - ::testing::Combine(::testing::Values(string("gpu/opticalflow/rubberwhale1.png"), - string("gpu/stereobm/aloe-L.png")), - ::testing::Range(0.0, 4.0, 3.0))) +OCL_PERF_TEST_P(GoodFeaturesToTrackFixture, GoodFeaturesToTrack, + ::testing::Combine(OCL_PERF_ENUM(String("gpu/opticalflow/rubberwhale1.png")), + OCL_PERF_ENUM(0.0, 3.0), testing::Bool())) { + const GoodFeaturesToTrackParams params = GetParam(); + const String fileName = get<0>(params); + const double minDistance = get<1>(params), qualityLevel = 0.01; + const bool harrisDetector = get<2>(params); + const int maxCorners = 1000; - const GoodFeaturesToTrackParams param = GetParam(); - const string fileName = getDataPath(get<0>(param)); - const int maxCorners = 2000; - const double qualityLevel = 0.01, minDistance = get<1>(param); - - Mat frame = imread(fileName, IMREAD_GRAYSCALE); - ASSERT_TRUE(!frame.empty()) << "no input image"; + Mat frame = imread(getDataPath(fileName), IMREAD_GRAYSCALE); + ASSERT_FALSE(frame.empty()) << "no input image"; vector pts_gold; declare.in(frame); @@ -75,7 +74,8 @@ PERF_TEST_P(GoodFeaturesToTrackFixture, GoodFeaturesToTrack, if (RUN_OCL_IMPL) { ocl::oclMat oclFrame(frame), pts_oclmat; - ocl::GoodFeaturesToTrackDetector_OCL detector(maxCorners, qualityLevel, minDistance); + ocl::GoodFeaturesToTrackDetector_OCL detector(maxCorners, qualityLevel, minDistance, 3, + harrisDetector); OCL_TEST_CYCLE() detector(oclFrame, pts_oclmat); @@ -86,7 +86,7 @@ PERF_TEST_P(GoodFeaturesToTrackFixture, GoodFeaturesToTrack, else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::goodFeaturesToTrack(frame, pts_gold, - maxCorners, qualityLevel, minDistance); + maxCorners, qualityLevel, minDistance, noArray(), 3, harrisDetector); SANITY_CHECK(pts_gold); } diff --git a/modules/ocl/perf/perf_haar.cpp b/modules/ocl/perf/perf_haar.cpp index 1a97e908c1..e70641d061 100644 --- a/modules/ocl/perf/perf_haar.cpp +++ b/modules/ocl/perf/perf_haar.cpp @@ -46,8 +46,13 @@ #include "perf_precomp.hpp" using namespace perf; +using namespace std; +using namespace cv; +using std::tr1::make_tuple; +using std::tr1::get; ///////////// Haar //////////////////////// + PERF_TEST(HaarFixture, Haar) { vector faces; @@ -84,23 +89,16 @@ PERF_TEST(HaarFixture, Haar) OCL_PERF_ELSE } -using namespace std; -using namespace cv; -using namespace perf; -using std::tr1::make_tuple; -using std::tr1::get; +typedef std::tr1::tuple Cascade_Image_MinSize_t; +typedef perf::TestBaseWithParam Cascade_Image_MinSize; -typedef std::tr1::tuple OCL_Cascade_Image_MinSize_t; -typedef perf::TestBaseWithParam OCL_Cascade_Image_MinSize; - -PERF_TEST_P( OCL_Cascade_Image_MinSize, CascadeClassifier, - testing::Combine( - testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml"), - string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt2.xml") ), - testing::Values( string("cv/shared/lena.png"), - string("cv/cascadeandhog/images/bttf301.png")/*, - string("cv/cascadeandhog/images/class57.png")*/ ), - testing::Values(30, 64, 90) ) ) +OCL_PERF_TEST_P(Cascade_Image_MinSize, DISABLED_CascadeClassifier, + testing::Combine(testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml"), + string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt2.xml") ), + testing::Values(string("cv/shared/lena.png"), + string("cv/cascadeandhog/images/bttf301.png")/*, + string("cv/cascadeandhog/images/class57.png")*/ ), + testing::Values(30, 64, 90))) { const string cascasePath = get<0>(GetParam()); const string imagePath = get<1>(GetParam()); @@ -109,7 +107,7 @@ PERF_TEST_P( OCL_Cascade_Image_MinSize, CascadeClassifier, vector faces; Mat img = imread(getDataPath(imagePath), IMREAD_GRAYSCALE); - ASSERT_TRUE(!img.empty()) << "Can't load source image: " << getDataPath(imagePath); + ASSERT_FALSE(img.empty()) << "Can't load source image: " << getDataPath(imagePath); equalizeHist(img, img); declare.in(img); @@ -146,7 +144,7 @@ PERF_TEST_P( OCL_Cascade_Image_MinSize, CascadeClassifier, else OCL_PERF_ELSE - //sort(faces.begin(), faces.end(), comparators::RectLess()); - SANITY_CHECK_NOTHING();//(faces, min_size/5); - // using SANITY_CHECK_NOTHING() since OCL and PLAIN version may find different faces number + //sort(faces.begin(), faces.end(), comparators::RectLess()); + SANITY_CHECK_NOTHING();//(faces, min_size/5); + // using SANITY_CHECK_NOTHING() since OCL and PLAIN version may find different faces number } diff --git a/modules/ocl/perf/perf_hog.cpp b/modules/ocl/perf/perf_hog.cpp index 2a67311170..a65769a26c 100644 --- a/modules/ocl/perf/perf_hog.cpp +++ b/modules/ocl/perf/perf_hog.cpp @@ -43,7 +43,9 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ + #include "perf_precomp.hpp" +#include using namespace perf; @@ -66,7 +68,7 @@ struct RectLess : } }; -PERF_TEST(HOGFixture, HOG) +OCL_PERF_TEST(HOGFixture, HOG) { Mat src = imread(getDataPath("gpu/hog/road.png"), cv::IMREAD_GRAYSCALE); ASSERT_TRUE(!src.empty()) << "can't open input image road.png"; diff --git a/modules/ocl/perf/perf_imgproc.cpp b/modules/ocl/perf/perf_imgproc.cpp index e0ffe56e7e..85f2c5528f 100644 --- a/modules/ocl/perf/perf_imgproc.cpp +++ b/modules/ocl/perf/perf_imgproc.cpp @@ -51,9 +51,9 @@ using std::tr1::get; ///////////// equalizeHist //////////////////////// -typedef TestBaseWithParam equalizeHistFixture; +typedef TestBaseWithParam EqualizeHistFixture; -PERF_TEST_P(equalizeHistFixture, equalizeHist, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(EqualizeHistFixture, EqualizeHist, OCL_TEST_SIZES) { const Size srcSize = GetParam(); const double eps = 1 + DBL_EPSILON; @@ -83,16 +83,13 @@ PERF_TEST_P(equalizeHistFixture, equalizeHist, OCL_TYPICAL_MAT_SIZES) /////////// CopyMakeBorder ////////////////////// -CV_ENUM(Border, BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT, - BORDER_WRAP, BORDER_REFLECT_101) +CV_ENUM(Border, BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT, BORDER_WRAP, BORDER_REFLECT_101) typedef tuple CopyMakeBorderParamType; typedef TestBaseWithParam CopyMakeBorderFixture; -PERF_TEST_P(CopyMakeBorderFixture, CopyMakeBorder, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4), - Border::all())) +OCL_PERF_TEST_P(CopyMakeBorderFixture, CopyMakeBorder, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, Border::all())) { const CopyMakeBorderParamType params = GetParam(); const Size srcSize = get<0>(params); @@ -125,11 +122,10 @@ PERF_TEST_P(CopyMakeBorderFixture, CopyMakeBorder, ///////////// cornerMinEigenVal //////////////////////// -typedef Size_MatType cornerMinEigenValFixture; +typedef Size_MatType CornerMinEigenValFixture; -PERF_TEST_P(cornerMinEigenValFixture, cornerMinEigenVal, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +OCL_PERF_TEST_P(CornerMinEigenValFixture, CornerMinEigenVal, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -165,11 +161,10 @@ PERF_TEST_P(cornerMinEigenValFixture, cornerMinEigenVal, ///////////// cornerHarris //////////////////////// -typedef Size_MatType cornerHarrisFixture; +typedef Size_MatType CornerHarrisFixture; -PERF_TEST_P(cornerHarrisFixture, cornerHarris, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +OCL_PERF_TEST_P(CornerHarrisFixture, CornerHarris, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -202,11 +197,14 @@ PERF_TEST_P(cornerHarrisFixture, cornerHarris, ///////////// integral //////////////////////// -typedef TestBaseWithParam integralFixture; +typedef tuple IntegralParams; +typedef TestBaseWithParam IntegralFixture; -PERF_TEST_P(integralFixture, integral, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(IntegralFixture, Integral1, ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32S, CV_32F))) { - const Size srcSize = GetParam(); + const IntegralParams params = GetParam(); + const Size srcSize = get<0>(params); + const int sdepth = get<1>(params); Mat src(srcSize, CV_8UC1), dst; declare.in(src, WARMUP_RNG); @@ -215,17 +213,17 @@ PERF_TEST_P(integralFixture, integral, OCL_TYPICAL_MAT_SIZES) { ocl::oclMat oclSrc(src), oclDst; - OCL_TEST_CYCLE() cv::ocl::integral(oclSrc, oclDst); + OCL_TEST_CYCLE() cv::ocl::integral(oclSrc, oclDst, sdepth); oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() cv::integral(src, dst); + TEST_CYCLE() cv::integral(src, dst, sdepth); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); } else OCL_PERF_ELSE @@ -233,15 +231,13 @@ PERF_TEST_P(integralFixture, integral, OCL_TYPICAL_MAT_SIZES) ///////////// threshold//////////////////////// -CV_ENUM(ThreshType, THRESH_BINARY, THRESH_TOZERO_INV) +CV_ENUM(ThreshType, THRESH_BINARY, THRESH_BINARY_INV, THRESH_TRUNC, THRESH_TOZERO_INV) typedef tuple ThreshParams; typedef TestBaseWithParam ThreshFixture; -PERF_TEST_P(ThreshFixture, threshold, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4, CV_16SC1, CV_16SC4, CV_32FC1), - ThreshType::all())) +OCL_PERF_TEST_P(ThreshFixture, Threshold, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, ThreshType::all())) { const ThreshParams params = GetParam(); const Size srcSize = get<0>(params); @@ -463,9 +459,9 @@ static void meanShiftFiltering_(const Mat &src_roi, Mat &dst_roi, int sp, int sr } } -typedef TestBaseWithParam meanShiftFilteringFixture; +typedef TestBaseWithParam MeanShiftFilteringFixture; -PERF_TEST_P(meanShiftFilteringFixture, meanShiftFiltering, +PERF_TEST_P(MeanShiftFilteringFixture, MeanShiftFiltering, OCL_TYPICAL_MAT_SIZES) { const Size srcSize = GetParam(); @@ -556,9 +552,9 @@ static void meanShiftProc_(const Mat &src_roi, Mat &dst_roi, Mat &dstCoor_roi, i } -typedef TestBaseWithParam meanShiftProcFixture; +typedef TestBaseWithParam MeanShiftProcFixture; -PERF_TEST_P(meanShiftProcFixture, meanShiftProc, +PERF_TEST_P(MeanShiftProcFixture, MeanShiftProc, OCL_TYPICAL_MAT_SIZES) { const Size srcSize = GetParam(); @@ -598,7 +594,7 @@ PERF_TEST_P(meanShiftProcFixture, meanShiftProc, typedef TestBaseWithParam CLAHEFixture; -PERF_TEST_P(CLAHEFixture, CLAHE, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(CLAHEFixture, CLAHE, OCL_TEST_SIZES) { const Size srcSize = GetParam(); const string impl = getSelectedImpl(); @@ -632,9 +628,9 @@ PERF_TEST_P(CLAHEFixture, CLAHE, OCL_TYPICAL_MAT_SIZES) OCL_PERF_ELSE } -///////////// columnSum//////////////////////// +///////////// ColumnSum//////////////////////// -typedef TestBaseWithParam columnSumFixture; +typedef TestBaseWithParam ColumnSumFixture; static void columnSumPerfTest(const Mat & src, Mat & dst) { @@ -646,7 +642,7 @@ static void columnSumPerfTest(const Mat & src, Mat & dst) dst.at(i, j) = dst.at(i - 1 , j) + src.at(i , j); } -PERF_TEST_P(columnSumFixture, columnSum, OCL_TYPICAL_MAT_SIZES) +PERF_TEST_P(ColumnSumFixture, ColumnSum, OCL_TYPICAL_MAT_SIZES) { const Size srcSize = GetParam(); @@ -680,8 +676,8 @@ PERF_TEST_P(columnSumFixture, columnSum, OCL_TYPICAL_MAT_SIZES) CV_ENUM(DistType, NORM_L1, NORM_L2SQR) -typedef tuple distanceToCentersParameters; -typedef TestBaseWithParam distanceToCentersFixture; +typedef tuple DistanceToCentersParams; +typedef TestBaseWithParam DistanceToCentersFixture; static void distanceToCentersPerfTest(Mat& src, Mat& centers, Mat& dists, Mat& labels, int distType) { @@ -706,10 +702,11 @@ static void distanceToCentersPerfTest(Mat& src, Mat& centers, Mat& dists, Mat& l Mat(labels_v).copyTo(labels); } -PERF_TEST_P(distanceToCentersFixture, distanceToCenters, ::testing::Combine(::testing::Values(cv::Size(256,256), cv::Size(512,512)), DistType::all()) ) +PERF_TEST_P(DistanceToCentersFixture, DistanceToCenters, ::testing::Combine(::testing::Values(cv::Size(256,256), cv::Size(512,512)), DistType::all()) ) { - Size size = get<0>(GetParam()); - int distType = get<1>(GetParam()); + const DistanceToCentersParams params = GetParam(); + Size size = get<0>(params); + int distType = get<1>(params); Mat src(size, CV_32FC1), centers(size, CV_32FC1); Mat dists(src.rows, 1, CV_32FC1), labels(src.rows, 1, CV_32SC1); diff --git a/modules/ocl/perf/perf_imgwarp.cpp b/modules/ocl/perf/perf_imgwarp.cpp index e768d66219..fe863fa701 100644 --- a/modules/ocl/perf/perf_imgwarp.cpp +++ b/modules/ocl/perf/perf_imgwarp.cpp @@ -51,11 +51,13 @@ using std::tr1::get; ///////////// WarpAffine //////////////////////// -typedef Size_MatType WarpAffineFixture; +CV_ENUM(InterType, INTER_NEAREST, INTER_LINEAR) -PERF_TEST_P(WarpAffineFixture, WarpAffine, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +typedef tuple WarpAffineParams; +typedef TestBaseWithParam WarpAffineFixture; + +OCL_PERF_TEST_P(WarpAffineFixture, WarpAffine, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134, InterType::all())) { static const double coeffs[2][3] = { @@ -63,11 +65,12 @@ PERF_TEST_P(WarpAffineFixture, WarpAffine, { sin(CV_PI / 6), cos(CV_PI / 6), -100.0 } }; Mat M(2, 3, CV_64F, (void *)coeffs); - const int interpolation = INTER_NEAREST; - const Size_MatType_t params = GetParam(); + const WarpAffineParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params); + const int type = get<1>(params), interpolation = get<2>(params); + + checkDeviceMaxMemoryAllocSize(srcSize, type); Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); @@ -80,13 +83,13 @@ PERF_TEST_P(WarpAffineFixture, WarpAffine, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-4); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::warpAffine(src, dst, M, srcSize, interpolation); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-4); } else OCL_PERF_ELSE @@ -94,11 +97,11 @@ PERF_TEST_P(WarpAffineFixture, WarpAffine, ///////////// WarpPerspective //////////////////////// -typedef Size_MatType WarpPerspectiveFixture; +typedef WarpAffineParams WarpPerspectiveParams; +typedef TestBaseWithParam WarpPerspectiveFixture; -PERF_TEST_P(WarpPerspectiveFixture, WarpPerspective, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(WarpPerspectiveFixture, WarpPerspective, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134, InterType::all())) { static const double coeffs[3][3] = { @@ -107,15 +110,15 @@ PERF_TEST_P(WarpPerspectiveFixture, WarpPerspective, {0.0, 0.0, 1.0} }; Mat M(3, 3, CV_64F, (void *)coeffs); - const int interpolation = INTER_LINEAR; - const Size_MatType_t params = GetParam(); + const WarpPerspectiveParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params); + const int type = get<1>(params), interpolation = get<2>(params); + + checkDeviceMaxMemoryAllocSize(srcSize, type); Mat src(srcSize, type), dst(srcSize, type); - declare.in(src, WARMUP_RNG).out(dst) - .time(srcSize == OCL_SIZE_4000 ? 18 : srcSize == OCL_SIZE_2000 ? 5 : 2); + declare.in(src, WARMUP_RNG).out(dst); if (RUN_OCL_IMPL) { @@ -125,32 +128,28 @@ PERF_TEST_P(WarpPerspectiveFixture, WarpPerspective, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-4); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::warpPerspective(src, dst, M, srcSize, interpolation); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-4); } else OCL_PERF_ELSE } -///////////// resize //////////////////////// +///////////// Resize //////////////////////// -CV_ENUM(resizeInterType, INTER_NEAREST, INTER_LINEAR) +typedef tuple ResizeParams; +typedef TestBaseWithParam ResizeFixture; -typedef tuple resizeParams; -typedef TestBaseWithParam resizeFixture; - -PERF_TEST_P(resizeFixture, resize, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4), - resizeInterType::all(), - ::testing::Values(0.5, 2.0))) +OCL_PERF_TEST_P(ResizeFixture, Resize, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134, + InterType::all(), ::testing::Values(0.5, 2.0))) { - const resizeParams params = GetParam(); + const ResizeParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params), interType = get<2>(params); double scale = get<3>(params); @@ -162,8 +161,6 @@ PERF_TEST_P(resizeFixture, resize, Mat src(srcSize, type), dst; dst.create(dstSize, type); declare.in(src, WARMUP_RNG).out(dst); - if (interType == INTER_LINEAR && type == CV_8UC4 && OCL_SIZE_4000 == srcSize) - declare.time(11); if (RUN_OCL_IMPL) { @@ -185,15 +182,13 @@ PERF_TEST_P(resizeFixture, resize, OCL_PERF_ELSE } -typedef tuple resizeAreaParams; -typedef TestBaseWithParam resizeAreaFixture; +typedef tuple ResizeAreaParams; +typedef TestBaseWithParam ResizeAreaFixture; -PERF_TEST_P(resizeAreaFixture, resize, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4), - ::testing::Values(0.3, 0.5, 0.6))) +OCL_PERF_TEST_P(ResizeAreaFixture, Resize, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES_134, ::testing::Values(0.3, 0.5, 0.6))) { - const resizeAreaParams params = GetParam(); + const ResizeAreaParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params); double scale = get<2>(params); @@ -225,19 +220,15 @@ PERF_TEST_P(resizeAreaFixture, resize, OCL_PERF_ELSE } -///////////// remap//////////////////////// +///////////// Remap //////////////////////// -CV_ENUM(RemapInterType, INTER_NEAREST, INTER_LINEAR) +typedef tuple RemapParams; +typedef TestBaseWithParam RemapFixture; -typedef tuple remapParams; -typedef TestBaseWithParam remapFixture; - -PERF_TEST_P(remapFixture, remap, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4), - RemapInterType::all())) +OCL_PERF_TEST_P(RemapFixture, Remap, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES, InterType::all())) { - const remapParams params = GetParam(); + const RemapParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params), interpolation = get<2>(params); @@ -287,7 +278,7 @@ PERF_TEST_P(remapFixture, remap, } -///////////// buildWarpPerspectiveMaps //////////////////////// +///////////// BuildWarpPerspectiveMaps //////////////////////// static void buildWarpPerspectiveMaps(const Mat &M, bool inverse, Size dsize, Mat &xmap, Mat &ymap) { @@ -323,9 +314,9 @@ static void buildWarpPerspectiveMaps(const Mat &M, bool inverse, Size dsize, Mat } } -typedef TestBaseWithParam buildWarpPerspectiveMapsFixture; +typedef TestBaseWithParam BuildWarpPerspectiveMapsFixture; -PERF_TEST_P(buildWarpPerspectiveMapsFixture, Inverse, OCL_TYPICAL_MAT_SIZES) +PERF_TEST_P(BuildWarpPerspectiveMapsFixture, Inverse, OCL_TYPICAL_MAT_SIZES) { static const double coeffs[3][3] = { diff --git a/modules/ocl/perf/perf_match_template.cpp b/modules/ocl/perf/perf_match_template.cpp index 561a978ae3..236ae17509 100644 --- a/modules/ocl/perf/perf_match_template.cpp +++ b/modules/ocl/perf/perf_match_template.cpp @@ -43,6 +43,7 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ + #include "perf_precomp.hpp" using namespace perf; @@ -53,8 +54,8 @@ using std::tr1::get; typedef Size_MatType CV_TM_CCORRFixture; -PERF_TEST_P(CV_TM_CCORRFixture, matchTemplate, - ::testing::Combine(::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000), +OCL_PERF_TEST_P(CV_TM_CCORRFixture, matchTemplate, + ::testing::Combine(::testing::Values(Size(1000, 1000), Size(2000, 2000)), OCL_PERF_ENUM(CV_32FC1, CV_32FC4))) { const Size_MatType_t params = GetParam(); @@ -66,7 +67,7 @@ PERF_TEST_P(CV_TM_CCORRFixture, matchTemplate, Mat dst(dstSize, CV_32F); randu(src, 0.0f, 1.0f); randu(templ, 0.0f, 1.0f); - declare.time(srcSize == OCL_SIZE_2000 ? 20 : 6).in(src, templ).out(dst); + declare.in(src, templ).out(dst); if (RUN_OCL_IMPL) { @@ -90,7 +91,8 @@ PERF_TEST_P(CV_TM_CCORRFixture, matchTemplate, typedef TestBaseWithParam CV_TM_CCORR_NORMEDFixture; -PERF_TEST_P(CV_TM_CCORR_NORMEDFixture, matchTemplate, OCL_TYPICAL_MAT_SIZES) +OCL_PERF_TEST_P(CV_TM_CCORR_NORMEDFixture, matchTemplate, + ::testing::Values(Size(1000, 1000), Size(2000, 2000), Size(4000, 4000))) { const Size srcSize = GetParam(), templSize(5, 5); @@ -125,7 +127,7 @@ CV_ENUM(MethodType, TM_SQDIFF, TM_SQDIFF_NORMED, TM_CCORR, TM_CCORR_NORMED, TM_C typedef std::tr1::tuple ImgSize_TmplSize_Method_t; typedef TestBaseWithParam ImgSize_TmplSize_Method; -PERF_TEST_P(ImgSize_TmplSize_Method, MatchTemplate, +OCL_PERF_TEST_P(ImgSize_TmplSize_Method, MatchTemplate, ::testing::Combine( testing::Values(szSmall128, cv::Size(320, 240), cv::Size(640, 480), cv::Size(800, 600), diff --git a/modules/ocl/perf/perf_matrix_operation.cpp b/modules/ocl/perf/perf_matrix_operation.cpp index 5ca322e22f..434f134ca3 100644 --- a/modules/ocl/perf/perf_matrix_operation.cpp +++ b/modules/ocl/perf/perf_matrix_operation.cpp @@ -53,9 +53,7 @@ using std::tr1::get; typedef Size_MatType ConvertToFixture; -PERF_TEST_P(ConvertToFixture, ConvertTo, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(ConvertToFixture, ConvertTo, ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -92,11 +90,9 @@ PERF_TEST_P(ConvertToFixture, ConvertTo, ///////////// copyTo//////////////////////// -typedef Size_MatType copyToFixture; +typedef Size_MatType CopyToFixture; -PERF_TEST_P(copyToFixture, copyTo, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(CopyToFixture, CopyTo, ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -127,11 +123,9 @@ PERF_TEST_P(copyToFixture, copyTo, ///////////// setTo//////////////////////// -typedef Size_MatType setToFixture; +typedef Size_MatType SetToFixture; -PERF_TEST_P(setToFixture, setTo, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(SetToFixture, SetTo, ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -164,16 +158,16 @@ PERF_TEST_P(setToFixture, setTo, /////////////////// upload /////////////////////////// -typedef tuple uploadParams; -typedef TestBaseWithParam uploadFixture; +typedef tuple UploadParams; +typedef TestBaseWithParam UploadFixture; -PERF_TEST_P(uploadFixture, upload, +PERF_TEST_P(UploadFixture, Upload, testing::Combine( OCL_TYPICAL_MAT_SIZES, testing::Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F), testing::Range(1, 5))) { - const uploadParams params = GetParam(); + const UploadParams params = GetParam(); const Size srcSize = get<0>(params); const int depth = get<1>(params), cn = get<2>(params); const int type = CV_MAKE_TYPE(depth, cn); @@ -201,15 +195,15 @@ PERF_TEST_P(uploadFixture, upload, /////////////////// download /////////////////////////// -typedef TestBaseWithParam downloadFixture; +typedef TestBaseWithParam DownloadFixture; -PERF_TEST_P(downloadFixture, download, +PERF_TEST_P(DownloadFixture, Download, testing::Combine( OCL_TYPICAL_MAT_SIZES, testing::Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F), testing::Range(1, 5))) { - const uploadParams params = GetParam(); + const UploadParams params = GetParam(); const Size srcSize = get<0>(params); const int depth = get<1>(params), cn = get<2>(params); const int type = CV_MAKE_TYPE(depth, cn); diff --git a/modules/ocl/perf/perf_moments.cpp b/modules/ocl/perf/perf_moments.cpp index 631031ecb4..d6a9b5c203 100644 --- a/modules/ocl/perf/perf_moments.cpp +++ b/modules/ocl/perf/perf_moments.cpp @@ -55,22 +55,19 @@ using namespace cvtest; using namespace testing; using namespace std; - ///////////// Moments //////////////////////// -//*! performance of image -typedef tuple MomentsParamType; -typedef TestBaseWithParam MomentsFixture; -PERF_TEST_P(MomentsFixture, Moments, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_16SC1, CV_16UC1, CV_32FC1), ::testing::Bool())) +typedef tuple MomentsParams; +typedef TestBaseWithParam MomentsFixture; + +OCL_PERF_TEST_P(MomentsFixture, Moments, + ::testing::Combine(OCL_TEST_SIZES, ::testing::Bool())) { - const MomentsParamType params = GetParam(); + const MomentsParams params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params); - const bool binaryImage = get<2>(params); + const bool binaryImage = get<1>(params); - Mat src(srcSize, type), dst(7, 1, CV_64F); + Mat src(srcSize, CV_8UC1), dst(7, 1, CV_64F); randu(src, 0, 255); cv::Moments mom; @@ -85,6 +82,7 @@ PERF_TEST_P(MomentsFixture, Moments, } else OCL_PERF_ELSE + cv::HuMoments(mom, dst); SANITY_CHECK(dst, 2e-1); } diff --git a/modules/ocl/perf/perf_norm.cpp b/modules/ocl/perf/perf_norm.cpp index ff49eb4eda..abeb93cf18 100644 --- a/modules/ocl/perf/perf_norm.cpp +++ b/modules/ocl/perf/perf_norm.cpp @@ -51,18 +51,21 @@ using std::tr1::get; ///////////// norm//////////////////////// -typedef tuple normParams; -typedef TestBaseWithParam normFixture; +CV_ENUM(NormType, NORM_INF, NORM_L1, NORM_L2) -PERF_TEST_P(normFixture, norm, testing::Combine( - OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +typedef std::tr1::tuple NormParams; +typedef TestBaseWithParam NormFixture; + +OCL_PERF_TEST_P(NormFixture, Norm, + ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), + OCL_TEST_TYPES, NormType::all())) { - const normParams params = GetParam(); + const NormParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params); - double value = 0.0; - const double eps = CV_MAT_DEPTH(type) == CV_8U ? DBL_EPSILON : 1e-3; + const int normType = get<2>(params); + perf::ERROR_TYPE errorType = type != NORM_INF ? ERROR_RELATIVE : ERROR_ABSOLUTE; + double eps = 1e-5, value; Mat src1(srcSize, type), src2(srcSize, type); declare.in(src1, src2, WARMUP_RNG); @@ -71,15 +74,15 @@ PERF_TEST_P(normFixture, norm, testing::Combine( { ocl::oclMat oclSrc1(src1), oclSrc2(src2); - OCL_TEST_CYCLE() value = cv::ocl::norm(oclSrc1, oclSrc2, NORM_INF); + OCL_TEST_CYCLE() value = cv::ocl::norm(oclSrc1, oclSrc2, normType); - SANITY_CHECK(value, eps); + SANITY_CHECK(value, eps, errorType); } else if (RUN_PLAIN_IMPL) { - TEST_CYCLE() value = cv::norm(src1, src2, NORM_INF); + TEST_CYCLE() value = cv::norm(src1, src2, normType); - SANITY_CHECK(value); + SANITY_CHECK(value, eps, errorType); } else OCL_PERF_ELSE diff --git a/modules/ocl/perf/perf_opticalflow.cpp b/modules/ocl/perf/perf_opticalflow.cpp index bc1761b497..3711e87d89 100644 --- a/modules/ocl/perf/perf_opticalflow.cpp +++ b/modules/ocl/perf/perf_opticalflow.cpp @@ -52,55 +52,28 @@ using std::tr1::get; using std::tr1::tuple; using std::tr1::make_tuple; -CV_ENUM(LoadMode, IMREAD_GRAYSCALE, IMREAD_COLOR) +typedef tuple PyrLKOpticalFlowParamType; +typedef TestBaseWithParam PyrLKOpticalFlowFixture; -typedef tuple > PyrLKOpticalFlowParamType; -typedef TestBaseWithParam PyrLKOpticalFlowFixture; - -PERF_TEST_P(PyrLKOpticalFlowFixture, - PyrLKOpticalFlow, - ::testing::Combine( - ::testing::Values(1000, 2000, 4000), - ::testing::Values( - make_tuple - ( - string("gpu/opticalflow/rubberwhale1.png"), - string("gpu/opticalflow/rubberwhale2.png"), - LoadMode(IMREAD_COLOR) - ), - make_tuple - ( - string("gpu/stereobm/aloe-L.png"), - string("gpu/stereobm/aloe-R.png"), - LoadMode(IMREAD_GRAYSCALE) - ) - ) - ) - ) +OCL_PERF_TEST_P(PyrLKOpticalFlowFixture, + PyrLKOpticalFlow, ::testing::Values(1000, 2000, 4000)) { - PyrLKOpticalFlowParamType params = GetParam(); - tuple fileParam = get<1>(params); - const int pointsCount = get<0>(params); - const int openMode = static_cast(get<2>(fileParam)); - const string fileName0 = get<0>(fileParam), fileName1 = get<1>(fileParam); - Mat frame0 = imread(getDataPath(fileName0), openMode); - Mat frame1 = imread(getDataPath(fileName1), openMode); + const int pointsCount = GetParam(); + + const string fileName0 = "gpu/opticalflow/rubberwhale1.png", + fileName1 = "gpu/opticalflow/rubberwhale2.png"; + Mat frame0 = imread(getDataPath(fileName0), cv::IMREAD_GRAYSCALE); + Mat frame1 = imread(getDataPath(fileName1), cv::IMREAD_GRAYSCALE); declare.in(frame0, frame1); ASSERT_FALSE(frame0.empty()) << "can't load " << fileName0; ASSERT_FALSE(frame1.empty()) << "can't load " << fileName1; - Mat grayFrame; - if (openMode == IMREAD_COLOR) - cvtColor(frame0, grayFrame, COLOR_BGR2GRAY); - else - grayFrame = frame0; - vector pts, nextPts; vector status; vector err; - goodFeaturesToTrack(grayFrame, pts, pointsCount, 0.01, 0.0); + goodFeaturesToTrack(frame0, pts, pointsCount, 0.01, 0.0); Mat ptsMat(1, static_cast(pts.size()), CV_32FC2, (void *)&pts[0]); if (RUN_PLAIN_IMPL) @@ -178,12 +151,11 @@ CV_ENUM(farneFlagType, 0, OPTFLOW_FARNEBACK_GAUSSIAN) typedef tuple, farneFlagType, bool> FarnebackOpticalFlowParams; typedef TestBaseWithParam FarnebackOpticalFlowFixture; -PERF_TEST_P(FarnebackOpticalFlowFixture, FarnebackOpticalFlow, - ::testing::Combine( - ::testing::Values(make_tuple(5, 1.1), - make_tuple(7, 1.5)), - farneFlagType::all(), - ::testing::Bool())) +OCL_PERF_TEST_P(FarnebackOpticalFlowFixture, FarnebackOpticalFlow, + ::testing::Combine( + ::testing::Values(make_tuple(5, 1.1), + make_tuple(7, 1.5)), + farneFlagType::all(), ::testing::Bool())) { Mat frame0 = imread(getDataPath("gpu/opticalflow/rubberwhale1.png"), cv::IMREAD_GRAYSCALE); ASSERT_FALSE(frame0.empty()) << "can't load rubberwhale1.png"; diff --git a/modules/ocl/perf/perf_precomp.hpp b/modules/ocl/perf/perf_precomp.hpp index a8663df99a..5f92be8203 100644 --- a/modules/ocl/perf/perf_precomp.hpp +++ b/modules/ocl/perf/perf_precomp.hpp @@ -70,8 +70,7 @@ #include "opencv2/ocl/ocl.hpp" #include "opencv2/ts/ts.hpp" -using namespace std; -using namespace cv; +// TODO remove it #define OCL_SIZE_1000 Size(1000, 1000) #define OCL_SIZE_2000 Size(2000, 2000) @@ -79,6 +78,19 @@ using namespace cv; #define OCL_TYPICAL_MAT_SIZES ::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000, OCL_SIZE_4000) +using namespace std; +using namespace cv; + +#define OCL_SIZE_1 szVGA +#define OCL_SIZE_2 sz720p +#define OCL_SIZE_3 sz1080p +#define OCL_SIZE_4 sz2160p + +#define OCL_TEST_SIZES ::testing::Values(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3, OCL_SIZE_4) +#define OCL_TEST_TYPES ::testing::Values(CV_8UC1, CV_32FC1, CV_8UC4, CV_32FC4) +#define OCL_TEST_TYPES_14 OCL_TEST_TYPES +#define OCL_TEST_TYPES_134 ::testing::Values(CV_8UC1, CV_32FC1, CV_8UC3, CV_32FC3, CV_8UC4, CV_32FC4) + #define OCL_PERF_ENUM(type, ...) ::testing::Values(type, ## __VA_ARGS__ ) #define IMPL_OCL "ocl" @@ -103,6 +115,31 @@ using namespace cv; CV_TEST_FAIL_NO_IMPL(); #endif +#define OCL_PERF_TEST(fixture, name) \ + class OCL##_##fixture##_##name : \ + public ::perf::TestBase \ + { \ + public: \ + OCL##_##fixture##_##name() { } \ + protected: \ + virtual void PerfTestBody(); \ + }; \ + TEST_F(OCL##_##fixture##_##name, name) { RunPerfTestBody(); } \ + void OCL##_##fixture##_##name::PerfTestBody() + +#define OCL_PERF_TEST_P(fixture, name, params) \ + class OCL##_##fixture##_##name : \ + public fixture \ + { \ + public: \ + OCL##_##fixture##_##name() { } \ + protected: \ + virtual void PerfTestBody(); \ + }; \ + TEST_P(OCL##_##fixture##_##name, name) { RunPerfTestBody(); } \ + INSTANTIATE_TEST_CASE_P(/*none*/, OCL##_##fixture##_##name, params); \ + void OCL##_##fixture##_##name::PerfTestBody() + #define OCL_TEST_CYCLE_N(n) for(declare.iterations(n); startTimer(), next(); cv::ocl::finish(), stopTimer()) #define OCL_TEST_CYCLE() for(; startTimer(), next(); cv::ocl::finish(), stopTimer()) #define OCL_TEST_CYCLE_MULTIRUN(runsNum) for(declare.runs(runsNum); startTimer(), next(); stopTimer()) for(int r = 0; r < runsNum; cv::ocl::finish(), ++r) diff --git a/modules/ocl/perf/perf_pyramid.cpp b/modules/ocl/perf/perf_pyramid.cpp index 820dd60620..b4ca253565 100644 --- a/modules/ocl/perf/perf_pyramid.cpp +++ b/modules/ocl/perf/perf_pyramid.cpp @@ -51,11 +51,10 @@ using std::tr1::get; ///////////// pyrDown ////////////////////// -typedef Size_MatType pyrDownFixture; +typedef Size_MatType PyrDownFixture; -PERF_TEST_P(pyrDownFixture, pyrDown, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(PyrDownFixture, PyrDown, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -77,7 +76,7 @@ PERF_TEST_P(pyrDownFixture, pyrDown, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-4); } else if (RUN_PLAIN_IMPL) { @@ -91,11 +90,10 @@ PERF_TEST_P(pyrDownFixture, pyrDown, ///////////// pyrUp //////////////////////// -typedef Size_MatType pyrUpFixture; +typedef Size_MatType PyrUpFixture; -PERF_TEST_P(pyrUpFixture, pyrUp, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_8UC4))) +OCL_PERF_TEST_P(PyrUpFixture, PyrUp, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) { const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); @@ -117,7 +115,7 @@ PERF_TEST_P(pyrUpFixture, pyrUp, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 5e-4); } else if (RUN_PLAIN_IMPL) { diff --git a/modules/ocl/perf/perf_split_merge.cpp b/modules/ocl/perf/perf_split_merge.cpp index ecfc49e33d..ab3f82f583 100644 --- a/modules/ocl/perf/perf_split_merge.cpp +++ b/modules/ocl/perf/perf_split_merge.cpp @@ -51,21 +51,22 @@ using std::tr1::get; ///////////// Merge//////////////////////// -typedef Size_MatType MergeFixture; +typedef tuple MergeParams; +typedef TestBaseWithParam MergeFixture; -PERF_TEST_P(MergeFixture, Merge, - ::testing::Combine(::testing::Values(OCL_SIZE_1000, OCL_SIZE_2000), - OCL_PERF_ENUM(CV_8U, CV_32F))) +OCL_PERF_TEST_P(MergeFixture, Merge, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8U, CV_32F), + OCL_PERF_ENUM(2, 3))) { - const Size_MatType_t params = GetParam(); + const MergeParams params = GetParam(); const Size srcSize = get<0>(params); - const int depth = get<1>(params), channels = 3; - const int dstType = CV_MAKE_TYPE(depth, channels); + const int depth = get<1>(params), cn = get<2>(params), + dtype = CV_MAKE_TYPE(depth, cn); - checkDeviceMaxMemoryAllocSize(srcSize, dstType); + checkDeviceMaxMemoryAllocSize(srcSize, dtype); - Mat dst(srcSize, dstType); - vector src(channels); + Mat dst(srcSize, dtype); + vector src(cn); for (vector::iterator i = src.begin(), end = src.end(); i != end; ++i) { i->create(srcSize, CV_MAKE_TYPE(depth, 1)); @@ -75,7 +76,7 @@ PERF_TEST_P(MergeFixture, Merge, if (RUN_OCL_IMPL) { - ocl::oclMat oclDst(srcSize, dstType); + ocl::oclMat oclDst(srcSize, dtype); vector oclSrc(src.size()); for (vector::size_type i = 0, end = src.size(); i < end; ++i) oclSrc[i] = src[i]; @@ -98,49 +99,69 @@ PERF_TEST_P(MergeFixture, Merge, ///////////// Split//////////////////////// -typedef Size_MatType SplitFixture; +typedef MergeParams SplitParams; +typedef TestBaseWithParam SplitFixture; -PERF_TEST_P(SplitFixture, Split, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8U, CV_32F))) +OCL_PERF_TEST_P(SplitFixture, Split, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8U, CV_32F), + OCL_PERF_ENUM(2, 3))) { - const Size_MatType_t params = GetParam(); + const SplitParams params = GetParam(); const Size srcSize = get<0>(params); - const int depth = get<1>(params), channels = 3; - const int type = CV_MAKE_TYPE(depth, channels); + const int depth = get<1>(params), cn = get<2>(params); + const int type = CV_MAKE_TYPE(depth, cn); checkDeviceMaxMemoryAllocSize(srcSize, type); Mat src(srcSize, type); + Mat dst0, dst1, dst2; declare.in(src, WARMUP_RNG); + ASSERT_TRUE(cn == 3 || cn == 2); + if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src); - vector oclDst(channels, ocl::oclMat(srcSize, CV_MAKE_TYPE(depth, 1))); + vector oclDst(cn); + oclDst[0] = ocl::oclMat(srcSize, depth); + oclDst[1] = ocl::oclMat(srcSize, depth); + if (cn == 3) + oclDst[2] = ocl::oclMat(srcSize, depth); OCL_TEST_CYCLE() cv::ocl::split(oclSrc, oclDst); - ASSERT_EQ(3, channels); - Mat dst0, dst1, dst2; oclDst[0].download(dst0); oclDst[1].download(dst1); - oclDst[2].download(dst2); - SANITY_CHECK(dst0); - SANITY_CHECK(dst1); - SANITY_CHECK(dst2); + if (cn == 3) + oclDst[2].download(dst2); } else if (RUN_PLAIN_IMPL) { - vector dst(channels, Mat(srcSize, CV_MAKE_TYPE(depth, 1))); + vector dst(cn); + dst[0] = Mat(srcSize, depth); + dst[1] = Mat(srcSize, depth); + if (cn == 3) + dst[2] = Mat(srcSize, depth); + TEST_CYCLE() cv::split(src, dst); - ASSERT_EQ(3, channels); - Mat & dst0 = dst[0], & dst1 = dst[1], & dst2 = dst[2]; + dst0 = dst[0]; + dst1 = dst[1]; + if (cn == 3) + dst2 = dst[2]; + } + else + OCL_PERF_ELSE + + if (cn == 2) + { + SANITY_CHECK(dst0); + SANITY_CHECK(dst1); + } + else if (cn == 3) + { SANITY_CHECK(dst0); SANITY_CHECK(dst1); SANITY_CHECK(dst2); } - else - OCL_PERF_ELSE } From fbc69e444b67b1643f62be065ca7b1e777abfed6 Mon Sep 17 00:00:00 2001 From: Cody Rigney Date: Mon, 24 Feb 2014 14:15:21 -0500 Subject: [PATCH 162/293] Added NEON optimizations for LK optical flow (Intrinsics). --- modules/video/src/lkpyramid.cpp | 252 ++++++++++++++++++++++++++++++++ modules/video/src/precomp.hpp | 1 + 2 files changed, 253 insertions(+) diff --git a/modules/video/src/lkpyramid.cpp b/modules/video/src/lkpyramid.cpp index 291cb86a26..e807db2d1d 100644 --- a/modules/video/src/lkpyramid.cpp +++ b/modules/video/src/lkpyramid.cpp @@ -87,6 +87,30 @@ static void calcSharrDeriv(const cv::Mat& src, cv::Mat& dst) _mm_store_si128((__m128i*)(trow1 + x), t1); } #endif + +#if CV_NEON + + const uint16x8_t q8 = vdupq_n_u16(3); + const uint8x8_t d18 = vdup_n_u8(10); + + const int16x8_t q8i = vdupq_n_s16(3); + const int16x8_t q9 = vdupq_n_s16(10); + + for( ; x <= colsn - 8; x += 8) + { + uint8x8_t d0 = vld1_u8((const uint8_t*)&srow0[x]); + uint8x8_t d1 = vld1_u8((const uint8_t*)&srow1[x]); + uint8x8_t d2 = vld1_u8((const uint8_t*)&srow2[x]); + uint16x8_t q4 = vaddl_u8(d0, d2); + uint16x8_t q11 = vsubl_u8(d2, d0); + uint16x8_t q5 = vmulq_u16(q4, q8); + uint16x8_t q6 = vmull_u8(d1, d18); + uint16x8_t q10 = vaddq_u16(q6, q5); + vst1q_u16((uint16_t*)&trow0[x], q10); + vst1q_u16((uint16_t*)&trow1[x], q11); + + } +#endif for( ; x < colsn; x++ ) { int t0 = (srow0[x] + srow2[x])*3 + srow1[x]*10; @@ -123,6 +147,35 @@ static void calcSharrDeriv(const cv::Mat& src, cv::Mat& dst) _mm_storeu_si128((__m128i*)(drow + x*2 + 8), t0); } #endif + +#if CV_NEON + for( ; x <= colsn - 8; x += 8 ) + { + + int16x8_t q0 = vld1q_s16((const int16_t*)&trow0[x+cn]); + int16x8_t q1 = vld1q_s16((const int16_t*)&trow0[x-cn]); + int16x8_t q2 = vld1q_s16((const int16_t*)&trow1[x+cn]); + int16x8_t q3 = vld1q_s16((const int16_t*)&trow1[x-cn]); + int16x8_t q5 = vsubq_s16(q0, q1); + int16x8_t q6 = vaddq_s16(q2, q3); + int16x8_t q4 = vld1q_s16((const int16_t*)&trow1[x]); + int16x8_t q7 = vmulq_s16(q6, q8i); + int16x8_t q10 = vmulq_s16(q4, q9); + int16x8_t q11 = vaddq_s16(q7, q10); + int16x4_t d22 = vget_low_s16(q11); + int16x4_t d23 = vget_high_s16(q11); + int16x4_t d11 = vget_high_s16(q5); + int16x4_t d10 = vget_low_s16(q5); + int16x4x2_t q5x2, q11x2; + q5x2.val[0] = d10; q5x2.val[1] = d22; + q11x2.val[0] = d11; q11x2.val[1] = d23; + vst2_s16((int16_t*)&drow[x*2], q5x2); + vst2_s16((int16_t*)&drow[(x*2)+8], q11x2); + + } + +#endif + for( ; x < colsn; x++ ) { deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]); @@ -226,6 +279,21 @@ void cv::detail::LKTrackerInvoker::operator()(const Range& range) const __m128 qA11 = _mm_setzero_ps(), qA12 = _mm_setzero_ps(), qA22 = _mm_setzero_ps(); #endif +#if CV_NEON + + int CV_DECL_ALIGNED(16) nA11[] = {0, 0, 0, 0}, nA12[] = {0, 0, 0, 0}, nA22[] = {0, 0, 0, 0}; + const int shifter1 = -(W_BITS - 5); //negative so it shifts right + const int shifter2 = -(W_BITS); + + const int16x4_t d26 = vdup_n_s16((int16_t)iw00); + const int16x4_t d27 = vdup_n_s16((int16_t)iw01); + const int16x4_t d28 = vdup_n_s16((int16_t)iw10); + const int16x4_t d29 = vdup_n_s16((int16_t)iw11); + const int32x4_t q11 = vdupq_n_s32((int32_t)shifter1); + const int32x4_t q12 = vdupq_n_s32((int32_t)shifter2); + +#endif + // extract the patch from the first image, compute covariation matrix of derivatives int x, y; for( y = 0; y < winSize.height; y++ ) @@ -279,6 +347,92 @@ void cv::detail::LKTrackerInvoker::operator()(const Range& range) const } #endif +#if CV_NEON + + for( ; x <= winSize.width*cn - 4; x += 4, dsrc += 4*2, dIptr += 4*2 ) + { + + uint8x8_t d0 = vld1_u8(&src[x]); + uint8x8_t d2 = vld1_u8(&src[x+cn]); + uint16x8_t q0 = vmovl_u8(d0); + uint16x8_t q1 = vmovl_u8(d2); + + int32x4_t q5 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q0)), d26); + int32x4_t q6 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q1)), d27); + + uint8x8_t d4 = vld1_u8(&src[x + stepI]); + uint8x8_t d6 = vld1_u8(&src[x + stepI + cn]); + uint16x8_t q2 = vmovl_u8(d4); + uint16x8_t q3 = vmovl_u8(d6); + + int32x4_t q7 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q2)), d28); + int32x4_t q8 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q3)), d29); + + q5 = vaddq_s32(q5, q6); + q7 = vaddq_s32(q7, q8); + q5 = vaddq_s32(q5, q7); + + int16x4x2_t d0d1 = vld2_s16(dsrc); + int16x4x2_t d2d3 = vld2_s16(&dsrc[cn2]); + + q5 = vqrshlq_s32(q5, q11); + + int32x4_t q4 = vmull_s16(d0d1.val[0], d26); + q6 = vmull_s16(d0d1.val[1], d26); + + int16x4_t nd0 = vmovn_s32(q5); + + q7 = vmull_s16(d2d3.val[0], d27); + q8 = vmull_s16(d2d3.val[1], d27); + + vst1_s16(&Iptr[x], nd0); + + int16x4x2_t d4d5 = vld2_s16(&dsrc[dstep]); + int16x4x2_t d6d7 = vld2_s16(&dsrc[dstep+cn2]); + + q4 = vaddq_s32(q4, q7); + q6 = vaddq_s32(q6, q8); + + q7 = vmull_s16(d4d5.val[0], d28); + int32x4_t nq0 = vmull_s16(d4d5.val[1], d28); + q8 = vmull_s16(d6d7.val[0], d29); + int32x4_t q15 = vmull_s16(d6d7.val[1], d29); + + q7 = vaddq_s32(q7, q8); + nq0 = vaddq_s32(nq0, q15); + + q4 = vaddq_s32(q4, q7); + q6 = vaddq_s32(q6, nq0); + + int32x4_t nq1 = vld1q_s32(nA12); + int32x4_t nq2 = vld1q_s32(nA22); + nq0 = vld1q_s32(nA11); + + q4 = vqrshlq_s32(q4, q12); + q6 = vqrshlq_s32(q6, q12); + + q7 = vmulq_s32(q4, q4); + q8 = vmulq_s32(q4, q6); + q15 = vmulq_s32(q6, q6); + + nq0 = vaddq_s32(nq0, q7); + nq1 = vaddq_s32(nq1, q8); + nq2 = vaddq_s32(nq2, q15); + + vst1q_s32(nA11, nq0); + vst1q_s32(nA12, nq1); + vst1q_s32(nA22, nq2); + + int16x4_t d8 = vmovn_s32(q4); + int16x4_t d12 = vmovn_s32(q6); + + int16x4x2_t d8d12; + d8d12.val[0] = d8; d8d12.val[1] = d12; + vst2_s16(dIptr, d8d12); + } + +#endif + for( ; x < winSize.width*cn; x++, dsrc += 2, dIptr += 2 ) { int ival = CV_DESCALE(src[x]*iw00 + src[x+cn]*iw01 + @@ -308,6 +462,12 @@ void cv::detail::LKTrackerInvoker::operator()(const Range& range) const A22 += A22buf[0] + A22buf[1] + A22buf[2] + A22buf[3]; #endif +#if CV_NEON + A11 += (float)(nA11[0] + nA11[1] + nA11[2] + nA11[3]); + A12 += (float)(nA12[0] + nA12[1] + nA12[2] + nA12[3]); + A22 += (float)(nA22[0] + nA22[1] + nA22[2] + nA22[3]); +#endif + A11 *= FLT_SCALE; A12 *= FLT_SCALE; A22 *= FLT_SCALE; @@ -357,6 +517,17 @@ void cv::detail::LKTrackerInvoker::operator()(const Range& range) const __m128 qb0 = _mm_setzero_ps(), qb1 = _mm_setzero_ps(); #endif +#if CV_NEON + + int CV_DECL_ALIGNED(16) nB1[] = {0,0,0,0}, nB2[] = {0,0,0,0}; + + const int16x4_t d26_2 = vdup_n_s16((int16_t)iw00); + const int16x4_t d27_2 = vdup_n_s16((int16_t)iw01); + const int16x4_t d28_2 = vdup_n_s16((int16_t)iw10); + const int16x4_t d29_2 = vdup_n_s16((int16_t)iw11); + +#endif + for( y = 0; y < winSize.height; y++ ) { const uchar* Jptr = (const uchar*)J.data + (y + inextPt.y)*stepJ + inextPt.x*cn; @@ -400,6 +571,80 @@ void cv::detail::LKTrackerInvoker::operator()(const Range& range) const } #endif +#if CV_NEON + + for( ; x <= winSize.width*cn - 8; x += 8, dIptr += 8*2 ) + { + + uint8x8_t d0 = vld1_u8(&Jptr[x]); + uint8x8_t d2 = vld1_u8(&Jptr[x+cn]); + uint8x8_t d4 = vld1_u8(&Jptr[x+stepJ]); + uint8x8_t d6 = vld1_u8(&Jptr[x+stepJ+cn]); + + uint16x8_t q0 = vmovl_u8(d0); + uint16x8_t q1 = vmovl_u8(d2); + uint16x8_t q2 = vmovl_u8(d4); + uint16x8_t q3 = vmovl_u8(d6); + + int32x4_t nq4 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q0)), d26_2); + int32x4_t nq5 = vmull_s16(vget_high_s16(vreinterpretq_s16_u16(q0)), d26_2); + + int32x4_t nq6 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q1)), d27_2); + int32x4_t nq7 = vmull_s16(vget_high_s16(vreinterpretq_s16_u16(q1)), d27_2); + + int32x4_t nq8 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q2)), d28_2); + int32x4_t nq9 = vmull_s16(vget_high_s16(vreinterpretq_s16_u16(q2)), d28_2); + + int32x4_t nq10 = vmull_s16(vget_low_s16(vreinterpretq_s16_u16(q3)), d29_2); + int32x4_t nq11 = vmull_s16(vget_high_s16(vreinterpretq_s16_u16(q3)), d29_2); + + nq4 = vaddq_s32(nq4, nq6); + nq5 = vaddq_s32(nq5, nq7); + nq8 = vaddq_s32(nq8, nq10); + nq9 = vaddq_s32(nq9, nq11); + + int16x8_t q6 = vld1q_s16(&Iptr[x]); + + nq4 = vaddq_s32(nq4, nq8); + nq5 = vaddq_s32(nq5, nq9); + + nq8 = vmovl_s16(vget_high_s16(q6)); + nq6 = vmovl_s16(vget_low_s16(q6)); + + nq4 = vqrshlq_s32(nq4, q11); + nq5 = vqrshlq_s32(nq5, q11); + + int16x8x2_t q0q1 = vld2q_s16(dIptr); + nq11 = vld1q_s32(nB1); + int32x4_t nq15 = vld1q_s32(nB2); + + nq4 = vsubq_s32(nq4, nq6); + nq5 = vsubq_s32(nq5, nq8); + + int32x4_t nq2 = vmovl_s16(vget_low_s16(q0q1.val[0])); + int32x4_t nq3 = vmovl_s16(vget_high_s16(q0q1.val[0])); + + nq7 = vmovl_s16(vget_low_s16(q0q1.val[1])); + nq8 = vmovl_s16(vget_high_s16(q0q1.val[1])); + + nq9 = vmulq_s32(nq4, nq2); + nq10 = vmulq_s32(nq5, nq3); + + nq4 = vmulq_s32(nq4, nq7); + nq5 = vmulq_s32(nq5, nq8); + + nq9 = vaddq_s32(nq9, nq10); + nq4 = vaddq_s32(nq4, nq5); + + nq11 = vaddq_s32(nq11, nq9); + nq15 = vaddq_s32(nq15, nq4); + + vst1q_s32(nB1, nq11); + vst1q_s32(nB2, nq15); + } + +#endif + for( ; x < winSize.width*cn; x++, dIptr += 2 ) { int diff = CV_DESCALE(Jptr[x]*iw00 + Jptr[x+cn]*iw01 + @@ -417,6 +662,13 @@ void cv::detail::LKTrackerInvoker::operator()(const Range& range) const b2 += bbuf[1] + bbuf[3]; #endif +#if CV_NEON + + b1 += (float)(nB1[0] + nB1[1] + nB1[2] + nB1[3]); + b2 += (float)(nB2[0] + nB2[1] + nB2[2] + nB2[3]); + +#endif + b1 *= FLT_SCALE; b2 *= FLT_SCALE; diff --git a/modules/video/src/precomp.hpp b/modules/video/src/precomp.hpp index 9b6077f1e5..ced9a37619 100644 --- a/modules/video/src/precomp.hpp +++ b/modules/video/src/precomp.hpp @@ -49,6 +49,7 @@ #include "opencv2/video/background_segm.hpp" #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/core/internal.hpp" +#include "opencv2/core/core.hpp" #include From 767b28f2e3bf75883edaf79e5a4de5566a1926e2 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sat, 1 Mar 2014 13:13:24 +0400 Subject: [PATCH 163/293] tests --- modules/ocl/perf/perf_arithm.cpp | 194 +++--------- modules/ocl/perf/perf_brute_force_matcher.cpp | 33 ++- modules/ocl/perf/perf_fft.cpp | 3 - modules/ocl/perf/perf_gemm.cpp | 12 +- modules/ocl/perf/perf_hog.cpp | 2 +- modules/ocl/perf/perf_imgproc.cpp | 20 +- modules/ocl/perf/perf_imgwarp.cpp | 3 - modules/ocl/perf/perf_kalman.cpp | 4 +- modules/ocl/perf/perf_match_template.cpp | 3 +- modules/ocl/perf/perf_ml.cpp | 14 +- modules/ocl/perf/perf_norm.cpp | 89 ------ modules/ocl/perf/perf_opticalflow.cpp | 7 +- modules/ocl/perf/perf_stat.cpp | 276 ++++++++++++++++++ 13 files changed, 364 insertions(+), 296 deletions(-) delete mode 100644 modules/ocl/perf/perf_norm.cpp create mode 100644 modules/ocl/perf/perf_stat.cpp diff --git a/modules/ocl/perf/perf_arithm.cpp b/modules/ocl/perf/perf_arithm.cpp index 7c194ae169..3faedfcc7f 100644 --- a/modules/ocl/perf/perf_arithm.cpp +++ b/modules/ocl/perf/perf_arithm.cpp @@ -60,15 +60,14 @@ OCL_PERF_TEST_P(LUTFixture, LUT, // getting params const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); - const int type = get<1>(params); + const int type = get<1>(params), cn = CV_MAT_CN(type); // creating src data - Mat src(srcSize, CV_8UC1), lut(1, 256, type); + Mat src(srcSize, CV_8UC(cn)), lut(1, 256, type); int dstType = CV_MAKETYPE(lut.depth(), src.channels()); Mat dst(srcSize, dstType); - randu(lut, 0, 2); - declare.in(src, WARMUP_RNG).in(lut).out(dst); + declare.in(src, lut, WARMUP_RNG).out(dst); // select implementation if (RUN_OCL_IMPL) @@ -564,158 +563,6 @@ OCL_PERF_TEST_P(FlipFixture, Flip, OCL_PERF_ELSE } -///////////// MinMax //////////////////////// - -typedef Size_MatType MinMaxFixture; - -PERF_TEST_P(MinMaxFixture, MinMax, - ::testing::Combine(OCL_TYPICAL_MAT_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) -{ - const Size_MatType_t params = GetParam(); - const Size srcSize = get<0>(params); - const int type = get<1>(params); - - Mat src(srcSize, type); - declare.in(src, WARMUP_RNG); - - double min_val = std::numeric_limits::max(), - max_val = std::numeric_limits::min(); - - if (RUN_OCL_IMPL) - { - ocl::oclMat oclSrc(src); - - OCL_TEST_CYCLE() cv::ocl::minMax(oclSrc, &min_val, &max_val); - - ASSERT_GE(max_val, min_val); - SANITY_CHECK(min_val); - SANITY_CHECK(max_val); - } - else if (RUN_PLAIN_IMPL) - { - Point min_loc, max_loc; - - TEST_CYCLE() cv::minMaxLoc(src, &min_val, &max_val, &min_loc, &max_loc); - - ASSERT_GE(max_val, min_val); - SANITY_CHECK(min_val); - SANITY_CHECK(max_val); - } - else - OCL_PERF_ELSE -} - -///////////// MinMaxLoc //////////////////////// - -typedef Size_MatType MinMaxLocFixture; - -OCL_PERF_TEST_P(MinMaxLocFixture, MinMaxLoc, - ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) -{ - const Size_MatType_t params = GetParam(); - const Size srcSize = get<0>(params); - const int type = get<1>(params); - - Mat src(srcSize, type); - randu(src, 0, 1); - declare.in(src); - - double min_val = 0.0, max_val = 0.0; - Point min_loc, max_loc; - - if (RUN_OCL_IMPL) - { - ocl::oclMat oclSrc(src); - - OCL_TEST_CYCLE() cv::ocl::minMaxLoc(oclSrc, &min_val, &max_val, &min_loc, &max_loc); - - ASSERT_GE(max_val, min_val); - SANITY_CHECK(min_val); - SANITY_CHECK(max_val); - } - else if (RUN_PLAIN_IMPL) - { - TEST_CYCLE() cv::minMaxLoc(src, &min_val, &max_val, &min_loc, &max_loc); - - ASSERT_GE(max_val, min_val); - SANITY_CHECK(min_val); - SANITY_CHECK(max_val); - } - else - OCL_PERF_ELSE -} - -///////////// Sum //////////////////////// - -typedef Size_MatType SumFixture; - -OCL_PERF_TEST_P(SumFixture, Sum, - ::testing::Combine(OCL_TEST_SIZES, - OCL_TEST_TYPES)) -{ - const Size_MatType_t params = GetParam(); - const Size srcSize = get<0>(params); - const int type = get<1>(params); - - Mat src(srcSize, type); - Scalar result; - randu(src, 0, 60); - declare.in(src); - - if (RUN_OCL_IMPL) - { - ocl::oclMat oclSrc(src); - - OCL_TEST_CYCLE() result = cv::ocl::sum(oclSrc); - - SANITY_CHECK(result, 1e-6, ERROR_RELATIVE); - } - else if (RUN_PLAIN_IMPL) - { - TEST_CYCLE() result = cv::sum(src); - - SANITY_CHECK(result, 1e-6, ERROR_RELATIVE); - } - else - OCL_PERF_ELSE -} - -///////////// countNonZero //////////////////////// - -typedef Size_MatType CountNonZeroFixture; - -OCL_PERF_TEST_P(CountNonZeroFixture, CountNonZero, - ::testing::Combine(OCL_TEST_SIZES, - OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) -{ - const Size_MatType_t params = GetParam(); - const Size srcSize = get<0>(params); - const int type = get<1>(params); - - Mat src(srcSize, type); - int result = 0; - randu(src, 0, 256); - declare.in(src); - - if (RUN_OCL_IMPL) - { - ocl::oclMat oclSrc(src); - - OCL_TEST_CYCLE() result = cv::ocl::countNonZero(oclSrc); - - SANITY_CHECK(result); - } - else if (RUN_PLAIN_IMPL) - { - TEST_CYCLE() result = cv::countNonZero(src); - - SANITY_CHECK(result); - } - else - OCL_PERF_ELSE -} - ///////////// Phase //////////////////////// typedef Size_MatType PhaseFixture; @@ -895,6 +742,41 @@ OCL_PERF_TEST_P(BitwiseNotFixture, Bitwise_not, OCL_PERF_ELSE } +///////////// SetIdentity //////////////////////// + +typedef Size_MatType SetIdentityFixture; + +OCL_PERF_TEST_P(SetIdentityFixture, SetIdentity, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) +{ + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + Mat src(srcSize, type); + Scalar s = Scalar::all(17); + declare.in(src, WARMUP_RNG).out(src); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src); + + OCL_TEST_CYCLE() cv::ocl::setIdentity(oclSrc, s); + + oclSrc.download(src); + + SANITY_CHECK(src); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() cv::setIdentity(src, s); + + SANITY_CHECK(src); + } + else + OCL_PERF_ELSE +} + ///////////// compare//////////////////////// CV_ENUM(CmpCode, CMP_LT, CMP_LE, CMP_EQ, CMP_NE, CMP_GE, CMP_GT) diff --git a/modules/ocl/perf/perf_brute_force_matcher.cpp b/modules/ocl/perf/perf_brute_force_matcher.cpp index 54e829f4bc..b8ad81361b 100644 --- a/modules/ocl/perf/perf_brute_force_matcher.cpp +++ b/modules/ocl/perf/perf_brute_force_matcher.cpp @@ -46,17 +46,22 @@ #include "perf_precomp.hpp" using namespace perf; +using std::tr1::get; //////////////////// BruteForceMatch ///////////////// -typedef TestBaseWithParam BruteForceMatcherFixture; +typedef Size_MatType BruteForceMatcherFixture; -OCL_PERF_TEST_P(BruteForceMatcherFixture, Match, OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3)) +OCL_PERF_TEST_P(BruteForceMatcherFixture, Match, + ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), + OCL_PERF_ENUM(MatType(CV_32FC1)))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); vector matches; - Mat query(srcSize, CV_32FC1), train(srcSize, CV_32FC1); + Mat query(srcSize, type), train(srcSize, type); declare.in(query, train); randu(query, 0.0f, 1.0f); randu(train, 0.0f, 1.0f); @@ -82,12 +87,16 @@ OCL_PERF_TEST_P(BruteForceMatcherFixture, Match, OCL_PERF_ENUM(OCL_SIZE_1, OCL_S OCL_PERF_ELSE } -OCL_PERF_TEST_P(BruteForceMatcherFixture, KnnMatch, OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3)) +OCL_PERF_TEST_P(BruteForceMatcherFixture, KnnMatch, + ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), + OCL_PERF_ENUM(MatType(CV_32FC1)))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); vector > matches(2); - Mat query(srcSize, CV_32F), train(srcSize, CV_32F); + Mat query(srcSize, type), train(srcSize, type); randu(query, 0.0f, 1.0f); randu(train, 0.0f, 1.0f); @@ -121,13 +130,17 @@ OCL_PERF_TEST_P(BruteForceMatcherFixture, KnnMatch, OCL_PERF_ENUM(OCL_SIZE_1, OC OCL_PERF_ELSE } -OCL_PERF_TEST_P(BruteForceMatcherFixture, RadiusMatch, OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3)) +OCL_PERF_TEST_P(BruteForceMatcherFixture, RadiusMatch, + ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), + OCL_PERF_ENUM(MatType(CV_32FC1)))) { - const Size srcSize = GetParam(); + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); const float max_distance = 2.0f; vector > matches(2); - Mat query(srcSize, CV_32FC1), train(srcSize, CV_32FC1); + Mat query(srcSize, type), train(srcSize, type); declare.in(query, train); randu(query, 0.0f, 1.0f); diff --git a/modules/ocl/perf/perf_fft.cpp b/modules/ocl/perf/perf_fft.cpp index 29e042ccde..95701b7dab 100644 --- a/modules/ocl/perf/perf_fft.cpp +++ b/modules/ocl/perf/perf_fft.cpp @@ -71,9 +71,6 @@ OCL_PERF_TEST_P(DftFixture, Dft, ::testing::Combine(testing::Values(OCL_SIZE_1, randu(src, 0.0f, 1.0f); declare.in(src); - if (srcSize == OCL_SIZE_4000) - declare.time(7.4); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst; diff --git a/modules/ocl/perf/perf_gemm.cpp b/modules/ocl/perf/perf_gemm.cpp index 32492ad978..69db3a099f 100644 --- a/modules/ocl/perf/perf_gemm.cpp +++ b/modules/ocl/perf/perf_gemm.cpp @@ -47,28 +47,32 @@ using namespace perf; using std::tr1::get; +using std::tr1::tuple; ///////////// gemm //////////////////////// -typedef Size_MatType GemmFixture; - #ifdef HAVE_CLAMDBLAS +typedef tuple GemmParams; +typedef TestBaseWithParam GemmFixture; + OCL_PERF_TEST_P(GemmFixture, Gemm, ::testing::Combine( ::testing::Values(Size(1000, 1000), Size(1500, 1500)), ::testing::Values((int)cv::GEMM_1_T, (int)cv::GEMM_1_T | (int)cv::GEMM_2_T))) { - const Size_MatType_t params = GetParam(); + const GemmParams params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params); Mat src1(srcSize, CV_32FC1), src2(srcSize, CV_32FC1), src3(srcSize, CV_32FC1), dst(srcSize, CV_32FC1); - declare.in(src1, src2, src3).out(dst).time(srcSize == OCL_SIZE_2000 ? 65 : 8); + randu(src1, -10.0f, 10.0f); randu(src2, -10.0f, 10.0f); randu(src3, -10.0f, 10.0f); + declare.in(src1, src2, src3).out(dst); + if (RUN_OCL_IMPL) { ocl::oclMat oclSrc1(src1), oclSrc2(src2), diff --git a/modules/ocl/perf/perf_hog.cpp b/modules/ocl/perf/perf_hog.cpp index a65769a26c..497b73d693 100644 --- a/modules/ocl/perf/perf_hog.cpp +++ b/modules/ocl/perf/perf_hog.cpp @@ -74,7 +74,7 @@ OCL_PERF_TEST(HOGFixture, HOG) ASSERT_TRUE(!src.empty()) << "can't open input image road.png"; vector found_locations; - declare.in(src).time(5); + declare.in(src); if (RUN_PLAIN_IMPL) { diff --git a/modules/ocl/perf/perf_imgproc.cpp b/modules/ocl/perf/perf_imgproc.cpp index 85f2c5528f..e6f6a0582e 100644 --- a/modules/ocl/perf/perf_imgproc.cpp +++ b/modules/ocl/perf/perf_imgproc.cpp @@ -133,8 +133,7 @@ OCL_PERF_TEST_P(CornerMinEigenValFixture, CornerMinEigenVal, const int blockSize = 7, apertureSize = 1 + 2 * 3; Mat src(srcSize, type), dst(srcSize, CV_32FC1); - declare.in(src, WARMUP_RNG).out(dst) - .time(srcSize == OCL_SIZE_4000 ? 20 : srcSize == OCL_SIZE_2000 ? 5 : 3); + declare.in(src, WARMUP_RNG).out(dst); const int depth = CV_MAT_DEPTH(type); const ERROR_TYPE errorType = depth == CV_8U ? ERROR_ABSOLUTE : ERROR_RELATIVE; @@ -172,8 +171,7 @@ OCL_PERF_TEST_P(CornerHarrisFixture, CornerHarris, Mat src(srcSize, type), dst(srcSize, CV_32FC1); randu(src, 0, 1); - declare.in(src).out(dst) - .time(srcSize == OCL_SIZE_4000 ? 20 : srcSize == OCL_SIZE_2000 ? 5 : 3); + declare.in(src).out(dst); if (RUN_OCL_IMPL) { @@ -469,9 +467,7 @@ PERF_TEST_P(MeanShiftFilteringFixture, MeanShiftFiltering, cv::TermCriteria crit(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 5, 1); Mat src(srcSize, CV_8UC4), dst(srcSize, CV_8UC4); - declare.in(src, WARMUP_RNG).out(dst) - .time(srcSize == OCL_SIZE_4000 ? - 56 : srcSize == OCL_SIZE_2000 ? 15 : 3.8); + declare.in(src, WARMUP_RNG).out(dst); if (RUN_PLAIN_IMPL) { @@ -562,9 +558,7 @@ PERF_TEST_P(MeanShiftProcFixture, MeanShiftProc, Mat src(srcSize, CV_8UC4), dst1(srcSize, CV_8UC4), dst2(srcSize, CV_16SC2); - declare.in(src, WARMUP_RNG).out(dst1, dst2) - .time(srcSize == OCL_SIZE_4000 ? - 56 : srcSize == OCL_SIZE_2000 ? 15 : 3.8);; + declare.in(src, WARMUP_RNG).out(dst1, dst2); if (RUN_PLAIN_IMPL) { @@ -603,9 +597,6 @@ OCL_PERF_TEST_P(CLAHEFixture, CLAHE, OCL_TEST_SIZES) const double clipLimit = 40.0; declare.in(src, WARMUP_RNG); - if (srcSize == OCL_SIZE_4000) - declare.time(11); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst; @@ -649,9 +640,6 @@ PERF_TEST_P(ColumnSumFixture, ColumnSum, OCL_TYPICAL_MAT_SIZES) Mat src(srcSize, CV_32FC1), dst(srcSize, CV_32FC1); declare.in(src, WARMUP_RNG).out(dst); - if (srcSize == OCL_SIZE_4000) - declare.time(5); - if (RUN_OCL_IMPL) { ocl::oclMat oclSrc(src), oclDst(srcSize, CV_32FC1); diff --git a/modules/ocl/perf/perf_imgwarp.cpp b/modules/ocl/perf/perf_imgwarp.cpp index fe863fa701..56d55c03c6 100644 --- a/modules/ocl/perf/perf_imgwarp.cpp +++ b/modules/ocl/perf/perf_imgwarp.cpp @@ -235,9 +235,6 @@ OCL_PERF_TEST_P(RemapFixture, Remap, Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); - if (srcSize == OCL_SIZE_4000 && interpolation == INTER_LINEAR) - declare.time(9); - Mat xmap, ymap; xmap.create(srcSize, CV_32FC1); ymap.create(srcSize, CV_32FC1); diff --git a/modules/ocl/perf/perf_kalman.cpp b/modules/ocl/perf/perf_kalman.cpp index 946444ad9f..e384fcd696 100644 --- a/modules/ocl/perf/perf_kalman.cpp +++ b/modules/ocl/perf/perf_kalman.cpp @@ -46,7 +46,7 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CLAMDBLAS +//#ifdef HAVE_CLAMDBLAS using namespace perf; using namespace std; @@ -100,4 +100,4 @@ PERF_TEST_P(KalmanFilterFixture, KalmanFilter, SANITY_CHECK(statePre_); } -#endif // HAVE_CLAMDBLAS +//#endif // HAVE_CLAMDBLAS diff --git a/modules/ocl/perf/perf_match_template.cpp b/modules/ocl/perf/perf_match_template.cpp index 236ae17509..ae8c55719c 100644 --- a/modules/ocl/perf/perf_match_template.cpp +++ b/modules/ocl/perf/perf_match_template.cpp @@ -99,8 +99,7 @@ OCL_PERF_TEST_P(CV_TM_CCORR_NORMEDFixture, matchTemplate, Mat src(srcSize, CV_8UC1), templ(templSize, CV_8UC1), dst; const Size dstSize(src.cols - templ.cols + 1, src.rows - templ.rows + 1); dst.create(dstSize, CV_8UC1); - declare.in(src, templ, WARMUP_RNG).out(dst) - .time(srcSize == OCL_SIZE_2000 ? 10 : srcSize == OCL_SIZE_4000 ? 23 : 2); + declare.in(src, templ, WARMUP_RNG).out(dst); if (RUN_OCL_IMPL) { diff --git a/modules/ocl/perf/perf_ml.cpp b/modules/ocl/perf/perf_ml.cpp index 13dcaa18f0..680045673b 100644 --- a/modules/ocl/perf/perf_ml.cpp +++ b/modules/ocl/perf/perf_ml.cpp @@ -55,7 +55,7 @@ static void genData(Mat& trainData, Size size, Mat& trainLabel = Mat().setTo(Sca trainData.create(size, CV_32FC1); randu(trainData, 1.0, 100.0); - if(nClasses != 0) + if (nClasses != 0) { trainLabel.create(size.height, 1, CV_8UC1); randu(trainLabel, 0, nClasses - 1); @@ -82,7 +82,7 @@ PERF_TEST_P(KNNFixture, KNN, genData(testData, size); Mat best_label; - if(RUN_PLAIN_IMPL) + if (RUN_PLAIN_IMPL) { TEST_CYCLE() { @@ -90,7 +90,8 @@ PERF_TEST_P(KNNFixture, KNN, knn_cpu.train(trainData, trainLabels); knn_cpu.find_nearest(testData, k, &best_label); } - }else if(RUN_OCL_IMPL) + } + else if (RUN_OCL_IMPL) { cv::ocl::oclMat best_label_ocl; cv::ocl::oclMat testdata; @@ -103,7 +104,8 @@ PERF_TEST_P(KNNFixture, KNN, knn_ocl.find_nearest(testdata, k, best_label_ocl); } best_label_ocl.download(best_label); - }else + } + else OCL_PERF_ELSE SANITY_CHECK(best_label); } @@ -188,7 +190,7 @@ PERF_TEST_P(SVMFixture, DISABLED_SVM, CvMat samples_ = samples; CvMat results_ = results; - if(RUN_PLAIN_IMPL) + if (RUN_PLAIN_IMPL) { CvSVM svm; svm.train(trainData, labels, Mat(), Mat(), params); @@ -197,7 +199,7 @@ PERF_TEST_P(SVMFixture, DISABLED_SVM, svm.predict(&samples_, &results_); } } - else if(RUN_OCL_IMPL) + else if (RUN_OCL_IMPL) { CvSVM_OCL svm; svm.train(trainData, labels, Mat(), Mat(), params); diff --git a/modules/ocl/perf/perf_norm.cpp b/modules/ocl/perf/perf_norm.cpp deleted file mode 100644 index abeb93cf18..0000000000 --- a/modules/ocl/perf/perf_norm.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Fangfang Bai, fangfang@multicorewareinc.com -// Jin Ma, jin@multicorewareinc.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not 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 Intel Corporation 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. -// -//M*/ -#include "perf_precomp.hpp" - -using namespace perf; -using std::tr1::tuple; -using std::tr1::get; - -///////////// norm//////////////////////// - -CV_ENUM(NormType, NORM_INF, NORM_L1, NORM_L2) - -typedef std::tr1::tuple NormParams; -typedef TestBaseWithParam NormFixture; - -OCL_PERF_TEST_P(NormFixture, Norm, - ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), - OCL_TEST_TYPES, NormType::all())) -{ - const NormParams params = GetParam(); - const Size srcSize = get<0>(params); - const int type = get<1>(params); - const int normType = get<2>(params); - perf::ERROR_TYPE errorType = type != NORM_INF ? ERROR_RELATIVE : ERROR_ABSOLUTE; - double eps = 1e-5, value; - - Mat src1(srcSize, type), src2(srcSize, type); - declare.in(src1, src2, WARMUP_RNG); - - if (RUN_OCL_IMPL) - { - ocl::oclMat oclSrc1(src1), oclSrc2(src2); - - OCL_TEST_CYCLE() value = cv::ocl::norm(oclSrc1, oclSrc2, normType); - - SANITY_CHECK(value, eps, errorType); - } - else if (RUN_PLAIN_IMPL) - { - TEST_CYCLE() value = cv::norm(src1, src2, normType); - - SANITY_CHECK(value, eps, errorType); - } - else - OCL_PERF_ELSE -} diff --git a/modules/ocl/perf/perf_opticalflow.cpp b/modules/ocl/perf/perf_opticalflow.cpp index 3711e87d89..67079f3926 100644 --- a/modules/ocl/perf/perf_opticalflow.cpp +++ b/modules/ocl/perf/perf_opticalflow.cpp @@ -52,13 +52,12 @@ using std::tr1::get; using std::tr1::tuple; using std::tr1::make_tuple; -typedef tuple PyrLKOpticalFlowParamType; -typedef TestBaseWithParam PyrLKOpticalFlowFixture; +typedef TestBaseWithParam > PyrLKOpticalFlowFixture; OCL_PERF_TEST_P(PyrLKOpticalFlowFixture, PyrLKOpticalFlow, ::testing::Values(1000, 2000, 4000)) { - const int pointsCount = GetParam(); + const int pointsCount = get<0>(GetParam()); const string fileName0 = "gpu/opticalflow/rubberwhale1.png", fileName1 = "gpu/opticalflow/rubberwhale2.png"; @@ -109,7 +108,7 @@ PERF_TEST(tvl1flowFixture, tvl1flow) const Size srcSize = frame0.size(); const double eps = 1.2; Mat flow(srcSize, CV_32FC2), flow1(srcSize, CV_32FC1), flow2(srcSize, CV_32FC1); - declare.in(frame0, frame1).out(flow1, flow2).time(159); + declare.in(frame0, frame1).out(flow1, flow2); if (RUN_PLAIN_IMPL) { diff --git a/modules/ocl/perf/perf_stat.cpp b/modules/ocl/perf/perf_stat.cpp new file mode 100644 index 0000000000..a48cacb59a --- /dev/null +++ b/modules/ocl/perf/perf_stat.cpp @@ -0,0 +1,276 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Fangfang Bai, fangfang@multicorewareinc.com +// Jin Ma, jin@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +//M*/ +#include "perf_precomp.hpp" + +using namespace perf; +using std::tr1::tuple; +using std::tr1::get; + + +///////////// MinMax //////////////////////// + +typedef Size_MatType MinMaxFixture; + +PERF_TEST_P(MinMaxFixture, MinMax, + ::testing::Combine(OCL_TYPICAL_MAT_SIZES, + OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +{ + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + Mat src(srcSize, type); + declare.in(src, WARMUP_RNG); + + double min_val = std::numeric_limits::max(), + max_val = std::numeric_limits::min(); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src); + + OCL_TEST_CYCLE() cv::ocl::minMax(oclSrc, &min_val, &max_val); + + ASSERT_GE(max_val, min_val); + SANITY_CHECK(min_val); + SANITY_CHECK(max_val); + } + else if (RUN_PLAIN_IMPL) + { + Point min_loc, max_loc; + + TEST_CYCLE() cv::minMaxLoc(src, &min_val, &max_val, &min_loc, &max_loc); + + ASSERT_GE(max_val, min_val); + SANITY_CHECK(min_val); + SANITY_CHECK(max_val); + } + else + OCL_PERF_ELSE +} + +///////////// MinMaxLoc //////////////////////// + +typedef Size_MatType MinMaxLocFixture; + +OCL_PERF_TEST_P(MinMaxLocFixture, MinMaxLoc, + ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +{ + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + Mat src(srcSize, type); + randu(src, 0, 1); + declare.in(src); + + double min_val = 0.0, max_val = 0.0; + Point min_loc, max_loc; + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src); + + OCL_TEST_CYCLE() cv::ocl::minMaxLoc(oclSrc, &min_val, &max_val, &min_loc, &max_loc); + + ASSERT_GE(max_val, min_val); + SANITY_CHECK(min_val); + SANITY_CHECK(max_val); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() cv::minMaxLoc(src, &min_val, &max_val, &min_loc, &max_loc); + + ASSERT_GE(max_val, min_val); + SANITY_CHECK(min_val); + SANITY_CHECK(max_val); + } + else + OCL_PERF_ELSE +} + +///////////// Sum //////////////////////// + +typedef Size_MatType SumFixture; + +OCL_PERF_TEST_P(SumFixture, Sum, + ::testing::Combine(OCL_TEST_SIZES, + OCL_TEST_TYPES)) +{ + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + Mat src(srcSize, type); + Scalar result; + randu(src, 0, 60); + declare.in(src); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src); + + OCL_TEST_CYCLE() result = cv::ocl::sum(oclSrc); + + SANITY_CHECK(result, 1e-6, ERROR_RELATIVE); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() result = cv::sum(src); + + SANITY_CHECK(result, 1e-6, ERROR_RELATIVE); + } + else + OCL_PERF_ELSE +} + +///////////// countNonZero //////////////////////// + +typedef Size_MatType CountNonZeroFixture; + +OCL_PERF_TEST_P(CountNonZeroFixture, CountNonZero, + ::testing::Combine(OCL_TEST_SIZES, + OCL_PERF_ENUM(CV_8UC1, CV_32FC1))) +{ + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + Mat src(srcSize, type); + int result = 0; + randu(src, 0, 256); + declare.in(src); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src); + + OCL_TEST_CYCLE() result = cv::ocl::countNonZero(oclSrc); + + SANITY_CHECK(result); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() result = cv::countNonZero(src); + + SANITY_CHECK(result); + } + else + OCL_PERF_ELSE +} + +///////////// meanStdDev //////////////////////// + +typedef Size_MatType MeanStdDevFixture; + +OCL_PERF_TEST_P(MeanStdDevFixture, MeanStdDev, + ::testing::Combine(OCL_TEST_SIZES, OCL_TEST_TYPES)) +{ + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + Mat src(srcSize, type); + Scalar mean, stddev; + randu(src, 0, 256); + declare.in(src); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src); + + OCL_TEST_CYCLE() cv::ocl::meanStdDev(oclSrc, mean, stddev); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() cv::meanStdDev(src, mean, stddev); + } + else + OCL_PERF_ELSE + + SANITY_CHECK_NOTHING(); +// SANITY_CHECK(mean, 1e-6, ERROR_RELATIVE); +// SANITY_CHECK(stddev, 1e-6, ERROR_RELATIVE); +} + +///////////// norm//////////////////////// + +CV_ENUM(NormType, NORM_INF, NORM_L1, NORM_L2) + +typedef std::tr1::tuple NormParams; +typedef TestBaseWithParam NormFixture; + +OCL_PERF_TEST_P(NormFixture, Norm, + ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), + OCL_TEST_TYPES, NormType::all())) +{ + const NormParams params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + const int normType = get<2>(params); + perf::ERROR_TYPE errorType = type != NORM_INF ? ERROR_RELATIVE : ERROR_ABSOLUTE; + double eps = 1e-5, value; + + Mat src1(srcSize, type), src2(srcSize, type); + declare.in(src1, src2, WARMUP_RNG); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc1(src1), oclSrc2(src2); + + OCL_TEST_CYCLE() value = cv::ocl::norm(oclSrc1, oclSrc2, normType); + + SANITY_CHECK(value, eps, errorType); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() value = cv::norm(src1, src2, normType); + + SANITY_CHECK(value, eps, errorType); + } + else + OCL_PERF_ELSE +} From eedf86402dcd3e1e4ba1e9868fab4c03ccd2fbbf Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 2 Mar 2014 21:04:17 +0400 Subject: [PATCH 164/293] typos --- doc/haartraining.htm | 2 +- .../introduction/windows_install/windows_install.rst | 2 +- modules/calib3d/test/test_cameracalibration.cpp | 2 +- modules/contrib/src/basicretinafilter.hpp | 4 ++-- modules/contrib/src/magnoretinafilter.hpp | 4 ++-- modules/contrib/src/parvoretinafilter.hpp | 4 ++-- modules/contrib/src/retinacolor.hpp | 4 ++-- modules/core/test/test_ds.cpp | 4 ++-- modules/gpu/include/opencv2/gpu/gpu.hpp | 4 ++-- modules/highgui/src/cap_tyzx.cpp | 2 +- modules/legacy/src/clique.cpp | 4 ++-- modules/legacy/src/epilines.cpp | 2 +- modules/legacy/src/lmeds.cpp | 2 +- modules/legacy/src/trifocal.cpp | 12 ++++++------ modules/nonfree/src/opencl/surf.cl | 2 +- modules/photo/src/denoising.cpp | 2 +- modules/ts/misc/chart.py | 2 +- .../ImageManipulations/common/LayoutAwarePage.cpp | 2 +- 18 files changed, 30 insertions(+), 30 deletions(-) diff --git a/doc/haartraining.htm b/doc/haartraining.htm index c8c3a0e929..9a0767d26f 100644 --- a/doc/haartraining.htm +++ b/doc/haartraining.htm @@ -492,7 +492,7 @@ class=Typewch>- weighttrimming <weight_trimming>  Specifies -wheter and how much weight trimming should be used. A decent choice is 0.90.

+whether and how much weight trimming should be used. A decent choice is 0.90.

- eqw

diff --git a/doc/tutorials/introduction/windows_install/windows_install.rst b/doc/tutorials/introduction/windows_install/windows_install.rst index c29c13aede..a99b270d88 100644 --- a/doc/tutorials/introduction/windows_install/windows_install.rst +++ b/doc/tutorials/introduction/windows_install/windows_install.rst @@ -81,7 +81,7 @@ Building the OpenCV library from scratch requires a couple of tools installed be + An IDE of choice (preferably), or just a C\C++ compiler that will actually make the binary files. Here we will use the `Microsoft Visual Studio `_. However, you can use any other IDE that has a valid C\C++ compiler. - + |CMake|_, which is a neat tool to make the project files (for your choosen IDE) from the OpenCV source files. It will also allow an easy configuration of the OpenCV build files, in order to make binary files that fits exactly to your needs. + + |CMake|_, which is a neat tool to make the project files (for your chosen IDE) from the OpenCV source files. It will also allow an easy configuration of the OpenCV build files, in order to make binary files that fits exactly to your needs. + Git to acquire the OpenCV source files. A good tool for this is |TortoiseGit|_. Alternatively, you can just download an archived version of the source files from our `page on Sourceforge `_ diff --git a/modules/calib3d/test/test_cameracalibration.cpp b/modules/calib3d/test/test_cameracalibration.cpp index 0b9d794a90..7e409517bb 100644 --- a/modules/calib3d/test/test_cameracalibration.cpp +++ b/modules/calib3d/test/test_cameracalibration.cpp @@ -478,7 +478,7 @@ void CV_CameraCalibrationTest::run( int start_from ) values_read = fscanf(file,"%lf",goodDistortion+2); CV_Assert(values_read == 1); values_read = fscanf(file,"%lf",goodDistortion+3); CV_Assert(values_read == 1); - /* Read good Rot matrixes */ + /* Read good Rot matrices */ for( currImage = 0; currImage < numImages; currImage++ ) { for( i = 0; i < 3; i++ ) diff --git a/modules/contrib/src/basicretinafilter.hpp b/modules/contrib/src/basicretinafilter.hpp index 8bd136d68e..f0b0de4aa0 100644 --- a/modules/contrib/src/basicretinafilter.hpp +++ b/modules/contrib/src/basicretinafilter.hpp @@ -439,8 +439,8 @@ namespace cv #ifdef MAKE_PARALLEL /****************************************************** ** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors - ** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary - ** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised + ** ==> main idea parallelize main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelized methods as necessary + ** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelized ** ==> functors constructors can differ from the parameters used with their related serial functions */ diff --git a/modules/contrib/src/magnoretinafilter.hpp b/modules/contrib/src/magnoretinafilter.hpp index 00124e984b..ac47b2e209 100644 --- a/modules/contrib/src/magnoretinafilter.hpp +++ b/modules/contrib/src/magnoretinafilter.hpp @@ -195,8 +195,8 @@ namespace cv #ifdef MAKE_PARALLEL /****************************************************** ** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors - ** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary - ** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised + ** ==> main idea parallelize main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelized methods as necessary + ** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelized ** ==> functors constructors can differ from the parameters used with their related serial functions */ class Parallel_amacrineCellsComputing: public cv::ParallelLoopBody diff --git a/modules/contrib/src/parvoretinafilter.hpp b/modules/contrib/src/parvoretinafilter.hpp index 55d61d1207..58e1303ec8 100644 --- a/modules/contrib/src/parvoretinafilter.hpp +++ b/modules/contrib/src/parvoretinafilter.hpp @@ -219,8 +219,8 @@ private: #ifdef MAKE_PARALLEL /****************************************************** ** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors -** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary -** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised +** ==> main idea parallelize main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelized methods as necessary +** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelized ** ==> functors constructors can differ from the parameters used with their related serial functions */ class Parallel_OPL_OnOffWaysComputing: public cv::ParallelLoopBody diff --git a/modules/contrib/src/retinacolor.hpp b/modules/contrib/src/retinacolor.hpp index 7b7294442b..2753096813 100644 --- a/modules/contrib/src/retinacolor.hpp +++ b/modules/contrib/src/retinacolor.hpp @@ -259,8 +259,8 @@ namespace cv #ifdef MAKE_PARALLEL /****************************************************** ** IF some parallelizing thread methods are available, then, main loops are parallelized using these functors - ** ==> main idea paralellise main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelised methods as necessary - ** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelised + ** ==> main idea parallelize main filters loops, then, only the most used methods are parallelized... TODO : increase the number of parallelized methods as necessary + ** ==> functors names = Parallel_$$$ where $$$= the name of the serial method that is parallelized ** ==> functors constructors can differ from the parameters used with their related serial functions */ diff --git a/modules/core/test/test_ds.cpp b/modules/core/test/test_ds.cpp index cd76ca2fe9..adc8f1a42c 100644 --- a/modules/core/test/test_ds.cpp +++ b/modules/core/test/test_ds.cpp @@ -1357,7 +1357,7 @@ int Core_SetTest::test_set_ops( int iters ) (cvset->total == 0 || cvset->total >= prev_total), "The total number of cvset elements is not correct" ); - // CvSet and simple set do not neccessary have the same "total" (active & free) number, + // CvSet and simple set do not necessary have the same "total" (active & free) number, // so pass "set->total" to skip that check test_seq_block_consistence( struct_idx, (CvSeq*)cvset, cvset->total ); update_progressbar(); @@ -1779,7 +1779,7 @@ int Core_GraphTest::test_graph_ops( int iters ) (graph->edges->total == 0 || graph->edges->total >= prev_edge_total), "The total number of graph vertices is not correct" ); - // CvGraph and simple graph do not neccessary have the same "total" (active & free) number, + // CvGraph and simple graph do not necessary have the same "total" (active & free) number, // so pass "graph->total" (or "graph->edges->total") to skip that check test_seq_block_consistence( struct_idx, (CvSeq*)graph, graph->total ); test_seq_block_consistence( struct_idx, (CvSeq*)graph->edges, graph->edges->total ); diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index e040ccfddb..053bcdbc51 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -733,11 +733,11 @@ CV_EXPORTS void cornerMinEigenVal(const GpuMat& src, GpuMat& dst, GpuMat& Dx, Gp int borderType=BORDER_REFLECT101, Stream& stream = Stream::Null()); //! performs per-element multiplication of two full (not packed) Fourier spectrums -//! supports 32FC2 matrixes only (interleaved format) +//! supports 32FC2 matrices only (interleaved format) CV_EXPORTS void mulSpectrums(const GpuMat& a, const GpuMat& b, GpuMat& c, int flags, bool conjB=false, Stream& stream = Stream::Null()); //! performs per-element multiplication of two full (not packed) Fourier spectrums -//! supports 32FC2 matrixes only (interleaved format) +//! supports 32FC2 matrices only (interleaved format) CV_EXPORTS void mulAndScaleSpectrums(const GpuMat& a, const GpuMat& b, GpuMat& c, int flags, float scale, bool conjB=false, Stream& stream = Stream::Null()); //! Performs a forward or inverse discrete Fourier transform (1D or 2D) of floating point matrix. diff --git a/modules/highgui/src/cap_tyzx.cpp b/modules/highgui/src/cap_tyzx.cpp index 5f9c3f3874..c4254597da 100644 --- a/modules/highgui/src/cap_tyzx.cpp +++ b/modules/highgui/src/cap_tyzx.cpp @@ -163,7 +163,7 @@ IplImage * CvCaptureCAM_TYZX::retrieveFrame(int) { if(!isOpened() || !g_tyzx_camera) return 0; - if(!image && !alocateImage()) + if(!image && !allocateImage()) return 0; // copy camera image into buffer. diff --git a/modules/legacy/src/clique.cpp b/modules/legacy/src/clique.cpp index d8f2f59da2..90111b1590 100644 --- a/modules/legacy/src/clique.cpp +++ b/modules/legacy/src/clique.cpp @@ -343,7 +343,7 @@ int cvFindNextMaximalClique( CvCliqueFinder* finder ) break; case NEXT: //here we will look for candidate to translate into not - //s[k] now contains index of choosen candidate + //s[k] now contains index of chosen candidate { int* new_ = All[k+1]; if( nod[k] != 0 ) @@ -590,7 +590,7 @@ void cvBronKerbosch( CvGraph* graph ) break; case NEXT: //here we will look for candidate to translate into not - //s[k] now contains index of choosen candidate + //s[k] now contains index of chosen candidate { int* new_ = All[k+1]; if( nod[k] != 0 ) diff --git a/modules/legacy/src/epilines.cpp b/modules/legacy/src/epilines.cpp index 7196d5109a..b63f8e1c77 100644 --- a/modules/legacy/src/epilines.cpp +++ b/modules/legacy/src/epilines.cpp @@ -3621,7 +3621,7 @@ int cvComputeEpipolesFromFundMatrix(CvMatr32f fundMatr, CvMat* matrV = cvCreateMat(3,3,CV_MAT32F); /* From svd we need just last vector of U and V or last row from U' and V' */ - /* We get transposed matrixes U and V */ + /* We get transposed matrices U and V */ cvSVD(&fundMatrC,matrW,matrU,matrV,CV_SVD_V_T|CV_SVD_U_T); /* Get last row from U' and compute epipole1 */ diff --git a/modules/legacy/src/lmeds.cpp b/modules/legacy/src/lmeds.cpp index 33b57a7597..2a56b483d0 100644 --- a/modules/legacy/src/lmeds.cpp +++ b/modules/legacy/src/lmeds.cpp @@ -1180,7 +1180,7 @@ icvSingularValueDecomposition( int M, } /* for */ } /* if */ - /* Iterations QR-algorithm for bidiagonal matrixes + /* Iterations QR-algorithm for bidiagonal matrices W[i] - is the main diagonal rv1[i] - is the top diagonal, rv1[0]=0. */ diff --git a/modules/legacy/src/trifocal.cpp b/modules/legacy/src/trifocal.cpp index f340283f1d..6bc5fb2d7c 100644 --- a/modules/legacy/src/trifocal.cpp +++ b/modules/legacy/src/trifocal.cpp @@ -905,7 +905,7 @@ int icvComputeProjectMatricesNPoints( CvMat* points1,CvMat* points2,CvMat* poin tmpProjMatr[1] = cvMat(9,4,CV_64F,tmpProjMatr_dat+36); tmpProjMatr[2] = cvMat(9,4,CV_64F,tmpProjMatr_dat+72); - /* choosen points */ + /* chosen points */ while( wasCount < NumSamples ) { @@ -1497,7 +1497,7 @@ void GetGeneratorReduceFundSolution(CvMat* points1,CvMat* points2,CvMat* fundRed matrV = cvMat(5,5,CV_64F,matrV_dat); /* From svd we need just two last vectors of V or two last row V' */ - /* We get transposed matrixes U and V */ + /* We get transposed matrices U and V */ cvSVD(&matrA,&matrW,0,&matrV,CV_SVD_V_T); @@ -1532,7 +1532,7 @@ int GetGoodReduceFundamMatrFromTwo(CvMat* fundReduceCoef1,CvMat* fundReduceCoef2 CV_ERROR( CV_StsUnsupportedFormat, "Input parameters must be a matrices" ); } - /* using two fundamental matrix comute matrixes for det(F)=0 */ + /* using two fundamental matrix comute matrices for det(F)=0 */ /* May compute 1 or 3 matrices. Returns number of solutions */ /* Here we will use case F=a*F1+(1-a)*F2 instead of F=m*F1+l*F2 */ @@ -1670,7 +1670,7 @@ void GetProjMatrFromReducedFundamental(CvMat* fundReduceCoefs,CvMat* projMatrCoe matrV = cvMat(3,3,CV_64F,matrV_dat); /* From svd we need just last vector of V or last row V' */ - /* We get transposed matrixes U and V */ + /* We get transposed matrices U and V */ cvSVD(&matrA,&matrW,0,&matrV,CV_SVD_V_T); @@ -1736,7 +1736,7 @@ void GetProjMatrFromReducedFundamental(CvMat* fundReduceCoefs,CvMat* projMatrCoe matrV1 = cvMat(6,6,CV_64F,matrV_dat1); /* From svd we need just last vector of V or last row V' */ - /* We get transposed matrixes U and V */ + /* We get transposed matrices U and V */ cvSVD(&matrK,&matrW1,0,&matrV1,CV_SVD_V_T); @@ -2037,7 +2037,7 @@ void icvComputeTransform4D(CvMat* points1,CvMat* points2,CvMat* transMatr) } /* From svd we need just two last vectors of V or two last row V' */ - /* We get transposed matrixes U and V */ + /* We get transposed matrices U and V */ cvSVD(matrA,matrW,0,&matrV,CV_SVD_V_T); diff --git a/modules/nonfree/src/opencl/surf.cl b/modules/nonfree/src/opencl/surf.cl index 405e48f02c..7421095c06 100644 --- a/modules/nonfree/src/opencl/surf.cl +++ b/modules/nonfree/src/opencl/surf.cl @@ -956,7 +956,7 @@ void icvCalcOrientation( // This reduction searches for the longest wavelet response vector. The first // step uses all of the work items in the workgroup to narrow the search // down to the three candidates. It requires s_mod to have a few more - // elements alocated past the work-group size, which are pre-initialized to + // elements allocated past the work-group size, which are pre-initialized to // 0.0f above. for(int t = ORI_RESPONSE_REDUCTION_WIDTH; t >= 3; t /= 2) { if (tid < t) { diff --git a/modules/photo/src/denoising.cpp b/modules/photo/src/denoising.cpp index 4762eda31a..a673e5820c 100644 --- a/modules/photo/src/denoising.cpp +++ b/modules/photo/src/denoising.cpp @@ -133,7 +133,7 @@ static void fastNlMeansDenoisingMultiCheckPreconditions( { CV_Error(CV_StsBadArg, "imgToDenoiseIndex and temporalWindowSize " - "should be choosen corresponding srcImgs size!"); + "should be chosen corresponding srcImgs size!"); } for (int i = 1; i < src_imgs_size; i++) { diff --git a/modules/ts/misc/chart.py b/modules/ts/misc/chart.py index 39a60eb2e7..2663c78758 100755 --- a/modules/ts/misc/chart.py +++ b/modules/ts/misc/chart.py @@ -168,7 +168,7 @@ if __name__ == "__main__": print >> sys.stderr, "%4s: %s" % (i, name) i += 1 if names1: - print >> sys.stderr, "Other suits in this log (can not be choosen):" + print >> sys.stderr, "Other suits in this log (can not be chosen):" for name in sorted(names1): print >> sys.stderr, "%4s: %s" % (i, name) i += 1 diff --git a/samples/winrt/ImageManipulations/common/LayoutAwarePage.cpp b/samples/winrt/ImageManipulations/common/LayoutAwarePage.cpp index 07092bb745..f3f4be2344 100644 --- a/samples/winrt/ImageManipulations/common/LayoutAwarePage.cpp +++ b/samples/winrt/ImageManipulations/common/LayoutAwarePage.cpp @@ -235,7 +235,7 @@ void LayoutAwarePage::CoreWindow_PointerPressed(CoreWindow^ sender, PointerEvent if (properties->IsLeftButtonPressed || properties->IsRightButtonPressed || properties->IsMiddleButtonPressed) return; - // If back or foward are pressed (but not both) navigate appropriately + // If back or forward are pressed (but not both) navigate appropriately bool backPressed = properties->IsXButton1Pressed; bool forwardPressed = properties->IsXButton2Pressed; if (backPressed ^ forwardPressed) From fb7e74c511077d2307f230abf6d4508bae96535a Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 16 Feb 2014 21:27:25 +0400 Subject: [PATCH 165/293] iteractor work --- modules/viz/src/interactor_style.cpp | 635 ---------- modules/viz/src/precomp.hpp | 5 +- modules/viz/src/vizcore.cpp | 1 + modules/viz/src/vizimpl.cpp | 3 +- modules/viz/src/vizimpl.hpp | 2 +- modules/viz/src/vtk/vtkCocoaInteractorFix.mm | 3 +- modules/viz/src/vtk/vtkOBJWriter.cpp | 2 +- modules/viz/src/vtk/vtkVizInteractorStyle.cpp | 1077 +++++++++++++++++ .../vtkVizInteractorStyle.hpp} | 116 +- 9 files changed, 1168 insertions(+), 676 deletions(-) delete mode 100644 modules/viz/src/interactor_style.cpp create mode 100644 modules/viz/src/vtk/vtkVizInteractorStyle.cpp rename modules/viz/src/{interactor_style.hpp => vtk/vtkVizInteractorStyle.hpp} (61%) diff --git a/modules/viz/src/interactor_style.cpp b/modules/viz/src/interactor_style.cpp deleted file mode 100644 index 7fa6714480..0000000000 --- a/modules/viz/src/interactor_style.cpp +++ /dev/null @@ -1,635 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2013, OpenCV Foundation, all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not 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 Intel Corporation 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. -// -// Authors: -// * Ozan Tonkal, ozantonkal@gmail.com -// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com -// -// OpenCV Viz module is complete rewrite of -// PCL visualization module (www.pointclouds.org) -// -//M*/ - -#include "precomp.hpp" - - -namespace cv { namespace viz -{ - vtkStandardNewMacro(InteractorStyle) -}} - - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::Initialize() -{ - // Set windows size (width, height) to unknown (-1) - win_size_ = Vec2i(-1, -1); - win_pos_ = Vec2i(0, 0); - max_win_size_ = Vec2i(-1, -1); - - init_ = true; - stereo_anaglyph_mask_default_ = true; - - // Initialize the keyboard event callback as none - keyboardCallback_ = 0; - keyboard_callback_cookie_ = 0; - - // Initialize the mouse event callback as none - mouseCallback_ = 0; - mouse_callback_cookie_ = 0; -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::saveScreenshot(const String &file) -{ - FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); - - vtkSmartPointer wif = vtkSmartPointer::New(); - wif->SetInput(Interactor->GetRenderWindow()); - - vtkSmartPointer snapshot_writer = vtkSmartPointer::New(); - snapshot_writer->SetInputConnection(wif->GetOutputPort()); - snapshot_writer->SetFileName(file.c_str()); - snapshot_writer->Write(); - - cout << "Screenshot successfully captured (" << file.c_str() << ")" << endl; -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::exportScene(const String &file) -{ - vtkSmartPointer exporter; - if (file.size() > 5 && file.substr(file.size() - 5) == ".vrml") - { - exporter = vtkSmartPointer::New(); - vtkVRMLExporter::SafeDownCast(exporter)->SetFileName(file.c_str()); - } - else - { - exporter = vtkSmartPointer::New(); - vtkOBJExporter::SafeDownCast(exporter)->SetFilePrefix(file.c_str()); - } - - exporter->SetInput(Interactor->GetRenderWindow()); - exporter->Write(); - - cout << "Scene successfully exported (" << file.c_str() << ")" << endl; -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::zoomIn() -{ - FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); - // Zoom in - StartDolly(); - double factor = 10.0 * 0.2 * .5; - Dolly(std::pow(1.1, factor)); - EndDolly(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::zoomOut() -{ - FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); - // Zoom out - StartDolly(); - double factor = 10.0 * -0.2 * .5; - Dolly(std::pow(1.1, factor)); - EndDolly(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnChar() -{ - // Make sure we ignore the same events we handle in OnKeyDown to avoid calling things twice - FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); - if (Interactor->GetKeyCode() >= '0' && Interactor->GetKeyCode() <= '9') - return; - - String key(Interactor->GetKeySym()); - if (key.find("XF86ZoomIn") != String::npos) - zoomIn(); - else if (key.find("XF86ZoomOut") != String::npos) - zoomOut(); - - int keymod = Interactor->GetAltKey(); - - switch (Interactor->GetKeyCode()) - { - // All of the options below simply exit - case 'h': case 'H': - case 'l': case 'L': - case 'p': case 'P': - case 'j': case 'J': - case 'c': case 'C': - case 43: // KEY_PLUS - case 45: // KEY_MINUS - case 'f': case 'F': - case 'g': case 'G': - case 'o': case 'O': - case 'u': case 'U': - case 'q': case 'Q': - { - break; - } - // S and R have a special !ALT case - case 'r': case 'R': - case 's': case 'S': - { - if (!keymod) - Superclass::OnChar(); - break; - } - default: - { - Superclass::OnChar(); - break; - } - } -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie) -{ - // Register the callback function and store the user data - mouseCallback_ = callback; - mouse_callback_cookie_ = cookie; -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void *cookie) -{ - // Register the callback function and store the user data - keyboardCallback_ = callback; - keyboard_callback_cookie_ = cookie; -} - -////////////////////////////////////////////////////////////////////////////////////////////// -int cv::viz::InteractorStyle::getModifiers() -{ - int modifiers = KeyboardEvent::NONE; - - if (Interactor->GetAltKey()) - modifiers |= KeyboardEvent::ALT; - - if (Interactor->GetControlKey()) - modifiers |= KeyboardEvent::CTRL; - - if (Interactor->GetShiftKey()) - modifiers |= KeyboardEvent::SHIFT; - return modifiers; -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnKeyDown() -{ - CV_Assert("Interactor style not initialized. Please call Initialize() before continuing" && init_); - FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); - - // Save the initial windows width/height - if (win_size_[0] == -1 || win_size_[1] == -1) - win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); - - bool alt = Interactor->GetAltKey() != 0; - - std::string key(Interactor->GetKeySym()); - if (key.find("XF86ZoomIn") != std::string::npos) - zoomIn(); - else if (key.find("XF86ZoomOut") != std::string::npos) - zoomOut(); - - switch (Interactor->GetKeyCode()) - { - case 'h': case 'H': - { - std::cout << "| Help:\n" - "-------\n" - " p, P : switch to a point-based representation\n" - " w, W : switch to a wireframe-based representation (where available)\n" - " s, S : switch to a surface-based representation (where available)\n" - "\n" - " j, J : take a .PNG snapshot of the current window view\n" - " k, K : export scene to Wavefront .obj format\n" - " ALT + k, K : export scene to VRML format\n" - " c, C : display current camera/window parameters\n" - " f, F : fly to point mode, hold the key and move mouse where to fly\n" - "\n" - " e, E : exit the interactor\n" - " q, Q : stop and call VTK's TerminateApp\n" - "\n" - " +/- : increment/decrement overall point size\n" - " +/- [+ ALT] : zoom in/out \n" - "\n" - " r, R [+ ALT] : reset camera [to viewpoint = {0, 0, 0} -> center_{x, y, z}]\n" - "\n" - " ALT + s, S : turn stereo mode on/off\n" - " ALT + f, F : switch between maximized window mode and original size\n" - "\n" - << std::endl; - break; - } - - // Switch representation to points - case 'p': case 'P': - { - vtkSmartPointer ac = CurrentRenderer->GetActors(); - vtkCollectionSimpleIterator ait; - for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) - for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) - { - vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); - apart->GetProperty()->SetRepresentationToPoints(); - } - break; - } - - // Save a PNG snapshot - case 'j': case 'J': - saveScreenshot(cv::format("screenshot-%d.png", (unsigned int)time(0))); break; - - // Export scene as in obj or vrml format - case 'k': case 'K': - { - String format = alt ? "scene-%d.vrml" : "scene-%d"; - exportScene(cv::format(format.c_str(), (unsigned int)time(0))); - break; - } - - // display current camera settings/parameters - case 'c': case 'C': - { - vtkSmartPointer cam = Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->GetActiveCamera(); - - Vec2d clip(cam->GetClippingRange()); - Vec3d focal(cam->GetFocalPoint()), pos(cam->GetPosition()), view(cam->GetViewUp()); - Vec2i win_pos(Interactor->GetRenderWindow()->GetPosition()); - Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); - double angle = cam->GetViewAngle () / 180.0 * CV_PI; - - String data = cv::format("clip(%f,%f) focal(%f,%f,%f) pos(%f,%f,%f) view(%f,%f,%f) angle(%f) winsz(%d,%d) winpos(%d,%d)", - clip[0], clip[1], focal[0], focal[1], focal[2], pos[0], pos[1], pos[2], view[0], view[1], view[2], - angle, win_size[0], win_size[1], win_pos[0], win_pos[1]); - - std::cout << data.c_str() << std::endl; - - break; - } - case '=': - { - zoomIn(); - break; - } - case 43: // KEY_PLUS - { - if (alt) - zoomIn(); - else - { - vtkSmartPointer ac = CurrentRenderer->GetActors(); - vtkCollectionSimpleIterator ait; - for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) - for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) - { - vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); - float psize = apart->GetProperty()->GetPointSize(); - if (psize < 63.0f) - apart->GetProperty()->SetPointSize(psize + 1.0f); - } - } - break; - } - case 45: // KEY_MINUS - { - if (alt) - zoomOut(); - else - { - vtkSmartPointer ac = CurrentRenderer->GetActors(); - vtkCollectionSimpleIterator ait; - for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) - for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) - { - vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); - float psize = apart->GetProperty()->GetPointSize(); - if (psize > 1.0f) - apart->GetProperty()->SetPointSize(psize - 1.0f); - } - } - break; - } - // Switch between maximize and original window size - case 'f': case 'F': - { - if (alt) - { - Vec2i screen_size(Interactor->GetRenderWindow()->GetScreenSize()); - Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); - - // Is window size = max? - if (win_size == max_win_size_) - { - Interactor->GetRenderWindow()->SetSize(win_size_.val); - Interactor->GetRenderWindow()->SetPosition(win_pos_.val); - Interactor->Render(); - } - // Set to max - else - { - win_pos_ = Vec2i(Interactor->GetRenderWindow()->GetPosition()); - win_size_ = win_size; - - Interactor->GetRenderWindow()->SetSize(screen_size.val); - Interactor->Render(); - max_win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); - } - } - else - { - AnimState = VTKIS_ANIM_ON; - Interactor->GetPicker()->Pick(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1], 0.0, CurrentRenderer); - vtkSmartPointer picker = vtkAbstractPropPicker::SafeDownCast(Interactor->GetPicker()); - if (picker) - if (picker->GetPath()) - Interactor->FlyTo(CurrentRenderer, picker->GetPickPosition()); - AnimState = VTKIS_ANIM_OFF; - } - break; - } - // 's'/'S' w/out ALT - case 's': case 'S': - { - if (alt) - { - vtkSmartPointer window = Interactor->GetRenderWindow(); - if (!window->GetStereoRender()) - { - static Vec2i red_blue(4, 3), magenta_green(2, 5); - window->SetAnaglyphColorMask (stereo_anaglyph_mask_default_ ? red_blue.val : magenta_green.val); - stereo_anaglyph_mask_default_ = !stereo_anaglyph_mask_default_; - } - window->SetStereoRender(!window->GetStereoRender()); - Interactor->Render(); - } - else - Superclass::OnKeyDown(); - break; - } - - case 'o': case 'O': - { - vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); - cam->SetParallelProjection(!cam->GetParallelProjection()); - Interactor->Render(); - break; - } - - // Overwrite the camera reset - case 'r': case 'R': - { - if (!alt) - { - Superclass::OnKeyDown(); - break; - } - - WidgetActorMap::iterator it = widget_actor_map_->begin(); - // it might be that some actors don't have a valid transformation set -> we skip them to avoid a seg fault. - for (; it != widget_actor_map_->end(); ++it) - { - vtkProp3D * actor = vtkProp3D::SafeDownCast(it->second); - if (actor && actor->GetUserMatrix()) - break; - } - - vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); - - // if a valid transformation was found, use it otherwise fall back to default view point. - if (it != widget_actor_map_->end()) - { - vtkMatrix4x4* m = vtkProp3D::SafeDownCast(it->second)->GetUserMatrix(); - - cam->SetFocalPoint(m->GetElement(0, 3) - m->GetElement(0, 2), - m->GetElement(1, 3) - m->GetElement(1, 2), - m->GetElement(2, 3) - m->GetElement(2, 2)); - - cam->SetViewUp (m->GetElement(0, 1), m->GetElement(1, 1), m->GetElement(2, 1)); - cam->SetPosition(m->GetElement(0, 3), m->GetElement(1, 3), m->GetElement(2, 3)); - } - else - { - cam->SetPosition(0, 0, 0); - cam->SetFocalPoint(0, 0, 1); - cam->SetViewUp(0, -1, 0); - } - - // go to the next actor for the next key-press event. - if (it != widget_actor_map_->end()) - ++it; - else - it = widget_actor_map_->begin(); - - CurrentRenderer->SetActiveCamera(cam); - CurrentRenderer->ResetCameraClippingRange(); - Interactor->Render(); - break; - } - - case 'q': case 'Q': - { - Interactor->ExitCallback(); - return; - } - default: - { - Superclass::OnKeyDown(); - break; - } - } - - KeyboardEvent event(KeyboardEvent::KEY_DOWN, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); - if (keyboardCallback_) - keyboardCallback_(event, keyboard_callback_cookie_); - Interactor->Render(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnKeyUp() -{ - KeyboardEvent event(KeyboardEvent::KEY_UP, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); - if (keyboardCallback_) - keyboardCallback_(event, keyboard_callback_cookie_); - Superclass::OnKeyUp(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnMouseMove() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent event(MouseEvent::MouseMove, MouseEvent::NoButton, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - Superclass::OnMouseMove(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnLeftButtonDown() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; - MouseEvent event(type, MouseEvent::LeftButton, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - Superclass::OnLeftButtonDown(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnLeftButtonUp() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::LeftButton, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - Superclass::OnLeftButtonUp(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnMiddleButtonDown() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; - MouseEvent event(type, MouseEvent::MiddleButton, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - Superclass::OnMiddleButtonDown(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnMiddleButtonUp() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::MiddleButton, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - Superclass::OnMiddleButtonUp(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnRightButtonDown() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; - MouseEvent event(type, MouseEvent::RightButton, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - Superclass::OnRightButtonDown(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnRightButtonUp() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::RightButton, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - Superclass::OnRightButtonUp(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnMouseWheelForward() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent event(MouseEvent::MouseScrollUp, MouseEvent::VScroll, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - if (Interactor->GetRepeatCount() && mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - - if (Interactor->GetAltKey()) - { - // zoom - vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); - double opening_angle = cam->GetViewAngle(); - if (opening_angle > 15.0) - opening_angle -= 1.0; - - cam->SetViewAngle(opening_angle); - cam->Modified(); - CurrentRenderer->ResetCameraClippingRange(); - CurrentRenderer->Modified(); - Interactor->Render(); - } - else - Superclass::OnMouseWheelForward(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnMouseWheelBackward() -{ - Vec2i p(Interactor->GetEventPosition()); - MouseEvent event(MouseEvent::MouseScrollDown, MouseEvent::VScroll, p, getModifiers()); - if (mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - - if (Interactor->GetRepeatCount() && mouseCallback_) - mouseCallback_(event, mouse_callback_cookie_); - - if (Interactor->GetAltKey()) - { - // zoom - vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); - double opening_angle = cam->GetViewAngle(); - if (opening_angle < 170.0) - opening_angle += 1.0; - - cam->SetViewAngle(opening_angle); - cam->Modified(); - CurrentRenderer->ResetCameraClippingRange(); - CurrentRenderer->Modified(); - Interactor->Render(); - } - else - Superclass::OnMouseWheelBackward(); -} - -////////////////////////////////////////////////////////////////////////////////////////////// -void cv::viz::InteractorStyle::OnTimer() -{ - CV_Assert("Interactor style not initialized." && init_); - Interactor->Render(); -} diff --git a/modules/viz/src/precomp.hpp b/modules/viz/src/precomp.hpp index 795ac16b07..feaca852e3 100644 --- a/modules/viz/src/precomp.hpp +++ b/modules/viz/src/precomp.hpp @@ -130,6 +130,7 @@ #include #include #include +#include "vtkCallbackCommand.h" #if !defined(_WIN32) || defined(__CYGWIN__) # include /* unlink */ @@ -145,6 +146,7 @@ #include #include + #include #include #include @@ -332,8 +334,7 @@ namespace cv } } -#include "interactor_style.hpp" +#include "vtk/vtkVizInteractorStyle.hpp" #include "vizimpl.hpp" - #endif diff --git a/modules/viz/src/vizcore.cpp b/modules/viz/src/vizcore.cpp index 4544f9b3cd..a33a192c6c 100644 --- a/modules/viz/src/vizcore.cpp +++ b/modules/viz/src/vizcore.cpp @@ -87,6 +87,7 @@ cv::Affine3d cv::viz::makeCameraPose(const Vec3d& position, const Vec3d& focal_p #else + void register_console_handler(); void register_console_handler() {} #endif diff --git a/modules/viz/src/vizimpl.cpp b/modules/viz/src/vizimpl.cpp index 37d008bc82..2401e76b5b 100644 --- a/modules/viz/src/vizimpl.cpp +++ b/modules/viz/src/vizimpl.cpp @@ -60,10 +60,9 @@ cv::viz::Viz3d::VizImpl::VizImpl(const String &name) : spin_once_state_(false), window_->AddRenderer(renderer_); // Create the interactor style - style_ = vtkSmartPointer::New(); + style_ = vtkSmartPointer::New(); style_->setWidgetActorMap(widget_actor_map_); style_->UseTimersOn(); - style_->Initialize(); timer_callback_ = vtkSmartPointer::New(); exit_callback_ = vtkSmartPointer::New(); diff --git a/modules/viz/src/vizimpl.hpp b/modules/viz/src/vizimpl.hpp index 67cab3674b..92113afa02 100644 --- a/modules/viz/src/vizimpl.hpp +++ b/modules/viz/src/vizimpl.hpp @@ -128,7 +128,7 @@ private: vtkSmartPointer exit_callback_; vtkSmartPointer renderer_; - vtkSmartPointer style_; + vtkSmartPointer style_; Ptr widget_actor_map_; bool removeActorFromRenderer(vtkSmartPointer actor); diff --git a/modules/viz/src/vtk/vtkCocoaInteractorFix.mm b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm index 0e55aebb08..23bba552d3 100644 --- a/modules/viz/src/vtk/vtkCocoaInteractorFix.mm +++ b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm @@ -40,8 +40,7 @@ // Authors: // * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com // -// OpenCV Viz module is complete rewrite of -// PCL visualization module (www.pointclouds.org) +// This workaround code was taken from PCL library(www.pointclouds.org) // //M*/ diff --git a/modules/viz/src/vtk/vtkOBJWriter.cpp b/modules/viz/src/vtk/vtkOBJWriter.cpp index e92424c665..7480b11ac2 100644 --- a/modules/viz/src/vtk/vtkOBJWriter.cpp +++ b/modules/viz/src/vtk/vtkOBJWriter.cpp @@ -84,7 +84,7 @@ void cv::viz::vtkOBJWriter::WriteData() std::ostream& outfile = *outfilep; //write header - outfile << "# wavefront obj file written by the visualization toolkit" << std::endl << std::endl; + outfile << "# wavefront obj file written by opencv viz module" << std::endl << std::endl; outfile << "mtllib NONE" << std::endl << std::endl; // write out the points diff --git a/modules/viz/src/vtk/vtkVizInteractorStyle.cpp b/modules/viz/src/vtk/vtkVizInteractorStyle.cpp new file mode 100644 index 0000000000..57aee636e6 --- /dev/null +++ b/modules/viz/src/vtk/vtkVizInteractorStyle.cpp @@ -0,0 +1,1077 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's 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. +// +// * The name of the copyright holders may not 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 Intel Corporation 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. +// +// Authors: +// * Ozan Tonkal, ozantonkal@gmail.com +// * Anatoly Baksheev, Itseez Inc. myname.mysurname <> mycompany.com +// +//M*/ + +#include "precomp.hpp" + +namespace cv { namespace viz +{ + vtkStandardNewMacro(vtkVizInteractorStyle) +}} + +////////////////////////////////////////////////////////////////////////////////////////////// + +cv::viz::vtkVizInteractorStyle::vtkVizInteractorStyle() +{ + FlyMode = false; + MotionFactor = 10.0; + + keyboardCallback_ = 0; + keyboard_callback_cookie_ = 0; + + mouseCallback_ = 0; + mouse_callback_cookie_ = 0; + + // Set windows size (width, height) to unknown (-1) + win_size_ = Vec2i(-1, -1); + win_pos_ = Vec2i(0, 0); + max_win_size_ = Vec2i(-1, -1); + + stereo_anaglyph_redblue_ = true; + + //from fly + KeysDown = 0; + UseTimers = 1; + + DiagonalLength = 1.0; + MotionStepSize = 1.0/100.0; + MotionUserScale = 1.0; // +/- key adjustment + MotionAccelerationFactor = 10.0; + AngleStepSize = 1.0; +} + +cv::viz::vtkVizInteractorStyle::~vtkVizInteractorStyle() {} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::saveScreenshot(const String &file) +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + vtkSmartPointer wif = vtkSmartPointer::New(); + wif->SetInput(Interactor->GetRenderWindow()); + + vtkSmartPointer snapshot_writer = vtkSmartPointer::New(); + snapshot_writer->SetInputConnection(wif->GetOutputPort()); + snapshot_writer->SetFileName(file.c_str()); + snapshot_writer->Write(); + + cout << "Screenshot successfully captured (" << file.c_str() << ")" << endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::exportScene(const String &file) +{ + vtkSmartPointer exporter; + if (file.size() > 5 && file.substr(file.size() - 5) == ".vrml") + { + exporter = vtkSmartPointer::New(); + vtkVRMLExporter::SafeDownCast(exporter)->SetFileName(file.c_str()); + } + else + { + exporter = vtkSmartPointer::New(); + vtkOBJExporter::SafeDownCast(exporter)->SetFilePrefix(file.c_str()); + } + + exporter->SetInput(Interactor->GetRenderWindow()); + exporter->Write(); + + cout << "Scene successfully exported (" << file.c_str() << ")" << endl; +} + +void cv::viz::vtkVizInteractorStyle::exportScene() +{ + // Export scene as in obj or vrml format + String format = Interactor->GetAltKey() ? "scene-%d.vrml" : "scene-%d"; + exportScene(cv::format(format.c_str(), (unsigned int)time(0))); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::changePointsSize(float delta) +{ + vtkSmartPointer ac = CurrentRenderer->GetActors(); + vtkCollectionSimpleIterator ait; + + for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) + for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) + { + vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); + float psize = apart->GetProperty()->GetPointSize() + delta; + psize = std::max(1.f, std::min(63.f, psize)); + apart->GetProperty()->SetPointSize(psize); + } +} + +void cv::viz::vtkVizInteractorStyle::setRepresentationToPoints() +{ + vtkSmartPointer ac = CurrentRenderer->GetActors(); + vtkCollectionSimpleIterator ait; + for (ac->InitTraversal(ait); vtkActor* actor = ac->GetNextActor(ait); ) + for (actor->InitPathTraversal(); vtkAssemblyPath* path = actor->GetNextPath(); ) + { + vtkActor* apart = vtkActor::SafeDownCast(path->GetLastNode()->GetViewProp()); + apart->GetProperty()->SetRepresentationToPoints(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::printCameraParams() +{ + vtkSmartPointer cam = Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->GetActiveCamera(); + + Vec2d clip(cam->GetClippingRange()); + Vec3d focal(cam->GetFocalPoint()), pos(cam->GetPosition()), view(cam->GetViewUp()); + Vec2i win_pos(Interactor->GetRenderWindow()->GetPosition()); + Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); + double angle = cam->GetViewAngle () / 180.0 * CV_PI; + + String data = cv::format("clip(%f,%f) focal(%f,%f,%f) pos(%f,%f,%f) view(%f,%f,%f) angle(%f) winsz(%d,%d) winpos(%d,%d)", + clip[0], clip[1], focal[0], focal[1], focal[2], pos[0], pos[1], pos[2], view[0], view[1], view[2], + angle, win_size[0], win_size[1], win_pos[0], win_pos[1]); + + std::cout << data.c_str() << std::endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::toggleFullScreen() +{ + Vec2i screen_size(Interactor->GetRenderWindow()->GetScreenSize()); + Vec2i win_size(Interactor->GetRenderWindow()->GetSize()); + + // Is window size = max? + if (win_size == max_win_size_) + { + Interactor->GetRenderWindow()->SetSize(win_size_.val); + Interactor->GetRenderWindow()->SetPosition(win_pos_.val); + Interactor->Render(); + } + // Set to max + else + { + win_pos_ = Vec2i(Interactor->GetRenderWindow()->GetPosition()); + win_size_ = win_size; + + Interactor->GetRenderWindow()->SetSize(screen_size.val); + Interactor->Render(); + max_win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::resetViewerPose() +{ + WidgetActorMap::iterator it = widget_actor_map_->begin(); + // it might be that some actors don't have a valid transformation set -> we skip them to avoid a seg fault. + for (; it != widget_actor_map_->end(); ++it) + { + vtkProp3D * actor = vtkProp3D::SafeDownCast(it->second); + if (actor && actor->GetUserMatrix()) + break; + } + + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + + // if a valid transformation was found, use it otherwise fall back to default view point. + if (it != widget_actor_map_->end()) + { + vtkMatrix4x4* m = vtkProp3D::SafeDownCast(it->second)->GetUserMatrix(); + + cam->SetFocalPoint(m->GetElement(0, 3) - m->GetElement(0, 2), + m->GetElement(1, 3) - m->GetElement(1, 2), + m->GetElement(2, 3) - m->GetElement(2, 2)); + + cam->SetViewUp (m->GetElement(0, 1), m->GetElement(1, 1), m->GetElement(2, 1)); + cam->SetPosition(m->GetElement(0, 3), m->GetElement(1, 3), m->GetElement(2, 3)); + } + else + { + cam->SetPosition(0, 0, 0); + cam->SetFocalPoint(0, 0, 1); + cam->SetViewUp(0, -1, 0); + } + + // go to the next actor for the next key-press event. + if (it != widget_actor_map_->end()) + ++it; + else + it = widget_actor_map_->begin(); + + CurrentRenderer->SetActiveCamera(cam); + CurrentRenderer->ResetCameraClippingRange(); + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::toggleStereo() +{ + vtkSmartPointer window = Interactor->GetRenderWindow(); + if (!window->GetStereoRender()) + { + static Vec2i red_blue(4, 3), magenta_green(2, 5); + window->SetAnaglyphColorMask (stereo_anaglyph_redblue_ ? red_blue.val : magenta_green.val); + stereo_anaglyph_redblue_ = !stereo_anaglyph_redblue_; + } + window->SetStereoRender(!window->GetStereoRender()); + Interactor->Render(); + +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::printHelp() +{ + std::cout << "| Help:\n" + "-------\n" + " p, P : switch to a point-based representation\n" + " w, W : switch to a wireframe-based representation (where available)\n" + " s, S : switch to a surface-based representation (where available)\n" + "\n" + " j, J : take a .PNG snapshot of the current window view\n" + " k, K : export scene to Wavefront .obj format\n" + " ALT + k, K : export scene to VRML format\n" + " c, C : display current camera/window parameters\n" + " F5 : enable/disable fly mode (changes control style)\n" + "\n" + " e, E : exit the interactor\n" + " q, Q : stop and call VTK's TerminateApp\n" + "\n" + " +/- : increment/decrement overall point size\n" + " +/- [+ ALT] : zoom in/out \n" + "\n" + " r, R [+ ALT] : reset camera [to viewpoint = {0, 0, 0} -> center_{x, y, z}]\n" + "\n" + " ALT + s, S : turn stereo mode on/off\n" + " ALT + f, F : switch between maximized window mode and original size\n" + "\n" + << std::endl; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::zoomIn() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + // Zoom in + StartDolly(); + double factor = 10.0 * 0.2 * .5; + Dolly(std::pow(1.1, factor)); + EndDolly(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::zoomOut() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + // Zoom out + StartDolly(); + double factor = 10.0 * -0.2 * .5; + Dolly(std::pow(1.1, factor)); + EndDolly(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnChar() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + String key(Interactor->GetKeySym()); + if (key.find("XF86ZoomIn") != String::npos) + zoomIn(); + else if (key.find("XF86ZoomOut") != String::npos) + zoomOut(); + + switch (Interactor->GetKeyCode()) + { +// // All of the options below simply exit +// case 'l': case 'L': case 'j': case 'J': case 'c': case 'C': case 'q': case 'Q': +// case 'f': case 'F': case 'g': case 'G': case 'o': case 'O': case 'u': case 'U': + case 'p': case 'P': + break; + + case '+': + if (FlyMode) + MotionUserScale = std::min(16.0, MotionUserScale*2.0); + break; + case '-': + if (FlyMode) + MotionUserScale = std::max(MotionUserScale * 0.5, 0.0625); + break; + + case 'r': case 'R': case 's': case 'S': + if (!Interactor->GetAltKey()) + Superclass::OnChar(); + break; + default: + Superclass::OnChar(); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie) +{ + mouseCallback_ = callback; + mouse_callback_cookie_ = cookie; +} + +void cv::viz::vtkVizInteractorStyle::registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void *cookie) +{ + keyboardCallback_ = callback; + keyboard_callback_cookie_ = cookie; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +int cv::viz::vtkVizInteractorStyle::getModifiers() +{ + int modifiers = KeyboardEvent::NONE; + + if (Interactor->GetAltKey()) + modifiers |= KeyboardEvent::ALT; + + if (Interactor->GetControlKey()) + modifiers |= KeyboardEvent::CTRL; + + if (Interactor->GetShiftKey()) + modifiers |= KeyboardEvent::SHIFT; + return modifiers; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnKeyDown() +{ + FindPokedRenderer(Interactor->GetEventPosition()[0], Interactor->GetEventPosition()[1]); + + String key(Interactor->GetKeySym()); + if (key.find("XF86ZoomIn") != String::npos) + zoomIn(); + else if (key.find("XF86ZoomOut") != String::npos) + zoomOut(); + else if (key.find("F5") != String::npos) + { + FlyMode = !FlyMode; + std::cout << (FlyMode ? "Fly mode: on" : "Fly mode: off") << std::endl; + } + + // Save the initial windows width/height + if (win_size_[0] == -1 || win_size_[1] == -1) + win_size_ = Vec2i(Interactor->GetRenderWindow()->GetSize()); + + switch (Interactor->GetKeyCode()) + { + case 'a': case 'A' : KeysDown |=16; break; + case 'z': case 'Z' : KeysDown |=32; break; + case 'h': case 'H' : printHelp(); break; + case 'p': case 'P' : setRepresentationToPoints(); break; + case 'k': case 'K' : exportScene(); break; + case 'j': case 'J' : saveScreenshot(cv::format("screenshot-%d.png", (unsigned int)time(0))); break; + case 'c': case 'C' : printCameraParams(); break; + case '=': zoomIn(); break; + case 43: // KEY_PLUS + { + if (FlyMode) + break; + if (Interactor->GetAltKey()) + zoomIn(); + else + changePointsSize(+1.f); + break; + } + case 45: // KEY_MINUS + { + if (FlyMode) + break; + if (Interactor->GetAltKey()) + zoomOut(); + else + changePointsSize(-1.f); + break; + } + // Switch between maximize and original window size + case 'f': case 'F': + { + if (Interactor->GetAltKey()) + toggleFullScreen(); + break; + } + // 's'/'S' w/out ALT + case 's': case 'S': + { + if (Interactor->GetAltKey()) + toggleStereo(); + break; + } + + case 'o': case 'O': + { + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + cam->SetParallelProjection(!cam->GetParallelProjection()); + Interactor->Render(); + break; + } + + // Overwrite the camera reset + case 'r': case 'R': + { + if (Interactor->GetAltKey()) + resetViewerPose(); + break; + } + case 'q': case 'Q': + Interactor->ExitCallback(); return; + default: + Superclass::OnKeyDown(); break; + } + + KeyboardEvent event(KeyboardEvent::KEY_DOWN, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); + if (keyboardCallback_) + keyboardCallback_(event, keyboard_callback_cookie_); + + if (FlyMode && (KeysDown & (32+16)) == (32+16)) + { + if (State == VTKIS_FORWARDFLY || State == VTKIS_REVERSEFLY) + StopState(); + } + else if (FlyMode && (KeysDown & 32) == 32) + { + if (State == VTKIS_FORWARDFLY) + StopState(); + + if (State == VTKIS_NONE) + StartState(VTKIS_REVERSEFLY); + } + else if (FlyMode && (KeysDown & 16) == 16) + { + if (State == VTKIS_REVERSEFLY) + StopState(); + + if (State == VTKIS_NONE) + StartState(VTKIS_FORWARDFLY); + } + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnKeyUp() +{ + KeyboardEvent event(KeyboardEvent::KEY_UP, Interactor->GetKeySym(), Interactor->GetKeyCode(), getModifiers()); + if (keyboardCallback_) + keyboardCallback_(event, keyboard_callback_cookie_); + + switch (Interactor->GetKeyCode()) + { + case 'a': case 'A' : KeysDown &= ~16; break; + case 'z': case 'Z' : KeysDown &= ~32; break; + } + + if (State == VTKIS_FORWARDFLY && (KeysDown & 16) == 0) + StopState(); + + if (State == VTKIS_REVERSEFLY && (KeysDown & 32) == 0) + StopState(); + + Superclass::OnKeyUp(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMouseMove() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseMove, MouseEvent::NoButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + + if (State == VTKIS_ROTATE || State == VTKIS_PAN || State == VTKIS_DOLLY || State == VTKIS_SPIN) + { + switch (State) + { + case VTKIS_ROTATE: Rotate(); break; + case VTKIS_PAN: Pan(); break; + case VTKIS_DOLLY: Dolly(); break; + case VTKIS_SPIN: Spin(); break; + } + + InvokeEvent(vtkCommand::InteractionEvent, NULL); + } + + if (State == VTKIS_FORWARDFLY || State == VTKIS_REVERSEFLY) + { + vtkCamera *cam = CurrentRenderer->GetActiveCamera(); + Vec2i thispos(Interactor->GetEventPosition()); + Vec2i lastpos(Interactor->GetLastEventPosition()); + + // we want to steer by an amount proportional to window viewangle and size + // compute dx and dy increments relative to last mouse click + Vec2i size(Interactor->GetSize()); + double scalefactor = 5*cam->GetViewAngle()/size[0]; + + double dx = - (thispos[0] - lastpos[0])*scalefactor*AngleStepSize; + double dy = (thispos[1] - lastpos[1])*scalefactor*AngleStepSize; + + // Temporary until I get smooth flight working + DeltaPitch = dy; + DeltaYaw = dx; + + InvokeEvent(vtkCommand::InteractionEvent, NULL); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnLeftButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::LeftButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + + if (FlyMode) + { + if(State == VTKIS_REVERSEFLY) + State = VTKIS_FORWARDFLY; + else + { + SetupMotionVars(); + if (State == VTKIS_NONE) + StartState(VTKIS_FORWARDFLY); + } + } + else + { + if (Interactor->GetShiftKey()) + { + if (Interactor->GetControlKey()) + StartDolly(); + else + StartPan(); + } + else + { + if (Interactor->GetControlKey()) + StartSpin(); + else + StartRotate(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnLeftButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::LeftButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + switch (State) + { + case VTKIS_DOLLY: EndDolly(); break; + case VTKIS_PAN: EndPan(); break; + case VTKIS_SPIN: EndSpin(); break; + case VTKIS_ROTATE: EndRotate(); break; + case VTKIS_FORWARDFLY: StopState(); break; + } + + if (Interactor ) + ReleaseFocus(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMiddleButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::MiddleButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + StartPan(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMiddleButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::MiddleButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (State == VTKIS_PAN) + { + EndPan(); + if (Interactor) + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnRightButtonDown() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent::Type type = (Interactor->GetRepeatCount() == 0) ? MouseEvent::MouseButtonPress : MouseEvent::MouseDblClick; + MouseEvent event(type, MouseEvent::RightButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + + if (FlyMode) + { + if (State == VTKIS_FORWARDFLY) + State = VTKIS_REVERSEFLY; + else + { + SetupMotionVars(); + if (State == VTKIS_NONE) + StartState(VTKIS_REVERSEFLY); + } + + } + else + StartDolly(); +} + + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnRightButtonUp() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseButtonRelease, MouseEvent::RightButton, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if(State == VTKIS_DOLLY) + { + EndDolly(); + if (Interactor) + ReleaseFocus(); + } + + if (State == VTKIS_REVERSEFLY) + { + StopState(); + if (Interactor) + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMouseWheelForward() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseScrollUp, MouseEvent::VScroll, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + if (Interactor->GetRepeatCount() && mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetAltKey()) + { + // zoom + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + double opening_angle = cam->GetViewAngle(); + if (opening_angle > 15.0) + opening_angle -= 1.0; + + cam->SetViewAngle(opening_angle); + cam->Modified(); + CurrentRenderer->ResetCameraClippingRange(); + CurrentRenderer->Modified(); + Interactor->Render(); + } + else + { + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + StartDolly(); + Dolly(pow(1.1, MotionFactor * 0.2 * MouseWheelMotionFactor)); + EndDolly(); + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnMouseWheelBackward() +{ + Vec2i p(Interactor->GetEventPosition()); + MouseEvent event(MouseEvent::MouseScrollDown, MouseEvent::VScroll, p, getModifiers()); + if (mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetRepeatCount() && mouseCallback_) + mouseCallback_(event, mouse_callback_cookie_); + + if (Interactor->GetAltKey()) + { + // zoom + vtkSmartPointer cam = CurrentRenderer->GetActiveCamera(); + double opening_angle = cam->GetViewAngle(); + if (opening_angle < 170.0) + opening_angle += 1.0; + + cam->SetViewAngle(opening_angle); + cam->Modified(); + CurrentRenderer->ResetCameraClippingRange(); + CurrentRenderer->Modified(); + Interactor->Render(); + } + else + { + FindPokedRenderer(p[0], p[1]); + if (!CurrentRenderer) + return; + + GrabFocus(EventCallbackCommand); + StartDolly(); + Dolly(pow(1.1, MotionFactor * -0.2 * MouseWheelMotionFactor)); + EndDolly(); + ReleaseFocus(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::OnTimer() +{ + if (State == VTKIS_FORWARDFLY || State == VTKIS_REVERSEFLY) + Fly(); + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::Rotate() +{ + if (!CurrentRenderer) + return; + + Vec2i dxy = Vec2i(Interactor->GetEventPosition()) - Vec2i(Interactor->GetLastEventPosition()); + Vec2i size(CurrentRenderer->GetRenderWindow()->GetSize()); + + double delta_elevation = -20.0 / size[1]; + double delta_azimuth = -20.0 / size[0]; + + double rxf = dxy[0] * delta_azimuth * MotionFactor; + double ryf = dxy[1] * delta_elevation * MotionFactor; + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + camera->Azimuth(rxf); + camera->Elevation(ryf); + camera->OrthogonalizeViewUp(); + + if (AutoAdjustCameraClippingRange) + CurrentRenderer->ResetCameraClippingRange(); + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::Spin() +{ + if (!CurrentRenderer) + return; + + vtkRenderWindowInteractor *rwi = Interactor; + + double *center = CurrentRenderer->GetCenter(); + + double newAngle = vtkMath::DegreesFromRadians( atan2( rwi->GetEventPosition()[1] - center[1], rwi->GetEventPosition()[0] - center[0] ) ); + double oldAngle = vtkMath::DegreesFromRadians( atan2( rwi->GetLastEventPosition()[1] - center[1], rwi->GetLastEventPosition()[0] - center[0] ) ); + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + camera->Roll( newAngle - oldAngle ); + camera->OrthogonalizeViewUp(); + + rwi->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +void cv::viz::vtkVizInteractorStyle::Pan() +{ + if (!CurrentRenderer) + return; + + vtkRenderWindowInteractor *rwi = Interactor; + + double viewFocus[4], focalDepth, viewPoint[3]; + double newPickPoint[4], oldPickPoint[4], motionVector[3]; + + // Calculate the focal depth since we'll be using it a lot + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + camera->GetFocalPoint(viewFocus); + ComputeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2], viewFocus); + focalDepth = viewFocus[2]; + + ComputeDisplayToWorld(rwi->GetEventPosition()[0], rwi->GetEventPosition()[1], focalDepth, newPickPoint); + + // Has to recalc old mouse point since the viewport has moved, so can't move it outside the loop + ComputeDisplayToWorld(rwi->GetLastEventPosition()[0], rwi->GetLastEventPosition()[1], focalDepth, oldPickPoint); + + // Camera motion is reversed + motionVector[0] = oldPickPoint[0] - newPickPoint[0]; + motionVector[1] = oldPickPoint[1] - newPickPoint[1]; + motionVector[2] = oldPickPoint[2] - newPickPoint[2]; + + camera->GetFocalPoint(viewFocus); + camera->GetPosition(viewPoint); + camera->SetFocalPoint(motionVector[0] + viewFocus[0], motionVector[1] + viewFocus[1], motionVector[2] + viewFocus[2]); + camera->SetPosition( motionVector[0] + viewPoint[0], motionVector[1] + viewPoint[1], motionVector[2] + viewPoint[2]); + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); + + Interactor->Render(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::Dolly() +{ + if (!CurrentRenderer) + return; + + int dy = Interactor->GetEventPosition()[1] - Interactor->GetLastEventPosition()[1]; + Dolly(pow(1.1, MotionFactor * dy / CurrentRenderer->GetCenter()[1])); +} + +void cv::viz::vtkVizInteractorStyle::Dolly(double factor) +{ + if (!CurrentRenderer) + return; + + vtkCamera *camera = CurrentRenderer->GetActiveCamera(); + if (camera->GetParallelProjection()) + camera->SetParallelScale(camera->GetParallelScale() / factor); + else + { + camera->Dolly(factor); + if (AutoAdjustCameraClippingRange) + CurrentRenderer->ResetCameraClippingRange(); + } + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); + + Interactor->Render(); +} +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::Fly() +{ + if (CurrentRenderer == NULL) + return; + + if (KeysDown) + FlyByKey(); + else + FlyByMouse(); + + CurrentRenderer->GetActiveCamera()->OrthogonalizeViewUp(); + + if (AutoAdjustCameraClippingRange) + CurrentRenderer->ResetCameraClippingRange(); + + if (Interactor->GetLightFollowCamera()) + CurrentRenderer->UpdateLightsGeometryToFollowCamera(); +} + +void cv::viz::vtkVizInteractorStyle::SetupMotionVars() +{ + Vec6d bounds; + CurrentRenderer->ComputeVisiblePropBounds(bounds.val); + + if ( !vtkMath::AreBoundsInitialized(bounds.val) ) + DiagonalLength = 1.0; + else + DiagonalLength = norm(Vec3d(bounds[0], bounds[2], bounds[4]) - Vec3d(bounds[1], bounds[3], bounds[5])); +} + +void cv::viz::vtkVizInteractorStyle::MotionAlongVector(const Vec3d& vector, double amount, vtkCamera* cam) +{ + // move camera and focus along DirectionOfProjection + Vec3d campos = Vec3d(cam->GetPosition()) - amount * vector; + Vec3d camfoc = Vec3d(cam->GetFocalPoint()) - amount * vector; + + cam->SetPosition(campos.val); + cam->SetFocalPoint(camfoc.val); +} + +void cv::viz::vtkVizInteractorStyle::FlyByMouse() +{ + vtkCamera* cam = CurrentRenderer->GetActiveCamera(); + double speed = DiagonalLength * MotionStepSize * MotionUserScale; + speed = speed * ( Interactor->GetShiftKey() ? MotionAccelerationFactor : 1.0); + + // Sidestep + if (Interactor->GetAltKey()) + { + if (DeltaYaw!=0.0) + { + vtkMatrix4x4 *vtm = cam->GetViewTransformMatrix(); + Vec3d a_vector(vtm->GetElement(0,0), vtm->GetElement(0,1), vtm->GetElement(0,2)); + + MotionAlongVector(a_vector, -DeltaYaw*speed, cam); + } + if (DeltaPitch!=0.0) + { + Vec3d a_vector(cam->GetViewUp()); + MotionAlongVector(a_vector, DeltaPitch*speed, cam); + } + } + else + { + cam->Yaw(DeltaYaw); + cam->Pitch(DeltaPitch); + DeltaYaw = 0; + DeltaPitch = 0; + } + // + if (!Interactor->GetControlKey()) + { + Vec3d a_vector(cam->GetDirectionOfProjection()); // reversed (use -speed) + switch (State) + { + case VTKIS_FORWARDFLY: MotionAlongVector(a_vector, -speed, cam); break; + case VTKIS_REVERSEFLY: MotionAlongVector(a_vector, speed, cam); break; + } + } +} + +void cv::viz::vtkVizInteractorStyle::FlyByKey() +{ + vtkCamera* cam = CurrentRenderer->GetActiveCamera(); + + double speed = DiagonalLength * MotionStepSize * MotionUserScale; + speed = speed * ( Interactor->GetShiftKey() ? MotionAccelerationFactor : 1.0); + + // Left and right + if (Interactor->GetAltKey()) + { // Sidestep + vtkMatrix4x4 *vtm = cam->GetViewTransformMatrix(); + Vec3d a_vector(vtm->GetElement(0,0), vtm->GetElement(0,1), vtm->GetElement(0,2)); + + if (KeysDown & 1) + MotionAlongVector(a_vector, -speed, cam); + + if (KeysDown & 2) + MotionAlongVector(a_vector, speed, cam); + } + else + { + if (KeysDown & 1) + cam->Yaw( AngleStepSize); + + if (KeysDown & 2) + cam->Yaw(-AngleStepSize); + } + + // Up and Down + if (Interactor->GetControlKey()) + { // Sidestep + Vec3d a_vector = Vec3d(cam->GetViewUp()); + if (KeysDown & 4) + MotionAlongVector(a_vector,-speed, cam); + + if (KeysDown & 8) + MotionAlongVector(a_vector, speed, cam); + } + else + { + if (KeysDown & 4) + cam->Pitch(-AngleStepSize); + + if (KeysDown & 8) + cam->Pitch( AngleStepSize); + } + + // forward and backward + Vec3d a_vector(cam->GetDirectionOfProjection()); + if (KeysDown & 16) + MotionAlongVector(a_vector, speed, cam); + + if (KeysDown & 32) + MotionAlongVector(a_vector,-speed, cam); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::viz::vtkVizInteractorStyle::PrintSelf(ostream& os, vtkIndent indent) +{ + Superclass::PrintSelf(os, indent); + os << indent << "MotionFactor: " << MotionFactor << "\n"; + os << indent << "MotionStepSize: " << MotionStepSize << "\n"; + os << indent << "MotionAccelerationFactor: "<< MotionAccelerationFactor << "\n"; + os << indent << "AngleStepSize: " << AngleStepSize << "\n"; + os << indent << "MotionUserScale: "<< MotionUserScale << "\n"; +} + diff --git a/modules/viz/src/interactor_style.hpp b/modules/viz/src/vtk/vtkVizInteractorStyle.hpp similarity index 61% rename from modules/viz/src/interactor_style.hpp rename to modules/viz/src/vtk/vtkVizInteractorStyle.hpp index 8d01697a87..3588a3b6cd 100644 --- a/modules/viz/src/interactor_style.hpp +++ b/modules/viz/src/vtk/vtkVizInteractorStyle.hpp @@ -46,46 +46,23 @@ #ifndef __OPENCV_VIZ_INTERACTOR_STYLE_H__ #define __OPENCV_VIZ_INTERACTOR_STYLE_H__ +#include + namespace cv { namespace viz { - class InteractorStyle : public vtkInteractorStyleTrackballCamera + class vtkVizInteractorStyle : public vtkInteractorStyle { public: - static InteractorStyle *New(); - virtual ~InteractorStyle() {} + static vtkVizInteractorStyle *New(); + vtkTypeMacro(vtkVizInteractorStyle, vtkInteractorStyle) + void PrintSelf(ostream& os, vtkIndent indent); - // this macro defines Superclass, the isA functionality and the safe downcast method - vtkTypeMacro(InteractorStyle, vtkInteractorStyleTrackballCamera) - - /** \brief Initialization routine. Must be called before anything else. */ - virtual void Initialize(); - - void setWidgetActorMap(const Ptr& actors) { widget_actor_map_ = actors; } - void registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie = 0); - void registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void * cookie = 0); - void saveScreenshot(const String &file); - void exportScene(const String &file); - - private: - /** \brief Set to true after initialization is complete. */ - bool init_; - - Ptr widget_actor_map_; - - Vec2i win_size_; - Vec2i win_pos_; - Vec2i max_win_size_; - - /** \brief Interactor style internal method. Gets called whenever a key is pressed. */ virtual void OnChar(); - - // Keyboard events virtual void OnKeyDown(); virtual void OnKeyUp(); - // mouse button events virtual void OnMouseMove(); virtual void OnLeftButtonDown(); virtual void OnLeftButtonUp(); @@ -95,15 +72,75 @@ namespace cv virtual void OnRightButtonUp(); virtual void OnMouseWheelForward(); virtual void OnMouseWheelBackward(); - - /** \brief Interactor style internal method. Gets called periodically if a timer is set. */ virtual void OnTimer(); + virtual void Rotate(); + virtual void Spin(); + virtual void Pan(); + virtual void Dolly(); + + vtkSetMacro(FlyMode,bool) + vtkGetMacro(FlyMode,bool) + + + vtkSetMacro(MotionFactor, double) + vtkGetMacro(MotionFactor, double) + + void registerMouseCallback(void (*callback)(const MouseEvent&, void*), void* cookie = 0); + void registerKeyboardCallback(void (*callback)(const KeyboardEvent&, void*), void * cookie = 0); + + void setWidgetActorMap(const Ptr& actors) { widget_actor_map_ = actors; } + void saveScreenshot(const String &file); + void exportScene(const String &file); + void exportScene(); + void changePointsSize(float delta); + void setRepresentationToPoints(); + void printCameraParams(); + void toggleFullScreen(); + void resetViewerPose(); + void toggleStereo(); + void printHelp(); + + // Set the basic unit step size : by default 1/250 of bounding diagonal + vtkSetMacro(MotionStepSize,double) + vtkGetMacro(MotionStepSize,double) + + // Set acceleration factor when shift key is applied : default 10 + vtkSetMacro(MotionAccelerationFactor,double) + vtkGetMacro(MotionAccelerationFactor,double) + + // Set the basic angular unit for turning : efault 1 degree + vtkSetMacro(AngleStepSize,double) + vtkGetMacro(AngleStepSize,double) + + private: + Ptr widget_actor_map_; + + Vec2i win_size_; + Vec2i win_pos_; + Vec2i max_win_size_; + void zoomIn(); void zoomOut(); - /** \brief True if we're using red-blue colors for anaglyphic stereo, false if magenta-green. */ - bool stereo_anaglyph_mask_default_; + protected: + vtkVizInteractorStyle(); + ~vtkVizInteractorStyle(); + + virtual void Dolly(double factor); + + void Fly(); + void FlyByMouse(); + void FlyByKey(); + void SetupMotionVars(); + void MotionAlongVector(const Vec3d& vector, double amount, vtkCamera* cam); + + private: + vtkVizInteractorStyle(const vtkVizInteractorStyle&); + vtkVizInteractorStyle& operator=(const vtkVizInteractorStyle&); + + //! True for red-blue colors, false for magenta-green. + bool stereo_anaglyph_redblue_; void (*keyboardCallback_)(const KeyboardEvent&, void*); void *keyboard_callback_cookie_; @@ -111,7 +148,20 @@ namespace cv void (*mouseCallback_)(const MouseEvent&, void*); void *mouse_callback_cookie_; + bool FlyMode; + double MotionFactor; + int getModifiers(); + + // from fly + unsigned char KeysDown; + double DiagonalLength; + double MotionStepSize; + double MotionUserScale; + double MotionAccelerationFactor; + double AngleStepSize; + double DeltaYaw; + double DeltaPitch; }; } } From 29deff8707c825d1992a3bda6569a3e5eefb1a36 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 2 Mar 2014 16:08:37 +0400 Subject: [PATCH 166/293] ability to merge logs with intersections only --- modules/ts/misc/summary.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/ts/misc/summary.py b/modules/ts/misc/summary.py index 4e0cb7e7f5..4cbb8901f4 100755 --- a/modules/ts/misc/summary.py +++ b/modules/ts/misc/summary.py @@ -44,6 +44,7 @@ if __name__ == "__main__": parser.add_option("", "--match", dest="match", default=None) parser.add_option("", "--match-replace", dest="match_replace", default="") parser.add_option("", "--regressions-only", dest="regressionsOnly", default=None, metavar="X-FACTOR", help="show only tests with performance regressions not") + parser.add_option("", "--intersect-logs", dest="intersect_logs", default=False, help="show only tests present in all log files") (options, args) = parser.parse_args() options.generateHtml = detectHtmlOutputType(options.format) @@ -162,6 +163,10 @@ if __name__ == "__main__": for i in range(setsCount): case = cases[i] if case is None: + if options.intersect_logs: + needNewRow = False + break + tbl.newCell(str(i), "-") if options.calc_relatives and i > 0: tbl.newCell(str(i) + "%", "-") From 099ea91823f75ccab2976031140c1f25e1ebb8bb Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 3 Mar 2014 19:37:47 +0400 Subject: [PATCH 167/293] typos --- modules/calib3d/perf/perf_pnp.cpp | 2 +- modules/calib3d/test/test_solvepnp_ransac.cpp | 2 +- modules/contrib/src/imagelogpolprojection.cpp | 4 ++-- modules/contrib/src/lda.cpp | 2 +- modules/contrib/src/templatebuffer.hpp | 10 +++++----- modules/core/include/opencv2/core/core_c.h | 2 +- modules/core/src/array.cpp | 2 +- .../doc/feature_detection_and_description.rst | 6 +++--- .../include/opencv2/features2d/features2d.hpp | 4 ++-- modules/features2d/src/evaluation.cpp | 2 +- .../gpu/doc/feature_detection_and_description.rst | 12 ++++++------ modules/gpu/include/opencv2/gpu/gpu.hpp | 12 ++++++------ modules/gpu/perf/perf_features2d.cpp | 4 ++-- modules/gpu/perf/perf_imgproc.cpp | 6 +++--- modules/gpu/perf4au/main.cpp | 10 +++++----- modules/gpu/src/cuda/fast.cu | 8 ++++---- modules/gpu/src/fast.cpp | 14 +++++++------- modules/gpu/test/test_features2d.cpp | 14 +++++++------- modules/highgui/src/window_QT.cpp | 2 +- modules/highgui/src/window_carbon.cpp | 2 +- modules/imgproc/doc/feature_detection.rst | 2 +- .../imgproc/include/opencv2/imgproc/imgproc_c.h | 6 +++--- modules/imgproc/src/approx.cpp | 2 +- modules/imgproc/src/canny.cpp | 2 +- modules/ocl/src/opencl/haarobjectdetect.cl | 2 +- modules/ts/include/opencv2/ts/ts.hpp | 2 +- modules/viz/doc/widget.rst | 2 +- .../org/opencv/engine/manager/ManagerActivity.java | 14 +++++++------- samples/cpp/morphology2.cpp | 2 +- samples/gpu/morphology.cpp | 2 +- 30 files changed, 78 insertions(+), 78 deletions(-) diff --git a/modules/calib3d/perf/perf_pnp.cpp b/modules/calib3d/perf/perf_pnp.cpp index e0ffd70cfb..557387d9ad 100644 --- a/modules/calib3d/perf/perf_pnp.cpp +++ b/modules/calib3d/perf/perf_pnp.cpp @@ -126,7 +126,7 @@ PERF_TEST_P(PointsNum, DISABLED_SolvePnPRansac, testing::Values(4, 3*9, 7*13)) Mat tvec; #ifdef HAVE_TBB - // limit concurrency to get determenistic result + // limit concurrency to get deterministic result cv::Ptr one_thread = new tbb::task_scheduler_init(1); #endif diff --git a/modules/calib3d/test/test_solvepnp_ransac.cpp b/modules/calib3d/test/test_solvepnp_ransac.cpp index 6c924a5804..4c8d56a968 100644 --- a/modules/calib3d/test/test_solvepnp_ransac.cpp +++ b/modules/calib3d/test/test_solvepnp_ransac.cpp @@ -271,7 +271,7 @@ TEST(DISABLED_Calib3d_SolvePnPRansac, concurrency) Mat tvec1, tvec2; { - // limit concurrency to get determenistic result + // limit concurrency to get deterministic result cv::theRNG().state = 20121010; cv::Ptr one_thread = new tbb::task_scheduler_init(1); solvePnPRansac(object, image, camera_mat, dist_coef, rvec1, tvec1); diff --git a/modules/contrib/src/imagelogpolprojection.cpp b/modules/contrib/src/imagelogpolprojection.cpp index ed821efa9d..2824949c7c 100644 --- a/modules/contrib/src/imagelogpolprojection.cpp +++ b/modules/contrib/src/imagelogpolprojection.cpp @@ -362,14 +362,14 @@ bool ImageLogPolProjection::_initLogPolarCortexSampling(const double reductionFa //std::cout<<"ImageLogPolProjection::Starting cortex projection"<size();++i) { - double curentValue=(double)*(bufferPTR++); + double currentValue=(double)*(bufferPTR++); // updating "closest to the high threshold" pixel value - double highValueTest=maxThreshold-curentValue; + double highValueTest=maxThreshold-currentValue; if (highValueTest>0) { if (deltaH>highValueTest) { deltaH=highValueTest; - updatedHighValue=curentValue; + updatedHighValue=currentValue; } } // updating "closest to the low threshold" pixel value - double lowValueTest=curentValue-minThreshold; + double lowValueTest=currentValue-minThreshold; if (lowValueTest>0) { if (deltaL>lowValueTest) { deltaL=lowValueTest; - updatedLowValue=curentValue; + updatedLowValue=currentValue; } } } diff --git a/modules/core/include/opencv2/core/core_c.h b/modules/core/include/opencv2/core/core_c.h index d4182d2f73..38abfc409b 100644 --- a/modules/core/include/opencv2/core/core_c.h +++ b/modules/core/include/opencv2/core/core_c.h @@ -105,7 +105,7 @@ CVAPI(void) cvResetImageROI( IplImage* image ); /* Retrieves image ROI */ CVAPI(CvRect) cvGetImageROI( const IplImage* image ); -/* Allocates and initalizes CvMat header */ +/* Allocates and initializes CvMat header */ CVAPI(CvMat*) cvCreateMatHeader( int rows, int cols, int type ); #define CV_AUTOSTEP 0x7fffffff diff --git a/modules/core/src/array.cpp b/modules/core/src/array.cpp index 9c024293eb..a35ad0e2a4 100644 --- a/modules/core/src/array.cpp +++ b/modules/core/src/array.cpp @@ -2897,7 +2897,7 @@ cvCreateImage( CvSize size, int depth, int channels ) } -// initalize IplImage header, allocated by the user +// initialize IplImage header, allocated by the user CV_IMPL IplImage* cvInitImageHeader( IplImage * image, CvSize size, int depth, int channels, int origin, int align ) diff --git a/modules/features2d/doc/feature_detection_and_description.rst b/modules/features2d/doc/feature_detection_and_description.rst index a0272026d6..c05a71d34a 100644 --- a/modules/features2d/doc/feature_detection_and_description.rst +++ b/modules/features2d/doc/feature_detection_and_description.rst @@ -11,9 +11,9 @@ FAST ---- Detects corners using the FAST algorithm -.. ocv:function:: void FAST( InputArray image, vector& keypoints, int threshold, bool nonmaxSupression=true ) +.. ocv:function:: void FAST( InputArray image, vector& keypoints, int threshold, bool nonmaxSuppression=true ) -.. ocv:function:: void FASTX( InputArray image, vector& keypoints, int threshold, bool nonmaxSupression, int type ) +.. ocv:function:: void FASTX( InputArray image, vector& keypoints, int threshold, bool nonmaxSuppression, int type ) :param image: grayscale image where keypoints (corners) are detected. @@ -21,7 +21,7 @@ Detects corners using the FAST algorithm :param threshold: threshold on difference between intensity of the central pixel and pixels of a circle around this pixel. - :param nonmaxSupression: if true, non-maximum suppression is applied to detected corners (keypoints). + :param nonmaxSuppression: if true, non-maximum suppression is applied to detected corners (keypoints). :param type: one of the three neighborhoods as defined in the paper: ``FastFeatureDetector::TYPE_9_16``, ``FastFeatureDetector::TYPE_7_12``, ``FastFeatureDetector::TYPE_5_8`` diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index d598ed6d78..7536128c71 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -566,10 +566,10 @@ protected: //! detects corners using FAST algorithm by E. Rosten CV_EXPORTS void FAST( InputArray image, CV_OUT vector& keypoints, - int threshold, bool nonmaxSupression=true ); + int threshold, bool nonmaxSuppression=true ); CV_EXPORTS void FASTX( InputArray image, CV_OUT vector& keypoints, - int threshold, bool nonmaxSupression, int type ); + int threshold, bool nonmaxSuppression, int type ); class CV_EXPORTS_W FastFeatureDetector : public FeatureDetector { diff --git a/modules/features2d/src/evaluation.cpp b/modules/features2d/src/evaluation.cpp index 44151c03cf..52740e6e50 100644 --- a/modules/features2d/src/evaluation.cpp +++ b/modules/features2d/src/evaluation.cpp @@ -128,7 +128,7 @@ public: Point2f center; Scalar ellipse; // 3 elements a, b, c: ax^2+2bxy+cy^2=1 - Size_ axes; // half lenght of elipse axes + Size_ axes; // half length of ellipse axes Size_ boundingBox; // half sizes of bounding box which sides are parallel to the coordinate axes }; diff --git a/modules/gpu/doc/feature_detection_and_description.rst b/modules/gpu/doc/feature_detection_and_description.rst index 191b0ada0f..66f4250044 100644 --- a/modules/gpu/doc/feature_detection_and_description.rst +++ b/modules/gpu/doc/feature_detection_and_description.rst @@ -24,7 +24,7 @@ Class used for corner detection using the FAST algorithm. :: // all features have same size static const int FEATURE_SIZE = 7; - explicit FAST_GPU(int threshold, bool nonmaxSupression = true, + explicit FAST_GPU(int threshold, bool nonmaxSuppression = true, double keypointsRatio = 0.05); void operator ()(const GpuMat& image, const GpuMat& mask, GpuMat& keypoints); @@ -39,7 +39,7 @@ Class used for corner detection using the FAST algorithm. :: void release(); - bool nonmaxSupression; + bool nonmaxSuppression; int threshold; @@ -61,11 +61,11 @@ gpu::FAST_GPU::FAST_GPU ------------------------------------- Constructor. -.. ocv:function:: gpu::FAST_GPU::FAST_GPU(int threshold, bool nonmaxSupression = true, double keypointsRatio = 0.05) +.. ocv:function:: gpu::FAST_GPU::FAST_GPU(int threshold, bool nonmaxSuppression = true, double keypointsRatio = 0.05) :param threshold: Threshold on difference between intensity of the central pixel and pixels on a circle around this pixel. - :param nonmaxSupression: If it is true, non-maximum suppression is applied to detected corners (keypoints). + :param nonmaxSuppression: If it is true, non-maximum suppression is applied to detected corners (keypoints). :param keypointsRatio: Inner buffer size for keypoints store is determined as (keypointsRatio * image_width * image_height). @@ -115,7 +115,7 @@ Releases inner buffer memory. gpu::FAST_GPU::calcKeyPointsLocation ------------------------------------- -Find keypoints and compute it's response if ``nonmaxSupression`` is true. +Find keypoints and compute it's response if ``nonmaxSuppression`` is true. .. ocv:function:: int gpu::FAST_GPU::calcKeyPointsLocation(const GpuMat& image, const GpuMat& mask) @@ -185,7 +185,7 @@ Class for extracting ORB features and descriptors from an image. :: int descriptorSize() const; void setParams(size_t n_features, const ORB::CommonParams& detector_params); - void setFastParams(int threshold, bool nonmaxSupression = true); + void setFastParams(int threshold, bool nonmaxSuppression = true); void release(); diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index 053bcdbc51..0ab0fb1cd9 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -1580,7 +1580,7 @@ public: // all features have same size static const int FEATURE_SIZE = 7; - explicit FAST_GPU(int threshold, bool nonmaxSupression = true, double keypointsRatio = 0.05); + explicit FAST_GPU(int threshold, bool nonmaxSuppression = true, double keypointsRatio = 0.05); //! finds the keypoints using FAST detector //! supports only CV_8UC1 images @@ -1596,19 +1596,19 @@ public: //! release temporary buffer's memory void release(); - bool nonmaxSupression; + bool nonmaxSuppression; int threshold; //! max keypoints = keypointsRatio * img.size().area() double keypointsRatio; - //! find keypoints and compute it's response if nonmaxSupression is true + //! find keypoints and compute it's response if nonmaxSuppression is true //! return count of detected keypoints int calcKeyPointsLocation(const GpuMat& image, const GpuMat& mask); //! get final array of keypoints - //! performs nonmax supression if needed + //! performs nonmax suppression if needed //! return final count of keypoints int getKeyPoints(GpuMat& keypoints); @@ -1670,10 +1670,10 @@ public: //! returns the descriptor size in bytes inline int descriptorSize() const { return kBytes; } - inline void setFastParams(int threshold, bool nonmaxSupression = true) + inline void setFastParams(int threshold, bool nonmaxSuppression = true) { fastDetector_.threshold = threshold; - fastDetector_.nonmaxSupression = nonmaxSupression; + fastDetector_.nonmaxSuppression = nonmaxSuppression; } //! release temporary buffer's memory diff --git a/modules/gpu/perf/perf_features2d.cpp b/modules/gpu/perf/perf_features2d.cpp index feee3a939e..45823cef2c 100644 --- a/modules/gpu/perf/perf_features2d.cpp +++ b/modules/gpu/perf/perf_features2d.cpp @@ -49,9 +49,9 @@ using namespace perf; ////////////////////////////////////////////////////////////////////// // FAST -DEF_PARAM_TEST(Image_Threshold_NonMaxSupression, string, int, bool); +DEF_PARAM_TEST(Image_Threshold_NonMaxSuppression, string, int, bool); -PERF_TEST_P(Image_Threshold_NonMaxSupression, Features2D_FAST, +PERF_TEST_P(Image_Threshold_NonMaxSuppression, Features2D_FAST, Combine(Values("gpu/perf/aloe.png"), Values(20), Bool())) diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index be0e312a02..fa0a42cfef 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -1746,7 +1746,7 @@ PERF_TEST_P(Image, ImgProc_HoughLinesP, const float rho = 1.0f; const float theta = static_cast(CV_PI / 180.0); const int threshold = 100; - const int minLineLenght = 50; + const int minLineLength = 50; const int maxLineGap = 5; const cv::Mat image = cv::imread(fileName, cv::IMREAD_GRAYSCALE); @@ -1761,7 +1761,7 @@ PERF_TEST_P(Image, ImgProc_HoughLinesP, cv::gpu::GpuMat d_lines; cv::gpu::HoughLinesBuf d_buf; - TEST_CYCLE() cv::gpu::HoughLinesP(d_mask, d_lines, d_buf, rho, theta, minLineLenght, maxLineGap); + TEST_CYCLE() cv::gpu::HoughLinesP(d_mask, d_lines, d_buf, rho, theta, minLineLength, maxLineGap); cv::Mat gpu_lines(d_lines); cv::Vec4i* begin = gpu_lines.ptr(); @@ -1773,7 +1773,7 @@ PERF_TEST_P(Image, ImgProc_HoughLinesP, { std::vector cpu_lines; - TEST_CYCLE() cv::HoughLinesP(mask, cpu_lines, rho, theta, threshold, minLineLenght, maxLineGap); + TEST_CYCLE() cv::HoughLinesP(mask, cpu_lines, rho, theta, threshold, minLineLength, maxLineGap); SANITY_CHECK(cpu_lines); } diff --git a/modules/gpu/perf4au/main.cpp b/modules/gpu/perf4au/main.cpp index 8c68385e1e..ceee61e1d5 100644 --- a/modules/gpu/perf4au/main.cpp +++ b/modules/gpu/perf4au/main.cpp @@ -74,7 +74,7 @@ PERF_TEST_P(Image, HoughLinesP, testing::Values(std::string("im1_1280x800.jpg")) const float rho = 1.f; const float theta = 1.f; const int threshold = 40; - const int minLineLenght = 20; + const int minLineLength = 20; const int maxLineGap = 5; cv::Mat image = cv::imread(fileName, cv::IMREAD_GRAYSCALE); @@ -85,11 +85,11 @@ PERF_TEST_P(Image, HoughLinesP, testing::Values(std::string("im1_1280x800.jpg")) cv::gpu::GpuMat d_lines; cv::gpu::HoughLinesBuf d_buf; - cv::gpu::HoughLinesP(d_image, d_lines, d_buf, rho, theta, minLineLenght, maxLineGap); + cv::gpu::HoughLinesP(d_image, d_lines, d_buf, rho, theta, minLineLength, maxLineGap); TEST_CYCLE() { - cv::gpu::HoughLinesP(d_image, d_lines, d_buf, rho, theta, minLineLenght, maxLineGap); + cv::gpu::HoughLinesP(d_image, d_lines, d_buf, rho, theta, minLineLength, maxLineGap); } } else @@ -98,11 +98,11 @@ PERF_TEST_P(Image, HoughLinesP, testing::Values(std::string("im1_1280x800.jpg")) cv::Canny(image, mask, 50, 100); std::vector lines; - cv::HoughLinesP(mask, lines, rho, theta, threshold, minLineLenght, maxLineGap); + cv::HoughLinesP(mask, lines, rho, theta, threshold, minLineLength, maxLineGap); TEST_CYCLE() { - cv::HoughLinesP(mask, lines, rho, theta, threshold, minLineLenght, maxLineGap); + cv::HoughLinesP(mask, lines, rho, theta, threshold, minLineLength, maxLineGap); } } diff --git a/modules/gpu/src/cuda/fast.cu b/modules/gpu/src/cuda/fast.cu index 9bf1f7ad93..f72b1fc04b 100644 --- a/modules/gpu/src/cuda/fast.cu +++ b/modules/gpu/src/cuda/fast.cu @@ -318,9 +318,9 @@ namespace cv { namespace gpu { namespace device } /////////////////////////////////////////////////////////////////////////// - // nonmaxSupression + // nonmaxSuppression - __global__ void nonmaxSupression(const short2* kpLoc, int count, const PtrStepSzi scoreMat, short2* locFinal, float* responseFinal) + __global__ void nonmaxSuppression(const short2* kpLoc, int count, const PtrStepSzi scoreMat, short2* locFinal, float* responseFinal) { #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) @@ -356,7 +356,7 @@ namespace cv { namespace gpu { namespace device #endif } - int nonmaxSupression_gpu(const short2* kpLoc, int count, PtrStepSzi score, short2* loc, float* response) + int nonmaxSuppression_gpu(const short2* kpLoc, int count, PtrStepSzi score, short2* loc, float* response) { void* counter_ptr; cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, g_counter) ); @@ -368,7 +368,7 @@ namespace cv { namespace gpu { namespace device cudaSafeCall( cudaMemset(counter_ptr, 0, sizeof(unsigned int)) ); - nonmaxSupression<<>>(kpLoc, count, score, loc, response); + nonmaxSuppression<<>>(kpLoc, count, score, loc, response); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); diff --git a/modules/gpu/src/fast.cpp b/modules/gpu/src/fast.cpp index a24a69962b..37ca92dc45 100644 --- a/modules/gpu/src/fast.cpp +++ b/modules/gpu/src/fast.cpp @@ -59,8 +59,8 @@ int cv::gpu::FAST_GPU::getKeyPoints(GpuMat&) { throw_nogpu(); return 0; } #else /* !defined (HAVE_CUDA) */ -cv::gpu::FAST_GPU::FAST_GPU(int _threshold, bool _nonmaxSupression, double _keypointsRatio) : - nonmaxSupression(_nonmaxSupression), threshold(_threshold), keypointsRatio(_keypointsRatio), count_(0) +cv::gpu::FAST_GPU::FAST_GPU(int _threshold, bool _nonmaxSuppression, double _keypointsRatio) : + nonmaxSuppression(_nonmaxSuppression), threshold(_threshold), keypointsRatio(_keypointsRatio), count_(0) { } @@ -114,7 +114,7 @@ namespace cv { namespace gpu { namespace device namespace fast { int calcKeypoints_gpu(PtrStepSzb img, PtrStepSzb mask, short2* kpLoc, int maxKeypoints, PtrStepSzi score, int threshold); - int nonmaxSupression_gpu(const short2* kpLoc, int count, PtrStepSzi score, short2* loc, float* response); + int nonmaxSuppression_gpu(const short2* kpLoc, int count, PtrStepSzi score, short2* loc, float* response); } }}} @@ -129,13 +129,13 @@ int cv::gpu::FAST_GPU::calcKeyPointsLocation(const GpuMat& img, const GpuMat& ma ensureSizeIsEnough(1, maxKeypoints, CV_16SC2, kpLoc_); - if (nonmaxSupression) + if (nonmaxSuppression) { ensureSizeIsEnough(img.size(), CV_32SC1, score_); score_.setTo(Scalar::all(0)); } - count_ = calcKeypoints_gpu(img, mask, kpLoc_.ptr(), maxKeypoints, nonmaxSupression ? score_ : PtrStepSzi(), threshold); + count_ = calcKeypoints_gpu(img, mask, kpLoc_.ptr(), maxKeypoints, nonmaxSuppression ? score_ : PtrStepSzi(), threshold); count_ = std::min(count_, maxKeypoints); return count_; @@ -150,8 +150,8 @@ int cv::gpu::FAST_GPU::getKeyPoints(GpuMat& keypoints) ensureSizeIsEnough(ROWS_COUNT, count_, CV_32FC1, keypoints); - if (nonmaxSupression) - return nonmaxSupression_gpu(kpLoc_.ptr(), count_, score_, keypoints.ptr(LOCATION_ROW), keypoints.ptr(RESPONSE_ROW)); + if (nonmaxSuppression) + return nonmaxSuppression_gpu(kpLoc_.ptr(), count_, score_, keypoints.ptr(LOCATION_ROW), keypoints.ptr(RESPONSE_ROW)); GpuMat locRow(1, count_, kpLoc_.type(), keypoints.ptr(0)); kpLoc_.colRange(0, count_).copyTo(locRow); diff --git a/modules/gpu/test/test_features2d.cpp b/modules/gpu/test/test_features2d.cpp index dfa3afa381..697483657e 100644 --- a/modules/gpu/test/test_features2d.cpp +++ b/modules/gpu/test/test_features2d.cpp @@ -52,20 +52,20 @@ using namespace cvtest; namespace { IMPLEMENT_PARAM_CLASS(FAST_Threshold, int) - IMPLEMENT_PARAM_CLASS(FAST_NonmaxSupression, bool) + IMPLEMENT_PARAM_CLASS(FAST_NonmaxSuppression, bool) } -PARAM_TEST_CASE(FAST, cv::gpu::DeviceInfo, FAST_Threshold, FAST_NonmaxSupression) +PARAM_TEST_CASE(FAST, cv::gpu::DeviceInfo, FAST_Threshold, FAST_NonmaxSuppression) { cv::gpu::DeviceInfo devInfo; int threshold; - bool nonmaxSupression; + bool nonmaxSuppression; virtual void SetUp() { devInfo = GET_PARAM(0); threshold = GET_PARAM(1); - nonmaxSupression = GET_PARAM(2); + nonmaxSuppression = GET_PARAM(2); cv::gpu::setDevice(devInfo.deviceID()); } @@ -77,7 +77,7 @@ GPU_TEST_P(FAST, Accuracy) ASSERT_FALSE(image.empty()); cv::gpu::FAST_GPU fast(threshold); - fast.nonmaxSupression = nonmaxSupression; + fast.nonmaxSuppression = nonmaxSuppression; if (!supportFeature(devInfo, cv::gpu::GLOBAL_ATOMICS)) { @@ -97,7 +97,7 @@ GPU_TEST_P(FAST, Accuracy) fast(loadMat(image), cv::gpu::GpuMat(), keypoints); std::vector keypoints_gold; - cv::FAST(image, keypoints_gold, threshold, nonmaxSupression); + cv::FAST(image, keypoints_gold, threshold, nonmaxSuppression); ASSERT_KEYPOINTS_EQ(keypoints_gold, keypoints); } @@ -106,7 +106,7 @@ GPU_TEST_P(FAST, Accuracy) INSTANTIATE_TEST_CASE_P(GPU_Features2D, FAST, testing::Combine( ALL_DEVICES, testing::Values(FAST_Threshold(25), FAST_Threshold(50)), - testing::Values(FAST_NonmaxSupression(false), FAST_NonmaxSupression(true)))); + testing::Values(FAST_NonmaxSuppression(false), FAST_NonmaxSuppression(true)))); ///////////////////////////////////////////////////////////////////////////////////////////////// // ORB diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index 84af3941a3..64ebf08701 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -1536,7 +1536,7 @@ CvWindow::CvWindow(QString name, int arg2) setWindowTitle(name); setObjectName(name); - setFocus( Qt::PopupFocusReason ); //#1695 arrow keys are not recieved without the explicit focus + setFocus( Qt::PopupFocusReason ); //#1695 arrow keys are not received without the explicit focus resize(400, 300); setMinimumSize(1, 1); diff --git a/modules/highgui/src/window_carbon.cpp b/modules/highgui/src/window_carbon.cpp index 722df2cddd..3d092e736e 100644 --- a/modules/highgui/src/window_carbon.cpp +++ b/modules/highgui/src/window_carbon.cpp @@ -569,7 +569,7 @@ static int icvCreateTrackbar (const char* trackbar_name, //pad size maxvalue in pixel Point qdSize; - char valueinchar[strlen(trackbar_name)+1 +1 +1+nbDigit+1];//lenght+\n +space +(+nbDigit+) + char valueinchar[strlen(trackbar_name)+1 +1 +1+nbDigit+1];//length+\n +space +(+nbDigit+) sprintf(valueinchar, "%s (%d)",trackbar_name, trackbar->maxval); SInt16 baseline; CFStringRef text = CFStringCreateWithCString(NULL,valueinchar,kCFStringEncodingASCII); diff --git a/modules/imgproc/doc/feature_detection.rst b/modules/imgproc/doc/feature_detection.rst index 4f922f2a7c..861c16432a 100644 --- a/modules/imgproc/doc/feature_detection.rst +++ b/modules/imgproc/doc/feature_detection.rst @@ -101,7 +101,7 @@ Harris edge detector. .. ocv:pyfunction:: cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]]) -> dst -.. ocv:cfunction:: void cvCornerHarris( const CvArr* image, CvArr* harris_responce, int block_size, int aperture_size=3, double k=0.04 ) +.. ocv:cfunction:: void cvCornerHarris( const CvArr* image, CvArr* harris_response, int block_size, int aperture_size=3, double k=0.04 ) .. ocv:pyoldfunction:: cv.CornerHarris(image, harris_dst, blockSize, aperture_size=3, k=0.04) -> None diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h index 4ba1b2b261..46d9f01393 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h +++ b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h @@ -304,7 +304,7 @@ CVAPI(int) cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_c int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE), CvPoint offset CV_DEFAULT(cvPoint(0,0))); -/* Initalizes contour retrieving process. +/* Initializes contour retrieving process. Calls cvStartFindContours. Calls cvFindNextContour until null pointer is returned or some other condition becomes true. @@ -334,7 +334,7 @@ CVAPI(CvSeq*) cvApproxChains( CvSeq* src_seq, CvMemStorage* storage, int minimal_perimeter CV_DEFAULT(0), int recursive CV_DEFAULT(0)); -/* Initalizes Freeman chain reader. +/* Initializes Freeman chain reader. The reader is used to iteratively get coordinates of all the chain points. If the Freeman codes should be read as is, a simple sequence reader should be used */ CVAPI(void) cvStartReadChainPoints( CvChain* chain, CvChainPtReader* reader ); @@ -573,7 +573,7 @@ CVAPI(void) cvCornerMinEigenVal( const CvArr* image, CvArr* eigenval, /* Harris corner detector: Calculates det(M) - k*(trace(M)^2), where M is 2x2 gradient covariation matrix for each pixel */ -CVAPI(void) cvCornerHarris( const CvArr* image, CvArr* harris_responce, +CVAPI(void) cvCornerHarris( const CvArr* image, CvArr* harris_response, int block_size, int aperture_size CV_DEFAULT(3), double k CV_DEFAULT(0.04) ); diff --git a/modules/imgproc/src/approx.cpp b/modules/imgproc/src/approx.cpp index c2831ca70d..8bd76c3cb0 100644 --- a/modules/imgproc/src/approx.cpp +++ b/modules/imgproc/src/approx.cpp @@ -220,7 +220,7 @@ CvSeq* icvApproximateChainTC89( CvChain* chain, int header_size, current = temp.next; /* Pass 2. - Performs non-maxima supression */ + Performs non-maxima suppression */ do { int k2 = current->k >> 1; diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index 44fd42a2a4..2161db91ca 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -171,7 +171,7 @@ void cv::Canny( InputArray _src, OutputArray _dst, #define CANNY_PUSH(d) *(d) = uchar(2), *stack_top++ = (d) #define CANNY_POP(d) (d) = *--stack_top - // calculate magnitude and angle of gradient, perform non-maxima supression. + // calculate magnitude and angle of gradient, perform non-maxima suppression. // fill the map with one of the following values: // 0 - the pixel might belong to an edge // 1 - the pixel can not belong to an edge diff --git a/modules/ocl/src/opencl/haarobjectdetect.cl b/modules/ocl/src/opencl/haarobjectdetect.cl index 8464a580bf..39d11b0e72 100644 --- a/modules/ocl/src/opencl/haarobjectdetect.cl +++ b/modules/ocl/src/opencl/haarobjectdetect.cl @@ -217,7 +217,7 @@ __kernel void gpuRunHaarClassifierCascadePacked( (SumL[M0(n0.x)+lcl_off] - SumL[M1(n0.x)+lcl_off] - SumL[M0(n0.y)+lcl_off] + SumL[M1(n0.y)+lcl_off]) * as_float(n1.z) + (SumL[M0(n0.z)+lcl_off] - SumL[M1(n0.z)+lcl_off] - SumL[M0(n0.w)+lcl_off] + SumL[M1(n0.w)+lcl_off]) * as_float(n1.w) + (SumL[M0(n1.x)+lcl_off] - SumL[M1(n1.x)+lcl_off] - SumL[M0(n1.y)+lcl_off] + SumL[M1(n1.y)+lcl_off]) * as_float(n2.x); - //accumulate stage responce + //accumulate stage response stage_sum += (classsum >= nodethreshold) ? as_float(n2.w) : as_float(n2.z); } result = (stage_sum >= stagethreshold); diff --git a/modules/ts/include/opencv2/ts/ts.hpp b/modules/ts/include/opencv2/ts/ts.hpp index 8ea1ad93ba..f4cd3ffbcd 100644 --- a/modules/ts/include/opencv2/ts/ts.hpp +++ b/modules/ts/include/opencv2/ts/ts.hpp @@ -372,7 +372,7 @@ public: // processing time (in this case there should be possibility to interrupt such a function FAIL_HANG=-13, - // unexpected responce on passing bad arguments to the tested function + // unexpected response on passing bad arguments to the tested function // (the function crashed, proceed succesfully (while it should not), or returned // error code that is different from what is expected) FAIL_BAD_ARG_CHECK=-14, diff --git a/modules/viz/doc/widget.rst b/modules/viz/doc/widget.rst index 008e0e68a5..7f8926d3f9 100644 --- a/modules/viz/doc/widget.rst +++ b/modules/viz/doc/widget.rst @@ -397,7 +397,7 @@ This 3D Widget defines a cone. :: { public: //! create default cone, oriented along x-axis with center of its base located at origin - WCone(double lenght, double radius, int resolution = 6.0, const Color &color = Color::white()); + WCone(double length, double radius, int resolution = 6.0, const Color &color = Color::white()); //! creates repositioned cone WCone(double radius, const Point3d& center, const Point3d& tip, int resolution = 6.0, const Color &color = Color::white()); diff --git a/platforms/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java b/platforms/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java index 8e8389dcc7..8d40242033 100644 --- a/platforms/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java +++ b/platforms/android/service/engine/src/org/opencv/engine/manager/ManagerActivity.java @@ -214,12 +214,12 @@ public class ManagerActivity extends Activity } }); - mPackageChangeReciever = new BroadcastReceiver() { + mPackageChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - Log.d("OpenCVManager/Reciever", "Bradcast message " + intent.getAction() + " reciever"); - Log.d("OpenCVManager/Reciever", "Filling package list on broadcast message"); + Log.d("OpenCVManager/Receiver", "Broadcast message " + intent.getAction() + " receiver"); + Log.d("OpenCVManager/Receiver", "Filling package list on broadcast message"); if (!bindService(new Intent("org.opencv.engine.BIND"), new OpenCVEngineServiceConnection(), Context.BIND_AUTO_CREATE)) { TextView EngineVersionView = (TextView)findViewById(R.id.EngineVersionValue); @@ -235,14 +235,14 @@ public class ManagerActivity extends Activity filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_REPLACED); - registerReceiver(mPackageChangeReciever, filter); + registerReceiver(mPackageChangeReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); - if (mPackageChangeReciever != null) - unregisterReceiver(mPackageChangeReciever); + if (mPackageChangeReceiver != null) + unregisterReceiver(mPackageChangeReceiver); } @Override @@ -273,7 +273,7 @@ public class ManagerActivity extends Activity protected int ManagerApiLevel = 0; protected String ManagerVersion; - protected BroadcastReceiver mPackageChangeReciever = null; + protected BroadcastReceiver mPackageChangeReceiver = null; protected class OpenCVEngineServiceConnection implements ServiceConnection { diff --git a/samples/cpp/morphology2.cpp b/samples/cpp/morphology2.cpp index 330f4e0643..a7793008a8 100644 --- a/samples/cpp/morphology2.cpp +++ b/samples/cpp/morphology2.cpp @@ -12,7 +12,7 @@ static void help() printf("\nShow off image morphology: erosion, dialation, open and close\n" "Call:\n morphology2 [image]\n" - "This program also shows use of rect, elipse and cross kernels\n\n"); + "This program also shows use of rect, ellipse and cross kernels\n\n"); printf( "Hot keys: \n" "\tESC - quit the program\n" "\tr - use rectangle structuring element\n" diff --git a/samples/gpu/morphology.cpp b/samples/gpu/morphology.cpp index 36f518b70d..2596b2d0f5 100644 --- a/samples/gpu/morphology.cpp +++ b/samples/gpu/morphology.cpp @@ -13,7 +13,7 @@ static void help() printf("\nShow off image morphology: erosion, dialation, open and close\n" "Call:\n morphology2 [image]\n" - "This program also shows use of rect, elipse and cross kernels\n\n"); + "This program also shows use of rect, ellipse and cross kernels\n\n"); printf( "Hot keys: \n" "\tESC - quit the program\n" "\tr - use rectangle structuring element\n" From b184d7f27b9115b7532c7e021e11051a74e93fde Mon Sep 17 00:00:00 2001 From: Dave Hughes Date: Wed, 5 Mar 2014 02:44:40 +0000 Subject: [PATCH 168/293] Fix for #3554 v4l2_scan_controls_enumerate_menu is unused and causes ioctl error on RaspberryPi and possibly other Video4Linux variants. See http://www.raspberrypi.org/forum/viewtopic.php?f=43&t=65026 for more detail. --- modules/highgui/src/cap_libv4l.cpp | 29 ----------------------------- modules/highgui/src/cap_v4l.cpp | 24 ------------------------ 2 files changed, 53 deletions(-) diff --git a/modules/highgui/src/cap_libv4l.cpp b/modules/highgui/src/cap_libv4l.cpp index 91047de1f1..e7aa5b5dfe 100644 --- a/modules/highgui/src/cap_libv4l.cpp +++ b/modules/highgui/src/cap_libv4l.cpp @@ -321,7 +321,6 @@ typedef struct CvCaptureCAM_V4L struct v4l2_control control; enum v4l2_buf_type type; struct v4l2_queryctrl queryctrl; - struct v4l2_querymenu querymenu; /* V4L2 control variables */ v4l2_ctrl_range** v4l2_ctrl_ranges; @@ -491,25 +490,6 @@ static int try_init_v4l2(CvCaptureCAM_V4L* capture, char *deviceName) } -static void v4l2_scan_controls_enumerate_menu(CvCaptureCAM_V4L* capture) -{ -// printf (" Menu items:\n"); - CLEAR (capture->querymenu); - capture->querymenu.id = capture->queryctrl.id; - for (capture->querymenu.index = capture->queryctrl.minimum; - (int)capture->querymenu.index <= capture->queryctrl.maximum; - capture->querymenu.index++) - { - if (0 == xioctl (capture->deviceHandle, VIDIOC_QUERYMENU, - &capture->querymenu)) - { - //printf (" %s\n", capture->querymenu.name); - } else { - perror ("VIDIOC_QUERYMENU"); - } - } -} - static void v4l2_free_ranges(CvCaptureCAM_V4L* capture) { int i; if (capture->v4l2_ctrl_ranges != NULL) { @@ -590,9 +570,6 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) { if(capture->queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { continue; } - if (capture->queryctrl.type == V4L2_CTRL_TYPE_MENU) { - v4l2_scan_controls_enumerate_menu(capture); - } if(capture->queryctrl.type != V4L2_CTRL_TYPE_INTEGER && capture->queryctrl.type != V4L2_CTRL_TYPE_BOOLEAN && capture->queryctrl.type != V4L2_CTRL_TYPE_MENU) { @@ -613,9 +590,6 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) { if(capture->queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { continue; } - if (capture->queryctrl.type == V4L2_CTRL_TYPE_MENU) { - v4l2_scan_controls_enumerate_menu(capture); - } if(capture->queryctrl.type != V4L2_CTRL_TYPE_INTEGER && capture->queryctrl.type != V4L2_CTRL_TYPE_BOOLEAN && capture->queryctrl.type != V4L2_CTRL_TYPE_MENU) { @@ -637,9 +611,6 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) { continue; } - if (capture->queryctrl.type == V4L2_CTRL_TYPE_MENU) { - v4l2_scan_controls_enumerate_menu(capture); - } if(capture->queryctrl.type != V4L2_CTRL_TYPE_INTEGER && capture->queryctrl.type != V4L2_CTRL_TYPE_BOOLEAN && diff --git a/modules/highgui/src/cap_v4l.cpp b/modules/highgui/src/cap_v4l.cpp index 045c6f889c..c9fca05819 100644 --- a/modules/highgui/src/cap_v4l.cpp +++ b/modules/highgui/src/cap_v4l.cpp @@ -325,7 +325,6 @@ typedef struct CvCaptureCAM_V4L struct v4l2_control control; enum v4l2_buf_type type; struct v4l2_queryctrl queryctrl; - struct v4l2_querymenu querymenu; struct timeval timestamp; @@ -641,24 +640,6 @@ static int autosetup_capture_mode_v4l(CvCaptureCAM_V4L* capture) #ifdef HAVE_CAMV4L2 -static void v4l2_scan_controls_enumerate_menu(CvCaptureCAM_V4L* capture) -{ -// printf (" Menu items:\n"); - CLEAR (capture->querymenu); - capture->querymenu.id = capture->queryctrl.id; - for (capture->querymenu.index = capture->queryctrl.minimum; - (int)capture->querymenu.index <= capture->queryctrl.maximum; - capture->querymenu.index++) - { - if (0 == ioctl (capture->deviceHandle, VIDIOC_QUERYMENU, - &capture->querymenu)) - { -// printf (" %s\n", capture->querymenu.name); - } else { - perror ("VIDIOC_QUERYMENU"); - } - } -} static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) { @@ -723,8 +704,6 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) capture->v4l2_exposure_max = capture->queryctrl.maximum; } - if (capture->queryctrl.type == V4L2_CTRL_TYPE_MENU) - v4l2_scan_controls_enumerate_menu(capture); } else { @@ -793,9 +772,6 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) capture->v4l2_exposure_max = capture->queryctrl.maximum; } - if (capture->queryctrl.type == V4L2_CTRL_TYPE_MENU) - v4l2_scan_controls_enumerate_menu(capture); - } else { if (errno == EINVAL) From 3c1917771b6f7ad532c90ab4691d2eee0cb5e0f6 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 5 Mar 2014 16:31:41 +0400 Subject: [PATCH 169/293] modified OpenCL SURF API and the tests in 2.4.x to prove that it gives different from CPU results --- .../nonfree/include/opencv2/nonfree/ocl.hpp | 22 ++++- modules/nonfree/src/nonfree_init.cpp | 14 ++++ modules/nonfree/src/surf_ocl.cpp | 80 +++++++++++++++++++ modules/nonfree/test/test_features2d.cpp | 16 ++-- .../test_rotation_and_scale_invariance.cpp | 18 +++-- 5 files changed, 136 insertions(+), 14 deletions(-) diff --git a/modules/nonfree/include/opencv2/nonfree/ocl.hpp b/modules/nonfree/include/opencv2/nonfree/ocl.hpp index 78b6b466c4..ad0c16ac48 100644 --- a/modules/nonfree/include/opencv2/nonfree/ocl.hpp +++ b/modules/nonfree/include/opencv2/nonfree/ocl.hpp @@ -53,7 +53,7 @@ namespace cv //! Speeded up robust features, port from GPU module. ////////////////////////////////// SURF ////////////////////////////////////////// - class CV_EXPORTS SURF_OCL + class CV_EXPORTS SURF_OCL : public Feature2D { public: enum KeypointLayout @@ -72,10 +72,13 @@ namespace cv SURF_OCL(); //! the full constructor taking all the necessary parameters explicit SURF_OCL(double _hessianThreshold, int _nOctaves = 4, - int _nOctaveLayers = 2, bool _extended = false, float _keypointsRatio = 0.01f, bool _upright = false); + int _nOctaveLayers = 2, bool _extended = true, float _keypointsRatio = 0.01f, bool _upright = false); //! returns the descriptor size in float's (64 or 128) int descriptorSize() const; + + int descriptorType() const; + //! upload host keypoints to device memory void uploadKeypoints(const vector &keypoints, oclMat &keypointsocl); //! download keypoints from device to host memory @@ -103,6 +106,17 @@ namespace cv void operator()(const oclMat &img, const oclMat &mask, std::vector &keypoints, std::vector &descriptors, bool useProvidedKeypoints = false); + //! finds the keypoints using fast hessian detector used in SURF + void operator()(InputArray img, InputArray mask, + CV_OUT vector& keypoints) const; + //! finds the keypoints and computes their descriptors. Optionally it can compute descriptors for the user-provided keypoints + void operator()(InputArray img, InputArray mask, + CV_OUT vector& keypoints, + OutputArray descriptors, + bool useProvidedKeypoints=false) const; + + AlgorithmInfo* info() const; + void releaseMemory(); // SURF parameters @@ -116,7 +130,9 @@ namespace cv oclMat sum, mask1, maskSum, intBuffer; oclMat det, trace; oclMat maxPosBuffer; - + protected: + void detectImpl( const Mat& image, vector& keypoints, const Mat& mask) const; + void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors) const; }; } } diff --git a/modules/nonfree/src/nonfree_init.cpp b/modules/nonfree/src/nonfree_init.cpp index f18e7d81d9..136b362ca4 100644 --- a/modules/nonfree/src/nonfree_init.cpp +++ b/modules/nonfree/src/nonfree_init.cpp @@ -63,6 +63,20 @@ CV_INIT_ALGORITHM(SIFT, "Feature2D.SIFT", obj.info()->addParam(obj, "edgeThreshold", obj.edgeThreshold); obj.info()->addParam(obj, "sigma", obj.sigma)) +#ifdef HAVE_OPENCV_OCL + +namespace ocl { +CV_INIT_ALGORITHM(SURF_OCL, "Feature2D.SURF_OCL", + obj.info()->addParam(obj, "hessianThreshold", obj.hessianThreshold); + obj.info()->addParam(obj, "nOctaves", obj.nOctaves); + obj.info()->addParam(obj, "nOctaveLayers", obj.nOctaveLayers); + obj.info()->addParam(obj, "extended", obj.extended); + obj.info()->addParam(obj, "upright", obj.upright)) +} + +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// bool initModule_nonfree(void) diff --git a/modules/nonfree/src/surf_ocl.cpp b/modules/nonfree/src/surf_ocl.cpp index 293fd84b56..2922c2cee6 100644 --- a/modules/nonfree/src/surf_ocl.cpp +++ b/modules/nonfree/src/surf_ocl.cpp @@ -305,6 +305,11 @@ int cv::ocl::SURF_OCL::descriptorSize() const return extended ? 128 : 64; } +int cv::ocl::SURF_OCL::descriptorType() const +{ + return CV_32F; +} + void cv::ocl::SURF_OCL::uploadKeypoints(const vector &keypoints, oclMat &keypointsGPU) { if (keypoints.empty()) @@ -447,6 +452,81 @@ void cv::ocl::SURF_OCL::operator()(const oclMat &img, const oclMat &mask, vector downloadDescriptors(descriptorsGPU, descriptors); } + +void cv::ocl::SURF_OCL::operator()(InputArray img, InputArray mask, + CV_OUT vector& keypoints) const +{ + this->operator()(img, mask, keypoints, noArray(), false); +} + +void cv::ocl::SURF_OCL::operator()(InputArray img, InputArray mask, vector &keypoints, + OutputArray descriptors, bool useProvidedKeypoints) const +{ + oclMat _img, _mask; + if(img.kind() == _InputArray::OCL_MAT) + _img = *(oclMat*)img.obj; + else + _img.upload(img.getMat()); + if(_img.channels() != 1) + { + oclMat temp; + cvtColor(_img, temp, COLOR_BGR2GRAY); + _img = temp; + } + + if( !mask.empty() ) + { + if(mask.kind() == _InputArray::OCL_MAT) + _mask = *(oclMat*)mask.obj; + else + _mask.upload(mask.getMat()); + } + + SURF_OCL_Invoker surf((SURF_OCL&)*this, _img, _mask); + oclMat keypointsGPU; + + if (!useProvidedKeypoints || !upright) + ((SURF_OCL*)this)->uploadKeypoints(keypoints, keypointsGPU); + + if (!useProvidedKeypoints) + surf.detectKeypoints(keypointsGPU); + else if (!upright) + surf.findOrientation(keypointsGPU); + if(keypointsGPU.cols*keypointsGPU.rows != 0) + ((SURF_OCL*)this)->downloadKeypoints(keypointsGPU, keypoints); + + if( descriptors.needed() ) + { + oclMat descriptorsGPU; + surf.computeDescriptors(keypointsGPU, descriptorsGPU, descriptorSize()); + Size sz = descriptorsGPU.size(); + if( descriptors.kind() == _InputArray::STD_VECTOR ) + { + CV_Assert(descriptors.type() == CV_32F); + std::vector* v = (std::vector*)descriptors.obj; + v->resize(sz.width*sz.height); + Mat m(sz, CV_32F, &v->at(0)); + descriptorsGPU.download(m); + } + else + { + descriptors.create(sz, CV_32F); + Mat m = descriptors.getMat(); + descriptorsGPU.download(m); + } + } +} + +void cv::ocl::SURF_OCL::detectImpl( const Mat& image, vector& keypoints, const Mat& mask) const +{ + (*this)(image, mask, keypoints, noArray(), false); +} + +void cv::ocl::SURF_OCL::computeImpl( const Mat& image, vector& keypoints, Mat& descriptors) const +{ + (*this)(image, Mat(), keypoints, descriptors, true); +} + void cv::ocl::SURF_OCL::releaseMemory() { sum.release(); diff --git a/modules/nonfree/test/test_features2d.cpp b/modules/nonfree/test/test_features2d.cpp index b680d948b8..66a35931ad 100644 --- a/modules/nonfree/test/test_features2d.cpp +++ b/modules/nonfree/test/test_features2d.cpp @@ -50,6 +50,12 @@ const string DETECTOR_DIR = FEATURES2D_DIR + "/feature_detectors"; const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors"; const string IMAGE_FILENAME = "tsukuba.png"; +#ifdef HAVE_OPENCV_OCL +#define SURF_NAME "SURF_OCL" +#else +#define SURF_NAME "SURF" +#endif + /****************************************************************************************\ * Regression tests for feature detectors comparing keypoints. * \****************************************************************************************/ @@ -978,7 +984,7 @@ TEST( Features2d_Detector_SIFT, regression ) TEST( Features2d_Detector_SURF, regression ) { - CV_FeatureDetectorTest test( "detector-surf", FeatureDetector::create("SURF") ); + CV_FeatureDetectorTest test( "detector-surf", FeatureDetector::create(SURF_NAME) ); test.safe_run(); } @@ -995,7 +1001,7 @@ TEST( Features2d_DescriptorExtractor_SIFT, regression ) TEST( Features2d_DescriptorExtractor_SURF, regression ) { CV_DescriptorExtractorTest > test( "descriptor-surf", 0.05f, - DescriptorExtractor::create("SURF") ); + DescriptorExtractor::create(SURF_NAME) ); test.safe_run(); } @@ -1036,10 +1042,10 @@ TEST(Features2d_BruteForceDescriptorMatcher_knnMatch, regression) const int sz = 100; const int k = 3; - Ptr ext = DescriptorExtractor::create("SURF"); + Ptr ext = DescriptorExtractor::create(SURF_NAME); ASSERT_TRUE(ext != NULL); - Ptr det = FeatureDetector::create("SURF"); + Ptr det = FeatureDetector::create(SURF_NAME); //"%YAML:1.0\nhessianThreshold: 8000.\noctaves: 3\noctaveLayers: 4\nupright: 0\n" ASSERT_TRUE(det != NULL); @@ -1144,7 +1150,7 @@ protected: }; TEST(Features2d_SIFTHomographyTest, regression) { CV_DetectPlanarTest test("SIFT", 80); test.safe_run(); } -TEST(Features2d_SURFHomographyTest, regression) { CV_DetectPlanarTest test("SURF", 80); test.safe_run(); } +TEST(Features2d_SURFHomographyTest, regression) { CV_DetectPlanarTest test(SURF_NAME, 80); test.safe_run(); } class FeatureDetectorUsingMaskTest : public cvtest::BaseTest { diff --git a/modules/nonfree/test/test_rotation_and_scale_invariance.cpp b/modules/nonfree/test/test_rotation_and_scale_invariance.cpp index 7ca9e3dd74..255cad313c 100644 --- a/modules/nonfree/test/test_rotation_and_scale_invariance.cpp +++ b/modules/nonfree/test/test_rotation_and_scale_invariance.cpp @@ -48,6 +48,12 @@ using namespace cv; const string IMAGE_TSUKUBA = "/features2d/tsukuba.png"; const string IMAGE_BIKES = "/detectors_descriptors_evaluation/images_datasets/bikes/img1.png"; +#ifdef HAVE_OPENCV_OCL +#define SURF_NAME "Feature2D.SURF_OCL" +#else +#define SURF_NAME "Feature2D.SURF" +#endif + #define SHOW_DEBUG_LOG 0 static @@ -615,7 +621,7 @@ protected: */ TEST(Features2d_RotationInvariance_Detector_SURF, regression) { - DetectorRotationInvarianceTest test(Algorithm::create("Feature2D.SURF"), + DetectorRotationInvarianceTest test(Algorithm::create(SURF_NAME), 0.44f, 0.76f); test.safe_run(); @@ -634,8 +640,8 @@ TEST(Features2d_RotationInvariance_Detector_SIFT, DISABLED_regression) */ TEST(Features2d_RotationInvariance_Descriptor_SURF, regression) { - DescriptorRotationInvarianceTest test(Algorithm::create("Feature2D.SURF"), - Algorithm::create("Feature2D.SURF"), + DescriptorRotationInvarianceTest test(Algorithm::create(SURF_NAME), + Algorithm::create(SURF_NAME), NORM_L1, 0.83f); test.safe_run(); @@ -655,7 +661,7 @@ TEST(Features2d_RotationInvariance_Descriptor_SIFT, regression) */ TEST(Features2d_ScaleInvariance_Detector_SURF, regression) { - DetectorScaleInvarianceTest test(Algorithm::create("Feature2D.SURF"), + DetectorScaleInvarianceTest test(Algorithm::create(SURF_NAME), 0.64f, 0.84f); test.safe_run(); @@ -674,8 +680,8 @@ TEST(Features2d_ScaleInvariance_Detector_SIFT, regression) */ TEST(Features2d_ScaleInvariance_Descriptor_SURF, regression) { - DescriptorScaleInvarianceTest test(Algorithm::create("Feature2D.SURF"), - Algorithm::create("Feature2D.SURF"), + DescriptorScaleInvarianceTest test(Algorithm::create(SURF_NAME), + Algorithm::create(SURF_NAME), NORM_L1, 0.61f); test.safe_run(); From da70b04262fa6c7c0d29d6ca69b496cab5e31259 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 5 Mar 2014 17:04:49 +0400 Subject: [PATCH 170/293] made SURF_OCL default constructor parameters the same as SURF --- modules/nonfree/src/surf_ocl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/nonfree/src/surf_ocl.cpp b/modules/nonfree/src/surf_ocl.cpp index 2922c2cee6..e6fd6eeb6b 100644 --- a/modules/nonfree/src/surf_ocl.cpp +++ b/modules/nonfree/src/surf_ocl.cpp @@ -283,9 +283,9 @@ private: cv::ocl::SURF_OCL::SURF_OCL() { hessianThreshold = 100.0f; - extended = true; + extended = false; nOctaves = 4; - nOctaveLayers = 2; + nOctaveLayers = 3; keypointsRatio = 0.01f; upright = false; } From 60ce2b2e9f2266a01c193b9e3c67d8dfcd8e35fd Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 5 Mar 2014 17:10:51 +0400 Subject: [PATCH 171/293] modified SURF's performance test to test OpenCL version as well --- modules/nonfree/perf/perf_surf.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/nonfree/perf/perf_surf.cpp b/modules/nonfree/perf/perf_surf.cpp index 20935a9a1e..f16b348231 100644 --- a/modules/nonfree/perf/perf_surf.cpp +++ b/modules/nonfree/perf/perf_surf.cpp @@ -12,6 +12,12 @@ typedef perf::TestBaseWithParam surf; "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\ "stitching/a3.png" +#ifdef HAVE_OPENCV_OCL +typedef ocl::SURF_OCL surf_class; +#else +typdef SURF surf_class; +#endif + PERF_TEST_P(surf, detect, testing::Values(SURF_IMAGES)) { String filename = getDataPath(GetParam()); @@ -22,7 +28,7 @@ PERF_TEST_P(surf, detect, testing::Values(SURF_IMAGES)) Mat mask; declare.in(frame).time(90); - SURF detector; + surf_class detector; vector points; TEST_CYCLE() detector(frame, mask, points); @@ -41,7 +47,7 @@ PERF_TEST_P(surf, extract, testing::Values(SURF_IMAGES)) Mat mask; declare.in(frame).time(90); - SURF detector; + surf_class detector; vector points; vector descriptors; detector(frame, mask, points); @@ -61,7 +67,7 @@ PERF_TEST_P(surf, full, testing::Values(SURF_IMAGES)) Mat mask; declare.in(frame).time(90); - SURF detector; + surf_class detector; vector points; vector descriptors; From 4aa891e77384e88a4e809f92fa2bc9b50f67a07c Mon Sep 17 00:00:00 2001 From: Alexander Karsakov Date: Wed, 5 Mar 2014 17:57:06 +0400 Subject: [PATCH 172/293] Remove clReleaseDevice call (workaround for pure virtual call on Windows) --- modules/ocl/src/cl_context.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/ocl/src/cl_context.cpp b/modules/ocl/src/cl_context.cpp index 37d6bb33b8..4edf1f7ff8 100644 --- a/modules/ocl/src/cl_context.cpp +++ b/modules/ocl/src/cl_context.cpp @@ -596,9 +596,16 @@ protected: CV_Assert(this != currentContext); #ifdef CL_VERSION_1_2 - if (supportsFeature(FEATURE_CL_VER_1_2)) +#ifdef WIN32 + // if process is on termination stage (ExitProcess was called and other threads were terminated) + // then disable command queue release because it may cause program hang + if (!__termination) +#endif { - openCLSafeCall(clReleaseDevice(clDeviceID)); + if (supportsFeature(FEATURE_CL_VER_1_2)) + { + openCLSafeCall(clReleaseDevice(clDeviceID)); + } } #endif if (deviceInfoImpl._id < 0) // not in the global registry, so we should cleanup it From 22f42a639fdc0233a740bbad06536f32d7d99382 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 5 Mar 2014 18:48:19 +0400 Subject: [PATCH 173/293] fixed doc builder warnings; make sure the tests give reasonable results when OpenCL is not available --- modules/nonfree/doc/feature_detection.rst | 2 +- .../nonfree/include/opencv2/nonfree/ocl.hpp | 2 +- modules/nonfree/perf/perf_surf.cpp | 29 ++++++++++++------ modules/nonfree/test/test_features2d.cpp | 30 ++++++++++++++----- 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/modules/nonfree/doc/feature_detection.rst b/modules/nonfree/doc/feature_detection.rst index f97dec1002..190c9f8475 100644 --- a/modules/nonfree/doc/feature_detection.rst +++ b/modules/nonfree/doc/feature_detection.rst @@ -240,7 +240,7 @@ The class ``SURF_GPU`` uses some buffers and provides access to it. All buffers ocl::SURF_OCL ------------- -.. ocv:class:: ocl::SURF_OCL +.. ocv:class:: ocl::SURF_OCL : public Feature2D Class used for extracting Speeded Up Robust Features (SURF) from an image. :: diff --git a/modules/nonfree/include/opencv2/nonfree/ocl.hpp b/modules/nonfree/include/opencv2/nonfree/ocl.hpp index ad0c16ac48..5d9eb86b50 100644 --- a/modules/nonfree/include/opencv2/nonfree/ocl.hpp +++ b/modules/nonfree/include/opencv2/nonfree/ocl.hpp @@ -114,7 +114,7 @@ namespace cv CV_OUT vector& keypoints, OutputArray descriptors, bool useProvidedKeypoints=false) const; - + AlgorithmInfo* info() const; void releaseMemory(); diff --git a/modules/nonfree/perf/perf_surf.cpp b/modules/nonfree/perf/perf_surf.cpp index f16b348231..84c21b4691 100644 --- a/modules/nonfree/perf/perf_surf.cpp +++ b/modules/nonfree/perf/perf_surf.cpp @@ -13,9 +13,19 @@ typedef perf::TestBaseWithParam surf; "stitching/a3.png" #ifdef HAVE_OPENCV_OCL -typedef ocl::SURF_OCL surf_class; +static Ptr getSURF() +{ + ocl::PlatformsInfo p; + if(ocl::getOpenCLPlatforms(p) > 0) + return new ocl::SURF_OCL; + else + return new SURF; +} #else -typdef SURF surf_class; +static Ptr getSURF() +{ + return new SURF; +} #endif PERF_TEST_P(surf, detect, testing::Values(SURF_IMAGES)) @@ -28,10 +38,11 @@ PERF_TEST_P(surf, detect, testing::Values(SURF_IMAGES)) Mat mask; declare.in(frame).time(90); - surf_class detector; + Ptr detector = getSURF(); + vector points; - TEST_CYCLE() detector(frame, mask, points); + TEST_CYCLE() detector->operator()(frame, mask, points, noArray()); SANITY_CHECK_KEYPOINTS(points, 1e-3); } @@ -47,12 +58,12 @@ PERF_TEST_P(surf, extract, testing::Values(SURF_IMAGES)) Mat mask; declare.in(frame).time(90); - surf_class detector; + Ptr detector = getSURF(); vector points; vector descriptors; - detector(frame, mask, points); + detector->operator()(frame, mask, points, noArray()); - TEST_CYCLE() detector(frame, mask, points, descriptors, true); + TEST_CYCLE() detector->operator()(frame, mask, points, descriptors, true); SANITY_CHECK(descriptors, 1e-4); } @@ -67,11 +78,11 @@ PERF_TEST_P(surf, full, testing::Values(SURF_IMAGES)) Mat mask; declare.in(frame).time(90); - surf_class detector; + Ptr detector = getSURF(); vector points; vector descriptors; - TEST_CYCLE() detector(frame, mask, points, descriptors, false); + TEST_CYCLE() detector->operator()(frame, mask, points, descriptors, false); SANITY_CHECK_KEYPOINTS(points, 1e-3); SANITY_CHECK(descriptors, 1e-4); diff --git a/modules/nonfree/test/test_features2d.cpp b/modules/nonfree/test/test_features2d.cpp index 66a35931ad..3dfad7cdab 100644 --- a/modules/nonfree/test/test_features2d.cpp +++ b/modules/nonfree/test/test_features2d.cpp @@ -51,9 +51,19 @@ const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors"; const string IMAGE_FILENAME = "tsukuba.png"; #ifdef HAVE_OPENCV_OCL -#define SURF_NAME "SURF_OCL" +static Ptr getSURF() +{ + ocl::PlatformsInfo p; + if(ocl::getOpenCLPlatforms(p) > 0) + return new ocl::SURF_OCL; + else + return new SURF; +} #else -#define SURF_NAME "SURF" +static Ptr getSURF() +{ + return new SURF; +} #endif /****************************************************************************************\ @@ -984,7 +994,7 @@ TEST( Features2d_Detector_SIFT, regression ) TEST( Features2d_Detector_SURF, regression ) { - CV_FeatureDetectorTest test( "detector-surf", FeatureDetector::create(SURF_NAME) ); + CV_FeatureDetectorTest test( "detector-surf", Ptr(getSURF()) ); test.safe_run(); } @@ -1001,7 +1011,7 @@ TEST( Features2d_DescriptorExtractor_SIFT, regression ) TEST( Features2d_DescriptorExtractor_SURF, regression ) { CV_DescriptorExtractorTest > test( "descriptor-surf", 0.05f, - DescriptorExtractor::create(SURF_NAME) ); + Ptr(getSURF()) ); test.safe_run(); } @@ -1042,10 +1052,10 @@ TEST(Features2d_BruteForceDescriptorMatcher_knnMatch, regression) const int sz = 100; const int k = 3; - Ptr ext = DescriptorExtractor::create(SURF_NAME); + Ptr ext = Ptr(getSURF()); ASSERT_TRUE(ext != NULL); - Ptr det = FeatureDetector::create(SURF_NAME); + Ptr det = Ptr(getSURF()); //"%YAML:1.0\nhessianThreshold: 8000.\noctaves: 3\noctaveLayers: 4\nupright: 0\n" ASSERT_TRUE(det != NULL); @@ -1102,7 +1112,11 @@ public: protected: void run(int) { - Ptr f = Algorithm::create("Feature2D." + fname); + Ptr f; + if(fname == "SURF") + f = getSURF(); + else + f = Algorithm::create("Feature2D." + fname); if(f.empty()) return; string path = string(ts->get_data_path()) + "detectors_descriptors_evaluation/planar/"; @@ -1150,7 +1164,7 @@ protected: }; TEST(Features2d_SIFTHomographyTest, regression) { CV_DetectPlanarTest test("SIFT", 80); test.safe_run(); } -TEST(Features2d_SURFHomographyTest, regression) { CV_DetectPlanarTest test(SURF_NAME, 80); test.safe_run(); } +TEST(Features2d_SURFHomographyTest, regression) { CV_DetectPlanarTest test("SURF", 80); test.safe_run(); } class FeatureDetectorUsingMaskTest : public cvtest::BaseTest { From fde0185a774b258988c9e37efbb8be14090e581a Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 16 Feb 2014 21:27:25 +0400 Subject: [PATCH 174/293] iteractor work --- modules/viz/doc/widget.rst | 9 ++++----- modules/viz/include/opencv2/viz/widgets.hpp | 5 ++--- modules/viz/src/vtk/vtkCocoaInteractorFix.mm | 3 +-- modules/viz/src/vtk/vtkVizInteractorStyle.cpp | 1 - modules/viz/test/test_viz3d.cpp | 1 - 5 files changed, 7 insertions(+), 12 deletions(-) diff --git a/modules/viz/doc/widget.rst b/modules/viz/doc/widget.rst index 31546ae8e2..0884ecede4 100644 --- a/modules/viz/doc/widget.rst +++ b/modules/viz/doc/widget.rst @@ -934,7 +934,7 @@ This 3D Widget defines a collection of clouds. :: void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()); //! All points in cloud have the same color void addCloud(InputArray cloud, const Color &color = Color::white(), Affine3d &pose = Affine3d::Identity()); - //! Repacks internal structure to sinle cloud + //! Repacks internal structure to single cloud void finalize(); }; @@ -1030,8 +1030,8 @@ viz::WWidgetMerger --------------------- .. ocv:class:: WWidgetMerger -This class allos to merge several widgets to single one. It has quite limited functionality and can't merge widgets with different attributes. For instance, -if widgetA has color array and widgetB has only global color defined, then result of merge won't have color at all. The class is suitable for merging large amount of similar widgets. +This class allows to merge several widgets to single one. It has quite limited functionality and can't merge widgets with different attributes. For instance, +if widgetA has color array and widgetB has only global color defined, then result of merge won't have color at all. The class is suitable for merging large amount of similar widgets. :: class CV_EXPORTS WWidgetMerger : public Widget3D { @@ -1041,11 +1041,10 @@ if widgetA has color array and widgetB has only global color defined, then resul //! Add widget to merge with optional position change void addWidget(const Widget3D& widget, const Affine3d &pose = Affine3d::Identity()); - //! Repacks internal structure to sinle widget + //! Repacks internal structure to single widget void finalize(); }; - viz::WWidgetMerger::WWidgetMerger --------------------------------------- Constructs a WWidgetMerger. diff --git a/modules/viz/include/opencv2/viz/widgets.hpp b/modules/viz/include/opencv2/viz/widgets.hpp index 0c2a4f7050..2949598c54 100644 --- a/modules/viz/include/opencv2/viz/widgets.hpp +++ b/modules/viz/include/opencv2/viz/widgets.hpp @@ -346,7 +346,7 @@ namespace cv void addCloud(InputArray cloud, InputArray colors, const Affine3d &pose = Affine3d::Identity()); //! All points in cloud have the same color void addCloud(InputArray cloud, const Color &color = Color::white(), const Affine3d &pose = Affine3d::Identity()); - //! Repacks internal structure to sinle cloud + //! Repacks internal structure to single cloud void finalize(); }; @@ -363,7 +363,6 @@ namespace cv WMesh(InputArray cloud, InputArray polygons, InputArray colors = noArray(), InputArray normals = noArray()); }; - class CV_EXPORTS WWidgetMerger : public Widget3D { public: @@ -372,7 +371,7 @@ namespace cv //! Add widget to merge with optional position change void addWidget(const Widget3D& widget, const Affine3d &pose = Affine3d::Identity()); - //! Repacks internal structure to sinle widget + //! Repacks internal structure to single widget void finalize(); }; diff --git a/modules/viz/src/vtk/vtkCocoaInteractorFix.mm b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm index 23bba552d3..dad41b073e 100644 --- a/modules/viz/src/vtk/vtkCocoaInteractorFix.mm +++ b/modules/viz/src/vtk/vtkCocoaInteractorFix.mm @@ -148,7 +148,7 @@ renWin->SetRootWindow(NULL); } } -} +} @end @@ -209,4 +209,3 @@ vtkSmartPointer cv::viz::vtkCocoaRenderWindowInteract { return vtkSmartPointer::New(); } - diff --git a/modules/viz/src/vtk/vtkVizInteractorStyle.cpp b/modules/viz/src/vtk/vtkVizInteractorStyle.cpp index 57aee636e6..9b5eca2b0e 100644 --- a/modules/viz/src/vtk/vtkVizInteractorStyle.cpp +++ b/modules/viz/src/vtk/vtkVizInteractorStyle.cpp @@ -1074,4 +1074,3 @@ void cv::viz::vtkVizInteractorStyle::PrintSelf(ostream& os, vtkIndent indent) os << indent << "AngleStepSize: " << AngleStepSize << "\n"; os << indent << "MotionUserScale: "<< MotionUserScale << "\n"; } - diff --git a/modules/viz/test/test_viz3d.cpp b/modules/viz/test/test_viz3d.cpp index 45d3cdc3cf..45128df2f7 100644 --- a/modules/viz/test/test_viz3d.cpp +++ b/modules/viz/test/test_viz3d.cpp @@ -59,6 +59,5 @@ TEST(Viz_viz3d, DISABLED_develop) //cv::Mat cloud = cv::viz::readCloud(get_dragon_ply_file_path()); //---->>>>> - viz.spin(); } From f7a474180be25f472a1e68426f803280454edc56 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 6 Mar 2014 09:24:02 +0400 Subject: [PATCH 175/293] tuned some tests --- modules/ocl/perf/perf_filters.cpp | 2 +- modules/ocl/perf/perf_kalman.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ocl/perf/perf_filters.cpp b/modules/ocl/perf/perf_filters.cpp index 70e51b4662..b3ffc51b30 100644 --- a/modules/ocl/perf/perf_filters.cpp +++ b/modules/ocl/perf/perf_filters.cpp @@ -298,7 +298,7 @@ OCL_PERF_TEST_P(ScharrFixture, Scharr, oclDst.download(dst); - SANITY_CHECK(dst, 3e-3); + SANITY_CHECK(dst, 1e-2); } else if (RUN_PLAIN_IMPL) { diff --git a/modules/ocl/perf/perf_kalman.cpp b/modules/ocl/perf/perf_kalman.cpp index e384fcd696..946444ad9f 100644 --- a/modules/ocl/perf/perf_kalman.cpp +++ b/modules/ocl/perf/perf_kalman.cpp @@ -46,7 +46,7 @@ #include "perf_precomp.hpp" -//#ifdef HAVE_CLAMDBLAS +#ifdef HAVE_CLAMDBLAS using namespace perf; using namespace std; @@ -100,4 +100,4 @@ PERF_TEST_P(KalmanFilterFixture, KalmanFilter, SANITY_CHECK(statePre_); } -//#endif // HAVE_CLAMDBLAS +#endif // HAVE_CLAMDBLAS From b3e18d23a31313b4885f0c2fb8ca8afc0ff8199c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 5 Mar 2014 11:25:27 +0400 Subject: [PATCH 176/293] Implicit CUDA and OpenCL control for module definition added. Feature allows to exclude CUDA or OpenCL optimizations at all even CUDA is used on build. Exclusion of CUDA or OpenCL cut unwanted dependencies. --- cmake/OpenCVModule.cmake | 66 +++++++++++++++++-------- modules/nonfree/CMakeLists.txt | 2 +- modules/superres/src/cuda/btv_l1_gpu.cu | 2 +- modules/ts/CMakeLists.txt | 2 +- 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 03818018d9..c9c351113c 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -479,39 +479,49 @@ endmacro() # finds and sets headers and sources for the standard OpenCV module # Usage: # ocv_glob_module_sources() -macro(ocv_glob_module_sources) +macro(ocv_glob_module_sources EXCLUDE_CUDA EXCLUDE_OPENCL) file(GLOB_RECURSE lib_srcs "src/*.cpp") file(GLOB_RECURSE lib_int_hdrs "src/*.hpp" "src/*.h") file(GLOB lib_hdrs "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") file(GLOB lib_hdrs_detail "include/opencv2/${name}/detail/*.hpp" "include/opencv2/${name}/detail/*.h") - file(GLOB lib_cuda_srcs "src/cuda/*.cu") - set(cuda_objs "") - set(lib_cuda_hdrs "") - if(HAVE_CUDA) - ocv_include_directories(${CUDA_INCLUDE_DIRS}) - file(GLOB lib_cuda_hdrs "src/cuda/*.hpp") + if (NOT ${EXCLUDE_CUDA}) + file(GLOB lib_cuda_srcs "src/cuda/*.cu") + set(cuda_objs "") + set(lib_cuda_hdrs "") + if(HAVE_CUDA) + ocv_include_directories(${CUDA_INCLUDE_DIRS}) + file(GLOB lib_cuda_hdrs "src/cuda/*.hpp") - ocv_cuda_compile(cuda_objs ${lib_cuda_srcs} ${lib_cuda_hdrs}) - source_group("Src\\Cuda" FILES ${lib_cuda_srcs} ${lib_cuda_hdrs}) + ocv_cuda_compile(cuda_objs ${lib_cuda_srcs} ${lib_cuda_hdrs}) + source_group("Src\\Cuda" FILES ${lib_cuda_srcs} ${lib_cuda_hdrs}) + endif() + else() + set(cuda_objs "") + set(lib_cuda_srcs "") + set(lib_cuda_hdrs "") endif() source_group("Src" FILES ${lib_srcs} ${lib_int_hdrs}) - file(GLOB cl_kernels "src/opencl/*.cl") - if(HAVE_opencv_ocl AND cl_kernels) - ocv_include_directories(${OPENCL_INCLUDE_DIRS}) - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp" - COMMAND ${CMAKE_COMMAND} -DCL_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/opencl" -DOUTPUT="${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" -P "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake" - DEPENDS ${cl_kernels} "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake") - source_group("OpenCL" FILES ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") - list(APPEND lib_srcs ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") + if (NOT ${EXCLUDE_OPENCL}) + file(GLOB cl_kernels "src/opencl/*.cl") + if(HAVE_opencv_ocl AND cl_kernels) + ocv_include_directories(${OPENCL_INCLUDE_DIRS}) + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp" + COMMAND ${CMAKE_COMMAND} -DCL_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/opencl" -DOUTPUT="${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" -P "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake" + DEPENDS ${cl_kernels} "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake") + source_group("OpenCL" FILES ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") + list(APPEND lib_srcs ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") + endif() endif() source_group("Include" FILES ${lib_hdrs}) source_group("Include\\detail" FILES ${lib_hdrs_detail}) + message(":${EXCLUDE_CUDA}: ${lib_cuda_srcs}") + ocv_set_module_sources(${ARGN} HEADERS ${lib_hdrs} ${lib_hdrs_detail} SOURCES ${lib_srcs} ${lib_int_hdrs} ${cuda_objs} ${lib_cuda_srcs} ${lib_cuda_hdrs}) endmacro() @@ -614,9 +624,25 @@ endmacro() # Usage: # ocv_define_module(module_name [INTERNAL] [REQUIRED] [] [OPTIONAL ]) macro(ocv_define_module module_name) - ocv_add_module(${module_name} ${ARGN}) + set(_tmp_argn ${ARGN}) + set(exclude_cuda 0) + set(exclude_opencl 0) + set(argv0 ${ARGV1}) + set(argv1 ${ARGV2}) + set(argv2 ${ARGV3}) + foreach(i RANGE 0 2) + if("${argv${i}}" STREQUAL "EXCLUDE_CUDA") + set(exclude_cuda 1) + list(REMOVE_AT _tmp_argn ${i}) + elseif ("${argv${i}}" STREQUAL "EXCLUDE_OPENCL") + set(exclude_opencl 1) + list(REMOVE_AT _tmp_argn ${i}) + endif() + endforeach() + + ocv_add_module(${module_name} ${_tmp_argn}) ocv_module_include_directories() - ocv_glob_module_sources() + ocv_glob_module_sources(${exclude_cuda} ${exclude_opencl}) ocv_create_module() ocv_add_precompiled_headers(${the_module}) diff --git a/modules/nonfree/CMakeLists.txt b/modules/nonfree/CMakeLists.txt index b43273bc80..571614be0d 100644 --- a/modules/nonfree/CMakeLists.txt +++ b/modules/nonfree/CMakeLists.txt @@ -6,7 +6,7 @@ set(the_description "Functionality with possible limitations on the use") ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wshadow) if(ENABLE_DYNAMIC_CUDA) add_definitions(-DDYNAMIC_CUDA_SUPPORT) - ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_ocl) + ocv_define_module(nonfree EXCLUDE_CUDA opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_ocl) else() ocv_define_module(nonfree opencv_imgproc opencv_features2d opencv_calib3d OPTIONAL opencv_gpu opencv_ocl) endif() diff --git a/modules/superres/src/cuda/btv_l1_gpu.cu b/modules/superres/src/cuda/btv_l1_gpu.cu index b4d96190ae..4b0ebdc592 100644 --- a/modules/superres/src/cuda/btv_l1_gpu.cu +++ b/modules/superres/src/cuda/btv_l1_gpu.cu @@ -42,7 +42,7 @@ #include "opencv2/opencv_modules.hpp" -#ifdef HAVE_OPENCV_GPU +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) #include "opencv2/gpu/device/common.hpp" #include "opencv2/gpu/device/transform.hpp" diff --git a/modules/ts/CMakeLists.txt b/modules/ts/CMakeLists.txt index bb56da2d98..dcd3e1563b 100644 --- a/modules/ts/CMakeLists.txt +++ b/modules/ts/CMakeLists.txt @@ -11,7 +11,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) ocv_add_module(ts opencv_core opencv_features2d) -ocv_glob_module_sources() +ocv_glob_module_sources(0 0) ocv_module_include_directories() ocv_create_module() From 806e9241a6c0255cde117e24e3a9de6fd315443e Mon Sep 17 00:00:00 2001 From: Alexander Karsakov Date: Thu, 6 Mar 2014 11:16:38 +0400 Subject: [PATCH 177/293] Clarifying comments --- modules/ocl/src/cl_context.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ocl/src/cl_context.cpp b/modules/ocl/src/cl_context.cpp index 4edf1f7ff8..156fa99168 100644 --- a/modules/ocl/src/cl_context.cpp +++ b/modules/ocl/src/cl_context.cpp @@ -598,7 +598,7 @@ protected: #ifdef CL_VERSION_1_2 #ifdef WIN32 // if process is on termination stage (ExitProcess was called and other threads were terminated) - // then disable command queue release because it may cause program hang + // then disable device release because it may cause program hang if (!__termination) #endif { @@ -624,7 +624,7 @@ protected: #ifdef WIN32 // if process is on termination stage (ExitProcess was called and other threads were terminated) - // then disable command queue release because it may cause program hang + // then disable context release because it may cause program hang if (!__termination) #endif { From 0eaeff06418223c89f86fc2fdcf48fbc90f4c4cb Mon Sep 17 00:00:00 2001 From: kurodash Date: Fri, 7 Mar 2014 19:02:37 +0900 Subject: [PATCH 178/293] fix: use "cvAlloc" wrapper function for malloc. --- modules/core/src/persistence.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/persistence.cpp b/modules/core/src/persistence.cpp index 7759f708b6..4a6e0c9ec5 100644 --- a/modules/core/src/persistence.cpp +++ b/modules/core/src/persistence.cpp @@ -4855,7 +4855,7 @@ cvRegisterType( const CvTypeInfo* _info ) "Type name should contain only letters, digits, - and _" ); } - info = (CvTypeInfo*)malloc( sizeof(*info) + len + 1 ); + info = (CvTypeInfo*)cvAlloc( sizeof(*info) + len + 1 ); *info = *_info; info->type_name = (char*)(info + 1); @@ -4893,7 +4893,7 @@ cvUnregisterType( const char* type_name ) if( !CvType::first || !CvType::last ) CvType::first = CvType::last = 0; - free( info ); + cvFree( info ); } } From 45969bb3ae6065bc152c62e19c420ada8f6b4315 Mon Sep 17 00:00:00 2001 From: Ian Taylor Date: Mon, 10 Mar 2014 03:02:06 -0400 Subject: [PATCH 179/293] fix variable name typo in calcBlurriness --- modules/videostab/src/deblurring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/videostab/src/deblurring.cpp b/modules/videostab/src/deblurring.cpp index 813e908be5..be9fd5c0f7 100644 --- a/modules/videostab/src/deblurring.cpp +++ b/modules/videostab/src/deblurring.cpp @@ -57,7 +57,7 @@ float calcBlurriness(const Mat &frame) Sobel(frame, Gx, CV_32F, 1, 0); Sobel(frame, Gy, CV_32F, 0, 1); double normGx = norm(Gx); - double normGy = norm(Gx); + double normGy = norm(Gy); double sumSq = normGx*normGx + normGy*normGy; return static_cast(1. / (sumSq / frame.size().area() + 1e-6)); } From 3eff05e3eb9297d0be9bb72f1131eee5947c209c Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 7 Mar 2014 14:35:51 +0400 Subject: [PATCH 180/293] added performance tests for cv::ocl::calcHist --- modules/ocl/perf/perf_imgproc.cpp | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/modules/ocl/perf/perf_imgproc.cpp b/modules/ocl/perf/perf_imgproc.cpp index e6f6a0582e..051ff2dba1 100644 --- a/modules/ocl/perf/perf_imgproc.cpp +++ b/modules/ocl/perf/perf_imgproc.cpp @@ -81,6 +81,45 @@ OCL_PERF_TEST_P(EqualizeHistFixture, EqualizeHist, OCL_TEST_SIZES) OCL_PERF_ELSE } +///////////// CalcHist //////////////////////// + +typedef TestBaseWithParam CalcHistFixture; + +OCL_PERF_TEST_P(CalcHistFixture, CalcHist, OCL_TEST_SIZES) +{ + const Size srcSize = GetParam(); + const std::vector channels(1, 0); + std::vector ranges(2); + std::vector histSize(1, 256); + ranges[0] = 0; + ranges[1] = 256; + + Mat src(srcSize, CV_8UC1), dst(srcSize, CV_32FC1); + declare.in(src, WARMUP_RNG).out(dst); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src), oclDst(srcSize, CV_32SC1); + + OCL_TEST_CYCLE() cv::ocl::calcHist(oclSrc, oclDst); + + oclDst.download(dst); + SANITY_CHECK(dst); + } + else if (RUN_PLAIN_IMPL) + { + TEST_CYCLE() cv::calcHist(std::vector(1, src), channels, + noArray(), dst, histSize, ranges, false); + + dst.convertTo(dst, CV_32S); + dst = dst.reshape(1, 1); + + SANITY_CHECK(dst); + } + else + OCL_PERF_ELSE +} + /////////// CopyMakeBorder ////////////////////// CV_ENUM(Border, BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT, BORDER_WRAP, BORDER_REFLECT_101) From 4d7a593c254657cd41e4c1659ac5420ef7082d8f Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Wed, 12 Mar 2014 16:21:36 +0400 Subject: [PATCH 181/293] minor cmake fix fo macos --- cmake/OpenCVModule.cmake | 8 ++------ modules/viz/CMakeLists.txt | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 6f727af8d9..05faa35a22 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -484,7 +484,7 @@ macro(ocv_glob_module_sources) file(GLOB_RECURSE lib_int_hdrs "src/*.hpp" "src/*.h") file(GLOB lib_hdrs "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") file(GLOB lib_hdrs_detail "include/opencv2/${name}/detail/*.hpp" "include/opencv2/${name}/detail/*.h") - file(GLOB_RECURSE lib_srcs_apple "src/*.m*") + file(GLOB_RECURSE lib_srcs_apple "src/*.mm") if (APPLE) list(APPEND lib_srcs ${lib_srcs_apple}) endif() @@ -748,12 +748,8 @@ function(ocv_add_accuracy_tests) endif() get_native_precompiled_header(${the_target} test_precomp.hpp) + add_executable(${the_target} ${OPENCV_TEST_${the_module}_SOURCES} ${${the_target}_pch}) - if(APPLE AND ${the_target} STREQUAL "opencv_test_viz") - add_executable(${the_target} MACOSX_BUNDLE ${OPENCV_TEST_${the_module}_SOURCES} ${${the_target}_pch}) - else() - add_executable(${the_target} ${OPENCV_TEST_${the_module}_SOURCES} ${${the_target}_pch}) - endif() target_link_libraries(${the_target} ${OPENCV_MODULE_${the_module}_DEPS} ${test_deps} ${OPENCV_LINKER_LIBS}) add_dependencies(opencv_tests ${the_target}) diff --git a/modules/viz/CMakeLists.txt b/modules/viz/CMakeLists.txt index 7ccd079216..d839491c1b 100644 --- a/modules/viz/CMakeLists.txt +++ b/modules/viz/CMakeLists.txt @@ -9,3 +9,7 @@ ocv_define_module(viz opencv_core ${VTK_LIBRARIES}) if(APPLE AND BUILD_opencv_viz) target_link_libraries(opencv_viz "-framework Cocoa") endif() + +if(TARGET opencv_test_viz) + set_target_properties(opencv_test_viz PROPERTIES MACOSX_BUNDLE TRUE) +endif() From a87607e3ef6d30a2ccd8bd3b516fde4647dce16d Mon Sep 17 00:00:00 2001 From: Firat Kalaycilar Date: Wed, 12 Mar 2014 16:14:59 +0200 Subject: [PATCH 182/293] Fixed an issue with weight assignment causing the resulting GMM weights to be unsorted in BackgroundSubtractorMOG2 --- modules/video/src/bgfg_gaussmix2.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/video/src/bgfg_gaussmix2.cpp b/modules/video/src/bgfg_gaussmix2.cpp index 6bbb960482..b14bc8e1e2 100644 --- a/modules/video/src/bgfg_gaussmix2.cpp +++ b/modules/video/src/bgfg_gaussmix2.cpp @@ -319,7 +319,7 @@ struct MOG2Invoker : ParallelLoopBody for( int mode = 0; mode < nmodes; mode++, mean_m += nchannels ) { float weight = alpha1*gmm[mode].weight + prune;//need only weight if fit is found - + int swap_count = 0; //// //fit not found yet if( !fitsPDF ) @@ -384,6 +384,7 @@ struct MOG2Invoker : ParallelLoopBody if( weight < gmm[i-1].weight ) break; + swap_count++; //swap one up std::swap(gmm[i], gmm[i-1]); for( int c = 0; c < nchannels; c++ ) @@ -401,7 +402,7 @@ struct MOG2Invoker : ParallelLoopBody nmodes--; } - gmm[mode].weight = weight;//update weight by the calculated value + gmm[mode-swap_count].weight = weight;//update weight by the calculated value totalWeight += weight; } //go through all modes From a5afcd9f115b82112cb2427f314c8af216655084 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Wed, 12 Mar 2014 19:58:53 +0400 Subject: [PATCH 183/293] improved performance of cv::ocl::minMax --- modules/ocl/src/arithm.cpp | 67 ++++++++++----------- modules/ocl/src/opencl/arithm_minMax.cl | 78 +++++-------------------- 2 files changed, 48 insertions(+), 97 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 0b316c5ea3..d72904ccef 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -473,20 +473,13 @@ void cv::ocl::meanStdDev(const oclMat &src, Scalar &mean, Scalar &stddev) ////////////////////////////////////////////////////////////////////////////// template -static void arithmetic_minMax_run(const oclMat &src, const oclMat & mask, cl_mem &dst, int groupnum, string kernelName) +static void arithmetic_minMax_run(const oclMat &src, const oclMat & mask, cl_mem &dst, int vlen, int groupnum) { - int all_cols = src.step / src.elemSize(); - int pre_cols = (src.offset % src.step) / src.elemSize(); - int sec_cols = all_cols - (src.offset % src.step + src.cols * src.elemSize() - 1) / src.elemSize() - 1; - int invalid_cols = pre_cols + sec_cols; - int cols = all_cols - invalid_cols , elemnum = cols * src.rows; - int offset = src.offset / src.elemSize(); - const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; - const char * const channelMap[] = { " ", " ", "2", "4", "4" }; + const char * const channelMap[] = { " ", " ", "2", "4", "4", "", "", "", "8" }; ostringstream stream; - stream << "-D T=" << typeMap[src.depth()] << channelMap[src.channels()]; + stream << "-D T=" << typeMap[src.depth()] << channelMap[vlen]; if (numeric_limits::is_integer) { stream << " -D MAX_VAL=" << (WT)numeric_limits::max(); @@ -494,38 +487,38 @@ static void arithmetic_minMax_run(const oclMat &src, const oclMat & mask, cl_mem } else stream << " -D DEPTH_" << src.depth(); + stream << " -D vlen=" << vlen; std::string buildOptions = stream.str(); + int vElemSize = src.elemSize1() * vlen, src_cols = src.cols / vlen; + int src_step = src.step / vElemSize, src_offset = src.offset / vElemSize; + int mask_step = mask.step / vlen, mask_offset = mask.offset / vlen; + int total = src.size().area() / vlen; + vector > args; args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data)); - args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&invalid_cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&offset)); - args.push_back( make_pair( sizeof(cl_int) , (void *)&elemnum)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_step )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_offset )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src.rows )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_cols )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&total)); args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum)); + args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst)); - int minvalid_cols = 0, moffset = 0; if (!mask.empty()) { - int mall_cols = mask.step / mask.elemSize(); - int mpre_cols = (mask.offset % mask.step) / mask.elemSize(); - int msec_cols = mall_cols - (mask.offset % mask.step + mask.cols * mask.elemSize() - 1) / mask.elemSize() - 1; - minvalid_cols = mpre_cols + msec_cols; - moffset = mask.offset / mask.elemSize(); - args.push_back( make_pair( sizeof(cl_mem) , (void *)&mask.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&minvalid_cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&moffset )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&mask_step )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&mask_offset )); - kernelName += "_mask"; + buildOptions += " -D WITH_MASK"; } - size_t globalThreads[3] = {groupnum * 256, 1, 1}; - size_t localThreads[3] = {256, 1, 1}; + size_t globalThreads[3] = { groupnum * 256, 1, 1 }; + size_t localThreads[3] = { 256, 1, 1 }; // kernel use fixed grid size, replace lt on NULL is imposible without kernel changes - openCLExecuteKernel(src.clCxt, &arithm_minMax, kernelName, globalThreads, localThreads, + openCLExecuteKernel(src.clCxt, &arithm_minMax, "arithm_op_minMax", globalThreads, localThreads, args, -1, -1, buildOptions.c_str()); } @@ -535,25 +528,33 @@ void arithmetic_minMax(const oclMat &src, double *minVal, double *maxVal, const size_t groupnum = src.clCxt->getDeviceInfo().maxComputeUnits; CV_Assert(groupnum != 0); - int dbsize = groupnum * 2 * src.elemSize(); + int vlen = mask.empty() ? 8 : 1, vElemSize = vlen * src.elemSize1(); + while (src.offset % vElemSize != 0 || src.step % vElemSize != 0 || src.cols % vlen != 0) + { + vlen >>= 1; + vElemSize >>= 1; + } + + int dbsize = groupnum * 2 * vElemSize; oclMat buf; ensureSizeIsEnough(1, dbsize, CV_8UC1, buf); cl_mem buf_data = reinterpret_cast(buf.data); - arithmetic_minMax_run(src, mask, buf_data, groupnum, "arithm_op_minMax"); + arithmetic_minMax_run(src, mask, buf_data, vlen, groupnum); Mat matbuf = Mat(buf); T *p = matbuf.ptr(); + if (minVal != NULL) { *minVal = std::numeric_limits::max(); - for (int i = 0, end = src.oclchannels() * (int)groupnum; i < end; i++) + for (int i = 0, end = vlen * (int)groupnum; i < end; i++) *minVal = *minVal < p[i] ? *minVal : p[i]; } if (maxVal != NULL) { *maxVal = -std::numeric_limits::max(); - for (int i = src.oclchannels() * (int)groupnum, end = i << 1; i < end; i++) + for (int i = vlen * (int)groupnum, end = i << 1; i < end; i++) *maxVal = *maxVal > p[i] ? *maxVal : p[i]; } } @@ -564,7 +565,7 @@ void cv::ocl::minMax(const oclMat &src, double *minVal, double *maxVal, const oc { CV_Assert(src.channels() == 1); CV_Assert(src.size() == mask.size() || mask.empty()); - CV_Assert(src.step % src.elemSize() == 0); + CV_Assert(src.step % src.elemSize1() == 0); if (minVal == NULL && maxVal == NULL) return; diff --git a/modules/ocl/src/opencl/arithm_minMax.cl b/modules/ocl/src/opencl/arithm_minMax.cl index 01db7d0646..b0cd1c8b68 100644 --- a/modules/ocl/src/opencl/arithm_minMax.cl +++ b/modules/ocl/src/opencl/arithm_minMax.cl @@ -63,81 +63,31 @@ /**************************************Array minMax**************************************/ -__kernel void arithm_op_minMax(__global const T * src, __global T * dst, - int cols, int invalid_cols, int offset, int elemnum, int groupnum) +__kernel void arithm_op_minMax(__global const T * src, int src_step, int src_offset, int src_rows, int src_cols, + int total, int groupnum, __global T * dst +#ifdef WITH_MASK + , __global const uchar * mask, int mask_step, int mask_offset +#endif +) { int lid = get_local_id(0); int gid = get_group_id(0); int id = get_global_id(0); - int idx = offset + id + (id / cols) * invalid_cols; - __local T localmem_max[128], localmem_min[128]; T minval = (T)(MAX_VAL), maxval = (T)(MIN_VAL), temp; + int y, x; - for (int grainSize = groupnum << 8; id < elemnum; id += grainSize) + for (int grainSize = groupnum << 8; id < total; id += grainSize) { - idx = offset + id + (id / cols) * invalid_cols; - temp = src[idx]; - minval = min(minval, temp); - maxval = max(maxval, temp); - } + y = id / src_cols; + x = id % src_cols; - if (lid > 127) - { - localmem_min[lid - 128] = minval; - localmem_max[lid - 128] = maxval; - } - barrier(CLK_LOCAL_MEM_FENCE); - - if (lid < 128) - { - localmem_min[lid] = min(minval, localmem_min[lid]); - localmem_max[lid] = max(maxval, localmem_max[lid]); - } - barrier(CLK_LOCAL_MEM_FENCE); - - for (int lsize = 64; lsize > 0; lsize >>= 1) - { - if (lid < lsize) +#ifdef WITH_MASK + if (mask[mad24(y, mask_step, x + mask_offset)]) +#endif { - int lid2 = lsize + lid; - localmem_min[lid] = min(localmem_min[lid], localmem_min[lid2]); - localmem_max[lid] = max(localmem_max[lid], localmem_max[lid2]); - } - barrier(CLK_LOCAL_MEM_FENCE); - } - - if (lid == 0) - { - dst[gid] = localmem_min[0]; - dst[gid + groupnum] = localmem_max[0]; - } -} - -__kernel void arithm_op_minMax_mask(__global const T * src, __global T * dst, - int cols, int invalid_cols, int offset, - int elemnum, int groupnum, - const __global uchar * mask, int minvalid_cols, int moffset) -{ - int lid = get_local_id(0); - int gid = get_group_id(0); - int id = get_global_id(0); - - int idx = offset + id + (id / cols) * invalid_cols; - int midx = moffset + id + (id / cols) * minvalid_cols; - - __local T localmem_max[128], localmem_min[128]; - T minval = (T)(MAX_VAL), maxval = (T)(MIN_VAL), temp; - - for (int grainSize = groupnum << 8; id < elemnum; id += grainSize) - { - idx = offset + id + (id / cols) * invalid_cols; - midx = moffset + id + (id / cols) * minvalid_cols; - - if (mask[midx]) - { - temp = src[idx]; + temp = src[mad24(y, src_step, x + src_offset)]; minval = min(minval, temp); maxval = max(maxval, temp); } From 8e79de35a800560f70631b614265ef12d5f95c29 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Mar 2014 14:57:13 +0400 Subject: [PATCH 184/293] changes for GFTT --- modules/ocl/src/gftt.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/modules/ocl/src/gftt.cpp b/modules/ocl/src/gftt.cpp index 4f24d13588..b30c0b502f 100644 --- a/modules/ocl/src/gftt.cpp +++ b/modules/ocl/src/gftt.cpp @@ -146,34 +146,33 @@ static void minMaxEig_caller(const oclMat &src, oclMat &dst, oclMat & tozero) CV_Assert(groupnum != 0); int dbsize = groupnum * 2 * src.elemSize(); - ensureSizeIsEnough(1, dbsize, CV_8UC1, dst); cl_mem dst_data = reinterpret_cast(dst.data); - int all_cols = src.step / src.elemSize(); - int pre_cols = (src.offset % src.step) / src.elemSize(); - int sec_cols = all_cols - (src.offset % src.step + src.cols * src.elemSize() - 1) / src.elemSize() - 1; - int invalid_cols = pre_cols + sec_cols; - int cols = all_cols - invalid_cols , elemnum = cols * src.rows; - int offset = src.offset / src.elemSize(); + int vElemSize = src.elemSize1(); + int src_step = src.step / vElemSize, src_offset = src.offset / vElemSize; + int total = src.size().area(); - {// first parallel pass + { + // first parallel pass vector > args; args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data)); - args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst_data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&invalid_cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&offset)); - args.push_back( make_pair( sizeof(cl_int) , (void *)&elemnum)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_offset)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_step)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src.rows )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src.cols )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&total)); args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum)); + args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst_data )); size_t globalThreads[3] = {groupnum * 256, 1, 1}; size_t localThreads[3] = {256, 1, 1}; openCLExecuteKernel(src.clCxt, &arithm_minMax, "arithm_op_minMax", globalThreads, localThreads, - args, -1, -1, "-D T=float -D DEPTH_5"); + args, -1, -1, "-D T=float -D DEPTH_5 -D vlen=1"); } - {// run final "serial" kernel to find accumulate results from threads and reset corner counter + { + // run final "serial" kernel to find accumulate results from threads and reset corner counter vector > args; args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst_data )); args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum )); From 3ebfe60008d9a0ea74cac18d1c30bcd8be8d16a9 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Mar 2014 15:35:53 +0400 Subject: [PATCH 185/293] improved performance of cv::ocl::countNonZero --- modules/ocl/src/arithm.cpp | 43 ++++++++++++------------ modules/ocl/src/opencl/arithm_nonzero.cl | 13 +++---- modules/ocl/test/test_arithm.cpp | 4 +-- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index d72904ccef..3a8524fa45 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -1263,38 +1263,35 @@ void cv::ocl::minMaxLoc(const oclMat &src, double *minVal, double *maxVal, ///////////////////////////// countNonZero /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -static void arithmetic_countNonZero_run(const oclMat &src, cl_mem &dst, int groupnum, string kernelName) +static void arithmetic_countNonZero_run(const oclMat &src, cl_mem &dst, int groupnum, int vlen) { - int ochannels = src.oclchannels(); - int all_cols = src.step / src.elemSize(); - int pre_cols = (src.offset % src.step) / src.elemSize(); - int sec_cols = all_cols - (src.offset % src.step + src.cols * src.elemSize() - 1) / src.elemSize() - 1; - int invalid_cols = pre_cols + sec_cols; - int cols = all_cols - invalid_cols , elemnum = cols * src.rows;; - int offset = src.offset / src.elemSize(); + int vElemSize = vlen * src.elemSize1(); + int src_step = src.step / vElemSize, src_offset = src.offset / vElemSize; + int src_cols = src.cols / vlen, total = src.size().area() / vlen; const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; - const char * const channelMap[] = { " ", " ", "2", "4", "4" }; - string buildOptions = format("-D srcT=%s%s -D dstT=int%s", typeMap[src.depth()], channelMap[ochannels], - channelMap[ochannels]); + const char * const channelMap[] = { "", "", "2", "4", "4", "", "", "", "8" }; + string buildOptions = format("-D srcT=%s%s -D dstT=int%s -D convertToDstT=convert_int%s", + typeMap[src.depth()], channelMap[vlen], + channelMap[vlen], channelMap[vlen]); vector > args; - args.push_back( make_pair( sizeof(cl_int) , (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&invalid_cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&offset)); - args.push_back( make_pair( sizeof(cl_int) , (void *)&elemnum)); - args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum)); args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_step )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_offset )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_cols )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&total )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst )); size_t globalThreads[3] = { groupnum * 256, 1, 1 }; #ifdef ANDROID - openCLExecuteKernel(src.clCxt, &arithm_nonzero, kernelName, globalThreads, NULL, + openCLExecuteKernel(src.clCxt, &arithm_nonzero, "arithm_op_nonzero", globalThreads, NULL, args, -1, -1, buildOptions.c_str()); #else size_t localThreads[3] = { 256, 1, 1 }; - openCLExecuteKernel(src.clCxt, &arithm_nonzero, kernelName, globalThreads, localThreads, + openCLExecuteKernel(src.clCxt, &arithm_nonzero, "arithm_op_nonzero", globalThreads, localThreads, args, -1, -1, buildOptions.c_str()); #endif } @@ -1311,18 +1308,20 @@ int cv::ocl::countNonZero(const oclMat &src) return -1; } + int vlen = 8, vElemSize = src.elemSize1() * vlen; + while (src.offset % vElemSize != 0 || src.step % vElemSize != 0 || src.cols % vlen != 0) + vlen >>= 1, vElemSize >>= 1; + size_t groupnum = src.clCxt->getDeviceInfo().maxComputeUnits; CV_Assert(groupnum != 0); - int dbsize = groupnum; - - string kernelName = "arithm_op_nonzero"; + int dbsize = groupnum * vlen; AutoBuffer _buf(dbsize); int *p = (int*)_buf, nonzero = 0; memset(p, 0, dbsize * sizeof(int)); cl_mem dstBuffer = openCLCreateBuffer(clCxt, CL_MEM_WRITE_ONLY, dbsize * sizeof(int)); - arithmetic_countNonZero_run(src, dstBuffer, groupnum, kernelName); + arithmetic_countNonZero_run(src, dstBuffer, groupnum, vlen); openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize * sizeof(int)); for (int i = 0; i < dbsize; i++) diff --git a/modules/ocl/src/opencl/arithm_nonzero.cl b/modules/ocl/src/opencl/arithm_nonzero.cl index 3180c26e86..6a21f27e0e 100644 --- a/modules/ocl/src/opencl/arithm_nonzero.cl +++ b/modules/ocl/src/opencl/arithm_nonzero.cl @@ -52,23 +52,18 @@ /**************************************Count NonZero**************************************/ -__kernel void arithm_op_nonzero(int cols, int invalid_cols, int offset, int elemnum, int groupnum, - __global srcT *src, __global dstT *dst) +__kernel void arithm_op_nonzero(__global srcT * src, int src_step, int src_offset, int src_cols, + int total, int groupnum, __global dstT * dst) { int lid = get_local_id(0); int gid = get_group_id(0); int id = get_global_id(0); - int idx = offset + id + (id / cols) * invalid_cols; __local dstT localmem_nonzero[128]; dstT nonzero = (dstT)(0); - srcT zero = (srcT)(0), one = (srcT)(1); - for (int grain = groupnum << 8; id < elemnum; id += grain) - { - idx = offset + id + (id / cols) * invalid_cols; - nonzero += src[idx] == zero ? zero : one; - } + for (int grain = groupnum << 8; id < total; id += grain) + nonzero += convertToDstT(src[mad24(id / src_cols, src_step, id % src_cols + src_offset)] == (srcT)(0)) ? (dstT)(0) : (dstT)(1); if (lid > 127) localmem_nonzero[lid - 128] = nonzero; diff --git a/modules/ocl/test/test_arithm.cpp b/modules/ocl/test/test_arithm.cpp index 17260580de..29976b6e4e 100644 --- a/modules/ocl/test/test_arithm.cpp +++ b/modules/ocl/test/test_arithm.cpp @@ -198,7 +198,7 @@ PARAM_TEST_CASE(ArithmTestBase, MatDepth, Channels, bool) Size roiSize = randomSize(1, MAX_VALUE); Border src1Border = randomBorder(0, use_roi ? MAX_VALUE : 0); - randomSubMat(src1, src1_roi, roiSize, src1Border, type, 2, 11); + randomSubMat(src1, src1_roi, roiSize, src1Border, type, -11, 11); Border src2Border = randomBorder(0, use_roi ? MAX_VALUE : 0); randomSubMat(src2, src2_roi, roiSize, src2Border, type, -1540, 1740); @@ -1163,7 +1163,7 @@ OCL_TEST_P(CountNonZero, MAT) int cpures = cv::countNonZero(src1_roi); int gpures = cv::ocl::countNonZero(gsrc1_roi); - EXPECT_DOUBLE_EQ((double)cpures, (double)gpures); + EXPECT_EQ(cpures, gpures); } } From adc15c2bbaecaee5e60345c7c10c4b6cac585219 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Mar 2014 16:27:07 +0400 Subject: [PATCH 186/293] improved performance of cv::ocl::sum --- modules/ocl/src/arithm.cpp | 42 ++++++++++++++-------------- modules/ocl/src/gftt.cpp | 2 +- modules/ocl/src/opencl/arithm_sum.cl | 10 +++---- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 3a8524fa45..34c665203f 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -313,32 +313,28 @@ void cv::ocl::compare(const oclMat &src1, const oclMat &src2, oclMat &dst , int enum { SUM = 0, ABS_SUM, SQR_SUM }; -static void arithmetic_sum_buffer_run(const oclMat &src, cl_mem &dst, int groupnum, int type, int ddepth) +static void arithmetic_sum_buffer_run(const oclMat &src, cl_mem &dst, int groupnum, int type, int ddepth, int vlen) { - int ochannels = src.oclchannels(); - int all_cols = src.step / src.elemSize(); - int pre_cols = (src.offset % src.step) / src.elemSize(); - int sec_cols = all_cols - (src.offset % src.step + src.cols * src.elemSize() - 1) / src.elemSize() - 1; - int invalid_cols = pre_cols + sec_cols; - int cols = all_cols - invalid_cols , elemnum = cols * src.rows;; - int offset = src.offset / src.elemSize(); + int vElemSize = vlen * src.elemSize(); + int src_offset = src.offset / vElemSize, src_step = src.step / vElemSize; + int src_cols = src.cols / vlen, total = src.size().area() / vlen; + + vlen *= src.oclchannels(); const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; const char * const funcMap[] = { "FUNC_SUM", "FUNC_ABS_SUM", "FUNC_SQR_SUM" }; - const char * const channelMap[] = { " ", " ", "2", "4", "4" }; + const char * const channelMap[] = { " ", " ", "2", "4", "4", "", "", "", "8" }; string buildOptions = format("-D srcT=%s%s -D dstT=%s%s -D convertToDstT=convert_%s%s -D %s", - typeMap[src.depth()], channelMap[ochannels], - typeMap[ddepth], channelMap[ochannels], - typeMap[ddepth], channelMap[ochannels], - funcMap[type]); + typeMap[src.depth()], channelMap[vlen], typeMap[ddepth], + channelMap[vlen], typeMap[ddepth], channelMap[vlen], funcMap[type]); vector > args; - args.push_back( make_pair( sizeof(cl_int) , (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&invalid_cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&offset)); - args.push_back( make_pair( sizeof(cl_int) , (void *)&elemnum)); - args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum)); args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_step )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_offset )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_cols )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&total )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst )); size_t globalThreads[3] = { groupnum * 256, 1, 1 }; @@ -360,7 +356,11 @@ Scalar arithmetic_sum(const oclMat &src, int type, int ddepth) size_t groupnum = src.clCxt->getDeviceInfo().maxComputeUnits; CV_Assert(groupnum != 0); - int dbsize = groupnum * src.oclchannels(); + int vlen = 8 / src.channels(), vElemSize = vlen * src.elemSize1(); + while (src.offset % vElemSize != 0 || src.step % vElemSize != 0 || src.cols % vlen != 0) + vlen >>= 1, vElemSize >>= 1; + + int dbsize = groupnum * src.oclchannels() * vlen; Context *clCxt = src.clCxt; AutoBuffer _buf(dbsize); @@ -368,12 +368,12 @@ Scalar arithmetic_sum(const oclMat &src, int type, int ddepth) memset(p, 0, dbsize * sizeof(T)); cl_mem dstBuffer = openCLCreateBuffer(clCxt, CL_MEM_WRITE_ONLY, dbsize * sizeof(T)); - arithmetic_sum_buffer_run(src, dstBuffer, groupnum, type, ddepth); + arithmetic_sum_buffer_run(src, dstBuffer, groupnum, type, ddepth, vlen); openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize * sizeof(T)); openCLFree(dstBuffer); Scalar s = Scalar::all(0.0); - for (int i = 0; i < dbsize;) + for (int i = 0; i < dbsize; ) for (int j = 0; j < src.oclchannels(); j++, i++) s.val[j] += p[i]; diff --git a/modules/ocl/src/gftt.cpp b/modules/ocl/src/gftt.cpp index b30c0b502f..57ed12f009 100644 --- a/modules/ocl/src/gftt.cpp +++ b/modules/ocl/src/gftt.cpp @@ -158,8 +158,8 @@ static void minMaxEig_caller(const oclMat &src, oclMat &dst, oclMat & tozero) // first parallel pass vector > args; args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data)); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src_offset)); args.push_back( make_pair( sizeof(cl_int) , (void *)&src_step)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&src_offset)); args.push_back( make_pair( sizeof(cl_int) , (void *)&src.rows )); args.push_back( make_pair( sizeof(cl_int) , (void *)&src.cols )); args.push_back( make_pair( sizeof(cl_int) , (void *)&total)); diff --git a/modules/ocl/src/opencl/arithm_sum.cl b/modules/ocl/src/opencl/arithm_sum.cl index 514cf2a7f1..86015cfe93 100644 --- a/modules/ocl/src/opencl/arithm_sum.cl +++ b/modules/ocl/src/opencl/arithm_sum.cl @@ -63,21 +63,19 @@ /**************************************Array buffer SUM**************************************/ -__kernel void arithm_op_sum(int cols,int invalid_cols,int offset,int elemnum,int groupnum, - __global srcT *src, __global dstT *dst) +__kernel void arithm_op_sum(__global srcT * src, int src_step, int src_offset, int src_cols, + int total, int groupnum, __global dstT * dst) { int lid = get_local_id(0); int gid = get_group_id(0); int id = get_global_id(0); - int idx = offset + id + (id / cols) * invalid_cols; __local dstT localmem_sum[128]; dstT sum = (dstT)(0), temp; - for (int grainSize = groupnum << 8; id < elemnum; id += grainSize) + for (int grainSize = groupnum << 8; id < total; id += grainSize) { - idx = offset + id + (id / cols) * invalid_cols; - temp = convertToDstT(src[idx]); + temp = convertToDstT(src[mad24(id / src_cols, src_step, id % src_cols + src_offset)]); FUNC(temp, sum); } From 6f76e7b42dc52308c13f8d64331cbd3ca08aa750 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Mar 2014 19:42:13 +0400 Subject: [PATCH 187/293] improved performance of bitwise ops --- modules/ocl/src/arithm.cpp | 155 ++++------- ...hm_bitwise_binary.cl => arithm_bitwise.cl} | 55 ++-- .../src/opencl/arithm_bitwise_binary_mask.cl | 88 ------ .../opencl/arithm_bitwise_binary_scalar.cl | 82 ------ .../arithm_bitwise_binary_scalar_mask.cl | 86 ------ modules/ocl/src/opencl/arithm_bitwise_not.cl | 253 ------------------ 6 files changed, 91 insertions(+), 628 deletions(-) rename modules/ocl/src/opencl/{arithm_bitwise_binary.cl => arithm_bitwise.cl} (67%) delete mode 100644 modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl delete mode 100644 modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl delete mode 100644 modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl delete mode 100644 modules/ocl/src/opencl/arithm_bitwise_not.cl diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 34c665203f..eabc055d0a 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -1336,157 +1336,118 @@ int cv::ocl::countNonZero(const oclMat &src) ////////////////////////////////bitwise_op//////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -static void bitwise_unary_run(const oclMat &src1, oclMat &dst, string kernelName, const cv::ocl::ProgramEntry* source) +enum { AND = 0, OR, XOR, NOT }; + +static void bitwise_run(const oclMat & src1, const oclMat & src2, const Scalar & src3, const oclMat & mask, + oclMat & dst, int operationType) { - dst.create(src1.size(), src1.type()); - - int channels = dst.oclchannels(); - int depth = dst.depth(); - - int vector_lengths[4][7] = {{4, 4, 4, 4, 1, 1, 1}, - {4, 4, 4, 4, 1, 1, 1}, - {4, 4, 4, 4, 1, 1, 1}, - {4, 4, 4, 4, 1, 1, 1} - }; - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols * channels + offset_cols, vector_length); - -#ifdef ANDROID - size_t localThreads[3] = { 64, 2, 1 }; -#else - size_t localThreads[3] = { 64, 4, 1 }; -#endif - size_t globalThreads[3] = { cols, dst.rows, 1 }; - - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - - openCLExecuteKernel(src1.clCxt, source, kernelName, globalThreads, localThreads, args, -1, depth); -} - -enum { AND = 0, OR, XOR }; - -static void bitwise_binary_run(const oclMat &src1, const oclMat &src2, const Scalar& src3, const oclMat &mask, - oclMat &dst, int operationType) -{ - CV_Assert(operationType >= AND && operationType <= XOR); - CV_Assert(src2.empty() || (!src2.empty() && src1.type() == src2.type() && src1.size() == src2.size())); - CV_Assert(mask.empty() || (!mask.empty() && mask.type() == CV_8UC1 && mask.size() == src1.size())); + CV_Assert(operationType >= AND && operationType <= NOT); + CV_Assert(src2.empty() || (src1.type() == src2.type() && src1.size() == src2.size())); + CV_Assert(mask.empty() || (mask.type() == CV_8UC1 && mask.size() == src1.size())); dst.create(src1.size(), src1.type()); - oclMat m; + double scalar[4]; - const char operationMap[] = { '&', '|', '^' }; - std::string kernelName("arithm_bitwise_binary"); + bool haveScalar = src2.empty() && operationType != NOT, haveMask = !mask.empty(); + int ocn = dst.oclchannels(), depth = dst.depth(); + const char operationMap[] = { '&', '|', '^', '~' }; + const char * const typeMap[] = { "uchar", "uchar", "ushort", "ushort", "int", "int", "ulong" }; + const char * const channelMap[] = { "", "", "2", "4", "4", "", "", "", "8", "", "", "", "", "", "", "", "16" }; + const int preferredVectorWidth[] = { 4, 4, 2, 2, 1, 1, 1 }; + int kercn = haveMask || haveScalar ? ocn : preferredVectorWidth[depth]; - int vlen = std::min(8, src1.elemSize1() * src1.oclchannels()); - std::string vlenstr = vlen > 1 ? format("%d", vlen) : ""; - std::string buildOptions = format("-D Operation=%c -D vloadn=vload%s -D vstoren=vstore%s -D elemSize=%d -D vlen=%d" - " -D ucharv=uchar%s", - operationMap[operationType], vlenstr.c_str(), vlenstr.c_str(), - (int)src1.elemSize(), vlen, vlenstr.c_str()); - -#ifdef ANDROID - size_t localThreads[3] = { 16, 10, 1 }; -#else - size_t localThreads[3] = { 16, 16, 1 }; -#endif - size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; - - vector > args; - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); - - if (src2.empty()) + if (!haveScalar && !haveMask) { - m.create(1, 1, dst.type()); - m.setTo(src3); - - args.push_back( make_pair( sizeof(cl_mem), (void *)&m.data )); - - kernelName += "_scalar"; + int velemsize = dst.elemSize1() * kercn; + while (src1.offset % velemsize != 0 || src1.step % velemsize != 0 || src1.cols * ocn % kercn != 0 || + src2.offset % velemsize != 0 || src2.step % velemsize != 0 || src2.cols * ocn % kercn != 0 || + dst.offset % velemsize != 0 || dst.step % velemsize != 0 || dst.cols * ocn % kercn != 0) + kercn >>= 1, velemsize >>= 1; } - else + + int cols = dst.cols * ocn / kercn; + + std::string buildOptions = format("-D Operation=%c -D T=%s%s", operationMap[operationType], + typeMap[depth], channelMap[kercn]); + + vector > args; + args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); + + if (haveScalar) + { + int sctype = CV_MAKE_TYPE(dst.depth(), ocn); + cv::scalarToRawData(src3, scalar, sctype); + + args.push_back( make_pair( CV_ELEM_SIZE(sctype), (void *)scalar )); + + buildOptions += " -D HAVE_SCALAR"; + } + else if (operationType != NOT) { args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset )); + + buildOptions += " -D OP_BINARY"; } - if (!mask.empty()) + if (haveMask) { args.push_back( make_pair( sizeof(cl_mem), (void *)&mask.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&mask.step )); args.push_back( make_pair( sizeof(cl_int), (void *)&mask.offset )); - kernelName += "_mask"; + buildOptions += " -D HAVE_MASK"; } args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dst.rows )); + args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - openCLExecuteKernel(src1.clCxt, mask.empty() ? (!src2.empty() ? &arithm_bitwise_binary : &arithm_bitwise_binary_scalar) : - (!src2.empty() ? &arithm_bitwise_binary_mask : &arithm_bitwise_binary_scalar_mask), - kernelName, globalThreads, localThreads, + size_t globalsize[3] = { dst.cols * ocn / kercn, dst.rows, 1 }; + globalsize[0] = divUp(globalsize[0], 256) * 256; + openCLExecuteKernel(src1.clCxt, &arithm_bitwise, "arithm_bitwise", globalsize, NULL, args, -1, -1, buildOptions.c_str()); } void cv::ocl::bitwise_not(const oclMat &src, oclMat &dst) { - if (!src.clCxt->supportsFeature(FEATURE_CL_DOUBLE) && src.depth() == CV_64F) - { - CV_Error(CV_OpenCLDoubleNotSupported, "Selected device doesn't support double"); - return; - } - - dst.create(src.size(), src.type()); - bitwise_unary_run(src, dst, "arithm_bitwise_not", &arithm_bitwise_not); + bitwise_run(src, oclMat(), Scalar(), oclMat(), dst, NOT); } void cv::ocl::bitwise_or(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) { - bitwise_binary_run(src1, src2, Scalar(), mask, dst, OR); + bitwise_run(src1, src2, Scalar(), mask, dst, OR); } void cv::ocl::bitwise_or(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - bitwise_binary_run(src1, oclMat(), src2, mask, dst, OR); + bitwise_run(src1, oclMat(), src2, mask, dst, OR); } void cv::ocl::bitwise_and(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) { - bitwise_binary_run(src1, src2, Scalar(), mask, dst, AND); + bitwise_run(src1, src2, Scalar(), mask, dst, AND); } void cv::ocl::bitwise_and(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - bitwise_binary_run(src1, oclMat(), src2, mask, dst, AND); + bitwise_run(src1, oclMat(), src2, mask, dst, AND); } void cv::ocl::bitwise_xor(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) { - bitwise_binary_run(src1, src2, Scalar(), mask, dst, XOR); + bitwise_run(src1, src2, Scalar(), mask, dst, XOR); } void cv::ocl::bitwise_xor(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - bitwise_binary_run(src1, oclMat(), src2, mask, dst, XOR); + bitwise_run(src1, oclMat(), src2, mask, dst, XOR); } oclMat cv::ocl::operator ~ (const oclMat &src) diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary.cl b/modules/ocl/src/opencl/arithm_bitwise.cl similarity index 67% rename from modules/ocl/src/opencl/arithm_bitwise_binary.cl rename to modules/ocl/src/opencl/arithm_bitwise.cl index 56cd745d29..899d41970f 100644 --- a/modules/ocl/src/opencl/arithm_bitwise_binary.cl +++ b/modules/ocl/src/opencl/arithm_bitwise.cl @@ -48,35 +48,46 @@ /////////////////////////////////////////// bitwise_binary ////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// -__kernel void arithm_bitwise_binary(__global uchar * src1, int src1_step, int src1_offset, - __global uchar * src2, int src2_step, int src2_offset, - __global uchar * dst, int dst_step, int dst_offset, - int cols, int rows) +__kernel void arithm_bitwise(__global uchar * src1ptr, int src1_step, int src1_offset, +#ifdef OP_BINARY + __global uchar * src2ptr, int src2_step, int src2_offset, +#elif defined HAVE_SCALAR + T scalar, +#endif +#ifdef HAVE_MASK + __global uchar * mask, int mask_step, int mask_offset, +#endif + __global uchar * dstptr, int dst_step, int dst_offset, int dst_rows, int dst_cols) { int x = get_global_id(0); int y = get_global_id(1); - if (x < cols && y < rows) + if (x < dst_cols && y < dst_rows) { -#if elemSize > 1 - x *= elemSize; +#ifdef HAVE_MASK + mask += mad24(y, mask_step, x + mask_offset); + if (mask[0]) #endif - int src1_index = mad24(y, src1_step, x + src1_offset); - int src2_index = mad24(y, src2_step, x + src2_offset); - int dst_index = mad24(y, dst_step, x + dst_offset); - -#if elemSize > 1 - #pragma unroll - for (int i = 0; i < elemSize; i += vlen) { - ucharv t0 = vloadn(0, src1 + src1_index + i); - ucharv t1 = vloadn(0, src2 + src2_index + i); - ucharv t2 = t0 Operation t1; - - vstoren(t2, 0, dst + dst_index + i); - } -#else - dst[dst_index] = src1[src1_index] Operation src2[src2_index]; + int src1_index = mad24(y, src1_step, mad24(x, (int)sizeof(T), src1_offset)); +#ifdef OP_BINARY + int src2_index = mad24(y, src2_step, mad24(x, (int)sizeof(T), src2_offset)); #endif + int dst_index = mad24(y, dst_step, mad24(x, (int)sizeof(T), dst_offset)); + + __global const T * src1 = (__global const T *)(src1ptr + src1_index); +#ifdef OP_BINARY + __global const T * src2 = (__global const T *)(src2ptr + src2_index); +#endif + __global T * dst = (__global T *)(dstptr + dst_index); + +#ifdef OP_BINARY + dst[0] = src1[0] Operation src2[0]; +#elif defined HAVE_SCALAR + dst[0] = src1[0] Operation scalar; +#else + dst[0] = Operation src1[0]; +#endif + } } } diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl b/modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl deleted file mode 100644 index 328ccd91ae..0000000000 --- a/modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl +++ /dev/null @@ -1,88 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jiang Liyuan, jlyuan001.good@163.com -// Peng Xiao, pengxiao@outlook.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not 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 Intel Corporation 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. -// -//M*/ - -////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////bitwise_binary//////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////// - -__kernel void arithm_bitwise_binary_mask(__global uchar * src1, int src1_step, int src1_offset, - __global uchar * src2, int src2_step, int src2_offset, - __global uchar * mask, int mask_step, int mask_offset, - __global uchar * dst, int dst_step, int dst_offset, - int cols1, int rows) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols1 && y < rows) - { - int mask_index = mad24(y, mask_step, mask_offset + x); - - if (mask[mask_index]) - { -#if elemSize > 1 - x *= elemSize; -#endif - int src1_index = mad24(y, src1_step, x + src1_offset); - int src2_index = mad24(y, src2_step, x + src2_offset); - int dst_index = mad24(y, dst_step, x + dst_offset); - -#if elemSize > 1 - #pragma unroll - for (int i = 0; i < elemSize; i += vlen) - { - ucharv t0 = vloadn(0, src1 + src1_index + i); - ucharv t1 = vloadn(0, src2 + src2_index + i); - ucharv t2 = t0 Operation t1; - - vstoren(t2, 0, dst + dst_index + i); - } -#else - dst[dst_index] = src1[src1_index] Operation src2[src2_index]; -#endif - } - } -} diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl b/modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl deleted file mode 100644 index 434bd5eca8..0000000000 --- a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl +++ /dev/null @@ -1,82 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jiang Liyuan, jlyuan001.good@163.com -// Peng Xiao, pengxiao@outlook.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not 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 Intel Corporation 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. -// -// - -/////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////bitwise_binary///////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// - -__kernel void arithm_bitwise_binary_scalar( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, - __global uchar *dst, int dst_step, int dst_offset, - int cols, int rows) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { -#if elemSize > 1 - x *= elemSize; -#endif - int src1_index = mad24(y, src1_step, src1_offset + x); - int dst_index = mad24(y, dst_step, dst_offset + x); - -#if elemSize > 1 - #pragma unroll - for (int i = 0; i < elemSize; i += vlen) - { - ucharv t0 = vloadn(0, src1 + src1_index + i); - ucharv t1 = vloadn(0, src2 + i); - ucharv t2 = t0 Operation t1; - - vstoren(t2, 0, dst + dst_index + i); - } -#else - dst[dst_index] = src1[src1_index] Operation src2[0]; -#endif - } -} diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl b/modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl deleted file mode 100644 index 756f20165b..0000000000 --- a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl +++ /dev/null @@ -1,86 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jiang Liyuan, jlyuan001.good@163.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not 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 Intel Corporation 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. -// -//M*/ - -////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////bitwise_binary//////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////// - -__kernel void arithm_bitwise_binary_scalar_mask(__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, - __global uchar *mask, int mask_step, int mask_offset, - __global uchar *dst, int dst_step, int dst_offset, - int cols, int rows) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int mask_index = mad24(y, mask_step, x + mask_offset); - - if (mask[mask_index]) - { -#if elemSize > 1 - x *= elemSize; -#endif - int src1_index = mad24(y, src1_step, x + src1_offset); - int dst_index = mad24(y, dst_step, x + dst_offset); - -#if elemSize > 1 - #pragma unroll - for (int i = 0; i < elemSize; i += vlen) - { - ucharv t0 = vloadn(0, src1 + src1_index + i); - ucharv t1 = vloadn(0, src2 + i); - ucharv t2 = t0 Operation t1; - - vstoren(t2, 0, dst + dst_index + i); - } -#else - dst[dst_index] = src1[src1_index] Operation src2[0]; -#endif - } - } -} diff --git a/modules/ocl/src/opencl/arithm_bitwise_not.cl b/modules/ocl/src/opencl/arithm_bitwise_not.cl deleted file mode 100644 index b6f76d6065..0000000000 --- a/modules/ocl/src/opencl/arithm_bitwise_not.cl +++ /dev/null @@ -1,253 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jiang Liyuan, jlyuan001.good@163.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's 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. -// -// * The name of the copyright holders may not 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 Intel Corporation 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. -// -//M*/ - -#ifdef DOUBLE_SUPPORT -#ifdef cl_amd_fp64 -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#elif defined (cl_khr_fp64) -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#endif -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////BITWISE_NOT//////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// - -__kernel void arithm_bitwise_not_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - int src1_index = mad24(y, src1_step, x + src1_offset); - - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 dst_data = vload4(0, dst + dst_index); - uchar4 tmp_data = ~src1_data; - - dst_data.x = dst_index + 0 < dst_end ? tmp_data.x : dst_data.x; - dst_data.y = dst_index + 1 < dst_end ? tmp_data.y : dst_data.y; - dst_data.z = dst_index + 2 < dst_end ? tmp_data.z : dst_data.z; - dst_data.w = dst_index + 3 < dst_end ? tmp_data.w : dst_data.w; - - vstore4(dst_data, 0, dst + dst_index); - } -} - - -__kernel void arithm_bitwise_not_D1 (__global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - int src1_index = mad24(y, src1_step, x + src1_offset); - - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x); - - char4 src1_data = vload4(0, src1 + src1_index); - char4 dst_data = vload4(0, dst + dst_index); - char4 tmp_data = ~src1_data; - - dst_data.x = dst_index + 0 < dst_end ? tmp_data.x : dst_data.x; - dst_data.y = dst_index + 1 < dst_end ? tmp_data.y : dst_data.y; - dst_data.z = dst_index + 2 < dst_end ? tmp_data.z : dst_data.z; - dst_data.w = dst_index + 3 < dst_end ? tmp_data.w : dst_data.w; - - vstore4(dst_data, 0, dst + dst_index); - } -} - - -__kernel void arithm_bitwise_not_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - ushort4 tmp_data = ~ src1_data; - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_bitwise_not_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - short4 tmp_data = ~ src1_data; - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_bitwise_not_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int data1 = *((__global int *)((__global char *)src1 + src1_index)); - int tmp = ~ data1; - - *((__global int *)((__global char *)dst + dst_index)) = tmp; - } -} - -__kernel void arithm_bitwise_not_D5 (__global char *src, int src_step, int src_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src_index = mad24(y, src_step, (x << 2) + src_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - char4 data; - - data = *((__global char4 *)((__global char *)src + src_index)); - data = ~ data; - - *((__global char4 *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_bitwise_not_D6 (__global char *src, int src_step, int src_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src_index = mad24(y, src_step, (x << 3) + src_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - char8 data; - - data = *((__global char8 *)((__global char *)src + src_index)); - data = ~ data; - - *((__global char8 *)((__global char *)dst + dst_index)) = data; - } -} -#endif From 357a856cf68b6bb63f05f66ba9d7556f292913a8 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Mar 2014 22:35:08 +0400 Subject: [PATCH 188/293] nonfree perf tests --- modules/nonfree/perf/perf_main.cpp | 2 +- modules/nonfree/perf/perf_surf_ocl.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/nonfree/perf/perf_main.cpp b/modules/nonfree/perf/perf_main.cpp index 03f9b71852..2cd609d414 100644 --- a/modules/nonfree/perf/perf_main.cpp +++ b/modules/nonfree/perf/perf_main.cpp @@ -5,7 +5,7 @@ static const char * impls[] = { #ifdef HAVE_CUDA "cuda", #endif -#ifdef HAVE_OPENCL +#ifdef HAVE_OPENCV_OCL "ocl", #endif "plain" diff --git a/modules/nonfree/perf/perf_surf_ocl.cpp b/modules/nonfree/perf/perf_surf_ocl.cpp index 4528b79268..8413ed072c 100644 --- a/modules/nonfree/perf/perf_surf_ocl.cpp +++ b/modules/nonfree/perf/perf_surf_ocl.cpp @@ -59,7 +59,7 @@ typedef perf::TestBaseWithParam OCL_SURF; #define OCL_TEST_CYCLE() for( ; startTimer(), next(); cv::ocl::finish(), stopTimer()) -PERF_TEST_P(OCL_SURF, with_data_transfer, testing::Values(SURF_IMAGES)) +PERF_TEST_P(OCL_SURF, DISABLED_with_data_transfer, testing::Values(SURF_IMAGES)) { string filename = getDataPath(GetParam()); Mat src = imread(filename, IMREAD_GRAYSCALE); @@ -94,7 +94,7 @@ PERF_TEST_P(OCL_SURF, with_data_transfer, testing::Values(SURF_IMAGES)) SANITY_CHECK_NOTHING(); } -PERF_TEST_P(OCL_SURF, without_data_transfer, testing::Values(SURF_IMAGES)) +PERF_TEST_P(OCL_SURF, DISABLED_without_data_transfer, testing::Values(SURF_IMAGES)) { string filename = getDataPath(GetParam()); Mat src = imread(filename, IMREAD_GRAYSCALE); From 61c347fb76ce3789e85fb41c526d007f11661870 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Thu, 13 Mar 2014 22:56:46 +0400 Subject: [PATCH 189/293] typos --- modules/ocl/src/arithm.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index eabc055d0a..e7638ecc4d 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -517,7 +517,7 @@ static void arithmetic_minMax_run(const oclMat &src, const oclMat & mask, cl_mem size_t globalThreads[3] = { groupnum * 256, 1, 1 }; size_t localThreads[3] = { 256, 1, 1 }; - // kernel use fixed grid size, replace lt on NULL is imposible without kernel changes + // kernel use fixed grid size, replace lt on NULL is impossible without kernel changes openCLExecuteKernel(src.clCxt, &arithm_minMax, "arithm_op_minMax", globalThreads, localThreads, args, -1, -1, buildOptions.c_str()); } @@ -1140,7 +1140,7 @@ static void arithmetic_minMaxLoc_run(const oclMat &src, cl_mem &dst, int vlen , sprintf(build_options, "-D DEPTH_%d -D REPEAT_S%d -D REPEAT_E%d", src.depth(), repeat_s, repeat_e); size_t gt[3] = {groupnum * 256, 1, 1}, lt[3] = {256, 1, 1}; - // kernel use fixed grid size, replace lt on NULL is imposible without kernel changes + // kernel use fixed grid size, replace lt on NULL is impossible without kernel changes openCLExecuteKernel(src.clCxt, &arithm_minMaxLoc, "arithm_op_minMaxLoc", gt, lt, args, -1, -1, build_options); } @@ -1170,7 +1170,7 @@ static void arithmetic_minMaxLoc_mask_run(const oclMat &src, const oclMat &mask, args.push_back( make_pair( sizeof(cl_mem) , (void *)&mask.data )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst )); - // kernel use fixed grid size, replace lt on NULL is imposible without kernel changes + // kernel use fixed grid size, replace lt on NULL is impossible without kernel changes openCLExecuteKernel(src.clCxt, &arithm_minMaxLoc_mask, "arithm_op_minMaxLoc_mask", gt, lt, args, -1, -1, build_options); } } From f9484bae8ab748132e753b8c217c5ded36a5c9dd Mon Sep 17 00:00:00 2001 From: kuroda sho Date: Fri, 14 Mar 2014 17:02:20 +0900 Subject: [PATCH 190/293] fix: use "cvAlloc" wrapper function for malloc. --- modules/core/src/persistence.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/persistence.cpp b/modules/core/src/persistence.cpp index 4a6e0c9ec5..6847eec34d 100644 --- a/modules/core/src/persistence.cpp +++ b/modules/core/src/persistence.cpp @@ -4893,7 +4893,7 @@ cvUnregisterType( const char* type_name ) if( !CvType::first || !CvType::last ) CvType::first = CvType::last = 0; - cvFree( info ); + cvFree( &info ); } } From 80708008a411f8beb50e300128b82a8d35921c40 Mon Sep 17 00:00:00 2001 From: berak Date: Sun, 16 Mar 2014 14:04:05 +0100 Subject: [PATCH 191/293] missing () on Mat::type() --- modules/core/doc/basic_structures.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/doc/basic_structures.rst b/modules/core/doc/basic_structures.rst index 6f9c10e1db..3edfa3b069 100644 --- a/modules/core/doc/basic_structures.rst +++ b/modules/core/doc/basic_structures.rst @@ -1350,7 +1350,7 @@ Copies the matrix to another one. The method copies the matrix data to another matrix. Before copying the data, the method invokes :: - m.create(this->size(), this->type); + m.create(this->size(), this->type()); so that the destination matrix is reallocated if needed. While ``m.copyTo(m);`` works flawlessly, the function does not handle the case of a partial overlap between the source and the destination matrices. @@ -1445,7 +1445,7 @@ Transposes a matrix. The method performs matrix transposition by means of matrix expressions. It does not perform the actual transposition but returns a temporary matrix transposition object that can be further used as a part of more complex matrix expressions or can be assigned to a matrix: :: - Mat A1 = A + Mat::eye(A.size(), A.type)*lambda; + Mat A1 = A + Mat::eye(A.size(), A.type())*lambda; Mat C = A1.t()*A1; // compute (A + lambda*I)^t * (A + lamda*I) From 24d8cbf940d47b6cea39634a6c3a2a051b33cd74 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Sun, 16 Mar 2014 20:07:05 +0400 Subject: [PATCH 192/293] Viz::setViewerPose() - changed up-vector to down (common for Computer Vision & Odometry) --- modules/viz/src/vizimpl.cpp | 6 +++--- modules/viz/test/test_tutorial3.cpp | 22 +++++++++------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/modules/viz/src/vizimpl.cpp b/modules/viz/src/vizimpl.cpp index 2401e76b5b..ab621ad16d 100644 --- a/modules/viz/src/vizimpl.cpp +++ b/modules/viz/src/vizimpl.cpp @@ -427,12 +427,12 @@ void cv::viz::Viz3d::VizImpl::setViewerPose(const Affine3d &pose) // Rotate the view vector cv::Matx33d rotation = pose.rotation(); - cv::Vec3d y_axis(0.0, 1.0, 0.0); + cv::Vec3d y_axis(0.0, -1.0, 0.0); // In Computer Vision Camera Y-axis is oriented down cv::Vec3d up_vec(rotation * y_axis); // Compute the new focal point cv::Vec3d z_axis(0.0, 0.0, 1.0); - cv::Vec3d focal_vec = pos_vec + rotation * z_axis; + cv::Vec3d focal_vec = pose * z_axis; camera.SetPosition(pos_vec.val); camera.SetFocalPoint(focal_vec.val); @@ -450,7 +450,7 @@ cv::Affine3d cv::viz::Viz3d::VizImpl::getViewerPose() Vec3d view_up(camera.GetViewUp()); Vec3d focal(camera.GetFocalPoint()); - Vec3d y_axis = normalized(view_up); + Vec3d y_axis = normalized(-view_up); // In Computer Vision Camera Y-axis is oriented down Vec3d z_axis = normalized(focal - pos); Vec3d x_axis = normalized(y_axis.cross(z_axis)); diff --git a/modules/viz/test/test_tutorial3.cpp b/modules/viz/test/test_tutorial3.cpp index 590e29ebfd..b20e8d7a11 100644 --- a/modules/viz/test/test_tutorial3.cpp +++ b/modules/viz/test/test_tutorial3.cpp @@ -15,50 +15,46 @@ void tutorial3(bool camera_pov) myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); /// Let's assume camera has the following properties - Point3d cam_pos(3.0, 3.0, 3.0), cam_focal_point(3.0, 3.0, 2.0), cam_y_dir(-1.0, 0.0, 0.0); + Point3d cam_origin(3.0, 3.0, 3.0), cam_focal_point(3.0, 3.0, 2.0), cam_y_dir(-1.0, 0.0, 0.0); /// We can get the pose of the cam using makeCameraPose - Affine3d cam_pose = viz::makeCameraPose(cam_pos, cam_focal_point, cam_y_dir); + Affine3d camera_pose = viz::makeCameraPose(cam_origin, cam_focal_point, cam_y_dir); /// We can get the transformation matrix from camera coordinate system to global using /// - makeTransformToGlobal. We need the axes of the camera - Affine3d transform = viz::makeTransformToGlobal(Vec3d(0.0, -1.0, 0.0), Vec3d(-1.0, 0.0, 0.0), Vec3d(0.0, 0.0, -1.0), cam_pos); + Affine3d transform = viz::makeTransformToGlobal(Vec3d(0.0, -1.0, 0.0), Vec3d(-1.0, 0.0, 0.0), Vec3d(0.0, 0.0, -1.0), cam_origin); /// Create a cloud widget. Mat dragon_cloud = viz::readCloud(get_dragon_ply_file_path()); viz::WCloud cloud_widget(dragon_cloud, viz::Color::green()); /// Pose of the widget in camera frame - Affine3d cloud_pose = Affine3d().translate(Vec3d(0.0, 0.0, 3.0)); + Affine3d cloud_pose = Affine3d().rotate(Vec3d(0.0, CV_PI/2, 0.0)).rotate(Vec3d(0.0, 0.0, CV_PI)).translate(Vec3d(0.0, 0.0, 3.0)); /// Pose of the widget in global frame Affine3d cloud_pose_global = transform * cloud_pose; /// Visualize camera frame + myWindow.showWidget("CPW_FRUSTUM", viz::WCameraPosition(Vec2f(0.889484f, 0.523599f)), camera_pose); if (!camera_pov) - { - viz::WCameraPosition cpw(0.5); // Coordinate axes - viz::WCameraPosition cpw_frustum(Vec2f(0.889484f, 0.523599f)); // Camera frustum - myWindow.showWidget("CPW", cpw, cam_pose); - myWindow.showWidget("CPW_FRUSTUM", cpw_frustum, cam_pose); - } + myWindow.showWidget("CPW", viz::WCameraPosition(0.5), camera_pose); /// Visualize widget myWindow.showWidget("bunny", cloud_widget, cloud_pose_global); /// Set the viewer pose to that of camera if (camera_pov) - myWindow.setViewerPose(cam_pose); + myWindow.setViewerPose(camera_pose); /// Start event loop. myWindow.spin(); } -TEST(Viz, DISABLED_tutorial3_global_view) +TEST(Viz, tutorial3_global_view) { tutorial3(false); } -TEST(Viz, DISABLED_tutorial3_camera_view) +TEST(Viz, tutorial3_camera_view) { tutorial3(true); } From 5f94a205d14180d5fd9c0b451add6da35a2b3f9f Mon Sep 17 00:00:00 2001 From: berak Date: Thu, 13 Mar 2014 08:39:15 +0100 Subject: [PATCH 193/293] fixed h / s ranges in histogram_calculation tutorial literalinclude literalinclude, dropped :lines: --- .../histogram_comparison.rst | 90 ++----------------- .../Histograms_Matching/compareHist_Demo.cpp | 6 +- 2 files changed, 9 insertions(+), 87 deletions(-) diff --git a/doc/tutorials/imgproc/histograms/histogram_comparison/histogram_comparison.rst b/doc/tutorials/imgproc/histograms/histogram_comparison/histogram_comparison.rst index 1a5c59de07..f5f636d08b 100644 --- a/doc/tutorials/imgproc/histograms/histogram_comparison/histogram_comparison.rst +++ b/doc/tutorials/imgproc/histograms/histogram_comparison/histogram_comparison.rst @@ -84,88 +84,10 @@ Code * **Code at glance:** -.. code-block:: cpp +.. literalinclude:: ../../../../../samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp + :language: cpp + :tab-width: 4 - #include "opencv2/highgui/highgui.hpp" - #include "opencv2/imgproc/imgproc.hpp" - #include - #include - - using namespace std; - using namespace cv; - - /** @function main */ - int main( int argc, char** argv ) - { - Mat src_base, hsv_base; - Mat src_test1, hsv_test1; - Mat src_test2, hsv_test2; - Mat hsv_half_down; - - /// Load three images with different environment settings - if( argc < 4 ) - { printf("** Error. Usage: ./compareHist_Demo \n"); - return -1; - } - - src_base = imread( argv[1], 1 ); - src_test1 = imread( argv[2], 1 ); - src_test2 = imread( argv[3], 1 ); - - /// Convert to HSV - cvtColor( src_base, hsv_base, CV_BGR2HSV ); - cvtColor( src_test1, hsv_test1, CV_BGR2HSV ); - cvtColor( src_test2, hsv_test2, CV_BGR2HSV ); - - hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) ); - - /// Using 30 bins for hue and 32 for saturation - int h_bins = 50; int s_bins = 60; - int histSize[] = { h_bins, s_bins }; - - // hue varies from 0 to 256, saturation from 0 to 180 - float h_ranges[] = { 0, 256 }; - float s_ranges[] = { 0, 180 }; - - const float* ranges[] = { h_ranges, s_ranges }; - - // Use the o-th and 1-st channels - int channels[] = { 0, 1 }; - - /// Histograms - MatND hist_base; - MatND hist_half_down; - MatND hist_test1; - MatND hist_test2; - - /// Calculate the histograms for the HSV images - calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false ); - normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() ); - - calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false ); - normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() ); - - calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false ); - normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() ); - - calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false ); - normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() ); - - /// Apply the histogram comparison methods - for( int i = 0; i < 4; i++ ) - { int compare_method = i; - double base_base = compareHist( hist_base, hist_base, compare_method ); - double base_half = compareHist( hist_base, hist_half_down, compare_method ); - double base_test1 = compareHist( hist_base, hist_test1, compare_method ); - double base_test2 = compareHist( hist_base, hist_test2, compare_method ); - - printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 ); - } - - printf( "Done \n" ); - - return 0; - } Explanation @@ -211,11 +133,11 @@ Explanation .. code-block:: cpp - int h_bins = 50; int s_bins = 32; + int h_bins = 50; int s_bins = 60; int histSize[] = { h_bins, s_bins }; - float h_ranges[] = { 0, 256 }; - float s_ranges[] = { 0, 180 }; + float h_ranges[] = { 0, 180 }; + float s_ranges[] = { 0, 256 }; const float* ranges[] = { h_ranges, s_ranges }; diff --git a/samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp b/samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp index f4dd4e5e4e..424a38e93a 100644 --- a/samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp +++ b/samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp @@ -40,13 +40,13 @@ int main( int argc, char** argv ) hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) ); - /// Using 30 bins for hue and 32 for saturation + /// Using 50 bins for hue and 60 for saturation int h_bins = 50; int s_bins = 60; int histSize[] = { h_bins, s_bins }; - // hue varies from 0 to 256, saturation from 0 to 180 - float s_ranges[] = { 0, 256 }; + // hue varies from 0 to 179, saturation from 0 to 255 float h_ranges[] = { 0, 180 }; + float s_ranges[] = { 0, 256 }; const float* ranges[] = { h_ranges, s_ranges }; From 6b8de222d770174fc1fb31b1b64da8995e8a8ebf Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 5 Mar 2014 09:57:16 +0400 Subject: [PATCH 194/293] OpenCV_MODULES_SUFFIX variable added to OpenCVConfig.cmake to enable custom module configurations. --- cmake/templates/OpenCVConfig.cmake.in | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/cmake/templates/OpenCVConfig.cmake.in b/cmake/templates/OpenCVConfig.cmake.in index 6db61d2112..3222048282 100644 --- a/cmake/templates/OpenCVConfig.cmake.in +++ b/cmake/templates/OpenCVConfig.cmake.in @@ -18,8 +18,8 @@ # This file will define the following variables: # - OpenCV_LIBS : The list of all imported targets for OpenCV modules. # - OpenCV_INCLUDE_DIRS : The OpenCV include directories. -# - OpenCV_COMPUTE_CAPABILITIES : The version of compute capability -# - OpenCV_ANDROID_NATIVE_API_LEVEL : Minimum required level of Android API +# - OpenCV_COMPUTE_CAPABILITIES : The version of compute capability. +# - OpenCV_ANDROID_NATIVE_API_LEVEL : Minimum required level of Android API. # - OpenCV_VERSION : The version of this OpenCV build: "@OPENCV_VERSION@" # - OpenCV_VERSION_MAJOR : Major version part of OpenCV_VERSION: "@OPENCV_VERSION_MAJOR@" # - OpenCV_VERSION_MINOR : Minor version part of OpenCV_VERSION: "@OPENCV_VERSION_MINOR@" @@ -27,22 +27,26 @@ # - OpenCV_VERSION_TWEAK : Tweak version part of OpenCV_VERSION: "@OPENCV_VERSION_TWEAK@" # # Advanced variables: -# - OpenCV_SHARED -# - OpenCV_CONFIG_PATH -# - OpenCV_INSTALL_PATH (not set on Windows) -# - OpenCV_LIB_COMPONENTS -# - OpenCV_USE_MANGLED_PATHS -# - OpenCV_HAVE_ANDROID_CAMERA +# - OpenCV_SHARED : Use OpenCV as shared library +# - OpenCV_CONFIG_PATH : Path to this OpenCVConfig.cmake +# - OpenCV_INSTALL_PATH : OpenCV location (not set on Windows) +# - OpenCV_LIB_COMPONENTS : Present OpenCV modules list +# - OpenCV_USE_MANGLED_PATHS : Mangled OpenCV path flag +# - OpenCV_MODULES_SUFFIX : The suffix for OpenCVModules-XXX.cmake file +# - OpenCV_HAVE_ANDROID_CAMERA : Presence of Android native camera wrappers # # =================================================================================== -set(modules_file_suffix "") -if(ANDROID) - string(REPLACE - _ modules_file_suffix "_${ANDROID_NDK_ABI_NAME}") +if(NOT DEFINED OpenCV_MODULES_SUFFIX) + if(ANDROID) + string(REPLACE - _ OpenCV_MODULES_SUFFIX "_${ANDROID_NDK_ABI_NAME}") + else() + set(OpenCV_MODULES_SUFFIX "") + endif() endif() if(NOT TARGET opencv_core) - include(${CMAKE_CURRENT_LIST_DIR}/OpenCVModules${modules_file_suffix}.cmake) + include(${CMAKE_CURRENT_LIST_DIR}/OpenCVModules${OpenCV_MODULES_SUFFIX}.cmake) endif() # TODO All things below should be reviewed. What is about of moving this code into related modules (special vars/hooks/files) From 0c02e5de254d32e8f00bde4047f12624e77a7ef5 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Mon, 17 Mar 2014 17:02:49 +0400 Subject: [PATCH 195/293] minor doc fix --- modules/viz/doc/widget.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/viz/doc/widget.rst b/modules/viz/doc/widget.rst index 7c1fc45751..0601ba1b72 100644 --- a/modules/viz/doc/widget.rst +++ b/modules/viz/doc/widget.rst @@ -1049,7 +1049,7 @@ viz::WWidgetMerger::WWidgetMerger --------------------------------------- Constructs a WWidgetMerger. -.. ocv:WWidgetMerger:: WWidgetMerger() +.. ocv:function:: WWidgetMerger() viz::WWidgetMerger::addCloud ------------------------------- From 51cb6998ea1661b58a7ca267d6e0d6511ec63f6d Mon Sep 17 00:00:00 2001 From: yash Date: Mon, 24 Feb 2014 18:28:55 +0530 Subject: [PATCH 196/293] made the example consistent with the fuction definition and improved doc made the example consistent with the fuction definition and improved doc --- modules/core/doc/old_basic_structures.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/core/doc/old_basic_structures.rst b/modules/core/doc/old_basic_structures.rst index c8de17adae..1d5880a624 100644 --- a/modules/core/doc/old_basic_structures.rst +++ b/modules/core/doc/old_basic_structures.rst @@ -1436,7 +1436,7 @@ description rewritten using IplImage* color_img = cvCreateImage(cvSize(320,240), IPL_DEPTH_8U, 3); IplImage gray_img_hdr, *gray_img; - gray_img = (IplImage*)cvReshapeND(color_img, &gray_img_hdr, 1, 0, 0); + gray_img = (IplImage*)cvReshapeMatND(color_img, sizeof(gray_img_hdr), &gray_img_hdr, 1, 0, 0); ... @@ -1444,6 +1444,18 @@ description rewritten using int size[] = { 2, 2, 2 }; CvMatND* mat = cvCreateMatND(3, size, CV_32F); CvMat row_header, *row; + row = (CvMat*)cvReshapeMatND(mat, sizeof(row_header), &row_header, 0, 1, 0); + +.. + +In C, the header file for this function includes a convenient macro ``cvReshapeND`` that does away with the ``sizeof_header`` parameter. So, the lines containing the call to ``cvReshapeMatND`` in the examples may be replaced as follow: + +:: + + gray_img = (IplImage*)cvReshapeND(color_img, &gray_img_hdr, 1, 0, 0); + + ... + row = (CvMat*)cvReshapeND(mat, &row_header, 0, 1, 0); .. From 3940b6163b8370312d2a7fe66c96d43ab6d12efd Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 17 Mar 2014 18:52:28 +0400 Subject: [PATCH 197/293] remove intel guard since the code is 2 times faster on AMD too --- modules/ocl/perf/perf_filters.cpp | 6 +++--- modules/ocl/src/filtering.cpp | 6 +++--- modules/ocl/src/opencl/filtering_sep_filter_singlepass.cl | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/ocl/perf/perf_filters.cpp b/modules/ocl/perf/perf_filters.cpp index b3ffc51b30..c542d647cf 100644 --- a/modules/ocl/perf/perf_filters.cpp +++ b/modules/ocl/perf/perf_filters.cpp @@ -262,13 +262,13 @@ OCL_PERF_TEST_P(SobelFixture, Sobel, oclDst.download(dst); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 1e-3); } else if (RUN_PLAIN_IMPL) { TEST_CYCLE() cv::Sobel(src, dst, -1, dx, dy); - SANITY_CHECK(dst); + SANITY_CHECK(dst, 1e-3); } else OCL_PERF_ELSE @@ -326,7 +326,7 @@ OCL_PERF_TEST_P(GaussianBlurFixture, GaussianBlur, Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); - const double eps = src.depth() == CV_8U ? 1 + DBL_EPSILON : 3e-4; + const double eps = src.depth() == CV_8U ? 1 + DBL_EPSILON : 5e-4; if (RUN_OCL_IMPL) { diff --git a/modules/ocl/src/filtering.cpp b/modules/ocl/src/filtering.cpp index 35aa226de6..77052ffbf3 100644 --- a/modules/ocl/src/filtering.cpp +++ b/modules/ocl/src/filtering.cpp @@ -774,12 +774,12 @@ static void sepFilter2D_SinglePass(const oclMat &src, oclMat &dst, option += " -D KERNEL_MATRIX_X="; for(int i=0; i( &row_kernel.at(i) ) ); + option += cv::format("DIG(0x%x)", *reinterpret_cast( &row_kernel.at(i) ) ); option += "0x0"; option += " -D KERNEL_MATRIX_Y="; for(int i=0; i( &col_kernel.at(i) ) ); + option += cv::format("DIG(0x%x)", *reinterpret_cast( &col_kernel.at(i) ) ); option += "0x0"; switch(src.type()) @@ -1410,7 +1410,7 @@ Ptr cv::ocl::createSeparableLinearFilter_GPU(int srcType, int //if image size is non-degenerate and large enough //and if filter support is reasonable to satisfy larger local memory requirements, //then we can use single pass routine to avoid extra runtime calls overhead - if( clCxt && clCxt->supportsFeature(FEATURE_CL_INTEL_DEVICE) && + if( clCxt && rowKernel.rows <= 21 && columnKernel.rows <= 21 && (rowKernel.rows & 1) == 1 && (columnKernel.rows & 1) == 1 && imgSize.width > optimizedSepFilterLocalSize + (rowKernel.rows>>1) && diff --git a/modules/ocl/src/opencl/filtering_sep_filter_singlepass.cl b/modules/ocl/src/opencl/filtering_sep_filter_singlepass.cl index c6555bff0f..c5f490284e 100644 --- a/modules/ocl/src/opencl/filtering_sep_filter_singlepass.cl +++ b/modules/ocl/src/opencl/filtering_sep_filter_singlepass.cl @@ -84,6 +84,8 @@ #define DST(_x,_y) (((global DSTTYPE*)(Dst+DstOffset+(_y)*DstPitch))[_x]) +#define DIG(a) a, + //horizontal and vertical filter kernels //should be defined on host during compile time to avoid overhead __constant uint mat_kernelX[] = {KERNEL_MATRIX_X}; From 16869225ffb6029cea0f694b8ee9ffab2eb835bd Mon Sep 17 00:00:00 2001 From: RJ2 Date: Tue, 18 Mar 2014 08:01:18 +0100 Subject: [PATCH 198/293] It's will be better --- doc/tutorials/core/adding_images/adding_images.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tutorials/core/adding_images/adding_images.rst b/doc/tutorials/core/adding_images/adding_images.rst index e3135693de..601dbc07ef 100644 --- a/doc/tutorials/core/adding_images/adding_images.rst +++ b/doc/tutorials/core/adding_images/adding_images.rst @@ -6,12 +6,12 @@ Adding (blending) two images using OpenCV Goal ===== -In this tutorial you will learn how to: +In this tutorial you will learn: .. container:: enumeratevisibleitemswithsquare - * What is *linear blending* and why it is useful. - * Add two images using :add_weighted:`addWeighted <>` + * what is *linear blending* and why it is useful; + * how to add two images using :add_weighted:`addWeighted <>` Theory ======= From 0470bb0e2958ae9725465fe6ab2767f19c5009fa Mon Sep 17 00:00:00 2001 From: RJ2 Date: Tue, 18 Mar 2014 08:59:53 +0100 Subject: [PATCH 199/293] I have changed one sentence in tutorial, making it more understandable --- doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst b/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst index ef0f8640ca..b6a18fee88 100644 --- a/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst +++ b/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst @@ -18,7 +18,7 @@ We'll seek answers for the following questions: Our test case ============= -Let us consider a simple color reduction method. Using the unsigned char C and C++ type for matrix item storing a channel of pixel may have up to 256 different values. For a three channel image this can allow the formation of way too many colors (16 million to be exact). Working with so many color shades may give a heavy blow to our algorithm performance. However, sometimes it is enough to work with a lot less of them to get the same final result. +Let us consider a simple color reduction method. By using the unsigned char C and C++ type for matrix item storing, a channel of pixel may have up to 256 different values. For a three channel image this can allow the formation of way too many colors (16 million to be exact). Working with so many color shades may give a heavy blow to our algorithm performance. However, sometimes it is enough to work with a lot less of them to get the same final result. In this cases it's common that we make a *color space reduction*. This means that we divide the color space current value with a new input value to end up with fewer colors. For instance every value between zero and nine takes the new value zero, every value between ten and nineteen the value ten and so on. From b4e4f13f9efdfe2d1f0909d78556d8c865d5f371 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 5 Mar 2014 12:31:04 +0400 Subject: [PATCH 200/293] Superres module enabled for Android. GPU samples build fixed for Android. --- cmake/OpenCVModule.cmake | 66 +++++++++----------- modules/superres/CMakeLists.txt | 9 ++- modules/superres/src/btv_l1_gpu.cpp | 2 +- modules/superres/src/frame_source.cpp | 2 +- modules/superres/src/input_array_utility.cpp | 2 +- modules/superres/src/optical_flow.cpp | 2 +- modules/superres/src/precomp.hpp | 2 +- modules/ts/CMakeLists.txt | 2 +- samples/gpu/CMakeLists.txt | 2 +- samples/gpu/brox_optical_flow.cpp | 1 + samples/gpu/opticalflow_nvidia_api.cpp | 1 + samples/gpu/super_resolution.cpp | 2 + 12 files changed, 49 insertions(+), 44 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index c9c351113c..e11f5e671f 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -27,7 +27,8 @@ # The verbose template for OpenCV module: # # ocv_add_module(modname ) -# ocv_glob_module_sources() or glob them manually and ocv_set_module_sources(...) +# ocv_glob_module_sources(([EXCLUDE_CUDA] ) +# or glob them manually and ocv_set_module_sources(...) # ocv_module_include_directories() # ocv_create_module() # @@ -478,14 +479,20 @@ endmacro() # finds and sets headers and sources for the standard OpenCV module # Usage: -# ocv_glob_module_sources() -macro(ocv_glob_module_sources EXCLUDE_CUDA EXCLUDE_OPENCL) +# ocv_glob_module_sources([EXCLUDE_CUDA] ) +macro(ocv_glob_module_sources) + set(_argn ${ARGN}) + list(FIND _argn "EXCLUDE_CUDA" exclude_cuda) + if(NOT exclude_cuda EQUAL -1) + list(REMOVE_AT _argn ${exclude_cuda}) + endif() + file(GLOB_RECURSE lib_srcs "src/*.cpp") file(GLOB_RECURSE lib_int_hdrs "src/*.hpp" "src/*.h") file(GLOB lib_hdrs "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") file(GLOB lib_hdrs_detail "include/opencv2/${name}/detail/*.hpp" "include/opencv2/${name}/detail/*.h") - if (NOT ${EXCLUDE_CUDA}) + if (exclude_cuda EQUAL -1) file(GLOB lib_cuda_srcs "src/cuda/*.cu") set(cuda_objs "") set(lib_cuda_hdrs "") @@ -504,26 +511,22 @@ macro(ocv_glob_module_sources EXCLUDE_CUDA EXCLUDE_OPENCL) source_group("Src" FILES ${lib_srcs} ${lib_int_hdrs}) - if (NOT ${EXCLUDE_OPENCL}) - file(GLOB cl_kernels "src/opencl/*.cl") - if(HAVE_opencv_ocl AND cl_kernels) - ocv_include_directories(${OPENCL_INCLUDE_DIRS}) - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp" - COMMAND ${CMAKE_COMMAND} -DCL_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/opencl" -DOUTPUT="${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" -P "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake" - DEPENDS ${cl_kernels} "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake") - source_group("OpenCL" FILES ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") - list(APPEND lib_srcs ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") - endif() + file(GLOB cl_kernels "src/opencl/*.cl") + if(HAVE_opencv_ocl AND cl_kernels) + ocv_include_directories(${OPENCL_INCLUDE_DIRS}) + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp" + COMMAND ${CMAKE_COMMAND} -DCL_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/opencl" -DOUTPUT="${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" -P "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake" + DEPENDS ${cl_kernels} "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake") + source_group("OpenCL" FILES ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") + list(APPEND lib_srcs ${cl_kernels} "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.cpp" "${CMAKE_CURRENT_BINARY_DIR}/opencl_kernels.hpp") endif() source_group("Include" FILES ${lib_hdrs}) source_group("Include\\detail" FILES ${lib_hdrs_detail}) - message(":${EXCLUDE_CUDA}: ${lib_cuda_srcs}") - - ocv_set_module_sources(${ARGN} HEADERS ${lib_hdrs} ${lib_hdrs_detail} - SOURCES ${lib_srcs} ${lib_int_hdrs} ${cuda_objs} ${lib_cuda_srcs} ${lib_cuda_hdrs}) + ocv_set_module_sources(${_argn} HEADERS ${lib_hdrs} ${lib_hdrs_detail} + SOURCES ${lib_srcs} ${lib_int_hdrs} ${cuda_objs} ${lib_cuda_srcs} ${lib_cuda_hdrs}) endmacro() # creates OpenCV module in current folder @@ -622,27 +625,20 @@ endmacro() # short command for adding simple OpenCV module # see ocv_add_module for argument details # Usage: -# ocv_define_module(module_name [INTERNAL] [REQUIRED] [] [OPTIONAL ]) +# ocv_define_module(module_name [INTERNAL] [EXCLUDE_CUDA] [REQUIRED] [] [OPTIONAL ]) macro(ocv_define_module module_name) - set(_tmp_argn ${ARGN}) - set(exclude_cuda 0) - set(exclude_opencl 0) - set(argv0 ${ARGV1}) - set(argv1 ${ARGV2}) - set(argv2 ${ARGV3}) - foreach(i RANGE 0 2) - if("${argv${i}}" STREQUAL "EXCLUDE_CUDA") - set(exclude_cuda 1) - list(REMOVE_AT _tmp_argn ${i}) - elseif ("${argv${i}}" STREQUAL "EXCLUDE_OPENCL") - set(exclude_opencl 1) - list(REMOVE_AT _tmp_argn ${i}) + set(_argn ${ARGN}) + set(exclude_cuda "") + foreach(arg ${_argn}) + if("${arg}" STREQUAL "EXCLUDE_CUDA") + set(exclude_cuda "${arg}") + list(REMOVE_ITEM _argn ${arg}) endif() endforeach() - ocv_add_module(${module_name} ${_tmp_argn}) + ocv_add_module(${module_name} ${_argn}) ocv_module_include_directories() - ocv_glob_module_sources(${exclude_cuda} ${exclude_opencl}) + ocv_glob_module_sources(${exclude_cuda}) ocv_create_module() ocv_add_precompiled_headers(${the_module}) diff --git a/modules/superres/CMakeLists.txt b/modules/superres/CMakeLists.txt index 82c61cccb4..8e3d7b2f2d 100644 --- a/modules/superres/CMakeLists.txt +++ b/modules/superres/CMakeLists.txt @@ -1,7 +1,12 @@ -if(ANDROID OR IOS) +if(IOS) ocv_module_disable(superres) endif() set(the_description "Super Resolution") ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 -Wundef -Wshadow) -ocv_define_module(superres opencv_imgproc opencv_video OPTIONAL opencv_gpu opencv_highgui opencv_ocl ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) +if(ENABLE_DYNAMIC_CUDA) + add_definitions(-DDYNAMIC_CUDA_SUPPORT) + ocv_define_module(superres EXCLUDE_CUDA opencv_imgproc opencv_video OPTIONAL opencv_highgui opencv_ocl) +else() + ocv_define_module(superres opencv_imgproc opencv_video OPTIONAL opencv_gpu opencv_highgui opencv_ocl ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) +endif() diff --git a/modules/superres/src/btv_l1_gpu.cpp b/modules/superres/src/btv_l1_gpu.cpp index b93bcfd577..2f40fa3771 100644 --- a/modules/superres/src/btv_l1_gpu.cpp +++ b/modules/superres/src/btv_l1_gpu.cpp @@ -51,7 +51,7 @@ using namespace cv::gpu; using namespace cv::superres; using namespace cv::superres::detail; -#if !defined(HAVE_CUDA) || !defined(HAVE_OPENCV_GPU) +#if !defined(HAVE_CUDA) || !defined(HAVE_OPENCV_GPU) || defined(DYNAMIC_CUDA_SUPPORT) Ptr cv::superres::createSuperResolution_BTVL1_GPU() { diff --git a/modules/superres/src/frame_source.cpp b/modules/superres/src/frame_source.cpp index 20e45d9518..5f59a98b80 100644 --- a/modules/superres/src/frame_source.cpp +++ b/modules/superres/src/frame_source.cpp @@ -200,7 +200,7 @@ Ptr cv::superres::createFrameSource_Camera(int deviceId) ////////////////////////////////////////////////////// // VideoFrameSource_GPU -#ifndef HAVE_OPENCV_GPU +#if !defined(HAVE_OPENCV_GPU) || defined(DYNAMIC_CUDA_SUPPORT) Ptr cv::superres::createFrameSource_Video_GPU(const string& fileName) { diff --git a/modules/superres/src/input_array_utility.cpp b/modules/superres/src/input_array_utility.cpp index 075cf95144..10fc1c96ed 100644 --- a/modules/superres/src/input_array_utility.cpp +++ b/modules/superres/src/input_array_utility.cpp @@ -207,7 +207,7 @@ namespace switch (src.kind()) { case _InputArray::GPU_MAT: - #ifdef HAVE_OPENCV_GPU + #if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) gpu::cvtColor(src.getGpuMat(), dst.getGpuMatRef(), code, cn); #else CV_Error(CV_StsNotImplemented, "The called functionality is disabled for current build or platform"); diff --git a/modules/superres/src/optical_flow.cpp b/modules/superres/src/optical_flow.cpp index e1e8a10275..617f83a5e1 100644 --- a/modules/superres/src/optical_flow.cpp +++ b/modules/superres/src/optical_flow.cpp @@ -344,7 +344,7 @@ Ptr cv::superres::createOptFlow_DualTVL1() /////////////////////////////////////////////////////////////////// // GpuOpticalFlow -#ifndef HAVE_OPENCV_GPU +#if !defined(HAVE_OPENCV_GPU) || defined(DYNAMIC_CUDA_SUPPORT) Ptr cv::superres::createOptFlow_Farneback_GPU() { diff --git a/modules/superres/src/precomp.hpp b/modules/superres/src/precomp.hpp index 4cf49411b6..65fc7c8502 100644 --- a/modules/superres/src/precomp.hpp +++ b/modules/superres/src/precomp.hpp @@ -56,7 +56,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/video/tracking.hpp" -#ifdef HAVE_OPENCV_GPU +#if defined(HAVE_OPENCV_GPU) && !defined(DYNAMIC_CUDA_SUPPORT) #include "opencv2/gpu/gpu.hpp" #ifdef HAVE_CUDA #include "opencv2/gpu/stream_accessor.hpp" diff --git a/modules/ts/CMakeLists.txt b/modules/ts/CMakeLists.txt index dcd3e1563b..bb56da2d98 100644 --- a/modules/ts/CMakeLists.txt +++ b/modules/ts/CMakeLists.txt @@ -11,7 +11,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) ocv_add_module(ts opencv_core opencv_features2d) -ocv_glob_module_sources(0 0) +ocv_glob_module_sources() ocv_module_include_directories() ocv_create_module() diff --git a/samples/gpu/CMakeLists.txt b/samples/gpu/CMakeLists.txt index 8fa539473b..d25c3a6b5d 100644 --- a/samples/gpu/CMakeLists.txt +++ b/samples/gpu/CMakeLists.txt @@ -41,7 +41,7 @@ if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) target_link_libraries(${the_target} ${OPENCV_LINKER_LIBS} ${OPENCV_GPU_SAMPLES_REQUIRED_DEPS}) - if(HAVE_CUDA) + if(HAVE_CUDA AND NOT ANDROID) target_link_libraries(${the_target} ${CUDA_CUDA_LIBRARY}) endif() diff --git a/samples/gpu/brox_optical_flow.cpp b/samples/gpu/brox_optical_flow.cpp index 722e19f02f..7cd5089b41 100644 --- a/samples/gpu/brox_optical_flow.cpp +++ b/samples/gpu/brox_optical_flow.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "cvconfig.h" #include "opencv2/core/core.hpp" diff --git a/samples/gpu/opticalflow_nvidia_api.cpp b/samples/gpu/opticalflow_nvidia_api.cpp index 05a37ef69d..31ee569788 100644 --- a/samples/gpu/opticalflow_nvidia_api.cpp +++ b/samples/gpu/opticalflow_nvidia_api.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "cvconfig.h" #include diff --git a/samples/gpu/super_resolution.cpp b/samples/gpu/super_resolution.cpp index 6efd24144c..85cb6cff1c 100644 --- a/samples/gpu/super_resolution.cpp +++ b/samples/gpu/super_resolution.cpp @@ -1,6 +1,8 @@ #include #include #include +#include + #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" From 0a16d93e1d2b8c23df77c769589cfc7982c68af1 Mon Sep 17 00:00:00 2001 From: Firat Kalaycilar Date: Tue, 18 Mar 2014 17:26:24 +0200 Subject: [PATCH 201/293] Fixed an issue with weight assignment causing the resulting GMM weights to be unsorted in the CUDA and OCL versions of BackgroundSubtractorMOG2 --- modules/gpu/src/cuda/bgfg_mog.cu | 5 +++-- modules/ocl/src/opencl/bgfg_mog.cl | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/gpu/src/cuda/bgfg_mog.cu b/modules/gpu/src/cuda/bgfg_mog.cu index 89ad5ff0b5..055c9e041e 100644 --- a/modules/gpu/src/cuda/bgfg_mog.cu +++ b/modules/gpu/src/cuda/bgfg_mog.cu @@ -489,7 +489,7 @@ namespace cv { namespace gpu { namespace device { //need only weight if fit is found float weight = alpha1 * gmm_weight(mode * frame.rows + y, x) + prune; - + int swap_count = 0; //fit not found yet if (!fitsPDF) { @@ -540,6 +540,7 @@ namespace cv { namespace gpu { namespace device if (weight < gmm_weight((i - 1) * frame.rows + y, x)) break; + swap_count++; //swap one up swap(gmm_weight, x, y, i - 1, frame.rows); swap(gmm_variance, x, y, i - 1, frame.rows); @@ -557,7 +558,7 @@ namespace cv { namespace gpu { namespace device nmodes--; } - gmm_weight(mode * frame.rows + y, x) = weight; //update weight by the calculated value + gmm_weight((mode - swap_count) * frame.rows + y, x) = weight; //update weight by the calculated value totalWeight += weight; } diff --git a/modules/ocl/src/opencl/bgfg_mog.cl b/modules/ocl/src/opencl/bgfg_mog.cl index 6a95316f0f..a7479b929c 100644 --- a/modules/ocl/src/opencl/bgfg_mog.cl +++ b/modules/ocl/src/opencl/bgfg_mog.cl @@ -376,7 +376,7 @@ __kernel void mog2_kernel(__global T_FRAME * frame, __global int* fgmask, __glob for (int mode = 0; mode < nmodes; ++mode) { float _weight = alpha1 * weight[(mode * frame_row + y) * weight_step + x] + prune; - + int swap_count = 0; if (!fitsPDF) { float var = variance[(mode * frame_row + y) * var_step + x]; @@ -404,6 +404,7 @@ __kernel void mog2_kernel(__global T_FRAME * frame, __global int* fgmask, __glob { if (_weight < weight[((i - 1) * frame_row + y) * weight_step + x]) break; + swap_count++; swap(weight, x, y, i - 1, frame_row, weight_step); swap(variance, x, y, i - 1, frame_row, var_step); #if defined (CN1) @@ -421,7 +422,7 @@ __kernel void mog2_kernel(__global T_FRAME * frame, __global int* fgmask, __glob nmodes--; } - weight[(mode * frame_row + y) * weight_step + x] = _weight; //update weight by the calculated value + weight[((mode - swap_count) * frame_row + y) * weight_step + x] = _weight; //update weight by the calculated value totalWeight += _weight; } From 17713f68315534179485c736b8452c5ead1caa60 Mon Sep 17 00:00:00 2001 From: Kang Liu Date: Wed, 19 Mar 2014 13:05:38 +0100 Subject: [PATCH 202/293] 1. fix an error in sample code 2. change an external link to maintain consistency with the previous tutorial --- .../mat_the_basic_image_container.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst b/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst index 171d2e683f..ce65c0a42a 100644 --- a/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst +++ b/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst @@ -45,7 +45,7 @@ All the above objects, in the end, point to the same single data matrix. Their h :linenos: Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle - Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries + Mat E = A(Range::all(), Range(1,3)); // using row and column boundaries Now you may ask if the matrix itself may belong to multiple *Mat* objects who takes responsibility for cleaning it up when it's no longer needed. The short answer is: the last object that used it. This is handled by using a reference counting mechanism. Whenever somebody copies a header of a *Mat* object, a counter is increased for the matrix. Whenever a header is cleaned this counter is decreased. When the counter reaches zero the matrix too is freed. Sometimes you will want to copy the matrix itself too, so OpenCV provides the :basicstructures:`clone() ` and :basicstructures:`copyTo() ` functions. @@ -86,7 +86,7 @@ Each of the building components has their own valid domains. This leads to the d Creating a *Mat* object explicitly ================================== -In the :ref:`Load_Save_Image` tutorial you have already learned how to write a matrix to an image file by using the :readWriteImageVideo:` imwrite() ` function. However, for debugging purposes it's much more convenient to see the actual values. You can do this using the << operator of *Mat*. Be aware that this only works for two dimensional matrices. +In the :ref:`Load_Save_Image` tutorial you have already learned how to write a matrix to an image file by using the :imwrite:`imwrite() <>` function. However, for debugging purposes it's much more convenient to see the actual values. You can do this using the << operator of *Mat*. Be aware that this only works for two dimensional matrices. Although *Mat* works really well as an image container, it is also a general matrix class. Therefore, it is possible to create and manipulate multidimensional matrices. You can create a Mat object in multiple ways: From 284b2fc1e735886c569685333d558f624ce58719 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 28 Feb 2014 18:18:20 +0400 Subject: [PATCH 203/293] Cut path to CUDA libraries to prevent generation of OpenCVModules.cmake with abs path. --- CMakeLists.txt | 11 --------- cmake/OpenCVDetectAndroidSDK.cmake | 6 ++--- cmake/OpenCVDetectCUDA.cmake | 39 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb49497412..747d207e36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -467,7 +467,6 @@ include(cmake/OpenCVFindLibsGUI.cmake) include(cmake/OpenCVFindLibsVideo.cmake) include(cmake/OpenCVFindLibsPerf.cmake) - # ---------------------------------------------------------------------------- # Detect other 3rd-party libraries/tools # ---------------------------------------------------------------------------- @@ -513,16 +512,6 @@ if(NOT HAVE_CUDA) set(ENABLE_DYNAMIC_CUDA OFF) endif() -if(HAVE_CUDA AND NOT ENABLE_DYNAMIC_CUDA) - set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) - if(HAVE_CUBLAS) - set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${CUDA_cublas_LIBRARY}) - endif() - if(HAVE_CUFFT) - set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${CUDA_cufft_LIBRARY}) - endif() -endif() - # ---------------------------------------------------------------------------- # Solution folders: # ---------------------------------------------------------------------------- diff --git a/cmake/OpenCVDetectAndroidSDK.cmake b/cmake/OpenCVDetectAndroidSDK.cmake index af7427194e..273758967c 100644 --- a/cmake/OpenCVDetectAndroidSDK.cmake +++ b/cmake/OpenCVDetectAndroidSDK.cmake @@ -326,12 +326,12 @@ macro(add_android_project target path) # copy all needed CUDA libs to project if EMBED_CUDA flag is present if(android_proj_EMBED_CUDA) - set(android_proj_culibs ${CUDA_npp_LIBRARY} ${CUDA_LIBRARIES}) + set(android_proj_culibs ${CUDA_npp_LIBRARY_ABS} ${CUDA_LIBRARIES_ABS}) if(HAVE_CUFFT) - list(INSERT android_proj_culibs 0 ${CUDA_cufft_LIBRARY}) + list(INSERT android_proj_culibs 0 ${CUDA_cufft_LIBRARY_ABS}) endif() if(HAVE_CUBLAS) - list(INSERT android_proj_culibs 0 ${CUDA_cublas_LIBRARY}) + list(INSERT android_proj_culibs 0 ${CUDA_cublas_LIBRARY_ABS}) endif() foreach(lib ${android_proj_culibs}) get_filename_component(f "${lib}" NAME) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index 56b142970e..24fbb03ce3 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -219,3 +219,42 @@ else() unset(CUDA_ARCH_BIN CACHE) unset(CUDA_ARCH_PTX CACHE) endif() + +if(HAVE_CUDA) + set(CUDA_LIBS_PATH "") + foreach(p ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) + get_filename_component(_tmp ${p} PATH) + list(APPEND CUDA_LIBS_PATH ${_tmp}) + endforeach() + + if(HAVE_CUBLAS) + foreach(p ${CUDA_cublas_LIBRARY}) + get_filename_component(_tmp ${p} PATH) + list(APPEND CUDA_LIBS_PATH ${_tmp}) + endforeach() + endif() + + if(HAVE_CUFFT) + foreach(p ${CUDA_cufft_LIBRARY}) + get_filename_component(_tmp ${p} PATH) + list(APPEND CUDA_LIBS_PATH ${_tmp}) + endforeach() + endif() + + list(REMOVE_DUPLICATES CUDA_LIBS_PATH) + link_directories(${CUDA_LIBS_PATH}) + + set(CUDA_LIBRARIES_ABS ${CUDA_LIBRARIES}) + ocv_convert_to_lib_name(CUDA_LIBRARIES ${CUDA_LIBRARIES}) + set(CUDA_npp_LIBRARY_ABS ${CUDA_npp_LIBRARY}) + ocv_convert_to_lib_name(CUDA_npp_LIBRARY ${CUDA_npp_LIBRARY}) + if(HAVE_CUBLAS) + set(CUDA_cublas_LIBRARY_ABS ${CUDA_cublas_LIBRARY}) + ocv_convert_to_lib_name(CUDA_cublas_LIBRARY ${CUDA_cublas_LIBRARY}) + endif() + + if(HAVE_CUFFT) + set(CUDA_cufft_LIBRARY_ABS ${CUDA_cufft_LIBRARY}) + ocv_convert_to_lib_name(CUDA_cufft_LIBRARY ${CUDA_cufft_LIBRARY}) + endif() +endif() \ No newline at end of file From 640e180efe645c32863af549b7a856d91ef0276f Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 20 Mar 2014 22:22:55 +0400 Subject: [PATCH 204/293] switching to CV_HAAR_SCALE_IMAGE mode, enabling test --- modules/ocl/perf/perf_haar.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/ocl/perf/perf_haar.cpp b/modules/ocl/perf/perf_haar.cpp index e70641d061..6b5100b6f2 100644 --- a/modules/ocl/perf/perf_haar.cpp +++ b/modules/ocl/perf/perf_haar.cpp @@ -92,12 +92,12 @@ PERF_TEST(HaarFixture, Haar) typedef std::tr1::tuple Cascade_Image_MinSize_t; typedef perf::TestBaseWithParam Cascade_Image_MinSize; -OCL_PERF_TEST_P(Cascade_Image_MinSize, DISABLED_CascadeClassifier, +OCL_PERF_TEST_P(Cascade_Image_MinSize, CascadeClassifier, testing::Combine(testing::Values( string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt.xml"), string("cv/cascadeandhog/cascades/haarcascade_frontalface_alt2.xml") ), - testing::Values(string("cv/shared/lena.png"), - string("cv/cascadeandhog/images/bttf301.png")/*, - string("cv/cascadeandhog/images/class57.png")*/ ), + testing::Values( string("cv/shared/lena.png"), + string("cv/cascadeandhog/images/bttf301.png"), + string("cv/cascadeandhog/images/class57.png") ), testing::Values(30, 64, 90))) { const string cascasePath = get<0>(GetParam()); @@ -121,7 +121,7 @@ OCL_PERF_TEST_P(Cascade_Image_MinSize, DISABLED_CascadeClassifier, faces.clear(); startTimer(); - cc.detectMultiScale(img, faces, 1.1, 3, 0, minSize); + cc.detectMultiScale(img, faces, 1.1, 3, CV_HAAR_SCALE_IMAGE, minSize); stopTimer(); } } @@ -137,7 +137,7 @@ OCL_PERF_TEST_P(Cascade_Image_MinSize, DISABLED_CascadeClassifier, ocl::finish(); startTimer(); - cc.detectMultiScale(uimg, faces, 1.1, 3, 0, minSize); + cc.detectMultiScale(uimg, faces, 1.1, 3, CV_HAAR_SCALE_IMAGE, minSize); stopTimer(); } } From c9f51d5eed07dbfe803c12a6b97f86bf662f2b21 Mon Sep 17 00:00:00 2001 From: Firat Kalaycilar Date: Fri, 21 Mar 2014 09:44:11 +0200 Subject: [PATCH 205/293] modified BackgroundSubtractorMOG2::getBackgroundImage so that it can now work with gray-level images. --- modules/video/src/bgfg_gaussmix2.cpp | 53 +++++++++++++--------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/modules/video/src/bgfg_gaussmix2.cpp b/modules/video/src/bgfg_gaussmix2.cpp index b14bc8e1e2..ddc8e64bdd 100644 --- a/modules/video/src/bgfg_gaussmix2.cpp +++ b/modules/video/src/bgfg_gaussmix2.cpp @@ -577,54 +577,49 @@ void BackgroundSubtractorMOG2::operator()(InputArray _image, OutputArray _fgmask void BackgroundSubtractorMOG2::getBackgroundImage(OutputArray backgroundImage) const { int nchannels = CV_MAT_CN(frameType); - CV_Assert( nchannels == 3 ); - Mat meanBackground(frameSize, CV_8UC3, Scalar::all(0)); - + CV_Assert(nchannels == 1 || nchannels == 3); + Mat meanBackground(frameSize, CV_MAKETYPE(CV_8U, nchannels), Scalar::all(0)); int firstGaussianIdx = 0; const GMM* gmm = (GMM*)bgmodel.data; - const Vec3f* mean = reinterpret_cast(gmm + frameSize.width*frameSize.height*nmixtures); + const float* mean = reinterpret_cast(gmm + frameSize.width*frameSize.height*nmixtures); for(int row=0; row(row, col); - Vec3f meanVal; + std::vector meanVal(nchannels, 0.f); float totalWeight = 0.f; for(int gaussianIdx = firstGaussianIdx; gaussianIdx < firstGaussianIdx + nmodes; gaussianIdx++) { GMM gaussian = gmm[gaussianIdx]; - meanVal += gaussian.weight * mean[gaussianIdx]; + size_t meanPosition = gaussianIdx*nchannels; + for(int chn = 0; chn < nchannels; chn++) + { + meanVal[chn] += gaussian.weight * mean[meanPosition + chn]; + } totalWeight += gaussian.weight; if(totalWeight > backgroundRatio) break; } - - meanVal *= (1.f / totalWeight); - meanBackground.at(row, col) = Vec3b(meanVal); + float invWeight = 1.f/totalWeight; + for(int chn = 0; chn < nchannels; chn++) + { + meanVal[chn] *= invWeight; + } + switch(nchannels) + { + case 1: + meanBackground.at(row, col) = (uchar)meanVal[0]; + break; + case 3: + meanBackground.at(row, col) = Vec3b(*reinterpret_cast(&meanVal[0])); + break; + } firstGaussianIdx += nmixtures; } } - - switch(CV_MAT_CN(frameType)) - { - case 1: - { - vector channels; - split(meanBackground, channels); - channels[0].copyTo(backgroundImage); - break; - } - - case 3: - { - meanBackground.copyTo(backgroundImage); - break; - } - - default: - CV_Error(CV_StsUnsupportedFormat, ""); - } + meanBackground.copyTo(backgroundImage); } } From b0ad84cfa2f4ecb884de542c9706177f22dade6d Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 21 Mar 2014 12:48:38 +0400 Subject: [PATCH 206/293] Libraries filter update after abs path cut. --- cmake/OpenCVGenAndroidMK.cmake | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index ee52fa6886..2622d2aaed 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -56,8 +56,11 @@ if(ANDROID) # remove CUDA runtime and NPP from regular deps # it can be added separately if needed. - ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "libcu") - ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "libnpp") + ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "cusparse") + ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "cufft") + ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "cublas") + ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "npp") + ocv_list_filterout(OPENCV_EXTRA_COMPONENTS_CONFIGMAKE "cudart") if(HAVE_CUDA) # CUDA runtime libraries and are required always From 846266fde4c82fe392ccf122ef64ea30b6da4f01 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 19 Feb 2014 16:20:40 +0400 Subject: [PATCH 207/293] Native camera fix for some deivices with Qualcomm SoC like Samsung Galaxy S4. --- .../armeabi-v7a/libnative_camera_r2.2.0.so | Bin 275932 -> 271836 bytes .../armeabi-v7a/libnative_camera_r2.3.3.so | Bin 275932 -> 271836 bytes .../armeabi-v7a/libnative_camera_r3.0.1.so | Bin 275932 -> 271836 bytes .../armeabi-v7a/libnative_camera_r4.0.0.so | Bin 263644 -> 259548 bytes .../armeabi-v7a/libnative_camera_r4.0.3.so | Bin 275932 -> 271836 bytes .../armeabi-v7a/libnative_camera_r4.1.1.so | Bin 275932 -> 271836 bytes .../armeabi-v7a/libnative_camera_r4.2.0.so | Bin 275932 -> 271836 bytes .../armeabi-v7a/libnative_camera_r4.3.0.so | Bin 275932 -> 271836 bytes .../armeabi-v7a/libnative_camera_r4.4.0.so | Bin 275932 -> 271836 bytes .../lib/armeabi/libnative_camera_r2.2.0.so | Bin 288212 -> 284116 bytes .../lib/armeabi/libnative_camera_r2.3.3.so | Bin 288212 -> 284116 bytes .../lib/armeabi/libnative_camera_r3.0.1.so | Bin 284124 -> 280028 bytes .../lib/armeabi/libnative_camera_r4.0.0.so | Bin 271832 -> 267736 bytes .../lib/armeabi/libnative_camera_r4.0.3.so | Bin 284120 -> 280024 bytes .../lib/armeabi/libnative_camera_r4.1.1.so | Bin 288216 -> 284120 bytes .../lib/armeabi/libnative_camera_r4.2.0.so | Bin 288216 -> 284120 bytes .../lib/armeabi/libnative_camera_r4.3.0.so | Bin 288220 -> 284124 bytes .../lib/armeabi/libnative_camera_r4.4.0.so | Bin 284124 -> 280028 bytes 3rdparty/lib/mips/libnative_camera_r4.0.3.so | Bin 546012 -> 545972 bytes 3rdparty/lib/mips/libnative_camera_r4.1.1.so | Bin 546104 -> 546008 bytes 3rdparty/lib/mips/libnative_camera_r4.2.0.so | Bin 546108 -> 546012 bytes 3rdparty/lib/mips/libnative_camera_r4.3.0.so | Bin 546104 -> 546008 bytes 3rdparty/lib/mips/libnative_camera_r4.4.0.so | Bin 550340 -> 550308 bytes 3rdparty/lib/x86/libnative_camera_r2.3.3.so | Bin 427444 -> 423348 bytes 3rdparty/lib/x86/libnative_camera_r3.0.1.so | Bin 427444 -> 423348 bytes 3rdparty/lib/x86/libnative_camera_r4.0.3.so | Bin 427444 -> 423348 bytes 3rdparty/lib/x86/libnative_camera_r4.1.1.so | Bin 431540 -> 427444 bytes 3rdparty/lib/x86/libnative_camera_r4.2.0.so | Bin 447940 -> 443844 bytes 3rdparty/lib/x86/libnative_camera_r4.3.0.so | Bin 447940 -> 443844 bytes 3rdparty/lib/x86/libnative_camera_r4.4.0.so | Bin 456132 -> 456132 bytes .../camera_wrapper/CMakeLists.txt | 2 +- .../camera_wrapper/camera_wrapper.cpp | 171 ++++++++++-------- .../src/java/android+NativeCameraView.java | 1 - 33 files changed, 101 insertions(+), 73 deletions(-) diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r2.2.0.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r2.2.0.so index 5b618a87459123ff5f70d6f0647ca5d1785140cb..9b8352e03fe48531bab41f5b28dccdc403c7898d 100755 GIT binary patch delta 80209 zcmZ_14}6XF|NnnoXB)$4V;F{EY8WQes;SW&^RFn?NK8>v)RbyOXNuk`)kGZ?#XI#@ zsYVnL1?00`&=XrKsozM4o-EOZvp7-bT`Fg#sKdnu>YtecCvaLi^n)w2qFndvj-Oe>FV^I@VJJ$;t zi+Z?v+G&f%x!QRSJYrkrntGEghECG_tT28YKBRLfUB3)JGsAY~RJz&(e+Cbru5c;5 z^I_Y%RN0PTrA@W15wO&+A7Oc{gP*yal_C|ok?`4d5msxha25Q_s0eG8@*sHo)wXp; zc{CiHWQHfghsQe`%b9_P;EmVVRthoc@YS&VHzGV#AR(6onSpP^5-P(uPBx)V(qSLC zf2M7%#+DAIhQ*(RPZ7`8_;&cV{0QoIDD5lBR0^!u;*qR2~?2bA{8D9OIRPqN8mo>Cux3ceGHwX!C_(iC_IXM zb`q!kxBeOTSik<$VKrkQ@4VJ_D#$9j1U>-EiW0sOJ_ySgkySn>jAz1C#AVPq+Z+x~ zvPM6HyJQ9Hd!nu1{^5wWnl-SjYwHrCt-fJ=3w-#q(4c)3o=yHH8kY`VfP<5C_z|4e zJhTIU0}mXiJFtfezu=gFLpFgl(6pgt-Snt!6~MB@ec{=}gG+ojJm;a%pj`lWcH0(5 z7^nWyFkTJ!Bfdl9-@(DjBXfA%a0oYOWLbMSgh+!j13lprupFj@Z-N_4V3V+?3Eu-R zVH?XPk}>o&eCi3?86y&33a{WeAY(vyJ$y&D&CIg@%M$x>1Sgq+8h9QF>DUr)6JuGM zVcC?z1L5E#`NP9_6nuCvGpM`SLt*jNbQ4YIy)W8<_k7+wMQ&<<~d z7aL;>KZZy28qV_UYRPSRfAFrE$v`*&vG-(d;c z4QB0HcJp9!fkAss7|#ylEn)0x@z46{EkgY}d%^uAtQccke)43KyaK;;YiKW63onDE z9f@xjLnm3nUGT`^fpfORe}wamL3shLy3MvS$(1pa)sp4Ef{Q6R0E%NuOUt^&;KlG2 z9D0NC4T(&V~`bf;3P9V#}npwJB$y6vDM15irA0Y zS)2w_;UrEjLC%DOlWem5Fn%qJJ-fpk&RIrVkOq3mSw|<~31Pf2j6VxS5O32oRHmkpb?j) z923U3!wqhVu<|(nIIHMB90PL#t3=kwqj3M*BAkM7DQKWy@+7#;w9QHH8rcHL?I3!}7m?7v07Emn^*`{EFkryCR%Jsqi^?p0NoUCUIyb z-c|G4!o{6Jht^K;nrU|3?Q<5q5ti!S5t+%kA))WmuOK01x_zZ?%FVKI)%7NLZ< zVf+42d^?RJ9XNU#^}EB-M*S>!xW{N zI(aN>s?os9VHMWGbBy>-*j!^i_yr?g1Dk`cb+To>MLc+JxDswVJ#bkrV{VKG$2=Sb zR$vg$geN>0y6j#CXS2%W{v?i_=>zyPBfb}2!V(4JW$*%nYv3IQH|@-wk#QC5Nyp)1 z)CU_F3HRq-EmvpgL3o7GzydgDSA=s37H2iG-h`8k8QcnI6PF7TiSLES8uiQIXyUTh z5cgOYa4g0l$8o99>|)C*G%BRPiNxhvQsVvLRYv~Ja36#3f%DyVcW#HE?6@?SiM%J@EEfp}n9SE--eZdMRvE;=uz^2iVNdfd4Y; zkAdU3wL=2u|NF77P>0On3OI)j0;{lzwFZ8Jc(8+Q@B}zZXZQ#B;FEUUvA!HOJ8am6 z-PRZ*$?$IO1?S8ChqD{txQRn?a4(o93Gf;1U@`nzVJQ9%9J@O-2)Dzja9|KOvi8A6 zbQn#2*=vr%Im9C!Ze*Q>{fvP~`gaCJW6mlCMnVU8gu$1?6Ac~;S2_&@>W_mnjrfCb zq`^)87LIdG9<~a`*o%*xkmskT_MmYQ-T*Nbe=}!jzmua&9F~>=cqlxuB z2}}PMN;n4lSY^TZ-|zxskTvba=~oXla(~|$o<;m3+Ur0E>99G&cfdLHC+7;;1T#E1 z@^Qqf<2m>Ma~#~}Z^Gs_+zgwKR=CK@X-k|MuhK=?; zmK#TKYKk!eZp_K#TWpzuNpSb4Y-^(O)9^z0j4Kd-9X`C-w&rU5Q+Pc~n529PUT!R) zs}GyFj=BGBiDN;Xqp8&uUS;qQc%{L2!K)3P3l|x@0xmH4Q+Teyd*N9@mh<0lIHm_3 zu+`VH<{R7sp7#t-!|lKlcY(XJ#Orhkv*0Ip+tw;wBlF>%-`Lhx&Hol|uoKpJ++`ft zzOt>Q#GU*9TX2lt#*r%`(7~(lK2{-nma_?t!uI>2tJoU&z_WacrSY`O+2%!oPr+o9 z-3A~1%2jtG20c$2{&z*`O83GX$y6du8DEf*kiT=)}S zMZB5AjjV=O=pgp!HtC4N+{S%ja|wsT{oe>&V#yPZyWri%^ML}m*Wa$XBk9Xw9fqYnPZJ!{;CUQ-NML@PgpNFk*k|nL*TDTb&;$?FFTleMJ_(ODxc^m_ zHOXKvJk8*f@a)w*e+f2tO$MXbNLU2FVDNExg~1v97!w9Bgf|*|4Bl?=RabLa|DJ8# ztP6ZAY`@R>Ke$RBz+oeF(&@-a<;07#V z@Ot29c(cJx2Jk4C15t2{T>?-0$mM*l=p3j9c-ZD2h8&AXcxyw5cfySgu7z(i8cevB zBOM1OInYRhm%<~B_z?K85x)bDZW~(TvpqO=v=2GF@WM+Yti5)i!Ow&*kFb7JJ_!#r zI*>&_Xmrp5zQTz2fDah)LGbc+p-t+!9mfvCF&m!xfy?5i*IA-h;pXdIb)Vbqgu5FZ z9D?^59sCXF(_R|&Wy~}g$TcC%mUZ}IIE@9?G2^j@;^_BRXdB%FAK>IZQD@)Eh5Bxy?_^6l9|No6+8n2>AP`2UE@MyS& za_d2C!+H^QH>bD2!Aa_jhZ`JmIZGt2r^79kkHe{LLJhdGxq{gjinoQ$BU?Y%JaCPM%>&ni@MhhF&iVgo9EWkZ z=unnmCA`Mi#vj86jrjlIH;i~0e87lXgE?>+@uu)TBYp||OduXO|7YOXVVEP<4F>NgJ=sp*;s|&!wZdg)JP7sMm!OI$(W%lg_}jx&Cm#VyDbQ4Q(D#V$v6*_-gli*24gU`Tb{q^t+;=wgq2G9B06?~=BWBG9`Bq2D* z&AC87Xmr>W-fVO*2sZO4!h4Lk7q;o(QC-3};iE?TU%-tkf(yj?e?N}6HKA4VS6GFn zw{S8co}>+Cz`KoAJ{%rMJh%xSfQK9T%iw)R{(JD$V7@&6-;3ix94XqtMLBFkDr{43 z2QMSuLit8`JsezP_riza;5L0SEWQ%{i+D$^|1E6m3_0ijpK-KW>*6RK7}b%Z*$d!c zg&y!(uJM9Pcr`rS$e#$`2B&F#5Bv-qoS~P);_KlR#CLK0adxA>aGaup7;T`z81DVJ z2+dW#1pdq5f$&Z1Lh(uPdV}Y|=0$5UJd%spV0%BoYaG_|e>ILc?gN4eZExl51P3od zv*6LEL$_8FVDrAg3peJ*BAEXvY~ITK1e-S=abvk;ll59CH9 z*uh%Zyx02V)=F&bzzo|B2eneZBe zAB1-ryb7LYw6_&LY{VG;cUTF``;LY%8BR)~`jrgvNLd74;2#C5j6orC)ssk;PsnB2cG_L^If4$k^}EE;?KYhjQC3U zFC+egurY&`u&4Q_p$aCY)fz$H2Kp1JmIVKL&2ar2cboA7cjIhWApxrMABv zjyCH1JUG&5psgm@llWZEXrLoJiw1)6YvFjKfr)UeQGXu1$yk6l;cTP+SMU-e?kU4z z9%}2|`OiabGHf1dvtaX3I}tVytFN3GS z{SBTEk2H7%JYg5-{}^px4UVZK1bGWQ!{FWUJcECM7aIH*yv*Rp`#6vp+zeh12WPk& zyxn6s2IAOf@NMu>gCBoFWBB4VlUhnuBQw1AGkT}Qr-Z!f`iA29k3@UDfIIA z9vrg{1-|tt4KnFk#Fbol8e!P!+bga z8y&=vkYiM61?L$Zbbu!s9i+hdMt(1NtkJ=haFNk|f4I=dADqwl%P~3_Nx~?jgB*B? zQDFi+%;?~5c!SZwG5iIM*XoMAM$ z8cv60o69Cy3zr%BAH#i&_C25Di0%;D#yeoYQQfZSkp)_I;aPyhbmZ2;6$T?7I2QyK_Z-OfatbTA5zuIr$YH3pt<u7`BWK9)+ua4sGMPaIulU0QM6P?v{(;5~KZ>;jNnQk^OoFjx$sU?zgMpN~44K z;QdAiAHwBE2cN=zql0a5nUVjEu+jd1;Uj_eJ8|~%5`EYO(jDz!x{5xUK6r+QCam+V5m<|^h6&{fWj1C@y zCmJ2hhl`B-A~@G*e+j(Yh`;s#=l`)rgKv_MV>GxL4o*!m*1;Y2bF0Q8$u-~iaAn^} zi|3czm^8Bv!AlH20jKu~#s7pyTo&S*2iX5IjSBUrvjj$krtm7GLR+}Zh^N5jXT6zl zHu*A!WQNAUGfG|75}n~fxR_VFxDO%T)Oyu}V~)>d&F9!FOY|vhewh3{Jh0qlT}=gv zH+_(cP&h+*IQ$tLyce7c?}TLrB)=HmZSY}u58OoKp7@7&EM_DOfbCPEXScV*<_8Fm z!M%+5zhU$9fzRPxha=>BiY#Fx>kwRQa5cQ2cTDt9?y=$?WvaWRd}+&U&B)k_QTVHEYJThp20Q>I^Y}NJq8!T z`wV^$K49>Va9Z!;xfED1ZkV6<&bNPd3-W zoBs$sIsFMfU~uFt?vxC^6ps5dl%ETCcbNTO2F(H-gN=ms@FatOh36O?H=BDsgZsf7 z4ZaiZ#Y-%SI;IxG!wvo%o-8cqf0=;_9P^BX#7B7qWAG4ov%wF+8NY-cTo%DO2EPp# z7`zQmF!*OU4d(r~z$mZ5F$zbJWzNzKz6zdh@EG_>K3WY9x;gMO2Csq_8vGsng28{o z>zi@@4-Sf$!he2P+z~!XgfKi2f)kPL|C7*%H(~(+u#XpBdl+gXTZTp7NQU~ z^Iw4{dHS#l_2jS-huOeBcu$3yZ%y zmr?kaQQ;dBuE+{iI02jS`tw)=*`eQPx)NSt@Km^FNGQGtjv5-`jqsAcLZ1ih_29^7 ziRGD&tg_$XVk4p96I>)3@y_rneH1I3Bpsee{zc9vYGe(CON{t9c)k&z2AiAA^E?i7 z6MO&%Cs}PDhw+v${xXce4dd^__~(BzzyI@3hjjv$-*9-A&gIDXCw!JSon;NlZf5_B zRZM)F#v8&1`OPSqBtHQ@3eR)}xI-BCfKL#A$cc0Qzx*E#eiQLv&{b7nv;Q|K&JNlQ2dTR)_Hh*!(*FcK8%u&!1uk8vGyp z7d%n9G>lKc=I;Ug0goOPVI|X_^ZZ}Fh!=FoDr^i-AR&0@Y!$|x;n~C&=nVG`<4oB6 z*{)%5;Oq8*8NLagc5{Sv8SkGvt7Kfr;qX21Ten15JW+M{fiQjqUPJs&jXxg7Ps1CD zkJI?#FkV{6?EkXFD{-`9kOfa3>){Tt-0Mkvdl>J7dl3&FSdPG>4gM3JYOw1`#*D#n zursLX-x<~IaZDH;`gVIC_-QyrXJ7~%{LKcLp&Q{fd>ek0j`Cc1-#rmlrsm%Zn?K3$ z2;6*1gvIH?S)jS_;QKiL@6`sL#c?z@aHNvm>q~m2f`D^>ixeg(I^~~#zt7T>k{O{U%@SupMnp<1C(Ed+w=PX!A-Ck?gg_v z&JunLe*<%E>hMw6p2V2Y1wIF#%;o&wQ5_AR=Gbn`aYy*H!B@il488&GZg3tv+~6l+ zbBUM1<}W0C2zT)qKbi2AG+^+rVO#@GGve{=esi$jT6#S0C_rRB$ zgJ?RAsJkPq5PNE1qAX{(moFzZWcW3d?a6eSSa8~g@9DtMv_8Sf8N!(g zsUws8?4L}0&TX9_muqWhYD+vqW0&9`p!s_OeR!_&m0(?Fb79)j;r zDko@6ZtrE1uZCPsod>mzk5!jYkMG!9uPRHfU)$nMaA&$gJ_}r+&Vb9Z9wnGa;fpxV z^g8yxwSxRMVH0eArqXhwtI%~?zZ~9={a?z}%pBj7wZ0S^|6KGqdLHdgY!x=oT;yJi z-xUG!C|hxSOqq#oQx@qA@Pk{{E$|2``cb|q=uDixtkJMchk`zG_~xJWfchmC&E^eG z8;G}{-f-ewu{~uNdM3A8Q1PHv{2!VMcOqG)$*MnNN73L8Vy|F7Ngh{+mJN^5oTu<{ ziE2HK9-=HJ#*JFtbPv87e1qh>;5ERNB>zSDN}O_Vl<6jlkC;q+(a!n=do}il=ua>= z%_E7gXP((QAo~g_n|f82vZC zVfem*pNF5)+zgb*P!=D$JJWU8yd`2?3)NyrQa+-5h;Jlq+$wp}|C3rl&O7M@gOhx6 zz7}|s_|51zjkOFWppD3pX_&SlcB1BuK<^>`9$nYfwq1Gc9cqXUWD`8(6~ zXgS;v{~zevl$DfXicH*$S$iqZ5dR$Bf&Dq<2Y82*!2-d25wC7?9(Y41>O6dU6X;LZ zKLH*cSV>f@Z;aM0Mk6J}zoo>nM1SJv0^Q=#qBH#mKd+fN({%iI1x)Af!!VDit>2)0 z+P_=#4!{Fs`;W&tOe^$IweVHzstvk+hq?E4rVc@;YA+h?K_|=6FUi}0y+}Kflo6EM z;RD3Dh_mLv&Cvxi8oxFY7sLN0=_*Qa>Ve%6m`Lnn>{m3_jCwA$Yl+`agR%JT(e~2B zN1YhT_mtJd-=wUia1OT0M8D@x2}Pz7lD1(FK*v+Eu-D;VO@lm!uu3(LA6K!yBK|47 zfx+9{mC>4EQ|ObSlZj?S=KURuxKRY{(vIW&u|)L5BwVX4Z59jr`BDF-cDOr68jbZ5p*5g6s=F$ zf^Q?`b$o@i=i!BG=e2#lOjkGkLSmFM-(k0Yg@e->xE?dM65m7`ilzjo?HFmw7m>FZ zevYzC>ldkV+hQe<_Zk;Rn4#$;2`^Id44qHVi|T10t`5}eMeeT6y}lJ|`IvalD>=6{LZge_A6 zI0S(F=b%E-E18+#wlAU*>Jz>VJux zCe-^F&L@W#;+*@pEAT&0`3v5P%9N_@AB~I&IOZewMlPCjuC-?%Ia%apXpSVbV;*EW zi8jSQk+@99(36y3@JFz{f5u)*{R!yRTE8B)OvT!U8~??s`*8e*ZXkIl_7{|7 z_y`=FmcRx0E$`tA5!Ws%I+wDj;9Tya%5w>JD*JLzyBB{ikLpT>+q>s*#CUH^-^ge$ zA34u$5Ilfql5*{$^z-p{qeTnPG_x<2#P1fxT!^-BU)23Vn?=jdM90a4D04dOq~1r> zOX5B<1K5L{*f#mwp(HH!AA_I4{#52NF2-1of8f1{x-IQrV9U}=hdz;d}0ov+)V3U zw2e4)dR?_MvEXzeXrNQo|DF2e!*eE=HC|?e6&~3+E8wd8n}^k#!l&0(1NFWsD=1DL zWKUO*NH&Yu$?ix?&04(+&(hA~uqUbgf@~b`%81C=&Ob^WW6IH#pz8~agEZNuFTcW9 zH?30pRrM@W-CLItFj{%vwe6Oj{|D&~=J>a0_3MoEyMoJZKjNL{vfGTFN$_hDSDJ9fd1o)in4-YuzHN!d@ePPgH|bWA!aW zkEt!OrfTn!MTqNeL?qhByD-9T*0~L{Hk?p9TrG7rB|Jd>Fl{5|kp?{SNrE|LYIZmD zdn3@*=;#K`dB*Tb>UjLQ8ar;({o1hi7pCLM z;;zw-#9JWtqJ^iLwH3M$R_6=lN1S!Tt{mfe*+{uT>M-3#xe>S z@z2F>fc}Iwrqsuth~AINl!X3@_N6?D{WAJEn>fo zs2C5M=AHwNIbsa^cO5s zGQMj9rgb@W-&6kw+RhKMQ+WPobalPaid39R$<ll-w-e1?TP=0%5*&?$FV|xE+cjm zb#5SUCH8i$FBuPGmr=gM?;`#(_8Zz(L_j^(1f79klYWxg5d5C<4)z2B%PAF_kb-{@ zbuLq0MNVt%bo`HLTl?W!SfynUZHJYP=6rG4ayOt(H{stM&ew`%|>EBxKjM&-l_SG zh>yb8iR;#Pv2!R*;NTRc_3NweoYp(7{uStEkHY(CjM`VAnIylbd>aj9!#5g@OD8^! ziyg~M*vc#6TD9BYpGLjWa3OjTEYm7VXY2--@aHLlbG#j5>}I_ZG#-sUOwI=SU7<5B z_4d=oEu_!ad8u%!#T65JTwU3O(*vhMuC_gwf}IJyM9R0=)#w^jrfJ%n*f&z;y7EyX(AJ>Ma zqT}$%bT9r+2YN6^} z)%mImREty>t1eMps=8ctrKpU*Rq9x+TCBQGb%W|A)vc=ARClQEQY}&4tGZvcRMn?? z1ogPppO)pgC-|D=Nr>e!^ZRdt){4%J<%C8~Q>_p6qw z`c#jomZ|zxt5q#eysk>sXw?|iSk-vd1l2^JruEsw-7jsjgGqpt?zQtLiS*64kw` z`$c8{_o?HEYME-eYNhHK)oN9Fb4Aufq-wNktZKY!f@-2_l4>gI?Eh)%=%bphnyH$l znyorYHAi)J-&{)mf^As&iZD{y$$G3sj3#m#QvTU8%ZCb+u}->IT(Ks#{fe zsg|hjRo&l0_y1CL_*9RmmZ_dm^{ZB^$|sz%31U=ZRpV6?RFhRxR8v*cJZfa9W~yeX zW~<77`jC!BsphE4fAA20uIfb9DXRIZ(^U&p3spUH)tIj;|D!}2EK*&pD*www{7Y4r ztFBaCrMg;mo$3bFO{!Z}cLda9?NUdHYN@JE^@wVj>KRqPYPG7xwXw`#jB2cEylR4~ z{7)3gOLmm~FGU@FRMS;6R5MlOzr9GsVX7lk$ExP4PE?hDtReYRRP$A5sTPXL{y$e8 z^Ht>^WJtxus`5Mb!b??Gs;*L9ty-+QPIZIoCe^K~+f;X;&i=nk9VM!JRrjlws`^xq zs8*`_RposinbAnqSk(m8WYrYa)KUclYPxEMYNl$IYPRYyRry74>3Ec?{F1-$ zSk*k$>8b^)vs&r?KUW>|RTrohsV-JsqPkRdx#~*Q)vCp+>r^+WZc^QMAanKWYtvFG}S(;>8cs3nW|Z; z*{Z`-N2%thjt!{C%2me{)qK_Iss*Zrs&iH6t1eJ2QeCRLQgyZJI@JxTn;d2T+p3Oj zsykG7sg|hjRrRTssaC4`RjXAkZtk7EK{Z-6UNuov_Wxvc$WIANg*4SZs_Cj3s+p=; zsv}f$RC86QsOGCqS1nMTg*yBHTy@M>U7%W|x>$9o>T=bUs;g92s}`%SQ{AAtNp-90 zwl=!|?@-4s)e_ZGRiEk+)iTv`)k@Vfs(#gKRe4p{*%YcVs|HrE%K{Zh|Sv5s9 zRW(huk7|Z$rfQaIw(2m|5vrq9b3AH{Rn1kMsG6rbMKxb_mTIBuT-Eui3sj3#7ppE& zU8?F?uEt8$RjR91i&fXDZcyE%x>a?X>JHUiswJv>Rrjlw2GnEu)Nw?$OtoCKQuU0g zU$t6QUdwW}t!j*FtZKY!f@-2_lB06|S4XO9nyUOhK) z>NeFKs(V%UtCp(zRF9~Zsg|o&s-98xt5&O8?R5W_U*3}?j!}(Ol^@g-e}ZbFYO-pI zYN~3QY9G~f)eO~4)hyNQcDnx$Q^yF^QK~trV^!rR24#jOs^+OqS1nMTrCO*uS9QMX z0@Wgq8jDqzs4i7qsk%yawQ8~II@JxTn^d=|?ousL-K)A^)l;g5PxXjunQEo#8CAb( zWc$FHh*phJja7|TO;Al#O;Sw`D9``Y(ML61HA6L1HA^*Hb(rc1)lsTBsuNZ7RHvxs zt4?>6{jWeBvs4RJ=c>+EU7%W|xT=bUs;g92s}`%SQ{5mc`~N0&Y*pQ+x=Xc0 zb+77v)lyZTYPo8q>KRqPYPG7x`;Rh+qfux7k5NagYP@QKYNBeAYO-poYMN>v)eO~4 z)hyL))nTfml63#iQO8)-iK=<3Q&jU+r>o9VEmWPWxXFO{7RjXAaJLoo2jZuwNjaN-nO;Sx( zO;t@(?W3BmnxX0~YhovOqc5^kyy+KV<%l(3tW{#|7t1eJN-S1(ELMS7OT^kCR+(4{ zadv_|#oH^+ZrMP74M~RAEN}lfyS-Rwg(X(I8L2Gr|~v%7ez)hV9_N-$D2Mpb@)Nc{1t@^eDMiK@w}^0PtW zPgU)sDnBnI{tVSj)vSOvwT7ufeild)M|ls#**(3nO<99!P3@#si#4!RRep{~5?6XB zG_~7%=ab_tCcD)U4V0;tt5$mVG__lHkoWeaUZiTYYP@QK_cUn@k`+_Db~83omSVQ* zFx4E@v8uVMQ&jU+r>hpI&QhJPTBN#Ib&2ZIfL>}XSI0`#RjR91H>hq>-RfN^^SxiO zRJBa?jH+L?T2($&b=Hn*v}%lMtZKY!f@-2_l4^25n_4OANL5Yqo^EEhw6nZ+yxqRR zDD~!eTgBU#beW=jR*H8*bGCnmVy1Umb9+#Oe8mFqf#&ulBNrs3#LWV3KayuJEHC8o2HQBqfg+0E3{E)4rjrI0# z$r8*`Ec8xjXWQ-i>5&fIZxbev(?-Y2KXHcC20CozR-v3pB0BJEt}4V5QJDb4c`r^iqL=cLw`d>Uwh-k#KcDF3ys{vg&inSF zSMo}VsJ!UOoHxINW3uQ(F1$o}t0zX3mnmXJ@8V^fcrkeWHbHbUrytR~Eh|Zs*MCw) z7qd*FFYuC)D6j9Ni+VZzh%Vvv6j9#j$P(oR#JPOj=z5C|`Q%7+y6SxK^X^)#@Y`}N z>=?WWAc1!{9}1(hRExyFni&*E3soaoQ?B<}GEv?(i4k2x!=h_xN0j%G5=7UrV4~|; zFwqa$(L_BTu|#5QV2MO|H!(x>W0pwt6P8GH6EEC|ZsuXU=%<`dMYr&lkLXqo5~80m zLu}IKpEEu6JNUdr*33a>0B*tS4aumdMHHh;9F17jqP*XkCVCMwCK}6(i8f`% zMB|w;(dNvUXnST%v?DVnn#`9vMLRQtq8BrRqAARvXg6j=G?hEb9$X8z=+591M-K*{ zXiq+#6z$J86U}B5iQd2_5#_C%LQ&qToGUtxSARsuvk65f@Ij8~?HrdxALnIE(YaiQ ziSjzga?vNa(CERnaEpKOI)XUn^Tj;TC;4W9=u=#fi9XF|<)XYQvPtwAo}Y^H4%#-+ z=lCv=Xc2ph=tAC56@8w)MsyK-jVLd3mx?kNeWDD)BOWmrY-OShu5!`s>_(!0@nVf= zC2uQ={>iIKqVf`7PqdtEE9z&Pi|%8aiymbViXLMSiXLJRivGYL6y<%_BvCg%d6F!~ zAB+LfGRA;t31dL?cgBDyul1yh{>T^*-OCsd-OU&f{V!uc^e4uEC~rcK5Iw>e5apGv z9MMw7fGF>-<%$mAYo-&$;FX>{QC`5DBFa}(@N8uqM^?fGST2!5Gj*b@zVtYMEb>|utTW!M>p-N&#~4LjMfJ%QDgXawR7 zJI1gh4ZHemX!V{k>~h0CV%VjIz1OgJc?{TQ*qaP{onfyw?3ISS)UX#D_5#D6YuK~Y z_Q+tFZUm+n_C&)TYuKX!`@`r>kNCfVXrjorG~xOuooEiT*ICvwnz8Z=|*6R zVNW#dv4%a$u!k9TmSJZYb|1q|HEd6^0f~kkZ`d)09ckFrRmLVT>~h0CV%VjIy*FTc zoTJq)Be2b|HyQRi!(MIJD-C<8VJ|l91%^G>uxC|yLQ62+2uv~TiH1Gautyp8FvHF= z>`+-hN6TZ&$TP8vNn^Bvw|-@6r@gBCXe$ z)8~BmT&VqR*559xQFhGW==B9zGqP%{T33wN*dV*%;D$qlWtKOWMQ3)Z9KWf4c0rb_ zvR|#W#myI7W~cD|A9Nb};U6x`|CG!?^srf3Nw>ILSGK6BH@NEN`7bZKPP_~FT@Y{!&@=Pu5Q$@Jjorq zxtiKby=w;BZCoEzdz%inlSkCMAc55cmekb_za4B}X+P#|FvRW`lXucp z%#1wDZygtSZx~{yrVTrpl2^2`?vw3?)Jy8%o|)CKcyn1k z3-;2-wN(R7dfyvjUwU=o$tYiAjn`JiJ9UD24}WZJnSR=3t!D1_(^CV!BG#4SrVOP# zT;Odt)K0Vi_Fg~K?vNZ&*>p?n`gpoL_FMXzj5Qsw%6_xb61Lb$v72Kz%ffm;9BNN= zy;b8)9A*z`xU441oyKrkR_)CnW+ydwRbEl+^4Y5rDi@x$U+I)vdz^*z{yxlZexfKfAs_f}^6>lzgJ-xZCa!$%U_g+`Q(*8{O zma>Dgma>#`FQw~)F6-`x_$PVT{`FDrChnGQD|y|3`aj;}PqwaLVJl7~t+D(3b^Q9u z+B83l>)-56yUtE<$8GFPwDiPH{^*NptA0N5dfv5bTC;6#@W;d?u%${*Se;(aeLb)D zKfEQ5H+{3U<=N>j>+FNxXRotcdpb{YC4X}&{jO!H6VP(>!UL`(E0I<3+XMWMP;?hM1Fohgr8hXa zp4zH&Wea^HSlSUotQTZE)mFV&Hs5z0p6i_DFZ5~EDH4)v6EBY44$Je7!Y4$#`t$A}z+M2FIPvl(btevKh zl{NESqE)YAShOzlK04BF!Fq`)i*ok3%FQus=k;vor}$w78AOj#rc-k6cUc8!Hu^GJ z%+Y5ZWdr3U_vRa-yx7T#dT_pJO~j#cTz8|5{2%Wm1BZfA`t>*VaHLC=M%{oaRf zu-is`EtR~U8|>!xChz+<*lj!=H?*u=_h~-gWlI^_g=2sB6P5kt=pT8yPjuwz#QKq^ zqw7VSj&Wu5cI8H%9v1b*#I1QFHZmM5qx|g#w;K|XBqQfS)pftSd>5)V{C@g)ChKx% zZ90FytWD$ZB;kYF+T)V`J}MRNK^y*`Doua4 zD|2g#PPi-|D{9Am9P-)Sf8*zRHhkxO_C`A)(ysiV*8BF2_QkHL)!zT!XlJ{&Rd^Fe z**(%ytL?P81FZdt8>2a_MpRzyk4=p}kmR=Cs;wGaZNHK?A!R~BR!VL}aTk`MYUCXYFUl-N4jO{zNu|JI3lU_kZaw${Z>(6rH!rV|3`wwCBykb zcgdOyRl6!$`XqPHh1%mOlQDKR4NKUu_B}&((6pP@KP6>^IrGaye6U z)~@{aeEu=6M!YL-wlAsw$0@G2sw%wS-E6nI^x(OOHp>P?90*)fRbH5oxOzb9mIkc` zx0*A+wSUEclr7P2Iq#fgM`^gGW98;e-qyF+ee8YS>u<4t@LYGQgS%rw3NxP0iQ=0r zeE(^E6Zga+QSOOsg_&8sx$e4S*oCU%@<}~rWm%Q;3R{=DdSsR~Dt+h!yGLf_e+tX; z?6+KRU#Kc8=U*M%o#u<|(WT_-pQ4DpRCv?Svb@7N7pjhxcl71;m|4=K^v)hrOYS1} zbm8?9^AKEGe%;XM4lcJZ=K`D8KI$1{?_;P=5M6)*Y;g1XNuE(=wPDL!8=o2iscp@zqwPpL^+er^_|4#96M7Dvr&J_O%}z zmDJujudpMX+)`mZGP1m7qew=@8NQIc;BNl&-VyH~qwTIuZTLra@PiZ|<(K=sJ;vDW z5)&r7te4>0s_IYWz}SCkuQf?&|DNiqtXJ#3@TV)uJAVv&|Fajo#bfMI_8M>0t#+r@ zdux)|bw@A)Yw!Gu z|4{Lj_q|)~)~@%@d%s4zuQ)HQM6Z85uhF%S=UzLx)d0K1uDozAnrr9oC#tS(?2hD$ z>yUqbVMJvkZ`@eBt>?|^H1`z@`||2+{{B?mVraTM#~%?JQCYtxWnw#@{hnQU?pzml zM|MWpg}An?y!d=$UoUqO`Fp4xQMt7`#U0t9w(28hG2wiqPa+?(yZ*w(d4%lvk5BRO zuN9f;s`YzL+x4!U9O+KqVqH9EVmt46$-e+&KQ`*EhD@RTsQSN2_gu)nBMLU+FwyLpr&u#YL?k&!B%dVMAa&@C_EVKMsGR_*xMuDtA=T^Z>eG|s-me%|}eIJG(!H-4R6*uPhsIV@Wpi(&gN> ziS_q%%5yn0lEjQO^}aLS?%v>;^P}BSMHi};cn^=ad)Z^X&2sI&*{}8SEawNSt1lnf2a)Mn%_GS`iWXSM5n5^R!=102SWfbM4Na z+s;-xXT~4=fkS3oP2ddqo!_c_?n2{NBI}kR`|Q%=cJD?nJUt-hg|!D7IyVomo|OhS z`VVqejd`ij%e7V4oZoz0eDC}Jcig^e$?-IHsQ=vMk_EedOYtFpCN~UGiw^lSE}NBA z|M}LQw;#!>R}^tNp?;A&Auqc3jsD2QPX;u1M{Muv{(DbCUdOlF%y{0NlG|V8x>Dp_e8Q*PY9P%%3 z&Urhj+cb`D%MSUMbmkZ1UYMB`waoQGy=C3yx;7pjJo z)pw8Bc*vjMydJk;`JLr#TvN63ck2@GFBhr?l%>1-vw5;SWrgnfYYzEGHIE=|gr*h! z-uQG%gKYQKK`HLRr2SCai<9<9tW@%g$WL|hQ#8MT{L}`$+@Ttu)+SIx>NcfDF*W%0 zPkOMlMlLndsNu0YhAOSB&8EWQ+N-EK+Feui_wSFpM|{xu^oWKd*rE$Au*%}4k!{H{ zvRb_&%acJfeWF{vlu7mx>~xwO;O54!ceZ=K&io9g)ycJzv;X%I^zeFZLwXQ85*`o633`nzR+PiF>i`27#A7UkGnpKF{hSeMpDy}}|zKbqU< zgF}8#OnfBW$2gtTRDE9&yE^Kn2CpX3)Qi8Tu@lx*E#X44rt0P2lSBO; zPVzNXYb#C=I#CyTzT)^$zY}@0;uw)*b&)43j%Z{_#VnnO8d0%NBNHq364_f9$*TCTMsBVsAyQHo zxvb(_jSQ*SO=Ndnq-(`D8p){GNn~dr;$g)kR(z$Qw2ChYeOXs2wqlz`I#zs6jS}L;fRcUG9xTt>p7rk7PwHTIg=&x8M2r zG(U+@^6}}^i|mpi?lZ^N4ro^5`SPSICe0o7((PO`$8N2u`ncjCTEHdgX!q@Ou(rZV ziQY2SEtTz(>)gK_w^F|0Jw!iqVZYip_= ztJp}rs1h$Xyk6(RB+~aHSBEb;b|-g@r|Q*H%a1o(eKzk?XK0P92n@PhmP>}(?G-n# zSwQ6lP6dYEsrXejJf@5CJxMc9I^I8zH(1i&ZFP!YR8y5#5$TrkURIO5vB8q44mDL1 zE24dcv`{#t6Bnei)82XN^zqr$o=t6!IL4i7utXXiv1TTTGqtN5PNmb(qVsPbeTd|T z)SG=O!zV|Hc{RNqom11((b+ZK9G$`WBa;~~J5_sJ_VqP2?b+9@%Aac5_+-YOt7*x# z1;>_C%|ty_f1J9=C#$OBRD=`wjf&F1F<2_hrNI<*c#Ty#{JdD%HR4#&g$_fXKoijd^tbUYD-V5hyvJo-i!lmEB6=m-AN_tD z4WiGYjnFvC-Po~c0@ouWuw$?ju`k1Jf?df?Q6hFz>?Hj18@L`8*pFg&#+JMLiE7Io{1CO}NkAHQcXGbD!(|PkJT#W2zLOb6 zKSHlWKSYz!L+IbPx~!w6vQH-nbt$aIx=-`J#Xmd>5*%PqECu9l2uZL+jiQbtpzQQoFhQkt=_gD7|0)x*UxSlC)S13+;-T#n{KpEMyC@m|4uneY0j} z&0^-nu>2WULiu<77h=t@vRJcNEN=e`@xJ$V|NNfkeJ|qk{q=afdY;#x^Z)Prob!I) zp$1iJFLWiFoYN%cyl+r78SoI`sqTHm9f`VJ!!l=(pf*Qqu=_RD?}#7r_llJj&mdFC zBKFEmq9GHQZYJqNUSqX0$s5#A#QGhwmb^~> zW%oK!f6|b`_Rb>x+P`NhsK!P~{7Yp8)y{+~#;~SYSSAED7js(}e`pqHTTYKAth8isF)JE)a zvybPH-W^LSg@%U)1Oe=3{97;d=rMHYxKYDu*pWH7^L7S1q1d%Qb@Vb$EZ%TG_<4*< z&3EmEhBeC5&RAxgLjp$lO-76dUwHGsF~Nxzc~d2fa{ch|?B-{YXM-D`E8O+yc4lRqWHsG*)^eM(les6l7FnSf0(HgG=Kp`m-JKoAQytxi#>D7M;p zwmE-y&r{tabiwAj3(a(O&*}Cw)7^%yd2#a96))&?*g(#ven#4MOtCXh%G@T;Q;g!- zx1Zr_d$CYAk2~Ty{><)Y^|znnSAdT?NY(CdIH}!-e;(5*QM{)W7Q~q4D|M}7g{~=Z z`C&Rq%x#6Ic(WW-C#wB&TMT#c3&BK^;XBMvZI;98gobfu;go+q75XAm(GH5y4Nf6l zL?`oUDwOM{eNK9jjyK0YpJ^{VH@lxmWV;uU9^R2Vd3v1GFW@YSmhs)i46Go{a=RLK ze-R0K=`}RkB0N0rIg0@tpVj)>X0y2-w8>X(t|v%$v5@q3DYVImlA{m%;J@TW z>RZF+|Cjh_+SLRkUUzwLwFT;whbZ_ZpKytNt5NzSAf8KHYX~G+XE0(T^c3KSpq7pX z?}b#m{U9 z(4?QAFWL2YrgT3s5^~pr(TT$o&9Yl18?}V=Z)=vL(eZ5ZRy^~#mlT0L6SKU%f~`#@alJRy_ww>nFYYwQ2P+}Ni6QaRRfXNv5eS9WOC9|tV|y&w z4@X(N=n8FCIhu9&lDy&JZApaSXWJuv!y$Ax9Va&bOER*374|)B@~6;jfF_F*yZR-0 zv;9#;@j4VS5RT!*`YeNDg`#*7ii(3&FmYl(aK!{gamV^dp9bL;PV6BRBSI9#K`6W+ z9N{1#01sdh;#{f1O&B^id6?_Lm;9RKLG^sF=#=OckDa7bD-K|KIQV0ZkT@M#>|4*$ zz9K8=qdMlboJ7;YIyPZB@uwH+*kaD_sbhJ|NeEq5$7+_7K(d&%{hD;4i|Sa!*Cd?I zu44&blZEXD)D8A=QMV`*{DTvFcWjc~>vRoY6F)*zYgoH)$lMn9Z9?$vqT~~6*tg%1 zYI2(`NWZa5k9T~NJQvG}s1wEev2fbY z>e9%NHnXe!W;V$)sx$FFv6>C~mUudK#Q^akd-q%NDJf^ae@pt2?X1O0(z(s?ruM^I z`8#DSh=Fkxi(E;fUi`tptH`hEkzP18X&44DjG%qll9i+fEycO;N+Qwq)hzrw%-cb1 z$#-O+CacO>Ed<9E4p9psFBiqnXp()as%LyS<=-h=r}UcnRqo80Z&iKa8jT`dt607a zOYdVmnJuek&J3D5b?Q|^lyn%+1g#6}XPy#xE0tiW&3)|%&&H$5>Y3Z8{yUX-hr#Op z&^A`y*Y^W9RQ{sx=LAPhB^#1X#v^;Zz9RwbWI74!bCOS2sNL}slZTBN+06YW6;=&# zxxxpkRw-vE5(9a4)evKmyE=JTU)FUM#+4v85I`Tj!6vLC{x9CfC%tXmX$fyD?@n}u zop9;`dv_HHpod1XL#wdTNn;&XldfJ(a$SXe1mTC>W4vjh7gASV!N#n{N+gQ?cQsnb z(UEM^YVsjHRLPvbCqX1l*YkVwuIOUoheRj**tcu3vPoj6)*>a7*xzeOkASiDE=oJ~ zPK@*MiuaEXSDWN_>dCshI0R2VQG!WgHS5kucjC|9W~3)+#g;HKu3NeFX`eO8>9x=H z*(SM-73(+F#K>2a*0a9@a%h4~h= zH%{#J^<-fNMa@K#M{1L99!TnDWx3g9`A;tU{gs1m&3+uay`1kqA zpi&1Tl`hzg1ngVC-OeI z$^Mf`I`{tFXqLaT@sKYlRnX1&KyN|}^I@3^s_BD+gdZ`DJ1Ca@oJn5mo>|#@dduuj zrUy>`)jMG_mMH2Lm}Z*f(WZdueCG2`o-;jSdXubUtv8e2eNFQ13cu+>z}a!dINb}F z{!ho3CcE$cwc5qyh)>VS!RlF)G2)ndz-utPPM+=$uc=LJ-e!`hGRdneSlMQh*2aXn zE;^<^wuMcyx`Hjz5-+xT3yGrPP3+26|mTH?b}wh>%% zVgGC!r`KwDW%#oBJ4qOwX=B@WlG#qK zwY{g0VMRMg`*uex%_B0&U2JSn79t9&c^r{njXhhXS;QmwHrm(fmvJqiH+CI*p{<2t zX_zwIXT=b&WIwD`Ncb3UBM!Zo*DhrKWy`Z^T4%Lqe#$OnUR!NX*_B;L*+46!*(AAp zNp<(>3E4BBYp5o9h52zZr%w-=Zju|=kJ;pNw?|bxmA%zQ`F@o>mHl^lw{aTUEQ>1f@ zn{X6ul4luD=nM2q^ctrBM!!v;sXwQ$$G(ZH6=zmjOV~yV`7-yWT z|4DxdgU#v?JfU{OZV|{6WDP_^02P7`gVuvqfaZcGg7~4!?uQp<`KZ6h5dvJ)%hfNk zQ@P{?l~KM>!LHQQ65<#1Ya+TRs5*oE{2=?iiZ0#+}vM(xC`LsJ|p2i1GiC5uW-iHFKfuNc_&{Ul=GKe*Hxj-~rB98EUK#kPC; zCR~H4)4OX;@(YS=hOJ4yXfdM1ve8`<1JHHw5!-E?*oz17+!V*&JU}{hG|YeYxe2G_ z@b7*?V*ll8?Pt%c?>f}Gxmx-k-lg_MbcY{Kcv6Q|Fs#b)Y|T>^ z0IMq83ynI#5{{EV&Ex%(wW&|{OI9{>uW068*vvh@nR|9K_lM2glbX5Tf!ltEDJpk! zVhiuC4I+`ci|ohaSln5w7TZRky^;=w-u^ zk%lT3b&>>)_Od9!-HR8Dazq9j;D3(Ig)h0m)hASFvN@X+2o1o=ISz>Hp)AGnmPG8~{ za{*h$ogc$l!=3F}VIJ{tdKY{$tIQ)k#b{d$>v$TDV4DXU&K>SH{4xbMO*>6IoSUq| zDODodewu`ex2+}Y>S?0)H)8c+k8L}U=Wq*<=Wr8FppEj626podi3r2^W-!UoR=aMY z;c>b@g^Q<~Sxj8M*S9 zML2bUb<2lri)A?*#~mvyTJ~i=l%GPG!gBLTpOH^@9*ocIJjnmE^I(uAlw-8Ta8L+v zQVaK7UD~K$H>h6ih$|tyCuUZ#h;t;olR-|aFv%S({H9x46K^?h*&gV}V>DaK{(FwR zo>$NA!;)(2ng1^&l&06SxL=@`>RB?!Wcd393#T~P{{p+H z`n3fR2iG${j$ZXFnxktyi!VTMhB~&i0A^)%Y+nJqwX9|Lfu2=0pUm*h`eBH3;X9qY zFmFYuWx1?|sedJr)D-Kx;tz`=2y3% zKXw#CX2z>-oT+vdx*~nrN_L@;T$4Mt&mk?9XeYOZzzU zw&|$$Bor)YB4{jY@h4eDXVkL|f8vTi?_Pi6%2yiGl#s4YpEvO_>gN)YOVjGu_`gVB znq0@y|H8iA_&RomHTiwKdaRd1YsbM}hi5X{V9seP7S~wf9j6}6i_WeKD z{+Y*4|3kVs4KNTPTzBUmQY606U~$1?`YEG9uEr=kMR)FB@)e;|ZMso+$THIQC#ymJ z!0g2L2n-nV%qw+w?vjTh^{8Q`Rb;GNRdsAk1a5vjl*_BxfNC;Nb)}LBEW3=X(Z$wa z->8kc>4l6(^3bMM_#fTG*3^>K?Uq%=?OdznZRuBod-_B9!zx`=9ogXA`zKpxtV^*5 zlqX_E`a;G-c`e#{OWMag>UoF0+|r1b?WxBbQ13jg!jH;>5o z?ON6~kE2nps?~+dJdS{-&RU~<0DB;1274UdP4+lEo9uB+0b<%xQz3E~&J~!&Lb|x8 zDQ(|%XOuc{a-*D7#X_xQHd(Cu(MtNby!fx#3uPo&`JRWx@<{&MY}BiDcbsS?!PgOf zccI-3?#*t{7Nnc-Ho=f!ls&YLvGkHU6FOB9}M2=}RMYY5p{mcKZZZSD%J`AD73u zz1h%+Z73dgh!^gU>PB~?f6yS=@~myWrG@qprtL;KyNTOIyohn%!yfvgVCpLd3@|_I zBht)$oU=5__l!pU8O%xZbSHYy;bP#jXYzPoUW`0Gl3y@4%C91i@3g_}(uSSvfpkyo zMWdX8PCb+Ed$`)k?PHA(gn(-2*GB)vcbBJc{p)12OuL#!>d59MRF z2eKZizp27_qbquaenz|9c_!8OzbYobyX9$ z^|w5bMlI6Td;Zwus%?<3S@_<+ zKYkd=4Rrh`cKKcEtxJ!kX~gLS8gYiM>j*SrGDX)lj!q>Q2EH6ke<6K!Z;qk=QZh!@ zdmJSyT2aFy-=*)+7z;~#mxi^kGr4N(-fuqTS9obiO*gkHli)=}MYuWH%G2C##C5J|3~SKwB?=fgR0O+6Qubbm^E!!B|NR3Ecrv~&EA z%f=?sL2gmCu`zskSSv@?vQ3F}0!>R|n?9l8x{&EKg3zQIHfaVj9$TZEKZ7<9a!vP9 z68*}lRXbxx?R^}O*2}-+dIRl7qpO(TTO$#wjyG;N)64sDd4xOAFgOqf6bFuVQcLp}f+-_mGe4 zHhx0iC*D5w2|IactsfqXZ~buJRMpFu48?tFWJ8_K<5OBkT$fvUvN{^r>?AruH}*5y z*Qr&0Q%CJRd7PzQUc!D@grwfDVaFFCsaFb!a1C@ucV`j(oZ$R;@?zSVy4NsiG4-Le zhHYBRE4`W>T1-a~iQxy0|3ix_*(42(qY);SqoI#fHF8*`E=!^ZNXzSnI(fY{_-!pE^5!pgp+NpxZz8?}s%C5Lrem(f;)X4UF)zM{v8+uN2pbRglBH-1!;r{9aEe@(;P zkJdC#%=#Ku_%&VIHUp3D599!h4etBVb@vnYk1y@YGFH&>?*7mCpfzj1fd@hH@C}Z2 z%J7qRb-4}?1>eBKit6T3%)ohK8hj+80X&c&;OH?YhX^Ng96k(bbUj^dVQapn8DzY! z=Std>@cr6}-%)R`HPwF`qQe6lL{+Cwzrl4&-&I{Lr&sG%en%I(&@ZgIHjLgD-(6+? z{*IW~X-3AdR_jM69o0W||Auu(wYmnWbV)nf*)mHU|5fFILT ztFOTpL@o9xg;Te85CMDR!X7sKNBR)U(bqQ60Blc9-9X=UorSH{YFXRF@;A^Z`b`tF zZlH(gUMQ(JVm4b@*a2;p(;yRFopRsT%yk>7PUw$s)`RW1Ky(ADk0l`6W#!rJYmLH%Y~ zg0)rh3=0qMBMT2N!Q!E}B>@7VXmIB!;lgN@mH+Zr}fo&7~?V&HV5`*IXmdVF$D5rQUHhkIxpXWY=bA ziSxauYvrZzcAxFvMX!>t*w@*(o3y8f_1{fLQdc9(K28R({N2=-rdP4@-86xo z31%bq&>7COKSqFlx}$sOGD1VD*^nF>+;XkWHQQ-TC00f(e!Li?JDfuegi4ic-#(g6 z*H^Om{Rk?sl1V^!kIHJ0s<=RE%{+XE@dVwd*QF1Q3x%V7#@eE=|u_2dwN zja7g*n+2ec(%8xa^fuKQ+1!J45#KRrI7lPum3kI*h#u-U9~Y@>qITpNHssu-|}c*xG*84F{` zx>|X@WzPa4NNnd>+KIkX$%xyk1E(QSi~hav$$(s2#dsa_ySf0e`ypiy!-Q=rPk^x@j}-M`Fw@_WZxBA#jTa= zfk+od2C+fkR3*Q`pVd^8SJ)662u90Uu4I}18bM|rL?tDcmP3+}MBgl?ZZTXCL7ib>`z?DTp1){8@% z86Cj{GGjgwJXyD2XdjiYna*dEexajYeAx6fq?s_f@F~^_>@(r_O}aom z-&lo0tCoF~wJX1$>NnX{oiMpIEH6A6Rx0GB&4v}d9QNFmg=*Psu9A~SkV| zF1TZkM9im#_Q!V)xp?mp9}j#Aa2)UWVrpIe5METZi;}vx4c+`A;)fc# z#q)RZTQ$r=3Zr2^giZb(OMM;dUPv{se}+vU3h5M(d5)c`fc?R@1MP#m0mN;V180NA zfc!yjAQfm5t1YC%d{UBmVw}`H65GbQyd2QrBBsRjh@a%Em&2=AToLsO39s@)!h>-w z`lL;T7Zvz@L%keWRVlZ$MZk9?$OB~kn5{0NVV%drUg#R_ugCQ{ejvf;nm3J=@;?|1 z2Cy4Nw1fI)WeC4jlX5H4Wyx!Z0;X4IMfs2 zn=NbbgTSuvk`Al`89@C(d7v>MchKEg?ED|J1jV?#qI=?Ro&R~jl4?7 zcT1D&^;ZRn6S^jK~xDm1geq z&D=+ux%b%J9!fx4nkm-W1g80u4s@CV;Y4=&Pa5FVAI?a-vmKmniqo~qitqPg)+2{nhST(I92lcDjpwI=`$xIwq>!cdBDVLv;3$n7i_p z8s>kUzT>m4ZZIBEW7TgOUdp*EZ>USeQRHw#km6=*uG0vbR?7;mQ=eJ0>x6(9L$`2M zM%0NgzvN+|pvN_aO^_$G6oFq|eJl@y`+3_(ZJyS;wVB(aSs1Qt^xw3-ccc6?sETIp zQkgCOn}%q%H}k$8?&qUg)=a+;ZhouqZ9`Y(uz3hRlGvQT@cOg7^K<&S;GblxNlBMR zX=MI(=6~EC%@XX|%-yn?J%zh@B5ppH2=C_-@y;`PAI##-Qnjh|(-dxM&CZn45R%7g zOX*DKg<~*)#j{B_=oF{5lL&77ox4Gg(Zp)q(wlS=Asg7Of9NaxYGoPy#OdgJ=-zBq z88$<6blq-IC!)G5CsnYp+jPRa%PWuS+r@s|wcUvJZ=~QQozKyj7FW7v%t_p$SFQi9 ztJ9BS#$9<{<^IGL>v!poCI0PltT-F%=LYca2Q z%Y=+%w) zdZWjddLh71J$33<-1`l{hf6Lfx9;O-mgAk$Iy?|rvFWMD77w4x?={lF0~+efrbgv1 z>o|2L@e4{2tG{&mc`EKy8^Q#yT<&dReHs2wK(JG%da3QfCZ9+b%H`sEHv1tS%NOc? zen>S=bYYDysEMv5xDRvCjH2>jcg$GxjAgqm;Jnxk3+>6zVce`V$o){$(<`v$c!Xyo zsOZb$tyD$Uv1wM?o%XL{>98cpI-Ql~5)%5ubg>r|2L7;K3?s!q{3`Y$+8>N!Pr9v& z$ixRpRW{qhRxR6rI3YIEnh!3;%d3TFNX=;sSpEKgC(hps97bb}phs=$Tr!##M}@ z2Q@PvnwMu>}=f_gIh@mv6l5OuIMp1vznYxJB zdcG}};++vM5-@#8$Mr+*7FFwS%f%IJuD>|Qb$wMU?QQu`1v~67j&)5^xaAehBS0KW zGTHP1wABQ*E-lou5xZXGY0V1v;Wz8z>C69%G#av4n&oeozsm96mJr~ zj~67qOWaOkKRN_=CU40{YuWN(F_0a4N%W^{Ygu8i=*LViiJmylY#S`jbc=anI!G~9 zQ*Xs861y%=-Nm>0?a~R|#Yt}4aa(kRdOE_)tYP`x#U%0$>m4Ev#-=u#7b1S>=262p z!TZAiTfcWh#2F+)H@b)TF(EVA=}_?{@*iDgsJMfWFW9OuuphG{VdB8vYpWjjtK0IZ zW*z^QY^r>Af9ekILY2!=x|e#1pAec;%{GLK@6Z+1tT9|%Ok=9qyx!s@Z;$HB`hXrn z!^S0^d+@rwhY7F5ha*w0)vTtsm`?Mm*y0FrJH;Vn(97Z|8dIg4|FRfG==MsM6Nx^U zQOPbwioSGtC947NyRh=}#1=hXlOy)DzV|K-zZ*T=bI;>IlPXzYA93vmb=X8ImlxRn zl`9q3Jh;lu9{5ajofe;<28Dszfa-P#LLuldC~S97}K4vzRDx5#3bJ&P0h5`CVwgei*){)U&95$fS2YyU-7rJYUD= z_7_Lc%sSo4{$eEYRn=j(MrNienVEcd^l;DJ5h$Txbs1iiX6^%!@w>Gwd;kifq?V23 zc%hah4iHy=FbW&6%m|H{J^S20{M#xt7*VG}3;RF)|SXFyv(sh}j#^`8XcCTPS) zL6`u%y9t?xdjlvHv=9^ziec{#6c_Zm+r;k^yLa_Y7Jl3{#YLoa z+co6J9tmFRnF+UWYC;ByFTR-Hv}=|VZqeD*!5<}lgqF{G3=+H19ZhV^ATfNv%BFzn zUXf(b*w=A4BfX*MoWW~bocf%>XPbN1Te7C0xRj4)K;M?2gG^T6T3d=j|lN^i;c%&!g?@Sc#Jt1?FoCr`M4@s%8r@9loa3q8~H zuk3E%Z#h?6{*_x9NWcW)51tN{>fsXh-D_eWQpwJ}CVrq9q!Wb8dHC3O20j1|@&K6_ zzDf?_GlDA~LUEPa;*1Kfs_dJvN}KEPgu7OiliT(QcNuSi94KltoPS<)MCV&_JM(V6o5I-(uBG{BeG3Qg z*9zrI%^R@{twYYsLDxY!AoW^7Sf9a$3>N(dmdTSU?e~78kh9Vzm(Z#rA+Rg%wi)GD zE&9$R(xrNsi*xlaE-9}_NO){B>vL@LV6lsP8E)7-4Ps9dD@4GtWwKW@=?_hAp?d5s z8RbCSBrd}T4V@n@nQDKF<(9k*-xx5;ofVC_!uet96U~Aq)_;f?I5yMxGCD()ns*1? z6d7HC8TZd{z8CJqn<;-Wqff%8=mu%Vmy?|D#j3cJzv*#cTH~}^@)G0zS8CKd06?ar~0Pb@(XOi zP;n@!WhaJ;pSC|_cs^ab*_Oq$?M`UT4!b_hl)O|1eRENbykZ# zRAqAC3N}(LhQZ~FPa)b}stT5)#@YgJYu;63pu1kroQH{iw4j~^4-@;o=4X30TV-+w z+y54Kne1l!-&~f-gw2T&+Y_1P4iiteT8)cmR{1R?^IP`cSn+i_17F=5fo1INSaE#& z2M}V@d4;3-kXwfK`hO3(C=yGLB&TL=Eqhwc zG!%*D`4PBGrj{NbJKalZ)jG)O-f_0$4e=NasbvWx#c)l#+J0)6YKg|-X!4eUkC>H3 z!<4Pne`FQDcBg_a8oECWLmg5>ja?cGX@OzTlToxx?q(hEWE4ehTl+m3MUhxs8+ts7 z{wo)wA;)0qvO3Gea9hSTuQ$bEq!XL`ruad}3kL;Z6n@WP3+AE>(4rNBFa?C4yn0i7 z8CT_7kHR8ZV%;n6LqS8`1=;gqeTzW0yjv+sSx;JAv0Rs)L|Kp%4Jz>!pMN_+WS?4 zuyhF)7i{wwaVOr+eB*6#sEf)P4Y@b_;caoWONpf%KZ0;xSNpa&hq(G!=$?P@nvCwl zapE4@<{B2=`|jfBC{|%kPuKa67tc_ii?{KZ4-&9r#6QZ*_xM~73NvovLqTib6McFw zs`FFdlucN{^3CI$@xTHK z)U>cW0_=;B_%{=i52og{nW5V0q)t)~HmG`rtG~tD$TwxYW_9~Lbaqd?X*^L3cUR#% zH$rejQSc11^QKI3L2;tkpXT9lW1^Vkly)8ygwE-Gaepg%vtD;JQ5;3^LSBm*7^84) zIDCe<2v5t^B&@|qfA;dn;zm-VgU{4B1X&J2wu6rp0kdDom7*@#^?eo0k~E9q-~nX-{a~2-n?Jg zcw13qOQA&w&?Q%j2l5D$ffdk)U$JOZNXTKJ7cl7$d%*J*)Jo|8%0wbB(JH%R!?qA5m%Yk+0IMj$U*gMwO#me_fff?BB!un=}W zK<+OG$TQ^nmqp`-gsuVdj2s5?l8nBNs?osV`8O1Mou#N!izXiY_#3DzV0k&}6e%yh zV~?m1IJP4mWq`bjLhnMp$4OeG=lU#ZJ&-4yR@n9ZfxOP8O5_w(Tnyxmw-lJOi%KJ_ zEE>h*8aTMeQlNkcN~`S=mMEx|JZdbOD9C+)+-@R}N0_Q$j+CaLR>}tYz^>RKSFp7t zscP;1MgqCNYzOaCrz9Xp3WZ}Y0v77I!5*oYBXX9|!1)~MqJrpt_w4*cAWujRummM_ zz#%^jECzp(^I1d^?gRI>ld>CuyHt|!(4xtM-W8aWB}$Km<;)DHz~=$^=OnprGyneM=Ej1%^Em( zU3wWUnoI;32;2sY2J(OtfXBfnI`}l;Vesh=K0`sRqy_RebAa=};|{$YwbHgG9$vQO zZnnpl4CE!PDN`I;$qY<|$4HAslMYM+@&uIwd4L0~_M%$@ECgQzECa^act9utU7$4}qbUNaHyoKm}118JtI3I83=bOL7xA zW=jnUYNbe(RVmuBKwgsbfJsP54v-i9abPld4=1}_EHDN9Vc>3jHC>8wwg>3p$`Sor z!OfCK3wr>W3g$@L6wHzefV?it9K26UZnsrB%yB1`E;{(cR(5_7$DA#apBoSv8o&|u zs@6aaTBRR%Af>TDK48QH-I2n1T#i6pg#PRkH|!Bw!kFe0u~0T<&4d=>}jqc)`=EaYg^$ z1`GyfzGT(Zcw^NRVvpEIL9J8{&!Kr3$mm{ zATL@8$mLpK2_o?5&Gk8wJCMu66<#aF0l9sGLto>NyGFo%50MVMY}I7*1V&ml1;At= z_kRG$!`;wFgQD()Ut;TH)%XF+f#E>czE({%FdP^M91n~KO2A~`HlPH&2;>PX2QCL6 z9A(wy14jajfyuyfU@VSv7%hL;@p$2F=+7IL!fuZ! zjr{_>2>#1cxn3njF5~DaX}_{+G9hmO9snL*0Xf1C{RXH(1R30cfD9b7ouq9mf$*4` zZq;mnhk2_IfmW0@aNOl8d91eU%f7bN;uBZO)3PEz2HxQ-|H)^3XmM{5QqJg`TQ3cLt@ zQKOyT04xW;=AoTG05pS-F{0=Lv7Rs?!&*@zCBP90!Nev-Af*CFf?s5|^V@*DcLZC2 zm<^9}+#4vR**JzuZYmpogd0O^OB<@fO>%8z)8v2;bpyg*Do6NpYYp7pQi^Q@+#4*# zwzFvt1ZvPtydcoFk}80H-OztKg2xaX>&-DsN(V+lUI2^+W^}S?;(=*C&|}1m?ab}A zN;$xJkjHlck2VqI2SftKceQC!Ay4(^b~)0-0J}UY(56|Vfugt@7pSDk zU>SI?9(FzuSOGrP!H-oCIdI4&hdj+8U*nJ;cF6OA4e(#gc?|Jhp}@UZ(g6*S=L40Z zYH%p#^|WcsP-Fmkf{GQ)l*%1)*D$->L%~eR56JCf9P&5?v5rtM$AMXrp5Gy_-z zYyb*DD8b$~jR!Cj7z`{1Mgc=L5jM?8I8uS}z*68mVARVt&2nHOFawwm%my|Bj|1Z( zQI|joSOzQwHUi!H*b@>8=;2tS-h@BrB!x0-2uiN~0Ef>>bzck=cBHZH)ls~XM?H%svx_qs}rBRHQUH7KZ+ zM!v!AL`lmLbAZ=KyWT@VtrW!(>w=Ou?ecO3v7&PDZlmn}g5Tn(l@ftzxVk7vT!4Vf zfy$U3XE%sZaEnx+AfAF9@)`%<;NZ;;UKnln7pP#46zt$*9sEcKpW@&b0hN?&fWsXL zIRFdZkGy{2#{l=m-r&1{z7!G*>ke5&~klUqE{o6Ud9`Fp!r>+$5W3VldhUkjpnHm?gP>VAqEN zxjcmh?1730`W|lmY}g4niDwU`F|S6EmF3EIZ}awTFLK2 zyFOgOtx~#!&r}erT_BIh=OYZ=$Vfb}7?=&@;VPI(C9{LaPf%%cz{hF00|w(A07_nZ%TJ0kE@&X_qxMFtL z6CADO`fO>yPManZa@SoLdV#9l7{XtBO$ywD-h#$#*oy>cxFZ*1I56`7_n0H)D2PUP zkn>s68erCIqExK#TFL8>O{40LV5F7^<_&V>x2CE0rTkD8nJo z1ZqNISnLp#0>i3V_MrRY&c-J1_-&(lPK@GX@?9qGa-b z%OQ6^$?dYFNChz@bHobC;}jo&P}d{#?EQW`kk?%@ke5V?f?6rV!EXR2AwVsVC*Yz( zAAZ_y&yo9&;XHCYPmyb-wq?E4kInsLIUTn>p9DLyK2nT!~ke85ip*;e31+%0fz`b+#NoI%K0L*^v6={s7 z$nGItLG0lwm?LfHh&GX}@H?eKpcZ?%We#3-#m;*Ivs>?zmIL|Iv&U6?#J<4n*N7DL zr#)k(C3d;+m))N`keBczH#qhn;3oIq$>X55vnAQ zf9)mVuD8owbIFpN93d4ZLciN=;RZ3NTie@wk1$)X*+{1WE$0a%REvZ-RVoqQQKb=K zh6+ues94v-VyA9Jw=6t4+_XbGD2Aw(6JeA}!pkTgDZ(}ak+7S!-iUM1RjfO}^Hcah zk#ire?y5i`1SXSju?ZV-+ic)1ws@l$MqA%vdp6?2*+X`2BhE~(vO61b`wQW0!m;WC zHh7cxwx(Md!r42BhqIqt=MmMA*c>!hOiC zr69O8EBSZ-Vb^{VC*T_^y)(tPM(n<+Y(EP&p*3-B6Wo{dcg3S0o-8)-Y7&HRp-)B4 zp+&&vMdC_TuA;jKw;}(9ex$>0+D&#LQ}p%>cJR$ByU$H#$`s%6yn)jTbQPsOyi|RK z0Vv5|Z?MIiaW^d2f$JQ&#DUWtIQj;=xmolN>xnR))@g*$A7OOlvR(LKuZ_6zmJzCY zN8r1e!jF-OY8b&+1l{vQ1mWAkXQrd7OTC zRhSToxW<*T>pYHrrR>obu`eNP^k&?%sAYqwmGTpT!ZL#*(Mm3sS|RF9^b&5;kHxn(XNkHhViRhHNfj zE4Pb-=;RW1c{_@3xC47Tuww}e+W|STBko@H+<}XmMeNdcz!8O`oBw1hcZfsC`-+sA zw~Ozvn>)moxZjCq?+$F<4%`O0wnJ-&qT>qL^*vB`E@bWY;2RjU`+^%J$>Wq^RXGW3*Vc_p< zQV!~?Y&Y&~H#V}ix$tw9Ey)4oDnP%z z#J1#$LEh6Y@racKwhUEe<7*_>`M4{*d5PUd(B9pj(Qk*oSkbq=#C-RmjsC?t?-O64 zn=i7J`_LFay~vJ2Ob0s9=^_i+5BXoev3K^1bLq6-*oFPbclY0z^8xS&FR+CNP#ND| zU>7(}bKpP+zIcIo9~6Vo2R0wTSc)5I95yOIKmC;*Jt!{ZRdz`1K`#}sDTl;`baVl` zc}NWL?1n?@$6f4kV{2Q$e07MY@)xGoAzdeaVRJeE!!K+-$63FyGdgiK{p&nic?3iH z&hu>01~i&g4qV{C$qpRuz~1MX^)UKV+w;u#2)gjYb9R3>9C%Jaf4cdcJ={6x*vTWJ zr|-Ks@z=g4Dh;*`YCPC`h=AK|!dSHLf#+D8qX?_31D)*Hu`=H=%wmZ0;C@u0LIebjXI8!&OO5xA4fueIK$Q-N1L8=hMna6I6Jb#TSPY&epqy)UC*#KC!ldU!$LS# zo@VM3sJqjr@jXh^=?|yb`V(T7cV!-rF^iY2TT7%WL=_=K@p8&zlTISW&3SC)Nu=!S z=j?)2;X+^7&B)Y#POwcJ z2cBT}Pm7c2jpJ<08Sq~pXWP$!?|z&)p9O#Q7~69eWpu=W8y&dBj-JzwJ&lv6ndf`- zF?RDT(%0=6YnhLlAjeo(K9)5Y`|~l@79C}i@=@G7kK%Q8OdZpYvh(@k1f9=0aYM_% zc^~_IGHsq;^4w|jr%(ILZ|<~DK2G{{-t>=W`$5w;GCUJ;up&tvH2uc7YgBF2OK?e}-aZmxM5X9pt{YBIW<#5!18bJaKT|pk8 zoa-0+ z8ZQcoaHN2igEBx`&<5lt8+06W5mXAQ0hvK5{gg3U7};JWECQiYpZvW$g$R$SE#Cck z{Z+QMoyym_AKd?W^@rZ1!}pnuE}C^s;{UOWiPttWq%5D`J72D}Kyi7OsL)*iCdCsu;+*lrPQI5oG+Mws(J%RzY_ zSTHpA_!m#@G-y73@gj_z8FK05v*WHkTAXLcV47*7A#XS)B5Lm&D~ z{|9t2X@%F}6~LK-TlOiuCU~ddbi*C~mg~IA*P|ar$8q$Y-iclu*Xb%1QnzKFcZWlN zz41Cup3Iz3Ns`<*YS$aeA3}J{SJQ=X33Z*V=oz--{%6Ga=ZL8`VRD~US(L; z@%AyWdcEJA;;jf<(>ct71NX-{bhtI_H+`HTJj&}bDv5hxjPebXpT};-MW*E~eskt| z-uM23-;{6VU6?yiOrdCg&MkC~0*Oaif>2=UCw|ig6b8_5K%axYjYDlSs$BhX^nFxt z3jOx`dHp0f`hedY=FrbJ4hc=$=Qr0lXgVi_ihtlYHM{{JMBE0)^k85J1LGJ-JkN~v z6~EyOO>>kBXZJS9q+3a6<#j9iD$715sG4Zt*7x<+y@y=+R`(K8} zY=*q*<)WJE<^899bSHA=l;0H0xE_leW;HqSm|L!xc*}Zsc5J5{N2HmfG zL)huWP6Cd3Vf|`m&e!O2P^pT+BzA^CALsy1KZX)9lnWHp!t)sFq*aR75jT&!Mr$#6 zf?k;fhd~YK{x!Ws4^=ki?_-0@+@z{@}99grNzs( zyv!(%=U;r9+mf&)DlOs@mbgAj{AiEnceSrC45x`@@jhl))YUhT8j=yqyG~j`Y4K()@2ch9u)Jx~(dWMuB3`z{ z2}?|U#O;~4NGm8U-k9Z`x4c5j8?ij4#XDztgO*oqd9Pa@X(vh?u*B1r*lvj@Em3J1 zJZ^ctmN#H|$1G22@p>%ph~-UNUYF%5Ener_P9WNahb%DxrMk4m5|tLQiL~_lEic>h z>Mc)c@#-wE*78a%Z;$0EEnc$SX>EKh0iN^E+Bc+v>Z z43G#Ey5u<5j&A2$VQtV9k?)lA$NLDTPsIut5BXnsijmsh7{zAj0CRVyeWrz(Cb5m z;^%B!>%UpzUc1KN@5Rn*fYaA*3er`P?zZ+*CKv2?YW=SYRAJLPo^E(N3Nqecjt2Q) zJJ>T`uYiH6y;TbA; zkB7$J7P>SQPvw+aS!liXS~`F}j$X?FUQ4B$rT!FbNc7uXmEoJB8aB)RS%b`#eHH zE~o_FQcUHvn7Bk`^I!)!LB98t({(b9!`@o)hK;KS;@@>YqCN92_rJm-2QaF%JS>5D zyL{;<;BL5vQy|3SdO0}tA*&pEA(} zX9t-lKr3hi^I#vuGN7O_&3kWQOv5%VM|RHr?@$d=xg64%?;&5j1B-Z$OYn%QWQ0Q7 zzlsB39cauCm~^BUddSZuT>~<}DET=t`PCb^|DQ#-DPXz_1E!MvxaSwj*O7mi{7KN^ zK$!VjzG3*E$=Q{ zPY<$RJ_>j-Y}8Tp)yglsLpMNzUrU)%j$z_o6-=TMc#+K z>qvfNWWsFp^V6NEBm8R0?5CF=io|UUe)Ai7Wc7e{kjEbL$uRrOzzfMS@ywX`i!t#q zPxp4xvnzr>ZTM*2ysdAfCohd~AL#veQEd;EgK2>Ea~ z$V|}1XQM$-4N^fEUD01zH4HL8f*l|WEC*A6;(a}oI{-@IjUWwD%c0pUdK_eD22JtP zR7|{ucn|SuWFfK_N2eX`t1zQI&E(t*YC$SUKtGFzVPrATCV;oeKVBLz?|>-~8$xC7 zE#T==pYoYI!MPLRGtPJJ=;3GU0OSAJR&WRVxpy%}w|7uq6X}cA;!vJ3{d^13U;W$f zhJz3Izcf;8jIIt{5t=MClkw}qs4lvKVMf!ec7Jo^)hB}whcu%JC6Uo5gKIvg8B&Pq)#CO6VJ96f!;F?hV@qiJ!B3DS>WAxu${>Xiq!AC-xI>Q0xpNKCj zxFWJ8Gq^02jV>GAmPlD9r8S%GXZtt7rF25M=McI>4@Q0|6TKU`_t>p1j*MppQ{%WT vO={#nnZZXD?Msb3mKA)Q1NOd-2r>(%xMrHNf{DJg$j_zaL-ItVoBIC?(gAx# delta 84472 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 delta 85093 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�k;^Z|9lNHO&7; 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 delta 84745 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 bqXyO6wN%pLMA$rY`nIU~5M8Uw@!|gfd9{_E diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r4.0.3.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r4.0.3.so index d3cf3b12422c124678a1b7aec9f16532ea0b7f5c..d5298d3aa81a4fb4dda0211fee2ac1e81e15a266 100755 GIT binary patch delta 81241 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 hgv(>SfMM7%Zva3 delta 85579 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%0A1t%(F;=>$=Aquj?LZh>#1rdEPNM@fphq z@64O*v0>j=dbiwU5A;Nxx04o(whqQ_jdV`mk>{fOM}8OQw%0XO_vP9le{$mFn6$*) z@X{nso}D;9$()ay937uKxuN=phMv={9$d#3L~kE(qTx!bDyxahJ!nFbdy#ty8#4X$ zfbsLvxG-vdDx074&7J68lPlwVEH`CnugSfpxI9$}5i%}`?p-x8MZ0QZiyNx{r-9AV zbRyHQq589iZeznaJ73}6TVoab2fv_l)Ne6N;fUHIH$N|19ecWaxx{W~v}8VQMcJTi z{snbxO!Pi95}i^PH9ko;NaJXZt*=h0w0)^{adeOp@ zBev?tFzb+a;m!8Y*4Ng=kB@Wzo*g+fwI=S;aNip5XE)nph9+{M-PdjPV7@dYQqv4L zth8Xe9vJbsF1g{^+oSFOE*!5$TrepXnTln{SNwUG3d`i#g0J zTkOm12fY1mwY{EaPsO`?#3V99lT#b2zu3Xk-Oa7sGr1s`$#$5NHk2Ec858QOx1F3e za9*0_d!nezp|FAOvZzDWz@cUKz*OHBt_tjRVXxI!*PlGhmB8?F*T8{gSNsrx@3o>^ z#^+8ykzHS1d$LdY^np@7>d@^2?FurMgn7bl-cKMxzEGpXh+rvdO zDE>r|tE!z97C)cM34aq`(u6(71#^fz)$~kp^DSqNxA2r-sm(lm(sr|dv+18|uzH4< zynN@t@=@w5JefNFRQ9sbw!2N)WlpYjnP;Svi7rcV^n$WW9c{Vq60S;`m&@O#Hd6L>LIyrEBq+^RLAE37W zC;N?ec^qRyb>z9_$NQ?|`;)R>@18l{yj{7hKflD_sLg5MwT5@k>}(!c-kr03;0WwH z!5u`4iIveUT+E_-^EoW!hFS0Y5M~`^?#S6&&g2!ON|KLsmQ+Kkt@C~`*=~|?9|zeC z($%DENLeJg5|FC`Inm2WUrz3FqIcC(=PhB@?;Ly^s!uv6^HDPgZ;VTNX=a#jQ-k-7 z^I>t`VN>khVJQvX^7COy-o;bwgs}S?yl+jhdvyJXJ&)`#*tBzfk zdy{uLYkhj&#ThKzmq(xfb&i~KLs!8sP({)6>dpJA>?T)Cce#^ySiR@xb?ZI)kzBis z#%k<0^C?zWD`($3ScMf0$?&K;v0mo&yj_ovPq&lPO8suo1J+}*B9#5KeFZk|2L z9_u}qXZP#yHb>#yF%;dLQ+btl z-ZXpcfL(v}%eUkGS56Jp<@L7j*T1aze$J&)Vj9QCQ4Q7W-?M#3|FV6XyuVMgr*iPP z;WoQhpHqLeW>{|fTlt6UxfP_0?R$zciz$=iefc&%QI7StxZUmFGN{g7^3$?S zeOG=|+H!ppu5}u!YwW0re@vUr;V`DwO1!abWldg^)o*y2{bth*$sBWe zAv~&S`p`9-Uj)`A^p`)>yvF8(Kc^YFvl@=T;oIhqid;iP2Hdy!Bmk=g^$-msI#f z&0$XQQOjFA*HAs+{C4NWHNWQTvi>qrAKOVvRk<$N{-ZIC(lc3 z`mb2eYY(P1DGslSX>N6*;u6oPqy6UISJUY7B_i_m;`3}#w*l3UV{rJ>DJ@*}{Del;W5{vuDIX$6# z%D@-Zb=9NKxZIgr zkJQ9CwZ&*{S=844zjf7@a3ws76-hZ$EjL?wwaA zxyN!KySsicM}SEbNT5J51@=1y%A5jq)rrLSZ+5XeSmOHn7)p$#L~}}%Qev%BVy%{l zrbH4Y>>j}~DfQ{J)y7kQDMcqz^vvmhx-;KyS(V*fE-**eAE}w)R5F7~x;!r%rn0hS z)v(Ae%TtLyM3u=@Ioi$RmZ9nHgWCDlPPNDDB&O_iCQbBfXif`4M`{u}$fk>Px^*|# zCw0}HU8kEpeH*OcQqaU6YXn>_DwH+`-XcYQ5tTK-Wq>u^m%aeB?J2?<-% zodI2cx@mdO{87%$S{xm0b^151hU=?;ajx11ll#0b;`!TiJG|NNZRS{e7maV~CBt4< z{Rk_Q8X4hPE|p4eYb*Vg)>jIJv3_Zh5zF`2v~#>?)H~;Ns{6&ClHJXg%WPqcoJ#Yl zw1YE7IhDE7FQ)u$__Eu}qY0*A<&m1q_Tij2GJ5&%1L~^Nv84^?cKk$kUGY^NGmECnVA`np^|}`nVF%PI~kdo85w!d%*euW4pRriMml<^>g5 z*j+9nI?J-Vyq`0>D*F81Kfe3<-p+N-oH@6decmeO@s`2;?fyIy)`;7g@U&dgTASir ziSPimibM4shg;@L*7I(=H|&*)z2c6&2KI8d-3tDYV&8wqeg?W7ZhI~GQpK*jV?S!v zUa}Ut9nQj`Kyk>w{)m0`Osy!?VG?) zQtT64b}gu>q1wL3Mb~dPGz2}s&ZgB>`-`ztTFdJ zYTON%$4_NsKhqNW#xpHV^BnRT^9xls$L8YJ61?9@_W1k;Bd?Q3wL>fytm;Eq+fp>1 zYo{Uh7>nnZLX`Kx0?j@|akS!;=Cbhu<|3}rM4*AEc5>w{1>TF0H(s{I$QzfJxbyZW z^7f}YZ>N#B(+WQl9@VC=ja{y%xyqN9sD)~h*>#glMg{Q#rknR5X`hOth~%idQ01lo zokLDDdsH1pbcYq~4I^JXAWai&m{<8on$SJGqMULUyQ+#WB{<|^=9|V5&A#wSf{(hB zq*3n)&)&DESC#j5RcE1(Zy37DGSeg^sxg<1orJQOC-Lc6{NQq{5L#|k#X2x?xMmE> z8tq;Ta>&O`4{kYPRZF8al~!LV3JF@;67l?Y$i8MS-DrzP-`D9Yr`*;Sg<+gS_B4Aa zs~5!HcTcARk3J50gUPGva}@7$t-E}aP0hy7P_ob5@_19H0*BmWO53&yZmZl{jfoFR zpKWcZUIE_~Zf$}o7TT1yU6ijFvtYCZ_pVd3$P;)z$37qmGdp~rc zTA@jDgjewnZbn;gAkSM*1=pD#Qg99O%CGHelc>n6Onh0uA+La*mv?cShzYM#{u~ML zjHud#CJTmjBlt+`(>d7Oxt2+VGoar=KY>ywQlSE)^x%ouEi-`egkl%4;(6@e0Cxaa z1Gj*dg8v?v4%`GB;P$cM=8;ML#*p|2T_;VSrG@JGRWf)541$*t#$jmzBn2O# zzo`GORn=z21SLQ-aE}_-HNnIx=8?!QQ6>*f)%4Ikyve=A#5XarUc_E~lW55#W_*)8 zMxJE7=9971znsmSPo~jX?Lq+z6K0lV}T zdHjJF9pz@6hRWzyRc`hq+>pllFCYW481&x-1fN@Ezb_zt)ge2CrdO>Y`+GNqIx!lm zW$AB|*`5;)V+MHGaPn=EOu7WOqp4w-t7&bQ16c2cWMEKAqtJBIYL}CNeOv!E3jOUHFF+*s|$Mj5nAibECEF@uL>}W>u zJG(ypZgNzXUG{5)*ETG-9`Fsd%NH>+o3u^DDoGy=Dzkr5#^em{MG<%W0TJVBq3IV) z4GWRR*$FIQ5g8U84PL!7FV>FrO%;|&g+6&Uy4_K0uW)pCU}r1h!-m8K6pY zZkoW#7LgSsi9P-fW+-a*%{%06y7xI2w3uX)N~T>*KJwX!#%iBL1>1A1`@2L#v+CL8 zcgc!Q^c6~*2fFEy7NitgG@-)B4B!FKFE+pZ3_ zA7SfQo9*&SER?Db6u<|1_JEdj3zA&Iohrw!rs9-U#D;0HESd&;$BYfVPd@hTy>`=m z@@mNOJE=xJ)O~tZkjKPGXn$nng|MwoaqH zLZdYclM6ydg=*~bVYATmyj?h$gb(C(N2!9{6l%DxCA~$@;N2+EZbQKPB!&d-O})R^ zfy&as+^^Y`ToN6W@HNkoho&pG*C|GogSQooib5hw>)6Iz64`GA+U+UKjBD^cyXFm4 z_^7?T-Fs)d{8zj80rId|K?ZtNw97H9F_-ivA*|~MCNyAuF0t*&|E=w#~^k+a0AH4-Q)w2i*&?5!oo7vdWe5{D6wSL zsepXbzjss9J_-IcplSDoOGG%KF9H_a37FC$VCB9P1SkVV?>3k|Ad3jiThC^ENT&Eq z*^ZMBpp@)Du(NKi{!= z@QgNjV?8VUm<0Jx?hu0&tFLXcrJkLHB`6c&T{{M+tg1KgCrO=FV06(Y&#Py2C5iMN zkJ&_=)W5msxoK(K9PB1B+`1OGFgn!2V)7t|SlHY=5>0NgFY`#>K9fhe_7;RivD>CEBB9}Xgz)L} z!skuLHeo2*orPV_!z1e=d*CzjPWMvth+r>GrwSn~Dr;bmHd!(o3O^&Eg!-D;;nifG zPfNQHb>-CD13NacVV@HdrJGoEKJI|fO-z$dVxnu&LC5d(QjX!Y$rtN9ur~ira<8nJ z!9i#gSL&kpqX*?tm9P!@BvIWa->MCr)h0LB7UH+5mNn#)0MBsrCCk2JK3|aeq=sp~ zz;fl$BDVJn671J(dth|uFpq+T$u@aMBdh*`#0M}dzl5P}x3D=g^a%_k(BV!jVuRL@ zXqsBbrmP_n^{Hc3Ye*WM@;w{G$WU#Y98~Lxc}!YGl17Lgd@5t6P2SXKo;hRM)oI^O zdvw-^+h)yr#`L~7Z-btS=U{jS?F*e{lUFy+niV;H`t-l7@e-e)bg5{Zbx?@c*yN^0 zPmONd@I8g_TGD8qwQ2g*>HNMKp&1J6;>Kp<Gl*{K53lTcO< zpr?m3pS2|H!5@cX`S@Xamba!C)4kz`bzatKEmjCuC$br9G1@6DWXIqdumJsz8jXz~ z(-|K`D*}&oeaX&k zAWdPNEbHnYw~>GAm`ni+a{kk-FtJl$%7nNHes8;CLhvy78X~C-Im$x%|bGZ zhFMruAxZ1}8t%9@d8XYVd$T9LA^ip(GTrVw+T=(_$LMS8?vH*Lf}Llde?uOPi?I8Q z?%d%pnL~a9>*5Z1JY06!cp4AbZyA?9N`>SCG(3gat#8Q2ZjmPJkclHS-e!RY!tX^^ zypcRXo-kb7NIoF_Dr{I5#go7x|JCM@v(f0KwOCqCz-S9Y;PW89tbLw+Rzw~c7LSKT zET{>p7LXdeH^>UU3EzjU1=+z@0*`}!1nmNqfi|sUSBl7*K${#=o3e+l4aGGGstw&E zDx7~U%lnSR_O&+(QP)m+K-VhY0)Ocg_ghx$fim#@D_(+q?AmuE{IPGWDjdXe$X+vk zn>`-M&FqhZJfJzCOq72)XeDS~H0D*HLeK=r-WZcC1)mABJ&Xf`53`c*Njh#BFCFPM z@CL@(pSAO-8?aO8ko&gYFxrsBc(`YP$`M3Zjj@!QBG~gfGW_AF#(~*BrEg|OO#Lk= zb1KF;nof8;waFj0-rmA}BYRMGn=G>fIx={0Ohah45dQP?eB~@W`%;g7Q`K93GkbaM z34Ua1l;*Xm=m=W-WA8T#d%sh&!w~QY3ok~SzF5!lipeV9HT8+fLorEjl~>oZ0eTY1 zl1fNCnakcOA+Haz*AL7N{y%b)ZoM}*$6HN3zDUBGBgZMn3x?&%o5>j8i3c2g+-4Hmy{i4*+@-a$4SEvHqDo0BZof~9@v3uvDTb6S?B`PQhR0dU z!0Z_I-d6HJxBuMP)OOX#_w2u0$UDA!&9@WWVRj{G-+~15%`Bn}3GQ>=n_zz{PcV?p zDMMebF|$KuNca!dWh;)!Ac7@ZNkF#;oE=MRliz3GZAD6JOt(|YGx3y$(YYozTTeo} zF-ONlY_b>oN>8$Bn2FbU2#ef?0&Ql`ZzHdJTV}9 zoAuX|9&F`y;ve-Rn$MhmREX<~J2nh+;lj>Occ9c5X zes6~Uakxs|{Rc8m)hf5(hUFPs`U5idF5C74dE?=Xx`(qfOK08J5N-0Iw%fVP&W_Hu z$+2wg4zkb>Y1Q(44n#{DR_m&G(+(2Rs})_u{cV)5C-LXkAw&P2YTONI8>2;R^-!Gp28pSdrjN^m|`#}(HvEx`B{nP5X@8BV-%}S31&bv z{S~=Tk+&*-rSKcm9;xW_l~}%2Vktz{!xZgDinc(}eqr5!<#?O?ne~9N-1weR%Zwiz zi;RWFUyO~|VY0~|Sj`H)XWgUVyVkSD-xV&$`iX+`t@Dgu8&9A=nG=G^Y!7tZpyogv z-URIjm4fm?3qUhK<3RBszFW2B=CRj;HSg<%a4*er&4cXqJ)|2AZeUCIkoj>{_2ql` zvYi#%4u#;J1$U-C2i%L`PS>Y_^IB__kJm?m^9EO5&!YE|w6PoNg{X69pFj~_|d9K;yvFw+<$eXN z^q1K4`^aE&nSHd6^b8w}X$zjp?QQZ&M=L61Iqrz$hcF6Qf)lF>v7`Gid7AVZbM7Mn zLHv8>9jD|t0fDQcKCt{X7PB9A{r}i&T=v;)3b!wxeSi3qirqS!t%BXp3OnD|R>fLz z(#x=YKk-q~+AQ|tA&hwRS?nT5?=0rzSn(2DhD zsUX{c#du7CCV*6+1kk3psc>-tCbI9~aW_ghXvK6KTc_<_y|CSlOR~rmsYD-%=p_@I z{4i@S2Q8*!TmtS5s;h-CaGo*nvOOfj`-SKiYvm+=1VI zmmhqv?5=QdQwM%S2YyWle$^s2+3q7GlP>DQLVm%7w6>8g{)NQT;~0AW zLc-Kmc}Z>GtdqF8Mx$6cwXF6R68Y?M8^4mm^$ej^wzjISrvO`kvA`x^AkczNs#UJT zulj(?ZF6el%)a9%gIpiyV#@VT*vMwQjC7r#2CwRf-1_F8u9D2aL0jw^$^dxZMtdzq3Z z@St$>!&f{i0-uaR_Fic%#lSxBX7yXpB=8KUv*&&#Beg?Yy_Hf80rFA}0`gMDv?ju- zKYsaiqhr86t>5veuIG3LR1DC#B73NH6Lfx925*(Swc6yZ?XFllx85|m;`Ic_&(5jZ zZE_*3ye93|CiY-C>F4=jyKt~#3QH;{k)CgWZ<)g8aQ-FmIaAoWa;*C$wWm&D$2b?= z9z2EFIM=;BVG4`>4W16CaL{)OoA?{F)y}H_vSq)K+TJIXgsk#0V60%3k1E-+DrW$# z@-lS6j(oH^~Xqm&mKr?emV>J9qHnL z?7@O`Hs*JvYjOmqvqhXc?MO&xCER|%AspQQ5Ry?-U89 z)WpV|BIA1}nm;t!(@g9Q0jqJ0tmPFHb0!O`B#GnC*WC{FEkeD9P=)Y33R(eL0$Ko? zk5IV?l>uG|=Uo*ROwf4qG#WJCrFt9)$GNQ@xSo+Fwxg2FdvcyF7E)84P40reG*ocP z+V*B6G&Y8Liv~DF}6vP5+BL6IhDC$1q59%1^eZ;U0)U zZz~)el+BL)MKo9<=yrzm@z2NnJ044XHVi!Z@Jl$T@4_@^a0{%nv888l_Hc4zVl(OP zTRcfqjxEkx^3X8gCEy(chKjaTW+S$|p;sn`6_UXT*7fqRJDEk*rF-(Gu7s%IS8vF1f>Fqz> zegx-|%BJBEHYgvoavIxvk>rtcZ0sex55(`mOE@gIiam-QVb6ElP~dbnv5M%4oq1ix zCq!_wUM2%+xtYzoj61)~%!)3P8omZ|g=7y6nu4Yaq97Y~)h464H$8 zSWqHviDVF8nBWT%i46qb(W$S-+0q?#tl%0MMAy}^`j7d{x;6Y~7{Tl6-^p23D22%(QhaxFx@8>x|z_Hu5Ihs`Fap z?Tv=on`FJ`pr4(+beLqd%JLM9Zo3xTl=nKDjXuC#P8p}tvFX;%S1uN#=2KDY@fyP} zJINt_4G!*Xc67#Xt%D^v$?EPC&G)7<#B9L9L8LOu)=Sqa@371A6{{;zWamBT93mOUdeXE6x8w`M`*SI zt41?vB*S|&cjIox>j&K3Zf%jHl{w28Kio@x3~>))3DI?0cp2slrYT}X$vx$5 zmN%ljx8$z&7Wqk(cYzA)2`UEHK?dLd(0Gr?tM`=Qy8JK7a7#{Uy&*49%HW6pm|Ejx zAxSjake@)0(TIunWdFK64%xpc?`yvy*CG42@b`0z+qnM}L)CB^NFzJhJ7)g6Ozhl$ zj`N1R-o}eN8Gj#p#cDu^c{K8X?Ox}dHtrnexFM&Zq{6|&H;FLj7LPv0P@PPV(uml5 z3VdCTLV<6}U$iyLKjXEPvSxgE~^qnN6cvOBRh#54tCY?hPy|8!<#HF$6L^ zbc6i$czvc(?&6MNA^M8gQ#^(lv}^{OF_Wgy8TCv*lLpYFdR9J@wy1x_{;w>X98L0J zM>YN;kR5szNgcIx%qfLa`Y=*%3beTuzA=VzcX8BuNqkM`LFQk2`zL8Zdq@g}* zej~y<(6Zl{Z6S^JFRf2(Rwb=2ek8NY&C?lUOD!^n-|>rRm^!cCr#MY{NJLDv@NLbR zi)e+vH}1n``7g{dZ_2Bze3AM5K?AasPJ*&5nZ-P^E;sjObh){Gx@zzVYvp4WIoV=3 zvY0jy?@|ZPR1Yf)nNPVoLlN7jw`G8lzrSuPRQ|@OBX~)hy4cXLDE3HR>>& zh-i^Fqs7hMiW^(chOMN@Z1TqlV6$`#fMw=_j}c%M8eEI~5CSBZ5g}|V4=_(X;~rm) zg&kT6UmJ#hH{|!R{TjRlchwf|8_n`Qp|h#Z%$%Rl3FN%txmC0?p|?zij8Ex);`_F} zL4FY9rdz{ZxesKCpV3&gr>SFRYU-Hu8C}zDosB0D?zm-)EhfUSV!}R+q3>;$z;G=!oE_bBDyGtmZa;F#SlW5p&S zT-@Xiqsyo3C~afOU(f;)XSnbMeT2}!I_Ar0P@t;rUu$A)M3boM+4FaBmyLZiX1rZ( z7{=%lFY>+Nzy^9%Bs*XylOi1#>qvxq)Uwz4fn)Idx6vl-vfA*3t&>O1;o ztOMEath|6R5Uay=a#`cF?C|VUJ=P{QR|Mw#9#rvsSoWWR;Zx1Vfv|39G$d}KSt_5L zNFvMyWks^>CDg=Y+f0kQTyb_TFv*vkKKMP)o-3t{r8CK^>^%6#b{>4W-Cx%r-*j5!@U|wS7c^S?-GVRv6lsGN zuVP`_=nT?>eYA}(BC$Fnz%?29fQ_${R>XBcE z6)2|85R(mAyQ!66;y!gR-9mjDnR*}M%5Pw?Ky_||S++YY_zTn!kn?>ug`wt!13?1ko~}z_t~)h^a}0Q&cY7R#e6ZL@BoeDPmD7M z=&qqPmROBNu63H_$#%1R*jbM?a|>Q{F=L;UTmA+2G_HdYjc?e4L@0ZY2sxlb5*v1q zMzMw;si$AaVj}!{qrLqgXtvAXZ+CiNz*+MS5k80ZV^D-*iSa9g?q$d?fGk=i8vjFz z_X1e@LmLj-@3OFq2a&)q806;m_C`>yJ4mh)#1odEq1^{c)3TUDG{)~3{zCR+$XbwE z%Vr*;`Krja0gDXuCptk?EowcxfK5I^6I7F1*Dqk}I5)I4V*xvNgr=&xx1N2AMgIcM zWX*kxE&7E_A8yBTGM`Z9t$g{X{y=n z(mb}HoE}%H+K;@!G{4bJo?Eee75Ijs`8V`%{?sXs5epV2leP62(w%&r2)0@uzR{Z)-+&^3mzLgcmz0Z%^^aO+haxpR=j9z+Pn@PHTHGz zNcO}x~A`WNb6ZPxA@d99Q~* z4eT^x_mAihdx5<>VlVViJV-4Yc!Cb}Z?WGkLY93a4pqjv9d@%*$1&_EIY9&ck96>g zw`<^4sbXhN&~X8u+f{ce$qUEfqG4O5Vna{TIMp2c;n{57Nq9Zg!D|m55-VmCp;=^S zxL2q>Y&P@z17335-MD7K%QTA!6Gb-f4|tt!TRMxCaIfMHUcKNo23~t9^Q(YYZrhca z22BOk5b_ZFGy#g8!YF4QRR!XcGYbO8CAbvaER8v8lw&MXJxF~ryV`TN1% zn#`$P;5h!yNrqZ^cZZ(GC@;FN=fSOSH_3Ahub-y92u*KetNz3)0J`~xKj~_fQO>Mo zxs@~p-!wf@NqamNYw|ElUi^U7HZ>X8B+s;>Tbu1VVr7&gYSFD_;)qp7sQZXjfx{#R z8hZXky~x!0b$m@7n++P*`5&X~S?8&o{|U0R)DN{Ti+-b z)e76J2f97o{h4=@waFe;g?L@Yh^|hEx`DUEjPjM*1Z-LuWfOb<4DG8`A>epU(}_Sc z@i0Lop2~qEqx@s-?E^<48AkbVZ4_4QOS15!1987jsZWP2POkS3MRdAaFC}x&S^I_# z${1A;nOr68O(wIA`stw6Rx*E@yxjQ_0Nov@PbGJ>-RU^M)t6O{-#>ZLM$Q; zh2J_*oSVXc^FU)jrdNp&1>`=5z;6Pr08IuZgAzb%uxE6ZYJ&AMcpg18{j<8IcnuD3 z@)C2B`)5oEsgVmC*Ze~RqYH5?7cVg5jh`R!4W$+%KkHZ{=Qq~LzU?y+cs3{%G-L)# z`iI7Zl*3=>lNeSb3(Dah{`7jfwNAdw7sO|UWx;J9cIqGr+KgHEMrT6HZNMwosSMZTnh~N+23akd51dXuceOKV^0~+JOYhGas&d~+xT@7?? z8ZxsmnS54*)5gqxjz;jMy0Cv~)PQ9Tb+XfO9M?Y)VakD#z*x{c@K(?YPzop{lP&m{ zo+DQF?0MP)=i+9ar#(Gt>+58!j&aRN_T72wj@7O$`ZGz1g|@&nnX5}^ikd@37uk>aR7zM*-0 z+wRtlyS%9bf3X99x&wc-1HZQeU)F)&*nwZufnVX`A5czTFX>>IcZ0=TqQg9fL5B}{ zT%zF~0btb%>)p_dZLnWLeH7fGJJJSl1$U$c;O25Ev5NY$v?}ULm%PlTRM9Z) zCW{c|dk`PTh|Ka*{l)8fBQ&E=WqaP0hMg*L+X`VDe5$|O#-*rJwO*nUZiA(1PaO{Y z@;EkF3im|c(WmV(+{p&GSLv`VG* zJ!|C2(EiyYOQSB(ml8(TI$&8%XDI}zhwkwpDCKBt^M@4kt|u*t{iQRRCtzr z1yfKvd;1FQrM+kt!jr9iV^sz52Vz3!jv4^EeU0Prk=EP1U(;RQ2mJlMrzyGPd#(fD zgf|s#$59RbzHmJcR&;PU(t+P?#u6}%)-LT3*#hwQMR0Ih2m3_u{Kz3b&7ka6Cp~em zd~E2wvgdvFd5~{s)(0Po1+P^aofBV)y=~~oH<|A0t2^*D9sDnX@0h8J_hlmJzD!KK z#~zHKLx)`P+4SvYQ?4I&^ zpYCHG7@LE4Gv7(RCO0&A7rc?R(WqMYX&;X-#e!>cb;A!?oz`tO?#ueuYhPt4h6~40 z3npZ8B`)RuIzhN7;Jdoe>A)WY@6A3l(bs55;~Fzfr^_1I6f=@fz6s)V90n=d@>%L_V%AGp1$aL}eE?^UD{AEjS)WGCQ#}kL8tF5XdZx0~*Qtgs zAI;8Pr^|faven8S7(?Z!u$j%Yta}d|!CbFK84#VpMBYA}Mc<&w4>Yurfo-R1<;Ae$ z!!eFp`5Jrg27Q3sU|-yzkCLAazucgsDEXRoxkX3P(ot;uEjqsYZ&;+@Uj(@(*R<8j zsn)zF*sfbh)(Z=&wep$P^qr0;kgqJ`;;eGjf}Zgso|29Vt;2G+iJrj zIJa3z3wpnOmPC@G#zYeO?QKAf#B#=}+POD5Iw6$=i0PY=|%;+=aSPpFoYu`%pG+UpVw6CHH1 zhpJkpCc_yjt|D~q5H>?4#?ZJSY>i4hO6Nburg%V(e~hj25C`x*oc$hRq}pP;JK?>G z$v?Ao5<{5UQ&f>J*@K?q!@N}`dWw-`p5awbaT}qjgISO_VhSD1#&B#I#1?TZAH+($ z#b{&#Cs8vxiBYO0gZPT0w-`X0*~^{8hgC!FC@NfvhJm*|$`nP+CMY_Nk*~^28*b0e zugSH180&sh;Hq3w&odiF80+OD_9iLpNguH%8Ngly3{~TE$sVKCJNf=5h93A1TM@1V z|Ii-}I$XKmw&srQ>=jn#BL>m^SJ*M&1BL(NJDXkVjC7{3HUNw1Ec!f<3MVDknHbq; zcF|nLNMmegu@`N&@VX207;UYQFR;%#Bb#cr4dH0ZWp=VN(m8vXnSew0U%o$2tMBBg z@PBN%cWk+r*)U&mPM2u--0sSTusyz_(Qo3fcU4nT59^_7f#S z$C&X~MPeL{#d54#JV*B*WD~oIgGi;}qi*7J6#sZ|;Q?{0=bXM6fkhft`-@JMpBmq` zpUJ;-k8j@V|1xaoA%=L+L%R$=hKSp}9xg0VJ~}b-PyLv2V(Bw2U%kOUowo!B9YXOC zy&^Ybei75>3YJmUMvBurM>V=O?k>w=%-l@+u&91$p7sVdv7Z=EOBz{TKhfWFLm84R zHk9-eGpTdJ!pqow9{Wn=oI;q+s#^%ox^J=q8S#c-6gxIb?=_~?7bhU1X8->Ga1KIne0 z?QWymzsKP0FJ2^kt?v=>MS`z7r6fn=WaeeOaLeLj#RxXzQ8A2OF)=At3}q#c;=V{@ zKRqhW^3B8gw##*eiMwj(48<#sPA+7-V#V?N5UbBXaf)vQ9OZ{KHE~GgM(o;uxy780I92aU^8E8Bcl?WV%w2sWs1z4%jjXwG*YO zl4syJ^hhy;PBgQsk*I?dGqZCXYG#4S;_8>vu%=rr|I>C=9; zxp`Wk#yst^+`Vn{Opoial&p4H$d~;y12wZUFUuZm+f(9$0XEy_*F3Ig2rhHft68sJ z#+eaz{wXnnTwp{a4(xrz7M>j#M}|#!>V15&tm)J**1)1P%`evAB6Xk3@=o@w23_m- z>@|(p>%l5{K&{H0EIzIo8t7sES;&dKEKgxyYsB8#D!F^DOWjGhERV92xWEDCQzJ&F zp|4+omN(2kd~~9lkGDTLI*LC7u6xtV@<96&qXoQnDeY#Y1Pqc~#LD)&+>xv=Ek zvFu@Wi0Xke*1>4h@fzN&4bx;a=@YEa81cdWhpmHWdtOgb@rKL4*SF)9mRByz2I~*6 zdERNxv9HcWbKb(HjuHFfwU-aah=ZwQWqGM$5IV9qtgI|m3=l8lXvvK+V*kzo_{gDC zZfv_EcViEa6-Sar_S#tSt>KpTd-G5wUv2+krswro51u%GVdSyQw=%2bv+e(zr7E7^ zRPh1wGkYu*xzMwhQpLe!4f`}z>_t9gTT;aycq#tZRPm{YdOIE+L)P+z*aXM_mF#H8|0X+}J^8Ho??Y>C zcWbB%PQ2k8qpeqDoXr|9jwab`;dt@o(FyM>W0_|>H1F$ib*6e;52UY66;@ZtnaaBz z9XrS>?3n!D9b}b^s(DU)@WCYe{T*bL9M8r+C;m#;nVA0sF;=_O^ti^$EYURVDPFen zey#4*C}mmn627|KN27u*5w<_ABi-68w>AaZa_g`=eO;COiX-7pUsr{bOpo8`>#8sV zx!c!Wl`GLsHF%mhJ!N9OBI7{U1aTDU#kxH&zTBgv7z+&>@HoNaVLoUQ{#R!hD3*Qn zJZ^&B*VvEG=p%%~ECp;b)tl9=Xw z&Y^--bIGvbC2=nC#>}kjqCBGA5b$5IjQZXBR}fC0#}fg6(RDc2aQbD`Oi#;M{QnO~ zz%mj4q&I&fzfVh_!1%?PXN zkyyt?ia47wo zD|FHh1@)5NE#Ct20KsQn_81_SC%EO^z_a)hHuX7I1_l7_53xu|4C%xZj{4W3^=D5@ z7}p9A*PnN2gMkqj9NJjmSs>5%6$N#Y9mt~}aM2wdaI+Wk&5>v0lFJ?oEJ3-`F1!3v z71T-7fINH)(1L75Tybcvz+9k`&~h-moU0YoOZjej1F#m3r9d7q@hb9-3>E+*c!sN8 z;ZlHckSl-_fSayCztvMJuW@K|AlDil+9TY4-JxCI1B;qKp788XeT3rECfIOhiEeG0M2}J?7B4WEDmq`_E z(4$}(c9*?eL7f!paLJQ^JR{mtFri3L?>6iJ2173AatV>_9Jh9tbb=ElR!MfCH|#0` zxkQwrIO+?fcm?%R97oj5dbeDtpiVjuJdaBdL7mzbU?R}J2mUNB6%4Pc6a`E0jT|nc z%0%E6UnxuFl2-ziDpq7vty^ye@@whu;ndb5;!vOkm;mJQW&l;laHd;c4iq4-RAen0 zo5JX%A|TJ19%u#bR`hzwg*vIwi$_>0`Fgt&oDJ0L%cS!P>Le@hIKstta%#^4mjii* zs(?Jccpq2Id4Cw!-4&tQpQB!CQSci{{h%xSdIj}Tp@JpS5g@;m=Ya@-oCk2n zZ>1d^i>XxZmNOr6$vGVL8>QetAW9U^5&l9CE~}(qj>u>-Fc}$32TlUc<$5&Y9d7%6 zU^?^_z_~zc4=oRP$$U13)+pg;A)aF5d9LQ^^63A=DH`u9k zFj>G#$P;?Iu6b^#tE2_M0l2jGK2B{KurLf60It^#c535#qDL9xN;ptKom2(nRa^ZZ zt}m7L$GhZ1kGo{uP>yJnPq;3bDuK%-QYMgB?P4ImltsW$#IJsm+wp7!Dm|Yf>m+R| z7#<+Q?NIABsD>dx8IknEaaRN(gNaV98WGI~@(A=m9&y14R5frvFbh}(%mJ#BoZ98U z0l)&_1fUMM7`Pi)2rTc3`@b9v&si0)0)~iWr`8w4h!kKja5iuNFdvuz+z(6zRsqw2 z-cLETvw^X|#lQ(b9^YJGE@T}rkNaty+9KfkvE2T>l$y#5qTMFV2V?1lJ{@QWCO?e~ zgkV(#7zdm^4gqjml>!qXpLNT=&p5RykW^l`z27TUi=u14L}o6_ER7y$X4&kF+}r@xdNA)755@XSwXhffneO&xfDROWFa{Cig)G-bK{_y_Yz( z`+@kk2t?#7rM$--iFAY`DUsrq;u^wU1S|!He!%?#q}pZZ@gT>3hy;K+E0AC*#!DPG z`$)4tc7?Cxi1^Z0@^HS=p-=ezkNde`6&%Y%DeY6-Mu?y)k2|QO*wq{Zq@vH!l|pX; z+JQUPK#v5X7!V2MD@dglj-?(_;W}41X}wch4ga}cA%2}G6>!|_EvYxS?B~BmNqNM_ z3tbK^K%U_KjcE1ImwxNgR|2_y{dee^!|?FfbC|^ z?TMwhdti^-;nqU%%63 z-_H^HSc6j=0e$Fhmp+jr^!hz`mOx*)*QGzi5g94nkGqKL54iN&L!2q`k}MpzhDa?3 z(FoyCd&uSB{S$JG=l_l)xb2`n^b7RJSkzIcHWPCCuiQ_RayV{QOBv-HeWl|-vK8|0 zkhca&aVI!>N_i)N=+M&s02ZsX(pfNg!%Lc5!5spn;M1-IcmIX^9|`ErK#v6d|8|uq z@hoH{n9dOiCjZ0HSJDG{f(hrj>@DemyvBB1;Qk&`C6E{F>_wMs1s3!BKlYMSn*qbh zDs&jYyvr^JJ+PEVaK#+~uo7}qwd=M@xaQP0LC$SJM-+~SN+UWNpt_a&dr9#e(Wn>O zTs>wbkl)toHjISem*qrb)`H1scO@VJ{UdPu2<=La3xES4=Mk5z2PQ*K^k~;+0^>c~ zl~$brTnu@VmrIs_`H*wGU2-9i-(?Yj=`$I;Xh#LSz&L=$5u00OD6vO@W z(xgXS`ncG3?Rn@c269;?%^vKsX93lb=(>Uaz`4Lc;Cx^(FbBxvkp^pBhFrH{x!aHj z7(guU!0TSJYR0T1RP*Cs25^1qp=E$G_>wysnR|@1Mu2Ae4=POtuS>1Z!37%ky z6b$745kT&rs_1oEX%ZMd(g{v*H$!2Q6u;qBTAU?#8%cnH`6#9snvRsE2GL|n>#T8v1*L_u*Dm;m%1;mSx9kY}ux z%f*=a0OMe3+;PI1fg-ExjwE&xtKM)bhhz;YmOoK?VFpcTkV9GuaP zsU4LPfH}|~(SqR#RRFo;{Hg8Q0&W2A1_n+;4+qp`B0^l-DqtQk>lN66hk)fkEAT8Z zC)<_4dLXaS9Y9_qsWaQPMNw!QK&~%Putd_TX1N@qfZQ;jBd*=@*)F*f$Rn8W8f0|E z6&z93i(dy~Qn`Gt%g?GH=KpVSd84FLP%j-(5RG&m*XyML3VtiCbjez2y~1F$3*-p} zzKOmYIZX!!19d7av0AF+ zh;;!K+7`bJ=PPZjOiGhnvK`2mYJ{cm(|-SuWc{FBdnvbvRIq|aq>{Wp<`L+m0UR-O zn!O5G>Lp2^y7Vg*{8sYK10kY9?k1w25hw0xbbC(K&U6A+}8U%BL>4X!RWqtF#$)JAmG zh&Thtd&JaY9==dYZQlHsCMp~yPP|8ToD z75-_5acwfdtb~GBxgIzhvIQssqkeYjlR09wOX7$c%6IG61Lwo9!Y!W#=0HAw#O2on z%!j=A7s$Zf9Ffiu;BLtHvv8Nf7kCJYtY0Bx&=^_{M3wCaRzUB2jQf>Hu?nI~=2%Q6 z_3uDj>y+cJ`+fqDU%J^qUK8^b)Jgenxd6BW@f87i2DIgFhXE&C0XXsq61j{L&sFp~ zDOZuvHr#gMq{}}T$ofpPiBu_an(S1jVpfdZR9Uj2+;`m7!E6~#nH2F z_3ZFkF$SM-yS`R@i2jC84zI&TZLx(#uM;Ebdloi+oj8a-ZDGsSiQ~1cW~^cl7kE^E zl9Qwe4N4MIPOF9EqUv;4;fxpo-YKfibrqJYBEfH0sjhStcB_KHSF6;vuEJ6e+6=3Q z%G6a@<)H$f@1cTzoks$EE_$ezs)ZI0)e5zc>zN2mxudpX#t$IOFFB zzSd85Of59|O$L9wE2`wI8b4nAn!7J~7Zet^F6#Cvk`M-CB(D!--S+J?8z(M*I$(+1glboVQ6Zv{D1UM|9|w4{eSem?&`bV zlYmDZ^VuMV#ayoCZTNO08YB!wrcQ9(W>gR|#T)1V)kGl$i>o_p+2jp4z`FK!7~VKl zKK>-oRd`jO z+ZPkMMP)?Tq1+Qfy^)1|Ee;9TiCv}J;ci#tH%2z+YcYU+WMs>}#jcohZViN6P zWVg6nU&F!*(ei((F^nk`j}h8h%>p)xV*<9gwz7DF-jS*lAq|;WTg?`1#9j1OHCwk4 zx6Sj_Z0|;K7=5ssIlo0Bj;k)b=Ef6OSxgc1JKgw|8$WQ{UvtaPUuAp0MW(vB^rV$_ z`&N92slOHd*tl;+AMzU;0R`En=*e0|VspP0<6`bF?~~Y>bL-Rr$Z`m>9`8F|7ze)3 z6+=UjIEB!ks@S+q$XBr&KdWL(HsMt4)GAiCNel{ls!G|6QZBS70{WpB3GR+_Fjefz zCRCP374!LCoJ@-^u^HcsCDd_|b=Tnz*>aIh(}@e{V;9*uE?>LAqKm~<^!W>HZ?QOp z_PW5Di&4&h&a(|A;tcwR8#|q6_|Nwt^whs>*k+tC_~Ku-XfyJ$5Zj_;EB=|Zopc_g zI^-!-(pw@L)6{=i8Tae!_Ui;cEVObzpE%WF4uZutW@ zPCduUxcx~t_HgORO*Wzw$AM3=UL3Z#;3Ia5ORp$ENBqN1ZV@Acdi=waR&o=_?3xaK^M+;v~PmFQnCMXmW?Sxkw0rh7Hm~&OXC7>2XO$oM9(9_HZG&&Q9pXe)PA$m}VPJ z?ti0*WT6Y5br~Q2i`8rs7tt-1Y})^;?R6B=bZcJ-1F|c?{b(nw)NOQIZQsJ z$9~|5Ie&T|UxQCPzD`}RpFTo6RTu2%(Rempuzw->Wf$x=iQ_KVA0ILQ)3f_s`{f36 zy4}@aMtky|jAyBi7w9<3iH%Wv+flAY*ShWb4V+e|y6vYM%$Y`Qz-&}FQHewk8+j7_ z?V@=3ir<-^n2TanG&_7Agk!IH(v(9X;_KuSW#S}Q&r%4s-5tTw*rseB(yj;i0UG|6KCaTMR^cYWYkJ|n>d7S^sJ5IL4$=2!Y zUME}PWIcuN*uOkx&Y7OUBMlR`ZQ?uj{~a^uO*ewdVywrMm(F~2-k#HF&RNv{L#0jy zp0jw}=`delq+8y=80(zpFvbQl$4U*%+k4O3FEw&{uRU+S-blxnowwWY<%vIU-;{iq z6WhE-Jlb=y(|)YUoH@IxlMP%OnCv5xWc^ZVSU=-8t}^9Tb=uE2v9n+3w13vbL9ndT zZfi1MpIH@n0>$ar1L)>kDRho4dCvaPTjrxFc~b89oDzyX+R9bg%qtla>Lpl7Y7yn} z=j@wrnKz$Ve0FSShfcB5)e};N&E0?z)}!hvY>`2>Z{S0A{OFl+om>>2#&+Y3J?}XE zZa!l_bDa68NE>@yM%w%fB$nBVtQ zwA(MAAfM50zbkqFX*=cDua+Y zwU~2K8rp&n(3HS}PBv@RJ?v#|_K#ZFJ^5|+;TA4Wv)b(6wwV8TET@%cJ?-YV%_r zOI4>i+5X9S^Te_5ykoA8eYNFN>)3yNVy=J4ck+~}+Tk2@!4~L&UbqbXa22k@Em(cY zRK>6dj>2V7ADAiz1}MJ`s?AiPFb!fM0aju!fDN!2q@B`Jrcdo4Q4agyH8=!~klTw8 z_CYZFnW2d^Dx88dx;(xc#pgQPoPH-7&Es$i6U9U(ru+)zWL{a}zU z%rw+Neg}Ar^yj+ouAlHcLtTUg{1)C^XQ&nYw!ecP(tVrX76$xK4?hdldebG>@ zPzA0o2VK|0;{!=Y_eKeaHH2N_fAt-_9FP(IQ zY^4SNW~dC1&hMvOI@m(|$X}1^qFLb7p;^UybxMc>LB=18Xc$9OZ~^ws*gLRagRu(O zbFk0YYN#}D?b6`fGp3S8Bz=p<2Xy20cVa&{K2T@>@1vjzutV5TGX6Xdo zp`g)UGROv(+oj(8)cmT;#C2w)HfJ{D+ zZ~F%SsSK8dc~oDhM-7pdV4>?qv*1^pxA)QY%(T=&bouD6wd=@d?23P>4sZhtUy3mBGeIP6zg&3rACjHt{~X)#$dNS&w#afm2rokb}Jvo1Ze* zUNqfjseNoJsU(~2vfga6Q{4TvoicH=OJeR@$YxG@*Evq2iRA$E63alu$9B>Z?&m}5 zw4m$p=}Ojxdsi9>YS@cTDz7A#T_+9h|1;`Jht9l2VVB5RgZa8$ySf5y0@|4d;X4R80T zj6{#>jvwz}aSZ#v0tW|h(C25yTIx}0OK`%0vzYflcavTP!%y*_9@y-?Y-%|!Bt5NX zJe?Di-W8N?4@z4X$Ms$}?ZeR)osp52a&XnX#i<9C(C|MV75=HyV`;SKeOAAGN|yBR zNf#tIv*SAIEk44?ArfvJrt>E~s`<1>Rl!#1#uoM&0|W`;!?#8pGRe4M7mh04q$141 z(GZnx;AmDmrzB4NP(s*_-VD zzS~SVL5_-Xw1H8H(^%47a!-JeHOo?4e}xlR3mLHfI9rJ=ltwxV!r>#f+Qg|?d>wz! zRQt6r@wb_Q#vkVh&cjI*T%qzHG{eNHRVr9?;0;FOhgvuQacztY1{sYTdeIe9wi{N# z3TUM~pDmaH5~M@r3@*tR)T44LM$-EQFgnCY;2K;3KXgDJxS?e>Q*skpSPFTK?0aHA zai{ES;z1gB!&Uruv+G77k=FdoI8fJQ7VZ_IPbqV2O2&OxpY74c7CqN*1Pp zMszxd24nrElBp>o)>4v>AumDl<;;Z98 zcCC}VI6SF!En1hLb;q?%@}g_hx}#c`qjkP|Efj+Y4{2eI78YyaYg#9H(N$~Reyyw4 zx_w$FdC~3Bx^k^+*ScMfj)#@CaEBId*TMlUEOmrR^5Srd))i}AG~e^cF50Yhk{4Z( z)@{%_uhy;8I?0zm?Bipn78YpXIxSqSg_0MCE440H>&mq*N9!anx@@h>(z-^iTcUN6 z7oAshGXJM*VZRopYN6ysm?T1U30fD5P4aWKPV%CQ)w&q1OVYY%t&_ayrfFRSG3#HR za@4{pS}1uDhH9ZjEV@#yQ(7l^(T#A5N~k97@8g;QeITJoI?m}Pr?VTn&_!KhqPxWT zA4Wnxg2{(s2~r_vLIpLK4~-viNe1~yHj8*UxcU+F!5_&A(69ZFY5fZS+$*5s`hhKz z{N8?EFwX~UA!{!CKMmtfjIxxP`=K)u>54cpe}CtWbSN9Ue5jCgkxt7xa1UvBTs6`F zeqI1xrFTaVTN?TG+J8%s{|(~bM+4Hq)m%Ea(&I`PzRVmAVScs>#Djr7k+|YFOxMI! zkO_-n2(24(@YDP;SJY3?4>GXdl7>3!FQ%^eS-~|#{M3Tabre3pMdRxDCOt_{rBgD> zTe{coNXxNzG9rU5=Z;itt=NY2NTYxAr(05dd9_{E3jRN^|A+s;Xz9`Yu6D9(igZQk zqPsM&!HGNeBrZgC`04=OETxviPN;yrPzkjlLHy+g{IUMMmqWR-UF51Y2q7#|lEGby zi4$2&@GdIV3h}U&a`!4H$H^`laMz0aby~K-A=1);e(8X$zwhb*P9-mb5fR*@TW;k5NyG#R~gCiFWEw}IY=u>j53R5iVIoM7 zeR}ElFOmzSvzh|li&__B9{@lCF;fR5R-tE#oO@4xV zL{JM7WMhsjaQ5v08Gb~o$`|rJRpLm^x=DuOTQQZeK&h$3auNFM&YkD-LwH3I=`37* zjW|N~KQ@)Hg7*~>&*9Y!vOVNwWYS!9xf9pwxIxDqI(|>b{W`v><4<*bhnR;>)MWh1 zPtI;faxfB0hxoCa^5o99FU_~U_iQNNK&*pyXoLvvGdCtVyT;NndKDd%P7O-u2Bj6( z`hDahrdqGYjl@qb!q=W8Nyqsbxw&6NncOv%Te0r_W2NiMIw1nPtSzo`=9F^FuZ8>o z^n!d%Tq8E(o7_wK5Di_pE-=-yhar!F@^ePef9vv8CE?!CYw(gWb-<9#WesfPWq z3u@psI0WTT4BKG~Y=#{m_nyQ4+ZemSox>zr&-E!nPA1aw-FA9{&r-`PxD(P~s9ruH z?_-2Td|KKEUKrpbn!*-!m373s%$N`lquosJPzMocRzecGYT_>H$pUF04L^{c$^vQ; z{z#XTt|5J$IE%R6M^76$)8re-MyBgh$btcS*H2Gd=Fz~?hpA7dzd055>nYak zFq!&<7`r>gdMHe$QX#<}CYvHNtspb6)Se9DRVK7WAg#y@>kecj#D03AH9NU9gRKJT z3#FD5+>ZvCWT{v5Lt;6o1+QJXkmlq#7xpFSIz-Z(gl9pT6Nlx8xfKHGnf6VZo75h` zRI2Q+r&@EuWX`>9FiqJ;nl;tVPPL|lDW+KEVHIi@rZUJ}PRCqs^F}z5{YWOQ!M0|m z{Y!DN<8PQ`xZPW9_wg3|5Kdy=@%G15>tRX05o^y&v%YD>+TSIzVwvF`H@niTNZ!Tx P3$ey=F58)wG4=le(j2NE delta 86172 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 diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r4.2.0.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r4.2.0.so index 58bef34553025ae5318aa41e3a819214207a1180..874ecd143c3f983897e424ed48806a69be208a20 100755 GIT binary patch delta 82258 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 Mp3gox@$=jMANl?aCIA2c diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r4.3.0.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r4.3.0.so index ce69b52eacc90967bfe810e879295245a61fc7ea..51f31d3a6d496958219ac683b8de51e9a25118ca 100755 GIT binary patch delta 82490 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%0D({&$$K%LA}E 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�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 delta 86434 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
{?l z(%7jf#ye5Ff3#=82sBu!jsbX)pGDqFV4ny3K-E6bZC?WWIPU@4dp;g_1j5G%_*e%Y zrMs@^Eam`_i@GRRLh|7y9$rZJJx@IyiHPi`eJH$+f!7?`)HDp?4b@^9WKg2E+@18Bb`j=JcAYSqW=a9m-|2%0 z1b4-f6(F8nrkbd1j5q8&)#S7BNfca~s!3Bdv#RlxfcYObuMHY)mrQTe{*IQUoqt)P zmC=^6XCe_bp(d}2h|DL{Ia(+ZH`&;XH%>&3A(&3`T<_rD+GnQ+?)<(9bjkWW9V^`?TW0)qm;0;J zztHQ{*Dn~tVz)so+*?(Ht#Lp4lxK`0h(ZkOUuDD`R{KW$dtd*k8 zs67kRaL`)D8}ZoY)jnJ@W6pVtcb=#r^J`Po+Owc`8Tx7rl4^AaQ0ctjEyZ8d_~b}Y z={yl=qQ7`8hpD_BqskwurP#q|)8|ZV34Lo~i+R4C$JRavyU?wow1Ok0C@Xw=tzEC} zZXw@`s;nvhzA5G*Qa(=GQay51m+~@`EGKHc)(2@lK1EAY>#;F?SE3HhM-WnS2&wlz zIz=lyo+>ZXR#tm$cIA0rZKtMGZ&w}?Vc8XV5GCM`JP1CZR%F4Wyc*N-YIX3Zsi?vz zy-IZtg2lX=ud2JbN3%r&lesJTYb>O?WL)a*wPKIMNRh(F!q$H_H;dONSF@u>JO z0{gEjy;&{R6)^0uCY4ALm`HV0v|3B)Q=tdakR&X|waFOt*!j7d+vpD+ytZvbbFf6r znN~5I_QV4lSnk>6-$4os4!83mH7Q!Vj-Rjj7iM6lG)w}?26#@c^~B7~rzx5U_xIyO zZdENEh>2n;oj*fvN}#EO=BIy`Zs!AQI#k4Iec;!|jg4aNY~vRjH&#T7>wew*$x;mt^FPhu3(PD-ChqeUmi`uWTWbvQ2XtniP zP~)Mle4K6TeZZ%{BMeoq#;by%q?lSJHh!$Jx%xAd(PwUcf1^(U-m03^U7x_}6SuSk zE9(yK-&$U|2DWS5(k;41&jJVU*p^Zu+GDS_r&W~Oo>b9keO$#ht5^lF^EEXhhYtRd ztv{M-$^5>(59YZJ-nT6RrGUCo(@Vw9H9b`HK~hB>@vMjBJ)TPQ_hIe}^Z>PC9(>^`Dow-4Mh_eb zTnkJBP6I{)lYp4FVlAFZzHko!Rt=+49B>~n9GI(vqbnSV!1_^G55#T7Wx(Hof?ohU z4vYlu0`3J_hhn=17=uy&MYy-Y9S8S8xVOM9CN&%2E`&QC?uBp{z?}g16u8&HJq+$4 zaO;FZEK&xz6^T&Xegb7#_(`LcED?mHofFGW6343HE>C

78f;T?297qV_8?h8*LS#PdFB zq7f-JSq5X9M_d&7+tmLy`7z>TIgjtpMm=I{Li#JIgMhJQX!&tyefh^$p5+dsf<+p% z-LOgAlh{|P@4BZT`iqgj$tV-1v8UBP$M7|D`EvJNYLvUCA|&u7Lgwc#`@Qj~2egos z*_Q;iBb`1sDL?+wHLnd~XY$404pr5cv$q{Y~~5Y3MjT5qEM8rRu< zom3y`j%J}Ri;ZO%>PH$(ek=MGx|+09{daNmYULYea$sA?$=Ud z)tUfTsO@%29aFp1_KlGbCFT@7R%694%MSR2JL~%UA%9z7>#MdNlxgNIll$Jaz}%ej zV8=0RsrdSPeXbHODzDV?2Z`B_Z40_bebdQ5p#G!e&1D$e+TXNr_e!}K?0(o+kblvS zyQ*ik;6T-ls%FtnKoIr|xdYEOuJaFy+N6K7|C>1)k(1>${OdHI3Fm1&UDYNrmr2K! zUq$N>v&Qg0Kz;)0W8&)JyGQF-gZ9(>-KtXGGWX9oGMLmjl`VT!JS=~ZoKzNX^g>IG z8;Na{D~kIfY+vh2e5d|unJkWLN)pKX6gMmjp_N){3HpNOuTUm})D*r;ZBlT#mVX^z zZ_=yEACaF!J`j~xQe+wA_TAzp{?;PBqy<(}>0tst*WhmOZqiWvermhNIpuf%#*TY2 zFU7P7*Fo|Z)GhVCN3cI;Pz=QEsk^V*eOq}nL!Zdyi`VcXR z?m`Z$h@jR$8jg3gvc}{)VwdHts^lBt-$k0Fl|6&675RzSJ|R7(_JQO*%X`F3Q~MaL z>n{A`T>%_^KUL!bPYKmrf*#iVBj`ygDJ6C|cBwy(Kd-3&Gp%C`wjt!dQ+p&lO@dik zY0M`0d3Z7Ux*YzW(ST@O%W>!~>=`6kc2O`{3qMC}8~me5N#p}b;pC^2%%vaD+^~H> z+(`H)E>=GzFUw!3(=iG-pp|#P=~;@%1gl+a)x^C6Zzese6^MNr90qTu^5?X{Uhop^ zBdG5y^4X+5uxIHC%hKI*quCbc-!L@S6*xe^DGmAtjn(`VG=oOIR$f85EO@a|xl|lv zY=~USchdL*d>z#{7=6jF9R~=Uo2a0#I`$AWiu~=Qzv6q_x!SfH-FY9_;5;C`gp z1^61SJK(P$F-jIPpGs(-g zvjmd=2Z6s)@l4WqxGh>vdYxcfFO^S_pNg*u+zp>BZ&B_YY~P}#@LcSVl9y#1`2dZ} zg_{t!kkpbia14KBsYgINch-tTzoC(NRPZqN+sUsaZW4L<-`36WN2|s-*S6rFMkhG8 zw6MF|sm`y2GMpELvYk!Bp&Vz|aA=crywDEkOrav@TA>o>7NH8~S3*I~3qldjCM}^D zXV;d{Fz0xvVQO1%WChZlYcV&^R$Q*SQFW8EWh=WG&2(*LcW)ll&Rr-@HC}a? z^8#)=&Do?i4Q4sJwzfMu(_7micAj&lm^L|AV6qFGZ$fp7wAc~n@z!>;<`s&Ss=+*X zl2r?F_KvVyJE9e1RAW^WRfnmjs!mkRRLxSItGYmSiRwDl4XX10^`!X%w>lfQu^Wy( zqQ;}D#i}K$CshM@;vMGSWs(GsGR5v)6wzWI9*`Zjdx?lC6YLRmnwL8zZwPSh&aoUiz4pj|P4Ofj&jZ}?t z4ryl(ZIh~)u9~5mshZ_n+s^LTZ>KfHN)s3o~R5z>UJL^Z<9j6~rJgQo(TB0id zu1Hq?ylR>171avWO4R^PAFf@b8mt=PTpDRl?w6=IQgw`KifXE={F^1ICS5hd8Q7jt zAK#uyyu7^~*8HG4id2uN9(C?&FCE|Bj_DT~<*p=5HC#19RsO4uG!m^U|9nC?);Xku z-M3DPVybgq2YYD4C5kImbDXC-uu7pF?XVvE)pk&|NcD*7QPpDA5@+v@OliwGuOl-y zypy}#2CXC5O!fuN-cdB08D%Fh#8)Z|QVmuOQ4LiMSB+4ObSamd zQEG@*ja7|PjaN-jO;jDGI#P9vYKm&A>O|Ev)pXSi(G=Evt{N7oE>X=^U9P%Pb(Lz4 z>KfHN)pgGLo$Zcew<#8=?olmN-LHC3^@!?G)ne5W)sxOqojHkIQLIp{RF#)fWIqI` z2C0UshN*_DMyN)rMyW=t#;C@s#<`Vma69L9X6B?i-|TEZ?~L!lX?d>v+1!QGW@xk> z=8TUfw^4EjB^TQjZ)R6J+fm8Gcv(leoDvOI4N(nM4O0zQjZlqLjZ%$PjZuwNjaN-j zO?0cn8m5Mks$*1BR8v*cRWnpGRkKv*sxDAnqMEHLzttxjxJot0r406UYS^H09#kz-J)(M4wOF-8RPOIis-aZ%ylR>5t0L8`&3A*!LO zVXEP(5vq}@QK&;IlppaEja7|Pm7h-(dxC1Bs{E><*hi|4QI#JH6nm=bMAdZF4Ao54 ztnPe}Ck5uJVS(y$)s?ENRC83 zRFA3_tCpyqR6Va+rg}xSQq|(FP}&Jl4N?tuXeLxOOf_6JQZ-67S~W&BRyAHVK{Ziz zr0N*e6xCGKx)U{%9^<^&)o$vTtGqyUiE6g$a@AF;IjU<^*Qst$-KZ+R>m)s}S#_K0 z4%Gs;I;=u9$PYb9frF|=sz+3dRZCP)s-9OZQ@x^Ep<1aL(8FDSkZQ0?nUA4rkl$&N z6$n?2P>ocLR+XPulDJsac+~{eMAc!cBUMvW<>!;6{6tYX9;B-wLp4)Xel|%0=c+DI z%~oBmx=K}kMoHq=sLHP*39nP#sJcmYGwM1XY*T~$Op+8RP~D?isCrOUe%(moj;I!^ zmZ+XoEmb|QdPTKDwX%mE4+46+D-2Q%Rt-@NRSj2-P>ocLR*g}ORgF`PS4~tMraH2x z9uHE~kg7USHBB{LHB&W9b*}0X)oj(}sw-7jsjgAYQ(foK%tqBss+(2wRkx`YsP0iM zR6VF#q~Tty2_=@zZ^B> zsjgGqpt@N#Uv-=64%GtHJ*xXv52_Za7OR%1o)ne&U#5mDsuijMylN^v5u_Td8loDi z8lf7c8m$_m8mk(o8n2pwy5@hP8pf!msHUo>t7fQXs%ELqRn1mirJAFd4W|8r3}2b*dXwH>qw`-J!Zi^`L5z>JinWs>P}$swds*uu9c%UbRfMLbXyg zsE>OKLsY|6BUB?*qg10+V^rf+6I2sj%KRIqhLNgcROMHSrAsHOrm4#BFN-}xHB&W9 zb*}0H)g`JcRdZDHL}mW3Q^O|JeANQgJ*tJO`&AFB7O5UlJ*rx)TB3SVwN$kXbNA}hEKSm8Hs;R0IRnt_{RWnpG zRkKv*sxDAnuDVinm1>Uan!Y;!^VG0Tb%W|g)lI6KRr6JMs1~U1S1nR4RxMFIsamRf z-l3T?)hntMssVgl<=Q2x!Kxvup{il3;i?g;QL53ZF{<(-<5I69PBZbU@&hsANK_r8 znyQ+nnyxB87$fnSstZ)JRadFzsIF1Xb1UCiP{Rh*&8qpT+f;X`7O3t~J*ZlwdPMc8 zYO!jG>Uq^NmoopZsG&l&QdK@yb9KFHuxf~^{Irhv!&JjnBUPhRqg7*6V^!lsW&X#j zAwe}!b(rc%)iJ6ms;R0IRnt^6R5MkxROhNLP+fw$=KpdvtW;g4nxnc#HBWV&>IT(~ zs+&|dtLCe2Q{ACjpt`4@&i{jIC{jJ5dQ`PowM6x#YN_gZ)iTv9suil0s`BE#?6LsW zpj&kQho~V`HB2>JH9|E~b=a-$ibkrAQB6@zRh_7su9~H~M0KS@Giy}yRM)8%s_s`k zuUe*hMYTe;QdPdt;TpWE!Kxvup{kBB&4jB)s79(rsYa{DsK%MGS7)io|<{^hA*o$3bFjjEee zH>>8WZd2W%TA;c|wNQ1x>Os{a)gz)Z|BtGnShYm;q-v?^dDSx2E28cs3nW|Z;b5$3pE>X=^U9P%Pb(Lz4>YDzZ^S>I_scul+sJcxxgSS4UYco~Tm~KtF zb1f)(8+OqIF2h9!@r5zb!IqURI)pd6M3ZO{940!8E){*A_pe2B=vvX$^sML$^sFea*`$fC z;fbs0TBhv)=6}-{nYUta^75Ey9`jf9CFZZ_%e=iN%FBJ(qU)H)qU(7BN0c|1R*AmK zd=`D1N3f#raBdOZ!Y!UCvuA@Svt^?wvtyG(GE9ffqWjqAqRfVEqRfUJqRfT@QD(y) zQD#G-D6?U|D6`?9=pn{{D6`>+D6`?HD6^qhl-W=s%4|3(%4~3yO6CY-K=cx0K(wW8 zT@huJRfsmTtx8dOY4bL;iZLJ>VOv3>yx<%x`Uhh`l=pi>MXxaiL>t;xxF|0-M~Kd{ ztw_YxQC?My6)k5Bhz8kKyl8FPN)WAOTZy89wlz%DvaOM#*BJw% zzcL0yOKdAulo!b-it=`1n&^Z4Lm|;_yrGvNndST>qUZ{LD@*hVe`~Jjll<>yQC>h> zBFgKn*`iPLuhd0(S97H(->q6Dn&WTfh_3dx)`;>=vOG~<9$hEO8*m#$`O4fzQ3tQf zZIX=B-`Xsi$NyLt<<;G7qA&YfJ49dcw+ckp`CEHL`SMqxC{uI4D6iWc6s=|qh*mNN zL^lOkM@9b?U=@oV39w2;zY4HUqK>9t2Uw+I_$I(QFM5?RAbK>wx*~ckz^V}aHo&SB z<-inB()Cn8$*ZRtlssP&RQA!Sla+q*Tbw_3|7_KdCo2n1dGf_Z{)mx3=*>HREcC*U z1x9|Gk>70OHyZhMMt+TvUuEQ%8~G(hey%I;aIbZy;Yc^~6ODX|ksoQ~6ODYlk&iX< z(MCSf$cH-&2sQG-Mn1sES6=gW?-e6|-pHRc^2J8}h><_2d57$?Lc>vDKhnr28u@r5A8X{JjeKNH-m&V( z@ETb4W2li2Hu3>RzVZ)a7Z~~TM*gIcFE;W=jQl~#J9OF<8jb=Zzs<;RHu4*d{5m7Q z#>lTS^2?3<5+m=JYe1%vPdD-tjeLrcA8F(hjeNY3k2UhqMn2M=ceoB3;f5pB$Ojww z03%;{)z}3_{=AVtY2=HI{1GF6@T$XGQK8`|F!I}s{AMG+(a5he@@tIzDkHz#$S?8a z9ZbKu9w47-6=s3I-RfTg#O0=VROYDamD@cU*amN z;)y#?a>wm)#eM!?;!akDjwGnCCUCPW@Pq#nSX6cXS3heVaRJt=tmyM49jdC&7MA;2 z^@ar}2W?-FxG=H0{ATB^qrb-vZ#?m6IvvwhjdH5v;&O{)X(5+?{9r%6|G|*kwO!W6c(ybZzmF>k<=Cc6f&~{x&<^Z$Y*5_1o;> zbu+62lVTXTnN`l;Z?hvD{$)W|{YvajVP$F8>{r^QRiBn_h}#~V)Ff%?)`?rCm4jy@ zT2`0uKNC3x3ZmNPH~cKjT>K`bYfBLa&b5YDZL;=XX2PWyIpdPtI6&XV-wdnfFTYWh&c7ts(T<*H0!wak6>E6a zuk7emzx-Y2{hrxXTDB}|#;nnw(a|50-XU!yJx5wV8cuq2p`Y~;Uzm*`AF@3-CX{s^ zb|!9X{MI(EoD~zc+l~m?CA}hgb&=YLJE%X58jDJ;c5kG;kP;{S;k>mTi~uxgvkj=)vd1x*R%V zwYw?l(ZmJA1Cwq|@{j1jM4vyhs(jC-Y2BA3-d)tB*zSIFQDFB@MV6y`LRnFEaBsi2 z{oj#c-?a4l#r=z;lKfw8b}{GXLl^uOg_bk8SwyOM(OLr$POblR5Rh3`48X;@SVp^gVOfb5y(OSa!=uB8M;I&{SNlA#>BWQ*@98KSxLnv%6BIk2YWZ6j`RxQe!?X#Wv$CH=L)fRcF4 z4Xr6!o1%SdiuTb)gD4q0qD4up#&~;QO+qH_lJ%_#B=e(a4DN2ea{gbat#h@Rf zGAcGR_3}yENMqp#NIS?EkoJ%!!m~+*Z1O9Mz0)M}#j-$u7U5p{`tPUFym9*GZBEDDKd(zt7Usc|SHQ7~K@wgP(QC)pn z3T;EB*vl8IPfNkYa9y*gtK^jncGMLNs{H$G) z|D~e(bOak!Sz(tozaINDmvhe5$#%z}U(T1zs4BmB-nl2)4(oaMAO0=Y5Ar|ezABBc zm=eBuQ1tFP&6Ari8{~KNwLww4gBax2?!C;>x?9_p)%nO-ceovE-{HJ%xc!Y|^!YYP zZNs8i>o`s+pYP_G<@N?ivp9gyqSqHC_TqRtlSB9m=caXAl4zB!&Td)k*KK%F{bH+I z?;^WfLfMDeav-ta_IszQ{O5DWFZeweR}#=IrfBGQfq37|zITN9Po!3r|9Gxb$@Fek z*>lKS`@uk$g<*EC*2SE<;#)&UZ)Ihe6UzU{JO&J&~UCSiRlgSdF?dZv7E{iJ}TW=TI(tjzW=8+_H-;C7BS-(QbO z>c^#cWYsYKHm+(qA}%Sl!au~n?6#_?^j0PIU+l7;m7S8>x-QsT%4K@ly_NM#dL%^< zU#Chg<14N+%jnV%s_n8dm2!dZUv`ms-JDw?f7cad5UU=-s+Vs+eA%uwczQrm+-|GG zvh-FR20f8x7tz>N`@IZ`J$b!NQe4u5Y{NQ^=l*4%Ul;3s=a;wJ?V9|JI!|29+HPH5 z9vG0+YBbk4WxqRXkFf{ZL!HTE>@H1TC1L}&lUKgyG`i2-UDiJ5(_`$Y78R}=Gr186 zOj=(NmK{uAv|=l&%da{Qjj@xvjlR|;!*1g~%~h9|(CZ7YS#7#V-*V#P5IK+&#)o#< ztZR1Jtyi6iW9|FwN1cBjYq#$>?^;9prR)#0g2$@36`+t^7WQ|$tSN>hTq z^QLij@1gHqjYx7`rt>ip8#DCd{Q8?~CCME_02^3co?JC3IpA`X71VZNqF>qTl~&Z< zMe8flW2`P5WZtX2CC+u1bkX_DIJ3aE~O9_d88B{Ol_A-8m zH0##Gaw7P?B96b`InRx=+dGo4mT|h|9!n3X4X?XTn*|kC*{9X@UkRw$hS;m?PTRfe zt$k)t@Y=r~tIO%nuk7nTq{6oZuLw0&#d>F=3E<7fEE zcHO=!|A&f`Pc%ph%>ALFq)$a+?H9rv?<`KNmE(UoEU;NpSVmBw$d3Kx zwACRt>AvW?6fABcl^ty_m0d3%ebH)v;K$nQWtH|fiT?|K+uLh7-mYKtf9U!axTuQv z?=y3D5fKO#5fKp)6%`c~4GoQyR#a3fG%He6GE_1$^Jix6(X7nK$jCvnA~Rc&6ir?4 zl98dAp_!R^0WT~pyIc%C%U<8_oLv>|{qy-eeCN5$<;--ucTPJDujfv5WMbq|yYarU)=U{&k_PMZ6Q|zaD?Coa1#;DfaH1~aE zyMp0kEPRx}M=^ZFXcaFp9xpfGB^q8x#C=awTuF#b;~EaHPr>Wvx_2~5Kln8!`J|xe zjdEVKGzIcxHbuUf*zt4zI3;G^9Ua%l!8Q*`jzxIGHIo`#gOfBDJV}pM!VE=oq;<%v z>QWHF7?%$saLDf=(~1IlW)~sAC5hQ6%?^2Ko%iRDA>%eGntVkw%a9pP8ZO$sK38j6 zZRmsnc%UVI$7M@+?ikCy7klhaQt~pR4jozMFwaoiA>T3IQiiA0rN$U+pcYYAIn|#B z?8r(-7p`ja0}5H73em?akvQb%ktgxq>h(O}1SDk?2FOnU6YDs)$Q-9puXo6C7zy)N zmrxg{X;m4g@rEp+&NWsQ?m{ORpybO?j0?F<|2i8M7vDrqWtz3kV?|B#I5FH^up-Se zhGM+4;1S=3?)E)hkon(xx@2=BbdC3P-sbvyx{+oxbmn_HtEtwl%jIuA##{q!&0Vcl zHkl0fyhfOBLV5G9Qodrkeoq%@u7s}gp03<<^`6eld>OjS_jJch|J>7AOn*c7_dVTi z(_i;=Mw9kWDF3{t++zB}tt{Y0?V_n1+VXqab*9tzyq+?hg6`Bk-73?`d%7d0GU&?g z>6Vznm7=@L6t_QIDgWC|%QZsOqh|(bD@rgzv{A*!+bh$o1{VD#^h7n=?=;@1eucVRldOBw8_Dt2OIx>IpGTm+P$nKEAnX6{VVctPfngUw!_3sK%j^EKS}$?`LrO2cJKpcJko(jNsk z36+m0J_**MTkxc#Gui^7;efMa1)oaBINDe6>^@fe2zZBlq_&O1AE*@( zkVD>E>-N73$RljB3k?J8QJN9TBy7z^DpY4v;cw7sP$?)l17lG1iq%@&Y1#&kvp^r< zMxbdf6(nF4kU%yOZX4WDKr7HP(`|DccnSOppdL5~xE2`f;djrV!fNnYz~#XAK}m4G z2Mol>EgEjE1SS|v02uBzVb0f-jzHlKh5S6+TpkYh0gpTq?l0k91vy`-SOGVWC<^k~ zaDNPUG~DCh{s3;i!VwMkyKwVWi~zXbfg7tKT2~Y9Gvh`)g?t$#1|C;ImIQt&kSC-P zXD42qjb)H&ROkS{6!5SR+g15N@i18RYMNwt;ch2#&4Ul=EbFHJ;WzkEqzH;n}~ zZJ)8%{%5hE!`Io>7208$1NTq%unxJ<$S^$4)c6iW=YhiGkV}oU$h(^m5f1l(+VSI6 zje){I-a^KB52l~;ch9Z%8AS|{o$}+BR=_!sNVS>xUt|oZ;Ch{LS_Sv%kT+qC-6{9A zOi}E6X)%92Y#Wg&h7TP(blkkTycs5+{%MJl{oZoZ>4Rr}GqEq8550xayuTOfNiUD}aF4an-6cP6>poVi)^u-S z-p4p(e>lv;J?0U{5$XKNbw=L`{yd3BigY{V0;ACIBgUogYoz$w7rfQi8Nc#keQ^k}O+ zblYf)$}d>M(_Cd0i(j;;Y!*z4f;0p72OtS{BTJn|diqrx)tZV~;b>x=@^kF{X=D~L zvf61xOUl^1>0}7`o_#%?j7KqIb<@dAddSJ1PA4fOf_;-tJ|<2!cn0b2XL6Kuj;Ltt zOe07`6`M7K3~PVFQC5o+W9hvr%4)p`SCp_FGcbPtj#bVeui&1bNi#8&<`Xqlj9sp( z57*e`YxvKX`R($RdZA&O#l7{>4;vq3giEe*dB<9?&7rKu;Tg)R!@KnEg5rh+>^Q~O zG58v7;Y-sUFsIS%424g=8BztG-b2X9+lFjxlK3JQx@bmltyVJ->c5~SBkm~5%R+Le z4vAr!**m@CzisqZi{B8;|G~Y{GU64RbAF zhr%1WS}}&ffx(77?-Zw4Vv~i&sZ_1;*L2mebu&pY8OnCdBu})-IDuCT!k=EANjeeo zC~KQRdi!;92@NL9Ei}z8xjh?`K?ZcT%N9KAZHpbN(LzIy=6?)A-@c?$2X0nklk&%6t zz#XtB7w_~|ok|n4Q|Oi(VVBRERgJkqz+tNR`51lzXR<9Xk!aNfXV6S`{w1=S>|!%! zV_qn+6SK+xXrCEu)XUf`TUp0+FOyY1?aBgALCl8X8>3R%DXZt>e%z_JKT? zb(llGn;B7r4gis4j}eYeL{mu7Ve_ZvyjKg|sE2gMZTI~Re@J(_g|7c}7B-gzbt;Ce zWv;qpNm;FI~~YlRQ$=|lQLc6cu7H2RjwFG=XOIMFUw z)T%U@JKJDDgi2H^YQlH69@{Fs4~DAwV|YcXs}&k1+mRPPqFX>ypW~rieW!V(tEi4T zir1wdF(3Ez&_2(z`LB{7vWmU?D(Op7+0Iuy7g zHA$~`Pv?>-BY|X42^BVg1W+B);*UrhBFk36J^rJX&h*d!z^Q;lT-7qHp^ed5hqtvX2&!={@G|$37P*6@;{n zY5ATzG(4@m$VArmb+n7`RkLZYV-?H7zJHxO+U3w8?2_$q$p)7+28#x+malplBMV7K z|Ex{iBq4KbrcKt>v7QUb@P0PA33Cyb{Ef0UBOJY;r`S^GRkm}cyVAPkN~O{YNB6Oh z7Lv}SfNfofcearI%^``keuIq0-EUk+8}_ofZ;<}9bT8Wg#64^$-hfdSYkq^g7~@s< zWT=;B&uvNTe?hGY?^0?}kwKW`*f1MEXHgC0{r2;dv1GG63bKWD?7c-Kf?nIh3Kx;k z@u$uIKi_{@RBdgtPYeGREIHdSo#vqj?0hMGh^E%y>fw&9zviYvIGWAc{tw0LOEc^9 zKhiU7nK=O-d=wAaJ1<&PufeP2PT27}HvfMlW#H510m0##Wu0FOwaY%P!=uvYsLFe5 z2EkvxN$M0n1Ix16;TjA20_?iFFtlLx|0Cnucf#tKOa9h%#}K+3yN;aLgvDe+V2zzC z-hiU)2P&Lbh;3g?lH2cBG*3d44e2sY%XC8F5<-cEg_x4rkQ#9=$sjxj-|2@@&e=tlNFt?v%sPf~;i&CGrNj&c2h#u;{aB;sW+eYFe57 zUa?b7vif!twy8cKqZs4x>D z%w*3kCw-~ChP}6(Nc2Dr^Im~*%69hL3Nk`Fx7rJH$CUC>8llhNGwE}!^4a>@xwB_p zpZU$q0rTG9Id9%1<2&BG08UXX2g5StVE8;MI>dSNdd`|P>rYFZ#HWRR<@NK93URPZ zfTeEdRajp;Z_BLfvv{p}0!t@WSyz9{Fbuf4{ujeAH5>)?EaV+Bm8z7KMX+`6 zkp7)C-=l>=8JL=uJ?2YXA$rfvF293{ykY{o@(z}_7feK(p~)V@$<>=);Kk;-G|r)W zXLDAP?m_+G`(ahkL^gLN$_$;z)~v)T?LOAXO*f&f!9j|n0eb>9_<85@V5Ba;j`ew$ z^dT8+^1CQTUK81(cS(lIDxa@sSKh^v{XV_=J@TB`Dn#*|%wGC{4D2$`?vR~!K8Ewg zajW;O;TZS^9L6B8+#$cne)xd&?Y_|5N-5(ZnJJ;c=@IEMh-{IWtgph^mh8jn-{I^B zL*L(Lu+Kll z)bFf`dFPOnHcne6UXsn`kkxEf4(T<}WW3vW*yPRjmcb7)J{ceLSv%xE5%g?Sf}8>>tYce0Cf!G@LYE+c8ZeD(0;R(}6=Z=s0T>6e!yO3> z1^I)#K+dnJaEG!yHA?oHCwIWM^`|25P zx2qb9X(HdN;|U(X{64{A{FiLtC#2(}nHzb%S8KdAzOa0+`u>idP2P48ITjUWi#?g`?eQd55|_j9$QD=Y7=$Of27<0#dW0TV3j$1j?n$5$>%JA zx!vbn?bK^V#P=s^TeaFB+HXdb=9L*%d7A6Nw3V{AdF;uIq|m)4cLEPFj2AL3%*04G zRhiked=k`lkMqHFbY}17lec_Trn}GKtYyCI$w+#omL;zz;q8VwTgGISzjCo<>q#bV zuN|meE*c*~w0CAWM8JjCbR?Xyow(v#ifa zPkO+}@;*mo9UKov_7=_+a-%DsDwC-+acq?IZwM|d&T}>Y=1ad&=-j9 zP|e-g_SUeCUy$gi{wNh!Vlf>bM1{;4Ows#exn}X4(AA@Yv%@j+ATdveG|M?PtlYpn7} z+ugLynbT(uIuHA;PU}Vq4O^FYpTpQE`J@A@+du-?=C8;&-<)cm;DIPktE%1E)2@K@ z?3`KsaCw-DwGQ4T#p<6aAiq%U7Z%>b4|c7+95Y$OUf#Lg1TXq*I4QeNs6=z zqfyj;#rhpsf8-ji$S*3^OBCz>DcXh5E_01gw7)98=2>nThAaF}3O~!jV@QYHT&-(} zqR=bBJgWqgss!^C)GxUDD_Wi6EeXN&RrmshPf+Yez;2AIry|c&LWxyE8Hl7uDAErU zX>Uc^)AA*bJy_)k%V9&AVYz_?ZzciH1zBpbOJSAUS@tXFZ@FOjRdKbpe59b-vcT|_ z;WsoBSNdXl*%K!gK{G+)K+&L3kP1}Q1A|A<5s;28-%NVZ*>$XNGg%nhr><;&0_wa) z?pap=*HdtX*DZ$YIk-aWQs8Q}-XaIpMZx6_SGzj)^tU7>x!Np5T|ASd__zl5Ik>r> zOK_itoBKKscNyH=Upd^z;I?3w!wW6@SWRoCGqTD@n^_$~Y}Zba-|eLErkh#H77|TO zs5o25AaaH6+(J4>lwy8^*W|LvzdD-n^cEEV-H~{@chOiX#Ok(?p`Ef9J~#t*%0B4e z0uBeW5#N!HAqD?Y-@@w%I9#@n&HoM)=z`ZD_I^>(7qBnjy=TCGydP84T<%^pvdCBJ z^cTM)KKPW1-Q0#zOWCW;tB8c*n%N#jV5{dd4QKP&OJFhjd6!Eq^Vt@+^yeaSGiFWg ztW2!z44##4!RjSn*07-CQ)TAuM33Go4%xY^c6MeQ)_!KgVr4D6_&pgu_CT#4%9&jr z-Rz44-G1Zvfo}JybBjE@_L{*x*G*}*$sMgr-$DL?HiJ?@IiOw3aM&1!jEAxpw_{G& zr?#gOMNdQ#Q_EIt#|hkcm~cWr+=ZVzw8*O3?n+X-!^%fV>fMEop)2k$Ui5F_?g=+5 z-%i5)+O?2U=0K!=cb4DxRL8pQAe~wI4zi5qRkMm6Bm?V!PZpCG>7Z)1xfuOaP&KCPtHe*e36>{%b(NMo^C`@^dvdd8EY) zvkaH~gR{}lgtEF%i-Cdi{(5!c?=G$8e#XwMVPU(_0VT5uyGZB2JC>qR&1Zsd-*`h^ z9vsipX11`UyT~Z*uNH6R34Q_cC-@1-pWvt^0al0bpAYj}`rKvtmWS22*ej?!TH_8) zXW0UoKQ=v@J$uNIzC)0#$DKUU z7I}cnooI{P2QHpy=CH~!&?Z5PyXN9rQ+?nlo5`l>@o~UHsPowedNQQNq#wn; z#iU>U&!pcX4{;*RtX@w#(ULmmvyTkwR8(h`gK)aXCFj_9kx)7RHLOG<62>O)Lqk2y z#Fp(NL3BwSTelA*_Ov>7b|2=~!|Pb!eoUkz>X>Ff38w*dEOS2^6M?HrC&1!k7SG1ts84`76p4f_MIn`&kkxID?sEF9y^498-^ zXq1`79YlD+X7>C+6jFhGaF9Gs3ykdKK{7H(GQK`Htnjl@Ugc9d2ctU~q><%CMizF6 z#D+(iP8z~=lNz$U6D$iq3TRN-hhb}je09`Y4Xb1$TX2Xx-L;|ieFFv!+rve_XT5IU5@c}C@>s!d+Fz7$^n- zg#eQfun8~h0O%aZ4%!UL0WD;WhslQ%`Hw$37hqBZwOzfBjvtKY8a>^rIDJa%^Fr^WWu3(KN zWDLn;!+s>)0==>J!Z${&7~omu6&3{2na%$ZrQFAx75+#P1MgscBHPrpyu` z-H}(;GkT0%dgK>01upp+YqNX|&5b`ZA0y*ccjT%%7XK3&7kUTBQRZcq*0!dfcM|%{ zOIKY!R_iTv)7+8M>e;5B$ob$qa$Vhu#8ZZ>%p0BmDo?=zBTgsDH|kh!DG4O2_1~6~ z-wCm^H;&`n+CNNxoOB_n`ah48VU+A(kDMT1k?Cy53DULwBzyU6ykG2bnLsx{mssTq z@(EpSW(!Y}K}zL1i4%GsvS)uK-PB8L!qG$Q?O(}GT7>D%DKeDinpoy3)aJz|wt*vR z{i#!=ieQoAH!^3~+?P=PKu?3lf%t~3Hx^WaK#`!Kpg3$l$AF?h;h<|M%WK)>)8u3N zbS~_mWBUL3dj`pWQ8zZvL{SB;0!g3*6zfC~pWO3FeX)(;GpG*b zIPaQaWF_Tzo^eLjR8H(a_~LeX_J!f8;FA z4L(xuXobn`(PoQmM8mm8|HXOoJ|V&S9v6UZQymt0nq3Wx{uYb;oc)0Q@ZaRNNDFJ& z?#m?ASE@-$9E7uTx8)_6t+W%vS?m>(O2caPtFMrA#NXEZNd6t!(%c6BZ#T273bM9c zXm!fgbvj;lPFMnRoA9}WKA@6p^ctAz?4m>0&@9U@VEFh*{%v`@^OnH}IL0X(lCY$A z1?%JJDUz|sfqYHWUpA08iNDstZI?RQ;QyNrmQjO93ThvWM5@($8+jxbn!D(l<=u8! zzG`tt@_Vy8l3$zMk<7I5Nb*MG0)a#{RP4_N(lsEWnkU$MYn&!>Mw3jdnW~Y@r*C80 z`wtnXKhj8yULCI4gOO2!ldl#y9Czf4_GZHZ{b38KA#|gI>8vEOZ7B{aJW||%s?{VP zvEPx)^p~wn>)J&NoPdf9Amt-B*jt|ob#jVEhbJJc(Ex6bq_u}7ozLE9QDxBc1CBsVlS z8|wVA?d|Ugd|w#tDRzJCfoHxY55_a!ksq-)$;0u?E80*YR*wbGdFmbuA4qwloQ0I% zk&~MLm0wp<9;U|meElcA=xy4=@IaCq<*P{YZTX<Xg~dhJ~WB; z++b^&?nZg7jeD5y{8wIY<zhK zC*W~~qi5~-tvmhY0Z3EikCq3c*l*#spV@B7C9Y<}5mf$oE1Ncy_R)tArXgaa-vjTo zL003P-j*wyZ^^!Rr&I9bO6TtAVXsrs@9fO`F&Y!J!RD=NkUzBZW%P-+h!6+z{#rlr zF}j6#9YRqqZO~iesZ&k1>zhZ>S%fCP%cdpL<79=t$CLCrC12`WCsCr}osw@dokE*! zEF+or4>Vi7b@lQUTccc!U>b0%$J~z@!VS@vB-7PIY-`QePnbv#tJ<06Af*Fpw>1TI z(k$PpW1-K`HDofono2veW6#jg_G577uwIU{+%dF4^=XgZFq<_#Lnj3+t>e}Cu2+oQ zQc`J-st#|CRix5Ef!{mhGQ4SveU~K&G`2EpB2}d)T<5?tqk*THcDJGUS6~6a6+`j2^ z@U63R-|HQ2-wUx4A{>on1D~U>hMlTKif3-Z_l~|>d762OPgoLieTSIkEw${(bJXZ} z&6b2zd)pi2%eJ0u&-3&RT4!X@FVK;`r;SO8d<wO@wc?X&6Qi%jx4GfRJoMg&|m`)o~7`jE&OCi#$= zt$m4>2dYt1Z^?gRGI$${QGB7c`nZ14Y??+wcA5Tv$ur4oOnTGHw2qJw`hVuo_tkCQ zw06=p%99=Svcz6_6=`c%%ko~O;Xclv32v`K?fhAPtCFpV^`l0|e~z=tS-O>#$=rs5Z6`|o8V7tusk_&?e|pxD$hY>acN|A(+; zjQswU-$BWb-c1BA_R(U>=&f4T^G%vcUvcP{zDa!uO{mqcT0##IA8&h|+zz9r*%R2r zrATm!v1Oph8r@O^%B(!l?kJG)#Y7m#CcI7G3fNcE(loz@U3eR&F1)>em8Qja2N6Qp z+sj}Y{(z}~eK0Ma?PbOEU+k+~`kn~J@7Z=OJw$ieSe8Wd$!5KJIqgTn%4+_xB*y$> zNk|ZD-&KD*s=mA%)2H2OuZu*(PJBeW;&@HX zN3?TTtkpmNmi)BMY%pQ1z-*{Tee(a72-eL+xVV``tf5Ub!^VO?rV;cbD~tb_PW8@e zj?&c1FIw5gk7*o!+v(4bsh)-mV{3BhP;PWQmo8|x22apNInFf@>#Q|6nZw3?LPJzF za&^5v^Ann>YF#~;2o<0U?C;NLjmn6%RCatF)%m~fY?E)4vzWpo{O!rJH4 zcj*;}enUPDCa`|TrQf@r`V;bu{^t#}JK>M?Iqfwp!XBkF%3bX|j8Hodqmw;QS0~?g zny@|EU}yzNfc^gCu3ATgg`gZZb|Zb6GCSL~kuIjs+gbD%cEh=EaGcA*K6WqM2L7yzxHeT9--rE zSjaat%I7necab`;MxJ8TCx1hSQ&lzJR9*coErjbpJ&W5yL*YuSXH(&7TP?@d*UHU~ z_#7h4$ImKGWbbZ4X&A)51yHXK+3_tj469r>x%50!e+M|gdH`7DcQlYq{f_$L!BtFBco9qxcnA_KS`Vp|{M;siS*`uQSJJOmeleR({rAD<5;3vD9qBM>DnZBsG%)E8`bkgsAIxJe0P92D{`m#ZFh?mZr1RNzWQ z(+--SAUy~waBJAIZ3zDSaw2>Pp%#?RKG{Y;6T92u^u3GdWKs3M<{NLb6+39W_-wO| z{kVgU5Fc&MVx*W(P(9LoV<~&Om?nz`OD_AmnD!!P^(TsHYeFWlKX%eu@h_Lee%wU| z;OJlDE;ON^x^BG5#{EE3#5t}U_QenMHx+S}FJ|+1(=F;EzIvtaw1-Y6?OhIWoS2U@ zp6ku>BFDu=xH*e0-%C5G4DzCS_T^q0)Bg7sCK|^gm}K2G(bQv+2_|2)FbQ%{m;~N6 z3984U6HH!hVNzw^@&*xhv#<5E6UD6eke-IB$MMM(yR4_3X;~eQIheKEM}ymUYvD29 z9tMvAtkRuAY^*DG3t$)6Xutm?nfC1qiLkjHyMh*lW_rxdan8n$jhFM}e=G;do zcF47>?qx+Qoa!11os-SjPlu@H*-yXDHteSpgGRM5+K*O!-Rne{*_Ju?(^#>Ky+0dr zfKKYrWV;{IJQ&?sK!oFL{Q){e{N1*Ooq2>K{&TwqN)Ji}X)k>Q8*R2W%62@UNyzZ$`2;G_`FD!5My$q~)@+30rCiWrkmuLjJy#Q!Zjcw$ zL}9tx`YI7d!k*i;PM#39G|}uGj_{_UEj&g;)Me10WW$cpU&vlYexlkZ7h#!ZHKaz+ zN)Ign9s_Lv4F@d-ahm|p9bD>n5mX4u1Lc6K*v~)FF<8@M{-rc{bV{|~_&fOQ;TcQM zgv6Z@nWHT|!}*e!K~BMuTYOZE?{|*4RQU3NpUN}HiPbf7YuABSh;RtB5%eCLQ%VO6 zT?})fTS7#YJWe@2!n=|Q%{B5B)bdi8#qEh6AD#SG;?l&Y9 zUmU0ZOwrZ#&iu8@Ki=fADVFXjFYfGFCmAtHuo&E(wyagr}Sw>TXcGX4g9Aar5 zGrA#M>;dEP;uKcTwVa%XK=jB`245_i5j2gvW<|1;rtGl z5sxe%vJkhdNm}5x%Y!V)W9RL$%Z03+M-~7XiKRoN=x@*Xj~BY(^d}o|g0}5Zg_m&+ zbP043^d4vdXc}l7XecO@y>x<3*DfIB-pStCE!@wya8GRE9@WAC6ZQWgIfsooA5y2g{VyZSvi(c^LFG&rZaokQV_ zaXbCt^maR|a1CSyoAE2gcBk390PPfP=lUM?(uDWSY^C~>zl*0K8GB}q*L`l}nL>{) z1-ij!`g(Mni#k)?N>uz!G&k(8!2wtv#zb?${yz8QDf?@2UyEBFuv60z;I(%b-)qw} zc(Y!oC_}@iJ~Q;Xg}&2j`vzm(J7&&rYA6!OwvrS^eRRT zHDzLFaIB@L;$~UD(LuDRmKFSlDHhHo9{G)iXgA}^%DX$4%N+OJOW^L0~`-muMZ zF!P^Q2hSCubB{+BJdbx^2S(%C*@;MP888?$9PY(3JMbr+tGX$lu4hr_X&N0`&E7jt zb0IO|DCS?ZC(`-EU$irJ9bWv4{z}vn#}i=?>-RT(#(Tp!G({k0zd$2d(cjeDs}S0W z<5>T{X$U*_H;p8|tod&`z^iU7TCTCI|3w<&-OgSue~V!mi@Au28`aObNGDMo5!-)> z@=Y0XnZDn8fwfvzV=z^~KDkVHv<{vz4;3IZQPVFq>_%tNryKp=`(E~#{vVt z`QxUXgmjIy=-A~-BtV6g(rWp9a~^x;8l8?`_x|k~eN4Rz!-sU{cbztq2z{#?^mm-~ zc3KSv`7&1R?-*`l%KWe4mVtkOdsBYQ%5L36IoOUYDq_nkY5NZL0T@DHTO${g2g(OM z&pxQ4kJ1YocC3nCqBzoAV4%Tm$C>NUl{w`fT?RQ7BkDAE!a$$kTc^I&G%Pg0Truy4 z>~9vj&zcpi`39$IyJut{&J}LRd1j`mrbEdV{oB=4tET;pdZ!tSg}8z0qk2p%64>E- z`U~6LKm%A_0}UsMY)=F2$M;okG|--p%(dPhUtX6jtlMqcRo%*Fkn`DyM%qht9m58e z*@%+ys{Z3fx|84+Tc7wB0Y|gfI0)=(06jO1o&A?a)6(j7O*D$EW;>fON7&@%M!7jj z;ixr^;Yz+>I!$5w?~3E&M~q5mU{pH8Pjele@!dnIH|5!umdR@}vyes_#u9E*70S+& zNNbmU4Y+D=Y`~rs#VVYoB!w<42J`z}_-WpaZ24^zW{G_PpcxJ9z-<~bJn26^R^9Uv z{U65O)vIfzR^d3PzcYXgG|6%N zFmIOosQz_19$Vs#G(tVrQYBwx`|n`dEwR55%kYf<#IpTfEJ^>-72MNB*R%dICQ$(x zi~*78mt^{?--f#TE#?+hDN}>raon7Jw|zU!{w3OE$(VX1NN~EZN)Gj{vBigZr9|^Ms}>NnC@SRo3h+HZPj7 zr>c}OVO`cv9NMK4XRqA{Z(Z_L=Who7=~R^*Ve>zPxb$b*i7(N>+`4m?VA6fgs7@P3 zRh_9dRN~8gHm-v>()&Pl8(pP57u&5J#8mGbg^RCaCUEpr5E~UF_Q$cz*Mh`uq1W(f z#NBg;xMoi&p8?%6W?YlkyV&+1F#=C~CP?IE%M74tzc8PUVhD}?Mc=cdc!lsqx=!MA z#5c7zDX|YuBv#5PwJg1}7|GUzh!HfpmgzcUQ}Jww7(_}~O^7(p_ac^{-KOV^+!Tcv ztBjq+SaOnG?kqmT4{#0&6{q>e;}GaLd>vXT4>hulp<))<#9DU|pP*N2SaKI}wr?Re zFMDIgSt)O+VMn@%bLp~e`aWUed_v~3^>|DNYNCtvPMz z-dAY&VtEkmmhcUo7Z_7sPbvAEggE3SobUAL#>=KJ9w z0kj0S;Xe-=4hjXSKnrw&un2VRTS2(>9c(~JThQ-<0zp2Y2I$V|8>7TO2tW0FypNbl zDC^u;e48FIvkiU4w2(FC-<2F%zdY#ss}>>f7LpmIsgPHgSwJ*$Xt54@NE*FqrTne!v9SWaxp8w}+TzBN zdmOXj!Xf1=eV%s(*6Z>kINa-$YaQ2R9}DUJyzmE4he~yOIeU4KID}NQF9wOzwQKeX zLfA3<7Q=e{J_9Hnqyfc&c>m#z`E0X%%i*QDiV@&I&7k0LRG(1W%X50pxzm~UkHusk zN9Ps&ntB{66uC_>YLcpy;`?3@E&)kx_D>Xdt4pXdv>4kS8fIwhX(t&^?QS_ z!nrf@rM;HuoN-hDzh4iAR% zk{m0BL29qNFMZR>mbj%iT1aPFnJ!k07;{xV)j~Sf>Kkpqid3^4=}_T0kJNeHUOvnH z)kmc~T*-khil!KvmG?CLSlc0DkKU=4!6?LW_{<|DTR&4q5gu>(VXhah*W-nhf9^0O z$A&^^b~OLhD`U zEy1yfHQim}kK-qxkK-3SE97e~7CKby)22P{)pE-9wreuJOB*VVCMNdTQ1SKX3LH;< zFkuz)4A*}XRv}MuvGn0$V2AOphf`J|KfzvnRO~_evv(d92a`Z|qSXIqqAX6<@Ti#B zG1M_&99hp7q;JZfq7`+@x2)H2&|`d@IEI+nr*Yy8jKFWliT&u5IufvIE?_5JTC5$HXZ9UDRXZ|N2zf9}fJo{r?yES-TQ=&@cA?=Gb97 zk13L^ju-cmb?k`|Vh4WcYJ@nJ%w}JY5T|3#M@EXBg03kaL$qxCD&z|`7Bf-|9`dX0 zzl~poTw;5;@vD&c+WNk#Zd}~Pucw;58ZUNc`6Inhc<_7O8**=K z!SZoAa^wlIE6HKqIApUYo)CYcsvPDuN{nm2xken%KdJ1DYrOb1q{300-W?@A>VFGg zL|v29tb83iu9l4(jkmdLlo&FsTu}^H6ir4!XoH2-7ee@;4YwqB$33CKtn{ncf_pgJ zjZ8gSeAF*r@lF-*tJqjBTBL|7@bMKsx?&%V7Wa2duX}KfT$VqyRm$G%#YAzI>WW-m z$4V2$FFU@A38pNIP)S(G4Z{rwr&nMUz}|dP+={-byG9({N_1u!(9W}0G~!dnoc z+^W(a*NEA~yR(CCzalrf^dpnR?bM%M7lbRI%(duT*Xpg~#A7tnauvM;h_Bh;LaG?P z*5bWKn0rMoG_ea4#L!L~aZ}J0IRt6pS4UitJ2)%lK{$4j%={*b9ckG=tmj0r558u9 zYNGg<{}j`xP?d(3_eKafcCle1s$sH;9hit}In=}&CW({;@V%5=yRi=qO__1E^7saLYLOFZyMR5hi zVSb-!Kzs$XW*TCMz+iBiSWTxH_1mY5X_O3SZDxvJkXp8Nrl_Y^PU&aP63=^i2b;b3 zT#^IL`h>aSVjt~P+y=9FOo>J)`Re8)4LKF55AjP6Kd4WCh+ld1L4E#1eCb1cI4+fG z5uaFLI?66C6vMO+DJo7Ke88}x3 z)E(Z^Qms3J_dJ4C3KmJ{fIMJa0;?@9Y%T5n*`fX3d#7Xv<^|*TvyQv{^;fW1>I39* z4Y25uozhy5JXgU&=>m|4cMG@}c7eZe|AkVt77hu5;4*iBu^hkmk}d(a@d$xifvG3l z9x_ilv{|q}cgk&lNx@?2f`>N(xqZNIZheG;#ZnwF0sc%LzQLo{T0H_gFc}5`rybf9 zU<9zZxJb$ZZf&(wDo}WxR1A!VKKplvHVtS2W&?}LxqY#;S3#Xr4&(_D&bZ|qw;>!I zN5~t1&}${tS%OcqZ|U?8g~GzfxH&vaef<>R7STv8OQ_91MY);UM-gwOSuZ_q*K5K$jOCo|#T1`B~vNZBbM zHx$%PZ4CHqAa8mbfIMeTwc=PT6?=31S~{hmPMX@P#0StR5FdCrspV}X$Y zPHiGEpuJOh=Lv!CCrt%zgnVBIr?v!`7K8)lvSW84wO0eK-!2j(JzQ$QX;nMYo&@H)wR zpxZta$n~2Q)JY{k6dEFB4R&fXcnX0MFe=s^a2AjUoH_(q4O|So0Ne5H7&P~fG-+%QPmyu_(Z0&fDQ0b`dT!lKsFK91Xbq$s>f?mw3! z!t<55KVNC-av-X6>I&Fx7bV|!P>bMy<4UgAswC69+z}+Dzwgveg+d3+0%os<91+~$ zhzL?XNZ%+5pq+ZQ=13* zrEPAxog?H~-#fJhkf&{T%a?LQLNkj|i@1EJN6xXZm89diBTOp(0SNu(-8=zENhurz z{eSj8R68h^?uP-g{08tG_~3)!k+rcLw*^R{hdBC5tAJz&_@m%=gh&k~9KEFEV?Z=$ zzCQuCs-!$1zQ&TGOSwK+y703*zJ(`HSaJ|S#z`m;VL6aLk^NWjh%opRmj_6~ZybH4 zEFh22@;m3frL1yyj%Ar=pF*hSt# zVFrf&>kdC2xDtG9lbhG3!m$y8CUXsJ|mY3=a8*vExe;VYE@Gkc)_ztP5}c#QW2!ec4N z?|me}AGjk*656@6S%@ez7`(2HQ~zv;b2&yR^YQ(GZ0~ zj}}wt!tK71GJ(;M2X_U(RZFD??m)@|y1BIRP{c=Y!(u70yIbA>OolwC2j^8%R4=za z5-35=`IW$E;3{AYFb5b5mqDP+0(=`!hz9_2d7MX{=#eLT(az@>2LV3@-V(8iZ@+<`G& zG!XKo98rCA93hYH=hE(mJO#)Tl%rsQw9z9k@yJhkM@tL4E9CwutV)K0V}{KjNtx?G5&|6 z8iJ)j3ovb@OB)`=f4tf4p@1V2Z~@38umD4$PfBn{R0iY$T~KhlbmIv(@0-Z6P%2Zf zSZd<9y{nYze?lY*1n9%B?NmZE_?;xx{q+dZVnzm|f> zg5TjS6^-Ne#ga}zounGi?L;Y^Bl>`H1&gIq3hJZ=ju;o@PT+bi28D_PLn;L^PFJu< zs+hOQy7Vr{qC6E`+jld@0ZXkc+3(vSPx1*8;SPi+9>JB#t z$n9b#yR_bTAPK0Ag5w+<$-s=M2oSgtDBvAOKIhU#0ha>ffV+W7z}Oev5ljX09LNUp z9Pys!(gyZM*#L5Rs)B`536Se=c;qqDp~tIBO!qWExo~iUfEiGrDbC@Dtd5%n#H2DY z1Bd{3D~S2Of}5m_m$u7MHFsd>6tx0a)Jlqf$E`?G@EfU2 z!A(;1yIfDCg!j;3J&vK;``jOuR(;@(&;*oFcH&lZ|7gO2OTn-7@OcViDfdI@ks~XC zcokYHFNb?5k`mXrTf)d(9)Tbwed6ZR*See7&^&j5H$FpCjetYfxm!f|5LkwtUC++9Ih*=h9AvqNET_FYr(i+UUn0mo9ye>H@xa z2NHl%UAz-*IB@E2?y*?PR1k$u!9po{k4szlxG3cSdDfN#55eAduS;9bYXy+Yvw)Yt z>wpbfIBs|pf*uA~?TY7!EK1=B4^x3RV3*_J^MKXh^Y^*!ih!y<$olcyc$IT zmBOo_PD=6cslZ5tmk#6!Sn1JMd-NQ+e>+fl;^-fFKsqUbBYLToKyGkIk)w0*@V>_| zctkH<^qV_Jih;$&Tctw^7D{#?@BNZab9u4!G;jwNop*WoOF+Juz4&)`d`Esq{pW^d zil9)k0(UIfCG9SE%Zq_Uk3TB;oN;>yR_(LB4 zG_c5Tmy~c8;b8WB>YO|L^IABH9w$=6dH0j$U2rS*0eL`W9=;T~9el_?Zh1V=j>0(- zC?EkzKow8}^adsZc}^8vLcD6BFiN#lNK&oDIa*Z?K1pH&J{G$@ zIhlvOL&Xh40}((hZij@yY9c(ZT7i!xR7-^I1oD2c!`l*Ef*CdfSFoy&#r~Z>f(>>SlynC{AElZiqygVx z{c^ECJDE+$#Tl`F?2TM;Fm1ld3UkG$wclRlX^iKd_LI{DAB+l;m~_!kI3=oX`3mR7 zNVuJ%%IYhuQuTy;mrAw5PuQmlh5Lree}kVOsp(oj;XSo#wV#lqR>8eit%5vX9S@sJ zYSkt`p+T)Ggr!#^BxPQzU4Fu8FJHK|SG-gQ`~;(y3XIk3n0VGtDDhDpgOyJt+;@Dq z;zpZ5xb1CJC;fz#z7cR2`HqC##9vQ^AN~o#zb~DnI*Uum_}j?D z`K>VK|wAx9Q7Z(dFx%&eiC`tzc_4QH( z3Ov3L&})$2Eh1jv{Z&CiDDb%}?CdAv^VD&f^^%JDKOQf<rJV#@9T4`?J)yA0p;9&Jaa;iFR}+(tta#^i|kvi$`ksDB71^0>xAAf z>9HsDxgvYG6?S4<C za)ikrVC1D%r(AohW9N{OHI9Ddn%15^bK73fqYITK_Q8cC`sPynI(bCjS8CsGRUOffm)en$tB&-(Hlo#_>y=t`gr8*7Zy87QiBhhS z3xCyP*4P`Yw7=?))^M$Kx9E{;?LS!gEqcRR(upnl;#&J@tNgHj>IHj@HRrJ2^MXBr zZ(qzSvmdtV-_ehh*}t|j-_h@ukv;eweQi14WoUa_PcFxeMn0eQZ1%4QKN;!W-A5g@ z{$VrpRd4IZg_mo1neut==6*6w`i=7L2~zyOp-hqFxAo`cd_g!ucf4p1^IXu0FCkj| z!m8qn(=Rft1&;L0a@47Z^rxsjgPmk|v)_c(+U&5ajVx@|YhSX5N6u=Ns{JnZi}&(E z@);-OR;ALL^?$!)4~>YsD*t8jkNWe&bjCXSPV4wvI&vK?FMmrvwT_9o@Gbqhq>~-# z3DPm^?c1#5Z}Qen`*+r?H}(GYG(7H2ZM{so=Aiz=%l2@+;uU*@E`Qk`W+flgYtdK( z4(frgAkQ>8=|&^h7`d=XZ+*pn)apKj z5*zi!4O~}YjXLsG7K97?^nI_&q%^Y8k^URIRo;^bADt`TXfR9r_1-8I&ZrLu)6rMl zZOeQJy_R%r|oCC0=ChJuj`EKeEM+EsrWC!1+aPI|jmyh+vtC7f-z4~);sm90> zBNy-0n>N~!ECGpCTt;aoIl@T}aFSN{9{u%38q&7M&$b#(x@wPpyviOruz+ub^zP$@ zd-T>Sd-%ZgfV@H8yHpC^jNPNpQEp7sV)hjNZuJGyx{pcoIRMeoK+r;e8ZP42#o#{y3xycT+f8`HN z@UHa8-S>HZb}9>S$8J4gGlSl+TmNP=lc8w0er~h9A+lqaIN&(e58kcZMUs#$yY#QN z*td@?mwd-&NMH2f-f3zIOG`noakz9HTuHD2=2xg8kl zy`3GLQrQ8z>#-r@o)(~!$vhd|=$-l>)qL?PSP%J&{V!Hyz5exI>w8SP^L}2v z-btPnSFf$tknKD4y|3B#TdQ{H=S3#((4W6%KWLqNL*Ms0>4G=(i?5T8dqZ0_q+7S^ zr)%irIwRK@neWJfv$prtDK1O1Gq&p!HFRstcHL(y%Mmw}t@cfk9d&^db)F?b- z{j05vMrECTVyivgnp3xJ&sO_IkAAV%j?)j-+T*vqRcm|tZM%D~y~wtd+O}=K9ekJX zh*{DEZ+a3dDj@AO%bU zdDvP4R)Ez&>M48Q_NfXYTR<(?2b#e#u;6oupcWhkr$HA8`GN|-IPefy3M#-fD#-#f zKn{2a%m#BoGx;BY&%jqeY=z+Xpw1jy#S<9^rhpk>F36!Z^T86Z5|n{0U^hsZ`Puw0e>w!}oGBhv0$xb>s6XdY0DJ*{ z!s`p}_?@txJ^aNr*1>cWy}_3;*`soS8zP7&gA(li8B~H`unYWWCO?ZrcHm}zWGlP~ zYTHZv8W0#NXJ9uQHy`n+Bfs^iA$YMF?g%^+JFgPQj!)eXU}!zRK~Lm2lM+52=2cyQ zP2H8Sj0)>O7r(F*NJbUux4_aXETvrDV{in4ynCX(Dg;Dt;EnNKWj2l?j* zqk4xxvk~OqnaNm2`jRlU9&hT+G?8LS+^?eEW4g(G%Y+JeXBIK0k1{q$H{_wOAw3p- zqtQ$GNgr}Sk|*&&CN5=`qHBRS1b*8h#!-ThVhw5eKT(Ld8^lNn1u`g58BoED0HeY^ z25&l#J~g*=aR!>ONnRrXSPaG6KNA^kzxe;QIHibilH z1N}G(EJ9b*OCOH@AV%5)3`vFeoU)ZvBJmL7wZtXlkuHH-(~F-4hx2NuK$r1W3Xcjf zw8X^4kQhwFnf`z5aa8zo;RLo+;)AKIJD@@-A5HnO==i@Bx1rAX=a4Z6<+7d(XX0R3 z#@`JI@Q&lag@7Ir2byu-ZLM%)W8ppHQG?kYNd8gm1m?4sQpw~`e};+TcDamQCq$7c zZO#PDgV?azqfUatB9EF01{X6QN*G6UgF!0D1dG68unMdJTbOh?e{<|`b5otz5}9D6 z-l;b89}^Wu0w#~^}NC|0#xy~$^_fD^9p7GPUqHn<@T71aQie_{-0=g-ibJd8Ss}MEd3Si! zLY@jo1ON56oQzyD&c5MM4Yc*(m(I97hCU4aDhjOUpFI|XqPr}Wm<;D`OD$S%sf<5a zDwtQs#{JP!Y4@^PPUz|K`IPTwsB$S^*}<~RyM4#<4&bm^mOA+#mTG;_@~LbTLD^2B zhbX`c%vB4HWUsYUe;^?XeL4DS^!*oFifAedJV`DkblTh^A1}o9gG0^=;|z6wF3!a!?#on z$^v1+Gz_Kgq98a#f%RexLx(U>1qwkNd6^i=4=|L2or=*tc6Ku~4e-u@HmMj)f7?>2 zAP$6q(YqM`0*|d~fCTZV7(=6ImBdqs7t50eA#{XSjo60~FaVsPcOn|;C3>ixIIm$+ z^NzIfkLnd6aY6Y zznvvnxZpwq4FL_H5G_Baq_EyubZ)L0|!AhC`5UN;fM$r+Ymgh zz0({XioVlyZxGYq4_S)B8{LvQ%kL&p`_J(YY&eIGLZvc)PQ z(GDa?*Bs`VMmDArKX2mU#8dumD_NS3VL;O3NK24(7As*AY0)Pbo=R|d@rEa9;l&zW z)ZcxM!B|5aWQdX$Vz?oOBBkP~h8JRZk``WH!}A(mp5ZCOleF-@I_Egpg%o>bK0`c@ zLPD7#b{gVmhS+3yrwvciV(??bJ7svChWCNtNm_Wv4DYDng(o^a<2!7Kq7Y)UAvPIe ziXrYZJV^_0x8dzDyxE3VYj~0t-s^_9#qd@d-bRP#Q=$-Jg(0pp#9BivbBIdP!Yehr zV#7OSc&iOh(!yJ5cq? z@FXp~xrR5}@Rk_fLxv}5;pGTV*8eO+tT4oBhA3$vP8A}&NrrdW@a{1@NeeH<@WvTl zm*FKDo}`60%J349{s*Nflu|*wAxc_^v4$9pl-WAT@S+S)(!v{L(zA&de#AWk6aWcH z;y71xjrBm^>0okOK326=%}4DCE)T0oxl2#ESAq{ z2Ky@Nj`@UysopgT)?B5=AJWd0Z?X1zD4zz$) zQ2hyOHZm1sZOC-eXOSIXJ#a%fcIJJ_79Je$reXg_{33sAkKGO+c0Ale#Ljf!a}CvZ zanm@{)1@EdsW>IA47uR+irdmG^owaxbwEo+mrHuRX=#!v6WEhtE63<%uTXfI@vpr6 z{dYSVnw6%Yxae-p=VQcedl&I+Y%K!ywD1xbOFR`!1ZiM8kRbLJ`0cU({WgPgXIsWy zs~psTHsEf>#E5JrGSTINRt%5euIb+8WS(rK0(Y;t!^EWrnu&`8N6`P#0oezZ+c*XCseBno>|?6|pgYJ`CqWy!z#W#@9T#BNccm*$#bQ{tT)7x6 z1nn5;01X&h4+_9MFp>R1;66dh$^Ac-GT9)F{B)23cl0&vC%`2f%Rm>fcl1Ah_%|0} z6dD1lAXJ0V6l|kFBd7#5=mK}I(vlDSWBBboB)j9(;Wz|5AP5wIdQc9od>cYGoM9+N zfMC!cHT0h|N{pbp&+J>wOdE3qlIB;FM4^{Et= zK?$=_#$YfR^sbdYa#bY^X`Gq!p7eiP{_<9H1nGR$Dwf{%ZMsni@o(@GCAA>Oep6Q*YV>vmvE$P@Vvefm%M!+l@|wm$)0k1LC6s z;?n}+O&m%WlU_c|`)tD(G1nxKUY#QGA-?(O{U^yXqu2$=X|eyKLn@G6!7(5^iH%$* zlQ`g84zCuJfhN-PMF+}&0*BGdF{hlRx(9;p$)bCZm=&@p*TBHlz? zap_ztftbG2qsn-C%%O!zJhbJ2zMz)pq$XtJXY3l@=azMXn?0xk%Rn|bM!6_>UGQcj zi;=P&>YPX%B;CWRaxWuEd?oP;;-`_5kXvwciI3~52`s_0Ngx!|;$a2eEeB2DG>}68 z_mDs1_qKWfWP`wv|9hF8Jz-ypM|~fB-$(e7^L^jz;g9P8*VCpxdh#&uEeZe1i>bk= z@lwbM?$bmnNs7&X0=aJ)9}|NVT|%i~?+ZK=fKJ^&vR~H$j(I?5s{VAC_vSvaLwK+X z)7M6OhX%_!opn8nf%e3BZ{VeP-jLuNbUEmD=$X-2lcjqQ2fd*Vg-`!N`oyD)zg2G% z6O;a#%lbAhX?;A}8x_PA&%2Vpj`j|dXmx@f9^?I)Cqdtb@NyO~$T71b#vA5I)N4h{ N^YW=@%fGzg{{e?`TyFpX diff --git a/3rdparty/lib/armeabi-v7a/libnative_camera_r3.0.1.so b/3rdparty/lib/armeabi-v7a/libnative_camera_r3.0.1.so index 80bf459cc114e69f17e940e964f741141e710e60..f8a9cad2c2379d6e214cf67a28434cb3b7569f8f 100755 GIT binary patch delta 79731 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