diff --git a/CMakeLists.txt b/CMakeLists.txt index fc53066c82..4c9b98ef5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ endif() option(ENABLE_PIC "Generate position independent code (necessary for shared libraries)" TRUE) set(CMAKE_POSITION_INDEPENDENT_CODE ${ENABLE_PIC}) -# Following block can break build in case of cross-compilng +# Following block can break build in case of cross-compiling # but CMAKE_CROSSCOMPILING variable will be set only on project(OpenCV) command # so we will try to detect cross-compiling by the presence of CMAKE_TOOLCHAIN_FILE if(NOT DEFINED CMAKE_INSTALL_PREFIX) @@ -43,17 +43,17 @@ if(NOT DEFINED CMAKE_INSTALL_PREFIX) else() set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Installation Directory") endif() - else(NOT CMAKE_TOOLCHAIN_FILE) + else() #Android: set output folder to ${CMAKE_BINARY_DIR} - set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_BINARY_DIR} CACHE PATH "root for library output, set this to change where android libs are compiled to" ) + set(LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_BINARY_DIR} CACHE PATH "root for library output, set this to change where android libs are compiled to" ) # any cross-compiling set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Installation Directory") - endif(NOT CMAKE_TOOLCHAIN_FILE) + endif() endif() if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore) set(WINRT TRUE) -endif(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore) +endif() if(WINRT) add_definitions(-DWINRT -DNO_GETENV) @@ -315,7 +315,7 @@ OCV_OPTION(INSTALL_TESTS "Install accuracy and performance test binar # OpenCV build options # =================================================== OCV_OPTION(ENABLE_CCACHE "Use ccache" (UNIX AND NOT IOS AND (CMAKE_GENERATOR MATCHES "Makefile" OR CMAKE_GENERATOR MATCHES "Ninja")) ) -OCV_OPTION(ENABLE_PRECOMPILED_HEADERS "Use precompiled headers" ON IF (NOT IOS AND NOT CMAKE_CROSSCOMPILING) ) +OCV_OPTION(ENABLE_PRECOMPILED_HEADERS "Use precompiled headers" ON IF (MSVC OR (NOT IOS AND NOT CMAKE_CROSSCOMPILING) ) ) OCV_OPTION(ENABLE_SOLUTION_FOLDERS "Solution folder in Visual Studio or in other IDEs" (MSVC_IDE OR CMAKE_GENERATOR MATCHES Xcode) ) OCV_OPTION(ENABLE_PROFILING "Enable profiling in the GCC compiler (Add flags: -g -pg)" OFF IF CV_GCC ) OCV_OPTION(ENABLE_COVERAGE "Enable coverage collection with GCov" OFF IF CV_GCC ) @@ -339,8 +339,8 @@ OCV_OPTION(CV_ENABLE_INTRINSICS "Use intrinsic-based optimized code" ON ) OCV_OPTION(CV_DISABLE_OPTIMIZATION "Disable explicit optimized code (dispatched code/intrinsics/loop unrolling/etc)" OFF ) OCV_OPTION(CV_TRACE "Enable OpenCV code trace" ON) -OCV_OPTION(ENABLE_PYLINT "Add target with Pylint checks" (${BUILD_DOCS} OR ${BUILD_EXAMPLES}) IF (NOT CMAKE_CROSSCOMPILING AND NOT APPLE_FRAMEWORK) ) -OCV_OPTION(ENABLE_FLAKE8 "Add target with Python flake8 checker" (${BUILD_DOCS} OR ${BUILD_EXAMPLES}) IF (NOT CMAKE_CROSSCOMPILING AND NOT APPLE_FRAMEWORK) ) +OCV_OPTION(ENABLE_PYLINT "Add target with Pylint checks" (BUILD_DOCS OR BUILD_EXAMPLES) IF (NOT CMAKE_CROSSCOMPILING AND NOT APPLE_FRAMEWORK) ) +OCV_OPTION(ENABLE_FLAKE8 "Add target with Python flake8 checker" (BUILD_DOCS OR BUILD_EXAMPLES) IF (NOT CMAKE_CROSSCOMPILING AND NOT APPLE_FRAMEWORK) ) if(ENABLE_IMPL_COLLECTION) add_definitions(-DCV_COLLECT_IMPL_DATA) @@ -1246,7 +1246,9 @@ if(WITH_1394 OR HAVE_DC1394) endif() if(WITH_FFMPEG OR HAVE_FFMPEG) - if(WIN32) + if(OPENCV_FFMPEG_USE_FIND_PACKAGE) + status(" FFMPEG:" HAVE_FFMPEG THEN "YES (find_package)" ELSE "NO (find_package)") + elseif(WIN32) status(" FFMPEG:" HAVE_FFMPEG THEN "YES (prebuilt binaries)" ELSE NO) else() status(" FFMPEG:" HAVE_FFMPEG THEN YES ELSE NO) diff --git a/cmake/FindCUDA.cmake b/cmake/FindCUDA.cmake index bbdfb91a07..632b8c8285 100644 --- a/cmake/FindCUDA.cmake +++ b/cmake/FindCUDA.cmake @@ -1042,7 +1042,7 @@ function(CUDA_COMPUTE_BUILD_PATH path build_path) # Only deal with CMake style paths from here on out file(TO_CMAKE_PATH "${path}" bpath) if (IS_ABSOLUTE "${bpath}") - # Absolute paths are generally unnessary, especially if something like + # Absolute paths are generally unnecessary, especially if something like # file(GLOB_RECURSE) is used to pick up the files. string(FIND "${bpath}" "${CMAKE_CURRENT_BINARY_DIR}" _binary_dir_pos) @@ -1065,7 +1065,7 @@ function(CUDA_COMPUTE_BUILD_PATH path build_path) # Avoid spaces string(REPLACE " " "_" bpath "${bpath}") - # Strip off the filename. I wait until here to do it, since removin the + # Strip off the filename. I wait until here to do it, since removing the # basename can make a path that looked like path/../basename turn into # path/.. (notice the trailing slash). get_filename_component(bpath "${bpath}" PATH) @@ -1362,7 +1362,7 @@ macro(CUDA_WRAP_SRCS cuda_target format generated_files) # Bring in the dependencies. Creates a variable CUDA_NVCC_DEPEND ####### cuda_include_nvcc_dependencies(${cmake_dependency_file}) - # Convience string for output ########################################### + # Convenience string for output ########################################### if(CUDA_BUILD_EMULATION) set(cuda_build_type "Emulation") else() @@ -1563,7 +1563,7 @@ macro(CUDA_ADD_LIBRARY cuda_target) ${_cmake_options} ${_cuda_shared_flag} OPTIONS ${_options} ) - # Compute the file name of the intermedate link file used for separable + # Compute the file name of the intermediate link file used for separable # compilation. CUDA_COMPUTE_SEPARABLE_COMPILATION_OBJECT_FILE_NAME(link_file ${cuda_target} "${${cuda_target}_SEPARABLE_COMPILATION_OBJECTS}") @@ -1607,7 +1607,7 @@ macro(CUDA_ADD_EXECUTABLE cuda_target) # Create custom commands and targets for each file. CUDA_WRAP_SRCS( ${cuda_target} OBJ _generated_files ${_sources} OPTIONS ${_options} ) - # Compute the file name of the intermedate link file used for separable + # Compute the file name of the intermediate link file used for separable # compilation. CUDA_COMPUTE_SEPARABLE_COMPILATION_OBJECT_FILE_NAME(link_file ${cuda_target} "${${cuda_target}_SEPARABLE_COMPILATION_OBJECTS}") @@ -1723,7 +1723,7 @@ endmacro() ############################################################################### ############################################################################### macro(CUDA_BUILD_CLEAN_TARGET) - # Call this after you add all your CUDA targets, and you will get a convience + # Call this after you add all your CUDA targets, and you will get a convenience # target. You should also make clean after running this target to get the # build system to generate all the code again. diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index d83777fe4b..30e4a00a3f 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -1,5 +1,5 @@ if("${CMAKE_CXX_COMPILER};${CMAKE_C_COMPILER};${CMAKE_CXX_COMPILER_LAUNCHER}" MATCHES "ccache") - set(CMAKE_COMPILER_IS_CCACHE 1) # FIXIT Avoid setting of CMAKE_ variables + set(CMAKE_COMPILER_IS_CCACHE 1) # TODO: FIXIT Avoid setting of CMAKE_ variables set(OPENCV_COMPILER_IS_CCACHE 1) endif() function(access_CMAKE_COMPILER_IS_CCACHE) diff --git a/cmake/OpenCVDetectInferenceEngine.cmake b/cmake/OpenCVDetectInferenceEngine.cmake index d33a8d966d..ae766a863a 100644 --- a/cmake/OpenCVDetectInferenceEngine.cmake +++ b/cmake/OpenCVDetectInferenceEngine.cmake @@ -38,8 +38,7 @@ set(INF_ENGINE_INCLUDE_DIRS "${INF_ENGINE_ROOT_DIR}/include" CACHE PATH "Path to if(NOT INF_ENGINE_ROOT_DIR OR NOT EXISTS "${INF_ENGINE_ROOT_DIR}" - OR NOT EXISTS "${INF_ENGINE_INCLUDE_DIRS}" - OR NOT EXISTS "${INF_ENGINE_INCLUDE_DIRS}/inference_engine.hpp" + OR NOT EXISTS "${INF_ENGINE_ROOT_DIR}/include/inference_engine.hpp" ) ie_fail() endif() @@ -49,10 +48,7 @@ set(INF_ENGINE_LIBRARIES "") set(ie_lib_list inference_engine) link_directories( - ${INTEL_CVSDK_DIR}/external/mklml_lnx/lib - ${INTEL_CVSDK_DIR}/inference_engine/external/mklml_lnx/lib ${INTEL_CVSDK_DIR}/inference_engine/external/mkltiny_lnx/lib - ${INTEL_CVSDK_DIR}/external/cldnn/lib ${INTEL_CVSDK_DIR}/inference_engine/external/cldnn/lib ) diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index e12a04656f..e3bd8384ab 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -43,7 +43,7 @@ endif(WITH_IPP_A) if(WITH_CUDA) include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDA.cmake") if(NOT HAVE_CUDA) - message(WARNING "OpenCV is not able to find/confidure CUDA SDK (required by WITH_CUDA). + message(WARNING "OpenCV is not able to find/configure CUDA SDK (required by WITH_CUDA). CUDA support will be disabled in OpenCV build. To eliminate this warning remove WITH_CUDA=ON CMake configuration option. ") diff --git a/cmake/OpenCVFindLibsVideo.cmake b/cmake/OpenCVFindLibsVideo.cmake index 50dc12a482..fb1b92ec5f 100644 --- a/cmake/OpenCVFindLibsVideo.cmake +++ b/cmake/OpenCVFindLibsVideo.cmake @@ -212,12 +212,23 @@ endif(WITH_XIMEA) # --- FFMPEG --- ocv_clear_vars(HAVE_FFMPEG) -if(WITH_FFMPEG) - if(WIN32 AND NOT ARM) +if(WITH_FFMPEG) # try FFmpeg autodetection + if(OPENCV_FFMPEG_USE_FIND_PACKAGE) + if(OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "1" OR OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "ON") + set(OPENCV_FFMPEG_USE_FIND_PACKAGE "FFMPEG") + endif() + find_package(${OPENCV_FFMPEG_USE_FIND_PACKAGE}) # Required components: AVCODEC AVFORMAT AVUTIL SWSCALE + if(FFMPEG_FOUND OR FFmpeg_FOUND) + set(HAVE_FFMPEG TRUE) + else() + message(STATUS "Can't find FFmpeg via find_package(${OPENCV_FFMPEG_USE_FIND_PACKAGE})") + endif() + elseif(WIN32 AND NOT ARM AND NOT OPENCV_FFMPEG_SKIP_DOWNLOAD) include("${OpenCV_SOURCE_DIR}/3rdparty/ffmpeg/ffmpeg.cmake") download_win_ffmpeg(FFMPEG_CMAKE_SCRIPT) if(FFMPEG_CMAKE_SCRIPT) set(HAVE_FFMPEG TRUE) + set(HAVE_FFMPEG_WRAPPER 1) include("${FFMPEG_CMAKE_SCRIPT}") endif() elseif(PKG_CONFIG_FOUND) @@ -226,27 +237,29 @@ if(WITH_FFMPEG) if(FFMPEG_libavresample_FOUND) ocv_append_build_options(FFMPEG FFMPEG_libavresample) endif() - if(HAVE_FFMPEG) - try_compile(__VALID_FFMPEG - "${OpenCV_BINARY_DIR}" - "${OpenCV_SOURCE_DIR}/cmake/checks/ffmpeg_test.cpp" - CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${FFMPEG_INCLUDE_DIRS}" - "-DLINK_DIRECTORIES:STRING=${FFMPEG_LIBRARY_DIRS}" - "-DLINK_LIBRARIES:STRING=${FFMPEG_LIBRARIES}" - OUTPUT_VARIABLE TRY_OUT - ) - if(NOT __VALID_FFMPEG) - #message(FATAL_ERROR "FFMPEG: test check build log:\n${TRY_OUT}") - message(STATUS "WARNING: Can't build ffmpeg test code") - set(HAVE_FFMPEG FALSE) - else() - ocv_append_build_options(VIDEOIO FFMPEG) - endif() - endif() else() message(STATUS "Can't find ffmpeg - 'pkg-config' utility is missing") endif() -endif(WITH_FFMPEG) +endif() +if(HAVE_FFMPEG + AND NOT HAVE_FFMPEG_WRAPPER +) + try_compile(__VALID_FFMPEG + "${OpenCV_BINARY_DIR}" + "${OpenCV_SOURCE_DIR}/cmake/checks/ffmpeg_test.cpp" + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${FFMPEG_INCLUDE_DIRS}" + "-DLINK_DIRECTORIES:STRING=${FFMPEG_LIBRARY_DIRS}" + "-DLINK_LIBRARIES:STRING=${FFMPEG_LIBRARIES}" + OUTPUT_VARIABLE TRY_OUT + ) + if(NOT __VALID_FFMPEG) + #message(FATAL_ERROR "FFMPEG: test check build log:\n${TRY_OUT}") + message(STATUS "WARNING: Can't build ffmpeg test code") + set(HAVE_FFMPEG FALSE) + else() + ocv_append_build_options(VIDEOIO FFMPEG) + endif() +endif() # --- VideoInput/DirectShow --- if(WITH_DSHOW) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 93b6123eba..db439b3981 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -455,7 +455,7 @@ function(__ocv_sort_modules_by_deps __lst) set(${__lst} "${result};${result_extra}" PARENT_SCOPE) endfunction() -# resolve dependensies +# resolve dependencies function(__ocv_resolve_dependencies) foreach(m ${OPENCV_MODULES_DISABLED_USER}) set(HAVE_${m} OFF CACHE INTERNAL "Module ${m} will not be built in current configuration") @@ -727,7 +727,7 @@ macro(ocv_set_module_sources) endif() endforeach() - # the hacky way to embeed any files into the OpenCV without modification of its build system + # the hacky way to embed any files into the OpenCV without modification of its build system if(COMMAND ocv_get_module_external_sources) ocv_get_module_external_sources() endif() @@ -958,7 +958,7 @@ macro(_ocv_create_module) target_compile_definitions(${the_module} PRIVATE CVAPI_EXPORTS) endif() - # For dynamic link numbering convenions + # For dynamic link numbering conventions if(NOT ANDROID) # Android SDK build scripts can include only .so files into final .apk # As result we should not set version properties for Android diff --git a/cmake/OpenCVPCHSupport.cmake b/cmake/OpenCVPCHSupport.cmake index b1dd60e849..b4658c604b 100644 --- a/cmake/OpenCVPCHSupport.cmake +++ b/cmake/OpenCVPCHSupport.cmake @@ -383,7 +383,7 @@ MACRO(ADD_NATIVE_PRECOMPILED_HEADER _targetName _input) # For Xcode, cmake needs my patch to process # GCC_PREFIX_HEADER and GCC_PRECOMPILE_PREFIX_HEADER as target properties - # When buiding out of the tree, precompiled may not be located + # When building out of the tree, precompiled may not be located # Use full path instead. GET_FILENAME_COMPONENT(fullPath ${_input} ABSOLUTE) diff --git a/doc/opencv.bib b/doc/opencv.bib index 1cb3d0b0af..edb7033e8d 100644 --- a/doc/opencv.bib +++ b/doc/opencv.bib @@ -20,6 +20,21 @@ volume = {34}, number = {7} } +@INPROCEEDINGS{Arandjelovic:2012:TTE:2354409.2355123, + author = {Arandjelovic, Relja}, + title = {Three Things Everyone Should Know to Improve Object Retrieval}, + booktitle = {Proceedings of the 2012 IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, + series = {CVPR '12}, + year = {2012}, + isbn = {978-1-4673-1226-4}, + pages = {2911--2918}, + numpages = {8}, + url = {http://dl.acm.org/citation.cfm?id=2354409.2355123}, + acmid = {2355123}, + publisher = {IEEE Computer Society}, + address = {Washington, DC, USA}, + keywords = {Vectors,Visualization,Kernel,Standards,Support vector machines,Indexes,Euclidean distance}, +} @ARTICLE{BA83, author = {Burt, Peter J and Adelson, Edward H}, title = {A multiresolution spline with application to image mosaics}, @@ -515,6 +530,25 @@ volume = {1}, organization = {IEEE} } +@ARTICLE{Lowe:2004:DIF:993451.996342, + author = {Lowe, David G.}, + title = {Distinctive Image Features from Scale-Invariant Keypoints}, + journal = {Int. J. Comput. Vision}, + issue_date = {November 2004}, + volume = {60}, + number = {2}, + month = nov, + year = {2004}, + issn = {0920-5691}, + pages = {91--110}, + numpages = {20}, + url = {https://doi.org/10.1023/B:VISI.0000029664.99615.94}, + doi = {10.1023/B:VISI.0000029664.99615.94}, + acmid = {996342}, + publisher = {Kluwer Academic Publishers}, + address = {Hingham, MA, USA}, + keywords = {image matching, invariant features, object recognition, scale invariance}, +} @INPROCEEDINGS{Lucas81, author = {Lucas, Bruce D and Kanade, Takeo and others}, title = {An iterative image registration technique with an application to stereo vision.}, diff --git a/doc/tools/html_functions.py b/doc/tools/html_functions.py index c3fd3bd8e9..b76639cea5 100644 --- a/doc/tools/html_functions.py +++ b/doc/tools/html_functions.py @@ -3,6 +3,7 @@ import sys import logging import os +import re from pprint import pprint import traceback @@ -17,12 +18,20 @@ except ImportError: def load_html_file(file_dir): """ Uses BeautifulSoup to load an html """ with open(file_dir, 'rb') as fp: - soup = BeautifulSoup(fp, 'html.parser') + data = fp.read() + if os.name == 'nt' or sys.version_info[0] == 3: + data = data.decode(encoding='utf-8', errors='strict') + data = re.sub(r'(\>)([ ]+)', lambda match: match.group(1) + ('!space!' * len(match.group(2))), data) + data = re.sub(r'([ ]+)(\<)', lambda match: ('!space!' * len(match.group(1))) + match.group(2), data) + if os.name == 'nt' or sys.version_info[0] == 3: + data = data.encode('utf-8', 'ignore') + soup = BeautifulSoup(data, 'html.parser') return soup def update_html(file, soup): s = str(soup) - if os.name == 'nt' or sys.version_info[0] == 3: # if Windows + s = s.replace('!space!', ' ') + if os.name == 'nt' or sys.version_info[0] == 3: s = s.encode('utf-8', 'ignore') with open(file, 'wb') as f: f.write(s) diff --git a/doc/tutorials/features2d/feature_description/feature_description.markdown b/doc/tutorials/features2d/feature_description/feature_description.markdown index eea5a29c1d..ec3cd0e4c5 100644 --- a/doc/tutorials/features2d/feature_description/feature_description.markdown +++ b/doc/tutorials/features2d/feature_description/feature_description.markdown @@ -10,74 +10,35 @@ In this tutorial you will learn how to: to the keypoints. Specifically: - Use cv::xfeatures2d::SURF and its function cv::xfeatures2d::SURF::compute to perform the required calculations. - - Use a @ref cv::BFMatcher to match the features vector + - Use a @ref cv::DescriptorMatcher to match the features vector - Use the function @ref cv::drawMatches to draw the detected matches. +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/xfeatures2d.hpp" - -using namespace cv; -using namespace cv::xfeatures2d; - -void readme(); - -/* @function main */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { return -1; } - - Mat img_1 = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_2 = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_1.data || !img_2.data ) - { return -1; } - - //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors - int minHessian = 400; - - Ptr detector = SURF::create(); - detector->setHessianThreshold(minHessian); - - std::vector keypoints_1, keypoints_2; - Mat descriptors_1, descriptors_2; - - detector->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1 ); - detector->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2 ); - - //-- Step 2: Matching descriptor vectors with a brute force matcher - BFMatcher matcher(NORM_L2); - std::vector< DMatch > matches; - matcher.match( descriptors_1, descriptors_2, matches ); - - //-- Draw matches - Mat img_matches; - drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches ); - - //-- Show detected matches - imshow("Matches", img_matches ); - - waitKey(0); - - return 0; - } - - /* @function readme */ - void readme() - { std::cout << " Usage: ./SURF_descriptor " << std::endl; } -@endcode +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_description/SURF_matching_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_description/SURF_matching_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java) +@include samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py) +@include samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py +@end_toggle Explanation ----------- diff --git a/doc/tutorials/features2d/feature_detection/feature_detection.markdown b/doc/tutorials/features2d/feature_detection/feature_detection.markdown index 8b2f423ee2..d0996512ef 100644 --- a/doc/tutorials/features2d/feature_detection/feature_detection.markdown +++ b/doc/tutorials/features2d/feature_detection/feature_detection.markdown @@ -11,67 +11,32 @@ In this tutorial you will learn how to: detection process - Use the function @ref cv::drawKeypoints to draw the detected keypoints +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/xfeatures2d.hpp" -#include "opencv2/highgui.hpp" - -using namespace cv; -using namespace cv::xfeatures2d; - -void readme(); - -/* @function main */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { readme(); return -1; } - - Mat img_1 = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_2 = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_1.data || !img_2.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } - - //-- Step 1: Detect the keypoints using SURF Detector - int minHessian = 400; - - Ptr detector = SURF::create( minHessian ); - - std::vector keypoints_1, keypoints_2; - - detector->detect( img_1, keypoints_1 ); - detector->detect( img_2, keypoints_2 ); - - //-- Draw keypoints - Mat img_keypoints_1; Mat img_keypoints_2; - - drawKeypoints( img_1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT ); - drawKeypoints( img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT ); - - //-- Show detected (drawn) keypoints - imshow("Keypoints 1", img_keypoints_1 ); - imshow("Keypoints 2", img_keypoints_2 ); - - waitKey(0); +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp +@end_toggle - return 0; - } +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java) +@include samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java +@end_toggle - /* @function readme */ - void readme() - { std::cout << " Usage: ./SURF_detector " << std::endl; } -@endcode +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py) +@include samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py +@end_toggle Explanation ----------- @@ -79,10 +44,10 @@ Explanation Result ------ --# Here is the result of the feature detection applied to the first image: +-# Here is the result of the feature detection applied to the `box.png` image: ![](images/Feature_Detection_Result_a.jpg) --# And here is the result for the second image: +-# And here is the result for the `box_in_scene.png` image: ![](images/Feature_Detection_Result_b.jpg) diff --git a/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown b/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown index 8243b430a6..e7f865c3ce 100644 --- a/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown +++ b/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.markdown @@ -9,114 +9,57 @@ In this tutorial you will learn how to: - Use the @ref cv::FlannBasedMatcher interface in order to perform a quick and efficient matching by using the @ref flann module +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ +Classical feature descriptors (SIFT, SURF, ...) are usually compared and matched using the Euclidean distance (or L2-norm). +Since SIFT and SURF descriptors represent the histogram of oriented gradient (of the Haar wavelet response for SURF) +in a neighborhood, alternatives of the Euclidean distance are histogram-based metrics (\f$ \chi^{2} \f$, Earth Mover’s Distance (EMD), ...). + +Arandjelovic et al. proposed in @cite Arandjelovic:2012:TTE:2354409.2355123 to extend to the RootSIFT descriptor: +> a square root (Hellinger) kernel instead of the standard Euclidean distance to measure the similarity between SIFT descriptors +> leads to a dramatic performance boost in all stages of the pipeline. + +Binary descriptors (ORB, BRISK, ...) are matched using the Hamming distance. +This distance is equivalent to count the number of different elements for binary strings (population count after applying a XOR operation): +\f[ d_{hamming} \left ( a,b \right ) = \sum_{i=0}^{n-1} \left ( a_i \oplus b_i \right ) \f] + +To filter the matches, Lowe proposed in @cite Lowe:2004:DIF:993451.996342 to use a distance ratio test to try to eliminate false matches. +The distance ratio between the two nearest matches of a considered keypoint is computed and it is a good match when this value is below +a thresold. Indeed, this ratio allows helping to discriminate between ambiguous matches (distance ratio between the two nearest neighbors is +close to one) and well discriminated matches. The figure below from the SIFT paper illustrates the probability that a match is correct +based on the nearest-neighbor distance ratio test. + +![](images/Feature_FlannMatcher_Lowe_ratio_test.png) + +Alternative or additional filterering tests are: +- cross check test (good match \f$ \left( f_a, f_b \right) \f$ if feature \f$ f_b \f$ is the best match for \f$ f_a \f$ in \f$ I_b \f$ + and feature \f$ f_a \f$ is the best match for \f$ f_b \f$ in \f$ I_a \f$) +- geometric test (eliminate matches that do not fit to a geometric model, e.g. RANSAC or robust homography for planar objects) + Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -/* - * @file SURF_FlannMatcher - * @brief SURF detector + descriptor + FLANN Matcher - * @author A. Huaman - */ - -#include -#include -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/imgcodecs.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/xfeatures2d.hpp" - -using namespace std; -using namespace cv; -using namespace cv::xfeatures2d; - -void readme(); - -/* - * @function main - * @brief Main function - */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { readme(); return -1; } - - Mat img_1 = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_2 = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_1.data || !img_2.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } - - //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors - int minHessian = 400; - - Ptr detector = SURF::create(); - detector->setHessianThreshold(minHessian); - - std::vector keypoints_1, keypoints_2; - Mat descriptors_1, descriptors_2; - - detector->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1 ); - detector->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2 ); - - //-- Step 2: Matching descriptor vectors using FLANN matcher - FlannBasedMatcher matcher; - std::vector< DMatch > matches; - matcher.match( descriptors_1, descriptors_2, matches ); - - double max_dist = 0; double min_dist = 100; - - //-- Quick calculation of max and min distances between keypoints - for( int i = 0; i < descriptors_1.rows; i++ ) - { double dist = matches[i].distance; - if( dist < min_dist ) min_dist = dist; - if( dist > max_dist ) max_dist = dist; - } - - printf("-- Max dist : %f \n", max_dist ); - printf("-- Min dist : %f \n", min_dist ); - - //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist, - //-- or a small arbitrary value ( 0.02 ) in the event that min_dist is very - //-- small) - //-- PS.- radiusMatch can also be used here. - std::vector< DMatch > good_matches; - - for( int i = 0; i < descriptors_1.rows; i++ ) - { if( matches[i].distance <= max(2*min_dist, 0.02) ) - { good_matches.push_back( matches[i]); } - } - - //-- Draw only "good" matches - Mat img_matches; - drawMatches( img_1, keypoints_1, img_2, keypoints_2, - good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), - vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); - - //-- Show detected matches - imshow( "Good Matches", img_matches ); - - for( int i = 0; i < (int)good_matches.size(); i++ ) - { printf( "-- Good Match [%d] Keypoint 1: %d -- Keypoint 2: %d \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx ); } - - waitKey(0); - - return 0; -} - -/* - * @function readme - */ -void readme() -{ std::cout << " Usage: ./SURF_FlannMatcher " << std::endl; } -@endcode +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java) +@include samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py) +@include samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py +@end_toggle Explanation ----------- @@ -124,10 +67,6 @@ Explanation Result ------ --# Here is the result of the feature detection applied to the first image: - - ![](images/Featur_FlannMatcher_Result.jpg) - --# Additionally, we get as console output the keypoints filtered: +- Here is the result of the SURF feature matching using the distance ratio test: - ![](images/Feature_FlannMatcher_Keypoints_Result.jpg) + ![](images/Feature_FlannMatcher_Result_ratio_test.jpg) diff --git a/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Lowe_ratio_test.png b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Lowe_ratio_test.png new file mode 100644 index 0000000000..f56a640b67 Binary files /dev/null and b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Lowe_ratio_test.png differ diff --git a/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Result_ratio_test.jpg b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Result_ratio_test.jpg new file mode 100644 index 0000000000..e4a88d0020 Binary files /dev/null and b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Result_ratio_test.jpg differ diff --git a/doc/tutorials/features2d/feature_homography/feature_homography.markdown b/doc/tutorials/features2d/feature_homography/feature_homography.markdown index ec7913c330..c4f0c00e55 100644 --- a/doc/tutorials/features2d/feature_homography/feature_homography.markdown +++ b/doc/tutorials/features2d/feature_homography/feature_homography.markdown @@ -9,125 +9,40 @@ In this tutorial you will learn how to: - Use the function @ref cv::findHomography to find the transform between matched keypoints. - Use the function @ref cv::perspectiveTransform to map the points. +\warning You need the OpenCV contrib modules to be able to use the SURF features +(alternatives are ORB, KAZE, ... features). + Theory ------ Code ---- -This tutorial code's is shown lines below. -@code{.cpp} -#include -#include -#include "opencv2/core.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/features2d.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/calib3d.hpp" -#include "opencv2/xfeatures2d.hpp" - -using namespace cv; -using namespace cv::xfeatures2d; - -void readme(); - -/* @function main */ -int main( int argc, char** argv ) -{ - if( argc != 3 ) - { readme(); return -1; } - - Mat img_object = imread( argv[1], IMREAD_GRAYSCALE ); - Mat img_scene = imread( argv[2], IMREAD_GRAYSCALE ); - - if( !img_object.data || !img_scene.data ) - { std::cout<< " --(!) Error reading images " << std::endl; return -1; } - - //-- Step 1: Detect the keypoints and extract descriptors using SURF - int minHessian = 400; - - Ptr detector = SURF::create( minHessian ); - - std::vector keypoints_object, keypoints_scene; - Mat descriptors_object, descriptors_scene; - - detector->detectAndCompute( img_object, Mat(), keypoints_object, descriptors_object ); - detector->detectAndCompute( img_scene, Mat(), keypoints_scene, descriptors_scene ); - - //-- Step 2: Matching descriptor vectors using FLANN matcher - FlannBasedMatcher matcher; - std::vector< DMatch > matches; - matcher.match( descriptors_object, descriptors_scene, matches ); - - double max_dist = 0; double min_dist = 100; - - //-- Quick calculation of max and min distances between keypoints - for( int i = 0; i < descriptors_object.rows; i++ ) - { double dist = matches[i].distance; - if( dist < min_dist ) min_dist = dist; - if( dist > max_dist ) max_dist = dist; - } - - printf("-- Max dist : %f \n", max_dist ); - printf("-- Min dist : %f \n", min_dist ); - - //-- Draw only "good" matches (i.e. whose distance is less than 3*min_dist ) - std::vector< DMatch > good_matches; - - for( int i = 0; i < descriptors_object.rows; i++ ) - { if( matches[i].distance <= 3*min_dist ) - { good_matches.push_back( matches[i]); } - } - - Mat img_matches; - drawMatches( img_object, keypoints_object, img_scene, keypoints_scene, - good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), - std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); - - //-- Localize the object - std::vector obj; - std::vector scene; - - for( size_t i = 0; i < good_matches.size(); i++ ) - { - //-- Get the keypoints from the good matches - obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt ); - scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt ); - } - - Mat H = findHomography( obj, scene, RANSAC ); - - //-- Get the corners from the image_1 ( the object to be "detected" ) - std::vector obj_corners(4); - obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( img_object.cols, 0 ); - obj_corners[2] = cvPoint( img_object.cols, img_object.rows ); obj_corners[3] = cvPoint( 0, img_object.rows ); - std::vector scene_corners(4); - - perspectiveTransform( obj_corners, scene_corners, H); - - //-- Draw lines between the corners (the mapped object in the scene - image_2 ) - line( img_matches, scene_corners[0] + Point2f( img_object.cols, 0), scene_corners[1] + Point2f( img_object.cols, 0), Scalar(0, 255, 0), 4 ); - line( img_matches, scene_corners[1] + Point2f( img_object.cols, 0), scene_corners[2] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); - line( img_matches, scene_corners[2] + Point2f( img_object.cols, 0), scene_corners[3] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); - line( img_matches, scene_corners[3] + Point2f( img_object.cols, 0), scene_corners[0] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp) +@include samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp +@end_toggle - //-- Show detected matches - imshow( "Good Matches & Object detection", img_matches ); +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java) +@include samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java +@end_toggle - waitKey(0); - return 0; - } +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py) +@include samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py +@end_toggle - /* @function readme */ - void readme() - { std::cout << " Usage: ./SURF_descriptor " << std::endl; } -@endcode Explanation ----------- Result ------ --# And here is the result for the detected object (highlighted in green) +- And here is the result for the detected object (highlighted in green). Note that since the homography is estimated with a RANSAC approach, + detected false matches will not impact the homography calculation. ![](images/Feature_Homography_Result.jpg) diff --git a/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg b/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg index d043a5a6e9..35b1bb634c 100644 Binary files a/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg and b/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg differ diff --git a/doc/tutorials/features2d/table_of_content_features2d.markdown b/doc/tutorials/features2d/table_of_content_features2d.markdown index a5d5a91676..37e4e41a1d 100644 --- a/doc/tutorials/features2d/table_of_content_features2d.markdown +++ b/doc/tutorials/features2d/table_of_content_features2d.markdown @@ -6,39 +6,51 @@ OpenCV. - @subpage tutorial_harris_detector + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán - Why is it a good idea to track corners? We learn to use the Harris method to detect - corners + Why is it a good idea to track corners? We learn how to use the Harris method to detect + corners. - @subpage tutorial_good_features_to_track + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán - Where we use an improved method to detect corners more accuratelyI + Where we use an improved method to detect corners more accurately. - @subpage tutorial_generic_corner_detector + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán Here you will learn how to use OpenCV functions to make your personalized corner detector! -- @subpage tutorial_corner_subpixeles + *Languages:* C++, Java, Python + +- @subpage tutorial_corner_subpixels + + *Languages:* C++, Java, Python *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán - Is pixel resolution enough? Here we learn a simple method to improve our accuracy. + Is pixel resolution enough? Here we learn a simple method to improve our corner location accuracy. - @subpage tutorial_feature_detection + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -47,6 +59,8 @@ OpenCV. - @subpage tutorial_feature_description + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -55,6 +69,8 @@ OpenCV. - @subpage tutorial_feature_flann_matcher + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -63,6 +79,8 @@ OpenCV. - @subpage tutorial_feature_homography + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/corner_subpixeles.markdown b/doc/tutorials/features2d/trackingmotion/corner_subpixeles/corner_subpixeles.markdown deleted file mode 100644 index 946fd77b29..0000000000 --- a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/corner_subpixeles.markdown +++ /dev/null @@ -1,32 +0,0 @@ -Detecting corners location in subpixeles {#tutorial_corner_subpixeles} -======================================== - -Goal ----- - -In this tutorial you will learn how to: - -- Use the OpenCV function @ref cv::cornerSubPix to find more exact corner positions (more exact - than integer pixels). - -Theory ------- - -Code ----- - -This tutorial code's is shown lines below. You can also download it from -[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp) -@include samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp - -Explanation ------------ - -Result ------- - -![](images/Corner_Subpixeles_Original_Image.jpg) - -Here is the result: - -![](images/Corner_Subpixeles_Result.jpg) diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixels/corner_subpixels.markdown b/doc/tutorials/features2d/trackingmotion/corner_subpixels/corner_subpixels.markdown new file mode 100644 index 0000000000..82b33dd256 --- /dev/null +++ b/doc/tutorials/features2d/trackingmotion/corner_subpixels/corner_subpixels.markdown @@ -0,0 +1,46 @@ +Detecting corners location in subpixels {#tutorial_corner_subpixels} +======================================= + +Goal +---- + +In this tutorial you will learn how to: + +- Use the OpenCV function @ref cv::cornerSubPix to find more exact corner positions (more exact + than integer pixels). + +Theory +------ + +Code +---- + +@add_toggle_cpp +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp) +@include samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java) +@include samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py) +@include samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py +@end_toggle + +Explanation +----------- + +Result +------ + +![](images/Corner_Subpixels_Original_Image.jpg) + +Here is the result: + +![](images/Corner_Subpixels_Result.jpg) diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Original_Image.jpg b/doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Original_Image.jpg similarity index 100% rename from doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Original_Image.jpg rename to doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Original_Image.jpg diff --git a/doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Result.jpg b/doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Result.jpg similarity index 100% rename from doc/tutorials/features2d/trackingmotion/corner_subpixeles/images/Corner_Subpixeles_Result.jpg rename to doc/tutorials/features2d/trackingmotion/corner_subpixels/images/Corner_Subpixels_Result.jpg diff --git a/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown b/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown index 7aba636746..f10d3efe4e 100644 --- a/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown +++ b/doc/tutorials/features2d/trackingmotion/generic_corner_detector/generic_corner_detector.markdown @@ -1,5 +1,5 @@ -Creating yor own corner detector {#tutorial_generic_corner_detector} -================================ +Creating your own corner detector {#tutorial_generic_corner_detector} +================================= Goal ---- @@ -10,7 +10,7 @@ In this tutorial you will learn how to: to determine if a pixel is a corner. - Use the OpenCV function @ref cv::cornerMinEigenVal to find the minimum eigenvalues for corner detection. -- To implement our own version of the Harris detector as well as the Shi-Tomasi detector, by using +- Implement our own version of the Harris detector as well as the Shi-Tomasi detector, by using the two functions above. Theory @@ -19,10 +19,26 @@ Theory Code ---- +@add_toggle_cpp This tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp) -@include cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp +@include samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java) + +@include samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py) + +@include samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py +@end_toggle Explanation ----------- diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown b/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown index 7c48aa189a..70d25ab9e2 100644 --- a/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown +++ b/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.markdown @@ -6,7 +6,7 @@ Goal In this tutorial you will learn how to: -- Use the function @ref cv::goodFeaturesToTrack to detect corners using the Shi-Tomasi method. +- Use the function @ref cv::goodFeaturesToTrack to detect corners using the Shi-Tomasi method (@cite Shi94). Theory ------ @@ -14,9 +14,23 @@ Theory Code ---- +@add_toggle_cpp This tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp) @include samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java) +@include samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py) +@include samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py +@end_toggle Explanation ----------- @@ -24,4 +38,4 @@ Explanation Result ------ -![](images/Feature_Detection_Result_a.jpg) +![](images/good_features_to_track_Shi_Tomasi.jpg) diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg deleted file mode 100644 index cca9a2b438..0000000000 Binary files a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg and /dev/null differ diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg deleted file mode 100644 index 129450eb4f..0000000000 Binary files a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg and /dev/null differ diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/good_features_to_track_Shi_Tomasi.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/good_features_to_track_Shi_Tomasi.jpg new file mode 100644 index 0000000000..007f09a203 Binary files /dev/null and b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/good_features_to_track_Shi_Tomasi.jpg differ diff --git a/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown b/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown index b1b8b67f1b..bbf4fdbd5b 100644 --- a/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown +++ b/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.markdown @@ -118,9 +118,23 @@ In this tutorial we will study the *corner* features, specifically. Code ---- +@add_toggle_cpp This tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp) @include samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java) +@include samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py) +@include samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py +@end_toggle Explanation ----------- diff --git a/doc/tutorials/viz/launching_viz/launching_viz.markdown b/doc/tutorials/viz/launching_viz/launching_viz.markdown index 07719c67eb..d4ec97d49e 100644 --- a/doc/tutorials/viz/launching_viz/launching_viz.markdown +++ b/doc/tutorials/viz/launching_viz/launching_viz.markdown @@ -37,7 +37,7 @@ Here is the general structure of the program: the same with **myWindow**. If the name does not exist, a new window is created. @code{.cpp} /// Access window via its name - viz::Viz3d sameWindow = viz::get("Viz Demo"); + viz::Viz3d sameWindow = viz::getWindowByName("Viz Demo"); @endcode - 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. diff --git a/modules/calib3d/src/calibinit.cpp b/modules/calib3d/src/calibinit.cpp index af7a3dd7bf..607c29e3cd 100644 --- a/modules/calib3d/src/calibinit.cpp +++ b/modules/calib3d/src/calibinit.cpp @@ -76,13 +76,17 @@ #include #include -using namespace cv; -using namespace std; +#include "opencv2/core/utility.hpp" +#include //#define ENABLE_TRIM_COL_ROW //#define DEBUG_CHESSBOARD +//#undef CV_LOG_STRIP_LEVEL +//#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1 +#include + #ifdef DEBUG_CHESSBOARD static int PRINTF( const char* fmt, ... ) { @@ -94,17 +98,34 @@ static int PRINTF( const char* fmt, ... ) #define PRINTF(...) #endif +using namespace cv; +using namespace std; + //===================================================================================== // Implementation for the enhanced calibration object detection //===================================================================================== #define MAX_CONTOUR_APPROX 7 +#define USE_CV_FINDCONTOURS // switch between cv::findContours() and legacy C API +#ifdef USE_CV_FINDCONTOURS +struct QuadCountour { + Point pt[4]; + int parent_contour; + + QuadCountour(const Point pt_[4], int parent_contour_) : + parent_contour(parent_contour_) + { + pt[0] = pt_[0]; pt[1] = pt_[1]; pt[2] = pt_[2]; pt[3] = pt_[3]; + } +}; +#else struct CvContourEx { CV_CONTOUR_FIELDS() int counter; }; +#endif //===================================================================================== @@ -517,7 +538,11 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, int max_quad_buf_size = 0; cvFree(&quads); cvFree(&corners); +#ifdef USE_CV_FINDCONTOURS + Mat binarized_img = thresh_img_new; +#else Mat binarized_img = thresh_img_new.clone(); // make clone because cvFindContours modifies the source image +#endif int quad_count = icvGenerateQuads( &quads, &corners, storage, binarized_img, flags, &max_quad_buf_size ); PRINTF("Quad count: %d/%d\n", quad_count, (pattern_size.width/2+1)*(pattern_size.height/2+1)); SHOW_QUADS("New quads", thresh_img_new, quads, quad_count); @@ -583,7 +608,11 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, int max_quad_buf_size = 0; cvFree(&quads); cvFree(&corners); +#ifdef USE_CV_FINDCONTOURS + Mat binarized_img = thresh_img; +#else Mat binarized_img = (useAdaptive) ? thresh_img : thresh_img.clone(); // make clone because cvFindContours modifies the source image +#endif int quad_count = icvGenerateQuads( &quads, &corners, storage, binarized_img, flags, &max_quad_buf_size); PRINTF("Quad count: %d/%d\n", quad_count, (pattern_size.width/2+1)*(pattern_size.height/2+1)); SHOW_QUADS("Old quads", thresh_img, quads, quad_count); @@ -1736,7 +1765,6 @@ static int icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, CvMemStorage *storage, const cv::Mat & image_, int flags, int *max_quad_buf_size ) { - CvMat image_old(image_), *image = &image_old; int quad_count = 0; cv::Ptr temp_storage; @@ -1746,17 +1774,144 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, if( out_corners ) *out_corners = 0; + // empiric bound for minimal allowed perimeter for squares + int min_size = 25; //cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 ); + + bool filterQuads = (flags & CALIB_CB_FILTER_QUADS) != 0; +#ifdef USE_CV_FINDCONTOURS // use cv::findContours + CV_UNUSED(storage); + + std::vector > contours; + std::vector hierarchy; + + cv::findContours(image_, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE); + + if (contours.empty()) + { + CV_LOG_DEBUG(NULL, "calib3d(chessboard): cv::findContours() returns no contours"); + *max_quad_buf_size = 0; + return 0; + } + + std::vector contour_child_counter(contours.size(), 0); + int boardIdx = -1; + + std::vector contour_quads; + + for (int idx = (int)(contours.size() - 1); idx >= 0; --idx) + { + int parentIdx = hierarchy[idx][3]; + if (hierarchy[idx][2] != -1 || parentIdx == -1) // holes only (no child contours and with parent) + continue; + const std::vector& contour = contours[idx]; + + Rect contour_rect = boundingRect(contour); + if (contour_rect.area() < min_size) + continue; + + std::vector approx_contour; + + const int min_approx_level = 1, max_approx_level = MAX_CONTOUR_APPROX; + for (int approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ ) + { + approxPolyDP(contour, approx_contour, (float)approx_level, true); + if (approx_contour.size() == 4) + break; + + // we call this again on its own output, because sometimes + // approxPoly() does not simplify as much as it should. + std::vector approx_contour_tmp; + std::swap(approx_contour, approx_contour_tmp); + approxPolyDP(approx_contour_tmp, approx_contour, (float)approx_level, true); + if (approx_contour.size() == 4) + break; + } + + // reject non-quadrangles + if (approx_contour.size() != 4) + continue; + if (!cv::isContourConvex(approx_contour)) + continue; + + cv::Point pt[4]; + for (int i = 0; i < 4; ++i) + pt[i] = approx_contour[i]; + CV_LOG_VERBOSE(NULL, 9, "... contours(" << contour_quads.size() << " added):" << pt[0] << " " << pt[1] << " " << pt[2] << " " << pt[3]); + + if (filterQuads) + { + double p = cv::arcLength(approx_contour, true); + double area = cv::contourArea(approx_contour, false); + + double d1 = sqrt(normL2Sqr(pt[0] - pt[2])); + double d2 = sqrt(normL2Sqr(pt[1] - pt[3])); + + // philipg. Only accept those quadrangles which are more square + // than rectangular and which are big enough + double d3 = sqrt(normL2Sqr(pt[0] - pt[1])); + double d4 = sqrt(normL2Sqr(pt[1] - pt[2])); + if (!(d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size && + d1 >= 0.15 * p && d2 >= 0.15 * p)) + continue; + } + + contour_child_counter[parentIdx]++; + if (boardIdx != parentIdx && (boardIdx < 0 || contour_child_counter[boardIdx] < contour_child_counter[parentIdx])) + boardIdx = parentIdx; + + contour_quads.push_back(QuadCountour(pt, parentIdx)); + } + + size_t total = contour_quads.size(); + *max_quad_buf_size = (int)std::max((size_t)2, total * 3); + *out_quads = (CvCBQuad*)cvAlloc(*max_quad_buf_size * sizeof((*out_quads)[0])); + *out_corners = (CvCBCorner*)cvAlloc(*max_quad_buf_size * 4 * sizeof((*out_corners)[0])); + + // Create array of quads structures + for(int idx = 0; idx < (int)contour_quads.size(); idx++ ) + { + CvCBQuad* q = &(*out_quads)[quad_count]; + + QuadCountour& qc = contour_quads[idx]; + if (filterQuads && qc.parent_contour != boardIdx) + continue; + + // reset group ID + memset(q, 0, sizeof(*q)); + q->group_idx = -1; + for (int i = 0; i < 4; ++i) + { + CvCBCorner* corner = &(*out_corners)[quad_count*4 + i]; + + memset(corner, 0, sizeof(*corner)); + corner->pt = qc.pt[i]; + q->corners[i] = corner; + } + q->edge_len = FLT_MAX; + for (int i = 0; i < 4; ++i) + { + // TODO simplify with normL2Sqr() + float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x; + float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y; + float d = dx*dx + dy*dy; + if (q->edge_len > d) + q->edge_len = d; + } + quad_count++; + } + +#else // use legacy API: cvStartFindContours / cvFindNextContour / cvEndFindContours + + CvMat image_old(image_), *image = &image_old; + CvSeq *src_contour = 0; CvSeq *root; CvContourEx* board = 0; CvContourScanner scanner; - int i, idx, min_size; + int i, idx; CV_Assert( out_corners && out_quads ); - // empiric bound for minimal allowed perimeter for squares - min_size = 25; //cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 ); - // create temporary storage for contours and the sequence of pointers to found quadrangles temp_storage.reset(cvCreateChildMemStorage( storage )); root = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage ); @@ -1820,9 +1975,9 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, dx = pt[1].x - pt[2].x; dy = pt[1].y - pt[2].y; d4 = sqrt(dx*dx + dy*dy); - if( !(flags & CV_CALIB_CB_FILTER_QUADS) || + if (!filterQuads || (d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size && - d1 >= 0.15 * p && d2 >= 0.15 * p) ) + d1 >= 0.15 * p && d2 >= 0.15 * p)) { CvContourEx* parent = (CvContourEx*)(src_contour->v_prev); parent->counter++; @@ -1840,7 +1995,8 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, cvEndFindContours( &scanner ); // allocate quad & corner buffers - *max_quad_buf_size = MAX(1, (root->total+root->total / 2)) * 2; + int total = root->total; + *max_quad_buf_size = MAX(1, (total + total / 2)) * 2; *out_quads = (CvCBQuad*)cvAlloc(*max_quad_buf_size * sizeof((*out_quads)[0])); *out_corners = (CvCBCorner*)cvAlloc(*max_quad_buf_size * 4 * sizeof((*out_corners)[0])); @@ -1849,7 +2005,7 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, { CvCBQuad* q = &(*out_quads)[quad_count]; src_contour = *(CvSeq**)cvGetSeqElem( root, idx ); - if( (flags & CV_CALIB_CB_FILTER_QUADS) && src_contour->v_prev != (CvSeq*)board ) + if (filterQuads && src_contour->v_prev != (CvSeq*)board) continue; // reset group ID @@ -1878,6 +2034,11 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, } quad_count++; } +#endif + + CV_LOG_VERBOSE(NULL, 3, "Total quad contours: " << total); + CV_LOG_VERBOSE(NULL, 3, "max_quad_buf_size=" << *max_quad_buf_size); + CV_LOG_VERBOSE(NULL, 3, "filtered quad_count=" << quad_count); return quad_count; } diff --git a/modules/calib3d/src/ptsetreg.cpp b/modules/calib3d/src/ptsetreg.cpp index e26e67dc3e..1ed98756ec 100644 --- a/modules/calib3d/src/ptsetreg.cpp +++ b/modules/calib3d/src/ptsetreg.cpp @@ -78,10 +78,7 @@ class RANSACPointSetRegistrator : public PointSetRegistrator public: RANSACPointSetRegistrator(const Ptr& _cb=Ptr(), int _modelPoints=0, double _threshold=0, double _confidence=0.99, int _maxIters=1000) - : cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters) - { - checkPartialSubsets = false; - } + : cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters) {} int findInliers( const Mat& m1, const Mat& m2, const Mat& model, Mat& err, Mat& mask, double thresh ) const { @@ -143,17 +140,9 @@ public: ms1ptr[i*esz1 + k] = m1ptr[idx_i*esz1 + k]; for( k = 0; k < esz2; k++ ) ms2ptr[i*esz2 + k] = m2ptr[idx_i*esz2 + k]; - if( checkPartialSubsets && !cb->checkSubset( ms1, ms2, i+1 )) - { - // we may have selected some bad points; - // so, let's remove some of them randomly - i = rng.uniform(0, i+1); - iters++; - continue; - } i++; } - if( !checkPartialSubsets && i == modelPoints && !cb->checkSubset(ms1, ms2, i)) + if( i == modelPoints && !cb->checkSubset(ms1, ms2, i) ) continue; break; } @@ -261,7 +250,6 @@ public: Ptr cb; int modelPoints; - bool checkPartialSubsets; double threshold; double confidence; int maxIters; diff --git a/modules/calib3d/src/stereobm.cpp b/modules/calib3d/src/stereobm.cpp index b3498c66d1..7aa721dd3d 100644 --- a/modules/calib3d/src/stereobm.cpp +++ b/modules/calib3d/src/stereobm.cpp @@ -284,7 +284,39 @@ prefilterXSobel( const Mat& src, Mat& dst, int ftzero ) static const int DISPARITY_SHIFT_16S = 4; static const int DISPARITY_SHIFT_32S = 8; +template +struct dispShiftTemplate +{ }; + +template<> +struct dispShiftTemplate +{ + enum { value = DISPARITY_SHIFT_16S }; +}; + +template<> +struct dispShiftTemplate +{ + enum { value = DISPARITY_SHIFT_32S }; +}; + +template +inline T dispDescale(int /*v1*/, int /*v2*/, int /*d*/); + +template<> +inline short dispDescale(int v1, int v2, int d) +{ + return (short)((v1*256 + (d != 0 ? v2*256/d : 0) + 15) >> 4); +} + +template <> +inline int dispDescale(int v1, int v2, int d) +{ + return (int)(v1*256 + (d != 0 ? v2*256/d : 0)); // no need to add 127, this will be converted to float +} + #if CV_SIMD128 +template static void findStereoCorrespondenceBM_SIMD( const Mat& left, const Mat& right, Mat& disp, Mat& cost, StereoBMParams& state, uchar* buf, int _dy0, int _dy1 ) @@ -302,7 +334,8 @@ static void findStereoCorrespondenceBM_SIMD( const Mat& left, const Mat& right, int ftzero = state.preFilterCap; int textureThreshold = state.textureThreshold; int uniquenessRatio = state.uniquenessRatio; - short FILTERED = (short)((mindisp - 1) << DISPARITY_SHIFT_16S); + const int disp_shift = dispShiftTemplate::value; + dType FILTERED = (dType)((mindisp - 1) << disp_shift); ushort *sad, *hsad0, *hsad, *hsad_sub; int *htext; @@ -310,7 +343,7 @@ static void findStereoCorrespondenceBM_SIMD( const Mat& left, const Mat& right, const uchar* lptr0 = left.ptr() + lofs; const uchar* rptr0 = right.ptr() + rofs; const uchar *lptr, *lptr_sub, *rptr; - short* dptr = disp.ptr(); + dType* dptr = disp.ptr(); int sstep = (int)left.step; int dstep = (int)(disp.step/sizeof(dptr[0])); int cstep = (height + dy0 + dy1)*ndisp; @@ -527,10 +560,10 @@ static void findStereoCorrespondenceBM_SIMD( const Mat& left, const Mat& right, { int p = sad[mind+1], n = sad[mind-1]; d = p + n - 2*sad[mind] + std::abs(p - n); - dptr[y*dstep] = (short)(((ndisp - mind - 1 + mindisp)*256 + (d != 0 ? (p-n)*256/d : 0) + 15) >> 4); + dptr[y*dstep] = dispDescale(ndisp - mind - 1 + mindisp, p-n, d); } else - dptr[y*dstep] = (short)((ndisp - mind - 1 + mindisp)*16); + dptr[y*dstep] = dispDescale(ndisp - mind - 1 + mindisp, 0, 0); costptr[y*coststep] = sad[mind]; } } @@ -540,8 +573,8 @@ static void findStereoCorrespondenceBM_SIMD( const Mat& left, const Mat& right, template static void findStereoCorrespondenceBM( const Mat& left, const Mat& right, - Mat& disp, Mat& cost, const StereoBMParams& state, - uchar* buf, int _dy0, int _dy1, const int disp_shift ) + Mat& disp, Mat& cost, const StereoBMParams& state, + uchar* buf, int _dy0, int _dy1 ) { const int ALIGN = 16; @@ -557,6 +590,7 @@ findStereoCorrespondenceBM( const Mat& left, const Mat& right, int ftzero = state.preFilterCap; int textureThreshold = state.textureThreshold; int uniquenessRatio = state.uniquenessRatio; + const int disp_shift = dispShiftTemplate::value; mType FILTERED = (mType)((mindisp - 1) << disp_shift); #if CV_SIMD128 @@ -849,8 +883,8 @@ findStereoCorrespondenceBM( const Mat& left, const Mat& right, sad[ndisp] = sad[ndisp-2]; int p = sad[mind+1], n = sad[mind-1]; d = p + n - 2*sad[mind] + std::abs(p - n); - dptr[y*dstep] = (mType)(((ndisp - mind - 1 + mindisp)*256 + (d != 0 ? (p-n)*256/d : 0) + 15) - >> (DISPARITY_SHIFT_32S - disp_shift)); + dptr[y*dstep] = dispDescale(ndisp - mind - 1 + mindisp, p-n, d); + costptr[y*coststep] = sad[mind]; } } @@ -980,7 +1014,10 @@ struct FindStereoCorrespInvoker : public ParallelLoopBody int _row0 = std::min(cvRound(range.start * rows / nstripes), rows); int _row1 = std::min(cvRound(range.end * rows / nstripes), rows); uchar *ptr = slidingSumBuf->ptr() + range.start * stripeBufSize; - int FILTERED = (state->minDisparity - 1)*16; + + int dispShift = disp->type() == CV_16S ? DISPARITY_SHIFT_16S : + DISPARITY_SHIFT_32S; + int FILTERED = (state->minDisparity - 1) << dispShift; Rect roi = validDisparityRect & Rect(0, _row0, cols, _row1 - _row0); if( roi.height == 0 ) @@ -1008,15 +1045,18 @@ struct FindStereoCorrespInvoker : public ParallelLoopBody #if CV_SIMD128 if( useSIMD && useShorts ) { - findStereoCorrespondenceBM_SIMD( left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1 ); + if( disp_i.type() == CV_16S) + findStereoCorrespondenceBM_SIMD( left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1 ); + else + findStereoCorrespondenceBM_SIMD( left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1); } else #endif { if( disp_i.type() == CV_16S ) - findStereoCorrespondenceBM( left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1, DISPARITY_SHIFT_16S ); + findStereoCorrespondenceBM( left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1 ); else - findStereoCorrespondenceBM( left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1, DISPARITY_SHIFT_32S ); + findStereoCorrespondenceBM( left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1 ); } if( state->disp12MaxDiff >= 0 ) @@ -1104,7 +1144,6 @@ public: else disp_shift = DISPARITY_SHIFT_32S; - int FILTERED = (params.minDisparity - 1) << disp_shift; #ifdef HAVE_OPENCL @@ -1115,6 +1154,9 @@ public: { if(ocl_stereobm(left, right, disparr, ¶ms)) { + disp_shift = DISPARITY_SHIFT_16S; + FILTERED = (params.minDisparity - 1) << disp_shift; + if( params.speckleRange >= 0 && params.speckleWindowSize > 0 ) filterSpeckles(disparr.getMat(), FILTERED, params.speckleWindowSize, params.speckleRange, slidingSumBuf); if (dtype == CV_32F) diff --git a/modules/calib3d/test/test_stereomatching.cpp b/modules/calib3d/test/test_stereomatching.cpp index 601e0cb93b..94fc9718cc 100644 --- a/modules/calib3d/test/test_stereomatching.cpp +++ b/modules/calib3d/test/test_stereomatching.cpp @@ -556,7 +556,7 @@ int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int ca assert( fs.isOpened() ); assert( trueLeftDisp.type() == CV_32FC1 ); assert( trueRightDisp.empty() || trueRightDisp.type() == CV_32FC1 ); - assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 ); + assert( leftDisp.type() == CV_32FC1 && (rightDisp.empty() || rightDisp.type() == CV_32FC1) ); // get masks for unknown ground truth disparity values Mat leftUnknMask, rightUnknMask; @@ -791,6 +791,12 @@ protected: bm->compute( leftImg, rightImg, tempDisp ); tempDisp.convertTo(leftDisp, CV_32F, 1./StereoMatcher::DISP_SCALE); + //check for fixed-type disparity data type + Mat_ fixedFloatDisp; + bm->compute( leftImg, rightImg, fixedFloatDisp ); + EXPECT_LT(cvtest::norm(fixedFloatDisp, leftDisp, cv::NORM_L2 | cv::NORM_RELATIVE), + 0.005 + DBL_EPSILON); + if (params.mindisp != 0) for (int y = 0; y < leftDisp.rows; y++) for (int x = 0; x < leftDisp.cols; x++) diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index c0c6c178f8..a560ffc1d1 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -532,8 +532,9 @@ CV_EXPORTS_W void convertScaleAbs(InputArray src, OutputArray dst, /** @brief Converts an array to half precision floating number. -This function converts FP32 (single precision floating point) from/to FP16 (half precision floating point). The input array has to have type of CV_32F or -CV_16S to represent the bit depth. If the input array is neither of them, the function will raise an error. +This function converts FP32 (single precision floating point) from/to FP16 (half precision floating point). CV_16S format is used to represent FP16 data. +There are two use modes (src -> dst): CV_32F -> CV_16S and CV_16S -> CV_32F. The input array has to have type of CV_32F or +CV_16S to represent the bit depth. If the input array is neither of them, the function will raise an error. The format of half precision floating point is defined in IEEE 754-2008. @param src input array. diff --git a/modules/core/include/opencv2/core/cvstd.inl.hpp b/modules/core/include/opencv2/core/cvstd.inl.hpp index f1637c44f0..631fe94e1a 100644 --- a/modules/core/include/opencv2/core/cvstd.inl.hpp +++ b/modules/core/include/opencv2/core/cvstd.inl.hpp @@ -77,11 +77,8 @@ inline String::String(const std::string& str) : cstr_(0), len_(0) { - if (!str.empty()) - { - size_t len = str.size(); - if (len) memcpy(allocate(len), str.c_str(), len); - } + size_t len = str.size(); + if (len) memcpy(allocate(len), str.c_str(), len); } inline @@ -99,11 +96,8 @@ inline String& String::operator = (const std::string& str) { deallocate(); - if (!str.empty()) - { - size_t len = str.size(); - if (len) memcpy(allocate(len), str.c_str(), len); - } + size_t len = str.size(); + if (len) memcpy(allocate(len), str.c_str(), len); return *this; } diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index fad989c0b2..be14227f47 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -1649,7 +1649,7 @@ Mat_<_Tp>& Mat_<_Tp>::operator = (const Mat& m) { return (*this = m.reshape(DataType<_Tp>::channels, m.dims, 0)); } - CV_DbgAssert(DataType<_Tp>::channels == m.channels()); + CV_Assert(DataType<_Tp>::channels == m.channels() || m.empty()); m.convertTo(*this, type()); return *this; } diff --git a/modules/core/include/opencv2/core/types.hpp b/modules/core/include/opencv2/core/types.hpp index fdb52efe8e..c3644cbda0 100644 --- a/modules/core/include/opencv2/core/types.hpp +++ b/modules/core/include/opencv2/core/types.hpp @@ -1372,6 +1372,20 @@ Point_<_Tp> operator / (const Point_<_Tp>& a, double b) } +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); +template static inline _AccTp normL2Sqr(const Point_& pt); + +template<> inline int normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline int64 normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline float normL2Sqr(const Point_& pt) { return pt.dot(pt); } +template<> inline double normL2Sqr(const Point_& pt) { return pt.dot(pt); } + +template<> inline double normL2Sqr(const Point_& pt) { return pt.ddot(pt); } +template<> inline double normL2Sqr(const Point_& pt) { return pt.ddot(pt); } + + //////////////////////////////// 3D Point /////////////////////////////// diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 2d9f90e206..a3e6c46b2a 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -1180,6 +1180,12 @@ void cv::compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op) CV_Assert( op == CMP_LT || op == CMP_LE || op == CMP_EQ || op == CMP_NE || op == CMP_GE || op == CMP_GT ); + if(_src1.empty() && _src2.empty()) + { + _dst.release(); + return; + } + bool haveScalar = false; if ((_src1.isMatx() + _src2.isMatx()) == 1 diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index 0fb1199987..481b86b4f1 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -1304,6 +1304,12 @@ void cv::Mat::convertTo(OutputArray _dst, int _type, double alpha, double beta) { CV_INSTRUMENT_REGION() + if( empty() ) + { + _dst.release(); + return; + } + bool noScale = fabs(alpha-1) < DBL_EPSILON && fabs(beta) < DBL_EPSILON; if( _type < 0 ) diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index f4f18cb740..5e7f4a879a 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -246,13 +246,14 @@ void Mat::copyTo( OutputArray _dst ) const return; } + if( empty() ) + { + _dst.release(); + return; + } + if( _dst.isUMat() ) { - if( empty() ) - { - _dst.release(); - return; - } _dst.create( dims, size.p, type() ); UMat dst = _dst.getUMat(); CV_Assert(dst.u != NULL); diff --git a/modules/dnn/include/opencv2/dnn.hpp b/modules/dnn/include/opencv2/dnn.hpp index 57a564bf11..af919005f6 100644 --- a/modules/dnn/include/opencv2/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn.hpp @@ -42,7 +42,7 @@ #ifndef OPENCV_DNN_HPP #define OPENCV_DNN_HPP -// This is an umbrealla header to include into you project. +// This is an umbrella header to include into you project. // We are free to change headers layout in dnn subfolder, so please include // this header for future compatibility @@ -52,10 +52,10 @@ This module contains: - API for new layers creation, layers are building bricks of neural networks; - set of built-in most-useful Layers; - - API to constuct and modify comprehensive neural networks from layers; + - API to construct and modify comprehensive neural networks from layers; - functionality for loading serialized networks models from different frameworks. - Functionality of this module is designed only for forward pass computations (i. e. network testing). + Functionality of this module is designed only for forward pass computations (i.e. network testing). A network training is in principle not supported. @} */ diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index f2124dd516..cc8521586c 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -58,7 +58,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN You can use both API, but factory API is less convenient for native C++ programming and basically designed for use inside importers (see @ref readNetFromCaffe(), @ref readNetFromTorch(), @ref readNetFromTensorflow()). Built-in layers partially reproduce functionality of corresponding Caffe and Torch7 layers. - In partuclar, the following layers and Caffe importer were tested to reproduce Caffe functionality: + In particular, the following layers and Caffe importer were tested to reproduce Caffe functionality: - Convolution - Deconvolution - Pooling @@ -108,13 +108,13 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN @f$W_{x?} \in R^{N_h \times N_x}@f$, @f$W_{h?} \in R^{N_h \times N_h}@f$, @f$b_? \in R^{N_h}@f$. For simplicity and performance purposes we use @f$ W_x = [W_{xi}; W_{xf}; W_{xo}, W_{xg}] @f$ - (i.e. @f$W_x@f$ is vertical contacentaion of @f$ W_{x?} @f$), @f$ W_x \in R^{4N_h \times N_x} @f$. + (i.e. @f$W_x@f$ is vertical concatenation of @f$ W_{x?} @f$), @f$ W_x \in R^{4N_h \times N_x} @f$. The same for @f$ W_h = [W_{hi}; W_{hf}; W_{ho}, W_{hg}], W_h \in R^{4N_h \times N_h} @f$ and for @f$ b = [b_i; b_f, b_o, b_g]@f$, @f$b \in R^{4N_h} @f$. - @param Wh is matrix defining how previous output is transformed to internal gates (i.e. according to abovemtioned notation is @f$ W_h @f$) - @param Wx is matrix defining how current input is transformed to internal gates (i.e. according to abovemtioned notation is @f$ W_x @f$) - @param b is bias vector (i.e. according to abovemtioned notation is @f$ b @f$) + @param Wh is matrix defining how previous output is transformed to internal gates (i.e. according to above mentioned notation is @f$ W_h @f$) + @param Wx is matrix defining how current input is transformed to internal gates (i.e. according to above mentioned notation is @f$ W_x @f$) + @param b is bias vector (i.e. according to above mentioned notation is @f$ b @f$) */ CV_DEPRECATED virtual void setWeights(const Mat &Wh, const Mat &Wx, const Mat &b) = 0; @@ -148,7 +148,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN * If setUseTimstampsDim() is set to true then @p input[0] should has at least two dimensions with the following shape: [`T`, `N`, `[data dims]`], * where `T` specifies number of timestamps, `N` is number of independent streams (i.e. @f$ x_{t_0 + t}^{stream} @f$ is stored inside @p input[0][t, stream, ...]). * - * If setUseTimstampsDim() is set to fase then @p input[0] should contain single timestamp, its shape should has form [`N`, `[data dims]`] with at least one dimension. + * If setUseTimstampsDim() is set to false then @p input[0] should contain single timestamp, its shape should has form [`N`, `[data dims]`] with at least one dimension. * (i.e. @f$ x_{t}^{stream} @f$ is stored inside @p input[0][stream, ...]). */ @@ -550,7 +550,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN * dst(x, y, c) = \frac{ src(x, y, c) }{norm(c)} * @f] * - * Where `x, y` - spatial cooridnates, `c` - channel. + * Where `x, y` - spatial coordinates, `c` - channel. * * An every sample in the batch is normalized separately. Optionally, * output is scaled by the trained parameters. @@ -565,7 +565,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN }; /** - * @brief Resize input 4-dimensional blob by nearest neghbor strategy. + * @brief Resize input 4-dimensional blob by nearest neighbor strategy. * * Layer is used to support TensorFlow's resize_nearest_neighbor op. */ @@ -581,6 +581,12 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN static Ptr create(const LayerParams& params); }; + class CV_EXPORTS CropAndResizeLayer : public Layer + { + public: + static Ptr create(const LayerParams& params); + }; + //! @} //! @} CV__DNN_EXPERIMENTAL_NS_END diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 6ac2f1a7fe..2a1d68af7e 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -81,12 +81,13 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN { DNN_TARGET_CPU, DNN_TARGET_OPENCL, - DNN_TARGET_OPENCL_FP16 + DNN_TARGET_OPENCL_FP16, + DNN_TARGET_MYRIAD }; /** @brief This class provides all data needed to initialize layer. * - * It includes dictionary with scalar params (which can be readed by using Dict interface), + * It includes dictionary with scalar params (which can be read by using Dict interface), * blob params #blobs and optional meta information: #name and #type of layer instance. */ class CV_EXPORTS LayerParams : public Dict @@ -137,7 +138,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN * Initialize wrapper from another one. It'll wrap the same host CPU * memory and mustn't allocate memory on device(i.e. GPU). It might * has different shape. Use in case of CPU memory reusing for reuse - * associented memory on device too. + * associated memory on device too. */ BackendWrapper(const Ptr& base, const MatShape& shape); @@ -345,7 +346,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN /** @brief Create a network from Intel's Model Optimizer intermediate representation. * @param[in] xml XML configuration file with network's topology. * @param[in] bin Binary file with trained weights. - * Networks imported from Intel's Model Optimizer are lauched in Intel's Inference Engine + * Networks imported from Intel's Model Optimizer are launched in Intel's Inference Engine * backend. */ CV_WRAP static Net readFromModelOptimizer(const String& xml, const String& bin); @@ -401,8 +402,8 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN /** @brief Connects #@p outNum output of the first layer to #@p inNum input of the second layer. * @param outLayerId identifier of the first layer - * @param inpLayerId identifier of the second layer * @param outNum number of the first layer output + * @param inpLayerId identifier of the second layer * @param inpNum number of the second layer input */ void connect(int outLayerId, int outNum, int inpLayerId, int inpNum); @@ -563,7 +564,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN */ CV_WRAP int getLayersCount(const String& layerType) const; - /** @brief Computes bytes number which are requered to store + /** @brief Computes bytes number which are required to store * all weights and intermediate blobs for model. * @param netInputShapes vector of shapes for all net inputs. * @param weights output parameter to store resulting bytes for weights. @@ -583,7 +584,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN const MatShape& netInputShape, CV_OUT size_t& weights, CV_OUT size_t& blobs) const; - /** @brief Computes bytes number which are requered to store + /** @brief Computes bytes number which are required to store * all weights and intermediate blobs for each layer. * @param netInputShapes vector of shapes for all net inputs. * @param layerIds output vector to save layer IDs. @@ -700,13 +701,13 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN * * `*.pb` (TensorFlow, https://www.tensorflow.org/) * * `*.t7` | `*.net` (Torch, http://torch.ch/) * * `*.weights` (Darknet, https://pjreddie.com/darknet/) - * * `*.bin` (DLDT, https://software.seek.intel.com/deep-learning-deployment) + * * `*.bin` (DLDT, https://software.intel.com/openvino-toolkit) * @param[in] config Text file contains network configuration. It could be a * file with the following extensions: * * `*.prototxt` (Caffe, http://caffe.berkeleyvision.org/) * * `*.pbtxt` (TensorFlow, https://www.tensorflow.org/) * * `*.cfg` (Darknet, https://pjreddie.com/darknet/) - * * `*.xml` (DLDT, https://software.seek.intel.com/deep-learning-deployment) + * * `*.xml` (DLDT, https://software.intel.com/openvino-toolkit) * @param[in] framework Explicit framework name tag to determine a format. * @returns Net object. * @@ -726,7 +727,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN * @param[in] xml XML configuration file with network's topology. * @param[in] bin Binary file with trained weights. * @returns Net object. - * Networks imported from Intel's Model Optimizer are lauched in Intel's Inference Engine + * Networks imported from Intel's Model Optimizer are launched in Intel's Inference Engine * backend. */ CV_EXPORTS_W Net readNetFromModelOptimizer(const String &xml, const String &bin); @@ -744,7 +745,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN * @details if @p crop is true, input image is resized so one side after resize is equal to corresponding * dimension in @p size and another one is equal or larger. Then, crop from the center is performed. * If @p crop is false, direct resize without cropping and preserving aspect ratio is performed. - * @returns 4-dimansional Mat with NCHW dimensions order. + * @returns 4-dimensional Mat with NCHW dimensions order. */ CV_EXPORTS_W Mat blobFromImage(InputArray image, double scalefactor=1.0, const Size& size = Size(), const Scalar& mean = Scalar(), bool swapRB=true, bool crop=true); diff --git a/modules/dnn/misc/quantize_face_detector.py b/modules/dnn/misc/quantize_face_detector.py index c66b735847..8a8b88d181 100644 --- a/modules/dnn/misc/quantize_face_detector.py +++ b/modules/dnn/misc/quantize_face_detector.py @@ -223,9 +223,9 @@ with tf.Session() as sess: # By default, float16 weights are stored in repeated tensor's field called # `half_val`. It has type int32 with leading zeros for unused bytes. - # This type is encoded by Varint that means only 7 bits are used for value + # This type is encoded by Variant that means only 7 bits are used for value # representation but the last one is indicated the end of encoding. This way - # float16 might takes 1 or 2 or 3 bytes depends on value. To impove compression, + # float16 might takes 1 or 2 or 3 bytes depends on value. To improve compression, # we replace all `half_val` values to `tensor_content` using only 2 bytes for everyone. for node in graph_def.node: if 'value' in node.attr: diff --git a/modules/dnn/perf/perf_net.cpp b/modules/dnn/perf/perf_net.cpp index c05a7088cd..aa4ac05881 100644 --- a/modules/dnn/perf/perf_net.cpp +++ b/modules/dnn/perf/perf_net.cpp @@ -13,7 +13,7 @@ namespace opencv_test { CV_ENUM(DNNBackend, DNN_BACKEND_DEFAULT, DNN_BACKEND_HALIDE, DNN_BACKEND_INFERENCE_ENGINE) -CV_ENUM(DNNTarget, DNN_TARGET_CPU, DNN_TARGET_OPENCL, DNN_TARGET_OPENCL_FP16) +CV_ENUM(DNNTarget, DNN_TARGET_CPU, DNN_TARGET_OPENCL, DNN_TARGET_OPENCL_FP16, DNN_TARGET_MYRIAD) class DNNTestNetwork : public ::perf::TestBaseWithParam< tuple > { @@ -29,6 +29,28 @@ public: target = (dnn::Target)(int)get<1>(GetParam()); } + static bool checkMyriadTarget() + { +#ifndef HAVE_INF_ENGINE + return false; +#endif + cv::dnn::Net net; + cv::dnn::LayerParams lp; + net.addLayerToPrev("testLayer", "Identity", lp); + net.setPreferableBackend(cv::dnn::DNN_BACKEND_INFERENCE_ENGINE); + net.setPreferableTarget(cv::dnn::DNN_TARGET_MYRIAD); + net.setInput(cv::Mat::zeros(1, 1, CV_32FC1)); + try + { + net.forward(); + } + catch(...) + { + return false; + } + return true; + } + void processNet(std::string weights, std::string proto, std::string halide_scheduler, const Mat& input, const std::string& outputLayer = "") { @@ -41,6 +63,13 @@ public: throw cvtest::SkipTestException("OpenCL is not available/disabled in OpenCV"); } } + if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + { + if (!checkMyriadTarget()) + { + throw SkipTestException("Myriad is not available/disabled in OpenCV"); + } + } randu(input, 0.0f, 1.0f); @@ -87,8 +116,6 @@ public: PERF_TEST_P_(DNNTestNetwork, AlexNet) { - if (backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) - throw SkipTestException(""); processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt", "alexnet.yml", Mat(cv::Size(227, 227), CV_32FC3)); } @@ -130,7 +157,6 @@ PERF_TEST_P_(DNNTestNetwork, ENet) PERF_TEST_P_(DNNTestNetwork, SSD) { - if (backend == DNN_BACKEND_INFERENCE_ENGINE) throw SkipTestException(""); processNet("dnn/VGG_ILSVRC2016_SSD_300x300_iter_440000.caffemodel", "dnn/ssd_vgg16.prototxt", "disabled", Mat(cv::Size(300, 300), CV_32FC3)); } @@ -146,18 +172,17 @@ PERF_TEST_P_(DNNTestNetwork, OpenFace) PERF_TEST_P_(DNNTestNetwork, MobileNet_SSD_Caffe) { - if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); processNet("dnn/MobileNetSSD_deploy.caffemodel", "dnn/MobileNetSSD_deploy.prototxt", "", Mat(cv::Size(300, 300), CV_32FC3)); } +// TODO: update MobileNet model. PERF_TEST_P_(DNNTestNetwork, MobileNet_SSD_TensorFlow) { - if (backend == DNN_BACKEND_DEFAULT && target == DNN_TARGET_OPENCL || - backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE) throw SkipTestException(""); processNet("dnn/ssd_mobilenet_v1_coco.pb", "ssd_mobilenet_v1_coco.pbtxt", "", Mat(cv::Size(300, 300), CV_32FC3)); @@ -166,7 +191,8 @@ PERF_TEST_P_(DNNTestNetwork, MobileNet_SSD_TensorFlow) PERF_TEST_P_(DNNTestNetwork, DenseNet_121) { if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16) + backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL_FP16 || + target == DNN_TARGET_MYRIAD)) throw SkipTestException(""); processNet("dnn/DenseNet_121.caffemodel", "dnn/DenseNet_121.prototxt", "", Mat(cv::Size(224, 224), CV_32FC3)); @@ -174,21 +200,27 @@ PERF_TEST_P_(DNNTestNetwork, DenseNet_121) PERF_TEST_P_(DNNTestNetwork, OpenPose_pose_coco) { - if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + throw SkipTestException(""); processNet("dnn/openpose_pose_coco.caffemodel", "dnn/openpose_pose_coco.prototxt", "", Mat(cv::Size(368, 368), CV_32FC3)); } PERF_TEST_P_(DNNTestNetwork, OpenPose_pose_mpi) { - if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + throw SkipTestException(""); processNet("dnn/openpose_pose_mpi.caffemodel", "dnn/openpose_pose_mpi.prototxt", "", Mat(cv::Size(368, 368), CV_32FC3)); } PERF_TEST_P_(DNNTestNetwork, OpenPose_pose_mpi_faster_4_stages) { - if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + throw SkipTestException(""); // The same .caffemodel but modified .prototxt // See https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/src/openpose/pose/poseParameters.cpp processNet("dnn/openpose_pose_mpi.caffemodel", "dnn/openpose_pose_mpi_faster_4_stages.prototxt", "", @@ -197,8 +229,7 @@ PERF_TEST_P_(DNNTestNetwork, OpenPose_pose_mpi_faster_4_stages) PERF_TEST_P_(DNNTestNetwork, opencv_face_detector) { - if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); processNet("dnn/opencv_face_detector.caffemodel", "dnn/opencv_face_detector.prototxt", "", Mat(cv::Size(300, 300), CV_32FC3)); @@ -207,7 +238,8 @@ PERF_TEST_P_(DNNTestNetwork, opencv_face_detector) PERF_TEST_P_(DNNTestNetwork, Inception_v2_SSD_TensorFlow) { if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL) || + (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16)) throw SkipTestException(""); processNet("dnn/ssd_inception_v2_coco_2017_11_17.pb", "ssd_inception_v2_coco_2017_11_17.pbtxt", "", Mat(cv::Size(300, 300), CV_32FC3)); @@ -215,7 +247,8 @@ PERF_TEST_P_(DNNTestNetwork, Inception_v2_SSD_TensorFlow) PERF_TEST_P_(DNNTestNetwork, YOLOv3) { - if (backend != DNN_BACKEND_DEFAULT) + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/dog416.png", false)); Mat inp; @@ -232,6 +265,7 @@ const tuple testCases[] = { tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU), tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL), tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16), + tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD), #endif tuple(DNN_BACKEND_DEFAULT, DNN_TARGET_CPU), tuple(DNN_BACKEND_DEFAULT, DNN_TARGET_OPENCL), diff --git a/modules/dnn/src/darknet/darknet_io.cpp b/modules/dnn/src/darknet/darknet_io.cpp index 71f762a09d..707cc29095 100644 --- a/modules/dnn/src/darknet/darknet_io.cpp +++ b/modules/dnn/src/darknet/darknet_io.cpp @@ -288,7 +288,7 @@ namespace cv { permute_params.set("order", paramOrder); darknet::LayerParameter lp; - std::string layer_name = cv::format("premute_%d", layer_id); + std::string layer_name = cv::format("permute_%d", layer_id); lp.layer_name = layer_name; lp.layer_type = permute_params.type; lp.layerParams = permute_params; diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 1a1002c794..6318863b58 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -541,7 +541,7 @@ public: { // if dst already has been allocated with total(shape) elements, - // it won't be recrreated and pointer of dst.data remains the same. + // it won't be recreated and pointer of dst.data remains the same. dst.create(shape, use_half ? CV_16S : CV_32F); addHost(lp, dst); } @@ -1132,7 +1132,7 @@ struct Net::Impl if (layerNet != ieInpNode->net) { // layerNet is empty or nodes are from different graphs. - ieInpNode->net->addOutput(inpLd.name); + ieInpNode->net->addOutput(ieInpNode->layer->name); } } } @@ -1182,7 +1182,9 @@ struct Net::Impl for (it = layers.begin(); it != layers.end(); ++it) { LayerData &ld = it->second; - bool fused = ld.skip && ld.id != 0; + if (ld.id == 0) + continue; + bool fused = ld.skip; Ptr layer = ld.layerInstance; if (!layer->supportBackend(preferableBackend)) @@ -1259,7 +1261,7 @@ struct Net::Impl CV_Assert(!ieNode.empty()); ieNode->net = net; - if (preferableTarget == DNN_TARGET_OPENCL_FP16 && !fused) + if ((preferableTarget == DNN_TARGET_OPENCL_FP16 || preferableTarget == DNN_TARGET_MYRIAD) && !fused) { ieNode->layer->precision = InferenceEngine::Precision::FP16; auto weightableLayer = std::dynamic_pointer_cast(ieNode->layer); @@ -1518,7 +1520,7 @@ struct Net::Impl } } - // fuse convlution layer followed by eltwise + relu + // fuse convolution layer followed by eltwise + relu if ( IS_DNN_OPENCL_TARGET(preferableTarget) ) { Ptr nextEltwiseLayer; @@ -1647,7 +1649,7 @@ struct Net::Impl // the optimization #3. if there is concat layer that concatenates channels // from the inputs together (i.e. axis == 1) then we make the inputs of - // the concat layer to write to the concatetion output buffer + // the concat layer to write to the concatenation output buffer // (and so we eliminate the concatenation layer, because the channels // are concatenated implicitly). Ptr concatLayer = ld.layerInstance.dynamicCast(); diff --git a/modules/dnn/src/halide_scheduler.cpp b/modules/dnn/src/halide_scheduler.cpp index a2cb410e7e..78335ddaf9 100644 --- a/modules/dnn/src/halide_scheduler.cpp +++ b/modules/dnn/src/halide_scheduler.cpp @@ -242,7 +242,7 @@ bool HalideScheduler::process(Ptr& node) std::map funcsMap; // Scheduled functions. // For every function, from top to bottom, we try to find a scheduling node. // Scheduling is successful (return true) if for the first function (top) - // node is respresented. + // node is represented. CV_Assert(!node.empty()); std::vector& funcs = node.dynamicCast()->funcs; for (int i = funcs.size() - 1; i >= 0; --i) diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index 28759daf2f..2bff16c4eb 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -84,6 +84,7 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(Reshape, ReshapeLayer); CV_DNN_REGISTER_LAYER_CLASS(Flatten, FlattenLayer); CV_DNN_REGISTER_LAYER_CLASS(ResizeNearestNeighbor, ResizeNearestNeighborLayer); + CV_DNN_REGISTER_LAYER_CLASS(CropAndResize, CropAndResizeLayer); CV_DNN_REGISTER_LAYER_CLASS(Convolution, ConvolutionLayer); CV_DNN_REGISTER_LAYER_CLASS(Deconvolution, DeconvolutionLayer); diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 4e38606ca3..2352b35c15 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -676,7 +676,7 @@ public: int j0 = std::max(0, (-in_j + dilation_w-1)/dilation_w); int j1 = std::min(kernel_w, (width - in_j + dilation_w-1)/dilation_w); - // here some non-continous sub-row of the row will not be + // here some non-continuous sub-row of the row will not be // filled from the tensor; we need to make sure that the uncovered // elements are explicitly set to 0's. the easiest way is to // set all the elements to 0's before the loop. @@ -966,8 +966,7 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); - CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget) && - OCL_PERFORMANCE_CHECK(ocl::Device::getDefault().isIntel()), + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), forward_ocl(inputs_arr, outputs_arr, internals_arr)) Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr); diff --git a/modules/dnn/src/layers/crop_and_resize_layer.cpp b/modules/dnn/src/layers/crop_and_resize_layer.cpp new file mode 100644 index 0000000000..3f92a8488d --- /dev/null +++ b/modules/dnn/src/layers/crop_and_resize_layer.cpp @@ -0,0 +1,108 @@ +#include "../precomp.hpp" +#include "layers_common.hpp" + +namespace cv { namespace dnn { + +class CropAndResizeLayerImpl CV_FINAL : public CropAndResizeLayer +{ +public: + CropAndResizeLayerImpl(const LayerParams& params) + { + CV_Assert(params.has("width"), params.has("height")); + outWidth = params.get("width"); + outHeight = params.get("height"); + } + + bool getMemoryShapes(const std::vector &inputs, + const int requiredOutputs, + std::vector &outputs, + std::vector &internals) const CV_OVERRIDE + { + CV_Assert(inputs.size() == 2, inputs[0].size() == 4); + if (inputs[0][0] != 1) + CV_Error(Error::StsNotImplemented, ""); + outputs.resize(1, MatShape(4)); + outputs[0][0] = inputs[1][2]; // Number of bounding boxes. + outputs[0][1] = inputs[0][1]; // Number of channels. + outputs[0][2] = outHeight; + outputs[0][3] = outWidth; + return false; + } + + void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE + { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr); + } + + void forward(std::vector &inputs, std::vector &outputs, std::vector &internals) CV_OVERRIDE + { + CV_TRACE_FUNCTION(); + CV_TRACE_ARG_VALUE(name, "name", name.c_str()); + + Mat& inp = *inputs[0]; + Mat& out = outputs[0]; + Mat boxes = inputs[1]->reshape(1, inputs[1]->total() / 7); + const int numChannels = inp.size[1]; + const int inpHeight = inp.size[2]; + const int inpWidth = inp.size[3]; + const int inpSpatialSize = inpHeight * inpWidth; + const int outSpatialSize = outHeight * outWidth; + CV_Assert(inp.isContinuous(), out.isContinuous()); + + for (int b = 0; b < boxes.rows; ++b) + { + float* outDataBox = out.ptr(b); + float left = boxes.at(b, 3); + float top = boxes.at(b, 4); + float right = boxes.at(b, 5); + float bottom = boxes.at(b, 6); + float boxWidth = right - left; + float boxHeight = bottom - top; + + float heightScale = boxHeight * static_cast(inpHeight - 1) / (outHeight - 1); + float widthScale = boxWidth * static_cast(inpWidth - 1) / (outWidth - 1); + for (int y = 0; y < outHeight; ++y) + { + float input_y = top * (inpHeight - 1) + y * heightScale; + int y0 = static_cast(input_y); + const float* inpData_row0 = (float*)inp.data + y0 * inpWidth; + const float* inpData_row1 = (y0 + 1 < inpHeight) ? (inpData_row0 + inpWidth) : inpData_row0; + for (int x = 0; x < outWidth; ++x) + { + float input_x = left * (inpWidth - 1) + x * widthScale; + int x0 = static_cast(input_x); + int x1 = std::min(x0 + 1, inpWidth - 1); + + float* outData = outDataBox + y * outWidth + x; + const float* inpData_row0_c = inpData_row0; + const float* inpData_row1_c = inpData_row1; + for (int c = 0; c < numChannels; ++c) + { + *outData = inpData_row0_c[x0] + + (input_y - y0) * (inpData_row1_c[x0] - inpData_row0_c[x0]) + + (input_x - x0) * (inpData_row0_c[x1] - inpData_row0_c[x0] + + (input_y - y0) * (inpData_row1_c[x1] - inpData_row0_c[x1] - inpData_row1_c[x0] + inpData_row0_c[x0])); + + inpData_row0_c += inpSpatialSize; + inpData_row1_c += inpSpatialSize; + outData += outSpatialSize; + } + } + } + } + } + +private: + int outWidth, outHeight; +}; + +Ptr CropAndResizeLayer::create(const LayerParams& params) +{ + return Ptr(new CropAndResizeLayerImpl(params)); +} + +} // namespace dnn +} // namespace cv diff --git a/modules/dnn/src/layers/detection_output_layer.cpp b/modules/dnn/src/layers/detection_output_layer.cpp index 44f7b32853..e838bcd55f 100644 --- a/modules/dnn/src/layers/detection_output_layer.cpp +++ b/modules/dnn/src/layers/detection_output_layer.cpp @@ -110,7 +110,7 @@ public: float _nmsThreshold; int _topK; - // Whenever predicted bounding boxes are respresented in YXHW instead of XYWH layout. + // Whenever predicted bounding boxes are represented in YXHW instead of XYWH layout. bool _locPredTransposed; // It's true whenever predicted bounding boxes and proposals are normalized to [0, 1]. bool _bboxesNormalized; @@ -208,8 +208,9 @@ public: CV_Assert(inputs[0][0] == inputs[1][0]); int numPriors = inputs[2][2] / 4; - CV_Assert((numPriors * _numLocClasses * 4) == inputs[0][1]); - CV_Assert(int(numPriors * _numClasses) == inputs[1][1]); + CV_Assert((numPriors * _numLocClasses * 4) == total(inputs[0], 1)); + CV_Assert(int(numPriors * _numClasses) == total(inputs[1], 1)); + CV_Assert(inputs[2][1] == 1 + (int)(!_varianceEncodedInTarget)); // num() and channels() are 1. // Since the number of bboxes to be kept is unknown before nms, we manually diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index a24b913ba4..32d39970ab 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -117,7 +117,7 @@ public: { return backendId == DNN_BACKEND_DEFAULT || backendId == DNN_BACKEND_HALIDE && haveHalide() || - backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine() && this->type != "Sigmoid"; + backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine(); } virtual Ptr tryAttach(const Ptr& node) CV_OVERRIDE @@ -176,8 +176,7 @@ public: { CV_TRACE_FUNCTION(); - CV_OCL_RUN(IS_DNN_OPENCL_TARGET(this->preferableTarget) && - OCL_PERFORMANCE_CHECK(ocl::Device::getDefault().isIntel()), + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(this->preferableTarget), func.applyOCL(inputs_arr, outputs_arr, internals_arr)) Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr); @@ -335,6 +334,7 @@ struct ReLUFunctor lp.type = "ReLU"; std::shared_ptr ieLayer(new InferenceEngine::ReLULayer(lp)); ieLayer->negative_slope = slope; + ieLayer->params["negative_slope"] = format("%f", slope); return ieLayer; } #endif // HAVE_INF_ENGINE @@ -432,6 +432,8 @@ struct ReLU6Functor std::shared_ptr ieLayer(new InferenceEngine::ClampLayer(lp)); ieLayer->min_value = minValue; ieLayer->max_value = maxValue; + ieLayer->params["min"] = format("%f", minValue); + ieLayer->params["max"] = format("%f", maxValue); return ieLayer; } #endif // HAVE_INF_ENGINE @@ -557,8 +559,9 @@ struct SigmoidFunctor #ifdef HAVE_INF_ENGINE InferenceEngine::CNNLayerPtr initInfEngine(InferenceEngine::LayerParams& lp) { - CV_Error(Error::StsNotImplemented, "Sigmoid"); - return InferenceEngine::CNNLayerPtr(); + lp.type = "Sigmoid"; + std::shared_ptr ieLayer(new InferenceEngine::CNNLayer(lp)); + return ieLayer; } #endif // HAVE_INF_ENGINE diff --git a/modules/dnn/src/layers/eltwise_layer.cpp b/modules/dnn/src/layers/eltwise_layer.cpp index 39961abb5f..61a7d0950c 100644 --- a/modules/dnn/src/layers/eltwise_layer.cpp +++ b/modules/dnn/src/layers/eltwise_layer.cpp @@ -79,7 +79,7 @@ public: else if (operation == "max") op = MAX; else - CV_Error(cv::Error::StsBadArg, "Unknown operaticon type \"" + operation + "\""); + CV_Error(cv::Error::StsBadArg, "Unknown operation type \"" + operation + "\""); } if (params.has("coeff")) diff --git a/modules/dnn/src/layers/mvn_layer.cpp b/modules/dnn/src/layers/mvn_layer.cpp index 647308ae0a..9e4f0ac39c 100644 --- a/modules/dnn/src/layers/mvn_layer.cpp +++ b/modules/dnn/src/layers/mvn_layer.cpp @@ -73,7 +73,7 @@ public: virtual bool tryFuse(Ptr& top) CV_OVERRIDE { - if (preferableTarget == DNN_TARGET_OPENCL && !fuse_batch_norm) + if (!fuse_batch_norm) { top->getScaleShift(scale, shift); fuse_batch_norm = !scale.empty() || !shift.empty(); @@ -252,8 +252,7 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); - CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget) && - OCL_PERFORMANCE_CHECK(ocl::Device::getDefault().isIntel()), + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), forward_ocl(inputs_arr, outputs_arr, internals_arr)) Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr); @@ -274,25 +273,53 @@ public: for( i = 0; i < splitDim; i++ ) newRows *= inpBlob.size[i]; - if (inpBlob.total() == newRows) + Mat inpMat = inpBlob.reshape(1, newRows); + Mat outMat = outBlob.reshape(1, newRows); + + if ( inpBlob.total() == newRows ) { // MVN is applied to single values at an every row. - outBlob.setTo(0); + if (shift.empty()) + { + outBlob.setTo(0); + } + else + { + for ( i = 0; i < newRows; i++ ) + { + outMat.row(i).setTo(((float*)shift.data)[i]); + } + } return; } - Mat inpMat = inpBlob.reshape(1, newRows); - Mat outMat = outBlob.reshape(1, newRows); - Scalar mean, dev; for ( i = 0; i < newRows; i++) { Mat inpRow = inpMat.row(i); Mat outRow = outMat.row(i); - + float weight = 1.f; + float bias = 0.f; + if (fuse_batch_norm) + { + weight = i < scale.cols ? ((float*)scale.data)[i] : weight; + bias = i < shift.cols ? ((float*)shift.data)[i] : bias; + } cv::meanStdDev(inpRow, mean, (normVariance) ? dev : noArray()); double alpha = (normVariance) ? 1/(eps + dev[0]) : 1; - inpRow.convertTo(outRow, outRow.type(), alpha, -mean[0] * alpha); + double normalizationScale = 1.0; + double normalizationShift = 0.0; + if (fuse_batch_norm) + { + normalizationScale = alpha * weight; + normalizationShift = -mean[0] * normalizationScale + bias; + } + else + { + normalizationScale = alpha; + normalizationShift = -mean[0] * alpha; + } + inpRow.convertTo(outRow, outRow.type(), normalizationScale, normalizationShift); } } } diff --git a/modules/dnn/src/layers/pooling_layer.cpp b/modules/dnn/src/layers/pooling_layer.cpp index 2bcce1d91e..548cb8acdd 100644 --- a/modules/dnn/src/layers/pooling_layer.cpp +++ b/modules/dnn/src/layers/pooling_layer.cpp @@ -191,8 +191,7 @@ public: CV_TRACE_FUNCTION(); CV_TRACE_ARG_VALUE(name, "name", name.c_str()); - CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget) && - OCL_PERFORMANCE_CHECK(ocl::Device::getDefault().isIntel()), + CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), forward_ocl(inputs_arr, outputs_arr, internals_arr)) Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr); diff --git a/modules/dnn/src/layers/prior_box_layer.cpp b/modules/dnn/src/layers/prior_box_layer.cpp index b854c2602a..5e0e338429 100644 --- a/modules/dnn/src/layers/prior_box_layer.cpp +++ b/modules/dnn/src/layers/prior_box_layer.cpp @@ -271,7 +271,7 @@ public: virtual bool supportBackend(int backendId) CV_OVERRIDE { return backendId == DNN_BACKEND_DEFAULT || - backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine() && !_explicitSizes; + backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine(); } bool getMemoryShapes(const std::vector &inputs, @@ -366,7 +366,7 @@ public: kernel.set(13, (int)_imageWidth); kernel.run(1, &nthreads, NULL, false); - // clip the prior's coordidate such that it is within [0, 1] + // clip the prior's coordinate such that it is within [0, 1] if (_clip) { Mat mat = outputs[0].getMat(ACCESS_READ); @@ -442,7 +442,7 @@ public: } } } - // clip the prior's coordidate such that it is within [0, 1] + // clip the prior's coordinate such that it is within [0, 1] if (_clip) { int _outChannelSize = _layerHeight * _layerWidth * _numPriors * 4; @@ -484,18 +484,33 @@ public: #ifdef HAVE_INF_ENGINE InferenceEngine::LayerParams lp; lp.name = name; - lp.type = "PriorBox"; + lp.type = _explicitSizes ? "PriorBoxClustered" : "PriorBox"; lp.precision = InferenceEngine::Precision::FP32; std::shared_ptr ieLayer(new InferenceEngine::CNNLayer(lp)); - ieLayer->params["min_size"] = format("%f", _minSize); - ieLayer->params["max_size"] = _maxSize > 0 ? format("%f", _maxSize) : ""; - - if (!_aspectRatios.empty()) + if (_explicitSizes) + { + CV_Assert(!_boxWidths.empty(), !_boxHeights.empty(), + _boxWidths.size() == _boxHeights.size()); + ieLayer->params["width"] = format("%f", _boxWidths[0]); + ieLayer->params["height"] = format("%f", _boxHeights[0]); + for (int i = 1; i < _boxWidths.size(); ++i) + { + ieLayer->params["width"] += format(",%f", _boxWidths[i]); + ieLayer->params["height"] += format(",%f", _boxHeights[i]); + } + } + else { - ieLayer->params["aspect_ratio"] = format("%f", _aspectRatios[0]); - for (int i = 1; i < _aspectRatios.size(); ++i) - ieLayer->params["aspect_ratio"] += format(",%f", _aspectRatios[i]); + ieLayer->params["min_size"] = format("%f", _minSize); + ieLayer->params["max_size"] = _maxSize > 0 ? format("%f", _maxSize) : ""; + + if (!_aspectRatios.empty()) + { + ieLayer->params["aspect_ratio"] = format("%f", _aspectRatios[0]); + for (int i = 1; i < _aspectRatios.size(); ++i) + ieLayer->params["aspect_ratio"] += format(",%f", _aspectRatios[i]); + } } ieLayer->params["flip"] = "0"; // We already flipped aspect ratios. @@ -550,7 +565,7 @@ private: std::vector _variance; std::vector _offsetsX; std::vector _offsetsY; - // Precomputed final widhts and heights based on aspect ratios or explicit sizes. + // Precomputed final widths and heights based on aspect ratios or explicit sizes. std::vector _boxWidths; std::vector _boxHeights; diff --git a/modules/dnn/src/layers/region_layer.cpp b/modules/dnn/src/layers/region_layer.cpp index 125fa0d14d..50e68b2fa5 100644 --- a/modules/dnn/src/layers/region_layer.cpp +++ b/modules/dnn/src/layers/region_layer.cpp @@ -95,11 +95,6 @@ public: return false; } - virtual bool supportBackend(int backendId) CV_OVERRIDE - { - return backendId == DNN_BACKEND_DEFAULT; - } - float logistic_activate(float x) { return 1.F / (1.F + exp(-x)); } void softmax_activate(const float* input, const int n, const float temp, float* output) diff --git a/modules/dnn/src/layers/resize_nearest_neighbor_layer.cpp b/modules/dnn/src/layers/resize_nearest_neighbor_layer.cpp index e9a966296e..448ea25ee4 100644 --- a/modules/dnn/src/layers/resize_nearest_neighbor_layer.cpp +++ b/modules/dnn/src/layers/resize_nearest_neighbor_layer.cpp @@ -6,6 +6,7 @@ // Third party copyrights are property of their respective owners. #include "../precomp.hpp" #include "layers_common.hpp" +#include "../op_inf_engine.hpp" #include namespace cv { namespace dnn { @@ -39,6 +40,12 @@ public: return (outputs[0][2] == inputs[0][2]) && (outputs[0][3] == inputs[0][3]); } + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_DEFAULT || + backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine(); + } + virtual void finalize(const std::vector& inputs, std::vector &outputs) CV_OVERRIDE { if (!outWidth && !outHeight) @@ -75,6 +82,26 @@ public: } } } + + virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE + { +#ifdef HAVE_INF_ENGINE + InferenceEngine::LayerParams lp; + lp.name = name; + lp.type = "Resample"; + lp.precision = InferenceEngine::Precision::FP32; + + std::shared_ptr ieLayer(new InferenceEngine::CNNLayer(lp)); + ieLayer->params["type"] = "caffe.ResampleParameter.NEAREST"; + ieLayer->params["antialias"] = "0"; + ieLayer->params["width"] = cv::format("%d", outWidth); + ieLayer->params["height"] = cv::format("%d", outHeight); + + return Ptr(new InfEngineBackendNode(ieLayer)); +#endif // HAVE_INF_ENGINE + return Ptr(); + } + private: int outWidth, outHeight, zoomFactor; bool alignCorners; diff --git a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp index 44a622f1d4..159319425e 100644 --- a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp +++ b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp @@ -709,7 +709,7 @@ bool OCL4DNNConvSpatial::swizzleWeight(const UMat &weight, return false; } } else { - // assumption: kernel dimesion is 2 + // assumption: kernel dimension is 2 Mat weightMat = weight.getMat(ACCESS_READ); Dtype* cpu_weight = (Dtype *)weightMat.ptr(); Mat swizzledWeightMat; diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index c24b137107..b03a67d5f3 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -18,11 +18,6 @@ namespace cv { namespace dnn { #ifdef HAVE_INF_ENGINE -static int infEngineVersion() -{ - return std::atoi(InferenceEngine::GetInferenceEngineVersion()->buildNumber); -} - InfEngineBackendNode::InfEngineBackendNode(const InferenceEngine::CNNLayerPtr& _layer) : BackendNode(DNN_BACKEND_INFERENCE_ENGINE), layer(_layer) {} @@ -59,27 +54,23 @@ infEngineWrappers(const std::vector >& ptrs) return wrappers; } +static InferenceEngine::Layout estimateLayout(const Mat& m) +{ + if (m.dims == 4) + return InferenceEngine::Layout::NCHW; + else if (m.dims == 2) + return InferenceEngine::Layout::NC; + else + return InferenceEngine::Layout::ANY; +} + static InferenceEngine::DataPtr wrapToInfEngineDataNode(const Mat& m, const std::string& name = "") { std::vector reversedShape(&m.size[0], &m.size[0] + m.dims); std::reverse(reversedShape.begin(), reversedShape.end()); - if (infEngineVersion() > 5855) - { - InferenceEngine::Layout l = InferenceEngine::Layout::ANY; - if (m.dims == 4) - l = InferenceEngine::Layout::NCHW; - else if (m.dims == 2) - l = InferenceEngine::Layout::NC; - return InferenceEngine::DataPtr( - new InferenceEngine::Data(name, reversedShape, InferenceEngine::Precision::FP32, l) - ); - } - else - { - return InferenceEngine::DataPtr( - new InferenceEngine::Data(name, reversedShape, InferenceEngine::Precision::FP32) - ); - } + return InferenceEngine::DataPtr( + new InferenceEngine::Data(name, reversedShape, InferenceEngine::Precision::FP32, estimateLayout(m)) + ); } InferenceEngine::TBlob::Ptr wrapToInfEngineBlob(const Mat& m, const std::vector& shape, @@ -108,7 +99,7 @@ InfEngineBackendWrapper::InfEngineBackendWrapper(int targetId, const cv::Mat& m) : BackendWrapper(DNN_BACKEND_INFERENCE_ENGINE, targetId) { dataPtr = wrapToInfEngineDataNode(m); - blob = wrapToInfEngineBlob(m); + blob = wrapToInfEngineBlob(m, estimateLayout(m)); } InfEngineBackendWrapper::~InfEngineBackendWrapper() @@ -252,7 +243,8 @@ InfEngineBackendNet::getLayerByName(const char *layerName, InferenceEngine::CNNL void InfEngineBackendNet::setTargetDevice(InferenceEngine::TargetDevice device) noexcept { if (device != InferenceEngine::TargetDevice::eCPU && - device != InferenceEngine::TargetDevice::eGPU) + device != InferenceEngine::TargetDevice::eGPU && + device != InferenceEngine::TargetDevice::eMYRIAD) CV_Error(Error::StsNotImplemented, ""); targetDevice = device; } @@ -296,7 +288,7 @@ void InfEngineBackendNet::init(int targetId) } for (const InferenceEngine::DataPtr& out : l->outData) { - // TODO: Replace to uniquness assertion. + // TODO: Replace to uniqueness assertion. if (internalOutputs.find(out->name) == internalOutputs.end()) internalOutputs[out->name] = out; } @@ -313,7 +305,7 @@ void InfEngineBackendNet::init(int targetId) // Add all outputs. for (const InferenceEngine::DataPtr& out : l->outData) { - // TODO: Replace to uniquness assertion. + // TODO: Replace to uniqueness assertion. if (unconnectedOuts.find(out->name) == unconnectedOuts.end()) unconnectedOuts[out->name] = out; } @@ -352,6 +344,11 @@ void InfEngineBackendNet::init(int targetId) case DNN_TARGET_CPU: setTargetDevice(InferenceEngine::TargetDevice::eCPU); break; case DNN_TARGET_OPENCL_FP16: setPrecision(InferenceEngine::Precision::FP16); // Fallback to the next. case DNN_TARGET_OPENCL: setTargetDevice(InferenceEngine::TargetDevice::eGPU); break; + case DNN_TARGET_MYRIAD: + { + setPrecision(InferenceEngine::Precision::FP16); + setTargetDevice(InferenceEngine::TargetDevice::eMYRIAD); break; + } default: CV_Error(Error::StsError, format("Unknown target identifier: %d", targetId)); } @@ -364,11 +361,21 @@ void InfEngineBackendNet::initPlugin(InferenceEngine::ICNNNetwork& net) { CV_Assert(!isInitialized()); - InferenceEngine::StatusCode status; - InferenceEngine::ResponseDesc resp; + static std::map sharedPlugins; + std::string deviceName = InferenceEngine::getDeviceName(targetDevice); + auto pluginIt = sharedPlugins.find(deviceName); + if (pluginIt != sharedPlugins.end()) + { + enginePtr = pluginIt->second; + } + else + { + enginePtr = InferenceEngine::PluginDispatcher({""}).getSuitablePlugin(targetDevice); + sharedPlugins[deviceName] = enginePtr; + } + plugin = InferenceEngine::InferencePlugin(enginePtr); - plugin = InferenceEngine::PluginDispatcher({""}).getSuitablePlugin(targetDevice); - if (infEngineVersion() > 5855 && targetDevice == InferenceEngine::TargetDevice::eCPU) + if (targetDevice == InferenceEngine::TargetDevice::eCPU) { #ifdef _WIN32 InferenceEngine::IExtensionPtr extension = @@ -377,18 +384,17 @@ void InfEngineBackendNet::initPlugin(InferenceEngine::ICNNNetwork& net) InferenceEngine::IExtensionPtr extension = InferenceEngine::make_so_pointer("libcpu_extension.so"); #endif // _WIN32 - status = plugin->AddExtension(extension, &resp); - if (status != InferenceEngine::StatusCode::OK) - CV_Error(Error::StsAssert, resp.msg); + plugin.AddExtension(extension); } - status = plugin->LoadNetwork(net, &resp); - if (status != InferenceEngine::StatusCode::OK) - CV_Error(Error::StsAssert, resp.msg); + netExec = plugin.LoadNetwork(net, {}); + infRequest = netExec.CreateInferRequest(); + infRequest.SetInput(inpBlobs); + infRequest.SetOutput(outBlobs); } bool InfEngineBackendNet::isInitialized() { - return (bool)plugin; + return (bool)enginePtr; } void InfEngineBackendNet::addBlobs(const std::vector >& ptrs) @@ -402,10 +408,7 @@ void InfEngineBackendNet::addBlobs(const std::vector >& ptrs void InfEngineBackendNet::forward() { - InferenceEngine::ResponseDesc resp; - InferenceEngine::StatusCode status = plugin->Infer(inpBlobs, outBlobs, &resp); - if (status != InferenceEngine::StatusCode::OK) - CV_Error(Error::StsAssert, resp.msg); + infRequest.Infer(); } Mat infEngineBlobToMat(const InferenceEngine::Blob::Ptr& blob) diff --git a/modules/dnn/src/op_inf_engine.hpp b/modules/dnn/src/op_inf_engine.hpp index a61678cab2..075c1be849 100644 --- a/modules/dnn/src/op_inf_engine.hpp +++ b/modules/dnn/src/op_inf_engine.hpp @@ -89,7 +89,10 @@ private: InferenceEngine::BlobMap allBlobs; InferenceEngine::TargetDevice targetDevice; InferenceEngine::Precision precision; - InferenceEngine::InferenceEnginePluginPtr plugin; + InferenceEngine::InferenceEnginePluginPtr enginePtr; + InferenceEngine::InferencePlugin plugin; + InferenceEngine::ExecutableNetwork netExec; + InferenceEngine::InferRequest infRequest; void initPlugin(InferenceEngine::ICNNNetwork& net); }; diff --git a/modules/dnn/src/opencl/mvn.cl b/modules/dnn/src/opencl/mvn.cl index 49a8ebbe64..ffc81a8704 100644 --- a/modules/dnn/src/opencl/mvn.cl +++ b/modules/dnn/src/opencl/mvn.cl @@ -89,7 +89,8 @@ __kernel void CALC_MEAN(__global const Dtype* src, Dtype mean_val = mean[x]; vec_type src_vec = load(src, index); - vec_type dst_vec = native_powr(src_vec - (vec_type)mean_val, 2); + vec_type dst_vec = src_vec - (vec_type)mean_val; + dst_vec = dst_vec * dst_vec; store(dst_vec, dst, index); } @@ -197,10 +198,14 @@ __kernel void MEAN_FUSE(__global const T * A, const T4 a2 = vload4(i, src0_read + 2 * A_col_size); const T4 a3 = vload4(i, src0_read + 3 * A_col_size); - dot0 = native_powr(convert_float4(a0) - (Dtype4)sum.x, 2); - dot1 = native_powr(convert_float4(a1) - (Dtype4)sum.y, 2); - dot2 = native_powr(convert_float4(a2) - (Dtype4)sum.z, 2); - dot3 = native_powr(convert_float4(a3) - (Dtype4)sum.w, 2); + dot0 = convert_float4(a0) - (Dtype4)sum.x; + dot1 = convert_float4(a1) - (Dtype4)sum.y; + dot2 = convert_float4(a2) - (Dtype4)sum.z; + dot3 = convert_float4(a3) - (Dtype4)sum.w; + dot0 = dot0 * dot0; + dot1 = dot1 * dot1; + dot2 = dot2 * dot2; + dot3 = dot3 * dot3; vstore4(dot0, i, dst0_read); vstore4(dot1, i, dst0_read + A_col_size); diff --git a/modules/dnn/src/tensorflow/graph.proto b/modules/dnn/src/tensorflow/graph.proto index f945201399..478d35a9fe 100644 --- a/modules/dnn/src/tensorflow/graph.proto +++ b/modules/dnn/src/tensorflow/graph.proto @@ -86,7 +86,7 @@ message NodeDef { // | ( ("gpu" | "cpu") ":" ([1-9][0-9]* | "*") ) // // Valid values for this string include: - // * "@other/node" (colocate with "other/node") + // * "@other/node" (collocate with "other/node") // * "/job:worker/replica:0/task:1/gpu:3" (full specification) // * "/job:worker/gpu:3" (partial specification) // * "" (no specification) diff --git a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp index 9208588e65..a537358a1f 100644 --- a/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp +++ b/modules/dnn/src/tensorflow/tf_graph_simplifier.cpp @@ -5,6 +5,8 @@ // Copyright (C) 2018, Intel Corporation, all rights reserved. // Third party copyrights are property of their respective owners. +#include "../precomp.hpp" + #ifdef HAVE_PROTOBUF #include "tf_graph_simplifier.hpp" diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index 195b516813..f19daf9cc6 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -644,8 +644,9 @@ void TFImporter::populateNet(Net dstNet) CV_Assert(layer.input_size() == 3); DictValue dilation = parseDims(getConstBlob(layer, value_id, 1)); - CV_Assert(dilation.size() == 2 && dilation.get(0) == dilation.get(1)); - layerParams.set("dilation", dilation.get(0)); + CV_Assert(dilation.size() == 2); + layerParams.set("dilation_h", dilation.get(0)); + layerParams.set("dilation_w", dilation.get(1)); Mat paddings; parseTensor(getConstBlob(layer, value_id, 2), paddings); @@ -655,6 +656,10 @@ void TFImporter::populateNet(Net dstNet) layerParams.set("pad_w", paddings.at(2)); StrIntVector next_layers = getNextLayers(net, name, "Conv2D"); + if (next_layers.empty()) + { + next_layers = getNextLayers(net, name, "DepthwiseConv2dNative"); + } CV_Assert(next_layers.size() == 1); layer = net.node(next_layers[0].second); layers_to_ignore.insert(next_layers[0].first); @@ -1089,9 +1094,9 @@ void TFImporter::populateNet(Net dstNet) CV_Assert(!begins.empty(), !sizes.empty(), begins.type() == CV_32SC1, sizes.type() == CV_32SC1); - if (begins.total() == 4) + if (begins.total() == 4 && data_layouts[name] == DATA_LAYOUT_NHWC) { - // Perhabs, we have an NHWC order. Swap it to NCHW. + // Swap NHWC parameters' order to NCHW. std::swap(*begins.ptr(0, 2), *begins.ptr(0, 3)); std::swap(*begins.ptr(0, 1), *begins.ptr(0, 2)); std::swap(*sizes.ptr(0, 2), *sizes.ptr(0, 3)); @@ -1171,6 +1176,9 @@ void TFImporter::populateNet(Net dstNet) layers_to_ignore.insert(next_layers[0].first); } + if (hasLayerAttr(layer, "axis")) + layerParams.set("axis", getLayerAttr(layer, "axis").i()); + id = dstNet.addLayer(name, "Scale", layerParams); } layer_id[name] = id; @@ -1542,6 +1550,10 @@ void TFImporter::populateNet(Net dstNet) layerParams.set("confidence_threshold", getLayerAttr(layer, "confidence_threshold").f()); if (hasLayerAttr(layer, "loc_pred_transposed")) layerParams.set("loc_pred_transposed", getLayerAttr(layer, "loc_pred_transposed").b()); + if (hasLayerAttr(layer, "clip")) + layerParams.set("clip", getLayerAttr(layer, "clip").b()); + if (hasLayerAttr(layer, "variance_encoded_in_target")) + layerParams.set("variance_encoded_in_target", getLayerAttr(layer, "variance_encoded_in_target").b()); int id = dstNet.addLayer(name, "DetectionOutput", layerParams); layer_id[name] = id; @@ -1558,6 +1570,26 @@ void TFImporter::populateNet(Net dstNet) layer_id[name] = id; connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, layer.input_size()); } + else if (type == "CropAndResize") + { + // op: "CropAndResize" + // input: "input" + // input: "boxes" + // input: "sizes" + CV_Assert(layer.input_size() == 3); + + Mat cropSize = getTensorContent(getConstBlob(layer, value_id, 2)); + CV_Assert(cropSize.type() == CV_32SC1, cropSize.total() == 2); + + layerParams.set("height", cropSize.at(0)); + layerParams.set("width", cropSize.at(1)); + + int id = dstNet.addLayer(name, "CropAndResize", layerParams); + layer_id[name] = id; + + connect(layer_id, dstNet, parsePin(layer.input(0)), id, 0); + connect(layer_id, dstNet, parsePin(layer.input(1)), id, 1); + } else if (type == "Mean") { Mat indices = getTensorContent(getConstBlob(layer, value_id, 1)); diff --git a/modules/dnn/src/torch/torch_importer.cpp b/modules/dnn/src/torch/torch_importer.cpp index 813ee085cb..3607e6c08e 100644 --- a/modules/dnn/src/torch/torch_importer.cpp +++ b/modules/dnn/src/torch/torch_importer.cpp @@ -311,11 +311,11 @@ struct TorchImporter int numModules = curModule->modules.size(); readTorchObject(index); - if (tensors.count(index)) //tensor was readed + if (tensors.count(index)) //tensor was read { tensorParams.insert(std::make_pair(key, std::make_pair(index, tensors[index]))); } - else if (storages.count(index)) //storage was readed + else if (storages.count(index)) //storage was read { Mat &matStorage = storages[index]; Mat matCasted; @@ -399,7 +399,7 @@ struct TorchImporter size_t requireElems = (size_t)offset + (size_t)steps[0] * (size_t)sizes[0]; size_t storageElems = storages[indexStorage].total(); if (requireElems > storageElems) - CV_Error(Error::StsBadSize, "Storage has insufficent number of elemements for requested Tensor"); + CV_Error(Error::StsBadSize, "Storage has insufficient number of elements for requested Tensor"); //convert sizes AutoBuffer isizes(ndims); diff --git a/modules/dnn/test/test_backends.cpp b/modules/dnn/test/test_backends.cpp index 2bcd357e2e..8dd823e553 100644 --- a/modules/dnn/test/test_backends.cpp +++ b/modules/dnn/test/test_backends.cpp @@ -49,7 +49,14 @@ public: throw SkipTestException("OpenCL is not available/disabled in OpenCV"); } } - if (target == DNN_TARGET_OPENCL_FP16) + if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + { + if (!checkMyriadTarget()) + { + throw SkipTestException("Myriad is not available/disabled in OpenCV"); + } + } + if (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) { l1 = l1 == 0.0 ? 4e-3 : l1; lInf = lInf == 0.0 ? 2e-2 : lInf; @@ -80,10 +87,7 @@ public: } Mat out = net.forward(outputLayer).clone(); - if (outputLayer == "detection_out") - normAssertDetections(outDefault, out, "First run", 0.2, l1, lInf); - else - normAssert(outDefault, out, "First run", l1, lInf); + check(outDefault, out, outputLayer, l1, lInf, "First run"); // Test 2: change input. float* inpData = (float*)inp.data; @@ -97,18 +101,33 @@ public: net.setInput(inp); outDefault = netDefault.forward(outputLayer).clone(); out = net.forward(outputLayer).clone(); + check(outDefault, out, outputLayer, l1, lInf, "Second run"); + } + void check(Mat& ref, Mat& out, const std::string& outputLayer, double l1, double lInf, const char* msg) + { if (outputLayer == "detection_out") - normAssertDetections(outDefault, out, "Second run", 0.2, l1, lInf); + { + if (backend == DNN_BACKEND_INFERENCE_ENGINE) + { + // Inference Engine produces detections terminated by a row which starts from -1. + out = out.reshape(1, out.total() / 7); + int numDetections = 0; + while (numDetections < out.rows && out.at(numDetections, 0) != -1) + { + numDetections += 1; + } + out = out.rowRange(0, numDetections); + } + normAssertDetections(ref, out, msg, 0.2, l1, lInf); + } else - normAssert(outDefault, out, "Second run", l1, lInf); + normAssert(ref, out, msg, l1, lInf); } }; TEST_P(DNNTestNetwork, AlexNet) { - if (backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) - throw SkipTestException(""); processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt", Size(227, 227), "prob", target == DNN_TARGET_OPENCL ? "dnn/halide_scheduler_opencl_alexnet.yml" : @@ -158,8 +177,7 @@ TEST_P(DNNTestNetwork, ENet) TEST_P(DNNTestNetwork, MobileNet_SSD_Caffe) { - if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/street.png", false)); Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false); @@ -170,10 +188,11 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_Caffe) inp, "detection_out", "", l1, lInf); } +// TODO: update MobileNet model. TEST_P(DNNTestNetwork, MobileNet_SSD_TensorFlow) { if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + backend == DNN_BACKEND_INFERENCE_ENGINE) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/street.png", false)); Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false); @@ -185,31 +204,38 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_TensorFlow) TEST_P(DNNTestNetwork, SSD_VGG16) { - if ((backend == DNN_BACKEND_DEFAULT && target == DNN_TARGET_OPENCL_FP16) || - (backend == DNN_BACKEND_HALIDE && target == DNN_TARGET_CPU) || - (backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU)) + if (backend == DNN_BACKEND_HALIDE && target == DNN_TARGET_CPU) throw SkipTestException(""); + double scoreThreshold = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.0252 : 0.0; + Mat sample = imread(findDataFile("dnn/street.png", false)); + Mat inp = blobFromImage(sample, 1.0f, Size(300, 300), Scalar(), false); processNet("dnn/VGG_ILSVRC2016_SSD_300x300_iter_440000.caffemodel", - "dnn/ssd_vgg16.prototxt", Size(300, 300), "detection_out"); + "dnn/ssd_vgg16.prototxt", inp, "detection_out", "", scoreThreshold); } TEST_P(DNNTestNetwork, OpenPose_pose_coco) { - if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + throw SkipTestException(""); processNet("dnn/openpose_pose_coco.caffemodel", "dnn/openpose_pose_coco.prototxt", Size(368, 368)); } TEST_P(DNNTestNetwork, OpenPose_pose_mpi) { - if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + throw SkipTestException(""); processNet("dnn/openpose_pose_mpi.caffemodel", "dnn/openpose_pose_mpi.prototxt", Size(368, 368)); } TEST_P(DNNTestNetwork, OpenPose_pose_mpi_faster_4_stages) { - if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); + if (backend == DNN_BACKEND_HALIDE || + backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + throw SkipTestException(""); // The same .caffemodel but modified .prototxt // See https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/src/openpose/pose/poseParameters.cpp processNet("dnn/openpose_pose_mpi.caffemodel", "dnn/openpose_pose_mpi_faster_4_stages.prototxt", @@ -226,11 +252,13 @@ TEST_P(DNNTestNetwork, OpenFace) TEST_P(DNNTestNetwork, opencv_face_detector) { - if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + if (backend == DNN_BACKEND_HALIDE) throw SkipTestException(""); + Size inpSize; + if (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD) + inpSize = Size(300, 300); Mat img = imread(findDataFile("gpu/lbpcascade/er.png", false)); - Mat inp = blobFromImage(img, 1.0, Size(), Scalar(104.0, 177.0, 123.0), false, false); + Mat inp = blobFromImage(img, 1.0, inpSize, Scalar(104.0, 177.0, 123.0), false, false); processNet("dnn/opencv_face_detector.caffemodel", "dnn/opencv_face_detector.prototxt", inp, "detection_out"); } @@ -238,12 +266,13 @@ TEST_P(DNNTestNetwork, opencv_face_detector) TEST_P(DNNTestNetwork, Inception_v2_SSD_TensorFlow) { if (backend == DNN_BACKEND_HALIDE || - backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU) + (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL) || + (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16)) throw SkipTestException(""); Mat sample = imread(findDataFile("dnn/street.png", false)); Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false); - float l1 = (backend == DNN_BACKEND_DEFAULT && target == DNN_TARGET_OPENCL_FP16) ? 0.008 : 0.0; - float lInf = (backend == DNN_BACKEND_DEFAULT && target == DNN_TARGET_OPENCL_FP16) ? 0.07 : 0.0; + float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.008 : 0.0; + float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.07 : 0.0; processNet("dnn/ssd_inception_v2_coco_2017_11_17.pb", "dnn/ssd_inception_v2_coco_2017_11_17.pbtxt", inp, "detection_out", "", l1, lInf); } @@ -252,7 +281,8 @@ TEST_P(DNNTestNetwork, DenseNet_121) { if ((backend == DNN_BACKEND_HALIDE) || (backend == DNN_BACKEND_DEFAULT && target == DNN_TARGET_OPENCL_FP16) || - (backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16)) + (backend == DNN_BACKEND_INFERENCE_ENGINE && (target == DNN_TARGET_OPENCL_FP16 || + target == DNN_TARGET_MYRIAD))) throw SkipTestException(""); processNet("dnn/DenseNet_121.caffemodel", "dnn/DenseNet_121.prototxt", Size(224, 224), "", "caffe"); } @@ -266,6 +296,7 @@ const tuple testCases[] = { tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU), tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL), tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16), + tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD), #endif tuple(DNN_BACKEND_DEFAULT, DNN_TARGET_OPENCL), tuple(DNN_BACKEND_DEFAULT, DNN_TARGET_OPENCL_FP16) diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp index 872d19dce4..8e8ea74d83 100644 --- a/modules/dnn/test/test_common.hpp +++ b/modules/dnn/test/test_common.hpp @@ -147,6 +147,28 @@ inline void normAssertDetections(cv::Mat ref, cv::Mat out, const char *comment = testBoxes, comment, confThreshold, scores_diff, boxes_iou_diff); } +inline bool checkMyriadTarget() +{ +#ifndef HAVE_INF_ENGINE + return false; +#endif + cv::dnn::Net net; + cv::dnn::LayerParams lp; + net.addLayerToPrev("testLayer", "Identity", lp); + net.setPreferableBackend(cv::dnn::DNN_BACKEND_INFERENCE_ENGINE); + net.setPreferableTarget(cv::dnn::DNN_TARGET_MYRIAD); + net.setInput(cv::Mat::zeros(1, 1, CV_32FC1)); + try + { + net.forward(); + } + catch(...) + { + return false; + } + return true; +} + inline bool readFileInMemory(const std::string& filename, std::string& content) { std::ios::openmode mode = std::ios::in | std::ios::binary; diff --git a/modules/dnn/test/test_darknet_importer.cpp b/modules/dnn/test/test_darknet_importer.cpp index a7679daf6f..17d33d7662 100644 --- a/modules/dnn/test/test_darknet_importer.cpp +++ b/modules/dnn/test/test_darknet_importer.cpp @@ -71,13 +71,31 @@ static void testDarknetModel(const std::string& cfg, const std::string& weights, const std::vector& refClassIds, const std::vector& refConfidences, const std::vector& refBoxes, - int targetId, float confThreshold = 0.24) + int backendId, int targetId, float scoreDiff = 0.0, + float iouDiff = 0.0, float confThreshold = 0.24) { + if (backendId == DNN_BACKEND_DEFAULT && targetId == DNN_TARGET_OPENCL) + { + #ifdef HAVE_OPENCL + if (!cv::ocl::useOpenCL()) + #endif + { + throw SkipTestException("OpenCL is not available/disabled in OpenCV"); + } + } + if (backendId == DNN_BACKEND_INFERENCE_ENGINE && targetId == DNN_TARGET_MYRIAD) + { + if (!checkMyriadTarget()) + { + throw SkipTestException("Myriad is not available/disabled in OpenCV"); + } + } Mat sample = imread(_tf("dog416.png")); Mat inp = blobFromImage(sample, 1.0/255, Size(416, 416), Scalar(), true, false); Net net = readNet(findDataFile("dnn/" + cfg, false), findDataFile("dnn/" + weights, false)); + net.setPreferableBackend(backendId); net.setPreferableTarget(targetId); net.setInput(inp); std::vector outs; @@ -108,42 +126,53 @@ static void testDarknetModel(const std::string& cfg, const std::string& weights, } } normAssertDetections(refClassIds, refConfidences, refBoxes, classIds, - confidences, boxes, "", confThreshold, 8e-5, 3e-5); + confidences, boxes, "", confThreshold, scoreDiff, iouDiff); } -typedef testing::TestWithParam Test_Darknet_nets; +typedef testing::TestWithParam > Test_Darknet_nets; TEST_P(Test_Darknet_nets, YoloVoc) { - int targetId = GetParam(); + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + if (backendId == DNN_BACKEND_INFERENCE_ENGINE && targetId == DNN_TARGET_MYRIAD) + throw SkipTestException(""); std::vector outNames(1, "detection_out"); std::vector classIds(3); std::vector confidences(3); std::vector boxes(3); classIds[0] = 6; confidences[0] = 0.750469f; boxes[0] = Rect2d(0.577374, 0.127391, 0.325575, 0.173418); // a car - classIds[1] = 1; confidences[1] = 0.780879f; boxes[1] = Rect2d(0.270762, 0.264102, 0.461713, 0.48131); // a bycicle + classIds[1] = 1; confidences[1] = 0.780879f; boxes[1] = Rect2d(0.270762, 0.264102, 0.461713, 0.48131); // a bicycle classIds[2] = 11; confidences[2] = 0.901615f; boxes[2] = Rect2d(0.1386, 0.338509, 0.282737, 0.60028); // a dog + double scoreDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 7e-3 : 8e-5; + double iouDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 0.013 : 3e-5; testDarknetModel("yolo-voc.cfg", "yolo-voc.weights", outNames, - classIds, confidences, boxes, targetId); + classIds, confidences, boxes, backendId, targetId, scoreDiff, iouDiff); } TEST_P(Test_Darknet_nets, TinyYoloVoc) { - int targetId = GetParam(); + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); std::vector outNames(1, "detection_out"); std::vector classIds(2); std::vector confidences(2); std::vector boxes(2); classIds[0] = 6; confidences[0] = 0.761967f; boxes[0] = Rect2d(0.579042, 0.159161, 0.31544, 0.160779); // a car classIds[1] = 11; confidences[1] = 0.780595f; boxes[1] = Rect2d(0.129696, 0.386467, 0.315579, 0.534527); // a dog + double scoreDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 8e-3 : 8e-5; + double iouDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 8e-3 : 3e-5; testDarknetModel("tiny-yolo-voc.cfg", "tiny-yolo-voc.weights", outNames, - classIds, confidences, boxes, targetId); + classIds, confidences, boxes, backendId, targetId, scoreDiff, iouDiff); } TEST_P(Test_Darknet_nets, YOLOv3) { - int targetId = GetParam(); + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + if (backendId == DNN_BACKEND_INFERENCE_ENGINE && targetId == DNN_TARGET_MYRIAD) + throw SkipTestException(""); std::vector outNames(3); outNames[0] = "yolo_82"; outNames[1] = "yolo_94"; @@ -153,13 +182,27 @@ TEST_P(Test_Darknet_nets, YOLOv3) std::vector confidences(3); std::vector boxes(3); classIds[0] = 7; confidences[0] = 0.952983f; boxes[0] = Rect2d(0.614622, 0.150257, 0.286747, 0.138994); // a truck - classIds[1] = 1; confidences[1] = 0.987908f; boxes[1] = Rect2d(0.150913, 0.221933, 0.591342, 0.524327); // a bycicle + classIds[1] = 1; confidences[1] = 0.987908f; boxes[1] = Rect2d(0.150913, 0.221933, 0.591342, 0.524327); // a bicycle classIds[2] = 16; confidences[2] = 0.998836f; boxes[2] = Rect2d(0.160024, 0.389964, 0.257861, 0.553752); // a dog (COCO) + double scoreDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 4e-3 : 8e-5; + double iouDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 0.011 : 3e-5; testDarknetModel("yolov3.cfg", "yolov3.weights", outNames, - classIds, confidences, boxes, targetId); + classIds, confidences, boxes, backendId, targetId, scoreDiff, iouDiff); } -INSTANTIATE_TEST_CASE_P(/**/, Test_Darknet_nets, availableDnnTargets()); +const tuple testCases[] = { +#ifdef HAVE_INF_ENGINE + tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_CPU), + tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL), + tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_OPENCL_FP16), + tuple(DNN_BACKEND_INFERENCE_ENGINE, DNN_TARGET_MYRIAD), +#endif + tuple(DNN_BACKEND_DEFAULT, DNN_TARGET_CPU), + tuple(DNN_BACKEND_DEFAULT, DNN_TARGET_OPENCL), + tuple(DNN_BACKEND_DEFAULT, DNN_TARGET_OPENCL_FP16) +}; + +INSTANTIATE_TEST_CASE_P(/**/, Test_Darknet_nets, testing::ValuesIn(testCases)); static void testDarknetLayer(const std::string& name, bool hasWeights = false) { diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 89c6ed8915..593864822c 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -834,6 +834,84 @@ TEST(Test_DLDT, two_inputs) normAssert(out, firstInp + secondInp); } + +class UnsupportedLayer : public Layer +{ +public: + UnsupportedLayer(const LayerParams ¶ms) {} + + static Ptr create(const LayerParams& params) + { + return Ptr(new UnsupportedLayer(params)); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_DEFAULT; + } + + virtual void forward(std::vector &inputs, std::vector &outputs, std::vector &internals) CV_OVERRIDE {} + + virtual void forward(cv::InputArrayOfArrays inputs, cv::OutputArrayOfArrays outputs, cv::OutputArrayOfArrays internals) CV_OVERRIDE {} +}; + +TEST(Test_DLDT, fused_output) +{ + static const int kNumChannels = 3; + CV_DNN_REGISTER_LAYER_CLASS(Unsupported, UnsupportedLayer); + Net net; + { + LayerParams lp; + lp.set("kernel_size", 1); + lp.set("num_output", 3); + lp.set("bias_term", false); + lp.type = "Convolution"; + lp.name = "testConv"; + lp.blobs.push_back(Mat({kNumChannels, 1, 1, 1}, CV_32F, Scalar(1))); + net.addLayerToPrev(lp.name, lp.type, lp); + } + { + LayerParams lp; + lp.set("bias_term", false); + lp.type = "Scale"; + lp.name = "testScale"; + lp.blobs.push_back(Mat({kNumChannels}, CV_32F, Scalar(1))); + net.addLayerToPrev(lp.name, lp.type, lp); + } + { + LayerParams lp; + net.addLayerToPrev("unsupported_layer", "Unsupported", lp); + } + net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE); + net.setInput(Mat({1, 1, 1, 1}, CV_32FC1, Scalar(1))); + ASSERT_NO_THROW(net.forward()); + LayerFactory::unregisterLayer("Unsupported"); +} + +TEST(Test_DLDT, multiple_networks) +{ + Net nets[2]; + for (int i = 0; i < 2; ++i) + { + nets[i].setInputsNames(std::vector(1, format("input_%d", i))); + + LayerParams lp; + lp.set("kernel_size", 1); + lp.set("num_output", 1); + lp.set("bias_term", false); + lp.type = "Convolution"; + lp.name = format("testConv_%d", i); + lp.blobs.push_back(Mat({1, 1, 1, 1}, CV_32F, Scalar(1 + i))); + nets[i].addLayerToPrev(lp.name, lp.type, lp); + nets[i].setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE); + nets[i].setInput(Mat({1, 1, 1, 1}, CV_32FC1, Scalar(1))); + } + Mat out_1 = nets[0].forward(); + Mat out_2 = nets[1].forward(); + // After the second model is initialized we try to receive an output from the first network again. + out_1 = nets[0].forward(); + normAssert(2 * out_1, out_2); +} #endif // HAVE_INF_ENGINE // Test a custom layer. diff --git a/modules/dnn/test/test_precomp.hpp b/modules/dnn/test/test_precomp.hpp index 54c9ce6c79..70b7b3d25e 100644 --- a/modules/dnn/test/test_precomp.hpp +++ b/modules/dnn/test/test_precomp.hpp @@ -49,11 +49,11 @@ #include "opencv2/dnn.hpp" #include "test_common.hpp" -namespace opencv_test { +namespace opencv_test { namespace { using namespace cv::dnn; CV_ENUM(DNNBackend, DNN_BACKEND_DEFAULT, DNN_BACKEND_HALIDE, DNN_BACKEND_INFERENCE_ENGINE) -CV_ENUM(DNNTarget, DNN_TARGET_CPU, DNN_TARGET_OPENCL, DNN_TARGET_OPENCL_FP16) +CV_ENUM(DNNTarget, DNN_TARGET_CPU, DNN_TARGET_OPENCL, DNN_TARGET_OPENCL_FP16, DNN_TARGET_MYRIAD) static testing::internal::ParamGenerator availableDnnTargets() { @@ -69,6 +69,6 @@ static testing::internal::ParamGenerator availableDnnTargets() return testing::ValuesIn(targets); } -} +}} #endif diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index 3f02fb2220..84205f72fb 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -124,6 +124,7 @@ TEST_P(Test_TensorFlow_layers, conv) runTensorFlowNet("atrous_conv2d_valid", targetId); runTensorFlowNet("atrous_conv2d_same", targetId); runTensorFlowNet("depthwise_conv2d", targetId); + runTensorFlowNet("keras_atrous_conv2d_same", targetId); } TEST_P(Test_TensorFlow_layers, padding) @@ -160,10 +161,12 @@ TEST_P(Test_TensorFlow_layers, batch_norm) TEST_P(Test_TensorFlow_layers, pooling) { int targetId = GetParam(); + cv::ocl::Device d = cv::ocl::Device::getDefault(); + bool loosenFlag = targetId == DNN_TARGET_OPENCL && d.isIntel() && d.type() == cv::ocl::Device::TYPE_CPU; runTensorFlowNet("max_pool_even", targetId); runTensorFlowNet("max_pool_odd_valid", targetId); runTensorFlowNet("ave_pool_same", targetId); - runTensorFlowNet("max_pool_odd_same", targetId); + runTensorFlowNet("max_pool_odd_same", targetId, false, loosenFlag ? 3e-5 : 1e-5, loosenFlag ? 3e-4 : 1e-4); runTensorFlowNet("reduce_mean", targetId); // an average pooling over all spatial dimensions. } @@ -267,6 +270,22 @@ TEST_P(Test_TensorFlow_nets, Inception_v2_SSD) normAssertDetections(ref, out, "", 0.5); } +TEST_P(Test_TensorFlow_nets, Inception_v2_Faster_RCNN) +{ + std::string proto = findDataFile("dnn/faster_rcnn_inception_v2_coco_2018_01_28.pbtxt", false); + std::string model = findDataFile("dnn/faster_rcnn_inception_v2_coco_2018_01_28.pb", false); + + Net net = readNetFromTensorflow(model, proto); + Mat img = imread(findDataFile("dnn/dog416.png", false)); + Mat blob = blobFromImage(img, 1.0f / 127.5, Size(800, 600), Scalar(127.5, 127.5, 127.5), true, false); + + net.setInput(blob); + Mat out = net.forward(); + + Mat ref = blobFromNPY(findDataFile("dnn/tensorflow/faster_rcnn_inception_v2_coco_2018_01_28.detection_out.npy")); + normAssertDetections(ref, out, "", 0.3); +} + TEST_P(Test_TensorFlow_nets, opencv_face_detector_uint8) { std::string proto = findDataFile("dnn/opencv_face_detector.pbtxt", false); diff --git a/modules/dnn/test/test_torch_importer.cpp b/modules/dnn/test/test_torch_importer.cpp index 33e0e94801..ab74b190af 100644 --- a/modules/dnn/test/test_torch_importer.cpp +++ b/modules/dnn/test/test_torch_importer.cpp @@ -250,7 +250,7 @@ TEST_P(Test_Torch_nets, ENet_accuracy) Mat out = net.forward(); Mat ref = blobFromNPY(_tf("torch_enet_prob.npy", false)); // Due to numerical instability in Pooling-Unpooling layers (indexes jittering) - // thresholds for ENet must be changed. Accuracy of resuults was checked on + // thresholds for ENet must be changed. Accuracy of results was checked on // Cityscapes dataset and difference in mIOU with Torch is 10E-4% normAssert(ref, out, "", 0.00044, 0.44); diff --git a/modules/features2d/src/agast.cpp b/modules/features2d/src/agast.cpp index ab01b67805..8b63234b29 100644 --- a/modules/features2d/src/agast.cpp +++ b/modules/features2d/src/agast.cpp @@ -7952,6 +7952,12 @@ public: { CV_INSTRUMENT_REGION() + if(_image.empty()) + { + keypoints.clear(); + return; + } + Mat mask = _mask.getMat(), grayImage; UMat ugrayImage; _InputArray gray = _image; diff --git a/modules/features2d/src/fast.cpp b/modules/features2d/src/fast.cpp index 5299e14ae5..b586d30a22 100644 --- a/modules/features2d/src/fast.cpp +++ b/modules/features2d/src/fast.cpp @@ -522,6 +522,12 @@ public: { CV_INSTRUMENT_REGION() + if(_image.empty()) + { + keypoints.clear(); + return; + } + Mat mask = _mask.getMat(), grayImage; UMat ugrayImage; _InputArray gray = _image; diff --git a/modules/features2d/src/gftt.cpp b/modules/features2d/src/gftt.cpp index 1b8010625b..e4a594a5c6 100644 --- a/modules/features2d/src/gftt.cpp +++ b/modules/features2d/src/gftt.cpp @@ -80,6 +80,12 @@ public: { CV_INSTRUMENT_REGION() + if(_image.empty()) + { + keypoints.clear(); + return; + } + std::vector corners; if (_image.isUMat()) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index f4cf3711d7..b03deba66b 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -159,40 +159,7 @@ In OpenCV you only need applyColorMap to apply a colormap on a given image. The code reads the path to an image from command line, applies a Jet colormap on it and shows the result: -@code -#include -#include -#include -#include -using namespace cv; - -#include -using namespace std; - -int main(int argc, const char *argv[]) -{ - // We need an input image. (can be grayscale or color) - if (argc < 2) - { - cerr << "We need an image to process here. Please run: colorMap [path_to_image]" << endl; - return -1; - } - Mat img_in = imread(argv[1]); - if(img_in.empty()) - { - cerr << "Sample image (" << argv[1] << ") is empty. Please adjust your path, so it points to a valid input image!" << endl; - return -1; - } - // Holds the colormap version of the image: - Mat img_color; - // Apply the colormap: - applyColorMap(img_in, img_color, COLORMAP_JET); - // Show the result: - imshow("colorMap", img_color); - waitKey(0); - return 0; -} -@endcode +@include snippets/imgproc_applyColorMap.cpp @see #ColormapTypes @@ -2007,58 +1974,7 @@ The function implements the probabilistic Hough transform algorithm for line det in @cite Matas00 See the line detection example below: - -@code - #include - #include - - using namespace cv; - using namespace std; - - int main(int argc, char** argv) - { - Mat src, dst, color_dst; - if( argc != 2 || !(src=imread(argv[1], 0)).data) - return -1; - - Canny( src, dst, 50, 200, 3 ); - cvtColor( dst, color_dst, COLOR_GRAY2BGR ); - - #if 0 - vector lines; - HoughLines( dst, lines, 1, CV_PI/180, 100 ); - - for( size_t i = 0; i < lines.size(); i++ ) - { - float rho = lines[i][0]; - float theta = lines[i][1]; - double a = cos(theta), b = sin(theta); - double x0 = a*rho, y0 = b*rho; - Point pt1(cvRound(x0 + 1000*(-b)), - cvRound(y0 + 1000*(a))); - Point pt2(cvRound(x0 - 1000*(-b)), - cvRound(y0 - 1000*(a))); - line( color_dst, pt1, pt2, Scalar(0,0,255), 3, 8 ); - } - #else - vector lines; - HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 ); - for( size_t i = 0; i < lines.size(); i++ ) - { - line( color_dst, Point(lines[i][0], lines[i][1]), - Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 ); - } - #endif - namedWindow( "Source", 1 ); - imshow( "Source", src ); - - namedWindow( "Detected Lines", 1 ); - imshow( "Detected Lines", color_dst ); - - waitKey(0); - return 0; - } -@endcode +@include snippets/imgproc_HoughLinesP.cpp This is a sample picture the function parameters have been tuned for: ![image](pics/building.jpg) @@ -2114,41 +2030,7 @@ An example using the Hough circle detector The function finds circles in a grayscale image using a modification of the Hough transform. Example: : -@code - #include - #include - #include - - using namespace cv; - using namespace std; - - int main(int argc, char** argv) - { - Mat img, gray; - if( argc != 2 || !(img=imread(argv[1], 1)).data) - return -1; - cvtColor(img, gray, COLOR_BGR2GRAY); - // smooth it, otherwise a lot of false circles may be detected - GaussianBlur( gray, gray, Size(9, 9), 2, 2 ); - vector circles; - HoughCircles(gray, circles, HOUGH_GRADIENT, - 2, gray.rows/4, 200, 100 ); - for( size_t i = 0; i < circles.size(); i++ ) - { - Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); - int radius = cvRound(circles[i][2]); - // draw the circle center - circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 ); - // draw the circle outline - circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 ); - } - namedWindow( "circles", 1 ); - imshow( "circles", img ); - - waitKey(0); - return 0; - } -@endcode +@include snippets/imgproc_HoughLinesCircles.cpp @note Usually the function detects the centers of circles well. However, it may fail to find correct radii. You can assist to the function by specifying the radius range ( minRadius and maxRadius ) if @@ -3247,63 +3129,7 @@ An example for creating histograms of an image The function cv::calcHist calculates the histogram of one or more arrays. The elements of a tuple used to increment a histogram bin are taken from the corresponding input arrays at the same location. The sample below shows how to compute a 2D Hue-Saturation histogram for a color image. : -@code - #include - #include - - using namespace cv; - - int main( int argc, char** argv ) - { - Mat src, hsv; - if( argc != 2 || !(src=imread(argv[1], 1)).data ) - return -1; - - cvtColor(src, hsv, COLOR_BGR2HSV); - - // Quantize the hue to 30 levels - // and the saturation to 32 levels - int hbins = 30, sbins = 32; - int histSize[] = {hbins, sbins}; - // hue varies from 0 to 179, see cvtColor - float hranges[] = { 0, 180 }; - // saturation varies from 0 (black-gray-white) to - // 255 (pure spectrum color) - float sranges[] = { 0, 256 }; - const float* ranges[] = { hranges, sranges }; - MatND hist; - // we compute the histogram from the 0-th and 1-st channels - int channels[] = {0, 1}; - - calcHist( &hsv, 1, channels, Mat(), // do not use mask - hist, 2, histSize, ranges, - true, // the histogram is uniform - false ); - double maxVal=0; - minMaxLoc(hist, 0, &maxVal, 0, 0); - - int scale = 10; - Mat histImg = Mat::zeros(sbins*scale, hbins*10, CV_8UC3); - - for( int h = 0; h < hbins; h++ ) - for( int s = 0; s < sbins; s++ ) - { - float binVal = hist.at(h, s); - int intensity = cvRound(binVal*255/maxVal); - rectangle( histImg, Point(h*scale, s*scale), - Point( (h+1)*scale - 1, (s+1)*scale - 1), - Scalar::all(intensity), - CV_FILLED ); - } - - namedWindow( "Source", 1 ); - imshow( "Source", src ); - - namedWindow( "H-S Histogram", 1 ); - imshow( "H-S Histogram", histImg ); - waitKey(); - } -@endcode +@include snippets/imgproc_calcHist.cpp @param images Source arrays. They all should have the same depth, CV_8U, CV_16U or CV_32F , and the same size. Each of them can have an arbitrary number of channels. @@ -4698,47 +4524,7 @@ An example using drawContours to clean up a background segmentation result The function draws contour outlines in the image if \f$\texttt{thickness} \ge 0\f$ or fills the area bounded by the contours if \f$\texttt{thickness}<0\f$ . The example below shows how to retrieve connected components from the binary image and label them: : -@code - #include "opencv2/imgproc.hpp" - #include "opencv2/highgui.hpp" - - using namespace cv; - using namespace std; - - int main( int argc, char** argv ) - { - Mat src; - // the first command-line parameter must be a filename of the binary - // (black-n-white) image - if( argc != 2 || !(src=imread(argv[1], 0)).data) - return -1; - - Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3); - - src = src > 1; - namedWindow( "Source", 1 ); - imshow( "Source", src ); - - vector > contours; - vector hierarchy; - - findContours( src, contours, hierarchy, - RETR_CCOMP, CHAIN_APPROX_SIMPLE ); - - // iterate through all the top-level contours, - // draw each connected component with its own random color - int idx = 0; - for( ; idx >= 0; idx = hierarchy[idx][0] ) - { - Scalar color( rand()&255, rand()&255, rand()&255 ); - drawContours( dst, contours, idx, color, FILLED, 8, hierarchy ); - } - - namedWindow( "Components", 1 ); - imshow( "Components", dst ); - waitKey(0); - } -@endcode +@include snippets/imgproc_drawContours.cpp @param image Destination image. @param contours All the input contours. Each contour is stored as a point vector. diff --git a/modules/imgproc/src/color_hsv.cpp b/modules/imgproc/src/color_hsv.cpp index d5a41dfcec..94a36f1106 100644 --- a/modules/imgproc/src/color_hsv.cpp +++ b/modules/imgproc/src/color_hsv.cpp @@ -213,6 +213,91 @@ struct RGB2HSV_f }; +#if CV_SIMD128 +inline void HSV2RGB_simd(v_float32x4& v_h, v_float32x4& v_s, v_float32x4& v_v, float hscale) +{ + v_h = v_h * v_setall_f32(hscale); + v_float32x4 v_pre_sector = v_cvt_f32(v_trunc(v_h)); + v_h = v_h - v_pre_sector; + v_float32x4 v_tab0 = v_v; + v_float32x4 v_one = v_setall_f32(1.0f); + v_float32x4 v_tab1 = v_v * (v_one - v_s); + v_float32x4 v_tab2 = v_v * (v_one - (v_s * v_h)); + v_float32x4 v_tab3 = v_v * (v_one - (v_s * (v_one - v_h))); + + v_float32x4 v_one_sixth = v_setall_f32(1.0f / 6.0f); + v_float32x4 v_sector = v_pre_sector * v_one_sixth; + v_sector = v_cvt_f32(v_trunc(v_sector)); + v_float32x4 v_six = v_setall_f32(6.0f); + v_sector = v_pre_sector - (v_sector * v_six); + + v_float32x4 v_two = v_setall_f32(2.0f); + v_h = v_tab1 & (v_sector < v_two); + v_h = v_h | (v_tab3 & (v_sector == v_two)); + v_float32x4 v_three = v_setall_f32(3.0f); + v_h = v_h | (v_tab0 & (v_sector == v_three)); + v_float32x4 v_four = v_setall_f32(4.0f); + v_h = v_h | (v_tab0 & (v_sector == v_four)); + v_h = v_h | (v_tab2 & (v_sector > v_four)); + + v_s = v_tab3 & (v_sector < v_one); + v_s = v_s | (v_tab0 & (v_sector == v_one)); + v_s = v_s | (v_tab0 & (v_sector == v_two)); + v_s = v_s | (v_tab2 & (v_sector == v_three)); + v_s = v_s | (v_tab1 & (v_sector > v_three)); + + v_v = v_tab0 & (v_sector < v_one); + v_v = v_v | (v_tab2 & (v_sector == v_one)); + v_v = v_v | (v_tab1 & (v_sector == v_two)); + v_v = v_v | (v_tab1 & (v_sector == v_three)); + v_v = v_v | (v_tab3 & (v_sector == v_four)); + v_v = v_v | (v_tab0 & (v_sector > v_four)); +} +#endif + + +inline void HSV2RGB_native(const float* src, float* dst, const float hscale, const int bidx) +{ + float h = src[0], s = src[1], v = src[2]; + float b, g, r; + + if( s == 0 ) + b = g = r = v; + else + { + static const int sector_data[][3]= + {{1,3,0}, {1,0,2}, {3,0,1}, {0,2,1}, {0,1,3}, {2,1,0}}; + float tab[4]; + int sector; + h *= hscale; + if( h < 0 ) + do h += 6; while( h < 0 ); + else if( h >= 6 ) + do h -= 6; while( h >= 6 ); + sector = cvFloor(h); + h -= sector; + if( (unsigned)sector >= 6u ) + { + sector = 0; + h = 0.f; + } + + tab[0] = v; + tab[1] = v*(1.f - s); + tab[2] = v*(1.f - s*h); + tab[3] = v*(1.f - s*(1.f - h)); + + b = tab[sector_data[sector][0]]; + g = tab[sector_data[sector][1]]; + r = tab[sector_data[sector][2]]; + } + + dst[bidx] = b; + dst[1] = g; + dst[bidx^2] = r; +} + + struct HSV2RGB_f { typedef float channel_type; @@ -224,152 +309,49 @@ struct HSV2RGB_f #endif } - #if CV_SIMD128 - inline void process(v_float32x4& v_h, v_float32x4& v_s, - v_float32x4& v_v, v_float32x4& v_scale) const - { - v_h = v_h * v_scale; - v_float32x4 v_pre_sector = v_cvt_f32(v_trunc(v_h)); - v_h = v_h - v_pre_sector; - v_float32x4 v_tab0 = v_v; - v_float32x4 v_one = v_setall_f32(1.0f); - v_float32x4 v_tab1 = v_v * (v_one - v_s); - v_float32x4 v_tab2 = v_v * (v_one - (v_s * v_h)); - v_float32x4 v_tab3 = v_v * (v_one - (v_s * (v_one - v_h))); - - v_float32x4 v_one_sixth = v_setall_f32(1.0f / 6.0f); - v_float32x4 v_sector = v_pre_sector * v_one_sixth; - v_sector = v_cvt_f32(v_trunc(v_sector)); - v_float32x4 v_six = v_setall_f32(6.0f); - v_sector = v_pre_sector - (v_sector * v_six); - - v_float32x4 v_two = v_setall_f32(2.0f); - v_h = v_tab1 & (v_sector < v_two); - v_h = v_h | (v_tab3 & (v_sector == v_two)); - v_float32x4 v_three = v_setall_f32(3.0f); - v_h = v_h | (v_tab0 & (v_sector == v_three)); - v_float32x4 v_four = v_setall_f32(4.0f); - v_h = v_h | (v_tab0 & (v_sector == v_four)); - v_h = v_h | (v_tab2 & (v_sector > v_four)); - - v_s = v_tab3 & (v_sector < v_one); - v_s = v_s | (v_tab0 & (v_sector == v_one)); - v_s = v_s | (v_tab0 & (v_sector == v_two)); - v_s = v_s | (v_tab2 & (v_sector == v_three)); - v_s = v_s | (v_tab1 & (v_sector > v_three)); - - v_v = v_tab0 & (v_sector < v_one); - v_v = v_v | (v_tab2 & (v_sector == v_one)); - v_v = v_v | (v_tab1 & (v_sector == v_two)); - v_v = v_v | (v_tab1 & (v_sector == v_three)); - v_v = v_v | (v_tab3 & (v_sector == v_four)); - v_v = v_v | (v_tab0 & (v_sector > v_four)); - } - #endif - void operator()(const float* src, float* dst, int n) const { int i = 0, bidx = blueIdx, dcn = dstcn; - float alpha = ColorChannel::max(); n *= 3; - #if CV_SIMD128 - if (hasSIMD) + if (dcn == 3) { - v_float32x4 v_scale = v_setall_f32(hscale); - if (dcn == 3) + #if CV_SIMD128 + if (hasSIMD) { - if (bidx) - { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_s; - v_float32x4 v_v; - v_load_deinterleave(src + i, v_h, v_s, v_v); - process(v_h, v_s, v_v, v_scale); - v_store_interleave(dst, v_v, v_s, v_h); - } - } else { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_s; - v_float32x4 v_v; - v_load_deinterleave(src + i, v_h, v_s, v_v); - process(v_h, v_s, v_v, v_scale); - v_store_interleave(dst, v_h, v_s, v_v); - } - } - } else { // dcn == 4 - v_float32x4 v_a = v_setall_f32(alpha); - if (bidx) + for (; i <= n - 12; i += 12, dst += dcn * 4) { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_s; - v_float32x4 v_v; - v_load_deinterleave(src + i, v_h, v_s, v_v); - process(v_h, v_s, v_v, v_scale); - v_store_interleave(dst, v_v, v_s, v_h, v_a); - } - } else { - for (; i <= n - 12; i += 12, dst += dcn * 4) - { - v_float32x4 v_h; - v_float32x4 v_s; - v_float32x4 v_v; - v_load_deinterleave(src + i, v_h, v_s, v_v); - process(v_h, v_s, v_v, v_scale); - v_store_interleave(dst, v_h, v_s, v_v, v_a); - } + v_float32x4 v_src[3]; + v_load_deinterleave(src + i, v_src[0], v_src[1], v_src[2]); + HSV2RGB_simd(v_src[0], v_src[1], v_src[2], hscale); + v_store_interleave(dst, v_src[bidx], v_src[1], v_src[bidx^2]); } } - } - #endif - - for( ; i < n; i += 3, dst += dcn ) - { - float h = src[i], s = src[i+1], v = src[i+2]; - float b, g, r; - - if( s == 0 ) - b = g = r = v; - else + #endif + for( ; i < n; i += 3, dst += dcn ) { - static const int sector_data[][3]= - {{1,3,0}, {1,0,2}, {3,0,1}, {0,2,1}, {0,1,3}, {2,1,0}}; - float tab[4]; - int sector; - h *= hscale; - if( h < 0 ) - do h += 6; while( h < 0 ); - else if( h >= 6 ) - do h -= 6; while( h >= 6 ); - sector = cvFloor(h); - h -= sector; - if( (unsigned)sector >= 6u ) + HSV2RGB_native(src + i, dst, hscale, bidx); + } + } else { // dcn == 4 + float alpha = ColorChannel::max(); + #if CV_SIMD128 + if (hasSIMD) + { + for (; i <= n - 12; i += 12, dst += dcn * 4) { - sector = 0; - h = 0.f; + v_float32x4 v_src[3]; + v_load_deinterleave(src + i, v_src[0], v_src[1], v_src[2]); + HSV2RGB_simd(v_src[0], v_src[1], v_src[2], hscale); + v_float32x4 v_a = v_setall_f32(alpha); + v_store_interleave(dst, v_src[bidx], v_src[1], v_src[bidx^2], v_a); } - - tab[0] = v; - tab[1] = v*(1.f - s); - tab[2] = v*(1.f - s*h); - tab[3] = v*(1.f - s*(1.f - h)); - - b = tab[sector_data[sector][0]]; - g = tab[sector_data[sector][1]]; - r = tab[sector_data[sector][2]]; } - - dst[bidx] = b; - dst[1] = g; - dst[bidx^2] = r; - if( dcn == 4 ) + #endif + for( ; i < n; i += 3, dst += dcn ) + { + HSV2RGB_native(src + i, dst, hscale, bidx); dst[3] = alpha; + } } } @@ -386,216 +368,111 @@ struct HSV2RGB_b typedef uchar channel_type; HSV2RGB_b(int _dstcn, int _blueIdx, int _hrange) - : dstcn(_dstcn), cvt(3, _blueIdx, (float)_hrange) + : dstcn(_dstcn), blueIdx(_blueIdx), hscale(6.0f / _hrange) { - #if CV_NEON - v_scale_inv = vdupq_n_f32(1.f/255.f); - v_scale = vdupq_n_f32(255.f); - v_alpha = vdup_n_u8(ColorChannel::max()); - #elif CV_SSE2 - v_scale = _mm_set1_ps(255.0f); - v_alpha = _mm_set1_ps(ColorChannel::max()); - v_zero = _mm_setzero_si128(); - haveSIMD = checkHardwareSupport(CV_CPU_SSE2); + #if CV_SIMD128 + hasSIMD = hasSIMD128(); #endif } - #if CV_SSE2 - void process(__m128i v_r, __m128i v_g, __m128i v_b, - const __m128& v_coeffs_, - float * buf) const - { - __m128 v_r0 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v_r, v_zero)); - __m128 v_g0 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v_g, v_zero)); - __m128 v_b0 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v_b, v_zero)); - - __m128 v_r1 = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v_r, v_zero)); - __m128 v_g1 = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v_g, v_zero)); - __m128 v_b1 = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v_b, v_zero)); - - __m128 v_coeffs = v_coeffs_; - - v_r0 = _mm_mul_ps(v_r0, v_coeffs); - v_g1 = _mm_mul_ps(v_g1, v_coeffs); - - v_coeffs = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v_coeffs), 0x49)); - - v_r1 = _mm_mul_ps(v_r1, v_coeffs); - v_b0 = _mm_mul_ps(v_b0, v_coeffs); - - v_coeffs = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v_coeffs), 0x49)); - - v_g0 = _mm_mul_ps(v_g0, v_coeffs); - v_b1 = _mm_mul_ps(v_b1, v_coeffs); - - _mm_store_ps(buf, v_r0); - _mm_store_ps(buf + 4, v_r1); - _mm_store_ps(buf + 8, v_g0); - _mm_store_ps(buf + 12, v_g1); - _mm_store_ps(buf + 16, v_b0); - _mm_store_ps(buf + 20, v_b1); - } - #endif - void operator()(const uchar* src, uchar* dst, int n) const { - int i, j, dcn = dstcn; + int j = 0, dcn = dstcn; uchar alpha = ColorChannel::max(); - float CV_DECL_ALIGNED(16) buf[3*BLOCK_SIZE]; - #if CV_SSE2 - __m128 v_coeffs = _mm_set_ps(1.f, 1.f/255.f, 1.f/255.f, 1.f); - #endif - for( i = 0; i < n; i += BLOCK_SIZE, src += BLOCK_SIZE*3 ) + #if CV_SIMD128 + if (hasSIMD) { - int dn = std::min(n - i, (int)BLOCK_SIZE); - j = 0; - - #if CV_NEON - for ( ; j <= (dn - 8) * 3; j += 24) - { - uint8x8x3_t v_src = vld3_u8(src + j); - uint16x8_t v_t0 = vmovl_u8(v_src.val[0]), - v_t1 = vmovl_u8(v_src.val[1]), - v_t2 = vmovl_u8(v_src.val[2]); - - float32x4x3_t v_dst; - v_dst.val[0] = vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t0))); - v_dst.val[1] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t1))), v_scale_inv); - v_dst.val[2] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_t2))), v_scale_inv); - vst3q_f32(buf + j, v_dst); - - v_dst.val[0] = vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t0))); - v_dst.val[1] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t1))), v_scale_inv); - v_dst.val[2] = vmulq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_t2))), v_scale_inv); - vst3q_f32(buf + j + 12, v_dst); - } - #elif CV_SSE2 - if (haveSIMD) + for (j = 0; j <= (n - 16) * 3; j += 48, dst += dcn * 16) { - for ( ; j <= (dn - 8) * 3; j += 24) + v_uint8x16 h_b, s_b, v_b; + v_uint16x8 h_w[2], s_w[2], v_w[2]; + v_uint32x4 h_u[4], s_u[4], v_u[4]; + v_load_deinterleave(src + j, h_b, s_b, v_b); + v_expand(h_b, h_w[0], h_w[1]); + v_expand(s_b, s_w[0], s_w[1]); + v_expand(v_b, v_w[0], v_w[1]); + v_expand(h_w[0], h_u[0], h_u[1]); + v_expand(h_w[1], h_u[2], h_u[3]); + v_expand(s_w[0], s_u[0], s_u[1]); + v_expand(s_w[1], s_u[2], s_u[3]); + v_expand(v_w[0], v_u[0], v_u[1]); + v_expand(v_w[1], v_u[2], v_u[3]); + + v_int32x4 b_i[4], g_i[4], r_i[4]; + v_float32x4 v_coeff0 = v_setall_f32(1.0f / 255.0f); + v_float32x4 v_coeff1 = v_setall_f32(255.0f); + + for( int k = 0; k < 4; k++ ) { - __m128i v_src0 = _mm_loadu_si128((__m128i const *)(src + j)); - __m128i v_src1 = _mm_loadl_epi64((__m128i const *)(src + j + 16)); - - process(_mm_unpacklo_epi8(v_src0, v_zero), - _mm_unpackhi_epi8(v_src0, v_zero), - _mm_unpacklo_epi8(v_src1, v_zero), - v_coeffs, - buf + j); + v_float32x4 v_src[3]; + v_src[0] = v_cvt_f32(v_reinterpret_as_s32(h_u[k])); + v_src[1] = v_cvt_f32(v_reinterpret_as_s32(s_u[k])); + v_src[2] = v_cvt_f32(v_reinterpret_as_s32(v_u[k])); + + v_src[1] *= v_coeff0; + v_src[2] *= v_coeff0; + HSV2RGB_simd(v_src[0], v_src[1], v_src[2], hscale); + + v_src[0] *= v_coeff1; + v_src[1] *= v_coeff1; + v_src[2] *= v_coeff1; + b_i[k] = v_trunc(v_src[0]); + g_i[k] = v_trunc(v_src[1]); + r_i[k] = v_trunc(v_src[2]); } - } - #endif - for( ; j < dn*3; j += 3 ) - { - buf[j] = src[j]; - buf[j+1] = src[j+1]*(1.f/255.f); - buf[j+2] = src[j+2]*(1.f/255.f); - } - cvt(buf, buf, dn); + v_uint16x8 r_w[2], g_w[2], b_w[2]; + v_uint8x16 r_b, g_b, b_b; - j = 0; - #if CV_NEON - for ( ; j <= (dn - 8) * 3; j += 24, dst += dcn * 8) - { - float32x4x3_t v_src0 = vld3q_f32(buf + j), v_src1 = vld3q_f32(buf + j + 12); - uint8x8_t v_dst0 = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[0], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[0], v_scale))))); - uint8x8_t v_dst1 = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[1], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[1], v_scale))))); - uint8x8_t v_dst2 = vqmovn_u16(vcombine_u16(vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src0.val[2], v_scale))), - vqmovn_u32(cv_vrndq_u32_f32(vmulq_f32(v_src1.val[2], v_scale))))); + r_w[0] = v_pack_u(r_i[0], r_i[1]); + r_w[1] = v_pack_u(r_i[2], r_i[3]); + r_b = v_pack(r_w[0], r_w[1]); + g_w[0] = v_pack_u(g_i[0], g_i[1]); + g_w[1] = v_pack_u(g_i[2], g_i[3]); + g_b = v_pack(g_w[0], g_w[1]); + b_w[0] = v_pack_u(b_i[0], b_i[1]); + b_w[1] = v_pack_u(b_i[2], b_i[3]); + b_b = v_pack(b_w[0], b_w[1]); - if (dcn == 4) + if( dcn == 3 ) { - uint8x8x4_t v_dst; - v_dst.val[0] = v_dst0; - v_dst.val[1] = v_dst1; - v_dst.val[2] = v_dst2; - v_dst.val[3] = v_alpha; - vst4_u8(dst, v_dst); + if( blueIdx == 0 ) + v_store_interleave(dst, b_b, g_b, r_b); + else + v_store_interleave(dst, r_b, g_b, b_b); } else { - uint8x8x3_t v_dst; - v_dst.val[0] = v_dst0; - v_dst.val[1] = v_dst1; - v_dst.val[2] = v_dst2; - vst3_u8(dst, v_dst); + v_uint8x16 alpha_b = v_setall_u8(alpha); + if( blueIdx == 0 ) + v_store_interleave(dst, b_b, g_b, r_b, alpha_b); + else + v_store_interleave(dst, r_b, g_b, b_b, alpha_b); } } - #elif CV_SSE2 - if (dcn == 3 && haveSIMD) - { - for ( ; j <= (dn * 3 - 16); j += 16, dst += 16) - { - __m128 v_src0 = _mm_mul_ps(_mm_load_ps(buf + j), v_scale); - __m128 v_src1 = _mm_mul_ps(_mm_load_ps(buf + j + 4), v_scale); - __m128 v_src2 = _mm_mul_ps(_mm_load_ps(buf + j + 8), v_scale); - __m128 v_src3 = _mm_mul_ps(_mm_load_ps(buf + j + 12), v_scale); - - __m128i v_dst0 = _mm_packs_epi32(_mm_cvtps_epi32(v_src0), - _mm_cvtps_epi32(v_src1)); - __m128i v_dst1 = _mm_packs_epi32(_mm_cvtps_epi32(v_src2), - _mm_cvtps_epi32(v_src3)); - - _mm_storeu_si128((__m128i *)dst, _mm_packus_epi16(v_dst0, v_dst1)); - } - - int jr = j % 3; - if (jr) - dst -= jr, j -= jr; - } - else if (dcn == 4 && haveSIMD) - { - for ( ; j <= (dn * 3 - 12); j += 12, dst += 16) - { - __m128 v_buf0 = _mm_mul_ps(_mm_load_ps(buf + j), v_scale); - __m128 v_buf1 = _mm_mul_ps(_mm_load_ps(buf + j + 4), v_scale); - __m128 v_buf2 = _mm_mul_ps(_mm_load_ps(buf + j + 8), v_scale); - - __m128 v_ba0 = _mm_unpackhi_ps(v_buf0, v_alpha); - __m128 v_ba1 = _mm_unpacklo_ps(v_buf2, v_alpha); - - __m128i v_src0 = _mm_cvtps_epi32(_mm_shuffle_ps(v_buf0, v_ba0, 0x44)); - __m128i v_src1 = _mm_shuffle_epi32(_mm_cvtps_epi32(_mm_shuffle_ps(v_ba0, v_buf1, 0x4e)), 0x78); - __m128i v_src2 = _mm_cvtps_epi32(_mm_shuffle_ps(v_buf1, v_ba1, 0x4e)); - __m128i v_src3 = _mm_shuffle_epi32(_mm_cvtps_epi32(_mm_shuffle_ps(v_ba1, v_buf2, 0xee)), 0x78); - - __m128i v_dst0 = _mm_packs_epi32(v_src0, v_src1); - __m128i v_dst1 = _mm_packs_epi32(v_src2, v_src3); - - _mm_storeu_si128((__m128i *)dst, _mm_packus_epi16(v_dst0, v_dst1)); - } - - int jr = j % 3; - if (jr) - dst -= jr, j -= jr; - } - #endif - - for( ; j < dn*3; j += 3, dst += dcn ) - { - dst[0] = saturate_cast(buf[j]*255.f); - dst[1] = saturate_cast(buf[j+1]*255.f); - dst[2] = saturate_cast(buf[j+2]*255.f); - if( dcn == 4 ) - dst[3] = alpha; - } + } + #endif + for( ; j < n * 3; j += 3, dst += dcn ) + { + float buf[6]; + buf[0] = src[j]; + buf[1] = src[j+1] * (1.0f / 255.0f); + buf[2] = src[j+2] * (1.0f / 255.0f); + HSV2RGB_native(buf, buf + 3, hscale, blueIdx); + dst[0] = saturate_cast(buf[3] * 255.0f); + dst[1] = saturate_cast(buf[4] * 255.0f); + dst[2] = saturate_cast(buf[5] * 255.0f); + if( dcn == 4 ) + dst[3] = alpha; } } int dstcn; - HSV2RGB_f cvt; - #if CV_NEON - float32x4_t v_scale, v_scale_inv; - uint8x8_t v_alpha; - #elif CV_SSE2 - __m128 v_scale; - __m128 v_alpha; - __m128i v_zero; - bool haveSIMD; + int blueIdx; + float hscale; + #if CV_SIMD128 + bool hasSIMD; #endif }; diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index 9ff52e3bcc..f3ddeaf78c 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -821,6 +821,10 @@ static bool ipp_calchist(const Mat &image, Mat &hist, int histSize, const float* return false; #endif + // IPP_DISABLE_HISTOGRAM - https://github.com/opencv/opencv/issues/11544 + if (uniform && (ranges[0][1] - ranges[0][0]) != histSize) + return false; + Mat ihist = hist; if(accumulate) ihist.create(1, &histSize, CV_32S); diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index ec05edf888..1f61146193 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -803,7 +803,7 @@ static bool ocl_HoughLines(InputArray _src, OutputArray _lines, double rho, doub int total_points = counters.getMat(ACCESS_READ).at(0, 0); if (total_points <= 0) { - _lines.assign(UMat(0,0,CV_32FC2)); + _lines.release(); return true; } @@ -831,7 +831,7 @@ static bool ocl_HoughLines(InputArray _src, OutputArray _lines, double rho, doub if (total_lines > 0) _lines.assign(lines.rowRange(Range(0, total_lines))); else - _lines.assign(UMat(0,0,CV_32FC2)); + _lines.release(); return true; } @@ -857,7 +857,7 @@ static bool ocl_HoughLinesP(InputArray _src, OutputArray _lines, double rho, dou int total_points = counters.getMat(ACCESS_READ).at(0, 0); if (total_points <= 0) { - _lines.assign(UMat(0,0,CV_32SC4)); + _lines.release(); return true; } @@ -885,7 +885,7 @@ static bool ocl_HoughLinesP(InputArray _src, OutputArray _lines, double rho, dou if (total_lines > 0) _lines.assign(lines.rowRange(Range(0, total_lines))); else - _lines.assign(UMat(0,0,CV_32SC4)); + _lines.release(); return true; } diff --git a/modules/imgproc/test/test_histograms.cpp b/modules/imgproc/test/test_histograms.cpp index 10f74a3eb5..5386c29ac7 100644 --- a/modules/imgproc/test/test_histograms.cpp +++ b/modules/imgproc/test/test_histograms.cpp @@ -1918,5 +1918,35 @@ TEST(Imgproc_Hist_CalcBackProject, accuracy) { CV_CalcBackProjectTest test; test TEST(Imgproc_Hist_CalcBackProjectPatch, accuracy) { CV_CalcBackProjectPatchTest test; test.safe_run(); } TEST(Imgproc_Hist_BayesianProb, accuracy) { CV_BayesianProbTest test; test.safe_run(); } +TEST(Imgproc_Hist_Calc, calcHist_regression_11544) +{ + cv::Mat1w m = cv::Mat1w::zeros(10, 10); + int n_images = 1; + int channels[] = { 0 }; + cv::Mat mask; + cv::MatND hist1, hist2; + cv::MatND hist1_opt, hist2_opt; + int dims = 1; + int hist_size[] = { 1000 }; + float range1[] = { 0, 900 }; + float range2[] = { 0, 1000 }; + const float* ranges1[] = { range1 }; + const float* ranges2[] = { range2 }; + + setUseOptimized(false); + cv::calcHist(&m, n_images, channels, mask, hist1, dims, hist_size, ranges1); + cv::calcHist(&m, n_images, channels, mask, hist2, dims, hist_size, ranges2); + + setUseOptimized(true); + cv::calcHist(&m, n_images, channels, mask, hist1_opt, dims, hist_size, ranges1); + cv::calcHist(&m, n_images, channels, mask, hist2_opt, dims, hist_size, ranges2); + + for(int i = 0; i < 1000; i++) + { + EXPECT_EQ(hist1.at(i, 0), hist1_opt.at(i, 0)) << i; + EXPECT_EQ(hist2.at(i, 0), hist2_opt.at(i, 0)) << i; + } +} + }} // namespace /* End Of File */ diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 4101022ec6..e16fcbacf2 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -10,6 +10,13 @@ #pragma warning(pop) #endif +#define CV_PY_FN_WITH_KW_(fn, flags) (PyCFunction)(void*)(PyCFunctionWithKeywords)(fn), (flags) | METH_VARARGS | METH_KEYWORDS +#define CV_PY_FN_NOARGS_(fn, flags) (PyCFunction)(fn), (flags) | METH_NOARGS + +#define CV_PY_FN_WITH_KW(fn) CV_PY_FN_WITH_KW_(fn, 0) +#define CV_PY_FN_NOARGS(fn) CV_PY_FN_NOARGS_(fn, 0) + + #define MODULESTR "cv2" #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include @@ -474,8 +481,14 @@ typedef struct { static bool PyObject_IsUMat(PyObject *o); // UMatWrapper init - try to map arguments from python to UMat constructors -static int UMatWrapper_init(cv2_UMatWrapperObject *self, PyObject *args, PyObject *kwds) +static int UMatWrapper_init(PyObject* self_, PyObject *args, PyObject *kwds) { + cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; + if (self == NULL) + { + PyErr_SetString(PyExc_TypeError, "Internal error"); + return -1; + } self->um = NULL; { // constructor () @@ -548,8 +561,11 @@ static void UMatWrapper_dealloc(cv2_UMatWrapperObject* self) // UMatWrapper.get() - returns numpy array by transferring UMat data to Mat and than wrapping it to numpy array // (using numpy allocator - and so without unnecessary copy) -static PyObject * UMatWrapper_get(cv2_UMatWrapperObject* self) +static PyObject * UMatWrapper_get(PyObject* self_, PyObject * /*args*/) { + cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; + if (self == NULL) + return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); Mat m; m.allocator = &g_numpyAllocator; self->um->copyTo(m); @@ -558,8 +574,11 @@ static PyObject * UMatWrapper_get(cv2_UMatWrapperObject* self) } // UMatWrapper.handle() - returns the OpenCL handle of the UMat object -static PyObject * UMatWrapper_handle(cv2_UMatWrapperObject* self, PyObject *args, PyObject *kwds) +static PyObject * UMatWrapper_handle(PyObject* self_, PyObject *args, PyObject *kwds) { + cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; + if (self == NULL) + return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); const char *kwlist[] = {"accessFlags", NULL}; int accessFlags; if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", (char**) kwlist, &accessFlags)) @@ -568,51 +587,60 @@ static PyObject * UMatWrapper_handle(cv2_UMatWrapperObject* self, PyObject *args } // UMatWrapper.isContinuous() - returns true if the matrix data is continuous -static PyObject * UMatWrapper_isContinuous(cv2_UMatWrapperObject* self) +static PyObject * UMatWrapper_isContinuous(PyObject* self_, PyObject * /*args*/) { + cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; + if (self == NULL) + return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); return PyBool_FromLong(self->um->isContinuous()); } // UMatWrapper.isContinuous() - returns true if the matrix is a submatrix of another matrix -static PyObject * UMatWrapper_isSubmatrix(cv2_UMatWrapperObject* self) +static PyObject * UMatWrapper_isSubmatrix(PyObject* self_, PyObject * /*args*/) { + cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; + if (self == NULL) + return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); return PyBool_FromLong(self->um->isSubmatrix()); } // UMatWrapper.context() - returns the OpenCL context used by OpenCV UMat -static PyObject * UMatWrapper_context(cv2_UMatWrapperObject*) +static PyObject * UMatWrapper_context(PyObject* /*self_*/, PyObject * /*args*/) { return PyLong_FromVoidPtr(cv::ocl::Context::getDefault().ptr()); } // UMatWrapper.context() - returns the OpenCL queue used by OpenCV UMat -static PyObject * UMatWrapper_queue(cv2_UMatWrapperObject*) +static PyObject * UMatWrapper_queue(PyObject* /*self_*/, PyObject * /*args*/) { return PyLong_FromVoidPtr(cv::ocl::Queue::getDefault().ptr()); } -static PyObject * UMatWrapper_offset_getter(cv2_UMatWrapperObject* self, void*) +static PyObject * UMatWrapper_offset_getter(PyObject* self_, void*) { + cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; + if (self == NULL) + return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); return PyLong_FromSsize_t(self->um->offset); } static PyMethodDef UMatWrapper_methods[] = { - {"get", (PyCFunction)UMatWrapper_get, METH_NOARGS, + {"get", CV_PY_FN_NOARGS(UMatWrapper_get), "Returns numpy array" }, - {"handle", (PyCFunction)UMatWrapper_handle, METH_VARARGS | METH_KEYWORDS, + {"handle", CV_PY_FN_WITH_KW(UMatWrapper_handle), "Returns UMat native handle" }, - {"isContinuous", (PyCFunction)UMatWrapper_isContinuous, METH_NOARGS, + {"isContinuous", CV_PY_FN_NOARGS(UMatWrapper_isContinuous), "Returns true if the matrix data is continuous" }, - {"isSubmatrix", (PyCFunction)UMatWrapper_isSubmatrix, METH_NOARGS, + {"isSubmatrix", CV_PY_FN_NOARGS(UMatWrapper_isSubmatrix), "Returns true if the matrix is a submatrix of another matrix" }, - {"context", (PyCFunction)UMatWrapper_context, METH_NOARGS | METH_STATIC, + {"context", CV_PY_FN_NOARGS_(UMatWrapper_context, METH_STATIC), "Returns OpenCL context handle" }, - {"queue", (PyCFunction)UMatWrapper_queue, METH_NOARGS | METH_STATIC, + {"queue", CV_PY_FN_NOARGS_(UMatWrapper_queue, METH_STATIC), "Returns OpenCL queue handle" }, {NULL, NULL, 0, NULL} /* Sentinel */ @@ -1778,15 +1806,15 @@ static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") #include "pyopencv_generated_funcs.h" static PyMethodDef special_methods[] = { - {"redirectError", (PyCFunction)pycvRedirectError, METH_VARARGS | METH_KEYWORDS, "redirectError(onError) -> None"}, + {"redirectError", CV_PY_FN_WITH_KW(pycvRedirectError), "redirectError(onError) -> None"}, #ifdef HAVE_OPENCV_HIGHGUI - {"createTrackbar", pycvCreateTrackbar, METH_VARARGS, "createTrackbar(trackbarName, windowName, value, count, onChange) -> None"}, - {"createButton", (PyCFunction)pycvCreateButton, METH_VARARGS | METH_KEYWORDS, "createButton(buttonName, onChange [, userData, buttonType, initialButtonState]) -> None"}, - {"setMouseCallback", (PyCFunction)pycvSetMouseCallback, METH_VARARGS | METH_KEYWORDS, "setMouseCallback(windowName, onMouse [, param]) -> None"}, + {"createTrackbar", (PyCFunction)pycvCreateTrackbar, METH_VARARGS, "createTrackbar(trackbarName, windowName, value, count, onChange) -> None"}, + {"createButton", CV_PY_FN_WITH_KW(pycvCreateButton), "createButton(buttonName, onChange [, userData, buttonType, initialButtonState]) -> None"}, + {"setMouseCallback", CV_PY_FN_WITH_KW(pycvSetMouseCallback), "setMouseCallback(windowName, onMouse [, param]) -> None"}, #endif #ifdef HAVE_OPENCV_DNN - {"dnn_registerLayer", (PyCFunction)pyopencv_cv_dnn_registerLayer, METH_VARARGS | METH_KEYWORDS, "registerLayer(type, class) -> None"}, - {"dnn_unregisterLayer", (PyCFunction)pyopencv_cv_dnn_unregisterLayer, METH_VARARGS | METH_KEYWORDS, "unregisterLayer(type) -> None"}, + {"dnn_registerLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_registerLayer), "registerLayer(type, class) -> None"}, + {"dnn_unregisterLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_unregisterLayer), "unregisterLayer(type) -> None"}, #endif {NULL, NULL}, }; diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 27928bc485..6995e23651 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -599,13 +599,9 @@ class FuncInfo(object): # Convert unicode chars to xml representation, but keep as string instead of bytes full_docstring = full_docstring.encode('ascii', errors='xmlcharrefreplace').decode() - flags = ["METH_VARARGS", "METH_KEYWORDS"] - if self.isclassmethod: - flags.append("METH_CLASS") - - return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, $flags, "$py_docstring"},\n' + return Template(' {"$py_funcname", CV_PY_FN_WITH_KW_($wrap_funcname, $flags), "$py_docstring"},\n' ).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(), - flags = " | ".join(flags), py_docstring = full_docstring) + flags = 'METH_CLASS' if self.isclassmethod else '0', py_docstring = full_docstring) def gen_code(self, codegen): all_classes = codegen.classes diff --git a/modules/python/test/test_misc.py b/modules/python/test/test_misc.py index abb66c13de..5f07d733f2 100644 --- a/modules/python/test/test_misc.py +++ b/modules/python/test/test_misc.py @@ -23,7 +23,7 @@ class Bindings(NewOpenCVTests): try: cv.imshow("", None) # This causes an assert self.assertEqual("Dead code", 0) - except cv.error as e: + except cv.error as _e: pass handler_called = [False] @@ -34,7 +34,7 @@ class Bindings(NewOpenCVTests): try: cv.imshow("", None) # This causes an assert self.assertEqual("Dead code", 0) - except cv.error as e: + except cv.error as _e: self.assertEqual(handler_called[0], True) pass @@ -42,7 +42,7 @@ class Bindings(NewOpenCVTests): try: cv.imshow("", None) # This causes an assert self.assertEqual("Dead code", 0) - except cv.error as e: + except cv.error as _e: pass diff --git a/modules/ts/include/opencv2/ts/ts_gtest.h b/modules/ts/include/opencv2/ts/ts_gtest.h index 7c1854e9a5..2b1299c3bf 100644 --- a/modules/ts/include/opencv2/ts/ts_gtest.h +++ b/modules/ts/include/opencv2/ts/ts_gtest.h @@ -11539,12 +11539,15 @@ typename ParamNameGenFunc::Type *GetParamNameGen() { return DefaultParamName; } +} // namespace internal:: // fixes MacOS X issue with "friend class internal/*::anon*/::ParameterizedTestFactory;" +namespace { // wrap into anynomous namespace to avoid build warnings like GCC's -Wsubobject-linkage + // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // Stores a parameter value and later creates tests parameterized with that // value. template -class ParameterizedTestFactory : public TestFactoryBase { +class ParameterizedTestFactory : public internal::TestFactoryBase { public: typedef typename TestClass::ParamType ParamType; explicit ParameterizedTestFactory(ParamType parameter) : @@ -11559,6 +11562,8 @@ class ParameterizedTestFactory : public TestFactoryBase { GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); }; +} // namespace +namespace internal { // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // @@ -20405,6 +20410,12 @@ class GTEST_API_ AssertHelper { } // namespace internal #if GTEST_HAS_PARAM_TEST + +namespace internal { +// Static value used for accessing test parameter during a test lifetime. +extern void* g_parameter_; +} // namespace internal + // The pure interface class that all value-parameterized tests inherit from. // A value-parameterized class must inherit from both ::testing::Test and // ::testing::WithParamInterface. In most cases that just means inheriting @@ -20451,29 +20462,28 @@ class WithParamInterface { // like writing 'WithParamInterface::GetParam()' for a test that // uses a fixture whose parameter type is int. const ParamType& GetParam() const { - GTEST_CHECK_(parameter_ != NULL) + GTEST_CHECK_(GetParameterPtrRef_() != NULL) << "GetParam() can only be called inside a value-parameterized test " << "-- did you intend to write TEST_P instead of TEST_F?"; - return *parameter_; + return *GetParameterPtrRef_(); } private: // Sets parameter value. The caller is responsible for making sure the value // remains alive and unchanged throughout the current test. static void SetParam(const ParamType* parameter) { - parameter_ = parameter; + GetParameterPtrRef_() = parameter; } - // Static value used for accessing parameter during a test lifetime. - static const ParamType* parameter_; + static const ParamType*& GetParameterPtrRef_() + { + return (const ParamType*&)internal::g_parameter_; + } // TestClass must be a subclass of WithParamInterface and Test. - template friend class internal::ParameterizedTestFactory; + template friend class /*internal::*/ParameterizedTestFactory; }; -template -const T* WithParamInterface::parameter_ = NULL; - // Most value-parameterized classes can ignore the existence of // WithParamInterface, and can just inherit from ::testing::TestWithParam. diff --git a/modules/ts/src/ts_gtest.cpp b/modules/ts/src/ts_gtest.cpp index ff9dd4a1bf..f71efbb0eb 100644 --- a/modules/ts/src/ts_gtest.cpp +++ b/modules/ts/src/ts_gtest.cpp @@ -10441,5 +10441,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames( #endif // GTEST_HAS_TYPED_TEST_P +void* g_parameter_ = NULL; + } // namespace internal } // namespace testing diff --git a/modules/videoio/CMakeLists.txt b/modules/videoio/CMakeLists.txt index a86a044da3..dc3e0233ed 100644 --- a/modules/videoio/CMakeLists.txt +++ b/modules/videoio/CMakeLists.txt @@ -20,6 +20,8 @@ set(videoio_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/precomp.hpp ) set(videoio_srcs + ${CMAKE_CURRENT_LIST_DIR}/src/videoio_registry.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/videoio_c.cpp ${CMAKE_CURRENT_LIST_DIR}/src/cap.cpp ${CMAKE_CURRENT_LIST_DIR}/src/cap_images.cpp ${CMAKE_CURRENT_LIST_DIR}/src/cap_mjpeg_encoder.cpp @@ -165,6 +167,9 @@ if(HAVE_FFMPEG) if(APPLE) list(APPEND VIDEOIO_LIBRARIES "-framework VideoDecodeAcceleration" bz2) endif() + if(HAVE_FFMPEG_WRAPPER) + add_definitions(-DHAVE_FFMPEG_WRAPPER=1) + endif() endif(HAVE_FFMPEG) if(HAVE_PVAPI) @@ -234,12 +239,6 @@ if(IOS) list(APPEND VIDEOIO_LIBRARIES "-framework Accelerate" "-framework AVFoundation" "-framework CoreGraphics" "-framework CoreImage" "-framework CoreMedia" "-framework CoreVideo" "-framework QuartzCore" "-framework UIKit") endif() -if(WIN32) - link_directories("${OpenCV_SOURCE_DIR}/3rdparty/lib") # for ffmpeg wrapper only - include_directories(AFTER SYSTEM "${OpenCV_SOURCE_DIR}/3rdparty/include") # for directshow in VS2005 and multi-monitor support on MinGW - include_directories(AFTER SYSTEM "${OpenCV_SOURCE_DIR}/3rdparty/include/ffmpeg_") # for tests -endif() - if(UNIX) #these variables are set by CHECK_MODULE macro foreach(P ${VIDEOIO_INCLUDE_DIRS}) @@ -272,7 +271,7 @@ endif() ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-deprecated-declarations) -if(WIN32 AND HAVE_FFMPEG) +if(WIN32 AND HAVE_FFMPEG_WRAPPER) #copy ffmpeg dll to the output folder if(MSVC64 OR MINGW64) set(FFMPEG_SUFFIX _64) diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index 521e658d20..c1ee87a15a 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -40,40 +40,10 @@ //M*/ #include "precomp.hpp" -#include -using namespace std; -#include "cap_intelperc.hpp" -#include "cap_librealsense.hpp" -#include "cap_dshow.hpp" - -#ifdef HAVE_MFX -#include "cap_mfx_reader.hpp" -#include "cap_mfx_writer.hpp" -#endif - -// All WinRT versions older than 8.0 should provide classes used for video support -#if defined(WINRT) && !defined(WINRT_8_0) && defined(__cplusplus_winrt) -# include "cap_winrt_capture.hpp" -# include "cap_winrt_bridge.hpp" -# define WINRT_VIDEO -#endif - -#if defined _M_X64 && defined _MSC_VER && !defined CV_ICC -#pragma optimize("",off) -#pragma warning(disable: 4748) -#endif -#if defined(__clang__) -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#endif -#if defined(__GNUC__) && __GNUC__ >= 7 -#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -#endif +#include "videoio_registry.hpp" -using namespace cv; - -namespace cv -{ +namespace cv { template<> void DefaultDeleter::operator ()(CvCapture* obj) const { cvReleaseCapture(&obj); } @@ -81,536 +51,7 @@ template<> void DefaultDeleter::operator ()(CvCapture* obj) const template<> void DefaultDeleter::operator ()(CvVideoWriter* obj) const { cvReleaseVideoWriter(&obj); } -} - -/************************* Reading AVIs & Camera data **************************/ - -static inline double icvGetCaptureProperty( const CvCapture* capture, int id ) -{ - return capture ? capture->getProperty(id) : 0; -} - -CV_IMPL void cvReleaseCapture( CvCapture** pcapture ) -{ - if( pcapture && *pcapture ) - { - delete *pcapture; - *pcapture = 0; - } -} - -CV_IMPL IplImage* cvQueryFrame( CvCapture* capture ) -{ - if(!capture) - return 0; - if(!capture->grabFrame()) - return 0; - return capture->retrieveFrame(0); -} - - -CV_IMPL int cvGrabFrame( CvCapture* capture ) -{ - return capture ? capture->grabFrame() : 0; -} - -CV_IMPL IplImage* cvRetrieveFrame( CvCapture* capture, int idx ) -{ - return capture ? capture->retrieveFrame(idx) : 0; -} - -CV_IMPL double cvGetCaptureProperty( CvCapture* capture, int id ) -{ - return icvGetCaptureProperty(capture, id); -} - -CV_IMPL int cvSetCaptureProperty( CvCapture* capture, int id, double value ) -{ - return capture ? capture->setProperty(id, value) : 0; -} - -CV_IMPL int cvGetCaptureDomain( CvCapture* capture) -{ - return capture ? capture->getCaptureDomain() : 0; -} - -static bool get_capture_debug_flag() -{ - static bool initialized = false; - static bool flag = false; - if (!initialized) - { -#ifndef NO_GETENV - flag = getenv("OPENCV_VIDEOCAPTURE_DEBUG") ? true : false; // TODO Use getBoolParameter -#endif - initialized = true; - } - return flag; -} - -#define TRY_OPEN(capture, backend_func) \ -{ \ - if (!capture) \ - CV_TRY { \ - if (get_capture_debug_flag()) fprintf(stderr, "VIDEOIO(%s): trying ...\n", #backend_func); \ - capture = backend_func; \ - if (get_capture_debug_flag()) fprintf(stderr, "VIDEOIO(%s): result=%p ...\n", #backend_func, capture); \ - } CV_CATCH (cv::Exception, e) { \ - fprintf(stderr, "VIDEOIO(%s): raised OpenCV exception:\n\n%s\n", #backend_func, e.what()); \ - } CV_CATCH (std::exception, e) { \ - fprintf(stderr, "VIDEOIO(%s): raised C++ exception:\n\n%s\n", #backend_func, e.what()); \ - } CV_CATCH_ALL { \ - fprintf(stderr, "VIDEOIO(%s): raised unknown C++ exception!\n\n", #backend_func); \ - } \ -} - - -/** - * Camera dispatching method: index is the camera number. - * If given an index from 0 to 99, it tries to find the first - * API that can access a given camera index. - * Add multiples of 100 to select an API. - */ -CV_IMPL CvCapture * cvCreateCameraCapture (int index) -{ - // interpret preferred interface (0 = autodetect) - int pref = (index / 100) * 100; - - // remove pref from index - index -= pref; - // local variable to memorize the captured device - CvCapture *capture = 0; - - switch (pref) - { - default: - // user specified an API we do not know - // bail out to let the user know that it is not available - if (pref) break; - - case CAP_VFW: // or CAP_V4L or CAP_V4L2 -#ifdef HAVE_VFW - TRY_OPEN(capture, cvCreateCameraCapture_VFW(index)) -#endif - -#if defined HAVE_LIBV4L || defined HAVE_CAMV4L || defined HAVE_CAMV4L2 || defined HAVE_VIDEOIO - TRY_OPEN(capture, cvCreateCameraCapture_V4L(index)) -#endif - - if (pref) break; // CAP_VFW or CAP_V4L or CAP_V4L2 - - case CAP_FIREWIRE: -#ifdef HAVE_DC1394_2 - TRY_OPEN(capture, cvCreateCameraCapture_DC1394_2(index)) -#endif - -#ifdef HAVE_DC1394 - TRY_OPEN(capture, cvCreateCameraCapture_DC1394(index)) -#endif - -#ifdef HAVE_CMU1394 - TRY_OPEN(capture, cvCreateCameraCapture_CMU(index)) -#endif - - if (pref) break; // CAP_FIREWIRE - -#ifdef HAVE_MIL - case CAP_MIL: - TRY_OPEN(capture, cvCreateCameraCapture_MIL(index)) - if (pref) break; -#endif - -#if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) - case CAP_QT: - TRY_OPEN(capture, cvCreateCameraCapture_QT(index)) - if (pref) break; -#endif - -#ifdef HAVE_UNICAP - case CAP_UNICAP: - TRY_OPEN(capture, cvCreateCameraCapture_Unicap(index)) - if (pref) break; -#endif - -#ifdef HAVE_PVAPI - case CAP_PVAPI: - TRY_OPEN(capture, cvCreateCameraCapture_PvAPI(index)) - if (pref) break; -#endif - -#ifdef HAVE_OPENNI - case CAP_OPENNI: - TRY_OPEN(capture, cvCreateCameraCapture_OpenNI(index)) - if (pref) break; -#endif - -#ifdef HAVE_OPENNI2 - case CAP_OPENNI2: - TRY_OPEN(capture, cvCreateCameraCapture_OpenNI2(index)) - if (pref) break; -#endif - -#ifdef HAVE_XIMEA - case CAP_XIAPI: - TRY_OPEN(capture, cvCreateCameraCapture_XIMEA(index)) - if (pref) break; -#endif - -#ifdef HAVE_AVFOUNDATION - case CAP_AVFOUNDATION: - TRY_OPEN(capture, cvCreateCameraCapture_AVFoundation(index)) - if (pref) break; -#endif - -#ifdef HAVE_GIGE_API - case CAP_GIGANETIX: - TRY_OPEN(capture, cvCreateCameraCapture_Giganetix(index)) - if (pref) break; // CAP_GIGANETIX -#endif - -#ifdef HAVE_ARAVIS_API - case CAP_ARAVIS: - TRY_OPEN(capture, cvCreateCameraCapture_Aravis(index)) - if (pref) break; -#endif - } - - return capture; -} - -/** - * Videoreader dispatching method: it tries to find the first - * API that can access a given filename. - */ -CV_IMPL CvCapture * cvCreateFileCaptureWithPreference (const char * filename, int apiPreference) -{ - CvCapture * result = 0; - - switch(apiPreference) { - default: - // user specified an API we do not know - // bail out to let the user know that it is not available - if (apiPreference) break; - -#if defined HAVE_LIBV4L || defined HAVE_CAMV4L || defined HAVE_CAMV4L2 || defined HAVE_VIDEOIO - case CAP_V4L: - TRY_OPEN(result, cvCreateCameraCapture_V4L(filename)) - if (apiPreference) break; -#endif - -#ifdef HAVE_VFW - case CAP_VFW: - TRY_OPEN(result, cvCreateFileCapture_VFW (filename)) - if (apiPreference) break; -#endif - -#if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) - case CAP_QT: - TRY_OPEN(result, cvCreateFileCapture_QT (filename)) - if (apiPreference) break; -#endif - -#ifdef HAVE_AVFOUNDATION - case CAP_AVFOUNDATION: - TRY_OPEN(result, cvCreateFileCapture_AVFoundation (filename)) - if (apiPreference) break; -#endif - -#ifdef HAVE_OPENNI - case CAP_OPENNI: - TRY_OPEN(result, cvCreateFileCapture_OpenNI (filename)) - if (apiPreference) break; -#endif - -#ifdef HAVE_OPENNI2 - case CAP_OPENNI2: - TRY_OPEN(result, cvCreateFileCapture_OpenNI2 (filename)) - if (apiPreference) break; -#endif -#ifdef HAVE_XIMEA - case CAP_XIAPI: - TRY_OPEN(result, cvCreateCameraCapture_XIMEA(filename)) - if (apiPreference) break; -#endif - case CAP_IMAGES: - TRY_OPEN(result, cvCreateFileCapture_Images (filename)) - } - - return result; -} - -CV_IMPL CvCapture * cvCreateFileCapture (const char * filename) -{ - return cvCreateFileCaptureWithPreference(filename, CAP_ANY); -} - -/** - * Videowriter dispatching method: it tries to find the first - * API that can write a given stream. - */ -static CvVideoWriter* cvCreateVideoWriterWithPreference(const char* filename, int apiPreference, int fourcc, - double fps, CvSize frameSize, int is_color ) -{ - CV_UNUSED(frameSize); - CV_UNUSED(is_color); - - CvVideoWriter *result = 0; - - if(!fourcc || !fps) - TRY_OPEN(result, cvCreateVideoWriter_Images(filename)) - - CV_Assert(result || fps != 0); - - switch(apiPreference) - { - default: - //exit if the specified API is unavaliable - if (apiPreference != CAP_ANY) break; - #ifdef HAVE_VFW - case CAP_VFW: - TRY_OPEN(result, cvCreateVideoWriter_VFW(filename, fourcc, fps, frameSize, is_color)) - if (apiPreference != CAP_ANY) break; - #endif - #ifdef HAVE_AVFOUNDATION - case CAP_AVFOUNDATION: - TRY_OPEN(result, cvCreateVideoWriter_AVFoundation(filename, fourcc, fps, frameSize, is_color)) - if (apiPreference != CAP_ANY) break; - #endif - #if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) - case(CAP_QT): - TRY_OPEN(result, cvCreateVideoWriter_QT(filename, fourcc, fps, frameSize, is_color)) - if (apiPreference != CAP_ANY) break; - #endif - #ifdef HAVE_GSTREAMER - case CAP_GSTREAMER: - TRY_OPEN(result, cvCreateVideoWriter_GStreamer (filename, fourcc, fps, frameSize, is_color)) - if (apiPreference != CAP_ANY) break; - #endif - case CAP_IMAGES: - TRY_OPEN(result, cvCreateVideoWriter_Images(filename)) - if (apiPreference != CAP_ANY) break; - } - - return result; -} - -CV_IMPL CvVideoWriter* cvCreateVideoWriter( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ) -{ - return cvCreateVideoWriterWithPreference(filename, CAP_ANY, fourcc, fps, frameSize, is_color); -} - -CV_IMPL int cvWriteFrame( CvVideoWriter* writer, const IplImage* image ) -{ - return writer ? writer->writeFrame(image) : 0; -} - -CV_IMPL void cvReleaseVideoWriter( CvVideoWriter** pwriter ) -{ - if( pwriter && *pwriter ) - { - delete *pwriter; - *pwriter = 0; - } -} - -namespace cv -{ - -static Ptr IVideoCapture_create(int index) -{ - int domains[] = - { -#ifdef HAVE_GSTREAMER - CAP_GSTREAMER, -#endif -#ifdef HAVE_MSMF - CAP_MSMF, -#endif -#ifdef HAVE_DSHOW - CAP_DSHOW, -#endif -#ifdef HAVE_INTELPERC - CAP_INTELPERC, -#endif -#ifdef WINRT_VIDEO - CAP_WINRT, -#endif -#ifdef HAVE_GPHOTO2 - CAP_GPHOTO2, -#endif - -1, -1 - }; - - // interpret preferred interface (0 = autodetect) - int pref = (index / 100) * 100; - if (pref) - { - domains[0]=pref; - index %= 100; - domains[1]=-1; - } - - // try every possibly installed camera API - for (int i = 0; domains[i] >= 0; i++) - { -#if defined(HAVE_GSTREAMER) || \ - defined(HAVE_MSMF) || \ - defined(HAVE_DSHOW) || \ - defined(HAVE_INTELPERC) || \ - defined(HAVE_LIBREALSENSE) || \ - defined(WINRT_VIDEO) || \ - defined(HAVE_GPHOTO2) || \ - (0) - Ptr capture; - - switch (domains[i]) - { -#ifdef HAVE_GSTREAMER - case CAP_GSTREAMER: - capture = createGStreamerCapture(index); - break; -#endif -#ifdef HAVE_MSMF - case CAP_MSMF: - capture = cvCreateCapture_MSMF(index); - break; // CAP_MSMF -#endif -#ifdef HAVE_DSHOW - case CAP_DSHOW: - capture = makePtr(index); - break; // CAP_DSHOW -#endif -#ifdef HAVE_INTELPERC - case CAP_INTELPERC: - capture = makePtr(); - break; // CAP_INTEL_PERC -#elif defined(HAVE_LIBREALSENSE) - case CAP_INTELPERC: - capture = makePtr(index); - break; -#endif -#ifdef WINRT_VIDEO - case CAP_WINRT: - capture = Ptr(new cv::VideoCapture_WinRT(index)); - if (capture) - return capture; - break; // CAP_WINRT -#endif -#ifdef HAVE_GPHOTO2 - case CAP_GPHOTO2: - capture = createGPhoto2Capture(index); - break; -#endif - } - if (capture && capture->isOpened()) - return capture; -#endif - } - - // failed open a camera - return Ptr(); -} - - -static Ptr IVideoCapture_create(const String& filename, int apiPreference) -{ - bool useAny = (apiPreference == CAP_ANY); - Ptr capture; -#ifdef HAVE_FFMPEG - if (useAny || apiPreference == CAP_FFMPEG) - { - capture = cvCreateFileCapture_FFMPEG_proxy(filename); - if (capture && capture->isOpened()) - return capture; - } -#endif -#ifdef HAVE_GSTREAMER - if (useAny || apiPreference == CAP_GSTREAMER) - { - capture = createGStreamerCapture(filename); - if (capture && capture->isOpened()) - return capture; - } -#endif -#ifdef HAVE_XINE - if (useAny || apiPreference == CAP_XINE) - { - capture = createXINECapture(filename.c_str()); - if (capture && capture->isOpened()) - return capture; - } -#endif -#ifdef HAVE_MSMF - if (useAny || apiPreference == CAP_MSMF) - { - capture = cvCreateCapture_MSMF(filename); - if (capture && capture->isOpened()) - return capture; - } -#endif -#ifdef HAVE_GPHOTO2 - if (useAny || apiPreference == CAP_GPHOTO2) - { - capture = createGPhoto2Capture(filename); - if (capture && capture->isOpened()) - return capture; - } -#endif -#ifdef HAVE_MFX - if (useAny || apiPreference == CAP_INTEL_MFX) - { - capture = makePtr(filename); - if (capture && capture->isOpened()) - return capture; - } -#endif - if (useAny || apiPreference == CAP_OPENCV_MJPEG) - { - capture = createMotionJpegCapture(filename); - if (capture && capture->isOpened()) - return capture; - } - if (capture && !capture->isOpened()) - capture.release(); - return capture; -} - -static Ptr IVideoWriter_create(const String& filename, int apiPreference, int _fourcc, double fps, Size frameSize, bool isColor) -{ - Ptr iwriter; -#ifdef HAVE_FFMPEG - if (apiPreference == CAP_FFMPEG || apiPreference == CAP_ANY) - { - iwriter = cvCreateVideoWriter_FFMPEG_proxy(filename, _fourcc, fps, frameSize, isColor); - if (!iwriter.empty()) - return iwriter; - } -#endif -#ifdef HAVE_MSMF - if (apiPreference == CAP_MSMF || apiPreference == CAP_ANY) - { - iwriter = cvCreateVideoWriter_MSMF(filename, _fourcc, fps, frameSize, isColor); - if (!iwriter.empty()) - return iwriter; - } -#endif -#ifdef HAVE_MFX - if (apiPreference == CAP_INTEL_MFX || apiPreference == CAP_ANY) - { - iwriter = VideoWriter_IntelMFX::create(filename, _fourcc, fps, frameSize, isColor); - if (!iwriter.empty()) - return iwriter; - } -#endif - - if( (apiPreference == CAP_OPENCV_MJPEG || apiPreference == CAP_ANY) - && _fourcc == CV_FOURCC('M', 'J', 'P', 'G') ) - iwriter = createMotionJpegWriter(filename, fps, frameSize, isColor); - - return iwriter; -} VideoCapture::VideoCapture() {} @@ -646,12 +87,30 @@ bool VideoCapture::open(const String& filename, int apiPreference) CV_TRACE_FUNCTION(); if (isOpened()) release(); - icap = IVideoCapture_create(filename, apiPreference); - if (!icap.empty()) - return true; - cap.reset(cvCreateFileCaptureWithPreference(filename.c_str(), apiPreference)); - return isOpened(); + const std::vector backends = cv::videoio_registry::getAvailableBackends_CaptureByFilename(); + for (size_t i = 0; i < backends.size(); i++) + { + const VideoBackendInfo& info = backends[i]; + if (apiPreference == CAP_ANY || apiPreference == info.id) + { + CvCapture* capture = NULL; + VideoCapture_create(capture, icap, info.id, filename); + if (!icap.empty()) + { + if (icap->isOpened()) + return true; + icap.release(); + } + if (capture) + { + cap.reset(capture); + // assume it is opened + return true; + } + } + } + return false; } bool VideoCapture::open(const String& filename) @@ -661,28 +120,56 @@ bool VideoCapture::open(const String& filename) return open(filename, CAP_ANY); } -bool VideoCapture::open(int index) +bool VideoCapture::open(int cameraNum, int apiPreference) { CV_TRACE_FUNCTION(); if (isOpened()) release(); - icap = IVideoCapture_create(index); - if (!icap.empty()) - return true; - cap.reset(cvCreateCameraCapture(index)); - return isOpened(); + + const std::vector backends = cv::videoio_registry::getAvailableBackends_CaptureByIndex(); + for (size_t i = 0; i < backends.size(); i++) + { + const VideoBackendInfo& info = backends[i]; + if (apiPreference == CAP_ANY || apiPreference == info.id) + { + CvCapture* capture = NULL; + VideoCapture_create(capture, icap, info.id, cameraNum); + if (!icap.empty()) + { + if (icap->isOpened()) + return true; + icap.release(); + } + if (capture) + { + cap.reset(capture); + // assume it is opened + return true; + } + } + } + return false; } -bool VideoCapture::open(int cameraNum, int apiPreference) + +bool VideoCapture::open(int index) { CV_TRACE_FUNCTION(); - cameraNum = cameraNum + apiPreference; - return open(cameraNum); + // interpret preferred interface (0 = autodetect) + int backendID = (index / 100) * 100; + if (backendID) + { + index %= 100; + } + + return open(index, backendID); } bool VideoCapture::isOpened() const { - return (!cap.empty() || !icap.empty()); + if (!icap.empty()) + return icap->isOpened(); + return !cap.empty(); // legacy interface doesn't support closed files } void VideoCapture::release() @@ -738,6 +225,7 @@ bool VideoCapture::read(OutputArray image) VideoCapture& VideoCapture::operator >> (Mat& image) { #ifdef WINRT_VIDEO + // FIXIT grab/retrieve methods() should work too if (grab()) { if (retrieve(image)) @@ -759,7 +247,6 @@ VideoCapture& VideoCapture::operator >> (Mat& image) #else read(image); #endif - return *this; } @@ -782,10 +269,14 @@ double VideoCapture::get(int propId) const { if (!icap.empty()) return icap->getProperty(propId); - return icvGetCaptureProperty(cap, propId); + return cap ? cap->getProperty(propId) : 0; } +//================================================================================================= + + + VideoWriter::VideoWriter() {} @@ -821,11 +312,30 @@ bool VideoWriter::open(const String& filename, int apiPreference, int _fourcc, d CV_INSTRUMENT_REGION() if (isOpened()) release(); - iwriter = IVideoWriter_create(filename, apiPreference, _fourcc, fps, frameSize, isColor); - if (!iwriter.empty()) - return true; - writer.reset(cvCreateVideoWriterWithPreference(filename.c_str(), apiPreference, _fourcc, fps, frameSize, isColor)); - return isOpened(); + + const std::vector backends = cv::videoio_registry::getAvailableBackends_Writer(); + for (size_t i = 0; i < backends.size(); i++) + { + const VideoBackendInfo& info = backends[i]; + if (apiPreference == CAP_ANY || apiPreference == info.id) + { + CvVideoWriter* writer_ = NULL; + VideoWriter_create(writer_, iwriter, info.id, filename, _fourcc, fps, frameSize, isColor); + if (!iwriter.empty()) + { + if (iwriter->isOpened()) + return true; + iwriter.release(); + } + if (writer_) + { + // assume it is opened + writer.reset(writer_); + return true; + } + } + } + return false; } bool VideoWriter::isOpened() const @@ -869,9 +379,10 @@ VideoWriter& VideoWriter::operator << (const Mat& image) return *this; } +// FIXIT OpenCV 4.0: make inline int VideoWriter::fourcc(char c1, char c2, char c3, char c4) { return (c1 & 255) + ((c2 & 255) << 8) + ((c3 & 255) << 16) + ((c4 & 255) << 24); } -} +} // namespace diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 25f7aa60b5..14353ad13c 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -41,13 +41,28 @@ #include "precomp.hpp" +#if defined(HAVE_FFMPEG) + #include -#if defined HAVE_FFMPEG && !defined _WIN32 +#if !defined(HAVE_FFMPEG_WRAPPER) #include "cap_ffmpeg_impl.hpp" + +#define icvCreateFileCapture_FFMPEG_p cvCreateFileCapture_FFMPEG +#define icvReleaseCapture_FFMPEG_p cvReleaseCapture_FFMPEG +#define icvGrabFrame_FFMPEG_p cvGrabFrame_FFMPEG +#define icvRetrieveFrame_FFMPEG_p cvRetrieveFrame_FFMPEG +#define icvSetCaptureProperty_FFMPEG_p cvSetCaptureProperty_FFMPEG +#define icvGetCaptureProperty_FFMPEG_p cvGetCaptureProperty_FFMPEG +#define icvCreateVideoWriter_FFMPEG_p cvCreateVideoWriter_FFMPEG +#define icvReleaseVideoWriter_FFMPEG_p cvReleaseVideoWriter_FFMPEG +#define icvWriteFrame_FFMPEG_p cvWriteFrame_FFMPEG + #else + #include "cap_ffmpeg_api.hpp" -#endif + +namespace cv { namespace { static CvCreateFileCapture_Plugin icvCreateFileCapture_FFMPEG_p = 0; static CvReleaseCapture_Plugin icvReleaseCapture_FFMPEG_p = 0; @@ -99,7 +114,7 @@ private: icvInitFFMPEG() { - #if defined _WIN32 +#if defined _WIN32 const wchar_t* module_name_ = L"opencv_ffmpeg" CVAUX_STRW(CV_MAJOR_VERSION) CVAUX_STRW(CV_MINOR_VERSION) CVAUX_STRW(CV_SUBMINOR_VERSION) #if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__) @@ -161,7 +176,7 @@ private: (CvReleaseVideoWriter_Plugin)GetProcAddress(icvFFOpenCV, "cvReleaseVideoWriter_FFMPEG"); icvWriteFrame_FFMPEG_p = (CvWriteFrame_Plugin)GetProcAddress(icvFFOpenCV, "cvWriteFrame_FFMPEG"); - +# endif // _WIN32 #if 0 if( icvCreateFileCapture_FFMPEG_p != 0 && icvReleaseCapture_FFMPEG_p != 0 && @@ -181,21 +196,18 @@ private: } #endif } - #elif defined HAVE_FFMPEG - icvCreateFileCapture_FFMPEG_p = (CvCreateFileCapture_Plugin)cvCreateFileCapture_FFMPEG; - icvReleaseCapture_FFMPEG_p = (CvReleaseCapture_Plugin)cvReleaseCapture_FFMPEG; - icvGrabFrame_FFMPEG_p = (CvGrabFrame_Plugin)cvGrabFrame_FFMPEG; - icvRetrieveFrame_FFMPEG_p = (CvRetrieveFrame_Plugin)cvRetrieveFrame_FFMPEG; - icvSetCaptureProperty_FFMPEG_p = (CvSetCaptureProperty_Plugin)cvSetCaptureProperty_FFMPEG; - icvGetCaptureProperty_FFMPEG_p = (CvGetCaptureProperty_Plugin)cvGetCaptureProperty_FFMPEG; - icvCreateVideoWriter_FFMPEG_p = (CvCreateVideoWriter_Plugin)cvCreateVideoWriter_FFMPEG; - icvReleaseVideoWriter_FFMPEG_p = (CvReleaseVideoWriter_Plugin)cvReleaseVideoWriter_FFMPEG; - icvWriteFrame_FFMPEG_p = (CvWriteFrame_Plugin)cvWriteFrame_FFMPEG; - #endif } }; +}} // namespace +#endif // HAVE_FFMPEG_WRAPPER + + + +namespace cv { +namespace { + class CvCapture_FFMPEG_proxy CV_FINAL : public cv::IVideoCapture { public: @@ -228,19 +240,20 @@ public: } virtual bool open( const cv::String& filename ) { - icvInitFFMPEG::Init(); close(); - if( !icvCreateFileCapture_FFMPEG_p ) - return false; ffmpegCapture = icvCreateFileCapture_FFMPEG_p( filename.c_str() ); return ffmpegCapture != 0; } virtual void close() { - if( ffmpegCapture && icvReleaseCapture_FFMPEG_p ) + if (ffmpegCapture +#if defined(HAVE_FFMPEG_WRAPPER) + && icvReleaseCapture_FFMPEG_p +#endif +) icvReleaseCapture_FFMPEG_p( &ffmpegCapture ); - assert( ffmpegCapture == 0 ); + CV_Assert(ffmpegCapture == 0); ffmpegCapture = 0; } @@ -248,18 +261,26 @@ public: virtual int getCaptureDomain() CV_OVERRIDE { return CV_CAP_FFMPEG; } protected: - void* ffmpegCapture; + CvCapture_FFMPEG* ffmpegCapture; }; +} // namespace -cv::Ptr cv::cvCreateFileCapture_FFMPEG_proxy(const cv::String& filename) +cv::Ptr cvCreateFileCapture_FFMPEG_proxy(const cv::String& filename) { +#if defined(HAVE_FFMPEG_WRAPPER) + icvInitFFMPEG::Init(); + if (!icvCreateFileCapture_FFMPEG_p) + return cv::Ptr(); +#endif cv::Ptr capture = cv::makePtr(filename); if (capture && capture->isOpened()) return capture; return cv::Ptr(); } +namespace { + class CvVideoWriter_FFMPEG_proxy CV_FINAL : public cv::IVideoWriter { @@ -278,19 +299,20 @@ public: } virtual bool open( const cv::String& filename, int fourcc, double fps, cv::Size frameSize, bool isColor ) { - icvInitFFMPEG::Init(); close(); - if( !icvCreateVideoWriter_FFMPEG_p ) - return false; ffmpegWriter = icvCreateVideoWriter_FFMPEG_p( filename.c_str(), fourcc, fps, frameSize.width, frameSize.height, isColor ); return ffmpegWriter != 0; } virtual void close() { - if( ffmpegWriter && icvReleaseVideoWriter_FFMPEG_p ) + if (ffmpegWriter +#if defined(HAVE_FFMPEG_WRAPPER) + && icvReleaseVideoWriter_FFMPEG_p +#endif + ) icvReleaseVideoWriter_FFMPEG_p( &ffmpegWriter ); - assert( ffmpegWriter == 0 ); + CV_Assert(ffmpegWriter == 0); ffmpegWriter = 0; } @@ -299,15 +321,25 @@ public: virtual bool isOpened() const CV_OVERRIDE { return ffmpegWriter != 0; } protected: - void* ffmpegWriter; + CvVideoWriter_FFMPEG* ffmpegWriter; }; +} // namespace -cv::Ptr cv::cvCreateVideoWriter_FFMPEG_proxy(const cv::String& filename, int fourcc, - double fps, cv::Size frameSize, int isColor) +cv::Ptr cvCreateVideoWriter_FFMPEG_proxy(const cv::String& filename, int fourcc, + double fps, cv::Size frameSize, int isColor) { +#if defined(HAVE_FFMPEG_WRAPPER) + icvInitFFMPEG::Init(); + if (!icvCreateVideoWriter_FFMPEG_p) + return cv::Ptr(); +#endif cv::Ptr writer = cv::makePtr(filename, fourcc, fps, frameSize, isColor != 0); if (writer && writer->isOpened()) return writer; return cv::Ptr(); } + +} // namespace + +#endif // defined(HAVE_FFMPEG) diff --git a/modules/videoio/src/cap_ffmpeg_api.hpp b/modules/videoio/src/cap_ffmpeg_api.hpp index 7144f4ab9d..96bb8ee47c 100644 --- a/modules/videoio/src/cap_ffmpeg_api.hpp +++ b/modules/videoio/src/cap_ffmpeg_api.hpp @@ -28,6 +28,8 @@ enum CV_FFMPEG_CAP_PROP_SAR_DEN=41 }; +typedef struct CvCapture_FFMPEG CvCapture_FFMPEG; +typedef struct CvVideoWriter_FFMPEG CvVideoWriter_FFMPEG; OPENCV_FFMPEG_API struct CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG(const char* filename); OPENCV_FFMPEG_API struct CvCapture_FFMPEG_2* cvCreateFileCapture_FFMPEG_2(const char* filename); @@ -55,19 +57,19 @@ OPENCV_FFMPEG_API int cvWriteFrame_FFMPEG(struct CvVideoWriter_FFMPEG* writer, c OPENCV_FFMPEG_API void cvReleaseVideoWriter_FFMPEG(struct CvVideoWriter_FFMPEG** writer); -typedef void* (*CvCreateFileCapture_Plugin)( const char* filename ); -typedef void* (*CvCreateCameraCapture_Plugin)( int index ); -typedef int (*CvGrabFrame_Plugin)( void* capture_handle ); -typedef int (*CvRetrieveFrame_Plugin)( void* capture_handle, unsigned char** data, int* step, +typedef CvCapture_FFMPEG* (*CvCreateFileCapture_Plugin)( const char* filename ); +typedef CvCapture_FFMPEG* (*CvCreateCameraCapture_Plugin)( int index ); +typedef int (*CvGrabFrame_Plugin)( CvCapture_FFMPEG* capture_handle ); +typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, unsigned char** data, int* step, int* width, int* height, int* cn ); -typedef int (*CvSetCaptureProperty_Plugin)( void* capture_handle, int prop_id, double value ); -typedef double (*CvGetCaptureProperty_Plugin)( void* capture_handle, int prop_id ); -typedef void (*CvReleaseCapture_Plugin)( void** capture_handle ); -typedef void* (*CvCreateVideoWriter_Plugin)( const char* filename, int fourcc, +typedef int (*CvSetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id, double value ); +typedef double (*CvGetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id ); +typedef void (*CvReleaseCapture_Plugin)( CvCapture_FFMPEG** capture_handle ); +typedef CvVideoWriter_FFMPEG* (*CvCreateVideoWriter_Plugin)( const char* filename, int fourcc, double fps, int width, int height, int iscolor ); -typedef int (*CvWriteFrame_Plugin)( void* writer_handle, const unsigned char* data, int step, +typedef int (*CvWriteFrame_Plugin)( CvVideoWriter_FFMPEG* writer_handle, const unsigned char* data, int step, int width, int height, int cn, int origin); -typedef void (*CvReleaseVideoWriter_Plugin)( void** writer ); +typedef void (*CvReleaseVideoWriter_Plugin)( CvVideoWriter_FFMPEG** writer ); /* * For CUDA encoder diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 0317831d3a..9e858add0e 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -754,6 +754,17 @@ private: AutoLock& operator = (const AutoLock&); // disabled }; +static void ffmpeg_log_callback(void *ptr, int level, const char *fmt, va_list vargs) +{ + static bool skip_header = false; + static int prev_level = -1; + (void)ptr; + if (!skip_header || level != prev_level) printf("[OPENCV:FFMPEG:%02d] ", level); + vprintf(fmt, vargs); + size_t fmt_len = strlen(fmt); + skip_header = fmt_len > 0 && fmt[fmt_len - 1] != '\n'; + prev_level = level; +} class InternalFFMpegRegister { @@ -773,7 +784,18 @@ public: /* register a callback function for synchronization */ av_lockmgr_register(&LockCallBack); - av_log_set_level(AV_LOG_ERROR); +#ifndef NO_GETENV + char* debug_option = getenv("OPENCV_FFMPEG_DEBUG"); + if (debug_option != NULL) + { + av_log_set_level(AV_LOG_VERBOSE); + av_log_set_callback(ffmpeg_log_callback); + } + else +#endif + { + av_log_set_level(AV_LOG_ERROR); + } _initialized = true; } @@ -1587,6 +1609,9 @@ static AVStream *icv_add_video_stream_FFMPEG(AVFormatContext *oc, #if LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(52, 42, 0) st->avg_frame_rate = (AVRational){frame_rate, frame_rate_base}; #endif +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 20, 0) + st->time_base = c->time_base; +#endif return st; } diff --git a/modules/videoio/src/cap_mjpeg_encoder.cpp b/modules/videoio/src/cap_mjpeg_encoder.cpp index b564f608b9..fb1ded4997 100644 --- a/modules/videoio/src/cap_mjpeg_encoder.cpp +++ b/modules/videoio/src/cap_mjpeg_encoder.cpp @@ -1530,8 +1530,11 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa } -Ptr createMotionJpegWriter( const String& filename, double fps, Size frameSize, bool iscolor ) +Ptr createMotionJpegWriter(const String& filename, int fourcc, double fps, Size frameSize, bool iscolor) { + if (fourcc != CV_FOURCC('M', 'J', 'P', 'G')) + return Ptr(); + Ptr iwriter = makePtr(filename, fps, frameSize, iscolor); if( !iwriter->isOpened() ) iwriter.release(); diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index f8afb41196..adf5524e39 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -261,12 +261,10 @@ namespace cv { /* V4L2 structure */ struct buffer { - void * start; - size_t length; + void * start; + size_t length; }; -static unsigned int n_buffers = 0; - struct CvCaptureCAM_V4L CV_FINAL : public CvCapture { int deviceHandle; @@ -277,64 +275,64 @@ struct CvCaptureCAM_V4L CV_FINAL : public CvCapture char *memoryMap; IplImage frame; - __u32 palette; - int width, height; - int bufferSize; - __u32 fps; - bool convert_rgb; - bool frame_allocated; - bool returnFrame; - - /* V4L2 variables */ - buffer buffers[MAX_V4L_BUFFERS + 1]; - v4l2_capability cap; - v4l2_input inp; - v4l2_format form; - v4l2_crop crop; - v4l2_cropcap cropcap; - v4l2_requestbuffers req; - v4l2_buf_type type; - v4l2_queryctrl queryctrl; - - timeval timestamp; - - /* V4L2 control variables */ - Range focus, brightness, contrast, saturation, hue, gain, exposure; - - bool open(int _index); - bool open(const char* deviceName); - - virtual double getProperty(int) const CV_OVERRIDE; - virtual bool setProperty(int, double) CV_OVERRIDE; - virtual bool grabFrame() CV_OVERRIDE; - virtual IplImage* retrieveFrame(int) CV_OVERRIDE; - - Range getRange(int property_id) const { - switch (property_id) { - case CV_CAP_PROP_BRIGHTNESS: - return brightness; - case CV_CAP_PROP_CONTRAST: - return contrast; - case CV_CAP_PROP_SATURATION: - return saturation; - case CV_CAP_PROP_HUE: - return hue; - case CV_CAP_PROP_GAIN: - return gain; - case CV_CAP_PROP_EXPOSURE: - return exposure; - case CV_CAP_PROP_FOCUS: - return focus; - case CV_CAP_PROP_AUTOFOCUS: - return Range(0, 1); - case CV_CAP_PROP_AUTO_EXPOSURE: - return Range(0, 4); - default: - return Range(0, 255); - } - } - - virtual ~CvCaptureCAM_V4L(); + __u32 palette; + int width, height; + int bufferSize; + __u32 fps; + bool convert_rgb; + bool frame_allocated; + bool returnFrame; + + /* V4L2 variables */ + buffer buffers[MAX_V4L_BUFFERS + 1]; + v4l2_capability cap; + v4l2_input inp; + v4l2_format form; + v4l2_crop crop; + v4l2_cropcap cropcap; + v4l2_requestbuffers req; + v4l2_buf_type type; + v4l2_queryctrl queryctrl; + + timeval timestamp; + + /* V4L2 control variables */ + Range focus, brightness, contrast, saturation, hue, gain, exposure; + + bool open(int _index); + bool open(const char* deviceName); + + virtual double getProperty(int) const CV_OVERRIDE; + virtual bool setProperty(int, double) CV_OVERRIDE; + virtual bool grabFrame() CV_OVERRIDE; + virtual IplImage* retrieveFrame(int) CV_OVERRIDE; + + Range getRange(int property_id) const { + switch (property_id) { + case CV_CAP_PROP_BRIGHTNESS: + return brightness; + case CV_CAP_PROP_CONTRAST: + return contrast; + case CV_CAP_PROP_SATURATION: + return saturation; + case CV_CAP_PROP_HUE: + return hue; + case CV_CAP_PROP_GAIN: + return gain; + case CV_CAP_PROP_EXPOSURE: + return exposure; + case CV_CAP_PROP_FOCUS: + return focus; + case CV_CAP_PROP_AUTOFOCUS: + return Range(0, 1); + case CV_CAP_PROP_AUTO_EXPOSURE: + return Range(0, 4); + default: + return Range(0, 255); + } + } + + virtual ~CvCaptureCAM_V4L(); }; static void icvCloseCAM_V4L( CvCaptureCAM_V4L* capture ); @@ -347,112 +345,79 @@ static int icvSetPropertyCAM_V4L( CvCaptureCAM_V4L* capture, int property_id, /*********************** Implementations ***************************************/ -static int numCameras = 0; -static int indexList = 0; - CvCaptureCAM_V4L::~CvCaptureCAM_V4L() { icvCloseCAM_V4L(this); } -/* Simple test program: Find number of Video Sources available. - Start from 0 and go to MAX_CAMERAS while checking for the device with that name. - If it fails on the first attempt of /dev/video0, then check if /dev/video is valid. - Returns the global numCameras with the correct value (we hope) */ - -static void icvInitCapture_V4L() { - int deviceHandle; - int CameraNumber; - char deviceName[MAX_DEVICE_DRIVER_NAME]; - - CameraNumber = 0; - while(CameraNumber < MAX_CAMERAS) { - /* Print the CameraNumber at the end of the string with a width of one character */ - sprintf(deviceName, "/dev/video%1d", CameraNumber); - /* Test using an open to see if this new device name really does exists. */ - deviceHandle = open(deviceName, O_RDONLY); - if (deviceHandle != -1) { - /* This device does indeed exist - add it to the total so far */ - // add indexList - indexList|=(1 << CameraNumber); - numCameras++; - } - if (deviceHandle != -1) - close(deviceHandle); - /* Set up to test the next /dev/video source in line */ - CameraNumber++; - } /* End while */ - -}; /* End icvInitCapture_V4L */ - static bool try_palette_v4l2(CvCaptureCAM_V4L* capture) { - capture->form = v4l2_format(); - capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - capture->form.fmt.pix.pixelformat = capture->palette; - capture->form.fmt.pix.field = V4L2_FIELD_ANY; - capture->form.fmt.pix.width = capture->width; - capture->form.fmt.pix.height = capture->height; - - if (-1 == ioctl (capture->deviceHandle, VIDIOC_S_FMT, &capture->form)) - return false; + capture->form = v4l2_format(); + capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + capture->form.fmt.pix.pixelformat = capture->palette; + capture->form.fmt.pix.field = V4L2_FIELD_ANY; + capture->form.fmt.pix.width = capture->width; + capture->form.fmt.pix.height = capture->height; + + if (-1 == ioctl (capture->deviceHandle, VIDIOC_S_FMT, &capture->form)) + return false; - return capture->palette == capture->form.fmt.pix.pixelformat; + return capture->palette == capture->form.fmt.pix.pixelformat; } static int try_init_v4l2(CvCaptureCAM_V4L* capture, const char *deviceName) { - // Test device for V4L2 compatibility - // Return value: - // -1 then unable to open device - // 0 then detected nothing - // 1 then V4L2 device - - int deviceIndex; - - /* Open and test V4L2 device */ - capture->deviceHandle = open (deviceName, O_RDWR /* required */ | O_NONBLOCK, 0); - if (-1 == capture->deviceHandle) - { + // Test device for V4L2 compatibility + // Return value: + // -1 then unable to open device + // 0 then detected nothing + // 1 then V4L2 device + + int deviceIndex; + + /* Open and test V4L2 device */ + capture->deviceHandle = open (deviceName, O_RDWR /* required */ | O_NONBLOCK, 0); + if (-1 == capture->deviceHandle) + { #ifndef NDEBUG - fprintf(stderr, "(DEBUG) try_init_v4l2 open \"%s\": %s\n", deviceName, strerror(errno)); + fprintf(stderr, "(DEBUG) try_init_v4l2 open \"%s\": %s\n", deviceName, strerror(errno)); #endif - icvCloseCAM_V4L(capture); - return -1; - } + icvCloseCAM_V4L(capture); + return -1; + } - capture->cap = v4l2_capability(); - if (-1 == ioctl (capture->deviceHandle, VIDIOC_QUERYCAP, &capture->cap)) - { + capture->cap = v4l2_capability(); + if (-1 == ioctl (capture->deviceHandle, VIDIOC_QUERYCAP, &capture->cap)) + { #ifndef NDEBUG - fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_QUERYCAP \"%s\": %s\n", deviceName, strerror(errno)); + fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_QUERYCAP \"%s\": %s\n", deviceName, strerror(errno)); #endif - icvCloseCAM_V4L(capture); - return 0; - } + icvCloseCAM_V4L(capture); + return 0; + } - /* Query channels number */ - if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_INPUT, &deviceIndex)) - { + /* Query channels number */ + if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_INPUT, &deviceIndex)) + { #ifndef NDEBUG - fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_G_INPUT \"%s\": %s\n", deviceName, strerror(errno)); + fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_G_INPUT \"%s\": %s\n", deviceName, strerror(errno)); #endif - icvCloseCAM_V4L(capture); - return 0; - } + icvCloseCAM_V4L(capture); + return 0; + } - /* Query information about current input */ - capture->inp = v4l2_input(); - capture->inp.index = deviceIndex; - if (-1 == ioctl (capture->deviceHandle, VIDIOC_ENUMINPUT, &capture->inp)) - { + /* Query information about current input */ + capture->inp = v4l2_input(); + capture->inp.index = deviceIndex; + if (-1 == ioctl (capture->deviceHandle, VIDIOC_ENUMINPUT, &capture->inp)) + { #ifndef NDEBUG - fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_ENUMINPUT \"%s\": %s\n", deviceName, strerror(errno)); + fprintf(stderr, "(DEBUG) try_init_v4l2 VIDIOC_ENUMINPUT \"%s\": %s\n", deviceName, strerror(errno)); #endif - icvCloseCAM_V4L(capture); - return 0; - } + icvCloseCAM_V4L(capture); + return 0; + } - return 1; + return 1; } @@ -536,22 +501,22 @@ static void v4l2_control_range(CvCaptureCAM_V4L* cap, __u32 id) static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) { - __u32 ctrl_id; + __u32 ctrl_id; - for (ctrl_id = V4L2_CID_BASE; ctrl_id < V4L2_CID_LASTP1; ctrl_id++) - { - v4l2_control_range(capture, ctrl_id); - } + for (ctrl_id = V4L2_CID_BASE; ctrl_id < V4L2_CID_LASTP1; ctrl_id++) + { + v4l2_control_range(capture, ctrl_id); + } - for (ctrl_id = V4L2_CID_PRIVATE_BASE;;ctrl_id++) - { - v4l2_control_range(capture, ctrl_id); + for (ctrl_id = V4L2_CID_PRIVATE_BASE;;ctrl_id++) + { + v4l2_control_range(capture, ctrl_id); - if (errno == EINVAL) - break; - } + if (errno == EINVAL) + break; + } - v4l2_control_range(capture, V4L2_CID_FOCUS_ABSOLUTE); + v4l2_control_range(capture, V4L2_CID_FOCUS_ABSOLUTE); } static int v4l2_set_fps(CvCaptureCAM_V4L* capture) { @@ -619,156 +584,156 @@ static void v4l2_create_frame(CvCaptureCAM_V4L *capture) { static int _capture_V4L2 (CvCaptureCAM_V4L *capture) { - const char* deviceName = capture->deviceName.c_str(); - if (try_init_v4l2(capture, deviceName) != 1) { - /* init of the v4l2 device is not OK */ - return -1; - } - - /* V4L2 control variables are zero (memset above) */ - - /* Scan V4L2 controls */ - v4l2_scan_controls(capture); - - if ((capture->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) { - /* Nope. */ - fprintf( stderr, "VIDEOIO ERROR: V4L2: device %s is unable to capture video memory.\n",deviceName); - icvCloseCAM_V4L(capture); - return -1; - } - - /* The following code sets the CHANNEL_NUMBER of the video input. Some video sources - have sub "Channel Numbers". For a typical V4L TV capture card, this is usually 1. - I myself am using a simple NTSC video input capture card that uses the value of 1. - If you are not in North America or have a different video standard, you WILL have to change - the following settings and recompile/reinstall. This set of settings is based on - the most commonly encountered input video source types (like my bttv card) */ - - if(capture->inp.index > 0) { - capture->inp = v4l2_input(); - capture->inp.index = CHANNEL_NUMBER; - /* Set only channel number to CHANNEL_NUMBER */ - /* V4L2 have a status field from selected video mode */ - if (-1 == ioctl (capture->deviceHandle, VIDIOC_ENUMINPUT, &capture->inp)) - { - fprintf (stderr, "VIDEOIO ERROR: V4L2: Aren't able to set channel number\n"); - icvCloseCAM_V4L (capture); - return -1; - } - } /* End if */ - - /* Find Window info */ - capture->form = v4l2_format(); - capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_FMT, &capture->form)) { - fprintf( stderr, "VIDEOIO ERROR: V4L2: Could not obtain specifics of capture window.\n\n"); - icvCloseCAM_V4L(capture); - return -1; - } - - if (autosetup_capture_mode_v4l2(capture) == -1) - return -1; - - /* try to set framerate */ - v4l2_set_fps(capture); - - unsigned int min; - - /* Buggy driver paranoia. */ - min = capture->form.fmt.pix.width * 2; - - if (capture->form.fmt.pix.bytesperline < min) - capture->form.fmt.pix.bytesperline = min; - - min = capture->form.fmt.pix.bytesperline * capture->form.fmt.pix.height; - - if (capture->form.fmt.pix.sizeimage < min) - capture->form.fmt.pix.sizeimage = min; - - capture->req = v4l2_requestbuffers(); - - unsigned int buffer_number = capture->bufferSize; - - try_again: - - capture->req.count = buffer_number; - capture->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - capture->req.memory = V4L2_MEMORY_MMAP; - - if (-1 == ioctl (capture->deviceHandle, VIDIOC_REQBUFS, &capture->req)) - { - if (EINVAL == errno) - { - fprintf (stderr, "%s does not support memory mapping\n", deviceName); - } else { - perror ("VIDIOC_REQBUFS"); - } - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); - return -1; - } - - if (capture->req.count < buffer_number) - { - if (buffer_number == 1) - { - fprintf (stderr, "Insufficient buffer memory on %s\n", deviceName); - - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); - return -1; - } else { - buffer_number--; - fprintf (stderr, "Insufficient buffer memory on %s -- decreaseing buffers\n", deviceName); - - goto try_again; - } - } - - for (n_buffers = 0; n_buffers < capture->req.count; ++n_buffers) - { - v4l2_buffer buf = v4l2_buffer(); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = n_buffers; - - if (-1 == ioctl (capture->deviceHandle, VIDIOC_QUERYBUF, &buf)) { - perror ("VIDIOC_QUERYBUF"); - - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); - return -1; - } - - capture->buffers[n_buffers].length = buf.length; - capture->buffers[n_buffers].start = - mmap (NULL /* start anywhere */, - buf.length, - PROT_READ | PROT_WRITE /* required */, - MAP_SHARED /* recommended */, - capture->deviceHandle, buf.m.offset); - - if (MAP_FAILED == capture->buffers[n_buffers].start) { - perror ("mmap"); - - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); - return -1; - } - - if (n_buffers == 0) { - capture->buffers[MAX_V4L_BUFFERS].start = malloc( buf.length ); - capture->buffers[MAX_V4L_BUFFERS].length = buf.length; - } - } - - v4l2_create_frame(capture); - - // reinitialize buffers - capture->FirstCapture = 1; - - return 1; + const char* deviceName = capture->deviceName.c_str(); + if (try_init_v4l2(capture, deviceName) != 1) { + /* init of the v4l2 device is not OK */ + return -1; + } + + /* V4L2 control variables are zero (memset above) */ + + /* Scan V4L2 controls */ + v4l2_scan_controls(capture); + + if ((capture->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) { + /* Nope. */ + fprintf( stderr, "VIDEOIO ERROR: V4L2: device %s is unable to capture video memory.\n",deviceName); + icvCloseCAM_V4L(capture); + return -1; + } + + /* The following code sets the CHANNEL_NUMBER of the video input. Some video sources + have sub "Channel Numbers". For a typical V4L TV capture card, this is usually 1. + I myself am using a simple NTSC video input capture card that uses the value of 1. + If you are not in North America or have a different video standard, you WILL have to change + the following settings and recompile/reinstall. This set of settings is based on + the most commonly encountered input video source types (like my bttv card) */ + + if(capture->inp.index > 0) { + capture->inp = v4l2_input(); + capture->inp.index = CHANNEL_NUMBER; + /* Set only channel number to CHANNEL_NUMBER */ + /* V4L2 have a status field from selected video mode */ + if (-1 == ioctl (capture->deviceHandle, VIDIOC_ENUMINPUT, &capture->inp)) + { + fprintf (stderr, "VIDEOIO ERROR: V4L2: Aren't able to set channel number\n"); + icvCloseCAM_V4L (capture); + return -1; + } + } /* End if */ + + /* Find Window info */ + capture->form = v4l2_format(); + capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_FMT, &capture->form)) { + fprintf( stderr, "VIDEOIO ERROR: V4L2: Could not obtain specifics of capture window.\n\n"); + icvCloseCAM_V4L(capture); + return -1; + } + + if (autosetup_capture_mode_v4l2(capture) == -1) + return -1; + + /* try to set framerate */ + v4l2_set_fps(capture); + + unsigned int min; + + /* Buggy driver paranoia. */ + min = capture->form.fmt.pix.width * 2; + + if (capture->form.fmt.pix.bytesperline < min) + capture->form.fmt.pix.bytesperline = min; + + min = capture->form.fmt.pix.bytesperline * capture->form.fmt.pix.height; + + if (capture->form.fmt.pix.sizeimage < min) + capture->form.fmt.pix.sizeimage = min; + + capture->req = v4l2_requestbuffers(); + + unsigned int buffer_number = capture->bufferSize; + +try_again: + + capture->req.count = buffer_number; + capture->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + capture->req.memory = V4L2_MEMORY_MMAP; + + if (-1 == ioctl (capture->deviceHandle, VIDIOC_REQBUFS, &capture->req)) + { + if (EINVAL == errno) + { + fprintf (stderr, "%s does not support memory mapping\n", deviceName); + } else { + perror ("VIDIOC_REQBUFS"); + } + /* free capture, and returns an error code */ + icvCloseCAM_V4L (capture); + return -1; + } + + if (capture->req.count < buffer_number) + { + if (buffer_number == 1) + { + fprintf (stderr, "Insufficient buffer memory on %s\n", deviceName); + + /* free capture, and returns an error code */ + icvCloseCAM_V4L (capture); + return -1; + } else { + buffer_number--; + fprintf (stderr, "Insufficient buffer memory on %s -- decreaseing buffers\n", deviceName); + + goto try_again; + } + } + + for (unsigned int n_buffers = 0; n_buffers < capture->req.count; ++n_buffers) + { + v4l2_buffer buf = v4l2_buffer(); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = n_buffers; + + if (-1 == ioctl (capture->deviceHandle, VIDIOC_QUERYBUF, &buf)) { + perror ("VIDIOC_QUERYBUF"); + + /* free capture, and returns an error code */ + icvCloseCAM_V4L (capture); + return -1; + } + + capture->buffers[n_buffers].length = buf.length; + capture->buffers[n_buffers].start = + mmap (NULL /* start anywhere */, + buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, + capture->deviceHandle, buf.m.offset); + + if (MAP_FAILED == capture->buffers[n_buffers].start) { + perror ("mmap"); + + /* free capture, and returns an error code */ + icvCloseCAM_V4L (capture); + return -1; + } + + if (n_buffers == 0) { + capture->buffers[MAX_V4L_BUFFERS].start = malloc( buf.length ); + capture->buffers[MAX_V4L_BUFFERS].length = buf.length; + } + } + + v4l2_create_frame(capture); + + // reinitialize buffers + capture->FirstCapture = 1; + + return 1; }; /* End _capture_V4L2 */ /** @@ -785,39 +750,48 @@ static bool v4l2_reset( CvCaptureCAM_V4L* capture) { bool CvCaptureCAM_V4L::open(int _index) { - int autoindex = 0; - char _deviceName[MAX_DEVICE_DRIVER_NAME]; - - if (!numCameras) - icvInitCapture_V4L(); /* Haven't called icvInitCapture yet - do it now! */ - if (!numCameras) - return false; /* Are there any /dev/video input sources? */ - - //search index in indexList - if ( (_index>-1) && ! ((1 << _index) & indexList) ) - { - fprintf( stderr, "VIDEOIO ERROR: V4L: index %d is not correct!\n",_index); - return false; /* Did someone ask for not correct video source number? */ - } - - /* Select camera, or rather, V4L video source */ - if (_index<0) { // Asking for the first device available - for (; autoindexdeviceHandle, VIDIOC_QBUF, &buf) == -1) - { + if (!(buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))) + { + if (ioctl(capture->deviceHandle, VIDIOC_QBUF, &buf) == -1) + { + return 0; + } + } return 0; - } - } - return 0; default: /* display the error and stop processing */ @@ -857,24 +831,24 @@ static int read_frame_v4l2(CvCaptureCAM_V4L* capture) { perror ("VIDIOC_DQBUF"); return -1; } - } + } - assert(buf.index < capture->req.count); + assert(buf.index < capture->req.count); - memcpy(capture->buffers[MAX_V4L_BUFFERS].start, - capture->buffers[buf.index].start, - capture->buffers[MAX_V4L_BUFFERS].length ); - capture->bufferIndex = MAX_V4L_BUFFERS; - //printf("got data in buff %d, len=%d, flags=0x%X, seq=%d, used=%d)\n", - // buf.index, buf.length, buf.flags, buf.sequence, buf.bytesused); + memcpy(capture->buffers[MAX_V4L_BUFFERS].start, + capture->buffers[buf.index].start, + capture->buffers[MAX_V4L_BUFFERS].length ); + capture->bufferIndex = MAX_V4L_BUFFERS; + //printf("got data in buff %d, len=%d, flags=0x%X, seq=%d, used=%d)\n", + // buf.index, buf.length, buf.flags, buf.sequence, buf.bytesused); - //set timestamp in capture struct to be timestamp of most recent frame - capture->timestamp = buf.timestamp; + //set timestamp in capture struct to be timestamp of most recent frame + capture->timestamp = buf.timestamp; - if (-1 == ioctl (capture->deviceHandle, VIDIOC_QBUF, &buf)) - perror ("VIDIOC_QBUF"); + if (-1 == ioctl (capture->deviceHandle, VIDIOC_QBUF, &buf)) + perror ("VIDIOC_QBUF"); - return 1; + return 1; } static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { @@ -922,55 +896,55 @@ static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { } static bool icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { - if (capture->FirstCapture) { - /* Some general initialization must take place the first time through */ + if (capture->FirstCapture) { + /* Some general initialization must take place the first time through */ - /* This is just a technicality, but all buffers must be filled up before any + /* This is just a technicality, but all buffers must be filled up before any staggered SYNC is applied. SO, filler up. (see V4L HowTo) */ - { - - for (capture->bufferIndex = 0; - capture->bufferIndex < ((int)capture->req.count); - ++capture->bufferIndex) { - v4l2_buffer buf = v4l2_buffer(); + for (capture->bufferIndex = 0; + capture->bufferIndex < ((int)capture->req.count); + ++capture->bufferIndex) + { - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = (unsigned long)capture->bufferIndex; + v4l2_buffer buf = v4l2_buffer(); - if (-1 == ioctl (capture->deviceHandle, VIDIOC_QBUF, &buf)) { - perror ("VIDIOC_QBUF"); - return false; - } - } + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = (unsigned long)capture->bufferIndex; - /* enable the streaming */ - capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == ioctl (capture->deviceHandle, VIDIOC_STREAMON, - &capture->type)) { - /* error enabling the stream */ - perror ("VIDIOC_STREAMON"); - return false; + if (-1 == ioctl (capture->deviceHandle, VIDIOC_QBUF, &buf)) { + perror ("VIDIOC_QBUF"); + return false; + } + } + + /* enable the streaming */ + capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == ioctl (capture->deviceHandle, VIDIOC_STREAMON, + &capture->type)) { + /* error enabling the stream */ + perror ("VIDIOC_STREAMON"); + return false; + } } - } #if defined(V4L_ABORT_BADJPEG) // skip first frame. it is often bad -- this is unnotied in traditional apps, // but could be fatal if bad jpeg is enabled if(mainloop_v4l2(capture) != 1) - return false; + return false; #endif - /* preparation is ok */ - capture->FirstCapture = 0; - } + /* preparation is ok */ + capture->FirstCapture = 0; + } - if(mainloop_v4l2(capture) != 1) return false; + if(mainloop_v4l2(capture) != 1) return false; - return true; + return true; } /* @@ -1004,7 +978,7 @@ static bool icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { static inline void move_411_block(int yTL, int yTR, int yBL, int yBR, int u, int v, - int /*rowPixels*/, unsigned char * rgb) + int /*rowPixels*/, unsigned char * rgb) { const int rvScale = 91881; const int guScale = -22553; @@ -1014,13 +988,13 @@ move_411_block(int yTL, int yTR, int yBL, int yBR, int u, int v, int r, g, b; g = guScale * u + gvScale * v; -// if (force_rgb) { -// r = buScale * u; -// b = rvScale * v; -// } else { - r = rvScale * v; - b = buScale * u; -// } + // if (force_rgb) { + // r = buScale * u; + // b = rvScale * v; + // } else { + r = rvScale * v; + b = buScale * u; + // } yTL *= yScale; yTR *= yScale; yBL *= yScale; yBR *= yScale; @@ -1046,7 +1020,7 @@ static inline void yuv420p_to_rgb24(int width, int height, uchar* src, uchar* dst) { cvtColor(Mat(height * 3 / 2, width, CV_8U, src), Mat(height, width, CV_8UC3, dst), - COLOR_YUV2BGR_YV12); + COLOR_YUV2BGR_YV12); } // Consider a YUV411P image of 8x2 pixels. @@ -1066,7 +1040,7 @@ yuv420p_to_rgb24(int width, int height, uchar* src, uchar* dst) /* [FD] untested... */ static void yuv411p_to_rgb24(int width, int height, - unsigned char *pIn0, unsigned char *pOut0) + unsigned char *pIn0, unsigned char *pOut0) { const int numpix = width * height; const int bytes = 24 >> 3; @@ -1086,7 +1060,7 @@ yuv411p_to_rgb24(int width, int height, v = (*pV++) - 128; move_411_block(y00, y01, y10, y11, u, v, - width, pOut); + width, pOut); pY += 4; pOut += 4 * bytes; @@ -1099,14 +1073,14 @@ yuv411p_to_rgb24(int width, int height, static void yuyv_to_rgb24(int width, int height, unsigned char* src, unsigned char* dst) { cvtColor(Mat(height, width, CV_8UC2, src), Mat(height, width, CV_8UC3, dst), - COLOR_YUV2BGR_YUYV); + COLOR_YUV2BGR_YUYV); } static inline void uyvy_to_rgb24 (int width, int height, unsigned char *src, unsigned char *dst) { cvtColor(Mat(height, width, CV_8UC2, src), Mat(height, width, CV_8UC3, dst), - COLOR_YUV2BGR_UYVY); + COLOR_YUV2BGR_UYVY); } static inline void @@ -1147,64 +1121,64 @@ static void bayer2rgb24(long int WIDTH, long int HEIGHT, unsigned char *src, uns size = WIDTH*HEIGHT; for ( i = 0; i < size; i++ ) { - if ( (i/WIDTH) % 2 == 0 ) { - if ( (i % 2) == 0 ) { - /* B */ - if ( (i > WIDTH) && ((i % WIDTH) > 0) ) { - *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ - *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */ - *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ - *(rawpt+WIDTH)+*(rawpt-WIDTH))/4; /* G */ - *scanpt++ = *rawpt; /* B */ - } else { - /* first line or left column */ - *scanpt++ = *(rawpt+WIDTH+1); /* R */ - *scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; /* G */ - *scanpt++ = *rawpt; /* B */ - } - } else { - /* (B)G */ - if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) { - *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* R */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */ - } else { - /* first line or right column */ - *scanpt++ = *(rawpt+WIDTH); /* R */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = *(rawpt-1); /* B */ - } - } - } else { - if ( (i % 2) == 0 ) { - /* G(R) */ - if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) { - *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* B */ - } else { - /* bottom line or left column */ - *scanpt++ = *(rawpt+1); /* R */ - *scanpt++ = *rawpt; /* G */ - *scanpt++ = *(rawpt-WIDTH); /* B */ - } - } else { - /* R */ - if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) { - *scanpt++ = *rawpt; /* R */ - *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ - *(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */ - *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ - *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* B */ - } else { - /* bottom line or right column */ - *scanpt++ = *rawpt; /* R */ - *scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */ - *scanpt++ = *(rawpt-WIDTH-1); /* B */ - } - } - } - rawpt++; + if ( (i/WIDTH) % 2 == 0 ) { + if ( (i % 2) == 0 ) { + /* B */ + if ( (i > WIDTH) && ((i % WIDTH) > 0) ) { + *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ + *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */ + *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ + *(rawpt+WIDTH)+*(rawpt-WIDTH))/4; /* G */ + *scanpt++ = *rawpt; /* B */ + } else { + /* first line or left column */ + *scanpt++ = *(rawpt+WIDTH+1); /* R */ + *scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; /* G */ + *scanpt++ = *rawpt; /* B */ + } + } else { + /* (B)G */ + if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) { + *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* R */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */ + } else { + /* first line or right column */ + *scanpt++ = *(rawpt+WIDTH); /* R */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = *(rawpt-1); /* B */ + } + } + } else { + if ( (i % 2) == 0 ) { + /* G(R) */ + if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) { + *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* B */ + } else { + /* bottom line or left column */ + *scanpt++ = *(rawpt+1); /* R */ + *scanpt++ = *rawpt; /* G */ + *scanpt++ = *(rawpt-WIDTH); /* B */ + } + } else { + /* R */ + if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) { + *scanpt++ = *rawpt; /* R */ + *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ + *(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */ + *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ + *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* B */ + } else { + /* bottom line or right column */ + *scanpt++ = *rawpt; /* R */ + *scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */ + *scanpt++ = *(rawpt-WIDTH-1); /* B */ + } + } + } + rawpt++; } } @@ -1237,11 +1211,11 @@ static void sgbrg2rgb24(long int WIDTH, long int HEIGHT, unsigned char *src, uns *scanpt++ = (*(rawpt-WIDTH) + *(rawpt+WIDTH))/2; /* B */ } else { - /* first line or left column */ + /* first line or left column */ - *scanpt++ = *(rawpt+1); /* R */ - *scanpt++ = *(rawpt); /* G */ - *scanpt++ = *(rawpt+WIDTH); /* B */ + *scanpt++ = *(rawpt+1); /* R */ + *scanpt++ = *(rawpt); /* G */ + *scanpt++ = *(rawpt+WIDTH); /* B */ } } else //odd pixel { @@ -1306,9 +1280,9 @@ rgb24_to_rgb24 (int width, int height, unsigned char *src, unsigned char *dst) #define CLAMP(x) ((x)<0?0:((x)>255)?255:(x)) typedef struct { - int is_abs; - int len; - int val; + int is_abs; + int len; + int val; } code_table_t; @@ -1325,68 +1299,68 @@ static int init_done = 0; Each entry at index x in the table represents the codeword present at the MSB of byte x. -*/ + */ static void sonix_decompress_init(void) { - int i; - int is_abs, val, len; - - for (i = 0; i < 256; i++) { - is_abs = 0; - val = 0; - len = 0; - if ((i & 0x80) == 0) { - /* code 0 */ - val = 0; - len = 1; - } - else if ((i & 0xE0) == 0x80) { - /* code 100 */ - val = +4; - len = 3; - } - else if ((i & 0xE0) == 0xA0) { - /* code 101 */ - val = -4; - len = 3; - } - else if ((i & 0xF0) == 0xD0) { - /* code 1101 */ - val = +11; - len = 4; - } - else if ((i & 0xF0) == 0xF0) { - /* code 1111 */ - val = -11; - len = 4; - } - else if ((i & 0xF8) == 0xC8) { - /* code 11001 */ - val = +20; - len = 5; - } - else if ((i & 0xFC) == 0xC0) { - /* code 110000 */ - val = -20; - len = 6; - } - else if ((i & 0xFC) == 0xC4) { - /* code 110001xx: unknown */ - val = 0; - len = 8; - } - else if ((i & 0xF0) == 0xE0) { - /* code 1110xxxx */ - is_abs = 1; - val = (i & 0x0F) << 4; - len = 8; + int i; + int is_abs, val, len; + + for (i = 0; i < 256; i++) { + is_abs = 0; + val = 0; + len = 0; + if ((i & 0x80) == 0) { + /* code 0 */ + val = 0; + len = 1; + } + else if ((i & 0xE0) == 0x80) { + /* code 100 */ + val = +4; + len = 3; + } + else if ((i & 0xE0) == 0xA0) { + /* code 101 */ + val = -4; + len = 3; + } + else if ((i & 0xF0) == 0xD0) { + /* code 1101 */ + val = +11; + len = 4; + } + else if ((i & 0xF0) == 0xF0) { + /* code 1111 */ + val = -11; + len = 4; + } + else if ((i & 0xF8) == 0xC8) { + /* code 11001 */ + val = +20; + len = 5; + } + else if ((i & 0xFC) == 0xC0) { + /* code 110000 */ + val = -20; + len = 6; + } + else if ((i & 0xFC) == 0xC4) { + /* code 110001xx: unknown */ + val = 0; + len = 8; + } + else if ((i & 0xF0) == 0xE0) { + /* code 1110xxxx */ + is_abs = 1; + val = (i & 0x0F) << 4; + len = 8; + } + table[i].is_abs = is_abs; + table[i].val = val; + table[i].len = len; } - table[i].is_abs = is_abs; - table[i].val = val; - table[i].len = len; - } - init_done = 1; + init_done = 1; } @@ -1403,75 +1377,75 @@ static void sonix_decompress_init(void) Returns 0 if the operation was successful. Returns <0 if operation failed. -*/ + */ static int sonix_decompress(int width, int height, unsigned char *inp, unsigned char *outp) { - int row, col; - int val; - int bitpos; - unsigned char code; - unsigned char *addr; - - if (!init_done) { - /* do sonix_decompress_init first! */ - return -1; - } - - bitpos = 0; - for (row = 0; row < height; row++) { + int row, col; + int val; + int bitpos; + unsigned char code; + unsigned char *addr; + + if (!init_done) { + /* do sonix_decompress_init first! */ + return -1; + } - col = 0; + bitpos = 0; + for (row = 0; row < height; row++) { + col = 0; - /* first two pixels in first two rows are stored as raw 8-bit */ - if (row < 2) { - addr = inp + (bitpos >> 3); - code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); - bitpos += 8; - *outp++ = code; - addr = inp + (bitpos >> 3); - code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); - bitpos += 8; - *outp++ = code; + /* first two pixels in first two rows are stored as raw 8-bit */ + if (row < 2) { + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + bitpos += 8; + *outp++ = code; - col += 2; - } + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + bitpos += 8; + *outp++ = code; - while (col < width) { - /* get bitcode from bitstream */ - addr = inp + (bitpos >> 3); - code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); - - /* update bit position */ - bitpos += table[code].len; - - /* calculate pixel value */ - val = table[code].val; - if (!table[code].is_abs) { - /* value is relative to top and left pixel */ - if (col < 2) { - /* left column: relative to top pixel */ - val += outp[-2*width]; + col += 2; } - else if (row < 2) { - /* top row: relative to left pixel */ - val += outp[-2]; - } - else { - /* main area: average of left pixel and top pixel */ - val += (outp[-2] + outp[-2*width]) / 2; - } - } - /* store pixel */ - *outp++ = CLAMP(val); - col++; + while (col < width) { + /* get bitcode from bitstream */ + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + + /* update bit position */ + bitpos += table[code].len; + + /* calculate pixel value */ + val = table[code].val; + if (!table[code].is_abs) { + /* value is relative to top and left pixel */ + if (col < 2) { + /* left column: relative to top pixel */ + val += outp[-2*width]; + } + else if (row < 2) { + /* top row: relative to left pixel */ + val += outp[-2]; + } + else { + /* main area: average of left pixel and top pixel */ + val += (outp[-2] + outp[-2*width]) / 2; + } + } + + /* store pixel */ + *outp++ = CLAMP(val); + col++; + } } - } - return 0; + return 0; } static IplImage* icvRetrieveFrameCAM_V4L( CvCaptureCAM_V4L* capture, int) { @@ -1500,47 +1474,47 @@ static IplImage* icvRetrieveFrameCAM_V4L( CvCaptureCAM_V4L* capture, int) { { case V4L2_PIX_FMT_BGR24: memcpy((char *)capture->frame.imageData, - (char *)capture->buffers[capture->bufferIndex].start, - capture->frame.imageSize); + (char *)capture->buffers[capture->bufferIndex].start, + capture->frame.imageSize); break; case V4L2_PIX_FMT_YVU420: yuv420p_to_rgb24(capture->form.fmt.pix.width, - capture->form.fmt.pix.height, - (unsigned char*)(capture->buffers[capture->bufferIndex].start), - (unsigned char*)capture->frame.imageData); + capture->form.fmt.pix.height, + (unsigned char*)(capture->buffers[capture->bufferIndex].start), + (unsigned char*)capture->frame.imageData); break; case V4L2_PIX_FMT_YUV411P: yuv411p_to_rgb24(capture->form.fmt.pix.width, - capture->form.fmt.pix.height, - (unsigned char*)(capture->buffers[capture->bufferIndex].start), - (unsigned char*)capture->frame.imageData); + capture->form.fmt.pix.height, + (unsigned char*)(capture->buffers[capture->bufferIndex].start), + (unsigned char*)capture->frame.imageData); break; #ifdef HAVE_JPEG case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: if (!mjpeg_to_rgb24(capture->form.fmt.pix.width, - capture->form.fmt.pix.height, - (unsigned char*)(capture->buffers[capture->bufferIndex] - .start), - capture->buffers[capture->bufferIndex].length, - &capture->frame)) - return 0; + capture->form.fmt.pix.height, + (unsigned char*)(capture->buffers[capture->bufferIndex] + .start), + capture->buffers[capture->bufferIndex].length, + &capture->frame)) + return 0; break; #endif case V4L2_PIX_FMT_YUYV: yuyv_to_rgb24(capture->form.fmt.pix.width, - capture->form.fmt.pix.height, - (unsigned char*)(capture->buffers[capture->bufferIndex].start), - (unsigned char*)capture->frame.imageData); + capture->form.fmt.pix.height, + (unsigned char*)(capture->buffers[capture->bufferIndex].start), + (unsigned char*)capture->frame.imageData); break; case V4L2_PIX_FMT_UYVY: uyvy_to_rgb24(capture->form.fmt.pix.width, - capture->form.fmt.pix.height, - (unsigned char*)(capture->buffers[capture->bufferIndex].start), - (unsigned char*)capture->frame.imageData); + capture->form.fmt.pix.height, + (unsigned char*)(capture->buffers[capture->bufferIndex].start), + (unsigned char*)capture->frame.imageData); break; case V4L2_PIX_FMT_SBGGR8: bayer2rgb24(capture->form.fmt.pix.width, @@ -1552,9 +1526,9 @@ static IplImage* icvRetrieveFrameCAM_V4L( CvCaptureCAM_V4L* capture, int) { case V4L2_PIX_FMT_SN9C10X: sonix_decompress_init(); sonix_decompress(capture->form.fmt.pix.width, - capture->form.fmt.pix.height, - (unsigned char*)capture->buffers[capture->bufferIndex].start, - (unsigned char*)capture->buffers[(capture->bufferIndex+1) % capture->req.count].start); + capture->form.fmt.pix.height, + (unsigned char*)capture->buffers[capture->bufferIndex].start, + (unsigned char*)capture->buffers[(capture->bufferIndex+1) % capture->req.count].start); bayer2rgb24(capture->form.fmt.pix.width, capture->form.fmt.pix.height, @@ -1577,13 +1551,13 @@ static IplImage* icvRetrieveFrameCAM_V4L( CvCaptureCAM_V4L* capture, int) { case V4L2_PIX_FMT_Y16: if(capture->convert_rgb){ y16_to_rgb24(capture->form.fmt.pix.width, - capture->form.fmt.pix.height, - (unsigned char*)capture->buffers[capture->bufferIndex].start, - (unsigned char*)capture->frame.imageData); + capture->form.fmt.pix.height, + (unsigned char*)capture->buffers[capture->bufferIndex].start, + (unsigned char*)capture->frame.imageData); }else{ memcpy((char *)capture->frame.imageData, - (char *)capture->buffers[capture->bufferIndex].start, - capture->frame.imageSize); + (char *)capture->buffers[capture->bufferIndex].start, + capture->frame.imageSize); } break; } @@ -1620,121 +1594,121 @@ static inline __u32 capPropertyToV4L2(int prop) { } static double icvGetPropertyCAM_V4L (const CvCaptureCAM_V4L* capture, - int property_id ) { - { - v4l2_format form; - memset(&form, 0, sizeof(v4l2_format)); - form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_FMT, &form)) { - /* display an error message, and return an error code */ - perror ("VIDIOC_G_FMT"); - return -1; - } - - switch (property_id) { - case CV_CAP_PROP_FRAME_WIDTH: - return form.fmt.pix.width; - case CV_CAP_PROP_FRAME_HEIGHT: - return form.fmt.pix.height; - case CV_CAP_PROP_FOURCC: - case CV_CAP_PROP_MODE: - return capture->palette; - case CV_CAP_PROP_FORMAT: - return CV_MAKETYPE(IPL2CV_DEPTH(capture->frame.depth), capture->frame.nChannels); - case CV_CAP_PROP_CONVERT_RGB: - return capture->convert_rgb; - case CV_CAP_PROP_BUFFERSIZE: - return capture->bufferSize; - } - - if(property_id == CV_CAP_PROP_FPS) { - v4l2_streamparm sp = v4l2_streamparm(); - sp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(capture->deviceHandle, VIDIOC_G_PARM, &sp) < 0){ - fprintf(stderr, "VIDEOIO ERROR: V4L: Unable to get camera FPS\n"); - return -1; - } - - return sp.parm.capture.timeperframe.denominator / (double)sp.parm.capture.timeperframe.numerator; - } - - /* initialize the control structure */ - - if(property_id == CV_CAP_PROP_POS_MSEC) { - if (capture->FirstCapture) { - return 0; - } else { - return 1000 * capture->timestamp.tv_sec + ((double) capture->timestamp.tv_usec) / 1000; - } - } - - __u32 v4l2id = capPropertyToV4L2(property_id); - - if(v4l2id == __u32(-1)) { - fprintf(stderr, - "VIDEOIO ERROR: V4L2: getting property #%d is not supported\n", - property_id); - return -1; - } - - v4l2_control control = {v4l2id, 0}; - - if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_CTRL, - &control)) { - - fprintf( stderr, "VIDEOIO ERROR: V4L2: "); - switch (property_id) { - case CV_CAP_PROP_BRIGHTNESS: - fprintf (stderr, "Brightness"); - break; - case CV_CAP_PROP_CONTRAST: - fprintf (stderr, "Contrast"); - break; - case CV_CAP_PROP_SATURATION: - fprintf (stderr, "Saturation"); - break; - case CV_CAP_PROP_HUE: - fprintf (stderr, "Hue"); - break; - case CV_CAP_PROP_GAIN: - fprintf (stderr, "Gain"); - break; - case CV_CAP_PROP_AUTO_EXPOSURE: - fprintf (stderr, "Auto Exposure"); - break; - case CV_CAP_PROP_EXPOSURE: - fprintf (stderr, "Exposure"); - break; - case CV_CAP_PROP_AUTOFOCUS: - fprintf (stderr, "Autofocus"); - break; - case CV_CAP_PROP_FOCUS: - fprintf (stderr, "Focus"); - break; - } - fprintf (stderr, " is not supported by your device\n"); - - return -1; - } - - /* get the min/max values */ - Range range = capture->getRange(property_id); - - /* all was OK, so convert to 0.0 - 1.0 range, and return the value */ - return ((double)control.value - range.start) / range.size(); - - } + int property_id ) { + { + v4l2_format form; + memset(&form, 0, sizeof(v4l2_format)); + form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_FMT, &form)) { + /* display an error message, and return an error code */ + perror ("VIDIOC_G_FMT"); + return -1; + } + + switch (property_id) { + case CV_CAP_PROP_FRAME_WIDTH: + return form.fmt.pix.width; + case CV_CAP_PROP_FRAME_HEIGHT: + return form.fmt.pix.height; + case CV_CAP_PROP_FOURCC: + case CV_CAP_PROP_MODE: + return capture->palette; + case CV_CAP_PROP_FORMAT: + return CV_MAKETYPE(IPL2CV_DEPTH(capture->frame.depth), capture->frame.nChannels); + case CV_CAP_PROP_CONVERT_RGB: + return capture->convert_rgb; + case CV_CAP_PROP_BUFFERSIZE: + return capture->bufferSize; + } + + if(property_id == CV_CAP_PROP_FPS) { + v4l2_streamparm sp = v4l2_streamparm(); + sp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(capture->deviceHandle, VIDIOC_G_PARM, &sp) < 0){ + fprintf(stderr, "VIDEOIO ERROR: V4L: Unable to get camera FPS\n"); + return -1; + } + + return sp.parm.capture.timeperframe.denominator / (double)sp.parm.capture.timeperframe.numerator; + } + + /* initialize the control structure */ + + if(property_id == CV_CAP_PROP_POS_MSEC) { + if (capture->FirstCapture) { + return 0; + } else { + return 1000 * capture->timestamp.tv_sec + ((double) capture->timestamp.tv_usec) / 1000; + } + } + + __u32 v4l2id = capPropertyToV4L2(property_id); + + if(v4l2id == __u32(-1)) { + fprintf(stderr, + "VIDEOIO ERROR: V4L2: getting property #%d is not supported\n", + property_id); + return -1; + } + + v4l2_control control = {v4l2id, 0}; + + if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_CTRL, + &control)) { + + fprintf( stderr, "VIDEOIO ERROR: V4L2: "); + switch (property_id) { + case CV_CAP_PROP_BRIGHTNESS: + fprintf (stderr, "Brightness"); + break; + case CV_CAP_PROP_CONTRAST: + fprintf (stderr, "Contrast"); + break; + case CV_CAP_PROP_SATURATION: + fprintf (stderr, "Saturation"); + break; + case CV_CAP_PROP_HUE: + fprintf (stderr, "Hue"); + break; + case CV_CAP_PROP_GAIN: + fprintf (stderr, "Gain"); + break; + case CV_CAP_PROP_AUTO_EXPOSURE: + fprintf (stderr, "Auto Exposure"); + break; + case CV_CAP_PROP_EXPOSURE: + fprintf (stderr, "Exposure"); + break; + case CV_CAP_PROP_AUTOFOCUS: + fprintf (stderr, "Autofocus"); + break; + case CV_CAP_PROP_FOCUS: + fprintf (stderr, "Focus"); + break; + } + fprintf (stderr, " is not supported by your device\n"); + + return -1; + } + + /* get the min/max values */ + Range range = capture->getRange(property_id); + + /* all was OK, so convert to 0.0 - 1.0 range, and return the value */ + return ((double)control.value - range.start) / range.size(); + + } }; static bool icvSetControl (CvCaptureCAM_V4L* capture, - int property_id, double value) { + int property_id, double value) { - /* limitation of the input value */ - if (value < 0.0) { - value = 0.0; - } else if (value > 1.0) { - value = 1.0; - } + /* limitation of the input value */ + if (value < 0.0) { + value = 0.0; + } else if (value > 1.0) { + value = 1.0; + } /* initialisations */ __u32 v4l2id = capPropertyToV4L2(property_id); @@ -1773,7 +1747,7 @@ static bool icvSetControl (CvCaptureCAM_V4L* capture, } static int icvSetPropertyCAM_V4L( CvCaptureCAM_V4L* capture, - int property_id, double value ){ + int property_id, double value ){ static int width = 0, height = 0; bool retval = false; bool possible; @@ -1813,19 +1787,19 @@ static int icvSetPropertyCAM_V4L( CvCaptureCAM_V4L* capture, retval = possible || !bool(value); break; case CV_CAP_PROP_FOURCC: - { - __u32 old_palette = capture->palette; - __u32 new_palette = static_cast<__u32>(value); - capture->palette = new_palette; - if (v4l2_reset(capture)) { - retval = true; - } else { - capture->palette = old_palette; - v4l2_reset(capture); - retval = false; - } + { + __u32 old_palette = capture->palette; + __u32 new_palette = static_cast<__u32>(value); + capture->palette = new_palette; + if (v4l2_reset(capture)) { + retval = true; + } else { + capture->palette = old_palette; + v4l2_reset(capture); + retval = false; } - break; + } + break; case CV_CAP_PROP_BUFFERSIZE: if ((int)value > MAX_V4L_BUFFERS || (int)value < 1) { fprintf(stderr, "V4L: Bad buffer size %d, buffer size must be from 1 to %d\n", (int)value, MAX_V4L_BUFFERS); @@ -1848,43 +1822,43 @@ static int icvSetPropertyCAM_V4L( CvCaptureCAM_V4L* capture, } static void icvCloseCAM_V4L( CvCaptureCAM_V4L* capture ){ - /* Deallocate space - Hopefully, no leaks */ - - if (!capture->deviceName.empty()) - { - if (capture->deviceHandle != -1) - { - capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == ioctl(capture->deviceHandle, VIDIOC_STREAMOFF, &capture->type)) { - perror ("Unable to stop the stream"); - } - - for (unsigned int n_buffers_ = 0; n_buffers_ < MAX_V4L_BUFFERS; ++n_buffers_) - { - if (capture->buffers[n_buffers_].start) { - if (-1 == munmap (capture->buffers[n_buffers_].start, capture->buffers[n_buffers_].length)) { - perror ("munmap"); - } else { - capture->buffers[n_buffers_].start = 0; - } - } - } - - if (capture->buffers[MAX_V4L_BUFFERS].start) - { - free(capture->buffers[MAX_V4L_BUFFERS].start); - capture->buffers[MAX_V4L_BUFFERS].start = 0; - } - } - - if (capture->deviceHandle != -1) - close(capture->deviceHandle); - - if (capture->frame_allocated && capture->frame.imageData) - cvFree(&capture->frame.imageData); - - capture->deviceName.clear(); // flag that the capture is closed - } + /* Deallocate space - Hopefully, no leaks */ + + if (!capture->deviceName.empty()) + { + if (capture->deviceHandle != -1) + { + capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == ioctl(capture->deviceHandle, VIDIOC_STREAMOFF, &capture->type)) { + perror ("Unable to stop the stream"); + } + + for (unsigned int n_buffers = 0; n_buffers < MAX_V4L_BUFFERS; ++n_buffers) + { + if (capture->buffers[n_buffers].start) { + if (-1 == munmap (capture->buffers[n_buffers].start, capture->buffers[n_buffers].length)) { + perror ("munmap"); + } else { + capture->buffers[n_buffers].start = 0; + } + } + } + + if (capture->buffers[MAX_V4L_BUFFERS].start) + { + free(capture->buffers[MAX_V4L_BUFFERS].start); + capture->buffers[MAX_V4L_BUFFERS].start = 0; + } + } + + if (capture->deviceHandle != -1) + close(capture->deviceHandle); + + if (capture->frame_allocated && capture->frame.imageData) + cvFree(&capture->frame.imageData); + + capture->deviceName.clear(); // flag that the capture is closed + } }; bool CvCaptureCAM_V4L::grabFrame() diff --git a/modules/videoio/src/precomp.hpp b/modules/videoio/src/precomp.hpp index c08a224b05..a664aa7448 100644 --- a/modules/videoio/src/precomp.hpp +++ b/modules/videoio/src/precomp.hpp @@ -47,6 +47,9 @@ #include "opencv2/core/utility.hpp" #include "opencv2/core/private.hpp" +#include +#include + #include "opencv2/imgcodecs.hpp" #include "opencv2/imgproc.hpp" @@ -59,7 +62,7 @@ #include #include #include -#include +#include // FIXIT remove this #if defined _WIN32 || defined WINCE #if !defined _WIN32_WINNT @@ -178,7 +181,7 @@ namespace cv }; Ptr createMotionJpegCapture(const String& filename); - Ptr createMotionJpegWriter( const String& filename, double fps, Size frameSize, bool iscolor ); + Ptr createMotionJpegWriter(const String& filename, int fourcc, double fps, Size frameSize, bool iscolor); Ptr createGPhoto2Capture(int index); Ptr createGPhoto2Capture(const String& deviceName); diff --git a/modules/videoio/src/videoio_c.cpp b/modules/videoio/src/videoio_c.cpp new file mode 100644 index 0000000000..59a45225bd --- /dev/null +++ b/modules/videoio/src/videoio_c.cpp @@ -0,0 +1,152 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "precomp.hpp" + +#include "videoio_registry.hpp" + +using namespace cv; + +// Legacy C-like API + +CV_IMPL CvCapture* cvCreateCameraCapture(int index) +{ + // interpret preferred interface (0 = autodetect) + int apiPreference = (index / 100) * 100; + if (apiPreference) + { + index %= 100; + } + + const std::vector backends = cv::videoio_registry::getAvailableBackends_CaptureByIndex(); + for (size_t i = 0; i < backends.size(); i++) + { + const VideoBackendInfo& info = backends[i]; + if (apiPreference == CAP_ANY || apiPreference == info.id) + { + CvCapture* capture = NULL; + Ptr icap; // unused + VideoCapture_create(capture, icap, info.id, index); + if (capture) + { + return capture; + } + if (!icap.empty()) + { + CV_LOG_WARNING(NULL, "cvCreateFileCaptureWithPreference: backend " << info.name << " doesn't support legacy API anymore.") + } + } + } + return NULL; +} + +CV_IMPL CvCapture* cvCreateFileCaptureWithPreference(const char* filename, int apiPreference) +{ + const std::vector backends = cv::videoio_registry::getAvailableBackends_CaptureByFilename(); + for (size_t i = 0; i < backends.size(); i++) + { + const VideoBackendInfo& info = backends[i]; + if (apiPreference == CAP_ANY || apiPreference == info.id) + { + CvCapture* capture = NULL; + Ptr icap; // unused + VideoCapture_create(capture, icap, info.id, filename); + if (capture) + { + return capture; + } + if (!icap.empty()) + { + CV_LOG_WARNING(NULL, "cvCreateFileCaptureWithPreference: backend " << info.name << " doesn't support legacy API anymore.") + } + } + } + return NULL; +} + +CV_IMPL CvCapture* cvCreateFileCapture(const char * filename) +{ + return cvCreateFileCaptureWithPreference(filename, CAP_ANY); +} + +CV_IMPL CvVideoWriter* cvCreateVideoWriter(const char* filename, int fourcc, + double fps, CvSize frameSize, int is_color) +{ + const std::vector backends = cv::videoio_registry::getAvailableBackends_Writer(); + for (size_t i = 0; i < backends.size(); i++) + { + const VideoBackendInfo& info = backends[i]; + { + CvVideoWriter* writer_ = NULL; + Ptr iwriter; // unused + VideoWriter_create(writer_, iwriter, info.id, filename, fourcc, fps, frameSize, is_color != 0); + if (writer_) + { + return writer_; + } + if (!iwriter.empty()) + { + CV_LOG_WARNING(NULL, "cvCreateVideoWriter: backend " << info.name << " doesn't support legacy API anymore.") + } + } + } + return NULL; +} + +CV_IMPL int cvWriteFrame(CvVideoWriter* writer, const IplImage* image) +{ + return writer ? writer->writeFrame(image) : 0; +} + +CV_IMPL void cvReleaseVideoWriter(CvVideoWriter** pwriter) +{ + if( pwriter && *pwriter ) + { + delete *pwriter; + *pwriter = 0; + } +} + +CV_IMPL void cvReleaseCapture(CvCapture** pcapture) +{ + if (pcapture && *pcapture) + { + delete *pcapture; + *pcapture = 0; + } +} + +CV_IMPL IplImage* cvQueryFrame(CvCapture* capture) +{ + if (!capture) + return 0; + if (!capture->grabFrame()) + return 0; + return capture->retrieveFrame(0); +} + +CV_IMPL int cvGrabFrame(CvCapture* capture) +{ + return capture ? capture->grabFrame() : 0; +} + +CV_IMPL IplImage* cvRetrieveFrame(CvCapture* capture, int idx) +{ + return capture ? capture->retrieveFrame(idx) : 0; +} + +CV_IMPL double cvGetCaptureProperty(CvCapture* capture, int id) +{ + return capture ? capture->getProperty(id) : 0; +} + +CV_IMPL int cvSetCaptureProperty(CvCapture* capture, int id, double value) +{ + return capture ? capture->setProperty(id, value) : 0; +} + +CV_IMPL int cvGetCaptureDomain(CvCapture* capture) +{ + return capture ? capture->getCaptureDomain() : 0; +} diff --git a/modules/videoio/src/videoio_registry.cpp b/modules/videoio/src/videoio_registry.cpp new file mode 100644 index 0000000000..f802692dad --- /dev/null +++ b/modules/videoio/src/videoio_registry.cpp @@ -0,0 +1,644 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "precomp.hpp" + +#include "videoio_registry.hpp" + +#include "cap_intelperc.hpp" +#include "cap_librealsense.hpp" +#include "cap_dshow.hpp" + +#ifdef HAVE_MFX +#include "cap_mfx_reader.hpp" +#include "cap_mfx_writer.hpp" +#endif + +// All WinRT versions older than 8.0 should provide classes used for video support +#if defined(WINRT) && !defined(WINRT_8_0) && defined(__cplusplus_winrt) +# include "cap_winrt_capture.hpp" +# include "cap_winrt_bridge.hpp" +# define WINRT_VIDEO +#endif + +#if defined _M_X64 && defined _MSC_VER && !defined CV_ICC +#pragma optimize("",off) +#pragma warning(disable: 4748) +#endif + +using namespace cv; + +namespace cv +{ + +static bool param_VIDEOIO_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOIO_DEBUG", false); +static bool param_VIDEOCAPTURE_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOCAPTURE_DEBUG", false); +static bool param_VIDEOWRITER_DEBUG = utils::getConfigurationParameterBool("OPENCV_VIDEOWRITER_DEBUG", false); + +namespace { + +#define DECLARE_BACKEND(cap, name, mode) { cap, (BackendMode)(mode), 1000, name } + +/** Ordering guidelines: +- modern optimized, multi-platform libraries: ffmpeg, gstreamer, Media SDK +- platform specific universal SDK: WINRT, QTKIT/AVFOUNDATION, MSMF/VFW/DSHOW, V4L/V4L2 +- RGB-D: OpenNI/OpenNI2, INTELPERC/REALSENSE +- special OpenCV (file-based): "images", "mjpeg" +- special camera SDKs, including stereo: other special SDKs: FIREWIRE/1394, XIMEA/ARAVIS/GIGANETIX/PVAPI(GigE), UNICAP +- other: XINE, gphoto2, etc +*/ +static const struct VideoBackendInfo builtin_backends[] = +{ +#ifdef HAVE_FFMPEG + DECLARE_BACKEND(CAP_FFMPEG, "FFMPEG", MODE_CAPTURE_BY_FILENAME | MODE_WRITER), +#endif +#ifdef HAVE_GSTREAMER + DECLARE_BACKEND(CAP_GSTREAMER, "GSTREAMER", MODE_CAPTURE_ALL | MODE_WRITER), +#endif +#ifdef HAVE_MFX // Media SDK + DECLARE_BACKEND(CAP_INTEL_MFX, "INTEL_MFX", MODE_CAPTURE_BY_FILENAME | MODE_WRITER), +#endif + + + // Apple platform +#if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) + DECLARE_BACKEND(CAP_QT, "QUICKTIME", MODE_CAPTURE_ALL | MODE_WRITER), +#endif +#ifdef HAVE_AVFOUNDATION + DECLARE_BACKEND(CAP_AVFOUNDATION, "AVFOUNDATION", MODE_CAPTURE_ALL | MODE_WRITER), +#endif + + // Windows +#ifdef WINRT_VIDEO + DECLARE_BACKEND(CAP_WINRT, "WINRT", MODE_CAPTURE_BY_FILENAME), +#endif +#ifdef HAVE_MSMF + DECLARE_BACKEND(CAP_MSMF, "MSMF", MODE_CAPTURE_ALL | MODE_WRITER), +#endif +#ifdef HAVE_VFW + DECLARE_BACKEND(CAP_VFW, "VFW", MODE_CAPTURE_ALL | MODE_WRITER), +#endif +#ifdef HAVE_DSHOW + DECLARE_BACKEND(CAP_DSHOW, "DSHOW", MODE_CAPTURE_ALL), +#endif + + // Linux, some Unix +#if defined HAVE_CAMV4L2 + DECLARE_BACKEND(CAP_V4L2, "V4L2", MODE_CAPTURE_ALL), +#elif defined HAVE_LIBV4L || defined HAVE_CAMV4L + DECLARE_BACKEND(CAP_V4L, "V4L", MODE_CAPTURE_ALL), +#endif + + + // RGB-D universal +#ifdef HAVE_OPENNI + DECLARE_BACKEND(CAP_OPENNI, "OPENNI", MODE_CAPTURE_ALL), +#endif +#ifdef HAVE_OPENNI2 + DECLARE_BACKEND(CAP_OPENNI2, "OPENNI2", MODE_CAPTURE_ALL), +#endif +#ifdef HAVE_INTELPERC + DECLARE_BACKEND(CAP_INTELPERC, "INTEL_PERC", MODE_CAPTURE_ALL), +#elif defined(HAVE_LIBREALSENSE) + DECLARE_BACKEND(CAP_INTELPERC, "INTEL_REALSENSE", MODE_CAPTURE_BY_INDEX), +#endif + + // OpenCV file-based only + DECLARE_BACKEND(CAP_IMAGES, "CV_IMAGES", MODE_CAPTURE_BY_FILENAME | MODE_WRITER), + DECLARE_BACKEND(CAP_OPENCV_MJPEG, "CV_MJPEG", MODE_CAPTURE_BY_FILENAME | MODE_WRITER), + + // special interfaces / stereo cameras / other SDKs +#if defined(HAVE_DC1394_2) || defined(HAVE_DC1394) || defined(HAVE_CMU1394) + DECLARE_BACKEND(CAP_FIREWIRE, "FIREWIRE", MODE_CAPTURE_ALL), +#endif + // GigE +#ifdef HAVE_PVAPI + DECLARE_BACKEND(CAP_PVAPI, "PVAPI", MODE_CAPTURE_ALL), +#endif +#ifdef HAVE_XIMEA + DECLARE_BACKEND(CAP_XIAPI, "XIMEA", MODE_CAPTURE_ALL), +#endif +#ifdef HAVE_GIGE_API + DECLARE_BACKEND(CAP_GIGANETIX, "GIGANETIX", MODE_CAPTURE_ALL), +#endif +#ifdef HAVE_ARAVIS_API + DECLARE_BACKEND(CAP_ARAVIS, "ARAVIS", MODE_CAPTURE_ALL), +#endif +#ifdef HAVE_UNICAP + DECLARE_BACKEND(CAP_UNICAP, "UNICAP", MODE_CAPTURE_BY_FILENAME), +#endif + +#ifdef HAVE_GPHOTO2 + DECLARE_BACKEND(CAP_GPHOTO2, "GPHOTO2", MODE_CAPTURE_ALL), +#endif +#ifdef HAVE_XINE + DECLARE_BACKEND(CAP_XINE, "XINE", MODE_CAPTURE_BY_FILENAME), +#endif + + // dropped backends: MIL, TYZX, Android +}; + +bool sortByPriority(const VideoBackendInfo &lhs, const VideoBackendInfo &rhs) +{ + return lhs.priority > rhs.priority; +} + +/** @brief Manages list of enabled backends + */ +class VideoBackendRegistry +{ +protected: + std::vector enabledBackends; + VideoBackendRegistry() + { + const int N = sizeof(builtin_backends)/sizeof(builtin_backends[0]); + enabledBackends.assign(builtin_backends, builtin_backends + N); + for (int i = 0; i < N; i++) + { + VideoBackendInfo& info = enabledBackends[i]; + info.priority = 1000 - i * 10; + } + CV_LOG_DEBUG(NULL, "VIDEOIO: Builtin backends(" << N << "): " << dumpBackends()); + if (readPrioritySettings()) + { + CV_LOG_INFO(NULL, "VIDEOIO: Updated backends priorities: " << dumpBackends()); + } + int enabled = 0; + for (int i = 0; i < N; i++) + { + VideoBackendInfo& info = enabledBackends[enabled]; + if (enabled != i) + info = enabledBackends[i]; + size_t param_priority = utils::getConfigurationParameterSizeT(cv::format("OPENCV_VIDEOIO_PRIORITY_%s", info.name).c_str(), (size_t)info.priority); + CV_Assert(param_priority == (size_t)(int)param_priority); // overflow check + if (param_priority > 0) + { + info.priority = (int)param_priority; + enabled++; + } + else + { + CV_LOG_INFO(NULL, "VIDEOIO: Disable backend: " << info.name); + } + } + enabledBackends.resize(enabled); + CV_LOG_DEBUG(NULL, "VIDEOIO: Available backends(" << enabled << "): " << dumpBackends()); + std::sort(enabledBackends.begin(), enabledBackends.end(), sortByPriority); + CV_LOG_INFO(NULL, "VIDEOIO: Enabled backends(" << enabled << ", sorted by priority): " << dumpBackends()); + } + + static std::vector tokenize_string(const std::string& input, char token) + { + std::vector result; + std::string::size_type prev_pos = 0, pos = 0; + while((pos = input.find(token, pos)) != std::string::npos) + { + result.push_back(input.substr(prev_pos, pos-prev_pos)); + prev_pos = ++pos; + } + result.push_back(input.substr(prev_pos)); + return result; + } + bool readPrioritySettings() + { + bool hasChanges = false; + cv::String prioritized_backends = utils::getConfigurationParameterString("OPENCV_VIDEOIO_PRIORITY_LIST", NULL); + if (prioritized_backends.empty()) + return hasChanges; + CV_LOG_INFO(NULL, "VIDEOIO: Configured priority list (OPENCV_VIDEOIO_PRIORITY_LIST): " << prioritized_backends); + const std::vector names = tokenize_string(prioritized_backends, ','); + for (size_t i = 0; i < names.size(); i++) + { + const std::string& name = names[i]; + bool found = false; + for (size_t k = 0; k < enabledBackends.size(); k++) + { + VideoBackendInfo& info = enabledBackends[k]; + if (name == info.name) + { + info.priority = (int)(100000 + (names.size() - i) * 1000); + CV_LOG_DEBUG(NULL, "VIDEOIO: New backend priority: '" << name << "' => " << info.priority); + found = true; + hasChanges = true; + break; + } + } + if (!found) + { + CV_LOG_WARNING(NULL, "VIDEOIO: Can't prioritize unknown/unavailable backend: '" << name << "'"); + } + } + return hasChanges; + } +public: + std::string dumpBackends() const + { + std::ostringstream os; + for (size_t i = 0; i < enabledBackends.size(); i++) + { + if (i > 0) os << "; "; + const VideoBackendInfo& info = enabledBackends[i]; + os << info.name << '(' << info.priority << ')'; + } + return os.str(); + } + + static VideoBackendRegistry& getInstance() + { + static VideoBackendRegistry g_instance; + return g_instance; + } + + inline std::vector getAvailableBackends_CaptureByIndex() const + { + std::vector result; + for (size_t i = 0; i < enabledBackends.size(); i++) + { + const VideoBackendInfo& info = enabledBackends[i]; + if (info.mode & MODE_CAPTURE_BY_INDEX) + result.push_back(info); + } + return result; + } + inline std::vector getAvailableBackends_CaptureByFilename() const + { + std::vector result; + for (size_t i = 0; i < enabledBackends.size(); i++) + { + const VideoBackendInfo& info = enabledBackends[i]; + if (info.mode & MODE_CAPTURE_BY_FILENAME) + result.push_back(info); + } + return result; + } + inline std::vector getAvailableBackends_Writer() const + { + std::vector result; + for (size_t i = 0; i < enabledBackends.size(); i++) + { + const VideoBackendInfo& info = enabledBackends[i]; + if (info.mode & MODE_WRITER) + result.push_back(info); + } + return result; + } +}; + +} // namespace + +namespace videoio_registry { + +std::vector getAvailableBackends_CaptureByIndex() +{ + const std::vector result = VideoBackendRegistry::getInstance().getAvailableBackends_CaptureByIndex(); + return result; +} +std::vector getAvailableBackends_CaptureByFilename() +{ + const std::vector result = VideoBackendRegistry::getInstance().getAvailableBackends_CaptureByFilename(); + return result; +} +std::vector getAvailableBackends_Writer() +{ + const std::vector result = VideoBackendRegistry::getInstance().getAvailableBackends_Writer(); + return result; +} + +} // namespace registry + +#define TRY_OPEN(backend_func) \ +{ \ + try { \ + if (param_VIDEOIO_DEBUG || param_VIDEOCAPTURE_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): trying ...\n", #backend_func)); \ + icap = backend_func; \ + if (param_VIDEOIO_DEBUG ||param_VIDEOCAPTURE_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): result=%p isOpened=%d ...\n", \ + #backend_func, icap.empty() ? NULL : icap.get(), icap.empty() ? -1: icap->isOpened())); \ + } catch(const cv::Exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n", #backend_func, e.what())); \ + } catch (const std::exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n", #backend_func, e.what())); \ + } catch(...) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n", #backend_func)); \ + } \ + break; \ +} + +#define TRY_OPEN_LEGACY(backend_func) \ +{ \ + try { \ + if (param_VIDEOIO_DEBUG || param_VIDEOCAPTURE_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): trying ...\n", #backend_func)); \ + capture = backend_func; \ + if (param_VIDEOIO_DEBUG || param_VIDEOCAPTURE_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): result=%p ...\n", #backend_func, capture)); \ + } catch(const cv::Exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n", #backend_func, e.what())); \ + } catch (const std::exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n", #backend_func, e.what())); \ + } catch(...) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n", #backend_func)); \ + } \ + break; \ +} + + +void VideoCapture_create(CvCapture*& capture, Ptr& icap, VideoCaptureAPIs api, int index) +{ + CV_UNUSED(capture); CV_UNUSED(icap); + switch (api) + { + default: + CV_LOG_WARNING(NULL, "VideoCapture(index=" << index << ") was built without support of requested backendID=" << (int)api); + break; +#ifdef HAVE_GSTREAMER + case CAP_GSTREAMER: + TRY_OPEN(createGStreamerCapture(index)); + break; +#endif +#ifdef HAVE_MSMF + case CAP_MSMF: + TRY_OPEN(cvCreateCapture_MSMF(index)); + break; +#endif +#ifdef HAVE_DSHOW + case CAP_DSHOW: + TRY_OPEN(makePtr(index)); + break; +#endif +#ifdef HAVE_INTELPERC + case CAP_INTELPERC: + TRY_OPEN(makePtr()); + break; +#elif defined(HAVE_LIBREALSENSE) + case CAP_INTELPERC: + TRY_OPEN(makePtr(index)); + break; +#endif +#ifdef WINRT_VIDEO + case CAP_WINRT: + TRY_OPEN(makePtr(index)); + break; +#endif +#ifdef HAVE_GPHOTO2 + case CAP_GPHOTO2: + TRY_OPEN(createGPhoto2Capture(index)); + break; +#endif + case CAP_VFW: // or CAP_V4L or CAP_V4L2 +#ifdef HAVE_VFW + TRY_OPEN_LEGACY(cvCreateCameraCapture_VFW(index)) +#endif +#if defined HAVE_LIBV4L || defined HAVE_CAMV4L || defined HAVE_CAMV4L2 || defined HAVE_VIDEOIO + TRY_OPEN_LEGACY(cvCreateCameraCapture_V4L(index)) +#endif + break; + case CAP_FIREWIRE: +#ifdef HAVE_DC1394_2 + TRY_OPEN_LEGACY(cvCreateCameraCapture_DC1394_2(index)) +#endif +#ifdef HAVE_DC1394 + TRY_OPEN_LEGACY(cvCreateCameraCapture_DC1394(index)) +#endif +#ifdef HAVE_CMU1394 + TRY_OPEN_LEGACY(cvCreateCameraCapture_CMU(index)) +#endif + break; // CAP_FIREWIRE +#ifdef HAVE_MIL + case CAP_MIL: + TRY_OPEN_LEGACY(cvCreateCameraCapture_MIL(index)) + break; +#endif +#if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) + case CAP_QT: + TRY_OPEN_LEGACY(cvCreateCameraCapture_QT(index)) + break; +#endif +#ifdef HAVE_UNICAP + case CAP_UNICAP: + TRY_OPEN_LEGACY(cvCreateCameraCapture_Unicap(index)) + break; +#endif +#ifdef HAVE_PVAPI + case CAP_PVAPI: + TRY_OPEN_LEGACY(cvCreateCameraCapture_PvAPI(index)) + break; +#endif +#ifdef HAVE_OPENNI + case CAP_OPENNI: + TRY_OPEN_LEGACY(cvCreateCameraCapture_OpenNI(index)) + break; +#endif +#ifdef HAVE_OPENNI2 + case CAP_OPENNI2: + TRY_OPEN_LEGACY(cvCreateCameraCapture_OpenNI2(index)) + break; +#endif +#ifdef HAVE_XIMEA + case CAP_XIAPI: + TRY_OPEN_LEGACY(cvCreateCameraCapture_XIMEA(index)) + break; +#endif + +#ifdef HAVE_AVFOUNDATION + case CAP_AVFOUNDATION: + TRY_OPEN_LEGACY(cvCreateCameraCapture_AVFoundation(index)) + break; +#endif + +#ifdef HAVE_GIGE_API + case CAP_GIGANETIX: + TRY_OPEN_LEGACY(cvCreateCameraCapture_Giganetix(index)) + break; +#endif + +#ifdef HAVE_ARAVIS_API + case CAP_ARAVIS: + TRY_OPEN_LEGACY(cvCreateCameraCapture_Aravis(index)) + break; +#endif + } // switch (api) +} + +void VideoCapture_create(CvCapture*& capture, Ptr& icap, VideoCaptureAPIs api, const cv::String& filename) +{ + switch (api) + { + default: + CV_LOG_WARNING(NULL, "VideoCapture(filename=" << filename << ") was built without support of requested backendID=" << (int)api); + break; +#if defined HAVE_LIBV4L || defined HAVE_CAMV4L || defined HAVE_CAMV4L2 || defined HAVE_VIDEOIO + case CAP_V4L: + TRY_OPEN_LEGACY(cvCreateCameraCapture_V4L(filename.c_str())) + break; +#endif + +#ifdef HAVE_VFW + case CAP_VFW: + TRY_OPEN_LEGACY(cvCreateFileCapture_VFW(filename.c_str())) + break; +#endif + +#if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) + case CAP_QT: + TRY_OPEN_LEGACY(cvCreateFileCapture_QT(filename.c_str())) + break; +#endif + +#ifdef HAVE_AVFOUNDATION + case CAP_AVFOUNDATION: + TRY_OPEN_LEGACY(cvCreateFileCapture_AVFoundation(filename.c_str())) + break; +#endif + +#ifdef HAVE_OPENNI + case CAP_OPENNI: + TRY_OPEN_LEGACY(cvCreateFileCapture_OpenNI(filename.c_str())) + break; +#endif + +#ifdef HAVE_OPENNI2 + case CAP_OPENNI2: + TRY_OPEN_LEGACY(cvCreateFileCapture_OpenNI2(filename.c_str())) + break; +#endif +#ifdef HAVE_XIMEA + case CAP_XIAPI: + TRY_OPEN_LEGACY(cvCreateCameraCapture_XIMEA(filename.c_str())) + break; +#endif + case CAP_IMAGES: + TRY_OPEN_LEGACY(cvCreateFileCapture_Images(filename.c_str())) + break; +#ifdef HAVE_FFMPEG + case CAP_FFMPEG: + TRY_OPEN(cvCreateFileCapture_FFMPEG_proxy(filename)) + break; +#endif +#ifdef HAVE_GSTREAMER + case CAP_GSTREAMER: + TRY_OPEN(createGStreamerCapture(filename)) + break; +#endif +#ifdef HAVE_XINE + case CAP_XINE: + TRY_OPEN(createXINECapture(filename.c_str())) + break; +#endif +#ifdef HAVE_MSMF + case CAP_MSMF: + TRY_OPEN(cvCreateCapture_MSMF(filename)) + break; +#endif +#ifdef HAVE_GPHOTO2 + case CAP_GPHOTO2: + TRY_OPEN(createGPhoto2Capture(filename)) + break; +#endif +#ifdef HAVE_MFX + case CAP_INTEL_MFX: + TRY_OPEN(makePtr(filename)) + break; +#endif + case CAP_OPENCV_MJPEG: + TRY_OPEN(createMotionJpegCapture(filename)) + break; + } // switch +} + + +void VideoWriter_create(CvVideoWriter*& writer, Ptr& iwriter, VideoCaptureAPIs api, + const String& filename, int fourcc, double fps, const Size& frameSize, bool isColor) +{ +#define CREATE_WRITER(backend_func) \ +{ \ + try { \ + if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): trying ...\n", #backend_func)); \ + iwriter = backend_func; \ + if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): result=%p isOpened=%d...\n", #backend_func, iwriter.empty() ? NULL : iwriter.get(), iwriter.empty() ? iwriter->isOpened() : -1)); \ + } catch(const cv::Exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n", #backend_func, e.what())); \ + } catch (const std::exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n", #backend_func, e.what())); \ + } catch(...) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n", #backend_func)); \ + } \ + break; \ +} + +#define CREATE_WRITER_LEGACY(backend_func) \ +{ \ + try { \ + if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): trying ...\n", #backend_func)); \ + writer = backend_func; \ + if (param_VIDEOIO_DEBUG || param_VIDEOWRITER_DEBUG) \ + CV_LOG_WARNING(NULL, cv::format("VIDEOIO(%s): result=%p...\n", #backend_func, writer)); \ + } catch(const cv::Exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised OpenCV exception:\n\n%s\n", #backend_func, e.what())); \ + } catch (const std::exception& e) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised C++ exception:\n\n%s\n", #backend_func, e.what())); \ + } catch(...) { \ + CV_LOG_ERROR(NULL, cv::format("VIDEOIO(%s): raised unknown C++ exception!\n\n", #backend_func)); \ + } \ + break; \ +} + + switch (api) + { + default: + CV_LOG_ERROR(NULL, "Unknown VideoWriter backend (check getBuildInformation()): " << (int)api); + break; +#ifdef HAVE_FFMPEG + case CAP_FFMPEG: + CREATE_WRITER(cvCreateVideoWriter_FFMPEG_proxy(filename, fourcc, fps, frameSize, isColor)); + break; +#endif +#ifdef HAVE_MSMF + case CAP_MSMF: + CREATE_WRITER(cvCreateVideoWriter_MSMF(filename, fourcc, fps, frameSize, isColor)); + break; +#endif +#ifdef HAVE_MFX + case CAP_INTEL_MFX: + CREATE_WRITER(VideoWriter_IntelMFX::create(filename, fourcc, fps, frameSize, isColor)); + break; +#endif +#ifdef HAVE_VFW + case CAP_VFW: + CREATE_WRITER_LEGACY(cvCreateVideoWriter_VFW(filename.c_str(), fourcc, fps, frameSize, isColor)) + break; +#endif +#ifdef HAVE_AVFOUNDATION + case CAP_AVFOUNDATION: + CREATE_WRITER_LEGACY(cvCreateVideoWriter_AVFoundation(filename.c_str(), fourcc, fps, frameSize, isColor)) + break; +#endif +#if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT) + case(CAP_QT): + CREATE_WRITER_LEGACY(cvCreateVideoWriter_QT(filename.c_str(), fourcc, fps, frameSize, isColor)) + break; +#endif +#ifdef HAVE_GSTREAMER +case CAP_GSTREAMER: + CREATE_WRITER_LEGACY(cvCreateVideoWriter_GStreamer (filename.c_str(), fourcc, fps, frameSize, isColor)) + break; +#endif + case CAP_OPENCV_MJPEG: + CREATE_WRITER(createMotionJpegWriter(filename, fourcc, fps, frameSize, isColor)); + break; + case CAP_IMAGES: + if(!fourcc || !fps) + { + CREATE_WRITER_LEGACY(cvCreateVideoWriter_Images(filename.c_str())); + } + break; + } // switch(api) +} + + +} // namespace diff --git a/modules/videoio/src/videoio_registry.hpp b/modules/videoio/src/videoio_registry.hpp new file mode 100644 index 0000000000..a6d4755bd2 --- /dev/null +++ b/modules/videoio/src/videoio_registry.hpp @@ -0,0 +1,43 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef __OPENCV_VIDEOIO_VIDEOIO_REGISTRY_HPP__ +#define __OPENCV_VIDEOIO_VIDEOIO_REGISTRY_HPP__ + +namespace cv +{ + +/** Capabilities bitmask */ +enum BackendMode { + MODE_CAPTURE_BY_INDEX = 1 << 0, //!< device index + MODE_CAPTURE_BY_FILENAME = 1 << 1, //!< filename or device path (v4l2) + MODE_WRITER = 1 << 4, //!< writer + + MODE_CAPTURE_ALL = MODE_CAPTURE_BY_INDEX + MODE_CAPTURE_BY_FILENAME, +}; + +struct VideoBackendInfo { + VideoCaptureAPIs id; + BackendMode mode; + int priority; // 1000- - default builtin priority + // 0 - disabled (OPENCV_VIDEOIO_PRIORITY_ = 0) + // >10000 - prioritized list (OPENCV_VIDEOIO_PRIORITY_LIST) + const char* name; +}; + +namespace videoio_registry { + +std::vector getAvailableBackends_CaptureByIndex(); +std::vector getAvailableBackends_CaptureByFilename(); +std::vector getAvailableBackends_Writer(); + +} // namespace + +void VideoCapture_create(CvCapture*& capture, Ptr& icap, VideoCaptureAPIs api, int index); +void VideoCapture_create(CvCapture*& capture, Ptr& icap, VideoCaptureAPIs api, const cv::String& filename); +void VideoWriter_create(CvVideoWriter*& writer, Ptr& iwriter, VideoCaptureAPIs api, + const String& filename, int fourcc, double fps, const Size& frameSize, bool isColor); + +} // namespace +#endif // __OPENCV_VIDEOIO_VIDEOIO_REGISTRY_HPP__ diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 2baeeb8b41..e9e5e9aca1 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -228,7 +228,7 @@ public: static std::string TmpDirectory; CreateVideoWriterInvoker(std::vector& _writers, std::vector& _files) : - ParallelLoopBody(), writers(&_writers), files(&_files) + writers(_writers), files(_files) { } @@ -240,16 +240,16 @@ public: stream << i << ".avi"; std::string fileName = tempfile(stream.str().c_str()); - files->operator[](i) = fileName; - writers->operator[](i) = new VideoWriter(fileName, CAP_FFMPEG, VideoWriter::fourcc('X','V','I','D'), 25.0f, FrameSize); + files[i] = fileName; + writers[i] = new VideoWriter(fileName, CAP_FFMPEG, VideoWriter::fourcc('X','V','I','D'), 25.0f, FrameSize); - CV_Assert(writers->operator[](i)->isOpened()); + CV_Assert(writers[i]->isOpened()); } } private: - std::vector* writers; - std::vector* files; + std::vector& writers; + std::vector& files; }; std::string CreateVideoWriterInvoker::TmpDirectory; @@ -357,6 +357,8 @@ public: for (unsigned int i = 0; i < frameCount && next; ++i) { + SCOPED_TRACE(cv::format("frame=%d/%d", (int)i, (int)frameCount)); + Mat actual; (*capture) >> actual; diff --git a/modules/viz/CMakeLists.txt b/modules/viz/CMakeLists.txt index 903022bbaf..1f1e1af3b9 100644 --- a/modules/viz/CMakeLists.txt +++ b/modules/viz/CMakeLists.txt @@ -19,7 +19,7 @@ if(NOT BUILD_SHARED_LIBS) endif() endforeach() if(_conflicts) - message(STATUS "Disabling VIZ module due conflicts with VTK dependencies: ${_conflicts}") + message(STATUS "Disabling VIZ module due to conflicts with VTK dependencies: ${_conflicts}") ocv_module_disable(viz) endif() endif() diff --git a/modules/viz/include/opencv2/viz/widgets.hpp b/modules/viz/include/opencv2/viz/widgets.hpp index cdfe20800f..4e2a4565d0 100644 --- a/modules/viz/include/opencv2/viz/widgets.hpp +++ b/modules/viz/include/opencv2/viz/widgets.hpp @@ -506,7 +506,7 @@ namespace cv }; ///////////////////////////////////////////////////////////////////////////// - /// Compond widgets + /// Compound widgets /** @brief This 3D Widget represents a coordinate system. : */ diff --git a/modules/viz/src/shapes.cpp b/modules/viz/src/shapes.cpp index 1dfa7c38e1..30fde1cc5c 100644 --- a/modules/viz/src/shapes.cpp +++ b/modules/viz/src/shapes.cpp @@ -528,7 +528,7 @@ void cv::viz::WText3D::setText(const String &text) cv::String cv::viz::WText3D::getText() const { - vtkFollower *actor = vtkFollower::SafeDownCast(WidgetAccessor::getProp(*this)); + vtkActor *actor = vtkActor::SafeDownCast(WidgetAccessor::getProp(*this)); CV_Assert("This widget does not support text." && actor); vtkPolyDataMapper *mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); diff --git a/platforms/android/android.toolchain.cmake b/platforms/android/android.toolchain.cmake index e566301d81..b37dea01ae 100644 --- a/platforms/android/android.toolchain.cmake +++ b/platforms/android/android.toolchain.cmake @@ -157,7 +157,7 @@ # Silently degrades to gnustl_static if not available. # c++_static -> Use the LLVM libc++ runtime as a static library. # Implies -frtti -fexceptions. -# c++_shared -> Use the LLVM libc++ runtime as a static library. +# c++_shared -> Use the LLVM libc++ runtime as a shared library. # Implies -frtti -fno-exceptions. # # ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on diff --git a/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp b/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp index 2b8471d35b..894b01ce56 100644 --- a/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp +++ b/samples/cpp/tutorial_code/TrackingMotion/cornerDetector_Demo.cpp @@ -13,15 +13,15 @@ using namespace std; /// Global variables Mat src, src_gray; -Mat myHarris_dst; Mat myHarris_copy; Mat Mc; -Mat myShiTomasi_dst; Mat myShiTomasi_copy; +Mat myHarris_dst, myHarris_copy, Mc; +Mat myShiTomasi_dst, myShiTomasi_copy; int myShiTomasi_qualityLevel = 50; int myHarris_qualityLevel = 50; int max_qualityLevel = 100; -double myHarris_minVal; double myHarris_maxVal; -double myShiTomasi_minVal; double myShiTomasi_maxVal; +double myHarris_minVal, myHarris_maxVal; +double myShiTomasi_minVal, myShiTomasi_maxVal; RNG rng(12345); @@ -37,56 +37,54 @@ void myHarris_function( int, void* ); */ int main( int argc, char** argv ) { - /// Load source image and convert it to gray - CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" ); - src = imread( parser.get( "@input" ), IMREAD_COLOR ); - if ( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); - - /// Set some parameters - int blockSize = 3; int apertureSize = 3; - - /// My Harris matrix -- Using cornerEigenValsAndVecs - myHarris_dst = Mat::zeros( src_gray.size(), CV_32FC(6) ); - Mc = Mat::zeros( src_gray.size(), CV_32FC1 ); - - cornerEigenValsAndVecs( src_gray, myHarris_dst, blockSize, apertureSize, BORDER_DEFAULT ); - - /* calculate Mc */ - for( int j = 0; j < src_gray.rows; j++ ) - { for( int i = 0; i < src_gray.cols; i++ ) - { - float lambda_1 = myHarris_dst.at(j, i)[0]; - float lambda_2 = myHarris_dst.at(j, i)[1]; - Mc.at(j,i) = lambda_1*lambda_2 - 0.04f*pow( ( lambda_1 + lambda_2 ), 2 ); - } - } - - minMaxLoc( Mc, &myHarris_minVal, &myHarris_maxVal, 0, 0, Mat() ); - - /* Create Window and Trackbar */ - namedWindow( myHarris_window, WINDOW_AUTOSIZE ); - createTrackbar( " Quality Level:", myHarris_window, &myHarris_qualityLevel, max_qualityLevel, myHarris_function ); - myHarris_function( 0, 0 ); - - /// My Shi-Tomasi -- Using cornerMinEigenVal - myShiTomasi_dst = Mat::zeros( src_gray.size(), CV_32FC1 ); - cornerMinEigenVal( src_gray, myShiTomasi_dst, blockSize, apertureSize, BORDER_DEFAULT ); - - minMaxLoc( myShiTomasi_dst, &myShiTomasi_minVal, &myShiTomasi_maxVal, 0, 0, Mat() ); - - /* Create Window and Trackbar */ - namedWindow( myShiTomasi_window, WINDOW_AUTOSIZE ); - createTrackbar( " Quality Level:", myShiTomasi_window, &myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function ); - myShiTomasi_function( 0, 0 ); - - waitKey(0); - return(0); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/building.jpg | input image}" ); + src = imread( parser.get( "@input" ) ); + if ( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); + + /// Set some parameters + int blockSize = 3, apertureSize = 3; + + /// My Harris matrix -- Using cornerEigenValsAndVecs + cornerEigenValsAndVecs( src_gray, myHarris_dst, blockSize, apertureSize ); + + /* calculate Mc */ + Mc = Mat( src_gray.size(), CV_32FC1 ); + for( int i = 0; i < src_gray.rows; i++ ) + { + for( int j = 0; j < src_gray.cols; j++ ) + { + float lambda_1 = myHarris_dst.at(i, j)[0]; + float lambda_2 = myHarris_dst.at(i, j)[1]; + Mc.at(i, j) = lambda_1*lambda_2 - 0.04f*pow( ( lambda_1 + lambda_2 ), 2 ); + } + } + + minMaxLoc( Mc, &myHarris_minVal, &myHarris_maxVal ); + + /* Create Window and Trackbar */ + namedWindow( myHarris_window ); + createTrackbar( "Quality Level:", myHarris_window, &myHarris_qualityLevel, max_qualityLevel, myHarris_function ); + myHarris_function( 0, 0 ); + + /// My Shi-Tomasi -- Using cornerMinEigenVal + cornerMinEigenVal( src_gray, myShiTomasi_dst, blockSize, apertureSize ); + + minMaxLoc( myShiTomasi_dst, &myShiTomasi_minVal, &myShiTomasi_maxVal ); + + /* Create Window and Trackbar */ + namedWindow( myShiTomasi_window ); + createTrackbar( "Quality Level:", myShiTomasi_window, &myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function ); + myShiTomasi_function( 0, 0 ); + + waitKey(); + return 0; } /** @@ -94,18 +92,20 @@ int main( int argc, char** argv ) */ void myShiTomasi_function( int, void* ) { - myShiTomasi_copy = src.clone(); - - if( myShiTomasi_qualityLevel < 1 ) { myShiTomasi_qualityLevel = 1; } - - for( int j = 0; j < src_gray.rows; j++ ) - { for( int i = 0; i < src_gray.cols; i++ ) - { - if( myShiTomasi_dst.at(j,i) > myShiTomasi_minVal + ( myShiTomasi_maxVal - myShiTomasi_minVal )*myShiTomasi_qualityLevel/max_qualityLevel ) - { circle( myShiTomasi_copy, Point(i,j), 4, Scalar( rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255) ), -1, 8, 0 ); } - } - } - imshow( myShiTomasi_window, myShiTomasi_copy ); + myShiTomasi_copy = src.clone(); + myShiTomasi_qualityLevel = MAX(myShiTomasi_qualityLevel, 1); + + for( int i = 0; i < src_gray.rows; i++ ) + { + for( int j = 0; j < src_gray.cols; j++ ) + { + if( myShiTomasi_dst.at(i,j) > myShiTomasi_minVal + ( myShiTomasi_maxVal - myShiTomasi_minVal )*myShiTomasi_qualityLevel/max_qualityLevel ) + { + circle( myShiTomasi_copy, Point(j,i), 4, Scalar( rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256) ), FILLED ); + } + } + } + imshow( myShiTomasi_window, myShiTomasi_copy ); } /** @@ -113,16 +113,18 @@ void myShiTomasi_function( int, void* ) */ void myHarris_function( int, void* ) { - myHarris_copy = src.clone(); - - if( myHarris_qualityLevel < 1 ) { myHarris_qualityLevel = 1; } - - for( int j = 0; j < src_gray.rows; j++ ) - { for( int i = 0; i < src_gray.cols; i++ ) - { - if( Mc.at(j,i) > myHarris_minVal + ( myHarris_maxVal - myHarris_minVal )*myHarris_qualityLevel/max_qualityLevel ) - { circle( myHarris_copy, Point(i,j), 4, Scalar( rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255) ), -1, 8, 0 ); } - } - } - imshow( myHarris_window, myHarris_copy ); + myHarris_copy = src.clone(); + myHarris_qualityLevel = MAX(myHarris_qualityLevel, 1); + + for( int i = 0; i < src_gray.rows; i++ ) + { + for( int j = 0; j < src_gray.cols; j++ ) + { + if( Mc.at(i,j) > myHarris_minVal + ( myHarris_maxVal - myHarris_minVal )*myHarris_qualityLevel/max_qualityLevel ) + { + circle( myHarris_copy, Point(j,i), 4, Scalar( rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256) ), FILLED ); + } + } + } + imshow( myHarris_window, myHarris_copy ); } diff --git a/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp b/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp index 2d44eeb4f6..35672706bc 100644 --- a/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp +++ b/samples/cpp/tutorial_code/TrackingMotion/cornerHarris_Demo.cpp @@ -27,26 +27,26 @@ void cornerHarris_demo( int, void* ); */ int main( int argc, char** argv ) { - /// Load source image and convert it to gray - CommandLineParser parser( argc, argv, "{@input | ../data/building.jpg | input image}" ); - src = imread( parser.get( "@input" ), IMREAD_COLOR ); - if ( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/building.jpg | input image}" ); + src = imread( parser.get( "@input" ) ); + if ( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); - /// Create a window and a trackbar - namedWindow( source_window, WINDOW_AUTOSIZE ); - createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo ); - imshow( source_window, src ); + /// Create a window and a trackbar + namedWindow( source_window ); + createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo ); + imshow( source_window, src ); - cornerHarris_demo( 0, 0 ); + cornerHarris_demo( 0, 0 ); - waitKey(0); - return(0); + waitKey(); + return 0; } /** @@ -55,33 +55,33 @@ int main( int argc, char** argv ) */ void cornerHarris_demo( int, void* ) { + /// Detector parameters + int blockSize = 2; + int apertureSize = 3; + double k = 0.04; - Mat dst, dst_norm, dst_norm_scaled; - dst = Mat::zeros( src.size(), CV_32FC1 ); + /// Detecting corners + Mat dst = Mat::zeros( src.size(), CV_32FC1 ); + cornerHarris( src_gray, dst, blockSize, apertureSize, k ); - /// Detector parameters - int blockSize = 2; - int apertureSize = 3; - double k = 0.04; + /// Normalizing + Mat dst_norm, dst_norm_scaled; + normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() ); + convertScaleAbs( dst_norm, dst_norm_scaled ); - /// Detecting corners - cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT ); + /// Drawing a circle around corners + for( int i = 0; i < dst_norm.rows ; i++ ) + { + for( int j = 0; j < dst_norm.cols; j++ ) + { + if( (int) dst_norm.at(i,j) > thresh ) + { + circle( dst_norm_scaled, Point(j,i), 5, Scalar(0), 2, 8, 0 ); + } + } + } - /// Normalizing - normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() ); - convertScaleAbs( dst_norm, dst_norm_scaled ); - - /// Drawing a circle around corners - for( int j = 0; j < dst_norm.rows ; j++ ) - { for( int i = 0; i < dst_norm.cols; i++ ) - { - if( (int) dst_norm.at(j,i) > thresh ) - { - circle( dst_norm_scaled, Point( i, j ), 5, Scalar(0), 2, 8, 0 ); - } - } - } - /// Showing the result - namedWindow( corners_window, WINDOW_AUTOSIZE ); - imshow( corners_window, dst_norm_scaled ); + /// Showing the result + namedWindow( corners_window ); + imshow( corners_window, dst_norm_scaled ); } diff --git a/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp b/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp index 0addc5503e..853078bada 100644 --- a/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp +++ b/samples/cpp/tutorial_code/TrackingMotion/cornerSubPix_Demo.cpp @@ -28,29 +28,29 @@ void goodFeaturesToTrack_Demo( int, void* ); */ int main( int argc, char** argv ) { - /// Load source image and convert it to gray - CommandLineParser parser( argc, argv, "{@input | ../data/pic3.png | input image}" ); - src = imread(parser.get( "@input" ), IMREAD_COLOR); - if ( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/pic3.png | input image}" ); + src = imread( parser.get( "@input" ) ); + if( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); - /// Create Window - namedWindow( source_window, WINDOW_AUTOSIZE ); + /// Create Window + namedWindow( source_window ); - /// Create Trackbar to set the number of corners - createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); + /// Create Trackbar to set the number of corners + createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); - imshow( source_window, src ); + imshow( source_window, src ); - goodFeaturesToTrack_Demo( 0, 0 ); + goodFeaturesToTrack_Demo( 0, 0 ); - waitKey(0); - return(0); + waitKey(); + return 0; } /** @@ -59,52 +59,54 @@ int main( int argc, char** argv ) */ void goodFeaturesToTrack_Demo( int, void* ) { - if( maxCorners < 1 ) { maxCorners = 1; } - - /// Parameters for Shi-Tomasi algorithm - vector corners; - double qualityLevel = 0.01; - double minDistance = 10; - int blockSize = 3, gradiantSize = 3; - bool useHarrisDetector = false; - double k = 0.04; - - /// Copy the source image - Mat copy; - copy = src.clone(); - - /// Apply corner detection - goodFeaturesToTrack( src_gray, - corners, - maxCorners, - qualityLevel, - minDistance, - Mat(), - blockSize, - gradiantSize, - useHarrisDetector, - k ); - - - /// Draw corners detected - cout<<"** Number of corners detected: "< corners; + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + bool useHarrisDetector = false; + double k = 0.04; + + /// Copy the source image + Mat copy = src.clone(); + + /// Apply corner detection + goodFeaturesToTrack( src_gray, + corners, + maxCorners, + qualityLevel, + minDistance, + Mat(), + blockSize, + gradientSize, + useHarrisDetector, + k ); + + + /// Draw corners detected + cout << "** Number of corners detected: " << corners.size() << endl; + int radius = 4; + for( size_t i = 0; i < corners.size(); i++ ) + { + circle( copy, corners[i], radius, Scalar(rng.uniform(0,255), rng.uniform(0, 256), rng.uniform(0, 256)), FILLED ); + } + + /// Show what you got + namedWindow( source_window ); + imshow( source_window, copy ); + + /// Set the needed parameters to find the refined corners + Size winSize = Size( 5, 5 ); + Size zeroZone = Size( -1, -1 ); + TermCriteria criteria = TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 40, 0.001 ); + + /// Calculate the refined corner locations + cornerSubPix( src_gray, corners, winSize, zeroZone, criteria ); + + /// Write them down + for( size_t i = 0; i < corners.size(); i++ ) + { + cout << " -- Refined Corner [" << i << "] (" << corners[i].x << "," << corners[i].y << ")" << endl; + } } diff --git a/samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp b/samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp index e72653f41e..022cd456d1 100644 --- a/samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp +++ b/samples/cpp/tutorial_code/TrackingMotion/goodFeaturesToTrack_Demo.cpp @@ -29,29 +29,29 @@ void goodFeaturesToTrack_Demo( int, void* ); */ int main( int argc, char** argv ) { - /// Load source image and convert it to gray - CommandLineParser parser( argc, argv, "{@input | ../data/pic3.png | input image}" ); - src = imread( parser.get( "@input" ), IMREAD_COLOR ); - if( src.empty() ) - { - cout << "Could not open or find the image!\n" << endl; - cout << "Usage: " << argv[0] << " " << endl; - return -1; - } - cvtColor( src, src_gray, COLOR_BGR2GRAY ); + /// Load source image and convert it to gray + CommandLineParser parser( argc, argv, "{@input | ../data/pic3.png | input image}" ); + src = imread( parser.get( "@input" ) ); + if( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + cvtColor( src, src_gray, COLOR_BGR2GRAY ); - /// Create Window - namedWindow( source_window, WINDOW_AUTOSIZE ); + /// Create Window + namedWindow( source_window ); - /// Create Trackbar to set the number of corners - createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); + /// Create Trackbar to set the number of corners + createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); - imshow( source_window, src ); + imshow( source_window, src ); - goodFeaturesToTrack_Demo( 0, 0 ); + goodFeaturesToTrack_Demo( 0, 0 ); - waitKey(0); - return(0); + waitKey(); + return 0; } /** @@ -60,40 +60,40 @@ int main( int argc, char** argv ) */ void goodFeaturesToTrack_Demo( int, void* ) { - if( maxCorners < 1 ) { maxCorners = 1; } - - /// Parameters for Shi-Tomasi algorithm - vector corners; - double qualityLevel = 0.01; - double minDistance = 10; - int blockSize = 3, gradiantSize = 3; - bool useHarrisDetector = false; - double k = 0.04; - - /// Copy the source image - Mat copy; - copy = src.clone(); - - /// Apply corner detection - goodFeaturesToTrack( src_gray, - corners, - maxCorners, - qualityLevel, - minDistance, - Mat(), - blockSize, - gradiantSize, - useHarrisDetector, - k ); - - - /// Draw corners detected - cout<<"** Number of corners detected: "< corners; + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + bool useHarrisDetector = false; + double k = 0.04; + + /// Copy the source image + Mat copy = src.clone(); + + /// Apply corner detection + goodFeaturesToTrack( src_gray, + corners, + maxCorners, + qualityLevel, + minDistance, + Mat(), + blockSize, + gradientSize, + useHarrisDetector, + k ); + + + /// Draw corners detected + cout << "** Number of corners detected: " << corners.size() << endl; + int radius = 4; + for( size_t i = 0; i < corners.size(); i++ ) + { + circle( copy, corners[i], radius, Scalar(rng.uniform(0,255), rng.uniform(0, 256), rng.uniform(0, 256)), FILLED ); + } + + /// Show what you got + namedWindow( source_window ); + imshow( source_window, copy ); } diff --git a/samples/cpp/tutorial_code/features2D/feature_description/SURF_matching_Demo.cpp b/samples/cpp/tutorial_code/features2D/feature_description/SURF_matching_Demo.cpp new file mode 100755 index 0000000000..3fb34e9f1b --- /dev/null +++ b/samples/cpp/tutorial_code/features2D/feature_description/SURF_matching_Demo.cpp @@ -0,0 +1,60 @@ +#include +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +const char* keys = + "{ help h | | Print help message. }" + "{ input1 | ../data/box.png | Path to input image 1. }" + "{ input2 | ../data/box_in_scene.png | Path to input image 2. }"; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, keys ); + Mat img1 = imread( parser.get("input1"), IMREAD_GRAYSCALE ); + Mat img2 = imread( parser.get("input2"), IMREAD_GRAYSCALE ); + if ( img1.empty() || img2.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + parser.printMessage(); + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints1, keypoints2; + Mat descriptors1, descriptors2; + detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 ); + detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 ); + + //-- Step 2: Matching descriptor vectors with a brute force matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + Ptr matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE); + std::vector< DMatch > matches; + matcher->match( descriptors1, descriptors2, matches ); + + //-- Draw matches + Mat img_matches; + drawMatches( img1, keypoints1, img2, keypoints2, matches, img_matches ); + + //-- Show detected matches + imshow("Matches", img_matches ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp b/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp new file mode 100755 index 0000000000..ba9494ed2c --- /dev/null +++ b/samples/cpp/tutorial_code/features2D/feature_detection/SURF_detection_Demo.cpp @@ -0,0 +1,46 @@ +#include +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, "{@input | ../data/box.png | input image}" ); + Mat src = imread( parser.get( "@input" ), IMREAD_GRAYSCALE ); + if ( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints; + detector->detect( src, keypoints ); + + //-- Draw keypoints + Mat img_keypoints; + drawKeypoints( src, keypoints, img_keypoints ); + + //-- Show detected (drawn) keypoints + imshow("SURF Keypoints", img_keypoints ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp b/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp new file mode 100755 index 0000000000..e22155f471 --- /dev/null +++ b/samples/cpp/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.cpp @@ -0,0 +1,72 @@ +#include +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/highgui.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +const char* keys = + "{ help h | | Print help message. }" + "{ input1 | ../data/box.png | Path to input image 1. }" + "{ input2 | ../data/box_in_scene.png | Path to input image 2. }"; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, keys ); + Mat img1 = imread( parser.get("input1"), IMREAD_GRAYSCALE ); + Mat img2 = imread( parser.get("input2"), IMREAD_GRAYSCALE ); + if ( img1.empty() || img2.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + parser.printMessage(); + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints1, keypoints2; + Mat descriptors1, descriptors2; + detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 ); + detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 ); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + Ptr matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED); + std::vector< std::vector > knn_matches; + matcher->knnMatch( descriptors1, descriptors2, knn_matches, 2 ); + + //-- Filter matches using the Lowe's ratio test + const float ratio_thresh = 0.7f; + std::vector good_matches; + for (size_t i = 0; i < knn_matches.size(); i++) + { + if (knn_matches[i].size() > 1 && knn_matches[i][0].distance / knn_matches[i][1].distance <= ratio_thresh) + { + good_matches.push_back(knn_matches[i][0]); + } + } + + //-- Draw matches + Mat img_matches; + drawMatches( img1, keypoints1, img2, keypoints2, good_matches, img_matches, Scalar::all(-1), + Scalar::all(-1), std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); + + //-- Show detected matches + imshow("Good Matches", img_matches ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp b/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp new file mode 100755 index 0000000000..68b1d2a720 --- /dev/null +++ b/samples/cpp/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.cpp @@ -0,0 +1,107 @@ +#include +#include "opencv2/core.hpp" +#ifdef HAVE_OPENCV_XFEATURES2D +#include "opencv2/calib3d.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/xfeatures2d.hpp" + +using namespace cv; +using namespace cv::xfeatures2d; +using std::cout; +using std::endl; + +const char* keys = + "{ help h | | Print help message. }" + "{ input1 | ../data/box.png | Path to input image 1. }" + "{ input2 | ../data/box_in_scene.png | Path to input image 2. }"; + +int main( int argc, char* argv[] ) +{ + CommandLineParser parser( argc, argv, keys ); + Mat img_object = imread( parser.get("input1"), IMREAD_GRAYSCALE ); + Mat img_scene = imread( parser.get("input2"), IMREAD_GRAYSCALE ); + if ( img_object.empty() || img_scene.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + parser.printMessage(); + return -1; + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + int minHessian = 400; + Ptr detector = SURF::create( minHessian ); + std::vector keypoints_object, keypoints_scene; + Mat descriptors_object, descriptors_scene; + detector->detectAndCompute( img_object, noArray(), keypoints_object, descriptors_object ); + detector->detectAndCompute( img_scene, noArray(), keypoints_scene, descriptors_scene ); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + Ptr matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED); + std::vector< std::vector > knn_matches; + matcher->knnMatch( descriptors_object, descriptors_scene, knn_matches, 2 ); + + //-- Filter matches using the Lowe's ratio test + const float ratio_thresh = 0.75f; + std::vector good_matches; + for (size_t i = 0; i < knn_matches.size(); i++) + { + if (knn_matches[i].size() > 1 && knn_matches[i][0].distance / knn_matches[i][1].distance <= ratio_thresh) + { + good_matches.push_back(knn_matches[i][0]); + } + } + + //-- Draw matches + Mat img_matches; + drawMatches( img_object, keypoints_object, img_scene, keypoints_scene, good_matches, img_matches, Scalar::all(-1), + Scalar::all(-1), std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); + + //-- Localize the object + std::vector obj; + std::vector scene; + + for( size_t i = 0; i < good_matches.size(); i++ ) + { + //-- Get the keypoints from the good matches + obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt ); + scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt ); + } + + Mat H = findHomography( obj, scene, RANSAC ); + + //-- Get the corners from the image_1 ( the object to be "detected" ) + std::vector obj_corners(4); + obj_corners[0] = Point2f(0, 0); + obj_corners[1] = Point2f( (float)img_object.cols, 0 ); + obj_corners[2] = Point2f( (float)img_object.cols, (float)img_object.rows ); + obj_corners[3] = Point2f( 0, (float)img_object.rows ); + std::vector scene_corners(4); + + perspectiveTransform( obj_corners, scene_corners, H); + + //-- Draw lines between the corners (the mapped object in the scene - image_2 ) + line( img_matches, scene_corners[0] + Point2f((float)img_object.cols, 0), + scene_corners[1] + Point2f((float)img_object.cols, 0), Scalar(0, 255, 0), 4 ); + line( img_matches, scene_corners[1] + Point2f((float)img_object.cols, 0), + scene_corners[2] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + line( img_matches, scene_corners[2] + Point2f((float)img_object.cols, 0), + scene_corners[3] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + line( img_matches, scene_corners[3] + Point2f((float)img_object.cols, 0), + scene_corners[0] + Point2f((float)img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + + //-- Show detected matches + imshow("Good Matches & Object detection", img_matches ); + + waitKey(); + return 0; +} +#else +int main() +{ + std::cout << "This tutorial code needs the xfeatures2d contrib module to be run." << std::endl; + return 0; +} +#endif diff --git a/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesCircles.cpp b/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesCircles.cpp new file mode 100644 index 0000000000..289484dca3 --- /dev/null +++ b/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesCircles.cpp @@ -0,0 +1,33 @@ +#include +#include +#include + +using namespace cv; +using namespace std; + +int main(int argc, char** argv) +{ + Mat img, gray; + if( argc != 2 || !(img=imread(argv[1], 1)).data) + return -1; + cvtColor(img, gray, COLOR_BGR2GRAY); + // smooth it, otherwise a lot of false circles may be detected + GaussianBlur( gray, gray, Size(9, 9), 2, 2 ); + vector circles; + HoughCircles(gray, circles, HOUGH_GRADIENT, + 2, gray.rows/4, 200, 100 ); + for( size_t i = 0; i < circles.size(); i++ ) + { + Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); + int radius = cvRound(circles[i][2]); + // draw the circle center + circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 ); + // draw the circle outline + circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 ); + } + namedWindow( "circles", 1 ); + imshow( "circles", img ); + + waitKey(0); + return 0; +} diff --git a/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesP.cpp b/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesP.cpp new file mode 100644 index 0000000000..e19d29abbb --- /dev/null +++ b/samples/cpp/tutorial_code/snippets/imgproc_HoughLinesP.cpp @@ -0,0 +1,31 @@ +#include +#include + +using namespace cv; +using namespace std; + +int main(int argc, char** argv) +{ + Mat src, dst, color_dst; + if( argc != 2 || !(src=imread(argv[1], 0)).data) + return -1; + + Canny( src, dst, 50, 200, 3 ); + cvtColor( dst, color_dst, COLOR_GRAY2BGR ); + + vector lines; + HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 ); + for( size_t i = 0; i < lines.size(); i++ ) + { + line( color_dst, Point(lines[i][0], lines[i][1]), + Point( lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 ); + } + namedWindow( "Source", 1 ); + imshow( "Source", src ); + + namedWindow( "Detected Lines", 1 ); + imshow( "Detected Lines", color_dst ); + + waitKey(0); + return 0; +} diff --git a/samples/cpp/tutorial_code/snippets/imgproc_applyColorMap.cpp b/samples/cpp/tutorial_code/snippets/imgproc_applyColorMap.cpp new file mode 100644 index 0000000000..280beba6cc --- /dev/null +++ b/samples/cpp/tutorial_code/snippets/imgproc_applyColorMap.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +using namespace cv; + +#include +using namespace std; + +int main(int argc, const char *argv[]) +{ + // We need an input image. (can be grayscale or color) + if (argc < 2) + { + cerr << "We need an image to process here. Please run: colorMap [path_to_image]" << endl; + return -1; + } + Mat img_in = imread(argv[1]); + if(img_in.empty()) + { + cerr << "Sample image (" << argv[1] << ") is empty. Please adjust your path, so it points to a valid input image!" << endl; + return -1; + } + // Holds the colormap version of the image: + Mat img_color; + // Apply the colormap: + applyColorMap(img_in, img_color, COLORMAP_JET); + // Show the result: + imshow("colorMap", img_color); + waitKey(0); + return 0; +} diff --git a/samples/cpp/tutorial_code/snippets/imgproc_calcHist.cpp b/samples/cpp/tutorial_code/snippets/imgproc_calcHist.cpp new file mode 100644 index 0000000000..9d1ca46033 --- /dev/null +++ b/samples/cpp/tutorial_code/snippets/imgproc_calcHist.cpp @@ -0,0 +1,55 @@ +#include +#include + +using namespace cv; + +int main( int argc, char** argv ) +{ + Mat src, hsv; + if( argc != 2 || !(src=imread(argv[1], 1)).data ) + return -1; + + cvtColor(src, hsv, COLOR_BGR2HSV); + + // Quantize the hue to 30 levels + // and the saturation to 32 levels + int hbins = 30, sbins = 32; + int histSize[] = {hbins, sbins}; + // hue varies from 0 to 179, see cvtColor + float hranges[] = { 0, 180 }; + // saturation varies from 0 (black-gray-white) to + // 255 (pure spectrum color) + float sranges[] = { 0, 256 }; + const float* ranges[] = { hranges, sranges }; + MatND hist; + // we compute the histogram from the 0-th and 1-st channels + int channels[] = {0, 1}; + + calcHist( &hsv, 1, channels, Mat(), // do not use mask + hist, 2, histSize, ranges, + true, // the histogram is uniform + false ); + double maxVal=0; + minMaxLoc(hist, 0, &maxVal, 0, 0); + + int scale = 10; + Mat histImg = Mat::zeros(sbins*scale, hbins*10, CV_8UC3); + + for( int h = 0; h < hbins; h++ ) + for( int s = 0; s < sbins; s++ ) + { + float binVal = hist.at(h, s); + int intensity = cvRound(binVal*255/maxVal); + rectangle( histImg, Point(h*scale, s*scale), + Point( (h+1)*scale - 1, (s+1)*scale - 1), + Scalar::all(intensity), + -1 ); + } + + namedWindow( "Source", 1 ); + imshow( "Source", src ); + + namedWindow( "H-S Histogram", 1 ); + imshow( "H-S Histogram", histImg ); + waitKey(); +} diff --git a/samples/cpp/tutorial_code/snippets/imgproc_drawContours.cpp b/samples/cpp/tutorial_code/snippets/imgproc_drawContours.cpp new file mode 100644 index 0000000000..4dfcde668e --- /dev/null +++ b/samples/cpp/tutorial_code/snippets/imgproc_drawContours.cpp @@ -0,0 +1,39 @@ +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" + +using namespace cv; +using namespace std; + +int main( int argc, char** argv ) +{ + Mat src; + // the first command-line parameter must be a filename of the binary + // (black-n-white) image + if( argc != 2 || !(src=imread(argv[1], 0)).data) + return -1; + + Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3); + + src = src > 1; + namedWindow( "Source", 1 ); + imshow( "Source", src ); + + vector > contours; + vector hierarchy; + + findContours( src, contours, hierarchy, + RETR_CCOMP, CHAIN_APPROX_SIMPLE ); + + // iterate through all the top-level contours, + // draw each connected component with its own random color + int idx = 0; + for( ; idx >= 0; idx = hierarchy[idx][0] ) + { + Scalar color( rand()&255, rand()&255, rand()&255 ); + drawContours( dst, contours, idx, color, FILLED, 8, hierarchy ); + } + + namedWindow( "Components", 1 ); + imshow( "Components", dst ); + waitKey(0); +} diff --git a/samples/dnn/README.md b/samples/dnn/README.md index c438bb0910..9072ddb2a8 100644 --- a/samples/dnn/README.md +++ b/samples/dnn/README.md @@ -11,8 +11,10 @@ | [SSDs from TensorFlow](https://github.com/tensorflow/models/tree/master/research/object_detection/) | `0.00784 (2/255)` | `300x300` | `127.5 127.5 127.5` | RGB | | [YOLO](https://pjreddie.com/darknet/yolo/) | `0.00392 (1/255)` | `416x416` | `0 0 0` | RGB | | [VGG16-SSD](https://github.com/weiliu89/caffe/tree/ssd) | `1.0` | `300x300` | `104 117 123` | BGR | -| [Faster-RCNN](https://github.com/rbgirshick/py-faster-rcnn) | `1.0` | `800x600` | `102.9801, 115.9465, 122.7717` | BGR | +| [Faster-RCNN](https://github.com/rbgirshick/py-faster-rcnn) | `1.0` | `800x600` | `102.9801 115.9465 122.7717` | BGR | | [R-FCN](https://github.com/YuwenXiong/py-R-FCN) | `1.0` | `800x600` | `102.9801 115.9465 122.7717` | BGR | +| [Faster-RCNN, ResNet backbone](https://github.com/tensorflow/models/tree/master/research/object_detection/) | `1.0` | `300x300` | `103.939 116.779 123.68` | RGB | +| [Faster-RCNN, InceptionV2 backbone](https://github.com/tensorflow/models/tree/master/research/object_detection/) | `0.00784 (2/255)` | `300x300` | `127.5 127.5 127.5` | RGB | #### Face detection [An origin model](https://github.com/opencv/opencv/tree/master/samples/dnn/face_detector) diff --git a/samples/dnn/classification.cpp b/samples/dnn/classification.cpp index 9407326831..21e9520743 100644 --- a/samples/dnn/classification.cpp +++ b/samples/dnn/classification.cpp @@ -23,7 +23,7 @@ const char* keys = "{ backend | 0 | Choose one of computation backends: " "0: default C++ backend, " "1: Halide language (http://halide-lang.org/), " - "2: Intel's Deep Learning Inference Engine (https://software.seek.intel.com/deep-learning-deployment)}" + "2: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit)}" "{ target | 0 | Choose one of target computation devices: " "0: CPU target (by default)," "1: OpenCL }"; diff --git a/samples/dnn/classification.py b/samples/dnn/classification.py index 2628195929..637309fe25 100644 --- a/samples/dnn/classification.py +++ b/samples/dnn/classification.py @@ -34,7 +34,7 @@ parser.add_argument('--backend', choices=backends, default=cv.dnn.DNN_BACKEND_DE help="Choose one of computation backends: " "%d: default C++ backend, " "%d: Halide language (http://halide-lang.org/), " - "%d: Intel's Deep Learning Inference Engine (https://software.seek.intel.com/deep-learning-deployment)" % backends) + "%d: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit)" % backends) parser.add_argument('--target', choices=targets, default=cv.dnn.DNN_TARGET_CPU, type=int, help='Choose one of target computation devices: ' '%d: CPU target (by default), ' diff --git a/samples/dnn/face_detector/how_to_train_face_detector.txt b/samples/dnn/face_detector/how_to_train_face_detector.txt index 9c170fadd6..11602297f2 100644 --- a/samples/dnn/face_detector/how_to_train_face_detector.txt +++ b/samples/dnn/face_detector/how_to_train_face_detector.txt @@ -13,7 +13,7 @@ The data preparation pipeline can be represented as: a) Find some datasets with face bounding boxes annotation. For some reasons I can't provide links here, but you easily find them on your own. Also study the data. It may contain small or low quality faces which can spoil training process. Often there are special flags about object quality in annotation. Remove such faces from annotation (smaller when 16 along at least one side, or blurred, of highly-occluded, or something else). b) The downloaded dataset will have some format of annotation. It may be one single file for all images, or separate file for each image or something else. But to train SSD in Caffe you need to convert annotation to PASCAL VOC format. -PASCAL VOC annoitation consist of .xml file for each image. In this xml file all face bounding boxes should be listed as: +PASCAL VOC annotation consist of .xml file for each image. In this xml file all face bounding boxes should be listed as: @@ -42,7 +42,7 @@ PASCAL VOC annoitation consist of .xml file for each image. In this xml file all -So, convert your dataset's annotation to the fourmat above. +So, convert your dataset's annotation to the format above. Also, you should create labelmap.prototxt file with the following content: item { name: "none_of_the_above" @@ -76,4 +76,4 @@ mkdir -p log /path_for_caffe_build_dir/tools/caffe train -solver="solver.prototxt" -gpu 0 2>&1 | tee -a log/log.log And wait. It will take about 8 hours to finish the process. -After it you can use your .caffemodel from snapshot/ subdirectory in resnet_face_ssd_python.py sample. \ No newline at end of file +After it you can use your .caffemodel from snapshot/ subdirectory in resnet_face_ssd_python.py sample. diff --git a/samples/dnn/object_detection.cpp b/samples/dnn/object_detection.cpp index 1298d7e39e..f2b761b387 100644 --- a/samples/dnn/object_detection.cpp +++ b/samples/dnn/object_detection.cpp @@ -25,7 +25,7 @@ const char* keys = "{ backend | 0 | Choose one of computation backends: " "0: default C++ backend, " "1: Halide language (http://halide-lang.org/), " - "2: Intel's Deep Learning Inference Engine (https://software.seek.intel.com/deep-learning-deployment)}" + "2: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit)}" "{ target | 0 | Choose one of target computation devices: " "0: CPU target (by default)," "1: OpenCL }"; diff --git a/samples/dnn/object_detection.py b/samples/dnn/object_detection.py index 01386f2363..2cfb6d2106 100644 --- a/samples/dnn/object_detection.py +++ b/samples/dnn/object_detection.py @@ -35,7 +35,7 @@ parser.add_argument('--backend', choices=backends, default=cv.dnn.DNN_BACKEND_DE help="Choose one of computation backends: " "%d: default C++ backend, " "%d: Halide language (http://halide-lang.org/), " - "%d: Intel's Deep Learning Inference Engine (https://software.seek.intel.com/deep-learning-deployment)" % backends) + "%d: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit)" % backends) parser.add_argument('--target', choices=targets, default=cv.dnn.DNN_TARGET_CPU, type=int, help='Choose one of target computation devices: ' '%d: CPU target (by default), ' @@ -174,7 +174,7 @@ while cv.waitKey(1) < 0: net.setInput(blob) if net.getLayer(0).outputNameToIndex('im_info') != -1: # Faster-RCNN or R-FCN frame = cv.resize(frame, (inpWidth, inpHeight)) - net.setInput(np.array([inpHeight, inpWidth, 1.6], dtype=np.float32), 'im_info'); + net.setInput(np.array([inpHeight, inpWidth, 1.6], dtype=np.float32), 'im_info') outs = net.forward(getOutputsNames(net)) postprocess(frame, outs) diff --git a/samples/dnn/segmentation.cpp b/samples/dnn/segmentation.cpp index 252140a275..920e325b83 100644 --- a/samples/dnn/segmentation.cpp +++ b/samples/dnn/segmentation.cpp @@ -26,7 +26,7 @@ const char* keys = "{ backend | 0 | Choose one of computation backends: " "0: default C++ backend, " "1: Halide language (http://halide-lang.org/), " - "2: Intel's Deep Learning Inference Engine (https://software.seek.intel.com/deep-learning-deployment)}" + "2: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit)}" "{ target | 0 | Choose one of target computation devices: " "0: CPU target (by default)," "1: OpenCL }"; diff --git a/samples/dnn/segmentation.py b/samples/dnn/segmentation.py index 1a3c5b4553..3649bbbe22 100644 --- a/samples/dnn/segmentation.py +++ b/samples/dnn/segmentation.py @@ -36,7 +36,7 @@ parser.add_argument('--backend', choices=backends, default=cv.dnn.DNN_BACKEND_DE help="Choose one of computation backends: " "%d: default C++ backend, " "%d: Halide language (http://halide-lang.org/), " - "%d: Intel's Deep Learning Inference Engine (https://software.seek.intel.com/deep-learning-deployment)" % backends) + "%d: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit)" % backends) parser.add_argument('--target', choices=targets, default=cv.dnn.DNN_TARGET_CPU, type=int, help='Choose one of target computation devices: ' '%d: CPU target (by default), ' diff --git a/samples/dnn/tf_text_graph_faster_rcnn.py b/samples/dnn/tf_text_graph_faster_rcnn.py new file mode 100644 index 0000000000..7ad5de283a --- /dev/null +++ b/samples/dnn/tf_text_graph_faster_rcnn.py @@ -0,0 +1,291 @@ +import argparse +import numpy as np +import tensorflow as tf + +from tensorflow.core.framework.node_def_pb2 import NodeDef +from tensorflow.tools.graph_transforms import TransformGraph +from google.protobuf import text_format + +parser = argparse.ArgumentParser(description='Run this script to get a text graph of ' + 'SSD model from TensorFlow Object Detection API. ' + 'Then pass it with .pb file to cv::dnn::readNetFromTensorflow function.') +parser.add_argument('--input', required=True, help='Path to frozen TensorFlow graph.') +parser.add_argument('--output', required=True, help='Path to output text graph.') +parser.add_argument('--num_classes', default=90, type=int, help='Number of trained classes.') +parser.add_argument('--scales', default=[0.25, 0.5, 1.0, 2.0], type=float, nargs='+', + help='Hyper-parameter of grid_anchor_generator from a config file.') +parser.add_argument('--aspect_ratios', default=[0.5, 1.0, 2.0], type=float, nargs='+', + help='Hyper-parameter of grid_anchor_generator from a config file.') +parser.add_argument('--features_stride', default=16, type=float, nargs='+', + help='Hyper-parameter from a config file.') +args = parser.parse_args() + +scopesToKeep = ('FirstStageFeatureExtractor', 'Conv', + 'FirstStageBoxPredictor/BoxEncodingPredictor', + 'FirstStageBoxPredictor/ClassPredictor', + 'CropAndResize', + 'MaxPool2D', + 'SecondStageFeatureExtractor', + 'SecondStageBoxPredictor', + 'image_tensor') + +scopesToIgnore = ('FirstStageFeatureExtractor/Assert', + 'FirstStageFeatureExtractor/Shape', + 'FirstStageFeatureExtractor/strided_slice', + 'FirstStageFeatureExtractor/GreaterEqual', + 'FirstStageFeatureExtractor/LogicalAnd') + +unusedAttrs = ['T', 'Tshape', 'N', 'Tidx', 'Tdim', 'use_cudnn_on_gpu', + 'Index', 'Tperm', 'is_training', 'Tpaddings'] + +# Read the graph. +with tf.gfile.FastGFile(args.input, 'rb') as f: + graph_def = tf.GraphDef() + graph_def.ParseFromString(f.read()) + +# Removes Identity nodes +def removeIdentity(): + identities = {} + for node in graph_def.node: + if node.op == 'Identity': + identities[node.name] = node.input[0] + graph_def.node.remove(node) + + for node in graph_def.node: + for i in range(len(node.input)): + if node.input[i] in identities: + node.input[i] = identities[node.input[i]] + +removeIdentity() + +removedNodes = [] + +for i in reversed(range(len(graph_def.node))): + op = graph_def.node[i].op + name = graph_def.node[i].name + + if op == 'Const' or name.startswith(scopesToIgnore) or not name.startswith(scopesToKeep): + if op != 'Const': + removedNodes.append(name) + + del graph_def.node[i] + else: + for attr in unusedAttrs: + if attr in graph_def.node[i].attr: + del graph_def.node[i].attr[attr] + +# Remove references to removed nodes except Const nodes. +for node in graph_def.node: + for i in reversed(range(len(node.input))): + if node.input[i] in removedNodes: + del node.input[i] + + +# Connect input node to the first layer +assert(graph_def.node[0].op == 'Placeholder') +graph_def.node[1].input.insert(0, graph_def.node[0].name) + +# Temporarily remove top nodes. +topNodes = [] +while True: + node = graph_def.node.pop() + topNodes.append(node) + if node.op == 'CropAndResize': + break + +def tensorMsg(values): + if all([isinstance(v, float) for v in values]): + dtype = 'DT_FLOAT' + field = 'float_val' + elif all([isinstance(v, int) for v in values]): + dtype = 'DT_INT32' + field = 'int_val' + else: + raise Exception('Wrong values types') + + msg = 'tensor { dtype: ' + dtype + ' tensor_shape { dim { size: %d } }' % len(values) + for value in values: + msg += '%s: %s ' % (field, str(value)) + return msg + '}' + +def addSlice(inp, out, begins, sizes): + beginsNode = NodeDef() + beginsNode.name = out + '/begins' + beginsNode.op = 'Const' + text_format.Merge(tensorMsg(begins), beginsNode.attr["value"]) + graph_def.node.extend([beginsNode]) + + sizesNode = NodeDef() + sizesNode.name = out + '/sizes' + sizesNode.op = 'Const' + text_format.Merge(tensorMsg(sizes), sizesNode.attr["value"]) + graph_def.node.extend([sizesNode]) + + sliced = NodeDef() + sliced.name = out + sliced.op = 'Slice' + sliced.input.append(inp) + sliced.input.append(beginsNode.name) + sliced.input.append(sizesNode.name) + graph_def.node.extend([sliced]) + +def addReshape(inp, out, shape): + shapeNode = NodeDef() + shapeNode.name = out + '/shape' + shapeNode.op = 'Const' + text_format.Merge(tensorMsg(shape), shapeNode.attr["value"]) + graph_def.node.extend([shapeNode]) + + reshape = NodeDef() + reshape.name = out + reshape.op = 'Reshape' + reshape.input.append(inp) + reshape.input.append(shapeNode.name) + graph_def.node.extend([reshape]) + +def addSoftMax(inp, out): + softmax = NodeDef() + softmax.name = out + softmax.op = 'Softmax' + text_format.Merge('i: -1', softmax.attr['axis']) + softmax.input.append(inp) + graph_def.node.extend([softmax]) + +addReshape('FirstStageBoxPredictor/ClassPredictor/BiasAdd', + 'FirstStageBoxPredictor/ClassPredictor/reshape_1', [0, -1, 2]) + +addSoftMax('FirstStageBoxPredictor/ClassPredictor/reshape_1', + 'FirstStageBoxPredictor/ClassPredictor/softmax') # Compare with Reshape_4 + +flatten = NodeDef() +flatten.name = 'FirstStageBoxPredictor/BoxEncodingPredictor/flatten' # Compare with FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd +flatten.op = 'Flatten' +flatten.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd') +graph_def.node.extend([flatten]) + +proposals = NodeDef() +proposals.name = 'proposals' # Compare with ClipToWindow/Gather/Gather (NOTE: normalized) +proposals.op = 'PriorBox' +proposals.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/BiasAdd') +proposals.input.append(graph_def.node[0].name) # image_tensor + +text_format.Merge('b: false', proposals.attr["flip"]) +text_format.Merge('b: true', proposals.attr["clip"]) +text_format.Merge('f: %f' % args.features_stride, proposals.attr["step"]) +text_format.Merge('f: 0.0', proposals.attr["offset"]) +text_format.Merge(tensorMsg([0.1, 0.1, 0.2, 0.2]), proposals.attr["variance"]) + +widths = [] +heights = [] +for a in args.aspect_ratios: + for s in args.scales: + ar = np.sqrt(a) + heights.append((args.features_stride**2) * s / ar) + widths.append((args.features_stride**2) * s * ar) + +text_format.Merge(tensorMsg(widths), proposals.attr["width"]) +text_format.Merge(tensorMsg(heights), proposals.attr["height"]) + +graph_def.node.extend([proposals]) + +# Compare with Reshape_5 +detectionOut = NodeDef() +detectionOut.name = 'detection_out' +detectionOut.op = 'DetectionOutput' + +detectionOut.input.append('FirstStageBoxPredictor/BoxEncodingPredictor/flatten') +detectionOut.input.append('FirstStageBoxPredictor/ClassPredictor/softmax') +detectionOut.input.append('proposals') + +text_format.Merge('i: 2', detectionOut.attr['num_classes']) +text_format.Merge('b: true', detectionOut.attr['share_location']) +text_format.Merge('i: 0', detectionOut.attr['background_label_id']) +text_format.Merge('f: 0.7', detectionOut.attr['nms_threshold']) +text_format.Merge('i: 6000', detectionOut.attr['top_k']) +text_format.Merge('s: "CENTER_SIZE"', detectionOut.attr['code_type']) +text_format.Merge('i: 100', detectionOut.attr['keep_top_k']) +text_format.Merge('b: true', detectionOut.attr['clip']) +text_format.Merge('b: true', detectionOut.attr['loc_pred_transposed']) + +graph_def.node.extend([detectionOut]) + +# Save as text. +for node in reversed(topNodes): + graph_def.node.extend([node]) + +addSoftMax('SecondStageBoxPredictor/Reshape_1', 'SecondStageBoxPredictor/Reshape_1/softmax') + +addSlice('SecondStageBoxPredictor/Reshape_1/softmax', + 'SecondStageBoxPredictor/Reshape_1/slice', + [0, 0, 1], [-1, -1, -1]) + +addReshape('SecondStageBoxPredictor/Reshape_1/slice', + 'SecondStageBoxPredictor/Reshape_1/Reshape', [1, -1]) + +# Replace Flatten subgraph onto a single node. +for i in reversed(range(len(graph_def.node))): + if graph_def.node[i].op == 'CropAndResize': + graph_def.node[i].input.insert(1, 'detection_out') + + if graph_def.node[i].name == 'SecondStageBoxPredictor/Reshape': + shapeNode = NodeDef() + shapeNode.name = 'SecondStageBoxPredictor/Reshape/shape2' + shapeNode.op = 'Const' + text_format.Merge(tensorMsg([1, -1, 4]), shapeNode.attr["value"]) + graph_def.node.extend([shapeNode]) + + graph_def.node[i].input.pop() + graph_def.node[i].input.append(shapeNode.name) + + if graph_def.node[i].name in ['SecondStageBoxPredictor/Flatten/flatten/Shape', + 'SecondStageBoxPredictor/Flatten/flatten/strided_slice', + 'SecondStageBoxPredictor/Flatten/flatten/Reshape/shape']: + del graph_def.node[i] + +for node in graph_def.node: + if node.name == 'SecondStageBoxPredictor/Flatten/flatten/Reshape': + node.op = 'Flatten' + node.input.pop() + break + +################################################################################ +### Postprocessing +################################################################################ +addSlice('detection_out', 'detection_out/slice', [0, 0, 0, 3], [-1, -1, -1, 4]) + +variance = NodeDef() +variance.name = 'proposals/variance' +variance.op = 'Const' +text_format.Merge(tensorMsg([0.1, 0.1, 0.2, 0.2]), variance.attr["value"]) +graph_def.node.extend([variance]) + +varianceEncoder = NodeDef() +varianceEncoder.name = 'variance_encoded' +varianceEncoder.op = 'Mul' +varianceEncoder.input.append('SecondStageBoxPredictor/Reshape') +varianceEncoder.input.append(variance.name) +text_format.Merge('i: 2', varianceEncoder.attr["axis"]) +graph_def.node.extend([varianceEncoder]) + +addReshape('detection_out/slice', 'detection_out/slice/reshape', [1, 1, -1]) + +detectionOut = NodeDef() +detectionOut.name = 'detection_out_final' +detectionOut.op = 'DetectionOutput' + +detectionOut.input.append('variance_encoded') +detectionOut.input.append('SecondStageBoxPredictor/Reshape_1/Reshape') +detectionOut.input.append('detection_out/slice/reshape') + +text_format.Merge('i: %d' % args.num_classes, detectionOut.attr['num_classes']) +text_format.Merge('b: false', detectionOut.attr['share_location']) +text_format.Merge('i: %d' % (args.num_classes + 1), detectionOut.attr['background_label_id']) +text_format.Merge('f: 0.6', detectionOut.attr['nms_threshold']) +text_format.Merge('s: "CENTER_SIZE"', detectionOut.attr['code_type']) +text_format.Merge('i: 100', detectionOut.attr['keep_top_k']) +text_format.Merge('b: true', detectionOut.attr['loc_pred_transposed']) +text_format.Merge('b: true', detectionOut.attr['clip']) +text_format.Merge('b: true', detectionOut.attr['variance_encoded_in_target']) +graph_def.node.extend([detectionOut]) + +tf.train.write_graph(graph_def, "", args.output, as_text=True) diff --git a/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java b/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java new file mode 100644 index 0000000000..3be2e58056 --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/corner_subpixels/CornerSubPixDemo.java @@ -0,0 +1,158 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; +import java.util.Random; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.core.TermCriteria; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class CornerSubPix { + private Mat src = new Mat(); + private Mat srcGray = new Mat(); + private JFrame frame; + private JLabel imgLabel; + private static final int MAX_CORNERS = 25; + private int maxCorners = 10; + private Random rng = new Random(12345); + + public CornerSubPix(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/pic3.png"; + src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Shi-Tomasi corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Max corners:")); + JSlider slider = new JSlider(0, MAX_CORNERS, maxCorners); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + maxCorners = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + imgLabel = new JLabel(new ImageIcon(img)); + pane.add(imgLabel, BorderLayout.CENTER); + } + + private void update() { + /// Parameters for Shi-Tomasi algorithm + maxCorners = Math.max(maxCorners, 1); + MatOfPoint corners = new MatOfPoint(); + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + boolean useHarrisDetector = false; + double k = 0.04; + + /// Copy the source image + Mat copy = src.clone(); + + /// Apply corner detection + Imgproc.goodFeaturesToTrack(srcGray, corners, maxCorners, qualityLevel, minDistance, new Mat(), + blockSize, gradientSize, useHarrisDetector, k); + + /// Draw corners detected + System.out.println("** Number of corners detected: " + corners.rows()); + int[] cornersData = new int[(int) (corners.total() * corners.channels())]; + corners.get(0, 0, cornersData); + int radius = 4; + Mat matCorners = new Mat(corners.rows(), 2, CvType.CV_32F); + float[] matCornersData = new float[(int) (matCorners.total() * matCorners.channels())]; + matCorners.get(0, 0, matCornersData); + for (int i = 0; i < corners.rows(); i++) { + Imgproc.circle(copy, new Point(cornersData[i * 2], cornersData[i * 2 + 1]), radius, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + matCornersData[i * 2] = cornersData[i * 2]; + matCornersData[i * 2 + 1] = cornersData[i * 2 + 1]; + } + matCorners.put(0, 0, matCornersData); + + imgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(copy))); + frame.repaint(); + + /// Set the needed parameters to find the refined corners + Size winSize = new Size(5, 5); + Size zeroZone = new Size(-1, -1); + TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.COUNT, 40, 0.001); + + /// Calculate the refined corner locations + Imgproc.cornerSubPix(srcGray, matCorners, winSize, zeroZone, criteria); + + /// Write them down + matCorners.get(0, 0, matCornersData); + for (int i = 0; i < corners.rows(); i++) { + System.out.println( + " -- Refined Corner [" + i + "] (" + matCornersData[i * 2] + "," + matCornersData[i * 2 + 1] + ")"); + } + } +} + +public class CornerSubPixDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new CornerSubPix(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java b/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java new file mode 100644 index 0000000000..30450f8db1 --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/generic_corner_detector/CornerDetectorDemo.java @@ -0,0 +1,190 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; +import java.util.Random; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.Core.MinMaxLocResult; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class CornerDetector { + private Mat src = new Mat(); + private Mat srcGray = new Mat(); + private Mat harrisDst = new Mat(); + private Mat shiTomasiDst = new Mat(); + private Mat harrisCopy = new Mat(); + private Mat shiTomasiCopy = new Mat(); + private Mat Mc = new Mat(); + private JFrame frame; + private JLabel harrisImgLabel; + private JLabel shiTomasiImgLabel; + private static final int MAX_QUALITY_LEVEL = 100; + private int qualityLevel = 50; + private double harrisMinVal; + private double harrisMaxVal; + private double shiTomasiMinVal; + private double shiTomasiMaxVal; + private Random rng = new Random(12345); + + public CornerDetector(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/building.jpg"; + src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Creating your own corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + + /// Set some parameters + int blockSize = 3, apertureSize = 3; + + /// My Harris matrix -- Using cornerEigenValsAndVecs + Imgproc.cornerEigenValsAndVecs(srcGray, harrisDst, blockSize, apertureSize); + + /* calculate Mc */ + Mc = Mat.zeros(srcGray.size(), CvType.CV_32F); + + float[] harrisData = new float[(int) (harrisDst.total() * harrisDst.channels())]; + harrisDst.get(0, 0, harrisData); + float[] McData = new float[(int) (Mc.total() * Mc.channels())]; + Mc.get(0, 0, McData); + + for( int i = 0; i < srcGray.rows(); i++ ) { + for( int j = 0; j < srcGray.cols(); j++ ) { + float lambda1 = harrisData[(i*srcGray.cols() + j) * 6]; + float lambda2 = harrisData[(i*srcGray.cols() + j) * 6 + 1]; + McData[i*srcGray.cols()+j] = (float) (lambda1*lambda2 - 0.04f*Math.pow( ( lambda1 + lambda2 ), 2 )); + } + } + Mc.put(0, 0, McData); + + MinMaxLocResult res = Core.minMaxLoc(Mc); + harrisMinVal = res.minVal; + harrisMaxVal = res.maxVal; + + /// My Shi-Tomasi -- Using cornerMinEigenVal + Imgproc.cornerMinEigenVal(srcGray, shiTomasiDst, blockSize, apertureSize); + res = Core.minMaxLoc(shiTomasiDst); + shiTomasiMinVal = res.minVal; + shiTomasiMaxVal = res.maxVal; + + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Max corners:")); + JSlider slider = new JSlider(0, MAX_QUALITY_LEVEL, qualityLevel); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + qualityLevel = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + JPanel imgPanel = new JPanel(); + harrisImgLabel = new JLabel(new ImageIcon(img)); + shiTomasiImgLabel = new JLabel(new ImageIcon(img)); + imgPanel.add(harrisImgLabel); + imgPanel.add(shiTomasiImgLabel); + pane.add(imgPanel, BorderLayout.CENTER); + } + + private void update() { + int qualityLevelVal = Math.max(qualityLevel, 1); + + //Harris + harrisCopy = src.clone(); + + float[] McData = new float[(int) (Mc.total() * Mc.channels())]; + Mc.get(0, 0, McData); + for (int i = 0; i < srcGray.rows(); i++) { + for (int j = 0; j < srcGray.cols(); j++) { + if (McData[i * srcGray.cols() + j] > harrisMinVal + + (harrisMaxVal - harrisMinVal) * qualityLevelVal / MAX_QUALITY_LEVEL) { + Imgproc.circle(harrisCopy, new Point(j, i), 4, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + } + } + } + + //Shi-Tomasi + shiTomasiCopy = src.clone(); + + float[] shiTomasiData = new float[(int) (shiTomasiDst.total() * shiTomasiDst.channels())]; + shiTomasiDst.get(0, 0, shiTomasiData); + for (int i = 0; i < srcGray.rows(); i++) { + for (int j = 0; j < srcGray.cols(); j++) { + if (shiTomasiData[i * srcGray.cols() + j] > shiTomasiMinVal + + (shiTomasiMaxVal - shiTomasiMinVal) * qualityLevelVal / MAX_QUALITY_LEVEL) { + Imgproc.circle(shiTomasiCopy, new Point(j, i), 4, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + } + } + } + + harrisImgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(harrisCopy))); + shiTomasiImgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(shiTomasiCopy))); + frame.repaint(); + } +} + +public class CornerDetectorDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new CornerDetector(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java b/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java new file mode 100644 index 0000000000..b5ee732e84 --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/good_features_to_track/GoodFeaturesToTrackDemo.java @@ -0,0 +1,134 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; +import java.util.Random; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class GoodFeaturesToTrack { + private Mat src = new Mat(); + private Mat srcGray = new Mat(); + private JFrame frame; + private JLabel imgLabel; + private static final int MAX_THRESHOLD = 100; + private int maxCorners = 23; + private Random rng = new Random(12345); + + public GoodFeaturesToTrack(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/pic3.png"; + src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Shi-Tomasi corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Max corners:")); + JSlider slider = new JSlider(0, MAX_THRESHOLD, maxCorners); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + maxCorners = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + imgLabel = new JLabel(new ImageIcon(img)); + pane.add(imgLabel, BorderLayout.CENTER); + } + + private void update() { + /// Parameters for Shi-Tomasi algorithm + maxCorners = Math.max(maxCorners, 1); + MatOfPoint corners = new MatOfPoint(); + double qualityLevel = 0.01; + double minDistance = 10; + int blockSize = 3, gradientSize = 3; + boolean useHarrisDetector = false; + double k = 0.04; + + /// Copy the source image + Mat copy = src.clone(); + + /// Apply corner detection + Imgproc.goodFeaturesToTrack(srcGray, corners, maxCorners, qualityLevel, minDistance, new Mat(), + blockSize, gradientSize, useHarrisDetector, k); + + /// Draw corners detected + System.out.println("** Number of corners detected: " + corners.rows()); + int[] cornersData = new int[(int) (corners.total() * corners.channels())]; + corners.get(0, 0, cornersData); + int radius = 4; + for (int i = 0; i < corners.rows(); i++) { + Imgproc.circle(copy, new Point(cornersData[i * 2], cornersData[i * 2 + 1]), radius, + new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Core.FILLED); + } + + imgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(copy))); + frame.repaint(); + } +} + +public class GoodFeaturesToTrackDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new GoodFeaturesToTrack(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java b/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java new file mode 100644 index 0000000000..b3c759d28f --- /dev/null +++ b/samples/java/tutorial_code/TrackingMotion/harris_detector/CornerHarrisDemo.java @@ -0,0 +1,142 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class CornerHarris { + private Mat srcGray = new Mat(); + private Mat dst = new Mat(); + private Mat dstNorm = new Mat(); + private Mat dstNormScaled = new Mat(); + private JFrame frame; + private JLabel imgLabel; + private JLabel cornerLabel; + private static final int MAX_THRESHOLD = 255; + private int threshold = 200; + + public CornerHarris(String[] args) { + /// Load source image and convert it to gray + String filename = args.length > 0 ? args[0] : "../data/building.jpg"; + Mat src = Imgcodecs.imread(filename); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + + // Create and set up the window. + frame = new JFrame("Harris corner detector demo"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + update(); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Threshold: ")); + JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold); + slider.setMajorTickSpacing(20); + slider.setMinorTickSpacing(10); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + threshold = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + pane.add(sliderPanel, BorderLayout.PAGE_START); + + JPanel imgPanel = new JPanel(); + imgLabel = new JLabel(new ImageIcon(img)); + imgPanel.add(imgLabel); + + Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U); + cornerLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg))); + imgPanel.add(cornerLabel); + + pane.add(imgPanel, BorderLayout.CENTER); + } + + private void update() { + dst = Mat.zeros(srcGray.size(), CvType.CV_32F); + + /// Detector parameters + int blockSize = 2; + int apertureSize = 3; + double k = 0.04; + + /// Detecting corners + Imgproc.cornerHarris(srcGray, dst, blockSize, apertureSize, k); + + /// Normalizing + Core.normalize(dst, dstNorm, 0, 255, Core.NORM_MINMAX); + Core.convertScaleAbs(dstNorm, dstNormScaled); + + /// Drawing a circle around corners + float[] dstNormData = new float[(int) (dstNorm.total() * dstNorm.channels())]; + dstNorm.get(0, 0, dstNormData); + + for (int i = 0; i < dstNorm.rows(); i++) { + for (int j = 0; j < dstNorm.cols(); j++) { + if ((int) dstNormData[i * dstNorm.cols() + j] > threshold) { + Imgproc.circle(dstNormScaled, new Point(j, i), 5, new Scalar(0), 2, 8, 0); + } + } + } + + cornerLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(dstNormScaled))); + frame.repaint(); + } +} + +public class CornerHarrisDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new CornerHarris(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java b/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java new file mode 100644 index 0000000000..ac64417d93 --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_description/SURFMatchingDemo.java @@ -0,0 +1,56 @@ +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfDMatch; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.features2d.DescriptorMatcher; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.xfeatures2d.SURF; + +class SURFMatching { + public void run(String[] args) { + String filename1 = args.length > 1 ? args[0] : "../data/box.png"; + String filename2 = args.length > 1 ? args[1] : "../data/box_in_scene.png"; + Mat img1 = Imgcodecs.imread(filename1, Imgcodecs.IMREAD_GRAYSCALE); + Mat img2 = Imgcodecs.imread(filename2, Imgcodecs.IMREAD_GRAYSCALE); + if (img1.empty() || img2.empty()) { + System.err.println("Cannot read images!"); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(), keypoints2 = new MatOfKeyPoint(); + Mat descriptors1 = new Mat(), descriptors2 = new Mat(); + detector.detectAndCompute(img1, new Mat(), keypoints1, descriptors1); + detector.detectAndCompute(img2, new Mat(), keypoints2, descriptors2); + + //-- Step 2: Matching descriptor vectors with a brute force matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE); + MatOfDMatch matches = new MatOfDMatch(); + matcher.match(descriptors1, descriptors2, matches); + + //-- Draw matches + Mat imgMatches = new Mat(); + Features2d.drawMatches(img1, keypoints1, img2, keypoints2, matches, imgMatches); + + HighGui.imshow("Matches", imgMatches); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFMatchingDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFMatching().run(args); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java b/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java new file mode 100644 index 0000000000..c78a0c66bd --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_detection/SURFDetectionDemo.java @@ -0,0 +1,44 @@ +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.xfeatures2d.SURF; + +class SURFDetection { + public void run(String[] args) { + String filename = args.length > 0 ? args[0] : "../data/box.png"; + Mat src = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypoints = new MatOfKeyPoint(); + detector.detect(src, keypoints); + + //-- Draw keypoints + Features2d.drawKeypoints(src, keypoints, src); + + //-- Show detected (drawn) keypoints + HighGui.imshow("SURF Keypoints", src); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFDetectionDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFDetection().run(args); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java b/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java new file mode 100644 index 0000000000..e02af9cadb --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_flann_matcher/SURFFLANNMatchingDemo.java @@ -0,0 +1,78 @@ +import java.util.ArrayList; +import java.util.List; + +import org.opencv.core.Core; +import org.opencv.core.DMatch; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfDMatch; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.Scalar; +import org.opencv.features2d.DescriptorMatcher; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.xfeatures2d.SURF; + +class SURFFLANNMatching { + public void run(String[] args) { + String filename1 = args.length > 1 ? args[0] : "../data/box.png"; + String filename2 = args.length > 1 ? args[1] : "../data/box_in_scene.png"; + Mat img1 = Imgcodecs.imread(filename1, Imgcodecs.IMREAD_GRAYSCALE); + Mat img2 = Imgcodecs.imread(filename2, Imgcodecs.IMREAD_GRAYSCALE); + if (img1.empty() || img2.empty()) { + System.err.println("Cannot read images!"); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypoints1 = new MatOfKeyPoint(), keypoints2 = new MatOfKeyPoint(); + Mat descriptors1 = new Mat(), descriptors2 = new Mat(); + detector.detectAndCompute(img1, new Mat(), keypoints1, descriptors1); + detector.detectAndCompute(img2, new Mat(), keypoints2, descriptors2); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED); + List knnMatches = new ArrayList<>(); + matcher.knnMatch(descriptors1, descriptors2, knnMatches, 2); + + //-- Filter matches using the Lowe's ratio test + float ratio_thresh = 0.7f; + List listOfGoodMatches = new ArrayList<>(); + for (int i = 0; i < knnMatches.size(); i++) { + if (knnMatches.get(i).rows() > 1) { + DMatch[] matches = knnMatches.get(i).toArray(); + if (matches[0].distance / matches[1].distance <= ratio_thresh) { + listOfGoodMatches.add(matches[0]); + } + } + } + MatOfDMatch goodMatches = new MatOfDMatch(); + goodMatches.fromList(listOfGoodMatches); + + //-- Draw matches + Mat imgMatches = new Mat(); + Features2d.drawMatches(img1, keypoints1, img2, keypoints2, goodMatches, imgMatches, Scalar.all(-1), + Scalar.all(-1), new MatOfByte(), Features2d.NOT_DRAW_SINGLE_POINTS); + + //-- Show detected matches + HighGui.imshow("Good Matches", imgMatches); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFFLANNMatchingDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFFLANNMatching().run(args); + } +} diff --git a/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java b/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java new file mode 100644 index 0000000000..1a5cbe7f30 --- /dev/null +++ b/samples/java/tutorial_code/features2D/feature_homography/SURFFLANNMatchingHomographyDemo.java @@ -0,0 +1,130 @@ +import java.util.ArrayList; +import java.util.List; + +import org.opencv.calib3d.Calib3d; +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.DMatch; +import org.opencv.core.KeyPoint; +import org.opencv.core.Mat; +import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfDMatch; +import org.opencv.core.MatOfKeyPoint; +import org.opencv.core.MatOfPoint2f; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.features2d.DescriptorMatcher; +import org.opencv.features2d.Features2d; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; +import org.opencv.xfeatures2d.SURF; + +class SURFFLANNMatchingHomography { + public void run(String[] args) { + String filenameObject = args.length > 1 ? args[0] : "../data/box.png"; + String filenameScene = args.length > 1 ? args[1] : "../data/box_in_scene.png"; + Mat imgObject = Imgcodecs.imread(filenameObject, Imgcodecs.IMREAD_GRAYSCALE); + Mat imgScene = Imgcodecs.imread(filenameScene, Imgcodecs.IMREAD_GRAYSCALE); + if (imgObject.empty() || imgScene.empty()) { + System.err.println("Cannot read images!"); + System.exit(0); + } + + //-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors + double hessianThreshold = 400; + int nOctaves = 4, nOctaveLayers = 3; + boolean extended = false, upright = false; + SURF detector = SURF.create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright); + MatOfKeyPoint keypointsObject = new MatOfKeyPoint(), keypointsScene = new MatOfKeyPoint(); + Mat descriptorsObject = new Mat(), descriptorsScene = new Mat(); + detector.detectAndCompute(imgObject, new Mat(), keypointsObject, descriptorsObject); + detector.detectAndCompute(imgScene, new Mat(), keypointsScene, descriptorsScene); + + //-- Step 2: Matching descriptor vectors with a FLANN based matcher + // Since SURF is a floating-point descriptor NORM_L2 is used + DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED); + List knnMatches = new ArrayList<>(); + matcher.knnMatch(descriptorsObject, descriptorsScene, knnMatches, 2); + + //-- Filter matches using the Lowe's ratio test + float ratio_thresh = 0.75f; + List listOfGoodMatches = new ArrayList<>(); + for (int i = 0; i < knnMatches.size(); i++) { + if (knnMatches.get(i).rows() > 1) { + DMatch[] matches = knnMatches.get(i).toArray(); + if (matches[0].distance / matches[1].distance <= ratio_thresh) { + listOfGoodMatches.add(matches[0]); + } + } + } + MatOfDMatch goodMatches = new MatOfDMatch(); + goodMatches.fromList(listOfGoodMatches); + + //-- Draw matches + Mat imgMatches = new Mat(); + Features2d.drawMatches(imgObject, keypointsObject, imgScene, keypointsScene, goodMatches, imgMatches, Scalar.all(-1), + Scalar.all(-1), new MatOfByte(), Features2d.NOT_DRAW_SINGLE_POINTS); + + //-- Localize the object + List obj = new ArrayList<>(); + List scene = new ArrayList<>(); + + List listOfKeypointsObject = keypointsObject.toList(); + List listOfKeypointsScene = keypointsScene.toList(); + for (int i = 0; i < listOfGoodMatches.size(); i++) { + //-- Get the keypoints from the good matches + obj.add(listOfKeypointsObject.get(listOfGoodMatches.get(i).queryIdx).pt); + scene.add(listOfKeypointsScene.get(listOfGoodMatches.get(i).trainIdx).pt); + } + + MatOfPoint2f objMat = new MatOfPoint2f(), sceneMat = new MatOfPoint2f(); + objMat.fromList(obj); + sceneMat.fromList(scene); + double ransacReprojThreshold = 3.0; + Mat H = Calib3d.findHomography( objMat, sceneMat, Calib3d.RANSAC, ransacReprojThreshold ); + + //-- Get the corners from the image_1 ( the object to be "detected" ) + Mat objCorners = new Mat(4, 1, CvType.CV_32FC2), sceneCorners = new Mat(); + float[] objCornersData = new float[(int) (objCorners.total() * objCorners.channels())]; + objCorners.get(0, 0, objCornersData); + objCornersData[0] = 0; + objCornersData[1] = 0; + objCornersData[2] = imgObject.cols(); + objCornersData[3] = 0; + objCornersData[4] = imgObject.cols(); + objCornersData[5] = imgObject.rows(); + objCornersData[6] = 0; + objCornersData[7] = imgObject.rows(); + objCorners.put(0, 0, objCornersData); + + Core.perspectiveTransform(objCorners, sceneCorners, H); + float[] sceneCornersData = new float[(int) (sceneCorners.total() * sceneCorners.channels())]; + sceneCorners.get(0, 0, sceneCornersData); + + //-- Draw lines between the corners (the mapped object in the scene - image_2 ) + Imgproc.line(imgMatches, new Point(sceneCornersData[0] + imgObject.cols(), sceneCornersData[1]), + new Point(sceneCornersData[2] + imgObject.cols(), sceneCornersData[3]), new Scalar(0, 255, 0), 4); + Imgproc.line(imgMatches, new Point(sceneCornersData[2] + imgObject.cols(), sceneCornersData[3]), + new Point(sceneCornersData[4] + imgObject.cols(), sceneCornersData[5]), new Scalar(0, 255, 0), 4); + Imgproc.line(imgMatches, new Point(sceneCornersData[4] + imgObject.cols(), sceneCornersData[5]), + new Point(sceneCornersData[6] + imgObject.cols(), sceneCornersData[7]), new Scalar(0, 255, 0), 4); + Imgproc.line(imgMatches, new Point(sceneCornersData[6] + imgObject.cols(), sceneCornersData[7]), + new Point(sceneCornersData[0] + imgObject.cols(), sceneCornersData[1]), new Scalar(0, 255, 0), 4); + + //-- Show detected matches + HighGui.imshow("Good Matches & Object detection", imgMatches); + HighGui.waitKey(0); + + System.exit(0); + } +} + +public class SURFFLANNMatchingHomographyDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new SURFFLANNMatchingHomography().run(args); + } +} diff --git a/samples/python/calibrate.py b/samples/python/calibrate.py index 14019127bc..a2970a95e7 100755 --- a/samples/python/calibrate.py +++ b/samples/python/calibrate.py @@ -71,7 +71,7 @@ if __name__ == '__main__': if debug_dir: vis = cv.cvtColor(img, cv.COLOR_GRAY2BGR) cv.drawChessboardCorners(vis, pattern_size, corners, found) - path, name, ext = splitfn(fn) + _path, name, _ext = splitfn(fn) outfile = os.path.join(debug_dir, name + '_chess.png') cv.imwrite(outfile, vis) diff --git a/samples/python/camera_calibration_show_extrinsics.py b/samples/python/camera_calibration_show_extrinsics.py index 7b1b0cf980..75274aea9e 100755 --- a/samples/python/camera_calibration_show_extrinsics.py +++ b/samples/python/camera_calibration_show_extrinsics.py @@ -91,7 +91,7 @@ def create_board_model(extrinsics, board_width, board_height, square_size, draw_ # draw calibration board X_board = np.ones((4,5)) - X_board_cam = np.ones((extrinsics.shape[0],4,5)) + #X_board_cam = np.ones((extrinsics.shape[0],4,5)) X_board[0:3,0] = [0,0,0] X_board[0:3,1] = [width,0,0] X_board[0:3,2] = [width,height,0] diff --git a/samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py b/samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py index 47caec4d0a..fb87cce75a 100644 --- a/samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py +++ b/samples/python/tutorial_code/Histograms_Matching/histogram_equalization/EqualizeHist_Demo.py @@ -18,7 +18,7 @@ src = cv.cvtColor(src, cv.COLOR_BGR2GRAY) ## [Convert to grayscale] ## [Apply Histogram Equalization] -dst = cv.equalizeHist(src); +dst = cv.equalizeHist(src) ## [Apply Histogram Equalization] ## [Display results] diff --git a/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py b/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py new file mode 100644 index 0000000000..72ce96b1a6 --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/corner_subpixels/cornerSubPix_Demo.py @@ -0,0 +1,70 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse +import random as rng + +source_window = 'Image' +maxTrackbar = 25 +rng.seed(12345) + +def goodFeaturesToTrack_Demo(val): + maxCorners = max(val, 1) + + # Parameters for Shi-Tomasi algorithm + qualityLevel = 0.01 + minDistance = 10 + blockSize = 3 + gradientSize = 3 + useHarrisDetector = False + k = 0.04 + + # Copy the source image + copy = np.copy(src) + + # Apply corner detection + corners = cv.goodFeaturesToTrack(src_gray, maxCorners, qualityLevel, minDistance, None, \ + blockSize=blockSize, gradientSize=gradientSize, useHarrisDetector=useHarrisDetector, k=k) + + # Draw corners detected + print('** Number of corners detected:', corners.shape[0]) + radius = 4 + for i in range(corners.shape[0]): + cv.circle(copy, (corners[i,0,0], corners[i,0,1]), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + # Show what you got + cv.namedWindow(source_window) + cv.imshow(source_window, copy) + + # Set the needed parameters to find the refined corners + winSize = (5, 5) + zeroZone = (-1, -1) + criteria = (cv.TERM_CRITERIA_EPS + cv.TermCriteria_COUNT, 40, 0.001) + + # Calculate the refined corner locations + corners = cv.cornerSubPix(src_gray, corners, winSize, zeroZone, criteria) + + # Write them down + for i in range(corners.shape[0]): + print(" -- Refined Corner [", i, "] (", corners[i,0,0], ",", corners[i,0,1], ")") + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Shi-Tomasi corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/pic3.png') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Create a window and a trackbar +cv.namedWindow(source_window) +maxCorners = 10 # initial threshold +cv.createTrackbar('Threshold: ', source_window, maxCorners, maxTrackbar, goodFeaturesToTrack_Demo) +cv.imshow(source_window, src) +goodFeaturesToTrack_Demo(maxCorners) + +cv.waitKey() diff --git a/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py b/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py new file mode 100644 index 0000000000..d135367fc2 --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/generic_corner_detector/cornerDetector_Demo.py @@ -0,0 +1,80 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse +import random as rng + +myHarris_window = 'My Harris corner detector' +myShiTomasi_window = 'My Shi Tomasi corner detector' +myHarris_qualityLevel = 50 +myShiTomasi_qualityLevel = 50 +max_qualityLevel = 100 +rng.seed(12345) + +def myHarris_function(val): + myHarris_copy = np.copy(src) + myHarris_qualityLevel = max(val, 1) + + for i in range(src_gray.shape[0]): + for j in range(src_gray.shape[1]): + if Mc[i,j] > myHarris_minVal + ( myHarris_maxVal - myHarris_minVal )*myHarris_qualityLevel/max_qualityLevel: + cv.circle(myHarris_copy, (j,i), 4, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + cv.imshow(myHarris_window, myHarris_copy) + +def myShiTomasi_function(val): + myShiTomasi_copy = np.copy(src) + myShiTomasi_qualityLevel = max(val, 1) + + for i in range(src_gray.shape[0]): + for j in range(src_gray.shape[1]): + if myShiTomasi_dst[i,j] > myShiTomasi_minVal + ( myShiTomasi_maxVal - myShiTomasi_minVal )*myShiTomasi_qualityLevel/max_qualityLevel: + cv.circle(myShiTomasi_copy, (j,i), 4, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + cv.imshow(myShiTomasi_window, myShiTomasi_copy) + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Creating your own corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/building.jpg') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Set some parameters +blockSize = 3 +apertureSize = 3 + +# My Harris matrix -- Using cornerEigenValsAndVecs +myHarris_dst = cv.cornerEigenValsAndVecs(src_gray, blockSize, apertureSize) + +# calculate Mc +Mc = np.empty(src_gray.shape, dtype=np.float32) +for i in range(src_gray.shape[0]): + for j in range(src_gray.shape[1]): + lambda_1 = myHarris_dst[i,j,0] + lambda_2 = myHarris_dst[i,j,1] + Mc[i,j] = lambda_1*lambda_2 - 0.04*pow( ( lambda_1 + lambda_2 ), 2 ) + +myHarris_minVal, myHarris_maxVal, _, _ = cv.minMaxLoc(Mc) + +# Create Window and Trackbar +cv.namedWindow(myHarris_window) +cv.createTrackbar('Quality Level:', myHarris_window, myHarris_qualityLevel, max_qualityLevel, myHarris_function) +myHarris_function(myHarris_qualityLevel) + +# My Shi-Tomasi -- Using cornerMinEigenVal +myShiTomasi_dst = cv.cornerMinEigenVal(src_gray, blockSize, apertureSize) + +myShiTomasi_minVal, myShiTomasi_maxVal, _, _ = cv.minMaxLoc(myShiTomasi_dst) + +# Create Window and Trackbar +cv.namedWindow(myShiTomasi_window) +cv.createTrackbar('Quality Level:', myShiTomasi_window, myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function) +myShiTomasi_function(myShiTomasi_qualityLevel) + +cv.waitKey() diff --git a/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py b/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py new file mode 100644 index 0000000000..57e767ccee --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/good_features_to_track/goodFeaturesToTrack_Demo.py @@ -0,0 +1,58 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse +import random as rng + +source_window = 'Image' +maxTrackbar = 100 +rng.seed(12345) + +def goodFeaturesToTrack_Demo(val): + maxCorners = max(val, 1) + + # Parameters for Shi-Tomasi algorithm + qualityLevel = 0.01 + minDistance = 10 + blockSize = 3 + gradientSize = 3 + useHarrisDetector = False + k = 0.04 + + # Copy the source image + copy = np.copy(src) + + # Apply corner detection + corners = cv.goodFeaturesToTrack(src_gray, maxCorners, qualityLevel, minDistance, None, \ + blockSize=blockSize, gradientSize=gradientSize, useHarrisDetector=useHarrisDetector, k=k) + + # Draw corners detected + print('** Number of corners detected:', corners.shape[0]) + radius = 4 + for i in range(corners.shape[0]): + cv.circle(copy, (corners[i,0,0], corners[i,0,1]), radius, (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)), cv.FILLED) + + # Show what you got + cv.namedWindow(source_window) + cv.imshow(source_window, copy) + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Shi-Tomasi corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/pic3.png') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Create a window and a trackbar +cv.namedWindow(source_window) +maxCorners = 23 # initial threshold +cv.createTrackbar('Threshold: ', source_window, maxCorners, maxTrackbar, goodFeaturesToTrack_Demo) +cv.imshow(source_window, src) +goodFeaturesToTrack_Demo(maxCorners) + +cv.waitKey() diff --git a/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py b/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py new file mode 100644 index 0000000000..cee7679adf --- /dev/null +++ b/samples/python/tutorial_code/TrackingMotion/harris_detector/cornerHarris_Demo.py @@ -0,0 +1,55 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +source_window = 'Source image' +corners_window = 'Corners detected' +max_thresh = 255 + +def cornerHarris_demo(val): + thresh = val + + # Detector parameters + blockSize = 2 + apertureSize = 3 + k = 0.04 + + # Detecting corners + dst = cv.cornerHarris(src_gray, blockSize, apertureSize, k) + + # Normalizing + dst_norm = np.empty(dst.shape, dtype=np.float32) + cv.normalize(dst, dst_norm, alpha=0, beta=255, norm_type=cv.NORM_MINMAX) + dst_norm_scaled = cv.convertScaleAbs(dst_norm) + + # Drawing a circle around corners + for i in range(dst_norm.shape[0]): + for j in range(dst_norm.shape[1]): + if int(dst_norm[i,j]) > thresh: + cv.circle(dst_norm_scaled, (j,i), 5, (0), 2) + + # Showing the result + cv.namedWindow(corners_window) + cv.imshow(corners_window, dst_norm_scaled) + +# Load source image and convert it to gray +parser = argparse.ArgumentParser(description='Code for Harris corner detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/building.jpg') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +# Create a window and a trackbar +cv.namedWindow(source_window) +thresh = 200 # initial threshold +cv.createTrackbar('Threshold: ', source_window, thresh, max_thresh, cornerHarris_demo) +cv.imshow(source_window, src) +cornerHarris_demo(thresh) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py b/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py new file mode 100644 index 0000000000..f50e48d858 --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_description/SURF_matching_Demo.py @@ -0,0 +1,35 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Detection tutorial.') +parser.add_argument('--input1', help='Path to input image 1.', default='../data/box.png') +parser.add_argument('--input2', help='Path to input image 2.', default='../data/box_in_scene.png') +args = parser.parse_args() + +img1 = cv.imread(args.input1, cv.IMREAD_GRAYSCALE) +img2 = cv.imread(args.input2, cv.IMREAD_GRAYSCALE) +if img1 is None or img2 is None: + print('Could not open or find the images!') + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints1, descriptors1 = detector.detectAndCompute(img1, None) +keypoints2, descriptors2 = detector.detectAndCompute(img2, None) + +#-- Step 2: Matching descriptor vectors with a brute force matcher +# Since SURF is a floating-point descriptor NORM_L2 is used +matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_BRUTEFORCE) +matches = matcher.match(descriptors1, descriptors2) + +#-- Draw matches +img_matches = np.empty((max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], 3), dtype=np.uint8) +cv.drawMatches(img1, keypoints1, img2, keypoints2, matches, img_matches) + +#-- Show detected matches +cv.imshow('Matches', img_matches) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py b/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py new file mode 100644 index 0000000000..717d9f13c0 --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_detection/SURF_detection_Demo.py @@ -0,0 +1,27 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Detection tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/box.png') +args = parser.parse_args() + +src = cv.imread(args.input, cv.IMREAD_GRAYSCALE) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints = detector.detect(src) + +#-- Draw keypoints +img_keypoints = np.empty((src.shape[0], src.shape[1], 3), dtype=np.uint8) +cv.drawKeypoints(src, keypoints, img_keypoints) + +#-- Show detected (drawn) keypoints +cv.imshow('SURF Keypoints', img_keypoints) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py b/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py new file mode 100644 index 0000000000..d22f9a8a6f --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py @@ -0,0 +1,43 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Matching with FLANN tutorial.') +parser.add_argument('--input1', help='Path to input image 1.', default='../data/box.png') +parser.add_argument('--input2', help='Path to input image 2.', default='../data/box_in_scene.png') +args = parser.parse_args() + +img1 = cv.imread(args.input1, cv.IMREAD_GRAYSCALE) +img2 = cv.imread(args.input2, cv.IMREAD_GRAYSCALE) +if img1 is None or img2 is None: + print('Could not open or find the images!') + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints1, descriptors1 = detector.detectAndCompute(img1, None) +keypoints2, descriptors2 = detector.detectAndCompute(img2, None) + +#-- Step 2: Matching descriptor vectors with a FLANN based matcher +# Since SURF is a floating-point descriptor NORM_L2 is used +matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED) +knn_matches = matcher.knnMatch(descriptors1, descriptors2, 2) + +#-- Filter matches using the Lowe's ratio test +ratio_thresh = 0.7 +good_matches = [] +for matches in knn_matches: + if len(matches) > 1: + if matches[0].distance / matches[1].distance <= ratio_thresh: + good_matches.append(matches[0]) + +#-- Draw matches +img_matches = np.empty((max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], 3), dtype=np.uint8) +cv.drawMatches(img1, keypoints1, img2, keypoints2, good_matches, img_matches, flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) + +#-- Show detected matches +cv.imshow('Good Matches', img_matches) + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py b/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py new file mode 100644 index 0000000000..8820addce2 --- /dev/null +++ b/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py @@ -0,0 +1,78 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +parser = argparse.ArgumentParser(description='Code for Feature Matching with FLANN tutorial.') +parser.add_argument('--input1', help='Path to input image 1.', default='../data/box.png') +parser.add_argument('--input2', help='Path to input image 2.', default='../data/box_in_scene.png') +args = parser.parse_args() + +img_object = cv.imread(args.input1, cv.IMREAD_GRAYSCALE) +img_scene = cv.imread(args.input2, cv.IMREAD_GRAYSCALE) +if img_object is None or img_scene is None: + print('Could not open or find the images!') + exit(0) + +#-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors +minHessian = 400 +detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian) +keypoints_obj, descriptors_obj = detector.detectAndCompute(img_object, None) +keypoints_scene, descriptors_scene = detector.detectAndCompute(img_scene, None) + +#-- Step 2: Matching descriptor vectors with a FLANN based matcher +# Since SURF is a floating-point descriptor NORM_L2 is used +matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED) +knn_matches = matcher.knnMatch(descriptors_obj, descriptors_scene, 2) + +#-- Filter matches using the Lowe's ratio test +ratio_thresh = 0.75 +good_matches = [] +for matches in knn_matches: + if len(matches) > 1: + if matches[0].distance / matches[1].distance <= ratio_thresh: + good_matches.append(matches[0]) + +#-- Draw matches +img_matches = np.empty((max(img_object.shape[0], img_scene.shape[0]), img_object.shape[1]+img_scene.shape[1], 3), dtype=np.uint8) +cv.drawMatches(img_object, keypoints_obj, img_scene, keypoints_scene, good_matches, img_matches, flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) + +#-- Localize the object +obj = np.empty((len(good_matches),2), dtype=np.float32) +scene = np.empty((len(good_matches),2), dtype=np.float32) +for i in range(len(good_matches)): + #-- Get the keypoints from the good matches + obj[i,0] = keypoints_obj[good_matches[i].queryIdx].pt[0] + obj[i,1] = keypoints_obj[good_matches[i].queryIdx].pt[1] + scene[i,0] = keypoints_scene[good_matches[i].trainIdx].pt[0] + scene[i,1] = keypoints_scene[good_matches[i].trainIdx].pt[1] + +H, _ = cv.findHomography(obj, scene, cv.RANSAC) + +#-- Get the corners from the image_1 ( the object to be "detected" ) +obj_corners = np.empty((4,1,2), dtype=np.float32) +obj_corners[0,0,0] = 0 +obj_corners[0,0,1] = 0 +obj_corners[1,0,0] = img_object.shape[1] +obj_corners[1,0,1] = 0 +obj_corners[2,0,0] = img_object.shape[1] +obj_corners[2,0,1] = img_object.shape[0] +obj_corners[3,0,0] = 0 +obj_corners[3,0,1] = img_object.shape[0] + +scene_corners = cv.perspectiveTransform(obj_corners, H) + +#-- Draw lines between the corners (the mapped object in the scene - image_2 ) +cv.line(img_matches, (int(scene_corners[0,0,0] + img_object.shape[1]), int(scene_corners[0,0,1])),\ + (int(scene_corners[1,0,0] + img_object.shape[1]), int(scene_corners[1,0,1])), (0,255,0), 4) +cv.line(img_matches, (int(scene_corners[1,0,0] + img_object.shape[1]), int(scene_corners[1,0,1])),\ + (int(scene_corners[2,0,0] + img_object.shape[1]), int(scene_corners[2,0,1])), (0,255,0), 4) +cv.line(img_matches, (int(scene_corners[2,0,0] + img_object.shape[1]), int(scene_corners[2,0,1])),\ + (int(scene_corners[3,0,0] + img_object.shape[1]), int(scene_corners[3,0,1])), (0,255,0), 4) +cv.line(img_matches, (int(scene_corners[3,0,0] + img_object.shape[1]), int(scene_corners[3,0,1])),\ + (int(scene_corners[0,0,0] + img_object.shape[1]), int(scene_corners[0,0,1])), (0,255,0), 4) + +#-- Show detected matches +cv.imshow('Good Matches & Object detection', img_matches) + +cv.waitKey() diff --git a/samples/python/tutorial_code/imgProc/Smoothing/smoothing.py b/samples/python/tutorial_code/imgProc/Smoothing/smoothing.py index 205ee6d488..e7096580ad 100644 --- a/samples/python/tutorial_code/imgProc/Smoothing/smoothing.py +++ b/samples/python/tutorial_code/imgProc/Smoothing/smoothing.py @@ -88,7 +88,7 @@ def main(argv): def display_caption(caption): global dst dst = np.zeros(src.shape, src.dtype) - rows, cols, ch = src.shape + rows, cols, _ch = src.shape cv.putText(dst, caption, (int(cols / 4), int(rows / 2)), cv.FONT_HERSHEY_COMPLEX, 1, (255, 255, 255)) diff --git a/samples/python/tutorial_code/imgProc/threshold/threshold.py b/samples/python/tutorial_code/imgProc/threshold/threshold.py index 0d640750aa..1ba38126c9 100644 --- a/samples/python/tutorial_code/imgProc/threshold/threshold.py +++ b/samples/python/tutorial_code/imgProc/threshold/threshold.py @@ -33,7 +33,7 @@ if src is None: print('Could not open or find the image: ', args.input) exit(0) # Convert the image to Gray -src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY); +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) ## [load] ## [window] diff --git a/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py b/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py index 77d95fe395..d54d93c7fc 100644 --- a/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py +++ b/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py @@ -94,7 +94,7 @@ while True: break frame_HSV = cv.cvtColor(frame, cv.COLOR_BGR2HSV) - frame_threshold = cv.inRange(frame_HSV, (low_H, low_S, low_V), (high_H, high_S, high_V)); + frame_threshold = cv.inRange(frame_HSV, (low_H, low_S, low_V), (high_H, high_S, high_V)) ## [while] ## [show]