diff --git a/3rdparty/libjpeg/CMakeLists.txt b/3rdparty/libjpeg/CMakeLists.txt index 028a583cff..65a9d1c8aa 100644 --- a/3rdparty/libjpeg/CMakeLists.txt +++ b/3rdparty/libjpeg/CMakeLists.txt @@ -9,7 +9,7 @@ ocv_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) file(GLOB lib_srcs *.c) file(GLOB lib_hdrs *.h) -if(ANDROID OR IOS) +if(ANDROID OR IOS OR APPLE) ocv_list_filterout(lib_srcs jmemansi.c) else() ocv_list_filterout(lib_srcs jmemnobs.c) diff --git a/3rdparty/readme.txt b/3rdparty/readme.txt index ca46fbd576..64e2563a85 100644 --- a/3rdparty/readme.txt +++ b/3rdparty/readme.txt @@ -1,5 +1,5 @@ This folder contains libraries and headers of a few very popular still image codecs -used by highgui module. +used by imgcodecs module. The libraries and headers are preferably to build Win32 and Win64 versions of OpenCV. On UNIX systems all the libraries are automatically detected by configure script. In order to use these versions of libraries instead of system ones on UNIX systems you @@ -11,7 +11,7 @@ libjpeg 8d (8.4) - The Independent JPEG Group's JPEG software. See IGJ home page http://www.ijg.org for details and links to the source code - HAVE_JPEG preprocessor flag must be set to make highgui use libjpeg. + HAVE_JPEG preprocessor flag must be set to make imgcodecs use libjpeg. On UNIX systems configure script takes care of it. ------------------------------------------------------------------------------------ libpng 1.5.12 - Portable Network Graphics library. @@ -19,7 +19,7 @@ libpng 1.5.12 - Portable Network Graphics library. See libpng home page http://www.libpng.org for details and links to the source code - HAVE_PNG preprocessor flag must be set to make highgui use libpng. + HAVE_PNG preprocessor flag must be set to make imgcodecs use libpng. On UNIX systems configure script takes care of it. ------------------------------------------------------------------------------------ libtiff 4.0.2 - Tag Image File Format (TIFF) Software @@ -28,7 +28,7 @@ libtiff 4.0.2 - Tag Image File Format (TIFF) Software See libtiff home page http://www.remotesensing.org/libtiff/ for details and links to the source code - HAVE_TIFF preprocessor flag must be set to make highgui use libtiff. + HAVE_TIFF preprocessor flag must be set to make imgcodecs use libtiff. On UNIX systems configure script takes care of it. In this build support for ZIP (LZ77 compression) is turned on. ------------------------------------------------------------------------------------ @@ -37,7 +37,7 @@ zlib 1.2.7 - General purpose LZ77 compression library See zlib home page http://www.zlib.net for details and links to the source code - No preprocessor definition is needed to make highgui use this library - + No preprocessor definition is needed to make imgcodecs use this library - it is included automatically if either libpng or libtiff are used. ------------------------------------------------------------------------------------ jasper-1.900.1 - JasPer is a collection of software diff --git a/CMakeLists.txt b/CMakeLists.txt index b9ff7bec16..09a530d41d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ # # ---------------------------------------------------------------------------- + + include(cmake/OpenCVMinDepVersions.cmake) if(CMAKE_GENERATOR MATCHES Xcode AND XCODE_VERSION VERSION_GREATER 4.3) @@ -135,6 +137,7 @@ OCV_OPTION(WITH_WEBP "Include WebP support" ON OCV_OPTION(WITH_OPENEXR "Include ILM support via OpenEXR" ON IF (NOT IOS) ) OCV_OPTION(WITH_OPENGL "Include OpenGL support" OFF IF (NOT ANDROID) ) OCV_OPTION(WITH_OPENNI "Include OpenNI support" OFF IF (NOT ANDROID AND NOT IOS) ) +OCV_OPTION(WITH_OPENNI2 "Include OpenNI2 support" OFF IF (NOT ANDROID AND NOT IOS) ) OCV_OPTION(WITH_PNG "Include PNG support" ON) OCV_OPTION(WITH_PVAPI "Include Prosilica GigE support" ON IF (NOT ANDROID AND NOT IOS) ) OCV_OPTION(WITH_GIGEAPI "Include Smartek GigE support" ON IF (NOT ANDROID AND NOT IOS) ) @@ -148,8 +151,8 @@ OCV_OPTION(WITH_TIFF "Include TIFF support" ON OCV_OPTION(WITH_UNICAP "Include Unicap support (GPL)" OFF IF (UNIX AND NOT APPLE AND NOT ANDROID) ) OCV_OPTION(WITH_V4L "Include Video 4 Linux support" ON IF (UNIX AND NOT ANDROID) ) OCV_OPTION(WITH_LIBV4L "Use libv4l for Video 4 Linux support" ON IF (UNIX AND NOT ANDROID) ) -OCV_OPTION(WITH_DSHOW "Build HighGUI with DirectShow support" ON IF (WIN32 AND NOT ARM) ) -OCV_OPTION(WITH_MSMF "Build HighGUI with Media Foundation support" OFF IF WIN32 ) +OCV_OPTION(WITH_DSHOW "Build VideoIO with DirectShow support" ON IF (WIN32 AND NOT ARM) ) +OCV_OPTION(WITH_MSMF "Build VideoIO with Media Foundation support" OFF IF WIN32 ) OCV_OPTION(WITH_XIMEA "Include XIMEA cameras support" OFF IF (NOT ANDROID AND NOT APPLE) ) OCV_OPTION(WITH_XINE "Include Xine support (GPL)" OFF IF (UNIX AND NOT APPLE AND NOT ANDROID) ) OCV_OPTION(WITH_CLP "Include Clp support (EPL)" OFF) @@ -865,6 +868,11 @@ if(DEFINED WITH_OPENNI) THEN "YES (${OPENNI_PRIME_SENSOR_MODULE})" ELSE NO) endif(DEFINED WITH_OPENNI) +if(DEFINED WITH_OPENNI2) + status(" OpenNI2:" HAVE_OPENNI2 THEN "YES (ver ${OPENNI2_VERSION_STRING}, build ${OPENNI2_VERSION_BUILD})" + ELSE NO) +endif(DEFINED WITH_OPENNI2) + if(DEFINED WITH_PVAPI) status(" PvAPI:" HAVE_PVAPI THEN YES ELSE NO) endif(DEFINED WITH_PVAPI) diff --git a/apps/traincascade/CMakeLists.txt b/apps/traincascade/CMakeLists.txt index e560ed815c..cca56361e3 100644 --- a/apps/traincascade/CMakeLists.txt +++ b/apps/traincascade/CMakeLists.txt @@ -1,4 +1,4 @@ -set(OPENCV_TRAINCASCADE_DEPS opencv_core opencv_ml opencv_imgproc opencv_photo opencv_objdetect opencv_highgui opencv_calib3d opencv_video opencv_features2d) +set(OPENCV_TRAINCASCADE_DEPS opencv_core opencv_ml opencv_imgproc opencv_photo opencv_objdetect opencv_imgcodecs opencv_videoio opencv_highgui opencv_calib3d opencv_video opencv_features2d) ocv_check_dependencies(${OPENCV_TRAINCASCADE_DEPS}) if(NOT OCV_DEPENDENCIES_FOUND) diff --git a/apps/traincascade/imagestorage.cpp b/apps/traincascade/imagestorage.cpp index e69a7df1ad..2f791240e4 100644 --- a/apps/traincascade/imagestorage.cpp +++ b/apps/traincascade/imagestorage.cpp @@ -1,6 +1,7 @@ #include "opencv2/core.hpp" +#include "opencv2/core/core_c.h" #include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" #include "imagestorage.h" #include diff --git a/apps/traincascade/imagestorage.h b/apps/traincascade/imagestorage.h index fb68e25cb2..38ca52a8b5 100644 --- a/apps/traincascade/imagestorage.h +++ b/apps/traincascade/imagestorage.h @@ -1,9 +1,6 @@ #ifndef _OPENCV_IMAGESTORAGE_H_ #define _OPENCV_IMAGESTORAGE_H_ -#include "highgui.h" - - class CvCascadeImageReader { diff --git a/apps/traincascade/traincascade.cpp b/apps/traincascade/traincascade.cpp index e6e16ba896..52bacc8083 100644 --- a/apps/traincascade/traincascade.cpp +++ b/apps/traincascade/traincascade.cpp @@ -13,6 +13,7 @@ int main( int argc, char* argv[] ) int numPos = 2000; int numNeg = 1000; int numStages = 20; + int numThreads = getNumThreads(); int precalcValBufSize = 256, precalcIdxBufSize = 256; bool baseFormatSave = false; @@ -36,6 +37,7 @@ int main( int argc, char* argv[] ) cout << " [-precalcValBufSize ]" << endl; cout << " [-precalcIdxBufSize ]" << endl; cout << " [-baseFormatSave]" << endl; + cout << " [-numThreads ]" << endl; cascadeParams.printDefaults(); stageParams.printDefaults(); for( int fi = 0; fi < fc; fi++ ) @@ -82,6 +84,10 @@ int main( int argc, char* argv[] ) { baseFormatSave = true; } + else if( !strcmp( argv[i], "-numThreads" ) ) + { + numThreads = atoi(argv[++i]); + } else if ( cascadeParams.scanAttr( argv[i], argv[i+1] ) ) { i++; } else if ( stageParams.scanAttr( argv[i], argv[i+1] ) ) { i++; } else if ( !set ) @@ -98,6 +104,7 @@ int main( int argc, char* argv[] ) } } + setNumThreads( numThreads ); classifier.train( cascadeDirName, vecName, bgName, diff --git a/cmake/OpenCVConfig.cmake b/cmake/OpenCVConfig.cmake index 2d80f765b5..896a1901c1 100644 --- a/cmake/OpenCVConfig.cmake +++ b/cmake/OpenCVConfig.cmake @@ -11,7 +11,7 @@ # # Or you can search for specific OpenCV modules: # -# FIND_PACKAGE(OpenCV REQUIRED core highgui) +# FIND_PACKAGE(OpenCV REQUIRED core imgcodecs) # # If the module is found then OPENCV__FOUND is set to TRUE. # diff --git a/cmake/OpenCVFindLibsVideo.cmake b/cmake/OpenCVFindLibsVideo.cmake index 5520d05521..fec7be4d84 100644 --- a/cmake/OpenCVFindLibsVideo.cmake +++ b/cmake/OpenCVFindLibsVideo.cmake @@ -131,7 +131,7 @@ if(WITH_1394) if(HAVE_DC1394_2) ocv_parse_pkg("libdc1394-2" "${DC1394_2_LIB_DIR}/pkgconfig" "") ocv_include_directories(${DC1394_2_INCLUDE_PATH}) - set(HIGHGUI_LIBRARIES ${HIGHGUI_LIBRARIES} + set(VIDEOIO_LIBRARIES ${VIDEOIO_LIBRARIES} "${DC1394_2_LIB_DIR}/libdc1394.a" "${CMU1394_LIB_DIR}/lib1394camera.a") endif(HAVE_DC1394_2) @@ -166,6 +166,11 @@ if(WITH_OPENNI) include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindOpenNI.cmake") endif(WITH_OPENNI) +ocv_clear_vars(HAVE_OPENNI2) +if(WITH_OPENNI2) + include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindOpenNI2.cmake") +endif(WITH_OPENNI2) + # --- XIMEA --- ocv_clear_vars(HAVE_XIMEA) if(WITH_XIMEA) @@ -234,7 +239,7 @@ if(WITH_FFMPEG) endif() endif(FFMPEG_INCLUDE_DIR) if(HAVE_FFMPEG) - set(HIGHGUI_LIBRARIES ${HIGHGUI_LIBRARIES} "${FFMPEG_LIB_DIR}/libavcodec.a" + set(VIDEOIO_LIBRARIES ${VIDEOIO_LIBRARIES} "${FFMPEG_LIB_DIR}/libavcodec.a" "${FFMPEG_LIB_DIR}/libavformat.a" "${FFMPEG_LIB_DIR}/libavutil.a" "${FFMPEG_LIB_DIR}/libswscale.a") ocv_include_directories(${FFMPEG_INCLUDE_DIR}) @@ -253,14 +258,15 @@ if(WITH_MSMF) check_include_file(Mfapi.h HAVE_MSMF) endif(WITH_MSMF) -# --- Extra HighGUI libs on Windows --- +# --- Extra HighGUI and VideoIO libs on Windows --- if(WIN32) - list(APPEND HIGHGUI_LIBRARIES comctl32 gdi32 ole32 setupapi ws2_32 vfw32) + list(APPEND HIGHGUI_LIBRARIES comctl32 gdi32 ole32 setupapi ws2_32) + list(APPEND VIDEOIO_LIBRARIES vfw32) if(MINGW64) - list(APPEND HIGHGUI_LIBRARIES avifil32 avicap32 winmm msvfw32) - list(REMOVE_ITEM HIGHGUI_LIBRARIES vfw32) + list(APPEND VIDEOIO_LIBRARIES avifil32 avicap32 winmm msvfw32) + list(REMOVE_ITEM VIDEOIO_LIBRARIES vfw32) elseif(MINGW) - list(APPEND HIGHGUI_LIBRARIES winmm) + list(APPEND VIDEOIO_LIBRARIES winmm) endif() endif(WIN32) diff --git a/cmake/OpenCVFindOpenNI2.cmake b/cmake/OpenCVFindOpenNI2.cmake new file mode 100644 index 0000000000..83acf1bc5d --- /dev/null +++ b/cmake/OpenCVFindOpenNI2.cmake @@ -0,0 +1,61 @@ +# Main variables: +# OPENNI2_LIBRARY and OPENNI2_INCLUDES to link OpenCV modules with OpenNI2 +# HAVE_OPENNI2 for conditional compilation OpenCV with/without OpenNI2 + +if(NOT "${OPENNI2_LIB_DIR}" STREQUAL "${OPENNI2_LIB_DIR_INTERNAL}") + unset(OPENNI2_LIBRARY CACHE) + unset(OPENNI2_LIB_DIR CACHE) +endif() + +if(NOT "${OPENNI2_INCLUDE_DIR}" STREQUAL "${OPENNI2_INCLUDE_DIR_INTERNAL}") + unset(OPENNI2_INCLUDES CACHE) + unset(OPENNI2_INCLUDE_DIR CACHE) +endif() + +if(WIN32) + if(NOT (MSVC64 OR MINGW64)) + find_file(OPENNI2_INCLUDES "OpenNI.h" PATHS "$ENV{OPEN_NI_INSTALL_PATH}Include" DOC "OpenNI2 c++ interface header") + find_library(OPENNI2_LIBRARY "OpenNI2" PATHS $ENV{OPENNI2_LIB} DOC "OpenNI2 library") + else() + find_file(OPENNI2_INCLUDES "OpenNI.h" PATHS "$ENV{OPEN_NI_INSTALL_PATH64}Include" DOC "OpenNI2 c++ interface header") + find_library(OPENNI2_LIBRARY "OpenNI2" PATHS $ENV{OPENNI2_LIB64} DOC "OpenNI2 library") + endif() +elseif(UNIX OR APPLE) + find_file(OPENNI2_INCLUDES "OpenNI.h" PATHS "/usr/include/ni2" "/usr/include/openni2" DOC "OpenNI2 c++ interface header") + find_library(OPENNI2_LIBRARY "OpenNI2" PATHS "/usr/lib" DOC "OpenNI2 library") +endif() + +if(OPENNI2_LIBRARY AND OPENNI2_INCLUDES) + set(HAVE_OPENNI2 TRUE) +endif() #if(OPENNI2_LIBRARY AND OPENNI2_INCLUDES) + +get_filename_component(OPENNI2_LIB_DIR "${OPENNI2_LIBRARY}" PATH) +get_filename_component(OPENNI2_INCLUDE_DIR ${OPENNI2_INCLUDES} PATH) + +if(HAVE_OPENNI2) + set(OPENNI2_LIB_DIR "${OPENNI2_LIB_DIR}" CACHE PATH "Path to OpenNI2 libraries" FORCE) + set(OPENNI2_INCLUDE_DIR "${OPENNI2_INCLUDE_DIR}" CACHE PATH "Path to OpenNI2 headers" FORCE) +endif() + +if(OPENNI2_LIBRARY) + set(OPENNI2_LIB_DIR_INTERNAL "${OPENNI2_LIB_DIR}" CACHE INTERNAL "This is the value of the last time OPENNI_LIB_DIR was set successfully." FORCE) +else() + message( WARNING, " OpenNI2 library directory (set by OPENNI2_LIB_DIR variable) is not found or does not have OpenNI2 libraries." ) +endif() + +if(OPENNI2_INCLUDES) + set(OPENNI2_INCLUDE_DIR_INTERNAL "${OPENNI2_INCLUDE_DIR}" CACHE INTERNAL "This is the value of the last time OPENNI2_INCLUDE_DIR was set successfully." FORCE) +else() + message( WARNING, " OpenNI2 include directory (set by OPENNI2_INCLUDE_DIR variable) is not found or does not have OpenNI2 include files." ) +endif() + +mark_as_advanced(FORCE OPENNI2_LIBRARY) +mark_as_advanced(FORCE OPENNI2_INCLUDES) + +if(HAVE_OPENNI2) + ocv_parse_header("${OPENNI2_INCLUDE_DIR}/OniVersion.h" ONI_VERSION_LINE ONI_VERSION_MAJOR ONI_VERSION_MINOR ONI_VERSION_MAINTENANCE ONI_VERSION_BUILD) + if(ONI_VERSION_MAJOR) + set(OPENNI2_VERSION_STRING ${ONI_VERSION_MAJOR}.${ONI_VERSION_MINOR}.${ONI_VERSION_MAINTENANCE} CACHE INTERNAL "OpenNI2 version") + set(OPENNI2_VERSION_BUILD ${ONI_VERSION_BUILD} CACHE INTERNAL "OpenNI2 build version") + endif() +endif() diff --git a/cmake/OpenCVGenInfoPlist.cmake b/cmake/OpenCVGenInfoPlist.cmake index 97c674ceb7..db418d1253 100644 --- a/cmake/OpenCVGenInfoPlist.cmake +++ b/cmake/OpenCVGenInfoPlist.cmake @@ -1,4 +1,7 @@ if(IOS) configure_file("${OpenCV_SOURCE_DIR}/platforms/ios/Info.plist.in" "${CMAKE_BINARY_DIR}/ios/Info.plist") +elseif(APPLE) + configure_file("${OpenCV_SOURCE_DIR}/platforms/osx/Info.plist.in" + "${CMAKE_BINARY_DIR}/osx/Info.plist") endif() diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index e6fa199119..3f4da5f106 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -704,8 +704,8 @@ function(ocv_add_perf_tests) if(BUILD_PERF_TESTS AND EXISTS "${perf_path}") __ocv_parse_test_sources(PERF ${ARGN}) - # opencv_highgui is required for imread/imwrite - set(perf_deps ${the_module} opencv_ts opencv_highgui ${OPENCV_PERF_${the_module}_DEPS} ${OPENCV_MODULE_opencv_ts_DEPS}) + # opencv_imgcodecs is required for imread/imwrite + set(perf_deps ${the_module} opencv_ts opencv_imgcodecs ${OPENCV_PERF_${the_module}_DEPS} ${OPENCV_MODULE_opencv_ts_DEPS}) ocv_check_dependencies(${perf_deps}) if(OCV_DEPENDENCIES_FOUND) @@ -757,8 +757,8 @@ function(ocv_add_accuracy_tests) if(BUILD_TESTS AND EXISTS "${test_path}") __ocv_parse_test_sources(TEST ${ARGN}) - # opencv_highgui is required for imread/imwrite - set(test_deps ${the_module} opencv_ts opencv_highgui ${OPENCV_TEST_${the_module}_DEPS} ${OPENCV_MODULE_opencv_ts_DEPS}) + # opencv_imgcodecs is required for imread/imwrite + set(test_deps ${the_module} opencv_ts opencv_imgcodecs opencv_videoio ${OPENCV_TEST_${the_module}_DEPS} ${OPENCV_MODULE_opencv_ts_DEPS}) ocv_check_dependencies(${test_deps}) if(OCV_DEPENDENCIES_FOUND) @@ -811,7 +811,7 @@ function(ocv_add_samples) string(REGEX REPLACE "^opencv_" "" module_id ${the_module}) if(BUILD_EXAMPLES AND EXISTS "${samples_path}") - set(samples_deps ${the_module} ${OPENCV_MODULE_${the_module}_DEPS} opencv_highgui ${ARGN}) + set(samples_deps ${the_module} ${OPENCV_MODULE_${the_module}_DEPS} opencv_imgcodecs opencv_videoio opencv_highgui ${ARGN}) ocv_check_dependencies(${samples_deps}) if(OCV_DEPENDENCIES_FOUND) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index f2a0197f82..d8171770de 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -265,16 +265,19 @@ macro(CHECK_MODULE module_name define) set(${define} 1) foreach(P "${ALIAS_INCLUDE_DIRS}") if(${P}) + list(APPEND VIDEOIO_INCLUDE_DIRS ${${P}}) list(APPEND HIGHGUI_INCLUDE_DIRS ${${P}}) endif() endforeach() foreach(P "${ALIAS_LIBRARY_DIRS}") if(${P}) + list(APPEND VIDEOIO_LIBRARY_DIRS ${${P}}) list(APPEND HIGHGUI_LIBRARY_DIRS ${${P}}) endif() endforeach() + list(APPEND VIDEOIO_LIBRARIES ${${ALIAS_LIBRARIES}}) list(APPEND HIGHGUI_LIBRARIES ${${ALIAS_LIBRARIES}}) endif() endif() diff --git a/cmake/templates/OpenCVConfig.cmake.in b/cmake/templates/OpenCVConfig.cmake.in index 24a9374ef8..e3bde4bbe3 100644 --- a/cmake/templates/OpenCVConfig.cmake.in +++ b/cmake/templates/OpenCVConfig.cmake.in @@ -12,7 +12,7 @@ # # Or you can search for specific OpenCV modules: # -# find_package(OpenCV REQUIRED core highgui) +# find_package(OpenCV REQUIRED core videoio) # # If the module is found then OPENCV__FOUND is set to TRUE. # diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index f81049495a..3f77a1bbe7 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -129,6 +129,9 @@ /* OpenNI library */ #cmakedefine HAVE_OPENNI +/* OpenNI library */ +#cmakedefine HAVE_OPENNI2 + /* PNG codec */ #cmakedefine HAVE_PNG diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 7c641cc260..957e69d00c 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -33,7 +33,7 @@ if(BUILD_DOCS AND HAVE_SPHINX) endif() endforeach() - set(FIXED_ORDER_MODULES core imgproc highgui video calib3d features2d objdetect ml flann photo stitching nonfree contrib legacy) + set(FIXED_ORDER_MODULES core imgproc imgcodecs videoio highgui video calib3d features2d objdetect ml flann photo stitching nonfree contrib legacy) list(REMOVE_ITEM BASE_MODULES ${FIXED_ORDER_MODULES}) diff --git a/doc/check_docs.py b/doc/check_docs.py index 0290fc70f4..96f62f068d 100755 --- a/doc/check_docs.py +++ b/doc/check_docs.py @@ -14,6 +14,8 @@ opencv_hdr_list = [ "../modules/video/include/opencv2/video/tracking.hpp", "../modules/video/include/opencv2/video/background_segm.hpp", "../modules/objdetect/include/opencv2/objdetect.hpp", +"../modules/imgcodecs/include/opencv2/imgcodecs.hpp", +"../modules/videoio/include/opencv2/videoio.hpp", "../modules/highgui/include/opencv2/highgui.hpp", ] @@ -24,6 +26,8 @@ opencv_module_list = [ "features2d", "video", "objdetect", +"imgcodecs", +"videoio", "highgui", "ml" ] diff --git a/doc/conf.py b/doc/conf.py index a0c231c3b5..a30ee8aea4 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -302,14 +302,16 @@ man_pages = [ extlinks = { 'basicstructures' : ('http://docs.opencv.org/modules/core/doc/basic_structures.html#%s', None), 'oldbasicstructures' : ('http://docs.opencv.org/modules/core/doc/old_basic_structures.html#%s', None), - 'readwriteimagevideo' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#%s', None), + 'readwriteimage' : ('http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html#%s', None), + 'readwritevideo' : ('http://docs.opencv.org/modules/videoio/doc/reading_and_writing_video.html#%s', None), 'operationsonarrays' : ('http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#%s', None), 'utilitysystemfunctions' : ('http://docs.opencv.org/modules/core/doc/utility_and_system_functions_and_macros.html#%s', None), 'imgprocfilter' : ('http://docs.opencv.org/modules/imgproc/doc/filtering.html#%s', None), 'svms' : ('http://docs.opencv.org/modules/ml/doc/support_vector_machines.html#%s', None), 'drawingfunc' : ('http://docs.opencv.org/modules/core/doc/drawing_functions.html#%s', None), 'xmlymlpers' : ('http://docs.opencv.org/modules/core/doc/xml_yaml_persistence.html#%s', None), - 'hgvideo' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#%s', None), + 'rwimg' : ('http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html#%s', None), + 'hgvideo' : ('http://docs.opencv.org/modules/videoio/doc/reading_and_writing_video.html#%s', None), 'gpuinit' : ('http://docs.opencv.org/modules/gpu/doc/initalization_and_information.html#%s', None), 'gpudatastructure' : ('http://docs.opencv.org/modules/gpu/doc/data_structures.html#%s', None), 'gpuopmatrices' : ('http://docs.opencv.org/modules/gpu/doc/operations_on_matrices.html#%s', None), @@ -329,8 +331,8 @@ extlinks = { 'how_to_contribute' : ('http://code.opencv.org/projects/opencv/wiki/How_to_contribute/%s', None), 'cvt_color' : ('http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html?highlight=cvtcolor#cvtcolor%s', None), - 'imread' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=imread#imread%s', None), - 'imwrite' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=imwrite#imwrite%s', None), + 'imread' : ('http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html?highlight=imread#imread%s', None), + 'imwrite' : ('http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html?highlight=imwrite#imwrite%s', None), 'imshow' : ('http://docs.opencv.org/modules/highgui/doc/user_interface.html?highlight=imshow#imshow%s', None), 'named_window' : ('http://docs.opencv.org/modules/highgui/doc/user_interface.html?highlight=namedwindow#namedwindow%s', None), 'wait_key' : ('http://docs.opencv.org/modules/highgui/doc/user_interface.html?highlight=waitkey#waitkey%s', None), @@ -418,7 +420,7 @@ extlinks = { 'background_subtractor' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractor#backgroundsubtractor%s', None), 'background_subtractor_mog' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractorMOG#backgroundsubtractormog%s', None), 'background_subtractor_mog_two' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractorMOG2#backgroundsubtractormog2%s', None), - 'video_capture' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=videocapture#videocapture%s', None), + 'video_capture' : ('http://docs.opencv.org/modules/videoio/doc/reading_and_writing_video.html?highlight=videocapture#videocapture%s', None), 'ippa_convert': ('http://docs.opencv.org/modules/core/doc/ipp_async_converters.html#%s', None), 'ptr':('http://docs.opencv.org/modules/core/doc/basic_structures.html?highlight=Ptr#Ptr%s', None) } diff --git a/doc/opencv_cheatsheet.tex b/doc/opencv_cheatsheet.tex index d6c339916d..01d5c275d9 100644 --- a/doc/opencv_cheatsheet.tex +++ b/doc/opencv_cheatsheet.tex @@ -522,9 +522,9 @@ samples on what are the contours and how to use them. \begin{tabbing} \textbf{Wr}\=\textbf{iting and reading raster images}\\ -\texttt{\href{http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html\#imwrite}{imwrite}("myimage.jpg", image);}\\ -\texttt{Mat image\_color\_copy = \href{http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html\#imread}{imread}("myimage.jpg", 1);}\\ -\texttt{Mat image\_grayscale\_copy = \href{http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html\#imread}{imread}("myimage.jpg", 0);}\\ +\texttt{\href{http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html\#imwrite}{imwrite}("myimage.jpg", image);}\\ +\texttt{Mat image\_color\_copy = \href{http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html\#imread}{imread}("myimage.jpg", 1);}\\ +\texttt{Mat image\_grayscale\_copy = \href{http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html\#imread}{imread}("myimage.jpg", 0);}\\ \end{tabbing} \emph{The functions can read/write images in the following formats: \textbf{BMP (.bmp), JPEG (.jpg, .jpeg), TIFF (.tif, .tiff), PNG (.png), PBM/PGM/PPM (.p?m), Sun Raster (.sr), JPEG 2000 (.jp2)}. Every format supports 8-bit, 1- or 3-channel images. Some formats (PNG, JPEG 2000) support 16 bits per channel.} diff --git a/doc/py_tutorials/py_gui/py_video_display/py_video_display.rst b/doc/py_tutorials/py_gui/py_video_display/py_video_display.rst index 56946bc5db..5bdf4fcb87 100644 --- a/doc/py_tutorials/py_gui/py_video_display/py_video_display.rst +++ b/doc/py_tutorials/py_gui/py_video_display/py_video_display.rst @@ -46,7 +46,7 @@ To capture a video, you need to create a **VideoCapture** object. Its argument c Sometimes, ``cap`` may not have initialized the capture. In that case, this code shows error. You can check whether it is initialized or not by the method **cap.isOpened()**. If it is True, OK. Otherwise open it using **cap.open()**. -You can also access some of the features of this video using **cap.get(propId)** method where propId is a number from 0 to 18. Each number denotes a property of the video (if it is applicable to that video) and full details can be seen here: `Property Identifier `_. Some of these values can be modified using **cap.set(propId, value)**. Value is the new value you want. +You can also access some of the features of this video using **cap.get(propId)** method where propId is a number from 0 to 18. Each number denotes a property of the video (if it is applicable to that video) and full details can be seen here: `Property Identifier `_. Some of these values can be modified using **cap.set(propId, value)**. Value is the new value you want. For example, I can check the frame width and height by ``cap.get(3)`` and ``cap.get(4)``. It gives me 640x480 by default. But I want to modify it to 320x240. Just use ``ret = cap.set(3,320)`` and ``ret = cap.set(4,240)``. diff --git a/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.rst b/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.rst index b7cf446687..ca3d75dca3 100644 --- a/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.rst +++ b/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.rst @@ -25,7 +25,7 @@ Here's a sample usage of :operationsonarrays:`dft() ` : :language: cpp :linenos: :tab-width: 4 - :lines: 1-3, 5, 19-20, 23-78 + :lines: 1-4, 6, 20-21, 24-79 Explanation =========== diff --git a/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst b/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst index b6a18fee88..6162985ddb 100644 --- a/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst +++ b/doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst @@ -45,7 +45,7 @@ The final argument is optional. If given the image will be loaded in gray scale .. literalinclude:: ../../../../samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp :language: cpp :tab-width: 4 - :lines: 48-60 + :lines: 49-61 Here we first use the C++ *stringstream* class to convert the third command line argument from text to an integer format. Then we use a simple look and the upper formula to calculate the lookup table. No OpenCV specific stuff here. @@ -99,7 +99,7 @@ When it comes to performance you cannot beat the classic C style operator[] (poi .. literalinclude:: ../../../../samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp :language: cpp :tab-width: 4 - :lines: 125-152 + :lines: 126-153 Here we basically just acquire a pointer to the start of each row and go through it until it ends. In the special case that the matrix is stored in a continues manner we only need to request the pointer a single time and go all the way to the end. We need to look out for color images: we have three channels so we need to pass through three times more items in each row. @@ -122,7 +122,7 @@ In case of the efficient way making sure that you pass through the right amount .. literalinclude:: ../../../../samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp :language: cpp :tab-width: 4 - :lines: 154-182 + :lines: 155-183 In case of color images we have three uchar items per column. This may be considered a short vector of uchar items, that has been baptized in OpenCV with the *Vec3b* name. To access the n-th sub column we use simple operator[] access. It's important to remember that OpenCV iterators go through the columns and automatically skip to the next row. Therefore in case of color images if you use a simple *uchar* iterator you'll be able to access only the blue channel values. @@ -134,7 +134,7 @@ The final method isn't recommended for scanning. It was made to acquire or modif .. literalinclude:: ../../../../samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp :language: cpp :tab-width: 4 - :lines: 184-216 + :lines: 185-217 The functions takes your input type and coordinates and calculates on the fly the address of the queried item. Then returns a reference to that. This may be a constant when you *get* the value and non-constant when you *set* the value. As a safety step in **debug mode only*** there is performed a check that your input coordinates are valid and does exist. If this isn't the case you'll get a nice output message of this on the standard error output stream. Compared to the efficient way in release mode the only difference in using this is that for every element of the image you'll get a new row pointer for what we use the C operator[] to acquire the column element. @@ -148,14 +148,14 @@ This is a bonus method of achieving lookup table modification in an image. Becau .. literalinclude:: ../../../../samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp :language: cpp :tab-width: 4 - :lines: 107-110 + :lines: 108-111 Finally call the function (I is our input image and J the output one): .. literalinclude:: ../../../../samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp :language: cpp :tab-width: 4 - :lines: 115 + :lines: 116 Performance Difference ====================== diff --git a/doc/tutorials/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.rst b/doc/tutorials/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.rst index 9285509b07..9d4189363f 100644 --- a/doc/tutorials/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.rst +++ b/doc/tutorials/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.rst @@ -77,7 +77,7 @@ Now that you have the basics done :download:`here's <../../../../samples/cpp/tut :language: cpp :linenos: :tab-width: 4 - :lines: 1-9, 22-25, 27-44 + :lines: 1-10, 23-26, 29-46 Here you can observe that with the new structure we have no pointer problems, although it is possible to use the old functions and in the end just transform the result to a *Mat* object. @@ -85,7 +85,7 @@ Here you can observe that with the new structure we have no pointer problems, al :language: cpp :linenos: :tab-width: 4 - :lines: 46-51 + :lines: 48-53 Because, we want to mess around with the images luma component we first convert from the default RGB to the YUV color space and then split the result up into separate planes. Here the program splits: in the first example it processes each plane using one of the three major image scanning algorithms in OpenCV (C [] operator, iterator, individual element access). In a second variant we add to the image some Gaussian noise and then mix together the channels according to some formula. @@ -95,7 +95,7 @@ The scanning version looks like: :language: cpp :linenos: :tab-width: 4 - :lines: 55-75 + :lines: 57-77 Here you can observe that we may go through all the pixels of an image in three fashions: an iterator, a C pointer and an individual element access style. You can read a more in-depth description of these in the :ref:`howToScanImagesOpenCV` tutorial. Converting from the old function names is easy. Just remove the cv prefix and use the new *Mat* data structure. Here's an example of this by using the weighted addition function: @@ -103,7 +103,7 @@ Here you can observe that we may go through all the pixels of an image in three :language: cpp :linenos: :tab-width: 4 - :lines: 79-112 + :lines: 81-113 As you may observe the *planes* variable is of type *Mat*. However, converting from *Mat* to *IplImage* is easy and made automatically with a simple assignment operator. @@ -111,7 +111,7 @@ As you may observe the *planes* variable is of type *Mat*. However, converting f :language: cpp :linenos: :tab-width: 4 - :lines: 115-127 + :lines: 117-129 The new *imshow* highgui function accepts both the *Mat* and *IplImage* data structures. Compile and run the program and if the first image below is your input you may get either the first or second as output: diff --git a/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst b/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst index 736aceb02d..67517d32f1 100644 --- a/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst +++ b/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst @@ -86,7 +86,7 @@ Each of the building components has their own valid domains. This leads to the d Creating a *Mat* object explicitly ================================== -In the :ref:`Load_Save_Image` tutorial you have already learned how to write a matrix to an image file by using the :readwriteimagevideo:`imwrite() ` function. However, for debugging purposes it's much more convenient to see the actual values. You can do this using the << operator of *Mat*. Be aware that this only works for two dimensional matrices. +In the :ref:`Load_Save_Image` tutorial you have already learned how to write a matrix to an image file by using the :readwriteimage:`imwrite() ` function. However, for debugging purposes it's much more convenient to see the actual values. You can do this using the << operator of *Mat*. Be aware that this only works for two dimensional matrices. Although *Mat* works really well as an image container, it is also a general matrix class. Therefore, it is possible to create and manipulate multidimensional matrices. You can create a Mat object in multiple ways: diff --git a/doc/tutorials/core/table_of_content_core/table_of_content_core.rst b/doc/tutorials/core/table_of_content_core/table_of_content_core.rst index 1fcf4ee2b1..ea5756da23 100644 --- a/doc/tutorials/core/table_of_content_core/table_of_content_core.rst +++ b/doc/tutorials/core/table_of_content_core/table_of_content_core.rst @@ -200,7 +200,6 @@ Here you will learn the about the basic building blocks of the library. A must r :height: 90pt :width: 90pt - =============== ====================================================== + .. tabularcolumns:: m{100pt} m{300pt} .. cssclass:: toctableopencv @@ -221,8 +220,6 @@ Here you will learn the about the basic building blocks of the library. A must r :width: 90pt .. |Author_ElenaG| unicode:: Elena U+0020 Gvozdeva - =============== ====================================================== - .. raw:: latex \pagebreak diff --git a/doc/tutorials/features2d/akaze_matching/akaze_matching.rst b/doc/tutorials/features2d/akaze_matching/akaze_matching.rst new file mode 100644 index 0000000000..3fe5df4f62 --- /dev/null +++ b/doc/tutorials/features2d/akaze_matching/akaze_matching.rst @@ -0,0 +1,161 @@ +.. _akazeMatching: + + +AKAZE local features matching +****************************** + +Introduction +------------------ + +In this tutorial we will learn how to use [AKAZE]_ local features to detect and match keypoints on two images. + +We will find keypoints on a pair of images with given homography matrix, +match them and count the number of inliers (i. e. matches that fit in the given homography). + +You can find expanded version of this example here: https://github.com/pablofdezalc/test_kaze_akaze_opencv + +.. [AKAZE] Fast Explicit Diffusion for Accelerated Features in Nonlinear Scale Spaces. Pablo F. Alcantarilla, Jesús Nuevo and Adrien Bartoli. In British Machine Vision Conference (BMVC), Bristol, UK, September 2013. + +Data +------------------ +We are going to use images 1 and 3 from *Graffity* sequence of Oxford dataset. + +.. image:: images/graf.png + :height: 200pt + :width: 320pt + :alt: Graffity + :align: center + +Homography is given by a 3 by 3 matrix: + +.. code-block:: none + + 7.6285898e-01 -2.9922929e-01 2.2567123e+02 + 3.3443473e-01 1.0143901e+00 -7.6999973e+01 + 3.4663091e-04 -1.4364524e-05 1.0000000e+00 + +You can find the images (*graf1.png*, *graf3.png*) and homography (*H1to3p.xml*) in *opencv/samples/cpp*. + +Source Code +=========== +.. literalinclude:: ../../../../samples/cpp/tutorial_code/features2D/AKAZE_match.cpp + :language: cpp + :linenos: + :tab-width: 4 + +Explanation +=========== + +1. **Load images and homography** + + .. code-block:: cpp + + Mat img1 = imread("graf1.png", IMREAD_GRAYSCALE); + Mat img2 = imread("graf3.png", IMREAD_GRAYSCALE); + + Mat homography; + FileStorage fs("H1to3p.xml", FileStorage::READ); + fs.getFirstTopLevelNode() >> homography; + + We are loading grayscale images here. Homography is stored in the xml created with FileStorage. + +2. **Detect keypoints and compute descriptors using AKAZE** + + .. code-block:: cpp + + vector kpts1, kpts2; + Mat desc1, desc2; + + AKAZE akaze; + akaze(img1, noArray(), kpts1, desc1); + akaze(img2, noArray(), kpts2, desc2); + + We create AKAZE object and use it's *operator()* functionality. Since we don't need the *mask* parameter, *noArray()* is used. + +3. **Use brute-force matcher to find 2-nn matches** + + .. code-block:: cpp + + BFMatcher matcher(NORM_HAMMING); + vector< vector > nn_matches; + matcher.knnMatch(desc1, desc2, nn_matches, 2); + + We use Hamming distance, because AKAZE uses binary descriptor by default. + +4. **Use 2-nn matches to find correct keypoint matches** + + .. code-block:: cpp + + for(size_t i = 0; i < nn_matches.size(); i++) { + DMatch first = nn_matches[i][0]; + float dist1 = nn_matches[i][0].distance; + float dist2 = nn_matches[i][1].distance; + + if(dist1 < nn_match_ratio * dist2) { + matched1.push_back(kpts1[first.queryIdx]); + matched2.push_back(kpts2[first.trainIdx]); + } + } + + If the closest match is *ratio* closer than the second closest one, then the match is correct. + +5. **Check if our matches fit in the homography model** + + .. code-block:: cpp + + for(int i = 0; i < matched1.size(); i++) { + Mat col = Mat::ones(3, 1, CV_64F); + col.at(0) = matched1[i].pt.x; + col.at(1) = matched1[i].pt.y; + + col = homography * col; + col /= col.at(2); + float dist = sqrt( pow(col.at(0) - matched2[i].pt.x, 2) + + pow(col.at(1) - matched2[i].pt.y, 2)); + + if(dist < inlier_threshold) { + int new_i = inliers1.size(); + inliers1.push_back(matched1[i]); + inliers2.push_back(matched2[i]); + good_matches.push_back(DMatch(new_i, new_i, 0)); + } + } + + If the distance from first keypoint's projection to the second keypoint is less than threshold, then it it fits in the homography. + + We create a new set of matches for the inliers, because it is required by the drawing function. + +6. **Output results** + + .. code-block:: cpp + + Mat res; + drawMatches(img1, inliers1, img2, inliers2, good_matches, res); + imwrite("res.png", res); + ... + + Here we save the resulting image and print some statistics. + +Results +======= + +Found matches +-------------- + +.. image:: images/res.png + :height: 200pt + :width: 320pt + :alt: Matches + :align: center + +A-KAZE Matching Results +-------------------------- +Keypoints 1: 2943 + +Keypoints 2: 3511 + +Matches: 447 + +Inliers: 308 + +Inliers Ratio: 0.689038 diff --git a/doc/tutorials/features2d/akaze_matching/images/graf.png b/doc/tutorials/features2d/akaze_matching/images/graf.png new file mode 100644 index 0000000000..d9213bcdf9 Binary files /dev/null and b/doc/tutorials/features2d/akaze_matching/images/graf.png differ diff --git a/doc/tutorials/features2d/akaze_matching/images/res.png b/doc/tutorials/features2d/akaze_matching/images/res.png new file mode 100644 index 0000000000..23fd5bd48f Binary files /dev/null and b/doc/tutorials/features2d/akaze_matching/images/res.png differ diff --git a/doc/tutorials/features2d/table_of_content_features2d/images/AKAZE_Match_Tutorial_Cover.png b/doc/tutorials/features2d/table_of_content_features2d/images/AKAZE_Match_Tutorial_Cover.png new file mode 100755 index 0000000000..fdf2007ba2 Binary files /dev/null and b/doc/tutorials/features2d/table_of_content_features2d/images/AKAZE_Match_Tutorial_Cover.png differ diff --git a/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst b/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst index f4107804bb..bb79ca32f9 100644 --- a/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst +++ b/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst @@ -183,6 +183,25 @@ Learn about how to use the feature points detectors, descriptors and matching f :height: 90pt :width: 90pt ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ===================== ============================================== + |AkazeMatch| **Title:** :ref:`akazeMatching` + + *Compatibility:* > OpenCV 3.0 + + *Author:* Fedor Morozov + + Use *AKAZE* local features to find correspondence between two images. + + ===================== ============================================== + + .. |AkazeMatch| image:: images/AKAZE_Match_Tutorial_Cover.png + :height: 90pt + :width: 90pt + .. raw:: latex \pagebreak @@ -201,3 +220,4 @@ Learn about how to use the feature points detectors, descriptors and matching f ../feature_flann_matcher/feature_flann_matcher ../feature_homography/feature_homography ../detection_of_planar_objects/detection_of_planar_objects + ../akaze_matching/akaze_matching diff --git a/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst b/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst index 6f5476cf05..8f63bf1a2e 100644 --- a/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst +++ b/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst @@ -22,7 +22,7 @@ As a test case where to show off these using OpenCV I've created a small program :language: cpp :linenos: :tab-width: 4 - :lines: 1-14, 28-29, 31-205 + :lines: 1-15, 29-31, 33-208 How to read a video stream (online-camera or offline-file)? =========================================================== diff --git a/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst b/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst index 248abdf6d2..0ad47863eb 100644 --- a/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst +++ b/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst @@ -656,7 +656,7 @@ classes we're going to use: Results: Stored in vars *1, *2, *3, an exception in *e user=> (import '[org.opencv.core Mat Size CvType] - '[org.opencv.highgui Highgui] + '[org.opencv.imgcodecs Imgcodecs] '[org.opencv.imgproc Imgproc]) org.opencv.imgproc.Imgproc diff --git a/doc/tutorials/introduction/desktop_java/java_dev_intro.rst b/doc/tutorials/introduction/desktop_java/java_dev_intro.rst index d5cb31f894..513f39d106 100644 --- a/doc/tutorials/introduction/desktop_java/java_dev_intro.rst +++ b/doc/tutorials/introduction/desktop_java/java_dev_intro.rst @@ -373,7 +373,7 @@ Now modify src/main/java/HelloOpenCV.java so it contains the following Java code import org.opencv.core.Point; import org.opencv.core.Rect; import org.opencv.core.Scalar; - import org.opencv.highgui.Highgui; + import org.opencv.imgcodecs.Imgcodecs; import org.opencv.objdetect.CascadeClassifier; // @@ -387,7 +387,7 @@ Now modify src/main/java/HelloOpenCV.java so it contains the following Java code // Create a face detector from the cascade file in the resources // directory. CascadeClassifier faceDetector = new CascadeClassifier(getClass().getResource("/lbpcascade_frontalface.xml").getPath()); - Mat image = Highgui.imread(getClass().getResource("/lena.png").getPath()); + Mat image = Imgcodecs.imread(getClass().getResource("/lena.png").getPath()); // Detect faces in the image. // MatOfRect is a special container class for Rect. @@ -404,7 +404,7 @@ Now modify src/main/java/HelloOpenCV.java so it contains the following Java code // Save the visualized detection. String filename = "faceDetection.png"; System.out.println(String.format("Writing %s", filename)); - Highgui.imwrite(filename, image); + Imgcodecs.imwrite(filename, image); } } diff --git a/doc/tutorials/introduction/display_image/display_image.rst b/doc/tutorials/introduction/display_image/display_image.rst index 6b30b7c295..fc6e6ca5cc 100644 --- a/doc/tutorials/introduction/display_image/display_image.rst +++ b/doc/tutorials/introduction/display_image/display_image.rst @@ -39,28 +39,28 @@ You'll almost always end up using the: .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp :tab-width: 4 - :lines: 1-3 + :lines: 1-4 We also include the *iostream* to facilitate console line output and input. To avoid data structure and function name conflicts with other libraries, OpenCV has its own namespace: *cv*. To avoid the need appending prior each of these the *cv::* keyword you can import the namespace in the whole file by using the lines: .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp :tab-width: 4 - :lines: 5-6 + :lines: 6-7 This is true for the STL library too (used for console I/O). Now, let's analyze the *main* function. We start up assuring that we acquire a valid image name argument from the command line. .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp :tab-width: 4 - :lines: 10-14 + :lines: 11-15 Then create a *Mat* object that will store the data of the loaded image. .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp :tab-width: 4 - :lines: 16 + :lines: 17 Now we call the :imread:`imread <>` function which loads the image name specified by the first argument (*argv[1]*). The second argument specifies the format in what we want the image. This may be: @@ -73,7 +73,7 @@ Now we call the :imread:`imread <>` function which loads the image name specifie .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp :tab-width: 4 - :lines: 17 + :lines: 18 .. note:: @@ -88,21 +88,21 @@ After checking that the image data was loaded correctly, we want to display our .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp - :lines: 25 + :lines: 26 :tab-width: 4 Finally, to update the content of the OpenCV window with a new image use the :imshow:`imshow <>` function. Specify the OpenCV window name to update and the image to use during this operation: .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp - :lines: 26 + :lines: 27 :tab-width: 4 Because we want our window to be displayed until the user presses a key (otherwise the program would end far too quickly), we use the :wait_key:`waitKey <>` function whose only parameter is just how long should it wait for a user input (measured in milliseconds). Zero means to wait forever. .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp - :lines: 28 + :lines: 29 :tab-width: 4 Result diff --git a/doc/tutorials/introduction/how_to_write_a_tutorial/how_to_write_a_tutorial.rst b/doc/tutorials/introduction/how_to_write_a_tutorial/how_to_write_a_tutorial.rst index 5ae5062492..7696be4a13 100644 --- a/doc/tutorials/introduction/how_to_write_a_tutorial/how_to_write_a_tutorial.rst +++ b/doc/tutorials/introduction/how_to_write_a_tutorial/how_to_write_a_tutorial.rst @@ -349,7 +349,7 @@ Now here's our recommendation for the structure of the tutorial (although, remem :language: cpp :linenos: :tab-width: 4 - :lines: 1-8, 21-22, 24- + :lines: 1-8, 21-23, 25- After the directive you specify a relative path to the file from what to import. It has four options: the language to use, if you add the ``:linenos:`` the line numbers will be shown, you can specify the tab size with the ``:tab-width:`` and you do not need to load the whole file, you can show just the important lines. Use the *lines* option to do not show redundant information (such as the *help* function). Here basically you specify ranges, if the second range line number is missing than that means that until the end of the file. The ranges specified here do no need to be in an ascending order, you may even reorganize the structure of how you want to show your sample inside the tutorial. @@ -361,16 +361,16 @@ Now here's our recommendation for the structure of the tutorial (although, remem # ---- External links for tutorials ----------------- extlinks = { - 'hgvideo' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#%s', None) + 'rwimg' : ('http://docs.opencv.org/modules/imgcodecs/doc/reading_and_writing_images.html#%s', None) } - In short here we defined a new **hgvideo** directive that refers to an external webpage link. Its usage is: + In short here we defined a new **rwimg** directive that refers to an external webpage link. Its usage is: .. code-block:: rst - A sample function of the highgui modules image write and read page is the :hgvideo:`imread() function `. + A sample function of the highgui modules image write and read page is the :rwimg:`imread() function `. - Which turns to: A sample function of the highgui modules image write and read page is the :hgvideo:`imread() function `. The argument you give between the <> will be put in place of the ``%s`` in the upper definition, and as the link will anchor to the correct function. To find out the anchor of a given function just open up a web page, search for the function and click on it. In the address bar it should appear like: ``http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#imread`` . Look here for the name of the directives for each page of the OpenCV reference manual. If none present for one of them feel free to add one for it. + Which turns to: A sample function of the highgui modules image write and read page is the :rwimg:`imread() function `. The argument you give between the <> will be put in place of the ``%s`` in the upper definition, and as the link will anchor to the correct function. To find out the anchor of a given function just open up a web page, search for the function and click on it. In the address bar it should appear like: ``http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images.html#imread`` . Look here for the name of the directives for each page of the OpenCV reference manual. If none present for one of them feel free to add one for it. For formulas you can add LATEX code that will translate in the web pages into images. You do this by using the *math* directive. A usage tip: diff --git a/doc/tutorials/introduction/load_save_image/load_save_image.rst b/doc/tutorials/introduction/load_save_image/load_save_image.rst index dec75c1958..cde81e12b5 100644 --- a/doc/tutorials/introduction/load_save_image/load_save_image.rst +++ b/doc/tutorials/introduction/load_save_image/load_save_image.rst @@ -5,7 +5,7 @@ Load, Modify, and Save an Image .. note:: - We assume that by now you know how to load an image using :readwriteimagevideo:`imread ` and to display it in a window (using :user_interface:`imshow `). Read the :ref:`Display_Image` tutorial otherwise. + We assume that by now you know how to load an image using :readwriteimage:`imread ` and to display it in a window (using :user_interface:`imshow `). Read the :ref:`Display_Image` tutorial otherwise. Goals ====== @@ -14,9 +14,9 @@ In this tutorial you will learn how to: .. container:: enumeratevisibleitemswithsquare - * Load an image using :readwriteimagevideo:`imread ` + * Load an image using :readwriteimage:`imread ` * Transform an image from BGR to Grayscale format by using :miscellaneous_transformations:`cvtColor ` - * Save your transformed image in a file on disk (using :readwriteimagevideo:`imwrite `) + * Save your transformed image in a file on disk (using :readwriteimage:`imwrite `) Code ====== @@ -62,7 +62,7 @@ Here it is: Explanation ============ -#. We begin by loading an image using :readwriteimagevideo:`imread `, located in the path given by *imageName*. For this example, assume you are loading a RGB image. +#. We begin by loading an image using :readwriteimage:`imread `, located in the path given by *imageName*. For this example, assume you are loading a RGB image. #. Now we are going to convert our image from BGR to Grayscale format. OpenCV has a really nice function to do this kind of transformations: @@ -76,9 +76,9 @@ Explanation * a source image (*image*) * a destination image (*gray_image*), in which we will save the converted image. - * an additional parameter that indicates what kind of transformation will be performed. In this case we use **CV_BGR2GRAY** (because of :readwriteimagevideo:`imread ` has BGR default channel order in case of color images). + * an additional parameter that indicates what kind of transformation will be performed. In this case we use **CV_BGR2GRAY** (because of :readwriteimage:`imread ` has BGR default channel order in case of color images). -#. So now we have our new *gray_image* and want to save it on disk (otherwise it will get lost after the program ends). To save it, we will use a function analagous to :readwriteimagevideo:`imread `: :readwriteimagevideo:`imwrite ` +#. So now we have our new *gray_image* and want to save it on disk (otherwise it will get lost after the program ends). To save it, we will use a function analagous to :readwriteimage:`imread `: :readwriteimage:`imwrite ` .. code-block:: cpp diff --git a/doc/tutorials/introduction/windows_visual_studio_image_watch/windows_visual_studio_image_watch.rst b/doc/tutorials/introduction/windows_visual_studio_image_watch/windows_visual_studio_image_watch.rst index 5a68dad5c9..21f679c971 100644 --- a/doc/tutorials/introduction/windows_visual_studio_image_watch/windows_visual_studio_image_watch.rst +++ b/doc/tutorials/introduction/windows_visual_studio_image_watch/windows_visual_studio_image_watch.rst @@ -32,7 +32,7 @@ Image Watch works with any existing project that uses OpenCV image objects (for #include // std::cout #include // cv::Mat - #include // cv::imread() + #include // cv::imread() #include // cv::Canny() using namespace std; diff --git a/doc/tutorials/ios/video_processing/video_processing.rst b/doc/tutorials/ios/video_processing/video_processing.rst index 84ccfcf971..eb5da5b111 100644 --- a/doc/tutorials/ios/video_processing/video_processing.rst +++ b/doc/tutorials/ios/video_processing/video_processing.rst @@ -80,7 +80,7 @@ We add a camera controller to the view controller and initialize it when the vie .. code-block:: objc :linenos: - #import + #import using namespace cv; diff --git a/doc/tutorials/ml/non_linear_svms/non_linear_svms.rst b/doc/tutorials/ml/non_linear_svms/non_linear_svms.rst index 57e0b1b6ea..bd7fde8775 100644 --- a/doc/tutorials/ml/non_linear_svms/non_linear_svms.rst +++ b/doc/tutorials/ml/non_linear_svms/non_linear_svms.rst @@ -73,7 +73,7 @@ You may also find the source code and these video file in the :file:`samples/cpp :language: cpp :linenos: :tab-width: 4 - :lines: 1-11, 22-23, 26- + :lines: 1-12, 23-24, 27- Explanation =========== diff --git a/doc/user_guide/ug_traincascade.rst b/doc/user_guide/ug_traincascade.rst index 601f504382..20c1b1683d 100644 --- a/doc/user_guide/ug_traincascade.rst +++ b/doc/user_guide/ug_traincascade.rst @@ -200,6 +200,12 @@ Command line arguments of ``opencv_traincascade`` application grouped by purpose This argument is actual in case of Haar-like features. If it is specified, the cascade will be saved in the old format. + * ``-numThreads `` + + Maximum number of threads to use during training. Notice that + the actual number of used threads may be lower, depending on + your machine and compilation options. + #. Cascade parameters: diff --git a/include/opencv2/opencv.hpp b/include/opencv2/opencv.hpp index b7c290a49c..fd9ca5898e 100644 --- a/include/opencv2/opencv.hpp +++ b/include/opencv2/opencv.hpp @@ -50,6 +50,8 @@ #include "opencv2/features2d.hpp" #include "opencv2/objdetect.hpp" #include "opencv2/calib3d.hpp" +#include "opencv2/imgcodecs.hpp" +#include "opencv2/videoio.hpp" #include "opencv2/highgui.hpp" #include "opencv2/ml.hpp" diff --git a/modules/calib3d/perf/perf_precomp.hpp b/modules/calib3d/perf/perf_precomp.hpp index 86312de1b8..9c5ab997d1 100644 --- a/modules/calib3d/perf/perf_precomp.hpp +++ b/modules/calib3d/perf/perf_precomp.hpp @@ -11,7 +11,7 @@ #include "opencv2/ts.hpp" #include "opencv2/calib3d.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" #include "opencv2/imgproc.hpp" #ifdef GTEST_CREATE_SHARED_LIBRARY diff --git a/modules/calib3d/test/test_precomp.hpp b/modules/calib3d/test/test_precomp.hpp index d670a4c220..ac3371cef3 100644 --- a/modules/calib3d/test/test_precomp.hpp +++ b/modules/calib3d/test/test_precomp.hpp @@ -13,7 +13,7 @@ #include "opencv2/ts.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/calib3d.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" namespace cvtest { diff --git a/modules/core/doc/basic_structures.rst b/modules/core/doc/basic_structures.rst index 459a6a9c98..a94fa1731e 100644 --- a/modules/core/doc/basic_structures.rst +++ b/modules/core/doc/basic_structures.rst @@ -2981,7 +2981,7 @@ The class provides the following features for all derived classes: * so called "virtual constructor". That is, each Algorithm derivative is registered at program start and you can get the list of registered algorithms and create instance of a particular algorithm by its name (see ``Algorithm::create``). If you plan to add your own algorithms, it is good practice to add a unique prefix to your algorithms to distinguish them from other algorithms. - * setting/retrieving algorithm parameters by name. If you used video capturing functionality from OpenCV highgui module, you are probably familar with ``cvSetCaptureProperty()``, ``cvGetCaptureProperty()``, ``VideoCapture::set()`` and ``VideoCapture::get()``. ``Algorithm`` provides similar method where instead of integer id's you specify the parameter names as text strings. See ``Algorithm::set`` and ``Algorithm::get`` for details. + * setting/retrieving algorithm parameters by name. If you used video capturing functionality from OpenCV videoio module, you are probably familar with ``cvSetCaptureProperty()``, ``cvGetCaptureProperty()``, ``VideoCapture::set()`` and ``VideoCapture::get()``. ``Algorithm`` provides similar method where instead of integer id's you specify the parameter names as text strings. See ``Algorithm::set`` and ``Algorithm::get`` for details. * reading and writing parameters from/to XML or YAML files. Every Algorithm derivative can store all its parameters and then read them back. There is no need to re-implement it each time. diff --git a/modules/core/doc/drawing_functions.rst b/modules/core/doc/drawing_functions.rst index 60b744f6df..e4f6d2f230 100644 --- a/modules/core/doc/drawing_functions.rst +++ b/modules/core/doc/drawing_functions.rst @@ -361,6 +361,37 @@ The function ``line`` draws the line segment between ``pt1`` and ``pt2`` points Antialiased lines are drawn using Gaussian filtering. +arrowedLine +---------------- +Draws a arrow segment pointing from the first point to the second one. + +.. ocv:function:: void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0, double tipLength=0.1) + + :param img: Image. + + :param pt1: The point the arrow starts from. + + :param pt2: The point the arrow points to. + + :param color: Line color. + + :param thickness: Line thickness. + + :param lineType: Type of the line: + + * **8** (or omitted) - 8-connected line. + + * **4** - 4-connected line. + + * **CV_AA** - antialiased line. + + :param shift: Number of fractional bits in the point coordinates. + + :param tipLength: The length of the arrow tip in relation to the arrow length + +The function ``arrowedLine`` draws an arrow between ``pt1`` and ``pt2`` points in the image. See also :ocv:func:`line`. + + LineIterator ------------ .. ocv:class:: LineIterator diff --git a/modules/core/doc/intro.rst b/modules/core/doc/intro.rst index 6d9fdfca5e..032e7453a5 100644 --- a/modules/core/doc/intro.rst +++ b/modules/core/doc/intro.rst @@ -14,7 +14,8 @@ OpenCV has a modular structure, which means that the package includes several sh * **calib3d** - basic multiple-view geometry algorithms, single and stereo camera calibration, object pose estimation, stereo correspondence algorithms, and elements of 3D reconstruction. * **features2d** - salient feature detectors, descriptors, and descriptor matchers. * **objdetect** - detection of objects and instances of the predefined classes (for example, faces, eyes, mugs, people, cars, and so on). - * **highgui** - an easy-to-use interface to video capturing, image and video codecs, as well as simple UI capabilities. + * **highgui** - an easy-to-use interface to simple UI capabilities. + * **videoio** - an easy-to-use interface to video capturing and video codecs. * **gpu** - GPU-accelerated algorithms from different OpenCV modules. * ... some other helper modules, such as FLANN and Google test wrappers, Python bindings, and others. diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index a5b9235152..b5249c9f50 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -510,6 +510,10 @@ CV_EXPORTS_W void randShuffle(InputOutputArray dst, double iterFactor = 1., RNG* CV_EXPORTS_W void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0); +//! draws an arrow from pt1 to pt2 in the image +CV_EXPORTS_W void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, + int thickness=1, int line_type=8, int shift=0, double tipLength=0.1); + //! draws the rectangle outline or a solid rectangle with the opposite corners pt1 and pt2 in the image CV_EXPORTS_W void rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index 8108a61e60..1f64cd2ace 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -244,6 +244,7 @@ typedef signed char schar; /* fundamental constants */ #define CV_PI 3.1415926535897932384626433832795 +#define CV_2PI 6.283185307179586476925286766559 #define CV_LOG2 0.69314718055994530941723212145818 /****************************************************************************************\ diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index d399265b0b..945b450303 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -360,7 +360,7 @@ struct CV_EXPORTS UMatData { enum { COPY_ON_MAP=1, HOST_COPY_OBSOLETE=2, DEVICE_COPY_OBSOLETE=4, TEMP_UMAT=8, TEMP_COPIED_UMAT=24, - USER_ALLOCATED=32 }; + USER_ALLOCATED=32, DEVICE_MEM_MAPPED=64}; UMatData(const MatAllocator* allocator); ~UMatData(); @@ -370,11 +370,13 @@ struct CV_EXPORTS UMatData bool hostCopyObsolete() const; bool deviceCopyObsolete() const; + bool deviceMemMapped() const; bool copyOnMap() const; bool tempUMat() const; bool tempCopiedUMat() const; void markHostCopyObsolete(bool flag); void markDeviceCopyObsolete(bool flag); + void markDeviceMemMapped(bool flag); const MatAllocator* prevAllocator; const MatAllocator* currAllocator; diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index d463eec671..dae0e137a8 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -3350,10 +3350,19 @@ size_t UMat::total() const inline bool UMatData::hostCopyObsolete() const { return (flags & HOST_COPY_OBSOLETE) != 0; } inline bool UMatData::deviceCopyObsolete() const { return (flags & DEVICE_COPY_OBSOLETE) != 0; } +inline bool UMatData::deviceMemMapped() const { return (flags & DEVICE_MEM_MAPPED) != 0; } inline bool UMatData::copyOnMap() const { return (flags & COPY_ON_MAP) != 0; } inline bool UMatData::tempUMat() const { return (flags & TEMP_UMAT) != 0; } inline bool UMatData::tempCopiedUMat() const { return (flags & TEMP_COPIED_UMAT) == TEMP_COPIED_UMAT; } +inline void UMatData::markDeviceMemMapped(bool flag) +{ + if(flag) + flags |= DEVICE_MEM_MAPPED; + else + flags &= ~DEVICE_MEM_MAPPED; +} + inline void UMatData::markHostCopyObsolete(bool flag) { if(flag) diff --git a/modules/core/perf/opencl/perf_arithm.cpp b/modules/core/perf/opencl/perf_arithm.cpp index 8488c1799b..17badca765 100644 --- a/modules/core/perf/opencl/perf_arithm.cpp +++ b/modules/core/perf/opencl/perf_arithm.cpp @@ -308,6 +308,23 @@ OCL_PERF_TEST_P(TransposeFixture, Transpose, ::testing::Combine( SANITY_CHECK(dst); } +OCL_PERF_TEST_P(TransposeFixture, TransposeInplace, ::testing::Combine( + OCL_PERF_ENUM(Size(640, 640), Size(1280, 1280), Size(2160, 2160)), OCL_TEST_TYPES_134)) +{ + const Size_MatType_t params = GetParam(); + const Size srcSize = get<0>(params); + const int type = get<1>(params); + + checkDeviceMaxMemoryAllocSize(srcSize, type); + + UMat src(srcSize, type); + declare.in(src, WARMUP_RNG).out(src, WARMUP_NONE); + + OCL_TEST_CYCLE() cv::transpose(src, src); + + SANITY_CHECK_NOTHING(); +} + ///////////// Flip //////////////////////// enum diff --git a/modules/core/perf/opencl/perf_dxt.cpp b/modules/core/perf/opencl/perf_dxt.cpp index d0219913b5..c0e41485e4 100644 --- a/modules/core/perf/opencl/perf_dxt.cpp +++ b/modules/core/perf/opencl/perf_dxt.cpp @@ -54,23 +54,42 @@ namespace ocl { ///////////// dft //////////////////////// -typedef tuple DftParams; +enum OCL_FFT_TYPE +{ + R2R = 0, + C2R = 1, + R2C = 2, + C2C = 3 +}; + +typedef tuple DftParams; typedef TestBaseWithParam DftFixture; -OCL_PERF_TEST_P(DftFixture, Dft, ::testing::Combine(Values(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), - Values((int)DFT_ROWS, (int)DFT_SCALE, (int)DFT_INVERSE, - (int)DFT_INVERSE | DFT_SCALE, (int)DFT_ROWS | DFT_INVERSE))) +OCL_PERF_TEST_P(DftFixture, Dft, ::testing::Combine(Values(C2C, R2R, C2R, R2C), + Values(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3, Size(512, 512), Size(1024, 1024), Size(2048, 2048)), + Values((int) 0, (int)DFT_ROWS, (int)DFT_SCALE, (int)DFT_INVERSE, + (int)DFT_INVERSE | DFT_SCALE, (int)DFT_ROWS | DFT_INVERSE))) { const DftParams params = GetParam(); - const Size srcSize = get<0>(params); - const int flags = get<1>(params); - - UMat src(srcSize, CV_32FC2), dst(srcSize, CV_32FC2); + const int dft_type = get<0>(params); + const Size srcSize = get<1>(params); + int flags = get<2>(params); + + int in_cn, out_cn; + switch (dft_type) + { + case R2R: flags |= cv::DFT_REAL_OUTPUT; in_cn = 1; out_cn = 1; break; + case C2R: flags |= cv::DFT_REAL_OUTPUT; in_cn = 2; out_cn = 2; break; + case R2C: flags |= cv::DFT_COMPLEX_OUTPUT; in_cn = 1; out_cn = 2; break; + case C2C: flags |= cv::DFT_COMPLEX_OUTPUT; in_cn = 2; out_cn = 2; break; + } + + UMat src(srcSize, CV_MAKE_TYPE(CV_32F, in_cn)), dst(srcSize, CV_MAKE_TYPE(CV_32F, out_cn)); declare.in(src, WARMUP_RNG).out(dst); - OCL_TEST_CYCLE() cv::dft(src, dst, flags | DFT_COMPLEX_OUTPUT); + OCL_TEST_CYCLE() cv::dft(src, dst, flags); - SANITY_CHECK(dst, 1e-3); + SANITY_CHECK(dst, 1e-5, ERROR_RELATIVE); } ///////////// MulSpectrums //////////////////////// diff --git a/modules/core/perf/opencl/perf_matop.cpp b/modules/core/perf/opencl/perf_matop.cpp index 9bb375587f..67d382239c 100644 --- a/modules/core/perf/opencl/perf_matop.cpp +++ b/modules/core/perf/opencl/perf_matop.cpp @@ -139,6 +139,7 @@ OCL_PERF_TEST_P(CopyToFixture, CopyToWithMaskUninit, dst.release(); startTimer(); src.copyTo(dst, mask); + cv::ocl::finish(); stopTimer(); } diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 10d8a1cd46..29501a0715 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -54,21 +54,23 @@ namespace cv struct NOP {}; -#if CV_SSE2 +#if CV_SSE2 || CV_NEON #define FUNCTOR_TEMPLATE(name) \ template struct name {} FUNCTOR_TEMPLATE(VLoadStore128); +#if CV_SSE2 FUNCTOR_TEMPLATE(VLoadStore64); FUNCTOR_TEMPLATE(VLoadStore128Aligned); +#endif #endif template void vBinOp(const T* src1, size_t step1, const T* src2, size_t step2, T* dst, size_t step, Size sz) { -#if CV_SSE2 +#if CV_SSE2 || CV_NEON VOp vop; #endif Op op; @@ -79,9 +81,11 @@ void vBinOp(const T* src1, size_t step1, const T* src2, size_t step2, T* dst, si { int x = 0; +#if CV_NEON || CV_SSE2 #if CV_SSE2 if( USE_SSE2 ) { +#endif for( ; x <= sz.width - 32/(int)sizeof(T); x += 32/sizeof(T) ) { typename VLoadStore128::reg_type r0 = VLoadStore128::load(src1 + x ); @@ -91,8 +95,10 @@ void vBinOp(const T* src1, size_t step1, const T* src2, size_t step2, T* dst, si VLoadStore128::store(dst + x , r0); VLoadStore128::store(dst + x + 16/sizeof(T), r1); } +#if CV_SSE2 } #endif +#endif #if CV_SSE2 if( USE_SSE2 ) { @@ -125,7 +131,7 @@ template void vBinOp32(const T* src1, size_t step1, const T* src2, size_t step2, T* dst, size_t step, Size sz) { -#if CV_SSE2 +#if CV_SSE2 || CV_NEON Op32 op32; #endif Op op; @@ -153,9 +159,11 @@ void vBinOp32(const T* src1, size_t step1, const T* src2, size_t step2, } } #endif +#if CV_NEON || CV_SSE2 #if CV_SSE2 if( USE_SSE2 ) { +#endif for( ; x <= sz.width - 8; x += 8 ) { typename VLoadStore128::reg_type r0 = VLoadStore128::load(src1 + x ); @@ -165,8 +173,10 @@ void vBinOp32(const T* src1, size_t step1, const T* src2, size_t step2, VLoadStore128::store(dst + x , r0); VLoadStore128::store(dst + x + 4, r1); } +#if CV_SSE2 } #endif +#endif #if CV_ENABLE_UNROLLED for( ; x <= sz.width - 4; x += 4 ) { @@ -383,7 +393,98 @@ FUNCTOR_TEMPLATE(VNot); FUNCTOR_CLOSURE_1arg(VNot, uchar, return _mm_xor_si128(_mm_set1_epi32(-1), a)); #endif -#if CV_SSE2 +#if CV_NEON + +#define FUNCTOR_LOADSTORE(name, template_arg, register_type, load_body, store_body)\ + template <> \ + struct name{ \ + typedef register_type reg_type; \ + static reg_type load(const template_arg * p) { return load_body (p);}; \ + static void store(template_arg * p, reg_type v) { store_body (p, v);}; \ + } + +#define FUNCTOR_CLOSURE_2arg(name, template_arg, body)\ + template<> \ + struct name \ + { \ + VLoadStore128::reg_type operator()( \ + VLoadStore128::reg_type a, \ + VLoadStore128::reg_type b) const \ + { \ + return body; \ + }; \ + } + +#define FUNCTOR_CLOSURE_1arg(name, template_arg, body)\ + template<> \ + struct name \ + { \ + VLoadStore128::reg_type operator()( \ + VLoadStore128::reg_type a, \ + VLoadStore128::reg_type ) const \ + { \ + return body; \ + }; \ + } + +FUNCTOR_LOADSTORE(VLoadStore128, uchar, uint8x16_t, vld1q_u8 , vst1q_u8 ); +FUNCTOR_LOADSTORE(VLoadStore128, schar, int8x16_t, vld1q_s8 , vst1q_s8 ); +FUNCTOR_LOADSTORE(VLoadStore128, ushort, uint16x8_t, vld1q_u16, vst1q_u16); +FUNCTOR_LOADSTORE(VLoadStore128, short, int16x8_t, vld1q_s16, vst1q_s16); +FUNCTOR_LOADSTORE(VLoadStore128, int, int32x4_t, vld1q_s32, vst1q_s32); +FUNCTOR_LOADSTORE(VLoadStore128, float, float32x4_t, vld1q_f32, vst1q_f32); + +FUNCTOR_TEMPLATE(VAdd); +FUNCTOR_CLOSURE_2arg(VAdd, uchar, vqaddq_u8 (a, b)); +FUNCTOR_CLOSURE_2arg(VAdd, schar, vqaddq_s8 (a, b)); +FUNCTOR_CLOSURE_2arg(VAdd, ushort, vqaddq_u16(a, b)); +FUNCTOR_CLOSURE_2arg(VAdd, short, vqaddq_s16(a, b)); +FUNCTOR_CLOSURE_2arg(VAdd, int, vaddq_s32 (a, b)); +FUNCTOR_CLOSURE_2arg(VAdd, float, vaddq_f32 (a, b)); + +FUNCTOR_TEMPLATE(VSub); +FUNCTOR_CLOSURE_2arg(VSub, uchar, vqsubq_u8 (a, b)); +FUNCTOR_CLOSURE_2arg(VSub, schar, vqsubq_s8 (a, b)); +FUNCTOR_CLOSURE_2arg(VSub, ushort, vqsubq_u16(a, b)); +FUNCTOR_CLOSURE_2arg(VSub, short, vqsubq_s16(a, b)); +FUNCTOR_CLOSURE_2arg(VSub, int, vsubq_s32 (a, b)); +FUNCTOR_CLOSURE_2arg(VSub, float, vsubq_f32 (a, b)); + +FUNCTOR_TEMPLATE(VMin); +FUNCTOR_CLOSURE_2arg(VMin, uchar, vminq_u8 (a, b)); +FUNCTOR_CLOSURE_2arg(VMin, schar, vminq_s8 (a, b)); +FUNCTOR_CLOSURE_2arg(VMin, ushort, vminq_u16(a, b)); +FUNCTOR_CLOSURE_2arg(VMin, short, vminq_s16(a, b)); +FUNCTOR_CLOSURE_2arg(VMin, int, vminq_s32(a, b)); +FUNCTOR_CLOSURE_2arg(VMin, float, vminq_f32(a, b)); + +FUNCTOR_TEMPLATE(VMax); +FUNCTOR_CLOSURE_2arg(VMax, uchar, vmaxq_u8 (a, b)); +FUNCTOR_CLOSURE_2arg(VMax, schar, vmaxq_s8 (a, b)); +FUNCTOR_CLOSURE_2arg(VMax, ushort, vmaxq_u16(a, b)); +FUNCTOR_CLOSURE_2arg(VMax, short, vmaxq_s16(a, b)); +FUNCTOR_CLOSURE_2arg(VMax, int, vmaxq_s32(a, b)); +FUNCTOR_CLOSURE_2arg(VMax, float, vmaxq_f32(a, b)); + +FUNCTOR_TEMPLATE(VAbsDiff); +FUNCTOR_CLOSURE_2arg(VAbsDiff, uchar, vabdq_u8 (a, b)); +FUNCTOR_CLOSURE_2arg(VAbsDiff, schar, vqabsq_s8 (vqsubq_s8(a, b))); +FUNCTOR_CLOSURE_2arg(VAbsDiff, ushort, vabdq_u16 (a, b)); +FUNCTOR_CLOSURE_2arg(VAbsDiff, short, vqabsq_s16(vqsubq_s16(a, b))); +FUNCTOR_CLOSURE_2arg(VAbsDiff, int, vabdq_s32 (a, b)); +FUNCTOR_CLOSURE_2arg(VAbsDiff, float, vabdq_f32 (a, b)); + +FUNCTOR_TEMPLATE(VAnd); +FUNCTOR_CLOSURE_2arg(VAnd, uchar, vandq_u8(a, b)); +FUNCTOR_TEMPLATE(VOr); +FUNCTOR_CLOSURE_2arg(VOr , uchar, vorrq_u8(a, b)); +FUNCTOR_TEMPLATE(VXor); +FUNCTOR_CLOSURE_2arg(VXor, uchar, veorq_u8(a, b)); +FUNCTOR_TEMPLATE(VNot); +FUNCTOR_CLOSURE_1arg(VNot, uchar, vmvnq_u8(a )); +#endif + +#if CV_SSE2 || CV_NEON #define IF_SIMD(op) op #else #define IF_SIMD(op) NOP @@ -2980,8 +3081,187 @@ void cv::compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op) namespace cv { -template static void -inRange_(const T* src1, size_t step1, const T* src2, size_t step2, +template +struct InRange_SSE +{ + int operator () (const T *, const T *, const T *, uchar *, int) const + { + return 0; + } +}; + +#if CV_SSE2 + +template <> +struct InRange_SSE +{ + int operator () (const uchar * src1, const uchar * src2, const uchar * src3, + uchar * dst, int len) const + { + int x = 0; + + if (USE_SSE2) + { + __m128i v_full = _mm_set1_epi8(-1), v_128 = _mm_set1_epi8(-128); + + for ( ; x <= len - 16; x += 16 ) + { + __m128i v_src = _mm_add_epi8(_mm_loadu_si128((const __m128i *)(src1 + x)), v_128); + __m128i v_mask1 = _mm_cmpgt_epi8(_mm_add_epi8(_mm_loadu_si128((const __m128i *)(src2 + x)), v_128), v_src); + __m128i v_mask2 = _mm_cmpgt_epi8(v_src, _mm_add_epi8(_mm_loadu_si128((const __m128i *)(src3 + x)), v_128)); + _mm_storeu_si128((__m128i *)(dst + x), _mm_andnot_si128(_mm_or_si128(v_mask1, v_mask2), v_full)); + } + } + + return x; + } +}; + +template <> +struct InRange_SSE +{ + int operator () (const schar * src1, const schar * src2, const schar * src3, + uchar * dst, int len) const + { + int x = 0; + + if (USE_SSE2) + { + __m128i v_full = _mm_set1_epi8(-1); + + for ( ; x <= len - 16; x += 16 ) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(src1 + x)); + __m128i v_mask1 = _mm_cmpgt_epi8(_mm_loadu_si128((const __m128i *)(src2 + x)), v_src); + __m128i v_mask2 = _mm_cmpgt_epi8(v_src, _mm_loadu_si128((const __m128i *)(src3 + x))); + _mm_storeu_si128((__m128i *)(dst + x), _mm_andnot_si128(_mm_or_si128(v_mask1, v_mask2), v_full)); + } + } + + return x; + } +}; + +template <> +struct InRange_SSE +{ + int operator () (const ushort * src1, const ushort * src2, const ushort * src3, + uchar * dst, int len) const + { + int x = 0; + + if (USE_SSE2) + { + __m128i v_zero = _mm_setzero_si128(), v_full = _mm_set1_epi16(-1), v_32768 = _mm_set1_epi16(-32768); + + for ( ; x <= len - 8; x += 8 ) + { + __m128i v_src = _mm_add_epi16(_mm_loadu_si128((const __m128i *)(src1 + x)), v_32768); + __m128i v_mask1 = _mm_cmpgt_epi16(_mm_add_epi16(_mm_loadu_si128((const __m128i *)(src2 + x)), v_32768), v_src); + __m128i v_mask2 = _mm_cmpgt_epi16(v_src, _mm_add_epi16(_mm_loadu_si128((const __m128i *)(src3 + x)), v_32768)); + __m128i v_res = _mm_andnot_si128(_mm_or_si128(v_mask1, v_mask2), v_full); + _mm_storel_epi64((__m128i *)(dst + x), _mm_packus_epi16(_mm_srli_epi16(v_res, 8), v_zero)); + } + } + + return x; + } +}; + +template <> +struct InRange_SSE +{ + int operator () (const short * src1, const short * src2, const short * src3, + uchar * dst, int len) const + { + int x = 0; + + if (USE_SSE2) + { + __m128i v_zero = _mm_setzero_si128(), v_full = _mm_set1_epi16(-1); + + for ( ; x <= len - 8; x += 8 ) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(src1 + x)); + __m128i v_mask1 = _mm_cmpgt_epi16(_mm_loadu_si128((const __m128i *)(src2 + x)), v_src); + __m128i v_mask2 = _mm_cmpgt_epi16(v_src, _mm_loadu_si128((const __m128i *)(src3 + x))); + __m128i v_res = _mm_andnot_si128(_mm_or_si128(v_mask1, v_mask2), v_full); + _mm_storel_epi64((__m128i *)(dst + x), _mm_packus_epi16(_mm_srli_epi16(v_res, 8), v_zero)); + } + } + + return x; + } +}; + +template <> +struct InRange_SSE +{ + int operator () (const int * src1, const int * src2, const int * src3, + uchar * dst, int len) const + { + int x = 0; + + if (USE_SSE2) + { + __m128i v_zero = _mm_setzero_si128(), v_full = _mm_set1_epi32(-1); + + for ( ; x <= len - 8; x += 8 ) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(src1 + x)); + __m128i v_res1 = _mm_or_si128(_mm_cmpgt_epi32(_mm_loadu_si128((const __m128i *)(src2 + x)), v_src), + _mm_cmpgt_epi32(v_src, _mm_loadu_si128((const __m128i *)(src3 + x)))); + + v_src = _mm_loadu_si128((const __m128i *)(src1 + x + 4)); + __m128i v_res2 = _mm_or_si128(_mm_cmpgt_epi32(_mm_loadu_si128((const __m128i *)(src2 + x + 4)), v_src), + _mm_cmpgt_epi32(v_src, _mm_loadu_si128((const __m128i *)(src3 + x + 4)))); + + __m128i v_res = _mm_packs_epi32(_mm_srli_epi32(_mm_andnot_si128(v_res1, v_full), 16), + _mm_srli_epi32(_mm_andnot_si128(v_res2, v_full), 16)); + _mm_storel_epi64((__m128i *)(dst + x), _mm_packus_epi16(v_res, v_zero)); + } + } + + return x; + } +}; + +template <> +struct InRange_SSE +{ + int operator () (const float * src1, const float * src2, const float * src3, + uchar * dst, int len) const + { + int x = 0; + + if (USE_SSE2) + { + __m128i v_zero = _mm_setzero_si128(); + + for ( ; x <= len - 8; x += 8 ) + { + __m128 v_src = _mm_loadu_ps(src1 + x); + __m128 v_res1 = _mm_and_ps(_mm_cmple_ps(_mm_loadu_ps(src2 + x), v_src), + _mm_cmple_ps(v_src, _mm_loadu_ps(src3 + x))); + + v_src = _mm_loadu_ps(src1 + x + 4); + __m128 v_res2 = _mm_and_ps(_mm_cmple_ps(_mm_loadu_ps(src2 + x + 4), v_src), + _mm_cmple_ps(v_src, _mm_loadu_ps(src3 + x + 4))); + + __m128i v_res1i = _mm_cvtps_epi32(v_res1), v_res2i = _mm_cvtps_epi32(v_res2); + __m128i v_res = _mm_packs_epi32(_mm_srli_epi32(v_res1i, 16), _mm_srli_epi32(v_res2i, 16)); + _mm_storel_epi64((__m128i *)(dst + x), _mm_packus_epi16(v_res, v_zero)); + } + } + + return x; + } +}; + +#endif + +template +static void inRange_(const T* src1, size_t step1, const T* src2, size_t step2, const T* src3, size_t step3, uchar* dst, size_t step, Size size) { @@ -2989,9 +3269,11 @@ inRange_(const T* src1, size_t step1, const T* src2, size_t step2, step2 /= sizeof(src2[0]); step3 /= sizeof(src3[0]); + InRange_SSE vop; + for( ; size.height--; src1 += step1, src2 += step2, src3 += step3, dst += step ) { - int x = 0; + int x = vop(src1, src2, src3, dst, size.width); #if CV_ENABLE_UNROLLED for( ; x <= size.width - 4; x += 4 ) { @@ -3132,9 +3414,16 @@ static bool ocl_inRange( InputArray _src, InputArray _lowerb, (!haveScalar && (sdepth != ldepth || sdepth != udepth)) ) return false; - ocl::Kernel ker("inrange", ocl::core::inrange_oclsrc, - format("%s-D cn=%d -D T=%s%s", haveScalar ? "-D HAVE_SCALAR " : "", - cn, ocl::typeToStr(sdepth), doubleSupport ? " -D DOUBLE_SUPPORT" : "")); + int kercn = haveScalar ? cn : std::max(std::min(ocl::predictOptimalVectorWidth(_src, _lowerb, _upperb, _dst), 4), cn); + if (kercn % cn != 0) + kercn = cn; + int colsPerWI = kercn / cn; + String opts = format("%s-D cn=%d -D srcT=%s -D srcT1=%s -D dstT=%s -D kercn=%d -D depth=%d%s -D colsPerWI=%d", + haveScalar ? "-D HAVE_SCALAR " : "", cn, ocl::typeToStr(CV_MAKE_TYPE(sdepth, kercn)), + ocl::typeToStr(sdepth), ocl::typeToStr(CV_8UC(colsPerWI)), kercn, sdepth, + doubleSupport ? " -D DOUBLE_SUPPORT" : "", colsPerWI); + + ocl::Kernel ker("inrange", ocl::core::inrange_oclsrc, opts); if (ker.empty()) return false; @@ -3182,7 +3471,7 @@ static bool ocl_inRange( InputArray _src, InputArray _lowerb, } ocl::KernelArg srcarg = ocl::KernelArg::ReadOnlyNoSize(src), - dstarg = ocl::KernelArg::WriteOnly(dst); + dstarg = ocl::KernelArg::WriteOnly(dst, 1, colsPerWI); if (haveScalar) { @@ -3196,7 +3485,7 @@ static bool ocl_inRange( InputArray _src, InputArray _lowerb, ker.args(srcarg, dstarg, ocl::KernelArg::ReadOnlyNoSize(lscalaru), ocl::KernelArg::ReadOnlyNoSize(uscalaru), rowsPerWI); - size_t globalsize[2] = { ssize.width, (ssize.height + rowsPerWI - 1) / rowsPerWI }; + size_t globalsize[2] = { ssize.width / colsPerWI, (ssize.height + rowsPerWI - 1) / rowsPerWI }; return ker.run(2, globalsize, NULL, false); } diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index 64d24304cd..21d5bdaca7 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -851,6 +851,175 @@ void cv::insertChannel(InputArray _src, InputOutputArray _dst, int coi) namespace cv { +template +struct cvtScaleAbs_SSE2 +{ + int operator () (const T *, DT *, int, WT, WT) const + { + return 0; + } +}; + +#if CV_SSE2 + +template <> +struct cvtScaleAbs_SSE2 +{ + int operator () (const uchar * src, uchar * dst, int width, + float scale, float shift) const + { + int x = 0; + + if (USE_SSE2) + { + __m128 v_scale = _mm_set1_ps(scale), v_shift = _mm_set1_ps(shift), + v_zero_f = _mm_setzero_ps(); + __m128i v_zero_i = _mm_setzero_si128(); + + for ( ; x <= width - 16; x += 16) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(src + x)); + __m128i v_src12 = _mm_unpacklo_epi8(v_src, v_zero_i), v_src_34 = _mm_unpackhi_epi8(v_src, v_zero_i); + __m128 v_dst1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(v_src12, v_zero_i)), v_scale), v_shift); + v_dst1 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst1), v_dst1); + __m128 v_dst2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(v_src12, v_zero_i)), v_scale), v_shift); + v_dst2 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst2), v_dst2); + __m128 v_dst3 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(v_src_34, v_zero_i)), v_scale), v_shift); + v_dst3 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst3), v_dst3); + __m128 v_dst4 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(v_src_34, v_zero_i)), v_scale), v_shift); + v_dst4 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst4), v_dst4); + + __m128i v_dst_i = _mm_packus_epi16(_mm_packs_epi32(_mm_cvtps_epi32(v_dst1), _mm_cvtps_epi32(v_dst2)), + _mm_packs_epi32(_mm_cvtps_epi32(v_dst3), _mm_cvtps_epi32(v_dst4))); + _mm_storeu_si128((__m128i *)(dst + x), v_dst_i); + } + } + + return x; + } +}; + +template <> +struct cvtScaleAbs_SSE2 +{ + int operator () (const ushort * src, uchar * dst, int width, + float scale, float shift) const + { + int x = 0; + + if (USE_SSE2) + { + __m128 v_scale = _mm_set1_ps(scale), v_shift = _mm_set1_ps(shift), + v_zero_f = _mm_setzero_ps(); + __m128i v_zero_i = _mm_setzero_si128(); + + for ( ; x <= width - 8; x += 8) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(src + x)); + __m128 v_dst1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(v_src, v_zero_i)), v_scale), v_shift); + v_dst1 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst1), v_dst1); + __m128 v_dst2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(v_src, v_zero_i)), v_scale), v_shift); + v_dst2 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst2), v_dst2); + + __m128i v_dst_i = _mm_packus_epi16(_mm_packs_epi32(_mm_cvtps_epi32(v_dst1), _mm_cvtps_epi32(v_dst2)), v_zero_i); + _mm_storel_epi64((__m128i *)(dst + x), v_dst_i); + } + } + + return x; + } +}; + +template <> +struct cvtScaleAbs_SSE2 +{ + int operator () (const short * src, uchar * dst, int width, + float scale, float shift) const + { + int x = 0; + + if (USE_SSE2) + { + __m128 v_scale = _mm_set1_ps(scale), v_shift = _mm_set1_ps(shift), + v_zero_f = _mm_setzero_ps(); + __m128i v_zero_i = _mm_setzero_si128(); + + for ( ; x <= width - 8; x += 8) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(src + x)); + __m128 v_dst1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_unpacklo_epi16(v_src, v_src), 16)), v_scale), v_shift); + v_dst1 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst1), v_dst1); + __m128 v_dst2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_unpackhi_epi16(v_src, v_src), 16)), v_scale), v_shift); + v_dst2 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst2), v_dst2); + + __m128i v_dst_i = _mm_packus_epi16(_mm_packs_epi32(_mm_cvtps_epi32(v_dst1), _mm_cvtps_epi32(v_dst2)), v_zero_i); + _mm_storel_epi64((__m128i *)(dst + x), v_dst_i); + } + } + + return x; + } +}; + +template <> +struct cvtScaleAbs_SSE2 +{ + int operator () (const int * src, uchar * dst, int width, + float scale, float shift) const + { + int x = 0; + + if (USE_SSE2) + { + __m128 v_scale = _mm_set1_ps(scale), v_shift = _mm_set1_ps(shift), + v_zero_f = _mm_setzero_ps(); + __m128i v_zero_i = _mm_setzero_si128(); + + for ( ; x <= width - 8; x += 4) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(src + x)); + __m128 v_dst1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(v_src), v_scale), v_shift); + v_dst1 = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst1), v_dst1); + + __m128i v_dst_i = _mm_packus_epi16(_mm_packs_epi32(_mm_cvtps_epi32(v_dst1), v_zero_i), v_zero_i); + _mm_storel_epi64((__m128i *)(dst + x), v_dst_i); + } + } + + return x; + } +}; + +template <> +struct cvtScaleAbs_SSE2 +{ + int operator () (const float * src, uchar * dst, int width, + float scale, float shift) const + { + int x = 0; + + if (USE_SSE2) + { + __m128 v_scale = _mm_set1_ps(scale), v_shift = _mm_set1_ps(shift), + v_zero_f = _mm_setzero_ps(); + __m128i v_zero_i = _mm_setzero_si128(); + + for ( ; x <= width - 8; x += 4) + { + __m128 v_dst = _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(src + x), v_scale), v_shift); + v_dst = _mm_max_ps(_mm_sub_ps(v_zero_f, v_dst), v_dst); + + __m128i v_dst_i = _mm_packs_epi32(_mm_cvtps_epi32(v_dst), v_zero_i); + _mm_storel_epi64((__m128i *)(dst + x), _mm_packus_epi16(v_dst_i, v_zero_i)); + } + } + + return x; + } +}; + +#endif + template static void cvtScaleAbs_( const T* src, size_t sstep, DT* dst, size_t dstep, Size size, @@ -858,10 +1027,12 @@ cvtScaleAbs_( const T* src, size_t sstep, { sstep /= sizeof(src[0]); dstep /= sizeof(dst[0]); + cvtScaleAbs_SSE2 vop; for( ; size.height--; src += sstep, dst += dstep ) { - int x = 0; + int x = vop(src, dst, size.width, scale, shift); + #if CV_ENABLE_UNROLLED for( ; x <= size.width - 4; x += 4 ) { @@ -879,7 +1050,6 @@ cvtScaleAbs_( const T* src, size_t sstep, } } - template static void cvtScale_( const T* src, size_t sstep, DT* dst, size_t dstep, Size size, @@ -1559,22 +1729,18 @@ static bool ocl_LUT(InputArray _src, InputArray _lut, OutputArray _dst) UMat src = _src.getUMat(), lut = _lut.getUMat(); _dst.create(src.size(), CV_MAKETYPE(ddepth, dcn)); UMat dst = _dst.getUMat(); - bool bAligned = (1 == lcn) && (0 == (src.offset % 4)) && (0 == ((dcn * src.cols) % 4)); - // dst.cols == src.cols by params of dst.create + int kercn = lcn == 1 ? std::min(4, ocl::predictOptimalVectorWidth(_dst)) : dcn; ocl::Kernel k("LUT", ocl::core::lut_oclsrc, - format("-D dcn=%d -D lcn=%d -D srcT=%s -D dstT=%s", bAligned ? 4 : dcn, lcn, - ocl::typeToStr(src.depth()), ocl::memopTypeToStr(ddepth) - )); + format("-D dcn=%d -D lcn=%d -D srcT=%s -D dstT=%s", kercn, lcn, + ocl::typeToStr(src.depth()), ocl::memopTypeToStr(ddepth))); if (k.empty()) return false; - int cols = bAligned ? dcn * dst.cols / 4 : dst.cols; - k.args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::ReadOnlyNoSize(lut), - ocl::KernelArg::WriteOnlyNoSize(dst), dst.rows, cols); + ocl::KernelArg::WriteOnly(dst, dcn, kercn)); - size_t globalSize[2] = { cols, (dst.rows + 3) / 4 }; + size_t globalSize[2] = { dst.cols * dcn / kercn, (dst.rows + 3) / 4 }; return k.run(2, globalSize, NULL, false); } diff --git a/modules/core/src/cuda_buffer_pool.cpp b/modules/core/src/cuda_buffer_pool.cpp index ea060a7c20..e5caf6ef25 100644 --- a/modules/core/src/cuda_buffer_pool.cpp +++ b/modules/core/src/cuda_buffer_pool.cpp @@ -207,7 +207,6 @@ namespace MemoryStack* MemoryPool::getFreeMemStack() { AutoLock lock(mtx_); - if (!initialized_) initilizeImpl(); @@ -256,22 +255,31 @@ namespace namespace { + Mutex mtx_; + bool memory_pool_manager_initialized; + class MemoryPoolManager { public: MemoryPoolManager(); ~MemoryPoolManager(); + void Init(); MemoryPool* getPool(int deviceId); private: std::vector pools_; - }; + } manager; + + //MemoryPoolManager ; MemoryPoolManager::MemoryPoolManager() { - int deviceCount = getCudaEnabledDeviceCount(); + } + void MemoryPoolManager::Init() + { + int deviceCount = getCudaEnabledDeviceCount(); if (deviceCount > 0) pools_.resize(deviceCount); } @@ -280,7 +288,7 @@ namespace { for (size_t i = 0; i < pools_.size(); ++i) { - cudaSetDevice(i); + cudaSetDevice(static_cast(i)); pools_[i].release(); } } @@ -293,7 +301,14 @@ namespace MemoryPool* memPool(int deviceId) { - static MemoryPoolManager manager; + { + AutoLock lock(mtx_); + if (!memory_pool_manager_initialized) + { + memory_pool_manager_initialized = true; + manager.Init(); + } + } return manager.getPool(deviceId); } } @@ -311,8 +326,10 @@ cv::cuda::StackAllocator::StackAllocator(cudaStream_t stream) : stream_(stream), if (enableMemoryPool) { const int deviceId = getDevice(); - memStack_ = memPool(deviceId)->getFreeMemStack(); - + { + AutoLock lock(mtx_); + memStack_ = memPool(deviceId)->getFreeMemStack(); + } DeviceInfo devInfo(deviceId); alignment_ = devInfo.textureAlignment(); } diff --git a/modules/core/src/cuda_stream.cpp b/modules/core/src/cuda_stream.cpp index 9f190c3fab..98a29df19b 100644 --- a/modules/core/src/cuda_stream.cpp +++ b/modules/core/src/cuda_stream.cpp @@ -190,10 +190,22 @@ void cv::cuda::Stream::enqueueHostCallback(StreamCallback callback, void* userDa #endif } +namespace +{ + bool default_stream_is_initialized; + Mutex mtx; + Ptr default_stream; +} + Stream& cv::cuda::Stream::Null() { - static Stream s(Ptr(new Impl(0))); - return s; + AutoLock lock(mtx); + if (!default_stream_is_initialized) + { + default_stream = Ptr(new Stream(Ptr(new Impl(0)))); + default_stream_is_initialized = true; + } + return *default_stream; } cv::cuda::Stream::operator bool_type() const diff --git a/modules/core/src/drawing.cpp b/modules/core/src/drawing.cpp index 0ba932163d..0b11aea7ea 100644 --- a/modules/core/src/drawing.cpp +++ b/modules/core/src/drawing.cpp @@ -1584,6 +1584,24 @@ void line( InputOutputArray _img, Point pt1, Point pt2, const Scalar& color, ThickLine( img, pt1, pt2, buf, thickness, line_type, 3, shift ); } +void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, + int thickness, int line_type, int shift, double tipLength) +{ + const double tipSize = norm(pt1-pt2)*tipLength; // Factor to normalize the size of the tip depending on the length of the arrow + + line(img, pt1, pt2, color, thickness, line_type, shift); + + const double angle = atan2( (double) pt1.y - pt2.y, (double) pt1.x - pt2.x ); + + Point p(cvRound(pt2.x + tipSize * cos(angle + CV_PI / 4)), + cvRound(pt2.y + tipSize * sin(angle + CV_PI / 4))); + line(img, p, pt2, color, thickness, line_type, shift); + + p.x = cvRound(pt2.x + tipSize * cos(angle - CV_PI / 4)); + p.y = cvRound(pt2.y + tipSize * sin(angle - CV_PI / 4)); + line(img, p, pt2, color, thickness, line_type, shift); +} + void rectangle( InputOutputArray _img, Point pt1, Point pt2, const Scalar& color, int thickness, int lineType, int shift ) diff --git a/modules/core/src/dxt.cpp b/modules/core/src/dxt.cpp index 2a08899167..bbe0f74001 100644 --- a/modules/core/src/dxt.cpp +++ b/modules/core/src/dxt.cpp @@ -43,6 +43,7 @@ #include "opencv2/core/opencl/runtime/opencl_clamdfft.hpp" #include "opencv2/core/opencl/runtime/opencl_core.hpp" #include "opencl_kernels.hpp" +#include namespace cv { @@ -1781,6 +1782,375 @@ static bool ippi_DFT_R_32F(const Mat& src, Mat& dst, bool inv, int norm_flag) #endif } +#ifdef HAVE_OPENCL + +namespace cv +{ + +enum FftType +{ + R2R = 0, // real to CCS in case forward transform, CCS to real otherwise + C2R = 1, // complex to real in case inverse transform + R2C = 2, // real to complex in case forward transform + C2C = 3 // complex to complex +}; + +struct OCL_FftPlan +{ +private: + UMat twiddles; + String buildOptions; + int thread_count; + bool status; + int dft_size; + +public: + OCL_FftPlan(int _size): dft_size(_size), status(true) + { + int min_radix; + std::vector radixes, blocks; + ocl_getRadixes(dft_size, radixes, blocks, min_radix); + thread_count = dft_size / min_radix; + + if (thread_count > (int) ocl::Device::getDefault().maxWorkGroupSize()) + { + status = false; + return; + } + + // generate string with radix calls + String radix_processing; + int n = 1, twiddle_size = 0; + for (size_t i=0; i 1) + radix_processing += format("fft_radix%d_B%d(smem,twiddles+%d,ind,%d,%d);", radix, block, twiddle_size, n, dft_size/radix); + else + radix_processing += format("fft_radix%d(smem,twiddles+%d,ind,%d,%d);", radix, twiddle_size, n, dft_size/radix); + twiddle_size += (radix-1)*n; + n *= radix; + } + + Mat tw(1, twiddle_size, CV_32FC2); + float* ptr = tw.ptr(); + int ptr_index = 0; + + n = 1; + for (size_t i=0; i& radixes, std::vector& blocks, int& min_radix) + { + int factors[34]; + int nf = DFTFactorize(cols, factors); + + int n = 1; + int factor_index = 0; + min_radix = INT_MAX; + + // 2^n transforms + if ((factors[factor_index] & 1) == 0) + { + for( ; n < factors[factor_index];) + { + int radix = 2, block = 1; + if (8*n <= factors[0]) + radix = 8; + else if (4*n <= factors[0]) + { + radix = 4; + if (cols % 12 == 0) + block = 3; + else if (cols % 8 == 0) + block = 2; + } + else + { + if (cols % 10 == 0) + block = 5; + else if (cols % 8 == 0) + block = 4; + else if (cols % 6 == 0) + block = 3; + else if (cols % 4 == 0) + block = 2; + } + + radixes.push_back(radix); + blocks.push_back(block); + min_radix = min(min_radix, block*radix); + n *= radix; + } + factor_index++; + } + + // all the other transforms + for( ; factor_index < nf; factor_index++) + { + int radix = factors[factor_index], block = 1; + if (radix == 3) + { + if (cols % 12 == 0) + block = 4; + else if (cols % 9 == 0) + block = 3; + else if (cols % 6 == 0) + block = 2; + } + else if (radix == 5) + { + if (cols % 10 == 0) + block = 2; + } + radixes.push_back(radix); + blocks.push_back(block); + min_radix = min(min_radix, block*radix); + } + } +}; + +class OCL_FftPlanCache +{ +public: + static OCL_FftPlanCache & getInstance() + { + static OCL_FftPlanCache planCache; + return planCache; + } + + Ptr getFftPlan(int dft_size) + { + std::map >::iterator f = planStorage.find(dft_size); + if (f != planStorage.end()) + { + return f->second; + } + else + { + Ptr newPlan = Ptr(new OCL_FftPlan(dft_size)); + planStorage[dft_size] = newPlan; + return newPlan; + } + } + + ~OCL_FftPlanCache() + { + planStorage.clear(); + } + +protected: + OCL_FftPlanCache() : + planStorage() + { + } + std::map > planStorage; +}; + +static bool ocl_dft_rows(InputArray _src, OutputArray _dst, int nonzero_rows, int flags, int fftType) +{ + Ptr plan = OCL_FftPlanCache::getInstance().getFftPlan(_src.cols()); + return plan->enqueueTransform(_src, _dst, nonzero_rows, flags, fftType, true); +} + +static bool ocl_dft_cols(InputArray _src, OutputArray _dst, int nonzero_cols, int flags, int fftType) +{ + Ptr plan = OCL_FftPlanCache::getInstance().getFftPlan(_src.rows()); + return plan->enqueueTransform(_src, _dst, nonzero_cols, flags, fftType, false); +} + +static bool ocl_dft(InputArray _src, OutputArray _dst, int flags, int nonzero_rows) +{ + int type = _src.type(), cn = CV_MAT_CN(type); + Size ssize = _src.size(); + if ( !(type == CV_32FC1 || type == CV_32FC2) ) + return false; + + // if is not a multiplication of prime numbers { 2, 3, 5 } + if (ssize.area() != getOptimalDFTSize(ssize.area())) + return false; + + UMat src = _src.getUMat(); + int complex_input = cn == 2 ? 1 : 0; + int complex_output = (flags & DFT_COMPLEX_OUTPUT) != 0; + int real_input = cn == 1 ? 1 : 0; + int real_output = (flags & DFT_REAL_OUTPUT) != 0; + bool inv = (flags & DFT_INVERSE) != 0 ? 1 : 0; + + if( nonzero_rows <= 0 || nonzero_rows > _src.rows() ) + nonzero_rows = _src.rows(); + bool is1d = (flags & DFT_ROWS) != 0 || nonzero_rows == 1; + + // if output format is not specified + if (complex_output + real_output == 0) + { + if (real_input) + real_output = 1; + else + complex_output = 1; + } + + FftType fftType = (FftType)(complex_input << 0 | complex_output << 1); + + // Forward Complex to CCS not supported + if (fftType == C2R && !inv) + fftType = C2C; + + // Inverse CCS to Complex not supported + if (fftType == R2C && inv) + fftType = R2R; + + UMat output; + if (fftType == C2C || fftType == R2C) + { + // complex output + _dst.create(src.size(), CV_32FC2); + output = _dst.getUMat(); + } + else + { + // real output + if (is1d) + { + _dst.create(src.size(), CV_32FC1); + output = _dst.getUMat(); + } + else + { + _dst.create(src.size(), CV_32FC1); + output.create(src.size(), CV_32FC2); + } + } + + if (!inv) + { + if (!ocl_dft_rows(src, output, nonzero_rows, flags, fftType)) + return false; + + if (!is1d) + { + int nonzero_cols = fftType == R2R ? output.cols/2 + 1 : output.cols; + if (!ocl_dft_cols(output, _dst, nonzero_cols, flags, fftType)) + return false; + } + } + else + { + if (fftType == C2C) + { + // complex output + if (!ocl_dft_rows(src, output, nonzero_rows, flags, fftType)) + return false; + + if (!is1d) + { + if (!ocl_dft_cols(output, output, output.cols, flags, fftType)) + return false; + } + } + else + { + if (is1d) + { + if (!ocl_dft_rows(src, output, nonzero_rows, flags, fftType)) + return false; + } + else + { + int nonzero_cols = src.cols/2 + 1; + if (!ocl_dft_cols(src, output, nonzero_cols, flags, fftType)) + return false; + + if (!ocl_dft_rows(output, _dst, nonzero_rows, flags, fftType)) + return false; + } + } + } + return true; +} + +} // namespace cv; + +#endif + #ifdef HAVE_CLAMDFFT namespace cv { @@ -1791,14 +2161,6 @@ namespace cv { CV_Assert(s == CLFFT_SUCCESS); \ } -enum FftType -{ - R2R = 0, // real to real - C2R = 1, // opencl HERMITIAN_INTERLEAVED to real - R2C = 2, // real to opencl HERMITIAN_INTERLEAVED - C2C = 3 // complex to complex -}; - class PlanCache { struct FftPlan @@ -1923,7 +2285,7 @@ public: } // no baked plan is found, so let's create a new one - FftPlan * newPlan = new FftPlan(dft_size, src_step, dst_step, doubleFP, inplace, flags, fftType); + Ptr newPlan = Ptr(new FftPlan(dft_size, src_step, dst_step, doubleFP, inplace, flags, fftType)); planStorage.push_back(newPlan); return newPlan->plHandle; @@ -1931,8 +2293,6 @@ public: ~PlanCache() { - for (std::vector::iterator i = planStorage.begin(), end = planStorage.end(); i != end; ++i) - delete (*i); planStorage.clear(); } @@ -1942,7 +2302,7 @@ protected: { } - std::vector planStorage; + std::vector > planStorage; }; extern "C" { @@ -1960,7 +2320,7 @@ static void CL_CALLBACK oclCleanupCallback(cl_event e, cl_int, void *p) } -static bool ocl_dft(InputArray _src, OutputArray _dst, int flags) +static bool ocl_dft_amdfft(InputArray _src, OutputArray _dst, int flags) { int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); Size ssize = _src.size(); @@ -2019,7 +2379,6 @@ static bool ocl_dft(InputArray _src, OutputArray _dst, int flags) tmpBuffer.addref(); clSetEventCallback(e, CL_COMPLETE, oclCleanupCallback, tmpBuffer.u); - return true; } @@ -2034,7 +2393,12 @@ void cv::dft( InputArray _src0, OutputArray _dst, int flags, int nonzero_rows ) #ifdef HAVE_CLAMDFFT CV_OCL_RUN(ocl::haveAmdFft() && ocl::Device::getDefault().type() != ocl::Device::TYPE_CPU && _dst.isUMat() && _src0.dims() <= 2 && nonzero_rows == 0, - ocl_dft(_src0, _dst, flags)) + ocl_dft_amdfft(_src0, _dst, flags)) +#endif + +#ifdef HAVE_OPENCL + CV_OCL_RUN(_dst.isUMat() && _src0.dims() <= 2, + ocl_dft(_src0, _dst, flags, nonzero_rows)) #endif static DFTFunc dft_tbl[6] = @@ -2046,10 +2410,8 @@ void cv::dft( InputArray _src0, OutputArray _dst, int flags, int nonzero_rows ) (DFTFunc)RealDFT_64f, (DFTFunc)CCSIDFT_64f }; - AutoBuffer buf; void *spec = 0; - Mat src0 = _src0.getMat(), src = src0; int prev_len = 0, stage = 0; bool inv = (flags & DFT_INVERSE) != 0; @@ -2080,32 +2442,32 @@ void cv::dft( InputArray _src0, OutputArray _dst, int flags, int nonzero_rows ) { if ((flags & DFT_ROWS) == 0) { - if (!real_transform) + if (src.channels() == 2 && !(inv && (flags & DFT_REAL_OUTPUT))) { - if (ippi_DFT_C_32F(src,dst, inv, ipp_norm_flag)) + if (ippi_DFT_C_32F(src, dst, inv, ipp_norm_flag)) return; setIppErrorStatus(); } - else if (inv || !(flags & DFT_COMPLEX_OUTPUT)) + if (src.channels() == 1 && (inv || !(flags & DFT_COMPLEX_OUTPUT))) { - if (ippi_DFT_R_32F(src,dst, inv, ipp_norm_flag)) + if (ippi_DFT_R_32F(src, dst, inv, ipp_norm_flag)) return; setIppErrorStatus(); } } else { - if (!real_transform) + if (src.channels() == 2 && !(inv && (flags & DFT_REAL_OUTPUT))) { ippiDFT_C_Func ippiFunc = inv ? (ippiDFT_C_Func)ippiDFTInv_CToC_32fc_C1R : (ippiDFT_C_Func)ippiDFTFwd_CToC_32fc_C1R; - if (Dft_C_IPPLoop(src,dst, IPPDFT_C_Functor(ippiFunc),ipp_norm_flag)) + if (Dft_C_IPPLoop(src, dst, IPPDFT_C_Functor(ippiFunc),ipp_norm_flag)) return; setIppErrorStatus(); } - else if (inv || !(flags & DFT_COMPLEX_OUTPUT)) + if (src.channels() == 1 && (inv || !(flags & DFT_COMPLEX_OUTPUT))) { ippiDFT_R_Func ippiFunc = inv ? (ippiDFT_R_Func)ippiDFTInv_PackToR_32f_C1R : (ippiDFT_R_Func)ippiDFTFwd_RToPack_32f_C1R; - if (Dft_R_IPPLoop(src,dst, IPPDFT_R_Functor(ippiFunc),ipp_norm_flag)) + if (Dft_R_IPPLoop(src, dst, IPPDFT_R_Functor(ippiFunc),ipp_norm_flag)) return; setIppErrorStatus(); } diff --git a/modules/core/src/mathfuncs.cpp b/modules/core/src/mathfuncs.cpp index 7a02bd6d72..f36e268d0d 100644 --- a/modules/core/src/mathfuncs.cpp +++ b/modules/core/src/mathfuncs.cpp @@ -348,7 +348,18 @@ static void InvSqrt_32f(const float* src, float* dst, int len) static void InvSqrt_64f(const double* src, double* dst, int len) { - for( int i = 0; i < len; i++ ) + int i = 0; + +#if CV_SSE2 + if (USE_SSE2) + { + __m128d v_1 = _mm_set1_pd(1.0); + for ( ; i <= len - 2; i += 2) + _mm_storeu_pd(dst + i, _mm_div_pd(v_1, _mm_sqrt_pd(_mm_loadu_pd(src + i)))); + } +#endif + + for( ; i < len; i++ ) dst[i] = 1/std::sqrt(src[i]); } @@ -2543,12 +2554,33 @@ void patchNaNs( InputOutputArray _a, double _val ) NAryMatIterator it(arrays, (uchar**)ptrs); size_t len = it.size*a.channels(); Cv32suf val; - val.f = (float)_val; + float fval = (float)_val; + val.f = fval; + +#if CV_SSE2 + __m128i v_mask1 = _mm_set1_epi32(0x7fffffff), v_mask2 = _mm_set1_epi32(0x7f800000); + __m128i v_val = _mm_set1_epi32(val.i); +#endif for( size_t i = 0; i < it.nplanes; i++, ++it ) { int* tptr = ptrs[0]; - for( size_t j = 0; j < len; j++ ) + size_t j = 0; + +#if CV_SSE2 + if (USE_SSE2) + { + for ( ; j < len; j += 4) + { + __m128i v_src = _mm_loadu_si128((__m128i const *)(tptr + j)); + __m128i v_cmp_mask = _mm_cmplt_epi32(v_mask2, _mm_and_si128(v_src, v_mask1)); + __m128i v_res = _mm_or_si128(_mm_andnot_si128(v_cmp_mask, v_src), _mm_and_si128(v_cmp_mask, v_val)); + _mm_storeu_si128((__m128i *)(tptr + j), v_res); + } + } +#endif + + for( ; j < len; j++ ) if( (tptr[j] & 0x7fffffff) > 0x7f800000 ) tptr[j] = val.i; } diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index 653efe63ae..ba6df7261a 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -2758,21 +2758,30 @@ namespace cv { static bool ocl_setIdentity( InputOutputArray _m, const Scalar& s ) { - int type = _m.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), - sctype = CV_MAKE_TYPE(depth, cn == 3 ? 4 : cn), + int type = _m.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), kercn = cn; + if (cn == 1) + { + kercn = std::min(ocl::predictOptimalVectorWidth(_m), 4); + if (kercn != 4) + kercn = 1; + } + int sctype = CV_MAKE_TYPE(depth, cn == 3 ? 4 : cn), rowsPerWI = ocl::Device::getDefault().isIntel() ? 4 : 1; ocl::Kernel k("setIdentity", ocl::core::set_identity_oclsrc, - format("-D T=%s -D T1=%s -D cn=%d -D ST=%s", ocl::memopTypeToStr(type), - ocl::memopTypeToStr(depth), cn, ocl::memopTypeToStr(sctype))); + format("-D T=%s -D T1=%s -D cn=%d -D ST=%s -D kercn=%d -D rowsPerWI=%d", + ocl::memopTypeToStr(CV_MAKE_TYPE(depth, kercn)), + ocl::memopTypeToStr(depth), cn, + ocl::memopTypeToStr(sctype), + kercn, rowsPerWI)); if (k.empty()) return false; UMat m = _m.getUMat(); - k.args(ocl::KernelArg::WriteOnly(m), ocl::KernelArg::Constant(Mat(1, 1, sctype, s)), - rowsPerWI); + k.args(ocl::KernelArg::WriteOnly(m, cn, kercn), + ocl::KernelArg::Constant(Mat(1, 1, sctype, s))); - size_t globalsize[2] = { m.cols, (m.rows + rowsPerWI - 1) / rowsPerWI }; + size_t globalsize[2] = { m.cols * cn / kercn, (m.rows + rowsPerWI - 1) / rowsPerWI }; return k.run(2, globalsize, NULL, false); } @@ -2973,8 +2982,10 @@ static inline int divUp(int a, int b) static bool ocl_transpose( InputArray _src, OutputArray _dst ) { + const ocl::Device & dev = ocl::Device::getDefault(); const int TILE_DIM = 32, BLOCK_ROWS = 8; - int type = _src.type(), cn = CV_MAT_CN(type), depth = CV_MAT_DEPTH(type); + int type = _src.type(), cn = CV_MAT_CN(type), depth = CV_MAT_DEPTH(type), + rowsPerWI = dev.isIntel() ? 4 : 1; UMat src = _src.getUMat(); _dst.create(src.cols, src.rows, type); @@ -2990,9 +3001,9 @@ static bool ocl_transpose( InputArray _src, OutputArray _dst ) } ocl::Kernel k(kernelName.c_str(), ocl::core::transpose_oclsrc, - format("-D T=%s -D T1=%s -D cn=%d -D TILE_DIM=%d -D BLOCK_ROWS=%d", + format("-D T=%s -D T1=%s -D cn=%d -D TILE_DIM=%d -D BLOCK_ROWS=%d -D rowsPerWI=%d", ocl::memopTypeToStr(type), ocl::memopTypeToStr(depth), - cn, TILE_DIM, BLOCK_ROWS)); + cn, TILE_DIM, BLOCK_ROWS, rowsPerWI)); if (k.empty()) return false; @@ -3002,8 +3013,14 @@ static bool ocl_transpose( InputArray _src, OutputArray _dst ) k.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::WriteOnlyNoSize(dst)); - size_t localsize[3] = { TILE_DIM, BLOCK_ROWS, 1 }; - size_t globalsize[3] = { src.cols, inplace ? src.rows : divUp(src.rows, TILE_DIM) * BLOCK_ROWS, 1 }; + size_t localsize[2] = { TILE_DIM, BLOCK_ROWS }; + size_t globalsize[2] = { src.cols, inplace ? (src.rows + rowsPerWI - 1) / rowsPerWI : (divUp(src.rows, TILE_DIM) * BLOCK_ROWS) }; + + if (inplace && dev.isIntel()) + { + localsize[0] = 16; + localsize[1] = dev.maxWorkGroupSize() / localsize[0]; + } return k.run(2, globalsize, localsize, false); } @@ -3433,8 +3450,11 @@ static bool ocl_reduce(InputArray _src, OutputArray _dst, const int min_opt_cols = 128, buf_cols = 32; int sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype), ddepth = CV_MAT_DEPTH(dtype), ddepth0 = ddepth; - bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0, - useOptimized = 1 == dim && _src.cols() > min_opt_cols; + const ocl::Device &defDev = ocl::Device::getDefault(); + bool doubleSupport = defDev.doubleFPConfig() > 0; + + size_t wgs = defDev.maxWorkGroupSize(); + bool useOptimized = 1 == dim && _src.cols() > min_opt_cols && (wgs >= buf_cols); if (!doubleSupport && (sdepth == CV_64F || ddepth == CV_64F)) return false; @@ -3447,78 +3467,80 @@ static bool ocl_reduce(InputArray _src, OutputArray _dst, const char * const ops[4] = { "OCL_CV_REDUCE_SUM", "OCL_CV_REDUCE_AVG", "OCL_CV_REDUCE_MAX", "OCL_CV_REDUCE_MIN" }; - char cvt[2][40]; - int wdepth = std::max(ddepth, CV_32F); - cv::String build_opt = format("-D %s -D dim=%d -D cn=%d -D ddepth=%d" - " -D srcT=%s -D dstT=%s -D dstT0=%s -D convertToWT=%s" - " -D convertToDT=%s -D convertToDT0=%s%s", - ops[op], dim, cn, ddepth, ocl::typeToStr(useOptimized ? ddepth : sdepth), - ocl::typeToStr(ddepth), ocl::typeToStr(ddepth0), - ocl::convertTypeStr(ddepth, wdepth, 1, cvt[0]), - ocl::convertTypeStr(sdepth, ddepth, 1, cvt[0]), - ocl::convertTypeStr(wdepth, ddepth0, 1, cvt[1]), - doubleSupport ? " -D DOUBLE_SUPPORT" : ""); - if (useOptimized) { - cv::String build_opt_pre = format("-D OP_REDUCE_PRE -D BUF_COLS=%d -D %s -D dim=1" - " -D cn=%d -D ddepth=%d -D srcT=%s -D dstT=%s -D convertToDT=%s%s", - buf_cols, ops[op], cn, ddepth, ocl::typeToStr(sdepth), ocl::typeToStr(ddepth), - ocl::convertTypeStr(sdepth, ddepth, 1, cvt[0]), - doubleSupport ? " -D DOUBLE_SUPPORT" : ""); - ocl::Kernel kpre("reduce_horz_pre", ocl::core::reduce2_oclsrc, build_opt_pre); - if (kpre.empty()) - return false; - - ocl::Kernel kmain("reduce", ocl::core::reduce2_oclsrc, build_opt); - if (kmain.empty()) + size_t tileHeight = (size_t)(wgs / buf_cols); + if (defDev.isIntel()) + { + static const size_t maxItemInGroupCount = 16; + tileHeight = min(tileHeight, defDev.localMemSize() / buf_cols / CV_ELEM_SIZE(CV_MAKETYPE(wdepth, cn)) / maxItemInGroupCount); + } + char cvt[3][40]; + cv::String build_opt = format("-D OP_REDUCE_PRE -D BUF_COLS=%d -D TILE_HEIGHT=%d -D %s -D dim=1" + " -D cn=%d -D ddepth=%d" + " -D srcT=%s -D bufT=%s -D dstT=%s" + " -D convertToWT=%s -D convertToBufT=%s -D convertToDT=%s%s", + buf_cols, tileHeight, ops[op], cn, ddepth, + ocl::typeToStr(sdepth), + ocl::typeToStr(ddepth), + ocl::typeToStr(ddepth0), + ocl::convertTypeStr(ddepth, wdepth, 1, cvt[0]), + ocl::convertTypeStr(sdepth, ddepth, 1, cvt[1]), + ocl::convertTypeStr(wdepth, ddepth0, 1, cvt[2]), + doubleSupport ? " -D DOUBLE_SUPPORT" : ""); + ocl::Kernel k("reduce_horz_opt", ocl::core::reduce2_oclsrc, build_opt); + if (k.empty()) return false; - UMat src = _src.getUMat(); Size dsize(1, src.rows); _dst.create(dsize, dtype); UMat dst = _dst.getUMat(); - UMat buf(src.rows, buf_cols, dst.type()); - - kpre.args(ocl::KernelArg::ReadOnly(src), - ocl::KernelArg::WriteOnlyNoSize(buf)); - - size_t globalSize[2] = { buf_cols, src.rows }; - if (!kpre.run(2, globalSize, NULL, false)) - return false; - if (op0 == CV_REDUCE_AVG) - kmain.args(ocl::KernelArg::ReadOnly(buf), - ocl::KernelArg::WriteOnlyNoSize(dst), 1.0f / src.cols); + k.args(ocl::KernelArg::ReadOnly(src), + ocl::KernelArg::WriteOnlyNoSize(dst), 1.0f / src.cols); else - kmain.args(ocl::KernelArg::ReadOnly(buf), - ocl::KernelArg::WriteOnlyNoSize(dst)); + k.args(ocl::KernelArg::ReadOnly(src), + ocl::KernelArg::WriteOnlyNoSize(dst)); - globalSize[0] = src.rows; - return kmain.run(1, globalSize, NULL, false); + size_t localSize[2] = { buf_cols, tileHeight}; + size_t globalSize[2] = { buf_cols, src.rows }; + return k.run(2, globalSize, localSize, false); } + else + { + char cvt[2][40]; + cv::String build_opt = format("-D %s -D dim=%d -D cn=%d -D ddepth=%d" + " -D srcT=%s -D dstT=%s -D dstT0=%s -D convertToWT=%s" + " -D convertToDT=%s -D convertToDT0=%s%s", + ops[op], dim, cn, ddepth, ocl::typeToStr(useOptimized ? ddepth : sdepth), + ocl::typeToStr(ddepth), ocl::typeToStr(ddepth0), + ocl::convertTypeStr(ddepth, wdepth, 1, cvt[0]), + ocl::convertTypeStr(sdepth, ddepth, 1, cvt[0]), + ocl::convertTypeStr(wdepth, ddepth0, 1, cvt[1]), + doubleSupport ? " -D DOUBLE_SUPPORT" : ""); + + ocl::Kernel k("reduce", ocl::core::reduce2_oclsrc, build_opt); + if (k.empty()) + return false; - ocl::Kernel k("reduce", ocl::core::reduce2_oclsrc, build_opt); - if (k.empty()) - return false; - - UMat src = _src.getUMat(); - Size dsize(dim == 0 ? src.cols : 1, dim == 0 ? 1 : src.rows); - _dst.create(dsize, dtype); - UMat dst = _dst.getUMat(); + UMat src = _src.getUMat(); + Size dsize(dim == 0 ? src.cols : 1, dim == 0 ? 1 : src.rows); + _dst.create(dsize, dtype); + UMat dst = _dst.getUMat(); - ocl::KernelArg srcarg = ocl::KernelArg::ReadOnly(src), - temparg = ocl::KernelArg::WriteOnlyNoSize(dst); + ocl::KernelArg srcarg = ocl::KernelArg::ReadOnly(src), + temparg = ocl::KernelArg::WriteOnlyNoSize(dst); - if (op0 == CV_REDUCE_AVG) - k.args(srcarg, temparg, 1.0f / (dim == 0 ? src.rows : src.cols)); - else - k.args(srcarg, temparg); + if (op0 == CV_REDUCE_AVG) + k.args(srcarg, temparg, 1.0f / (dim == 0 ? src.rows : src.cols)); + else + k.args(srcarg, temparg); - size_t globalsize = std::max(dsize.width, dsize.height); - return k.run(1, &globalsize, NULL, false); + size_t globalsize = std::max(dsize.width, dsize.height); + return k.run(1, &globalsize, NULL, false); + } } } diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index 24ca6ee4d3..32db8c91b4 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -1416,7 +1416,16 @@ bool useOpenCL() { CoreTLSData* data = coreTlsData.get(); if( data->useOpenCL < 0 ) - data->useOpenCL = (int)haveOpenCL() && Device::getDefault().ptr() != NULL; + { + try + { + data->useOpenCL = (int)haveOpenCL() && Device::getDefault().ptr() != NULL; + } + catch (...) + { + data->useOpenCL = 0; + } + } return data->useOpenCL > 0; } @@ -2228,7 +2237,8 @@ static cl_device_id selectOpenCLDevice() if (!isID) { deviceTypes.push_back("GPU"); - deviceTypes.push_back("CPU"); + if (configuration) + deviceTypes.push_back("CPU"); } else deviceTypes.push_back("ALL"); @@ -3484,9 +3494,8 @@ public: OpenCLBufferPoolImpl() : currentReservedSize(0), maxReservedSize(0) { - // Note: Buffer pool is disabled by default, - // because we didn't receive significant performance improvement - maxReservedSize = getConfigurationParameterForSize("OPENCV_OPENCL_BUFFERPOOL_LIMIT", 0); + int poolSize = ocl::Device::getDefault().isIntel() ? 1 << 27 : 0; + maxReservedSize = getConfigurationParameterForSize("OPENCV_OPENCL_BUFFERPOOL_LIMIT", poolSize); } virtual ~OpenCLBufferPoolImpl() { @@ -3729,6 +3738,7 @@ public: u->handle = clCreateBuffer(ctx_handle, CL_MEM_COPY_HOST_PTR|CL_MEM_READ_WRITE|createFlags, u->size, u->origdata, &retval); tempUMatFlags = UMatData::TEMP_COPIED_UMAT; + } if(!u->handle || retval != CL_SUCCESS) return false; @@ -3870,6 +3880,7 @@ public: if(u->data && retval == CL_SUCCESS) { u->markHostCopyObsolete(false); + u->markDeviceMemMapped(true); return; } @@ -3898,6 +3909,7 @@ public: if(!u) return; + CV_Assert(u->handle != 0); UMatDataAutoLock autolock(u); @@ -3908,8 +3920,10 @@ public: cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); cl_int retval = 0; - if( !u->copyOnMap() && u->data ) + if( !u->copyOnMap() && u->deviceMemMapped() ) { + CV_Assert(u->data != NULL); + u->markDeviceMemMapped(false); CV_Assert( (retval = clEnqueueUnmapMemObject(q, (cl_mem)u->handle, u->data, 0, 0, 0)) == CL_SUCCESS ); CV_OclDbgAssert(clFinish(q) == CL_SUCCESS); @@ -4427,11 +4441,13 @@ int predictOptimalVectorWidth(InputArray src1, InputArray src2, InputArray src3, d.preferredVectorWidthShort(), d.preferredVectorWidthShort(), d.preferredVectorWidthInt(), d.preferredVectorWidthFloat(), d.preferredVectorWidthDouble(), -1 }, kercn = vectorWidths[depth]; - if (d.isIntel()) + + // if the device says don't use vectors + if (vectorWidths[0] == 1) { // it's heuristic - int vectorWidthsIntel[] = { 16, 16, 8, 8, 1, 1, 1, -1 }; - kercn = vectorWidthsIntel[depth]; + int vectorWidthsOthers[] = { 16, 16, 8, 8, 1, 1, 1, -1 }; + kercn = vectorWidthsOthers[depth]; } if (ssize.width * cn < kercn || kercn <= 0) diff --git a/modules/core/src/opencl/fft.cl b/modules/core/src/opencl/fft.cl new file mode 100644 index 0000000000..1268c4d6e4 --- /dev/null +++ b/modules/core/src/opencl/fft.cl @@ -0,0 +1,864 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2014, Itseez, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#define SQRT_2 0.707106781188f +#define sin_120 0.866025403784f +#define fft5_2 0.559016994374f +#define fft5_3 -0.951056516295f +#define fft5_4 -1.538841768587f +#define fft5_5 0.363271264002f + +__attribute__((always_inline)) +float2 mul_float2(float2 a, float2 b) { + return (float2)(fma(a.x, b.x, -a.y * b.y), fma(a.x, b.y, a.y * b.x)); +} + +__attribute__((always_inline)) +float2 twiddle(float2 a) { + return (float2)(a.y, -a.x); +} + +__attribute__((always_inline)) +void butterfly2(float2 a0, float2 a1, __local float2* smem, __global const float2* twiddles, + const int x, const int block_size) +{ + const int k = x & (block_size - 1); + a1 = mul_float2(twiddles[k], a1); + const int dst_ind = (x << 1) - k; + + smem[dst_ind] = a0 + a1; + smem[dst_ind+block_size] = a0 - a1; +} + +__attribute__((always_inline)) +void butterfly4(float2 a0, float2 a1, float2 a2, float2 a3, __local float2* smem, __global const float2* twiddles, + const int x, const int block_size) +{ + const int k = x & (block_size - 1); + a1 = mul_float2(twiddles[k], a1); + a2 = mul_float2(twiddles[k + block_size], a2); + a3 = mul_float2(twiddles[k + 2*block_size], a3); + + const int dst_ind = ((x - k) << 2) + k; + + float2 b0 = a0 + a2; + a2 = a0 - a2; + float2 b1 = a1 + a3; + a3 = twiddle(a1 - a3); + + smem[dst_ind] = b0 + b1; + smem[dst_ind + block_size] = a2 + a3; + smem[dst_ind + 2*block_size] = b0 - b1; + smem[dst_ind + 3*block_size] = a2 - a3; +} + +__attribute__((always_inline)) +void butterfly3(float2 a0, float2 a1, float2 a2, __local float2* smem, __global const float2* twiddles, + const int x, const int block_size) +{ + const int k = x % block_size; + a1 = mul_float2(twiddles[k], a1); + a2 = mul_float2(twiddles[k+block_size], a2); + const int dst_ind = ((x - k) * 3) + k; + + float2 b1 = a1 + a2; + a2 = twiddle(sin_120*(a1 - a2)); + float2 b0 = a0 - (float2)(0.5f)*b1; + + smem[dst_ind] = a0 + b1; + smem[dst_ind + block_size] = b0 + a2; + smem[dst_ind + 2*block_size] = b0 - a2; +} + +__attribute__((always_inline)) +void butterfly5(float2 a0, float2 a1, float2 a2, float2 a3, float2 a4, __local float2* smem, __global const float2* twiddles, + const int x, const int block_size) +{ + const int k = x % block_size; + a1 = mul_float2(twiddles[k], a1); + a2 = mul_float2(twiddles[k + block_size], a2); + a3 = mul_float2(twiddles[k+2*block_size], a3); + a4 = mul_float2(twiddles[k+3*block_size], a4); + + const int dst_ind = ((x - k) * 5) + k; + __local float2* dst = smem + dst_ind; + + float2 b0, b1, b5; + + b1 = a1 + a4; + a1 -= a4; + + a4 = a3 + a2; + a3 -= a2; + + a2 = b1 + a4; + b0 = a0 - (float2)0.25f * a2; + + b1 = fft5_2 * (b1 - a4); + a4 = fft5_3 * (float2)(-a1.y - a3.y, a1.x + a3.x); + b5 = (float2)(a4.x - fft5_5 * a1.y, a4.y + fft5_5 * a1.x); + + a4.x += fft5_4 * a3.y; + a4.y -= fft5_4 * a3.x; + + a1 = b0 + b1; + b0 -= b1; + + dst[0] = a0 + a2; + dst[block_size] = a1 + a4; + dst[2 * block_size] = b0 + b5; + dst[3 * block_size] = b0 - b5; + dst[4 * block_size] = a1 - a4; +} + +__attribute__((always_inline)) +void fft_radix2(__local float2* smem, __global const float2* twiddles, const int x, const int block_size, const int t) +{ + float2 a0, a1; + + if (x < t) + { + a0 = smem[x]; + a1 = smem[x+t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x < t) + butterfly2(a0, a1, smem, twiddles, x, block_size); + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix2_B2(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int x2 = x1 + t/2; + float2 a0, a1, a2, a3; + + if (x1 < t/2) + { + a0 = smem[x1]; a1 = smem[x1+t]; + a2 = smem[x2]; a3 = smem[x2+t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/2) + { + butterfly2(a0, a1, smem, twiddles, x1, block_size); + butterfly2(a2, a3, smem, twiddles, x2, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix2_B3(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int x2 = x1 + t/3; + const int x3 = x1 + 2*t/3; + float2 a0, a1, a2, a3, a4, a5; + + if (x1 < t/3) + { + a0 = smem[x1]; a1 = smem[x1+t]; + a2 = smem[x2]; a3 = smem[x2+t]; + a4 = smem[x3]; a5 = smem[x3+t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/3) + { + butterfly2(a0, a1, smem, twiddles, x1, block_size); + butterfly2(a2, a3, smem, twiddles, x2, block_size); + butterfly2(a4, a5, smem, twiddles, x3, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix2_B4(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int thread_block = t/4; + const int x2 = x1 + thread_block; + const int x3 = x1 + 2*thread_block; + const int x4 = x1 + 3*thread_block; + float2 a0, a1, a2, a3, a4, a5, a6, a7; + + if (x1 < t/4) + { + a0 = smem[x1]; a1 = smem[x1+t]; + a2 = smem[x2]; a3 = smem[x2+t]; + a4 = smem[x3]; a5 = smem[x3+t]; + a6 = smem[x4]; a7 = smem[x4+t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/4) + { + butterfly2(a0, a1, smem, twiddles, x1, block_size); + butterfly2(a2, a3, smem, twiddles, x2, block_size); + butterfly2(a4, a5, smem, twiddles, x3, block_size); + butterfly2(a6, a7, smem, twiddles, x4, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix2_B5(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int thread_block = t/5; + const int x2 = x1 + thread_block; + const int x3 = x1 + 2*thread_block; + const int x4 = x1 + 3*thread_block; + const int x5 = x1 + 4*thread_block; + float2 a0, a1, a2, a3, a4, a5, a6, a7, a8, a9; + + if (x1 < t/5) + { + a0 = smem[x1]; a1 = smem[x1+t]; + a2 = smem[x2]; a3 = smem[x2+t]; + a4 = smem[x3]; a5 = smem[x3+t]; + a6 = smem[x4]; a7 = smem[x4+t]; + a8 = smem[x5]; a9 = smem[x5+t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/5) + { + butterfly2(a0, a1, smem, twiddles, x1, block_size); + butterfly2(a2, a3, smem, twiddles, x2, block_size); + butterfly2(a4, a5, smem, twiddles, x3, block_size); + butterfly2(a6, a7, smem, twiddles, x4, block_size); + butterfly2(a8, a9, smem, twiddles, x5, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix4(__local float2* smem, __global const float2* twiddles, const int x, const int block_size, const int t) +{ + float2 a0, a1, a2, a3; + + if (x < t) + { + a0 = smem[x]; a1 = smem[x+t]; a2 = smem[x+2*t]; a3 = smem[x+3*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x < t) + butterfly4(a0, a1, a2, a3, smem, twiddles, x, block_size); + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix4_B2(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int x2 = x1 + t/2; + float2 a0, a1, a2, a3, a4, a5, a6, a7; + + if (x1 < t/2) + { + a0 = smem[x1]; a1 = smem[x1+t]; a2 = smem[x1+2*t]; a3 = smem[x1+3*t]; + a4 = smem[x2]; a5 = smem[x2+t]; a6 = smem[x2+2*t]; a7 = smem[x2+3*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/2) + { + butterfly4(a0, a1, a2, a3, smem, twiddles, x1, block_size); + butterfly4(a4, a5, a6, a7, smem, twiddles, x2, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix4_B3(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int x2 = x1 + t/3; + const int x3 = x2 + t/3; + float2 a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11; + + if (x1 < t/3) + { + a0 = smem[x1]; a1 = smem[x1+t]; a2 = smem[x1+2*t]; a3 = smem[x1+3*t]; + a4 = smem[x2]; a5 = smem[x2+t]; a6 = smem[x2+2*t]; a7 = smem[x2+3*t]; + a8 = smem[x3]; a9 = smem[x3+t]; a10 = smem[x3+2*t]; a11 = smem[x3+3*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/3) + { + butterfly4(a0, a1, a2, a3, smem, twiddles, x1, block_size); + butterfly4(a4, a5, a6, a7, smem, twiddles, x2, block_size); + butterfly4(a8, a9, a10, a11, smem, twiddles, x3, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix8(__local float2* smem, __global const float2* twiddles, const int x, const int block_size, const int t) +{ + const int k = x % block_size; + float2 a0, a1, a2, a3, a4, a5, a6, a7; + + if (x < t) + { + int tw_ind = block_size / 8; + + a0 = smem[x]; + a1 = mul_float2(twiddles[k], smem[x + t]); + a2 = mul_float2(twiddles[k + block_size],smem[x+2*t]); + a3 = mul_float2(twiddles[k+2*block_size],smem[x+3*t]); + a4 = mul_float2(twiddles[k+3*block_size],smem[x+4*t]); + a5 = mul_float2(twiddles[k+4*block_size],smem[x+5*t]); + a6 = mul_float2(twiddles[k+5*block_size],smem[x+6*t]); + a7 = mul_float2(twiddles[k+6*block_size],smem[x+7*t]); + + float2 b0, b1, b6, b7; + + b0 = a0 + a4; + a4 = a0 - a4; + b1 = a1 + a5; + a5 = a1 - a5; + a5 = (float2)(SQRT_2) * (float2)(a5.x + a5.y, -a5.x + a5.y); + b6 = twiddle(a2 - a6); + a2 = a2 + a6; + b7 = a3 - a7; + b7 = (float2)(SQRT_2) * (float2)(-b7.x + b7.y, -b7.x - b7.y); + a3 = a3 + a7; + + a0 = b0 + a2; + a2 = b0 - a2; + a1 = b1 + a3; + a3 = twiddle(b1 - a3); + a6 = a4 - b6; + a4 = a4 + b6; + a7 = twiddle(a5 - b7); + a5 = a5 + b7; + + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x < t) + { + const int dst_ind = ((x - k) << 3) + k; + __local float2* dst = smem + dst_ind; + + dst[0] = a0 + a1; + dst[block_size] = a4 + a5; + dst[2 * block_size] = a2 + a3; + dst[3 * block_size] = a6 + a7; + dst[4 * block_size] = a0 - a1; + dst[5 * block_size] = a4 - a5; + dst[6 * block_size] = a2 - a3; + dst[7 * block_size] = a6 - a7; + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix3(__local float2* smem, __global const float2* twiddles, const int x, const int block_size, const int t) +{ + float2 a0, a1, a2; + + if (x < t) + { + a0 = smem[x]; a1 = smem[x+t]; a2 = smem[x+2*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x < t) + butterfly3(a0, a1, a2, smem, twiddles, x, block_size); + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix3_B2(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int x2 = x1 + t/2; + float2 a0, a1, a2, a3, a4, a5; + + if (x1 < t/2) + { + a0 = smem[x1]; a1 = smem[x1+t]; a2 = smem[x1+2*t]; + a3 = smem[x2]; a4 = smem[x2+t]; a5 = smem[x2+2*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/2) + { + butterfly3(a0, a1, a2, smem, twiddles, x1, block_size); + butterfly3(a3, a4, a5, smem, twiddles, x2, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix3_B3(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int x2 = x1 + t/3; + const int x3 = x2 + t/3; + float2 a0, a1, a2, a3, a4, a5, a6, a7, a8; + + if (x1 < t/2) + { + a0 = smem[x1]; a1 = smem[x1+t]; a2 = smem[x1+2*t]; + a3 = smem[x2]; a4 = smem[x2+t]; a5 = smem[x2+2*t]; + a6 = smem[x3]; a7 = smem[x3+t]; a8 = smem[x3+2*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/2) + { + butterfly3(a0, a1, a2, smem, twiddles, x1, block_size); + butterfly3(a3, a4, a5, smem, twiddles, x2, block_size); + butterfly3(a6, a7, a8, smem, twiddles, x3, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix3_B4(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int thread_block = t/4; + const int x2 = x1 + thread_block; + const int x3 = x1 + 2*thread_block; + const int x4 = x1 + 3*thread_block; + float2 a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11; + + if (x1 < t/4) + { + a0 = smem[x1]; a1 = smem[x1+t]; a2 = smem[x1+2*t]; + a3 = smem[x2]; a4 = smem[x2+t]; a5 = smem[x2+2*t]; + a6 = smem[x3]; a7 = smem[x3+t]; a8 = smem[x3+2*t]; + a9 = smem[x4]; a10 = smem[x4+t]; a11 = smem[x4+2*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/4) + { + butterfly3(a0, a1, a2, smem, twiddles, x1, block_size); + butterfly3(a3, a4, a5, smem, twiddles, x2, block_size); + butterfly3(a6, a7, a8, smem, twiddles, x3, block_size); + butterfly3(a9, a10, a11, smem, twiddles, x4, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix5(__local float2* smem, __global const float2* twiddles, const int x, const int block_size, const int t) +{ + const int k = x % block_size; + float2 a0, a1, a2, a3, a4; + + if (x < t) + { + a0 = smem[x]; a1 = smem[x + t]; a2 = smem[x+2*t]; a3 = smem[x+3*t]; a4 = smem[x+4*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x < t) + butterfly5(a0, a1, a2, a3, a4, smem, twiddles, x, block_size); + + barrier(CLK_LOCAL_MEM_FENCE); +} + +__attribute__((always_inline)) +void fft_radix5_B2(__local float2* smem, __global const float2* twiddles, const int x1, const int block_size, const int t) +{ + const int x2 = x1+t/2; + float2 a0, a1, a2, a3, a4, a5, a6, a7, a8, a9; + + if (x1 < t/2) + { + a0 = smem[x1]; a1 = smem[x1 + t]; a2 = smem[x1+2*t]; a3 = smem[x1+3*t]; a4 = smem[x1+4*t]; + a5 = smem[x2]; a6 = smem[x2 + t]; a7 = smem[x2+2*t]; a8 = smem[x2+3*t]; a9 = smem[x2+4*t]; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x1 < t/2) + { + butterfly5(a0, a1, a2, a3, a4, smem, twiddles, x1, block_size); + butterfly5(a5, a6, a7, a8, a9, smem, twiddles, x2, block_size); + } + + barrier(CLK_LOCAL_MEM_FENCE); +} + +#ifdef DFT_SCALE +#define SCALE_VAL(x, scale) x*scale +#else +#define SCALE_VAL(x, scale) x +#endif + +__kernel void fft_multi_radix_rows(__global const uchar* src_ptr, int src_step, int src_offset, int src_rows, int src_cols, + __global uchar* dst_ptr, int dst_step, int dst_offset, int dst_rows, int dst_cols, + __global float2* twiddles_ptr, const int t, const int nz) +{ + const int x = get_global_id(0); + const int y = get_group_id(1); + const int block_size = LOCAL_SIZE/kercn; + if (y < nz) + { + __local float2 smem[LOCAL_SIZE]; + __global const float2* twiddles = (__global float2*) twiddles_ptr; + const int ind = x; +#ifdef IS_1D + float scale = 1.f/dst_cols; +#else + float scale = 1.f/(dst_cols*dst_rows); +#endif + +#ifdef COMPLEX_INPUT + __global const float2* src = (__global const float2*)(src_ptr + mad24(y, src_step, mad24(x, (int)(sizeof(float)*2), src_offset))); + #pragma unroll + for (int i=0; i= cn && kercn == 4 && depth <= 4 && !defined HAVE_SCALAR + srcT src1 = *(__global const srcT *)(src1ptr + src1_index); + srcT src2 = *(__global const srcT *)(src2ptr + src2_index); + srcT src3 = *(__global const srcT *)(src3ptr + src3_index); + __global dstT * dst = (__global dstT *)(dstptr + dst_index); +#if cn == 1 + dst[0] = src2 > src1 || src3 < src1 ? (dstT)(0) : (dstT)(255); +#elif cn == 2 + dst[0] = (dstT)(src2.xy > src1.xy || src3.xy < src1.xy || + src2.zw > src1.zw || src3.zw < src1.zw ? (dstT)(0) : (dstT)(255); +#elif cn == 4 + dst[0] = (dstT)(src2.x > src1.x || src3.x < src1.x || + src2.y > src1.y || src3.y < src1.y || + src2.z > src1.z || src3.z < src1.z || + src2.w > src1.w || src3.w < src1.w ? 0 : 255); +#endif +#else + __global const srcT1 * src1 = (__global const srcT1 *)(src1ptr + src1_index); __global uchar * dst = dstptr + dst_index; #ifndef HAVE_SCALAR - __global const T * src2 = (__global const T *)(src2ptr + src2_index); - __global const T * src3 = (__global const T *)(src3ptr + src3_index); + __global const srcT1 * src2 = (__global const srcT1 *)(src2ptr + src2_index); + __global const srcT1 * src3 = (__global const srcT1 *)(src3ptr + src3_index); #endif - dst[0] = 255; - - for (int c = 0; c < cn; ++c) - if (src2[c] > src1[c] || src3[c] < src1[c]) - { - dst[0] = 0; - break; - } + #pragma unroll + for (int px = 0; px < colsPerWI; ++px, src1 += cn +#ifndef HAVE_SCALAR + , src2 += cn, src3 += cn +#endif + ) + { + dst[px] = 255; + for (int c = 0; c < cn; ++c) + if (src2[c] > src1[c] || src3[c] < src1[c]) + { + dst[px] = 0; + break; + } + } +#endif // kercn >= cn #ifndef HAVE_SCALAR src2_index += src2_step; src3_index += src3_step; diff --git a/modules/core/src/opencl/lut.cl b/modules/core/src/opencl/lut.cl index 9bcd1b66f2..81ca4349a3 100644 --- a/modules/core/src/opencl/lut.cl +++ b/modules/core/src/opencl/lut.cl @@ -36,114 +36,118 @@ #if lcn == 1 #if dcn == 4 - #define LUT_OP(num)\ - int idx = *(__global const int *)(srcptr + mad24(num, src_step, src_index));\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - dst[0] = lut_l[idx & 0xff];\ - dst[1] = lut_l[(idx >> 8) & 0xff];\ - dst[2] = lut_l[(idx >> 16) & 0xff];\ + #define LUT_OP \ + int idx = *(__global const int *)(srcptr + src_index); \ + dst = (__global dstT *)(dstptr + dst_index); \ + dst[0] = lut_l[idx & 0xff]; \ + dst[1] = lut_l[(idx >> 8) & 0xff]; \ + dst[2] = lut_l[(idx >> 16) & 0xff]; \ dst[3] = lut_l[(idx >> 24) & 0xff]; #elif dcn == 3 - #define LUT_OP(num)\ - uchar3 idx = vload3(0, srcptr + mad24(num, src_step, src_index));\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - dst[0] = lut_l[idx.x];\ - dst[1] = lut_l[idx.y];\ + #define LUT_OP \ + uchar3 idx = vload3(0, srcptr + src_index); \ + dst = (__global dstT *)(dstptr + dst_index); \ + dst[0] = lut_l[idx.x]; \ + dst[1] = lut_l[idx.y]; \ dst[2] = lut_l[idx.z]; #elif dcn == 2 - #define LUT_OP(num)\ - short idx = *(__global const short *)(srcptr + mad24(num, src_step, src_index));\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - dst[0] = lut_l[idx & 0xff];\ + #define LUT_OP \ + short idx = *(__global const short *)(srcptr + src_index); \ + dst = (__global dstT *)(dstptr + dst_index); \ + dst[0] = lut_l[idx & 0xff]; \ dst[1] = lut_l[(idx >> 8) & 0xff]; #elif dcn == 1 - #define LUT_OP(num)\ - uchar idx = (srcptr + mad24(num, src_step, src_index))[0];\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ + #define LUT_OP \ + uchar idx = (srcptr + src_index)[0]; \ + dst = (__global dstT *)(dstptr + dst_index); \ dst[0] = lut_l[idx]; #else - #define LUT_OP(num)\ - __global const srcT * src = (__global const srcT *)(srcptr + mad24(num, src_step, src_index));\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - for (int cn = 0; cn < dcn; ++cn)\ + #define LUT_OP \ + __global const srcT * src = (__global const srcT *)(srcptr + src_index); \ + dst = (__global dstT *)(dstptr + dst_index); \ + for (int cn = 0; cn < dcn; ++cn) \ dst[cn] = lut_l[src[cn]]; #endif #else #if dcn == 4 - #define LUT_OP(num)\ - __global const uchar4 *src_pixel = (__global const uchar4 *)(srcptr + mad24(num, src_step, src_index));\ - int4 idx = convert_int4(src_pixel[0]) * lcn + (int4)(0, 1, 2, 3);\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - dst[0] = lut_l[idx.x];\ - dst[1] = lut_l[idx.y];\ - dst[2] = lut_l[idx.z];\ + #define LUT_OP \ + __global const uchar4 * src_pixel = (__global const uchar4 *)(srcptr + src_index); \ + int4 idx = mad24(convert_int4(src_pixel[0]), (int4)(lcn), (int4)(0, 1, 2, 3)); \ + dst = (__global dstT *)(dstptr + dst_index); \ + dst[0] = lut_l[idx.x]; \ + dst[1] = lut_l[idx.y]; \ + dst[2] = lut_l[idx.z]; \ dst[3] = lut_l[idx.w]; #elif dcn == 3 - #define LUT_OP(num)\ - uchar3 src_pixel = vload3(0, srcptr + mad24(num, src_step, src_index));\ - int3 idx = convert_int3(src_pixel) * lcn + (int3)(0, 1, 2);\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - dst[0] = lut_l[idx.x];\ - dst[1] = lut_l[idx.y];\ + #define LUT_OP \ + uchar3 src_pixel = vload3(0, srcptr + src_index); \ + int3 idx = mad24(convert_int3(src_pixel), (int3)(lcn), (int3)(0, 1, 2)); \ + dst = (__global dstT *)(dstptr + dst_index); \ + dst[0] = lut_l[idx.x]; \ + dst[1] = lut_l[idx.y]; \ dst[2] = lut_l[idx.z]; #elif dcn == 2 - #define LUT_OP(num)\ - __global const uchar2 *src_pixel = (__global const uchar2 *)(srcptr + mad24(num, src_step, src_index));\ - int2 idx = convert_int2(src_pixel[0]) * lcn + (int2)(0, 1);\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - dst[0] = lut_l[idx.x];\ + #define LUT_OP \ + __global const uchar2 * src_pixel = (__global const uchar2 *)(srcptr + src_index); \ + int2 idx = mad24(convert_int2(src_pixel[0]), lcn, (int2)(0, 1)); \ + dst = (__global dstT *)(dstptr + dst_index); \ + dst[0] = lut_l[idx.x]; \ dst[1] = lut_l[idx.y]; #elif dcn == 1 //error case (1 < lcn) ==> lcn == scn == dcn - #define LUT_OP(num)\ - uchar idx = (srcptr + mad24(num, src_step, src_index))[0];\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ + #define LUT_OP \ + uchar idx = (srcptr + src_index)[0]; \ + dst = (__global dstT *)(dstptr + dst_index); \ dst[0] = lut_l[idx]; #else - #define LUT_OP(num)\ - __global const srcT *src = (__global const srcT *)(srcptr + mad24(num, src_step, src_index));\ - dst = (__global dstT *)(dstptr + mad24(num, dst_step, dst_index));\ - for (int cn = 0; cn < dcn; ++cn)\ + #define LUT_OP \ + __global const srcT * src = (__global const srcT *)(srcptr + src_index); \ + dst = (__global dstT *)(dstptr + dst_index); \ + for (int cn = 0; cn < dcn; ++cn) \ dst[cn] = lut_l[mad24(src[cn], lcn, cn)]; #endif #endif -#define LOCAL_LUT_INIT\ - {\ - __global const dstT * lut = (__global const dstT *)(lutptr + lut_offset);\ - int init = mad24((int)get_local_id(1), (int)get_local_size(0), (int)get_local_id(0));\ - int step = get_local_size(0) * get_local_size(1);\ - for (int i = init; i < 256 * lcn; i += step)\ - {\ - lut_l[i] = lut[i];\ - }\ - barrier(CLK_LOCAL_MEM_FENCE);\ - } - __kernel void LUT(__global const uchar * srcptr, int src_step, int src_offset, __global const uchar * lutptr, int lut_step, int lut_offset, __global uchar * dstptr, int dst_step, int dst_offset, int rows, int cols) { + int x = get_global_id(0); + int y = get_global_id(1) << 2; + __local dstT lut_l[256 * lcn]; - LOCAL_LUT_INIT; + __global const dstT * lut = (__global const dstT *)(lutptr + lut_offset); - int x = get_global_id(0); - int y = 4 * get_global_id(1); + for (int i = mad24((int)get_local_id(1), (int)get_local_size(0), (int)get_local_id(0)), + step = get_local_size(0) * get_local_size(1); i < 256 * lcn; i += step) + lut_l[i] = lut[i]; + barrier(CLK_LOCAL_MEM_FENCE); if (x < cols && y < rows) { int src_index = mad24(y, src_step, mad24(x, (int)sizeof(srcT) * dcn, src_offset)); int dst_index = mad24(y, dst_step, mad24(x, (int)sizeof(dstT) * dcn, dst_offset)); + __global dstT * dst; - LUT_OP(0); + + LUT_OP; + if (y < rows - 1) { - LUT_OP(1); + src_index += src_step; + dst_index += dst_step; + LUT_OP; + if (y < rows - 2) { - LUT_OP(2); + src_index += src_step; + dst_index += dst_step; + LUT_OP; + if (y < rows - 3) { - LUT_OP(3); + src_index += src_step; + dst_index += dst_step; + LUT_OP; } } } diff --git a/modules/core/src/opencl/minmaxloc.cl b/modules/core/src/opencl/minmaxloc.cl index a51c5d93a3..664673e5a2 100644 --- a/modules/core/src/opencl/minmaxloc.cl +++ b/modules/core/src/opencl/minmaxloc.cl @@ -42,9 +42,13 @@ #if wdepth <= 4 #define MIN_ABS(a) convertFromU(abs(a)) #define MIN_ABS2(a, b) convertFromU(abs_diff(a, b)) +#define MIN(a, b) min(a, b) +#define MAX(a, b) max(a, b) #else #define MIN_ABS(a) fabs(a) #define MIN_ABS2(a, b) fabs(a - b) +#define MIN(a, b) fmin(a, b) +#define MAX(a, b) fmax(a, b) #endif #if kercn != 3 @@ -60,44 +64,41 @@ #define srcTSIZE (int)sizeof(srcT1) #endif -#ifdef NEED_MINLOC -#define CALC_MINLOC(inc) minloc = id + inc -#else -#define CALC_MINLOC(inc) -#endif - -#ifdef NEED_MAXLOC -#define CALC_MAXLOC(inc) maxloc = id + inc -#else -#define CALC_MAXLOC(inc) -#endif - #ifdef NEED_MINVAL +#ifdef NEED_MINLOC #define CALC_MIN(p, inc) \ if (minval > temp.p) \ { \ minval = temp.p; \ - CALC_MINLOC(inc); \ + minloc = id + inc; \ } #else +#define CALC_MIN(p, inc) \ + minval = MIN(minval, temp.p); +#endif +#else #define CALC_MIN(p, inc) #endif #ifdef NEED_MAXVAL +#ifdef NEED_MAXLOC #define CALC_MAX(p, inc) \ if (maxval < temp.p) \ { \ maxval = temp.p; \ - CALC_MAXLOC(inc); \ + maxloc = id + inc; \ } #else +#define CALC_MAX(p, inc) \ + maxval = MAX(maxval, temp.p); +#endif +#else #define CALC_MAX(p, inc) #endif #ifdef OP_CALC2 #define CALC_MAX2(p) \ - if (maxval2 < temp.p) \ - maxval2 = temp.p; + maxval2 = MAX(maxval2, temp.p); #else #define CALC_MAX2(p) #endif @@ -208,25 +209,28 @@ __kernel void minmaxloc(__global const uchar * srcptr, int src_step, int src_off #if kercn == 1 #ifdef NEED_MINVAL +#if NEED_MINLOC if (minval > temp) { minval = temp; -#ifdef NEED_MINLOC minloc = id; -#endif } +#else + minval = MIN(minval, temp); +#endif #endif #ifdef NEED_MAXVAL +#ifdef NEED_MAXLOC if (maxval < temp) { maxval = temp; -#ifdef NEED_MAXLOC maxloc = id; -#endif } +#else + maxval = MAX(maxval, temp); +#endif #ifdef OP_CALC2 - if (maxval2 < temp2) - maxval2 = temp2; + maxval2 = MAX(maxval2, temp2); #endif #endif #elif kercn >= 2 @@ -282,32 +286,35 @@ __kernel void minmaxloc(__global const uchar * srcptr, int src_step, int src_off { int lid3 = lid - WGS2_ALIGNED; #ifdef NEED_MINVAL +#ifdef NEED_MINLOC if (localmem_min[lid3] >= minval) { -#ifdef NEED_MINLOC if (localmem_min[lid3] == minval) localmem_minloc[lid3] = min(localmem_minloc[lid3], minloc); else localmem_minloc[lid3] = minloc, -#endif - localmem_min[lid3] = minval; + localmem_min[lid3] = minval; } +#else + localmem_min[lid3] = MIN(localmem_min[lid3], minval); +#endif #endif #ifdef NEED_MAXVAL +#ifdef NEED_MAXLOC if (localmem_max[lid3] <= maxval) { -#ifdef NEED_MAXLOC if (localmem_max[lid3] == maxval) localmem_maxloc[lid3] = min(localmem_maxloc[lid3], maxloc); else localmem_maxloc[lid3] = maxloc, -#endif - localmem_max[lid3] = maxval; + localmem_max[lid3] = maxval; } +#else + localmem_max[lid3] = MAX(localmem_max[lid3], maxval); +#endif #endif #ifdef OP_CALC2 - if (localmem_max2[lid3] < maxval2) - localmem_max2[lid3] = maxval2; + localmem_max2[lid3] = MAX(localmem_max2[lid3], maxval2); #endif } barrier(CLK_LOCAL_MEM_FENCE); @@ -319,32 +326,35 @@ __kernel void minmaxloc(__global const uchar * srcptr, int src_step, int src_off int lid2 = lsize + lid; #ifdef NEED_MINVAL +#ifdef NEED_MAXLOC if (localmem_min[lid] >= localmem_min[lid2]) { -#ifdef NEED_MINLOC if (localmem_min[lid] == localmem_min[lid2]) localmem_minloc[lid] = min(localmem_minloc[lid2], localmem_minloc[lid]); else localmem_minloc[lid] = localmem_minloc[lid2], -#endif - localmem_min[lid] = localmem_min[lid2]; + localmem_min[lid] = localmem_min[lid2]; } +#else + localmem_min[lid] = MIN(localmem_min[lid], localmem_min[lid2]); +#endif #endif #ifdef NEED_MAXVAL +#ifdef NEED_MAXLOC if (localmem_max[lid] <= localmem_max[lid2]) { -#ifdef NEED_MAXLOC if (localmem_max[lid] == localmem_max[lid2]) localmem_maxloc[lid] = min(localmem_maxloc[lid2], localmem_maxloc[lid]); else localmem_maxloc[lid] = localmem_maxloc[lid2], -#endif - localmem_max[lid] = localmem_max[lid2]; + localmem_max[lid] = localmem_max[lid2]; } +#else + localmem_max[lid] = MAX(localmem_max[lid], localmem_max[lid2]); +#endif #endif #ifdef OP_CALC2 - if (localmem_max2[lid] < localmem_max2[lid2]) - localmem_max2[lid] = localmem_max2[lid2]; + localmem_max2[lid] = MAX(localmem_max2[lid], localmem_max2[lid2]); #endif } barrier(CLK_LOCAL_MEM_FENCE); diff --git a/modules/core/src/opencl/reduce.cl b/modules/core/src/opencl/reduce.cl index f16a742e54..c89f1cf005 100644 --- a/modules/core/src/opencl/reduce.cl +++ b/modules/core/src/opencl/reduce.cl @@ -379,7 +379,7 @@ #define REDUCE_GLOBAL \ dstTK temp = convertToDT(loadpix(srcptr + src_index)); \ dstTK temp2 = convertToDT(loadpix(src2ptr + src2_index)); \ - temp = SUM_ABS2(temp, temp2)); \ + temp = SUM_ABS2(temp, temp2); \ FUNC(accumulator, temp.s0); \ FUNC(accumulator, temp.s1); \ FUNC(accumulator, temp.s2); \ diff --git a/modules/core/src/opencl/reduce2.cl b/modules/core/src/opencl/reduce2.cl index 7800e7a743..457378cc13 100644 --- a/modules/core/src/opencl/reduce2.cl +++ b/modules/core/src/opencl/reduce2.cl @@ -81,29 +81,34 @@ #define PROCESS_ELEM(acc, value) acc += value #elif defined OCL_CV_REDUCE_MAX #define INIT_VALUE MIN_VAL -#define PROCESS_ELEM(acc, value) acc = value > acc ? value : acc +#define PROCESS_ELEM(acc, value) acc = max(value, acc) #elif defined OCL_CV_REDUCE_MIN #define INIT_VALUE MAX_VAL -#define PROCESS_ELEM(acc, value) acc = value < acc ? value : acc +#define PROCESS_ELEM(acc, value) acc = min(value, acc) #else #error "No operation is specified" #endif #ifdef OP_REDUCE_PRE -__kernel void reduce_horz_pre(__global const uchar * srcptr, int src_step, int src_offset, int rows, int cols, - __global uchar * bufptr, int buf_step, int buf_offset) +__kernel void reduce_horz_opt(__global const uchar * srcptr, int src_step, int src_offset, int rows, int cols, + __global uchar * dstptr, int dst_step, int dst_offset +#ifdef OCL_CV_REDUCE_AVG + , float fscale +#endif + ) { + __local bufT lsmem[TILE_HEIGHT][BUF_COLS][cn]; + int x = get_global_id(0); int y = get_global_id(1); - if (x < BUF_COLS) + int liy = get_local_id(1); + if ((x < BUF_COLS) && (y < rows)) { int src_index = mad24(y, src_step, mad24(x, (int)sizeof(srcT) * cn, src_offset)); - int buf_index = mad24(y, buf_step, mad24(x, (int)sizeof(dstT) * cn, buf_offset)); __global const srcT * src = (__global const srcT *)(srcptr + src_index); - __global dstT * buf = (__global dstT *)(bufptr + buf_index); - dstT tmp[cn] = { INIT_VALUE }; + bufT tmp[cn] = { INIT_VALUE }; int src_step_mul = BUF_COLS * cn; for (int idx = x; idx < cols; idx += BUF_COLS, src += src_step_mul) @@ -111,14 +116,49 @@ __kernel void reduce_horz_pre(__global const uchar * srcptr, int src_step, int s #pragma unroll for (int c = 0; c < cn; ++c) { - dstT value = convertToDT(src[c]); + bufT value = convertToBufT(src[c]); PROCESS_ELEM(tmp[c], value); } } #pragma unroll for (int c = 0; c < cn; ++c) - buf[c] = tmp[c]; + lsmem[liy][x][c] = tmp[c]; + } + barrier(CLK_LOCAL_MEM_FENCE); + if ((x < BUF_COLS / 2) && (y < rows)) + { + #pragma unroll + for (int c = 0; c < cn; ++c) + { + PROCESS_ELEM(lsmem[liy][x][c], lsmem[liy][x + BUF_COLS / 2][c]); + } + } + barrier(CLK_LOCAL_MEM_FENCE); + if ((x == 0) && (y < rows)) + { + int dst_index = mad24(y, dst_step, dst_offset); + + __global dstT * dst = (__global dstT *)(dstptr + dst_index); + bufT tmp[cn] = { INIT_VALUE }; + + #pragma unroll + for (int xin = 0; xin < BUF_COLS / 2; xin ++) + { + #pragma unroll + for (int c = 0; c < cn; ++c) + { + PROCESS_ELEM(tmp[c], lsmem[liy][xin][c]); + } + } + + #pragma unroll + for (int c = 0; c < cn; ++c) +#ifdef OCL_CV_REDUCE_AVG + dst[c] = convertToDT(convertToWT(tmp[c]) * fscale); +#else + dst[c] = convertToDT(tmp[c]); +#endif } } diff --git a/modules/core/src/opencl/set_identity.cl b/modules/core/src/opencl/set_identity.cl index 6b277fe0e4..952204d3ff 100644 --- a/modules/core/src/opencl/set_identity.cl +++ b/modules/core/src/opencl/set_identity.cl @@ -43,20 +43,18 @@ // //M*/ -#if cn != 3 -#define loadpix(addr) *(__global const T *)(addr) +#if kercn != 3 #define storepix(val, addr) *(__global T *)(addr) = val #define TSIZE (int)sizeof(T) #define scalar scalar_ #else -#define loadpix(addr) vload3(0, (__global const T1 *)(addr)) #define storepix(val, addr) vstore3(val, 0, (__global T1 *)(addr)) #define TSIZE ((int)sizeof(T1)*3) #define scalar (T)(scalar_.x, scalar_.y, scalar_.z) #endif __kernel void setIdentity(__global uchar * srcptr, int src_step, int src_offset, int rows, int cols, - ST scalar_, int rowsPerWI) + ST scalar_) { int x = get_global_id(0); int y0 = get_global_id(1) * rowsPerWI; @@ -65,7 +63,35 @@ __kernel void setIdentity(__global uchar * srcptr, int src_step, int src_offset, { int src_index = mad24(y0, src_step, mad24(x, TSIZE, src_offset)); - for (int y = y0, y1 = min(rows, y0 + rowsPerWI); y < y1; ++y, src_index += src_step) - storepix(x == y ? scalar : (T)(0), srcptr + src_index); +#if kercn == cn + #pragma unroll + for (int y = y0, i = 0, y1 = min(rows, y0 + rowsPerWI); i < rowsPerWI; ++y, ++i, src_index += src_step) + if (y < y1) + storepix(x == y ? scalar : (T)(0), srcptr + src_index); +#elif kercn == 4 && cn == 1 + if (y0 < rows) + { + storepix(x == y0 >> 2 ? (T)(scalar, 0, 0, 0) : (T)(0), srcptr + src_index); + if (++y0 < rows) + { + src_index += src_step; + storepix(x == y0 >> 2 ? (T)(0, scalar, 0, 0) : (T)(0), srcptr + src_index); + + if (++y0 < rows) + { + src_index += src_step; + storepix(x == y0 >> 2 ? (T)(0, 0, scalar, 0) : (T)(0), srcptr + src_index); + + if (++y0 < rows) + { + src_index += src_step; + storepix(x == y0 >> 2 ? (T)(0, 0, 0, scalar) : (T)(0), srcptr + src_index); + } + } + } + } +#else +#error "Incorrect combination of cn && kercn" +#endif } } diff --git a/modules/core/src/opencl/transpose.cl b/modules/core/src/opencl/transpose.cl index b5ec4b6f95..cad3616b5d 100644 --- a/modules/core/src/opencl/transpose.cl +++ b/modules/core/src/opencl/transpose.cl @@ -53,7 +53,7 @@ #define TSIZE ((int)sizeof(T1)*3) #endif -#define LDS_STEP TILE_DIM +#define LDS_STEP (TILE_DIM + 1) __kernel void transpose(__global const uchar * srcptr, int src_step, int src_offset, int src_rows, int src_cols, __global uchar * dstptr, int dst_step, int dst_offset) @@ -90,6 +90,7 @@ __kernel void transpose(__global const uchar * srcptr, int src_step, int src_off { int index_src = mad24(y, src_step, mad24(x, TSIZE, src_offset)); + #pragma unroll for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) if (y + i < src_rows) { @@ -103,6 +104,7 @@ __kernel void transpose(__global const uchar * srcptr, int src_step, int src_off { int index_dst = mad24(y_index, dst_step, mad24(x_index, TSIZE, dst_offset)); + #pragma unroll for (int i = 0; i < TILE_DIM; i += BLOCK_ROWS) if ((y_index + i) < src_cols) { @@ -115,18 +117,24 @@ __kernel void transpose(__global const uchar * srcptr, int src_step, int src_off __kernel void transpose_inplace(__global uchar * srcptr, int src_step, int src_offset, int src_rows) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * rowsPerWI; - if (y < src_rows && x < y) + if (x < y + rowsPerWI) { int src_index = mad24(y, src_step, mad24(x, TSIZE, src_offset)); int dst_index = mad24(x, src_step, mad24(y, TSIZE, src_offset)); + T tmp; - __global const uchar * src = srcptr + src_index; - __global uchar * dst = srcptr + dst_index; + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, src_index += src_step, dst_index += TSIZE) + if (y < src_rows && x < y) + { + __global uchar * src = srcptr + src_index; + __global uchar * dst = srcptr + dst_index; - T tmp = loadpix(dst); - storepix(loadpix(src), dst); - storepix(tmp, src); + tmp = loadpix(dst); + storepix(loadpix(src), dst); + storepix(tmp, src); + } } } diff --git a/modules/core/src/stat.cpp b/modules/core/src/stat.cpp index 3dd042860d..888fd7cacc 100644 --- a/modules/core/src/stat.cpp +++ b/modules/core/src/stat.cpp @@ -479,9 +479,10 @@ static bool ocl_sum( InputArray _src, Scalar & res, int sum_op, InputArray _mask haveMask = _mask.kind() != _InputArray::NONE, haveSrc2 = _src2.kind() != _InputArray::NONE; int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), - kercn = cn == 1 && !haveMask ? ocl::predictOptimalVectorWidth(_src) : 1, + kercn = cn == 1 && !haveMask ? ocl::predictOptimalVectorWidth(_src, _src2) : 1, mcn = std::max(cn, kercn); CV_Assert(!haveSrc2 || _src2.type() == type); + int convert_cn = haveSrc2 ? mcn : cn; if ( (!doubleSupport && depth == CV_64F) || cn > 4 ) return false; @@ -513,7 +514,7 @@ static bool ocl_sum( InputArray _src, Scalar & res, int sum_op, InputArray _mask haveMask && _mask.isContinuous() ? " -D HAVE_MASK_CONT" : "", kercn, haveSrc2 ? " -D HAVE_SRC2" : "", calc2 ? " -D OP_CALC2" : "", haveSrc2 && _src2.isContinuous() ? " -D HAVE_SRC2_CONT" : "", - depth <= CV_32S && ddepth == CV_32S ? ocl::convertTypeStr(CV_8U, ddepth, mcn, cvt[1]) : "noconvert"); + depth <= CV_32S && ddepth == CV_32S ? ocl::convertTypeStr(CV_8U, ddepth, convert_cn, cvt[1]) : "noconvert"); ocl::Kernel k("reduce", ocl::core::reduce_oclsrc, opts); if (k.empty()) @@ -918,8 +919,14 @@ static bool ocl_meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0, isContinuous = _src.isContinuous(); - int groups = ocl::Device::getDefault().maxComputeUnits(); - size_t wgs = ocl::Device::getDefault().maxWorkGroupSize(); + const ocl::Device &defDev = ocl::Device::getDefault(); + int groups = defDev.maxComputeUnits(); + if (defDev.isIntel()) + { + static const int subSliceEUCount = 10; + groups = (groups / subSliceEUCount) * 2; + } + size_t wgs = defDev.maxWorkGroupSize(); int ddepth = std::max(CV_32S, depth), sqddepth = std::max(CV_32F, depth), dtype = CV_MAKE_TYPE(ddepth, cn), @@ -1445,6 +1452,9 @@ static bool ocl_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* CV_Assert(!haveSrc2 || _src2.type() == type); + if (depth == CV_32S || depth == CV_32F) + return false; + if ((depth == CV_64F || ddepth == CV_64F) && !doubleSupport) return false; @@ -2178,6 +2188,9 @@ static bool ocl_norm( InputArray _src, int normType, InputArray _mask, double & (!doubleSupport && depth == CV_64F)) return false; + if( depth == CV_32F && (!_mask.empty() || normType == NORM_INF) ) + return false; + UMat src = _src.getUMat(); if (normType == NORM_INF) @@ -2533,7 +2546,7 @@ static bool ocl_norm( InputArray _src1, InputArray _src2, int normType, InputArr normType &= ~NORM_RELATIVE; bool normsum = normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR; - if ( !(normType == NORM_INF || normsum) ) + if ( !normsum || !_mask.empty() ) return false; if (normsum) diff --git a/modules/core/test/ocl/test_dft.cpp b/modules/core/test/ocl/test_dft.cpp index 1f0e43b20e..cd0c1f07d0 100644 --- a/modules/core/test/ocl/test_dft.cpp +++ b/modules/core/test/ocl/test_dft.cpp @@ -48,17 +48,26 @@ #ifdef HAVE_OPENCL +enum OCL_FFT_TYPE +{ + R2R = 0, + C2R = 1, + R2C = 2, + C2C = 3 +}; + namespace cvtest { namespace ocl { //////////////////////////////////////////////////////////////////////////// // Dft -PARAM_TEST_CASE(Dft, cv::Size, MatDepth, bool, bool, bool, bool) +PARAM_TEST_CASE(Dft, cv::Size, OCL_FFT_TYPE, bool, bool, bool, bool) { cv::Size dft_size; - int dft_flags, depth; - bool inplace; + int dft_flags, depth, cn, dft_type; + bool hint; + bool is1d; TEST_DECLARE_INPUT_PARAMETER(src); TEST_DECLARE_OUTPUT_PARAMETER(dst); @@ -66,34 +75,50 @@ PARAM_TEST_CASE(Dft, cv::Size, MatDepth, bool, bool, bool, bool) virtual void SetUp() { dft_size = GET_PARAM(0); - depth = GET_PARAM(1); - inplace = GET_PARAM(2); + dft_type = GET_PARAM(1); + depth = CV_32F; dft_flags = 0; + switch (dft_type) + { + case R2R: dft_flags |= cv::DFT_REAL_OUTPUT; cn = 1; break; + case C2R: dft_flags |= cv::DFT_REAL_OUTPUT; cn = 2; break; + case R2C: dft_flags |= cv::DFT_COMPLEX_OUTPUT; cn = 1; break; + case C2C: dft_flags |= cv::DFT_COMPLEX_OUTPUT; cn = 2; break; + } + + if (GET_PARAM(2)) + dft_flags |= cv::DFT_INVERSE; if (GET_PARAM(3)) dft_flags |= cv::DFT_ROWS; if (GET_PARAM(4)) dft_flags |= cv::DFT_SCALE; - if (GET_PARAM(5)) - dft_flags |= cv::DFT_INVERSE; + hint = GET_PARAM(5); + is1d = (dft_flags & DFT_ROWS) != 0 || dft_size.height == 1; } - void generateTestData(int cn = 2) + void generateTestData() { src = randomMat(dft_size, CV_MAKE_TYPE(depth, cn), 0.0, 100.0); usrc = src.getUMat(ACCESS_READ); - - if (inplace) - dst = src, udst = usrc; } }; -OCL_TEST_P(Dft, C2C) +OCL_TEST_P(Dft, Mat) { generateTestData(); - OCL_OFF(cv::dft(src, dst, dft_flags | cv::DFT_COMPLEX_OUTPUT)); - OCL_ON(cv::dft(usrc, udst, dft_flags | cv::DFT_COMPLEX_OUTPUT)); + int nonzero_rows = hint ? src.cols - randomInt(1, src.rows-1) : 0; + OCL_OFF(cv::dft(src, dst, dft_flags, nonzero_rows)); + OCL_ON(cv::dft(usrc, udst, dft_flags, nonzero_rows)); + + // In case forward R2C 1d tranform dst contains only half of output + // without complex conjugate + if (dft_type == R2C && is1d && (dft_flags & cv::DFT_INVERSE) == 0) + { + dst = dst(cv::Range(0, dst.rows), cv::Range(0, dst.cols/2 + 1)); + udst = udst(cv::Range(0, udst.rows), cv::Range(0, udst.cols/2 + 1)); + } double eps = src.size().area() * 1e-4; EXPECT_MAT_NEAR(dst, udst, eps); @@ -150,15 +175,15 @@ OCL_TEST_P(MulSpectrums, Mat) OCL_INSTANTIATE_TEST_CASE_P(OCL_ImgProc, MulSpectrums, testing::Combine(Bool(), Bool())); -OCL_INSTANTIATE_TEST_CASE_P(Core, Dft, Combine(Values(cv::Size(2, 3), cv::Size(5, 4), cv::Size(25, 20), - cv::Size(512, 1), cv::Size(1024, 768)), - Values(CV_32F, CV_64F), - Bool(), // inplace +OCL_INSTANTIATE_TEST_CASE_P(Core, Dft, Combine(Values(cv::Size(10, 10), cv::Size(36, 36), cv::Size(512, 1), cv::Size(1280, 768)), + Values((OCL_FFT_TYPE) R2C, (OCL_FFT_TYPE) C2C, (OCL_FFT_TYPE) R2R, (OCL_FFT_TYPE) C2R), + Bool(), // DFT_INVERSE Bool(), // DFT_ROWS Bool(), // DFT_SCALE - Bool()) // DFT_INVERSE + Bool() // hint + ) ); } } // namespace cvtest::ocl -#endif // HAVE_OPENCL +#endif // HAVE_OPENCL \ No newline at end of file diff --git a/modules/cudabgsegm/perf/perf_bgsegm.cpp b/modules/cudabgsegm/perf/perf_bgsegm.cpp index 9d3da2927f..02fc9a8ee9 100644 --- a/modules/cudabgsegm/perf/perf_bgsegm.cpp +++ b/modules/cudabgsegm/perf/perf_bgsegm.cpp @@ -42,8 +42,8 @@ #include "perf_precomp.hpp" -#ifdef HAVE_OPENCV_LEGACY -# include "opencv2/legacy.hpp" +#ifdef HAVE_OPENCV_CUDALEGACY +# include "opencv2/cudalegacy.hpp" #endif #ifdef HAVE_OPENCV_CUDAIMGPROC @@ -72,7 +72,7 @@ using namespace perf; #if BUILD_WITH_VIDEO_INPUT_SUPPORT -#ifdef HAVE_OPENCV_LEGACY +#ifdef HAVE_OPENCV_CUDALEGACY namespace cv { @@ -150,7 +150,7 @@ PERF_TEST_P(Video, FGDStatModel, } else { -#ifdef HAVE_OPENCV_LEGACY +#ifdef HAVE_OPENCV_CUDALEGACY IplImage ipl_frame = frame; cv::Ptr model(cvCreateFGDStatModel(&ipl_frame)); diff --git a/modules/cudabgsegm/test/test_bgsegm.cpp b/modules/cudabgsegm/test/test_bgsegm.cpp index 75d6d73a3f..34f3dcc9ab 100644 --- a/modules/cudabgsegm/test/test_bgsegm.cpp +++ b/modules/cudabgsegm/test/test_bgsegm.cpp @@ -42,8 +42,8 @@ #include "test_precomp.hpp" -#ifdef HAVE_OPENCV_LEGACY -# include "opencv2/legacy.hpp" +#ifdef HAVE_OPENCV_CUDALEGACY +# include "opencv2/cudalegacy.hpp" #endif #ifdef HAVE_CUDA @@ -66,7 +66,7 @@ using namespace cvtest; ////////////////////////////////////////////////////// // FGDStatModel -#if BUILD_WITH_VIDEO_INPUT_SUPPORT && defined(HAVE_OPENCV_LEGACY) +#if BUILD_WITH_VIDEO_INPUT_SUPPORT && defined(HAVE_OPENCV_CUDALEGACY) namespace cv { diff --git a/modules/cudacodec/CMakeLists.txt b/modules/cudacodec/CMakeLists.txt index ace7cb3763..5d8f7327c0 100644 --- a/modules/cudacodec/CMakeLists.txt +++ b/modules/cudacodec/CMakeLists.txt @@ -6,7 +6,7 @@ set(the_description "CUDA-accelerated Video Encoding/Decoding") ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 /wd4324 /wd4512 -Wundef) -ocv_add_module(cudacodec opencv_highgui OPTIONAL opencv_cudev) +ocv_add_module(cudacodec opencv_core opencv_videoio OPTIONAL opencv_cudev) ocv_module_include_directories() ocv_glob_module_sources() diff --git a/modules/cudastereo/src/cuda/disparity_bilateral_filter.cu b/modules/cudastereo/src/cuda/disparity_bilateral_filter.cu index b5de989ae7..a9f2d2650c 100644 --- a/modules/cudastereo/src/cuda/disparity_bilateral_filter.cu +++ b/modules/cudastereo/src/cuda/disparity_bilateral_filter.cu @@ -45,34 +45,12 @@ #include "opencv2/core/cuda/common.hpp" #include "opencv2/core/cuda/limits.hpp" +#include "cuda/disparity_bilateral_filter.hpp" + namespace cv { namespace cuda { namespace device { namespace disp_bilateral_filter { - __constant__ float* ctable_color; - __constant__ float* ctable_space; - __constant__ size_t ctable_space_step; - - __constant__ int cndisp; - __constant__ int cradius; - - __constant__ short cedge_disc; - __constant__ short cmax_disc; - - void disp_load_constants(float* table_color, PtrStepSzf table_space, int ndisp, int radius, short edge_disc, short max_disc) - { - cudaSafeCall( cudaMemcpyToSymbol(ctable_color, &table_color, sizeof(table_color)) ); - cudaSafeCall( cudaMemcpyToSymbol(ctable_space, &table_space.data, sizeof(table_space.data)) ); - size_t table_space_step = table_space.step / sizeof(float); - cudaSafeCall( cudaMemcpyToSymbol(ctable_space_step, &table_space_step, sizeof(size_t)) ); - - cudaSafeCall( cudaMemcpyToSymbol(cndisp, &ndisp, sizeof(int)) ); - cudaSafeCall( cudaMemcpyToSymbol(cradius, &radius, sizeof(int)) ); - - cudaSafeCall( cudaMemcpyToSymbol(cedge_disc, &edge_disc, sizeof(short)) ); - cudaSafeCall( cudaMemcpyToSymbol(cmax_disc, &max_disc, sizeof(short)) ); - } - template struct DistRgbMax { @@ -95,7 +73,11 @@ namespace cv { namespace cuda { namespace device }; template - __global__ void disp_bilateral_filter(int t, T* disp, size_t disp_step, const uchar* img, size_t img_step, int h, int w) + __global__ void disp_bilateral_filter(int t, T* disp, size_t disp_step, + const uchar* img, size_t img_step, int h, int w, + const float* ctable_color, const float * ctable_space, size_t ctable_space_step, + int cradius, + short cedge_disc, short cmax_disc) { const int y = blockIdx.y * blockDim.y + threadIdx.y; const int x = ((blockIdx.x * blockDim.x + threadIdx.x) << 1) + ((y + t) & 1); @@ -178,7 +160,7 @@ namespace cv { namespace cuda { namespace device } template - void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, cudaStream_t stream) + void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, const float *table_color, const float* table_space, size_t table_step, int radius, short edge_disc, short max_disc, cudaStream_t stream) { dim3 threads(32, 8, 1); dim3 grid(1, 1, 1); @@ -190,20 +172,20 @@ namespace cv { namespace cuda { namespace device case 1: for (int i = 0; i < iters; ++i) { - disp_bilateral_filter<1><<>>(0, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols); + disp_bilateral_filter<1><<>>(0, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); cudaSafeCall( cudaGetLastError() ); - disp_bilateral_filter<1><<>>(1, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols); + disp_bilateral_filter<1><<>>(1, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); cudaSafeCall( cudaGetLastError() ); } break; case 3: for (int i = 0; i < iters; ++i) { - disp_bilateral_filter<3><<>>(0, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols); + disp_bilateral_filter<3><<>>(0, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); cudaSafeCall( cudaGetLastError() ); - disp_bilateral_filter<3><<>>(1, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols); + disp_bilateral_filter<3><<>>(1, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); cudaSafeCall( cudaGetLastError() ); } break; @@ -215,8 +197,8 @@ namespace cv { namespace cuda { namespace device cudaSafeCall( cudaDeviceSynchronize() ); } - template void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, cudaStream_t stream); - template void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, cudaStream_t stream); + template void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, const float *table_color, const float *table_space, size_t table_step, int radius, short, short, cudaStream_t stream); + template void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, const float *table_color, const float *table_space, size_t table_step, int radius, short, short, cudaStream_t stream); } // namespace bilateral_filter }}} // namespace cv { namespace cuda { namespace cudev diff --git a/modules/cudastereo/src/cuda/disparity_bilateral_filter.hpp b/modules/cudastereo/src/cuda/disparity_bilateral_filter.hpp new file mode 100644 index 0000000000..95be834573 --- /dev/null +++ b/modules/cudastereo/src/cuda/disparity_bilateral_filter.hpp @@ -0,0 +1,8 @@ +namespace cv { namespace cuda { namespace device +{ + namespace disp_bilateral_filter + { + template + void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, const float *, const float *, size_t, int radius, short edge_disc, short max_disc, cudaStream_t stream); + } +}}} diff --git a/modules/cudastereo/src/cuda/stereocsbp.cu b/modules/cudastereo/src/cuda/stereocsbp.cu index b1426607dd..dd535e8b20 100644 --- a/modules/cudastereo/src/cuda/stereocsbp.cu +++ b/modules/cudastereo/src/cuda/stereocsbp.cu @@ -48,109 +48,61 @@ #include "opencv2/core/cuda/reduce.hpp" #include "opencv2/core/cuda/functional.hpp" +#include "cuda/stereocsbp.hpp" + namespace cv { namespace cuda { namespace device { namespace stereocsbp { - /////////////////////////////////////////////////////////////// - /////////////////////// load constants //////////////////////// - /////////////////////////////////////////////////////////////// - - __constant__ int cndisp; - - __constant__ float cmax_data_term; - __constant__ float cdata_weight; - __constant__ float cmax_disc_term; - __constant__ float cdisc_single_jump; - - __constant__ int cth; - - __constant__ size_t cimg_step; - __constant__ size_t cmsg_step; - __constant__ size_t cdisp_step1; - __constant__ size_t cdisp_step2; - - __constant__ uchar* cleft; - __constant__ uchar* cright; - __constant__ uchar* ctemp; - - - void load_constants(int ndisp, float max_data_term, float data_weight, float max_disc_term, float disc_single_jump, int min_disp_th, - const PtrStepSzb& left, const PtrStepSzb& right, const PtrStepSzb& temp) - { - cudaSafeCall( cudaMemcpyToSymbol(cndisp, &ndisp, sizeof(int)) ); - - cudaSafeCall( cudaMemcpyToSymbol(cmax_data_term, &max_data_term, sizeof(float)) ); - cudaSafeCall( cudaMemcpyToSymbol(cdata_weight, &data_weight, sizeof(float)) ); - cudaSafeCall( cudaMemcpyToSymbol(cmax_disc_term, &max_disc_term, sizeof(float)) ); - cudaSafeCall( cudaMemcpyToSymbol(cdisc_single_jump, &disc_single_jump, sizeof(float)) ); - - cudaSafeCall( cudaMemcpyToSymbol(cth, &min_disp_th, sizeof(int)) ); - - cudaSafeCall( cudaMemcpyToSymbol(cimg_step, &left.step, sizeof(size_t)) ); - - cudaSafeCall( cudaMemcpyToSymbol(cleft, &left.data, sizeof(left.data)) ); - cudaSafeCall( cudaMemcpyToSymbol(cright, &right.data, sizeof(right.data)) ); - cudaSafeCall( cudaMemcpyToSymbol(ctemp, &temp.data, sizeof(temp.data)) ); - } - /////////////////////////////////////////////////////////////// /////////////////////// init data cost //////////////////////// /////////////////////////////////////////////////////////////// - template struct DataCostPerPixel; - template <> struct DataCostPerPixel<1> + template static float __device__ pixeldiff(const uchar* left, const uchar* right, float max_data_term); + template<> __device__ __forceinline__ static float pixeldiff<1>(const uchar* left, const uchar* right, float max_data_term) { - static __device__ __forceinline__ float compute(const uchar* left, const uchar* right) - { - return fmin(cdata_weight * ::abs((int)*left - *right), cdata_weight * cmax_data_term); - } - }; - template <> struct DataCostPerPixel<3> + return fmin( ::abs((int)*left - *right), max_data_term); + } + template<> __device__ __forceinline__ static float pixeldiff<3>(const uchar* left, const uchar* right, float max_data_term) { - static __device__ __forceinline__ float compute(const uchar* left, const uchar* right) - { - float tb = 0.114f * ::abs((int)left[0] - right[0]); - float tg = 0.587f * ::abs((int)left[1] - right[1]); - float tr = 0.299f * ::abs((int)left[2] - right[2]); + float tb = 0.114f * ::abs((int)left[0] - right[0]); + float tg = 0.587f * ::abs((int)left[1] - right[1]); + float tr = 0.299f * ::abs((int)left[2] - right[2]); - return fmin(cdata_weight * (tr + tg + tb), cdata_weight * cmax_data_term); - } - }; - template <> struct DataCostPerPixel<4> + return fmin(tr + tg + tb, max_data_term); + } + template<> __device__ __forceinline__ static float pixeldiff<4>(const uchar* left, const uchar* right, float max_data_term) { - static __device__ __forceinline__ float compute(const uchar* left, const uchar* right) - { - uchar4 l = *((const uchar4*)left); - uchar4 r = *((const uchar4*)right); + uchar4 l = *((const uchar4*)left); + uchar4 r = *((const uchar4*)right); - float tb = 0.114f * ::abs((int)l.x - r.x); - float tg = 0.587f * ::abs((int)l.y - r.y); - float tr = 0.299f * ::abs((int)l.z - r.z); + float tb = 0.114f * ::abs((int)l.x - r.x); + float tg = 0.587f * ::abs((int)l.y - r.y); + float tr = 0.299f * ::abs((int)l.z - r.z); - return fmin(cdata_weight * (tr + tg + tb), cdata_weight * cmax_data_term); - } - }; + return fmin(tr + tg + tb, max_data_term); + } template - __global__ void get_first_k_initial_global(T* data_cost_selected_, T *selected_disp_pyr, int h, int w, int nr_plane) + __global__ void get_first_k_initial_global(uchar *ctemp, T* data_cost_selected_, T *selected_disp_pyr, int h, int w, int nr_plane, int ndisp, + size_t msg_step, size_t disp_step) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; if (y < h && x < w) { - T* selected_disparity = selected_disp_pyr + y * cmsg_step + x; - T* data_cost_selected = data_cost_selected_ + y * cmsg_step + x; - T* data_cost = (T*)ctemp + y * cmsg_step + x; + T* selected_disparity = selected_disp_pyr + y * msg_step + x; + T* data_cost_selected = data_cost_selected_ + y * msg_step + x; + T* data_cost = (T*)ctemp + y * msg_step + x; for(int i = 0; i < nr_plane; i++) { T minimum = device::numeric_limits::max(); int id = 0; - for(int d = 0; d < cndisp; d++) + for(int d = 0; d < ndisp; d++) { - T cur = data_cost[d * cdisp_step1]; + T cur = data_cost[d * disp_step]; if(cur < minimum) { minimum = cur; @@ -158,46 +110,47 @@ namespace cv { namespace cuda { namespace device } } - data_cost_selected[i * cdisp_step1] = minimum; - selected_disparity[i * cdisp_step1] = id; - data_cost [id * cdisp_step1] = numeric_limits::max(); + data_cost_selected[i * disp_step] = minimum; + selected_disparity[i * disp_step] = id; + data_cost [id * disp_step] = numeric_limits::max(); } } } template - __global__ void get_first_k_initial_local(T* data_cost_selected_, T* selected_disp_pyr, int h, int w, int nr_plane) + __global__ void get_first_k_initial_local(uchar *ctemp, T* data_cost_selected_, T* selected_disp_pyr, int h, int w, int nr_plane, int ndisp, + size_t msg_step, size_t disp_step) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; if (y < h && x < w) { - T* selected_disparity = selected_disp_pyr + y * cmsg_step + x; - T* data_cost_selected = data_cost_selected_ + y * cmsg_step + x; - T* data_cost = (T*)ctemp + y * cmsg_step + x; + T* selected_disparity = selected_disp_pyr + y * msg_step + x; + T* data_cost_selected = data_cost_selected_ + y * msg_step + x; + T* data_cost = (T*)ctemp + y * msg_step + x; int nr_local_minimum = 0; - T prev = data_cost[0 * cdisp_step1]; - T cur = data_cost[1 * cdisp_step1]; - T next = data_cost[2 * cdisp_step1]; + T prev = data_cost[0 * disp_step]; + T cur = data_cost[1 * disp_step]; + T next = data_cost[2 * disp_step]; - for (int d = 1; d < cndisp - 1 && nr_local_minimum < nr_plane; d++) + for (int d = 1; d < ndisp - 1 && nr_local_minimum < nr_plane; d++) { if (cur < prev && cur < next) { - data_cost_selected[nr_local_minimum * cdisp_step1] = cur; - selected_disparity[nr_local_minimum * cdisp_step1] = d; + data_cost_selected[nr_local_minimum * disp_step] = cur; + selected_disparity[nr_local_minimum * disp_step] = d; - data_cost[d * cdisp_step1] = numeric_limits::max(); + data_cost[d * disp_step] = numeric_limits::max(); nr_local_minimum++; } prev = cur; cur = next; - next = data_cost[(d + 1) * cdisp_step1]; + next = data_cost[(d + 1) * disp_step]; } for (int i = nr_local_minimum; i < nr_plane; i++) @@ -205,25 +158,27 @@ namespace cv { namespace cuda { namespace device T minimum = numeric_limits::max(); int id = 0; - for (int d = 0; d < cndisp; d++) + for (int d = 0; d < ndisp; d++) { - cur = data_cost[d * cdisp_step1]; + cur = data_cost[d * disp_step]; if (cur < minimum) { minimum = cur; id = d; } } - data_cost_selected[i * cdisp_step1] = minimum; - selected_disparity[i * cdisp_step1] = id; + data_cost_selected[i * disp_step] = minimum; + selected_disparity[i * disp_step] = id; - data_cost[id * cdisp_step1] = numeric_limits::max(); + data_cost[id * disp_step] = numeric_limits::max(); } } } template - __global__ void init_data_cost(int h, int w, int level) + __global__ void init_data_cost(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, + int h, int w, int level, int ndisp, float data_weight, float max_data_term, + int min_disp, size_t msg_step, size_t disp_step) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -236,9 +191,9 @@ namespace cv { namespace cuda { namespace device int x0 = x << level; int xt = (x + 1) << level; - T* data_cost = (T*)ctemp + y * cmsg_step + x; + T* data_cost = (T*)ctemp + y * msg_step + x; - for(int d = 0; d < cndisp; ++d) + for(int d = 0; d < ndisp; ++d) { float val = 0.0f; for(int yi = y0; yi < yt; yi++) @@ -246,24 +201,26 @@ namespace cv { namespace cuda { namespace device for(int xi = x0; xi < xt; xi++) { int xr = xi - d; - if(d < cth || xr < 0) - val += cdata_weight * cmax_data_term; + if(d < min_disp || xr < 0) + val += data_weight * max_data_term; else { const uchar* lle = cleft + yi * cimg_step + xi * channels; const uchar* lri = cright + yi * cimg_step + xr * channels; - val += DataCostPerPixel::compute(lle, lri); + val += data_weight * pixeldiff(lle, lri, max_data_term); } } } - data_cost[cdisp_step1 * d] = saturate_cast(val); + data_cost[disp_step * d] = saturate_cast(val); } } } template - __global__ void init_data_cost_reduce(int level, int rows, int cols, int h) + __global__ void init_data_cost_reduce(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, + int level, int rows, int cols, int h, int ndisp, float data_weight, float max_data_term, + int min_disp, size_t msg_step, size_t disp_step) { int x_out = blockIdx.x; int y_out = blockIdx.y % h; @@ -271,7 +228,7 @@ namespace cv { namespace cuda { namespace device int tid = threadIdx.x; - if (d < cndisp) + if (d < ndisp) { int x0 = x_out << level; int y0 = y_out << level; @@ -281,8 +238,8 @@ namespace cv { namespace cuda { namespace device float val = 0.0f; if (x0 + tid < cols) { - if (x0 + tid - d < 0 || d < cth) - val = cdata_weight * cmax_data_term * len; + if (x0 + tid - d < 0 || d < min_disp) + val = data_weight * max_data_term * len; else { const uchar* lle = cleft + y0 * cimg_step + channels * (x0 + tid ); @@ -290,7 +247,7 @@ namespace cv { namespace cuda { namespace device for(int y = 0; y < len; ++y) { - val += DataCostPerPixel::compute(lle, lri); + val += data_weight * pixeldiff(lle, lri, max_data_term); lle += cimg_step; lri += cimg_step; @@ -302,16 +259,16 @@ namespace cv { namespace cuda { namespace device reduce(smem + winsz * threadIdx.z, val, tid, plus()); - T* data_cost = (T*)ctemp + y_out * cmsg_step + x_out; + T* data_cost = (T*)ctemp + y_out * msg_step + x_out; if (tid == 0) - data_cost[cdisp_step1 * d] = saturate_cast(val); + data_cost[disp_step * d] = saturate_cast(val); } } template - void init_data_cost_caller_(int /*rows*/, int /*cols*/, int h, int w, int level, int /*ndisp*/, int channels, cudaStream_t stream) + void init_data_cost_caller_(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, int /*rows*/, int /*cols*/, int h, int w, int level, int ndisp, int channels, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step, cudaStream_t stream) { dim3 threads(32, 8, 1); dim3 grid(1, 1, 1); @@ -321,15 +278,15 @@ namespace cv { namespace cuda { namespace device switch (channels) { - case 1: init_data_cost<<>>(h, w, level); break; - case 3: init_data_cost<<>>(h, w, level); break; - case 4: init_data_cost<<>>(h, w, level); break; + case 1: init_data_cost<<>>(cleft, cright, ctemp, cimg_step, h, w, level, ndisp, data_weight, max_data_term, min_disp, msg_step, disp_step); break; + case 3: init_data_cost<<>>(cleft, cright, ctemp, cimg_step, h, w, level, ndisp, data_weight, max_data_term, min_disp, msg_step, disp_step); break; + case 4: init_data_cost<<>>(cleft, cright, ctemp, cimg_step, h, w, level, ndisp, data_weight, max_data_term, min_disp, msg_step, disp_step); break; default: CV_Error(cv::Error::BadNumChannels, "Unsupported channels count"); } } template - void init_data_cost_reduce_caller_(int rows, int cols, int h, int w, int level, int ndisp, int channels, cudaStream_t stream) + void init_data_cost_reduce_caller_(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, int rows, int cols, int h, int w, int level, int ndisp, int channels, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step, cudaStream_t stream) { const int threadsNum = 256; const size_t smem_size = threadsNum * sizeof(float); @@ -340,19 +297,19 @@ namespace cv { namespace cuda { namespace device switch (channels) { - case 1: init_data_cost_reduce<<>>(level, rows, cols, h); break; - case 3: init_data_cost_reduce<<>>(level, rows, cols, h); break; - case 4: init_data_cost_reduce<<>>(level, rows, cols, h); break; + case 1: init_data_cost_reduce<<>>(cleft, cright, ctemp, cimg_step, level, rows, cols, h, ndisp, data_weight, max_data_term, min_disp, msg_step, disp_step); break; + case 3: init_data_cost_reduce<<>>(cleft, cright, ctemp, cimg_step, level, rows, cols, h, ndisp, data_weight, max_data_term, min_disp, msg_step, disp_step); break; + case 4: init_data_cost_reduce<<>>(cleft, cright, ctemp, cimg_step, level, rows, cols, h, ndisp, data_weight, max_data_term, min_disp, msg_step, disp_step); break; default: CV_Error(cv::Error::BadNumChannels, "Unsupported channels count"); } } template - void init_data_cost(int rows, int cols, T* disp_selected_pyr, T* data_cost_selected, size_t msg_step, - int h, int w, int level, int nr_plane, int ndisp, int channels, bool use_local_init_data_cost, cudaStream_t stream) + void init_data_cost(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, int rows, int cols, T* disp_selected_pyr, T* data_cost_selected, size_t msg_step, + int h, int w, int level, int nr_plane, int ndisp, int channels, float data_weight, float max_data_term, int min_disp, bool use_local_init_data_cost, cudaStream_t stream) { - typedef void (*InitDataCostCaller)(int cols, int rows, int w, int h, int level, int ndisp, int channels, cudaStream_t stream); + typedef void (*InitDataCostCaller)(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, int cols, int rows, int w, int h, int level, int ndisp, int channels, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step, cudaStream_t stream); static const InitDataCostCaller init_data_cost_callers[] = { @@ -362,10 +319,8 @@ namespace cv { namespace cuda { namespace device }; size_t disp_step = msg_step * h; - cudaSafeCall( cudaMemcpyToSymbol(cdisp_step1, &disp_step, sizeof(size_t)) ); - cudaSafeCall( cudaMemcpyToSymbol(cmsg_step, &msg_step, sizeof(size_t)) ); - init_data_cost_callers[level](rows, cols, h, w, level, ndisp, channels, stream); + init_data_cost_callers[level](cleft, cright, ctemp, cimg_step, rows, cols, h, w, level, ndisp, channels, data_weight, max_data_term, min_disp, msg_step, disp_step, stream); cudaSafeCall( cudaGetLastError() ); if (stream == 0) @@ -378,9 +333,9 @@ namespace cv { namespace cuda { namespace device grid.y = divUp(h, threads.y); if (use_local_init_data_cost == true) - get_first_k_initial_local<<>> (data_cost_selected, disp_selected_pyr, h, w, nr_plane); + get_first_k_initial_local<<>> (ctemp, data_cost_selected, disp_selected_pyr, h, w, nr_plane, ndisp, msg_step, disp_step); else - get_first_k_initial_global<<>>(data_cost_selected, disp_selected_pyr, h, w, nr_plane); + get_first_k_initial_global<<>>(ctemp, data_cost_selected, disp_selected_pyr, h, w, nr_plane, ndisp, msg_step, disp_step); cudaSafeCall( cudaGetLastError() ); @@ -388,18 +343,18 @@ namespace cv { namespace cuda { namespace device cudaSafeCall( cudaDeviceSynchronize() ); } - template void init_data_cost(int rows, int cols, short* disp_selected_pyr, short* data_cost_selected, size_t msg_step, - int h, int w, int level, int nr_plane, int ndisp, int channels, bool use_local_init_data_cost, cudaStream_t stream); + template void init_data_cost(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, int rows, int cols, short* disp_selected_pyr, short* data_cost_selected, size_t msg_step, + int h, int w, int level, int nr_plane, int ndisp, int channels, float data_weight, float max_data_term, int min_disp, bool use_local_init_data_cost, cudaStream_t stream); - template void init_data_cost(int rows, int cols, float* disp_selected_pyr, float* data_cost_selected, size_t msg_step, - int h, int w, int level, int nr_plane, int ndisp, int channels, bool use_local_init_data_cost, cudaStream_t stream); + template void init_data_cost(const uchar *cleft, const uchar *cright, uchar *ctemp, size_t cimg_step, int rows, int cols, float* disp_selected_pyr, float* data_cost_selected, size_t msg_step, + int h, int w, int level, int nr_plane, int ndisp, int channels, float data_weight, float max_data_term, int min_disp, bool use_local_init_data_cost, cudaStream_t stream); /////////////////////////////////////////////////////////////// ////////////////////// compute data cost ////////////////////// /////////////////////////////////////////////////////////////// template - __global__ void compute_data_cost(const T* selected_disp_pyr, T* data_cost_, int h, int w, int level, int nr_plane) + __global__ void compute_data_cost(const uchar *cleft, const uchar *cright, size_t cimg_step, const T* selected_disp_pyr, T* data_cost_, int h, int w, int level, int nr_plane, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step1, size_t disp_step2) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -412,8 +367,8 @@ namespace cv { namespace cuda { namespace device int x0 = x << level; int xt = (x + 1) << level; - const T* selected_disparity = selected_disp_pyr + y/2 * cmsg_step + x/2; - T* data_cost = data_cost_ + y * cmsg_step + x; + const T* selected_disparity = selected_disp_pyr + y/2 * msg_step + x/2; + T* data_cost = data_cost_ + y * msg_step + x; for(int d = 0; d < nr_plane; d++) { @@ -422,27 +377,27 @@ namespace cv { namespace cuda { namespace device { for(int xi = x0; xi < xt; xi++) { - int sel_disp = selected_disparity[d * cdisp_step2]; + int sel_disp = selected_disparity[d * disp_step2]; int xr = xi - sel_disp; - if (xr < 0 || sel_disp < cth) - val += cdata_weight * cmax_data_term; + if (xr < 0 || sel_disp < min_disp) + val += data_weight * max_data_term; else { const uchar* left_x = cleft + yi * cimg_step + xi * channels; const uchar* right_x = cright + yi * cimg_step + xr * channels; - val += DataCostPerPixel::compute(left_x, right_x); + val += data_weight * pixeldiff(left_x, right_x, max_data_term); } } } - data_cost[cdisp_step1 * d] = saturate_cast(val); + data_cost[disp_step1 * d] = saturate_cast(val); } } } template - __global__ void compute_data_cost_reduce(const T* selected_disp_pyr, T* data_cost_, int level, int rows, int cols, int h, int nr_plane) + __global__ void compute_data_cost_reduce(const uchar *cleft, const uchar *cright, size_t cimg_step, const T* selected_disp_pyr, T* data_cost_, int level, int rows, int cols, int h, int nr_plane, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step1, size_t disp_step2) { int x_out = blockIdx.x; int y_out = blockIdx.y % h; @@ -450,12 +405,12 @@ namespace cv { namespace cuda { namespace device int tid = threadIdx.x; - const T* selected_disparity = selected_disp_pyr + y_out/2 * cmsg_step + x_out/2; - T* data_cost = data_cost_ + y_out * cmsg_step + x_out; + const T* selected_disparity = selected_disp_pyr + y_out/2 * msg_step + x_out/2; + T* data_cost = data_cost_ + y_out * msg_step + x_out; if (d < nr_plane) { - int sel_disp = selected_disparity[d * cdisp_step2]; + int sel_disp = selected_disparity[d * disp_step2]; int x0 = x_out << level; int y0 = y_out << level; @@ -465,8 +420,8 @@ namespace cv { namespace cuda { namespace device float val = 0.0f; if (x0 + tid < cols) { - if (x0 + tid - sel_disp < 0 || sel_disp < cth) - val = cdata_weight * cmax_data_term * len; + if (x0 + tid - sel_disp < 0 || sel_disp < min_disp) + val = data_weight * max_data_term * len; else { const uchar* lle = cleft + y0 * cimg_step + channels * (x0 + tid ); @@ -474,7 +429,7 @@ namespace cv { namespace cuda { namespace device for(int y = 0; y < len; ++y) { - val += DataCostPerPixel::compute(lle, lri); + val += data_weight * pixeldiff(lle, lri, max_data_term); lle += cimg_step; lri += cimg_step; @@ -487,13 +442,13 @@ namespace cv { namespace cuda { namespace device reduce(smem + winsz * threadIdx.z, val, tid, plus()); if (tid == 0) - data_cost[cdisp_step1 * d] = saturate_cast(val); + data_cost[disp_step1 * d] = saturate_cast(val); } } template - void compute_data_cost_caller_(const T* disp_selected_pyr, T* data_cost, int /*rows*/, int /*cols*/, - int h, int w, int level, int nr_plane, int channels, cudaStream_t stream) + void compute_data_cost_caller_(const uchar *cleft, const uchar *cright, size_t cimg_step, const T* disp_selected_pyr, T* data_cost, int /*rows*/, int /*cols*/, + int h, int w, int level, int nr_plane, int channels, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step1, size_t disp_step2, cudaStream_t stream) { dim3 threads(32, 8, 1); dim3 grid(1, 1, 1); @@ -503,16 +458,16 @@ namespace cv { namespace cuda { namespace device switch(channels) { - case 1: compute_data_cost<<>>(disp_selected_pyr, data_cost, h, w, level, nr_plane); break; - case 3: compute_data_cost<<>>(disp_selected_pyr, data_cost, h, w, level, nr_plane); break; - case 4: compute_data_cost<<>>(disp_selected_pyr, data_cost, h, w, level, nr_plane); break; + case 1: compute_data_cost<<>>(cleft, cright, cimg_step, disp_selected_pyr, data_cost, h, w, level, nr_plane, data_weight, max_data_term, min_disp, msg_step, disp_step1, disp_step2); break; + case 3: compute_data_cost<<>>(cleft, cright, cimg_step, disp_selected_pyr, data_cost, h, w, level, nr_plane, data_weight, max_data_term, min_disp, msg_step, disp_step1, disp_step2); break; + case 4: compute_data_cost<<>>(cleft, cright, cimg_step, disp_selected_pyr, data_cost, h, w, level, nr_plane, data_weight, max_data_term, min_disp, msg_step, disp_step1, disp_step2); break; default: CV_Error(cv::Error::BadNumChannels, "Unsupported channels count"); } } template - void compute_data_cost_reduce_caller_(const T* disp_selected_pyr, T* data_cost, int rows, int cols, - int h, int w, int level, int nr_plane, int channels, cudaStream_t stream) + void compute_data_cost_reduce_caller_(const uchar *cleft, const uchar *cright, size_t cimg_step, const T* disp_selected_pyr, T* data_cost, int rows, int cols, + int h, int w, int level, int nr_plane, int channels, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step1, size_t disp_step2, cudaStream_t stream) { const int threadsNum = 256; const size_t smem_size = threadsNum * sizeof(float); @@ -523,19 +478,20 @@ namespace cv { namespace cuda { namespace device switch (channels) { - case 1: compute_data_cost_reduce<<>>(disp_selected_pyr, data_cost, level, rows, cols, h, nr_plane); break; - case 3: compute_data_cost_reduce<<>>(disp_selected_pyr, data_cost, level, rows, cols, h, nr_plane); break; - case 4: compute_data_cost_reduce<<>>(disp_selected_pyr, data_cost, level, rows, cols, h, nr_plane); break; + case 1: compute_data_cost_reduce<<>>(cleft, cright, cimg_step, disp_selected_pyr, data_cost, level, rows, cols, h, nr_plane, data_weight, max_data_term, min_disp, msg_step, disp_step1, disp_step2); break; + case 3: compute_data_cost_reduce<<>>(cleft, cright, cimg_step, disp_selected_pyr, data_cost, level, rows, cols, h, nr_plane, data_weight, max_data_term, min_disp, msg_step, disp_step1, disp_step2); break; + case 4: compute_data_cost_reduce<<>>(cleft, cright, cimg_step, disp_selected_pyr, data_cost, level, rows, cols, h, nr_plane, data_weight, max_data_term, min_disp, msg_step, disp_step1, disp_step2); break; default: CV_Error(cv::Error::BadNumChannels, "Unsupported channels count"); } } template - void compute_data_cost(const T* disp_selected_pyr, T* data_cost, size_t msg_step, - int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, cudaStream_t stream) + void compute_data_cost(const uchar *cleft, const uchar *cright, size_t cimg_step, const T* disp_selected_pyr, T* data_cost, size_t msg_step, + int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, float data_weight, float max_data_term, + int min_disp, cudaStream_t stream) { - typedef void (*ComputeDataCostCaller)(const T* disp_selected_pyr, T* data_cost, int rows, int cols, - int h, int w, int level, int nr_plane, int channels, cudaStream_t stream); + typedef void (*ComputeDataCostCaller)(const uchar *cleft, const uchar *cright, size_t cimg_step, const T* disp_selected_pyr, T* data_cost, int rows, int cols, + int h, int w, int level, int nr_plane, int channels, float data_weight, float max_data_term, int min_disp, size_t msg_step, size_t disp_step1, size_t disp_step2, cudaStream_t stream); static const ComputeDataCostCaller callers[] = { @@ -546,22 +502,19 @@ namespace cv { namespace cuda { namespace device size_t disp_step1 = msg_step * h; size_t disp_step2 = msg_step * h2; - cudaSafeCall( cudaMemcpyToSymbol(cdisp_step1, &disp_step1, sizeof(size_t)) ); - cudaSafeCall( cudaMemcpyToSymbol(cdisp_step2, &disp_step2, sizeof(size_t)) ); - cudaSafeCall( cudaMemcpyToSymbol(cmsg_step, &msg_step, sizeof(size_t)) ); - callers[level](disp_selected_pyr, data_cost, rows, cols, h, w, level, nr_plane, channels, stream); + callers[level](cleft, cright, cimg_step, disp_selected_pyr, data_cost, rows, cols, h, w, level, nr_plane, channels, data_weight, max_data_term, min_disp, msg_step, disp_step1, disp_step2, stream); cudaSafeCall( cudaGetLastError() ); if (stream == 0) cudaSafeCall( cudaDeviceSynchronize() ); } - template void compute_data_cost(const short* disp_selected_pyr, short* data_cost, size_t msg_step, - int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, cudaStream_t stream); + template void compute_data_cost(const uchar *cleft, const uchar *cright, size_t cimg_step, const short* disp_selected_pyr, short* data_cost, size_t msg_step, + int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, float data_weight, float max_data_term, int min_disp, cudaStream_t stream); - template void compute_data_cost(const float* disp_selected_pyr, float* data_cost, size_t msg_step, - int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, cudaStream_t stream); + template void compute_data_cost(const uchar *cleft, const uchar *cright, size_t cimg_step, const float* disp_selected_pyr, float* data_cost, size_t msg_step, + int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, float data_weight, float max_data_term, int min_disp, cudaStream_t stream); /////////////////////////////////////////////////////////////// @@ -574,7 +527,7 @@ namespace cv { namespace cuda { namespace device const T* u_cur, const T* d_cur, const T* l_cur, const T* r_cur, T* data_cost_selected, T* disparity_selected_new, T* data_cost_new, const T* data_cost_cur, const T* disparity_selected_cur, - int nr_plane, int nr_plane2) + int nr_plane, int nr_plane2, size_t disp_step1, size_t disp_step2) { for(int i = 0; i < nr_plane; i++) { @@ -582,7 +535,7 @@ namespace cv { namespace cuda { namespace device int id = 0; for(int j = 0; j < nr_plane2; j++) { - T cur = data_cost_new[j * cdisp_step1]; + T cur = data_cost_new[j * disp_step1]; if(cur < minimum) { minimum = cur; @@ -590,70 +543,72 @@ namespace cv { namespace cuda { namespace device } } - data_cost_selected[i * cdisp_step1] = data_cost_cur[id * cdisp_step1]; - disparity_selected_new[i * cdisp_step1] = disparity_selected_cur[id * cdisp_step2]; + data_cost_selected[i * disp_step1] = data_cost_cur[id * disp_step1]; + disparity_selected_new[i * disp_step1] = disparity_selected_cur[id * disp_step2]; - u_new[i * cdisp_step1] = u_cur[id * cdisp_step2]; - d_new[i * cdisp_step1] = d_cur[id * cdisp_step2]; - l_new[i * cdisp_step1] = l_cur[id * cdisp_step2]; - r_new[i * cdisp_step1] = r_cur[id * cdisp_step2]; + u_new[i * disp_step1] = u_cur[id * disp_step2]; + d_new[i * disp_step1] = d_cur[id * disp_step2]; + l_new[i * disp_step1] = l_cur[id * disp_step2]; + r_new[i * disp_step1] = r_cur[id * disp_step2]; - data_cost_new[id * cdisp_step1] = numeric_limits::max(); + data_cost_new[id * disp_step1] = numeric_limits::max(); } } template - __global__ void init_message(T* u_new_, T* d_new_, T* l_new_, T* r_new_, + __global__ void init_message(uchar *ctemp, T* u_new_, T* d_new_, T* l_new_, T* r_new_, const T* u_cur_, const T* d_cur_, const T* l_cur_, const T* r_cur_, T* selected_disp_pyr_new, const T* selected_disp_pyr_cur, T* data_cost_selected_, const T* data_cost_, - int h, int w, int nr_plane, int h2, int w2, int nr_plane2) + int h, int w, int nr_plane, int h2, int w2, int nr_plane2, + size_t msg_step, size_t disp_step1, size_t disp_step2) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; if (y < h && x < w) { - const T* u_cur = u_cur_ + ::min(h2-1, y/2 + 1) * cmsg_step + x/2; - const T* d_cur = d_cur_ + ::max(0, y/2 - 1) * cmsg_step + x/2; - const T* l_cur = l_cur_ + (y/2) * cmsg_step + ::min(w2-1, x/2 + 1); - const T* r_cur = r_cur_ + (y/2) * cmsg_step + ::max(0, x/2 - 1); + const T* u_cur = u_cur_ + ::min(h2-1, y/2 + 1) * msg_step + x/2; + const T* d_cur = d_cur_ + ::max(0, y/2 - 1) * msg_step + x/2; + const T* l_cur = l_cur_ + (y/2) * msg_step + ::min(w2-1, x/2 + 1); + const T* r_cur = r_cur_ + (y/2) * msg_step + ::max(0, x/2 - 1); - T* data_cost_new = (T*)ctemp + y * cmsg_step + x; + T* data_cost_new = (T*)ctemp + y * msg_step + x; - const T* disparity_selected_cur = selected_disp_pyr_cur + y/2 * cmsg_step + x/2; - const T* data_cost = data_cost_ + y * cmsg_step + x; + const T* disparity_selected_cur = selected_disp_pyr_cur + y/2 * msg_step + x/2; + const T* data_cost = data_cost_ + y * msg_step + x; for(int d = 0; d < nr_plane2; d++) { - int idx2 = d * cdisp_step2; + int idx2 = d * disp_step2; - T val = data_cost[d * cdisp_step1] + u_cur[idx2] + d_cur[idx2] + l_cur[idx2] + r_cur[idx2]; - data_cost_new[d * cdisp_step1] = val; + T val = data_cost[d * disp_step1] + u_cur[idx2] + d_cur[idx2] + l_cur[idx2] + r_cur[idx2]; + data_cost_new[d * disp_step1] = val; } - T* data_cost_selected = data_cost_selected_ + y * cmsg_step + x; - T* disparity_selected_new = selected_disp_pyr_new + y * cmsg_step + x; + T* data_cost_selected = data_cost_selected_ + y * msg_step + x; + T* disparity_selected_new = selected_disp_pyr_new + y * msg_step + x; - T* u_new = u_new_ + y * cmsg_step + x; - T* d_new = d_new_ + y * cmsg_step + x; - T* l_new = l_new_ + y * cmsg_step + x; - T* r_new = r_new_ + y * cmsg_step + x; + T* u_new = u_new_ + y * msg_step + x; + T* d_new = d_new_ + y * msg_step + x; + T* l_new = l_new_ + y * msg_step + x; + T* r_new = r_new_ + y * msg_step + x; - u_cur = u_cur_ + y/2 * cmsg_step + x/2; - d_cur = d_cur_ + y/2 * cmsg_step + x/2; - l_cur = l_cur_ + y/2 * cmsg_step + x/2; - r_cur = r_cur_ + y/2 * cmsg_step + x/2; + u_cur = u_cur_ + y/2 * msg_step + x/2; + d_cur = d_cur_ + y/2 * msg_step + x/2; + l_cur = l_cur_ + y/2 * msg_step + x/2; + r_cur = r_cur_ + y/2 * msg_step + x/2; get_first_k_element_increase(u_new, d_new, l_new, r_new, u_cur, d_cur, l_cur, r_cur, data_cost_selected, disparity_selected_new, data_cost_new, - data_cost, disparity_selected_cur, nr_plane, nr_plane2); + data_cost, disparity_selected_cur, nr_plane, nr_plane2, + disp_step1, disp_step2); } } template - void init_message(T* u_new, T* d_new, T* l_new, T* r_new, + void init_message(uchar *ctemp, T* u_new, T* d_new, T* l_new, T* r_new, const T* u_cur, const T* d_cur, const T* l_cur, const T* r_cur, T* selected_disp_pyr_new, const T* selected_disp_pyr_cur, T* data_cost_selected, const T* data_cost, size_t msg_step, @@ -662,9 +617,6 @@ namespace cv { namespace cuda { namespace device size_t disp_step1 = msg_step * h; size_t disp_step2 = msg_step * h2; - cudaSafeCall( cudaMemcpyToSymbol(cdisp_step1, &disp_step1, sizeof(size_t)) ); - cudaSafeCall( cudaMemcpyToSymbol(cdisp_step2, &disp_step2, sizeof(size_t)) ); - cudaSafeCall( cudaMemcpyToSymbol(cmsg_step, &msg_step, sizeof(size_t)) ); dim3 threads(32, 8, 1); dim3 grid(1, 1, 1); @@ -672,11 +624,12 @@ namespace cv { namespace cuda { namespace device grid.x = divUp(w, threads.x); grid.y = divUp(h, threads.y); - init_message<<>>(u_new, d_new, l_new, r_new, + init_message<<>>(ctemp, u_new, d_new, l_new, r_new, u_cur, d_cur, l_cur, r_cur, selected_disp_pyr_new, selected_disp_pyr_cur, data_cost_selected, data_cost, - h, w, nr_plane, h2, w2, nr_plane2); + h, w, nr_plane, h2, w2, nr_plane2, + msg_step, disp_step1, disp_step2); cudaSafeCall( cudaGetLastError() ); if (stream == 0) @@ -684,13 +637,13 @@ namespace cv { namespace cuda { namespace device } - template void init_message(short* u_new, short* d_new, short* l_new, short* r_new, + template void init_message(uchar *ctemp, short* u_new, short* d_new, short* l_new, short* r_new, const short* u_cur, const short* d_cur, const short* l_cur, const short* r_cur, short* selected_disp_pyr_new, const short* selected_disp_pyr_cur, short* data_cost_selected, const short* data_cost, size_t msg_step, int h, int w, int nr_plane, int h2, int w2, int nr_plane2, cudaStream_t stream); - template void init_message(float* u_new, float* d_new, float* l_new, float* r_new, + template void init_message(uchar *ctemp, float* u_new, float* d_new, float* l_new, float* r_new, const float* u_cur, const float* d_cur, const float* l_cur, const float* r_cur, float* selected_disp_pyr_new, const float* selected_disp_pyr_cur, float* data_cost_selected, const float* data_cost, size_t msg_step, @@ -702,13 +655,14 @@ namespace cv { namespace cuda { namespace device template __device__ void message_per_pixel(const T* data, T* msg_dst, const T* msg1, const T* msg2, const T* msg3, - const T* dst_disp, const T* src_disp, int nr_plane, volatile T* temp) + const T* dst_disp, const T* src_disp, int nr_plane, int max_disc_term, float disc_single_jump, volatile T* temp, + size_t disp_step) { T minimum = numeric_limits::max(); for(int d = 0; d < nr_plane; d++) { - int idx = d * cdisp_step1; + int idx = d * disp_step; T val = data[idx] + msg1[idx] + msg2[idx] + msg3[idx]; if(val < minimum) @@ -720,55 +674,53 @@ namespace cv { namespace cuda { namespace device float sum = 0; for(int d = 0; d < nr_plane; d++) { - float cost_min = minimum + cmax_disc_term; - T src_disp_reg = src_disp[d * cdisp_step1]; + float cost_min = minimum + max_disc_term; + T src_disp_reg = src_disp[d * disp_step]; for(int d2 = 0; d2 < nr_plane; d2++) - cost_min = fmin(cost_min, msg_dst[d2 * cdisp_step1] + cdisc_single_jump * ::abs(dst_disp[d2 * cdisp_step1] - src_disp_reg)); + cost_min = fmin(cost_min, msg_dst[d2 * disp_step] + disc_single_jump * ::abs(dst_disp[d2 * disp_step] - src_disp_reg)); - temp[d * cdisp_step1] = saturate_cast(cost_min); + temp[d * disp_step] = saturate_cast(cost_min); sum += cost_min; } sum /= nr_plane; for(int d = 0; d < nr_plane; d++) - msg_dst[d * cdisp_step1] = saturate_cast(temp[d * cdisp_step1] - sum); + msg_dst[d * disp_step] = saturate_cast(temp[d * disp_step] - sum); } template - __global__ void compute_message(T* u_, T* d_, T* l_, T* r_, const T* data_cost_selected, const T* selected_disp_pyr_cur, int h, int w, int nr_plane, int i) + __global__ void compute_message(uchar *ctemp, T* u_, T* d_, T* l_, T* r_, const T* data_cost_selected, const T* selected_disp_pyr_cur, int h, int w, int nr_plane, int i, int max_disc_term, float disc_single_jump, size_t msg_step, size_t disp_step) { int y = blockIdx.y * blockDim.y + threadIdx.y; int x = ((blockIdx.x * blockDim.x + threadIdx.x) << 1) + ((y + i) & 1); if (y > 0 && y < h - 1 && x > 0 && x < w - 1) { - const T* data = data_cost_selected + y * cmsg_step + x; + const T* data = data_cost_selected + y * msg_step + x; - T* u = u_ + y * cmsg_step + x; - T* d = d_ + y * cmsg_step + x; - T* l = l_ + y * cmsg_step + x; - T* r = r_ + y * cmsg_step + x; + T* u = u_ + y * msg_step + x; + T* d = d_ + y * msg_step + x; + T* l = l_ + y * msg_step + x; + T* r = r_ + y * msg_step + x; - const T* disp = selected_disp_pyr_cur + y * cmsg_step + x; + const T* disp = selected_disp_pyr_cur + y * msg_step + x; - T* temp = (T*)ctemp + y * cmsg_step + x; + T* temp = (T*)ctemp + y * msg_step + x; - message_per_pixel(data, u, r - 1, u + cmsg_step, l + 1, disp, disp - cmsg_step, nr_plane, temp); - message_per_pixel(data, d, d - cmsg_step, r - 1, l + 1, disp, disp + cmsg_step, nr_plane, temp); - message_per_pixel(data, l, u + cmsg_step, d - cmsg_step, l + 1, disp, disp - 1, nr_plane, temp); - message_per_pixel(data, r, u + cmsg_step, d - cmsg_step, r - 1, disp, disp + 1, nr_plane, temp); + message_per_pixel(data, u, r - 1, u + msg_step, l + 1, disp, disp - msg_step, nr_plane, max_disc_term, disc_single_jump, temp, disp_step); + message_per_pixel(data, d, d - msg_step, r - 1, l + 1, disp, disp + msg_step, nr_plane, max_disc_term, disc_single_jump, temp, disp_step); + message_per_pixel(data, l, u + msg_step, d - msg_step, l + 1, disp, disp - 1, nr_plane, max_disc_term, disc_single_jump, temp, disp_step); + message_per_pixel(data, r, u + msg_step, d - msg_step, r - 1, disp, disp + 1, nr_plane, max_disc_term, disc_single_jump, temp, disp_step); } } template - void calc_all_iterations(T* u, T* d, T* l, T* r, const T* data_cost_selected, - const T* selected_disp_pyr_cur, size_t msg_step, int h, int w, int nr_plane, int iters, cudaStream_t stream) + void calc_all_iterations(uchar *ctemp, T* u, T* d, T* l, T* r, const T* data_cost_selected, + const T* selected_disp_pyr_cur, size_t msg_step, int h, int w, int nr_plane, int iters, int max_disc_term, float disc_single_jump, cudaStream_t stream) { size_t disp_step = msg_step * h; - cudaSafeCall( cudaMemcpyToSymbol(cdisp_step1, &disp_step, sizeof(size_t)) ); - cudaSafeCall( cudaMemcpyToSymbol(cmsg_step, &msg_step, sizeof(size_t)) ); dim3 threads(32, 8, 1); dim3 grid(1, 1, 1); @@ -778,18 +730,18 @@ namespace cv { namespace cuda { namespace device for(int t = 0; t < iters; ++t) { - compute_message<<>>(u, d, l, r, data_cost_selected, selected_disp_pyr_cur, h, w, nr_plane, t & 1); + compute_message<<>>(ctemp, u, d, l, r, data_cost_selected, selected_disp_pyr_cur, h, w, nr_plane, t & 1, max_disc_term, disc_single_jump, msg_step, disp_step); cudaSafeCall( cudaGetLastError() ); } if (stream == 0) cudaSafeCall( cudaDeviceSynchronize() ); }; - template void calc_all_iterations(short* u, short* d, short* l, short* r, const short* data_cost_selected, const short* selected_disp_pyr_cur, size_t msg_step, - int h, int w, int nr_plane, int iters, cudaStream_t stream); + template void calc_all_iterations(uchar *ctemp, short* u, short* d, short* l, short* r, const short* data_cost_selected, const short* selected_disp_pyr_cur, size_t msg_step, + int h, int w, int nr_plane, int iters, int max_disc_term, float disc_single_jump, cudaStream_t stream); - template void calc_all_iterations(float* u, float* d, float* l, float* r, const float* data_cost_selected, const float* selected_disp_pyr_cur, size_t msg_step, - int h, int w, int nr_plane, int iters, cudaStream_t stream); + template void calc_all_iterations(uchar *ctemp, float* u, float* d, float* l, float* r, const float* data_cost_selected, const float* selected_disp_pyr_cur, size_t msg_step, + int h, int w, int nr_plane, int iters, int max_disc_term, float disc_single_jump, cudaStream_t stream); /////////////////////////////////////////////////////////////// @@ -800,26 +752,26 @@ namespace cv { namespace cuda { namespace device template __global__ void compute_disp(const T* u_, const T* d_, const T* l_, const T* r_, const T* data_cost_selected, const T* disp_selected_pyr, - PtrStepSz disp, int nr_plane) + PtrStepSz disp, int nr_plane, size_t msg_step, size_t disp_step) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; if (y > 0 && y < disp.rows - 1 && x > 0 && x < disp.cols - 1) { - const T* data = data_cost_selected + y * cmsg_step + x; - const T* disp_selected = disp_selected_pyr + y * cmsg_step + x; + const T* data = data_cost_selected + y * msg_step + x; + const T* disp_selected = disp_selected_pyr + y * msg_step + x; - const T* u = u_ + (y+1) * cmsg_step + (x+0); - const T* d = d_ + (y-1) * cmsg_step + (x+0); - const T* l = l_ + (y+0) * cmsg_step + (x+1); - const T* r = r_ + (y+0) * cmsg_step + (x-1); + const T* u = u_ + (y+1) * msg_step + (x+0); + const T* d = d_ + (y-1) * msg_step + (x+0); + const T* l = l_ + (y+0) * msg_step + (x+1); + const T* r = r_ + (y+0) * msg_step + (x-1); int best = 0; T best_val = numeric_limits::max(); for (int i = 0; i < nr_plane; ++i) { - int idx = i * cdisp_step1; + int idx = i * disp_step; T val = data[idx]+ u[idx] + d[idx] + l[idx] + r[idx]; if (val < best_val) @@ -837,8 +789,6 @@ namespace cv { namespace cuda { namespace device const PtrStepSz& disp, int nr_plane, cudaStream_t stream) { size_t disp_step = disp.rows * msg_step; - cudaSafeCall( cudaMemcpyToSymbol(cdisp_step1, &disp_step, sizeof(size_t)) ); - cudaSafeCall( cudaMemcpyToSymbol(cmsg_step, &msg_step, sizeof(size_t)) ); dim3 threads(32, 8, 1); dim3 grid(1, 1, 1); @@ -846,7 +796,7 @@ namespace cv { namespace cuda { namespace device grid.x = divUp(disp.cols, threads.x); grid.y = divUp(disp.rows, threads.y); - compute_disp<<>>(u, d, l, r, data_cost_selected, disp_selected, disp, nr_plane); + compute_disp<<>>(u, d, l, r, data_cost_selected, disp_selected, disp, nr_plane, msg_step, disp_step); cudaSafeCall( cudaGetLastError() ); if (stream == 0) diff --git a/modules/cudastereo/src/cuda/stereocsbp.hpp b/modules/cudastereo/src/cuda/stereocsbp.hpp new file mode 100644 index 0000000000..305497292d --- /dev/null +++ b/modules/cudastereo/src/cuda/stereocsbp.hpp @@ -0,0 +1,29 @@ +namespace cv { namespace cuda { namespace device +{ + namespace stereocsbp + { + template + void init_data_cost(const uchar *left, const uchar *right, uchar *ctemp, size_t cimg_step, int rows, int cols, T* disp_selected_pyr, T* data_cost_selected, size_t msg_step, + int h, int w, int level, int nr_plane, int ndisp, int channels, float data_weight, float max_data_term, int min_disp, bool use_local_init_data_cost, cudaStream_t stream); + + template + void compute_data_cost(const uchar *left, const uchar *right, size_t cimg_step, const T* disp_selected_pyr, T* data_cost, size_t msg_step, + int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, float data_weight, float max_data_term, + int min_disp, cudaStream_t stream); + + template + void init_message(uchar *ctemp, T* u_new, T* d_new, T* l_new, T* r_new, + const T* u_cur, const T* d_cur, const T* l_cur, const T* r_cur, + T* selected_disp_pyr_new, const T* selected_disp_pyr_cur, + T* data_cost_selected, const T* data_cost, size_t msg_step, + int h, int w, int nr_plane, int h2, int w2, int nr_plane2, cudaStream_t stream); + + template + void calc_all_iterations(uchar *ctemp, T* u, T* d, T* l, T* r, const T* data_cost_selected, + const T* selected_disp_pyr_cur, size_t msg_step, int h, int w, int nr_plane, int iters, int max_disc_term, float disc_single_jump, cudaStream_t stream); + + template + void compute_disp(const T* u, const T* d, const T* l, const T* r, const T* data_cost_selected, const T* disp_selected, size_t msg_step, + const PtrStepSz& disp, int nr_plane, cudaStream_t stream); + } +}}} diff --git a/modules/cudastereo/src/disparity_bilateral_filter.cpp b/modules/cudastereo/src/disparity_bilateral_filter.cpp index 75cbce48a9..c59e3b2cb4 100644 --- a/modules/cudastereo/src/disparity_bilateral_filter.cpp +++ b/modules/cudastereo/src/disparity_bilateral_filter.cpp @@ -51,16 +51,7 @@ Ptr cv::cuda::createDisparityBilateralFilter(int #else /* !defined (HAVE_CUDA) */ -namespace cv { namespace cuda { namespace device -{ - namespace disp_bilateral_filter - { - void disp_load_constants(float* table_color, PtrStepSzf table_space, int ndisp, int radius, short edge_disc, short max_disc); - - template - void disp_bilateral_filter(PtrStepSz disp, PtrStepSzb img, int channels, int iters, cudaStream_t stream); - } -}}} +#include "cuda/disparity_bilateral_filter.hpp" namespace { @@ -165,7 +156,7 @@ namespace const short edge_disc = std::max(short(1), short(ndisp * edge_threshold + 0.5)); const short max_disc = short(ndisp * max_disc_threshold + 0.5); - disp_load_constants(table_color.ptr(), table_space, ndisp, radius, edge_disc, max_disc); + size_t table_space_step = table_space.step / sizeof(float); _dst.create(disp.size(), disp.type()); GpuMat dst = _dst.getGpuMat(); @@ -173,7 +164,7 @@ namespace if (dst.data != disp.data) disp.copyTo(dst, stream); - disp_bilateral_filter(dst, img, img.channels(), iters, StreamAccessor::getStream(stream)); + disp_bilateral_filter(dst, img, img.channels(), iters, table_color.ptr(), (float *)table_space.data, table_space_step, radius, edge_disc, max_disc, StreamAccessor::getStream(stream)); } void DispBilateralFilterImpl::apply(InputArray _disp, InputArray _image, OutputArray dst, Stream& stream) diff --git a/modules/cudastereo/src/stereocsbp.cpp b/modules/cudastereo/src/stereocsbp.cpp index 474562baf2..ded5fa20e1 100644 --- a/modules/cudastereo/src/stereocsbp.cpp +++ b/modules/cudastereo/src/stereocsbp.cpp @@ -53,37 +53,7 @@ Ptr cv::cuda::createStereoConstantSpaceBP(int, int, #else /* !defined (HAVE_CUDA) */ -namespace cv { namespace cuda { namespace device -{ - namespace stereocsbp - { - void load_constants(int ndisp, float max_data_term, float data_weight, float max_disc_term, float disc_single_jump, int min_disp_th, - const PtrStepSzb& left, const PtrStepSzb& right, const PtrStepSzb& temp); - - template - void init_data_cost(int rows, int cols, T* disp_selected_pyr, T* data_cost_selected, size_t msg_step, - int h, int w, int level, int nr_plane, int ndisp, int channels, bool use_local_init_data_cost, cudaStream_t stream); - - template - void compute_data_cost(const T* disp_selected_pyr, T* data_cost, size_t msg_step, - int rows, int cols, int h, int w, int h2, int level, int nr_plane, int channels, cudaStream_t stream); - - template - void init_message(T* u_new, T* d_new, T* l_new, T* r_new, - const T* u_cur, const T* d_cur, const T* l_cur, const T* r_cur, - T* selected_disp_pyr_new, const T* selected_disp_pyr_cur, - T* data_cost_selected, const T* data_cost, size_t msg_step, - int h, int w, int nr_plane, int h2, int w2, int nr_plane2, cudaStream_t stream); - - template - void calc_all_iterations(T* u, T* d, T* l, T* r, const T* data_cost_selected, - const T* selected_disp_pyr_cur, size_t msg_step, int h, int w, int nr_plane, int iters, cudaStream_t stream); - - template - void compute_disp(const T* u, const T* d, const T* l, const T* r, const T* data_cost_selected, const T* disp_selected, size_t msg_step, - const PtrStepSz& disp, int nr_plane, cudaStream_t stream); - } -}}} +#include "cuda/stereocsbp.hpp" namespace { @@ -252,8 +222,6 @@ namespace //////////////////////////////////////////////////////////////////////////// // Compute - load_constants(ndisp_, max_data_term_, data_weight_, max_disc_term_, disc_single_jump_, min_disp_th_, left, right, temp_); - l[0].setTo(0, _stream); d[0].setTo(0, _stream); r[0].setTo(0, _stream); @@ -275,17 +243,18 @@ namespace { if (i == levels_ - 1) { - init_data_cost(left.rows, left.cols, disp_selected_pyr[cur_idx].ptr(), data_cost_selected.ptr(), - elem_step, rows_pyr[i], cols_pyr[i], i, nr_plane_pyr[i], ndisp_, left.channels(), use_local_init_data_cost_, stream); + init_data_cost(left.ptr(), right.ptr(), temp_.ptr(), left.step, left.rows, left.cols, disp_selected_pyr[cur_idx].ptr(), data_cost_selected.ptr(), + elem_step, rows_pyr[i], cols_pyr[i], i, nr_plane_pyr[i], ndisp_, left.channels(), data_weight_, max_data_term_, min_disp_th_, use_local_init_data_cost_, stream); } else { - compute_data_cost(disp_selected_pyr[cur_idx].ptr(), data_cost.ptr(), elem_step, - left.rows, left.cols, rows_pyr[i], cols_pyr[i], rows_pyr[i+1], i, nr_plane_pyr[i+1], left.channels(), stream); + compute_data_cost(left.ptr(), right.ptr(), left.step, disp_selected_pyr[cur_idx].ptr(), data_cost.ptr(), elem_step, + left.rows, left.cols, rows_pyr[i], cols_pyr[i], rows_pyr[i+1], i, nr_plane_pyr[i+1], left.channels(), data_weight_, max_data_term_, min_disp_th_, stream); int new_idx = (cur_idx + 1) & 1; - init_message(u[new_idx].ptr(), d[new_idx].ptr(), l[new_idx].ptr(), r[new_idx].ptr(), + init_message(temp_.ptr(), + u[new_idx].ptr(), d[new_idx].ptr(), l[new_idx].ptr(), r[new_idx].ptr(), u[cur_idx].ptr(), d[cur_idx].ptr(), l[cur_idx].ptr(), r[cur_idx].ptr(), disp_selected_pyr[new_idx].ptr(), disp_selected_pyr[cur_idx].ptr(), data_cost_selected.ptr(), data_cost.ptr(), elem_step, rows_pyr[i], @@ -294,9 +263,9 @@ namespace cur_idx = new_idx; } - calc_all_iterations(u[cur_idx].ptr(), d[cur_idx].ptr(), l[cur_idx].ptr(), r[cur_idx].ptr(), + calc_all_iterations(temp_.ptr(), u[cur_idx].ptr(), d[cur_idx].ptr(), l[cur_idx].ptr(), r[cur_idx].ptr(), data_cost_selected.ptr(), disp_selected_pyr[cur_idx].ptr(), elem_step, - rows_pyr[i], cols_pyr[i], nr_plane_pyr[i], iters_, stream); + rows_pyr[i], cols_pyr[i], nr_plane_pyr[i], iters_, max_disc_term_, disc_single_jump_, stream); } } else @@ -305,17 +274,18 @@ namespace { if (i == levels_ - 1) { - init_data_cost(left.rows, left.cols, disp_selected_pyr[cur_idx].ptr(), data_cost_selected.ptr(), - elem_step, rows_pyr[i], cols_pyr[i], i, nr_plane_pyr[i], ndisp_, left.channels(), use_local_init_data_cost_, stream); + init_data_cost(left.ptr(), right.ptr(), temp_.ptr(), left.step, left.rows, left.cols, disp_selected_pyr[cur_idx].ptr(), data_cost_selected.ptr(), + elem_step, rows_pyr[i], cols_pyr[i], i, nr_plane_pyr[i], ndisp_, left.channels(), data_weight_, max_data_term_, min_disp_th_, use_local_init_data_cost_, stream); } else { - compute_data_cost(disp_selected_pyr[cur_idx].ptr(), data_cost.ptr(), elem_step, - left.rows, left.cols, rows_pyr[i], cols_pyr[i], rows_pyr[i+1], i, nr_plane_pyr[i+1], left.channels(), stream); + compute_data_cost(left.ptr(), right.ptr(), left.step, disp_selected_pyr[cur_idx].ptr(), data_cost.ptr(), elem_step, + left.rows, left.cols, rows_pyr[i], cols_pyr[i], rows_pyr[i+1], i, nr_plane_pyr[i+1], left.channels(), data_weight_, max_data_term_, min_disp_th_, stream); int new_idx = (cur_idx + 1) & 1; - init_message(u[new_idx].ptr(), d[new_idx].ptr(), l[new_idx].ptr(), r[new_idx].ptr(), + init_message(temp_.ptr(), + u[new_idx].ptr(), d[new_idx].ptr(), l[new_idx].ptr(), r[new_idx].ptr(), u[cur_idx].ptr(), d[cur_idx].ptr(), l[cur_idx].ptr(), r[cur_idx].ptr(), disp_selected_pyr[new_idx].ptr(), disp_selected_pyr[cur_idx].ptr(), data_cost_selected.ptr(), data_cost.ptr(), elem_step, rows_pyr[i], @@ -324,9 +294,9 @@ namespace cur_idx = new_idx; } - calc_all_iterations(u[cur_idx].ptr(), d[cur_idx].ptr(), l[cur_idx].ptr(), r[cur_idx].ptr(), + calc_all_iterations(temp_.ptr(), u[cur_idx].ptr(), d[cur_idx].ptr(), l[cur_idx].ptr(), r[cur_idx].ptr(), data_cost_selected.ptr(), disp_selected_pyr[cur_idx].ptr(), elem_step, - rows_pyr[i], cols_pyr[i], nr_plane_pyr[i], iters_, stream); + rows_pyr[i], cols_pyr[i], nr_plane_pyr[i], iters_, max_disc_term_, disc_single_jump_, stream); } } diff --git a/modules/cudev/test/CMakeLists.txt b/modules/cudev/test/CMakeLists.txt index 438e0a64c0..363970e4b7 100644 --- a/modules/cudev/test/CMakeLists.txt +++ b/modules/cudev/test/CMakeLists.txt @@ -1,4 +1,4 @@ -set(test_deps opencv_cudev opencv_core opencv_imgproc opencv_highgui opencv_ts ${OPENCV_MODULE_opencv_ts_DEPS}) +set(test_deps opencv_cudev opencv_core opencv_imgproc opencv_imgcodecs opencv_videoio opencv_highgui opencv_ts ${OPENCV_MODULE_opencv_ts_DEPS}) ocv_check_dependencies(${test_deps}) diff --git a/modules/features2d/doc/feature_detection_and_description.rst b/modules/features2d/doc/feature_detection_and_description.rst index 409fe54b7a..4aa7dd3498 100644 --- a/modules/features2d/doc/feature_detection_and_description.rst +++ b/modules/features2d/doc/feature_detection_and_description.rst @@ -31,7 +31,7 @@ Detects corners using the FAST algorithm Detects corners using the FAST algorithm by [Rosten06]_. -..note:: In Python API, types are given as ``cv2.FAST_FEATURE_DETECTOR_TYPE_5_8``, ``cv2.FAST_FEATURE_DETECTOR_TYPE_7_12`` and ``cv2.FAST_FEATURE_DETECTOR_TYPE_9_16``. For corner detection, use ``cv2.FAST.detect()`` method. +.. note:: In Python API, types are given as ``cv2.FAST_FEATURE_DETECTOR_TYPE_5_8``, ``cv2.FAST_FEATURE_DETECTOR_TYPE_7_12`` and ``cv2.FAST_FEATURE_DETECTOR_TYPE_9_16``. For corner detection, use ``cv2.FAST.detect()`` method. .. [Rosten06] E. Rosten. Machine Learning for High-speed Corner Detection, 2006. @@ -254,7 +254,17 @@ KAZE ---- .. ocv:class:: KAZE : public Feature2D -Class implementing the KAZE keypoint detector and descriptor extractor, described in [ABD12]_. +Class implementing the KAZE keypoint detector and descriptor extractor, described in [ABD12]_. :: + + class CV_EXPORTS_W KAZE : public Feature2D + { + public: + CV_WRAP KAZE(); + CV_WRAP explicit KAZE(bool extended, bool upright, float threshold = 0.001f, + int octaves = 4, int sublevels = 4, int diffusivity = DIFF_PM_G2); + }; + +.. note:: AKAZE descriptor can only be used with KAZE or AKAZE keypoints .. [ABD12] KAZE Features. Pablo F. Alcantarilla, Adrien Bartoli and Andrew J. Davison. In European Conference on Computer Vision (ECCV), Fiorenze, Italy, October 2012. @@ -262,12 +272,14 @@ KAZE::KAZE ---------- The KAZE constructor -.. ocv:function:: KAZE::KAZE(bool extended, bool upright) +.. ocv:function:: KAZE::KAZE(bool extended, bool upright, float threshold, int octaves, int sublevels, int diffusivity) :param extended: Set to enable extraction of extended (128-byte) descriptor. :param upright: Set to enable use of upright descriptors (non rotation-invariant). - - + :param threshold: Detector response threshold to accept point + :param octaves: Maximum octave evolution of the image + :param sublevels: Default number of sublevels per scale level + :param diffusivity: Diffusivity type. DIFF_PM_G1, DIFF_PM_G2, DIFF_WEICKERT or DIFF_CHARBONNIER AKAZE ----- @@ -278,25 +290,25 @@ Class implementing the AKAZE keypoint detector and descriptor extractor, describ class CV_EXPORTS_W AKAZE : public Feature2D { public: - /// AKAZE Descriptor Type - enum DESCRIPTOR_TYPE { - DESCRIPTOR_KAZE_UPRIGHT = 2, ///< Upright descriptors, not invariant to rotation - DESCRIPTOR_KAZE = 3, - DESCRIPTOR_MLDB_UPRIGHT = 4, ///< Upright descriptors, not invariant to rotation - DESCRIPTOR_MLDB = 5 - }; CV_WRAP AKAZE(); - explicit AKAZE(DESCRIPTOR_TYPE descriptor_type, int descriptor_size = 0, int descriptor_channels = 3); + CV_WRAP explicit AKAZE(int descriptor_type, int descriptor_size = 0, int descriptor_channels = 3, + float threshold = 0.001f, int octaves = 4, int sublevels = 4, int diffusivity = DIFF_PM_G2); }; +.. note:: AKAZE descriptor can only be used with KAZE or AKAZE keypoints + .. [ANB13] Fast Explicit Diffusion for Accelerated Features in Nonlinear Scale Spaces. Pablo F. Alcantarilla, Jesús Nuevo and Adrien Bartoli. In British Machine Vision Conference (BMVC), Bristol, UK, September 2013. AKAZE::AKAZE ------------ The AKAZE constructor -.. ocv:function:: AKAZE::AKAZE(DESCRIPTOR_TYPE descriptor_type, int descriptor_size = 0, int descriptor_channels = 3) +.. ocv:function:: AKAZE::AKAZE(int descriptor_type, int descriptor_size, int descriptor_channels, float threshold, int octaves, int sublevels, int diffusivity) - :param descriptor_type: Type of the extracted descriptor. + :param descriptor_type: Type of the extracted descriptor: DESCRIPTOR_KAZE, DESCRIPTOR_KAZE_UPRIGHT, DESCRIPTOR_MLDB or DESCRIPTOR_MLDB_UPRIGHT. :param descriptor_size: Size of the descriptor in bits. 0 -> Full size - :param descriptor_channels: Number of channels in the descriptor (1, 2, 3). + :param descriptor_channels: Number of channels in the descriptor (1, 2, 3) + :param threshold: Detector response threshold to accept point + :param octaves: Maximum octave evolution of the image + :param sublevels: Default number of sublevels per scale level + :param diffusivity: Diffusivity type. DIFF_PM_G1, DIFF_PM_G2, DIFF_WEICKERT or DIFF_CHARBONNIER diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index 9f46ee23ef..5aeab1c393 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -895,6 +895,22 @@ protected: PixelTestFn test_fn_; }; +// KAZE/AKAZE diffusivity +enum { + DIFF_PM_G1 = 0, + DIFF_PM_G2 = 1, + DIFF_WEICKERT = 2, + DIFF_CHARBONNIER = 3 +}; + +// AKAZE descriptor type +enum { + DESCRIPTOR_KAZE_UPRIGHT = 2, ///< Upright descriptors, not invariant to rotation + DESCRIPTOR_KAZE = 3, + DESCRIPTOR_MLDB_UPRIGHT = 4, ///< Upright descriptors, not invariant to rotation + DESCRIPTOR_MLDB = 5 +}; + /*! KAZE implementation */ @@ -902,7 +918,8 @@ class CV_EXPORTS_W KAZE : public Feature2D { public: CV_WRAP KAZE(); - CV_WRAP explicit KAZE(bool extended, bool upright); + CV_WRAP explicit KAZE(bool extended, bool upright, float threshold = 0.001f, + int octaves = 4, int sublevels = 4, int diffusivity = DIFF_PM_G2); virtual ~KAZE(); @@ -928,6 +945,10 @@ protected: CV_PROP bool extended; CV_PROP bool upright; + CV_PROP float threshold; + CV_PROP int octaves; + CV_PROP int sublevels; + CV_PROP int diffusivity; }; /*! @@ -936,16 +957,9 @@ AKAZE implementation class CV_EXPORTS_W AKAZE : public Feature2D { public: - /// AKAZE Descriptor Type - enum DESCRIPTOR_TYPE { - DESCRIPTOR_KAZE_UPRIGHT = 2, ///< Upright descriptors, not invariant to rotation - DESCRIPTOR_KAZE = 3, - DESCRIPTOR_MLDB_UPRIGHT = 4, ///< Upright descriptors, not invariant to rotation - DESCRIPTOR_MLDB = 5 - }; - CV_WRAP AKAZE(); - explicit AKAZE(DESCRIPTOR_TYPE descriptor_type, int descriptor_size = 0, int descriptor_channels = 3); + CV_WRAP explicit AKAZE(int descriptor_type, int descriptor_size = 0, int descriptor_channels = 3, + float threshold = 0.001f, int octaves = 4, int sublevels = 4, int diffusivity = DIFF_PM_G2); virtual ~AKAZE(); @@ -973,7 +987,10 @@ protected: CV_PROP int descriptor; CV_PROP int descriptor_channels; CV_PROP int descriptor_size; - + CV_PROP float threshold; + CV_PROP int octaves; + CV_PROP int sublevels; + CV_PROP int diffusivity; }; /****************************************************************************************\ * Distance * diff --git a/modules/features2d/perf/perf_precomp.hpp b/modules/features2d/perf/perf_precomp.hpp index 30607daaf6..f5280a84e8 100644 --- a/modules/features2d/perf/perf_precomp.hpp +++ b/modules/features2d/perf/perf_precomp.hpp @@ -10,7 +10,7 @@ #define __OPENCV_PERF_PRECOMP_HPP__ #include "opencv2/ts.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" #include "opencv2/features2d.hpp" #ifdef GTEST_CREATE_SHARED_LIBRARY diff --git a/modules/features2d/src/akaze.cpp b/modules/features2d/src/akaze.cpp index 4b1eb196a2..1d09d06158 100644 --- a/modules/features2d/src/akaze.cpp +++ b/modules/features2d/src/akaze.cpp @@ -49,7 +49,10 @@ http://www.robesafe.com/personal/pablo.alcantarilla/papers/Alcantarilla13bmvc.pd */ #include "precomp.hpp" -#include "akaze/AKAZEFeatures.h" +#include "kaze/AKAZEFeatures.h" + +#include +using namespace std; namespace cv { @@ -57,13 +60,22 @@ namespace cv : descriptor(DESCRIPTOR_MLDB) , descriptor_channels(3) , descriptor_size(0) + , threshold(0.001f) + , octaves(4) + , sublevels(4) + , diffusivity(DIFF_PM_G2) { } - AKAZE::AKAZE(DESCRIPTOR_TYPE _descriptor_type, int _descriptor_size, int _descriptor_channels) + AKAZE::AKAZE(int _descriptor_type, int _descriptor_size, int _descriptor_channels, + float _threshold, int _octaves, int _sublevels, int _diffusivity) : descriptor(_descriptor_type) , descriptor_channels(_descriptor_channels) , descriptor_size(_descriptor_size) + , threshold(_threshold) + , octaves(_octaves) + , sublevels(_sublevels) + , diffusivity(_diffusivity) { } @@ -78,12 +90,12 @@ namespace cv { switch (descriptor) { - case cv::AKAZE::DESCRIPTOR_KAZE: - case cv::AKAZE::DESCRIPTOR_KAZE_UPRIGHT: + case cv::DESCRIPTOR_KAZE: + case cv::DESCRIPTOR_KAZE_UPRIGHT: return 64; - case cv::AKAZE::DESCRIPTOR_MLDB: - case cv::AKAZE::DESCRIPTOR_MLDB_UPRIGHT: + case cv::DESCRIPTOR_MLDB: + case cv::DESCRIPTOR_MLDB_UPRIGHT: // We use the full length binary descriptor -> 486 bits if (descriptor_size == 0) { @@ -106,12 +118,12 @@ namespace cv { switch (descriptor) { - case cv::AKAZE::DESCRIPTOR_KAZE: - case cv::AKAZE::DESCRIPTOR_KAZE_UPRIGHT: + case cv::DESCRIPTOR_KAZE: + case cv::DESCRIPTOR_KAZE_UPRIGHT: return CV_32F; - case cv::AKAZE::DESCRIPTOR_MLDB: - case cv::AKAZE::DESCRIPTOR_MLDB_UPRIGHT: + case cv::DESCRIPTOR_MLDB: + case cv::DESCRIPTOR_MLDB_UPRIGHT: return CV_8U; default: @@ -124,12 +136,12 @@ namespace cv { switch (descriptor) { - case cv::AKAZE::DESCRIPTOR_KAZE: - case cv::AKAZE::DESCRIPTOR_KAZE_UPRIGHT: + case cv::DESCRIPTOR_KAZE: + case cv::DESCRIPTOR_KAZE_UPRIGHT: return cv::NORM_L2; - case cv::AKAZE::DESCRIPTOR_MLDB: - case cv::AKAZE::DESCRIPTOR_MLDB_UPRIGHT: + case cv::DESCRIPTOR_MLDB: + case cv::DESCRIPTOR_MLDB_UPRIGHT: return cv::NORM_HAMMING; default: @@ -153,11 +165,15 @@ namespace cv cv::Mat& desc = descriptors.getMatRef(); AKAZEOptions options; - options.descriptor = static_cast(descriptor); + options.descriptor = descriptor; options.descriptor_channels = descriptor_channels; options.descriptor_size = descriptor_size; options.img_width = img.cols; options.img_height = img.rows; + options.dthreshold = threshold; + options.omax = octaves; + options.nsublevels = sublevels; + options.diffusivity = diffusivity; AKAZEFeatures impl(options); impl.Create_Nonlinear_Scale_Space(img1_32); @@ -188,7 +204,7 @@ namespace cv img.convertTo(img1_32, CV_32F, 1.0 / 255.0, 0); AKAZEOptions options; - options.descriptor = static_cast(descriptor); + options.descriptor = descriptor; options.descriptor_channels = descriptor_channels; options.descriptor_size = descriptor_size; options.img_width = img.cols; @@ -216,7 +232,7 @@ namespace cv cv::Mat& desc = descriptors.getMatRef(); AKAZEOptions options; - options.descriptor = static_cast(descriptor); + options.descriptor = descriptor; options.descriptor_channels = descriptor_channels; options.descriptor_size = descriptor_size; options.img_width = img.cols; @@ -229,4 +245,4 @@ namespace cv CV_Assert((!desc.rows || desc.cols == descriptorSize())); CV_Assert((!desc.rows || (desc.type() == descriptorType()))); } -} \ No newline at end of file +} diff --git a/modules/features2d/src/akaze/AKAZEFeatures.cpp b/modules/features2d/src/akaze/AKAZEFeatures.cpp deleted file mode 100644 index e5955b21c2..0000000000 --- a/modules/features2d/src/akaze/AKAZEFeatures.cpp +++ /dev/null @@ -1,1941 +0,0 @@ -/** - * @file AKAZEFeatures.cpp - * @brief Main class for detecting and describing binary features in an - * accelerated nonlinear scale space - * @date Sep 15, 2013 - * @author Pablo F. Alcantarilla, Jesus Nuevo - */ - -#include "AKAZEFeatures.h" -#include "../kaze/fed.h" -#include "../kaze/nldiffusion_functions.h" - -using namespace std; -using namespace cv; -using namespace cv::details::kaze; - -/* ************************************************************************* */ -/** - * @brief AKAZEFeatures constructor with input options - * @param options AKAZEFeatures configuration options - * @note This constructor allocates memory for the nonlinear scale space - */ -AKAZEFeatures::AKAZEFeatures(const AKAZEOptions& options) : options_(options) { - - ncycles_ = 0; - reordering_ = true; - - if (options_.descriptor_size > 0 && options_.descriptor >= cv::AKAZE::DESCRIPTOR_MLDB_UPRIGHT) - { - generateDescriptorSubsample(descriptorSamples_, descriptorBits_, options_.descriptor_size, - options_.descriptor_pattern_size, options_.descriptor_channels); - } - - Allocate_Memory_Evolution(); -} - -/* ************************************************************************* */ -/** - * @brief This method allocates the memory for the nonlinear diffusion evolution - */ -void AKAZEFeatures::Allocate_Memory_Evolution(void) { - - float rfactor = 0.0f; - int level_height = 0, level_width = 0; - - // Allocate the dimension of the matrices for the evolution - for (int i = 0; i <= options_.omax - 1; i++) { - rfactor = 1.0f / pow(2.f, i); - level_height = (int)(options_.img_height*rfactor); - level_width = (int)(options_.img_width*rfactor); - - // Smallest possible octave and allow one scale if the image is small - if ((level_width < 80 || level_height < 40) && i != 0) { - options_.omax = i; - break; - } - - for (int j = 0; j < options_.nsublevels; j++) { - TEvolution step; - step.Lx = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Ly = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lxx = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lxy = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lyy = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lt = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Ldet = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lflow = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lstep = cv::Mat::zeros(level_height, level_width, CV_32F); - step.esigma = options_.soffset*pow(2.f, (float)(j) / (float)(options_.nsublevels) + i); - step.sigma_size = fRound(step.esigma); - step.etime = 0.5f*(step.esigma*step.esigma); - step.octave = i; - step.sublevel = j; - evolution_.push_back(step); - } - } - - // Allocate memory for the number of cycles and time steps - for (size_t i = 1; i < evolution_.size(); i++) { - int naux = 0; - vector tau; - float ttime = 0.0f; - ttime = evolution_[i].etime - evolution_[i - 1].etime; - naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau); - nsteps_.push_back(naux); - tsteps_.push_back(tau); - ncycles_++; - } -} - -/* ************************************************************************* */ -/** - * @brief This method creates the nonlinear scale space for a given image - * @param img Input image for which the nonlinear scale space needs to be created - * @return 0 if the nonlinear scale space was created successfully, -1 otherwise - */ -int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) -{ - CV_Assert(evolution_.size() > 0); - - // Copy the original image to the first level of the evolution - img.copyTo(evolution_[0].Lt); - gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset); - evolution_[0].Lt.copyTo(evolution_[0].Lsmooth); - - // First compute the kcontrast factor - options_.kcontrast = compute_k_percentile(img, options_.kcontrast_percentile, 1.0f, options_.kcontrast_nbins, 0, 0); - - // Now generate the rest of evolution levels - for (size_t i = 1; i < evolution_.size(); i++) { - - if (evolution_[i].octave > evolution_[i - 1].octave) { - halfsample_image(evolution_[i - 1].Lt, evolution_[i].Lt); - options_.kcontrast = options_.kcontrast*0.75f; - } - else { - evolution_[i - 1].Lt.copyTo(evolution_[i].Lt); - } - - gaussian_2D_convolution(evolution_[i].Lt, evolution_[i].Lsmooth, 0, 0, 1.0f); - - // Compute the Gaussian derivatives Lx and Ly - image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Lx, 1, 0); - image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Ly, 0, 1); - - // Compute the conductivity equation - switch (options_.diffusivity) { - case AKAZEOptions::PM_G1: - pm_g1(evolution_[i].Lx, evolution_[i].Ly, evolution_[i].Lflow, options_.kcontrast); - break; - case AKAZEOptions::PM_G2: - pm_g2(evolution_[i].Lx, evolution_[i].Ly, evolution_[i].Lflow, options_.kcontrast); - break; - case AKAZEOptions::WEICKERT: - weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, evolution_[i].Lflow, options_.kcontrast); - break; - case AKAZEOptions::CHARBONNIER: - charbonnier_diffusivity(evolution_[i].Lx, evolution_[i].Ly, evolution_[i].Lflow, options_.kcontrast); - break; - default: - CV_Error(options_.diffusivity, "Diffusivity is not supported"); - break; - } - - // Perform FED n inner steps - for (int j = 0; j < nsteps_[i - 1]; j++) { - cv::details::kaze::nld_step_scalar(evolution_[i].Lt, evolution_[i].Lflow, evolution_[i].Lstep, tsteps_[i - 1][j]); - } - } - - return 0; -} - -/* ************************************************************************* */ -/** - * @brief This method selects interesting keypoints through the nonlinear scale space - * @param kpts Vector of detected keypoints - */ -void AKAZEFeatures::Feature_Detection(std::vector& kpts) -{ - kpts.clear(); - - Compute_Determinant_Hessian_Response(); - Find_Scale_Space_Extrema(kpts); - Do_Subpixel_Refinement(kpts); -} - -/* ************************************************************************* */ - -class MultiscaleDerivativesInvoker : public cv::ParallelLoopBody -{ -public: - explicit MultiscaleDerivativesInvoker(std::vector& ev, const AKAZEOptions& opt) - : evolution_(&ev) - , options_(opt) - { - } - - - void operator()(const cv::Range& range) const - { - std::vector& evolution = *evolution_; - - for (int i = range.start; i < range.end; i++) - { - float ratio = pow(2.f, (float)evolution[i].octave); - int sigma_size_ = fRound(evolution[i].esigma * options_.derivative_factor / ratio); - - compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Lx, 1, 0, sigma_size_); - compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Ly, 0, 1, sigma_size_); - compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxx, 1, 0, sigma_size_); - compute_scharr_derivatives(evolution[i].Ly, evolution[i].Lyy, 0, 1, sigma_size_); - compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxy, 0, 1, sigma_size_); - - evolution[i].Lx = evolution[i].Lx*((sigma_size_)); - evolution[i].Ly = evolution[i].Ly*((sigma_size_)); - evolution[i].Lxx = evolution[i].Lxx*((sigma_size_)*(sigma_size_)); - evolution[i].Lxy = evolution[i].Lxy*((sigma_size_)*(sigma_size_)); - evolution[i].Lyy = evolution[i].Lyy*((sigma_size_)*(sigma_size_)); - } - } - -private: - std::vector* evolution_; - AKAZEOptions options_; -}; - -/** - * @brief This method computes the multiscale derivatives for the nonlinear scale space - */ -void AKAZEFeatures::Compute_Multiscale_Derivatives(void) -{ - cv::parallel_for_(cv::Range(0, (int)evolution_.size()), - MultiscaleDerivativesInvoker(evolution_, options_)); -} - -/* ************************************************************************* */ -/** - * @brief This method computes the feature detector response for the nonlinear scale space - * @note We use the Hessian determinant as the feature detector response - */ -void AKAZEFeatures::Compute_Determinant_Hessian_Response(void) { - - // Firstly compute the multiscale derivatives - Compute_Multiscale_Derivatives(); - - for (size_t i = 0; i < evolution_.size(); i++) - { - for (int ix = 0; ix < evolution_[i].Ldet.rows; ix++) - { - for (int jx = 0; jx < evolution_[i].Ldet.cols; jx++) - { - float lxx = *(evolution_[i].Lxx.ptr(ix)+jx); - float lxy = *(evolution_[i].Lxy.ptr(ix)+jx); - float lyy = *(evolution_[i].Lyy.ptr(ix)+jx); - *(evolution_[i].Ldet.ptr(ix)+jx) = (lxx*lyy - lxy*lxy); - } - } - } -} - -/* ************************************************************************* */ -/** - * @brief This method finds extrema in the nonlinear scale space - * @param kpts Vector of detected keypoints - */ -void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector& kpts) -{ - - float value = 0.0; - float dist = 0.0, ratio = 0.0, smax = 0.0; - int npoints = 0, id_repeated = 0; - int sigma_size_ = 0, left_x = 0, right_x = 0, up_y = 0, down_y = 0; - bool is_extremum = false, is_repeated = false, is_out = false; - cv::KeyPoint point; - vector kpts_aux; - - // Set maximum size - if (options_.descriptor == cv::AKAZE::DESCRIPTOR_MLDB_UPRIGHT || options_.descriptor == cv::AKAZE::DESCRIPTOR_MLDB) { - smax = 10.0f*sqrtf(2.0f); - } - else if (options_.descriptor == cv::AKAZE::DESCRIPTOR_KAZE_UPRIGHT || options_.descriptor == cv::AKAZE::DESCRIPTOR_KAZE) { - smax = 12.0f*sqrtf(2.0f); - } - - for (size_t i = 0; i < evolution_.size(); i++) { - for (int ix = 1; ix < evolution_[i].Ldet.rows - 1; ix++) { - for (int jx = 1; jx < evolution_[i].Ldet.cols - 1; jx++) { - is_extremum = false; - is_repeated = false; - is_out = false; - value = *(evolution_[i].Ldet.ptr(ix)+jx); - - // Filter the points with the detector threshold - if (value > options_.dthreshold && value >= options_.min_dthreshold && - value > *(evolution_[i].Ldet.ptr(ix)+jx - 1) && - value > *(evolution_[i].Ldet.ptr(ix)+jx + 1) && - value > *(evolution_[i].Ldet.ptr(ix - 1) + jx - 1) && - value > *(evolution_[i].Ldet.ptr(ix - 1) + jx) && - value > *(evolution_[i].Ldet.ptr(ix - 1) + jx + 1) && - value > *(evolution_[i].Ldet.ptr(ix + 1) + jx - 1) && - value > *(evolution_[i].Ldet.ptr(ix + 1) + jx) && - value > *(evolution_[i].Ldet.ptr(ix + 1) + jx + 1)) { - - is_extremum = true; - point.response = fabs(value); - point.size = evolution_[i].esigma*options_.derivative_factor; - point.octave = (int)evolution_[i].octave; - point.class_id = (int)i; - ratio = pow(2.f, point.octave); - sigma_size_ = fRound(point.size / ratio); - point.pt.x = static_cast(jx); - point.pt.y = static_cast(ix); - - // Compare response with the same and lower scale - for (size_t ik = 0; ik < kpts_aux.size(); ik++) { - - if ((point.class_id - 1) == kpts_aux[ik].class_id || - point.class_id == kpts_aux[ik].class_id) { - dist = sqrt(pow(point.pt.x*ratio - kpts_aux[ik].pt.x, 2) + pow(point.pt.y*ratio - kpts_aux[ik].pt.y, 2)); - if (dist <= point.size) { - if (point.response > kpts_aux[ik].response) { - id_repeated = (int)ik; - is_repeated = true; - } - else { - is_extremum = false; - } - break; - } - } - } - - // Check out of bounds - if (is_extremum == true) { - - // Check that the point is under the image limits for the descriptor computation - left_x = fRound(point.pt.x - smax*sigma_size_) - 1; - right_x = fRound(point.pt.x + smax*sigma_size_) + 1; - up_y = fRound(point.pt.y - smax*sigma_size_) - 1; - down_y = fRound(point.pt.y + smax*sigma_size_) + 1; - - if (left_x < 0 || right_x >= evolution_[i].Ldet.cols || - up_y < 0 || down_y >= evolution_[i].Ldet.rows) { - is_out = true; - } - - if (is_out == false) { - if (is_repeated == false) { - point.pt.x *= ratio; - point.pt.y *= ratio; - kpts_aux.push_back(point); - npoints++; - } - else { - point.pt.x *= ratio; - point.pt.y *= ratio; - kpts_aux[id_repeated] = point; - } - } // if is_out - } //if is_extremum - } - } // for jx - } // for ix - } // for i - - // Now filter points with the upper scale level - for (size_t i = 0; i < kpts_aux.size(); i++) { - - is_repeated = false; - const cv::KeyPoint& pt = kpts_aux[i]; - for (size_t j = i + 1; j < kpts_aux.size(); j++) { - - // Compare response with the upper scale - if ((pt.class_id + 1) == kpts_aux[j].class_id) { - dist = sqrt(pow(pt.pt.x - kpts_aux[j].pt.x, 2) + pow(pt.pt.y - kpts_aux[j].pt.y, 2)); - if (dist <= pt.size) { - if (pt.response < kpts_aux[j].response) { - is_repeated = true; - break; - } - } - } - } - - if (is_repeated == false) - kpts.push_back(pt); - } -} - -/* ************************************************************************* */ -/** - * @brief This method performs subpixel refinement of the detected keypoints - * @param kpts Vector of detected keypoints - */ -void AKAZEFeatures::Do_Subpixel_Refinement(std::vector& kpts) -{ - float Dx = 0.0, Dy = 0.0, ratio = 0.0; - float Dxx = 0.0, Dyy = 0.0, Dxy = 0.0; - int x = 0, y = 0; - cv::Mat A = cv::Mat::zeros(2, 2, CV_32F); - cv::Mat b = cv::Mat::zeros(2, 1, CV_32F); - cv::Mat dst = cv::Mat::zeros(2, 1, CV_32F); - - for (size_t i = 0; i < kpts.size(); i++) { - ratio = pow(2.f, kpts[i].octave); - x = fRound(kpts[i].pt.x / ratio); - y = fRound(kpts[i].pt.y / ratio); - - // Compute the gradient - Dx = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x + 1) - - *(evolution_[kpts[i].class_id].Ldet.ptr(y)+x - 1)); - Dy = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x) - - *(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x)); - - // Compute the Hessian - Dxx = (*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x + 1) - + *(evolution_[kpts[i].class_id].Ldet.ptr(y)+x - 1) - - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x))); - - Dyy = (*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x) - + *(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x) - - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x))); - - Dxy = (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x + 1) - + (*(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x - 1))) - - (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x + 1) - + (*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x - 1))); - - // Solve the linear system - *(A.ptr(0)) = Dxx; - *(A.ptr(1) + 1) = Dyy; - *(A.ptr(0) + 1) = *(A.ptr(1)) = Dxy; - *(b.ptr(0)) = -Dx; - *(b.ptr(1)) = -Dy; - - cv::solve(A, b, dst, DECOMP_LU); - - if (fabs(*(dst.ptr(0))) <= 1.0f && fabs(*(dst.ptr(1))) <= 1.0f) { - kpts[i].pt.x = x + (*(dst.ptr(0))); - kpts[i].pt.y = y + (*(dst.ptr(1))); - kpts[i].pt.x *= powf(2.f, (float)evolution_[kpts[i].class_id].octave); - kpts[i].pt.y *= powf(2.f, (float)evolution_[kpts[i].class_id].octave); - kpts[i].angle = 0.0; - - // In OpenCV the size of a keypoint its the diameter - kpts[i].size *= 2.0f; - } - // Delete the point since its not stable - else { - kpts.erase(kpts.begin() + i); - i--; - } - } -} - -/* ************************************************************************* */ - -class SURF_Descriptor_Upright_64_Invoker : public cv::ParallelLoopBody -{ -public: - SURF_Descriptor_Upright_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - { - } - - void operator() (const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - Get_SURF_Descriptor_Upright_64((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_SURF_Descriptor_Upright_64(const cv::KeyPoint& kpt, float* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; -}; - -class SURF_Descriptor_64_Invoker : public cv::ParallelLoopBody -{ -public: - SURF_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - { - } - - void operator()(const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); - Get_SURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_SURF_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; -}; - -class MSURF_Upright_Descriptor_64_Invoker : public cv::ParallelLoopBody -{ -public: - MSURF_Upright_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - { - } - - void operator()(const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - Get_MSURF_Upright_Descriptor_64((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_MSURF_Upright_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; -}; - -class MSURF_Descriptor_64_Invoker : public cv::ParallelLoopBody -{ -public: - MSURF_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - { - } - - void operator() (const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); - Get_MSURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_MSURF_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; -}; - -class Upright_MLDB_Full_Descriptor_Invoker : public cv::ParallelLoopBody -{ -public: - Upright_MLDB_Full_Descriptor_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution, AKAZEOptions& options) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - , options_(&options) - { - } - - void operator() (const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - Get_Upright_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_Upright_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; - AKAZEOptions* options_; -}; - -class Upright_MLDB_Descriptor_Subset_Invoker : public cv::ParallelLoopBody -{ -public: - Upright_MLDB_Descriptor_Subset_Invoker(std::vector& kpts, - cv::Mat& desc, - std::vector& evolution, - AKAZEOptions& options, - cv::Mat descriptorSamples, - cv::Mat descriptorBits) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - , options_(&options) - , descriptorSamples_(descriptorSamples) - , descriptorBits_(descriptorBits) - { - } - - void operator() (const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - Get_Upright_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_Upright_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; - AKAZEOptions* options_; - - cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. - cv::Mat descriptorBits_; -}; - -class MLDB_Full_Descriptor_Invoker : public cv::ParallelLoopBody -{ -public: - MLDB_Full_Descriptor_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution, AKAZEOptions& options) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - , options_(&options) - { - } - - void operator() (const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); - Get_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; - AKAZEOptions* options_; -}; - -class MLDB_Descriptor_Subset_Invoker : public cv::ParallelLoopBody -{ -public: - MLDB_Descriptor_Subset_Invoker(std::vector& kpts, - cv::Mat& desc, - std::vector& evolution, - AKAZEOptions& options, - cv::Mat descriptorSamples, - cv::Mat descriptorBits) - : keypoints_(&kpts) - , descriptors_(&desc) - , evolution_(&evolution) - , options_(&options) - , descriptorSamples_(descriptorSamples) - , descriptorBits_(descriptorBits) - { - } - - void operator() (const Range& range) const - { - for (int i = range.start; i < range.end; i++) - { - AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); - Get_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr(i)); - } - } - - void Get_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char* desc) const; - -private: - std::vector* keypoints_; - cv::Mat* descriptors_; - std::vector* evolution_; - AKAZEOptions* options_; - - cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. - cv::Mat descriptorBits_; -}; - -/** - * @brief This method computes the set of descriptors through the nonlinear scale space - * @param kpts Vector of detected keypoints - * @param desc Matrix to store the descriptors - */ -void AKAZEFeatures::Compute_Descriptors(std::vector& kpts, cv::Mat& desc) -{ - // Allocate memory for the matrix with the descriptors - if (options_.descriptor < cv::AKAZE::DESCRIPTOR_MLDB_UPRIGHT) { - desc = cv::Mat::zeros((int)kpts.size(), 64, CV_32FC1); - } - else { - // We use the full length binary descriptor -> 486 bits - if (options_.descriptor_size == 0) { - int t = (6 + 36 + 120)*options_.descriptor_channels; - desc = cv::Mat::zeros((int)kpts.size(), (int)ceil(t / 8.), CV_8UC1); - } - else { - // We use the random bit selection length binary descriptor - desc = cv::Mat::zeros((int)kpts.size(), (int)ceil(options_.descriptor_size / 8.), CV_8UC1); - } - } - - switch (options_.descriptor) - { - case cv::AKAZE::DESCRIPTOR_KAZE_UPRIGHT: // Upright descriptors, not invariant to rotation - { - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MSURF_Upright_Descriptor_64_Invoker(kpts, desc, evolution_)); - } - break; - case cv::AKAZE::DESCRIPTOR_KAZE: - { - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MSURF_Descriptor_64_Invoker(kpts, desc, evolution_)); - } - break; - case cv::AKAZE::DESCRIPTOR_MLDB_UPRIGHT: // Upright descriptors, not invariant to rotation - { - if (options_.descriptor_size == 0) - cv::parallel_for_(cv::Range(0, (int)kpts.size()), Upright_MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); - else - cv::parallel_for_(cv::Range(0, (int)kpts.size()), Upright_MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); - } - break; - case cv::AKAZE::DESCRIPTOR_MLDB: - { - if (options_.descriptor_size == 0) - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); - else - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); - } - break; - } -} - -/* ************************************************************************* */ -/** - * @brief This method computes the main orientation for a given keypoint - * @param kpt Input keypoint - * @note The orientation is computed using a similar approach as described in the - * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006 - */ -void AKAZEFeatures::Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vector& evolution_) { - - int ix = 0, iy = 0, idx = 0, s = 0, level = 0; - float xf = 0.0, yf = 0.0, gweight = 0.0, ratio = 0.0; - std::vector resX(109), resY(109), Ang(109); - const int id[] = { 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 }; - - // Variables for computing the dominant direction - float sumX = 0.0, sumY = 0.0, max = 0.0, ang1 = 0.0, ang2 = 0.0; - - // Get the information from the keypoint - level = kpt.class_id; - ratio = (float)(1 << evolution_[level].octave); - s = fRound(0.5f*kpt.size / ratio); - xf = kpt.pt.x / ratio; - yf = kpt.pt.y / ratio; - - // Calculate derivatives responses for points within radius of 6*scale - for (int i = -6; i <= 6; ++i) { - for (int j = -6; j <= 6; ++j) { - if (i*i + j*j < 36) { - iy = fRound(yf + j*s); - ix = fRound(xf + i*s); - - gweight = gauss25[id[i + 6]][id[j + 6]]; - resX[idx] = gweight*(*(evolution_[level].Lx.ptr(iy)+ix)); - resY[idx] = gweight*(*(evolution_[level].Ly.ptr(iy)+ix)); - - Ang[idx] = get_angle(resX[idx], resY[idx]); - ++idx; - } - } - } - - // Loop slides pi/3 window around feature point - for (ang1 = 0; ang1 < (float)(2.0 * CV_PI); ang1 += 0.15f) { - ang2 = (ang1 + (float)(CV_PI / 3.0) >(float)(2.0*CV_PI) ? ang1 - (float)(5.0*CV_PI / 3.0) : ang1 + (float)(CV_PI / 3.0)); - sumX = sumY = 0.f; - - for (size_t k = 0; k < Ang.size(); ++k) { - // Get angle from the x-axis of the sample point - const float & ang = Ang[k]; - - // Determine whether the point is within the window - if (ang1 < ang2 && ang1 < ang && ang < ang2) { - sumX += resX[k]; - sumY += resY[k]; - } - else if (ang2 < ang1 && - ((ang > 0 && ang < ang2) || (ang > ang1 && ang < 2.0f*CV_PI))) { - sumX += resX[k]; - sumY += resY[k]; - } - } - - // if the vector produced from this window is longer than all - // previous vectors then this forms the new dominant direction - if (sumX*sumX + sumY*sumY > max) { - // store largest orientation - max = sumX*sumX + sumY*sumY; - kpt.angle = get_angle(sumX, sumY); - } - } -} - -/* ************************************************************************* */ -/** - * @brief This method computes the upright descriptor (not rotation invariant) of - * the provided keypoint - * @param kpt Input keypoint - * @param desc Descriptor vector - * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired - * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, - * ECCV 2008 - */ -void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const { - - float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; - float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; - float sample_x = 0.0, sample_y = 0.0; - int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; - int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0; - float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0; - int scale = 0, dsize = 0, level = 0; - - // Subregion centers for the 4x4 gaussian weighting - float cx = -0.5f, cy = 0.5f; - - const std::vector& evolution = *evolution_; - - // Set the descriptor size and the sample and pattern sizes - dsize = 64; - sample_step = 5; - pattern_size = 12; - - // Get the information from the keypoint - ratio = (float)(1 << kpt.octave); - scale = fRound(0.5f*kpt.size / ratio); - level = kpt.class_id; - yf = kpt.pt.y / ratio; - xf = kpt.pt.x / ratio; - - i = -8; - - // Calculate descriptor for this interest point - // Area of size 24 s x 24 s - while (i < pattern_size) { - j = -8; - i = i - 4; - - cx += 1.0f; - cy = -0.5f; - - while (j < pattern_size) { - dx = dy = mdx = mdy = 0.0; - cy += 1.0f; - j = j - 4; - - ky = i + sample_step; - kx = j + sample_step; - - ys = yf + (ky*scale); - xs = xf + (kx*scale); - - for (int k = i; k < i + 9; k++) { - for (int l = j; l < j + 9; l++) { - sample_y = k*scale + yf; - sample_x = l*scale + xf; - - //Get the gaussian weighted x and y responses - gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.50f*scale); - - y1 = (int)(sample_y - .5); - x1 = (int)(sample_x - .5); - - y2 = (int)(sample_y + .5); - x2 = (int)(sample_x + .5); - - fx = sample_x - x1; - fy = sample_y - y1; - - res1 = *(evolution[level].Lx.ptr(y1)+x1); - res2 = *(evolution[level].Lx.ptr(y1)+x2); - res3 = *(evolution[level].Lx.ptr(y2)+x1); - res4 = *(evolution[level].Lx.ptr(y2)+x2); - rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - - res1 = *(evolution[level].Ly.ptr(y1)+x1); - res2 = *(evolution[level].Ly.ptr(y1)+x2); - res3 = *(evolution[level].Ly.ptr(y2)+x1); - res4 = *(evolution[level].Ly.ptr(y2)+x2); - ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - - rx = gauss_s1*rx; - ry = gauss_s1*ry; - - // Sum the derivatives to the cumulative descriptor - dx += rx; - dy += ry; - mdx += fabs(rx); - mdy += fabs(ry); - } - } - - // Add the values to the descriptor vector - gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f); - - desc[dcount++] = dx*gauss_s2; - desc[dcount++] = dy*gauss_s2; - desc[dcount++] = mdx*gauss_s2; - desc[dcount++] = mdy*gauss_s2; - - len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2; - - j += 9; - } - - i += 9; - } - - // convert to unit vector - len = sqrt(len); - - for (i = 0; i < dsize; i++) { - desc[i] /= len; - } -} - -/* ************************************************************************* */ -/** - * @brief This method computes the descriptor of the provided keypoint given the - * main orientation of the keypoint - * @param kpt Input keypoint - * @param desc Descriptor vector - * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired - * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, - * ECCV 2008 - */ -void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const { - - float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; - float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; - float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0; - float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0; - int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0; - int kx = 0, ky = 0, i = 0, j = 0, dcount = 0; - int scale = 0, dsize = 0, level = 0; - - // Subregion centers for the 4x4 gaussian weighting - float cx = -0.5f, cy = 0.5f; - - const std::vector& evolution = *evolution_; - - // Set the descriptor size and the sample and pattern sizes - dsize = 64; - sample_step = 5; - pattern_size = 12; - - // Get the information from the keypoint - ratio = (float)(1 << kpt.octave); - scale = fRound(0.5f*kpt.size / ratio); - angle = kpt.angle; - level = kpt.class_id; - yf = kpt.pt.y / ratio; - xf = kpt.pt.x / ratio; - co = cos(angle); - si = sin(angle); - - i = -8; - - // Calculate descriptor for this interest point - // Area of size 24 s x 24 s - while (i < pattern_size) { - j = -8; - i = i - 4; - - cx += 1.0f; - cy = -0.5f; - - while (j < pattern_size) { - dx = dy = mdx = mdy = 0.0; - cy += 1.0f; - j = j - 4; - - ky = i + sample_step; - kx = j + sample_step; - - xs = xf + (-kx*scale*si + ky*scale*co); - ys = yf + (kx*scale*co + ky*scale*si); - - for (int k = i; k < i + 9; ++k) { - for (int l = j; l < j + 9; ++l) { - // Get coords of sample point on the rotated axis - sample_y = yf + (l*scale*co + k*scale*si); - sample_x = xf + (-l*scale*si + k*scale*co); - - // Get the gaussian weighted x and y responses - gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale); - - y1 = fRound(sample_y - 0.5f); - x1 = fRound(sample_x - 0.5f); - - y2 = fRound(sample_y + 0.5f); - x2 = fRound(sample_x + 0.5f); - - fx = sample_x - x1; - fy = sample_y - y1; - - res1 = *(evolution[level].Lx.ptr(y1)+x1); - res2 = *(evolution[level].Lx.ptr(y1)+x2); - res3 = *(evolution[level].Lx.ptr(y2)+x1); - res4 = *(evolution[level].Lx.ptr(y2)+x2); - rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - - res1 = *(evolution[level].Ly.ptr(y1)+x1); - res2 = *(evolution[level].Ly.ptr(y1)+x2); - res3 = *(evolution[level].Ly.ptr(y2)+x1); - res4 = *(evolution[level].Ly.ptr(y2)+x2); - ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - - // Get the x and y derivatives on the rotated axis - rry = gauss_s1*(rx*co + ry*si); - rrx = gauss_s1*(-rx*si + ry*co); - - // Sum the derivatives to the cumulative descriptor - dx += rrx; - dy += rry; - mdx += fabs(rrx); - mdy += fabs(rry); - } - } - - // Add the values to the descriptor vector - gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f); - desc[dcount++] = dx*gauss_s2; - desc[dcount++] = dy*gauss_s2; - desc[dcount++] = mdx*gauss_s2; - desc[dcount++] = mdy*gauss_s2; - - len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2; - - j += 9; - } - - i += 9; - } - - // convert to unit vector - len = sqrt(len); - - for (i = 0; i < dsize; i++) { - desc[i] /= len; - } -} - -/* ************************************************************************* */ -/** - * @brief This method computes the rupright descriptor (not rotation invariant) of - * the provided keypoint - * @param kpt Input keypoint - * @param desc Descriptor vector - */ -void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char *desc) const { - - float di = 0.0, dx = 0.0, dy = 0.0; - float ri = 0.0, rx = 0.0, ry = 0.0, xf = 0.0, yf = 0.0; - float sample_x = 0.0, sample_y = 0.0, ratio = 0.0; - int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; - int level = 0, nsamples = 0, scale = 0; - int dcount1 = 0, dcount2 = 0; - - const AKAZEOptions & options = *options_; - const std::vector& evolution = *evolution_; - - // Matrices for the M-LDB descriptor - cv::Mat values_1 = cv::Mat::zeros(4, options.descriptor_channels, CV_32FC1); - cv::Mat values_2 = cv::Mat::zeros(9, options.descriptor_channels, CV_32FC1); - cv::Mat values_3 = cv::Mat::zeros(16, options.descriptor_channels, CV_32FC1); - - // Get the information from the keypoint - ratio = (float)(1 << kpt.octave); - scale = fRound(0.5f*kpt.size / ratio); - level = kpt.class_id; - yf = kpt.pt.y / ratio; - xf = kpt.pt.x / ratio; - - // First 2x2 grid - pattern_size = options_->descriptor_pattern_size; - sample_step = pattern_size; - - for (int i = -pattern_size; i < pattern_size; i += sample_step) { - for (int j = -pattern_size; j < pattern_size; j += sample_step) { - di = dx = dy = 0.0; - nsamples = 0; - - for (int k = i; k < i + sample_step; k++) { - for (int l = j; l < j + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + l*scale; - sample_x = xf + k*scale; - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - - ri = *(evolution[level].Lt.ptr(y1)+x1); - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - - di += ri; - dx += rx; - dy += ry; - nsamples++; - } - } - - di /= nsamples; - dx /= nsamples; - dy /= nsamples; - - *(values_1.ptr(dcount2)) = di; - *(values_1.ptr(dcount2)+1) = dx; - *(values_1.ptr(dcount2)+2) = dy; - dcount2++; - } - } - - // Do binary comparison first level - for (int i = 0; i < 4; i++) { - for (int j = i + 1; j < 4; j++) { - if (*(values_1.ptr(i)) > *(values_1.ptr(j))) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - - if (*(values_1.ptr(i)+1) > *(values_1.ptr(j)+1)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - - if (*(values_1.ptr(i)+2) > *(values_1.ptr(j)+2)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - - // Second 3x3 grid - sample_step = static_cast(ceil(pattern_size*2. / 3.)); - dcount2 = 0; - - for (int i = -pattern_size; i < pattern_size; i += sample_step) { - for (int j = -pattern_size; j < pattern_size; j += sample_step) { - di = dx = dy = 0.0; - nsamples = 0; - - for (int k = i; k < i + sample_step; k++) { - for (int l = j; l < j + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + l*scale; - sample_x = xf + k*scale; - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - - ri = *(evolution[level].Lt.ptr(y1)+x1); - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - - di += ri; - dx += rx; - dy += ry; - nsamples++; - } - } - - di /= nsamples; - dx /= nsamples; - dy /= nsamples; - - *(values_2.ptr(dcount2)) = di; - *(values_2.ptr(dcount2)+1) = dx; - *(values_2.ptr(dcount2)+2) = dy; - dcount2++; - } - } - - //Do binary comparison second level - dcount2 = 0; - for (int i = 0; i < 9; i++) { - for (int j = i + 1; j < 9; j++) { - if (*(values_2.ptr(i)) > *(values_2.ptr(j))) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - - if (*(values_2.ptr(i)+1) > *(values_2.ptr(j)+1)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - - if (*(values_2.ptr(i)+2) > *(values_2.ptr(j)+2)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - - // Third 4x4 grid - sample_step = pattern_size / 2; - dcount2 = 0; - - for (int i = -pattern_size; i < pattern_size; i += sample_step) { - for (int j = -pattern_size; j < pattern_size; j += sample_step) { - di = dx = dy = 0.0; - nsamples = 0; - - for (int k = i; k < i + sample_step; k++) { - for (int l = j; l < j + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + l*scale; - sample_x = xf + k*scale; - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - - ri = *(evolution[level].Lt.ptr(y1)+x1); - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - - di += ri; - dx += rx; - dy += ry; - nsamples++; - } - } - - di /= nsamples; - dx /= nsamples; - dy /= nsamples; - - *(values_3.ptr(dcount2)) = di; - *(values_3.ptr(dcount2)+1) = dx; - *(values_3.ptr(dcount2)+2) = dy; - dcount2++; - } - } - - //Do binary comparison third level - dcount2 = 0; - for (int i = 0; i < 16; i++) { - for (int j = i + 1; j < 16; j++) { - if (*(values_3.ptr(i)) > *(values_3.ptr(j))) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - - if (*(values_3.ptr(i)+1) > *(values_3.ptr(j)+1)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - - if (*(values_3.ptr(i)+2) > *(values_3.ptr(j)+2)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } -} - -/* ************************************************************************* */ -/** - * @brief This method computes the descriptor of the provided keypoint given the - * main orientation of the keypoint - * @param kpt Input keypoint - * @param desc Descriptor vector - */ -void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char *desc) const { - - float di = 0.0, dx = 0.0, dy = 0.0, ratio = 0.0; - float ri = 0.0, rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, xf = 0.0, yf = 0.0; - float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0; - int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; - int level = 0, nsamples = 0, scale = 0; - int dcount1 = 0, dcount2 = 0; - - const AKAZEOptions & options = *options_; - const std::vector& evolution = *evolution_; - - // Matrices for the M-LDB descriptor - cv::Mat values_1 = cv::Mat::zeros(4, options.descriptor_channels, CV_32FC1); - cv::Mat values_2 = cv::Mat::zeros(9, options.descriptor_channels, CV_32FC1); - cv::Mat values_3 = cv::Mat::zeros(16, options.descriptor_channels, CV_32FC1); - - // Get the information from the keypoint - ratio = (float)(1 << kpt.octave); - scale = fRound(0.5f*kpt.size / ratio); - angle = kpt.angle; - level = kpt.class_id; - yf = kpt.pt.y / ratio; - xf = kpt.pt.x / ratio; - co = cos(angle); - si = sin(angle); - - // First 2x2 grid - pattern_size = options.descriptor_pattern_size; - sample_step = pattern_size; - - for (int i = -pattern_size; i < pattern_size; i += sample_step) { - for (int j = -pattern_size; j < pattern_size; j += sample_step) { - - di = dx = dy = 0.0; - nsamples = 0; - - for (float k = (float)i; k < i + sample_step; k++) { - for (float l = (float)j; l < j + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + (l*scale*co + k*scale*si); - sample_x = xf + (-l*scale*si + k*scale*co); - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - - ri = *(evolution[level].Lt.ptr(y1)+x1); - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - - di += ri; - - if (options.descriptor_channels == 2) { - dx += sqrtf(rx*rx + ry*ry); - } - else if (options.descriptor_channels == 3) { - // Get the x and y derivatives on the rotated axis - rry = rx*co + ry*si; - rrx = -rx*si + ry*co; - dx += rrx; - dy += rry; - } - - nsamples++; - } - } - - di /= nsamples; - dx /= nsamples; - dy /= nsamples; - - *(values_1.ptr(dcount2)) = di; - if (options.descriptor_channels > 1) { - *(values_1.ptr(dcount2)+1) = dx; - } - - if (options.descriptor_channels > 2) { - *(values_1.ptr(dcount2)+2) = dy; - } - - dcount2++; - } - } - - // Do binary comparison first level - for (int i = 0; i < 4; i++) { - for (int j = i + 1; j < 4; j++) { - if (*(values_1.ptr(i)) > *(values_1.ptr(j))) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - - if (options.descriptor_channels > 1) { - for (int i = 0; i < 4; i++) { - for (int j = i + 1; j < 4; j++) { - if (*(values_1.ptr(i)+1) > *(values_1.ptr(j)+1)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - - dcount1++; - } - } - } - - if (options.descriptor_channels > 2) { - for (int i = 0; i < 4; i++) { - for (int j = i + 1; j < 4; j++) { - if (*(values_1.ptr(i)+2) > *(values_1.ptr(j)+2)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - } - - // Second 3x3 grid - sample_step = static_cast(ceil(pattern_size*2. / 3.)); - dcount2 = 0; - - for (int i = -pattern_size; i < pattern_size; i += sample_step) { - for (int j = -pattern_size; j < pattern_size; j += sample_step) { - - di = dx = dy = 0.0; - nsamples = 0; - - for (int k = i; k < i + sample_step; k++) { - for (int l = j; l < j + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + (l*scale*co + k*scale*si); - sample_x = xf + (-l*scale*si + k*scale*co); - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - - ri = *(evolution[level].Lt.ptr(y1)+x1); - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - di += ri; - - if (options.descriptor_channels == 2) { - dx += sqrtf(rx*rx + ry*ry); - } - else if (options.descriptor_channels == 3) { - // Get the x and y derivatives on the rotated axis - rry = rx*co + ry*si; - rrx = -rx*si + ry*co; - dx += rrx; - dy += rry; - } - - nsamples++; - } - } - - di /= nsamples; - dx /= nsamples; - dy /= nsamples; - - *(values_2.ptr(dcount2)) = di; - if (options.descriptor_channels > 1) { - *(values_2.ptr(dcount2)+1) = dx; - } - - if (options.descriptor_channels > 2) { - *(values_2.ptr(dcount2)+2) = dy; - } - - dcount2++; - } - } - - // Do binary comparison second level - for (int i = 0; i < 9; i++) { - for (int j = i + 1; j < 9; j++) { - if (*(values_2.ptr(i)) > *(values_2.ptr(j))) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - - if (options.descriptor_channels > 1) { - for (int i = 0; i < 9; i++) { - for (int j = i + 1; j < 9; j++) { - if (*(values_2.ptr(i)+1) > *(values_2.ptr(j)+1)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - } - - if (options.descriptor_channels > 2) { - for (int i = 0; i < 9; i++) { - for (int j = i + 1; j < 9; j++) { - if (*(values_2.ptr(i)+2) > *(values_2.ptr(j)+2)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - } - - // Third 4x4 grid - sample_step = pattern_size / 2; - dcount2 = 0; - - for (int i = -pattern_size; i < pattern_size; i += sample_step) { - for (int j = -pattern_size; j < pattern_size; j += sample_step) { - di = dx = dy = 0.0; - nsamples = 0; - - for (int k = i; k < i + sample_step; k++) { - for (int l = j; l < j + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + (l*scale*co + k*scale*si); - sample_x = xf + (-l*scale*si + k*scale*co); - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - - ri = *(evolution[level].Lt.ptr(y1)+x1); - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - di += ri; - - if (options.descriptor_channels == 2) { - dx += sqrtf(rx*rx + ry*ry); - } - else if (options.descriptor_channels == 3) { - // Get the x and y derivatives on the rotated axis - rry = rx*co + ry*si; - rrx = -rx*si + ry*co; - dx += rrx; - dy += rry; - } - - nsamples++; - } - } - - di /= nsamples; - dx /= nsamples; - dy /= nsamples; - - *(values_3.ptr(dcount2)) = di; - if (options.descriptor_channels > 1) - *(values_3.ptr(dcount2)+1) = dx; - - if (options.descriptor_channels > 2) - *(values_3.ptr(dcount2)+2) = dy; - - dcount2++; - } - } - - // Do binary comparison third level - for (int i = 0; i < 16; i++) { - for (int j = i + 1; j < 16; j++) { - if (*(values_3.ptr(i)) > *(values_3.ptr(j))) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - - if (options.descriptor_channels > 1) { - for (int i = 0; i < 16; i++) { - for (int j = i + 1; j < 16; j++) { - if (*(values_3.ptr(i)+1) > *(values_3.ptr(j)+1)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - } - - if (options.descriptor_channels > 2) { - for (int i = 0; i < 16; i++) { - for (int j = i + 1; j < 16; j++) { - if (*(values_3.ptr(i)+2) > *(values_3.ptr(j)+2)) { - desc[dcount1 / 8] |= (1 << (dcount1 % 8)); - } - dcount1++; - } - } - } -} - -/* ************************************************************************* */ -/** - * @brief This method computes the M-LDB descriptor of the provided keypoint given the - * main orientation of the keypoint. The descriptor is computed based on a subset of - * the bits of the whole descriptor - * @param kpt Input keypoint - * @param desc Descriptor vector - */ -void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char *desc) const { - - float di = 0.f, dx = 0.f, dy = 0.f; - float rx = 0.f, ry = 0.f; - float sample_x = 0.f, sample_y = 0.f; - int x1 = 0, y1 = 0; - - const AKAZEOptions & options = *options_; - const std::vector& evolution = *evolution_; - - // Get the information from the keypoint - float ratio = (float)(1 << kpt.octave); - int scale = fRound(0.5f*kpt.size / ratio); - float angle = kpt.angle; - int level = kpt.class_id; - float yf = kpt.pt.y / ratio; - float xf = kpt.pt.x / ratio; - float co = cos(angle); - float si = sin(angle); - - // Allocate memory for the matrix of values - cv::Mat values = cv::Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); - - // Sample everything, but only do the comparisons - vector steps(3); - steps.at(0) = options.descriptor_pattern_size; - steps.at(1) = (int)ceil(2.f*options.descriptor_pattern_size / 3.f); - steps.at(2) = options.descriptor_pattern_size / 2; - - for (int i = 0; i < descriptorSamples_.rows; i++) { - const int *coords = descriptorSamples_.ptr(i); - int sample_step = steps.at(coords[0]); - di = 0.0f; - dx = 0.0f; - dy = 0.0f; - - for (int k = coords[1]; k < coords[1] + sample_step; k++) { - for (int l = coords[2]; l < coords[2] + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + (l*scale*co + k*scale*si); - sample_x = xf + (-l*scale*si + k*scale*co); - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - - di += *(evolution[level].Lt.ptr(y1)+x1); - - if (options.descriptor_channels > 1) { - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - - if (options.descriptor_channels == 2) { - dx += sqrtf(rx*rx + ry*ry); - } - else if (options.descriptor_channels == 3) { - // Get the x and y derivatives on the rotated axis - dx += rx*co + ry*si; - dy += -rx*si + ry*co; - } - } - } - } - - *(values.ptr(options.descriptor_channels*i)) = di; - - if (options.descriptor_channels == 2) { - *(values.ptr(options.descriptor_channels*i + 1)) = dx; - } - else if (options.descriptor_channels == 3) { - *(values.ptr(options.descriptor_channels*i + 1)) = dx; - *(values.ptr(options.descriptor_channels*i + 2)) = dy; - } - } - - // Do the comparisons - const float *vals = values.ptr(0); - const int *comps = descriptorBits_.ptr(0); - - for (int i = 0; i vals[comps[2 * i + 1]]) { - desc[i / 8] |= (1 << (i % 8)); - } - } -} - -/* ************************************************************************* */ -/** - * @brief This method computes the upright (not rotation invariant) M-LDB descriptor - * of the provided keypoint given the main orientation of the keypoint. - * The descriptor is computed based on a subset of the bits of the whole descriptor - * @param kpt Input keypoint - * @param desc Descriptor vector - */ -void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char *desc) const { - - float di = 0.0f, dx = 0.0f, dy = 0.0f; - float rx = 0.0f, ry = 0.0f; - float sample_x = 0.0f, sample_y = 0.0f; - int x1 = 0, y1 = 0; - - const AKAZEOptions & options = *options_; - const std::vector& evolution = *evolution_; - - // Get the information from the keypoint - float ratio = (float)(1 << kpt.octave); - int scale = fRound(0.5f*kpt.size / ratio); - int level = kpt.class_id; - float yf = kpt.pt.y / ratio; - float xf = kpt.pt.x / ratio; - - // Allocate memory for the matrix of values - Mat values = cv::Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); - - vector steps(3); - steps.at(0) = options.descriptor_pattern_size; - steps.at(1) = static_cast(ceil(2.f*options.descriptor_pattern_size / 3.f)); - steps.at(2) = options.descriptor_pattern_size / 2; - - for (int i = 0; i < descriptorSamples_.rows; i++) { - const int *coords = descriptorSamples_.ptr(i); - int sample_step = steps.at(coords[0]); - di = 0.0f, dx = 0.0f, dy = 0.0f; - - for (int k = coords[1]; k < coords[1] + sample_step; k++) { - for (int l = coords[2]; l < coords[2] + sample_step; l++) { - - // Get the coordinates of the sample point - sample_y = yf + l*scale; - sample_x = xf + k*scale; - - y1 = fRound(sample_y); - x1 = fRound(sample_x); - di += *(evolution[level].Lt.ptr(y1)+x1); - - if (options.descriptor_channels > 1) { - rx = *(evolution[level].Lx.ptr(y1)+x1); - ry = *(evolution[level].Ly.ptr(y1)+x1); - - if (options.descriptor_channels == 2) { - dx += sqrtf(rx*rx + ry*ry); - } - else if (options.descriptor_channels == 3) { - dx += rx; - dy += ry; - } - } - } - } - - *(values.ptr(options.descriptor_channels*i)) = di; - - if (options.descriptor_channels == 2) { - *(values.ptr(options.descriptor_channels*i + 1)) = dx; - } - else if (options.descriptor_channels == 3) { - *(values.ptr(options.descriptor_channels*i + 1)) = dx; - *(values.ptr(options.descriptor_channels*i + 2)) = dy; - } - } - - // Do the comparisons - const float *vals = values.ptr(0); - const int *comps = descriptorBits_.ptr(0); - - for (int i = 0; i vals[comps[2 * i + 1]]) { - desc[i / 8] |= (1 << (i % 8)); - } - } -} - -/* ************************************************************************* */ -/** - * @brief This function computes a (quasi-random) list of bits to be taken - * from the full descriptor. To speed the extraction, the function creates - * a list of the samples that are involved in generating at least a bit (sampleList) - * and a list of the comparisons between those samples (comparisons) - * @param sampleList - * @param comparisons The matrix with the binary comparisons - * @param nbits The number of bits of the descriptor - * @param pattern_size The pattern size for the binary descriptor - * @param nchannels Number of channels to consider in the descriptor (1-3) - * @note The function keeps the 18 bits (3-channels by 6 comparisons) of the - * coarser grid, since it provides the most robust estimations - */ -void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons, int nbits, - int pattern_size, int nchannels) { - - int ssz = 0; - for (int i = 0; i < 3; i++) { - int gz = (i + 2)*(i + 2); - ssz += gz*(gz - 1) / 2; - } - ssz *= nchannels; - - CV_Assert(nbits <= ssz); // Descriptor size can't be bigger than full descriptor - - // Since the full descriptor is usually under 10k elements, we pick - // the selection from the full matrix. We take as many samples per - // pick as the number of channels. For every pick, we - // take the two samples involved and put them in the sampling list - - Mat_ fullM(ssz / nchannels, 5); - for (int i = 0, c = 0; i < 3; i++) { - int gdiv = i + 2; //grid divisions, per row - int gsz = gdiv*gdiv; - int psz = (int)ceil(2.f*pattern_size / (float)gdiv); - - for (int j = 0; j < gsz; j++) { - for (int k = j + 1; k < gsz; k++, c++) { - fullM(c, 0) = i; - fullM(c, 1) = psz*(j % gdiv) - pattern_size; - fullM(c, 2) = psz*(j / gdiv) - pattern_size; - fullM(c, 3) = psz*(k % gdiv) - pattern_size; - fullM(c, 4) = psz*(k / gdiv) - pattern_size; - } - } - } - - srand(1024); - Mat_ comps = Mat_(nchannels * (int)ceil(nbits / (float)nchannels), 2); - comps = 1000; - - // Select some samples. A sample includes all channels - int count = 0; - int npicks = (int)ceil(nbits / (float)nchannels); - Mat_ samples(29, 3); - Mat_ fullcopy = fullM.clone(); - samples = -1; - - for (int i = 0; i < npicks; i++) { - int k = rand() % (fullM.rows - i); - if (i < 6) { - // Force use of the coarser grid values and comparisons - k = i; - } - - bool n = true; - - for (int j = 0; j < count; j++) { - if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 1) && samples(j, 2) == fullcopy(k, 2)) { - n = false; - comps(i*nchannels, 0) = nchannels*j; - comps(i*nchannels + 1, 0) = nchannels*j + 1; - comps(i*nchannels + 2, 0) = nchannels*j + 2; - break; - } - } - - if (n) { - samples(count, 0) = fullcopy(k, 0); - samples(count, 1) = fullcopy(k, 1); - samples(count, 2) = fullcopy(k, 2); - comps(i*nchannels, 0) = nchannels*count; - comps(i*nchannels + 1, 0) = nchannels*count + 1; - comps(i*nchannels + 2, 0) = nchannels*count + 2; - count++; - } - - n = true; - for (int j = 0; j < count; j++) { - if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 3) && samples(j, 2) == fullcopy(k, 4)) { - n = false; - comps(i*nchannels, 1) = nchannels*j; - comps(i*nchannels + 1, 1) = nchannels*j + 1; - comps(i*nchannels + 2, 1) = nchannels*j + 2; - break; - } - } - - if (n) { - samples(count, 0) = fullcopy(k, 0); - samples(count, 1) = fullcopy(k, 3); - samples(count, 2) = fullcopy(k, 4); - comps(i*nchannels, 1) = nchannels*count; - comps(i*nchannels + 1, 1) = nchannels*count + 1; - comps(i*nchannels + 2, 1) = nchannels*count + 2; - count++; - } - - Mat tmp = fullcopy.row(k); - fullcopy.row(fullcopy.rows - i - 1).copyTo(tmp); - } - - sampleList = samples.rowRange(0, count).clone(); - comparisons = comps.rowRange(0, nbits).clone(); -} - -/* ************************************************************************* */ -/** - * @brief This function computes the angle from the vector given by (X Y). From 0 to 2*Pi - */ -inline float get_angle(float x, float y) { - - if (x >= 0 && y >= 0) { - return atanf(y / x); - } - - if (x < 0 && y >= 0) { - return static_cast(CV_PI)-atanf(-y / x); - } - - if (x < 0 && y < 0) { - return static_cast(CV_PI)+atanf(y / x); - } - - if (x >= 0 && y < 0) { - return static_cast(2.0 * CV_PI) - atanf(-y / x); - } - - return 0; -} - -/* ************************************************************************* */ -/** - * @brief This function computes the value of a 2D Gaussian function - * @param x X Position - * @param y Y Position - * @param sig Standard Deviation - */ -inline float gaussian(float x, float y, float sigma) { - return expf(-(x*x + y*y) / (2.0f*sigma*sigma)); -} - -/* ************************************************************************* */ -/** - * @brief This function checks descriptor limits - * @param x X Position - * @param y Y Position - * @param width Image width - * @param height Image height - */ -inline void check_descriptor_limits(int &x, int &y, int width, int height) { - - if (x < 0) { - x = 0; - } - - if (y < 0) { - y = 0; - } - - if (x > width - 1) { - x = width - 1; - } - - if (y > height - 1) { - y = height - 1; - } -} - -/* ************************************************************************* */ -/** - * @brief This funtion rounds float to nearest integer - * @param flt Input float - * @return dst Nearest integer - */ -inline int fRound(float flt) { - return (int)(flt + 0.5f); -} \ No newline at end of file diff --git a/modules/features2d/src/akaze/AKAZEFeatures.h b/modules/features2d/src/akaze/AKAZEFeatures.h deleted file mode 100644 index 302ef0d06d..0000000000 --- a/modules/features2d/src/akaze/AKAZEFeatures.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @file AKAZE.h - * @brief Main class for detecting and computing binary descriptors in an - * accelerated nonlinear scale space - * @date Mar 27, 2013 - * @author Pablo F. Alcantarilla, Jesus Nuevo - */ - -#pragma once - -/* ************************************************************************* */ -// Includes -#include "precomp.hpp" -#include "AKAZEConfig.h" - -/* ************************************************************************* */ -// AKAZE Class Declaration -class AKAZEFeatures { - -private: - - AKAZEOptions options_; ///< Configuration options for AKAZE - std::vector evolution_; ///< Vector of nonlinear diffusion evolution - - /// FED parameters - int ncycles_; ///< Number of cycles - bool reordering_; ///< Flag for reordering time steps - std::vector > tsteps_; ///< Vector of FED dynamic time steps - std::vector nsteps_; ///< Vector of number of steps per cycle - - /// Matrices for the M-LDB descriptor computation - cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. - cv::Mat descriptorBits_; - cv::Mat bitMask_; - -public: - - /// Constructor with input arguments - AKAZEFeatures(const AKAZEOptions& options); - - /// Scale Space methods - void Allocate_Memory_Evolution(); - int Create_Nonlinear_Scale_Space(const cv::Mat& img); - void Feature_Detection(std::vector& kpts); - void Compute_Determinant_Hessian_Response(void); - void Compute_Multiscale_Derivatives(void); - void Find_Scale_Space_Extrema(std::vector& kpts); - void Do_Subpixel_Refinement(std::vector& kpts); - - // Feature description methods - void Compute_Descriptors(std::vector& kpts, cv::Mat& desc); - - static void Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vector& evolution_); -}; - -/* ************************************************************************* */ -// Inline functions - -// Inline functions -void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons, - int nbits, int pattern_size, int nchannels); -float get_angle(float x, float y); -float gaussian(float x, float y, float sigma); -void check_descriptor_limits(int& x, int& y, int width, int height); -int fRound(float flt); diff --git a/modules/features2d/src/kaze.cpp b/modules/features2d/src/kaze.cpp index 88fb999d5f..910ec271f6 100644 --- a/modules/features2d/src/kaze.cpp +++ b/modules/features2d/src/kaze.cpp @@ -55,12 +55,21 @@ namespace cv KAZE::KAZE() : extended(false) , upright(false) + , threshold(0.001f) + , octaves(4) + , sublevels(4) + , diffusivity(DIFF_PM_G2) { } - KAZE::KAZE(bool _extended, bool _upright) + KAZE::KAZE(bool _extended, bool _upright, float _threshold, int _octaves, + int _sublevels, int _diffusivity) : extended(_extended) , upright(_upright) + , threshold(_threshold) + , octaves(_octaves) + , sublevels(_sublevels) + , diffusivity(_diffusivity) { } @@ -111,6 +120,10 @@ namespace cv options.img_height = img.rows; options.extended = extended; options.upright = upright; + options.dthreshold = threshold; + options.omax = octaves; + options.nsublevels = sublevels; + options.diffusivity = diffusivity; KAZEFeatures impl(options); impl.Create_Nonlinear_Scale_Space(img1_32); @@ -180,4 +193,4 @@ namespace cv CV_Assert((!desc.rows || desc.cols == descriptorSize())); CV_Assert((!desc.rows || (desc.type() == descriptorType()))); } -} \ No newline at end of file +} diff --git a/modules/features2d/src/akaze/AKAZEConfig.h b/modules/features2d/src/kaze/AKAZEConfig.h similarity index 70% rename from modules/features2d/src/akaze/AKAZEConfig.h rename to modules/features2d/src/kaze/AKAZEConfig.h index 1c1203f574..c7ac1cfc0b 100644 --- a/modules/features2d/src/akaze/AKAZEConfig.h +++ b/modules/features2d/src/kaze/AKAZEConfig.h @@ -5,7 +5,8 @@ * @author Pablo F. Alcantarilla, Jesus Nuevo */ -#pragma once +#ifndef __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__ +#define __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__ /* ************************************************************************* */ // OpenCV @@ -28,14 +29,6 @@ const float gauss25[7][7] = { /// AKAZE configuration options structure struct AKAZEOptions { - /// AKAZE Diffusivities - enum DIFFUSIVITY_TYPE { - PM_G1 = 0, - PM_G2 = 1, - WEICKERT = 2, - CHARBONNIER = 3 - }; - AKAZEOptions() : omax(4) , nsublevels(4) @@ -44,12 +37,12 @@ struct AKAZEOptions { , soffset(1.6f) , derivative_factor(1.5f) , sderivatives(1.0) - , diffusivity(PM_G2) + , diffusivity(cv::DIFF_PM_G2) , dthreshold(0.001f) , min_dthreshold(0.00001f) - , descriptor(cv::AKAZE::DESCRIPTOR_MLDB) + , descriptor(cv::DESCRIPTOR_MLDB) , descriptor_size(0) , descriptor_channels(3) , descriptor_pattern_size(10) @@ -67,12 +60,12 @@ struct AKAZEOptions { float soffset; ///< Base scale offset (sigma units) float derivative_factor; ///< Factor for the multiscale derivatives float sderivatives; ///< Smoothing factor for the derivatives - DIFFUSIVITY_TYPE diffusivity; ///< Diffusivity type + int diffusivity; ///< Diffusivity type float dthreshold; ///< Detector response threshold to accept point float min_dthreshold; ///< Minimum detector threshold to accept a point - cv::AKAZE::DESCRIPTOR_TYPE descriptor; ///< Type of descriptor + int descriptor; ///< Type of descriptor int descriptor_size; ///< Size of the descriptor in bits. 0->Full size int descriptor_channels; ///< Number of channels in the descriptor (1, 2, 3) int descriptor_pattern_size; ///< Actual patch size is 2*pattern_size*point.scale @@ -82,28 +75,4 @@ struct AKAZEOptions { int kcontrast_nbins; ///< Number of bins for the contrast factor histogram }; -/* ************************************************************************* */ -/// AKAZE nonlinear diffusion filtering evolution -struct TEvolution { - - TEvolution() { - etime = 0.0f; - esigma = 0.0f; - octave = 0; - sublevel = 0; - sigma_size = 0; - } - - cv::Mat Lx, Ly; // First order spatial derivatives - cv::Mat Lxx, Lxy, Lyy; // Second order spatial derivatives - cv::Mat Lflow; // Diffusivity image - cv::Mat Lt; // Evolution image - cv::Mat Lsmooth; // Smoothed image - cv::Mat Lstep; // Evolution step update - cv::Mat Ldet; // Detector response - float etime; // Evolution time - float esigma; // Evolution sigma. For linear diffusion t = sigma^2 / 2 - size_t octave; // Image octave - size_t sublevel; // Image sublevel in each octave - size_t sigma_size; // Integer sigma. For computing the feature detector responses -}; \ No newline at end of file +#endif diff --git a/modules/features2d/src/kaze/AKAZEFeatures.cpp b/modules/features2d/src/kaze/AKAZEFeatures.cpp new file mode 100644 index 0000000000..97222dfa4f --- /dev/null +++ b/modules/features2d/src/kaze/AKAZEFeatures.cpp @@ -0,0 +1,1880 @@ +/** + * @file AKAZEFeatures.cpp + * @brief Main class for detecting and describing binary features in an + * accelerated nonlinear scale space + * @date Sep 15, 2013 + * @author Pablo F. Alcantarilla, Jesus Nuevo + */ + +#include "AKAZEFeatures.h" +#include "fed.h" +#include "nldiffusion_functions.h" +#include "utils.h" + +#include + +// Namespaces +using namespace std; +using namespace cv; +using namespace cv::details::kaze; + +/* ************************************************************************* */ +/** + * @brief AKAZEFeatures constructor with input options + * @param options AKAZEFeatures configuration options + * @note This constructor allocates memory for the nonlinear scale space + */ +AKAZEFeatures::AKAZEFeatures(const AKAZEOptions& options) : options_(options) { + + ncycles_ = 0; + reordering_ = true; + + if (options_.descriptor_size > 0 && options_.descriptor >= cv::DESCRIPTOR_MLDB_UPRIGHT) { + generateDescriptorSubsample(descriptorSamples_, descriptorBits_, options_.descriptor_size, + options_.descriptor_pattern_size, options_.descriptor_channels); + } + + Allocate_Memory_Evolution(); +} + +/* ************************************************************************* */ +/** + * @brief This method allocates the memory for the nonlinear diffusion evolution + */ +void AKAZEFeatures::Allocate_Memory_Evolution(void) { + + float rfactor = 0.0f; + int level_height = 0, level_width = 0; + + // Allocate the dimension of the matrices for the evolution + for (int i = 0; i <= options_.omax - 1; i++) { + rfactor = 1.0f / pow(2.f, i); + level_height = (int)(options_.img_height*rfactor); + level_width = (int)(options_.img_width*rfactor); + + // Smallest possible octave and allow one scale if the image is small + if ((level_width < 80 || level_height < 40) && i != 0) { + options_.omax = i; + break; + } + + for (int j = 0; j < options_.nsublevels; j++) { + TEvolution step; + step.Lx = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Ly = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Lxx = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Lxy = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Lyy = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Lt = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Ldet = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Lsmooth = cv::Mat::zeros(level_height, level_width, CV_32F); + step.esigma = options_.soffset*pow(2.f, (float)(j) / (float)(options_.nsublevels) + i); + step.sigma_size = fRound(step.esigma); + step.etime = 0.5f*(step.esigma*step.esigma); + step.octave = i; + step.sublevel = j; + evolution_.push_back(step); + } + } + + // Allocate memory for the number of cycles and time steps + for (size_t i = 1; i < evolution_.size(); i++) { + int naux = 0; + vector tau; + float ttime = 0.0f; + ttime = evolution_[i].etime - evolution_[i - 1].etime; + naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau); + nsteps_.push_back(naux); + tsteps_.push_back(tau); + ncycles_++; + } +} + +/* ************************************************************************* */ +/** + * @brief This method creates the nonlinear scale space for a given image + * @param img Input image for which the nonlinear scale space needs to be created + * @return 0 if the nonlinear scale space was created successfully, -1 otherwise + */ +int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) +{ + CV_Assert(evolution_.size() > 0); + + // Copy the original image to the first level of the evolution + img.copyTo(evolution_[0].Lt); + gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset); + evolution_[0].Lt.copyTo(evolution_[0].Lsmooth); + + // Allocate memory for the flow and step images + cv::Mat Lflow = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); + cv::Mat Lstep = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); + + // First compute the kcontrast factor + options_.kcontrast = compute_k_percentile(img, options_.kcontrast_percentile, 1.0f, options_.kcontrast_nbins, 0, 0); + + // Now generate the rest of evolution levels + for (size_t i = 1; i < evolution_.size(); i++) { + + if (evolution_[i].octave > evolution_[i - 1].octave) { + halfsample_image(evolution_[i - 1].Lt, evolution_[i].Lt); + options_.kcontrast = options_.kcontrast*0.75f; + + // Allocate memory for the resized flow and step images + Lflow = cv::Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); + Lstep = cv::Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); + } + else { + evolution_[i - 1].Lt.copyTo(evolution_[i].Lt); + } + + gaussian_2D_convolution(evolution_[i].Lt, evolution_[i].Lsmooth, 0, 0, 1.0f); + + // Compute the Gaussian derivatives Lx and Ly + image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Lx, 1, 0); + image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Ly, 0, 1); + + // Compute the conductivity equation + switch (options_.diffusivity) { + case cv::DIFF_PM_G1: + pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + break; + case cv::DIFF_PM_G2: + pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + break; + case cv::DIFF_WEICKERT: + weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + break; + case cv::DIFF_CHARBONNIER: + charbonnier_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + break; + default: + CV_Error(options_.diffusivity, "Diffusivity is not supported"); + break; + } + + // Perform FED n inner steps + for (int j = 0; j < nsteps_[i - 1]; j++) { + cv::details::kaze::nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]); + } + } + + return 0; +} + +/* ************************************************************************* */ +/** + * @brief This method selects interesting keypoints through the nonlinear scale space + * @param kpts Vector of detected keypoints + */ +void AKAZEFeatures::Feature_Detection(std::vector& kpts) +{ + kpts.clear(); + Compute_Determinant_Hessian_Response(); + Find_Scale_Space_Extrema(kpts); + Do_Subpixel_Refinement(kpts); +} + +/* ************************************************************************* */ +class MultiscaleDerivativesAKAZEInvoker : public cv::ParallelLoopBody +{ +public: + explicit MultiscaleDerivativesAKAZEInvoker(std::vector& ev, const AKAZEOptions& opt) + : evolution_(&ev) + , options_(opt) + { + } + + void operator()(const cv::Range& range) const + { + std::vector& evolution = *evolution_; + + for (int i = range.start; i < range.end; i++) + { + float ratio = pow(2.f, (float)evolution[i].octave); + int sigma_size_ = fRound(evolution[i].esigma * options_.derivative_factor / ratio); + + compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Lx, 1, 0, sigma_size_); + compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Ly, 0, 1, sigma_size_); + compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxx, 1, 0, sigma_size_); + compute_scharr_derivatives(evolution[i].Ly, evolution[i].Lyy, 0, 1, sigma_size_); + compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxy, 0, 1, sigma_size_); + + evolution[i].Lx = evolution[i].Lx*((sigma_size_)); + evolution[i].Ly = evolution[i].Ly*((sigma_size_)); + evolution[i].Lxx = evolution[i].Lxx*((sigma_size_)*(sigma_size_)); + evolution[i].Lxy = evolution[i].Lxy*((sigma_size_)*(sigma_size_)); + evolution[i].Lyy = evolution[i].Lyy*((sigma_size_)*(sigma_size_)); + } + } + +private: + std::vector* evolution_; + AKAZEOptions options_; +}; + +/* ************************************************************************* */ +/** + * @brief This method computes the multiscale derivatives for the nonlinear scale space + */ +void AKAZEFeatures::Compute_Multiscale_Derivatives(void) +{ + cv::parallel_for_(cv::Range(0, (int)evolution_.size()), + MultiscaleDerivativesAKAZEInvoker(evolution_, options_)); +} + +/* ************************************************************************* */ +/** + * @brief This method computes the feature detector response for the nonlinear scale space + * @note We use the Hessian determinant as the feature detector response + */ +void AKAZEFeatures::Compute_Determinant_Hessian_Response(void) { + + // Firstly compute the multiscale derivatives + Compute_Multiscale_Derivatives(); + + for (size_t i = 0; i < evolution_.size(); i++) + { + for (int ix = 0; ix < evolution_[i].Ldet.rows; ix++) + { + for (int jx = 0; jx < evolution_[i].Ldet.cols; jx++) + { + float lxx = *(evolution_[i].Lxx.ptr(ix)+jx); + float lxy = *(evolution_[i].Lxy.ptr(ix)+jx); + float lyy = *(evolution_[i].Lyy.ptr(ix)+jx); + *(evolution_[i].Ldet.ptr(ix)+jx) = (lxx*lyy - lxy*lxy); + } + } + } +} + +/* ************************************************************************* */ +/** + * @brief This method finds extrema in the nonlinear scale space + * @param kpts Vector of detected keypoints + */ +void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector& kpts) +{ + + float value = 0.0; + float dist = 0.0, ratio = 0.0, smax = 0.0; + int npoints = 0, id_repeated = 0; + int sigma_size_ = 0, left_x = 0, right_x = 0, up_y = 0, down_y = 0; + bool is_extremum = false, is_repeated = false, is_out = false; + cv::KeyPoint point; + vector kpts_aux; + + // Set maximum size + if (options_.descriptor == cv::DESCRIPTOR_MLDB_UPRIGHT || options_.descriptor == cv::DESCRIPTOR_MLDB) { + smax = 10.0f*sqrtf(2.0f); + } + else if (options_.descriptor == cv::DESCRIPTOR_KAZE_UPRIGHT || options_.descriptor == cv::DESCRIPTOR_KAZE) { + smax = 12.0f*sqrtf(2.0f); + } + + for (size_t i = 0; i < evolution_.size(); i++) { + for (int ix = 1; ix < evolution_[i].Ldet.rows - 1; ix++) { + for (int jx = 1; jx < evolution_[i].Ldet.cols - 1; jx++) { + is_extremum = false; + is_repeated = false; + is_out = false; + value = *(evolution_[i].Ldet.ptr(ix)+jx); + + // Filter the points with the detector threshold + if (value > options_.dthreshold && value >= options_.min_dthreshold && + value > *(evolution_[i].Ldet.ptr(ix)+jx - 1) && + value > *(evolution_[i].Ldet.ptr(ix)+jx + 1) && + value > *(evolution_[i].Ldet.ptr(ix - 1) + jx - 1) && + value > *(evolution_[i].Ldet.ptr(ix - 1) + jx) && + value > *(evolution_[i].Ldet.ptr(ix - 1) + jx + 1) && + value > *(evolution_[i].Ldet.ptr(ix + 1) + jx - 1) && + value > *(evolution_[i].Ldet.ptr(ix + 1) + jx) && + value > *(evolution_[i].Ldet.ptr(ix + 1) + jx + 1)) { + + is_extremum = true; + point.response = fabs(value); + point.size = evolution_[i].esigma*options_.derivative_factor; + point.octave = (int)evolution_[i].octave; + point.class_id = (int)i; + ratio = pow(2.f, point.octave); + sigma_size_ = fRound(point.size / ratio); + point.pt.x = static_cast(jx); + point.pt.y = static_cast(ix); + + // Compare response with the same and lower scale + for (size_t ik = 0; ik < kpts_aux.size(); ik++) { + + if ((point.class_id - 1) == kpts_aux[ik].class_id || + point.class_id == kpts_aux[ik].class_id) { + dist = sqrt(pow(point.pt.x*ratio - kpts_aux[ik].pt.x, 2) + pow(point.pt.y*ratio - kpts_aux[ik].pt.y, 2)); + if (dist <= point.size) { + if (point.response > kpts_aux[ik].response) { + id_repeated = (int)ik; + is_repeated = true; + } + else { + is_extremum = false; + } + break; + } + } + } + + // Check out of bounds + if (is_extremum == true) { + + // Check that the point is under the image limits for the descriptor computation + left_x = fRound(point.pt.x - smax*sigma_size_) - 1; + right_x = fRound(point.pt.x + smax*sigma_size_) + 1; + up_y = fRound(point.pt.y - smax*sigma_size_) - 1; + down_y = fRound(point.pt.y + smax*sigma_size_) + 1; + + if (left_x < 0 || right_x >= evolution_[i].Ldet.cols || + up_y < 0 || down_y >= evolution_[i].Ldet.rows) { + is_out = true; + } + + if (is_out == false) { + if (is_repeated == false) { + point.pt.x *= ratio; + point.pt.y *= ratio; + kpts_aux.push_back(point); + npoints++; + } + else { + point.pt.x *= ratio; + point.pt.y *= ratio; + kpts_aux[id_repeated] = point; + } + } // if is_out + } //if is_extremum + } + } // for jx + } // for ix + } // for i + + // Now filter points with the upper scale level + for (size_t i = 0; i < kpts_aux.size(); i++) { + + is_repeated = false; + const cv::KeyPoint& pt = kpts_aux[i]; + for (size_t j = i + 1; j < kpts_aux.size(); j++) { + + // Compare response with the upper scale + if ((pt.class_id + 1) == kpts_aux[j].class_id) { + dist = sqrt(pow(pt.pt.x - kpts_aux[j].pt.x, 2) + pow(pt.pt.y - kpts_aux[j].pt.y, 2)); + if (dist <= pt.size) { + if (pt.response < kpts_aux[j].response) { + is_repeated = true; + break; + } + } + } + } + + if (is_repeated == false) + kpts.push_back(pt); + } +} + +/* ************************************************************************* */ +/** + * @brief This method performs subpixel refinement of the detected keypoints + * @param kpts Vector of detected keypoints + */ +void AKAZEFeatures::Do_Subpixel_Refinement(std::vector& kpts) +{ + float Dx = 0.0, Dy = 0.0, ratio = 0.0; + float Dxx = 0.0, Dyy = 0.0, Dxy = 0.0; + int x = 0, y = 0; + cv::Mat A = cv::Mat::zeros(2, 2, CV_32F); + cv::Mat b = cv::Mat::zeros(2, 1, CV_32F); + cv::Mat dst = cv::Mat::zeros(2, 1, CV_32F); + + for (size_t i = 0; i < kpts.size(); i++) { + ratio = pow(2.f, kpts[i].octave); + x = fRound(kpts[i].pt.x / ratio); + y = fRound(kpts[i].pt.y / ratio); + + // Compute the gradient + Dx = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x + 1) + - *(evolution_[kpts[i].class_id].Ldet.ptr(y)+x - 1)); + Dy = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x) + - *(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x)); + + // Compute the Hessian + Dxx = (*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x + 1) + + *(evolution_[kpts[i].class_id].Ldet.ptr(y)+x - 1) + - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x))); + + Dyy = (*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x) + + *(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x) + - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr(y)+x))); + + Dxy = (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x + 1) + + (*(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x - 1))) + - (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr(y - 1) + x + 1) + + (*(evolution_[kpts[i].class_id].Ldet.ptr(y + 1) + x - 1))); + + // Solve the linear system + *(A.ptr(0)) = Dxx; + *(A.ptr(1) + 1) = Dyy; + *(A.ptr(0) + 1) = *(A.ptr(1)) = Dxy; + *(b.ptr(0)) = -Dx; + *(b.ptr(1)) = -Dy; + + cv::solve(A, b, dst, DECOMP_LU); + + if (fabs(*(dst.ptr(0))) <= 1.0f && fabs(*(dst.ptr(1))) <= 1.0f) { + kpts[i].pt.x = x + (*(dst.ptr(0))); + kpts[i].pt.y = y + (*(dst.ptr(1))); + kpts[i].pt.x *= powf(2.f, (float)evolution_[kpts[i].class_id].octave); + kpts[i].pt.y *= powf(2.f, (float)evolution_[kpts[i].class_id].octave); + kpts[i].angle = 0.0; + + // In OpenCV the size of a keypoint its the diameter + kpts[i].size *= 2.0f; + } + // Delete the point since its not stable + else { + kpts.erase(kpts.begin() + i); + i--; + } + } +} + +/* ************************************************************************* */ + +class SURF_Descriptor_Upright_64_Invoker : public cv::ParallelLoopBody +{ +public: + SURF_Descriptor_Upright_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + { + } + + void operator() (const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + Get_SURF_Descriptor_Upright_64((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_SURF_Descriptor_Upright_64(const cv::KeyPoint& kpt, float* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; +}; + +class SURF_Descriptor_64_Invoker : public cv::ParallelLoopBody +{ +public: + SURF_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + { + } + + void operator()(const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); + Get_SURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_SURF_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; +}; + +class MSURF_Upright_Descriptor_64_Invoker : public cv::ParallelLoopBody +{ +public: + MSURF_Upright_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + { + } + + void operator()(const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + Get_MSURF_Upright_Descriptor_64((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_MSURF_Upright_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; +}; + +class MSURF_Descriptor_64_Invoker : public cv::ParallelLoopBody +{ +public: + MSURF_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + { + } + + void operator() (const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); + Get_MSURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_MSURF_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; +}; + +class Upright_MLDB_Full_Descriptor_Invoker : public cv::ParallelLoopBody +{ +public: + Upright_MLDB_Full_Descriptor_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution, AKAZEOptions& options) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + , options_(&options) + { + } + + void operator() (const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + Get_Upright_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_Upright_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; + AKAZEOptions* options_; +}; + +class Upright_MLDB_Descriptor_Subset_Invoker : public cv::ParallelLoopBody +{ +public: + Upright_MLDB_Descriptor_Subset_Invoker(std::vector& kpts, + cv::Mat& desc, + std::vector& evolution, + AKAZEOptions& options, + cv::Mat descriptorSamples, + cv::Mat descriptorBits) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + , options_(&options) + , descriptorSamples_(descriptorSamples) + , descriptorBits_(descriptorBits) + { + } + + void operator() (const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + Get_Upright_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_Upright_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; + AKAZEOptions* options_; + + cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. + cv::Mat descriptorBits_; +}; + +class MLDB_Full_Descriptor_Invoker : public cv::ParallelLoopBody +{ +public: + MLDB_Full_Descriptor_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution, AKAZEOptions& options) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + , options_(&options) + { + } + + void operator() (const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); + Get_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; + AKAZEOptions* options_; +}; + +class MLDB_Descriptor_Subset_Invoker : public cv::ParallelLoopBody +{ +public: + MLDB_Descriptor_Subset_Invoker(std::vector& kpts, + cv::Mat& desc, + std::vector& evolution, + AKAZEOptions& options, + cv::Mat descriptorSamples, + cv::Mat descriptorBits) + : keypoints_(&kpts) + , descriptors_(&desc) + , evolution_(&evolution) + , options_(&options) + , descriptorSamples_(descriptorSamples) + , descriptorBits_(descriptorBits) + { + } + + void operator() (const Range& range) const + { + for (int i = range.start; i < range.end; i++) + { + AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); + Get_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr(i)); + } + } + + void Get_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char* desc) const; + +private: + std::vector* keypoints_; + cv::Mat* descriptors_; + std::vector* evolution_; + AKAZEOptions* options_; + + cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. + cv::Mat descriptorBits_; +}; + +/** + * @brief This method computes the set of descriptors through the nonlinear scale space + * @param kpts Vector of detected keypoints + * @param desc Matrix to store the descriptors + */ +void AKAZEFeatures::Compute_Descriptors(std::vector& kpts, cv::Mat& desc) +{ + for(size_t i = 0; i < kpts.size(); i++) + { + CV_Assert(0 <= kpts[i].class_id && kpts[i].class_id < static_cast(evolution_.size())); + } + + // Allocate memory for the matrix with the descriptors + if (options_.descriptor < cv::DESCRIPTOR_MLDB_UPRIGHT) { + desc = cv::Mat::zeros((int)kpts.size(), 64, CV_32FC1); + } + else { + // We use the full length binary descriptor -> 486 bits + if (options_.descriptor_size == 0) { + int t = (6 + 36 + 120)*options_.descriptor_channels; + desc = cv::Mat::zeros((int)kpts.size(), (int)ceil(t / 8.), CV_8UC1); + } + else { + // We use the random bit selection length binary descriptor + desc = cv::Mat::zeros((int)kpts.size(), (int)ceil(options_.descriptor_size / 8.), CV_8UC1); + } + } + + switch (options_.descriptor) + { + case cv::DESCRIPTOR_KAZE_UPRIGHT: // Upright descriptors, not invariant to rotation + { + cv::parallel_for_(cv::Range(0, (int)kpts.size()), MSURF_Upright_Descriptor_64_Invoker(kpts, desc, evolution_)); + } + break; + case cv::DESCRIPTOR_KAZE: + { + cv::parallel_for_(cv::Range(0, (int)kpts.size()), MSURF_Descriptor_64_Invoker(kpts, desc, evolution_)); + } + break; + case cv::DESCRIPTOR_MLDB_UPRIGHT: // Upright descriptors, not invariant to rotation + { + if (options_.descriptor_size == 0) + cv::parallel_for_(cv::Range(0, (int)kpts.size()), Upright_MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); + else + cv::parallel_for_(cv::Range(0, (int)kpts.size()), Upright_MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); + } + break; + case cv::DESCRIPTOR_MLDB: + { + if (options_.descriptor_size == 0) + cv::parallel_for_(cv::Range(0, (int)kpts.size()), MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); + else + cv::parallel_for_(cv::Range(0, (int)kpts.size()), MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); + } + break; + } +} + +/* ************************************************************************* */ +/** + * @brief This method computes the main orientation for a given keypoint + * @param kpt Input keypoint + * @note The orientation is computed using a similar approach as described in the + * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006 + */ +void AKAZEFeatures::Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vector& evolution_) { + + int ix = 0, iy = 0, idx = 0, s = 0, level = 0; + float xf = 0.0, yf = 0.0, gweight = 0.0, ratio = 0.0; + std::vector resX(109), resY(109), Ang(109); + const int id[] = { 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 }; + + // Variables for computing the dominant direction + float sumX = 0.0, sumY = 0.0, max = 0.0, ang1 = 0.0, ang2 = 0.0; + + // Get the information from the keypoint + level = kpt.class_id; + ratio = (float)(1 << evolution_[level].octave); + s = fRound(0.5f*kpt.size / ratio); + xf = kpt.pt.x / ratio; + yf = kpt.pt.y / ratio; + + // Calculate derivatives responses for points within radius of 6*scale + for (int i = -6; i <= 6; ++i) { + for (int j = -6; j <= 6; ++j) { + if (i*i + j*j < 36) { + iy = fRound(yf + j*s); + ix = fRound(xf + i*s); + + gweight = gauss25[id[i + 6]][id[j + 6]]; + resX[idx] = gweight*(*(evolution_[level].Lx.ptr(iy)+ix)); + resY[idx] = gweight*(*(evolution_[level].Ly.ptr(iy)+ix)); + + Ang[idx] = getAngle(resX[idx], resY[idx]); + ++idx; + } + } + } + // Loop slides pi/3 window around feature point + for (ang1 = 0; ang1 < (float)(2.0 * CV_PI); ang1 += 0.15f) { + ang2 = (ang1 + (float)(CV_PI / 3.0) >(float)(2.0*CV_PI) ? ang1 - (float)(5.0*CV_PI / 3.0) : ang1 + (float)(CV_PI / 3.0)); + sumX = sumY = 0.f; + + for (size_t k = 0; k < Ang.size(); ++k) { + // Get angle from the x-axis of the sample point + const float & ang = Ang[k]; + + // Determine whether the point is within the window + if (ang1 < ang2 && ang1 < ang && ang < ang2) { + sumX += resX[k]; + sumY += resY[k]; + } + else if (ang2 < ang1 && + ((ang > 0 && ang < ang2) || (ang > ang1 && ang < 2.0f*CV_PI))) { + sumX += resX[k]; + sumY += resY[k]; + } + } + + // if the vector produced from this window is longer than all + // previous vectors then this forms the new dominant direction + if (sumX*sumX + sumY*sumY > max) { + // store largest orientation + max = sumX*sumX + sumY*sumY; + kpt.angle = getAngle(sumX, sumY); + } + } +} + +/* ************************************************************************* */ +/** + * @brief This method computes the upright descriptor (not rotation invariant) of + * the provided keypoint + * @param kpt Input keypoint + * @param desc Descriptor vector + * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired + * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, + * ECCV 2008 + */ +void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const { + + float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; + float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; + float sample_x = 0.0, sample_y = 0.0; + int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; + int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0; + float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0; + int scale = 0, dsize = 0, level = 0; + + // Subregion centers for the 4x4 gaussian weighting + float cx = -0.5f, cy = 0.5f; + + const std::vector& evolution = *evolution_; + + // Set the descriptor size and the sample and pattern sizes + dsize = 64; + sample_step = 5; + pattern_size = 12; + + // Get the information from the keypoint + ratio = (float)(1 << kpt.octave); + scale = fRound(0.5f*kpt.size / ratio); + level = kpt.class_id; + yf = kpt.pt.y / ratio; + xf = kpt.pt.x / ratio; + + i = -8; + + // Calculate descriptor for this interest point + // Area of size 24 s x 24 s + while (i < pattern_size) { + j = -8; + i = i - 4; + + cx += 1.0f; + cy = -0.5f; + + while (j < pattern_size) { + dx = dy = mdx = mdy = 0.0; + cy += 1.0f; + j = j - 4; + + ky = i + sample_step; + kx = j + sample_step; + + ys = yf + (ky*scale); + xs = xf + (kx*scale); + + for (int k = i; k < i + 9; k++) { + for (int l = j; l < j + 9; l++) { + sample_y = k*scale + yf; + sample_x = l*scale + xf; + + //Get the gaussian weighted x and y responses + gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.50f*scale); + + y1 = (int)(sample_y - .5); + x1 = (int)(sample_x - .5); + + y2 = (int)(sample_y + .5); + x2 = (int)(sample_x + .5); + + fx = sample_x - x1; + fy = sample_y - y1; + + res1 = *(evolution[level].Lx.ptr(y1)+x1); + res2 = *(evolution[level].Lx.ptr(y1)+x2); + res3 = *(evolution[level].Lx.ptr(y2)+x1); + res4 = *(evolution[level].Lx.ptr(y2)+x2); + rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; + + res1 = *(evolution[level].Ly.ptr(y1)+x1); + res2 = *(evolution[level].Ly.ptr(y1)+x2); + res3 = *(evolution[level].Ly.ptr(y2)+x1); + res4 = *(evolution[level].Ly.ptr(y2)+x2); + ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; + + rx = gauss_s1*rx; + ry = gauss_s1*ry; + + // Sum the derivatives to the cumulative descriptor + dx += rx; + dy += ry; + mdx += fabs(rx); + mdy += fabs(ry); + } + } + + // Add the values to the descriptor vector + gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f); + + desc[dcount++] = dx*gauss_s2; + desc[dcount++] = dy*gauss_s2; + desc[dcount++] = mdx*gauss_s2; + desc[dcount++] = mdy*gauss_s2; + + len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2; + + j += 9; + } + + i += 9; + } + + // convert to unit vector + len = sqrt(len); + + for (i = 0; i < dsize; i++) { + desc[i] /= len; + } +} + +/* ************************************************************************* */ +/** + * @brief This method computes the descriptor of the provided keypoint given the + * main orientation of the keypoint + * @param kpt Input keypoint + * @param desc Descriptor vector + * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired + * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, + * ECCV 2008 + */ +void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const { + + float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; + float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; + float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0; + float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0; + int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0; + int kx = 0, ky = 0, i = 0, j = 0, dcount = 0; + int scale = 0, dsize = 0, level = 0; + + // Subregion centers for the 4x4 gaussian weighting + float cx = -0.5f, cy = 0.5f; + + const std::vector& evolution = *evolution_; + + // Set the descriptor size and the sample and pattern sizes + dsize = 64; + sample_step = 5; + pattern_size = 12; + + // Get the information from the keypoint + ratio = (float)(1 << kpt.octave); + scale = fRound(0.5f*kpt.size / ratio); + angle = kpt.angle; + level = kpt.class_id; + yf = kpt.pt.y / ratio; + xf = kpt.pt.x / ratio; + co = cos(angle); + si = sin(angle); + + i = -8; + + // Calculate descriptor for this interest point + // Area of size 24 s x 24 s + while (i < pattern_size) { + j = -8; + i = i - 4; + + cx += 1.0f; + cy = -0.5f; + + while (j < pattern_size) { + dx = dy = mdx = mdy = 0.0; + cy += 1.0f; + j = j - 4; + + ky = i + sample_step; + kx = j + sample_step; + + xs = xf + (-kx*scale*si + ky*scale*co); + ys = yf + (kx*scale*co + ky*scale*si); + + for (int k = i; k < i + 9; ++k) { + for (int l = j; l < j + 9; ++l) { + // Get coords of sample point on the rotated axis + sample_y = yf + (l*scale*co + k*scale*si); + sample_x = xf + (-l*scale*si + k*scale*co); + + // Get the gaussian weighted x and y responses + gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale); + + y1 = fRound(sample_y - 0.5f); + x1 = fRound(sample_x - 0.5f); + + y2 = fRound(sample_y + 0.5f); + x2 = fRound(sample_x + 0.5f); + + fx = sample_x - x1; + fy = sample_y - y1; + + res1 = *(evolution[level].Lx.ptr(y1)+x1); + res2 = *(evolution[level].Lx.ptr(y1)+x2); + res3 = *(evolution[level].Lx.ptr(y2)+x1); + res4 = *(evolution[level].Lx.ptr(y2)+x2); + rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; + + res1 = *(evolution[level].Ly.ptr(y1)+x1); + res2 = *(evolution[level].Ly.ptr(y1)+x2); + res3 = *(evolution[level].Ly.ptr(y2)+x1); + res4 = *(evolution[level].Ly.ptr(y2)+x2); + ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; + + // Get the x and y derivatives on the rotated axis + rry = gauss_s1*(rx*co + ry*si); + rrx = gauss_s1*(-rx*si + ry*co); + + // Sum the derivatives to the cumulative descriptor + dx += rrx; + dy += rry; + mdx += fabs(rrx); + mdy += fabs(rry); + } + } + + // Add the values to the descriptor vector + gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f); + desc[dcount++] = dx*gauss_s2; + desc[dcount++] = dy*gauss_s2; + desc[dcount++] = mdx*gauss_s2; + desc[dcount++] = mdy*gauss_s2; + + len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2; + + j += 9; + } + + i += 9; + } + + // convert to unit vector + len = sqrt(len); + + for (i = 0; i < dsize; i++) { + desc[i] /= len; + } +} + +/* ************************************************************************* */ +/** + * @brief This method computes the rupright descriptor (not rotation invariant) of + * the provided keypoint + * @param kpt Input keypoint + * @param desc Descriptor vector + */ +void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char *desc) const { + + float di = 0.0, dx = 0.0, dy = 0.0; + float ri = 0.0, rx = 0.0, ry = 0.0, xf = 0.0, yf = 0.0; + float sample_x = 0.0, sample_y = 0.0, ratio = 0.0; + int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; + int level = 0, nsamples = 0, scale = 0; + int dcount1 = 0, dcount2 = 0; + + const AKAZEOptions & options = *options_; + const std::vector& evolution = *evolution_; + + // Matrices for the M-LDB descriptor + cv::Mat values_1 = cv::Mat::zeros(4, options.descriptor_channels, CV_32FC1); + cv::Mat values_2 = cv::Mat::zeros(9, options.descriptor_channels, CV_32FC1); + cv::Mat values_3 = cv::Mat::zeros(16, options.descriptor_channels, CV_32FC1); + + // Get the information from the keypoint + ratio = (float)(1 << kpt.octave); + scale = fRound(0.5f*kpt.size / ratio); + level = kpt.class_id; + yf = kpt.pt.y / ratio; + xf = kpt.pt.x / ratio; + + // First 2x2 grid + pattern_size = options_->descriptor_pattern_size; + sample_step = pattern_size; + + for (int i = -pattern_size; i < pattern_size; i += sample_step) { + for (int j = -pattern_size; j < pattern_size; j += sample_step) { + di = dx = dy = 0.0; + nsamples = 0; + + for (int k = i; k < i + sample_step; k++) { + for (int l = j; l < j + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + l*scale; + sample_x = xf + k*scale; + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + + ri = *(evolution[level].Lt.ptr(y1)+x1); + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + + di += ri; + dx += rx; + dy += ry; + nsamples++; + } + } + + di /= nsamples; + dx /= nsamples; + dy /= nsamples; + + *(values_1.ptr(dcount2)) = di; + *(values_1.ptr(dcount2)+1) = dx; + *(values_1.ptr(dcount2)+2) = dy; + dcount2++; + } + } + + // Do binary comparison first level + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 4; j++) { + if (*(values_1.ptr(i)) > *(values_1.ptr(j))) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + + if (*(values_1.ptr(i)+1) > *(values_1.ptr(j)+1)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + + if (*(values_1.ptr(i)+2) > *(values_1.ptr(j)+2)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + + // Second 3x3 grid + sample_step = static_cast(ceil(pattern_size*2. / 3.)); + dcount2 = 0; + + for (int i = -pattern_size; i < pattern_size; i += sample_step) { + for (int j = -pattern_size; j < pattern_size; j += sample_step) { + di = dx = dy = 0.0; + nsamples = 0; + + for (int k = i; k < i + sample_step; k++) { + for (int l = j; l < j + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + l*scale; + sample_x = xf + k*scale; + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + + ri = *(evolution[level].Lt.ptr(y1)+x1); + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + + di += ri; + dx += rx; + dy += ry; + nsamples++; + } + } + + di /= nsamples; + dx /= nsamples; + dy /= nsamples; + + *(values_2.ptr(dcount2)) = di; + *(values_2.ptr(dcount2)+1) = dx; + *(values_2.ptr(dcount2)+2) = dy; + dcount2++; + } + } + + //Do binary comparison second level + dcount2 = 0; + for (int i = 0; i < 9; i++) { + for (int j = i + 1; j < 9; j++) { + if (*(values_2.ptr(i)) > *(values_2.ptr(j))) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + + if (*(values_2.ptr(i)+1) > *(values_2.ptr(j)+1)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + + if (*(values_2.ptr(i)+2) > *(values_2.ptr(j)+2)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + + // Third 4x4 grid + sample_step = pattern_size / 2; + dcount2 = 0; + + for (int i = -pattern_size; i < pattern_size; i += sample_step) { + for (int j = -pattern_size; j < pattern_size; j += sample_step) { + di = dx = dy = 0.0; + nsamples = 0; + + for (int k = i; k < i + sample_step; k++) { + for (int l = j; l < j + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + l*scale; + sample_x = xf + k*scale; + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + + ri = *(evolution[level].Lt.ptr(y1)+x1); + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + + di += ri; + dx += rx; + dy += ry; + nsamples++; + } + } + + di /= nsamples; + dx /= nsamples; + dy /= nsamples; + + *(values_3.ptr(dcount2)) = di; + *(values_3.ptr(dcount2)+1) = dx; + *(values_3.ptr(dcount2)+2) = dy; + dcount2++; + } + } + + //Do binary comparison third level + dcount2 = 0; + for (int i = 0; i < 16; i++) { + for (int j = i + 1; j < 16; j++) { + if (*(values_3.ptr(i)) > *(values_3.ptr(j))) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + + if (*(values_3.ptr(i)+1) > *(values_3.ptr(j)+1)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + + if (*(values_3.ptr(i)+2) > *(values_3.ptr(j)+2)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } +} + +/* ************************************************************************* */ +/** + * @brief This method computes the descriptor of the provided keypoint given the + * main orientation of the keypoint + * @param kpt Input keypoint + * @param desc Descriptor vector + */ +void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char *desc) const { + + float di = 0.0, dx = 0.0, dy = 0.0, ratio = 0.0; + float ri = 0.0, rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, xf = 0.0, yf = 0.0; + float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0; + int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; + int level = 0, nsamples = 0, scale = 0; + int dcount1 = 0, dcount2 = 0; + + const AKAZEOptions & options = *options_; + const std::vector& evolution = *evolution_; + + // Matrices for the M-LDB descriptor + cv::Mat values_1 = cv::Mat::zeros(4, options.descriptor_channels, CV_32FC1); + cv::Mat values_2 = cv::Mat::zeros(9, options.descriptor_channels, CV_32FC1); + cv::Mat values_3 = cv::Mat::zeros(16, options.descriptor_channels, CV_32FC1); + + // Get the information from the keypoint + ratio = (float)(1 << kpt.octave); + scale = fRound(0.5f*kpt.size / ratio); + angle = kpt.angle; + level = kpt.class_id; + yf = kpt.pt.y / ratio; + xf = kpt.pt.x / ratio; + co = cos(angle); + si = sin(angle); + + // First 2x2 grid + pattern_size = options.descriptor_pattern_size; + sample_step = pattern_size; + + for (int i = -pattern_size; i < pattern_size; i += sample_step) { + for (int j = -pattern_size; j < pattern_size; j += sample_step) { + + di = dx = dy = 0.0; + nsamples = 0; + + for (float k = (float)i; k < i + sample_step; k++) { + for (float l = (float)j; l < j + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + (l*scale*co + k*scale*si); + sample_x = xf + (-l*scale*si + k*scale*co); + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + + ri = *(evolution[level].Lt.ptr(y1)+x1); + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + + di += ri; + + if (options.descriptor_channels == 2) { + dx += sqrtf(rx*rx + ry*ry); + } + else if (options.descriptor_channels == 3) { + // Get the x and y derivatives on the rotated axis + rry = rx*co + ry*si; + rrx = -rx*si + ry*co; + dx += rrx; + dy += rry; + } + + nsamples++; + } + } + + di /= nsamples; + dx /= nsamples; + dy /= nsamples; + + *(values_1.ptr(dcount2)) = di; + if (options.descriptor_channels > 1) { + *(values_1.ptr(dcount2)+1) = dx; + } + + if (options.descriptor_channels > 2) { + *(values_1.ptr(dcount2)+2) = dy; + } + + dcount2++; + } + } + + // Do binary comparison first level + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 4; j++) { + if (*(values_1.ptr(i)) > *(values_1.ptr(j))) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + + if (options.descriptor_channels > 1) { + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 4; j++) { + if (*(values_1.ptr(i)+1) > *(values_1.ptr(j)+1)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + + dcount1++; + } + } + } + + if (options.descriptor_channels > 2) { + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 4; j++) { + if (*(values_1.ptr(i)+2) > *(values_1.ptr(j)+2)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + } + + // Second 3x3 grid + sample_step = static_cast(ceil(pattern_size*2. / 3.)); + dcount2 = 0; + + for (int i = -pattern_size; i < pattern_size; i += sample_step) { + for (int j = -pattern_size; j < pattern_size; j += sample_step) { + + di = dx = dy = 0.0; + nsamples = 0; + + for (int k = i; k < i + sample_step; k++) { + for (int l = j; l < j + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + (l*scale*co + k*scale*si); + sample_x = xf + (-l*scale*si + k*scale*co); + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + + ri = *(evolution[level].Lt.ptr(y1)+x1); + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + di += ri; + + if (options.descriptor_channels == 2) { + dx += sqrtf(rx*rx + ry*ry); + } + else if (options.descriptor_channels == 3) { + // Get the x and y derivatives on the rotated axis + rry = rx*co + ry*si; + rrx = -rx*si + ry*co; + dx += rrx; + dy += rry; + } + + nsamples++; + } + } + + di /= nsamples; + dx /= nsamples; + dy /= nsamples; + + *(values_2.ptr(dcount2)) = di; + if (options.descriptor_channels > 1) { + *(values_2.ptr(dcount2)+1) = dx; + } + + if (options.descriptor_channels > 2) { + *(values_2.ptr(dcount2)+2) = dy; + } + + dcount2++; + } + } + + // Do binary comparison second level + for (int i = 0; i < 9; i++) { + for (int j = i + 1; j < 9; j++) { + if (*(values_2.ptr(i)) > *(values_2.ptr(j))) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + + if (options.descriptor_channels > 1) { + for (int i = 0; i < 9; i++) { + for (int j = i + 1; j < 9; j++) { + if (*(values_2.ptr(i)+1) > *(values_2.ptr(j)+1)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + } + + if (options.descriptor_channels > 2) { + for (int i = 0; i < 9; i++) { + for (int j = i + 1; j < 9; j++) { + if (*(values_2.ptr(i)+2) > *(values_2.ptr(j)+2)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + } + + // Third 4x4 grid + sample_step = pattern_size / 2; + dcount2 = 0; + + for (int i = -pattern_size; i < pattern_size; i += sample_step) { + for (int j = -pattern_size; j < pattern_size; j += sample_step) { + di = dx = dy = 0.0; + nsamples = 0; + + for (int k = i; k < i + sample_step; k++) { + for (int l = j; l < j + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + (l*scale*co + k*scale*si); + sample_x = xf + (-l*scale*si + k*scale*co); + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + + ri = *(evolution[level].Lt.ptr(y1)+x1); + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + di += ri; + + if (options.descriptor_channels == 2) { + dx += sqrtf(rx*rx + ry*ry); + } + else if (options.descriptor_channels == 3) { + // Get the x and y derivatives on the rotated axis + rry = rx*co + ry*si; + rrx = -rx*si + ry*co; + dx += rrx; + dy += rry; + } + + nsamples++; + } + } + + di /= nsamples; + dx /= nsamples; + dy /= nsamples; + + *(values_3.ptr(dcount2)) = di; + if (options.descriptor_channels > 1) + *(values_3.ptr(dcount2)+1) = dx; + + if (options.descriptor_channels > 2) + *(values_3.ptr(dcount2)+2) = dy; + + dcount2++; + } + } + + // Do binary comparison third level + for (int i = 0; i < 16; i++) { + for (int j = i + 1; j < 16; j++) { + if (*(values_3.ptr(i)) > *(values_3.ptr(j))) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + + if (options.descriptor_channels > 1) { + for (int i = 0; i < 16; i++) { + for (int j = i + 1; j < 16; j++) { + if (*(values_3.ptr(i)+1) > *(values_3.ptr(j)+1)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + } + + if (options.descriptor_channels > 2) { + for (int i = 0; i < 16; i++) { + for (int j = i + 1; j < 16; j++) { + if (*(values_3.ptr(i)+2) > *(values_3.ptr(j)+2)) { + desc[dcount1 / 8] |= (1 << (dcount1 % 8)); + } + dcount1++; + } + } + } +} + +/* ************************************************************************* */ +/** + * @brief This method computes the M-LDB descriptor of the provided keypoint given the + * main orientation of the keypoint. The descriptor is computed based on a subset of + * the bits of the whole descriptor + * @param kpt Input keypoint + * @param desc Descriptor vector + */ +void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char *desc) const { + + float di = 0.f, dx = 0.f, dy = 0.f; + float rx = 0.f, ry = 0.f; + float sample_x = 0.f, sample_y = 0.f; + int x1 = 0, y1 = 0; + + const AKAZEOptions & options = *options_; + const std::vector& evolution = *evolution_; + + // Get the information from the keypoint + float ratio = (float)(1 << kpt.octave); + int scale = fRound(0.5f*kpt.size / ratio); + float angle = kpt.angle; + int level = kpt.class_id; + float yf = kpt.pt.y / ratio; + float xf = kpt.pt.x / ratio; + float co = cos(angle); + float si = sin(angle); + + // Allocate memory for the matrix of values + cv::Mat values = cv::Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); + + // Sample everything, but only do the comparisons + vector steps(3); + steps.at(0) = options.descriptor_pattern_size; + steps.at(1) = (int)ceil(2.f*options.descriptor_pattern_size / 3.f); + steps.at(2) = options.descriptor_pattern_size / 2; + + for (int i = 0; i < descriptorSamples_.rows; i++) { + const int *coords = descriptorSamples_.ptr(i); + int sample_step = steps.at(coords[0]); + di = 0.0f; + dx = 0.0f; + dy = 0.0f; + + for (int k = coords[1]; k < coords[1] + sample_step; k++) { + for (int l = coords[2]; l < coords[2] + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + (l*scale*co + k*scale*si); + sample_x = xf + (-l*scale*si + k*scale*co); + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + + di += *(evolution[level].Lt.ptr(y1)+x1); + + if (options.descriptor_channels > 1) { + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + + if (options.descriptor_channels == 2) { + dx += sqrtf(rx*rx + ry*ry); + } + else if (options.descriptor_channels == 3) { + // Get the x and y derivatives on the rotated axis + dx += rx*co + ry*si; + dy += -rx*si + ry*co; + } + } + } + } + + *(values.ptr(options.descriptor_channels*i)) = di; + + if (options.descriptor_channels == 2) { + *(values.ptr(options.descriptor_channels*i + 1)) = dx; + } + else if (options.descriptor_channels == 3) { + *(values.ptr(options.descriptor_channels*i + 1)) = dx; + *(values.ptr(options.descriptor_channels*i + 2)) = dy; + } + } + + // Do the comparisons + const float *vals = values.ptr(0); + const int *comps = descriptorBits_.ptr(0); + + for (int i = 0; i vals[comps[2 * i + 1]]) { + desc[i / 8] |= (1 << (i % 8)); + } + } +} + +/* ************************************************************************* */ +/** + * @brief This method computes the upright (not rotation invariant) M-LDB descriptor + * of the provided keypoint given the main orientation of the keypoint. + * The descriptor is computed based on a subset of the bits of the whole descriptor + * @param kpt Input keypoint + * @param desc Descriptor vector + */ +void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char *desc) const { + + float di = 0.0f, dx = 0.0f, dy = 0.0f; + float rx = 0.0f, ry = 0.0f; + float sample_x = 0.0f, sample_y = 0.0f; + int x1 = 0, y1 = 0; + + const AKAZEOptions & options = *options_; + const std::vector& evolution = *evolution_; + + // Get the information from the keypoint + float ratio = (float)(1 << kpt.octave); + int scale = fRound(0.5f*kpt.size / ratio); + int level = kpt.class_id; + float yf = kpt.pt.y / ratio; + float xf = kpt.pt.x / ratio; + + // Allocate memory for the matrix of values + Mat values = cv::Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); + + vector steps(3); + steps.at(0) = options.descriptor_pattern_size; + steps.at(1) = static_cast(ceil(2.f*options.descriptor_pattern_size / 3.f)); + steps.at(2) = options.descriptor_pattern_size / 2; + + for (int i = 0; i < descriptorSamples_.rows; i++) { + const int *coords = descriptorSamples_.ptr(i); + int sample_step = steps.at(coords[0]); + di = 0.0f, dx = 0.0f, dy = 0.0f; + + for (int k = coords[1]; k < coords[1] + sample_step; k++) { + for (int l = coords[2]; l < coords[2] + sample_step; l++) { + + // Get the coordinates of the sample point + sample_y = yf + l*scale; + sample_x = xf + k*scale; + + y1 = fRound(sample_y); + x1 = fRound(sample_x); + di += *(evolution[level].Lt.ptr(y1)+x1); + + if (options.descriptor_channels > 1) { + rx = *(evolution[level].Lx.ptr(y1)+x1); + ry = *(evolution[level].Ly.ptr(y1)+x1); + + if (options.descriptor_channels == 2) { + dx += sqrtf(rx*rx + ry*ry); + } + else if (options.descriptor_channels == 3) { + dx += rx; + dy += ry; + } + } + } + } + + *(values.ptr(options.descriptor_channels*i)) = di; + + if (options.descriptor_channels == 2) { + *(values.ptr(options.descriptor_channels*i + 1)) = dx; + } + else if (options.descriptor_channels == 3) { + *(values.ptr(options.descriptor_channels*i + 1)) = dx; + *(values.ptr(options.descriptor_channels*i + 2)) = dy; + } + } + + // Do the comparisons + const float *vals = values.ptr(0); + const int *comps = descriptorBits_.ptr(0); + + for (int i = 0; i vals[comps[2 * i + 1]]) { + desc[i / 8] |= (1 << (i % 8)); + } + } +} + +/* ************************************************************************* */ +/** + * @brief This function computes a (quasi-random) list of bits to be taken + * from the full descriptor. To speed the extraction, the function creates + * a list of the samples that are involved in generating at least a bit (sampleList) + * and a list of the comparisons between those samples (comparisons) + * @param sampleList + * @param comparisons The matrix with the binary comparisons + * @param nbits The number of bits of the descriptor + * @param pattern_size The pattern size for the binary descriptor + * @param nchannels Number of channels to consider in the descriptor (1-3) + * @note The function keeps the 18 bits (3-channels by 6 comparisons) of the + * coarser grid, since it provides the most robust estimations + */ +void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons, int nbits, + int pattern_size, int nchannels) { + + int ssz = 0; + for (int i = 0; i < 3; i++) { + int gz = (i + 2)*(i + 2); + ssz += gz*(gz - 1) / 2; + } + ssz *= nchannels; + + CV_Assert(nbits <= ssz); // Descriptor size can't be bigger than full descriptor + + // Since the full descriptor is usually under 10k elements, we pick + // the selection from the full matrix. We take as many samples per + // pick as the number of channels. For every pick, we + // take the two samples involved and put them in the sampling list + + Mat_ fullM(ssz / nchannels, 5); + for (int i = 0, c = 0; i < 3; i++) { + int gdiv = i + 2; //grid divisions, per row + int gsz = gdiv*gdiv; + int psz = (int)ceil(2.f*pattern_size / (float)gdiv); + + for (int j = 0; j < gsz; j++) { + for (int k = j + 1; k < gsz; k++, c++) { + fullM(c, 0) = i; + fullM(c, 1) = psz*(j % gdiv) - pattern_size; + fullM(c, 2) = psz*(j / gdiv) - pattern_size; + fullM(c, 3) = psz*(k % gdiv) - pattern_size; + fullM(c, 4) = psz*(k / gdiv) - pattern_size; + } + } + } + + srand(1024); + Mat_ comps = Mat_(nchannels * (int)ceil(nbits / (float)nchannels), 2); + comps = 1000; + + // Select some samples. A sample includes all channels + int count = 0; + int npicks = (int)ceil(nbits / (float)nchannels); + Mat_ samples(29, 3); + Mat_ fullcopy = fullM.clone(); + samples = -1; + + for (int i = 0; i < npicks; i++) { + int k = rand() % (fullM.rows - i); + if (i < 6) { + // Force use of the coarser grid values and comparisons + k = i; + } + + bool n = true; + + for (int j = 0; j < count; j++) { + if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 1) && samples(j, 2) == fullcopy(k, 2)) { + n = false; + comps(i*nchannels, 0) = nchannels*j; + comps(i*nchannels + 1, 0) = nchannels*j + 1; + comps(i*nchannels + 2, 0) = nchannels*j + 2; + break; + } + } + + if (n) { + samples(count, 0) = fullcopy(k, 0); + samples(count, 1) = fullcopy(k, 1); + samples(count, 2) = fullcopy(k, 2); + comps(i*nchannels, 0) = nchannels*count; + comps(i*nchannels + 1, 0) = nchannels*count + 1; + comps(i*nchannels + 2, 0) = nchannels*count + 2; + count++; + } + + n = true; + for (int j = 0; j < count; j++) { + if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 3) && samples(j, 2) == fullcopy(k, 4)) { + n = false; + comps(i*nchannels, 1) = nchannels*j; + comps(i*nchannels + 1, 1) = nchannels*j + 1; + comps(i*nchannels + 2, 1) = nchannels*j + 2; + break; + } + } + + if (n) { + samples(count, 0) = fullcopy(k, 0); + samples(count, 1) = fullcopy(k, 3); + samples(count, 2) = fullcopy(k, 4); + comps(i*nchannels, 1) = nchannels*count; + comps(i*nchannels + 1, 1) = nchannels*count + 1; + comps(i*nchannels + 2, 1) = nchannels*count + 2; + count++; + } + + Mat tmp = fullcopy.row(k); + fullcopy.row(fullcopy.rows - i - 1).copyTo(tmp); + } + + sampleList = samples.rowRange(0, count).clone(); + comparisons = comps.rowRange(0, nbits).clone(); +} diff --git a/modules/features2d/src/kaze/AKAZEFeatures.h b/modules/features2d/src/kaze/AKAZEFeatures.h new file mode 100644 index 0000000000..f8ce7a4889 --- /dev/null +++ b/modules/features2d/src/kaze/AKAZEFeatures.h @@ -0,0 +1,62 @@ +/** + * @file AKAZE.h + * @brief Main class for detecting and computing binary descriptors in an + * accelerated nonlinear scale space + * @date Mar 27, 2013 + * @author Pablo F. Alcantarilla, Jesus Nuevo + */ + +#ifndef __OPENCV_FEATURES_2D_AKAZE_FEATURES_H__ +#define __OPENCV_FEATURES_2D_AKAZE_FEATURES_H__ + +/* ************************************************************************* */ +// Includes +#include "precomp.hpp" +#include "AKAZEConfig.h" +#include "TEvolution.h" + +/* ************************************************************************* */ +// AKAZE Class Declaration +class AKAZEFeatures { + +private: + + AKAZEOptions options_; ///< Configuration options for AKAZE + std::vector evolution_; ///< Vector of nonlinear diffusion evolution + + /// FED parameters + int ncycles_; ///< Number of cycles + bool reordering_; ///< Flag for reordering time steps + std::vector > tsteps_; ///< Vector of FED dynamic time steps + std::vector nsteps_; ///< Vector of number of steps per cycle + + /// Matrices for the M-LDB descriptor computation + cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. + cv::Mat descriptorBits_; + cv::Mat bitMask_; + +public: + + /// Constructor with input arguments + AKAZEFeatures(const AKAZEOptions& options); + + /// Scale Space methods + void Allocate_Memory_Evolution(); + int Create_Nonlinear_Scale_Space(const cv::Mat& img); + void Feature_Detection(std::vector& kpts); + void Compute_Determinant_Hessian_Response(void); + void Compute_Multiscale_Derivatives(void); + void Find_Scale_Space_Extrema(std::vector& kpts); + void Do_Subpixel_Refinement(std::vector& kpts); + + /// Feature description methods + void Compute_Descriptors(std::vector& kpts, cv::Mat& desc); + static void Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vector& evolution_); +}; + +/* ************************************************************************* */ +/// Inline functions +void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons, + int nbits, int pattern_size, int nchannels); + +#endif diff --git a/modules/features2d/src/kaze/KAZEConfig.h b/modules/features2d/src/kaze/KAZEConfig.h index 988e247372..21489a07a4 100644 --- a/modules/features2d/src/kaze/KAZEConfig.h +++ b/modules/features2d/src/kaze/KAZEConfig.h @@ -5,7 +5,8 @@ * @author Pablo F. Alcantarilla */ -#pragma once +#ifndef __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__ +#define __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__ // OpenCV Includes #include "precomp.hpp" @@ -15,14 +16,8 @@ struct KAZEOptions { - enum DIFFUSIVITY_TYPE { - PM_G1 = 0, - PM_G2 = 1, - WEICKERT = 2 - }; - KAZEOptions() - : diffusivity(PM_G2) + : diffusivity(cv::DIFF_PM_G2) , soffset(1.60f) , omax(4) @@ -33,20 +28,13 @@ struct KAZEOptions { , dthreshold(0.001f) , kcontrast(0.01f) , kcontrast_percentille(0.7f) - , kcontrast_bins(300) - - , use_fed(true) + , kcontrast_bins(300) , upright(false) , extended(false) - - , use_clipping_normalilzation(false) - , clipping_normalization_ratio(1.6f) - , clipping_normalization_niter(5) { } - DIFFUSIVITY_TYPE diffusivity; - + int diffusivity; float soffset; int omax; int nsublevels; @@ -57,27 +45,8 @@ struct KAZEOptions { float kcontrast; float kcontrast_percentille; int kcontrast_bins; - - bool use_fed; bool upright; bool extended; - - bool use_clipping_normalilzation; - float clipping_normalization_ratio; - int clipping_normalization_niter; }; -struct TEvolution { - cv::Mat Lx, Ly; // First order spatial derivatives - cv::Mat Lxx, Lxy, Lyy; // Second order spatial derivatives - cv::Mat Lflow; // Diffusivity image - cv::Mat Lt; // Evolution image - cv::Mat Lsmooth; // Smoothed image - cv::Mat Lstep; // Evolution step update - cv::Mat Ldet; // Detector response - float etime; // Evolution time - float esigma; // Evolution sigma. For linear diffusion t = sigma^2 / 2 - float octave; // Image octave - float sublevel; // Image sublevel in each octave - int sigma_size; // Integer esigma. For computing the feature detector responses -}; +#endif diff --git a/modules/features2d/src/kaze/KAZEFeatures.cpp b/modules/features2d/src/kaze/KAZEFeatures.cpp index 634f68da80..69e9a4b69f 100644 --- a/modules/features2d/src/kaze/KAZEFeatures.cpp +++ b/modules/features2d/src/kaze/KAZEFeatures.cpp @@ -22,22 +22,21 @@ */ #include "KAZEFeatures.h" +#include "utils.h" // Namespaces using namespace std; using namespace cv; using namespace cv::details::kaze; -//******************************************************************************* -//******************************************************************************* - +/* ************************************************************************* */ /** * @brief KAZE constructor with input options * @param options KAZE configuration options * @note The constructor allocates memory for the nonlinear scale space */ -KAZEFeatures::KAZEFeatures(KAZEOptions& _options) - : options(_options) +KAZEFeatures::KAZEFeatures(KAZEOptions& options) + : options_(options) { ncycles_ = 0; reordering_ = true; @@ -46,70 +45,48 @@ KAZEFeatures::KAZEFeatures(KAZEOptions& _options) Allocate_Memory_Evolution(); } -//******************************************************************************* -//******************************************************************************* - +/* ************************************************************************* */ /** * @brief This method allocates the memory for the nonlinear diffusion evolution */ void KAZEFeatures::Allocate_Memory_Evolution(void) { // Allocate the dimension of the matrices for the evolution - for (int i = 0; i <= options.omax - 1; i++) { - for (int j = 0; j <= options.nsublevels - 1; j++) { + for (int i = 0; i <= options_.omax - 1; i++) { + for (int j = 0; j <= options_.nsublevels - 1; j++) { TEvolution aux; - aux.Lx = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Ly = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Lxx = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Lxy = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Lyy = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Lflow = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Lt = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Lsmooth = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Lstep = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.Ldet = cv::Mat::zeros(options.img_height, options.img_width, CV_32F); - aux.esigma = options.soffset*pow((float)2.0f, (float)(j) / (float)(options.nsublevels)+i); + aux.Lx = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Ly = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lxx = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lxy = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lyy = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lt = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lsmooth = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Ldet = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.esigma = options_.soffset*pow((float)2.0f, (float)(j) / (float)(options_.nsublevels)+i); aux.etime = 0.5f*(aux.esigma*aux.esigma); aux.sigma_size = fRound(aux.esigma); - aux.octave = (float)i; - aux.sublevel = (float)j; + aux.octave = i; + aux.sublevel = j; evolution_.push_back(aux); } } // Allocate memory for the FED number of cycles and time steps - if (options.use_fed) { - for (size_t i = 1; i < evolution_.size(); i++) { - int naux = 0; - vector tau; - float ttime = 0.0; - ttime = evolution_[i].etime - evolution_[i - 1].etime; - naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau); - nsteps_.push_back(naux); - tsteps_.push_back(tau); - ncycles_++; - } - } - else { - // Allocate memory for the auxiliary variables that are used in the AOS scheme - Ltx_ = Mat::zeros(options.img_width, options.img_height, CV_32F); // TODO? IS IT A BUG??? - Lty_ = Mat::zeros(options.img_height, options.img_width, CV_32F); - px_ = Mat::zeros(options.img_height, options.img_width, CV_32F); - py_ = Mat::zeros(options.img_height, options.img_width, CV_32F); - ax_ = Mat::zeros(options.img_height, options.img_width, CV_32F); - ay_ = Mat::zeros(options.img_height, options.img_width, CV_32F); - bx_ = Mat::zeros(options.img_height - 1, options.img_width, CV_32F); - by_ = Mat::zeros(options.img_height - 1, options.img_width, CV_32F); - qr_ = Mat::zeros(options.img_height - 1, options.img_width, CV_32F); - qc_ = Mat::zeros(options.img_height, options.img_width - 1, CV_32F); + for (size_t i = 1; i < evolution_.size(); i++) { + int naux = 0; + vector tau; + float ttime = 0.0; + ttime = evolution_[i].etime - evolution_[i - 1].etime; + naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau); + nsteps_.push_back(naux); + tsteps_.push_back(tau); + ncycles_++; } - } -//******************************************************************************* -//******************************************************************************* - +/* ************************************************************************* */ /** * @brief This method creates the nonlinear scale space for a given image * @param img Input image for which the nonlinear scale space needs to be created @@ -121,52 +98,47 @@ int KAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat &img) // Copy the original image to the first level of the evolution img.copyTo(evolution_[0].Lt); - gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options.soffset); - gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lsmooth, 0, 0, options.sderivatives); + gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset); + gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lsmooth, 0, 0, options_.sderivatives); // Firstly compute the kcontrast factor - Compute_KContrast(evolution_[0].Lt, options.kcontrast_percentille); + Compute_KContrast(evolution_[0].Lt, options_.kcontrast_percentille); + + // Allocate memory for the flow and step images + cv::Mat Lflow = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); + cv::Mat Lstep = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); // Now generate the rest of evolution levels for (size_t i = 1; i < evolution_.size(); i++) { evolution_[i - 1].Lt.copyTo(evolution_[i].Lt); - gaussian_2D_convolution(evolution_[i - 1].Lt, evolution_[i].Lsmooth, 0, 0, options.sderivatives); + gaussian_2D_convolution(evolution_[i - 1].Lt, evolution_[i].Lsmooth, 0, 0, options_.sderivatives); // Compute the Gaussian derivatives Lx and Ly Scharr(evolution_[i].Lsmooth, evolution_[i].Lx, CV_32F, 1, 0, 1, 0, BORDER_DEFAULT); Scharr(evolution_[i].Lsmooth, evolution_[i].Ly, CV_32F, 0, 1, 1, 0, BORDER_DEFAULT); // Compute the conductivity equation - if (options.diffusivity == KAZEOptions::PM_G1) { - pm_g1(evolution_[i].Lx, evolution_[i].Ly, evolution_[i].Lflow, options.kcontrast); + if (options_.diffusivity == cv::DIFF_PM_G1) { + pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); } - else if (options.diffusivity == KAZEOptions::PM_G2) { - pm_g2(evolution_[i].Lx, evolution_[i].Ly, evolution_[i].Lflow, options.kcontrast); + else if (options_.diffusivity == cv::DIFF_PM_G2) { + pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); } - else if (options.diffusivity == KAZEOptions::WEICKERT) { - weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, evolution_[i].Lflow, options.kcontrast); + else if (options_.diffusivity == cv::DIFF_WEICKERT) { + weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); } // Perform FED n inner steps - if (options.use_fed) { - for (int j = 0; j < nsteps_[i - 1]; j++) { - nld_step_scalar(evolution_[i].Lt, evolution_[i].Lflow, evolution_[i].Lstep, tsteps_[i - 1][j]); - } - } - else { - // Perform the evolution step with AOS - AOS_Step_Scalar(evolution_[i].Lt, evolution_[i - 1].Lt, evolution_[i].Lflow, - evolution_[i].etime - evolution_[i - 1].etime); + for (int j = 0; j < nsteps_[i - 1]; j++) { + nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]); } } return 0; } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method computes the k contrast factor * @param img Input image @@ -174,38 +146,10 @@ int KAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat &img) */ void KAZEFeatures::Compute_KContrast(const cv::Mat &img, const float &kpercentile) { - options.kcontrast = compute_k_percentile(img, kpercentile, options.sderivatives, options.kcontrast_bins, 0, 0); -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This method computes the multiscale derivatives for the nonlinear scale space - */ -void KAZEFeatures::Compute_Multiscale_Derivatives(void) -{ - // TODO: use cv::parallel_for_ - for (size_t i = 0; i < evolution_.size(); i++) - { - // Compute multiscale derivatives for the detector - compute_scharr_derivatives(evolution_[i].Lsmooth, evolution_[i].Lx, 1, 0, evolution_[i].sigma_size); - compute_scharr_derivatives(evolution_[i].Lsmooth, evolution_[i].Ly, 0, 1, evolution_[i].sigma_size); - compute_scharr_derivatives(evolution_[i].Lx, evolution_[i].Lxx, 1, 0, evolution_[i].sigma_size); - compute_scharr_derivatives(evolution_[i].Ly, evolution_[i].Lyy, 0, 1, evolution_[i].sigma_size); - compute_scharr_derivatives(evolution_[i].Lx, evolution_[i].Lxy, 0, 1, evolution_[i].sigma_size); - - evolution_[i].Lx = evolution_[i].Lx*((evolution_[i].sigma_size)); - evolution_[i].Ly = evolution_[i].Ly*((evolution_[i].sigma_size)); - evolution_[i].Lxx = evolution_[i].Lxx*((evolution_[i].sigma_size)*(evolution_[i].sigma_size)); - evolution_[i].Lxy = evolution_[i].Lxy*((evolution_[i].sigma_size)*(evolution_[i].sigma_size)); - evolution_[i].Lyy = evolution_[i].Lyy*((evolution_[i].sigma_size)*(evolution_[i].sigma_size)); - } + options_.kcontrast = compute_k_percentile(img, kpercentile, options_.sderivatives, options_.kcontrast_bins, 0, 0); } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method computes the feature detector response for the nonlinear scale space * @note We use the Hessian determinant as feature detector @@ -219,9 +163,9 @@ void KAZEFeatures::Compute_Detector_Response(void) for (size_t i = 0; i < evolution_.size(); i++) { - for (int ix = 0; ix < options.img_height; ix++) + for (int ix = 0; ix < options_.img_height; ix++) { - for (int jx = 0; jx < options.img_width; jx++) + for (int jx = 0; jx < options_.img_width; jx++) { lxx = *(evolution_[i].Lxx.ptr(ix)+jx); lxy = *(evolution_[i].Lxy.ptr(ix)+jx); @@ -232,9 +176,7 @@ void KAZEFeatures::Compute_Detector_Response(void) } } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method selects interesting keypoints through the nonlinear scale space * @param kpts Vector of keypoints @@ -242,27 +184,127 @@ void KAZEFeatures::Compute_Detector_Response(void) void KAZEFeatures::Feature_Detection(std::vector& kpts) { kpts.clear(); + Compute_Detector_Response(); + Determinant_Hessian(kpts); + Do_Subpixel_Refinement(kpts); +} - // Firstly compute the detector response for each pixel and scale level - Compute_Detector_Response(); +/* ************************************************************************* */ +class MultiscaleDerivativesKAZEInvoker : public cv::ParallelLoopBody +{ +public: + explicit MultiscaleDerivativesKAZEInvoker(std::vector& ev) : evolution_(&ev) + { + } + + void operator()(const cv::Range& range) const + { + std::vector& evolution = *evolution_; + for (int i = range.start; i < range.end; i++) + { + compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Lx, 1, 0, evolution[i].sigma_size); + compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Ly, 0, 1, evolution[i].sigma_size); + compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxx, 1, 0, evolution[i].sigma_size); + compute_scharr_derivatives(evolution[i].Ly, evolution[i].Lyy, 0, 1, evolution[i].sigma_size); + compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxy, 0, 1, evolution[i].sigma_size); + + evolution[i].Lx = evolution[i].Lx*((evolution[i].sigma_size)); + evolution[i].Ly = evolution[i].Ly*((evolution[i].sigma_size)); + evolution[i].Lxx = evolution[i].Lxx*((evolution[i].sigma_size)*(evolution[i].sigma_size)); + evolution[i].Lxy = evolution[i].Lxy*((evolution[i].sigma_size)*(evolution[i].sigma_size)); + evolution[i].Lyy = evolution[i].Lyy*((evolution[i].sigma_size)*(evolution[i].sigma_size)); + } + } - // Find scale space extrema - Determinant_Hessian_Parallel(kpts); +private: + std::vector* evolution_; +}; - // Perform some subpixel refinement - Do_Subpixel_Refinement(kpts); +/* ************************************************************************* */ +/** + * @brief This method computes the multiscale derivatives for the nonlinear scale space + */ +void KAZEFeatures::Compute_Multiscale_Derivatives(void) +{ + cv::parallel_for_(cv::Range(0, (int)evolution_.size()), + MultiscaleDerivativesKAZEInvoker(evolution_)); } -//************************************************************************************* -//************************************************************************************* +/* ************************************************************************* */ +class FindExtremumKAZEInvoker : public cv::ParallelLoopBody +{ +public: + explicit FindExtremumKAZEInvoker(std::vector& ev, std::vector >& kpts_par, + const KAZEOptions& options) : evolution_(&ev), kpts_par_(&kpts_par), options_(options) + { + } + + void operator()(const cv::Range& range) const + { + std::vector& evolution = *evolution_; + std::vector >& kpts_par = *kpts_par_; + for (int i = range.start; i < range.end; i++) + { + float value = 0.0; + bool is_extremum = false; + + for (int ix = 1; ix < options_.img_height - 1; ix++) { + for (int jx = 1; jx < options_.img_width - 1; jx++) { + + is_extremum = false; + value = *(evolution[i].Ldet.ptr(ix)+jx); + + // Filter the points with the detector threshold + if (value > options_.dthreshold) { + if (value >= *(evolution[i].Ldet.ptr(ix)+jx - 1)) { + // First check on the same scale + if (check_maximum_neighbourhood(evolution[i].Ldet, 1, value, ix, jx, 1)) { + // Now check on the lower scale + if (check_maximum_neighbourhood(evolution[i - 1].Ldet, 1, value, ix, jx, 0)) { + // Now check on the upper scale + if (check_maximum_neighbourhood(evolution[i + 1].Ldet, 1, value, ix, jx, 0)) { + is_extremum = true; + } + } + } + } + } + + // Add the point of interest!! + if (is_extremum == true) { + cv::KeyPoint point; + point.pt.x = (float)jx; + point.pt.y = (float)ix; + point.response = fabs(value); + point.size = evolution[i].esigma; + point.octave = (int)evolution[i].octave; + point.class_id = i; + + // We use the angle field for the sublevel value + // Then, we will replace this angle field with the main orientation + point.angle = static_cast(evolution[i].sublevel); + kpts_par[i - 1].push_back(point); + } + } + } + } + } + +private: + std::vector* evolution_; + std::vector >* kpts_par_; + KAZEOptions options_; +}; + +/* ************************************************************************* */ /** * @brief This method performs the detection of keypoints by using the normalized * score of the Hessian determinant through the nonlinear scale space * @param kpts Vector of keypoints * @note We compute features for each of the nonlinear scale space level in a different processing thread */ -void KAZEFeatures::Determinant_Hessian_Parallel(std::vector& kpts) +void KAZEFeatures::Determinant_Hessian(std::vector& kpts) { int level = 0; float dist = 0.0, smax = 3.0; @@ -283,10 +325,8 @@ void KAZEFeatures::Determinant_Hessian_Parallel(std::vector& kpts) kpts_par_.push_back(aux); } - // TODO: Use cv::parallel_for_ - for (int i = 1; i < (int)evolution_.size() - 1; i++) { - Find_Extremum_Threading(i); - } + cv::parallel_for_(cv::Range(1, (int)evolution_.size()-1), + FindExtremumKAZEInvoker(evolution_, kpts_par_, options_)); // Now fill the vector of keypoints!!! for (int i = 0; i < (int)kpts_par_.size(); i++) { @@ -343,63 +383,7 @@ void KAZEFeatures::Determinant_Hessian_Parallel(std::vector& kpts) } } -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This method is called by the thread which is responsible of finding extrema - * at a given nonlinear scale level - * @param level Index in the nonlinear scale space evolution - */ -void KAZEFeatures::Find_Extremum_Threading(const int& level) { - - float value = 0.0; - bool is_extremum = false; - - for (int ix = 1; ix < options.img_height - 1; ix++) { - for (int jx = 1; jx < options.img_width - 1; jx++) { - - is_extremum = false; - value = *(evolution_[level].Ldet.ptr(ix)+jx); - - // Filter the points with the detector threshold - if (value > options.dthreshold) { - if (value >= *(evolution_[level].Ldet.ptr(ix)+jx - 1)) { - // First check on the same scale - if (check_maximum_neighbourhood(evolution_[level].Ldet, 1, value, ix, jx, 1)) { - // Now check on the lower scale - if (check_maximum_neighbourhood(evolution_[level - 1].Ldet, 1, value, ix, jx, 0)) { - // Now check on the upper scale - if (check_maximum_neighbourhood(evolution_[level + 1].Ldet, 1, value, ix, jx, 0)) { - is_extremum = true; - } - } - } - } - } - - // Add the point of interest!! - if (is_extremum == true) { - KeyPoint point; - point.pt.x = (float)jx; - point.pt.y = (float)ix; - point.response = fabs(value); - point.size = evolution_[level].esigma; - point.octave = (int)evolution_[level].octave; - point.class_id = level; - - // We use the angle field for the sublevel value - // Then, we will replace this angle field with the main orientation - point.angle = evolution_[level].sublevel; - kpts_par_[level - 1].push_back(point); - } - } - } -} - -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method performs subpixel refinement of the detected keypoints * @param kpts Vector of detected keypoints @@ -475,10 +459,10 @@ void KAZEFeatures::Do_Subpixel_Refinement(std::vector &kpts) { if (fabs(*(dst.ptr(0))) <= 1.0f && fabs(*(dst.ptr(1))) <= 1.0f && fabs(*(dst.ptr(2))) <= 1.0f) { kpts_[i].pt.x += *(dst.ptr(0)); kpts_[i].pt.y += *(dst.ptr(1)); - dsc = kpts_[i].octave + (kpts_[i].angle + *(dst.ptr(2))) / ((float)(options.nsublevels)); + dsc = kpts_[i].octave + (kpts_[i].angle + *(dst.ptr(2))) / ((float)(options_.nsublevels)); // In OpenCV the size of a keypoint is the diameter!! - kpts_[i].size = 2.0f*options.soffset*pow((float)2.0f, dsc); + kpts_[i].size = 2.0f*options_.soffset*pow((float)2.0f, dsc); kpts_[i].angle = 0.0; } // Set the points to be deleted after the for loop @@ -497,17 +481,15 @@ void KAZEFeatures::Do_Subpixel_Refinement(std::vector &kpts) { } } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ class KAZE_Descriptor_Invoker : public cv::ParallelLoopBody { public: - KAZE_Descriptor_Invoker(std::vector &kpts, cv::Mat &desc, std::vector& evolution, const KAZEOptions& _options) - : _kpts(&kpts) - , _desc(&desc) - , _evolution(&evolution) - , options(_options) + KAZE_Descriptor_Invoker(std::vector &kpts, cv::Mat &desc, std::vector& evolution, const KAZEOptions& options) + : kpts_(&kpts) + , desc_(&desc) + , evolution_(&evolution) + , options_(options) { } @@ -517,26 +499,26 @@ public: void operator() (const cv::Range& range) const { - std::vector &kpts = *_kpts; - cv::Mat &desc = *_desc; - std::vector &evolution = *_evolution; + std::vector &kpts = *kpts_; + cv::Mat &desc = *desc_; + std::vector &evolution = *evolution_; for (int i = range.start; i < range.end; i++) { kpts[i].angle = 0.0; - if (options.upright) + if (options_.upright) { kpts[i].angle = 0.0; - if (options.extended) + if (options_.extended) Get_KAZE_Upright_Descriptor_128(kpts[i], desc.ptr((int)i)); else Get_KAZE_Upright_Descriptor_64(kpts[i], desc.ptr((int)i)); } else { - KAZEFeatures::Compute_Main_Orientation(kpts[i], evolution, options); + KAZEFeatures::Compute_Main_Orientation(kpts[i], evolution, options_); - if (options.extended) + if (options_.extended) Get_KAZE_Descriptor_128(kpts[i], desc.ptr((int)i)); else Get_KAZE_Descriptor_64(kpts[i], desc.ptr((int)i)); @@ -549,12 +531,13 @@ private: void Get_KAZE_Upright_Descriptor_128(const cv::KeyPoint& kpt, float* desc) const; void Get_KAZE_Descriptor_128(const cv::KeyPoint& kpt, float *desc) const; - std::vector * _kpts; - cv::Mat * _desc; - std::vector * _evolution; - KAZEOptions options; + std::vector * kpts_; + cv::Mat * desc_; + std::vector * evolution_; + KAZEOptions options_; }; +/* ************************************************************************* */ /** * @brief This method computes the set of descriptors through the nonlinear scale space * @param kpts Vector of keypoints @@ -562,20 +545,23 @@ private: */ void KAZEFeatures::Feature_Description(std::vector &kpts, cv::Mat &desc) { + for(size_t i = 0; i < kpts.size(); i++) + { + CV_Assert(0 <= kpts[i].class_id && kpts[i].class_id < static_cast(evolution_.size())); + } + // Allocate memory for the matrix of descriptors - if (options.extended == true) { + if (options_.extended == true) { desc = Mat::zeros((int)kpts.size(), 128, CV_32FC1); } else { desc = Mat::zeros((int)kpts.size(), 64, CV_32FC1); } - cv::parallel_for_(cv::Range(0, (int)kpts.size()), KAZE_Descriptor_Invoker(kpts, desc, evolution_, options)); + cv::parallel_for_(cv::Range(0, (int)kpts.size()), KAZE_Descriptor_Invoker(kpts, desc, evolution_, options_)); } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method computes the main orientation for a given keypoint * @param kpt Input keypoint @@ -651,9 +637,7 @@ void KAZEFeatures::Compute_Main_Orientation(cv::KeyPoint &kpt, const std::vector } } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method computes the upright descriptor (not rotation invariant) of * the provided keypoint @@ -673,7 +657,7 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const cv::KeyPoint float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0; int dsize = 0, scale = 0, level = 0; - std::vector& evolution_ = *_evolution; + std::vector& evolution = *evolution_; // Subregion centers for the 4x4 gaussian weighting float cx = -0.5f, cy = 0.5f; @@ -724,26 +708,26 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const cv::KeyPoint y1 = (int)(sample_y - 0.5f); x1 = (int)(sample_x - 0.5f); - checkDescriptorLimits(x1, y1, options.img_width, options.img_height); + checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height); y2 = (int)(sample_y + 0.5f); x2 = (int)(sample_x + 0.5f); - checkDescriptorLimits(x2, y2, options.img_width, options.img_height); + checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height); fx = sample_x - x1; fy = sample_y - y1; - res1 = *(evolution_[level].Lx.ptr(y1)+x1); - res2 = *(evolution_[level].Lx.ptr(y1)+x2); - res3 = *(evolution_[level].Lx.ptr(y2)+x1); - res4 = *(evolution_[level].Lx.ptr(y2)+x2); + res1 = *(evolution[level].Lx.ptr(y1)+x1); + res2 = *(evolution[level].Lx.ptr(y1)+x2); + res3 = *(evolution[level].Lx.ptr(y2)+x1); + res4 = *(evolution[level].Lx.ptr(y2)+x2); rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - res1 = *(evolution_[level].Ly.ptr(y1)+x1); - res2 = *(evolution_[level].Ly.ptr(y1)+x2); - res3 = *(evolution_[level].Ly.ptr(y2)+x1); - res4 = *(evolution_[level].Ly.ptr(y2)+x2); + res1 = *(evolution[level].Ly.ptr(y1)+x1); + res2 = *(evolution[level].Ly.ptr(y1)+x2); + res3 = *(evolution[level].Ly.ptr(y2)+x1); + res4 = *(evolution[level].Ly.ptr(y2)+x2); ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; rx = gauss_s1*rx; @@ -779,15 +763,9 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const cv::KeyPoint for (i = 0; i < dsize; i++) { desc[i] /= len; } - - if (options.use_clipping_normalilzation) { - clippingDescriptor(desc, dsize, options.clipping_normalization_niter, options.clipping_normalization_ratio); - } } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method computes the descriptor of the provided keypoint given the * main orientation of the keypoint @@ -807,7 +785,7 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const cv::KeyPoint &kpt, fl int kx = 0, ky = 0, i = 0, j = 0, dcount = 0; int dsize = 0, scale = 0, level = 0; - std::vector& evolution_ = *_evolution; + std::vector& evolution = *evolution_; // Subregion centers for the 4x4 gaussian weighting float cx = -0.5f, cy = 0.5f; @@ -862,26 +840,26 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const cv::KeyPoint &kpt, fl y1 = fRound(sample_y - 0.5f); x1 = fRound(sample_x - 0.5f); - checkDescriptorLimits(x1, y1, options.img_width, options.img_height); + checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height); y2 = (int)(sample_y + 0.5f); x2 = (int)(sample_x + 0.5f); - checkDescriptorLimits(x2, y2, options.img_width, options.img_height); + checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height); fx = sample_x - x1; fy = sample_y - y1; - res1 = *(evolution_[level].Lx.ptr(y1)+x1); - res2 = *(evolution_[level].Lx.ptr(y1)+x2); - res3 = *(evolution_[level].Lx.ptr(y2)+x1); - res4 = *(evolution_[level].Lx.ptr(y2)+x2); + res1 = *(evolution[level].Lx.ptr(y1)+x1); + res2 = *(evolution[level].Lx.ptr(y1)+x2); + res3 = *(evolution[level].Lx.ptr(y2)+x1); + res4 = *(evolution[level].Lx.ptr(y2)+x2); rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - res1 = *(evolution_[level].Ly.ptr(y1)+x1); - res2 = *(evolution_[level].Ly.ptr(y1)+x2); - res3 = *(evolution_[level].Ly.ptr(y2)+x1); - res4 = *(evolution_[level].Ly.ptr(y2)+x2); + res1 = *(evolution[level].Ly.ptr(y1)+x1); + res2 = *(evolution[level].Ly.ptr(y1)+x2); + res3 = *(evolution[level].Ly.ptr(y2)+x1); + res4 = *(evolution[level].Ly.ptr(y2)+x2); ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; // Get the x and y derivatives on the rotated axis @@ -914,15 +892,9 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const cv::KeyPoint &kpt, fl for (i = 0; i < dsize; i++) { desc[i] /= len; } - - if (options.use_clipping_normalilzation) { - clippingDescriptor(desc, dsize, options.clipping_normalization_niter, options.clipping_normalization_ratio); - } } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method computes the extended upright descriptor (not rotation invariant) of * the provided keypoint @@ -947,7 +919,7 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const cv::KeyPoint // Subregion centers for the 4x4 gaussian weighting float cx = -0.5f, cy = 0.5f; - std::vector& evolution_ = *_evolution; + std::vector& evolution = *evolution_; // Set the descriptor size and the sample and pattern sizes dsize = 128; @@ -998,26 +970,26 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const cv::KeyPoint y1 = (int)(sample_y - 0.5f); x1 = (int)(sample_x - 0.5f); - checkDescriptorLimits(x1, y1, options.img_width, options.img_height); + checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height); y2 = (int)(sample_y + 0.5f); x2 = (int)(sample_x + 0.5f); - checkDescriptorLimits(x2, y2, options.img_width, options.img_height); + checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height); fx = sample_x - x1; fy = sample_y - y1; - res1 = *(evolution_[level].Lx.ptr(y1)+x1); - res2 = *(evolution_[level].Lx.ptr(y1)+x2); - res3 = *(evolution_[level].Lx.ptr(y2)+x1); - res4 = *(evolution_[level].Lx.ptr(y2)+x2); + res1 = *(evolution[level].Lx.ptr(y1)+x1); + res2 = *(evolution[level].Lx.ptr(y1)+x2); + res3 = *(evolution[level].Lx.ptr(y2)+x1); + res4 = *(evolution[level].Lx.ptr(y2)+x2); rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - res1 = *(evolution_[level].Ly.ptr(y1)+x1); - res2 = *(evolution_[level].Ly.ptr(y1)+x2); - res3 = *(evolution_[level].Ly.ptr(y2)+x1); - res4 = *(evolution_[level].Ly.ptr(y2)+x2); + res1 = *(evolution[level].Ly.ptr(y1)+x1); + res2 = *(evolution[level].Ly.ptr(y1)+x2); + res3 = *(evolution[level].Ly.ptr(y2)+x1); + res4 = *(evolution[level].Ly.ptr(y2)+x2); ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; rx = gauss_s1*rx; @@ -1072,15 +1044,9 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const cv::KeyPoint for (i = 0; i < dsize; i++) { desc[i] /= len; } - - if (options.use_clipping_normalilzation) { - clippingDescriptor(desc, dsize, options.clipping_normalization_niter, options.clipping_normalization_ratio); - } } -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ /** * @brief This method computes the extended G-SURF descriptor of the provided keypoint * given the main orientation of the keypoint @@ -1102,7 +1068,7 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const cv::KeyPoint &kpt, f int kx = 0, ky = 0, i = 0, j = 0, dcount = 0; int dsize = 0, scale = 0, level = 0; - std::vector& evolution_ = *_evolution; + std::vector& evolution = *evolution_; // Subregion centers for the 4x4 gaussian weighting float cx = -0.5f, cy = 0.5f; @@ -1160,26 +1126,26 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const cv::KeyPoint &kpt, f y1 = fRound(sample_y - 0.5f); x1 = fRound(sample_x - 0.5f); - checkDescriptorLimits(x1, y1, options.img_width, options.img_height); + checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height); y2 = (int)(sample_y + 0.5f); x2 = (int)(sample_x + 0.5f); - checkDescriptorLimits(x2, y2, options.img_width, options.img_height); + checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height); fx = sample_x - x1; fy = sample_y - y1; - res1 = *(evolution_[level].Lx.ptr(y1)+x1); - res2 = *(evolution_[level].Lx.ptr(y1)+x2); - res3 = *(evolution_[level].Lx.ptr(y2)+x1); - res4 = *(evolution_[level].Lx.ptr(y2)+x2); + res1 = *(evolution[level].Lx.ptr(y1)+x1); + res2 = *(evolution[level].Lx.ptr(y1)+x2); + res3 = *(evolution[level].Lx.ptr(y2)+x1); + res4 = *(evolution[level].Lx.ptr(y2)+x2); rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; - res1 = *(evolution_[level].Ly.ptr(y1)+x1); - res2 = *(evolution_[level].Ly.ptr(y1)+x2); - res3 = *(evolution_[level].Ly.ptr(y2)+x1); - res4 = *(evolution_[level].Ly.ptr(y2)+x2); + res1 = *(evolution[level].Ly.ptr(y1)+x1); + res2 = *(evolution[level].Ly.ptr(y1)+x2); + res3 = *(evolution[level].Ly.ptr(y2)+x1); + res4 = *(evolution[level].Ly.ptr(y2)+x2); ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; // Get the x and y derivatives on the rotated axis @@ -1235,298 +1201,4 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const cv::KeyPoint &kpt, f for (i = 0; i < dsize; i++) { desc[i] /= len; } - - if (options.use_clipping_normalilzation) { - clippingDescriptor(desc, dsize, options.clipping_normalization_niter, options.clipping_normalization_ratio); - } -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This method performs a scalar non-linear diffusion step using AOS schemes - * @param Ld Image at a given evolution step - * @param Ldprev Image at a previous evolution step - * @param c Conductivity image - * @param stepsize Stepsize for the nonlinear diffusion evolution - * @note If c is constant, the diffusion will be linear - * If c is a matrix of the same size as Ld, the diffusion will be nonlinear - * The stepsize can be arbitrarily large - */ -void KAZEFeatures::AOS_Step_Scalar(cv::Mat &Ld, const cv::Mat &Ldprev, const cv::Mat &c, const float& stepsize) { - - AOS_Rows(Ldprev, c, stepsize); - AOS_Columns(Ldprev, c, stepsize); - - Ld = 0.5f*(Lty_ + Ltx_.t()); -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This method performs performs 1D-AOS for the image rows - * @param Ldprev Image at a previous evolution step - * @param c Conductivity image - * @param stepsize Stepsize for the nonlinear diffusion evolution - */ -void KAZEFeatures::AOS_Rows(const cv::Mat &Ldprev, const cv::Mat &c, const float& stepsize) { - - // Operate on rows - for (int i = 0; i < qr_.rows; i++) { - for (int j = 0; j < qr_.cols; j++) { - *(qr_.ptr(i)+j) = *(c.ptr(i)+j) + *(c.ptr(i + 1) + j); - } - } - - for (int j = 0; j < py_.cols; j++) { - *(py_.ptr(0) + j) = *(qr_.ptr(0) + j); - } - - for (int j = 0; j < py_.cols; j++) { - *(py_.ptr(py_.rows - 1) + j) = *(qr_.ptr(qr_.rows - 1) + j); - } - - for (int i = 1; i < py_.rows - 1; i++) { - for (int j = 0; j < py_.cols; j++) { - *(py_.ptr(i)+j) = *(qr_.ptr(i - 1) + j) + *(qr_.ptr(i)+j); - } - } - - // a = 1 + t.*p; (p is -1*p) - // b = -t.*q; - ay_ = 1.0f + stepsize*py_; // p is -1*p - by_ = -stepsize*qr_; - - // Do Thomas algorithm to solve the linear system of equations - Thomas(ay_, by_, Ldprev, Lty_); -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This method performs performs 1D-AOS for the image columns - * @param Ldprev Image at a previous evolution step - * @param c Conductivity image - * @param stepsize Stepsize for the nonlinear diffusion evolution - */ -void KAZEFeatures::AOS_Columns(const cv::Mat &Ldprev, const cv::Mat &c, const float& stepsize) { - - // Operate on columns - for (int j = 0; j < qc_.cols; j++) { - for (int i = 0; i < qc_.rows; i++) { - *(qc_.ptr(i)+j) = *(c.ptr(i)+j) + *(c.ptr(i)+j + 1); - } - } - - for (int i = 0; i < px_.rows; i++) { - *(px_.ptr(i)) = *(qc_.ptr(i)); - } - - for (int i = 0; i < px_.rows; i++) { - *(px_.ptr(i)+px_.cols - 1) = *(qc_.ptr(i)+qc_.cols - 1); - } - - for (int j = 1; j < px_.cols - 1; j++) { - for (int i = 0; i < px_.rows; i++) { - *(px_.ptr(i)+j) = *(qc_.ptr(i)+j - 1) + *(qc_.ptr(i)+j); - } - } - - // a = 1 + t.*p'; - ax_ = 1.0f + stepsize*px_.t(); - - // b = -t.*q'; - bx_ = -stepsize*qc_.t(); - - // But take care since we need to transpose the solution!! - Mat Ldprevt = Ldprev.t(); - - // Do Thomas algorithm to solve the linear system of equations - Thomas(ax_, bx_, Ldprevt, Ltx_); -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This method does the Thomas algorithm for solving a tridiagonal linear system - * @note The matrix A must be strictly diagonally dominant for a stable solution - */ -void KAZEFeatures::Thomas(const cv::Mat &a, const cv::Mat &b, const cv::Mat &Ld, cv::Mat &x) { - - // Auxiliary variables - int n = a.rows; - Mat m = cv::Mat::zeros(a.rows, a.cols, CV_32F); - Mat l = cv::Mat::zeros(b.rows, b.cols, CV_32F); - Mat y = cv::Mat::zeros(Ld.rows, Ld.cols, CV_32F); - - /** A*x = d; */ - /** / a1 b1 0 0 0 ... 0 \ / x1 \ = / d1 \ */ - /** | c1 a2 b2 0 0 ... 0 | | x2 | = | d2 | */ - /** | 0 c2 a3 b3 0 ... 0 | | x3 | = | d3 | */ - /** | : : : : 0 ... 0 | | : | = | : | */ - /** | : : : : 0 cn-1 an | | xn | = | dn | */ - - /** 1. LU decomposition - / L = / 1 \ U = / m1 r1 \ - / | l1 1 | | m2 r2 | - / | l2 1 | | m3 r3 | - / | : : : | | : : : | - / \ ln-1 1 / \ mn / */ - - for (int j = 0; j < m.cols; j++) { - *(m.ptr(0) + j) = *(a.ptr(0) + j); - } - - for (int j = 0; j < y.cols; j++) { - *(y.ptr(0) + j) = *(Ld.ptr(0) + j); - } - - // 1. Forward substitution L*y = d for y - for (int k = 1; k < n; k++) { - for (int j = 0; j < l.cols; j++) { - *(l.ptr(k - 1) + j) = *(b.ptr(k - 1) + j) / *(m.ptr(k - 1) + j); - } - - for (int j = 0; j < m.cols; j++) { - *(m.ptr(k)+j) = *(a.ptr(k)+j) - *(l.ptr(k - 1) + j)*(*(b.ptr(k - 1) + j)); - } - - for (int j = 0; j < y.cols; j++) { - *(y.ptr(k)+j) = *(Ld.ptr(k)+j) - *(l.ptr(k - 1) + j)*(*(y.ptr(k - 1) + j)); - } - } - - // 2. Backward substitution U*x = y - for (int j = 0; j < y.cols; j++) { - *(x.ptr(n - 1) + j) = (*(y.ptr(n - 1) + j)) / (*(m.ptr(n - 1) + j)); - } - - for (int i = n - 2; i >= 0; i--) { - for (int j = 0; j < x.cols; j++) { - *(x.ptr(i)+j) = (*(y.ptr(i)+j) - (*(b.ptr(i)+j))*(*(x.ptr(i + 1) + j))) / (*(m.ptr(i)+j)); - } - } -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This function computes the angle from the vector given by (X Y). From 0 to 2*Pi - */ -inline float getAngle(const float& x, const float& y) { - - if (x >= 0 && y >= 0) - { - return atan(y / x); - } - - if (x < 0 && y >= 0) { - return (float)CV_PI - atan(-y / x); - } - - if (x < 0 && y < 0) { - return (float)CV_PI + atan(y / x); - } - - if (x >= 0 && y < 0) { - return 2.0f * (float)CV_PI - atan(-y / x); - } - - return 0; -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This function performs descriptor clipping - * @param desc_ Pointer to the descriptor vector - * @param dsize Size of the descriptor vector - * @param iter Number of iterations - * @param ratio Clipping ratio - */ -inline void clippingDescriptor(float *desc, const int& dsize, const int& niter, const float& ratio) { - - float cratio = ratio / sqrtf(static_cast(dsize)); - float len = 0.0; - - for (int i = 0; i < niter; i++) { - len = 0.0; - for (int j = 0; j < dsize; j++) { - if (desc[j] > cratio) { - desc[j] = cratio; - } - else if (desc[j] < -cratio) { - desc[j] = -cratio; - } - len += desc[j] * desc[j]; - } - - // Normalize again - len = sqrt(len); - - for (int j = 0; j < dsize; j++) { - desc[j] = desc[j] / len; - } - } -} - -//************************************************************************************** -//************************************************************************************** - -/** - * @brief This function computes the value of a 2D Gaussian function - * @param x X Position - * @param y Y Position - * @param sig Standard Deviation - */ -inline float gaussian(const float& x, const float& y, const float& sig) { - return exp(-(x*x + y*y) / (2.0f*sig*sig)); -} - -//************************************************************************************** -//************************************************************************************** - -/** - * @brief This function checks descriptor limits - * @param x X Position - * @param y Y Position - * @param width Image width - * @param height Image height - */ -inline void checkDescriptorLimits(int &x, int &y, const int& width, const int& height) { - - if (x < 0) { - x = 0; - } - - if (y < 0) { - y = 0; - } - - if (x > width - 1) { - x = width - 1; - } - - if (y > height - 1) { - y = height - 1; - } -} - -//************************************************************************************* -//************************************************************************************* - -/** - * @brief This funtion rounds float to nearest integer - * @param flt Input float - * @return dst Nearest integer - */ -inline int fRound(const float& flt) -{ - return (int)(flt + 0.5f); } diff --git a/modules/features2d/src/kaze/KAZEFeatures.h b/modules/features2d/src/kaze/KAZEFeatures.h index 81509c47d9..b62f94831e 100644 --- a/modules/features2d/src/kaze/KAZEFeatures.h +++ b/modules/features2d/src/kaze/KAZEFeatures.h @@ -7,84 +7,53 @@ * @author Pablo F. Alcantarilla */ -#ifndef KAZE_H_ -#define KAZE_H_ - -//************************************************************************************* -//************************************************************************************* +#ifndef __OPENCV_FEATURES_2D_KAZE_FEATURES_H__ +#define __OPENCV_FEATURES_2D_KAZE_FEATURES_H__ +/* ************************************************************************* */ // Includes #include "KAZEConfig.h" #include "nldiffusion_functions.h" #include "fed.h" +#include "TEvolution.h" -//************************************************************************************* -//************************************************************************************* - +/* ************************************************************************* */ // KAZE Class Declaration class KAZEFeatures { private: - KAZEOptions options; - - // Parameters of the Nonlinear diffusion class - std::vector evolution_; // Vector of nonlinear diffusion evolution + /// Parameters of the Nonlinear diffusion class + KAZEOptions options_; ///< Configuration options for KAZE + std::vector evolution_; ///< Vector of nonlinear diffusion evolution - // Vector of keypoint vectors for finding extrema in multiple threads + /// Vector of keypoint vectors for finding extrema in multiple threads std::vector > kpts_par_; - // FED parameters - int ncycles_; // Number of cycles - bool reordering_; // Flag for reordering time steps - std::vector > tsteps_; // Vector of FED dynamic time steps - std::vector nsteps_; // Vector of number of steps per cycle - - // Some auxiliary variables used in the AOS step - cv::Mat Ltx_, Lty_, px_, py_, ax_, ay_, bx_, by_, qr_, qc_; + /// FED parameters + int ncycles_; ///< Number of cycles + bool reordering_; ///< Flag for reordering time steps + std::vector > tsteps_; ///< Vector of FED dynamic time steps + std::vector nsteps_; ///< Vector of number of steps per cycle public: - // Constructor + /// Constructor KAZEFeatures(KAZEOptions& options); - // Public methods for KAZE interface + /// Public methods for KAZE interface void Allocate_Memory_Evolution(void); int Create_Nonlinear_Scale_Space(const cv::Mat& img); void Feature_Detection(std::vector& kpts); void Feature_Description(std::vector& kpts, cv::Mat& desc); - static void Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vector& evolution_, const KAZEOptions& options); -private: - - // Feature Detection Methods + /// Feature Detection Methods void Compute_KContrast(const cv::Mat& img, const float& kper); void Compute_Multiscale_Derivatives(void); void Compute_Detector_Response(void); - void Determinant_Hessian_Parallel(std::vector& kpts); - void Find_Extremum_Threading(const int& level); + void Determinant_Hessian(std::vector& kpts); void Do_Subpixel_Refinement(std::vector& kpts); - - // AOS Methods - void AOS_Step_Scalar(cv::Mat &Ld, const cv::Mat &Ldprev, const cv::Mat &c, const float& stepsize); - void AOS_Rows(const cv::Mat &Ldprev, const cv::Mat &c, const float& stepsize); - void AOS_Columns(const cv::Mat &Ldprev, const cv::Mat &c, const float& stepsize); - void Thomas(const cv::Mat &a, const cv::Mat &b, const cv::Mat &Ld, cv::Mat &x); - }; -//************************************************************************************* -//************************************************************************************* - -// Inline functions -float getAngle(const float& x, const float& y); -float gaussian(const float& x, const float& y, const float& sig); -void checkDescriptorLimits(int &x, int &y, const int& width, const int& height); -void clippingDescriptor(float *desc, const int& dsize, const int& niter, const float& ratio); -int fRound(const float& flt); - -//************************************************************************************* -//************************************************************************************* - -#endif // KAZE_H_ +#endif diff --git a/modules/features2d/src/kaze/TEvolution.h b/modules/features2d/src/kaze/TEvolution.h new file mode 100644 index 0000000000..bc7654ef05 --- /dev/null +++ b/modules/features2d/src/kaze/TEvolution.h @@ -0,0 +1,35 @@ +/** + * @file TEvolution.h + * @brief Header file with the declaration of the TEvolution struct + * @date Jun 02, 2014 + * @author Pablo F. Alcantarilla + */ + +#ifndef __OPENCV_FEATURES_2D_TEVOLUTION_H__ +#define __OPENCV_FEATURES_2D_TEVOLUTION_H__ + +/* ************************************************************************* */ +/// KAZE/A-KAZE nonlinear diffusion filtering evolution +struct TEvolution { + + TEvolution() { + etime = 0.0f; + esigma = 0.0f; + octave = 0; + sublevel = 0; + sigma_size = 0; + } + + cv::Mat Lx, Ly; ///< First order spatial derivatives + cv::Mat Lxx, Lxy, Lyy; ///< Second order spatial derivatives + cv::Mat Lt; ///< Evolution image + cv::Mat Lsmooth; ///< Smoothed image + cv::Mat Ldet; ///< Detector response + float etime; ///< Evolution time + float esigma; ///< Evolution sigma. For linear diffusion t = sigma^2 / 2 + int octave; ///< Image octave + int sublevel; ///< Image sublevel in each octave + int sigma_size; ///< Integer esigma. For computing the feature detector responses +}; + +#endif diff --git a/modules/features2d/src/kaze/fed.h b/modules/features2d/src/kaze/fed.h index c313b8134d..0cfa4005c8 100644 --- a/modules/features2d/src/kaze/fed.h +++ b/modules/features2d/src/kaze/fed.h @@ -1,5 +1,5 @@ -#ifndef FED_H -#define FED_H +#ifndef __OPENCV_FEATURES_2D_FED_H__ +#define __OPENCV_FEATURES_2D_FED_H__ //****************************************************************************** //****************************************************************************** @@ -22,4 +22,4 @@ bool fed_is_prime_internal(const int& number); //************************************************************************************* //************************************************************************************* -#endif // FED_H +#endif // __OPENCV_FEATURES_2D_FED_H__ diff --git a/modules/features2d/src/kaze/nldiffusion_functions.h b/modules/features2d/src/kaze/nldiffusion_functions.h index 773f7e4619..5c161a6e7c 100644 --- a/modules/features2d/src/kaze/nldiffusion_functions.h +++ b/modules/features2d/src/kaze/nldiffusion_functions.h @@ -8,8 +8,8 @@ * @author Pablo F. Alcantarilla */ -#ifndef KAZE_NLDIFFUSION_FUNCTIONS_H -#define KAZE_NLDIFFUSION_FUNCTIONS_H +#ifndef __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__ +#define __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__ /* ************************************************************************* */ // Includes diff --git a/modules/features2d/src/kaze/utils.h b/modules/features2d/src/kaze/utils.h new file mode 100644 index 0000000000..13ae352474 --- /dev/null +++ b/modules/features2d/src/kaze/utils.h @@ -0,0 +1,77 @@ +#ifndef __OPENCV_FEATURES_2D_KAZE_UTILS_H__ +#define __OPENCV_FEATURES_2D_KAZE_UTILS_H__ + +/* ************************************************************************* */ +/** + * @brief This function computes the angle from the vector given by (X Y). From 0 to 2*Pi + */ +inline float getAngle(float x, float y) { + + if (x >= 0 && y >= 0) { + return atanf(y / x); + } + + if (x < 0 && y >= 0) { + return static_cast(CV_PI)-atanf(-y / x); + } + + if (x < 0 && y < 0) { + return static_cast(CV_PI)+atanf(y / x); + } + + if (x >= 0 && y < 0) { + return static_cast(2.0 * CV_PI) - atanf(-y / x); + } + + return 0; +} + +/* ************************************************************************* */ +/** + * @brief This function computes the value of a 2D Gaussian function + * @param x X Position + * @param y Y Position + * @param sig Standard Deviation + */ +inline float gaussian(float x, float y, float sigma) { + return expf(-(x*x + y*y) / (2.0f*sigma*sigma)); +} + +/* ************************************************************************* */ +/** + * @brief This function checks descriptor limits + * @param x X Position + * @param y Y Position + * @param width Image width + * @param height Image height + */ +inline void checkDescriptorLimits(int &x, int &y, int width, int height) { + + if (x < 0) { + x = 0; + } + + if (y < 0) { + y = 0; + } + + if (x > width - 1) { + x = width - 1; + } + + if (y > height - 1) { + y = height - 1; + } +} + +/* ************************************************************************* */ +/** + * @brief This funtion rounds float to nearest integer + * @param flt Input float + * @return dst Nearest integer + */ +inline int fRound(float flt) { + return (int)(flt + 0.5f); +} + +#endif diff --git a/modules/features2d/test/test_descriptors_regression.cpp b/modules/features2d/test/test_descriptors_regression.cpp index 7119d590ef..3c6ae97ccc 100644 --- a/modules/features2d/test/test_descriptors_regression.cpp +++ b/modules/features2d/test/test_descriptors_regression.cpp @@ -40,7 +40,6 @@ //M*/ #include "test_precomp.hpp" -#include "opencv2/highgui.hpp" using namespace std; using namespace cv; @@ -102,8 +101,14 @@ public: typedef typename Distance::ResultType DistanceType; CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr& _dextractor, - Distance d = Distance() ): - name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) {} + Distance d = Distance(), Ptr _detector = Ptr()): + name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) , detector(_detector) {} + + ~CV_DescriptorExtractorTest() + { + if(!detector.empty()) + detector.release(); + } protected: virtual void createDescriptorExtractor() {} @@ -190,7 +195,6 @@ protected: // Read the test image. string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; - Mat img = imread( imgFilename ); if( img.empty() ) { @@ -198,13 +202,15 @@ protected: ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); return; } - vector keypoints; FileStorage fs( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::READ ); - if( fs.isOpened() ) - { + if(!detector.empty()) { + detector->detect(img, keypoints); + } else { read( fs.getFirstTopLevelNode(), keypoints ); - + } + if(!keypoints.empty()) + { Mat calcDescriptors; double t = (double)getTickCount(); dextractor->compute( img, keypoints, calcDescriptors ); @@ -245,7 +251,7 @@ protected: } } } - else + if(!fs.isOpened()) { ts->printf( cvtest::TS::LOG, "Compute and write keypoints.\n" ); fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); @@ -296,6 +302,7 @@ protected: const DistanceType maxDist; Ptr dextractor; Distance distance; + Ptr detector; private: CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; } @@ -341,3 +348,19 @@ TEST( Features2d_DescriptorExtractor_OpponentBRIEF, regression ) DescriptorExtractor::create("OpponentBRIEF") ); test.safe_run(); } + +TEST( Features2d_DescriptorExtractor_KAZE, regression ) +{ + CV_DescriptorExtractorTest< L2 > test( "descriptor-kaze", 0.03f, + DescriptorExtractor::create("KAZE"), + L2(), FeatureDetector::create("KAZE")); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_AKAZE, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-akaze", (CV_DescriptorExtractorTest::DistanceType)12.f, + DescriptorExtractor::create("AKAZE"), + Hamming(), FeatureDetector::create("AKAZE")); + test.safe_run(); +} diff --git a/modules/features2d/test/test_detectors_regression.cpp b/modules/features2d/test/test_detectors_regression.cpp index 8f34913a9a..25e2c7f682 100644 --- a/modules/features2d/test/test_detectors_regression.cpp +++ b/modules/features2d/test/test_detectors_regression.cpp @@ -289,6 +289,18 @@ TEST( Features2d_Detector_ORB, regression ) test.safe_run(); } +TEST( Features2d_Detector_KAZE, regression ) +{ + CV_FeatureDetectorTest test( "detector-kaze", FeatureDetector::create("KAZE") ); + test.safe_run(); +} + +TEST( Features2d_Detector_AKAZE, regression ) +{ + CV_FeatureDetectorTest test( "detector-akaze", FeatureDetector::create("AKAZE") ); + test.safe_run(); +} + TEST( Features2d_Detector_GridFAST, regression ) { CV_FeatureDetectorTest test( "detector-grid-fast", FeatureDetector::create("GridFAST") ); diff --git a/modules/features2d/test/test_keypoints.cpp b/modules/features2d/test/test_keypoints.cpp index 6f9a7e1ed3..a9f30b11b0 100644 --- a/modules/features2d/test/test_keypoints.cpp +++ b/modules/features2d/test/test_keypoints.cpp @@ -167,19 +167,17 @@ TEST(Features2d_Detector_Keypoints_Dense, validation) test.safe_run(); } -// FIXIT #2807 Crash on Windows 7 x64 MSVS 2012, Linux Fedora 19 x64 with GCC 4.8.2, Linux Ubuntu 14.04 LTS x64 with GCC 4.8.2 -TEST(Features2d_Detector_Keypoints_KAZE, DISABLED_validation) +TEST(Features2d_Detector_Keypoints_KAZE, validation) { CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.KAZE")); test.safe_run(); } -// FIXIT #2807 Crash on Windows 7 x64 MSVS 2012, Linux Fedora 19 x64 with GCC 4.8.2, Linux Ubuntu 14.04 LTS x64 with GCC 4.8.2 -TEST(Features2d_Detector_Keypoints_AKAZE, DISABLED_validation) +TEST(Features2d_Detector_Keypoints_AKAZE, validation) { - CV_FeatureDetectorKeypointsTest test_kaze(cv::Ptr(new cv::AKAZE(cv::AKAZE::DESCRIPTOR_KAZE))); + CV_FeatureDetectorKeypointsTest test_kaze(cv::Ptr(new cv::AKAZE(cv::DESCRIPTOR_KAZE))); test_kaze.safe_run(); - CV_FeatureDetectorKeypointsTest test_mldb(cv::Ptr(new cv::AKAZE(cv::AKAZE::DESCRIPTOR_MLDB))); + CV_FeatureDetectorKeypointsTest test_mldb(cv::Ptr(new cv::AKAZE(cv::DESCRIPTOR_MLDB))); test_mldb.safe_run(); } diff --git a/modules/features2d/test/test_precomp.hpp b/modules/features2d/test/test_precomp.hpp index 49bc1dfd18..bce72f7296 100644 --- a/modules/features2d/test/test_precomp.hpp +++ b/modules/features2d/test/test_precomp.hpp @@ -12,7 +12,7 @@ #include "opencv2/ts.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/features2d.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" #include #endif diff --git a/modules/features2d/test/test_rotation_and_scale_invariance.cpp b/modules/features2d/test/test_rotation_and_scale_invariance.cpp index 69ba6f0525..fa6a136f34 100644 --- a/modules/features2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/features2d/test/test_rotation_and_scale_invariance.cpp @@ -652,8 +652,7 @@ TEST(Features2d_ScaleInvariance_Detector_BRISK, regression) test.safe_run(); } -// FIXIT #2807 Crash on Windows 7 x64 MSVS 2012, Linux Fedora 19 x64 with GCC 4.8.2, Linux Ubuntu 14.04 LTS x64 with GCC 4.8.2 -TEST(Features2d_ScaleInvariance_Detector_KAZE, DISABLED_regression) +TEST(Features2d_ScaleInvariance_Detector_KAZE, regression) { DetectorScaleInvarianceTest test(Algorithm::create("Feature2D.KAZE"), 0.08f, @@ -661,8 +660,7 @@ TEST(Features2d_ScaleInvariance_Detector_KAZE, DISABLED_regression) test.safe_run(); } -// FIXIT #2807 Crash on Windows 7 x64 MSVS 2012, Linux Fedora 19 x64 with GCC 4.8.2, Linux Ubuntu 14.04 LTS x64 with GCC 4.8.2 -TEST(Features2d_ScaleInvariance_Detector_AKAZE, DISABLED_regression) +TEST(Features2d_ScaleInvariance_Detector_AKAZE, regression) { DetectorScaleInvarianceTest test(Algorithm::create("Feature2D.AKAZE"), 0.08f, diff --git a/modules/flann/include/opencv2/flann/dist.h b/modules/flann/include/opencv2/flann/dist.h index 6a03b48a12..5d941e67fc 100644 --- a/modules/flann/include/opencv2/flann/dist.h +++ b/modules/flann/include/opencv2/flann/dist.h @@ -595,7 +595,7 @@ struct HellingerDistance typedef typename Accumulator::Type ResultType; /** - * Compute the histogram intersection distance + * Compute the Hellinger distance */ template ResultType operator()(Iterator1 a, Iterator2 b, size_t size, ResultType /*worst_dist*/ = -1) const @@ -628,7 +628,8 @@ struct HellingerDistance template inline ResultType accum_dist(const U& a, const V& b, int) const { - return sqrt(static_cast(a)) - sqrt(static_cast(b)); + ResultType diff = sqrt(static_cast(a)) - sqrt(static_cast(b)); + return diff * diff; } }; @@ -729,9 +730,11 @@ struct KL_Divergence inline ResultType accum_dist(const U& a, const V& b, int) const { ResultType result = ResultType(); - ResultType ratio = (ResultType)(a / b); - if (ratio>0) { - result = a * log(ratio); + if( *b != 0 ) { + ResultType ratio = (ResultType)(a / b); + if (ratio>0) { + result = a * log(ratio); + } } return result; } diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index f4a19cffe6..78d6bfb094 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -1,5 +1,5 @@ set(the_description "High-level GUI and Media I/O") -ocv_add_module(highgui opencv_imgproc OPTIONAL opencv_androidcamera) +ocv_add_module(highgui opencv_imgproc opencv_imgcodecs opencv_videoio OPTIONAL opencv_androidcamera) # ---------------------------------------------------------------------------- # CMake file for highgui. See root CMakeLists.txt @@ -7,70 +7,20 @@ ocv_add_module(highgui opencv_imgproc OPTIONAL opencv_androidcamera) # Jose Luis Blanco, 2008 # ---------------------------------------------------------------------------- -ocv_clear_vars(GRFMT_LIBS) - if(HAVE_WINRT_CX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") endif() -if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR) +if(APPLE) ocv_include_directories(${ZLIB_INCLUDE_DIRS}) - list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES}) -endif() - -if(HAVE_JPEG) - ocv_include_directories(${JPEG_INCLUDE_DIR}) - list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES}) -endif() - -if(WITH_WEBP) - add_definitions(-DHAVE_WEBP) - ocv_include_directories(${WEBP_INCLUDE_DIR}) - list(APPEND GRFMT_LIBS ${WEBP_LIBRARIES}) -endif() - -if(HAVE_PNG) - add_definitions(${PNG_DEFINITIONS}) - ocv_include_directories(${PNG_INCLUDE_DIR}) - list(APPEND GRFMT_LIBS ${PNG_LIBRARIES}) + list(APPEND HIGHGUI_LIBRARIES ${ZLIB_LIBRARIES}) endif() -if(HAVE_TIFF) - ocv_include_directories(${TIFF_INCLUDE_DIR}) - list(APPEND GRFMT_LIBS ${TIFF_LIBRARIES}) -endif() - -if(HAVE_JASPER) - ocv_include_directories(${JASPER_INCLUDE_DIR}) - list(APPEND GRFMT_LIBS ${JASPER_LIBRARIES}) -endif() - -if(HAVE_OPENEXR) - include_directories(SYSTEM ${OPENEXR_INCLUDE_PATHS}) - list(APPEND GRFMT_LIBS ${OPENEXR_LIBRARIES}) -endif() - -file(GLOB grfmt_hdrs src/grfmt*.hpp) -file(GLOB grfmt_srcs src/grfmt*.cpp) -list(APPEND grfmt_hdrs src/bitstrm.hpp) -list(APPEND grfmt_srcs src/bitstrm.cpp) -list(APPEND grfmt_hdrs src/rgbe.hpp) -list(APPEND grfmt_srcs src/rgbe.cpp) - -source_group("Src\\grfmts" FILES ${grfmt_hdrs} ${grfmt_srcs}) - set(highgui_hdrs src/precomp.hpp - src/utils.hpp - src/cap_ffmpeg_impl.hpp ) set(highgui_srcs - src/cap.cpp - src/cap_images.cpp - src/cap_ffmpeg.cpp - src/loadsave.cpp - src/utils.cpp src/window.cpp ) @@ -122,128 +72,6 @@ elseif(HAVE_COCOA) list(APPEND HIGHGUI_LIBRARIES "-framework Cocoa") endif() -if(WIN32 AND NOT ARM) - list(APPEND highgui_srcs src/cap_cmu.cpp) -endif() - -if (WIN32 AND HAVE_DSHOW) - list(APPEND highgui_srcs src/cap_dshow.cpp) -endif() - -if (WIN32 AND HAVE_MSMF) - list(APPEND highgui_srcs src/cap_msmf.cpp) -endif() - -if (WIN32 AND HAVE_VFW) - list(APPEND highgui_srcs src/cap_vfw.cpp) -endif() - -if(HAVE_XINE) - list(APPEND highgui_srcs src/cap_xine.cpp) -endif(HAVE_XINE) - -if(HAVE_DC1394_2) - list(APPEND highgui_srcs src/cap_dc1394_v2.cpp) -endif(HAVE_DC1394_2) - -if(HAVE_DC1394) - list(APPEND highgui_srcs src/cap_dc1394.cpp) -endif(HAVE_DC1394) - -if(HAVE_GSTREAMER) - list(APPEND highgui_srcs src/cap_gstreamer.cpp) -endif(HAVE_GSTREAMER) - -if(HAVE_UNICAP) - list(APPEND highgui_srcs src/cap_unicap.cpp) -endif(HAVE_UNICAP) - -if(HAVE_LIBV4L) - list(APPEND highgui_srcs src/cap_libv4l.cpp) -elseif(HAVE_CAMV4L OR HAVE_CAMV4L2 OR HAVE_VIDEOIO) - list(APPEND highgui_srcs src/cap_v4l.cpp) -endif() - -if(HAVE_OPENNI) - list(APPEND highgui_srcs src/cap_openni.cpp) - ocv_include_directories(${OPENNI_INCLUDE_DIR}) - list(APPEND HIGHGUI_LIBRARIES ${OPENNI_LIBRARY}) -endif(HAVE_OPENNI) - -if(HAVE_opencv_androidcamera) - list(APPEND highgui_srcs src/cap_android.cpp) - add_definitions(-DHAVE_ANDROID_NATIVE_CAMERA)#TODO: remove this line -endif(HAVE_opencv_androidcamera) - -if(HAVE_XIMEA) - list(APPEND highgui_srcs src/cap_ximea.cpp) - ocv_include_directories(${XIMEA_PATH}) - if(XIMEA_LIBRARY_DIR) - link_directories("${XIMEA_LIBRARY_DIR}") - endif() - if(X86_64) - list(APPEND HIGHGUI_LIBRARIES m3apiX64) - else() - list(APPEND HIGHGUI_LIBRARIES m3api) - endif() -endif(HAVE_XIMEA) - -if(HAVE_FFMPEG) - if(UNIX AND BZIP2_LIBRARIES) - list(APPEND HIGHGUI_LIBRARIES ${BZIP2_LIBRARIES}) - endif() - if(APPLE) - list(APPEND HIGHGUI_LIBRARIES "-framework VideoDecodeAcceleration" bz2) - endif() -endif(HAVE_FFMPEG) - -if(HAVE_PVAPI) - add_definitions(-DHAVE_PVAPI) - add_definitions(${PVAPI_DEFINITIONS}) - ocv_include_directories(${PVAPI_INCLUDE_PATH}) - set(highgui_srcs src/cap_pvapi.cpp ${highgui_srcs}) - list(APPEND HIGHGUI_LIBRARIES ${PVAPI_LIBRARY}) -endif() - -if(HAVE_GIGE_API) - add_definitions(-DHAVE_GIGE_API) - ocv_include_directories(${GIGEAPI_INCLUDE_PATH}) - set(highgui_srcs src/cap_giganetix.cpp ${highgui_srcs}) - list(APPEND HIGHGUI_LIBRARIES ${GIGEAPI_LIBRARIES}) - list(APPEND highgui_srcs src/cap_giganetix.cpp) -endif(HAVE_GIGE_API) - -if(HAVE_AVFOUNDATION) - list(APPEND highgui_srcs src/cap_avfoundation.mm) - list(APPEND HIGHGUI_LIBRARIES "-framework AVFoundation" "-framework QuartzCore") -endif() - -if(HAVE_QUICKTIME) - list(APPEND highgui_srcs src/cap_qt.cpp) - list(APPEND HIGHGUI_LIBRARIES "-framework Carbon" "-framework QuickTime" "-framework CoreFoundation" "-framework QuartzCore") -elseif(HAVE_QTKIT) - list(APPEND highgui_srcs src/cap_qtkit.mm) - list(APPEND HIGHGUI_LIBRARIES "-framework QTKit" "-framework QuartzCore" "-framework AppKit") -endif() - -if(HAVE_INTELPERC) - list(APPEND highgui_srcs src/cap_intelperc.cpp) - ocv_include_directories(${INTELPERC_INCLUDE_DIR}) - list(APPEND HIGHGUI_LIBRARIES ${INTELPERC_LIBRARIES}) -endif(HAVE_INTELPERC) - -if(IOS) - add_definitions(-DHAVE_IOS=1) - list(APPEND highgui_srcs src/ios_conversions.mm src/cap_ios_abstract_camera.mm src/cap_ios_photo_camera.mm src/cap_ios_video_camera.mm) - list(APPEND HIGHGUI_LIBRARIES "-framework Accelerate" "-framework AVFoundation" "-framework CoreGraphics" "-framework CoreImage" "-framework CoreMedia" "-framework CoreVideo" "-framework QuartzCore" "-framework AssetsLibrary") -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 ${HIGHGUI_INCLUDE_DIRS}) @@ -257,10 +85,10 @@ endif() source_group("Src" FILES ${highgui_srcs} ${highgui_hdrs}) source_group("Include" FILES ${highgui_ext_hdrs}) -ocv_set_module_sources(HEADERS ${highgui_ext_hdrs} SOURCES ${highgui_srcs} ${highgui_hdrs} ${grfmt_srcs} ${grfmt_hdrs}) +ocv_set_module_sources(HEADERS ${highgui_ext_hdrs} SOURCES ${highgui_srcs} ${highgui_hdrs}) ocv_module_include_directories() -ocv_create_module(${GRFMT_LIBS} ${HIGHGUI_LIBRARIES}) +ocv_create_module(${HIGHGUI_LIBRARIES}) if(APPLE) ocv_check_flag_support(OBJCXX "-fobjc-exceptions" HAVE_OBJC_EXCEPTIONS) @@ -294,33 +122,5 @@ set_target_properties(${the_module} PROPERTIES LINK_INTERFACE_LIBRARIES "") ocv_add_precompiled_headers(${the_module}) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-deprecated-declarations) -if(WIN32 AND WITH_FFMPEG) - #copy ffmpeg dll to the output folder - if(MSVC64 OR MINGW64) - set(FFMPEG_SUFFIX _64) - endif() - - set(ffmpeg_bare_name "opencv_ffmpeg${FFMPEG_SUFFIX}.dll") - set(ffmpeg_bare_name_ver "opencv_ffmpeg${OPENCV_DLLVERSION}${FFMPEG_SUFFIX}.dll") - set(ffmpeg_path "${OpenCV_SOURCE_DIR}/3rdparty/ffmpeg/${ffmpeg_bare_name}") - - if(MSVC_IDE) - add_custom_command(TARGET ${the_module} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/Release/${ffmpeg_bare_name_ver}" - COMMAND ${CMAKE_COMMAND} -E copy "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/Debug/${ffmpeg_bare_name_ver}" - COMMENT "Copying ${ffmpeg_path} to the output directory") - elseif(MSVC AND (CMAKE_GENERATOR MATCHES "Visual")) - add_custom_command(TARGET ${the_module} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}/${ffmpeg_bare_name_ver}" - COMMENT "Copying ${ffmpeg_path} to the output directory") - else() - add_custom_command(TARGET ${the_module} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy "${ffmpeg_path}" "${EXECUTABLE_OUTPUT_PATH}/${ffmpeg_bare_name_ver}" - COMMENT "Copying ${ffmpeg_path} to the output directory") - endif() - - install(FILES "${ffmpeg_path}" DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs RENAME "${ffmpeg_bare_name_ver}") -endif() - ocv_add_accuracy_tests() ocv_add_perf_tests() diff --git a/modules/highgui/doc/highgui.rst b/modules/highgui/doc/highgui.rst index 02beddbafb..b42ac7a8ef 100644 --- a/modules/highgui/doc/highgui.rst +++ b/modules/highgui/doc/highgui.rst @@ -9,12 +9,9 @@ It provides easy interface to: * Create and manipulate windows that can display images and "remember" their content (no need to handle repaint events from OS). * Add trackbars to the windows, handle simple mouse events as well as keyboard commands. -* Read and write images to/from disk or memory. -* Read video from camera or file and write video to a file. .. toctree:: :maxdepth: 2 user_interface - reading_and_writing_images_and_video qt_new_functions diff --git a/modules/highgui/doc/user_interface.rst b/modules/highgui/doc/user_interface.rst index 0d0ccde946..565ea6d449 100644 --- a/modules/highgui/doc/user_interface.rst +++ b/modules/highgui/doc/user_interface.rst @@ -83,6 +83,9 @@ If window was created with OpenGL support, ``imshow`` also support :ocv:class:`o .. note:: This function should be followed by ``waitKey`` function which displays the image for specified milliseconds. Otherwise, it won't display the image. For example, ``waitKey(0)`` will display the window infinitely until any keypress (it is suitable for image display). ``waitKey(25)`` will display a frame for 25 ms, after which display will be automatically closed. (If you put it in a loop to read videos, it will display the video frame-by-frame) +.. note:: + + [Windows Backend Only] Pressing Ctrl+C will copy the image to the clipboard. namedWindow --------------- diff --git a/modules/highgui/include/opencv2/highgui.hpp b/modules/highgui/include/opencv2/highgui.hpp index f05825f784..aef9105f74 100644 --- a/modules/highgui/include/opencv2/highgui.hpp +++ b/modules/highgui/include/opencv2/highgui.hpp @@ -44,6 +44,8 @@ #define __OPENCV_HIGHGUI_HPP__ #include "opencv2/core.hpp" +#include "opencv2/imgcodecs.hpp" +#include "opencv2/videoio.hpp" ///////////////////////// graphical user interface ////////////////////////// @@ -201,392 +203,4 @@ CV_EXPORTS int createButton( const String& bar_name, ButtonCallback on_change, bool initial_button_state = false); } // cv - - - -//////////////////////////////// image codec //////////////////////////////// -namespace cv -{ - -enum { IMREAD_UNCHANGED = -1, // 8bit, color or not - IMREAD_GRAYSCALE = 0, // 8bit, gray - IMREAD_COLOR = 1, // ?, color - IMREAD_ANYDEPTH = 2, // any depth, ? - IMREAD_ANYCOLOR = 4 // ?, any color - }; - -enum { IMWRITE_JPEG_QUALITY = 1, - IMWRITE_JPEG_PROGRESSIVE = 2, - IMWRITE_JPEG_OPTIMIZE = 3, - IMWRITE_PNG_COMPRESSION = 16, - IMWRITE_PNG_STRATEGY = 17, - IMWRITE_PNG_BILEVEL = 18, - IMWRITE_PXM_BINARY = 32, - IMWRITE_WEBP_QUALITY = 64 - }; - -enum { IMWRITE_PNG_STRATEGY_DEFAULT = 0, - IMWRITE_PNG_STRATEGY_FILTERED = 1, - IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY = 2, - IMWRITE_PNG_STRATEGY_RLE = 3, - IMWRITE_PNG_STRATEGY_FIXED = 4 - }; - -CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR ); - -CV_EXPORTS_W bool imwrite( const String& filename, InputArray img, - const std::vector& params = std::vector()); - -CV_EXPORTS_W Mat imdecode( InputArray buf, int flags ); - -CV_EXPORTS Mat imdecode( InputArray buf, int flags, Mat* dst); - -CV_EXPORTS_W bool imencode( const String& ext, InputArray img, - CV_OUT std::vector& buf, - const std::vector& params = std::vector()); - -} // cv - - - -////////////////////////////////// video io ///////////////////////////////// - -typedef struct CvCapture CvCapture; -typedef struct CvVideoWriter CvVideoWriter; - -namespace cv -{ - -// Camera API -enum { CAP_ANY = 0, // autodetect - CAP_VFW = 200, // platform native - CAP_V4L = 200, - CAP_V4L2 = CAP_V4L, - CAP_FIREWARE = 300, // IEEE 1394 drivers - CAP_FIREWIRE = CAP_FIREWARE, - CAP_IEEE1394 = CAP_FIREWARE, - CAP_DC1394 = CAP_FIREWARE, - CAP_CMU1394 = CAP_FIREWARE, - CAP_QT = 500, // QuickTime - CAP_UNICAP = 600, // Unicap drivers - CAP_DSHOW = 700, // DirectShow (via videoInput) - CAP_PVAPI = 800, // PvAPI, Prosilica GigE SDK - CAP_OPENNI = 900, // OpenNI (for Kinect) - CAP_OPENNI_ASUS = 910, // OpenNI (for Asus Xtion) - CAP_ANDROID = 1000, // Android - CAP_XIAPI = 1100, // XIMEA Camera API - CAP_AVFOUNDATION = 1200, // AVFoundation framework for iOS (OS X Lion will have the same API) - CAP_GIGANETIX = 1300, // Smartek Giganetix GigEVisionSDK - CAP_MSMF = 1400, // Microsoft Media Foundation (via videoInput) - CAP_INTELPERC = 1500 // Intel Perceptual Computing SDK - }; - -// generic properties (based on DC1394 properties) -enum { CAP_PROP_POS_MSEC =0, - CAP_PROP_POS_FRAMES =1, - CAP_PROP_POS_AVI_RATIO =2, - CAP_PROP_FRAME_WIDTH =3, - CAP_PROP_FRAME_HEIGHT =4, - CAP_PROP_FPS =5, - CAP_PROP_FOURCC =6, - CAP_PROP_FRAME_COUNT =7, - CAP_PROP_FORMAT =8, - CAP_PROP_MODE =9, - CAP_PROP_BRIGHTNESS =10, - CAP_PROP_CONTRAST =11, - CAP_PROP_SATURATION =12, - CAP_PROP_HUE =13, - CAP_PROP_GAIN =14, - CAP_PROP_EXPOSURE =15, - CAP_PROP_CONVERT_RGB =16, - CAP_PROP_WHITE_BALANCE_BLUE_U =17, - CAP_PROP_RECTIFICATION =18, - CAP_PROP_MONOCROME =19, - CAP_PROP_SHARPNESS =20, - CAP_PROP_AUTO_EXPOSURE =21, // DC1394: exposure control done by camera, user can adjust refernce level using this feature - CAP_PROP_GAMMA =22, - CAP_PROP_TEMPERATURE =23, - CAP_PROP_TRIGGER =24, - CAP_PROP_TRIGGER_DELAY =25, - CAP_PROP_WHITE_BALANCE_RED_V =26, - CAP_PROP_ZOOM =27, - CAP_PROP_FOCUS =28, - CAP_PROP_GUID =29, - CAP_PROP_ISO_SPEED =30, - CAP_PROP_BACKLIGHT =32, - CAP_PROP_PAN =33, - CAP_PROP_TILT =34, - CAP_PROP_ROLL =35, - CAP_PROP_IRIS =36, - CAP_PROP_SETTINGS =37 - }; - - -// DC1394 only -// modes of the controlling registers (can be: auto, manual, auto single push, absolute Latter allowed with any other mode) -// every feature can have only one mode turned on at a time -enum { CAP_PROP_DC1394_OFF = -4, //turn the feature off (not controlled manually nor automatically) - CAP_PROP_DC1394_MODE_MANUAL = -3, //set automatically when a value of the feature is set by the user - CAP_PROP_DC1394_MODE_AUTO = -2, - CAP_PROP_DC1394_MODE_ONE_PUSH_AUTO = -1, - CAP_PROP_DC1394_MAX = 31 - }; - - -// OpenNI map generators -enum { CAP_OPENNI_DEPTH_GENERATOR = 1 << 31, - CAP_OPENNI_IMAGE_GENERATOR = 1 << 30, - CAP_OPENNI_GENERATORS_MASK = CAP_OPENNI_DEPTH_GENERATOR + CAP_OPENNI_IMAGE_GENERATOR - }; - -// Properties of cameras available through OpenNI interfaces -enum { CAP_PROP_OPENNI_OUTPUT_MODE = 100, - CAP_PROP_OPENNI_FRAME_MAX_DEPTH = 101, // in mm - CAP_PROP_OPENNI_BASELINE = 102, // in mm - CAP_PROP_OPENNI_FOCAL_LENGTH = 103, // in pixels - CAP_PROP_OPENNI_REGISTRATION = 104, // flag that synchronizes the remapping depth map to image map - // by changing depth generator's view point (if the flag is "on") or - // sets this view point to its normal one (if the flag is "off"). - CAP_PROP_OPENNI_REGISTRATION_ON = CAP_PROP_OPENNI_REGISTRATION, - CAP_PROP_OPENNI_APPROX_FRAME_SYNC = 105, - CAP_PROP_OPENNI_MAX_BUFFER_SIZE = 106, - CAP_PROP_OPENNI_CIRCLE_BUFFER = 107, - CAP_PROP_OPENNI_MAX_TIME_DURATION = 108, - CAP_PROP_OPENNI_GENERATOR_PRESENT = 109 - }; - -// OpenNI shortcats -enum { CAP_OPENNI_IMAGE_GENERATOR_PRESENT = CAP_OPENNI_IMAGE_GENERATOR + CAP_PROP_OPENNI_GENERATOR_PRESENT, - CAP_OPENNI_IMAGE_GENERATOR_OUTPUT_MODE = CAP_OPENNI_IMAGE_GENERATOR + CAP_PROP_OPENNI_OUTPUT_MODE, - CAP_OPENNI_DEPTH_GENERATOR_BASELINE = CAP_OPENNI_DEPTH_GENERATOR + CAP_PROP_OPENNI_BASELINE, - CAP_OPENNI_DEPTH_GENERATOR_FOCAL_LENGTH = CAP_OPENNI_DEPTH_GENERATOR + CAP_PROP_OPENNI_FOCAL_LENGTH, - CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION = CAP_OPENNI_DEPTH_GENERATOR + CAP_PROP_OPENNI_REGISTRATION, - CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION_ON = CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION - }; - -// OpenNI data given from depth generator -enum { CAP_OPENNI_DEPTH_MAP = 0, // Depth values in mm (CV_16UC1) - CAP_OPENNI_POINT_CLOUD_MAP = 1, // XYZ in meters (CV_32FC3) - CAP_OPENNI_DISPARITY_MAP = 2, // Disparity in pixels (CV_8UC1) - CAP_OPENNI_DISPARITY_MAP_32F = 3, // Disparity in pixels (CV_32FC1) - CAP_OPENNI_VALID_DEPTH_MASK = 4, // CV_8UC1 - - // Data given from RGB image generator - CAP_OPENNI_BGR_IMAGE = 5, - CAP_OPENNI_GRAY_IMAGE = 6 - }; - -// Supported output modes of OpenNI image generator -enum { CAP_OPENNI_VGA_30HZ = 0, - CAP_OPENNI_SXGA_15HZ = 1, - CAP_OPENNI_SXGA_30HZ = 2, - CAP_OPENNI_QVGA_30HZ = 3, - CAP_OPENNI_QVGA_60HZ = 4 - }; - - -// GStreamer -enum { CAP_PROP_GSTREAMER_QUEUE_LENGTH = 200 // default is 1 - }; - - -// PVAPI -enum { CAP_PROP_PVAPI_MULTICASTIP = 300, // ip for anable multicast master mode. 0 for disable multicast - CAP_PROP_PVAPI_FRAMESTARTTRIGGERMODE = 301 // FrameStartTriggerMode: Determines how a frame is initiated - }; - -// PVAPI: FrameStartTriggerMode -enum { CAP_PVAPI_FSTRIGMODE_FREERUN = 0, // Freerun - CAP_PVAPI_FSTRIGMODE_SYNCIN1 = 1, // SyncIn1 - CAP_PVAPI_FSTRIGMODE_SYNCIN2 = 2, // SyncIn2 - CAP_PVAPI_FSTRIGMODE_FIXEDRATE = 3, // FixedRate - CAP_PVAPI_FSTRIGMODE_SOFTWARE = 4 // Software - }; - -// Properties of cameras available through XIMEA SDK interface -enum { CAP_PROP_XI_DOWNSAMPLING = 400, // Change image resolution by binning or skipping. - CAP_PROP_XI_DATA_FORMAT = 401, // Output data format. - CAP_PROP_XI_OFFSET_X = 402, // Horizontal offset from the origin to the area of interest (in pixels). - CAP_PROP_XI_OFFSET_Y = 403, // Vertical offset from the origin to the area of interest (in pixels). - CAP_PROP_XI_TRG_SOURCE = 404, // Defines source of trigger. - CAP_PROP_XI_TRG_SOFTWARE = 405, // Generates an internal trigger. PRM_TRG_SOURCE must be set to TRG_SOFTWARE. - CAP_PROP_XI_GPI_SELECTOR = 406, // Selects general purpose input - CAP_PROP_XI_GPI_MODE = 407, // Set general purpose input mode - CAP_PROP_XI_GPI_LEVEL = 408, // Get general purpose level - CAP_PROP_XI_GPO_SELECTOR = 409, // Selects general purpose output - CAP_PROP_XI_GPO_MODE = 410, // Set general purpose output mode - CAP_PROP_XI_LED_SELECTOR = 411, // Selects camera signalling LED - CAP_PROP_XI_LED_MODE = 412, // Define camera signalling LED functionality - CAP_PROP_XI_MANUAL_WB = 413, // Calculates White Balance(must be called during acquisition) - CAP_PROP_XI_AUTO_WB = 414, // Automatic white balance - CAP_PROP_XI_AEAG = 415, // Automatic exposure/gain - CAP_PROP_XI_EXP_PRIORITY = 416, // Exposure priority (0.5 - exposure 50%, gain 50%). - CAP_PROP_XI_AE_MAX_LIMIT = 417, // Maximum limit of exposure in AEAG procedure - CAP_PROP_XI_AG_MAX_LIMIT = 418, // Maximum limit of gain in AEAG procedure - CAP_PROP_XI_AEAG_LEVEL = 419, // Average intensity of output signal AEAG should achieve(in %) - CAP_PROP_XI_TIMEOUT = 420 // Image capture timeout in milliseconds - }; - - -// Properties for Android cameras -enum { CAP_PROP_ANDROID_AUTOGRAB = 1024, - CAP_PROP_ANDROID_PREVIEW_SIZES_STRING = 1025, // readonly, tricky property, returns const char* indeed - CAP_PROP_ANDROID_PREVIEW_FORMAT = 1026, // readonly, tricky property, returns const char* indeed - CAP_PROP_ANDROID_FLASH_MODE = 8001, - CAP_PROP_ANDROID_FOCUS_MODE = 8002, - CAP_PROP_ANDROID_WHITE_BALANCE = 8003, - CAP_PROP_ANDROID_ANTIBANDING = 8004, - CAP_PROP_ANDROID_FOCAL_LENGTH = 8005, - CAP_PROP_ANDROID_FOCUS_DISTANCE_NEAR = 8006, - CAP_PROP_ANDROID_FOCUS_DISTANCE_OPTIMAL = 8007, - CAP_PROP_ANDROID_FOCUS_DISTANCE_FAR = 8008 - }; - - -// Android camera output formats -enum { CAP_ANDROID_COLOR_FRAME_BGR = 0, //BGR - CAP_ANDROID_COLOR_FRAME = CAP_ANDROID_COLOR_FRAME_BGR, - CAP_ANDROID_GREY_FRAME = 1, //Y - CAP_ANDROID_COLOR_FRAME_RGB = 2, - CAP_ANDROID_COLOR_FRAME_BGRA = 3, - CAP_ANDROID_COLOR_FRAME_RGBA = 4 - }; - - -// Android camera flash modes -enum { CAP_ANDROID_FLASH_MODE_AUTO = 0, - CAP_ANDROID_FLASH_MODE_OFF = 1, - CAP_ANDROID_FLASH_MODE_ON = 2, - CAP_ANDROID_FLASH_MODE_RED_EYE = 3, - CAP_ANDROID_FLASH_MODE_TORCH = 4 - }; - - -// Android camera focus modes -enum { CAP_ANDROID_FOCUS_MODE_AUTO = 0, - CAP_ANDROID_FOCUS_MODE_CONTINUOUS_VIDEO = 1, - CAP_ANDROID_FOCUS_MODE_EDOF = 2, - CAP_ANDROID_FOCUS_MODE_FIXED = 3, - CAP_ANDROID_FOCUS_MODE_INFINITY = 4, - CAP_ANDROID_FOCUS_MODE_MACRO = 5 - }; - - -// Android camera white balance modes -enum { CAP_ANDROID_WHITE_BALANCE_AUTO = 0, - CAP_ANDROID_WHITE_BALANCE_CLOUDY_DAYLIGHT = 1, - CAP_ANDROID_WHITE_BALANCE_DAYLIGHT = 2, - CAP_ANDROID_WHITE_BALANCE_FLUORESCENT = 3, - CAP_ANDROID_WHITE_BALANCE_INCANDESCENT = 4, - CAP_ANDROID_WHITE_BALANCE_SHADE = 5, - CAP_ANDROID_WHITE_BALANCE_TWILIGHT = 6, - CAP_ANDROID_WHITE_BALANCE_WARM_FLUORESCENT = 7 - }; - - -// Android camera antibanding modes -enum { CAP_ANDROID_ANTIBANDING_50HZ = 0, - CAP_ANDROID_ANTIBANDING_60HZ = 1, - CAP_ANDROID_ANTIBANDING_AUTO = 2, - CAP_ANDROID_ANTIBANDING_OFF = 3 - }; - - -// Properties of cameras available through AVFOUNDATION interface -enum { CAP_PROP_IOS_DEVICE_FOCUS = 9001, - CAP_PROP_IOS_DEVICE_EXPOSURE = 9002, - CAP_PROP_IOS_DEVICE_FLASH = 9003, - CAP_PROP_IOS_DEVICE_WHITEBALANCE = 9004, - CAP_PROP_IOS_DEVICE_TORCH = 9005 - }; - - -// Properties of cameras available through Smartek Giganetix Ethernet Vision interface -/* --- Vladimir Litvinenko (litvinenko.vladimir@gmail.com) --- */ -enum { CAP_PROP_GIGA_FRAME_OFFSET_X = 10001, - CAP_PROP_GIGA_FRAME_OFFSET_Y = 10002, - CAP_PROP_GIGA_FRAME_WIDTH_MAX = 10003, - CAP_PROP_GIGA_FRAME_HEIGH_MAX = 10004, - CAP_PROP_GIGA_FRAME_SENS_WIDTH = 10005, - CAP_PROP_GIGA_FRAME_SENS_HEIGH = 10006 - }; - -enum { CAP_PROP_INTELPERC_PROFILE_COUNT = 11001, - CAP_PROP_INTELPERC_PROFILE_IDX = 11002, - CAP_PROP_INTELPERC_DEPTH_LOW_CONFIDENCE_VALUE = 11003, - CAP_PROP_INTELPERC_DEPTH_SATURATION_VALUE = 11004, - CAP_PROP_INTELPERC_DEPTH_CONFIDENCE_THRESHOLD = 11005, - CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_HORZ = 11006, - CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_VERT = 11007 - }; - -// Intel PerC streams -enum { CAP_INTELPERC_DEPTH_GENERATOR = 1 << 29, - CAP_INTELPERC_IMAGE_GENERATOR = 1 << 28, - CAP_INTELPERC_GENERATORS_MASK = CAP_INTELPERC_DEPTH_GENERATOR + CAP_INTELPERC_IMAGE_GENERATOR - }; - -enum { CAP_INTELPERC_DEPTH_MAP = 0, // Each pixel is a 16-bit integer. The value indicates the distance from an object to the camera's XY plane or the Cartesian depth. - CAP_INTELPERC_UVDEPTH_MAP = 1, // Each pixel contains two 32-bit floating point values in the range of 0-1, representing the mapping of depth coordinates to the color coordinates. - CAP_INTELPERC_IR_MAP = 2, // Each pixel is a 16-bit integer. The value indicates the intensity of the reflected laser beam. - CAP_INTELPERC_IMAGE = 3 - }; - - -class IVideoCapture; -class CV_EXPORTS_W VideoCapture -{ -public: - CV_WRAP VideoCapture(); - CV_WRAP VideoCapture(const String& filename); - CV_WRAP VideoCapture(int device); - - virtual ~VideoCapture(); - CV_WRAP virtual bool open(const String& filename); - CV_WRAP virtual bool open(int device); - CV_WRAP virtual bool isOpened() const; - CV_WRAP virtual void release(); - - CV_WRAP virtual bool grab(); - CV_WRAP virtual bool retrieve(OutputArray image, int flag = 0); - virtual VideoCapture& operator >> (CV_OUT Mat& image); - virtual VideoCapture& operator >> (CV_OUT UMat& image); - CV_WRAP virtual bool read(OutputArray image); - - CV_WRAP virtual bool set(int propId, double value); - CV_WRAP virtual double get(int propId); - -protected: - Ptr cap; - Ptr icap; -private: - static Ptr createCameraCapture(int index); -}; - -class CV_EXPORTS_W VideoWriter -{ -public: - CV_WRAP VideoWriter(); - CV_WRAP VideoWriter(const String& filename, int fourcc, double fps, - Size frameSize, bool isColor = true); - - virtual ~VideoWriter(); - CV_WRAP virtual bool open(const String& filename, int fourcc, double fps, - Size frameSize, bool isColor = true); - CV_WRAP virtual bool isOpened() const; - CV_WRAP virtual void release(); - virtual VideoWriter& operator << (const Mat& image); - CV_WRAP virtual void write(const Mat& image); - - CV_WRAP static int fourcc(char c1, char c2, char c3, char c4); - -protected: - Ptr writer; -}; - -template<> CV_EXPORTS void DefaultDeleter::operator ()(CvCapture* obj) const; -template<> CV_EXPORTS void DefaultDeleter::operator ()(CvVideoWriter* obj) const; - -} // cv - #endif diff --git a/modules/highgui/include/opencv2/highgui/highgui_c.h b/modules/highgui/include/opencv2/highgui/highgui_c.h index 130302150f..b6b56ceb02 100644 --- a/modules/highgui/include/opencv2/highgui/highgui_c.h +++ b/modules/highgui/include/opencv2/highgui/highgui_c.h @@ -43,6 +43,8 @@ #define __OPENCV_HIGHGUI_H__ #include "opencv2/core/core_c.h" +#include "opencv2/imgcodecs/imgcodecs_c.h" +#include "opencv2/videoio/videoio_c.h" #ifdef __cplusplus extern "C" { @@ -194,67 +196,6 @@ typedef void (CV_CDECL *CvMouseCallback )(int event, int x, int y, int flags, vo CVAPI(void) cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param CV_DEFAULT(NULL)); -enum -{ -/* 8bit, color or not */ - CV_LOAD_IMAGE_UNCHANGED =-1, -/* 8bit, gray */ - CV_LOAD_IMAGE_GRAYSCALE =0, -/* ?, color */ - CV_LOAD_IMAGE_COLOR =1, -/* any depth, ? */ - CV_LOAD_IMAGE_ANYDEPTH =2, -/* ?, any color */ - CV_LOAD_IMAGE_ANYCOLOR =4 -}; - -/* load image from file - iscolor can be a combination of above flags where CV_LOAD_IMAGE_UNCHANGED - overrides the other flags - using CV_LOAD_IMAGE_ANYCOLOR alone is equivalent to CV_LOAD_IMAGE_UNCHANGED - unless CV_LOAD_IMAGE_ANYDEPTH is specified images are converted to 8bit -*/ -CVAPI(IplImage*) cvLoadImage( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); -CVAPI(CvMat*) cvLoadImageM( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); - -enum -{ - CV_IMWRITE_JPEG_QUALITY =1, - CV_IMWRITE_JPEG_PROGRESSIVE =2, - CV_IMWRITE_JPEG_OPTIMIZE =3, - CV_IMWRITE_PNG_COMPRESSION =16, - CV_IMWRITE_PNG_STRATEGY =17, - CV_IMWRITE_PNG_BILEVEL =18, - CV_IMWRITE_PNG_STRATEGY_DEFAULT =0, - CV_IMWRITE_PNG_STRATEGY_FILTERED =1, - CV_IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =2, - CV_IMWRITE_PNG_STRATEGY_RLE =3, - CV_IMWRITE_PNG_STRATEGY_FIXED =4, - CV_IMWRITE_PXM_BINARY =32, - CV_IMWRITE_WEBP_QUALITY =64 -}; - -/* save image to file */ -CVAPI(int) cvSaveImage( const char* filename, const CvArr* image, - const int* params CV_DEFAULT(0) ); - -/* decode image stored in the buffer */ -CVAPI(IplImage*) cvDecodeImage( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); -CVAPI(CvMat*) cvDecodeImageM( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); - -/* encode image and store the result as a byte vector (single-row 8uC1 matrix) */ -CVAPI(CvMat*) cvEncodeImage( const char* ext, const CvArr* image, - const int* params CV_DEFAULT(0) ); - -enum -{ - CV_CVTIMG_FLIP =1, - CV_CVTIMG_SWAP_RB =2 -}; - -/* utility function: convert one image to another with optional vertical flip */ -CVAPI(void) cvConvertImage( const CvArr* src, CvArr* dst, int flags CV_DEFAULT(0)); - /* wait for key event infinitely (delay<=0) or for "delay" milliseconds */ CVAPI(int) cvWaitKey(int delay CV_DEFAULT(0)); @@ -268,363 +209,10 @@ CVAPI(void) cvUpdateWindow(const char* window_name); /****************************************************************************************\ -* Working with Video Files and Cameras * -\****************************************************************************************/ - -/* "black box" capture structure */ -typedef struct CvCapture CvCapture; - -/* start capturing frames from video file */ -CVAPI(CvCapture*) cvCreateFileCapture( const char* filename ); - -enum -{ - CV_CAP_ANY =0, // autodetect - CV_CAP_MIL =100, // MIL proprietary drivers - - CV_CAP_VFW =200, // platform native - CV_CAP_V4L =200, - CV_CAP_V4L2 =200, - - CV_CAP_FIREWARE =300, // IEEE 1394 drivers - CV_CAP_FIREWIRE =300, - CV_CAP_IEEE1394 =300, - CV_CAP_DC1394 =300, - CV_CAP_CMU1394 =300, - - CV_CAP_STEREO =400, // TYZX proprietary drivers - CV_CAP_TYZX =400, - CV_TYZX_LEFT =400, - CV_TYZX_RIGHT =401, - CV_TYZX_COLOR =402, - CV_TYZX_Z =403, - - CV_CAP_QT =500, // QuickTime - - CV_CAP_UNICAP =600, // Unicap drivers - - CV_CAP_DSHOW =700, // DirectShow (via videoInput) - CV_CAP_MSMF =1400, // Microsoft Media Foundation (via videoInput) - - CV_CAP_PVAPI =800, // PvAPI, Prosilica GigE SDK - - CV_CAP_OPENNI =900, // OpenNI (for Kinect) - CV_CAP_OPENNI_ASUS =910, // OpenNI (for Asus Xtion) - - CV_CAP_ANDROID =1000, // Android - CV_CAP_ANDROID_BACK =CV_CAP_ANDROID+99, // Android back camera - CV_CAP_ANDROID_FRONT =CV_CAP_ANDROID+98, // Android front camera - - CV_CAP_XIAPI =1100, // XIMEA Camera API - - CV_CAP_AVFOUNDATION = 1200, // AVFoundation framework for iOS (OS X Lion will have the same API) - - CV_CAP_GIGANETIX = 1300, // Smartek Giganetix GigEVisionSDK - - CV_CAP_INTELPERC = 1500 // Intel Perceptual Computing SDK -}; - -/* start capturing frames from camera: index = camera_index + domain_offset (CV_CAP_*) */ -CVAPI(CvCapture*) cvCreateCameraCapture( int index ); - -/* grab a frame, return 1 on success, 0 on fail. - this function is thought to be fast */ -CVAPI(int) cvGrabFrame( CvCapture* capture ); - -/* get the frame grabbed with cvGrabFrame(..) - This function may apply some frame processing like - frame decompression, flipping etc. - !!!DO NOT RELEASE or MODIFY the retrieved frame!!! */ -CVAPI(IplImage*) cvRetrieveFrame( CvCapture* capture, int streamIdx CV_DEFAULT(0) ); - -/* Just a combination of cvGrabFrame and cvRetrieveFrame - !!!DO NOT RELEASE or MODIFY the retrieved frame!!! */ -CVAPI(IplImage*) cvQueryFrame( CvCapture* capture ); - -/* stop capturing/reading and free resources */ -CVAPI(void) cvReleaseCapture( CvCapture** capture ); - -enum -{ - // modes of the controlling registers (can be: auto, manual, auto single push, absolute Latter allowed with any other mode) - // every feature can have only one mode turned on at a time - CV_CAP_PROP_DC1394_OFF = -4, //turn the feature off (not controlled manually nor automatically) - CV_CAP_PROP_DC1394_MODE_MANUAL = -3, //set automatically when a value of the feature is set by the user - CV_CAP_PROP_DC1394_MODE_AUTO = -2, - CV_CAP_PROP_DC1394_MODE_ONE_PUSH_AUTO = -1, - CV_CAP_PROP_POS_MSEC =0, - CV_CAP_PROP_POS_FRAMES =1, - CV_CAP_PROP_POS_AVI_RATIO =2, - CV_CAP_PROP_FRAME_WIDTH =3, - CV_CAP_PROP_FRAME_HEIGHT =4, - CV_CAP_PROP_FPS =5, - CV_CAP_PROP_FOURCC =6, - CV_CAP_PROP_FRAME_COUNT =7, - CV_CAP_PROP_FORMAT =8, - CV_CAP_PROP_MODE =9, - CV_CAP_PROP_BRIGHTNESS =10, - CV_CAP_PROP_CONTRAST =11, - CV_CAP_PROP_SATURATION =12, - CV_CAP_PROP_HUE =13, - CV_CAP_PROP_GAIN =14, - CV_CAP_PROP_EXPOSURE =15, - CV_CAP_PROP_CONVERT_RGB =16, - CV_CAP_PROP_WHITE_BALANCE_BLUE_U =17, - CV_CAP_PROP_RECTIFICATION =18, - CV_CAP_PROP_MONOCROME =19, - CV_CAP_PROP_SHARPNESS =20, - CV_CAP_PROP_AUTO_EXPOSURE =21, // exposure control done by camera, - // user can adjust refernce level - // using this feature - CV_CAP_PROP_GAMMA =22, - CV_CAP_PROP_TEMPERATURE =23, - CV_CAP_PROP_TRIGGER =24, - CV_CAP_PROP_TRIGGER_DELAY =25, - CV_CAP_PROP_WHITE_BALANCE_RED_V =26, - CV_CAP_PROP_ZOOM =27, - CV_CAP_PROP_FOCUS =28, - CV_CAP_PROP_GUID =29, - CV_CAP_PROP_ISO_SPEED =30, - CV_CAP_PROP_MAX_DC1394 =31, - CV_CAP_PROP_BACKLIGHT =32, - CV_CAP_PROP_PAN =33, - CV_CAP_PROP_TILT =34, - CV_CAP_PROP_ROLL =35, - CV_CAP_PROP_IRIS =36, - CV_CAP_PROP_SETTINGS =37, - - CV_CAP_PROP_AUTOGRAB =1024, // property for highgui class CvCapture_Android only - CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING=1025, // readonly, tricky property, returns cpnst char* indeed - CV_CAP_PROP_PREVIEW_FORMAT=1026, // readonly, tricky property, returns cpnst char* indeed - - // OpenNI map generators - CV_CAP_OPENNI_DEPTH_GENERATOR = 1 << 31, - CV_CAP_OPENNI_IMAGE_GENERATOR = 1 << 30, - CV_CAP_OPENNI_GENERATORS_MASK = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_OPENNI_IMAGE_GENERATOR, - - // Properties of cameras available through OpenNI interfaces - CV_CAP_PROP_OPENNI_OUTPUT_MODE = 100, - CV_CAP_PROP_OPENNI_FRAME_MAX_DEPTH = 101, // in mm - CV_CAP_PROP_OPENNI_BASELINE = 102, // in mm - CV_CAP_PROP_OPENNI_FOCAL_LENGTH = 103, // in pixels - CV_CAP_PROP_OPENNI_REGISTRATION = 104, // flag - CV_CAP_PROP_OPENNI_REGISTRATION_ON = CV_CAP_PROP_OPENNI_REGISTRATION, // flag that synchronizes the remapping depth map to image map - // by changing depth generator's view point (if the flag is "on") or - // sets this view point to its normal one (if the flag is "off"). - CV_CAP_PROP_OPENNI_APPROX_FRAME_SYNC = 105, - CV_CAP_PROP_OPENNI_MAX_BUFFER_SIZE = 106, - CV_CAP_PROP_OPENNI_CIRCLE_BUFFER = 107, - CV_CAP_PROP_OPENNI_MAX_TIME_DURATION = 108, - - CV_CAP_PROP_OPENNI_GENERATOR_PRESENT = 109, - - CV_CAP_OPENNI_IMAGE_GENERATOR_PRESENT = CV_CAP_OPENNI_IMAGE_GENERATOR + CV_CAP_PROP_OPENNI_GENERATOR_PRESENT, - CV_CAP_OPENNI_IMAGE_GENERATOR_OUTPUT_MODE = CV_CAP_OPENNI_IMAGE_GENERATOR + CV_CAP_PROP_OPENNI_OUTPUT_MODE, - CV_CAP_OPENNI_DEPTH_GENERATOR_BASELINE = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_BASELINE, - CV_CAP_OPENNI_DEPTH_GENERATOR_FOCAL_LENGTH = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_FOCAL_LENGTH, - CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION = CV_CAP_OPENNI_DEPTH_GENERATOR + CV_CAP_PROP_OPENNI_REGISTRATION, - CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION_ON = CV_CAP_OPENNI_DEPTH_GENERATOR_REGISTRATION, - - // Properties of cameras available through GStreamer interface - CV_CAP_GSTREAMER_QUEUE_LENGTH = 200, // default is 1 - - // PVAPI - CV_CAP_PROP_PVAPI_MULTICASTIP = 300, // ip for anable multicast master mode. 0 for disable multicast - CV_CAP_PROP_PVAPI_FRAMESTARTTRIGGERMODE = 301, // FrameStartTriggerMode: Determines how a frame is initiated - - // Properties of cameras available through XIMEA SDK interface - CV_CAP_PROP_XI_DOWNSAMPLING = 400, // Change image resolution by binning or skipping. - CV_CAP_PROP_XI_DATA_FORMAT = 401, // Output data format. - CV_CAP_PROP_XI_OFFSET_X = 402, // Horizontal offset from the origin to the area of interest (in pixels). - CV_CAP_PROP_XI_OFFSET_Y = 403, // Vertical offset from the origin to the area of interest (in pixels). - CV_CAP_PROP_XI_TRG_SOURCE = 404, // Defines source of trigger. - CV_CAP_PROP_XI_TRG_SOFTWARE = 405, // Generates an internal trigger. PRM_TRG_SOURCE must be set to TRG_SOFTWARE. - CV_CAP_PROP_XI_GPI_SELECTOR = 406, // Selects general purpose input - CV_CAP_PROP_XI_GPI_MODE = 407, // Set general purpose input mode - CV_CAP_PROP_XI_GPI_LEVEL = 408, // Get general purpose level - CV_CAP_PROP_XI_GPO_SELECTOR = 409, // Selects general purpose output - CV_CAP_PROP_XI_GPO_MODE = 410, // Set general purpose output mode - CV_CAP_PROP_XI_LED_SELECTOR = 411, // Selects camera signalling LED - CV_CAP_PROP_XI_LED_MODE = 412, // Define camera signalling LED functionality - CV_CAP_PROP_XI_MANUAL_WB = 413, // Calculates White Balance(must be called during acquisition) - CV_CAP_PROP_XI_AUTO_WB = 414, // Automatic white balance - CV_CAP_PROP_XI_AEAG = 415, // Automatic exposure/gain - CV_CAP_PROP_XI_EXP_PRIORITY = 416, // Exposure priority (0.5 - exposure 50%, gain 50%). - CV_CAP_PROP_XI_AE_MAX_LIMIT = 417, // Maximum limit of exposure in AEAG procedure - CV_CAP_PROP_XI_AG_MAX_LIMIT = 418, // Maximum limit of gain in AEAG procedure - CV_CAP_PROP_XI_AEAG_LEVEL = 419, // Average intensity of output signal AEAG should achieve(in %) - CV_CAP_PROP_XI_TIMEOUT = 420, // Image capture timeout in milliseconds - - // Properties for Android cameras - CV_CAP_PROP_ANDROID_FLASH_MODE = 8001, - CV_CAP_PROP_ANDROID_FOCUS_MODE = 8002, - CV_CAP_PROP_ANDROID_WHITE_BALANCE = 8003, - CV_CAP_PROP_ANDROID_ANTIBANDING = 8004, - CV_CAP_PROP_ANDROID_FOCAL_LENGTH = 8005, - CV_CAP_PROP_ANDROID_FOCUS_DISTANCE_NEAR = 8006, - CV_CAP_PROP_ANDROID_FOCUS_DISTANCE_OPTIMAL = 8007, - CV_CAP_PROP_ANDROID_FOCUS_DISTANCE_FAR = 8008, - CV_CAP_PROP_ANDROID_EXPOSE_LOCK = 8009, - CV_CAP_PROP_ANDROID_WHITEBALANCE_LOCK = 8010, - - // Properties of cameras available through AVFOUNDATION interface - CV_CAP_PROP_IOS_DEVICE_FOCUS = 9001, - CV_CAP_PROP_IOS_DEVICE_EXPOSURE = 9002, - CV_CAP_PROP_IOS_DEVICE_FLASH = 9003, - CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE = 9004, - CV_CAP_PROP_IOS_DEVICE_TORCH = 9005, - - // Properties of cameras available through Smartek Giganetix Ethernet Vision interface - /* --- Vladimir Litvinenko (litvinenko.vladimir@gmail.com) --- */ - CV_CAP_PROP_GIGA_FRAME_OFFSET_X = 10001, - CV_CAP_PROP_GIGA_FRAME_OFFSET_Y = 10002, - CV_CAP_PROP_GIGA_FRAME_WIDTH_MAX = 10003, - CV_CAP_PROP_GIGA_FRAME_HEIGH_MAX = 10004, - CV_CAP_PROP_GIGA_FRAME_SENS_WIDTH = 10005, - CV_CAP_PROP_GIGA_FRAME_SENS_HEIGH = 10006, - - CV_CAP_PROP_INTELPERC_PROFILE_COUNT = 11001, - CV_CAP_PROP_INTELPERC_PROFILE_IDX = 11002, - CV_CAP_PROP_INTELPERC_DEPTH_LOW_CONFIDENCE_VALUE = 11003, - CV_CAP_PROP_INTELPERC_DEPTH_SATURATION_VALUE = 11004, - CV_CAP_PROP_INTELPERC_DEPTH_CONFIDENCE_THRESHOLD = 11005, - CV_CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_HORZ = 11006, - CV_CAP_PROP_INTELPERC_DEPTH_FOCAL_LENGTH_VERT = 11007, - - // Intel PerC streams - CV_CAP_INTELPERC_DEPTH_GENERATOR = 1 << 29, - CV_CAP_INTELPERC_IMAGE_GENERATOR = 1 << 28, - CV_CAP_INTELPERC_GENERATORS_MASK = CV_CAP_INTELPERC_DEPTH_GENERATOR + CV_CAP_INTELPERC_IMAGE_GENERATOR -}; - -enum -{ - // Data given from depth generator. - CV_CAP_OPENNI_DEPTH_MAP = 0, // Depth values in mm (CV_16UC1) - CV_CAP_OPENNI_POINT_CLOUD_MAP = 1, // XYZ in meters (CV_32FC3) - CV_CAP_OPENNI_DISPARITY_MAP = 2, // Disparity in pixels (CV_8UC1) - CV_CAP_OPENNI_DISPARITY_MAP_32F = 3, // Disparity in pixels (CV_32FC1) - CV_CAP_OPENNI_VALID_DEPTH_MASK = 4, // CV_8UC1 - - // Data given from RGB image generator. - CV_CAP_OPENNI_BGR_IMAGE = 5, - CV_CAP_OPENNI_GRAY_IMAGE = 6 -}; - -// Supported output modes of OpenNI image generator -enum -{ - CV_CAP_OPENNI_VGA_30HZ = 0, - CV_CAP_OPENNI_SXGA_15HZ = 1, - CV_CAP_OPENNI_SXGA_30HZ = 2, - CV_CAP_OPENNI_QVGA_30HZ = 3, - CV_CAP_OPENNI_QVGA_60HZ = 4 -}; - -//supported by Android camera output formats -enum -{ - CV_CAP_ANDROID_COLOR_FRAME_BGR = 0, //BGR - CV_CAP_ANDROID_COLOR_FRAME = CV_CAP_ANDROID_COLOR_FRAME_BGR, - CV_CAP_ANDROID_GREY_FRAME = 1, //Y - CV_CAP_ANDROID_COLOR_FRAME_RGB = 2, - CV_CAP_ANDROID_COLOR_FRAME_BGRA = 3, - CV_CAP_ANDROID_COLOR_FRAME_RGBA = 4 -}; - -// supported Android camera flash modes -enum -{ - CV_CAP_ANDROID_FLASH_MODE_AUTO = 0, - CV_CAP_ANDROID_FLASH_MODE_OFF, - CV_CAP_ANDROID_FLASH_MODE_ON, - CV_CAP_ANDROID_FLASH_MODE_RED_EYE, - CV_CAP_ANDROID_FLASH_MODE_TORCH -}; - -// supported Android camera focus modes -enum -{ - CV_CAP_ANDROID_FOCUS_MODE_AUTO = 0, - CV_CAP_ANDROID_FOCUS_MODE_CONTINUOUS_PICTURE, - CV_CAP_ANDROID_FOCUS_MODE_CONTINUOUS_VIDEO, - CV_CAP_ANDROID_FOCUS_MODE_EDOF, - CV_CAP_ANDROID_FOCUS_MODE_FIXED, - CV_CAP_ANDROID_FOCUS_MODE_INFINITY, - CV_CAP_ANDROID_FOCUS_MODE_MACRO -}; - -// supported Android camera white balance modes -enum -{ - CV_CAP_ANDROID_WHITE_BALANCE_AUTO = 0, - CV_CAP_ANDROID_WHITE_BALANCE_CLOUDY_DAYLIGHT, - CV_CAP_ANDROID_WHITE_BALANCE_DAYLIGHT, - CV_CAP_ANDROID_WHITE_BALANCE_FLUORESCENT, - CV_CAP_ANDROID_WHITE_BALANCE_INCANDESCENT, - CV_CAP_ANDROID_WHITE_BALANCE_SHADE, - CV_CAP_ANDROID_WHITE_BALANCE_TWILIGHT, - CV_CAP_ANDROID_WHITE_BALANCE_WARM_FLUORESCENT -}; - -// supported Android camera antibanding modes -enum -{ - CV_CAP_ANDROID_ANTIBANDING_50HZ = 0, - CV_CAP_ANDROID_ANTIBANDING_60HZ, - CV_CAP_ANDROID_ANTIBANDING_AUTO, - CV_CAP_ANDROID_ANTIBANDING_OFF -}; - -enum -{ - CV_CAP_INTELPERC_DEPTH_MAP = 0, // Each pixel is a 16-bit integer. The value indicates the distance from an object to the camera's XY plane or the Cartesian depth. - CV_CAP_INTELPERC_UVDEPTH_MAP = 1, // Each pixel contains two 32-bit floating point values in the range of 0-1, representing the mapping of depth coordinates to the color coordinates. - CV_CAP_INTELPERC_IR_MAP = 2, // Each pixel is a 16-bit integer. The value indicates the intensity of the reflected laser beam. - CV_CAP_INTELPERC_IMAGE = 3 -}; - -/* retrieve or set capture properties */ -CVAPI(double) cvGetCaptureProperty( CvCapture* capture, int property_id ); -CVAPI(int) cvSetCaptureProperty( CvCapture* capture, int property_id, double value ); - -// Return the type of the capturer (eg, CV_CAP_V4W, CV_CAP_UNICAP), which is unknown if created with CV_CAP_ANY -CVAPI(int) cvGetCaptureDomain( CvCapture* capture); - -/* "black box" video file writer structure */ -typedef struct CvVideoWriter CvVideoWriter; - -#define CV_FOURCC_MACRO(c1, c2, c3, c4) (((c1) & 255) + (((c2) & 255) << 8) + (((c3) & 255) << 16) + (((c4) & 255) << 24)) - -CV_INLINE int CV_FOURCC(char c1, char c2, char c3, char c4) -{ - return CV_FOURCC_MACRO(c1, c2, c3, c4); -} - -#define CV_FOURCC_PROMPT -1 /* Open Codec Selection Dialog (Windows only) */ -#define CV_FOURCC_DEFAULT CV_FOURCC('I', 'Y', 'U', 'V') /* Use default codec for specified filename (Linux only) */ - -/* initialize video file writer */ -CVAPI(CvVideoWriter*) cvCreateVideoWriter( const char* filename, int fourcc, - double fps, CvSize frame_size, - int is_color CV_DEFAULT(1)); - -/* write frame to video file */ -CVAPI(int) cvWriteFrame( CvVideoWriter* writer, const IplImage* image ); - -/* close video file writer */ -CVAPI(void) cvReleaseVideoWriter( CvVideoWriter** writer ); - -/****************************************************************************************\ * Obsolete functions/synonyms * \****************************************************************************************/ -#define cvCaptureFromFile cvCreateFileCapture -#define cvCaptureFromCAM cvCreateCameraCapture -#define cvCaptureFromAVI cvCaptureFromFile -#define cvCreateAVIWriter cvCreateVideoWriter -#define cvWriteToAVI cvWriteFrame #define cvAddSearchPath(path) #define cvvInitSystem cvInitSystem #define cvvNamedWindow cvNamedWindow @@ -632,12 +220,9 @@ CVAPI(void) cvReleaseVideoWriter( CvVideoWriter** writer ); #define cvvResizeWindow cvResizeWindow #define cvvDestroyWindow cvDestroyWindow #define cvvCreateTrackbar cvCreateTrackbar -#define cvvLoadImage(name) cvLoadImage((name),1) -#define cvvSaveImage cvSaveImage #define cvvAddSearchPath cvAddSearchPath #define cvvWaitKey(name) cvWaitKey(0) #define cvvWaitKeyEx(name,delay) cvWaitKey(delay) -#define cvvConvertImage cvConvertImage #define HG_AUTOSIZE CV_WINDOW_AUTOSIZE #define set_preprocess_func cvSetPreprocessFuncWin32 #define set_postprocess_func cvSetPostprocessFuncWin32 diff --git a/modules/highgui/src/precomp.hpp b/modules/highgui/src/precomp.hpp index bb4ed8ede5..c9517783f9 100644 --- a/modules/highgui/src/precomp.hpp +++ b/modules/highgui/src/precomp.hpp @@ -47,7 +47,10 @@ #include "opencv2/core/utility.hpp" #include "opencv2/core/private.hpp" +#include "opencv2/imgcodecs.hpp" + #include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/imgcodecs/imgcodecs_c.h" #include "opencv2/highgui/highgui_c.h" #include @@ -92,90 +95,6 @@ #define CV_WINDOW_MAGIC_VAL 0x00420042 #define CV_TRACKBAR_MAGIC_VAL 0x00420043 -/***************************** CvCapture structure ******************************/ - -struct CvCapture -{ - virtual ~CvCapture() {} - virtual double getProperty(int) { return 0; } - virtual bool setProperty(int, double) { return 0; } - virtual bool grabFrame() { return true; } - virtual IplImage* retrieveFrame(int) { return 0; } - virtual int getCaptureDomain() { return CV_CAP_ANY; } // Return the type of the capture object: CV_CAP_VFW, etc... -}; - -/*************************** CvVideoWriter structure ****************************/ - -struct CvVideoWriter -{ - virtual ~CvVideoWriter() {} - virtual bool writeFrame(const IplImage*) { return false; } -}; - -CvCapture * cvCreateCameraCapture_V4L( int index ); -CvCapture * cvCreateCameraCapture_DC1394( int index ); -CvCapture * cvCreateCameraCapture_DC1394_2( int index ); -CvCapture* cvCreateCameraCapture_MIL( int index ); -CvCapture* cvCreateCameraCapture_Giganetix( int index ); -CvCapture * cvCreateCameraCapture_CMU( int index ); -CV_IMPL CvCapture * cvCreateCameraCapture_TYZX( int index ); -CvCapture* cvCreateFileCapture_Win32( const char* filename ); -CvCapture* cvCreateCameraCapture_VFW( int index ); -CvCapture* cvCreateFileCapture_VFW( const char* filename ); -CvVideoWriter* cvCreateVideoWriter_Win32( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ); -CvVideoWriter* cvCreateVideoWriter_VFW( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ); -CvCapture* cvCreateCameraCapture_DShow( int index ); -CvCapture* cvCreateCameraCapture_MSMF( int index ); -CvCapture* cvCreateFileCapture_MSMF (const char* filename); -CvVideoWriter* cvCreateVideoWriter_MSMF( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ); -CvCapture* cvCreateCameraCapture_OpenNI( int index ); -CvCapture* cvCreateFileCapture_OpenNI( const char* filename ); -CvCapture* cvCreateCameraCapture_Android( int index ); -CvCapture* cvCreateCameraCapture_XIMEA( int index ); -CvCapture* cvCreateCameraCapture_AVFoundation(int index); - -CVAPI(int) cvHaveImageReader(const char* filename); -CVAPI(int) cvHaveImageWriter(const char* filename); - -CvCapture* cvCreateFileCapture_Images(const char* filename); -CvVideoWriter* cvCreateVideoWriter_Images(const char* filename); - -CvCapture* cvCreateFileCapture_XINE (const char* filename); - - - - -#define CV_CAP_GSTREAMER_1394 0 -#define CV_CAP_GSTREAMER_V4L 1 -#define CV_CAP_GSTREAMER_V4L2 2 -#define CV_CAP_GSTREAMER_FILE 3 - -CvCapture* cvCreateCapture_GStreamer(int type, const char *filename); -CvCapture* cvCreateFileCapture_FFMPEG_proxy(const char* filename); - - -CvVideoWriter* cvCreateVideoWriter_FFMPEG_proxy( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ); - -CvCapture * cvCreateFileCapture_QT (const char * filename); -CvCapture * cvCreateCameraCapture_QT (const int index); - -CvVideoWriter* cvCreateVideoWriter_QT ( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ); - -CvCapture* cvCreateFileCapture_AVFoundation (const char * filename); -CvVideoWriter* cvCreateVideoWriter_AVFoundation( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ); - - -CvCapture * cvCreateCameraCapture_Unicap (const int index); -CvCapture * cvCreateCameraCapture_PvAPI (const int index); -CvVideoWriter* cvCreateVideoWriter_GStreamer( const char* filename, int fourcc, - double fps, CvSize frameSize, int is_color ); - //Yannick Verdie 2010 void cvSetModeWindow_W32(const char* name, double prop_value); void cvSetModeWindow_GTK(const char* name, double prop_value); @@ -196,20 +115,6 @@ double cvGetRatioWindow_GTK(const char* name); double cvGetOpenGlProp_W32(const char* name); double cvGetOpenGlProp_GTK(const char* name); -namespace cv -{ - class IVideoCapture - { - public: - virtual ~IVideoCapture() {} - virtual double getProperty(int) { return 0; } - virtual bool setProperty(int, double) { return 0; } - virtual bool grabFrame() = 0; - virtual bool retrieveFrame(int, cv::OutputArray) = 0; - virtual int getCaptureDomain() { return CAP_ANY; } // Return the type of the capture object: CAP_VFW, etc... - }; -}; - //for QT #if defined (HAVE_QT) double cvGetModeWindow_QT(const char* name); diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 8b317dff71..bcf1bae8be 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -1286,6 +1286,10 @@ MainWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) switch(uMsg) { + case WM_COPY: + ::WindowProc(hwnd, uMsg, wParam, lParam); // call highgui proc. There may be a better way to do this. + break; + case WM_DESTROY: icvRemoveWindow(window); @@ -1448,6 +1452,81 @@ static LRESULT CALLBACK HighGUIProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM // Process the message switch(uMsg) { + case WM_COPY: + { + if (!::OpenClipboard(hwnd) ) + break; + + HDC hDC = 0; + HDC memDC = 0; + HBITMAP memBM = 0; + + // We'll use a do-while(0){} scope as a single-run breakable scope + // Upon any error we can jump out of the single-time while scope to clean up the resources. + do + { + if (!::EmptyClipboard()) + break; + + if(!window->image) + break; + + // Get window device context + if (0 == (hDC = ::GetDC(hwnd))) + break; + + // Create another DC compatible with hDC + if (0 == (memDC = ::CreateCompatibleDC( hDC ))) + break; + + // Determine the bitmap's dimensions + int nchannels = 3; + SIZE size = {0,0}; + icvGetBitmapData( window, &size, &nchannels, 0 ); + + // Create bitmap to draw on and it in the new DC + if (0 == (memBM = ::CreateCompatibleBitmap ( hDC, size.cx, size.cy))) + break; + + if (!::SelectObject( memDC, memBM )) + break; + + // Begin drawing to DC + if (!::SetStretchBltMode(memDC, COLORONCOLOR)) + break; + + RGBQUAD table[256]; + if( 1 == nchannels ) + { + for(int i = 0; i < 256; ++i) + { + table[i].rgbBlue = (unsigned char)i; + table[i].rgbGreen = (unsigned char)i; + table[i].rgbRed = (unsigned char)i; + } + if (!::SetDIBColorTable(window->dc, 0, 255, table)) + break; + } + + // The image copied to the clipboard will be in its original size, regardless if the window itself was resized. + + // Render the image to the dc/bitmap (at original size). + if (!::BitBlt( memDC, 0, 0, size.cx, size.cy, window->dc, 0, 0, SRCCOPY )) + break; + + // Finally, set bitmap to clipboard + ::SetClipboardData(CF_BITMAP, memBM); + } while (0,0); // (0,0) instead of (0) to avoid MSVC compiler warning C4127: "conditional expression is constant" + + ////////////////////////////////////////////////////////////////////////// + // if handle is allocated (i.e. != 0) then clean-up. + if (memBM) ::DeleteObject(memBM); + if (memDC) ::DeleteDC(memDC); + if (hDC) ::ReleaseDC(hwnd, hDC); + ::CloseClipboard(); + break; + } + case WM_WINDOWPOSCHANGING: { LPWINDOWPOS pos = (LPWINDOWPOS)lParam; @@ -1798,6 +1877,11 @@ cvWaitKey( int delay ) is_processed = 1; return (int)(message.wParam << 16); } + + // Intercept Ctrl+C for copy to clipboard + if ('C' == message.wParam && (::GetKeyState(VK_CONTROL)>>15)) + ::PostMessage(message.hwnd, WM_COPY, 0, 0); + default: DispatchMessage(&message); is_processed = 1; diff --git a/modules/highgui/test/test_precomp.hpp b/modules/highgui/test/test_precomp.hpp index 826d165749..e4d7797158 100644 --- a/modules/highgui/test/test_precomp.hpp +++ b/modules/highgui/test/test_precomp.hpp @@ -11,80 +11,11 @@ #include #include "opencv2/ts.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" -#include "opencv2/imgproc/imgproc_c.h" +//#include "opencv2/imgproc.hpp" +//#include "opencv2/imgcodecs.hpp" +//#include "opencv2/highgui.hpp" +//#include "opencv2/imgproc/imgproc_c.h" -#include "opencv2/core/private.hpp" - -#if defined(HAVE_DSHOW) || \ - defined(HAVE_TYZX) || \ - defined(HAVE_VFW) || \ - defined(HAVE_LIBV4L) || \ - (defined(HAVE_CAMV4L) && defined(HAVE_CAMV4L2)) || \ - defined(HAVE_GSTREAMER) || \ - defined(HAVE_DC1394_2) || \ - defined(HAVE_DC1394) || \ - defined(HAVE_CMU1394) || \ - defined(HAVE_MIL) || \ - defined(HAVE_QUICKTIME) || \ - defined(HAVE_QTKIT) || \ - defined(HAVE_UNICAP) || \ - defined(HAVE_PVAPI) || \ - defined(HAVE_OPENNI) || \ - defined(HAVE_XIMEA) || \ - defined(HAVE_AVFOUNDATION) || \ - defined(HAVE_GIGE_API) || \ - defined(HAVE_INTELPERC) || \ - (0) - //defined(HAVE_ANDROID_NATIVE_CAMERA) || - enable after #1193 -# define BUILD_WITH_CAMERA_SUPPORT 1 -#else -# define BUILD_WITH_CAMERA_SUPPORT 0 -#endif - -#if defined(HAVE_XINE) || \ - defined(HAVE_GSTREAMER) || \ - defined(HAVE_QUICKTIME) || \ - defined(HAVE_QTKIT) || \ - defined(HAVE_AVFOUNDATION) || \ - /*defined(HAVE_OPENNI) || too specialized */ \ - defined(HAVE_FFMPEG) || \ - defined(HAVE_MSMF) -# define BUILD_WITH_VIDEO_INPUT_SUPPORT 1 -#else -# define BUILD_WITH_VIDEO_INPUT_SUPPORT 0 -#endif - -#if /*defined(HAVE_XINE) || */\ - defined(HAVE_GSTREAMER) || \ - defined(HAVE_QUICKTIME) || \ - defined(HAVE_QTKIT) || \ - defined(HAVE_AVFOUNDATION) || \ - defined(HAVE_FFMPEG) || \ - defined(HAVE_MSMF) -# define BUILD_WITH_VIDEO_OUTPUT_SUPPORT 1 -#else -# define BUILD_WITH_VIDEO_OUTPUT_SUPPORT 0 -#endif - -namespace cvtest -{ - -string fourccToString(int fourcc); - -struct VideoFormat -{ - VideoFormat() { fourcc = -1; } - VideoFormat(const string& _ext, int _fourcc) : ext(_ext), fourcc(_fourcc) {} - bool empty() const { return ext.empty(); } - - string ext; - int fourcc; -}; - -extern const VideoFormat g_specific_fmt_list[]; - -} +//#include "opencv2/core/private.hpp" #endif diff --git a/modules/imgcodecs/CMakeLists.txt b/modules/imgcodecs/CMakeLists.txt new file mode 100644 index 0000000000..8cf60e5469 --- /dev/null +++ b/modules/imgcodecs/CMakeLists.txt @@ -0,0 +1,131 @@ +set(the_description "Image codecs") +ocv_add_module(imgcodecs opencv_imgproc) + +# ---------------------------------------------------------------------------- +# CMake file for imgcodecs. See root CMakeLists.txt +# Some parts taken from version of Hartmut Seichter, HIT Lab NZ. +# Jose Luis Blanco, 2008 +# ---------------------------------------------------------------------------- + +ocv_clear_vars(GRFMT_LIBS) + +if(HAVE_WINRT_CX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") +endif() + +if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR) + ocv_include_directories(${ZLIB_INCLUDE_DIRS}) + list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES}) +endif() + +if(HAVE_JPEG) + ocv_include_directories(${JPEG_INCLUDE_DIR}) + list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES}) +endif() + +if(WITH_WEBP) + add_definitions(-DHAVE_WEBP) + ocv_include_directories(${WEBP_INCLUDE_DIR}) + list(APPEND GRFMT_LIBS ${WEBP_LIBRARIES}) +endif() + +if(HAVE_PNG) + add_definitions(${PNG_DEFINITIONS}) + ocv_include_directories(${PNG_INCLUDE_DIR}) + list(APPEND GRFMT_LIBS ${PNG_LIBRARIES}) +endif() + +if(HAVE_TIFF) + ocv_include_directories(${TIFF_INCLUDE_DIR}) + list(APPEND GRFMT_LIBS ${TIFF_LIBRARIES}) +endif() + +if(HAVE_JASPER) + ocv_include_directories(${JASPER_INCLUDE_DIR}) + list(APPEND GRFMT_LIBS ${JASPER_LIBRARIES}) +endif() + +if(HAVE_OPENEXR) + include_directories(SYSTEM ${OPENEXR_INCLUDE_PATHS}) + list(APPEND GRFMT_LIBS ${OPENEXR_LIBRARIES}) +endif() + +file(GLOB grfmt_hdrs src/grfmt*.hpp) +file(GLOB grfmt_srcs src/grfmt*.cpp) +list(APPEND grfmt_hdrs src/bitstrm.hpp) +list(APPEND grfmt_srcs src/bitstrm.cpp) +list(APPEND grfmt_hdrs src/rgbe.hpp) +list(APPEND grfmt_srcs src/rgbe.cpp) + +source_group("Src\\grfmts" FILES ${grfmt_hdrs} ${grfmt_srcs}) + +set(imgcodecs_hdrs + src/precomp.hpp + src/utils.hpp + ) + +set(imgcodecs_srcs + src/loadsave.cpp + src/utils.cpp + ) + +file(GLOB imgcodecs_ext_hdrs "include/opencv2/*.hpp" "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") + +if(IOS) + add_definitions(-DHAVE_IOS=1) + list(APPEND imgcodecs_srcs src/ios_conversions.mm) + list(APPEND IMGCODECS_LIBRARIES "-framework Accelerate" "-framework CoreGraphics" "-framework CoreImage" "-framework QuartzCore" "-framework AssetsLibrary") +endif() + +if(UNIX) + #these variables are set by CHECK_MODULE macro + foreach(P ${IMGCODECS_INCLUDE_DIRS}) + ocv_include_directories(${P}) + endforeach() + + foreach(P ${IMGCODECS_LIBRARY_DIRS}) + link_directories(${P}) + endforeach() +endif() + +source_group("Src" FILES ${imgcodecs_srcs} ${imgcodecs_hdrs}) +source_group("Include" FILES ${imgcodecs_ext_hdrs}) +ocv_set_module_sources(HEADERS ${imgcodecs_ext_hdrs} SOURCES ${imgcodecs_srcs} ${imgcodecs_hdrs} ${grfmt_srcs} ${grfmt_hdrs}) +ocv_module_include_directories() + +ocv_create_module(${GRFMT_LIBS} ${IMGCODECS_LIBRARIES}) + +if(APPLE) + ocv_check_flag_support(OBJCXX "-fobjc-exceptions" HAVE_OBJC_EXCEPTIONS) + if(HAVE_OBJC_EXCEPTIONS) + foreach(source ${OPENCV_MODULE_${the_module}_SOURCES}) + if("${source}" MATCHES "\\.mm$") + get_source_file_property(flags "${source}" COMPILE_FLAGS) + if(flags) + set(flags "${_flags} -fobjc-exceptions") + else() + set(flags "-fobjc-exceptions") + endif() + + set_source_files_properties("${source}" PROPERTIES COMPILE_FLAGS "${flags}") + endif() + endforeach() + endif() +endif() + +if(BUILD_SHARED_LIBS) + add_definitions(-DIMGCODECS_EXPORTS) +endif() + +if(MSVC) + set_target_properties(${the_module} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:atlsd.lib /NODEFAULTLIB:libcmt.lib /DEBUG") +endif() + +#stop automatic dependencies propagation for this module +set_target_properties(${the_module} PROPERTIES LINK_INTERFACE_LIBRARIES "") + +ocv_add_precompiled_headers(${the_module}) +ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-deprecated-declarations) + +ocv_add_accuracy_tests() +ocv_add_perf_tests() diff --git a/modules/imgcodecs/doc/imgcodecs.rst b/modules/imgcodecs/doc/imgcodecs.rst new file mode 100644 index 0000000000..bcb3131f56 --- /dev/null +++ b/modules/imgcodecs/doc/imgcodecs.rst @@ -0,0 +1,10 @@ +***************************************** +imgcodecs. Image file reading and writing +***************************************** + +This module of the OpenCV help you read and write images to/from disk or memory. + +.. toctree:: + :maxdepth: 2 + + reading_and_writing_images diff --git a/modules/imgcodecs/doc/reading_and_writing_images.rst b/modules/imgcodecs/doc/reading_and_writing_images.rst new file mode 100644 index 0000000000..9f8dcaafef --- /dev/null +++ b/modules/imgcodecs/doc/reading_and_writing_images.rst @@ -0,0 +1,187 @@ +Reading and Writing Images +========================== + +.. highlight:: cpp + +imdecode +-------- +Reads an image from a buffer in memory. + +.. ocv:function:: Mat imdecode( InputArray buf, int flags ) + +.. ocv:function:: Mat imdecode( InputArray buf, int flags, Mat* dst ) + +.. ocv:cfunction:: IplImage* cvDecodeImage( const CvMat* buf, int iscolor=CV_LOAD_IMAGE_COLOR) + +.. ocv:cfunction:: CvMat* cvDecodeImageM( const CvMat* buf, int iscolor=CV_LOAD_IMAGE_COLOR) + +.. ocv:pyfunction:: cv2.imdecode(buf, flags) -> retval + + :param buf: Input array or vector of bytes. + + :param flags: The same flags as in :ocv:func:`imread` . + + :param dst: The optional output placeholder for the decoded matrix. It can save the image reallocations when the function is called repeatedly for images of the same size. + +The function reads an image from the specified buffer in the memory. +If the buffer is too short or contains invalid data, the empty matrix/image is returned. + +See +:ocv:func:`imread` for the list of supported formats and flags description. + +.. note:: In the case of color images, the decoded images will have the channels stored in ``B G R`` order. + +imencode +-------- +Encodes an image into a memory buffer. + +.. ocv:function:: bool imencode( const String& ext, InputArray img, vector& buf, const vector& params=vector()) + +.. ocv:cfunction:: CvMat* cvEncodeImage( const char* ext, const CvArr* image, const int* params=0 ) + +.. ocv:pyfunction:: cv2.imencode(ext, img[, params]) -> retval, buf + + :param ext: File extension that defines the output format. + + :param img: Image to be written. + + :param buf: Output buffer resized to fit the compressed image. + + :param params: Format-specific parameters. See :ocv:func:`imwrite` . + +The function compresses the image and stores it in the memory buffer that is resized to fit the result. +See +:ocv:func:`imwrite` for the list of supported formats and flags description. + +.. note:: ``cvEncodeImage`` returns single-row matrix of type ``CV_8UC1`` that contains encoded image as array of bytes. + +imread +------ +Loads an image from a file. + +.. ocv:function:: Mat imread( const String& filename, int flags=IMREAD_COLOR ) + +.. ocv:pyfunction:: cv2.imread(filename[, flags]) -> retval + +.. ocv:cfunction:: IplImage* cvLoadImage( const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR ) + +.. ocv:cfunction:: CvMat* cvLoadImageM( const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR ) + + :param filename: Name of file to be loaded. + + :param flags: Flags specifying the color type of a loaded image: + + * CV_LOAD_IMAGE_ANYDEPTH - If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit. + + * CV_LOAD_IMAGE_COLOR - If set, always convert image to the color one + + * CV_LOAD_IMAGE_GRAYSCALE - If set, always convert image to the grayscale one + + * **>0** Return a 3-channel color image. + .. note:: In the current implementation the alpha channel, if any, is stripped from the output image. Use negative value if you need the alpha channel. + + * **=0** Return a grayscale image. + + * **<0** Return the loaded image as is (with alpha channel). + +The function ``imread`` loads an image from the specified file and returns it. If the image cannot be read (because of missing file, improper permissions, unsupported or invalid format), the function returns an empty matrix ( ``Mat::data==NULL`` ). Currently, the following file formats are supported: + + * Windows bitmaps - ``*.bmp, *.dib`` (always supported) + + * JPEG files - ``*.jpeg, *.jpg, *.jpe`` (see the *Notes* section) + + * JPEG 2000 files - ``*.jp2`` (see the *Notes* section) + + * Portable Network Graphics - ``*.png`` (see the *Notes* section) + + * WebP - ``*.webp`` (see the *Notes* section) + + * Portable image format - ``*.pbm, *.pgm, *.ppm`` (always supported) + + * Sun rasters - ``*.sr, *.ras`` (always supported) + + * TIFF files - ``*.tiff, *.tif`` (see the *Notes* section) + +.. note:: + + * The function determines the type of an image by the content, not by the file extension. + + * On Microsoft Windows* OS and MacOSX*, the codecs shipped with an OpenCV image (libjpeg, libpng, libtiff, and libjasper) are used by default. So, OpenCV can always read JPEGs, PNGs, and TIFFs. On MacOSX, there is also an option to use native MacOSX image readers. But beware that currently these native image loaders give images with different pixel values because of the color management embedded into MacOSX. + + * On Linux*, BSD flavors and other Unix-like open-source operating systems, OpenCV looks for codecs supplied with an OS image. Install the relevant packages (do not forget the development files, for example, "libjpeg-dev", in Debian* and Ubuntu*) to get the codec support or turn on the ``OPENCV_BUILD_3RDPARTY_LIBS`` flag in CMake. + +.. note:: In the case of color images, the decoded images will have the channels stored in ``B G R`` order. + +imwrite +----------- +Saves an image to a specified file. + +.. ocv:function:: bool imwrite( const String& filename, InputArray img, const vector& params=vector() ) + +.. ocv:pyfunction:: cv2.imwrite(filename, img[, params]) -> retval + +.. ocv:cfunction:: int cvSaveImage( const char* filename, const CvArr* image, const int* params=0 ) + + :param filename: Name of the file. + + :param image: Image to be saved. + + :param params: Format-specific save parameters encoded as pairs ``paramId_1, paramValue_1, paramId_2, paramValue_2, ...`` . The following parameters are currently supported: + + * For JPEG, it can be a quality ( ``CV_IMWRITE_JPEG_QUALITY`` ) from 0 to 100 (the higher is the better). Default value is 95. + + * For WEBP, it can be a quality ( CV_IMWRITE_WEBP_QUALITY ) from 1 to 100 (the higher is the better). + By default (without any parameter) and for quality above 100 the lossless compression is used. + + * For PNG, it can be the compression level ( ``CV_IMWRITE_PNG_COMPRESSION`` ) from 0 to 9. A higher value means a smaller size and longer compression time. Default value is 3. + + * For PPM, PGM, or PBM, it can be a binary format flag ( ``CV_IMWRITE_PXM_BINARY`` ), 0 or 1. Default value is 1. + +The function ``imwrite`` saves the image to the specified file. The image format is chosen based on the ``filename`` extension (see +:ocv:func:`imread` for the list of extensions). Only 8-bit (or 16-bit unsigned (``CV_16U``) in case of PNG, JPEG 2000, and TIFF) single-channel or 3-channel (with 'BGR' channel order) images can be saved using this function. If the format, depth or channel order is different, use +:ocv:func:`Mat::convertTo` , and +:ocv:func:`cvtColor` to convert it before saving. Or, use the universal :ocv:class:`FileStorage` I/O functions to save the image to XML or YAML format. + +It is possible to store PNG images with an alpha channel using this function. To do this, create 8-bit (or 16-bit) 4-channel image BGRA, where the alpha channel goes last. Fully transparent pixels should have alpha set to 0, fully opaque pixels should have alpha set to 255/65535. The sample below shows how to create such a BGRA image and store to PNG file. It also demonstrates how to set custom compression parameters :: + + #include + #include + #include + + using namespace cv; + using namespace std; + + void createAlphaMat(Mat &mat) + { + for (int i = 0; i < mat.rows; ++i) { + for (int j = 0; j < mat.cols; ++j) { + Vec4b& rgba = mat.at(i, j); + rgba[0] = UCHAR_MAX; + rgba[1] = saturate_cast((float (mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX); + rgba[2] = saturate_cast((float (mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX); + rgba[3] = saturate_cast(0.5 * (rgba[1] + rgba[2])); + } + } + } + + int main(int argv, char **argc) + { + // Create mat with alpha channel + Mat mat(480, 640, CV_8UC4); + createAlphaMat(mat); + + vector compression_params; + compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION); + compression_params.push_back(9); + + try { + imwrite("alpha.png", mat, compression_params); + } + catch (runtime_error& ex) { + fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what()); + return 1; + } + + fprintf(stdout, "Saved PNG file with alpha data.\n"); + return 0; + } diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp new file mode 100644 index 0000000000..97fff83e14 --- /dev/null +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -0,0 +1,94 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_IMGCODECS_HPP__ +#define __OPENCV_IMGCODECS_HPP__ + +#include "opencv2/core.hpp" + +//////////////////////////////// image codec //////////////////////////////// +namespace cv +{ + +enum { IMREAD_UNCHANGED = -1, // 8bit, color or not + IMREAD_GRAYSCALE = 0, // 8bit, gray + IMREAD_COLOR = 1, // ?, color + IMREAD_ANYDEPTH = 2, // any depth, ? + IMREAD_ANYCOLOR = 4 // ?, any color + }; + +enum { IMWRITE_JPEG_QUALITY = 1, + IMWRITE_JPEG_PROGRESSIVE = 2, + IMWRITE_JPEG_OPTIMIZE = 3, + IMWRITE_JPEG_RST_INTERVAL = 4, + IMWRITE_JPEG_LUMA_QUALITY = 5, + IMWRITE_JPEG_CHROMA_QUALITY = 6, + IMWRITE_PNG_COMPRESSION = 16, + IMWRITE_PNG_STRATEGY = 17, + IMWRITE_PNG_BILEVEL = 18, + IMWRITE_PXM_BINARY = 32, + IMWRITE_WEBP_QUALITY = 64 + }; + +enum { IMWRITE_PNG_STRATEGY_DEFAULT = 0, + IMWRITE_PNG_STRATEGY_FILTERED = 1, + IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY = 2, + IMWRITE_PNG_STRATEGY_RLE = 3, + IMWRITE_PNG_STRATEGY_FIXED = 4 + }; + +CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR ); + +CV_EXPORTS_W bool imwrite( const String& filename, InputArray img, + const std::vector& params = std::vector()); + +CV_EXPORTS_W Mat imdecode( InputArray buf, int flags ); + +CV_EXPORTS Mat imdecode( InputArray buf, int flags, Mat* dst); + +CV_EXPORTS_W bool imencode( const String& ext, InputArray img, + CV_OUT std::vector& buf, + const std::vector& params = std::vector()); + +} // cv + +#endif //__OPENCV_IMGCODECS_HPP__ diff --git a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs.hpp new file mode 100644 index 0000000000..a3cd232645 --- /dev/null +++ b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs.hpp @@ -0,0 +1,48 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifdef __OPENCV_BUILD +#error this is a compatibility header which should not be used inside the OpenCV library +#endif + +#include "opencv2/imgcodecs.hpp" diff --git a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h new file mode 100644 index 0000000000..ccd29a7c1c --- /dev/null +++ b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h @@ -0,0 +1,132 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_IMGCODECS_H__ +#define __OPENCV_IMGCODECS_H__ + +#include "opencv2/core/core_c.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +enum +{ +/* 8bit, color or not */ + CV_LOAD_IMAGE_UNCHANGED =-1, +/* 8bit, gray */ + CV_LOAD_IMAGE_GRAYSCALE =0, +/* ?, color */ + CV_LOAD_IMAGE_COLOR =1, +/* any depth, ? */ + CV_LOAD_IMAGE_ANYDEPTH =2, +/* ?, any color */ + CV_LOAD_IMAGE_ANYCOLOR =4 +}; + +/* load image from file + iscolor can be a combination of above flags where CV_LOAD_IMAGE_UNCHANGED + overrides the other flags + using CV_LOAD_IMAGE_ANYCOLOR alone is equivalent to CV_LOAD_IMAGE_UNCHANGED + unless CV_LOAD_IMAGE_ANYDEPTH is specified images are converted to 8bit +*/ +CVAPI(IplImage*) cvLoadImage( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); +CVAPI(CvMat*) cvLoadImageM( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); + +enum +{ + CV_IMWRITE_JPEG_QUALITY =1, + CV_IMWRITE_JPEG_PROGRESSIVE =2, + CV_IMWRITE_JPEG_OPTIMIZE =3, + CV_IMWRITE_JPEG_RST_INTERVAL =4, + CV_IMWRITE_JPEG_LUMA_QUALITY =5, + CV_IMWRITE_JPEG_CHROMA_QUALITY =6, + CV_IMWRITE_PNG_COMPRESSION =16, + CV_IMWRITE_PNG_STRATEGY =17, + CV_IMWRITE_PNG_BILEVEL =18, + CV_IMWRITE_PNG_STRATEGY_DEFAULT =0, + CV_IMWRITE_PNG_STRATEGY_FILTERED =1, + CV_IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY =2, + CV_IMWRITE_PNG_STRATEGY_RLE =3, + CV_IMWRITE_PNG_STRATEGY_FIXED =4, + CV_IMWRITE_PXM_BINARY =32, + CV_IMWRITE_WEBP_QUALITY =64 +}; + +/* save image to file */ +CVAPI(int) cvSaveImage( const char* filename, const CvArr* image, + const int* params CV_DEFAULT(0) ); + +/* decode image stored in the buffer */ +CVAPI(IplImage*) cvDecodeImage( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); +CVAPI(CvMat*) cvDecodeImageM( const CvMat* buf, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR)); + +/* encode image and store the result as a byte vector (single-row 8uC1 matrix) */ +CVAPI(CvMat*) cvEncodeImage( const char* ext, const CvArr* image, + const int* params CV_DEFAULT(0) ); + +enum +{ + CV_CVTIMG_FLIP =1, + CV_CVTIMG_SWAP_RB =2 +}; + +/* utility function: convert one image to another with optional vertical flip */ +CVAPI(void) cvConvertImage( const CvArr* src, CvArr* dst, int flags CV_DEFAULT(0)); + +CVAPI(int) cvHaveImageReader(const char* filename); +CVAPI(int) cvHaveImageWriter(const char* filename); + + +/****************************************************************************************\ +* Obsolete functions/synonyms * +\****************************************************************************************/ + +#define cvvLoadImage(name) cvLoadImage((name),1) +#define cvvSaveImage cvSaveImage +#define cvvConvertImage cvConvertImage + + +#ifdef __cplusplus +} +#endif + +#endif // __OPENCV_IMGCODECS_H__ diff --git a/modules/highgui/include/opencv2/highgui/ios.h b/modules/imgcodecs/include/opencv2/imgcodecs/ios.h similarity index 94% rename from modules/highgui/include/opencv2/highgui/ios.h rename to modules/imgcodecs/include/opencv2/imgcodecs/ios.h index a7f0395d71..8ec1356053 100644 --- a/modules/highgui/include/opencv2/highgui/ios.h +++ b/modules/imgcodecs/include/opencv2/imgcodecs/ios.h @@ -41,8 +41,11 @@ // //M*/ +#import +#import +#import +#import #include "opencv2/core/core.hpp" -#import "opencv2/highgui/cap_ios.h" UIImage* MatToUIImage(const cv::Mat& image); void UIImageToMat(const UIImage* image, diff --git a/modules/imgcodecs/perf/perf_main.cpp b/modules/imgcodecs/perf/perf_main.cpp new file mode 100644 index 0000000000..403402112d --- /dev/null +++ b/modules/imgcodecs/perf/perf_main.cpp @@ -0,0 +1,3 @@ +#include "perf_precomp.hpp" + +CV_PERF_TEST_MAIN(imgcodecs) diff --git a/modules/imgcodecs/perf/perf_precomp.hpp b/modules/imgcodecs/perf/perf_precomp.hpp new file mode 100644 index 0000000000..e6e34b40c6 --- /dev/null +++ b/modules/imgcodecs/perf/perf_precomp.hpp @@ -0,0 +1,19 @@ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_PERF_PRECOMP_HPP__ +#define __OPENCV_PERF_PRECOMP_HPP__ + +#include "opencv2/ts.hpp" +#include "opencv2/imgcodecs.hpp" + +#ifdef GTEST_CREATE_SHARED_LIBRARY +#error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined +#endif + +#endif diff --git a/modules/highgui/src/bitstrm.cpp b/modules/imgcodecs/src/bitstrm.cpp similarity index 100% rename from modules/highgui/src/bitstrm.cpp rename to modules/imgcodecs/src/bitstrm.cpp diff --git a/modules/highgui/src/bitstrm.hpp b/modules/imgcodecs/src/bitstrm.hpp similarity index 100% rename from modules/highgui/src/bitstrm.hpp rename to modules/imgcodecs/src/bitstrm.hpp diff --git a/modules/highgui/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp similarity index 100% rename from modules/highgui/src/grfmt_base.cpp rename to modules/imgcodecs/src/grfmt_base.cpp diff --git a/modules/highgui/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp similarity index 100% rename from modules/highgui/src/grfmt_base.hpp rename to modules/imgcodecs/src/grfmt_base.hpp diff --git a/modules/highgui/src/grfmt_bmp.cpp b/modules/imgcodecs/src/grfmt_bmp.cpp similarity index 100% rename from modules/highgui/src/grfmt_bmp.cpp rename to modules/imgcodecs/src/grfmt_bmp.cpp diff --git a/modules/highgui/src/grfmt_bmp.hpp b/modules/imgcodecs/src/grfmt_bmp.hpp similarity index 100% rename from modules/highgui/src/grfmt_bmp.hpp rename to modules/imgcodecs/src/grfmt_bmp.hpp diff --git a/modules/highgui/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp similarity index 100% rename from modules/highgui/src/grfmt_exr.cpp rename to modules/imgcodecs/src/grfmt_exr.cpp diff --git a/modules/highgui/src/grfmt_exr.hpp b/modules/imgcodecs/src/grfmt_exr.hpp similarity index 100% rename from modules/highgui/src/grfmt_exr.hpp rename to modules/imgcodecs/src/grfmt_exr.hpp diff --git a/modules/highgui/src/grfmt_hdr.cpp b/modules/imgcodecs/src/grfmt_hdr.cpp similarity index 100% rename from modules/highgui/src/grfmt_hdr.cpp rename to modules/imgcodecs/src/grfmt_hdr.cpp diff --git a/modules/highgui/src/grfmt_hdr.hpp b/modules/imgcodecs/src/grfmt_hdr.hpp similarity index 100% rename from modules/highgui/src/grfmt_hdr.hpp rename to modules/imgcodecs/src/grfmt_hdr.hpp diff --git a/modules/highgui/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp similarity index 91% rename from modules/highgui/src/grfmt_jpeg.cpp rename to modules/imgcodecs/src/grfmt_jpeg.cpp index 147f185e4c..ec1793287f 100644 --- a/modules/highgui/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -600,6 +600,9 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) int quality = 95; int progressive = 0; int optimize = 0; + int rst_interval = 0; + int luma_quality = -1; + int chroma_quality = -1; for( size_t i = 0; i < params.size(); i += 2 ) { @@ -618,15 +621,64 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) { optimize = params[i+1]; } + + if( params[i] == CV_IMWRITE_JPEG_LUMA_QUALITY ) + { + if (params[i+1] >= 0) + { + luma_quality = MIN(MAX(params[i+1], 0), 100); + + quality = luma_quality; + + if (chroma_quality < 0) + { + chroma_quality = luma_quality; + } + } + } + + if( params[i] == CV_IMWRITE_JPEG_CHROMA_QUALITY ) + { + if (params[i+1] >= 0) + { + chroma_quality = MIN(MAX(params[i+1], 0), 100); + } + } + + if( params[i] == CV_IMWRITE_JPEG_RST_INTERVAL ) + { + rst_interval = params[i+1]; + rst_interval = MIN(MAX(rst_interval, 0), 65535L); + } } jpeg_set_defaults( &cinfo ); + cinfo.restart_interval = rst_interval; + jpeg_set_quality( &cinfo, quality, TRUE /* limit to baseline-JPEG values */ ); if( progressive ) jpeg_simple_progression( &cinfo ); if( optimize ) cinfo.optimize_coding = TRUE; + +#if JPEG_LIB_VERSION >= 70 + if (luma_quality >= 0 && chroma_quality >= 0) + { + cinfo.q_scale_factor[0] = jpeg_quality_scaling(luma_quality); + cinfo.q_scale_factor[1] = jpeg_quality_scaling(chroma_quality); + if ( luma_quality != chroma_quality ) + { + /* disable subsampling - ref. Libjpeg.txt */ + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + } + jpeg_default_qtables( &cinfo, TRUE ); + } +#endif // #if JPEG_LIB_VERSION >= 70 + jpeg_start_compress( &cinfo, TRUE ); if( channels > 1 ) diff --git a/modules/highgui/src/grfmt_jpeg.hpp b/modules/imgcodecs/src/grfmt_jpeg.hpp similarity index 100% rename from modules/highgui/src/grfmt_jpeg.hpp rename to modules/imgcodecs/src/grfmt_jpeg.hpp diff --git a/modules/highgui/src/grfmt_jpeg2000.cpp b/modules/imgcodecs/src/grfmt_jpeg2000.cpp similarity index 100% rename from modules/highgui/src/grfmt_jpeg2000.cpp rename to modules/imgcodecs/src/grfmt_jpeg2000.cpp diff --git a/modules/highgui/src/grfmt_jpeg2000.hpp b/modules/imgcodecs/src/grfmt_jpeg2000.hpp similarity index 100% rename from modules/highgui/src/grfmt_jpeg2000.hpp rename to modules/imgcodecs/src/grfmt_jpeg2000.hpp diff --git a/modules/highgui/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp similarity index 100% rename from modules/highgui/src/grfmt_png.cpp rename to modules/imgcodecs/src/grfmt_png.cpp diff --git a/modules/highgui/src/grfmt_png.hpp b/modules/imgcodecs/src/grfmt_png.hpp similarity index 100% rename from modules/highgui/src/grfmt_png.hpp rename to modules/imgcodecs/src/grfmt_png.hpp diff --git a/modules/highgui/src/grfmt_pxm.cpp b/modules/imgcodecs/src/grfmt_pxm.cpp similarity index 100% rename from modules/highgui/src/grfmt_pxm.cpp rename to modules/imgcodecs/src/grfmt_pxm.cpp diff --git a/modules/highgui/src/grfmt_pxm.hpp b/modules/imgcodecs/src/grfmt_pxm.hpp similarity index 100% rename from modules/highgui/src/grfmt_pxm.hpp rename to modules/imgcodecs/src/grfmt_pxm.hpp diff --git a/modules/highgui/src/grfmt_sunras.cpp b/modules/imgcodecs/src/grfmt_sunras.cpp similarity index 100% rename from modules/highgui/src/grfmt_sunras.cpp rename to modules/imgcodecs/src/grfmt_sunras.cpp diff --git a/modules/highgui/src/grfmt_sunras.hpp b/modules/imgcodecs/src/grfmt_sunras.hpp similarity index 100% rename from modules/highgui/src/grfmt_sunras.hpp rename to modules/imgcodecs/src/grfmt_sunras.hpp diff --git a/modules/highgui/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp similarity index 96% rename from modules/highgui/src/grfmt_tiff.cpp rename to modules/imgcodecs/src/grfmt_tiff.cpp index 9013c39d11..06b2ab6a15 100644 --- a/modules/highgui/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -158,7 +158,7 @@ bool TiffDecoder::readHeader() m_type = CV_MAKETYPE(CV_8U, photometric > 1 ? wanted_channels : 1); break; case 16: - m_type = CV_MAKETYPE(CV_16U, photometric > 1 ? 3 : 1); + m_type = CV_MAKETYPE(CV_16U, photometric > 1 ? wanted_channels : 1); break; case 32: @@ -326,6 +326,21 @@ bool TiffDecoder::readData( Mat& img ) (ushort*)(data + img.step*i) + x*3, 0, cvSize(tile_width,1) ); } + else if (ncn == 4) + { + if (wanted_channels == 4) + { + icvCvt_BGRA2RGBA_16u_C4R(buffer16 + i*tile_width0*ncn, 0, + (ushort*)(data + img.step*i) + x * 4, 0, + cvSize(tile_width, 1)); + } + else + { + icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0, + (ushort*)(data + img.step*i) + x * 3, 0, + cvSize(tile_width, 1), 2); + } + } else { icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0, diff --git a/modules/highgui/src/grfmt_tiff.hpp b/modules/imgcodecs/src/grfmt_tiff.hpp similarity index 100% rename from modules/highgui/src/grfmt_tiff.hpp rename to modules/imgcodecs/src/grfmt_tiff.hpp diff --git a/modules/highgui/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp similarity index 100% rename from modules/highgui/src/grfmt_webp.cpp rename to modules/imgcodecs/src/grfmt_webp.cpp diff --git a/modules/highgui/src/grfmt_webp.hpp b/modules/imgcodecs/src/grfmt_webp.hpp similarity index 100% rename from modules/highgui/src/grfmt_webp.hpp rename to modules/imgcodecs/src/grfmt_webp.hpp diff --git a/modules/highgui/src/grfmts.hpp b/modules/imgcodecs/src/grfmts.hpp similarity index 100% rename from modules/highgui/src/grfmts.hpp rename to modules/imgcodecs/src/grfmts.hpp diff --git a/modules/highgui/src/ios_conversions.mm b/modules/imgcodecs/src/ios_conversions.mm similarity index 97% rename from modules/highgui/src/ios_conversions.mm rename to modules/imgcodecs/src/ios_conversions.mm index fa6208a17f..af522900c8 100644 --- a/modules/highgui/src/ios_conversions.mm +++ b/modules/imgcodecs/src/ios_conversions.mm @@ -40,7 +40,11 @@ // //M*/ -#import "opencv2/highgui/cap_ios.h" +#import +#import +#import +#import +#include "opencv2/core.hpp" #include "precomp.hpp" UIImage* MatToUIImage(const cv::Mat& image) { diff --git a/modules/highgui/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp similarity index 99% rename from modules/highgui/src/loadsave.cpp rename to modules/imgcodecs/src/loadsave.cpp index cdcaa23e5d..a3fcaba630 100644 --- a/modules/highgui/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -454,7 +454,7 @@ bool imencode( const String& ext, InputArray _image, } /****************************************************************************************\ -* HighGUI loading & saving function implementation * +* Imgcodecs loading & saving function implementation * \****************************************************************************************/ CV_IMPL int diff --git a/modules/imgcodecs/src/precomp.hpp b/modules/imgcodecs/src/precomp.hpp new file mode 100644 index 0000000000..19e6fb9e3c --- /dev/null +++ b/modules/imgcodecs/src/precomp.hpp @@ -0,0 +1,84 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __IMGCODECS_H_ +#define __IMGCODECS_H_ + +#include "opencv2/imgcodecs.hpp" + +#include "opencv2/core/utility.hpp" +#include "opencv2/core/private.hpp" + +#include "opencv2/imgproc/imgproc_c.h" +#include "opencv2/imgcodecs/imgcodecs_c.h" + +#include +#include +#include +#include +#include +#include + +#if defined WIN32 || defined WINCE + #if !defined _WIN32_WINNT + #ifdef HAVE_MSMF + #define _WIN32_WINNT 0x0600 // Windows Vista + #else + #define _WIN32_WINNT 0x0500 // Windows 2000 + #endif + #endif + + #include + #undef small + #undef min + #undef max + #undef abs +#endif + +#ifdef HAVE_TEGRA_OPTIMIZATION +#include "opencv2/imgcodecs/imgcodecs_tegra.hpp" +#endif + +#define __BEGIN__ __CV_BEGIN__ +#define __END__ __CV_END__ +#define EXIT __CV_EXIT__ + +#endif /* __IMGCODECS_H_ */ diff --git a/modules/highgui/src/rgbe.cpp b/modules/imgcodecs/src/rgbe.cpp similarity index 100% rename from modules/highgui/src/rgbe.cpp rename to modules/imgcodecs/src/rgbe.cpp diff --git a/modules/highgui/src/rgbe.hpp b/modules/imgcodecs/src/rgbe.hpp similarity index 100% rename from modules/highgui/src/rgbe.hpp rename to modules/imgcodecs/src/rgbe.hpp diff --git a/modules/highgui/src/utils.cpp b/modules/imgcodecs/src/utils.cpp similarity index 100% rename from modules/highgui/src/utils.cpp rename to modules/imgcodecs/src/utils.cpp diff --git a/modules/highgui/src/utils.hpp b/modules/imgcodecs/src/utils.hpp similarity index 100% rename from modules/highgui/src/utils.hpp rename to modules/imgcodecs/src/utils.hpp diff --git a/modules/highgui/test/test_drawing.cpp b/modules/imgcodecs/test/test_drawing.cpp similarity index 98% rename from modules/highgui/test/test_drawing.cpp rename to modules/imgcodecs/test/test_drawing.cpp index 9d9e17dde2..e4936f63a0 100644 --- a/modules/highgui/test/test_drawing.cpp +++ b/modules/imgcodecs/test/test_drawing.cpp @@ -41,7 +41,6 @@ //M*/ #include "test_precomp.hpp" -#include "opencv2/highgui.hpp" using namespace std; using namespace cv; @@ -408,8 +407,8 @@ int CV_DrawingTest_C::checkLineIterator( Mat& _img ) } #ifdef HAVE_JPEG -TEST(Highgui_Drawing, cpp_regression) { CV_DrawingTest_CPP test; test.safe_run(); } -TEST(Highgui_Drawing, c_regression) { CV_DrawingTest_C test; test.safe_run(); } +TEST(Imgcodecs_Drawing, cpp_regression) { CV_DrawingTest_CPP test; test.safe_run(); } +TEST(Imgcodecs_Drawing, c_regression) { CV_DrawingTest_C test; test.safe_run(); } #endif class CV_FillConvexPolyTest : public cvtest::BaseTest @@ -444,4 +443,4 @@ protected: } }; -TEST(Highgui_Drawing, fillconvexpoly_clipping) { CV_FillConvexPolyTest test; test.safe_run(); } +TEST(Imgcodecs_Drawing, fillconvexpoly_clipping) { CV_FillConvexPolyTest test; test.safe_run(); } diff --git a/modules/highgui/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp similarity index 92% rename from modules/highgui/test/test_grfmt.cpp rename to modules/imgcodecs/test/test_grfmt.cpp index e4d3e70461..ed991a6f31 100644 --- a/modules/highgui/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -41,7 +41,6 @@ //M*/ #include "test_precomp.hpp" -#include "opencv2/highgui.hpp" using namespace cv; using namespace std; @@ -140,9 +139,6 @@ public: string filename = cv::tempfile(".jpg"); imwrite(filename, img); - img = imread(filename, IMREAD_UNCHANGED); - - filename = string(ts->get_data_path() + "readwrite/test_" + char(k + 48) + "_c" + char(num_channels + 48) + ".jpg"); ts->printf(ts->LOG, "reading test image : %s\n", filename.c_str()); Mat img_test = imread(filename, IMREAD_UNCHANGED); @@ -161,8 +157,9 @@ public: #endif #ifdef HAVE_TIFF - for (int num_channels = 1; num_channels <= 3; num_channels+=2) + for (int num_channels = 1; num_channels <= 4; num_channels++) { + if (num_channels == 2) continue; // tiff ts->printf(ts->LOG, "image type depth:%d channels:%d ext: %s\n", CV_16U, num_channels, ".tiff"); Mat img(img_r * k, img_c * k, CV_MAKETYPE(CV_16U, num_channels), Scalar::all(0)); @@ -223,12 +220,12 @@ public: #ifdef HAVE_PNG -TEST(Highgui_Image, write_big) { CV_GrfmtWriteBigImageTest test; test.safe_run(); } +TEST(Imgcodecs_Image, write_big) { CV_GrfmtWriteBigImageTest test; test.safe_run(); } #endif -TEST(Highgui_Image, write_imageseq) { CV_GrfmtWriteSequenceImageTest test; test.safe_run(); } +TEST(Imgcodecs_Image, write_imageseq) { CV_GrfmtWriteSequenceImageTest test; test.safe_run(); } -TEST(Highgui_Image, read_bmp_rle8) { CV_GrfmtReadBMPRLE8Test test; test.safe_run(); } +TEST(Imgcodecs_Image, read_bmp_rle8) { CV_GrfmtReadBMPRLE8Test test; test.safe_run(); } #ifdef HAVE_PNG class CV_GrfmtPNGEncodeTest : public cvtest::BaseTest @@ -257,9 +254,9 @@ public: } }; -TEST(Highgui_Image, encode_png) { CV_GrfmtPNGEncodeTest test; test.safe_run(); } +TEST(Imgcodecs_Image, encode_png) { CV_GrfmtPNGEncodeTest test; test.safe_run(); } -TEST(Highgui_ImreadVSCvtColor, regression) +TEST(Imgcodecs_ImreadVSCvtColor, regression) { cvtest::TS& ts = *cvtest::TS::ptr(); @@ -375,11 +372,11 @@ public: } }; -TEST(Highgui_Image, read_png_color_palette_with_alpha) { CV_GrfmtReadPNGColorPaletteWithAlphaTest test; test.safe_run(); } +TEST(Imgcodecs_Image, read_png_color_palette_with_alpha) { CV_GrfmtReadPNGColorPaletteWithAlphaTest test; test.safe_run(); } #endif #ifdef HAVE_JPEG -TEST(Highgui_Jpeg, encode_empty) +TEST(Imgcodecs_Jpeg, encode_empty) { cv::Mat img; std::vector jpegImg; @@ -387,7 +384,7 @@ TEST(Highgui_Jpeg, encode_empty) ASSERT_THROW(cv::imencode(".jpg", img, jpegImg), cv::Exception); } -TEST(Highgui_Jpeg, encode_decode_progressive_jpeg) +TEST(Imgcodecs_Jpeg, encode_decode_progressive_jpeg) { cvtest::TS& ts = *cvtest::TS::ptr(); string input = string(ts.get_data_path()) + "../cv/shared/lena.png"; @@ -411,7 +408,7 @@ TEST(Highgui_Jpeg, encode_decode_progressive_jpeg) remove(output_progressive.c_str()); } -TEST(Highgui_Jpeg, encode_decode_optimize_jpeg) +TEST(Imgcodecs_Jpeg, encode_decode_optimize_jpeg) { cvtest::TS& ts = *cvtest::TS::ptr(); string input = string(ts.get_data_path()) + "../cv/shared/lena.png"; @@ -434,6 +431,31 @@ TEST(Highgui_Jpeg, encode_decode_optimize_jpeg) remove(output_optimized.c_str()); } + +TEST(Imgcodecs_Jpeg, encode_decode_rst_jpeg) +{ + cvtest::TS& ts = *cvtest::TS::ptr(); + string input = string(ts.get_data_path()) + "../cv/shared/lena.png"; + cv::Mat img = cv::imread(input); + ASSERT_FALSE(img.empty()); + + std::vector params; + params.push_back(IMWRITE_JPEG_RST_INTERVAL); + params.push_back(1); + + string output_rst = cv::tempfile(".jpg"); + EXPECT_NO_THROW(cv::imwrite(output_rst, img, params)); + cv::Mat img_jpg_rst = cv::imread(output_rst); + + string output_normal = cv::tempfile(".jpg"); + EXPECT_NO_THROW(cv::imwrite(output_normal, img)); + cv::Mat img_jpg_normal = cv::imread(output_normal); + + EXPECT_EQ(0, cvtest::norm(img_jpg_rst, img_jpg_normal, NORM_INF)); + + remove(output_rst.c_str()); +} + #endif @@ -447,9 +469,9 @@ TEST(Highgui_Jpeg, encode_decode_optimize_jpeg) #ifdef ANDROID // Test disabled as it uses a lot of memory. // It is killed with SIGKILL by out of memory killer. -TEST(Highgui_Tiff, DISABLED_decode_tile16384x16384) +TEST(Imgcodecs_Tiff, DISABLED_decode_tile16384x16384) #else -TEST(Highgui_Tiff, decode_tile16384x16384) +TEST(Imgcodecs_Tiff, decode_tile16384x16384) #endif { // see issue #2161 @@ -478,7 +500,7 @@ TEST(Highgui_Tiff, decode_tile16384x16384) remove(file4.c_str()); } -TEST(Highgui_Tiff, write_read_16bit_big_little_endian) +TEST(Imgcodecs_Tiff, write_read_16bit_big_little_endian) { // see issue #2601 "16-bit Grayscale TIFF Load Failures Due to Buffer Underflow and Endianness" @@ -561,7 +583,7 @@ public: } }; -TEST(Highgui_Tiff, decode_tile_remainder) +TEST(Imgcodecs_Tiff, decode_tile_remainder) { CV_GrfmtReadTifTiledWithNotFullTiles test; test.safe_run(); } @@ -570,7 +592,7 @@ TEST(Highgui_Tiff, decode_tile_remainder) #ifdef HAVE_WEBP -TEST(Highgui_WebP, encode_decode_lossless_webp) +TEST(Imgcodecs_WebP, encode_decode_lossless_webp) { cvtest::TS& ts = *cvtest::TS::ptr(); string input = string(ts.get_data_path()) + "../cv/shared/lena.png"; @@ -619,7 +641,7 @@ TEST(Highgui_WebP, encode_decode_lossless_webp) EXPECT_TRUE(cvtest::norm(img, img_webp, NORM_INF) == 0); } -TEST(Highgui_WebP, encode_decode_lossy_webp) +TEST(Imgcodecs_WebP, encode_decode_lossy_webp) { cvtest::TS& ts = *cvtest::TS::ptr(); std::string input = std::string(ts.get_data_path()) + "../cv/shared/lena.png"; @@ -643,7 +665,7 @@ TEST(Highgui_WebP, encode_decode_lossy_webp) } } -TEST(Highgui_WebP, encode_decode_with_alpha_webp) +TEST(Imgcodecs_WebP, encode_decode_with_alpha_webp) { cvtest::TS& ts = *cvtest::TS::ptr(); std::string input = std::string(ts.get_data_path()) + "../cv/shared/lena.png"; @@ -669,7 +691,7 @@ TEST(Highgui_WebP, encode_decode_with_alpha_webp) #endif -TEST(Highgui_Hdr, regression) +TEST(Imgcodecs_Hdr, regression) { string folder = string(cvtest::TS::ptr()->get_data_path()) + "/readwrite/"; string name_rle = folder + "rle.hdr"; diff --git a/modules/imgcodecs/test/test_main.cpp b/modules/imgcodecs/test/test_main.cpp new file mode 100644 index 0000000000..461e7fac7f --- /dev/null +++ b/modules/imgcodecs/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("imgcodecs") diff --git a/modules/imgcodecs/test/test_precomp.hpp b/modules/imgcodecs/test/test_precomp.hpp new file mode 100644 index 0000000000..2aed614337 --- /dev/null +++ b/modules/imgcodecs/test/test_precomp.hpp @@ -0,0 +1,20 @@ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include +#include "opencv2/ts.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/imgcodecs.hpp" +#include "opencv2/imgproc/imgproc_c.h" + +#include "opencv2/core/private.hpp" + +#endif diff --git a/modules/imgproc/doc/histograms.rst b/modules/imgproc/doc/histograms.rst index 91199f3781..5ebf168611 100644 --- a/modules/imgproc/doc/histograms.rst +++ b/modules/imgproc/doc/histograms.rst @@ -181,6 +181,8 @@ Compares two histograms. * **CV_COMP_HELLINGER** Synonym for ``CV_COMP_BHATTACHARYYA`` + * **CV_COMP_KL_DIV** Kullback-Leibler divergence + The functions ``compareHist`` compare two dense or two sparse histograms using the specified method: * Correlation (``method=CV_COMP_CORREL``) @@ -224,6 +226,12 @@ The functions ``compareHist`` compare two dense or two sparse histograms using t d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}} +* Kullback-Leibler divergence (``method=CV_COMP_KL_DIV``). + + .. math:: + + d(H_1,H_2) = \sum _I H_1(I) \log \left(\frac{H_1(I)}{H_2(I)}\right) + The function returns :math:`d(H_1, H_2)` . diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 7928ae0fe8..76d65c2802 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -204,7 +204,8 @@ enum { HISTCMP_CORREL = 0, HISTCMP_INTERSECT = 2, HISTCMP_BHATTACHARYYA = 3, HISTCMP_HELLINGER = HISTCMP_BHATTACHARYYA, - HISTCMP_CHISQR_ALT = 4 + HISTCMP_CHISQR_ALT = 4, + HISTCMP_KL_DIV = 5 }; //! the color conversion code diff --git a/modules/imgproc/include/opencv2/imgproc/types_c.h b/modules/imgproc/include/opencv2/imgproc/types_c.h index dd0d8b8a6e..de8fb62038 100644 --- a/modules/imgproc/include/opencv2/imgproc/types_c.h +++ b/modules/imgproc/include/opencv2/imgproc/types_c.h @@ -509,7 +509,8 @@ enum CV_COMP_INTERSECT =2, CV_COMP_BHATTACHARYYA =3, CV_COMP_HELLINGER =CV_COMP_BHATTACHARYYA, - CV_COMP_CHISQR_ALT =4 + CV_COMP_CHISQR_ALT =4, + CV_COMP_KL_DIV =5 }; /* Mask size for distance transform */ diff --git a/modules/imgproc/perf/opencl/perf_imgproc.cpp b/modules/imgproc/perf/opencl/perf_imgproc.cpp index 1b3ba7f19e..7f0770853d 100644 --- a/modules/imgproc/perf/opencl/perf_imgproc.cpp +++ b/modules/imgproc/perf/opencl/perf_imgproc.cpp @@ -231,7 +231,7 @@ OCL_PERF_TEST_P(IntegralFixture, Integral1, ::testing::Combine(OCL_TEST_SIZES, O OCL_TEST_CYCLE() cv::integral(src, dst, ddepth); - SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); + SANITY_CHECK(dst, 2e-6, ERROR_RELATIVE); } OCL_PERF_TEST_P(IntegralFixture, Integral2, ::testing::Combine(OCL_TEST_SIZES, OCL_PERF_ENUM(CV_32S, CV_32F))) @@ -243,11 +243,11 @@ OCL_PERF_TEST_P(IntegralFixture, Integral2, ::testing::Combine(OCL_TEST_SIZES, O checkDeviceMaxMemoryAllocSize(srcSize, ddepth); UMat src(srcSize, CV_8UC1), sum(srcSize + Size(1, 1), ddepth), sqsum(srcSize + Size(1, 1), CV_32F); - declare.in(src, WARMUP_RNG).out(sum).out(sqsum); + declare.in(src, WARMUP_RNG).out(sum, sqsum); OCL_TEST_CYCLE() cv::integral(src, sum, sqsum, ddepth, CV_32F); - SANITY_CHECK(sum, 1e-6, ERROR_RELATIVE); + SANITY_CHECK(sum, 2e-4, ERROR_RELATIVE); SANITY_CHECK(sqsum, 5e-5, ERROR_RELATIVE); } diff --git a/modules/imgproc/perf/perf_moments.cpp b/modules/imgproc/perf/perf_moments.cpp new file mode 100644 index 0000000000..9b3c5428f3 --- /dev/null +++ b/modules/imgproc/perf/perf_moments.cpp @@ -0,0 +1,38 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// Copyright (C) 2014, Itseez, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace perf; +using namespace testing; +using std::tr1::make_tuple; +using std::tr1::get; + +typedef std::tr1::tuple MomentsParams_t; +typedef perf::TestBaseWithParam MomentsFixture_val; + +PERF_TEST_P(MomentsFixture_val, Moments1, + ::testing::Combine( + testing::Values(TYPICAL_MAT_SIZES), + testing::Values(CV_16U, CV_16S, CV_32F, CV_64F), + testing::Bool())) +{ + const MomentsParams_t params = GetParam(); + const Size srcSize = get<0>(params); + const MatDepth srcDepth = get<1>(params); + const bool binaryImage = get<2>(params); + + cv::Moments m; + Mat src(srcSize, srcDepth); + declare.in(src, WARMUP_RNG); + + TEST_CYCLE() m = cv::moments(src, binaryImage); + + SANITY_CHECK_MOMENTS(m, 1e-4, ERROR_RELATIVE); +} diff --git a/modules/imgproc/perf/perf_precomp.hpp b/modules/imgproc/perf/perf_precomp.hpp index 19f62712c1..5bede78db8 100644 --- a/modules/imgproc/perf/perf_precomp.hpp +++ b/modules/imgproc/perf/perf_precomp.hpp @@ -11,7 +11,7 @@ #include "opencv2/ts.hpp" #include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" #ifdef GTEST_CREATE_SHARED_LIBRARY #error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined diff --git a/modules/imgproc/src/canny.cpp b/modules/imgproc/src/canny.cpp index 3590e290db..2a87ae05b4 100644 --- a/modules/imgproc/src/canny.cpp +++ b/modules/imgproc/src/canny.cpp @@ -348,6 +348,10 @@ void cv::Canny( InputArray _src, OutputArray _dst, #define CANNY_PUSH(d) *(d) = uchar(2), *stack_top++ = (d) #define CANNY_POP(d) (d) = *--stack_top +#if CV_SSE2 + bool haveSSE2 = checkHardwareSupport(CV_CPU_SSE2); +#endif + // calculate magnitude and angle of gradient, perform non-maxima suppression. // fill the map with one of the following values: // 0 - the pixel might belong to an edge @@ -363,12 +367,52 @@ void cv::Canny( InputArray _src, OutputArray _dst, if (!L2gradient) { - for (int j = 0; j < src.cols*cn; j++) + int j = 0, width = src.cols * cn; +#if CV_SSE2 + if (haveSSE2) + { + __m128i v_zero = _mm_setzero_si128(); + for ( ; j <= width - 8; j += 8) + { + __m128i v_dx = _mm_loadu_si128((const __m128i *)(_dx + j)); + __m128i v_dy = _mm_loadu_si128((const __m128i *)(_dy + j)); + v_dx = _mm_max_epi16(v_dx, _mm_sub_epi16(v_zero, v_dx)); + v_dy = _mm_max_epi16(v_dy, _mm_sub_epi16(v_zero, v_dy)); + + __m128i v_norm = _mm_add_epi32(_mm_unpacklo_epi16(v_dx, v_zero), _mm_unpacklo_epi16(v_dy, v_zero)); + _mm_storeu_si128((__m128i *)(_norm + j), v_norm); + + v_norm = _mm_add_epi32(_mm_unpackhi_epi16(v_dx, v_zero), _mm_unpackhi_epi16(v_dy, v_zero)); + _mm_storeu_si128((__m128i *)(_norm + j + 4), v_norm); + } + } +#endif + for ( ; j < width; ++j) _norm[j] = std::abs(int(_dx[j])) + std::abs(int(_dy[j])); } else { - for (int j = 0; j < src.cols*cn; j++) + int j = 0, width = src.cols * cn; +#if CV_SSE2 + if (haveSSE2) + { + for ( ; j <= width - 8; j += 8) + { + __m128i v_dx = _mm_loadu_si128((const __m128i *)(_dx + j)); + __m128i v_dy = _mm_loadu_si128((const __m128i *)(_dy + j)); + + __m128i v_dx_ml = _mm_mullo_epi16(v_dx, v_dx), v_dx_mh = _mm_mulhi_epi16(v_dx, v_dx); + __m128i v_dy_ml = _mm_mullo_epi16(v_dy, v_dy), v_dy_mh = _mm_mulhi_epi16(v_dy, v_dy); + + __m128i v_norm = _mm_add_epi32(_mm_unpacklo_epi16(v_dx_ml, v_dx_mh), _mm_unpacklo_epi16(v_dy_ml, v_dy_mh)); + _mm_storeu_si128((__m128i *)(_norm + j), v_norm); + + v_norm = _mm_add_epi32(_mm_unpackhi_epi16(v_dx_ml, v_dx_mh), _mm_unpackhi_epi16(v_dy_ml, v_dy_mh)); + _mm_storeu_si128((__m128i *)(_norm + j + 4), v_norm); + } + } +#endif + for ( ; j < width; ++j) _norm[j] = int(_dx[j])*_dx[j] + int(_dy[j])*_dy[j]; } diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index 94401781ee..fe460ee75a 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -2730,8 +2730,6 @@ struct mRGBA2RGBA #ifdef HAVE_OPENCL -#define DIVUP(total, grain) (((total) + (grain) - 1) / (grain)) - static bool ocl_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) { bool ok = false; @@ -2739,23 +2737,17 @@ static bool ocl_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) Size sz = src.size(), dstSz = sz; int scn = src.channels(), depth = src.depth(), bidx; int dims = 2, stripeSize = 1; - size_t globalsize[] = { src.cols, src.rows }; ocl::Kernel k; if (depth != CV_8U && depth != CV_16U && depth != CV_32F) return false; - cv::String opts = format("-D depth=%d -D scn=%d ", depth, scn); - ocl::Device dev = ocl::Device::getDefault(); - int pxPerWIy = 1; - if (dev.isIntel() && (dev.type() & ocl::Device::TYPE_GPU) && - !(code == CV_BGR2Luv || code == CV_RGB2Luv || code == CV_LBGR2Luv || code == CV_LRGB2Luv || - code == CV_Luv2BGR || code == CV_Luv2RGB || code == CV_Luv2LBGR || code == CV_Luv2LRGB)) - pxPerWIy = 4; - - globalsize[1] = DIVUP(globalsize[1], pxPerWIy); - opts += format("-D PIX_PER_WI_Y=%d ", pxPerWIy); + int pxPerWIy = dev.isIntel() && (dev.type() & ocl::Device::TYPE_GPU) ? 4 : 1; + + size_t globalsize[] = { src.cols, (src.rows + pxPerWIy - 1) / pxPerWIy }; + cv::String opts = format("-D depth=%d -D scn=%d -D PIX_PER_WI_Y=%d ", + depth, scn, pxPerWIy); switch (code) { diff --git a/modules/imgproc/src/contours.cpp b/modules/imgproc/src/contours.cpp index 5ea4a07c61..ee7f21ac88 100644 --- a/modules/imgproc/src/contours.cpp +++ b/modules/imgproc/src/contours.cpp @@ -1704,8 +1704,10 @@ void cv::findContours( InputOutputArray _image, OutputArrayOfArrays _contours, OutputArray _hierarchy, int mode, int method, Point offset ) { // Sanity check: output must be of type vector> - CV_Assert( _contours.kind() == _InputArray::STD_VECTOR_VECTOR && - _contours.channels() == 2 && _contours.depth() == CV_32S ); + CV_Assert((_contours.kind() == _InputArray::STD_VECTOR_VECTOR || _contours.kind() == _InputArray::STD_VECTOR_MAT || + _contours.kind() == _InputArray::STD_VECTOR_UMAT)); + + CV_Assert(_contours.empty() || (_contours.channels() == 2 && _contours.depth() == CV_32S)); Mat image = _image.getMat(); MemStorage storage(cvCreateMemStorage()); diff --git a/modules/imgproc/src/corner.cpp b/modules/imgproc/src/corner.cpp index eeb20fbc16..923d78b30f 100644 --- a/modules/imgproc/src/corner.cpp +++ b/modules/imgproc/src/corner.cpp @@ -608,6 +608,11 @@ void cv::preCornerDetect( InputArray _src, OutputArray _dst, int ksize, int bord factor *= 255; factor = 1./(factor * factor * factor); +#if CV_SSE2 + volatile bool haveSSE2 = cv::checkHardwareSupport(CV_CPU_SSE2); + __m128 v_factor = _mm_set1_ps((float)factor), v_m2 = _mm_set1_ps(-2.0f); +#endif + Size size = src.size(); int i, j; for( i = 0; i < size.height; i++ ) @@ -619,7 +624,26 @@ void cv::preCornerDetect( InputArray _src, OutputArray _dst, int ksize, int bord const float* d2ydata = (const float*)(D2y.data + i*D2y.step); const float* dxydata = (const float*)(Dxy.data + i*Dxy.step); - for( j = 0; j < size.width; j++ ) + j = 0; + +#if CV_SSE2 + if (haveSSE2) + { + for( ; j <= size.width - 4; j += 4 ) + { + __m128 v_dx = _mm_loadu_ps((const float *)(dxdata + j)); + __m128 v_dy = _mm_loadu_ps((const float *)(dydata + j)); + + __m128 v_s1 = _mm_mul_ps(_mm_mul_ps(v_dx, v_dx), _mm_loadu_ps((const float *)(d2ydata + j))); + __m128 v_s2 = _mm_mul_ps(_mm_mul_ps(v_dy, v_dy), _mm_loadu_ps((const float *)(d2xdata + j))); + __m128 v_s3 = _mm_mul_ps(_mm_mul_ps(v_dx, v_dy), _mm_loadu_ps((const float *)(dxydata + j))); + v_s1 = _mm_mul_ps(v_factor, _mm_add_ps(v_s1, _mm_add_ps(v_s2, _mm_mul_ps(v_s3, v_m2)))); + _mm_storeu_ps(dstdata + j, v_s1); + } + } +#endif + + for( ; j < size.width; j++ ) { float dx = dxdata[j]; float dy = dydata[j]; diff --git a/modules/imgproc/src/deriv.cpp b/modules/imgproc/src/deriv.cpp index 5e920ec0b8..1a29c38abf 100644 --- a/modules/imgproc/src/deriv.cpp +++ b/modules/imgproc/src/deriv.cpp @@ -702,7 +702,7 @@ void cv::Laplacian( InputArray _src, OutputArray _dst, int ddepth, int ksize, #ifdef HAVE_IPP if ((ksize == 3 || ksize == 5) && ((borderType & BORDER_ISOLATED) != 0 || !_src.isSubmatrix()) && - ((stype == CV_8UC1 && ddepth == CV_16S) || (ddepth == CV_32F && stype == CV_32FC1))) + ((stype == CV_8UC1 && ddepth == CV_16S) || (ddepth == CV_32F && stype == CV_32FC1)) && !ocl::useOpenCL()) { int iscale = saturate_cast(scale), idelta = saturate_cast(delta); bool floatScale = std::fabs(scale - iscale) > DBL_EPSILON, needScale = iscale != 1; diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index e51986c394..6c0da79ccf 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -3471,7 +3471,8 @@ static bool ocl_sepColFilter2D(const UMat & buf, UMat & dst, const Mat & kernelY return k.run(2, globalsize, localsize, false); } -const int optimizedSepFilterLocalSize = 16; +const int optimizedSepFilterLocalWidth = 16; +const int optimizedSepFilterLocalHeight = 8; static bool ocl_sepFilter2D_SinglePass(InputArray _src, OutputArray _dst, Mat row_kernel, Mat col_kernel, @@ -3491,8 +3492,8 @@ static bool ocl_sepFilter2D_SinglePass(InputArray _src, OutputArray _dst, borderType == BORDER_REFLECT_101)) return false; - size_t lt2[2] = { optimizedSepFilterLocalSize, optimizedSepFilterLocalSize }; - size_t gt2[2] = { lt2[0] * (1 + (size.width - 1) / lt2[0]), lt2[1] * (1 + (size.height - 1) / lt2[1]) }; + size_t lt2[2] = { optimizedSepFilterLocalWidth, optimizedSepFilterLocalHeight }; + size_t gt2[2] = { lt2[0] * (1 + (size.width - 1) / lt2[0]), lt2[1]}; char cvt[2][40]; const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE", "BORDER_REFLECT", "BORDER_WRAP", @@ -3584,8 +3585,8 @@ static bool ocl_sepFilter2D( InputArray _src, OutputArray _dst, int ddepth, } CV_OCL_RUN_(kernelY.cols <= 21 && kernelX.cols <= 21 && - imgSize.width > optimizedSepFilterLocalSize + anchor.x && - imgSize.height > optimizedSepFilterLocalSize + anchor.y && + imgSize.width > optimizedSepFilterLocalWidth + anchor.x && + imgSize.height > optimizedSepFilterLocalHeight + anchor.y && (!(borderType & BORDER_ISOLATED) || _src.offset() == 0) && anchor == Point(kernelX.cols >> 1, kernelY.cols >> 1) && (d.isIntel() || (d.isAMD() && !d.hostUnifiedMemory())), diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index 441d2226b8..b38ddcad0c 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -2325,6 +2325,21 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method ) s2 += b; } } + else if( method == CV_COMP_KL_DIV ) + { + for( j = 0; j < len; j++ ) + { + double p = h1[j]; + double q = h2[j]; + if( fabs(p) <= DBL_EPSILON ) { + continue; + } + if( fabs(q) <= DBL_EPSILON ) { + q = 1e-10; + } + result += p * std::log( p / q ); + } + } else CV_Error( CV_StsBadArg, "Unknown comparison method" ); } @@ -2360,7 +2375,7 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) CV_Assert( H1.size(i) == H2.size(i) ); const SparseMat *PH1 = &H1, *PH2 = &H2; - if( PH1->nzcount() > PH2->nzcount() && method != CV_COMP_CHISQR && method != CV_COMP_CHISQR_ALT) + if( PH1->nzcount() > PH2->nzcount() && method != CV_COMP_CHISQR && method != CV_COMP_CHISQR_ALT && method != CV_COMP_KL_DIV ) std::swap(PH1, PH2); SparseMatConstIterator it = PH1->begin(); @@ -2440,6 +2455,18 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) s1 = fabs(s1) > FLT_EPSILON ? 1./std::sqrt(s1) : 1.; result = std::sqrt(std::max(1. - result*s1, 0.)); } + else if( method == CV_COMP_KL_DIV ) + { + for( i = 0; i < N1; i++, ++it ) + { + double v1 = it.value(); + const SparseMat::Node* node = it.node(); + double v2 = PH2->value(node->idx, (size_t*)&node->hashval); + if( !v2 ) + v2 = 1e-10; + result += v1 * std::log( v1 / v2 ); + } + } else CV_Error( CV_StsBadArg, "Unknown comparison method" ); @@ -2785,7 +2812,7 @@ cvCompareHist( const CvHistogram* hist1, CvSparseMatIterator iterator; CvSparseNode *node1, *node2; - if( mat1->heap->active_count > mat2->heap->active_count && method != CV_COMP_CHISQR && method != CV_COMP_CHISQR_ALT) + if( mat1->heap->active_count > mat2->heap->active_count && method != CV_COMP_CHISQR && method != CV_COMP_CHISQR_ALT && method != CV_COMP_KL_DIV ) { CvSparseMat* t; CV_SWAP( mat1, mat2, t ); @@ -2887,6 +2914,13 @@ cvCompareHist( const CvHistogram* hist1, result = 1. - result*s1; result = sqrt(MAX(result,0.)); } + else if( method == CV_COMP_KL_DIV ) + { + cv::SparseMat sH1, sH2; + ((const CvSparseMat*)hist1->bins)->copyToSparseMat(sH1); + ((const CvSparseMat*)hist2->bins)->copyToSparseMat(sH2); + result = cv::compareHist( sH1, sH2, CV_COMP_KL_DIV ); + } else CV_Error( CV_StsBadArg, "Unknown comparison method" ); diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 8b3c4be096..1d16bcc3fc 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3582,7 +3582,9 @@ private: static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, InputArray _map2, int interpolation, int borderType, const Scalar& borderValue) { - int cn = _src.channels(), type = _src.type(), depth = _src.depth(); + const ocl::Device & dev = ocl::Device::getDefault(); + int cn = _src.channels(), type = _src.type(), depth = _src.depth(), + rowsPerWI = dev.isIntel() ? 4 : 1; if (borderType == BORDER_TRANSPARENT || !(interpolation == INTER_LINEAR || interpolation == INTER_NEAREST) || _map1.type() == CV_16SC1 || _map2.type() == CV_16SC1) @@ -3619,12 +3621,14 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input static const char * const interMap[] = { "INTER_NEAREST", "INTER_LINEAR", "INTER_CUBIC", "INTER_LINEAR", "INTER_LANCZOS" }; static const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE", "BORDER_REFLECT", "BORDER_WRAP", "BORDER_REFLECT_101", "BORDER_TRANSPARENT" }; - String buildOptions = format("-D %s -D %s -D T=%s", interMap[interpolation], borderMap[borderType], ocl::typeToStr(type)); + String buildOptions = format("-D %s -D %s -D T=%s -D rowsPerWI=%d", + interMap[interpolation], borderMap[borderType], + ocl::typeToStr(type), rowsPerWI); if (interpolation != INTER_NEAREST) { char cvt[3][40]; - int wdepth = std::max(CV_32F, dst.depth()); + int wdepth = std::max(CV_32F, depth); buildOptions = buildOptions + format(" -D WT=%s -D convertToT=%s -D convertToWT=%s" " -D convertToWT2=%s -D WT2=%s", @@ -3636,10 +3640,9 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input } int scalarcn = cn == 3 ? 4 : cn; int sctype = CV_MAKETYPE(depth, scalarcn); - buildOptions += format(" -D T=%s -D T1=%s" - " -D cn=%d -D ST=%s", + buildOptions += format(" -D T=%s -D T1=%s -D cn=%d -D ST=%s -D depth=%d", ocl::typeToStr(type), ocl::typeToStr(depth), - cn, ocl::typeToStr(sctype)); + cn, ocl::typeToStr(sctype), depth); ocl::Kernel k(kernelName.c_str(), ocl::imgproc::remap_oclsrc, buildOptions); @@ -3653,7 +3656,7 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input else k.args(srcarg, dstarg, map1arg, ocl::KernelArg::ReadOnlyNoSize(map2), scalararg); - size_t globalThreads[2] = { dst.cols, dst.rows }; + size_t globalThreads[2] = { dst.cols, (dst.rows + rowsPerWI - 1) / rowsPerWI }; return k.run(2, globalThreads, NULL, false); } @@ -4188,7 +4191,8 @@ static bool ocl_warpTransform(InputArray _src, OutputArray _dst, InputArray _M0, const char * const kernelName = op_type == OCL_OP_AFFINE ? "warpAffine" : "warpPerspective"; int scalarcn = cn == 3 ? 4 : cn; - int wdepth = interpolation == INTER_NEAREST ? depth : std::max(CV_32S, depth); + bool is32f = !dev.isAMD() && (interpolation == INTER_CUBIC || interpolation == INTER_LINEAR) && op_type == OCL_OP_AFFINE; + int wdepth = interpolation == INTER_NEAREST ? depth : std::max(is32f ? CV_32F : CV_32S, depth); int sctype = CV_MAKETYPE(wdepth, scalarcn); ocl::Kernel k; diff --git a/modules/imgproc/src/moments.cpp b/modules/imgproc/src/moments.cpp index 61fff29852..a61002a792 100644 --- a/modules/imgproc/src/moments.cpp +++ b/modules/imgproc/src/moments.cpp @@ -203,69 +203,27 @@ static Moments contourMoments( const Mat& contour ) \****************************************************************************************/ template -#if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9 -// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60196 -__attribute__((optimize("no-tree-vectorize"))) -#endif -static void momentsInTile( const Mat& img, double* moments ) +struct MomentsInTile_SSE { - Size size = img.size(); - int x, y; - MT mom[10] = {0,0,0,0,0,0,0,0,0,0}; - - for( y = 0; y < size.height; y++ ) + int operator() (const T *, int, WT &, WT &, WT &, MT &) { - const T* ptr = (const T*)(img.data + y*img.step); - WT x0 = 0, x1 = 0, x2 = 0; - MT x3 = 0; - - for( x = 0; x < size.width; x++ ) - { - WT p = ptr[x]; - WT xp = x * p, xxp; - - x0 += p; - x1 += xp; - xxp = xp * x; - x2 += xxp; - x3 += xxp * x; - } - - WT py = y * x0, sy = y*y; - - mom[9] += ((MT)py) * sy; // m03 - mom[8] += ((MT)x1) * sy; // m12 - mom[7] += ((MT)x2) * y; // m21 - mom[6] += x3; // m30 - mom[5] += x0 * sy; // m02 - mom[4] += x1 * y; // m11 - mom[3] += x2; // m20 - mom[2] += py; // m01 - mom[1] += x1; // m10 - mom[0] += x0; // m00 + return 0; } - - for( x = 0; x < 10; x++ ) - moments[x] = (double)mom[x]; -} - +}; #if CV_SSE2 -template<> void momentsInTile( const cv::Mat& img, double* moments ) +template <> +struct MomentsInTile_SSE { - typedef uchar T; - typedef int WT; - typedef int MT; - Size size = img.size(); - int y; - MT mom[10] = {0,0,0,0,0,0,0,0,0,0}; - bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); + MomentsInTile_SSE() + { + useSIMD = checkHardwareSupport(CV_CPU_SSE2); + } - for( y = 0; y < size.height; y++ ) + int operator() (const uchar * ptr, int len, int & x0, int & x1, int & x2, int & x3) { - const T* ptr = img.ptr(y); - int x0 = 0, x1 = 0, x2 = 0, x3 = 0, x = 0; + int x = 0; if( useSIMD ) { @@ -273,7 +231,7 @@ template<> void momentsInTile( const cv::Mat& img, double* mome __m128i dx = _mm_set1_epi16(8); __m128i z = _mm_setzero_si128(), qx0 = z, qx1 = z, qx2 = z, qx3 = z, qx = qx_init; - for( ; x <= size.width - 8; x += 8 ) + for( ; x <= len - 8; x += 8 ) { __m128i p = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(ptr + x)), z); qx0 = _mm_add_epi32(qx0, _mm_sad_epu8(p, z)); @@ -285,6 +243,7 @@ template<> void momentsInTile( const cv::Mat& img, double* mome qx = _mm_add_epi16(qx, dx); } + int CV_DECL_ALIGNED(16) buf[4]; _mm_store_si128((__m128i*)buf, qx0); x0 = buf[0] + buf[1] + buf[2] + buf[3]; @@ -296,6 +255,94 @@ template<> void momentsInTile( const cv::Mat& img, double* mome x3 = buf[0] + buf[1] + buf[2] + buf[3]; } + return x; + } + + bool useSIMD; +}; + +#endif + +#if CV_SSE4_1 + +template <> +struct MomentsInTile_SSE +{ + MomentsInTile_SSE() + { + useSIMD = checkHardwareSupport(CV_CPU_SSE4_1); + } + + int operator() (const ushort * ptr, int len, int & x0, int & x1, int & x2, int64 & x3) + { + int x = 0; + + if (useSIMD) + { + __m128i vx_init0 = _mm_setr_epi32(0, 1, 2, 3), vx_init1 = _mm_setr_epi32(4, 5, 6, 7), + v_delta = _mm_set1_epi32(8), v_zero = _mm_setzero_si128(), v_x0 = v_zero, + v_x1 = v_zero, v_x2 = v_zero, v_x3 = v_zero, v_ix0 = vx_init0, v_ix1 = vx_init1; + + for( ; x <= len - 8; x += 8 ) + { + __m128i v_src = _mm_loadu_si128((const __m128i *)(ptr + x)); + __m128i v_src0 = _mm_unpacklo_epi16(v_src, v_zero), v_src1 = _mm_unpackhi_epi16(v_src, v_zero); + + v_x0 = _mm_add_epi32(v_x0, _mm_add_epi32(v_src0, v_src1)); + __m128i v_x1_0 = _mm_mullo_epi32(v_src0, v_ix0), v_x1_1 = _mm_mullo_epi32(v_src1, v_ix1); + v_x1 = _mm_add_epi32(v_x1, _mm_add_epi32(v_x1_0, v_x1_1)); + + __m128i v_2ix0 = _mm_mullo_epi32(v_ix0, v_ix0), v_2ix1 = _mm_mullo_epi32(v_ix1, v_ix1); + v_x2 = _mm_add_epi32(v_x2, _mm_add_epi32(_mm_mullo_epi32(v_2ix0, v_src0), _mm_mullo_epi32(v_2ix1, v_src1))); + + __m128i t = _mm_add_epi32(_mm_mullo_epi32(v_2ix0, v_x1_0), _mm_mullo_epi32(v_2ix1, v_x1_1)); + v_x3 = _mm_add_epi64(v_x3, _mm_add_epi64(_mm_unpacklo_epi32(t, v_zero), _mm_unpackhi_epi32(t, v_zero))); + + v_ix0 = _mm_add_epi32(v_ix0, v_delta); + v_ix1 = _mm_add_epi32(v_ix1, v_delta); + } + + int CV_DECL_ALIGNED(16) buf[4]; + int64 CV_DECL_ALIGNED(16) buf64[2]; + + _mm_store_si128((__m128i*)buf, v_x0); + x0 = buf[0] + buf[1] + buf[2] + buf[3]; + _mm_store_si128((__m128i*)buf, v_x1); + x1 = buf[0] + buf[1] + buf[2] + buf[3]; + _mm_store_si128((__m128i*)buf, v_x2); + x2 = buf[0] + buf[1] + buf[2] + buf[3]; + + _mm_store_si128((__m128i*)buf64, v_x3); + x3 = buf64[0] + buf64[1]; + } + + return x; + } + + bool useSIMD; +}; + +#endif + +template +#if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9 +// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60196 +__attribute__((optimize("no-tree-vectorize"))) +#endif +static void momentsInTile( const Mat& img, double* moments ) +{ + Size size = img.size(); + int x, y; + MT mom[10] = {0,0,0,0,0,0,0,0,0,0}; + MomentsInTile_SSE vop; + + for( y = 0; y < size.height; y++ ) + { + const T* ptr = (const T*)(img.data + y*img.step); + WT x0 = 0, x1 = 0, x2 = 0; + MT x3 = 0; + x = vop(ptr, size.width, x0, x1, x2, x3); + for( ; x < size.width; x++ ) { WT p = ptr[x]; @@ -322,12 +369,10 @@ template<> void momentsInTile( const cv::Mat& img, double* mome mom[0] += x0; // m00 } - for(int x = 0; x < 10; x++ ) + for( x = 0; x < 10; x++ ) moments[x] = (double)mom[x]; } -#endif - typedef void (*MomentsInTileFunc)(const Mat& img, double* moments); Moments::Moments() diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index 3ab495d75f..4f696b4209 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -1257,8 +1257,8 @@ static bool IPPMorphReplicate(int op, const Mat &src, Mat &dst, const Mat &kerne } #undef IPP_MORPH_CASE -#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8 - return false; /// It disables false positive warning in GCC 4.8.2 +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 8 + return false; /// It disables false positive warning in GCC 4.8 and further #endif } } @@ -1333,69 +1333,98 @@ static bool IPPMorphOp(int op, InputArray _src, OutputArray _dst, if( iterations > 1 ) return false; - if (IPPMorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel )) - return true; - - return false; + return IPPMorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel ); } #endif #ifdef HAVE_OPENCL -static bool ocl_morphology_op(InputArray _src, OutputArray _dst, Mat kernel, - const Size & ksize, const Point & anchor, int iterations, int op) +static bool ocl_morphOp(InputArray _src, OutputArray _dst, InputArray _kernel, + Point anchor, int iterations, int op, int borderType, + const Scalar &, int actual_op = -1, InputArray _extraMat = noArray()) { - CV_Assert(op == MORPH_ERODE || op == MORPH_DILATE); - + const ocl::Device & dev = ocl::Device::getDefault(); int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0; + bool doubleSupport = dev.doubleFPConfig() > 0; - if (depth == CV_64F && !doubleSupport) + if ((depth == CV_64F && !doubleSupport) || borderType != BORDER_CONSTANT) return false; - UMat kernel8U; - kernel.convertTo(kernel8U, CV_8U); - kernel8U = kernel8U.reshape(1, 1); + Mat kernel = _kernel.getMat(); + bool haveExtraMat = !_extraMat.empty(); + Size ksize = kernel.data ? kernel.size() : Size(3, 3), ssize = _src.size(); + CV_Assert(actual_op <= 3 || haveExtraMat); - bool rectKernel = true; + if (iterations == 0 || kernel.rows*kernel.cols == 1) { - Mat m = kernel.reshape(1, 1); - for (int i = 0; i < m.size().area(); ++i) - if (m.at(i) != 1) - { - rectKernel = false; - break; - } + _src.copyTo(_dst); + return true; } - UMat src = _src.getUMat(); + if (!kernel.data) + { + kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2)); + anchor = Point(iterations, iterations); + iterations = 1; + } + else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols ) + { + anchor = Point(anchor.x*iterations, anchor.y*iterations); + kernel = getStructuringElement(MORPH_RECT, + Size(ksize.width + (iterations-1)*(ksize.width-1), + ksize.height + (iterations-1)*(ksize.height-1)), + anchor); + iterations = 1; + } #ifdef ANDROID - size_t localThreads[3] = {16, 8, 1}; + size_t localThreads[2] = { 16, 8 }; #else - size_t localThreads[3] = {16, 16, 1}; + size_t localThreads[2] = { 16, 16 }; #endif - size_t globalThreads[3] = {(src.cols + localThreads[0] - 1) / localThreads[0] *localThreads[0], (src.rows + localThreads[1] - 1) / localThreads[1] *localThreads[1], 1}; + size_t globalThreads[2] = { ssize.width, ssize.height }; if (localThreads[0]*localThreads[1] * 2 < (localThreads[0] + ksize.width - 1) * (localThreads[1] + ksize.height - 1)) return false; - static const char * const op2str[] = { "ERODE", "DILATE" }; - String buildOptions = format("-D RADIUSX=%d -D RADIUSY=%d -D LSIZE0=%d -D LSIZE1=%d -D %s%s%s" - " -D T=%s -D DEPTH_%d -D cn=%d -D T1=%s", anchor.x, anchor.y, - (int)localThreads[0], (int)localThreads[1], op2str[op], - doubleSupport ? " -D DOUBLE_SUPPORT" : "", rectKernel ? " -D RECTKERNEL" : "", - ocl::typeToStr(_src.type()), _src.depth(), cn, ocl::typeToStr(depth)); + // build processing + String processing; + Mat kernel8u; + kernel.convertTo(kernel8u, CV_8U); + for (int y = 0; y < kernel8u.rows; ++y) + for (int x = 0; x < kernel8u.cols; ++x) + if (kernel8u.at(y, x) != 0) + processing += format("PROCESS(%d,%d)", y, x); + + static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" }; + + char cvt[2][50]; + int wdepth = std::max(depth, CV_32F), scalarcn = cn == 3 ? 4 : cn; - std::vector kernels; + if (actual_op < 0) + actual_op = op; + + std::vector kernels(iterations); for (int i = 0; i < iterations; i++) { - ocl::Kernel k("morph", ocl::imgproc::morph_oclsrc, buildOptions); - if (k.empty()) + int current_op = iterations == i + 1 ? actual_op : op; + String buildOptions = format("-D RADIUSX=%d -D RADIUSY=%d -D LSIZE0=%d -D LSIZE1=%d -D %s%s" + " -D PROCESS_ELEMS=%s -D T=%s -D DEPTH_%d -D cn=%d -D T1=%s" + " -D convertToWT=%s -D convertToT=%s -D ST=%s%s", + anchor.x, anchor.y, (int)localThreads[0], (int)localThreads[1], op2str[op], + doubleSupport ? " -D DOUBLE_SUPPORT" : "", processing.c_str(), + ocl::typeToStr(type), depth, cn, ocl::typeToStr(depth), + ocl::convertTypeStr(depth, wdepth, cn, cvt[0]), + ocl::convertTypeStr(wdepth, depth, cn, cvt[1]), + ocl::typeToStr(CV_MAKE_TYPE(depth, scalarcn)), + current_op == op ? "" : cv::format(" -D %s", op2str[current_op]).c_str()); + + kernels[i].create("morph", ocl::imgproc::morph_oclsrc, buildOptions); + if (kernels[i].empty()) return false; - kernels.push_back(k); } + UMat src = _src.getUMat(), extraMat = _extraMat.getUMat(); _dst.create(src.size(), src.type()); UMat dst = _dst.getUMat(); @@ -1406,9 +1435,13 @@ static bool ocl_morphology_op(InputArray _src, OutputArray _dst, Mat kernel, src.locateROI(wholesize, ofs); int wholecols = wholesize.width, wholerows = wholesize.height; - kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst), - ofs.x, ofs.y, src.cols, src.rows, ocl::KernelArg::PtrReadOnly(kernel8U), - wholecols, wholerows); + if (haveExtraMat) + kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst), + ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows, + ocl::KernelArg::ReadOnlyNoSize(extraMat)); + else + kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst), + ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows); return kernels[0].run(2, globalThreads, localThreads, false); } @@ -1422,19 +1455,20 @@ static bool ocl_morphology_op(InputArray _src, OutputArray _dst, Mat kernel, if (i == 0) { int cols = src.cols, rows = src.rows; - src.locateROI(wholesize,ofs); + src.locateROI(wholesize, ofs); src.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x); if(src.u != dst.u) source = src; else src.copyTo(source); + src.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x); source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x); } else { int cols = dst.cols, rows = dst.rows; - dst.locateROI(wholesize,ofs); + dst.locateROI(wholesize, ofs); dst.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x); dst.copyTo(source); dst.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x); @@ -1442,13 +1476,18 @@ static bool ocl_morphology_op(InputArray _src, OutputArray _dst, Mat kernel, } source.locateROI(wholesize, ofs); - kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst), - ofs.x, ofs.y, source.cols, source.rows, ocl::KernelArg::PtrReadOnly(kernel8U), - wholesize.width, wholesize.height); + if (haveExtraMat && iterations == i + 1) + kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst), + ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height, + ocl::KernelArg::ReadOnlyNoSize(extraMat)); + else + kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst), + ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height); if (!kernels[i].run(2, globalThreads, localThreads, false)) return false; } + return true; } @@ -1459,15 +1498,16 @@ static void morphOp( int op, InputArray _src, OutputArray _dst, Point anchor, int iterations, int borderType, const Scalar& borderValue ) { -#ifdef HAVE_OPENCL - int src_type = _src.type(), - src_cn = CV_MAT_CN(src_type), src_depth = CV_MAT_DEPTH(src_type); -#endif - Mat kernel = _kernel.getMat(); Size ksize = kernel.data ? kernel.size() : Size(3,3); anchor = normalizeAnchor(anchor, ksize); + CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 && + borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() && + (op == MORPH_ERODE || op == MORPH_DILATE) && + anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1, + ocl_morphOp(_src, _dst, kernel, anchor, iterations, op, borderType, borderValue) ) + if (iterations == 0 || kernel.rows*kernel.cols == 1) { _src.copyTo(_dst); @@ -1490,12 +1530,6 @@ static void morphOp( int op, InputArray _src, OutputArray _dst, iterations = 1; } - CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && src_cn <= 4 && - (src_depth == CV_8U || src_depth == CV_32F || src_depth == CV_64F ) && - borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() && - (op == MORPH_ERODE || op == MORPH_DILATE), - ocl_morphology_op(_src, _dst, kernel, ksize, anchor, iterations, op) ) - #if IPP_VERSION_X100 >= 801 if( IPPMorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue) ) return; @@ -1515,13 +1549,6 @@ static void morphOp( int op, InputArray _src, OutputArray _dst, parallel_for_(Range(0, nStripes), MorphologyRunner(src, dst, nStripes, iterations, op, kernel, anchor, borderType, borderType, borderValue)); - - //Ptr f = createMorphologyFilter(op, src.type(), - // kernel, anchor, borderType, borderType, borderValue ); - - //f->apply( src, dst ); - //for( int i = 1; i < iterations; i++ ) - // f->apply( dst, dst ); } } @@ -1541,97 +1568,122 @@ void cv::dilate( InputArray src, OutputArray dst, InputArray kernel, morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue ); } +#ifdef HAVE_OPENCL + +namespace cv { + +static bool ocl_morphologyEx(InputArray _src, OutputArray _dst, int op, + InputArray kernel, Point anchor, int iterations, + int borderType, const Scalar& borderValue) +{ + _dst.createSameSize(_src, _src.type()); + bool submat = _dst.isSubmatrix(); + UMat temp; + _OutputArray _temp = submat ? _dst : _OutputArray(temp); + + switch( op ) + { + case MORPH_ERODE: + if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) + return false; + break; + case MORPH_DILATE: + if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) + return false; + break; + case MORPH_OPEN: + if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) + return false; + if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) + return false; + break; + case MORPH_CLOSE: + if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) + return false; + if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) + return false; + break; + case MORPH_GRADIENT: + if (!ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) + return false; + if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_GRADIENT, temp )) + return false; + break; + case MORPH_TOPHAT: + if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) + return false; + if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_TOPHAT, _src )) + return false; + break; + case MORPH_BLACKHAT: + if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) + return false; + if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue, MORPH_BLACKHAT, _src )) + return false; + break; + default: + CV_Error( CV_StsBadArg, "unknown morphological operation" ); + } + + return true; +} + +} + +#endif + void cv::morphologyEx( InputArray _src, OutputArray _dst, int op, InputArray kernel, Point anchor, int iterations, int borderType, const Scalar& borderValue ) { - int src_type = _src.type(), dst_type = _dst.type(), - src_cn = CV_MAT_CN(src_type), src_depth = CV_MAT_DEPTH(src_type); +#ifdef HAVE_OPENCL + Size ksize = kernel.size(); + anchor = normalizeAnchor(anchor, ksize); - bool use_opencl = cv::ocl::useOpenCL() && _src.isUMat() && _src.size() == _dst.size() && src_type == dst_type && - _src.dims()<=2 && (src_cn == 1 || src_cn == 4) && (anchor.x == -1) && (anchor.y == -1) && - (src_depth == CV_8U || src_depth == CV_32F || src_depth == CV_64F ) && - (borderType == cv::BORDER_CONSTANT) && (borderValue == morphologyDefaultBorderValue()); + CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 && + anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 && + borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(), + ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue)) +#endif - _dst.create(_src.size(), _src.type()); - Mat src, dst, temp; - UMat usrc, udst, utemp; + Mat src = _src.getMat(), temp; + _dst.create(src.size(), src.type()); + Mat dst = _dst.getMat(); switch( op ) { case MORPH_ERODE: - erode( _src, _dst, kernel, anchor, iterations, borderType, borderValue ); + erode( src, dst, kernel, anchor, iterations, borderType, borderValue ); break; case MORPH_DILATE: - dilate( _src, _dst, kernel, anchor, iterations, borderType, borderValue ); + dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); break; case MORPH_OPEN: - erode( _src, _dst, kernel, anchor, iterations, borderType, borderValue ); - dilate( _dst, _dst, kernel, anchor, iterations, borderType, borderValue ); + erode( src, dst, kernel, anchor, iterations, borderType, borderValue ); + dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue ); break; case CV_MOP_CLOSE: - dilate( _src, _dst, kernel, anchor, iterations, borderType, borderValue ); - erode( _dst, _dst, kernel, anchor, iterations, borderType, borderValue ); + dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); + erode( dst, dst, kernel, anchor, iterations, borderType, borderValue ); break; case CV_MOP_GRADIENT: - erode( _src, use_opencl ? (cv::OutputArray)utemp : (cv::OutputArray)temp, kernel, anchor, iterations, borderType, borderValue ); - dilate( _src, _dst, kernel, anchor, iterations, borderType, borderValue ); - if(use_opencl) - { - udst = _dst.getUMat(); - subtract(udst, utemp, udst); - } - else - { - dst = _dst.getMat(); - dst -= temp; - } + erode( src, temp, kernel, anchor, iterations, borderType, borderValue ); + dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); + dst -= temp; break; case CV_MOP_TOPHAT: - if(use_opencl) - { - usrc = _src.getUMat(); - udst = _dst.getUMat(); - if( usrc.u != udst.u ) - utemp = udst; - } - else - { - src = _src.getMat(); - dst = _dst.getMat(); - if( src.data != dst.data ) - temp = dst; - } - erode( _src, use_opencl ? (cv::OutputArray)utemp : (cv::OutputArray)temp, kernel, anchor, iterations, borderType, borderValue ); - dilate( use_opencl ? (cv::OutputArray)utemp : (cv::OutputArray)temp, use_opencl ? (cv::OutputArray)utemp : (cv::OutputArray)temp, kernel, - anchor, iterations, borderType, borderValue ); - if(use_opencl) - subtract(usrc, utemp, udst); - else - dst = src - temp; + if( src.data != dst.data ) + temp = dst; + erode( src, temp, kernel, anchor, iterations, borderType, borderValue ); + dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue ); + dst = src - temp; break; case CV_MOP_BLACKHAT: - if(use_opencl) - { - usrc = _src.getUMat(); - udst = _dst.getUMat(); - if( usrc.u != udst.u ) - utemp = udst; - } - else - { - src = _src.getMat(); - dst = _dst.getMat(); - if( src.data != dst.data ) - temp = dst; - } - dilate( _src, use_opencl ? (cv::OutputArray)utemp : (cv::OutputArray)temp, kernel, anchor, iterations, borderType, borderValue ); - erode( use_opencl ? (cv::OutputArray)utemp : (cv::OutputArray)temp, use_opencl ? (cv::OutputArray)utemp : (cv::OutputArray)temp, kernel, - anchor, iterations, borderType, borderValue ); - if(use_opencl) - subtract(utemp, usrc, udst); - else - dst = temp - src; + if( src.data != dst.data ) + temp = dst; + dilate( src, temp, kernel, anchor, iterations, borderType, borderValue ); + erode( temp, temp, kernel, anchor, iterations, borderType, borderValue ); + dst = temp - src; break; default: CV_Error( CV_StsBadArg, "unknown morphological operation" ); diff --git a/modules/imgproc/src/opencl/corner.cl b/modules/imgproc/src/opencl/corner.cl index 563cb98081..e1c2e407dc 100644 --- a/modules/imgproc/src/opencl/corner.cl +++ b/modules/imgproc/src/opencl/corner.cl @@ -114,25 +114,23 @@ __kernel void corner(__global const float * Dx, int dx_step, int dx_offset, int int dst_startX = gX * (THREADS-ksX+1) + dst_x_off; int dst_startY = (gY << 1) + dst_y_off; - float dx_data[ksY+1],dy_data[ksY+1], data[3][ksY+1]; + float data[3][ksY+1]; __local float temp[6][THREADS]; #ifdef BORDER_CONSTANT for (int i=0; i < ksY+1; i++) { bool dx_con = dx_startX+col >= 0 && dx_startX+col < dx_whole_cols && dx_startY+i >= 0 && dx_startY+i < dx_whole_rows; - int indexDx = (dx_startY+i)*(dx_step>>2)+(dx_startX+col); + int indexDx = mad24(dx_startY+i, dx_step>>2, dx_startX+col); float dx_s = dx_con ? Dx[indexDx] : 0.0f; - dx_data[i] = dx_s; bool dy_con = dy_startX+col >= 0 && dy_startX+col < dy_whole_cols && dy_startY+i >= 0 && dy_startY+i < dy_whole_rows; - int indexDy = (dy_startY+i)*(dy_step>>2)+(dy_startX+col); + int indexDy = mad24(dy_startY+i, dy_step>>2, dy_startX+col); float dy_s = dy_con ? Dy[indexDy] : 0.0f; - dy_data[i] = dy_s; - data[0][i] = dx_data[i] * dx_data[i]; - data[1][i] = dx_data[i] * dy_data[i]; - data[2][i] = dy_data[i] * dy_data[i]; + data[0][i] = dx_s * dx_s; + data[1][i] = dx_s * dy_s; + data[2][i] = dy_s * dy_s; } #else int clamped_col = min(2*dst_cols, col); @@ -141,16 +139,16 @@ __kernel void corner(__global const float * Dx, int dx_step, int dx_offset, int int dx_selected_row = dx_startY+i, dx_selected_col = dx_startX+clamped_col; EXTRAPOLATE(dx_selected_row, dx_whole_rows) EXTRAPOLATE(dx_selected_col, dx_whole_cols) - dx_data[i] = Dx[dx_selected_row * (dx_step>>2) + dx_selected_col]; + float dx_s = Dx[mad24(dx_selected_row, dx_step>>2, dx_selected_col)]; int dy_selected_row = dy_startY+i, dy_selected_col = dy_startX+clamped_col; EXTRAPOLATE(dy_selected_row, dy_whole_rows) EXTRAPOLATE(dy_selected_col, dy_whole_cols) - dy_data[i] = Dy[dy_selected_row * (dy_step>>2) + dy_selected_col]; + float dy_s = Dy[mad24(dy_selected_row, dy_step>>2, dy_selected_col)]; - data[0][i] = dx_data[i] * dx_data[i]; - data[1][i] = dx_data[i] * dy_data[i]; - data[2][i] = dy_data[i] * dy_data[i]; + data[0][i] = dx_s * dx_s; + data[1][i] = dx_s * dy_s; + data[2][i] = dy_s * dy_s; } #endif float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f; @@ -180,7 +178,7 @@ __kernel void corner(__global const float * Dx, int dx_step, int dx_offset, int col += anX; int posX = dst_startX - dst_x_off + col - anX; int posY = (gly << 1); - int till = (ksX + 1)%2; + int till = (ksX + 1) & 1; float tmp_sum[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; for (int k=0; k<6; k++) { @@ -210,7 +208,7 @@ __kernel void corner(__global const float * Dx, int dx_step, int dx_offset, int float a = tmp_sum[0] * 0.5f; float b = tmp_sum[2]; float c = tmp_sum[4] * 0.5f; - *(__global float *)(dst + dst_index) = (float)((a+c) - sqrt((a-c)*(a-c) + b*b)); + *(__global float *)(dst + dst_index) = (float)((a+c) - native_sqrt((a-c)*(a-c) + b*b)); } if (posX < dst_cols && (posY + 1) < dst_rows) { @@ -218,7 +216,7 @@ __kernel void corner(__global const float * Dx, int dx_step, int dx_offset, int float a = tmp_sum[1] * 0.5f; float b = tmp_sum[3]; float c = tmp_sum[5] * 0.5f; - *(__global float *)(dst + dst_index) = (float)((a+c) - sqrt((a-c)*(a-c) + b*b)); + *(__global float *)(dst + dst_index) = (float)((a+c) - native_sqrt((a-c)*(a-c) + b*b)); } #else #error "No such corners type" diff --git a/modules/imgproc/src/opencl/cvtcolor.cl b/modules/imgproc/src/opencl/cvtcolor.cl index 5bad3eee61..da835e08f3 100644 --- a/modules/imgproc/src/opencl/cvtcolor.cl +++ b/modules/imgproc/src/opencl/cvtcolor.cl @@ -71,10 +71,6 @@ #error "invalid depth: should be 0 (CV_8U), 2 (CV_16U) or 5 (CV_32F)" #endif -#ifndef STRIPE_SIZE -#define STRIPE_SIZE 1 -#endif - #define CV_DESCALE(x,n) (((x) + (1 << ((n)-1))) >> (n)) enum @@ -122,8 +118,8 @@ enum ///////////////////////////////////// RGB <-> GRAY ////////////////////////////////////// -__kernel void RGB2Gray(__global const uchar* srcptr, int srcstep, int srcoffset, - __global uchar* dstptr, int dststep, int dstoffset, +__kernel void RGB2Gray(__global const uchar * srcptr, int src_step, int src_offset, + __global uchar * dstptr, int dst_step, int dst_offset, int rows, int cols) { int x = get_global_id(0); @@ -131,27 +127,32 @@ __kernel void RGB2Gray(__global const uchar* srcptr, int srcstep, int srcoffset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + mad24(y, srcstep, srcoffset + x * scnbytes)); - __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + mad24(y, dststep, dstoffset + x * dcnbytes)); + __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + src_index); + __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + dst_index); DATA_TYPE_4 src_pix = vload4(0, src); #ifdef DEPTH_5 - dst[0] = src_pix.B_COMP * 0.114f + src_pix.G_COMP * 0.587f + src_pix.R_COMP * 0.299f; + dst[0] = fma(src_pix.B_COMP, 0.114f, fma(src_pix.G_COMP, 0.587f, src_pix.R_COMP * 0.299f)); #else - dst[0] = (DATA_TYPE)CV_DESCALE((src_pix.B_COMP * B2Y + src_pix.G_COMP * G2Y + src_pix.R_COMP * R2Y), yuv_shift); + dst[0] = (DATA_TYPE)CV_DESCALE(mad24(src_pix.B_COMP, B2Y, mad24(src_pix.G_COMP, G2Y, src_pix.R_COMP * R2Y)), yuv_shift); #endif + ++y; + src_index += src_step; + dst_index += dst_step; } - ++y; } } } -__kernel void Gray2RGB(__global const uchar* srcptr, int srcstep, int srcoffset, - __global uchar* dstptr, int dststep, int dstoffset, +__kernel void Gray2RGB(__global const uchar * srcptr, int src_step, int src_offset, + __global uchar * dstptr, int dst_step, int dst_offset, int rows, int cols) { int x = get_global_id(0); @@ -159,20 +160,29 @@ __kernel void Gray2RGB(__global const uchar* srcptr, int srcstep, int srcoffset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + mad24(y, srcstep, srcoffset + x * scnbytes)); - __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + mad24(y, dststep, dstoffset + x * dcnbytes)); + __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + src_index); + __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + dst_index); DATA_TYPE val = src[0]; +#if dcn == 3 || defined DEPTH_5 dst[0] = dst[1] = dst[2] = val; #if dcn == 4 dst[3] = MAX_NUM; #endif +#else + *(__global DATA_TYPE_4 *)dst = (DATA_TYPE_4)(val, val, val, MAX_NUM); +#endif + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -182,8 +192,8 @@ __kernel void Gray2RGB(__global const uchar* srcptr, int srcstep, int srcoffset, __constant float c_RGB2YUVCoeffs_f[5] = { 0.114f, 0.587f, 0.299f, 0.492f, 0.877f }; __constant int c_RGB2YUVCoeffs_i[5] = { B2Y, G2Y, R2Y, 8061, 14369 }; -__kernel void RGB2YUV(__global const uchar* srcptr, int srcstep, int srcoffset, - __global uchar* dstptr, int dststep, int dstoffset, +__kernel void RGB2YUV(__global const uchar* srcptr, int src_step, int src_offset, + __global uchar* dstptr, int dst_step, int dt_offset, int rows, int cols) { int x = get_global_id(0); @@ -191,34 +201,40 @@ __kernel void RGB2YUV(__global const uchar* srcptr, int srcstep, int srcoffset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dt_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + mad24(y, srcstep, srcoffset + x * scnbytes)); - __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + mad24(y, dststep, dstoffset + x * dcnbytes)); + __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + src_index); + __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + dst_index); DATA_TYPE_4 src_pix = vload4(0, src); - DATA_TYPE b=src_pix.B_COMP, g=src_pix.G_COMP, r=src_pix.R_COMP; + DATA_TYPE b = src_pix.B_COMP, g = src_pix.G_COMP, r = src_pix.R_COMP; #ifdef DEPTH_5 __constant float * coeffs = c_RGB2YUVCoeffs_f; - const DATA_TYPE Y = b * coeffs[0] + g * coeffs[1] + r * coeffs[2]; - const DATA_TYPE U = (b - Y) * coeffs[3] + HALF_MAX; - const DATA_TYPE V = (r - Y) * coeffs[4] + HALF_MAX; + const DATA_TYPE Y = fma(b, coeffs[0], fma(g, coeffs[1], r * coeffs[2])); + const DATA_TYPE U = fma(b - Y, coeffs[3], HALF_MAX); + const DATA_TYPE V = fma(r - Y, coeffs[4], HALF_MAX); #else __constant int * coeffs = c_RGB2YUVCoeffs_i; const int delta = HALF_MAX * (1 << yuv_shift); - const int Y = CV_DESCALE(b * coeffs[0] + g * coeffs[1] + r * coeffs[2], yuv_shift); - const int U = CV_DESCALE((b - Y) * coeffs[3] + delta, yuv_shift); - const int V = CV_DESCALE((r - Y) * coeffs[4] + delta, yuv_shift); + const int Y = CV_DESCALE(mad24(b, coeffs[0], mad24(g, coeffs[1], r * coeffs[2])), yuv_shift); + const int U = CV_DESCALE(mad24(b - Y, coeffs[3], delta), yuv_shift); + const int V = CV_DESCALE(mad24(r - Y, coeffs[4], delta), yuv_shift); #endif dst[0] = SAT_CAST( Y ); dst[1] = SAT_CAST( U ); dst[2] = SAT_CAST( V ); + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -226,8 +242,8 @@ __kernel void RGB2YUV(__global const uchar* srcptr, int srcstep, int srcoffset, __constant float c_YUV2RGBCoeffs_f[5] = { 2.032f, -0.395f, -0.581f, 1.140f }; __constant int c_YUV2RGBCoeffs_i[5] = { 33292, -6472, -9519, 18678 }; -__kernel void YUV2RGB(__global const uchar* srcptr, int srcstep, int srcoffset, - __global uchar* dstptr, int dststep, int dstoffset, +__kernel void YUV2RGB(__global const uchar* srcptr, int src_step, int src_offset, + __global uchar* dstptr, int dst_step, int dt_offset, int rows, int cols) { int x = get_global_id(0); @@ -235,25 +251,28 @@ __kernel void YUV2RGB(__global const uchar* srcptr, int srcstep, int srcoffset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dt_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + mad24(y, srcstep, srcoffset + x * scnbytes)); - __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + mad24(y, dststep, dstoffset + x * dcnbytes)); + __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + src_index); + __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + dst_index); DATA_TYPE_4 src_pix = vload4(0, src); DATA_TYPE Y = src_pix.x, U = src_pix.y, V = src_pix.z; #ifdef DEPTH_5 __constant float * coeffs = c_YUV2RGBCoeffs_f; - const float r = Y + (V - HALF_MAX) * coeffs[3]; - const float g = Y + (V - HALF_MAX) * coeffs[2] + (U - HALF_MAX) * coeffs[1]; - const float b = Y + (U - HALF_MAX) * coeffs[0]; + float r = fma(V - HALF_MAX, coeffs[3], Y); + float g = fma(V - HALF_MAX, coeffs[2], fma(U - HALF_MAX, coeffs[1], Y)); + float b = fma(U - HALF_MAX, coeffs[0], Y); #else __constant int * coeffs = c_YUV2RGBCoeffs_i; const int r = Y + CV_DESCALE((V - HALF_MAX) * coeffs[3], yuv_shift); - const int g = Y + CV_DESCALE((V - HALF_MAX) * coeffs[2] + (U - HALF_MAX) * coeffs[1], yuv_shift); + const int g = Y + CV_DESCALE(mad24(V - HALF_MAX, coeffs[2], (U - HALF_MAX) * coeffs[1]), yuv_shift); const int b = Y + CV_DESCALE((U - HALF_MAX) * coeffs[0], yuv_shift); #endif @@ -263,8 +282,10 @@ __kernel void YUV2RGB(__global const uchar* srcptr, int srcstep, int srcoffset, #if dcn == 4 dst[3] = MAX_NUM; #endif + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -276,8 +297,8 @@ __constant int ITUR_BT_601_CVG = 852492; __constant int ITUR_BT_601_CVR = 1673527; __constant int ITUR_BT_601_SHIFT = 20; -__kernel void YUV2RGB_NV12(__global const uchar* srcptr, int srcstep, int srcoffset, - __global uchar* dstptr, int dststep, int dstoffset, +__kernel void YUV2RGB_NV12(__global const uchar* srcptr, int src_step, int src_offset, + __global uchar* dstptr, int dst_step, int dt_offset, int rows, int cols) { int x = get_global_id(0); @@ -290,15 +311,15 @@ __kernel void YUV2RGB_NV12(__global const uchar* srcptr, int srcstep, int srcoff { if (y < rows / 2 ) { - __global const uchar* ysrc = srcptr + mad24(y << 1, srcstep, (x << 1) + srcoffset); - __global const uchar* usrc = srcptr + mad24(rows + y, srcstep, (x << 1) + srcoffset); - __global uchar* dst1 = dstptr + mad24(y << 1, dststep, x * (dcn<<1) + dstoffset); - __global uchar* dst2 = dstptr + mad24((y << 1) + 1, dststep, x * (dcn<<1) + dstoffset); + __global const uchar* ysrc = srcptr + mad24(y << 1, src_step, (x << 1) + src_offset); + __global const uchar* usrc = srcptr + mad24(rows + y, src_step, (x << 1) + src_offset); + __global uchar* dst1 = dstptr + mad24(y << 1, dst_step, x * (dcn<<1) + dt_offset); + __global uchar* dst2 = dstptr + mad24((y << 1) + 1, dst_step, x * (dcn<<1) + dt_offset); int Y1 = ysrc[0]; int Y2 = ysrc[1]; - int Y3 = ysrc[srcstep]; - int Y4 = ysrc[srcstep + 1]; + int Y3 = ysrc[src_step]; + int Y4 = ysrc[src_step + 1]; int U = usrc[0] - 128; int V = usrc[1] - 128; @@ -349,8 +370,8 @@ __kernel void YUV2RGB_NV12(__global const uchar* srcptr, int srcstep, int srcoff __constant float c_RGB2YCrCbCoeffs_f[5] = {0.299f, 0.587f, 0.114f, 0.713f, 0.564f}; __constant int c_RGB2YCrCbCoeffs_i[5] = {R2Y, G2Y, B2Y, 11682, 9241}; -__kernel void RGB2YCrCb(__global const uchar* srcptr, int srcstep, int srcoffset, - __global uchar* dstptr, int dststep, int dstoffset, +__kernel void RGB2YCrCb(__global const uchar* srcptr, int src_step, int src_offset, + __global uchar* dstptr, int dst_step, int dt_offset, int rows, int cols) { int x = get_global_id(0); @@ -358,34 +379,40 @@ __kernel void RGB2YCrCb(__global const uchar* srcptr, int srcstep, int srcoffset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dt_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + mad24(y, srcstep, srcoffset + x * scnbytes)); - __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + mad24(y, dststep, dstoffset + x * dcnbytes)); + __global const DATA_TYPE* src = (__global const DATA_TYPE*)(srcptr + src_index); + __global DATA_TYPE* dst = (__global DATA_TYPE*)(dstptr + dst_index); DATA_TYPE_4 src_pix = vload4(0, src); - DATA_TYPE b=src_pix.B_COMP, g=src_pix.G_COMP, r=src_pix.R_COMP; + DATA_TYPE b = src_pix.B_COMP, g = src_pix.G_COMP, r = src_pix.R_COMP; #ifdef DEPTH_5 __constant float * coeffs = c_RGB2YCrCbCoeffs_f; - DATA_TYPE Y = b * coeffs[2] + g * coeffs[1] + r * coeffs[0]; - DATA_TYPE Cr = (r - Y) * coeffs[3] + HALF_MAX; - DATA_TYPE Cb = (b - Y) * coeffs[4] + HALF_MAX; + DATA_TYPE Y = fma(b, coeffs[2], fma(g, coeffs[1], r * coeffs[0])); + DATA_TYPE Cr = fma(r - Y, coeffs[3], HALF_MAX); + DATA_TYPE Cb = fma(b - Y, coeffs[4], HALF_MAX); #else __constant int * coeffs = c_RGB2YCrCbCoeffs_i; int delta = HALF_MAX * (1 << yuv_shift); - int Y = CV_DESCALE(b * coeffs[2] + g * coeffs[1] + r * coeffs[0], yuv_shift); - int Cr = CV_DESCALE((r - Y) * coeffs[3] + delta, yuv_shift); - int Cb = CV_DESCALE((b - Y) * coeffs[4] + delta, yuv_shift); + int Y = CV_DESCALE(mad24(b, coeffs[2], mad24(g, coeffs[1], r * coeffs[0])), yuv_shift); + int Cr = CV_DESCALE(mad24(r - Y, coeffs[3], delta), yuv_shift); + int Cb = CV_DESCALE(mad24(b - Y, coeffs[4], delta), yuv_shift); #endif dst[0] = SAT_CAST( Y ); dst[1] = SAT_CAST( Cr ); dst[2] = SAT_CAST( Cb ); + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -402,28 +429,29 @@ __kernel void YCrCb2RGB(__global const uchar* src, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - __global const DATA_TYPE * srcptr = (__global const DATA_TYPE*)(src + src_idx); - __global DATA_TYPE * dstptr = (__global DATA_TYPE*)(dst + dst_idx); + __global const DATA_TYPE * srcptr = (__global const DATA_TYPE*)(src + src_index); + __global DATA_TYPE * dstptr = (__global DATA_TYPE*)(dst + dst_index); DATA_TYPE_4 src_pix = vload4(0, srcptr); DATA_TYPE y = src_pix.x, cr = src_pix.y, cb = src_pix.z; #ifdef DEPTH_5 __constant float * coeff = c_YCrCb2RGBCoeffs_f; - float r = y + coeff[0] * (cr - HALF_MAX); - float g = y + coeff[1] * (cr - HALF_MAX) + coeff[2] * (cb - HALF_MAX); - float b = y + coeff[3] * (cb - HALF_MAX); + float r = fma(coeff[0], cr - HALF_MAX, y); + float g = fma(coeff[1], cr - HALF_MAX, fma(coeff[2], cb - HALF_MAX, y)); + float b = fma(coeff[3], cb - HALF_MAX, y); #else __constant int * coeff = c_YCrCb2RGBCoeffs_i; int r = y + CV_DESCALE(coeff[0] * (cr - HALF_MAX), yuv_shift); - int g = y + CV_DESCALE(coeff[1] * (cr - HALF_MAX) + coeff[2] * (cb - HALF_MAX), yuv_shift); + int g = y + CV_DESCALE(mad24(coeff[1], cr - HALF_MAX, coeff[2] * (cb - HALF_MAX)), yuv_shift); int b = y + CV_DESCALE(coeff[3] * (cb - HALF_MAX), yuv_shift); #endif @@ -433,8 +461,11 @@ __kernel void YCrCb2RGB(__global const uchar* src, int src_step, int src_offset, #if dcn == 4 dstptr[3] = MAX_NUM; #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -450,34 +481,37 @@ __kernel void RGB2XYZ(__global const uchar * srcptr, int src_step, int src_offse if (dx < cols) { + int src_index = mad24(dy, src_step, mad24(dx, scnbytes, src_offset)); + int dst_index = mad24(dy, dst_step, mad24(dx, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (dy < rows) { - int src_idx = mad24(dy, src_step, src_offset + dx * scnbytes); - int dst_idx = mad24(dy, dst_step, dst_offset + dx * dcnbytes); - - __global const DATA_TYPE * src = (__global const DATA_TYPE *)(srcptr + src_idx); - __global DATA_TYPE * dst = (__global DATA_TYPE *)(dstptr + dst_idx); + __global const DATA_TYPE * src = (__global const DATA_TYPE *)(srcptr + src_index); + __global DATA_TYPE * dst = (__global DATA_TYPE *)(dstptr + dst_index); DATA_TYPE_4 src_pix = vload4(0, src); DATA_TYPE r = src_pix.x, g = src_pix.y, b = src_pix.z; #ifdef DEPTH_5 - float x = r * coeffs[0] + g * coeffs[1] + b * coeffs[2]; - float y = r * coeffs[3] + g * coeffs[4] + b * coeffs[5]; - float z = r * coeffs[6] + g * coeffs[7] + b * coeffs[8]; + float x = fma(r, coeffs[0], fma(g, coeffs[1], b * coeffs[2])); + float y = fma(r, coeffs[3], fma(g, coeffs[4], b * coeffs[5])); + float z = fma(r, coeffs[6], fma(g, coeffs[7], b * coeffs[8])); #else - int x = CV_DESCALE(r * coeffs[0] + g * coeffs[1] + b * coeffs[2], xyz_shift); - int y = CV_DESCALE(r * coeffs[3] + g * coeffs[4] + b * coeffs[5], xyz_shift); - int z = CV_DESCALE(r * coeffs[6] + g * coeffs[7] + b * coeffs[8], xyz_shift); + int x = CV_DESCALE(mad24(r, coeffs[0], mad24(g, coeffs[1], b * coeffs[2])), xyz_shift); + int y = CV_DESCALE(mad24(r, coeffs[3], mad24(g, coeffs[4], b * coeffs[5])), xyz_shift); + int z = CV_DESCALE(mad24(r, coeffs[6], mad24(g, coeffs[7], b * coeffs[8])), xyz_shift); #endif dst[0] = SAT_CAST(x); dst[1] = SAT_CAST(y); dst[2] = SAT_CAST(z); + + ++dy; + dst_index += dst_step; + src_index += src_step; } - ++dy; } } } @@ -491,37 +525,48 @@ __kernel void XYZ2RGB(__global const uchar * srcptr, int src_step, int src_offse if (dx < cols) { + int src_index = mad24(dy, src_step, mad24(dx, scnbytes, src_offset)); + int dst_index = mad24(dy, dst_step, mad24(dx, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (dy < rows) { - int src_idx = mad24(dy, src_step, src_offset + dx * scnbytes); - int dst_idx = mad24(dy, dst_step, dst_offset + dx * dcnbytes); - - __global const DATA_TYPE * src = (__global const DATA_TYPE *)(srcptr + src_idx); - __global DATA_TYPE * dst = (__global DATA_TYPE *)(dstptr + dst_idx); + __global const DATA_TYPE * src = (__global const DATA_TYPE *)(srcptr + src_index); + __global DATA_TYPE * dst = (__global DATA_TYPE *)(dstptr + dst_index); DATA_TYPE_4 src_pix = vload4(0, src); DATA_TYPE x = src_pix.x, y = src_pix.y, z = src_pix.z; #ifdef DEPTH_5 - float b = x * coeffs[0] + y * coeffs[1] + z * coeffs[2]; - float g = x * coeffs[3] + y * coeffs[4] + z * coeffs[5]; - float r = x * coeffs[6] + y * coeffs[7] + z * coeffs[8]; + float b = fma(x, coeffs[0], fma(y, coeffs[1], z * coeffs[2])); + float g = fma(x, coeffs[3], fma(y, coeffs[4], z * coeffs[5])); + float r = fma(x, coeffs[6], fma(y, coeffs[7], z * coeffs[8])); #else - int b = CV_DESCALE(x * coeffs[0] + y * coeffs[1] + z * coeffs[2], xyz_shift); - int g = CV_DESCALE(x * coeffs[3] + y * coeffs[4] + z * coeffs[5], xyz_shift); - int r = CV_DESCALE(x * coeffs[6] + y * coeffs[7] + z * coeffs[8], xyz_shift); + int b = CV_DESCALE(mad24(x, coeffs[0], mad24(y, coeffs[1], z * coeffs[2])), xyz_shift); + int g = CV_DESCALE(mad24(x, coeffs[3], mad24(y, coeffs[4], z * coeffs[5])), xyz_shift); + int r = CV_DESCALE(mad24(x, coeffs[6], mad24(y, coeffs[7], z * coeffs[8])), xyz_shift); #endif - dst[0] = SAT_CAST(b); - dst[1] = SAT_CAST(g); - dst[2] = SAT_CAST(r); + + DATA_TYPE dst0 = SAT_CAST(b); + DATA_TYPE dst1 = SAT_CAST(g); + DATA_TYPE dst2 = SAT_CAST(r); +#if dcn == 3 || defined DEPTH_5 + dst[0] = dst0; + dst[1] = dst1; + dst[2] = dst2; #if dcn == 4 dst[3] = MAX_NUM; #endif +#else + *(__global DATA_TYPE_4 *)dst = (DATA_TYPE_4)(dst0, dst1, dst2, MAX_NUM); +#endif + + ++dy; + dst_index += dst_step; + src_index += src_step; } - ++dy; } } } @@ -537,16 +582,16 @@ __kernel void RGB(__global const uchar* srcptr, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const DATA_TYPE * src = (__global const DATA_TYPE *)(srcptr + src_idx); - __global DATA_TYPE * dst = (__global DATA_TYPE *)(dstptr + dst_idx); + __global const DATA_TYPE * src = (__global const DATA_TYPE *)(srcptr + src_index); + __global DATA_TYPE * dst = (__global DATA_TYPE *)(dstptr + dst_index); DATA_TYPE_4 src_pix = vload4(0, src); #ifdef REVERSE @@ -566,8 +611,11 @@ __kernel void RGB(__global const uchar* srcptr, int src_step, int src_offset, dst[3] = src[3]; #endif #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -583,34 +631,38 @@ __kernel void RGB5x52RGB(__global const uchar* src, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - ushort t = *((__global const ushort*)(src + src_idx)); + ushort t = *((__global const ushort*)(src + src_index)); #if greenbits == 6 - dst[dst_idx + bidx] = (uchar)(t << 3); - dst[dst_idx + 1] = (uchar)((t >> 3) & ~3); - dst[dst_idx + (bidx^2)] = (uchar)((t >> 8) & ~7); + dst[dst_index + bidx] = (uchar)(t << 3); + dst[dst_index + 1] = (uchar)((t >> 3) & ~3); + dst[dst_index + (bidx^2)] = (uchar)((t >> 8) & ~7); #else - dst[dst_idx + bidx] = (uchar)(t << 3); - dst[dst_idx + 1] = (uchar)((t >> 2) & ~7); - dst[dst_idx + (bidx^2)] = (uchar)((t >> 7) & ~7); + dst[dst_index + bidx] = (uchar)(t << 3); + dst[dst_index + 1] = (uchar)((t >> 2) & ~7); + dst[dst_index + (bidx^2)] = (uchar)((t >> 7) & ~7); #endif #if dcn == 4 #if greenbits == 6 - dst[dst_idx + 3] = 255; + dst[dst_index + 3] = 255; #else - dst[dst_idx + 3] = t & 0x8000 ? 255 : 0; + dst[dst_index + 3] = t & 0x8000 ? 255 : 0; #endif #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -624,25 +676,29 @@ __kernel void RGB2RGB5x5(__global const uchar* src, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - uchar4 src_pix = vload4(0, src + src_idx); + uchar4 src_pix = vload4(0, src + src_index); #if greenbits == 6 - *((__global ushort*)(dst + dst_idx)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~3) << 3)|((src_pix.R_COMP&~7) << 8)); + *((__global ushort*)(dst + dst_index)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~3) << 3)|((src_pix.R_COMP&~7) << 8)); #elif scn == 3 - *((__global ushort*)(dst + dst_idx)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~7) << 2)|((src_pix.R_COMP&~7) << 7)); + *((__global ushort*)(dst + dst_index)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~7) << 2)|((src_pix.R_COMP&~7) << 7)); #else - *((__global ushort*)(dst + dst_idx)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~7) << 2)| + *((__global ushort*)(dst + dst_index)) = (ushort)((src_pix.B_COMP >> 3)|((src_pix.G_COMP&~7) << 2)| ((src_pix.R_COMP&~7) << 7)|(src_pix.w ? 0x8000 : 0)); #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -658,26 +714,25 @@ __kernel void BGR5x52Gray(__global const uchar* src, int src_step, int src_offse if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, dst_offset + x); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x); - int t = *((__global const ushort*)(src + src_idx)); + int t = *((__global const ushort*)(src + src_index)); #if greenbits == 6 - dst[dst_idx] = (uchar)CV_DESCALE(((t << 3) & 0xf8)*B2Y + - ((t >> 3) & 0xfc)*G2Y + - ((t >> 8) & 0xf8)*R2Y, yuv_shift); + dst[dst_index] = (uchar)CV_DESCALE(mad24((t << 3) & 0xf8, B2Y, mad24((t >> 3) & 0xfc, G2Y, ((t >> 8) & 0xf8) * R2Y)), yuv_shift); #else - dst[dst_idx] = (uchar)CV_DESCALE(((t << 3) & 0xf8)*B2Y + - ((t >> 2) & 0xf8)*G2Y + - ((t >> 7) & 0xf8)*R2Y, yuv_shift); + dst[dst_index] = (uchar)CV_DESCALE(mad24((t << 3) & 0xf8, B2Y, mad24((t >> 2) & 0xf8, G2Y, ((t >> 7) & 0xf8) * R2Y)), yuv_shift); #endif + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -691,23 +746,26 @@ __kernel void Gray2BGR5x5(__global const uchar* src, int src_step, int src_offse if (x < cols) { + int src_index = mad24(y, src_step, src_offset + x); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - int t = src[src_idx]; + int t = src[src_index]; #if greenbits == 6 - *((__global ushort*)(dst + dst_idx)) = (ushort)((t >> 3) | ((t & ~3) << 3) | ((t & ~7) << 8)); + *((__global ushort*)(dst + dst_index)) = (ushort)((t >> 3) | ((t & ~3) << 3) | ((t & ~7) << 8)); #else t >>= 3; - *((__global ushort*)(dst + dst_idx)) = (ushort)(t|(t << 5)|(t << 10)); + *((__global ushort*)(dst + dst_index)) = (ushort)(t|(t << 5)|(t << 10)); #endif + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -733,40 +791,44 @@ __kernel void RGB2HSV(__global const uchar* src, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - uchar4 src_pix = vload4(0, src + src_idx); + uchar4 src_pix = vload4(0, src + src_index); int b = src_pix.B_COMP, g = src_pix.G_COMP, r = src_pix.R_COMP; int h, s, v = b; int vmin = b, diff; int vr, vg; - v = max( v, g ); - v = max( v, r ); - vmin = min( vmin, g ); - vmin = min( vmin, r ); + v = max(v, g); + v = max(v, r); + vmin = min(vmin, g); + vmin = min(vmin, r); diff = v - vmin; vr = v == r ? -1 : 0; vg = v == g ? -1 : 0; - s = (diff * sdiv_table[v] + (1 << (hsv_shift-1))) >> hsv_shift; + s = mad24(diff, sdiv_table[v], (1 << (hsv_shift-1))) >> hsv_shift; h = (vr & (g - b)) + - (~vr & ((vg & (b - r + 2 * diff)) + ((~vg) & (r - g + 4 * diff)))); - h = (h * hdiv_table[diff] + (1 << (hsv_shift-1))) >> hsv_shift; + (~vr & ((vg & mad24(diff, 2, b - r)) + ((~vg) & mad24(4, diff, r - g)))); + h = mad24(h, hdiv_table[diff], (1 << (hsv_shift-1))) >> hsv_shift; h += h < 0 ? hrange : 0; - dst[dst_idx] = convert_uchar_sat_rte(h); - dst[dst_idx + 1] = (uchar)s; - dst[dst_idx + 2] = (uchar)v; + dst[dst_index] = convert_uchar_sat_rte(h); + dst[dst_index + 1] = (uchar)s; + dst[dst_index + 2] = (uchar)v; + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -780,14 +842,15 @@ __kernel void HSV2RGB(__global const uchar* src, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - uchar4 src_pix = vload4(0, src + src_idx); + uchar4 src_pix = vload4(0, src + src_index); float h = src_pix.x, s = src_pix.y*(1/255.f), v = src_pix.z*(1/255.f); float b, g, r; @@ -821,14 +884,17 @@ __kernel void HSV2RGB(__global const uchar* src, int src_step, int src_offset, else b = g = r = v; - dst[dst_idx + bidx] = convert_uchar_sat_rte(b*255.f); - dst[dst_idx + 1] = convert_uchar_sat_rte(g*255.f); - dst[dst_idx + (bidx^2)] = convert_uchar_sat_rte(r*255.f); + dst[dst_index + bidx] = convert_uchar_sat_rte(b*255.f); + dst[dst_index + 1] = convert_uchar_sat_rte(g*255.f); + dst[dst_index + (bidx^2)] = convert_uchar_sat_rte(r*255.f); #if dcn == 4 - dst[dst_idx + 3] = MAX_NUM; + dst[dst_index + 3] = MAX_NUM; #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -844,16 +910,16 @@ __kernel void RGB2HSV(__global const uchar* srcptr, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); float4 src_pix = vload4(0, src); float b = src_pix.B_COMP, g = src_pix.G_COMP, r = src_pix.R_COMP; @@ -873,17 +939,21 @@ __kernel void RGB2HSV(__global const uchar* srcptr, int src_step, int src_offset if( v == r ) h = (g - b)*diff; else if( v == g ) - h = (b - r)*diff + 120.f; + h = fma(b - r, diff, 120.f); else - h = (r - g)*diff + 240.f; + h = fma(r - g, diff, 240.f); - if( h < 0 ) h += 360.f; + if( h < 0 ) + h += 360.f; dst[0] = h*hscale; dst[1] = s; dst[2] = v; + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -897,16 +967,17 @@ __kernel void HSV2RGB(__global const uchar* srcptr, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); float4 src_pix = vload4(0, src); float h = src_pix.x, s = src_pix.y, v = src_pix.z; @@ -947,8 +1018,11 @@ __kernel void HSV2RGB(__global const uchar* srcptr, int src_step, int src_offset #if dcn == 4 dst[3] = MAX_NUM; #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -968,14 +1042,15 @@ __kernel void RGB2HLS(__global const uchar* src, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - uchar4 src_pix = vload4(0, src + src_idx); + uchar4 src_pix = vload4(0, src + src_index); float b = src_pix.B_COMP*(1/255.f), g = src_pix.G_COMP*(1/255.f), r = src_pix.R_COMP*(1/255.f); float h = 0.f, s = 0.f, l; @@ -998,18 +1073,22 @@ __kernel void RGB2HLS(__global const uchar* src, int src_step, int src_offset, if( vmax == r ) h = (g - b)*diff; else if( vmax == g ) - h = (b - r)*diff + 120.f; + h = fma(b - r, diff, 120.f); else - h = (r - g)*diff + 240.f; + h = fma(r - g, diff, 240.f); - if( h < 0.f ) h += 360.f; + if( h < 0.f ) + h += 360.f; } - dst[dst_idx] = convert_uchar_sat_rte(h*hscale); - dst[dst_idx + 1] = convert_uchar_sat_rte(l*255.f); - dst[dst_idx + 2] = convert_uchar_sat_rte(s*255.f); + dst[dst_index] = convert_uchar_sat_rte(h*hscale); + dst[dst_index + 1] = convert_uchar_sat_rte(l*255.f); + dst[dst_index + 2] = convert_uchar_sat_rte(s*255.f); + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1023,14 +1102,15 @@ __kernel void HLS2RGB(__global const uchar* src, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - uchar4 src_pix = vload4(0, src + src_idx); + uchar4 src_pix = vload4(0, src + src_index); float h = src_pix.x, l = src_pix.y*(1.f/255.f), s = src_pix.z*(1.f/255.f); float b, g, r; @@ -1053,8 +1133,8 @@ __kernel void HLS2RGB(__global const uchar* src, int src_step, int src_offset, tab[0] = p2; tab[1] = p1; - tab[2] = p1 + (p2 - p1)*(1-h); - tab[3] = p1 + (p2 - p1)*h; + tab[2] = fma(p2 - p1, 1-h, p1); + tab[3] = fma(p2 - p1, h, p1); b = tab[sector_data[sector][0]]; g = tab[sector_data[sector][1]]; @@ -1063,14 +1143,17 @@ __kernel void HLS2RGB(__global const uchar* src, int src_step, int src_offset, else b = g = r = l; - dst[dst_idx + bidx] = convert_uchar_sat_rte(b*255.f); - dst[dst_idx + 1] = convert_uchar_sat_rte(g*255.f); - dst[dst_idx + (bidx^2)] = convert_uchar_sat_rte(r*255.f); + dst[dst_index + bidx] = convert_uchar_sat_rte(b*255.f); + dst[dst_index + 1] = convert_uchar_sat_rte(g*255.f); + dst[dst_index + (bidx^2)] = convert_uchar_sat_rte(r*255.f); #if dcn == 4 - dst[dst_idx + 3] = MAX_NUM; + dst[dst_index + 3] = MAX_NUM; #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1086,16 +1169,16 @@ __kernel void RGB2HLS(__global const uchar* srcptr, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); float4 src_pix = vload4(0, src); float b = src_pix.B_COMP, g = src_pix.G_COMP, r = src_pix.R_COMP; @@ -1119,9 +1202,9 @@ __kernel void RGB2HLS(__global const uchar* srcptr, int src_step, int src_offset if( vmax == r ) h = (g - b)*diff; else if( vmax == g ) - h = (b - r)*diff + 120.f; + h = fma(b - r, diff, 120.f); else - h = (r - g)*diff + 240.f; + h = fma(r - g, diff, 240.f); if( h < 0.f ) h += 360.f; } @@ -1129,8 +1212,11 @@ __kernel void RGB2HLS(__global const uchar* srcptr, int src_step, int src_offset dst[0] = h*hscale; dst[1] = l; dst[2] = s; + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1144,16 +1230,16 @@ __kernel void HLS2RGB(__global const uchar* srcptr, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); float4 src_pix = vload4(0, src); float h = src_pix.x, l = src_pix.y, s = src_pix.z; @@ -1178,8 +1264,8 @@ __kernel void HLS2RGB(__global const uchar* srcptr, int src_step, int src_offset tab[0] = p2; tab[1] = p1; - tab[2] = p1 + (p2 - p1)*(1-h); - tab[3] = p1 + (p2 - p1)*h; + tab[2] = fma(p2 - p1, 1-h, p1); + tab[3] = fma(p2 - p1, h, p1); b = tab[sector_data[sector][0]]; g = tab[sector_data[sector][1]]; @@ -1194,8 +1280,11 @@ __kernel void HLS2RGB(__global const uchar* srcptr, int src_step, int src_offset #if dcn == 4 dst[3] = MAX_NUM; #endif + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1215,24 +1304,25 @@ __kernel void RGBA2mRGBA(__global const uchar* src, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, src_offset + (x << 2)); + int dst_index = mad24(y, dst_step, dst_offset + (x << 2)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + (x << 2)); - int dst_idx = mad24(y, dst_step, dst_offset + (x << 2)); - uchar4 src_pix = vload4(0, src + src_idx); + uchar4 src_pix = *(__global const uchar4 *)(src + src_index); - uchar v0 = src_pix.x, v1 = src_pix.y; - uchar v2 = src_pix.z, v3 = src_pix.w; + *(__global uchar4 *)(dst + dst_index) = + (uchar4)(mad24(src_pix.x, src_pix.w, HALF_MAX) / MAX_NUM, + mad24(src_pix.y, src_pix.w, HALF_MAX) / MAX_NUM, + mad24(src_pix.z, src_pix.w, HALF_MAX) / MAX_NUM, src_pix.w); - dst[dst_idx] = (v0 * v3 + HALF_MAX) / MAX_NUM; - dst[dst_idx + 1] = (v1 * v3 + HALF_MAX) / MAX_NUM; - dst[dst_idx + 2] = (v2 * v3 + HALF_MAX) / MAX_NUM; - dst[dst_idx + 3] = v3; + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1246,25 +1336,29 @@ __kernel void mRGBA2RGBA(__global const uchar* src, int src_step, int src_offset if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, 4, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, 4, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + (x << 2)); - int dst_idx = mad24(y, dst_step, dst_offset + (x << 2)); - uchar4 src_pix = vload4(0, src + src_idx); - - uchar v0 = src_pix.x, v1 = src_pix.y; - uchar v2 = src_pix.z, v3 = src_pix.w; - uchar v3_half = v3 / 2; - - dst[dst_idx] = v3 == 0 ? 0 : (v0 * MAX_NUM + v3_half) / v3; - dst[dst_idx + 1] = v3 == 0 ? 0 : (v1 * MAX_NUM + v3_half) / v3; - dst[dst_idx + 2] = v3 == 0 ? 0 : (v2 * MAX_NUM + v3_half) / v3; - dst[dst_idx + 3] = v3; + uchar4 src_pix = *(__global const uchar4 *)(src + src_index); + uchar v3 = src_pix.w, v3_half = v3 / 2; + + if (v3 == 0) + *(__global uchar4 *)(dst + dst_index) = (uchar4)(0, 0, 0, 0); + else + *(__global uchar4 *)(dst + dst_index) = + (uchar4)(mad24(src_pix.x, MAX_NUM, v3_half) / v3, + mad24(src_pix.y, MAX_NUM, v3_half) / v3, + mad24(src_pix.z, MAX_NUM, v3_half) / v3, v3); + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1283,8 +1377,8 @@ inline float splineInterpolate(float x, __global const float * tab, int n) { int ix = clamp(convert_int_sat_rtn(x), 0, n-1); x -= ix; - tab += ix*4; - return ((tab[3]*x + tab[2])*x + tab[1])*x + tab[0]; + tab += ix << 2; + return fma(fma(fma(tab[3], x, tab[2]), x, tab[1]), x, tab[0]); } #ifdef DEPTH_0 @@ -1299,16 +1393,16 @@ __kernel void BGR2Lab(__global const uchar * src, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const uchar* src_ptr = src + src_idx; - __global uchar* dst_ptr = dst + dst_idx; + __global const uchar* src_ptr = src + src_index; + __global uchar* dst_ptr = dst + dst_index; uchar4 src_pix = vload4(0, src_ptr); int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], @@ -1316,19 +1410,22 @@ __kernel void BGR2Lab(__global const uchar * src, int src_step, int src_offset, C6 = coeffs[6], C7 = coeffs[7], C8 = coeffs[8]; int R = gammaTab[src_pix.x], G = gammaTab[src_pix.y], B = gammaTab[src_pix.z]; - int fX = LabCbrtTab_b[CV_DESCALE(R*C0 + G*C1 + B*C2, lab_shift)]; - int fY = LabCbrtTab_b[CV_DESCALE(R*C3 + G*C4 + B*C5, lab_shift)]; - int fZ = LabCbrtTab_b[CV_DESCALE(R*C6 + G*C7 + B*C8, lab_shift)]; + int fX = LabCbrtTab_b[CV_DESCALE(mad24(R, C0, mad24(G, C1, B*C2)), lab_shift)]; + int fY = LabCbrtTab_b[CV_DESCALE(mad24(R, C3, mad24(G, C4, B*C5)), lab_shift)]; + int fZ = LabCbrtTab_b[CV_DESCALE(mad24(R, C6, mad24(G, C7, B*C8)), lab_shift)]; int L = CV_DESCALE( Lscale*fY + Lshift, lab_shift2 ); - int a = CV_DESCALE( 500*(fX - fY) + 128*(1 << lab_shift2), lab_shift2 ); - int b = CV_DESCALE( 200*(fY - fZ) + 128*(1 << lab_shift2), lab_shift2 ); + int a = CV_DESCALE( mad24(500, fX - fY, 128*(1 << lab_shift2)), lab_shift2 ); + int b = CV_DESCALE( mad24(200, fY - fZ, 128*(1 << lab_shift2)), lab_shift2 ); dst_ptr[0] = SAT_CAST(L); dst_ptr[1] = SAT_CAST(a); dst_ptr[2] = SAT_CAST(b); + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1347,16 +1444,16 @@ __kernel void BGR2Lab(__global const uchar * srcptr, int src_step, int src_offse if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); float4 src_pix = vload4(0, src); float C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], @@ -1373,23 +1470,26 @@ __kernel void BGR2Lab(__global const uchar * srcptr, int src_step, int src_offse B = splineInterpolate(B * GammaTabScale, gammaTab, GAMMA_TAB_SIZE); #endif - float X = R*C0 + G*C1 + B*C2; - float Y = R*C3 + G*C4 + B*C5; - float Z = R*C6 + G*C7 + B*C8; + float X = fma(R, C0, fma(G, C1, B*C2)); + float Y = fma(R, C3, fma(G, C4, B*C5)); + float Z = fma(R, C6, fma(G, C7, B*C8)); - float FX = X > 0.008856f ? pow(X, _1_3) : (7.787f * X + _a); - float FY = Y > 0.008856f ? pow(Y, _1_3) : (7.787f * Y + _a); - float FZ = Z > 0.008856f ? pow(Z, _1_3) : (7.787f * Z + _a); + float FX = X > 0.008856f ? rootn(X, 3) : fma(7.787f, X, _a); + float FY = Y > 0.008856f ? rootn(Y, 3) : fma(7.787f, Y, _a); + float FZ = Z > 0.008856f ? rootn(Z, 3) : fma(7.787f, Z, _a); - float L = Y > 0.008856f ? (116.f * FY - 16.f) : (903.3f * Y); + float L = Y > 0.008856f ? fma(116.f, FY, -16.f) : (903.3f * Y); float a = 500.f * (FX - FY); float b = 200.f * (FY - FZ); dst[0] = L; dst[1] = a; dst[2] = b; + + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1412,7 +1512,7 @@ inline void Lab2BGR_f(const float * srcbuf, float * dstbuf, if (li <= lThresh) { y = li / 903.3f; - fy = 7.787f * y + 16.0f / 116.0f; + fy = fma(7.787f, y, 16.0f / 116.0f); } else { @@ -1422,6 +1522,7 @@ inline void Lab2BGR_f(const float * srcbuf, float * dstbuf, float fxz[] = { ai / 500.0f + fy, fy - bi / 200.0f }; + #pragma unroll for (int j = 0; j < 2; j++) if (fxz[j] <= fThresh) fxz[j] = (fxz[j] - 16.0f / 116.0f) / 7.787f; @@ -1429,9 +1530,9 @@ inline void Lab2BGR_f(const float * srcbuf, float * dstbuf, fxz[j] = fxz[j] * fxz[j] * fxz[j]; float x = fxz[0], z = fxz[1]; - float ro = clamp(C0 * x + C1 * y + C2 * z, 0.0f, 1.0f); - float go = clamp(C3 * x + C4 * y + C5 * z, 0.0f, 1.0f); - float bo = clamp(C6 * x + C7 * y + C8 * z, 0.0f, 1.0f); + float ro = clamp(fma(C0, x, fma(C1, y, C2 * z)), 0.0f, 1.0f); + float go = clamp(fma(C3, x, fma(C4, y, C5 * z)), 0.0f, 1.0f); + float bo = clamp(fma(C6, x, fma(C7, y, C8 * z)), 0.0f, 1.0f); #ifdef SRGB ro = splineInterpolate(ro * GammaTabScale, gammaTab, GAMMA_TAB_SIZE); @@ -1456,16 +1557,16 @@ __kernel void Lab2BGR(__global const uchar * src, int src_step, int src_offset, if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const uchar* src_ptr = src + src_idx; - __global uchar* dst_ptr = dst + dst_idx; + __global const uchar* src_ptr = src + src_index; + __global uchar * dst_ptr = dst + dst_index; uchar4 src_pix = vload4(0, src_ptr); float srcbuf[3], dstbuf[3]; @@ -1479,14 +1580,18 @@ __kernel void Lab2BGR(__global const uchar * src, int src_step, int src_offset, #endif coeffs, lThresh, fThresh); +#if dcn == 3 dst_ptr[0] = SAT_CAST(dstbuf[0] * 255.0f); dst_ptr[1] = SAT_CAST(dstbuf[1] * 255.0f); dst_ptr[2] = SAT_CAST(dstbuf[2] * 255.0f); -#if dcn == 4 - dst_ptr[3] = MAX_NUM; +#else + *(__global uchar4 *)dst_ptr = (uchar4)(SAT_CAST(dstbuf[0] * 255.0f), + SAT_CAST(dstbuf[1] * 255.0f), SAT_CAST(dstbuf[2] * 255.0f), MAX_NUM); #endif + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1505,16 +1610,16 @@ __kernel void Lab2BGR(__global const uchar * srcptr, int src_step, int src_offse if (x < cols) { + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + #pragma unroll for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) { if (y < rows) { - int src_idx = mad24(y, src_step, src_offset + x * scnbytes); - int dst_idx = mad24(y, dst_step, dst_offset + x * dcnbytes); - - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); float4 src_pix = vload4(0, src); float srcbuf[3], dstbuf[3]; @@ -1530,8 +1635,10 @@ __kernel void Lab2BGR(__global const uchar * srcptr, int src_step, int src_offse #if dcn == 4 dst[3] = MAX_NUM; #endif + ++y; + dst_index += dst_step; + src_index += src_step; } - ++y; } } } @@ -1555,37 +1662,46 @@ __kernel void BGR2Luv(__global const uchar * srcptr, int src_step, int src_offse __global const float * LabCbrtTab, __constant float * coeffs, float _un, float _vn) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * PIX_PER_WI_Y; - if (x < cols && y < rows) + if (x < cols) { - int src_idx = mad24(y, src_step, mad24(x, scnbytes, src_offset)); - int dst_idx = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); + #pragma unroll + for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) + if (y < rows) + { + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); - float R = src[0], G = src[1], B = src[2]; + float R = src[0], G = src[1], B = src[2]; #ifdef SRGB - R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); #endif - float X = R*coeffs[0] + G*coeffs[1] + B*coeffs[2]; - float Y = R*coeffs[3] + G*coeffs[4] + B*coeffs[5]; - float Z = R*coeffs[6] + G*coeffs[7] + B*coeffs[8]; + float X = fma(R, coeffs[0], fma(G, coeffs[1], B*coeffs[2])); + float Y = fma(R, coeffs[3], fma(G, coeffs[4], B*coeffs[5])); + float Z = fma(R, coeffs[6], fma(G, coeffs[7], B*coeffs[8])); - float L = splineInterpolate(Y*LabCbrtTabScale, LabCbrtTab, LAB_CBRT_TAB_SIZE); - L = 116.f*L - 16.f; + float L = splineInterpolate(Y*LabCbrtTabScale, LabCbrtTab, LAB_CBRT_TAB_SIZE); + L = fma(116.f, L, -16.f); - float d = (4*13) / max(X + 15 * Y + 3 * Z, FLT_EPSILON); - float u = L*(X*d - _un); - float v = L*((9*0.25f)*Y*d - _vn); + float d = 52.0f / fmax(fma(15.0f, Y, fma(3.0f, Z, X)), FLT_EPSILON); + float u = L*fma(X, d, -_un); + float v = L*fma(2.25f, Y*d, -_vn); - dst[0] = L; - dst[1] = u; - dst[2] = v; + dst[0] = L; + dst[1] = u; + dst[2] = v; + + ++y; + dst_index += dst_step; + src_index += src_step; + } } } @@ -1599,38 +1715,44 @@ __kernel void BGR2Luv(__global const uchar * src, int src_step, int src_offset, __global const float * LabCbrtTab, __constant float * coeffs, float _un, float _vn) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * PIX_PER_WI_Y; - if (x < cols && y < rows) + if (x < cols) { - int src_idx = mad24(y, src_step, mad24(x, scnbytes, src_offset)); - int dst_idx = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + src += mad24(y, src_step, mad24(x, scnbytes, src_offset)); + dst += mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); - src += src_idx; - dst += dst_idx; - - float scale = 1.0f / 255.0f; - float R = src[0]*scale, G = src[1]*scale, B = src[2]*scale; + #pragma unroll + for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) + if (y < rows) + { + float scale = 1.0f / 255.0f; + float R = src[0]*scale, G = src[1]*scale, B = src[2]*scale; #ifdef SRGB - R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); #endif - float X = R*coeffs[0] + G*coeffs[1] + B*coeffs[2]; - float Y = R*coeffs[3] + G*coeffs[4] + B*coeffs[5]; - float Z = R*coeffs[6] + G*coeffs[7] + B*coeffs[8]; + float X = fma(R, coeffs[0], fma(G, coeffs[1], B*coeffs[2])); + float Y = fma(R, coeffs[3], fma(G, coeffs[4], B*coeffs[5])); + float Z = fma(R, coeffs[6], fma(G, coeffs[7], B*coeffs[8])); + + float L = splineInterpolate(Y*LabCbrtTabScale, LabCbrtTab, LAB_CBRT_TAB_SIZE); + L = 116.f*L - 16.f; - float L = splineInterpolate(Y*LabCbrtTabScale, LabCbrtTab, LAB_CBRT_TAB_SIZE); - L = 116.f*L - 16.f; + float d = (4*13) / fmax(fma(15.0f, Y, fma(3.0f, Z, X)), FLT_EPSILON); + float u = L*(X*d - _un); + float v = L*fma(2.25f, Y*d, -_vn); - float d = (4*13) / max(X + 15 * Y + 3 * Z, FLT_EPSILON); - float u = L*(X*d - _un); - float v = L*((9*0.25f)*Y*d - _vn); + dst[0] = SAT_CAST(L * 2.55f); + dst[1] = SAT_CAST(fma(u, 0.72033898305084743f, 96.525423728813564f)); + dst[2] = SAT_CAST(fma(v, 0.99609375f, 139.453125f)); - dst[0] = SAT_CAST(L * 2.55f); - dst[1] = SAT_CAST(mad(u, 0.72033898305084743f, 96.525423728813564f)); - dst[2] = SAT_CAST(mad(v, 0.99609375f, 139.453125f)); + ++y; + dst += dst_step; + src += src_step; + } } } @@ -1646,42 +1768,50 @@ __kernel void Luv2BGR(__global const uchar * srcptr, int src_step, int src_offse __constant float * coeffs, float _un, float _vn) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * PIX_PER_WI_Y; - if (x < cols && y < rows) + if (x < cols) { - int src_idx = mad24(y, src_step, mad24(x, scnbytes, src_offset)); - int dst_idx = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); - - __global const float * src = (__global const float *)(srcptr + src_idx); - __global float * dst = (__global float *)(dstptr + dst_idx); - - float L = src[0], u = src[1], v = src[2], d, X, Y, Z; - Y = (L + 16.f) * (1.f/116.f); - Y = Y*Y*Y; - d = (1.f/13.f)/L; - u = u*d + _un; - v = v*d + _vn; - float iv = 1.f/v; - X = 2.25f * u * Y * iv ; - Z = (12 - 3 * u - 20 * v) * Y * 0.25f * iv; - - float R = X*coeffs[0] + Y*coeffs[1] + Z*coeffs[2]; - float G = X*coeffs[3] + Y*coeffs[4] + Z*coeffs[5]; - float B = X*coeffs[6] + Y*coeffs[7] + Z*coeffs[8]; + int src_index = mad24(y, src_step, mad24(x, scnbytes, src_offset)); + int dst_index = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + + #pragma unroll + for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) + if (y < rows) + { + __global const float * src = (__global const float *)(srcptr + src_index); + __global float * dst = (__global float *)(dstptr + dst_index); + + float L = src[0], u = src[1], v = src[2], d, X, Y, Z; + Y = (L + 16.f) * (1.f/116.f); + Y = Y*Y*Y; + d = (1.f/13.f)/L; + u = fma(u, d, _un); + v = fma(v, d, _vn); + float iv = 1.f/v; + X = 2.25f * u * Y * iv; + Z = (12 - fma(3.0f, u, 20.0f * v)) * Y * 0.25f * iv; + + float R = fma(X, coeffs[0], fma(Y, coeffs[1], Z * coeffs[2])); + float G = fma(X, coeffs[3], fma(Y, coeffs[4], Z * coeffs[5])); + float B = fma(X, coeffs[6], fma(Y, coeffs[7], Z * coeffs[8])); #ifdef SRGB - R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); #endif - dst[0] = R; - dst[1] = G; - dst[2] = B; + dst[0] = R; + dst[1] = G; + dst[2] = B; #if dcn == 4 - dst[3] = MAX_NUM; + dst[3] = MAX_NUM; #endif + ++y; + dst_index += dst_step; + src_index += src_step; + } } } @@ -1695,46 +1825,56 @@ __kernel void Luv2BGR(__global const uchar * src, int src_step, int src_offset, __constant float * coeffs, float _un, float _vn) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * PIX_PER_WI_Y; - if (x < cols && y < rows) + if (x < cols) { - int src_idx = mad24(y, src_step, mad24(x, scnbytes, src_offset)); - int dst_idx = mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); - - src += src_idx; - dst += dst_idx; - - float d, X, Y, Z; - float L = src[0]*(100.f/255.f); - float u = (float)(src[1]*1.388235294117647f - 134.f); - float v = (float)(src[2]*1.003921568627451f - 140.f); - Y = (L + 16.f) * (1.f/116.f); - Y = Y*Y*Y; - d = (1.f/13.f)/L; - u = u*d + _un; - v = v*d + _vn; - float iv = 1.f/v; - X = 2.25f * u * Y * iv ; - Z = (12 - 3 * u - 20 * v) * Y * 0.25f * iv; - - float R = X*coeffs[0] + Y*coeffs[1] + Z*coeffs[2]; - float G = X*coeffs[3] + Y*coeffs[4] + Z*coeffs[5]; - float B = X*coeffs[6] + Y*coeffs[7] + Z*coeffs[8]; + src += mad24(y, src_step, mad24(x, scnbytes, src_offset)); + dst += mad24(y, dst_step, mad24(x, dcnbytes, dst_offset)); + + #pragma unroll + for (int cy = 0; cy < PIX_PER_WI_Y; ++cy) + if (y < rows) + { + float d, X, Y, Z; + float L = src[0]*(100.f/255.f); + float u = fma(convert_float(src[1]), 1.388235294117647f, -134.f); + float v = fma(convert_float(src[2]), 1.003921568627451f, - 140.f); + Y = (L + 16.f) * (1.f/116.f); + Y = Y*Y*Y; + d = (1.f/13.f)/L; + u = fma(u, d, _un); + v = fma(v, d, _vn); + float iv = 1.f/v; + X = 2.25f * u * Y * iv ; + Z = (12 - fma(3.0f, u, 20.0f * v)) * Y * 0.25f * iv; + + float R = fma(X, coeffs[0], fma(Y, coeffs[1], Z * coeffs[2])); + float G = fma(X, coeffs[3], fma(Y, coeffs[4], Z * coeffs[5])); + float B = fma(X, coeffs[6], fma(Y, coeffs[7], Z * coeffs[8])); #ifdef SRGB - R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); - B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + R = splineInterpolate(R*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + G = splineInterpolate(G*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); + B = splineInterpolate(B*GammaTabScale, gammaTab, GAMMA_TAB_SIZE); #endif - dst[0] = SAT_CAST(R * 255.0f); - dst[1] = SAT_CAST(G * 255.0f); - dst[2] = SAT_CAST(B * 255.0f); + uchar dst0 = SAT_CAST(R * 255.0f); + uchar dst1 = SAT_CAST(G * 255.0f); + uchar dst2 = SAT_CAST(B * 255.0f); #if dcn == 4 - dst[3] = MAX_NUM; + *(__global uchar4 *)dst = (uchar4)(dst0, dst1, dst2, MAX_NUM); +#else + dst[0] = dst0; + dst[1] = dst1; + dst[2] = dst2; #endif + + ++y; + dst += dst_step; + src += src_step; + } } } diff --git a/modules/imgproc/src/opencl/filterSep_singlePass.cl b/modules/imgproc/src/opencl/filterSep_singlePass.cl index 3952577d77..8c14f2d77e 100644 --- a/modules/imgproc/src/opencl/filterSep_singlePass.cl +++ b/modules/imgproc/src/opencl/filterSep_singlePass.cl @@ -119,20 +119,17 @@ __kernel void sep_filter(__global uchar* Src, int src_step, int srcOffsetX, int int liy = get_local_id(1); int x = get_global_id(0); - int y = get_global_id(1); // calculate pixel position in source image taking image offset into account int srcX = x + srcOffsetX - RADIUSX; - int srcY = y + srcOffsetY - RADIUSY; // extrapolate coordinates, if needed // and read my own source pixel into local memory // with account for extra border pixels, which will be read by starting workitems int clocY = liy; - int cSrcY = srcY; do { - int yb = cSrcY; + int yb = clocY + srcOffsetY - RADIUSY; EXTRAPOLATE(yb, (height)); int clocX = lix; @@ -149,53 +146,80 @@ __kernel void sep_filter(__global uchar* Src, int src_step, int srcOffsetX, int while(clocX < BLK_X+(RADIUSX*2)); clocY += BLK_Y; - cSrcY += BLK_Y; } while (clocY < BLK_Y+(RADIUSY*2)); barrier(CLK_LOCAL_MEM_FENCE); - // do vertical filter pass - // and store intermediate results to second local memory array - int i, clocX = lix; - WT sum = (WT) 0; - do + for (int y = 0; y < dst_rows; y+=BLK_Y) { - sum = (WT) 0; - for (i=0; i<=2*RADIUSY; i++) + // do vertical filter pass + // and store intermediate results to second local memory array + int i, clocX = lix; + WT sum = (WT) 0; + do + { + sum = (WT) 0; + for (i=0; i<=2*RADIUSY; i++) #if (defined(INTEGER_ARITHMETIC) && !INTEL_DEVICE) - sum = mad24(lsmem[liy+i][clocX], mat_kernelY[i], sum); + sum = mad24(lsmem[liy + i][clocX], mat_kernelY[i], sum); #else - sum = mad(lsmem[liy+i][clocX], mat_kernelY[i], sum); + sum = mad(lsmem[liy + i][clocX], mat_kernelY[i], sum); #endif - lsmemDy[liy][clocX] = sum; - clocX += BLK_X; - } - while(clocX < BLK_X+(RADIUSX*2)); - barrier(CLK_LOCAL_MEM_FENCE); - - // if this pixel happened to be out of image borders because of global size rounding, - // then just return - if( x >= dst_cols || y >=dst_rows ) - return; + lsmemDy[liy][clocX] = sum; + clocX += BLK_X; + } + while(clocX < BLK_X+(RADIUSX*2)); + barrier(CLK_LOCAL_MEM_FENCE); - // do second horizontal filter pass - // and calculate final result - sum = 0.0f; - for (i=0; i<=2*RADIUSX; i++) + // if this pixel happened to be out of image borders because of global size rounding, + // then just return + if ((x < dst_cols) && (y + liy < dst_rows)) + { + // do second horizontal filter pass + // and calculate final result + sum = 0.0f; + for (i=0; i<=2*RADIUSX; i++) #if (defined(INTEGER_ARITHMETIC) && !INTEL_DEVICE) - sum = mad24(lsmemDy[liy][lix+i], mat_kernelX[i], sum); + sum = mad24(lsmemDy[liy][lix+i], mat_kernelX[i], sum); #else - sum = mad(lsmemDy[liy][lix+i], mat_kernelX[i], sum); + sum = mad(lsmemDy[liy][lix+i], mat_kernelX[i], sum); #endif #ifdef INTEGER_ARITHMETIC #ifdef INTEL_DEVICE - sum = (sum + (1 << (SHIFT_BITS-1))) / (1 << SHIFT_BITS); + sum = (sum + (1 << (SHIFT_BITS-1))) / (1 << SHIFT_BITS); #else - sum = (sum + (1 << (SHIFT_BITS-1))) >> SHIFT_BITS; + sum = (sum + (1 << (SHIFT_BITS-1))) >> SHIFT_BITS; #endif #endif + // store result into destination image + storepix(convertToDstT(sum + (WT)(delta)), Dst + mad24(y + liy, dst_step, mad24(x, DSTSIZE, dst_offset))); + } + + for (int i = liy * BLK_X + lix; i < (RADIUSY*2) * (BLK_X+(RADIUSX*2)); i += BLK_X * BLK_Y) + { + int clocX = i % (BLK_X+(RADIUSX*2)); + int clocY = i / (BLK_X+(RADIUSX*2)); + lsmem[clocY][clocX] = lsmem[clocY + BLK_Y][clocX]; + } + barrier(CLK_LOCAL_MEM_FENCE); + + int yb = y + liy + BLK_Y + srcOffsetY + RADIUSY; + EXTRAPOLATE(yb, (height)); + + clocX = lix; + int cSrcX = x + srcOffsetX - RADIUSX; + do + { + int xb = cSrcX; + EXTRAPOLATE(xb,(width)); + lsmem[liy + 2*RADIUSY][clocX] = ELEM(xb, yb, (width), (height), 0 ); + + clocX += BLK_X; + cSrcX += BLK_X; + } + while(clocX < BLK_X+(RADIUSX*2)); + barrier(CLK_LOCAL_MEM_FENCE); + } - // store result into destination image - storepix(convertToDstT(sum + (WT)(delta)), Dst + mad24(y, dst_step, mad24(x, DSTSIZE, dst_offset))); } diff --git a/modules/imgproc/src/opencl/integral_sqrsum.cl b/modules/imgproc/src/opencl/integral_sqrsum.cl deleted file mode 100644 index 8b5d2452b8..0000000000 --- a/modules/imgproc/src/opencl/integral_sqrsum.cl +++ /dev/null @@ -1,512 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Shengen Yan,yanshengen@gmail.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#ifdef DOUBLE_SUPPORT -#ifdef cl_amd_fp64 -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#elif defined (cl_khr_fp64) -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#endif -#endif - -#if sqdepth == 6 -#define CONVERT(step) ((step)>>1) -#else -#define CONVERT(step) ((step)) -#endif - -#define LSIZE 256 -#define LSIZE_1 255 -#define LSIZE_2 254 -#define HF_LSIZE 128 -#define LOG_LSIZE 8 -#define LOG_NUM_BANKS 5 -#define NUM_BANKS 32 -#define GET_CONFLICT_OFFSET(lid) ((lid) >> LOG_NUM_BANKS) - -#define noconvert - -#if sdepth == 4 - -kernel void integral_cols(__global uchar4 *src, __global int *sum, __global TYPE *sqsum, - int src_offset, int pre_invalid, int rows, int cols, int src_step, int dst_step, int dst1_step) -{ - int lid = get_local_id(0); - int gid = get_group_id(0); - int4 src_t[2], sum_t[2]; - TYPE4 sqsum_t[2]; - __local int4 lm_sum[2][LSIZE + LOG_LSIZE]; - __local TYPE4 lm_sqsum[2][LSIZE + LOG_LSIZE]; - __local int* sum_p; - __local TYPE* sqsum_p; - src_step = src_step >> 2; - gid = gid << 1; - for(int i = 0; i < rows; i =i + LSIZE_1) - { - src_t[0] = (i + lid < rows ? convert_int4(src[src_offset + (lid+i) * src_step + min(gid, cols - 1)]) : 0); - src_t[1] = (i + lid < rows ? convert_int4(src[src_offset + (lid+i) * src_step + min(gid + 1, cols - 1)]) : 0); - - sum_t[0] = (i == 0 ? 0 : lm_sum[0][LSIZE_2 + LOG_LSIZE]); - sqsum_t[0] = (i == 0 ? (TYPE4)0 : lm_sqsum[0][LSIZE_2 + LOG_LSIZE]); - sum_t[1] = (i == 0 ? 0 : lm_sum[1][LSIZE_2 + LOG_LSIZE]); - sqsum_t[1] = (i == 0 ? (TYPE4)0 : lm_sqsum[1][LSIZE_2 + LOG_LSIZE]); - barrier(CLK_LOCAL_MEM_FENCE); - - int bf_loc = lid + GET_CONFLICT_OFFSET(lid); - lm_sum[0][bf_loc] = src_t[0]; - lm_sqsum[0][bf_loc] = convert_TYPE4(src_t[0] * src_t[0]); - - lm_sum[1][bf_loc] = src_t[1]; - lm_sqsum[1][bf_loc] = convert_TYPE4(src_t[1] * src_t[1]); - - int offset = 1; - for(int d = LSIZE >> 1 ; d > 0; d>>=1) - { - barrier(CLK_LOCAL_MEM_FENCE); - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - } - offset <<= 1; - } - barrier(CLK_LOCAL_MEM_FENCE); - if(lid < 2) - { - lm_sum[lid][LSIZE_2 + LOG_LSIZE] = 0; - lm_sqsum[lid][LSIZE_2 + LOG_LSIZE] = 0; - } - for(int d = 1; d < LSIZE; d <<= 1) - { - barrier(CLK_LOCAL_MEM_FENCE); - offset >>= 1; - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sum[lid >> 7][ai] = lm_sum[lid >> 7][bi] - lm_sum[lid >> 7][ai]; - - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - lm_sqsum[lid >> 7][ai] = lm_sqsum[lid >> 7][bi] - lm_sqsum[lid >> 7][ai]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - int loc_s0 = gid * dst_step + i + lid - 1 - pre_invalid * dst_step /4, loc_s1 = loc_s0 + dst_step ; - int loc_sq0 = gid * CONVERT(dst1_step) + i + lid - 1 - pre_invalid * dst1_step / sizeof(TYPE),loc_sq1 = loc_sq0 + CONVERT(dst1_step); - if(lid > 0 && (i+lid) <= rows) - { - lm_sum[0][bf_loc] += sum_t[0]; - lm_sum[1][bf_loc] += sum_t[1]; - lm_sqsum[0][bf_loc] += sqsum_t[0]; - lm_sqsum[1][bf_loc] += sqsum_t[1]; - sum_p = (__local int*)(&(lm_sum[0][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[0][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 4 + k >= cols + pre_invalid || gid * 4 + k < pre_invalid) continue; - sum[loc_s0 + k * dst_step / 4] = sum_p[k]; - sqsum[loc_sq0 + k * dst1_step / sizeof(TYPE)] = sqsum_p[k]; - } - sum_p = (__local int*)(&(lm_sum[1][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[1][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 4 + k + 4 >= cols + pre_invalid) break; - sum[loc_s1 + k * dst_step / 4] = sum_p[k]; - sqsum[loc_sq1 + k * dst1_step / sizeof(TYPE)] = sqsum_p[k]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - } -} - -kernel void integral_rows(__global int4 *srcsum, __global TYPE4 * srcsqsum,__global int *sum, - __global TYPE *sqsum, int rows, int cols, int src_step, int src1_step, int sum_step, - int sqsum_step, int sum_offset, int sqsum_offset) -{ - int lid = get_local_id(0); - int gid = get_group_id(0); - int4 src_t[2], sum_t[2]; - TYPE4 sqsrc_t[2],sqsum_t[2]; - __local int4 lm_sum[2][LSIZE + LOG_LSIZE]; - __local TYPE4 lm_sqsum[2][LSIZE + LOG_LSIZE]; - __local int *sum_p; - __local TYPE *sqsum_p; - src_step = src_step >> 4; - src1_step = (src1_step / sizeof(TYPE)) >> 2 ; - gid <<= 1; - for(int i = 0; i < rows; i =i + LSIZE_1) - { - src_t[0] = i + lid < rows ? srcsum[(lid+i) * src_step + gid ] : (int4)0; - sqsrc_t[0] = i + lid < rows ? srcsqsum[(lid+i) * src1_step + gid ] : (TYPE4)0; - src_t[1] = i + lid < rows ? srcsum[(lid+i) * src_step + gid + 1] : (int4)0; - sqsrc_t[1] = i + lid < rows ? srcsqsum[(lid+i) * src1_step + gid + 1] : (TYPE4)0; - - sum_t[0] = (i == 0 ? 0 : lm_sum[0][LSIZE_2 + LOG_LSIZE]); - sqsum_t[0] = (i == 0 ? (TYPE4)0 : lm_sqsum[0][LSIZE_2 + LOG_LSIZE]); - sum_t[1] = (i == 0 ? 0 : lm_sum[1][LSIZE_2 + LOG_LSIZE]); - sqsum_t[1] = (i == 0 ? (TYPE4)0 : lm_sqsum[1][LSIZE_2 + LOG_LSIZE]); - barrier(CLK_LOCAL_MEM_FENCE); - - int bf_loc = lid + GET_CONFLICT_OFFSET(lid); - lm_sum[0][bf_loc] = src_t[0]; - lm_sqsum[0][bf_loc] = sqsrc_t[0]; - - lm_sum[1][bf_loc] = src_t[1]; - lm_sqsum[1][bf_loc] = sqsrc_t[1]; - - int offset = 1; - for(int d = LSIZE >> 1 ; d > 0; d>>=1) - { - barrier(CLK_LOCAL_MEM_FENCE); - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - } - offset <<= 1; - } - barrier(CLK_LOCAL_MEM_FENCE); - if(lid < 2) - { - lm_sum[lid][LSIZE_2 + LOG_LSIZE] = 0; - lm_sqsum[lid][LSIZE_2 + LOG_LSIZE] = 0; - } - for(int d = 1; d < LSIZE; d <<= 1) - { - barrier(CLK_LOCAL_MEM_FENCE); - offset >>= 1; - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sum[lid >> 7][ai] = lm_sum[lid >> 7][bi] - lm_sum[lid >> 7][ai]; - - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - lm_sqsum[lid >> 7][ai] = lm_sqsum[lid >> 7][bi] - lm_sqsum[lid >> 7][ai]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - if(gid == 0 && (i + lid) <= rows) - { - sum[sum_offset + i + lid] = 0; - sqsum[sqsum_offset + i + lid] = 0; - } - if(i + lid == 0) - { - int loc0 = gid * sum_step; - int loc1 = gid * CONVERT(sqsum_step); - for(int k = 1; k <= 8; k++) - { - if(gid * 4 + k > cols) break; - sum[sum_offset + loc0 + k * sum_step / 4] = 0; - sqsum[sqsum_offset + loc1 + k * sqsum_step / sizeof(TYPE)] = 0; - } - } - int loc_s0 = sum_offset + gid * sum_step + sum_step / 4 + i + lid, loc_s1 = loc_s0 + sum_step ; - int loc_sq0 = sqsum_offset + gid * CONVERT(sqsum_step) + sqsum_step / sizeof(TYPE) + i + lid, loc_sq1 = loc_sq0 + CONVERT(sqsum_step) ; - - if(lid > 0 && (i+lid) <= rows) - { - lm_sum[0][bf_loc] += sum_t[0]; - lm_sum[1][bf_loc] += sum_t[1]; - lm_sqsum[0][bf_loc] += sqsum_t[0]; - lm_sqsum[1][bf_loc] += sqsum_t[1]; - sum_p = (__local int*)(&(lm_sum[0][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[0][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 4 + k >= cols) break; - sum[loc_s0 + k * sum_step / 4] = sum_p[k]; - sqsum[loc_sq0 + k * sqsum_step / sizeof(TYPE)] = sqsum_p[k]; - } - sum_p = (__local int*)(&(lm_sum[1][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[1][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 4 + 4 + k >= cols) break; - sum[loc_s1 + k * sum_step / 4] = sum_p[k]; - sqsum[loc_sq1 + k * sqsum_step / sizeof(TYPE)] = sqsum_p[k]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - } -} - -#elif sdepth == 5 - -kernel void integral_cols(__global uchar4 *src, __global float *sum, __global TYPE *sqsum, - int src_offset, int pre_invalid, int rows, int cols, int src_step, int dst_step, int dst1_step) -{ - int lid = get_local_id(0); - int gid = get_group_id(0); - float4 src_t[2], sum_t[2]; - TYPE4 sqsum_t[2]; - __local float4 lm_sum[2][LSIZE + LOG_LSIZE]; - __local TYPE4 lm_sqsum[2][LSIZE + LOG_LSIZE]; - __local float* sum_p; - __local TYPE* sqsum_p; - src_step = src_step >> 2; - gid = gid << 1; - for(int i = 0; i < rows; i =i + LSIZE_1) - { - src_t[0] = (i + lid < rows ? convert_float4(src[src_offset + (lid+i) * src_step + min(gid, cols - 1)]) : (float4)0); - src_t[1] = (i + lid < rows ? convert_float4(src[src_offset + (lid+i) * src_step + min(gid + 1, cols - 1)]) : (float4)0); - - sum_t[0] = (i == 0 ? (float4)0 : lm_sum[0][LSIZE_2 + LOG_LSIZE]); - sqsum_t[0] = (i == 0 ? (TYPE4)0 : lm_sqsum[0][LSIZE_2 + LOG_LSIZE]); - sum_t[1] = (i == 0 ? (float4)0 : lm_sum[1][LSIZE_2 + LOG_LSIZE]); - sqsum_t[1] = (i == 0 ? (TYPE4)0 : lm_sqsum[1][LSIZE_2 + LOG_LSIZE]); - barrier(CLK_LOCAL_MEM_FENCE); - - int bf_loc = lid + GET_CONFLICT_OFFSET(lid); - lm_sum[0][bf_loc] = src_t[0]; - lm_sqsum[0][bf_loc] = convert_TYPE4(src_t[0] * src_t[0]); -// printf("%f\n", src_t[0].s0); - - lm_sum[1][bf_loc] = src_t[1]; - lm_sqsum[1][bf_loc] = convert_TYPE4(src_t[1] * src_t[1]); - - int offset = 1; - for(int d = LSIZE >> 1 ; d > 0; d>>=1) - { - barrier(CLK_LOCAL_MEM_FENCE); - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - } - offset <<= 1; - } - barrier(CLK_LOCAL_MEM_FENCE); - if(lid < 2) - { - lm_sum[lid][LSIZE_2 + LOG_LSIZE] = 0; - lm_sqsum[lid][LSIZE_2 + LOG_LSIZE] = 0; - } - for(int d = 1; d < LSIZE; d <<= 1) - { - barrier(CLK_LOCAL_MEM_FENCE); - offset >>= 1; - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sum[lid >> 7][ai] = lm_sum[lid >> 7][bi] - lm_sum[lid >> 7][ai]; - - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - lm_sqsum[lid >> 7][ai] = lm_sqsum[lid >> 7][bi] - lm_sqsum[lid >> 7][ai]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - int loc_s0 = gid * dst_step + i + lid - 1 - pre_invalid * dst_step / 4, loc_s1 = loc_s0 + dst_step ; - int loc_sq0 = gid * CONVERT(dst1_step) + i + lid - 1 - pre_invalid * dst1_step / sizeof(TYPE), loc_sq1 = loc_sq0 + CONVERT(dst1_step); - if(lid > 0 && (i+lid) <= rows) - { - lm_sum[0][bf_loc] += sum_t[0]; - lm_sum[1][bf_loc] += sum_t[1]; - lm_sqsum[0][bf_loc] += sqsum_t[0]; - lm_sqsum[1][bf_loc] += sqsum_t[1]; - sum_p = (__local float*)(&(lm_sum[0][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[0][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 4 + k >= cols + pre_invalid || gid * 4 + k < pre_invalid) continue; - sum[loc_s0 + k * dst_step / 4] = sum_p[k]; - sqsum[loc_sq0 + k * dst1_step / sizeof(TYPE)] = sqsum_p[k]; - } - sum_p = (__local float*)(&(lm_sum[1][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[1][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 4 + k + 4 >= cols + pre_invalid) break; - sum[loc_s1 + k * dst_step / 4] = sum_p[k]; - sqsum[loc_sq1 + k * dst1_step / sizeof(TYPE)] = sqsum_p[k]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - } -} - -kernel void integral_rows(__global float4 *srcsum, __global TYPE4 * srcsqsum, __global float *sum , - __global TYPE *sqsum, int rows, int cols, int src_step, int src1_step, int sum_step, - int sqsum_step, int sum_offset, int sqsum_offset) -{ - int lid = get_local_id(0); - int gid = get_group_id(0); - float4 src_t[2], sum_t[2]; - TYPE4 sqsrc_t[2],sqsum_t[2]; - __local float4 lm_sum[2][LSIZE + LOG_LSIZE]; - __local TYPE4 lm_sqsum[2][LSIZE + LOG_LSIZE]; - __local float *sum_p; - __local TYPE *sqsum_p; - src_step = src_step >> 4; - src1_step = (src1_step / sizeof(TYPE)) >> 2; - for(int i = 0; i < rows; i =i + LSIZE_1) - { - src_t[0] = i + lid < rows ? srcsum[(lid+i) * src_step + gid * 2] : (float4)0; - sqsrc_t[0] = i + lid < rows ? srcsqsum[(lid+i) * src1_step + gid * 2] : (TYPE4)0; - src_t[1] = i + lid < rows ? srcsum[(lid+i) * src_step + gid * 2 + 1] : (float4)0; - sqsrc_t[1] = i + lid < rows ? srcsqsum[(lid+i) * src1_step + gid * 2 + 1] : (TYPE4)0; - - sum_t[0] = (i == 0 ? (float4)0 : lm_sum[0][LSIZE_2 + LOG_LSIZE]); - sqsum_t[0] = (i == 0 ? (TYPE4)0 : lm_sqsum[0][LSIZE_2 + LOG_LSIZE]); - sum_t[1] = (i == 0 ? (float4)0 : lm_sum[1][LSIZE_2 + LOG_LSIZE]); - sqsum_t[1] = (i == 0 ? (TYPE4)0 : lm_sqsum[1][LSIZE_2 + LOG_LSIZE]); - barrier(CLK_LOCAL_MEM_FENCE); - - int bf_loc = lid + GET_CONFLICT_OFFSET(lid); - lm_sum[0][bf_loc] = src_t[0]; - lm_sqsum[0][bf_loc] = sqsrc_t[0]; - - lm_sum[1][bf_loc] = src_t[1]; - lm_sqsum[1][bf_loc] = sqsrc_t[1]; - - int offset = 1; - for(int d = LSIZE >> 1 ; d > 0; d>>=1) - { - barrier(CLK_LOCAL_MEM_FENCE); - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - } - offset <<= 1; - } - barrier(CLK_LOCAL_MEM_FENCE); - if(lid < 2) - { - lm_sum[lid][LSIZE_2 + LOG_LSIZE] = 0; - lm_sqsum[lid][LSIZE_2 + LOG_LSIZE] = 0; - } - for(int d = 1; d < LSIZE; d <<= 1) - { - barrier(CLK_LOCAL_MEM_FENCE); - offset >>= 1; - int ai = offset * (((lid & 127)<<1) +1) - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sum[lid >> 7][ai] = lm_sum[lid >> 7][bi] - lm_sum[lid >> 7][ai]; - - lm_sqsum[lid >> 7][bi] += lm_sqsum[lid >> 7][ai]; - lm_sqsum[lid >> 7][ai] = lm_sqsum[lid >> 7][bi] - lm_sqsum[lid >> 7][ai]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - if(gid == 0 && (i + lid) <= rows) - { - sum[sum_offset + i + lid] = 0; - sqsum[sqsum_offset + i + lid] = 0; - } - if(i + lid == 0) - { - int loc0 = gid * 2 * sum_step; - int loc1 = gid * 2 * CONVERT(sqsum_step); - for(int k = 1; k <= 8; k++) - { - if(gid * 8 + k > cols) break; - sum[sum_offset + loc0 + k * sum_step / 4] = 0; - sqsum[sqsum_offset + loc1 + k * sqsum_step / sizeof(TYPE)] = 0; - } - } - int loc_s0 = sum_offset + gid * 2 * sum_step + sum_step / 4 + i + lid, loc_s1 = loc_s0 + sum_step ; - int loc_sq0 = sqsum_offset + gid * 2 * CONVERT(sqsum_step) + sqsum_step / sizeof(TYPE) + i + lid, loc_sq1 = loc_sq0 + CONVERT(sqsum_step) ; - if(lid > 0 && (i+lid) <= rows) - { - lm_sum[0][bf_loc] += sum_t[0]; - lm_sum[1][bf_loc] += sum_t[1]; - lm_sqsum[0][bf_loc] += sqsum_t[0]; - lm_sqsum[1][bf_loc] += sqsum_t[1]; - sum_p = (__local float*)(&(lm_sum[0][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[0][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 8 + k >= cols) break; - sum[loc_s0 + k * sum_step / 4] = sum_p[k]; - sqsum[loc_sq0 + k * sqsum_step / sizeof(TYPE)] = sqsum_p[k]; - } - sum_p = (__local float*)(&(lm_sum[1][bf_loc])); - sqsum_p = (__local TYPE*)(&(lm_sqsum[1][bf_loc])); - for(int k = 0; k < 4; k++) - { - if(gid * 8 + 4 + k >= cols) break; - sum[loc_s1 + k * sum_step / 4] = sum_p[k]; - sqsum[loc_sq1 + k * sqsum_step / sizeof(TYPE)] = sqsum_p[k]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - } -} - -#endif diff --git a/modules/imgproc/src/opencl/integral_sum.cl b/modules/imgproc/src/opencl/integral_sum.cl index 333c7121cb..49a3bde955 100644 --- a/modules/imgproc/src/opencl/integral_sum.cl +++ b/modules/imgproc/src/opencl/integral_sum.cl @@ -1,46 +1,9 @@ /*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// Copyright (C) 2014, Itseez, Inc., all rights reserved. // Third party copyrights are property of their respective owners. -// -// @Authors -// Shengen Yan,yanshengen@gmail.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// //M*/ #ifdef DOUBLE_SUPPORT @@ -51,237 +14,170 @@ #endif #endif -#define LSIZE 256 -#define LSIZE_1 255 -#define LSIZE_2 254 -#define HF_LSIZE 128 -#define LOG_LSIZE 8 -#define LOG_NUM_BANKS 5 -#define NUM_BANKS 32 -#define GET_CONFLICT_OFFSET(lid) ((lid) >> LOG_NUM_BANKS) - -#if sdepth == 4 -#define sumT int -#define vecSumT int4 -#define convertToSum4 convert_int4 -#elif sdepth == 5 -#define sumT float -#define vecSumT float4 -#define convertToSum4 convert_float4 +#ifndef LOCAL_SUM_SIZE +#define LOCAL_SUM_SIZE 16 #endif +#define LOCAL_SUM_STRIDE (LOCAL_SUM_SIZE + 1) + -kernel void integral_sum_cols(__global const uchar4 *src, __global uchar *sum_ptr, - int src_offset, int rows, int cols, int src_step, int dst_step) +kernel void integral_sum_cols(__global const uchar *src_ptr, int src_step, int src_offset, int rows, int cols, + __global uchar *buf_ptr, int buf_step, int buf_offset +#ifdef SUM_SQUARE + ,__global uchar *buf_sq_ptr, int buf_sq_step, int buf_sq_offset +#endif + ) { - __global sumT *sum = (__global sumT *)sum_ptr; + __local sumT lm_sum[LOCAL_SUM_STRIDE * LOCAL_SUM_SIZE]; +#ifdef SUM_SQUARE + __local sumSQT lm_sum_sq[LOCAL_SUM_STRIDE * LOCAL_SUM_SIZE]; +#endif int lid = get_local_id(0); int gid = get_group_id(0); - vecSumT src_t[2], sum_t[2]; - __local vecSumT lm_sum[2][LSIZE + LOG_LSIZE]; - __local sumT* sum_p; - src_step = src_step >> 2; - gid = gid << 1; - int lid_prim = ((lid & 127) << 1) + 1; - for (int i = 0; i < rows; i += LSIZE_1) - { - if (i + lid < rows) - { - int src_index = mad24((lid+i), src_step, gid + src_offset); - src_t[0] = convertToSum4(src[src_index]); - src_t[1] = convertToSum4(src[src_index + 1]); - } - else - { - src_t[0] = (vecSumT)0; - src_t[1] = (vecSumT)0; - } - if (i == 0) - { - sum_t[0] = (vecSumT)0; - sum_t[1] = (vecSumT)0; - } - else - { - sum_t[0] = lm_sum[0][LSIZE_2 + LOG_LSIZE]; - sum_t[1] = lm_sum[1][LSIZE_2 + LOG_LSIZE]; - } - barrier(CLK_LOCAL_MEM_FENCE); - - int bf_loc = lid + GET_CONFLICT_OFFSET(lid); - - lm_sum[0][bf_loc] = src_t[0]; - lm_sum[1][bf_loc] = src_t[1]; + int x = get_global_id(0); + int src_index = x + src_offset; - int offset = 1; - for (int d = LSIZE >> 1 ; d > 0; d>>=1) + sumT accum = 0; +#ifdef SUM_SQUARE + sumSQT accum_sq = 0; +#endif + for (int y = 0; y < rows; y += LOCAL_SUM_SIZE) + { + int lsum_index = lid; + #pragma unroll + for (int yin = 0; yin < LOCAL_SUM_SIZE; yin++, src_index+=src_step, lsum_index += LOCAL_SUM_STRIDE) { - barrier(CLK_LOCAL_MEM_FENCE); - int ai = offset * lid_prim - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - - if((lid & 127) < d) + if ((x < cols) && (y + yin < rows)) { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; + __global const uchar *src = src_ptr + src_index; + accum += src[0]; +#ifdef SUM_SQUARE + sumSQT temp = src[0] * src[0]; + accum_sq += temp; +#endif } - offset <<= 1; + lm_sum[lsum_index] = accum; +#ifdef SUM_SQUARE + lm_sum_sq[lsum_index] = accum_sq; +#endif } barrier(CLK_LOCAL_MEM_FENCE); - if (lid < 2) - { - lm_sum[lid][LSIZE_2 + LOG_LSIZE] = 0; - } - for (int d = 1; d < LSIZE; d <<= 1) - { - barrier(CLK_LOCAL_MEM_FENCE); - offset >>= 1; - int ai = offset * lid_prim - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sum[lid >> 7][ai] = lm_sum[lid >> 7][bi] - lm_sum[lid >> 7][ai]; - } - } - barrier(CLK_LOCAL_MEM_FENCE); - if (lid > 0 && (i+lid) <= rows) - { - int loc_s0 = mad24(gid, dst_step, i + lid - 1), loc_s1 = loc_s0 + dst_step; - lm_sum[0][bf_loc] += sum_t[0]; - lm_sum[1][bf_loc] += sum_t[1]; - sum_p = (__local sumT*)(&(lm_sum[0][bf_loc])); - for (int k = 0; k < 4; k++) - { - if (gid * 4 + k >= cols) - break; - sum[loc_s0 + k * dst_step / 4] = sum_p[k]; - } - sum_p = (__local sumT*)(&(lm_sum[1][bf_loc])); - for (int k = 0; k < 4; k++) - { - if (gid * 4 + k + 4 >= cols) - break; - sum[loc_s1 + k * dst_step / 4] = sum_p[k]; - } + //int buf_index = buf_offset + buf_step * LOCAL_SUM_COLS * gid + sizeof(sumT) * y + sizeof(sumT) * lid; + int buf_index = mad24(buf_step, LOCAL_SUM_SIZE * gid, mad24((int)sizeof(sumT), y + lid, buf_offset)); +#ifdef SUM_SQUARE + int buf_sq_index = mad24(buf_sq_step, LOCAL_SUM_SIZE * gid, mad24((int)sizeof(sumSQT), y + lid, buf_sq_offset)); +#endif + + lsum_index = LOCAL_SUM_STRIDE * lid; + #pragma unroll + for (int yin = 0; yin < LOCAL_SUM_SIZE; yin++, lsum_index ++) + { + __global sumT *buf = (__global sumT *)(buf_ptr + buf_index); + buf[0] = lm_sum[lsum_index]; + buf_index += buf_step; +#ifdef SUM_SQUARE + __global sumSQT *bufsq = (__global sumSQT *)(buf_sq_ptr + buf_sq_index); + bufsq[0] = lm_sum_sq[lsum_index]; + buf_sq_index += buf_sq_step; +#endif } barrier(CLK_LOCAL_MEM_FENCE); } } - -kernel void integral_sum_rows(__global const uchar *srcsum_ptr, __global uchar *sum_ptr, - int rows, int cols, int src_step, int sum_step, int sum_offset) +kernel void integral_sum_rows(__global const uchar *buf_ptr, int buf_step, int buf_offset, +#ifdef SUM_SQUARE + __global uchar *buf_sq_ptr, int buf_sq_step, int buf_sq_offset, +#endif + __global uchar *dst_ptr, int dst_step, int dst_offset, int rows, int cols +#ifdef SUM_SQUARE + ,__global uchar *dst_sq_ptr, int dst_sq_step, int dst_sq_offset +#endif + ) { - __global const vecSumT *srcsum = (__global const vecSumT *)srcsum_ptr; - __global sumT *sum = (__global sumT *)sum_ptr; + __local sumT lm_sum[LOCAL_SUM_STRIDE * LOCAL_SUM_SIZE]; +#ifdef SUM_SQUARE + __local sumSQT lm_sum_sq[LOCAL_SUM_STRIDE * LOCAL_SUM_SIZE]; +#endif int lid = get_local_id(0); int gid = get_group_id(0); - vecSumT src_t[2], sum_t[2]; - __local vecSumT lm_sum[2][LSIZE + LOG_LSIZE]; - __local sumT *sum_p; - src_step = src_step >> 4; - int lid_prim = ((lid & 127) << 1) + 1; - for (int i = 0; i < rows; i += LSIZE_1) + + int gs = get_global_size(0); + + int x = get_global_id(0); + + __global sumT *dst = (__global sumT *)(dst_ptr + dst_offset); + for (int xin = x; xin < cols; xin += gs) { - if (i + lid < rows) - { - int sum_idx = mad24(lid + i, src_step, gid * 2); - src_t[0] = srcsum[sum_idx]; - src_t[1] = srcsum[sum_idx + 1]; - } - else - { - src_t[0] = 0; - src_t[1] = 0; - } - if (i == 0) - { - sum_t[0] = 0; - sum_t[1] = 0; - } - else - { - sum_t[0] = lm_sum[0][LSIZE_2 + LOG_LSIZE]; - sum_t[1] = lm_sum[1][LSIZE_2 + LOG_LSIZE]; - } - barrier(CLK_LOCAL_MEM_FENCE); + dst[xin] = 0; + } + dst_offset += dst_step; - int bf_loc = lid + GET_CONFLICT_OFFSET(lid); + if (x < rows - 1) + { + dst = (__global sumT *)(dst_ptr + mad24(x, dst_step, dst_offset)); + dst[0] = 0; + } - lm_sum[0][bf_loc] = src_t[0]; - lm_sum[1][bf_loc] = src_t[1]; + int buf_index = mad24((int)sizeof(sumT), x, buf_offset); + sumT accum = 0; - int offset = 1; - for (int d = LSIZE >> 1 ; d > 0; d>>=1) - { - barrier(CLK_LOCAL_MEM_FENCE); - int ai = offset * lid_prim - 1, bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); +#ifdef SUM_SQUARE + __global sumSQT *dst_sq = (__global sumT *)(dst_sq_ptr + dst_sq_offset); + for (int xin = x; xin < cols; xin += gs) + { + dst_sq[xin] = 0; + } + dst_sq_offset += dst_sq_step; - if((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - } - offset <<= 1; - } - barrier(CLK_LOCAL_MEM_FENCE); - if (lid < 2) - { - lm_sum[lid][LSIZE_2 + LOG_LSIZE] = 0; - } - for (int d = 1; d < LSIZE; d <<= 1) - { - barrier(CLK_LOCAL_MEM_FENCE); - offset >>= 1; - int ai = offset * lid_prim - 1,bi = ai + offset; - ai += GET_CONFLICT_OFFSET(ai); - bi += GET_CONFLICT_OFFSET(bi); + dst_sq = (__global sumSQT *)(dst_sq_ptr + mad24(x, dst_sq_step, dst_sq_offset)); + dst_sq[0] = 0; - if ((lid & 127) < d) - { - lm_sum[lid >> 7][bi] += lm_sum[lid >> 7][ai]; - lm_sum[lid >> 7][ai] = lm_sum[lid >> 7][bi] - lm_sum[lid >> 7][ai]; - } + int buf_sq_index = mad24((int)sizeof(sumSQT), x, buf_sq_offset); + sumSQT accum_sq = 0; +#endif + + for (int y = 1; y < cols; y += LOCAL_SUM_SIZE) + { + int lsum_index = lid; + #pragma unroll + for (int yin = 0; yin < LOCAL_SUM_SIZE; yin++, lsum_index += LOCAL_SUM_STRIDE) + { + __global const sumT *buf = (__global const sumT *)(buf_ptr + buf_index); + accum += buf[0]; + lm_sum[lsum_index] = accum; + buf_index += buf_step; +#ifdef SUM_SQUARE + __global const sumSQT *buf_sq = (__global const sumSQT *)(buf_sq_ptr + buf_sq_index); + accum_sq += buf_sq[0]; + lm_sum_sq[lsum_index] = accum_sq; + buf_sq_index += buf_sq_step; +#endif } barrier(CLK_LOCAL_MEM_FENCE); - if (gid == 0 && (i + lid) <= rows) - { - sum[sum_offset + i + lid] = 0; - } - if (i + lid == 0) - { - int loc0 = gid * 2 * sum_step; - for(int k = 1; k <= 8; k++) - { - if (gid * 8 + k > cols) - break; - sum[sum_offset + loc0 + k * sum_step / 4] = 0; - } - } - if (lid > 0 && (i+lid) <= rows) + if (y + lid < cols) { - int loc_s0 = sum_offset + gid * 2 * sum_step + sum_step / 4 + i + lid, loc_s1 = loc_s0 + sum_step ; - lm_sum[0][bf_loc] += sum_t[0]; - lm_sum[1][bf_loc] += sum_t[1]; - sum_p = (__local sumT*)(&(lm_sum[0][bf_loc])); - for(int k = 0; k < 4; k++) - { - if (gid * 8 + k >= cols) - break; - sum[loc_s0 + k * sum_step / 4] = sum_p[k]; - } - sum_p = (__local sumT*)(&(lm_sum[1][bf_loc])); - for(int k = 0; k < 4; k++) + //int dst_index = dst_offset + dst_step * LOCAL_SUM_COLS * gid + sizeof(sumT) * y + sizeof(sumT) * lid; + int dst_index = mad24(dst_step, LOCAL_SUM_SIZE * gid, mad24((int)sizeof(sumT), y + lid, dst_offset)); +#ifdef SUM_SQUARE + int dst_sq_index = mad24(dst_sq_step, LOCAL_SUM_SIZE * gid, mad24((int)sizeof(sumSQT), y + lid, dst_sq_offset)); +#endif + lsum_index = LOCAL_SUM_STRIDE * lid; + int yin_max = min(rows - 1 - LOCAL_SUM_SIZE * gid, LOCAL_SUM_SIZE); + #pragma unroll + for (int yin = 0; yin < yin_max; yin++, lsum_index++) { - if (gid * 8 + 4 + k >= cols) - break; - sum[loc_s1 + k * sum_step / 4] = sum_p[k]; + dst = (__global sumT *)(dst_ptr + dst_index); + dst[0] = lm_sum[lsum_index]; + dst_index += dst_step; +#ifdef SUM_SQUARE + dst_sq = (__global sumSQT *)(dst_sq_ptr + dst_sq_index); + dst_sq[0] = lm_sum_sq[lsum_index]; + dst_sq_index += dst_sq_step; +#endif } } barrier(CLK_LOCAL_MEM_FENCE); diff --git a/modules/imgproc/src/opencl/morph.cl b/modules/imgproc/src/opencl/morph.cl index a7611c50f9..f78af89c9c 100644 --- a/modules/imgproc/src/opencl/morph.cl +++ b/modules/imgproc/src/opencl/morph.cl @@ -43,6 +43,8 @@ #endif #endif +#define noconvert + #if cn != 3 #define loadpix(addr) *(__global const T *)(addr) #define storepix(val, addr) *(__global T *)(addr) = val @@ -54,59 +56,75 @@ #endif #ifdef DEPTH_0 -#ifdef ERODE -#define VAL 255 -#endif -#ifdef DILATE -#define VAL 0 -#endif +#define MIN_VAL 0 +#define MAX_VAL UCHAR_MAX +#elif defined DEPTH_1 +#define MIN_VAL SCHAR_MIN +#define MAX_VAL SCHAR_MAX +#elif defined DEPTH_2 +#define MIN_VAL 0 +#define MAX_VAL USHRT_MAX +#elif defined DEPTH_3 +#define MIN_VAL SHRT_MIN +#define MAX_VAL SHRT_MAX +#elif defined DEPTH_4 +#define MIN_VAL INT_MIN +#define MAX_VAL INT_MAX #elif defined DEPTH_5 -#ifdef ERODE -#define VAL FLT_MAX -#endif -#ifdef DILATE -#define VAL -FLT_MAX -#endif +#define MIN_VAL (-FLT_MAX) +#define MAX_VAL FLT_MAX #elif defined DEPTH_6 -#ifdef ERODE -#define VAL DBL_MAX -#endif -#ifdef DILATE -#define VAL -DBL_MAX +#define MIN_VAL (-DBL_MAX) +#define MAX_VAL DBL_MAX #endif + +#ifdef OP_ERODE +#define VAL MAX_VAL +#elif defined OP_DILATE +#define VAL MIN_VAL +#else +#error "Unknown operation" #endif -#ifdef ERODE -#if defined(INTEL_DEVICE) && (DEPTH_0) +#ifdef OP_ERODE +#if defined INTEL_DEVICE && defined DEPTH_0 // workaround for bug in Intel HD graphics drivers (10.18.10.3496 or older) #define __CAT(x, y) x##y #define CAT(x, y) __CAT(x, y) #define WA_CONVERT_1 CAT(convert_uint, cn) #define WA_CONVERT_2 CAT(convert_, T) #define convert_uint1 convert_uint -#define MORPH_OP(A,B) WA_CONVERT_2(min(WA_CONVERT_1(A),WA_CONVERT_1(B))) +#define MORPH_OP(A, B) WA_CONVERT_2(min(WA_CONVERT_1(A), WA_CONVERT_1(B))) #else -#define MORPH_OP(A,B) min((A),(B)) +#define MORPH_OP(A, B) min((A), (B)) #endif #endif -#ifdef DILATE -#define MORPH_OP(A,B) max((A),(B)) +#ifdef OP_DILATE +#define MORPH_OP(A, B) max((A), (B)) #endif +#define PROCESS(y, x) \ + res = MORPH_OP(res, LDS_DAT[mad24(l_y + y, width, l_x + x)]); + // BORDER_CONSTANT: iiiiii|abcdefgh|iiiiiii #define ELEM(i, l_edge, r_edge, elem1, elem2) (i) < (l_edge) | (i) >= (r_edge) ? (elem1) : (elem2) +#if defined OP_GRADIENT || defined OP_TOPHAT || defined OP_BLACKHAT +#define EXTRA_PARAMS , __global const uchar * matptr, int mat_step, int mat_offset +#else +#define EXTRA_PARAMS +#endif + __kernel void morph(__global const uchar * srcptr, int src_step, int src_offset, __global uchar * dstptr, int dst_step, int dst_offset, int src_offset_x, int src_offset_y, int cols, int rows, - __constant uchar * mat_kernel, int src_whole_cols, int src_whole_rows) + int src_whole_cols, int src_whole_rows EXTRA_PARAMS) { int gidx = get_global_id(0), gidy = get_global_id(1); int l_x = get_local_id(0), l_y = get_local_id(1); int x = get_group_id(0) * LSIZE0, y = get_group_id(1) * LSIZE1; int start_x = x + src_offset_x - RADIUSX; - int end_x = x + src_offset_x + LSIZE0 + RADIUSX; - int width = end_x - (x + src_offset_x - RADIUSX) + 1; + int width = mad24(RADIUSX, 2, LSIZE0 + 1); int start_y = y + src_offset_y - RADIUSY; int point1 = mad24(l_y, LSIZE0, l_x); int point2 = point1 + LSIZE0 * LSIZE1; @@ -117,7 +135,7 @@ __kernel void morph(__global const uchar * srcptr, int src_step, int src_offset, int start_addr = mad24(cur_y, src_step, cur_x * TSIZE); int start_addr2 = mad24(cur_y2, src_step, cur_x2 * TSIZE); - __local T LDS_DAT[2*LSIZE1*LSIZE0]; + __local T LDS_DAT[2 * LSIZE1 * LSIZE0]; // read pixels from src int end_addr = mad24(src_whole_rows - 1, src_step, src_whole_cols * TSIZE); @@ -128,8 +146,8 @@ __kernel void morph(__global const uchar * srcptr, int src_step, int src_offset, T temp1 = loadpix(srcptr + start_addr2); // judge if read out of boundary - temp0 = ELEM(cur_x, 0, src_whole_cols, (T)(VAL),temp0); - temp0 = ELEM(cur_y, 0, src_whole_rows, (T)(VAL),temp0); + temp0 = ELEM(cur_x, 0, src_whole_cols, (T)(VAL), temp0); + temp0 = ELEM(cur_y, 0, src_whole_rows, (T)(VAL), temp0); temp1 = ELEM(cur_x2, 0, src_whole_cols, (T)(VAL), temp1); temp1 = ELEM(cur_y2, 0, src_whole_rows, (T)(VAL), temp1); @@ -138,24 +156,26 @@ __kernel void morph(__global const uchar * srcptr, int src_step, int src_offset, LDS_DAT[point2] = temp1; barrier(CLK_LOCAL_MEM_FENCE); - T res = (T)(VAL); - for (int i = 0, sizey = 2 * RADIUSY + 1; i < sizey; i++) - for (int j = 0, sizex = 2 * RADIUSX + 1; j < sizex; j++) - { - res = -#ifndef RECTKERNEL - mat_kernel[i*(2*RADIUSX+1)+j] ? -#endif - MORPH_OP(res, LDS_DAT[mad24(l_y + i, width, l_x + j)]) -#ifndef RECTKERNEL - : res -#endif - ; - } - if (gidx < cols && gidy < rows) { + T res = (T)(VAL); + PROCESS_ELEMS; + int dst_index = mad24(gidy, dst_step, mad24(gidx, TSIZE, dst_offset)); + +#if defined OP_GRADIENT || defined OP_TOPHAT || defined OP_BLACKHAT + int mat_index = mad24(gidy, mat_step, mad24(gidx, TSIZE, mat_offset)); + T value = loadpix(matptr + mat_index); + +#ifdef OP_GRADIENT + storepix(convertToT(convertToWT(res) - convertToWT(value)), dstptr + dst_index); +#elif defined OP_TOPHAT + storepix(convertToT(convertToWT(value) - convertToWT(res)), dstptr + dst_index); +#elif defined OP_BLACKHAT + storepix(convertToT(convertToWT(res) - convertToWT(value)), dstptr + dst_index); +#endif +#else // erode or dilate storepix(res, dstptr + dst_index); +#endif } } diff --git a/modules/imgproc/src/opencl/pyr_down.cl b/modules/imgproc/src/opencl/pyr_down.cl index b8b06b712b..2358775e7a 100644 --- a/modules/imgproc/src/opencl/pyr_down.cl +++ b/modules/imgproc/src/opencl/pyr_down.cl @@ -79,12 +79,22 @@ #define SRC(_x,_y) convertToFT(loadpix(srcData + mad24(_y, src_step, PIXSIZE * _x))) +#if kercn == 4 +#define SRC4(_x,_y) convert_float4(vload4(0, srcData + mad24(_y, src_step, PIXSIZE * _x))) +#endif + +#ifdef INTEL_DEVICE +#define MAD(x,y,z) fma((x),(y),(z)) +#else +#define MAD(x,y,z) mad((x),(y),(z)) +#endif + #define noconvert __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, int src_rows, int src_cols, __global uchar * dst, int dst_step, int dst_offset, int dst_rows, int dst_cols) { - const int x = get_global_id(0); + const int x = get_global_id(0)*kercn; const int y = get_group_id(1); __local FT smem[LOCAL_SIZE + 4]; @@ -97,98 +107,190 @@ __kernel void pyrDown(__global const uchar * src, int src_step, int src_offset, FT co3 = 0.0625f; const int src_y = 2*y; + int col; - if (src_y >= 2 && src_y < src_rows - 2 && x >= 2 && x < src_cols - 2) + if (src_y >= 2 && src_y < src_rows - 2) { - sum = co3 * SRC(x, src_y - 2); - sum = sum + co2 * SRC(x, src_y - 1); - sum = sum + co1 * SRC(x, src_y ); - sum = sum + co2 * SRC(x, src_y + 1); - sum = sum + co3 * SRC(x, src_y + 2); +#if kercn == 1 + col = EXTRAPOLATE(x, src_cols); + + sum = co3* SRC(col, src_y - 2); + sum = MAD(co2, SRC(col, src_y - 1), sum); + sum = MAD(co1, SRC(col, src_y ), sum); + sum = MAD(co2, SRC(col, src_y + 1), sum); + sum = MAD(co3, SRC(col, src_y + 2), sum); smem[2 + get_local_id(0)] = sum; +#else + if (x < src_cols-4) + { + float4 sum4; + sum4 = co3* SRC4(x, src_y - 2); + sum4 = MAD(co2, SRC4(x, src_y - 1), sum4); + sum4 = MAD(co1, SRC4(x, src_y ), sum4); + sum4 = MAD(co2, SRC4(x, src_y + 1), sum4); + sum4 = MAD(co3, SRC4(x, src_y + 2), sum4); + vstore4(sum4, get_local_id(0), (__local float*) &smem[2]); + } + else + { + for (int i=0; i<4; i++) + { + col = EXTRAPOLATE(x+i, src_cols); + sum = co3* SRC(col, src_y - 2); + sum = MAD(co2, SRC(col, src_y - 1), sum); + sum = MAD(co1, SRC(col, src_y ), sum); + sum = MAD(co2, SRC(col, src_y + 1), sum); + sum = MAD(co3, SRC(col, src_y + 2), sum); + + smem[2 + 4*get_local_id(0)+i] = sum; + } + } +#endif if (get_local_id(0) < 2) { - const int left_x = x - 2; + col = EXTRAPOLATE((int)(get_group_id(0)*LOCAL_SIZE + get_local_id(0) - 2), src_cols); - sum = co3 * SRC(left_x, src_y - 2); - sum = sum + co2 * SRC(left_x, src_y - 1); - sum = sum + co1 * SRC(left_x, src_y ); - sum = sum + co2 * SRC(left_x, src_y + 1); - sum = sum + co3 * SRC(left_x, src_y + 2); + sum = co3* SRC(col, src_y - 2); + sum = MAD(co2, SRC(col, src_y - 1), sum); + sum = MAD(co1, SRC(col, src_y ), sum); + sum = MAD(co2, SRC(col, src_y + 1), sum); + sum = MAD(co3, SRC(col, src_y + 2), sum); smem[get_local_id(0)] = sum; } - if (get_local_id(0) > LOCAL_SIZE - 3) + if (get_local_id(0) > 1 && get_local_id(0) < 4) { - const int right_x = x + 2; + col = EXTRAPOLATE((int)((get_group_id(0)+1)*LOCAL_SIZE + get_local_id(0) - 2), src_cols); - sum = co3 * SRC(right_x, src_y - 2); - sum = sum + co2 * SRC(right_x, src_y - 1); - sum = sum + co1 * SRC(right_x, src_y ); - sum = sum + co2 * SRC(right_x, src_y + 1); - sum = sum + co3 * SRC(right_x, src_y + 2); + sum = co3* SRC(col, src_y - 2); + sum = MAD(co2, SRC(col, src_y - 1), sum); + sum = MAD(co1, SRC(col, src_y ), sum); + sum = MAD(co2, SRC(col, src_y + 1), sum); + sum = MAD(co3, SRC(col, src_y + 2), sum); - smem[4 + get_local_id(0)] = sum; + smem[LOCAL_SIZE + get_local_id(0)] = sum; } } - else + else // need extrapolate y { - int col = EXTRAPOLATE(x, src_cols); +#if kercn == 1 + col = EXTRAPOLATE(x, src_cols); - sum = co3 * SRC(col, EXTRAPOLATE(src_y - 2, src_rows)); - sum = sum + co2 * SRC(col, EXTRAPOLATE(src_y - 1, src_rows)); - sum = sum + co1 * SRC(col, EXTRAPOLATE(src_y , src_rows)); - sum = sum + co2 * SRC(col, EXTRAPOLATE(src_y + 1, src_rows)); - sum = sum + co3 * SRC(col, EXTRAPOLATE(src_y + 2, src_rows)); + sum = co3* SRC(col, EXTRAPOLATE(src_y - 2, src_rows)); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y - 1, src_rows)), sum); + sum = MAD(co1, SRC(col, EXTRAPOLATE(src_y , src_rows)), sum); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y + 1, src_rows)), sum); + sum = MAD(co3, SRC(col, EXTRAPOLATE(src_y + 2, src_rows)), sum); smem[2 + get_local_id(0)] = sum; +#else + if (x < src_cols-4) + { + float4 sum4; + sum4 = co3* SRC4(x, EXTRAPOLATE(src_y - 2, src_rows)); + sum4 = MAD(co2, SRC4(x, EXTRAPOLATE(src_y - 1, src_rows)), sum4); + sum4 = MAD(co1, SRC4(x, EXTRAPOLATE(src_y , src_rows)), sum4); + sum4 = MAD(co2, SRC4(x, EXTRAPOLATE(src_y + 1, src_rows)), sum4); + sum4 = MAD(co3, SRC4(x, EXTRAPOLATE(src_y + 2, src_rows)), sum4); + vstore4(sum4, get_local_id(0), (__local float*) &smem[2]); + } + else + { + for (int i=0; i<4; i++) + { + col = EXTRAPOLATE(x+i, src_cols); + sum = co3* SRC(col, EXTRAPOLATE(src_y - 2, src_rows)); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y - 1, src_rows)), sum); + sum = MAD(co1, SRC(col, EXTRAPOLATE(src_y , src_rows)), sum); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y + 1, src_rows)), sum); + sum = MAD(co3, SRC(col, EXTRAPOLATE(src_y + 2, src_rows)), sum); + + smem[2 + 4*get_local_id(0)+i] = sum; + } + } +#endif if (get_local_id(0) < 2) { - col = EXTRAPOLATE(x - 2, src_cols); + col = EXTRAPOLATE((int)(get_group_id(0)*LOCAL_SIZE + get_local_id(0) - 2), src_cols); - sum = co3 * SRC(col, EXTRAPOLATE(src_y - 2, src_rows)); - sum = sum + co2 * SRC(col, EXTRAPOLATE(src_y - 1, src_rows)); - sum = sum + co1 * SRC(col, EXTRAPOLATE(src_y , src_rows)); - sum = sum + co2 * SRC(col, EXTRAPOLATE(src_y + 1, src_rows)); - sum = sum + co3 * SRC(col, EXTRAPOLATE(src_y + 2, src_rows)); + sum = co3* SRC(col, EXTRAPOLATE(src_y - 2, src_rows)); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y - 1, src_rows)), sum); + sum = MAD(co1, SRC(col, EXTRAPOLATE(src_y , src_rows)), sum); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y + 1, src_rows)), sum); + sum = MAD(co3, SRC(col, EXTRAPOLATE(src_y + 2, src_rows)), sum); smem[get_local_id(0)] = sum; } - if (get_local_id(0) > LOCAL_SIZE - 3) + if (get_local_id(0) > 1 && get_local_id(0) < 4) { - col = EXTRAPOLATE(x + 2, src_cols); + col = EXTRAPOLATE((int)((get_group_id(0)+1)*LOCAL_SIZE + get_local_id(0) - 2), src_cols); - sum = co3 * SRC(col, EXTRAPOLATE(src_y - 2, src_rows)); - sum = sum + co2 * SRC(col, EXTRAPOLATE(src_y - 1, src_rows)); - sum = sum + co1 * SRC(col, EXTRAPOLATE(src_y , src_rows)); - sum = sum + co2 * SRC(col, EXTRAPOLATE(src_y + 1, src_rows)); - sum = sum + co3 * SRC(col, EXTRAPOLATE(src_y + 2, src_rows)); + sum = co3* SRC(col, EXTRAPOLATE(src_y - 2, src_rows)); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y - 1, src_rows)), sum); + sum = MAD(co1, SRC(col, EXTRAPOLATE(src_y , src_rows)), sum); + sum = MAD(co2, SRC(col, EXTRAPOLATE(src_y + 1, src_rows)), sum); + sum = MAD(co3, SRC(col, EXTRAPOLATE(src_y + 2, src_rows)), sum); - smem[4 + get_local_id(0)] = sum; + smem[LOCAL_SIZE + get_local_id(0)] = sum; } } barrier(CLK_LOCAL_MEM_FENCE); +#if kercn == 1 if (get_local_id(0) < LOCAL_SIZE / 2) { const int tid2 = get_local_id(0) * 2; - sum = co3 * smem[2 + tid2 - 2]; - sum = sum + co2 * smem[2 + tid2 - 1]; - sum = sum + co1 * smem[2 + tid2 ]; - sum = sum + co2 * smem[2 + tid2 + 1]; - sum = sum + co3 * smem[2 + tid2 + 2]; + sum = 0.f; +#if cn == 1 +#if fdepth <= 5 + sum = sum + dot(vload4(0, (__local float*) (&smem)+tid2), (float4)(co3, co2, co1, co2)); +#else + sum = sum + dot(vload4(0, (__local double*) (&smem)+tid2), (double4)(co3, co2, co1, co2)); +#endif +#else + sum = MAD(co3, smem[2 + tid2 - 2], sum); + sum = MAD(co2, smem[2 + tid2 - 1], sum); + sum = MAD(co1, smem[2 + tid2 ], sum); + sum = MAD(co2, smem[2 + tid2 + 1], sum); +#endif + sum = MAD(co3, smem[2 + tid2 + 2], sum); const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; if (dst_x < dst_cols) storepix(convertToT(sum), dstData + y * dst_step + dst_x * PIXSIZE); } +#else + int tid4 = get_local_id(0) * 4; + + sum = co3* smem[2 + tid4 + 2]; + sum = MAD(co3, smem[2 + tid4 - 2], sum); + sum = MAD(co2, smem[2 + tid4 - 1], sum); + sum = MAD(co1, smem[2 + tid4 ], sum); + sum = MAD(co2, smem[2 + tid4 + 1], sum); + + int dst_x = (get_group_id(0) * LOCAL_SIZE + tid4) / 2; + + if (dst_x < dst_cols) + storepix(convertToT(sum), dstData + mad24(y, dst_step, dst_x * PIXSIZE)); + tid4 += 2; + dst_x += 1; + + sum = co3* smem[2 + tid4 + 2]; + sum = MAD(co3, smem[2 + tid4 - 2], sum); + sum = MAD(co2, smem[2 + tid4 - 1], sum); + sum = MAD(co1, smem[2 + tid4 ], sum); + sum = MAD(co2, smem[2 + tid4 + 1], sum); + + if (dst_x < dst_cols) + storepix(convertToT(sum), dstData + mad24(y, dst_step, dst_x * PIXSIZE)); +#endif } diff --git a/modules/imgproc/src/opencl/remap.cl b/modules/imgproc/src/opencl/remap.cl index bd043c5e4b..4e45b40bd3 100644 --- a/modules/imgproc/src/opencl/remap.cl +++ b/modules/imgproc/src/opencl/remap.cl @@ -147,37 +147,43 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src ST nVal) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * rowsPerWI; - T scalar = convertScalar(nVal); - - if (x < dst_cols && y < dst_rows) + if (x < dst_cols) { - int map1_index = mad24(y, map1_step, x * (int)sizeof(float) + map1_offset); - int map2_index = mad24(y, map2_step, x * (int)sizeof(float) + map2_offset); - int dst_index = mad24(y, dst_step, x * TSIZE + dst_offset); - - __global const float * map1 = (__global const float *)(map1ptr + map1_index); - __global const float * map2 = (__global const float *)(map2ptr + map2_index); - __global T * dst = (__global T *)(dstptr + dst_index); - - int gx = convert_int_sat_rte(map1[0]); - int gy = convert_int_sat_rte(map2[0]); - - if (NEED_EXTRAPOLATION(gx, gy)) - { + T scalar = convertScalar(nVal); + + int map1_index = mad24(y, map1_step, mad24(x, (int)sizeof(float), map1_offset)); + int map2_index = mad24(y, map2_step, mad24(x, (int)sizeof(float), map2_offset)); + int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); + + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, + map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) + if (y < dst_rows) + { + __global const float * map1 = (__global const float *)(map1ptr + map1_index); + __global const float * map2 = (__global const float *)(map2ptr + map2_index); + __global T * dst = (__global T *)(dstptr + dst_index); + + int gx = convert_int_sat_rte(map1[0]); + int gy = convert_int_sat_rte(map2[0]); + + if (NEED_EXTRAPOLATION(gx, gy)) + { #ifndef BORDER_CONSTANT - int2 gxy = (int2)(gx, gy); + int2 gxy = (int2)(gx, gy); #endif - T v; - EXTRAPOLATE(gxy, v) - storepix(v, dst); - } - else - { - int src_index = mad24(gy, src_step, gx * TSIZE + src_offset); - storepix(loadpix((__global const T*)(srcptr + src_index)), dst); - } + T v; + EXTRAPOLATE(gxy, v) + storepix(v, dst); + } + else + { + int src_index = mad24(gy, src_step, mad24(gx, TSIZE, src_offset)); + storepix(loadpix((__global const T*)(srcptr + src_index)), dst); + } + } } } @@ -187,31 +193,36 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o ST nVal) { int x = get_global_id(0); - int y = get_global_id(1); - - T scalar = convertScalar(nVal); + int y = get_global_id(1) * rowsPerWI; - if (x < dst_cols && y < dst_rows) + if (x < dst_cols) { - int dst_index = mad24(y, dst_step, x * TSIZE + dst_offset); - int map_index = mad24(y, map_step, x * (int)sizeof(float2) + map_offset); - - __global const float2 * map = (__global const float2 *)(mapptr + map_index); - __global T * dst = (__global T *)(dstptr + dst_index); - - int2 gxy = convert_int2_sat_rte(map[0]); - int gx = gxy.x, gy = gxy.y; - - if (NEED_EXTRAPOLATION(gx, gy)) - { - T v; - EXTRAPOLATE(gxy, v) - storepix(v, dst); - } - else - { - int src_index = mad24(gy, src_step, gx * TSIZE + src_offset); - storepix(loadpix((__global const T *)(srcptr + src_index)), dst); + T scalar = convertScalar(nVal); + int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); + int map_index = mad24(y, map_step, mad24(x, (int)sizeof(float2), map_offset)); + + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, + map_index += map_step, dst_index += dst_step) + if (y < dst_rows) + { + __global const float2 * map = (__global const float2 *)(mapptr + map_index); + __global T * dst = (__global T *)(dstptr + dst_index); + + int2 gxy = convert_int2_sat_rte(map[0]); + int gx = gxy.x, gy = gxy.y; + + if (NEED_EXTRAPOLATION(gx, gy)) + { + T v; + EXTRAPOLATE(gxy, v) + storepix(v, dst); + } + else + { + int src_index = mad24(gy, src_step, mad24(gx, TSIZE, src_offset)); + storepix(loadpix((__global const T *)(srcptr + src_index)), dst); + } } } } @@ -222,32 +233,37 @@ __kernel void remap_16SC2(__global const uchar * srcptr, int src_step, int src_o ST nVal) { int x = get_global_id(0); - int y = get_global_id(1); - - T scalar = convertScalar(nVal); + int y = get_global_id(1) * rowsPerWI; - if (x < dst_cols && y < dst_rows) + if (x < dst_cols) { - int dst_index = mad24(y, dst_step, x * TSIZE + dst_offset); - int map_index = mad24(y, map_step, x * (int)sizeof(short2) + map_offset); - - __global const short2 * map = (__global const short2 *)(mapptr + map_index); - __global T * dst = (__global T *)(dstptr + dst_index); - - int2 gxy = convert_int2(map[0]); - int gx = gxy.x, gy = gxy.y; - - if (NEED_EXTRAPOLATION(gx, gy)) - { - T v; - EXTRAPOLATE(gxy, v) - storepix(v, dst); - } - else - { - int src_index = mad24(gy, src_step, gx * TSIZE + src_offset); - storepix(loadpix((__global const T *)(srcptr + src_index)), dst); - } + T scalar = convertScalar(nVal); + int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); + int map_index = mad24(y, map_step, mad24(x, (int)sizeof(short2), map_offset)); + + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, + map_index += map_step, dst_index += dst_step) + if (y < dst_rows) + { + __global const short2 * map = (__global const short2 *)(mapptr + map_index); + __global T * dst = (__global T *)(dstptr + dst_index); + + int2 gxy = convert_int2(map[0]); + int gx = gxy.x, gy = gxy.y; + + if (NEED_EXTRAPOLATION(gx, gy)) + { + T v; + EXTRAPOLATE(gxy, v) + storepix(v, dst); + } + else + { + int src_index = mad24(gy, src_step, mad24(gx, TSIZE, src_offset)); + storepix(loadpix((__global const T *)(srcptr + src_index)), dst); + } + } } } @@ -258,41 +274,54 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int ST nVal) { int x = get_global_id(0); - int y = get_global_id(1); - - T scalar = convertScalar(nVal); + int y = get_global_id(1) * rowsPerWI; - if (x < dst_cols && y < dst_rows) + if (x < dst_cols) { - int dst_index = mad24(y, dst_step, x * TSIZE + dst_offset); - int map1_index = mad24(y, map1_step, x * (int)sizeof(short2) + map1_offset); - int map2_index = mad24(y, map2_step, x * (int)sizeof(ushort) + map2_offset); - - __global const short2 * map1 = (__global const short2 *)(map1ptr + map1_index); - __global const ushort * map2 = (__global const ushort *)(map2ptr + map2_index); - __global T * dst = (__global T *)(dstptr + dst_index); - - int map2Value = convert_int(map2[0]) & (INTER_TAB_SIZE2 - 1); - int dx = (map2Value & (INTER_TAB_SIZE - 1)) < (INTER_TAB_SIZE >> 1) ? 1 : 0; - int dy = (map2Value >> INTER_BITS) < (INTER_TAB_SIZE >> 1) ? 1 : 0; - int2 gxy = convert_int2(map1[0]) + (int2)(dx, dy); - int gx = gxy.x, gy = gxy.y; - - if (NEED_EXTRAPOLATION(gx, gy)) - { - T v; - EXTRAPOLATE(gxy, v) - storepix(v, dst); - } - else - { - int src_index = mad24(gy, src_step, gx * TSIZE + src_offset); - storepix(loadpix((__global const T *)(srcptr + src_index)), dst); - } + T scalar = convertScalar(nVal); + int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); + int map1_index = mad24(y, map1_step, mad24(x, (int)sizeof(short2), map1_offset)); + int map2_index = mad24(y, map2_step, mad24(x, (int)sizeof(ushort), map2_offset)); + + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, + map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) + if (y < dst_rows) + { + __global const short2 * map1 = (__global const short2 *)(map1ptr + map1_index); + __global const ushort * map2 = (__global const ushort *)(map2ptr + map2_index); + __global T * dst = (__global T *)(dstptr + dst_index); + + int map2Value = convert_int(map2[0]) & (INTER_TAB_SIZE2 - 1); + int dx = (map2Value & (INTER_TAB_SIZE - 1)) < (INTER_TAB_SIZE >> 1) ? 1 : 0; + int dy = (map2Value >> INTER_BITS) < (INTER_TAB_SIZE >> 1) ? 1 : 0; + int2 gxy = convert_int2(map1[0]) + (int2)(dx, dy); + int gx = gxy.x, gy = gxy.y; + + if (NEED_EXTRAPOLATION(gx, gy)) + { + T v; + EXTRAPOLATE(gxy, v) + storepix(v, dst); + } + else + { + int src_index = mad24(gy, src_step, mad24(gx, TSIZE, src_offset)); + storepix(loadpix((__global const T *)(srcptr + src_index)), dst); + } + } } } -#elif INTER_LINEAR +#elif defined INTER_LINEAR + +__constant float coeffs[64] = +{ 1.000000f, 0.000000f, 0.968750f, 0.031250f, 0.937500f, 0.062500f, 0.906250f, 0.093750f, 0.875000f, 0.125000f, 0.843750f, 0.156250f, + 0.812500f, 0.187500f, 0.781250f, 0.218750f, 0.750000f, 0.250000f, 0.718750f, 0.281250f, 0.687500f, 0.312500f, 0.656250f, 0.343750f, + 0.625000f, 0.375000f, 0.593750f, 0.406250f, 0.562500f, 0.437500f, 0.531250f, 0.468750f, 0.500000f, 0.500000f, 0.468750f, 0.531250f, + 0.437500f, 0.562500f, 0.406250f, 0.593750f, 0.375000f, 0.625000f, 0.343750f, 0.656250f, 0.312500f, 0.687500f, 0.281250f, 0.718750f, + 0.250000f, 0.750000f, 0.218750f, 0.781250f, 0.187500f, 0.812500f, 0.156250f, 0.843750f, 0.125000f, 0.875000f, 0.093750f, 0.906250f, + 0.062500f, 0.937500f, 0.031250f, 0.968750f }; __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int src_offset, int src_rows, int src_cols, __global uchar * dstptr, int dst_step, int dst_offset, int dst_rows, int dst_cols, @@ -301,54 +330,60 @@ __kernel void remap_16SC2_16UC1(__global const uchar * srcptr, int src_step, int ST nVal) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * rowsPerWI; - if (x < dst_cols && y < dst_rows) + if (x < dst_cols) { - int dst_index = mad24(y, dst_step, x * TSIZE + dst_offset); - int map1_index = mad24(y, map1_step, x * (int)sizeof(short2) + map1_offset); - int map2_index = mad24(y, map2_step, x * (int)sizeof(ushort) + map2_offset); - - __global const short2 * map1 = (__global const short2 *)(map1ptr + map1_index); - __global const ushort * map2 = (__global const ushort *)(map2ptr + map2_index); - __global T * dst = (__global T *)(dstptr + dst_index); - - int2 map_dataA = convert_int2(map1[0]); - int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); - int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); - int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); - - ushort map2Value = (ushort)(map2[0] & (INTER_TAB_SIZE2 - 1)); - WT2 u = (WT2)(map2Value & (INTER_TAB_SIZE - 1), map2Value >> INTER_BITS) / (WT2)(INTER_TAB_SIZE); - WT scalar = convertToWT(convertScalar(nVal)); - WT a = scalar, b = scalar, c = scalar, d = scalar; - - if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) - a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataA, a); - - if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) - b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataB, b); - - if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) - c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataC, c); - - if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) - d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataD, d); - - WT dst_data = a * (1 - u.x) * (1 - u.y) + - b * (u.x) * (1 - u.y) + - c * (1 - u.x) * (u.y) + - d * (u.x) * (u.y); - storepix(convertToT(dst_data), dst); + int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); + int map1_index = mad24(y, map1_step, mad24(x, (int)sizeof(short2), map1_offset)); + int map2_index = mad24(y, map2_step, mad24(x, (int)sizeof(ushort), map2_offset)); + + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, + map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) + if (y < dst_rows) + { + __global const short2 * map1 = (__global const short2 *)(map1ptr + map1_index); + __global const ushort * map2 = (__global const ushort *)(map2ptr + map2_index); + __global T * dst = (__global T *)(dstptr + dst_index); + + int2 map_dataA = convert_int2(map1[0]); + int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); + int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); + int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); + + ushort map2Value = (ushort)(map2[0] & (INTER_TAB_SIZE2 - 1)); + WT2 u = (WT2)(map2Value & (INTER_TAB_SIZE - 1), map2Value >> INTER_BITS) / (WT2)(INTER_TAB_SIZE); + + WT a = scalar, b = scalar, c = scalar, d = scalar; + + if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) + a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataA, a); + + if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) + b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataB, b); + + if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) + c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataC, c); + + if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) + d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataD, d); + + WT dst_data = a * (1 - u.x) * (1 - u.y) + + b * (u.x) * (1 - u.y) + + c * (1 - u.x) * (u.y) + + d * (u.x) * (u.y); + storepix(convertToT(dst_data), dst); + } } } @@ -359,55 +394,106 @@ __kernel void remap_2_32FC1(__global const uchar * srcptr, int src_step, int src ST nVal) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * rowsPerWI; - if (x < dst_cols && y < dst_rows) + if (x < dst_cols) { - int dst_index = mad24(y, dst_step, x * TSIZE + dst_offset); - int map1_index = mad24(y, map1_step, x * (int)sizeof(float) + map1_offset); - int map2_index = mad24(y, map2_step, x * (int)sizeof(float) + map2_offset); - - __global const float * map1 = (__global const float *)(map1ptr + map1_index); - __global const float * map2 = (__global const float *)(map2ptr + map2_index); - __global T * dst = (__global T *)(dstptr + dst_index); - - float2 map_data = (float2)(map1[0], map2[0]); - - int2 map_dataA = convert_int2_sat_rtn(map_data); - int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); - int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); - int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); - - float2 _u = map_data - convert_float2(map_dataA); - WT2 u = convertToWT2(convert_int2_rte(convertToWT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; WT scalar = convertToWT(convertScalar(nVal)); - WT a = scalar, b = scalar, c = scalar, d = scalar; - - if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) - a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataA, a); - - if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) - b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataB, b); - - if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) - c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataC, c); - - if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) - d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataD, d); - - WT dst_data = a * (1 - u.x) * (1 - u.y) + - b * (u.x) * (1 - u.y) + - c * (1 - u.x) * (u.y) + - d * (u.x) * (u.y); - storepix(convertToT(dst_data), dst); + int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); + int map1_index = mad24(y, map1_step, mad24(x, (int)sizeof(float), map1_offset)); + int map2_index = mad24(y, map2_step, mad24(x, (int)sizeof(float), map2_offset)); + + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, + map1_index += map1_step, map2_index += map2_step, dst_index += dst_step) + if (y < dst_rows) + { + __global const float * map1 = (__global const float *)(map1ptr + map1_index); + __global const float * map2 = (__global const float *)(map2ptr + map2_index); + __global T * dst = (__global T *)(dstptr + dst_index); + +#if defined BORDER_CONSTANT + + float xf = map1[0], yf = map2[0]; + int sx = convert_int_sat_rtn(xf), sy = convert_int_sat_rtn(yf); + + __constant float * coeffs_x = coeffs + ((convert_int_rte(xf * INTER_TAB_SIZE) & (INTER_TAB_SIZE - 1)) << 1); + __constant float * coeffs_y = coeffs + ((convert_int_rte(yf * INTER_TAB_SIZE) & (INTER_TAB_SIZE - 1)) << 1); + + WT sum = (WT)(0), xsum; + int src_index = mad24(sy, src_step, mad24(sx, TSIZE, src_offset)); + + #pragma unroll + for (int yp = 0; yp < 2; ++yp, src_index += src_step) + { + if (sy + yp >= 0 && sy + yp < src_rows) + { + xsum = (WT)(0); + if (sx >= 0 && sx + 2 < src_cols) + { +#if depth == 0 && cn == 1 + uchar2 value = vload2(0, srcptr + src_index); + xsum = dot(convert_float2(value), (float2)(coeffs_x[0], coeffs_x[1])); +#else + #pragma unroll + for (int xp = 0; xp < 2; ++xp) + xsum = fma(convertToWT(loadpix(srcptr + mad24(xp, TSIZE, src_index))), coeffs_x[xp], xsum); +#endif + } + else + { + #pragma unroll + for (int xp = 0; xp < 2; ++xp) + xsum = fma(sx + xp >= 0 && sx + xp < src_cols ? + convertToWT(loadpix(srcptr + mad24(xp, TSIZE, src_index))) : scalar, coeffs_x[xp], xsum); + } + sum = fma(xsum, coeffs_y[yp], sum); + } + else + sum = fma(scalar, coeffs_y[yp], sum); + } + + storepix(convertToT(sum), dst); +#else + float2 map_data = (float2)(map1[0], map2[0]); + + int2 map_dataA = convert_int2_sat_rtn(map_data); + int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); + int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); + int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); + + float2 _u = map_data - convert_float2(map_dataA); + WT2 u = convertToWT2(convert_int2_rte(convertToWT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; + WT scalar = convertToWT(convertScalar(nVal)); + WT a = scalar, b = scalar, c = scalar, d = scalar; + + if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) + a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataA, a); + + if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) + b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataB, b); + + if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) + c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataC, c); + + if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) + d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataD, d); + + WT dst_data = a * (1 - u.x) * (1 - u.y) + + b * (u.x) * (1 - u.y) + + c * (1 - u.x) * (u.y) + + d * (u.x) * (u.y); + storepix(convertToT(dst_data), dst); +#endif + } } } @@ -417,52 +503,58 @@ __kernel void remap_32FC2(__global const uchar * srcptr, int src_step, int src_o ST nVal) { int x = get_global_id(0); - int y = get_global_id(1); + int y = get_global_id(1) * rowsPerWI; - if (x < dst_cols && y < dst_rows) + if (x < dst_cols) { - int dst_index = mad24(y, dst_step, x * TSIZE + dst_offset); - int map_index = mad24(y, map_step, x * (int)sizeof(float2) + map_offset); - - __global const float2 * map = (__global const float2 *)(mapptr + map_index); - __global T * dst = (__global T *)(dstptr + dst_index); - - float2 map_data = map[0]; - int2 map_dataA = convert_int2_sat_rtn(map_data); - int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); - int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); - int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); - - float2 _u = map_data - convert_float2(map_dataA); - WT2 u = convertToWT2(convert_int2_rte(convertToWT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; WT scalar = convertToWT(convertScalar(nVal)); - WT a = scalar, b = scalar, c = scalar, d = scalar; - - if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) - a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataA, a); - - if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) - b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataB, b); - - if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) - c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataC, c); - - if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) - d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); - else - EXTRAPOLATE(map_dataD, d); - - WT dst_data = a * (1 - u.x) * (1 - u.y) + - b * (u.x) * (1 - u.y) + - c * (1 - u.x) * (u.y) + - d * (u.x) * (u.y); - storepix(convertToT(dst_data), dst); + int dst_index = mad24(y, dst_step, mad24(x, TSIZE, dst_offset)); + int map_index = mad24(y, map_step, mad24(x, (int)sizeof(float2), map_offset)); + + #pragma unroll + for (int i = 0; i < rowsPerWI; ++i, ++y, + map_index += map_step, dst_index += dst_step) + if (y < dst_rows) + { + __global const float2 * map = (__global const float2 *)(mapptr + map_index); + __global T * dst = (__global T *)(dstptr + dst_index); + + float2 map_data = map[0]; + int2 map_dataA = convert_int2_sat_rtn(map_data); + int2 map_dataB = (int2)(map_dataA.x + 1, map_dataA.y); + int2 map_dataC = (int2)(map_dataA.x, map_dataA.y + 1); + int2 map_dataD = (int2)(map_dataA.x + 1, map_dataA.y + 1); + + float2 _u = map_data - convert_float2(map_dataA); + WT2 u = convertToWT2(convert_int2_rte(convertToWT2(_u) * (WT2)INTER_TAB_SIZE)) / (WT2)INTER_TAB_SIZE; + WT a = scalar, b = scalar, c = scalar, d = scalar; + + if (!NEED_EXTRAPOLATION(map_dataA.x, map_dataA.y)) + a = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataA.y, src_step, map_dataA.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataA, a); + + if (!NEED_EXTRAPOLATION(map_dataB.x, map_dataB.y)) + b = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataB.y, src_step, map_dataB.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataB, b); + + if (!NEED_EXTRAPOLATION(map_dataC.x, map_dataC.y)) + c = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataC.y, src_step, map_dataC.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataC, c); + + if (!NEED_EXTRAPOLATION(map_dataD.x, map_dataD.y)) + d = convertToWT(loadpix((__global const T *)(srcptr + mad24(map_dataD.y, src_step, map_dataD.x * TSIZE + src_offset)))); + else + EXTRAPOLATE(map_dataD, d); + + WT dst_data = a * (1 - u.x) * (1 - u.y) + + b * (u.x) * (1 - u.y) + + c * (1 - u.x) * (u.y) + + d * (u.x) * (u.y); + storepix(convertToT(dst_data), dst); + } } } diff --git a/modules/imgproc/src/opencl/warp_affine.cl b/modules/imgproc/src/opencl/warp_affine.cl index bb041d1601..649f10db7a 100644 --- a/modules/imgproc/src/opencl/warp_affine.cl +++ b/modules/imgproc/src/opencl/warp_affine.cl @@ -61,6 +61,7 @@ #define AB_SCALE (1 << AB_BITS) #define INTER_REMAP_COEF_BITS 15 #define INTER_REMAP_COEF_SCALE (1 << INTER_REMAP_COEF_BITS) +#define ROUND_DELTA (1 << (AB_BITS - INTER_BITS - 1)) #define noconvert @@ -97,15 +98,15 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of { int round_delta = (AB_SCALE >> 1); - int X0_ = rint(M[0] * dx * AB_SCALE); - int Y0_ = rint(M[3] * dx * AB_SCALE); + int X0 = rint(fma(M[0], dx, fma(M[1], dy0, M[2])) * AB_SCALE) + round_delta; + int Y0 = rint(fma(M[3], dx, fma(M[4], dy0, M[5])) * AB_SCALE) + round_delta; + + int XSTEP = (int)(M[1] * AB_SCALE); + int YSTEP = (int)(M[4] * AB_SCALE); int dst_index = mad24(dy0, dst_step, mad24(dx, pixsize, dst_offset)); for (int dy = dy0, dy1 = min(dst_rows, dy0 + rowsPerWI); dy < dy1; ++dy, dst_index += dst_step) { - int X0 = X0_ + rint(fma(M[1], dy, M[2]) * AB_SCALE) + round_delta; - int Y0 = Y0_ + rint(fma(M[4], dy, M[5]) * AB_SCALE) + round_delta; - short sx = convert_short_sat(X0 >> AB_BITS); short sy = convert_short_sat(Y0 >> AB_BITS); @@ -116,12 +117,23 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of } else storepix(scalar, dstptr + dst_index); + + X0 += XSTEP; + Y0 += YSTEP; } } } #elif defined INTER_LINEAR +__constant float coeffs[64] = +{ 1.000000f, 0.000000f, 0.968750f, 0.031250f, 0.937500f, 0.062500f, 0.906250f, 0.093750f, 0.875000f, 0.125000f, 0.843750f, 0.156250f, + 0.812500f, 0.187500f, 0.781250f, 0.218750f, 0.750000f, 0.250000f, 0.718750f, 0.281250f, 0.687500f, 0.312500f, 0.656250f, 0.343750f, + 0.625000f, 0.375000f, 0.593750f, 0.406250f, 0.562500f, 0.437500f, 0.531250f, 0.468750f, 0.500000f, 0.500000f, 0.468750f, 0.531250f, + 0.437500f, 0.562500f, 0.406250f, 0.593750f, 0.375000f, 0.625000f, 0.343750f, 0.656250f, 0.312500f, 0.687500f, 0.281250f, 0.718750f, + 0.250000f, 0.750000f, 0.218750f, 0.781250f, 0.187500f, 0.812500f, 0.156250f, 0.843750f, 0.125000f, 0.875000f, 0.093750f, 0.906250f, + 0.062500f, 0.937500f, 0.031250f, 0.968750f }; + __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_offset, int src_rows, int src_cols, __global uchar * dstptr, int dst_step, int dst_offset, int dst_rows, int dst_cols, __constant CT * M, ST scalar_) @@ -131,24 +143,21 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of if (dx < dst_cols) { - int round_delta = AB_SCALE/INTER_TAB_SIZE/2; - - int tmp = (dx << AB_BITS); + int tmp = dx << AB_BITS; int X0_ = rint(M[0] * tmp); int Y0_ = rint(M[3] * tmp); for (int dy = dy0, dy1 = min(dst_rows, dy0 + rowsPerWI); dy < dy1; ++dy) { - int X0 = X0_ + rint(fma(M[1], dy, M[2]) * AB_SCALE) + round_delta; - int Y0 = Y0_ + rint(fma(M[4], dy, M[5]) * AB_SCALE) + round_delta; + int X0 = X0_ + rint(fma(M[1], dy, M[2]) * AB_SCALE) + ROUND_DELTA; + int Y0 = Y0_ + rint(fma(M[4], dy, M[5]) * AB_SCALE) + ROUND_DELTA; X0 = X0 >> (AB_BITS - INTER_BITS); Y0 = Y0 >> (AB_BITS - INTER_BITS); - short sx = convert_short_sat(X0 >> INTER_BITS); - short sy = convert_short_sat(Y0 >> INTER_BITS); - short ax = convert_short(X0 & (INTER_TAB_SIZE-1)); - short ay = convert_short(Y0 & (INTER_TAB_SIZE-1)); + short sx = convert_short_sat(X0 >> INTER_BITS), sy = convert_short_sat(Y0 >> INTER_BITS); + short ax = convert_short(X0 & (INTER_TAB_SIZE-1)), ay = convert_short(Y0 & (INTER_TAB_SIZE-1)); +#if defined AMD_DEVICE || depth > 4 WT v0 = scalar, v1 = scalar, v2 = scalar, v3 = scalar; if (sx >= 0 && sx < src_cols) { @@ -180,8 +189,48 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of storepix(convertToT((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS), dstptr + dst_index); #else float tabx2 = 1.0f - tabx, taby2 = 1.0f - taby; - WT val = fma(v0, tabx2 * taby2, fma(v1, tabx * taby2, fma(v2, tabx2 * taby, v3 * tabx * taby))); + WT val = fma(tabx2, fma(v0, taby2, v2 * taby), tabx * fma(v1, taby2, v3 * taby)); storepix(convertToT(val), dstptr + dst_index); +#endif +#else // INTEL_DEVICE + __constant float * coeffs_y = coeffs + (ay << 1), * coeffs_x = coeffs + (ax << 1); + + int src_index0 = mad24(sy, src_step, mad24(sx, pixsize, src_offset)), src_index; + int dst_index = mad24(dy, dst_step, mad24(dx, pixsize, dst_offset)); + + WT sum = (WT)(0), xsum; + #pragma unroll + for (int y = 0; y < 2; y++) + { + src_index = mad24(y, src_step, src_index0); + if (sy + y >= 0 && sy + y < src_rows) + { + xsum = (WT)(0); + if (sx >= 0 && sx + 2 < src_cols) + { +#if depth == 0 && cn == 1 + uchar2 value = vload2(0, srcptr + src_index); + xsum = dot(convert_float2(value), (float2)(coeffs_x[0], coeffs_x[1])); +#else + #pragma unroll + for (int x = 0; x < 2; x++) + xsum = fma(convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))), coeffs_x[x], xsum); +#endif + } + else + { + #pragma unroll + for (int x = 0; x < 2; x++) + xsum = fma(sx + x >= 0 && sx + x < src_cols ? + convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))) : scalar, coeffs_x[x], xsum); + } + sum = fma(xsum, coeffs_y[y], sum); + } + else + sum = fma(scalar, coeffs_y[y], sum); + } + + storepix(convertToT(sum), dstptr + dst_index); #endif } } @@ -189,6 +238,8 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of #elif defined INTER_CUBIC +#ifdef AMD_DEVICE + inline void interpolateCubic( float x, float* coeffs ) { const float A = -0.75f; @@ -199,6 +250,23 @@ inline void interpolateCubic( float x, float* coeffs ) coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2]; } +#else + +__constant float coeffs[128] = + { 0.000000f, 1.000000f, 0.000000f, 0.000000f, -0.021996f, 0.997841f, 0.024864f, -0.000710f, -0.041199f, 0.991516f, 0.052429f, -0.002747f, + -0.057747f, 0.981255f, 0.082466f, -0.005974f, -0.071777f, 0.967285f, 0.114746f, -0.010254f, -0.083427f, 0.949837f, 0.149040f, -0.015450f, + -0.092834f, 0.929138f, 0.185120f, -0.021423f, -0.100136f, 0.905418f, 0.222755f, -0.028038f, -0.105469f, 0.878906f, 0.261719f, -0.035156f, + -0.108971f, 0.849831f, 0.301781f, -0.042641f, -0.110779f, 0.818420f, 0.342712f, -0.050354f, -0.111031f, 0.784904f, 0.384285f, -0.058159f, + -0.109863f, 0.749512f, 0.426270f, -0.065918f, -0.107414f, 0.712471f, 0.468437f, -0.073494f, -0.103821f, 0.674011f, 0.510559f, -0.080750f, + -0.099220f, 0.634361f, 0.552406f, -0.087547f, -0.093750f, 0.593750f, 0.593750f, -0.093750f, -0.087547f, 0.552406f, 0.634361f, -0.099220f, + -0.080750f, 0.510559f, 0.674011f, -0.103821f, -0.073494f, 0.468437f, 0.712471f, -0.107414f, -0.065918f, 0.426270f, 0.749512f, -0.109863f, + -0.058159f, 0.384285f, 0.784904f, -0.111031f, -0.050354f, 0.342712f, 0.818420f, -0.110779f, -0.042641f, 0.301781f, 0.849831f, -0.108971f, + -0.035156f, 0.261719f, 0.878906f, -0.105469f, -0.028038f, 0.222755f, 0.905418f, -0.100136f, -0.021423f, 0.185120f, 0.929138f, -0.092834f, + -0.015450f, 0.149040f, 0.949837f, -0.083427f, -0.010254f, 0.114746f, 0.967285f, -0.071777f, -0.005974f, 0.082466f, 0.981255f, -0.057747f, + -0.002747f, 0.052429f, 0.991516f, -0.041199f, -0.000710f, 0.024864f, 0.997841f, -0.021996f }; + +#endif + __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_offset, int src_rows, int src_cols, __global uchar * dstptr, int dst_step, int dst_offset, int dst_rows, int dst_cols, __constant CT * M, ST scalar_) @@ -208,22 +276,17 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of if (dx < dst_cols && dy < dst_rows) { - int round_delta = ((AB_SCALE>>INTER_BITS)>>1); - int tmp = (dx << AB_BITS); - int X0 = rint(M[0] * tmp); - int Y0 = rint(M[3] * tmp); + int X0 = rint(M[0] * tmp) + rint(fma(M[1], dy, M[2]) * AB_SCALE) + ROUND_DELTA; + int Y0 = rint(M[3] * tmp) + rint(fma(M[4], dy, M[5]) * AB_SCALE) + ROUND_DELTA; - X0 += rint(fma(M[1], dy, M[2]) * AB_SCALE) + round_delta; - Y0 += rint(fma(M[4], dy, M[5]) * AB_SCALE) + round_delta; X0 = X0 >> (AB_BITS - INTER_BITS); Y0 = Y0 >> (AB_BITS - INTER_BITS); - int sx = (short)(X0 >> INTER_BITS) - 1; - int sy = (short)(Y0 >> INTER_BITS) - 1; - int ay = (short)(Y0 & (INTER_TAB_SIZE-1)); - int ax = (short)(X0 & (INTER_TAB_SIZE-1)); + int sx = (short)(X0 >> INTER_BITS) - 1, sy = (short)(Y0 >> INTER_BITS) - 1; + int ay = (short)(Y0 & (INTER_TAB_SIZE - 1)), ax = (short)(X0 & (INTER_TAB_SIZE - 1)); +#ifdef AMD_DEVICE WT v[16]; #pragma unroll for (int y = 0; y < 4; y++) @@ -269,6 +332,46 @@ __kernel void warpAffine(__global const uchar * srcptr, int src_step, int src_of for (int i = 0; i < 16; i++) sum = fma(v[i], tab1y[(i>>2)] * tab1x[(i&3)], sum); storepix(convertToT( sum ), dstptr + dst_index); +#endif +#else // INTEL_DEVICE + __constant float * coeffs_y = coeffs + (ay << 2), * coeffs_x = coeffs + (ax << 2); + + int src_index0 = mad24(sy, src_step, mad24(sx, pixsize, src_offset)), src_index; + int dst_index = mad24(dy, dst_step, mad24(dx, pixsize, dst_offset)); + + WT sum = (WT)(0), xsum; + #pragma unroll + for (int y = 0; y < 4; y++) + { + src_index = mad24(y, src_step, src_index0); + if (sy + y >= 0 && sy + y < src_rows) + { + xsum = (WT)(0); + if (sx >= 0 && sx + 4 < src_cols) + { +#if depth == 0 && cn == 1 + uchar4 value = vload4(0, srcptr + src_index); + xsum = dot(convert_float4(value), (float4)(coeffs_x[0], coeffs_x[1], coeffs_x[2], coeffs_x[3])); +#else + #pragma unroll + for (int x = 0; x < 4; x++) + xsum = fma(convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))), coeffs_x[x], xsum); +#endif + } + else + { + #pragma unroll + for (int x = 0; x < 4; x++) + xsum = fma(sx + x >= 0 && sx + x < src_cols ? + convertToWT(loadpix(srcptr + mad24(x, pixsize, src_index))) : scalar, coeffs_x[x], xsum); + } + sum = fma(xsum, coeffs_y[y], sum); + } + else + sum = fma(scalar, coeffs_y[y], sum); + } + + storepix(convertToT(sum), dstptr + dst_index); #endif } } diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 1e4f89cc64..cbbe399301 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -405,10 +405,10 @@ typedef void (*PyrFunc)(const Mat&, Mat&, int); static bool ocl_pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType) { - int type = _src.type(), depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type); + int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0; - if (channels > 4 || (depth == CV_64F && !doubleSupport)) + if (cn > 4 || (depth == CV_64F && !doubleSupport)) return false; Size ssize = _src.size(); @@ -423,17 +423,20 @@ static bool ocl_pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, in int float_depth = depth == CV_64F ? CV_64F : CV_32F; const int local_size = 256; + int kercn = 1; + if (depth == CV_8U && float_depth == CV_32F && cn == 1 && ocl::Device::getDefault().isIntel()) + kercn = 4; const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE", "BORDER_REFLECT", "BORDER_WRAP", "BORDER_REFLECT_101" }; char cvt[2][50]; String buildOptions = format( "-D T=%s -D FT=%s -D convertToT=%s -D convertToFT=%s%s " - "-D T1=%s -D cn=%d -D %s -D LOCAL_SIZE=%d", - ocl::typeToStr(type), ocl::typeToStr(CV_MAKETYPE(float_depth, channels)), - ocl::convertTypeStr(float_depth, depth, channels, cvt[0]), - ocl::convertTypeStr(depth, float_depth, channels, cvt[1]), - doubleSupport ? " -D DOUBLE_SUPPORT" : "", - ocl::typeToStr(depth), channels, borderMap[borderType], local_size + "-D T1=%s -D cn=%d -D kercn=%d -D fdepth=%d -D %s -D LOCAL_SIZE=%d", + ocl::typeToStr(type), ocl::typeToStr(CV_MAKETYPE(float_depth, cn)), + ocl::convertTypeStr(float_depth, depth, cn, cvt[0]), + ocl::convertTypeStr(depth, float_depth, cn, cvt[1]), + doubleSupport ? " -D DOUBLE_SUPPORT" : "", ocl::typeToStr(depth), + cn, kercn, float_depth, borderMap[borderType], local_size ); ocl::Kernel k("pyrDown", ocl::imgproc::pyr_down_oclsrc, buildOptions); if (k.empty()) @@ -441,8 +444,8 @@ static bool ocl_pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, in k.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::WriteOnly(dst)); - size_t localThreads[2] = { local_size, 1 }; - size_t globalThreads[2] = { src.cols, dst.rows }; + size_t localThreads[2] = { local_size/kercn, 1 }; + size_t globalThreads[2] = { (src.cols + (kercn-1))/kercn, dst.rows }; return k.run(2, globalThreads, localThreads, false); } diff --git a/modules/imgproc/src/sumpixels.cpp b/modules/imgproc/src/sumpixels.cpp index 1d246ec7bc..e7694b01a9 100755 --- a/modules/imgproc/src/sumpixels.cpp +++ b/modules/imgproc/src/sumpixels.cpp @@ -235,97 +235,87 @@ typedef void (*IntegralFunc)(const uchar* src, size_t srcstep, uchar* sum, size_ #ifdef HAVE_OPENCL -enum { vlen = 4 }; - static bool ocl_integral( InputArray _src, OutputArray _sum, int sdepth ) { - if ( _src.type() != CV_8UC1 || _src.step() % vlen != 0 || _src.offset() % vlen != 0 || - !(sdepth == CV_32S || sdepth == CV_32F) ) - return false; + bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0; - ocl::Kernel k1("integral_sum_cols", ocl::imgproc::integral_sum_oclsrc, - format("-D sdepth=%d", sdepth)); - if (k1.empty()) + if ( (_src.type() != CV_8UC1) || + !(sdepth == CV_32S || sdepth == CV_32F || (doubleSupport && sdepth == CV_64F))) return false; - Size size = _src.size(), t_size = Size(((size.height + vlen - 1) / vlen) * vlen, size.width), - ssize(size.width + 1, size.height + 1); - _sum.create(ssize, sdepth); - UMat src = _src.getUMat(), t_sum(t_size, sdepth), sum = _sum.getUMat(); - t_sum = t_sum(Range::all(), Range(0, size.height)); + static const int tileSize = 16; + + String build_opt = format("-D sumT=%s -D LOCAL_SUM_SIZE=%d%s", + ocl::typeToStr(sdepth), tileSize, + doubleSupport ? " -D DOUBLE_SUPPORT" : ""); + + ocl::Kernel kcols("integral_sum_cols", ocl::imgproc::integral_sum_oclsrc, build_opt); + if (kcols.empty()) + return false; - int offset = (int)src.offset / vlen; - int vcols = (src.cols + vlen - 1) / vlen; - int sum_offset = (int)sum.offset / vlen; + UMat src = _src.getUMat(); + Size src_size = src.size(); + Size bufsize(((src_size.height + tileSize - 1) / tileSize) * tileSize, ((src_size.width + tileSize - 1) / tileSize) * tileSize); + UMat buf(bufsize, sdepth); + kcols.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::WriteOnlyNoSize(buf)); + size_t gt = src.cols, lt = tileSize; + if (!kcols.run(1, >, <, false)) + return false; - k1.args(ocl::KernelArg::PtrReadOnly(src), ocl::KernelArg::PtrWriteOnly(t_sum), - offset, src.rows, src.cols, (int)src.step, (int)t_sum.step); - size_t gt = ((vcols + 1) / 2) * 256, lt = 256; - if (!k1.run(1, >, <, false)) + ocl::Kernel krows("integral_sum_rows", ocl::imgproc::integral_sum_oclsrc, build_opt); + if (krows.empty()) return false; - ocl::Kernel k2("integral_sum_rows", ocl::imgproc::integral_sum_oclsrc, - format("-D sdepth=%d", sdepth)); - k2.args(ocl::KernelArg::PtrReadOnly(t_sum), ocl::KernelArg::PtrWriteOnly(sum), - t_sum.rows, t_sum.cols, (int)t_sum.step, (int)sum.step, sum_offset); + Size sumsize(src_size.width + 1, src_size.height + 1); + _sum.create(sumsize, sdepth); + UMat sum = _sum.getUMat(); - size_t gt2 = t_sum.cols * 32, lt2 = 256; - return k2.run(1, >2, <2, false); + krows.args(ocl::KernelArg::ReadOnlyNoSize(buf), ocl::KernelArg::WriteOnly(sum)); + gt = src.rows; + return krows.run(1, >, <, false); } static bool ocl_integral( InputArray _src, OutputArray _sum, OutputArray _sqsum, int sdepth, int sqdepth ) { bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0; - if ( _src.type() != CV_8UC1 || _src.step() % vlen != 0 || _src.offset() % vlen != 0 || - (!doubleSupport && (sdepth == CV_64F || sqdepth == CV_64F)) ) - return false; - - char cvt[40]; - String opts = format("-D sdepth=%d -D sqdepth=%d -D TYPE=%s -D TYPE4=%s4 -D convert_TYPE4=%s%s", - sdepth, sqdepth, ocl::typeToStr(sqdepth), ocl::typeToStr(sqdepth), - ocl::convertTypeStr(sdepth, sqdepth, 4, cvt), - doubleSupport ? " -D DOUBLE_SUPPORT" : ""); - - ocl::Kernel k1("integral_cols", ocl::imgproc::integral_sqrsum_oclsrc, opts); - if (k1.empty()) + if ( _src.type() != CV_8UC1 || (!doubleSupport && (sdepth == CV_64F || sqdepth == CV_64F)) ) return false; - Size size = _src.size(), dsize = Size(size.width + 1, size.height + 1), - t_size = Size(((size.height + vlen - 1) / vlen) * vlen, size.width); - UMat src = _src.getUMat(), t_sum(t_size, sdepth), t_sqsum(t_size, sqdepth); - t_sum = t_sum(Range::all(), Range(0, size.height)); - t_sqsum = t_sqsum(Range::all(), Range(0, size.height)); - - _sum.create(dsize, sdepth); - _sqsum.create(dsize, sqdepth); - UMat sum = _sum.getUMat(), sqsum = _sqsum.getUMat(); + static const int tileSize = 16; - int offset = (int)src.offset / vlen; - int pre_invalid = src.offset % vlen; - int vcols = (pre_invalid + src.cols + vlen - 1) / vlen; - int sum_offset = (int)(sum.offset / sum.elemSize()); - int sqsum_offset = (int)(sqsum.offset / sqsum.elemSize()); + String build_opt = format("-D SUM_SQUARE -D sumT=%s -D sumSQT=%s -D LOCAL_SUM_SIZE=%d%s", + ocl::typeToStr(sdepth), ocl::typeToStr(sqdepth), + tileSize, + doubleSupport ? " -D DOUBLE_SUPPORT" : ""); - k1.args(ocl::KernelArg::PtrReadOnly(src), ocl::KernelArg::PtrWriteOnly(t_sum), - ocl::KernelArg::PtrWriteOnly(t_sqsum), offset, pre_invalid, src.rows, - src.cols, (int)src.step, (int)t_sum.step, (int)t_sqsum.step); + ocl::Kernel kcols("integral_sum_cols", ocl::imgproc::integral_sum_oclsrc, build_opt); + if (kcols.empty()) + return false; - size_t gt = ((vcols + 1) / 2) * 256, lt = 256; - if (!k1.run(1, >, <, false)) + UMat src = _src.getUMat(); + Size src_size = src.size(); + Size bufsize(((src_size.height + tileSize - 1) / tileSize) * tileSize, ((src_size.width + tileSize - 1) / tileSize) * tileSize); + UMat buf(bufsize, sdepth); + UMat buf_sq(bufsize, sqdepth); + kcols.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::WriteOnlyNoSize(buf), ocl::KernelArg::WriteOnlyNoSize(buf_sq)); + size_t gt = src.cols, lt = tileSize; + if (!kcols.run(1, >, <, false)) return false; - ocl::Kernel k2("integral_rows", ocl::imgproc::integral_sqrsum_oclsrc, opts); - if (k2.empty()) + ocl::Kernel krows("integral_sum_rows", ocl::imgproc::integral_sum_oclsrc, build_opt); + if (krows.empty()) return false; - k2.args(ocl::KernelArg::PtrReadOnly(t_sum), ocl::KernelArg::PtrReadOnly(t_sqsum), - ocl::KernelArg::PtrWriteOnly(sum), ocl::KernelArg::PtrWriteOnly(sqsum), - t_sum.rows, t_sum.cols, (int)t_sum.step, (int)t_sqsum.step, - (int)sum.step, (int)sqsum.step, sum_offset, sqsum_offset); + Size sumsize(src_size.width + 1, src_size.height + 1); + _sum.create(sumsize, sdepth); + UMat sum = _sum.getUMat(); + _sqsum.create(sumsize, sqdepth); + UMat sum_sq = _sqsum.getUMat(); - size_t gt2 = t_sum.cols * 32, lt2 = 256; - return k2.run(1, >2, <2, false); + krows.args(ocl::KernelArg::ReadOnlyNoSize(buf), ocl::KernelArg::ReadOnlyNoSize(buf_sq), ocl::KernelArg::WriteOnly(sum), ocl::KernelArg::WriteOnlyNoSize(sum_sq)); + gt = src.rows; + return krows.run(1, >, <, false); } #endif diff --git a/modules/imgproc/test/ocl/test_color.cpp b/modules/imgproc/test/ocl/test_color.cpp index e9fb0d3896..82bf2c06f1 100644 --- a/modules/imgproc/test/ocl/test_color.cpp +++ b/modules/imgproc/test/ocl/test_color.cpp @@ -305,11 +305,11 @@ OCL_TEST_P(CvtColor8u32f, Lab2LRGBA) { performTest(3, 4, CVTCODE(Lab2LRGB), dept OCL_TEST_P(CvtColor8u32f, BGR2Luv) { performTest(3, 3, CVTCODE(BGR2Luv), depth == CV_8U ? 1 : 1e-2); } OCL_TEST_P(CvtColor8u32f, RGB2Luv) { performTest(3, 3, CVTCODE(RGB2Luv), depth == CV_8U ? 1 : 1e-2); } OCL_TEST_P(CvtColor8u32f, LBGR2Luv) { performTest(3, 3, CVTCODE(LBGR2Luv), depth == CV_8U ? 1 : 4e-3); } -OCL_TEST_P(CvtColor8u32f, LRGB2Luv) { performTest(3, 3, CVTCODE(LRGB2Luv), depth == CV_8U ? 1 : 4e-3); } +OCL_TEST_P(CvtColor8u32f, LRGB2Luv) { performTest(3, 3, CVTCODE(LRGB2Luv), depth == CV_8U ? 1 : 5e-3); } OCL_TEST_P(CvtColor8u32f, BGRA2Luv) { performTest(4, 3, CVTCODE(BGR2Luv), depth == CV_8U ? 1 : 8e-3); } OCL_TEST_P(CvtColor8u32f, RGBA2Luv) { performTest(4, 3, CVTCODE(RGB2Luv), depth == CV_8U ? 1 : 9e-3); } -OCL_TEST_P(CvtColor8u32f, LBGRA2Luv) { performTest(4, 3, CVTCODE(LBGR2Luv), depth == CV_8U ? 1 : 4e-3); } -OCL_TEST_P(CvtColor8u32f, LRGBA2Luv) { performTest(4, 3, CVTCODE(LRGB2Luv), depth == CV_8U ? 1 : 4e-3); } +OCL_TEST_P(CvtColor8u32f, LBGRA2Luv) { performTest(4, 3, CVTCODE(LBGR2Luv), depth == CV_8U ? 1 : 5e-3); } +OCL_TEST_P(CvtColor8u32f, LRGBA2Luv) { performTest(4, 3, CVTCODE(LRGB2Luv), depth == CV_8U ? 1 : 5e-3); } OCL_TEST_P(CvtColor8u32f, Luv2BGR) { performTest(3, 3, CVTCODE(Luv2BGR), depth == CV_8U ? 1 : 7e-5); } OCL_TEST_P(CvtColor8u32f, Luv2RGB) { performTest(3, 3, CVTCODE(Luv2RGB), depth == CV_8U ? 1 : 7e-5); } diff --git a/modules/imgproc/test/ocl/test_filters.cpp b/modules/imgproc/test/ocl/test_filters.cpp index 46d77285d7..1fe2927886 100644 --- a/modules/imgproc/test/ocl/test_filters.cpp +++ b/modules/imgproc/test/ocl/test_filters.cpp @@ -63,7 +63,7 @@ PARAM_TEST_CASE(FilterTestBase, MatType, BorderType, // border type double, // optional parameter bool, // roi or not - int) //width multiplier + int) // width multiplier { int type, borderType, ksize; Size size; @@ -244,8 +244,8 @@ OCL_TEST_P(Erode, Mat) random_roi(); Mat kernel = randomMat(kernelSize, CV_8UC1, 0, 3); - OCL_OFF(cv::erode(src_roi, dst_roi, kernel, Point(-1,-1), iterations) ); - OCL_ON(cv::erode(usrc_roi, udst_roi, kernel, Point(-1,-1), iterations) ); + OCL_OFF(cv::erode(src_roi, dst_roi, kernel, Point(-1, -1), iterations) ); + OCL_ON(cv::erode(usrc_roi, udst_roi, kernel, Point(-1, -1), iterations) ); Near(); } @@ -266,8 +266,8 @@ OCL_TEST_P(Dilate, Mat) random_roi(); Mat kernel = randomMat(kernelSize, CV_8UC1, 0, 3); - OCL_OFF(cv::dilate(src_roi, dst_roi, kernel, Point(-1,-1), iterations) ); - OCL_ON(cv::dilate(usrc_roi, udst_roi, kernel, Point(-1,-1), iterations) ); + OCL_OFF(cv::dilate(src_roi, dst_roi, kernel, Point(-1, -1), iterations) ); + OCL_ON(cv::dilate(usrc_roi, udst_roi, kernel, Point(-1, -1), iterations) ); Near(); } @@ -289,8 +289,8 @@ OCL_TEST_P(MorphologyEx, Mat) random_roi(); Mat kernel = randomMat(kernelSize, CV_8UC1, 0, 3); - OCL_OFF(cv::morphologyEx(src_roi, dst_roi, op, kernel, Point(-1,-1), iterations) ); - OCL_ON(cv::morphologyEx(usrc_roi, udst_roi, op, kernel, Point(-1,-1), iterations) ); + OCL_OFF(cv::morphologyEx(src_roi, dst_roi, op, kernel, Point(-1, -1), iterations) ); + OCL_ON(cv::morphologyEx(usrc_roi, udst_roi, op, kernel, Point(-1, -1), iterations) ); Near(); } @@ -360,8 +360,8 @@ OCL_INSTANTIATE_TEST_CASE_P(Filter, GaussianBlurTest, Combine( OCL_INSTANTIATE_TEST_CASE_P(Filter, Erode, Combine( Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32FC1, CV_32FC3, CV_32FC4, CV_64FC1, CV_64FC4), Values(3, 5, 7), - Values(Size(0,0)),//not used - Values((BorderType)BORDER_CONSTANT),//not used + Values(Size(0, 0)), //not used + Values((BorderType)BORDER_CONSTANT), Values(1.0, 2.0, 3.0), Bool(), Values(1))); // not used @@ -369,20 +369,20 @@ OCL_INSTANTIATE_TEST_CASE_P(Filter, Erode, Combine( OCL_INSTANTIATE_TEST_CASE_P(Filter, Dilate, Combine( Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32FC1, CV_32FC3, CV_32FC4, CV_64FC1, CV_64FC4), Values(3, 5, 7), - Values(Size(0,0)),//not used - Values((BorderType)BORDER_CONSTANT),//not used + Values(Size(0, 0)), // not used + Values((BorderType)BORDER_CONSTANT), Values(1.0, 2.0, 3.0), Bool(), - Values(1))); //not used + Values(1))); // not used OCL_INSTANTIATE_TEST_CASE_P(Filter, MorphologyEx, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32FC1, CV_32FC3, CV_32FC4, CV_64FC1, CV_64FC4), + Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32FC1, CV_32FC3, CV_32FC4), Values(3, 5, 7), - Values(Size(0, 0), Size(0, 1), Size(0, 2), Size(0, 3), Size(0, 4), Size(0, 5), Size(0, 6)), // used as generator of operations - Values((BorderType)BORDER_CONSTANT),// not used + Values(Size(0, 2), Size(0, 3), Size(0, 4), Size(0, 5), Size(0, 6)), // used as generator of operations + Values((BorderType)BORDER_CONSTANT), Values(1.0, 2.0, 3.0), Bool(), - Values(1))); //not used + Values(1))); // not used } } // namespace cvtest::ocl diff --git a/modules/imgproc/test/ocl/test_warp.cpp b/modules/imgproc/test/ocl/test_warp.cpp index 416bd523ed..53d82187f9 100644 --- a/modules/imgproc/test/ocl/test_warp.cpp +++ b/modules/imgproc/test/ocl/test_warp.cpp @@ -267,7 +267,7 @@ PARAM_TEST_CASE(Remap, MatDepth, Channels, std::pair, BorderTy Border map1Border = randomBorder(0, useRoi ? MAX_VALUE : 0); randomSubMat(map1, map1_roi, dstROISize, map1Border, map1Type, -mapMaxValue, mapMaxValue); - Border map2Border = randomBorder(0, useRoi ? MAX_VALUE : 0); + Border map2Border = randomBorder(0, useRoi ? MAX_VALUE + 1 : 0); if (map2Type != noType) { int mapMinValue = -mapMaxValue; diff --git a/modules/imgproc/test/test_histograms.cpp b/modules/imgproc/test/test_histograms.cpp index 19ccc656b5..e9db6fcde0 100644 --- a/modules/imgproc/test/test_histograms.cpp +++ b/modules/imgproc/test/test_histograms.cpp @@ -948,7 +948,7 @@ int CV_ThreshHistTest::validate_test_results( int /*test_case_idx*/ ) class CV_CompareHistTest : public CV_BaseHistTest { public: - enum { MAX_METHOD = 5 }; + enum { MAX_METHOD = 6 }; CV_CompareHistTest(); protected: @@ -1021,6 +1021,13 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) sq0 += v0*v0; sq1 += v1*v1; result0[CV_COMP_BHATTACHARYYA] += sqrt(v0*v1); + { + if( fabs(v0) <= DBL_EPSILON ) + continue; + if( fabs(v1) <= DBL_EPSILON ) + v1 = 1e-10; + result0[CV_COMP_KL_DIV] += v0 * std::log( v0 / v1 ); + } } } else @@ -1046,6 +1053,13 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) s0 += v0; sq0 += v0*v0; result0[CV_COMP_BHATTACHARYYA] += sqrt(v0*v1); + { + if (v0 <= DBL_EPSILON) + continue; + if (!v1) + v1 = 1e-10; + result0[CV_COMP_KL_DIV] += v0 * std::log( v0 / v1 ); + } } for( node = cvInitSparseMatIterator( sparse1, &iterator ); @@ -1076,7 +1090,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) i == CV_COMP_CHISQR_ALT ? "Alternative Chi-Square" : i == CV_COMP_CORREL ? "Correlation" : i == CV_COMP_INTERSECT ? "Intersection" : - i == CV_COMP_BHATTACHARYYA ? "Bhattacharyya" : "Unknown"; + i == CV_COMP_BHATTACHARYYA ? "Bhattacharyya" : + i == CV_COMP_KL_DIV ? "Kullback-Leibler" : "Unknown"; if( cvIsNaN(v) || cvIsInf(v) ) { diff --git a/modules/imgproc/test/test_precomp.hpp b/modules/imgproc/test/test_precomp.hpp index 53f315ee4f..249ec8d629 100644 --- a/modules/imgproc/test/test_precomp.hpp +++ b/modules/imgproc/test/test_precomp.hpp @@ -13,7 +13,7 @@ #include "opencv2/ts.hpp" #include "opencv2/core/private.hpp" #include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" #include "opencv2/imgproc/imgproc_c.h" diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt index 4db3847bd2..1362aecad9 100644 --- a/modules/java/CMakeLists.txt +++ b/modules/java/CMakeLists.txt @@ -6,7 +6,7 @@ if(IOS OR NOT PYTHON_DEFAULT_AVAILABLE OR NOT ANT_EXECUTABLE OR NOT (JNI_FOUND O endif() set(the_description "The java bindings") -ocv_add_module(java BINDINGS opencv_core opencv_imgproc OPTIONAL opencv_objdetect opencv_features2d opencv_video opencv_highgui opencv_ml opencv_calib3d opencv_photo opencv_nonfree opencv_contrib) +ocv_add_module(java BINDINGS opencv_core opencv_imgproc OPTIONAL opencv_objdetect opencv_features2d opencv_video opencv_imgcodecs opencv_videoio opencv_ml opencv_calib3d opencv_photo opencv_nonfree opencv_contrib) ocv_module_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/generator/src/cpp") if(NOT ANDROID) diff --git a/modules/java/android_test/src/org/opencv/test/OpenCVTestCase.java b/modules/java/android_test/src/org/opencv/test/OpenCVTestCase.java index 97cdca783a..78eb738cb5 100644 --- a/modules/java/android_test/src/org/opencv/test/OpenCVTestCase.java +++ b/modules/java/android_test/src/org/opencv/test/OpenCVTestCase.java @@ -21,7 +21,7 @@ import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.core.DMatch; import org.opencv.core.KeyPoint; -import org.opencv.highgui.Highgui; +import org.opencv.imgcodecs.Imgcodecs; import android.util.Log; @@ -134,8 +134,8 @@ public class OpenCVTestCase extends TestCase { rgba0 = new Mat(matSize, matSize, CvType.CV_8UC4, Scalar.all(0)); rgba128 = new Mat(matSize, matSize, CvType.CV_8UC4, Scalar.all(128)); - rgbLena = Highgui.imread(OpenCVTestRunner.LENA_PATH); - grayChess = Highgui.imread(OpenCVTestRunner.CHESS_PATH, 0); + rgbLena = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH); + grayChess = Imgcodecs.imread(OpenCVTestRunner.CHESS_PATH, 0); v1 = new Mat(1, 3, CvType.CV_32F); v1.put(0, 0, 1.0, 3.0, 2.0); diff --git a/modules/java/android_test/src/org/opencv/test/android/UtilsTest.java b/modules/java/android_test/src/org/opencv/test/android/UtilsTest.java index ac20139104..5494513158 100644 --- a/modules/java/android_test/src/org/opencv/test/android/UtilsTest.java +++ b/modules/java/android_test/src/org/opencv/test/android/UtilsTest.java @@ -5,7 +5,7 @@ import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Scalar; -import org.opencv.highgui.Highgui; +import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import org.opencv.test.OpenCVTestCase; import org.opencv.test.OpenCVTestRunner; @@ -57,7 +57,7 @@ public class UtilsTest extends OpenCVTestCase { } public void testMatToBitmap() { - Mat imgBGR = Highgui.imread( OpenCVTestRunner.LENA_PATH ); + Mat imgBGR = Imgcodecs.imread( OpenCVTestRunner.LENA_PATH ); assertTrue(imgBGR != null && !imgBGR.empty() && imgBGR.channels() == 3); Mat m16 = new Mat(imgBGR.rows(), imgBGR.cols(), CvType.CV_8UC4); diff --git a/modules/java/android_test/src/org/opencv/test/features2d/Features2dTest.java b/modules/java/android_test/src/org/opencv/test/features2d/Features2dTest.java index 2118b53065..b9e8983fdc 100644 --- a/modules/java/android_test/src/org/opencv/test/features2d/Features2dTest.java +++ b/modules/java/android_test/src/org/opencv/test/features2d/Features2dTest.java @@ -18,7 +18,7 @@ import org.opencv.features2d.DescriptorMatcher; import org.opencv.features2d.FeatureDetector; import org.opencv.features2d.Features2d; import org.opencv.core.KeyPoint; -import org.opencv.highgui.Highgui; +import org.opencv.imgcodecs.Imgcodecs; import org.opencv.test.OpenCVTestCase; import org.opencv.test.OpenCVTestRunner; @@ -93,7 +93,7 @@ public class Features2dTest extends OpenCVTestCase { writeFile(extractorCfgFile, extractorCfg); extractor.read(extractorCfgFile); - Mat imgTrain = Highgui.imread(OpenCVTestRunner.LENA_PATH, Highgui.CV_LOAD_IMAGE_GRAYSCALE); + Mat imgTrain = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH, Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE); Mat imgQuery = imgTrain.submat(new Range(0, imgTrain.rows() - 100), Range.all()); MatOfKeyPoint trainKeypoints = new MatOfKeyPoint(); @@ -139,7 +139,7 @@ public class Features2dTest extends OpenCVTestCase { Mat outimg = new Mat(); Features2d.drawMatches(imgQuery, queryKeypoints, imgTrain, trainKeypoints, matches, outimg); String outputPath = OpenCVTestRunner.getOutputFileName("PTODresult.png"); - Highgui.imwrite(outputPath, outimg); + Imgcodecs.imwrite(outputPath, outimg); // OpenCVTestRunner.Log("Output image is saved to: " + outputPath); } } diff --git a/modules/java/android_test/src/org/opencv/test/highgui/HighguiTest.java b/modules/java/android_test/src/org/opencv/test/highgui/HighguiTest.java index b8e7c3b8b2..312462c3a5 100644 --- a/modules/java/android_test/src/org/opencv/test/highgui/HighguiTest.java +++ b/modules/java/android_test/src/org/opencv/test/highgui/HighguiTest.java @@ -2,7 +2,7 @@ package org.opencv.test.highgui; import org.opencv.core.MatOfByte; import org.opencv.core.MatOfInt; -import org.opencv.highgui.Highgui; +import org.opencv.imgcodecs.Imgcodecs; import org.opencv.test.OpenCVTestCase; import org.opencv.test.OpenCVTestRunner; @@ -15,29 +15,29 @@ public class HighguiTest extends OpenCVTestCase { public void testImencodeStringMatListOfByte() { MatOfByte buff = new MatOfByte(); assertEquals(0, buff.total()); - assertTrue( Highgui.imencode(".jpg", gray127, buff) ); + assertTrue( Imgcodecs.imencode(".jpg", gray127, buff) ); assertFalse(0 == buff.total()); } public void testImencodeStringMatListOfByteListOfInteger() { - MatOfInt params40 = new MatOfInt(Highgui.IMWRITE_JPEG_QUALITY, 40); - MatOfInt params90 = new MatOfInt(Highgui.IMWRITE_JPEG_QUALITY, 90); + MatOfInt params40 = new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 40); + MatOfInt params90 = new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 90); /* or MatOfInt params = new MatOfInt(); - params.fromArray(Highgui.IMWRITE_JPEG_QUALITY, 40); + params.fromArray(Imgcodecs.IMWRITE_JPEG_QUALITY, 40); */ MatOfByte buff40 = new MatOfByte(); MatOfByte buff90 = new MatOfByte(); - assertTrue( Highgui.imencode(".jpg", rgbLena, buff40, params40) ); - assertTrue( Highgui.imencode(".jpg", rgbLena, buff90, params90) ); + assertTrue( Imgcodecs.imencode(".jpg", rgbLena, buff40, params40) ); + assertTrue( Imgcodecs.imencode(".jpg", rgbLena, buff90, params90) ); assertTrue(buff40.total() > 0); assertTrue(buff40.total() < buff90.total()); } public void testImreadString() { - dst = Highgui.imread(OpenCVTestRunner.LENA_PATH); + dst = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH); assertTrue(!dst.empty()); assertEquals(3, dst.channels()); assertTrue(512 == dst.cols()); @@ -45,7 +45,7 @@ public class HighguiTest extends OpenCVTestCase { } public void testImreadStringInt() { - dst = Highgui.imread(OpenCVTestRunner.LENA_PATH, 0); + dst = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH, 0); assertTrue(!dst.empty()); assertEquals(1, dst.channels()); assertTrue(512 == dst.cols()); diff --git a/modules/java/android_test/src/org/opencv/test/highgui/VideoCaptureTest.java b/modules/java/android_test/src/org/opencv/test/highgui/VideoCaptureTest.java index ec7211a294..f4cccdf5ce 100644 --- a/modules/java/android_test/src/org/opencv/test/highgui/VideoCaptureTest.java +++ b/modules/java/android_test/src/org/opencv/test/highgui/VideoCaptureTest.java @@ -3,8 +3,8 @@ package org.opencv.test.highgui; import java.util.List; import org.opencv.core.Size; -import org.opencv.highgui.Highgui; -import org.opencv.highgui.VideoCapture; +import org.opencv.videoio.Videoio; +import org.opencv.videoio.VideoCapture; import org.opencv.test.OpenCVTestCase; @@ -26,8 +26,8 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testGet() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); - double frameWidth = capture.get(Highgui.CV_CAP_PROP_FRAME_WIDTH); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); + double frameWidth = capture.get(Videoio.CV_CAP_PROP_FRAME_WIDTH); assertTrue(0 != frameWidth); } finally { if (capture != null) capture.release(); @@ -36,7 +36,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testGetSupportedPreviewSizes() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); List sizes = capture.getSupportedPreviewSizes(); assertNotNull(sizes); assertFalse(sizes.isEmpty()); @@ -53,7 +53,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testGrabFromRealCamera() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); isSucceed = capture.grab(); assertTrue(isSucceed); } finally { @@ -68,7 +68,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testIsOpenedRealCamera() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); isOpened = capture.isOpened(); assertTrue(isOpened); } finally { @@ -79,7 +79,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testOpen() { try { capture = new VideoCapture(); - capture.open(Highgui.CV_CAP_ANDROID); + capture.open(Videoio.CV_CAP_ANDROID); isOpened = capture.isOpened(); assertTrue(isOpened); } finally { @@ -89,7 +89,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testRead() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); isSucceed = capture.read(dst); assertTrue(isSucceed); assertFalse(dst.empty()); @@ -101,7 +101,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testRelease() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); capture.release(); assertFalse(capture.isOpened()); capture = null; @@ -112,7 +112,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testRetrieveMat() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); capture.grab(); isSucceed = capture.retrieve(dst); assertTrue(isSucceed); @@ -125,9 +125,9 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testRetrieveMatInt() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); capture.grab(); - isSucceed = capture.retrieve(dst, Highgui.CV_CAP_ANDROID_GREY_FRAME); + isSucceed = capture.retrieve(dst, Videoio.CV_CAP_ANDROID_GREY_FRAME); assertTrue(isSucceed); assertFalse(dst.empty()); assertEquals(1, dst.channels()); @@ -138,10 +138,10 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testSet() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); - capture.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, 640); - capture.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, 480); - double frameWidth = capture.get(Highgui.CV_CAP_PROP_FRAME_WIDTH); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); + capture.set(Videoio.CV_CAP_PROP_FRAME_WIDTH, 640); + capture.set(Videoio.CV_CAP_PROP_FRAME_HEIGHT, 480); + double frameWidth = capture.get(Videoio.CV_CAP_PROP_FRAME_WIDTH); capture.read(dst); assertEquals(640.0, frameWidth); assertEquals(640, dst.cols()); @@ -158,7 +158,7 @@ public class VideoCaptureTest extends OpenCVTestCase { public void testVideoCaptureInt() { try { - capture = new VideoCapture(Highgui.CV_CAP_ANDROID); + capture = new VideoCapture(Videoio.CV_CAP_ANDROID); assertNotNull(capture); assertTrue(capture.isOpened()); } finally { diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py index cce2708284..0f317d5248 100755 --- a/modules/java/generator/gen_java.py +++ b/modules/java/generator/gen_java.py @@ -11,7 +11,7 @@ except: class_ignore_list = ( #core "FileNode", "FileStorage", "KDTree", "KeyPoint", "DMatch", - #highgui + #videoio "VideoWriter", ) @@ -536,13 +536,13 @@ JNIEXPORT jdoubleArray JNICALL Java_org_opencv_core_Core_n_1getTextSize """\n private static native String getSupportedPreviewSizes_0(long nativeObj);\n""", 'cpp_code' : """ -JNIEXPORT jstring JNICALL Java_org_opencv_highgui_VideoCapture_getSupportedPreviewSizes_10 +JNIEXPORT jstring JNICALL Java_org_opencv_videoio_VideoCapture_getSupportedPreviewSizes_10 (JNIEnv *env, jclass, jlong self); -JNIEXPORT jstring JNICALL Java_org_opencv_highgui_VideoCapture_getSupportedPreviewSizes_10 +JNIEXPORT jstring JNICALL Java_org_opencv_videoio_VideoCapture_getSupportedPreviewSizes_10 (JNIEnv *env, jclass, jlong self) { - static const char method_name[] = "highgui::VideoCapture_getSupportedPreviewSizes_10()"; + static const char method_name[] = "videoio::VideoCapture_getSupportedPreviewSizes_10()"; try { LOGD("%s", method_name); VideoCapture* me = (VideoCapture*) self; //TODO: check for NULL diff --git a/modules/java/generator/rst_parser.py b/modules/java/generator/rst_parser.py index 750d6f0be8..80b09ac40b 100755 --- a/modules/java/generator/rst_parser.py +++ b/modules/java/generator/rst_parser.py @@ -2,7 +2,7 @@ from __future__ import print_function import os, sys, re, string, fnmatch -allmodules = ["core", "flann", "imgproc", "ml", "highgui", "video", "features2d", "calib3d", "objdetect", "legacy", "contrib", "cuda", "androidcamera", "java", "python", "stitching", "ts", "photo", "nonfree", "videostab", "softcascade", "superres"] +allmodules = ["core", "flann", "imgproc", "ml", "imgcodecs", "videoio", "highgui", "video", "features2d", "calib3d", "objdetect", "legacy", "contrib", "cuda", "androidcamera", "java", "python", "stitching", "ts", "photo", "nonfree", "videostab", "softcascade", "superres"] verbose = False show_warnings = True show_errors = True diff --git a/modules/java/generator/src/java/android+CameraBridgeViewBase.java b/modules/java/generator/src/java/android+CameraBridgeViewBase.java index c0c9f5bde7..67a7489cfd 100644 --- a/modules/java/generator/src/java/android+CameraBridgeViewBase.java +++ b/modules/java/generator/src/java/android+CameraBridgeViewBase.java @@ -6,7 +6,7 @@ import org.opencv.R; import org.opencv.android.Utils; import org.opencv.core.Mat; import org.opencv.core.Size; -import org.opencv.highgui.Highgui; +import org.opencv.videoio.Videoio; import android.app.Activity; import android.app.AlertDialog; @@ -46,7 +46,7 @@ public abstract class CameraBridgeViewBase extends SurfaceView implements Surfac protected int mMaxHeight; protected int mMaxWidth; protected float mScale = 0; - protected int mPreviewFormat = Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA; + protected int mPreviewFormat = Videoio.CV_CAP_ANDROID_COLOR_FRAME_RGBA; protected int mCameraIndex = CAMERA_ID_ANY; protected boolean mEnabled; protected FpsMeter mFpsMeter = null; @@ -151,10 +151,10 @@ public abstract class CameraBridgeViewBase extends SurfaceView implements Surfac public Mat onCameraFrame(CvCameraViewFrame inputFrame) { Mat result = null; switch (mPreviewFormat) { - case Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA: + case Videoio.CV_CAP_ANDROID_COLOR_FRAME_RGBA: result = mOldStyleListener.onCameraFrame(inputFrame.rgba()); break; - case Highgui.CV_CAP_ANDROID_GREY_FRAME: + case Videoio.CV_CAP_ANDROID_GREY_FRAME: result = mOldStyleListener.onCameraFrame(inputFrame.gray()); break; default: @@ -168,7 +168,7 @@ public abstract class CameraBridgeViewBase extends SurfaceView implements Surfac mPreviewFormat = format; } - private int mPreviewFormat = Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA; + private int mPreviewFormat = Videoio.CV_CAP_ANDROID_COLOR_FRAME_RGBA; private CvCameraViewListener mOldStyleListener; }; diff --git a/modules/java/generator/src/java/android+NativeCameraView.java b/modules/java/generator/src/java/android+NativeCameraView.java index db146d8aed..47d6a27c23 100644 --- a/modules/java/generator/src/java/android+NativeCameraView.java +++ b/modules/java/generator/src/java/android+NativeCameraView.java @@ -2,8 +2,8 @@ package org.opencv.android; import org.opencv.core.Mat; import org.opencv.core.Size; -import org.opencv.highgui.Highgui; -import org.opencv.highgui.VideoCapture; +import org.opencv.videoio.Videoio; +import org.opencv.videoio.VideoCapture; import android.content.Context; import android.util.AttributeSet; @@ -88,9 +88,9 @@ public class NativeCameraView extends CameraBridgeViewBase { synchronized (this) { if (mCameraIndex == -1) - mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID); + mCamera = new VideoCapture(Videoio.CV_CAP_ANDROID); else - mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID + mCameraIndex); + mCamera = new VideoCapture(Videoio.CV_CAP_ANDROID + mCameraIndex); if (mCamera == null) return false; @@ -119,8 +119,8 @@ public class NativeCameraView extends CameraBridgeViewBase { AllocateCache(); - mCamera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, frameSize.width); - mCamera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, frameSize.height); + mCamera.set(Videoio.CV_CAP_PROP_FRAME_WIDTH, frameSize.width); + mCamera.set(Videoio.CV_CAP_PROP_FRAME_HEIGHT, frameSize.height); } Log.i(TAG, "Selected camera frame size = (" + mFrameWidth + ", " + mFrameHeight + ")"); @@ -139,13 +139,13 @@ public class NativeCameraView extends CameraBridgeViewBase { @Override public Mat rgba() { - mCapture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); + mCapture.retrieve(mRgba, Videoio.CV_CAP_ANDROID_COLOR_FRAME_RGBA); return mRgba; } @Override public Mat gray() { - mCapture.retrieve(mGray, Highgui.CV_CAP_ANDROID_GREY_FRAME); + mCapture.retrieve(mGray, Videoio.CV_CAP_ANDROID_GREY_FRAME); return mGray; } diff --git a/modules/java/generator/src/java/android+Utils.java b/modules/java/generator/src/java/android+Utils.java index 9c461087c4..404c986da8 100644 --- a/modules/java/generator/src/java/android+Utils.java +++ b/modules/java/generator/src/java/android+Utils.java @@ -6,7 +6,7 @@ import android.graphics.Bitmap; import org.opencv.core.CvException; import org.opencv.core.CvType; import org.opencv.core.Mat; -import org.opencv.highgui.Highgui; +import org.opencv.imgcodecs.Imgcodecs; import java.io.ByteArrayOutputStream; import java.io.File; @@ -67,7 +67,7 @@ public class Utils { encoded.put(0, 0, os.toByteArray()); os.close(); - Mat decoded = Highgui.imdecode(encoded, flags); + Mat decoded = Imgcodecs.imdecode(encoded, flags); encoded.release(); return decoded; diff --git a/modules/java/test/src/org/opencv/test/OpenCVTestCase.java b/modules/java/test/src/org/opencv/test/OpenCVTestCase.java index 496f96242b..864c17ef14 100644 --- a/modules/java/test/src/org/opencv/test/OpenCVTestCase.java +++ b/modules/java/test/src/org/opencv/test/OpenCVTestCase.java @@ -25,7 +25,7 @@ import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.core.DMatch; import org.opencv.core.KeyPoint; -import org.opencv.highgui.Highgui; +import org.opencv.imgcodecs.Imgcodecs; public class OpenCVTestCase extends TestCase { //change to 'true' to unblock fail on fail("Not yet implemented") @@ -164,8 +164,8 @@ public class OpenCVTestCase extends TestCase { rgba0 = new Mat(matSize, matSize, CvType.CV_8UC4, Scalar.all(0)); rgba128 = new Mat(matSize, matSize, CvType.CV_8UC4, Scalar.all(128)); - rgbLena = Highgui.imread(OpenCVTestRunner.LENA_PATH); - grayChess = Highgui.imread(OpenCVTestRunner.CHESS_PATH, 0); + rgbLena = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH); + grayChess = Imgcodecs.imread(OpenCVTestRunner.CHESS_PATH, 0); v1 = new Mat(1, 3, CvType.CV_32F); v1.put(0, 0, 1.0, 3.0, 2.0); diff --git a/modules/matlab/CMakeLists.txt b/modules/matlab/CMakeLists.txt index 095db36499..45e3197c43 100644 --- a/modules/matlab/CMakeLists.txt +++ b/modules/matlab/CMakeLists.txt @@ -85,7 +85,8 @@ endif() set(the_description "The Matlab/Octave bindings") ocv_add_module(matlab BINDINGS OPTIONAL opencv_core - opencv_imgproc opencv_ml opencv_highgui + opencv_imgproc opencv_ml + opencv_imgcodecs opencv_videoio opencv_highgui opencv_objdetect opencv_flann opencv_features2d opencv_photo opencv_video opencv_videostab opencv_calib opencv_calib3d diff --git a/modules/nonfree/perf/perf_precomp.hpp b/modules/nonfree/perf/perf_precomp.hpp index 45478eb8a3..ed84be8e37 100644 --- a/modules/nonfree/perf/perf_precomp.hpp +++ b/modules/nonfree/perf/perf_precomp.hpp @@ -13,7 +13,7 @@ #include "opencv2/ts.hpp" #include "opencv2/nonfree.hpp" -#include "opencv2/highgui.hpp" +#include "opencv2/imgcodecs.hpp" #include "opencv2/opencv_modules.hpp" diff --git a/modules/nonfree/src/surf.cuda.cpp b/modules/nonfree/src/surf.cuda.cpp index 4089b506b3..461ba0f7ef 100644 --- a/modules/nonfree/src/surf.cuda.cpp +++ b/modules/nonfree/src/surf.cuda.cpp @@ -93,6 +93,8 @@ using namespace ::cv::cuda::device::surf; namespace { + Mutex mtx; + int calcSize(int octave, int layer) { /* Wavelet size at first layer of first octave. */ @@ -166,7 +168,6 @@ namespace { const int layer_rows = img_rows >> octave; const int layer_cols = img_cols >> octave; - loadOctaveConstants(octave, layer_rows, layer_cols); icvCalcLayerDetAndTrace_gpu(surf_.det, surf_.trace, img_rows, img_cols, octave, surf_.nOctaveLayers); @@ -354,6 +355,7 @@ void cv::cuda::SURF_CUDA::downloadDescriptors(const GpuMat& descriptorsGPU, std: void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints) { + AutoLock lock(mtx); if (!img.empty()) { SURF_CUDA_Invoker surf(*this, img, mask); @@ -365,6 +367,7 @@ void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, GpuM void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints, GpuMat& descriptors, bool useProvidedKeypoints) { + AutoLock lock(mtx); if (!img.empty()) { SURF_CUDA_Invoker surf(*this, img, mask); @@ -382,6 +385,7 @@ void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, GpuM void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, std::vector& keypoints) { + AutoLock lock(mtx); GpuMat keypointsGPU; (*this)(img, mask, keypointsGPU); @@ -392,6 +396,7 @@ void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, std: void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, std::vector& keypoints, GpuMat& descriptors, bool useProvidedKeypoints) { + AutoLock lock(mtx); GpuMat keypointsGPU; if (useProvidedKeypoints) @@ -405,6 +410,7 @@ void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, std: void cv::cuda::SURF_CUDA::operator()(const GpuMat& img, const GpuMat& mask, std::vector& keypoints, std::vector& descriptors, bool useProvidedKeypoints) { + AutoLock lock(mtx); GpuMat descriptorsGPU; (*this)(img, mask, keypoints, descriptorsGPU, useProvidedKeypoints); diff --git a/modules/objdetect/doc/erfilter.rst b/modules/objdetect/doc/erfilter.rst deleted file mode 100644 index 85d6bcc7fe..0000000000 --- a/modules/objdetect/doc/erfilter.rst +++ /dev/null @@ -1,211 +0,0 @@ -Scene Text Detection -==================== - -.. highlight:: cpp - -Class-specific Extremal Regions for Scene Text Detection --------------------------------------------------------- - -The scene text detection algorithm described below has been initially proposed by Lukás Neumann & Jiri Matas [Neumann12]. The main idea behind Class-specific Extremal Regions is similar to the MSER in that suitable Extremal Regions (ERs) are selected from the whole component tree of the image. However, this technique differs from MSER in that selection of suitable ERs is done by a sequential classifier trained for character detection, i.e. dropping the stability requirement of MSERs and selecting class-specific (not necessarily stable) regions. - -The component tree of an image is constructed by thresholding by an increasing value step-by-step from 0 to 255 and then linking the obtained connected components from successive levels in a hierarchy by their inclusion relation: - -.. image:: pics/component_tree.png - :width: 100% - -The component tree may conatain a huge number of regions even for a very simple image as shown in the previous image. This number can easily reach the order of 1 x 10^6 regions for an average 1 Megapixel image. In order to efficiently select suitable regions among all the ERs the algorithm make use of a sequential classifier with two differentiated stages. - -In the first stage incrementally computable descriptors (area, perimeter, bounding box, and euler number) are computed (in O(1)) for each region r and used as features for a classifier which estimates the class-conditional probability p(r|character). Only the ERs which correspond to local maximum of the probability p(r|character) are selected (if their probability is above a global limit p_min and the difference between local maximum and local minimum is greater than a \delta_min value). - -In the second stage, the ERs that passed the first stage are classified into character and non-character classes using more informative but also more computationally expensive features. (Hole area ratio, convex hull ratio, and the number of outer boundary inflexion points). - -This ER filtering process is done in different single-channel projections of the input image in order to increase the character localization recall. - -After the ER filtering is done on each input channel, character candidates must be grouped in high-level text blocks (i.e. words, text lines, paragraphs, ...). The grouping algorithm used in this implementation has been proposed by Lluis Gomez and Dimosthenis Karatzas in [Gomez13] and basically consist in finding meaningful groups of regions using a perceptual organization based clustering analisys (see :ocv:func:`erGrouping`). - - -To see the text detector at work, have a look at the textdetection demo: https://github.com/Itseez/opencv/blob/master/samples/cpp/textdetection.cpp - - -.. [Neumann12] Neumann L., Matas J.: Real-Time Scene Text Localization and Recognition, CVPR 2012. The paper is available online at http://cmp.felk.cvut.cz/~neumalu1/neumann-cvpr2012.pdf - -.. [Gomez13] Gomez L. and Karatzas D.: Multi-script Text Extraction from Natural Scenes, ICDAR 2013. The paper is available online at http://158.109.8.37/files/GoK2013.pdf - - -ERStat ------- -.. ocv:struct:: ERStat - -The ERStat structure represents a class-specific Extremal Region (ER). - -An ER is a 4-connected set of pixels with all its grey-level values smaller than the values in its outer boundary. A class-specific ER is selected (using a classifier) from all the ER's in the component tree of the image. :: - - struct CV_EXPORTS ERStat - { - public: - //! Constructor - explicit ERStat(int level = 256, int pixel = 0, int x = 0, int y = 0); - //! Destructor - ~ERStat() { } - - //! seed point and threshold (max grey-level value) - int pixel; - int level; - - //! incrementally computable features - int area; - int perimeter; - int euler; //!< euler number - Rect rect; //!< bounding box - double raw_moments[2]; //!< order 1 raw moments to derive the centroid - double central_moments[3]; //!< order 2 central moments to construct the covariance matrix - std::deque *crossings;//!< horizontal crossings - float med_crossings; //!< median of the crossings at three different height levels - - //! 2nd stage features - float hole_area_ratio; - float convex_hull_ratio; - float num_inflexion_points; - - //! probability that the ER belongs to the class we are looking for - double probability; - - //! pointers preserving the tree structure of the component tree - ERStat* parent; - ERStat* child; - ERStat* next; - ERStat* prev; - }; - -computeNMChannels ------------------ -Compute the different channels to be processed independently in the N&M algorithm [Neumann12]. - -.. ocv:function:: void computeNMChannels(InputArray _src, OutputArrayOfArrays _channels, int _mode = ERFILTER_NM_RGBLGrad) - - :param _src: Source image. Must be RGB ``CV_8UC3``. - :param _channels: Output vector where computed channels are stored. - :param _mode: Mode of operation. Currently the only available options are: **ERFILTER_NM_RGBLGrad** (used by default) and **ERFILTER_NM_IHSGrad**. - -In N&M algorithm, the combination of intensity (I), hue (H), saturation (S), and gradient magnitude channels (Grad) are used in order to obtain high localization recall. This implementation also provides an alternative combination of red (R), green (G), blue (B), lightness (L), and gradient magnitude (Grad). - - -ERFilter --------- -.. ocv:class:: ERFilter : public Algorithm - -Base class for 1st and 2nd stages of Neumann and Matas scene text detection algorithm [Neumann12]. :: - - class CV_EXPORTS ERFilter : public Algorithm - { - public: - - //! callback with the classifier is made a class. - //! By doing it we hide SVM, Boost etc. Developers can provide their own classifiers - class CV_EXPORTS Callback - { - public: - virtual ~Callback() { } - //! The classifier must return probability measure for the region. - virtual double eval(const ERStat& stat) = 0; - }; - - /*! - the key method. Takes image on input and returns the selected regions in a vector of ERStat - only distinctive ERs which correspond to characters are selected by a sequential classifier - */ - virtual void run( InputArray image, std::vector& regions ) = 0; - - (...) - - }; - - - -ERFilter::Callback ------------------- -Callback with the classifier is made a class. By doing it we hide SVM, Boost etc. Developers can provide their own classifiers to the ERFilter algorithm. - -.. ocv:class:: ERFilter::Callback - -ERFilter::Callback::eval ------------------------- -The classifier must return probability measure for the region. - -.. ocv:function:: double ERFilter::Callback::eval(const ERStat& stat) - - :param stat: The region to be classified - -ERFilter::run -------------- -The key method of ERFilter algorithm. Takes image on input and returns the selected regions in a vector of ERStat only distinctive ERs which correspond to characters are selected by a sequential classifier - -.. ocv:function:: void ERFilter::run( InputArray image, std::vector& regions ) - - :param image: Sinle channel image ``CV_8UC1`` - :param regions: Output for the 1st stage and Input/Output for the 2nd. The selected Extremal Regions are stored here. - -Extracts the component tree (if needed) and filter the extremal regions (ER's) by using a given classifier. - -createERFilterNM1 ------------------ -Create an Extremal Region Filter for the 1st stage classifier of N&M algorithm [Neumann12]. - -.. ocv:function:: Ptr createERFilterNM1( const Ptr& cb, int thresholdDelta = 1, float minArea = 0.00025, float maxArea = 0.13, float minProbability = 0.4, bool nonMaxSuppression = true, float minProbabilityDiff = 0.1 ) - - :param cb: Callback with the classifier. Default classifier can be implicitly load with function :ocv:func:`loadClassifierNM1`, e.g. from file in samples/cpp/trained_classifierNM1.xml - :param thresholdDelta: Threshold step in subsequent thresholds when extracting the component tree - :param minArea: The minimum area (% of image size) allowed for retreived ER's - :param minArea: The maximum area (% of image size) allowed for retreived ER's - :param minProbability: The minimum probability P(er|character) allowed for retreived ER's - :param nonMaxSuppression: Whenever non-maximum suppression is done over the branch probabilities - :param minProbability: The minimum probability difference between local maxima and local minima ERs - -The component tree of the image is extracted by a threshold increased step by step from 0 to 255, incrementally computable descriptors (aspect_ratio, compactness, number of holes, and number of horizontal crossings) are computed for each ER and used as features for a classifier which estimates the class-conditional probability P(er|character). The value of P(er|character) is tracked using the inclusion relation of ER across all thresholds and only the ERs which correspond to local maximum of the probability P(er|character) are selected (if the local maximum of the probability is above a global limit pmin and the difference between local maximum and local minimum is greater than minProbabilityDiff). - -createERFilterNM2 ------------------ -Create an Extremal Region Filter for the 2nd stage classifier of N&M algorithm [Neumann12]. - -.. ocv:function:: Ptr createERFilterNM2( const Ptr& cb, float minProbability = 0.3 ) - - :param cb: Callback with the classifier. Default classifier can be implicitly load with function :ocv:func:`loadClassifierNM2`, e.g. from file in samples/cpp/trained_classifierNM2.xml - :param minProbability: The minimum probability P(er|character) allowed for retreived ER's - -In the second stage, the ERs that passed the first stage are classified into character and non-character classes using more informative but also more computationally expensive features. The classifier uses all the features calculated in the first stage and the following additional features: hole area ratio, convex hull ratio, and number of outer inflexion points. - -loadClassifierNM1 ------------------ -Allow to implicitly load the default classifier when creating an ERFilter object. - -.. ocv:function:: Ptr loadClassifierNM1(const std::string& filename) - - :param filename: The XML or YAML file with the classifier model (e.g. trained_classifierNM1.xml) - -returns a pointer to ERFilter::Callback. - -loadClassifierNM2 ------------------ -Allow to implicitly load the default classifier when creating an ERFilter object. - -.. ocv:function:: Ptr loadClassifierNM2(const std::string& filename) - - :param filename: The XML or YAML file with the classifier model (e.g. trained_classifierNM2.xml) - -returns a pointer to ERFilter::Callback. - -erGrouping ----------- -Find groups of Extremal Regions that are organized as text blocks. - -.. ocv:function:: void erGrouping( InputArrayOfArrays src, std::vector > ®ions, const std::string& filename, float minProbablity, std::vector &groups) - - :param src: Vector of sinle channel images CV_8UC1 from wich the regions were extracted - :param regions: Vector of ER's retreived from the ERFilter algorithm from each channel - :param filename: The XML or YAML file with the classifier model (e.g. trained_classifier_erGrouping.xml) - :param minProbability: The minimum probability for accepting a group - :param groups: The output of the algorithm are stored in this parameter as list of rectangles. - -This function implements the grouping algorithm described in [Gomez13]. Notice that this implementation constrains the results to horizontally-aligned text and latin script (since ERFilter classifiers are trained only for latin script detection). - -The algorithm combines two different clustering techniques in a single parameter-free procedure to detect groups of regions organized as text. The maximally meaningful groups are fist detected in several feature spaces, where each feature space is a combination of proximity information (x,y coordinates) and a similarity measure (intensity, color, size, gradient magnitude, etc.), thus providing a set of hypotheses of text groups. Evidence Accumulation framework is used to combine all these hypotheses to get the final estimate. Each of the resulting groups are finally validated using a classifier in order to assess if they form a valid horizontally-aligned text block. diff --git a/modules/objdetect/doc/latent_svm.rst b/modules/objdetect/doc/latent_svm.rst deleted file mode 100644 index 4b4ff117fa..0000000000 --- a/modules/objdetect/doc/latent_svm.rst +++ /dev/null @@ -1,262 +0,0 @@ -Latent SVM -=============================================================== - -Discriminatively Trained Part Based Models for Object Detection ---------------------------------------------------------------- - -The object detector described below has been initially proposed by -P.F. Felzenszwalb in [Felzenszwalb2010]_. It is based on a -Dalal-Triggs detector that uses a single filter on histogram of -oriented gradients (HOG) features to represent an object category. -This detector uses a sliding window approach, where a filter is -applied at all positions and scales of an image. The first -innovation is enriching the Dalal-Triggs model using a -star-structured part-based model defined by a "root" filter -(analogous to the Dalal-Triggs filter) plus a set of parts filters -and associated deformation models. The score of one of star models -at a particular position and scale within an image is the score of -the root filter at the given location plus the sum over parts of the -maximum, over placements of that part, of the part filter score on -its location minus a deformation cost easuring the deviation of the -part from its ideal location relative to the root. Both root and -part filter scores are defined by the dot product between a filter -(a set of weights) and a subwindow of a feature pyramid computed -from the input image. Another improvement is a representation of the -class of models by a mixture of star models. The score of a mixture -model at a particular position and scale is the maximum over -components, of the score of that component model at the given -location. - -In OpenCV there are C implementation of Latent SVM and C++ wrapper of it. -C version is the structure :ocv:struct:`CvObjectDetection` and a set of functions -working with this structure (see :ocv:func:`cvLoadLatentSvmDetector`, -:ocv:func:`cvReleaseLatentSvmDetector`, :ocv:func:`cvLatentSvmDetectObjects`). -C++ version is the class :ocv:class:`LatentSvmDetector` and has slightly different -functionality in contrast with C version - it supports loading and detection -of several models. - -There are two examples of Latent SVM usage: ``samples/c/latentsvmdetect.cpp`` -and ``samples/cpp/latentsvm_multidetect.cpp``. - -.. highlight:: c - - -CvLSVMFilterPosition --------------------- -.. ocv:struct:: CvLSVMFilterPosition - - Structure describes the position of the filter in the feature pyramid. - - .. ocv:member:: unsigned int l - - level in the feature pyramid - - .. ocv:member:: unsigned int x - - x-coordinate in level l - - .. ocv:member:: unsigned int y - - y-coordinate in level l - - -CvLSVMFilterObject ------------------- -.. ocv:struct:: CvLSVMFilterObject - - Description of the filter, which corresponds to the part of the object. - - .. ocv:member:: CvLSVMFilterPosition V - - ideal (penalty = 0) position of the partial filter - from the root filter position (V_i in the paper) - - .. ocv:member:: float fineFunction[4] - - vector describes penalty function (d_i in the paper) - pf[0] * x + pf[1] * y + pf[2] * x^2 + pf[3] * y^2 - - .. ocv:member:: int sizeX - .. ocv:member:: int sizeY - - Rectangular map (sizeX x sizeY), - every cell stores feature vector (dimension = p) - - .. ocv:member:: int numFeatures - - number of features - - .. ocv:member:: float *H - - matrix of feature vectors to set and get - feature vectors (i,j) used formula H[(j * sizeX + i) * p + k], - where k - component of feature vector in cell (i, j) - -CvLatentSvmDetector -------------------- -.. ocv:struct:: CvLatentSvmDetector - - Structure contains internal representation of trained Latent SVM detector. - - .. ocv:member:: int num_filters - - total number of filters (root plus part) in model - - .. ocv:member:: int num_components - - number of components in model - - .. ocv:member:: int* num_part_filters - - array containing number of part filters for each component - - .. ocv:member:: CvLSVMFilterObject** filters - - root and part filters for all model components - - .. ocv:member:: float* b - - biases for all model components - - .. ocv:member:: float score_threshold - - confidence level threshold - - -CvObjectDetection ------------------ -.. ocv:struct:: CvObjectDetection - - Structure contains the bounding box and confidence level for detected object. - - .. ocv:member:: CvRect rect - - bounding box for a detected object - - .. ocv:member:: float score - - confidence level - - -cvLoadLatentSvmDetector ------------------------ -Loads trained detector from a file. - -.. ocv:function:: CvLatentSvmDetector* cvLoadLatentSvmDetector(const char* filename) - - :param filename: Name of the file containing the description of a trained detector - - -cvReleaseLatentSvmDetector --------------------------- -Release memory allocated for CvLatentSvmDetector structure. - -.. ocv:function:: void cvReleaseLatentSvmDetector(CvLatentSvmDetector** detector) - - :param detector: CvLatentSvmDetector structure to be released - - -cvLatentSvmDetectObjects ------------------------- -Find rectangular regions in the given image that are likely to contain objects -and corresponding confidence levels. - -.. ocv:function:: CvSeq* cvLatentSvmDetectObjects( IplImage* image, CvLatentSvmDetector* detector, CvMemStorage* storage, float overlap_threshold=0.5f, int numThreads=-1 ) - - :param image: image - :param detector: LatentSVM detector in internal representation - :param storage: Memory storage to store the resultant sequence of the object candidate rectangles - :param overlap_threshold: Threshold for the non-maximum suppression algorithm - :param numThreads: Number of threads used in parallel version of the algorithm - -.. highlight:: cpp - -LatentSvmDetector ------------------ -.. ocv:class:: LatentSvmDetector - -This is a C++ wrapping class of Latent SVM. It contains internal representation of several -trained Latent SVM detectors (models) and a set of methods to load the detectors and detect objects -using them. - -LatentSvmDetector::ObjectDetection ----------------------------------- -.. ocv:struct:: LatentSvmDetector::ObjectDetection - - Structure contains the detection information. - - .. ocv:member:: Rect rect - - bounding box for a detected object - - .. ocv:member:: float score - - confidence level - - .. ocv:member:: int classID - - class (model or detector) ID that detect an object - - -LatentSvmDetector::LatentSvmDetector ------------------------------------- -Two types of constructors. - -.. ocv:function:: LatentSvmDetector::LatentSvmDetector() - -.. ocv:function:: LatentSvmDetector::LatentSvmDetector(const vector& filenames, const vector& classNames=vector()) - - - - :param filenames: A set of filenames storing the trained detectors (models). Each file contains one model. See examples of such files here /opencv_extra/testdata/cv/latentsvmdetector/models_VOC2007/. - - :param classNames: A set of trained models names. If it's empty then the name of each model will be constructed from the name of file containing the model. E.g. the model stored in "/home/user/cat.xml" will get the name "cat". - -LatentSvmDetector::~LatentSvmDetector -------------------------------------- -Destructor. - -.. ocv:function:: LatentSvmDetector::~LatentSvmDetector() - -LatentSvmDetector::~clear -------------------------- -Clear all trained models and their names stored in an class object. - -.. ocv:function:: void LatentSvmDetector::clear() - -LatentSvmDetector::load ------------------------ -Load the trained models from given ``.xml`` files and return ``true`` if at least one model was loaded. - -.. ocv:function:: bool LatentSvmDetector::load( const vector& filenames, const vector& classNames=vector() ) - - :param filenames: A set of filenames storing the trained detectors (models). Each file contains one model. See examples of such files here /opencv_extra/testdata/cv/latentsvmdetector/models_VOC2007/. - - :param classNames: A set of trained models names. If it's empty then the name of each model will be constructed from the name of file containing the model. E.g. the model stored in "/home/user/cat.xml" will get the name "cat". - -LatentSvmDetector::detect -------------------------- -Find rectangular regions in the given image that are likely to contain objects of loaded classes (models) -and corresponding confidence levels. - -.. ocv:function:: void LatentSvmDetector::detect( const Mat& image, vector& objectDetections, float overlapThreshold=0.5f, int numThreads=-1 ) - - :param image: An image. - :param objectDetections: The detections: rectangulars, scores and class IDs. - :param overlapThreshold: Threshold for the non-maximum suppression algorithm. - :param numThreads: Number of threads used in parallel version of the algorithm. - -LatentSvmDetector::getClassNames --------------------------------- -Return the class (model) names that were passed in constructor or method ``load`` or extracted from models filenames in those methods. - -.. ocv:function:: const vector& LatentSvmDetector::getClassNames() const - -LatentSvmDetector::getClassCount --------------------------------- -Return a count of loaded models (classes). - -.. ocv:function:: size_t LatentSvmDetector::getClassCount() const - - -.. [Felzenszwalb2010] Felzenszwalb, P. F. and Girshick, R. B. and McAllester, D. and Ramanan, D. *Object Detection with Discriminatively Trained Part Based Models*. PAMI, vol. 32, no. 9, pp. 1627-1645, September 2010 diff --git a/modules/objdetect/doc/objdetect.rst b/modules/objdetect/doc/objdetect.rst index 0cd8cf3ef9..c00e64e59d 100644 --- a/modules/objdetect/doc/objdetect.rst +++ b/modules/objdetect/doc/objdetect.rst @@ -8,5 +8,3 @@ objdetect. Object Detection :maxdepth: 2 cascade_classification - latent_svm - erfilter diff --git a/modules/objdetect/doc/pics/component_tree.png b/modules/objdetect/doc/pics/component_tree.png deleted file mode 100644 index 7391e2de62..0000000000 Binary files a/modules/objdetect/doc/pics/component_tree.png and /dev/null differ diff --git a/modules/objdetect/include/opencv2/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect.hpp index 79e213ee39..4ccb810703 100644 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@ -315,8 +315,6 @@ public: } -#include "opencv2/objdetect/linemod.hpp" -#include "opencv2/objdetect/erfilter.hpp" #include "opencv2/objdetect/detection_based_tracker.hpp" #endif diff --git a/modules/objdetect/include/opencv2/objdetect/erfilter.hpp b/modules/objdetect/include/opencv2/objdetect/erfilter.hpp deleted file mode 100644 index d7e07d80d8..0000000000 --- a/modules/objdetect/include/opencv2/objdetect/erfilter.hpp +++ /dev/null @@ -1,266 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2009, Willow Garage Inc., all rights reserved. -// Copyright (C) 2013, OpenCV Foundation, all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "as is" and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#ifndef __OPENCV_OBJDETECT_ERFILTER_HPP__ -#define __OPENCV_OBJDETECT_ERFILTER_HPP__ - -#include "opencv2/core.hpp" -#include -#include -#include - -namespace cv -{ - -/*! - Extremal Region Stat structure - - The ERStat structure represents a class-specific Extremal Region (ER). - - An ER is a 4-connected set of pixels with all its grey-level values smaller than the values - in its outer boundary. A class-specific ER is selected (using a classifier) from all the ER's - in the component tree of the image. -*/ -struct CV_EXPORTS ERStat -{ -public: - //! Constructor - explicit ERStat(int level = 256, int pixel = 0, int x = 0, int y = 0); - //! Destructor - ~ERStat() { } - - //! seed point and the threshold (max grey-level value) - int pixel; - int level; - - //! incrementally computable features - int area; - int perimeter; - int euler; //!< euler number - Rect rect; - double raw_moments[2]; //!< order 1 raw moments to derive the centroid - double central_moments[3]; //!< order 2 central moments to construct the covariance matrix - std::deque *crossings;//!< horizontal crossings - float med_crossings; //!< median of the crossings at three different height levels - - //! 2nd stage features - float hole_area_ratio; - float convex_hull_ratio; - float num_inflexion_points; - - // TODO Other features can be added (average color, standard deviation, and such) - - - // TODO shall we include the pixel list whenever available (i.e. after 2nd stage) ? - std::vector *pixels; - - //! probability that the ER belongs to the class we are looking for - double probability; - - //! pointers preserving the tree structure of the component tree - ERStat* parent; - ERStat* child; - ERStat* next; - ERStat* prev; - - //! wenever the regions is a local maxima of the probability - bool local_maxima; - ERStat* max_probability_ancestor; - ERStat* min_probability_ancestor; -}; - -/*! - Base class for 1st and 2nd stages of Neumann and Matas scene text detection algorithms - Neumann L., Matas J.: Real-Time Scene Text Localization and Recognition, CVPR 2012 - - Extracts the component tree (if needed) and filter the extremal regions (ER's) by using a given classifier. -*/ -class CV_EXPORTS ERFilter : public Algorithm -{ -public: - - //! callback with the classifier is made a class. By doing it we hide SVM, Boost etc. - class CV_EXPORTS Callback - { - public: - virtual ~Callback() { } - //! The classifier must return probability measure for the region. - virtual double eval(const ERStat& stat) = 0; //const = 0; //TODO why cannot use const = 0 here? - }; - - /*! - the key method. Takes image on input and returns the selected regions in a vector of ERStat - only distinctive ERs which correspond to characters are selected by a sequential classifier - \param image is the input image - \param regions is output for the first stage, input/output for the second one. - */ - virtual void run( InputArray image, std::vector& regions ) = 0; - - - //! set/get methods to set the algorithm properties, - virtual void setCallback(const Ptr& cb) = 0; - virtual void setThresholdDelta(int thresholdDelta) = 0; - virtual void setMinArea(float minArea) = 0; - virtual void setMaxArea(float maxArea) = 0; - virtual void setMinProbability(float minProbability) = 0; - virtual void setMinProbabilityDiff(float minProbabilityDiff) = 0; - virtual void setNonMaxSuppression(bool nonMaxSuppression) = 0; - virtual int getNumRejected() = 0; -}; - - -/*! - Create an Extremal Region Filter for the 1st stage classifier of N&M algorithm - Neumann L., Matas J.: Real-Time Scene Text Localization and Recognition, CVPR 2012 - - The component tree of the image is extracted by a threshold increased step by step - from 0 to 255, incrementally computable descriptors (aspect_ratio, compactness, - number of holes, and number of horizontal crossings) are computed for each ER - and used as features for a classifier which estimates the class-conditional - probability P(er|character). The value of P(er|character) is tracked using the inclusion - relation of ER across all thresholds and only the ERs which correspond to local maximum - of the probability P(er|character) are selected (if the local maximum of the - probability is above a global limit pmin and the difference between local maximum and - local minimum is greater than minProbabilityDiff). - - \param cb Callback with the classifier. - default classifier can be implicitly load with function loadClassifierNM1() - from file in samples/cpp/trained_classifierNM1.xml - \param thresholdDelta Threshold step in subsequent thresholds when extracting the component tree - \param minArea The minimum area (% of image size) allowed for retreived ER's - \param minArea The maximum area (% of image size) allowed for retreived ER's - \param minProbability The minimum probability P(er|character) allowed for retreived ER's - \param nonMaxSuppression Whenever non-maximum suppression is done over the branch probabilities - \param minProbability The minimum probability difference between local maxima and local minima ERs -*/ -CV_EXPORTS Ptr createERFilterNM1(const Ptr& cb, - int thresholdDelta = 1, float minArea = 0.00025, - float maxArea = 0.13, float minProbability = 0.4, - bool nonMaxSuppression = true, - float minProbabilityDiff = 0.1); - -/*! - Create an Extremal Region Filter for the 2nd stage classifier of N&M algorithm - Neumann L., Matas J.: Real-Time Scene Text Localization and Recognition, CVPR 2012 - - In the second stage, the ERs that passed the first stage are classified into character - and non-character classes using more informative but also more computationally expensive - features. The classifier uses all the features calculated in the first stage and the following - additional features: hole area ratio, convex hull ratio, and number of outer inflexion points. - - \param cb Callback with the classifier - default classifier can be implicitly load with function loadClassifierNM2() - from file in samples/cpp/trained_classifierNM2.xml - \param minProbability The minimum probability P(er|character) allowed for retreived ER's -*/ -CV_EXPORTS Ptr createERFilterNM2(const Ptr& cb, - float minProbability = 0.3); - - -/*! - Allow to implicitly load the default classifier when creating an ERFilter object. - The function takes as parameter the XML or YAML file with the classifier model - (e.g. trained_classifierNM1.xml) returns a pointer to ERFilter::Callback. -*/ - -CV_EXPORTS Ptr loadClassifierNM1(const std::string& filename); - -/*! - Allow to implicitly load the default classifier when creating an ERFilter object. - The function takes as parameter the XML or YAML file with the classifier model - (e.g. trained_classifierNM1.xml) returns a pointer to ERFilter::Callback. -*/ - -CV_EXPORTS Ptr loadClassifierNM2(const std::string& filename); - - -// computeNMChannels operation modes -enum { ERFILTER_NM_RGBLGrad = 0, - ERFILTER_NM_IHSGrad = 1 - }; - -/*! - Compute the different channels to be processed independently in the N&M algorithm - Neumann L., Matas J.: Real-Time Scene Text Localization and Recognition, CVPR 2012 - - In N&M algorithm, the combination of intensity (I), hue (H), saturation (S), and gradient - magnitude channels (Grad) are used in order to obtain high localization recall. - This implementation also provides an alternative combination of red (R), green (G), blue (B), - lightness (L), and gradient magnitude (Grad). - - \param _src Source image. Must be RGB CV_8UC3. - \param _channels Output vector where computed channels are stored. - \param _mode Mode of operation. Currently the only available options are - ERFILTER_NM_RGBLGrad (by default) and ERFILTER_NM_IHSGrad. - -*/ -CV_EXPORTS void computeNMChannels(InputArray _src, OutputArrayOfArrays _channels, int _mode = ERFILTER_NM_RGBLGrad); - - -/*! - Find groups of Extremal Regions that are organized as text blocks. This function implements - the grouping algorithm described in: - Gomez L. and Karatzas D.: Multi-script Text Extraction from Natural Scenes, ICDAR 2013. - Notice that this implementation constrains the results to horizontally-aligned text and - latin script (since ERFilter classifiers are trained only for latin script detection). - - The algorithm combines two different clustering techniques in a single parameter-free procedure - to detect groups of regions organized as text. The maximally meaningful groups are fist detected - in several feature spaces, where each feature space is a combination of proximity information - (x,y coordinates) and a similarity measure (intensity, color, size, gradient magnitude, etc.), - thus providing a set of hypotheses of text groups. Evidence Accumulation framework is used to - combine all these hypotheses to get the final estimate. Each of the resulting groups are finally - validated using a classifier in order to assest if they form a valid horizontally-aligned text block. - - \param src Vector of sinle channel images CV_8UC1 from wich the regions were extracted. - \param regions Vector of ER's retreived from the ERFilter algorithm from each channel - \param filename The XML or YAML file with the classifier model (e.g. trained_classifier_erGrouping.xml) - \param minProbability The minimum probability for accepting a group - \param groups The output of the algorithm are stored in this parameter as list of rectangles. -*/ -CV_EXPORTS void erGrouping(InputArrayOfArrays src, std::vector > ®ions, - const std::string& filename, float minProbablity, - std::vector &groups); - -} -#endif // _OPENCV_ERFILTER_HPP_ diff --git a/modules/objdetect/include/opencv2/objdetect/linemod.hpp b/modules/objdetect/include/opencv2/objdetect/linemod.hpp deleted file mode 100644 index 46d869926f..0000000000 --- a/modules/objdetect/include/opencv2/objdetect/linemod.hpp +++ /dev/null @@ -1,455 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2009, Willow Garage Inc., all rights reserved. -// Copyright (C) 2013, OpenCV Foundation, all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "as is" and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#ifndef __OPENCV_OBJDETECT_LINEMOD_HPP__ -#define __OPENCV_OBJDETECT_LINEMOD_HPP__ - -#include "opencv2/core.hpp" -#include - -/****************************************************************************************\ -* LINE-MOD * -\****************************************************************************************/ - -namespace cv { -namespace linemod { - -/// @todo Convert doxy comments to rst - -/** - * \brief Discriminant feature described by its location and label. - */ -struct CV_EXPORTS Feature -{ - int x; ///< x offset - int y; ///< y offset - int label; ///< Quantization - - Feature() : x(0), y(0), label(0) {} - Feature(int x, int y, int label); - - void read(const FileNode& fn); - void write(FileStorage& fs) const; -}; - -inline Feature::Feature(int _x, int _y, int _label) : x(_x), y(_y), label(_label) {} - -struct CV_EXPORTS Template -{ - int width; - int height; - int pyramid_level; - std::vector features; - - void read(const FileNode& fn); - void write(FileStorage& fs) const; -}; - -/** - * \brief Represents a modality operating over an image pyramid. - */ -class QuantizedPyramid -{ -public: - // Virtual destructor - virtual ~QuantizedPyramid() {} - - /** - * \brief Compute quantized image at current pyramid level for online detection. - * - * \param[out] dst The destination 8-bit image. For each pixel at most one bit is set, - * representing its classification. - */ - virtual void quantize(Mat& dst) const =0; - - /** - * \brief Extract most discriminant features at current pyramid level to form a new template. - * - * \param[out] templ The new template. - */ - virtual bool extractTemplate(Template& templ) const =0; - - /** - * \brief Go to the next pyramid level. - * - * \todo Allow pyramid scale factor other than 2 - */ - virtual void pyrDown() =0; - -protected: - /// Candidate feature with a score - struct Candidate - { - Candidate(int x, int y, int label, float score); - - /// Sort candidates with high score to the front - bool operator<(const Candidate& rhs) const - { - return score > rhs.score; - } - - Feature f; - float score; - }; - - /** - * \brief Choose candidate features so that they are not bunched together. - * - * \param[in] candidates Candidate features sorted by score. - * \param[out] features Destination vector of selected features. - * \param[in] num_features Number of candidates to select. - * \param[in] distance Hint for desired distance between features. - */ - static void selectScatteredFeatures(const std::vector& candidates, - std::vector& features, - size_t num_features, float distance); -}; - -inline QuantizedPyramid::Candidate::Candidate(int x, int y, int label, float _score) : f(x, y, label), score(_score) {} - -/** - * \brief Interface for modalities that plug into the LINE template matching representation. - * - * \todo Max response, to allow optimization of summing (255/MAX) features as uint8 - */ -class CV_EXPORTS Modality -{ -public: - // Virtual destructor - virtual ~Modality() {} - - /** - * \brief Form a quantized image pyramid from a source image. - * - * \param[in] src The source image. Type depends on the modality. - * \param[in] mask Optional mask. If not empty, unmasked pixels are set to zero - * in quantized image and cannot be extracted as features. - */ - Ptr process(const Mat& src, - const Mat& mask = Mat()) const - { - return processImpl(src, mask); - } - - virtual String name() const =0; - - virtual void read(const FileNode& fn) =0; - virtual void write(FileStorage& fs) const =0; - - /** - * \brief Create modality by name. - * - * The following modality types are supported: - * - "ColorGradient" - * - "DepthNormal" - */ - static Ptr create(const String& modality_type); - - /** - * \brief Load a modality from file. - */ - static Ptr create(const FileNode& fn); - -protected: - // Indirection is because process() has a default parameter. - virtual Ptr processImpl(const Mat& src, - const Mat& mask) const =0; -}; - -/** - * \brief Modality that computes quantized gradient orientations from a color image. - */ -class CV_EXPORTS ColorGradient : public Modality -{ -public: - /** - * \brief Default constructor. Uses reasonable default parameter values. - */ - ColorGradient(); - - /** - * \brief Constructor. - * - * \param weak_threshold When quantizing, discard gradients with magnitude less than this. - * \param num_features How many features a template must contain. - * \param strong_threshold Consider as candidate features only gradients whose norms are - * larger than this. - */ - ColorGradient(float weak_threshold, size_t num_features, float strong_threshold); - - virtual String name() const; - - virtual void read(const FileNode& fn); - virtual void write(FileStorage& fs) const; - - float weak_threshold; - size_t num_features; - float strong_threshold; - -protected: - virtual Ptr processImpl(const Mat& src, - const Mat& mask) const; -}; - -/** - * \brief Modality that computes quantized surface normals from a dense depth map. - */ -class CV_EXPORTS DepthNormal : public Modality -{ -public: - /** - * \brief Default constructor. Uses reasonable default parameter values. - */ - DepthNormal(); - - /** - * \brief Constructor. - * - * \param distance_threshold Ignore pixels beyond this distance. - * \param difference_threshold When computing normals, ignore contributions of pixels whose - * depth difference with the central pixel is above this threshold. - * \param num_features How many features a template must contain. - * \param extract_threshold Consider as candidate feature only if there are no differing - * orientations within a distance of extract_threshold. - */ - DepthNormal(int distance_threshold, int difference_threshold, size_t num_features, - int extract_threshold); - - virtual String name() const; - - virtual void read(const FileNode& fn); - virtual void write(FileStorage& fs) const; - - int distance_threshold; - int difference_threshold; - size_t num_features; - int extract_threshold; - -protected: - virtual Ptr processImpl(const Mat& src, - const Mat& mask) const; -}; - -/** - * \brief Debug function to colormap a quantized image for viewing. - */ -void colormap(const Mat& quantized, Mat& dst); - -/** - * \brief Represents a successful template match. - */ -struct CV_EXPORTS Match -{ - Match() - { - } - - Match(int x, int y, float similarity, const String& class_id, int template_id); - - /// Sort matches with high similarity to the front - bool operator<(const Match& rhs) const - { - // Secondarily sort on template_id for the sake of duplicate removal - if (similarity != rhs.similarity) - return similarity > rhs.similarity; - else - return template_id < rhs.template_id; - } - - bool operator==(const Match& rhs) const - { - return x == rhs.x && y == rhs.y && similarity == rhs.similarity && class_id == rhs.class_id; - } - - int x; - int y; - float similarity; - String class_id; - int template_id; -}; - -inline -Match::Match(int _x, int _y, float _similarity, const String& _class_id, int _template_id) - : x(_x), y(_y), similarity(_similarity), class_id(_class_id), template_id(_template_id) -{} - -/** - * \brief Object detector using the LINE template matching algorithm with any set of - * modalities. - */ -class CV_EXPORTS Detector -{ -public: - /** - * \brief Empty constructor, initialize with read(). - */ - Detector(); - - /** - * \brief Constructor. - * - * \param modalities Modalities to use (color gradients, depth normals, ...). - * \param T_pyramid Value of the sampling step T at each pyramid level. The - * number of pyramid levels is T_pyramid.size(). - */ - Detector(const std::vector< Ptr >& modalities, const std::vector& T_pyramid); - - /** - * \brief Detect objects by template matching. - * - * Matches globally at the lowest pyramid level, then refines locally stepping up the pyramid. - * - * \param sources Source images, one for each modality. - * \param threshold Similarity threshold, a percentage between 0 and 100. - * \param[out] matches Template matches, sorted by similarity score. - * \param class_ids If non-empty, only search for the desired object classes. - * \param[out] quantized_images Optionally return vector of quantized images. - * \param masks The masks for consideration during matching. The masks should be CV_8UC1 - * where 255 represents a valid pixel. If non-empty, the vector must be - * the same size as sources. Each element must be - * empty or the same size as its corresponding source. - */ - void match(const std::vector& sources, float threshold, std::vector& matches, - const std::vector& class_ids = std::vector(), - OutputArrayOfArrays quantized_images = noArray(), - const std::vector& masks = std::vector()) const; - - /** - * \brief Add new object template. - * - * \param sources Source images, one for each modality. - * \param class_id Object class ID. - * \param object_mask Mask separating object from background. - * \param[out] bounding_box Optionally return bounding box of the extracted features. - * - * \return Template ID, or -1 if failed to extract a valid template. - */ - int addTemplate(const std::vector& sources, const String& class_id, - const Mat& object_mask, Rect* bounding_box = NULL); - - /** - * \brief Add a new object template computed by external means. - */ - int addSyntheticTemplate(const std::vector