mirror of
https://github.com/opencv/opencv.git
synced 2026-07-25 21:33:04 +04:00
Compare commits
151 Commits
4.5.1-openvino
...
3.4.13
| Author | SHA1 | Date | |
|---|---|---|---|
| 8869dc7762 | |||
| e6c9291e32 | |||
| a6f14ca97c | |||
| c555a6747d | |||
| dd276dbb59 | |||
| 70d82017fe | |||
| 663bd73518 | |||
| c526705f4f | |||
| c84d2cb32a | |||
| ff211371bc | |||
| e9a4734a57 | |||
| 3359bdc464 | |||
| 17faee5d81 | |||
| 5cd852f9bd | |||
| 4c75b1c102 | |||
| ef27c11d50 | |||
| cd3939a153 | |||
| 9f5463ce42 | |||
| 3bc1b53962 | |||
| de1b919641 | |||
| 03e224ee83 | |||
| c2bc171ef6 | |||
| f8740e124c | |||
| 57da381ae3 | |||
| 935cb4076b | |||
| b2ea15da35 | |||
| 9733177083 | |||
| fa665141bb | |||
| 64720f15a2 | |||
| 8df0f13230 | |||
| 94e7be3714 | |||
| 9b4adc9acb | |||
| e27162397a | |||
| 99d750d597 | |||
| 8391a23600 | |||
| ec3ef520e6 | |||
| 28aab134db | |||
| 752cc26ad6 | |||
| d159417474 | |||
| 7b0d7d0c9a | |||
| 7631056b8a | |||
| 4107dc7355 | |||
| 085a131801 | |||
| 50fed1d774 | |||
| c240355cc6 | |||
| a9edcc1705 | |||
| 4b3d2c8834 | |||
| 48d9031efb | |||
| a8adb99e94 | |||
| b01ae2de05 | |||
| af71b03000 | |||
| 392991fa0b | |||
| 1bfc75ac23 | |||
| def679554f | |||
| 08b6abd711 | |||
| 33761ee06b | |||
| d2b8fd6401 | |||
| 4f236d1c50 | |||
| f9f9e2ad4a | |||
| 23c246882e | |||
| f290ff215e | |||
| 1f3255d76b | |||
| fdeac73a59 | |||
| 175cd03ff2 | |||
| d6a7f5e1e0 | |||
| 8ce08dedfe | |||
| 32377ce57d | |||
| d84a9484b7 | |||
| 55a2bcbe15 | |||
| f2422ace7d | |||
| 3e5d7e1718 | |||
| 8286d84fb1 | |||
| c42d0c8374 | |||
| 37bfb3c48d | |||
| b3937288e5 | |||
| 7eaa548b6d | |||
| d7e936de5c | |||
| d2bc0e5fe0 | |||
| d8107a5125 | |||
| a82c50eac2 | |||
| 7fa9efbfd8 | |||
| fe3893ff01 | |||
| e8348e5f64 | |||
| fb85974d01 | |||
| 3377ddaf09 | |||
| 962f5c9b82 | |||
| 2c634eeef2 | |||
| c6e60f06eb | |||
| e5d2642780 | |||
| a9f4f8ded4 | |||
| 26e8048a0a | |||
| 6bfb0dda85 | |||
| 8ae1552a5b | |||
| 00f36a3149 | |||
| 12a36b5a94 | |||
| e371592f75 | |||
| 84a3654371 | |||
| 7e5c4fe1cd | |||
| 773ccc4bf8 | |||
| b31ce408ae | |||
| 7983c484b2 | |||
| 6502737bd5 | |||
| b98dd728ca | |||
| 7f3ba5963d | |||
| 43e58de918 | |||
| e309ad8465 | |||
| 2fa624aef0 | |||
| e958600f32 | |||
| 484251c52b | |||
| 512be4ab65 | |||
| 6f8120cb3a | |||
| c42d47d94a | |||
| d35e2f5339 | |||
| 91ce6ef190 | |||
| aac30e772f | |||
| acc142d4ba | |||
| 24fac5f56d | |||
| 4e4458416d | |||
| da2978f607 | |||
| 7c78c59e64 | |||
| 0cc92bd67c | |||
| 2cf2456f4c | |||
| 8c5b3c4150 | |||
| 36d771affc | |||
| 387a76ba59 | |||
| f601e817fe | |||
| 3fd0d0dafb | |||
| f28895cd6b | |||
| 0800f6f91b | |||
| f4f462c50b | |||
| 359ecda4fc | |||
| bf0846f0ea | |||
| 0401d5920c | |||
| ac418e999d | |||
| ae42815b7d | |||
| 5a3a915a9b | |||
| 632a08ff40 | |||
| 1b3dd8f38b | |||
| ce31c9c448 | |||
| bc434e8f67 | |||
| e05c2e0f1d | |||
| 2255973b0f | |||
| ac24a72e66 | |||
| bb067c7ebf | |||
| 599bb9c457 | |||
| 12b8d542b7 | |||
| fef23768fe | |||
| 328883b6ea | |||
| 87ed750510 | |||
| cc7f17f011 | |||
| 3c25fd1ba5 |
Vendored
+5
@@ -153,6 +153,11 @@ set_target_properties(libprotobuf
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
# https://github.com/opencv/opencv/issues/17282
|
||||
target_link_libraries(libprotobuf INTERFACE "-landroid" "-llog")
|
||||
endif()
|
||||
|
||||
get_protobuf_version(Protobuf_VERSION "${PROTOBUF_ROOT}/src")
|
||||
set(Protobuf_VERSION ${Protobuf_VERSION} CACHE INTERNAL "" FORCE)
|
||||
|
||||
|
||||
+6
-1
@@ -32,6 +32,11 @@ endif()
|
||||
#
|
||||
# Configure CMake policies
|
||||
#
|
||||
|
||||
if(POLICY CMP0025)
|
||||
cmake_policy(SET CMP0025 NEW) # CMAKE_CXX_COMPILER_ID=AppleClang
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0026)
|
||||
cmake_policy(SET CMP0026 NEW)
|
||||
endif()
|
||||
@@ -481,7 +486,7 @@ OCV_OPTION(OPENCV_ENABLE_MEMORY_SANITIZER "Better support for memory/address san
|
||||
OCV_OPTION(ENABLE_OMIT_FRAME_POINTER "Enable -fomit-frame-pointer for GCC" ON IF CV_GCC )
|
||||
OCV_OPTION(ENABLE_POWERPC "Enable PowerPC for GCC" ON IF (CV_GCC AND CMAKE_SYSTEM_PROCESSOR MATCHES powerpc.*) )
|
||||
OCV_OPTION(ENABLE_FAST_MATH "Enable compiler options for fast math optimizations on FP computations (not recommended)" OFF)
|
||||
if(NOT IOS) # Use CPU_BASELINE instead
|
||||
if(NOT IOS AND CMAKE_CROSSCOMPILING) # Use CPU_BASELINE instead
|
||||
OCV_OPTION(ENABLE_NEON "Enable NEON instructions" (NEON OR ANDROID_ARM_NEON OR AARCH64) IF (CV_GCC OR CV_CLANG) AND (ARM OR AARCH64 OR IOS) )
|
||||
OCV_OPTION(ENABLE_VFPV3 "Enable VFPv3-D32 instructions" OFF IF (CV_GCC OR CV_CLANG) AND (ARM OR AARCH64 OR IOS) )
|
||||
endif()
|
||||
|
||||
@@ -120,7 +120,6 @@ if(CV_GCC OR CV_CLANG)
|
||||
add_extra_compiler_option(-Wshadow)
|
||||
add_extra_compiler_option(-Wsign-promo)
|
||||
add_extra_compiler_option(-Wuninitialized)
|
||||
add_extra_compiler_option(-Winit-self)
|
||||
if(CV_GCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0) AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0))
|
||||
add_extra_compiler_option(-Wno-psabi)
|
||||
endif()
|
||||
@@ -151,7 +150,7 @@ if(CV_GCC OR CV_CLANG)
|
||||
if(CV_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
add_extra_compiler_option(-Wno-missing-field-initializers) # GCC 4.x emits warnings about {}, fixed in GCC 5+
|
||||
endif()
|
||||
if(CV_CLANG AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0)
|
||||
if(CV_CLANG AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0)
|
||||
add_extra_compiler_option(-Wno-deprecated-enum-enum-conversion)
|
||||
add_extra_compiler_option(-Wno-deprecated-anon-enum-enum-conversion)
|
||||
endif()
|
||||
|
||||
@@ -135,9 +135,9 @@ endif()
|
||||
|
||||
if(INF_ENGINE_TARGET)
|
||||
if(NOT INF_ENGINE_RELEASE)
|
||||
message(WARNING "InferenceEngine version has not been set, 2021.1 will be used by default. Set INF_ENGINE_RELEASE variable if you experience build errors.")
|
||||
message(WARNING "InferenceEngine version has not been set, 2021.2 will be used by default. Set INF_ENGINE_RELEASE variable if you experience build errors.")
|
||||
endif()
|
||||
set(INF_ENGINE_RELEASE "2021010000" CACHE STRING "Force IE version, should be in form YYYYAABBCC (e.g. 2020.1.0.2 -> 2020010002)")
|
||||
set(INF_ENGINE_RELEASE "2021020000" CACHE STRING "Force IE version, should be in form YYYYAABBCC (e.g. 2020.1.0.2 -> 2020010002)")
|
||||
set_target_properties(${INF_ENGINE_TARGET} PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "HAVE_INF_ENGINE=1;INF_ENGINE_RELEASE=${INF_ENGINE_RELEASE}"
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
if(BUILD_ZLIB)
|
||||
ocv_clear_vars(ZLIB_FOUND)
|
||||
else()
|
||||
ocv_clear_internal_cache_vars(ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
|
||||
find_package(ZLIB "${MIN_VER_ZLIB}")
|
||||
if(ZLIB_FOUND AND ANDROID)
|
||||
if(ZLIB_LIBRARIES MATCHES "/usr/(lib|lib32|lib64)/libz.so$")
|
||||
@@ -15,11 +16,12 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT ZLIB_FOUND)
|
||||
ocv_clear_vars(ZLIB_LIBRARY ZLIB_LIBRARIES ZLIB_INCLUDE_DIRS)
|
||||
ocv_clear_vars(ZLIB_LIBRARY ZLIB_LIBRARIES ZLIB_INCLUDE_DIR)
|
||||
|
||||
set(ZLIB_LIBRARY zlib)
|
||||
set(ZLIB_LIBRARY zlib CACHE INTERNAL "")
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/zlib")
|
||||
set(ZLIB_INCLUDE_DIRS "${${ZLIB_LIBRARY}_SOURCE_DIR}" "${${ZLIB_LIBRARY}_BINARY_DIR}")
|
||||
set(ZLIB_INCLUDE_DIR "${${ZLIB_LIBRARY}_SOURCE_DIR}" "${${ZLIB_LIBRARY}_BINARY_DIR}" CACHE INTERNAL "")
|
||||
set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR})
|
||||
set(ZLIB_LIBRARIES ${ZLIB_LIBRARY})
|
||||
|
||||
ocv_parse_header2(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h" ZLIB_VERSION)
|
||||
@@ -30,23 +32,25 @@ if(WITH_JPEG)
|
||||
if(BUILD_JPEG)
|
||||
ocv_clear_vars(JPEG_FOUND)
|
||||
else()
|
||||
ocv_clear_internal_cache_vars(JPEG_LIBRARY JPEG_INCLUDE_DIR)
|
||||
include(FindJPEG)
|
||||
endif()
|
||||
|
||||
if(NOT JPEG_FOUND)
|
||||
ocv_clear_vars(JPEG_LIBRARY JPEG_LIBRARIES JPEG_INCLUDE_DIR)
|
||||
ocv_clear_vars(JPEG_LIBRARY JPEG_INCLUDE_DIR)
|
||||
|
||||
if(NOT BUILD_JPEG_TURBO_DISABLE)
|
||||
set(JPEG_LIBRARY libjpeg-turbo)
|
||||
set(JPEG_LIBRARY libjpeg-turbo CACHE INTERNAL "")
|
||||
set(JPEG_LIBRARIES ${JPEG_LIBRARY})
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libjpeg-turbo")
|
||||
set(JPEG_INCLUDE_DIR "${${JPEG_LIBRARY}_SOURCE_DIR}/src")
|
||||
set(JPEG_INCLUDE_DIR "${${JPEG_LIBRARY}_SOURCE_DIR}/src" CACHE INTERNAL "")
|
||||
else()
|
||||
set(JPEG_LIBRARY libjpeg)
|
||||
set(JPEG_LIBRARY libjpeg CACHE INTERNAL "")
|
||||
set(JPEG_LIBRARIES ${JPEG_LIBRARY})
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libjpeg")
|
||||
set(JPEG_INCLUDE_DIR "${${JPEG_LIBRARY}_SOURCE_DIR}")
|
||||
set(JPEG_INCLUDE_DIR "${${JPEG_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "")
|
||||
endif()
|
||||
set(JPEG_INCLUDE_DIRS "${JPEG_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
macro(ocv_detect_jpeg_version header_file)
|
||||
@@ -74,6 +78,7 @@ if(WITH_TIFF)
|
||||
if(BUILD_TIFF)
|
||||
ocv_clear_vars(TIFF_FOUND)
|
||||
else()
|
||||
ocv_clear_internal_cache_vars(TIFF_LIBRARY TIFF_INCLUDE_DIR)
|
||||
include(FindTIFF)
|
||||
if(TIFF_FOUND)
|
||||
ocv_parse_header("${TIFF_INCLUDE_DIR}/tiff.h" TIFF_VERSION_LINES TIFF_VERSION_CLASSIC TIFF_VERSION_BIG TIFF_VERSION TIFF_BIGTIFF_VERSION)
|
||||
@@ -83,10 +88,10 @@ if(WITH_TIFF)
|
||||
if(NOT TIFF_FOUND)
|
||||
ocv_clear_vars(TIFF_LIBRARY TIFF_LIBRARIES TIFF_INCLUDE_DIR)
|
||||
|
||||
set(TIFF_LIBRARY libtiff)
|
||||
set(TIFF_LIBRARY libtiff CACHE INTERNAL "")
|
||||
set(TIFF_LIBRARIES ${TIFF_LIBRARY})
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libtiff")
|
||||
set(TIFF_INCLUDE_DIR "${${TIFF_LIBRARY}_SOURCE_DIR}" "${${TIFF_LIBRARY}_BINARY_DIR}")
|
||||
set(TIFF_INCLUDE_DIR "${${TIFF_LIBRARY}_SOURCE_DIR}" "${${TIFF_LIBRARY}_BINARY_DIR}" CACHE INTERNAL "")
|
||||
ocv_parse_header("${${TIFF_LIBRARY}_SOURCE_DIR}/tiff.h" TIFF_VERSION_LINES TIFF_VERSION_CLASSIC TIFF_VERSION_BIG TIFF_VERSION TIFF_BIGTIFF_VERSION)
|
||||
endif()
|
||||
|
||||
@@ -117,6 +122,7 @@ if(WITH_WEBP)
|
||||
if(BUILD_WEBP)
|
||||
ocv_clear_vars(WEBP_FOUND WEBP_LIBRARY WEBP_LIBRARIES WEBP_INCLUDE_DIR)
|
||||
else()
|
||||
ocv_clear_internal_cache_vars(WEBP_LIBRARY WEBP_INCLUDE_DIR)
|
||||
include(cmake/OpenCVFindWebP.cmake)
|
||||
if(WEBP_FOUND)
|
||||
set(HAVE_WEBP 1)
|
||||
@@ -128,12 +134,12 @@ endif()
|
||||
if(WITH_WEBP AND NOT WEBP_FOUND
|
||||
AND (NOT ANDROID OR HAVE_CPUFEATURES)
|
||||
)
|
||||
|
||||
set(WEBP_LIBRARY libwebp)
|
||||
ocv_clear_vars(WEBP_LIBRARY WEBP_INCLUDE_DIR)
|
||||
set(WEBP_LIBRARY libwebp CACHE INTERNAL "")
|
||||
set(WEBP_LIBRARIES ${WEBP_LIBRARY})
|
||||
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libwebp")
|
||||
set(WEBP_INCLUDE_DIR "${${WEBP_LIBRARY}_SOURCE_DIR}/src")
|
||||
set(WEBP_INCLUDE_DIR "${${WEBP_LIBRARY}_SOURCE_DIR}/src" CACHE INTERNAL "")
|
||||
set(HAVE_WEBP 1)
|
||||
endif()
|
||||
|
||||
@@ -164,10 +170,10 @@ if(WITH_JASPER)
|
||||
if(NOT JASPER_FOUND)
|
||||
ocv_clear_vars(JASPER_LIBRARY JASPER_LIBRARIES JASPER_INCLUDE_DIR)
|
||||
|
||||
set(JASPER_LIBRARY libjasper)
|
||||
set(JASPER_LIBRARY libjasper CACHE INTERNAL "")
|
||||
set(JASPER_LIBRARIES ${JASPER_LIBRARY})
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libjasper")
|
||||
set(JASPER_INCLUDE_DIR "${${JASPER_LIBRARY}_SOURCE_DIR}")
|
||||
set(JASPER_INCLUDE_DIR "${${JASPER_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "")
|
||||
endif()
|
||||
|
||||
set(HAVE_JASPER YES)
|
||||
@@ -182,6 +188,7 @@ if(WITH_PNG)
|
||||
if(BUILD_PNG)
|
||||
ocv_clear_vars(PNG_FOUND)
|
||||
else()
|
||||
ocv_clear_internal_cache_vars(PNG_LIBRARY PNG_INCLUDE_DIR)
|
||||
include(FindPNG)
|
||||
if(PNG_FOUND)
|
||||
include(CheckIncludeFile)
|
||||
@@ -197,10 +204,10 @@ if(WITH_PNG)
|
||||
if(NOT PNG_FOUND)
|
||||
ocv_clear_vars(PNG_LIBRARY PNG_LIBRARIES PNG_INCLUDE_DIR PNG_PNG_INCLUDE_DIR HAVE_LIBPNG_PNG_H PNG_DEFINITIONS)
|
||||
|
||||
set(PNG_LIBRARY libpng)
|
||||
set(PNG_LIBRARY libpng CACHE INTERNAL "")
|
||||
set(PNG_LIBRARIES ${PNG_LIBRARY})
|
||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libpng")
|
||||
set(PNG_INCLUDE_DIR "${${PNG_LIBRARY}_SOURCE_DIR}")
|
||||
set(PNG_INCLUDE_DIR "${${PNG_LIBRARY}_SOURCE_DIR}" CACHE INTERNAL "")
|
||||
set(PNG_DEFINITIONS "")
|
||||
ocv_parse_header("${PNG_INCLUDE_DIR}/png.h" PNG_VERSION_LINES PNG_LIBPNG_VER_MAJOR PNG_LIBPNG_VER_MINOR PNG_LIBPNG_VER_RELEASE)
|
||||
endif()
|
||||
@@ -213,6 +220,7 @@ endif()
|
||||
if(WITH_OPENEXR)
|
||||
ocv_clear_vars(HAVE_OPENEXR)
|
||||
if(NOT BUILD_OPENEXR)
|
||||
ocv_clear_internal_cache_vars(OPENEXR_INCLUDE_PATHS OPENEXR_LIBRARIES OPENEXR_ILMIMF_LIBRARY OPENEXR_VERSION)
|
||||
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindOpenEXR.cmake")
|
||||
endif()
|
||||
|
||||
@@ -242,7 +250,7 @@ if(WITH_GDAL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WITH_GDCM)
|
||||
if(WITH_GDCM)
|
||||
find_package(GDCM QUIET)
|
||||
if(NOT GDCM_FOUND)
|
||||
set(HAVE_GDCM NO)
|
||||
|
||||
@@ -51,7 +51,10 @@ endif(WITH_CUDA)
|
||||
|
||||
# --- Eigen ---
|
||||
if(WITH_EIGEN AND NOT HAVE_EIGEN)
|
||||
if(NOT OPENCV_SKIP_EIGEN_FIND_PACKAGE_CONFIG)
|
||||
if((OPENCV_FORCE_EIGEN_FIND_PACKAGE_CONFIG
|
||||
OR NOT (CMAKE_VERSION VERSION_LESS "3.0.0") # Eigen3Targets.cmake required CMake 3.0.0+
|
||||
) AND NOT OPENCV_SKIP_EIGEN_FIND_PACKAGE_CONFIG
|
||||
)
|
||||
find_package(Eigen3 CONFIG QUIET) # Ceres 2.0.0 CMake scripts doesn't work with CMake's FindEigen3.cmake module (due to missing EIGEN3_VERSION_STRING)
|
||||
endif()
|
||||
if(NOT Eigen3_FOUND)
|
||||
|
||||
+87
-65
@@ -3,7 +3,14 @@
|
||||
# installation/package
|
||||
#
|
||||
# Parameters:
|
||||
# MKL_WITH_TBB
|
||||
# MKL_ROOT_DIR / ENV{MKLROOT}
|
||||
# MKL_INCLUDE_DIR
|
||||
# MKL_LIBRARIES
|
||||
# MKL_USE_SINGLE_DYNAMIC_LIBRARY - use single dynamic library mkl_rt.lib / libmkl_rt.so
|
||||
# MKL_WITH_TBB / MKL_WITH_OPENMP
|
||||
#
|
||||
# Extra:
|
||||
# MKL_LIB_FIND_PATHS
|
||||
#
|
||||
# On return this will define:
|
||||
#
|
||||
@@ -13,12 +20,6 @@
|
||||
# MKL_LIBRARIES - MKL libraries that are used by OpenCV
|
||||
#
|
||||
|
||||
macro (mkl_find_lib VAR NAME DIRS)
|
||||
find_path(${VAR} ${NAME} ${DIRS} NO_DEFAULT_PATH)
|
||||
set(${VAR} ${${VAR}}/${NAME})
|
||||
unset(${VAR} CACHE)
|
||||
endmacro()
|
||||
|
||||
macro(mkl_fail)
|
||||
set(HAVE_MKL OFF)
|
||||
set(MKL_ROOT_DIR "${MKL_ROOT_DIR}" CACHE PATH "Path to MKL directory")
|
||||
@@ -39,43 +40,50 @@ macro(get_mkl_version VERSION_FILE)
|
||||
set(MKL_VERSION_STR "${MKL_VERSION_MAJOR}.${MKL_VERSION_MINOR}.${MKL_VERSION_UPDATE}" CACHE STRING "MKL version" FORCE)
|
||||
endmacro()
|
||||
|
||||
OCV_OPTION(MKL_USE_SINGLE_DYNAMIC_LIBRARY "Use MKL Single Dynamic Library thorugh mkl_rt.lib / libmkl_rt.so" OFF)
|
||||
OCV_OPTION(MKL_WITH_TBB "Use MKL with TBB multithreading" OFF)#ON IF WITH_TBB)
|
||||
OCV_OPTION(MKL_WITH_OPENMP "Use MKL with OpenMP multithreading" OFF)#ON IF WITH_OPENMP)
|
||||
|
||||
if(NOT DEFINED MKL_USE_MULTITHREAD)
|
||||
OCV_OPTION(MKL_WITH_TBB "Use MKL with TBB multithreading" OFF)#ON IF WITH_TBB)
|
||||
OCV_OPTION(MKL_WITH_OPENMP "Use MKL with OpenMP multithreading" OFF)#ON IF WITH_OPENMP)
|
||||
if(NOT MKL_ROOT_DIR AND DEFINED MKL_INCLUDE_DIR AND EXISTS "${MKL_INCLUDE_DIR}/mkl.h")
|
||||
file(TO_CMAKE_PATH "${MKL_INCLUDE_DIR}" MKL_INCLUDE_DIR)
|
||||
get_filename_component(MKL_ROOT_DIR "${MKL_INCLUDE_DIR}/.." ABSOLUTE)
|
||||
endif()
|
||||
if(NOT MKL_ROOT_DIR)
|
||||
file(TO_CMAKE_PATH "${MKL_ROOT_DIR}" mkl_root_paths)
|
||||
if(DEFINED ENV{MKLROOT})
|
||||
file(TO_CMAKE_PATH "$ENV{MKLROOT}" path)
|
||||
list(APPEND mkl_root_paths "${path}")
|
||||
endif()
|
||||
|
||||
if(WITH_MKL AND NOT mkl_root_paths)
|
||||
if(WIN32)
|
||||
set(ProgramFilesx86 "ProgramFiles(x86)")
|
||||
file(TO_CMAKE_PATH "$ENV{${ProgramFilesx86}}" path)
|
||||
list(APPEND mkl_root_paths ${path}/IntelSWTools/compilers_and_libraries/windows/mkl)
|
||||
endif()
|
||||
if(UNIX)
|
||||
list(APPEND mkl_root_paths "/opt/intel/mkl")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(MKL_ROOT_DIR include/mkl.h PATHS ${mkl_root_paths})
|
||||
endif()
|
||||
|
||||
#check current MKL_ROOT_DIR
|
||||
if(NOT MKL_ROOT_DIR OR NOT EXISTS "${MKL_ROOT_DIR}/include/mkl.h")
|
||||
set(mkl_root_paths "${MKL_ROOT_DIR}")
|
||||
if(DEFINED ENV{MKLROOT})
|
||||
list(APPEND mkl_root_paths "$ENV{MKLROOT}")
|
||||
endif()
|
||||
|
||||
if(WITH_MKL AND NOT mkl_root_paths)
|
||||
if(WIN32)
|
||||
set(ProgramFilesx86 "ProgramFiles(x86)")
|
||||
list(APPEND mkl_root_paths $ENV{${ProgramFilesx86}}/IntelSWTools/compilers_and_libraries/windows/mkl)
|
||||
endif()
|
||||
if(UNIX)
|
||||
list(APPEND mkl_root_paths "/opt/intel/mkl")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(MKL_ROOT_DIR include/mkl.h PATHS ${mkl_root_paths})
|
||||
mkl_fail()
|
||||
endif()
|
||||
|
||||
set(MKL_INCLUDE_DIRS "${MKL_ROOT_DIR}/include" CACHE PATH "Path to MKL include directory")
|
||||
set(MKL_INCLUDE_DIR "${MKL_ROOT_DIR}/include" CACHE PATH "Path to MKL include directory")
|
||||
|
||||
if(NOT MKL_ROOT_DIR
|
||||
OR NOT EXISTS "${MKL_ROOT_DIR}"
|
||||
OR NOT EXISTS "${MKL_INCLUDE_DIRS}"
|
||||
OR NOT EXISTS "${MKL_INCLUDE_DIRS}/mkl_version.h"
|
||||
OR NOT EXISTS "${MKL_INCLUDE_DIR}"
|
||||
OR NOT EXISTS "${MKL_INCLUDE_DIR}/mkl_version.h"
|
||||
)
|
||||
mkl_fail()
|
||||
mkl_fail()
|
||||
endif()
|
||||
|
||||
get_mkl_version(${MKL_INCLUDE_DIRS}/mkl_version.h)
|
||||
get_mkl_version(${MKL_INCLUDE_DIR}/mkl_version.h)
|
||||
|
||||
#determine arch
|
||||
if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
|
||||
@@ -95,52 +103,66 @@ else()
|
||||
set(MKL_ARCH_SUFFIX "c")
|
||||
endif()
|
||||
|
||||
if(MKL_VERSION_STR VERSION_GREATER "11.3.0" OR MKL_VERSION_STR VERSION_EQUAL "11.3.0")
|
||||
set(mkl_lib_find_paths
|
||||
${MKL_ROOT_DIR}/lib)
|
||||
foreach(MKL_ARCH ${MKL_ARCH_LIST})
|
||||
list(APPEND mkl_lib_find_paths
|
||||
${MKL_ROOT_DIR}/lib/${MKL_ARCH}
|
||||
${MKL_ROOT_DIR}/../tbb/lib/${MKL_ARCH}
|
||||
${MKL_ROOT_DIR}/${MKL_ARCH})
|
||||
endforeach()
|
||||
set(mkl_lib_find_paths ${MKL_LIB_FIND_PATHS} ${MKL_ROOT_DIR}/lib)
|
||||
foreach(MKL_ARCH ${MKL_ARCH_LIST})
|
||||
list(APPEND mkl_lib_find_paths
|
||||
${MKL_ROOT_DIR}/lib/${MKL_ARCH}
|
||||
${MKL_ROOT_DIR}/${MKL_ARCH}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
set(mkl_lib_list "mkl_intel_${MKL_ARCH_SUFFIX}")
|
||||
if(MKL_USE_SINGLE_DYNAMIC_LIBRARY AND NOT (MKL_VERSION_STR VERSION_LESS "10.3.0"))
|
||||
|
||||
if(MKL_WITH_TBB)
|
||||
list(APPEND mkl_lib_list mkl_tbb_thread tbb)
|
||||
elseif(MKL_WITH_OPENMP)
|
||||
if(MSVC)
|
||||
list(APPEND mkl_lib_list mkl_intel_thread libiomp5md)
|
||||
else()
|
||||
list(APPEND mkl_lib_list mkl_gnu_thread)
|
||||
endif()
|
||||
# https://software.intel.com/content/www/us/en/develop/articles/a-new-linking-model-single-dynamic-library-mkl_rt-since-intel-mkl-103.html
|
||||
set(mkl_lib_list "mkl_rt")
|
||||
|
||||
elseif(NOT (MKL_VERSION_STR VERSION_LESS "11.3.0"))
|
||||
|
||||
foreach(MKL_ARCH ${MKL_ARCH_LIST})
|
||||
list(APPEND mkl_lib_find_paths
|
||||
${MKL_ROOT_DIR}/../tbb/lib/${MKL_ARCH}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
set(mkl_lib_list "mkl_intel_${MKL_ARCH_SUFFIX}")
|
||||
|
||||
if(MKL_WITH_TBB)
|
||||
list(APPEND mkl_lib_list mkl_tbb_thread tbb)
|
||||
elseif(MKL_WITH_OPENMP)
|
||||
if(MSVC)
|
||||
list(APPEND mkl_lib_list mkl_intel_thread libiomp5md)
|
||||
else()
|
||||
list(APPEND mkl_lib_list mkl_sequential)
|
||||
list(APPEND mkl_lib_list mkl_gnu_thread)
|
||||
endif()
|
||||
else()
|
||||
list(APPEND mkl_lib_list mkl_sequential)
|
||||
endif()
|
||||
|
||||
list(APPEND mkl_lib_list mkl_core)
|
||||
list(APPEND mkl_lib_list mkl_core)
|
||||
else()
|
||||
message(STATUS "MKL version ${MKL_VERSION_STR} is not supported")
|
||||
mkl_fail()
|
||||
message(STATUS "MKL version ${MKL_VERSION_STR} is not supported")
|
||||
mkl_fail()
|
||||
endif()
|
||||
|
||||
set(MKL_LIBRARIES "")
|
||||
foreach(lib ${mkl_lib_list})
|
||||
find_library(${lib} NAMES ${lib} ${lib}_dll HINTS ${mkl_lib_find_paths})
|
||||
mark_as_advanced(${lib})
|
||||
if(NOT ${lib})
|
||||
mkl_fail()
|
||||
if(NOT MKL_LIBRARIES)
|
||||
set(MKL_LIBRARIES "")
|
||||
foreach(lib ${mkl_lib_list})
|
||||
set(lib_var_name MKL_LIBRARY_${lib})
|
||||
find_library(${lib_var_name} NAMES ${lib} ${lib}_dll HINTS ${mkl_lib_find_paths})
|
||||
mark_as_advanced(${lib_var_name})
|
||||
if(NOT ${lib_var_name})
|
||||
mkl_fail()
|
||||
endif()
|
||||
list(APPEND MKL_LIBRARIES ${${lib}})
|
||||
endforeach()
|
||||
list(APPEND MKL_LIBRARIES ${${lib_var_name}})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
message(STATUS "Found MKL ${MKL_VERSION_STR} at: ${MKL_ROOT_DIR}")
|
||||
set(HAVE_MKL ON)
|
||||
set(MKL_ROOT_DIR "${MKL_ROOT_DIR}" CACHE PATH "Path to MKL directory")
|
||||
set(MKL_INCLUDE_DIRS "${MKL_INCLUDE_DIRS}" CACHE PATH "Path to MKL include directory")
|
||||
set(MKL_LIBRARIES "${MKL_LIBRARIES}" CACHE STRING "MKL libraries")
|
||||
if(UNIX AND NOT MKL_LIBRARIES_DONT_HACK)
|
||||
set(MKL_INCLUDE_DIRS "${MKL_INCLUDE_DIR}")
|
||||
set(MKL_LIBRARIES "${MKL_LIBRARIES}")
|
||||
if(UNIX AND NOT MKL_USE_SINGLE_DYNAMIC_LIBRARY AND NOT MKL_LIBRARIES_DONT_HACK)
|
||||
#it's ugly but helps to avoid cyclic lib problem
|
||||
set(MKL_LIBRARIES ${MKL_LIBRARIES} ${MKL_LIBRARIES} ${MKL_LIBRARIES} "-lpthread" "-lm" "-ldl")
|
||||
endif()
|
||||
|
||||
+15
-14
@@ -98,15 +98,6 @@ macro(ocv_add_dependencies full_modname)
|
||||
endforeach()
|
||||
unset(__depsvar)
|
||||
|
||||
# hack for python
|
||||
set(__python_idx)
|
||||
list(FIND OPENCV_MODULE_${full_modname}_WRAPPERS "python" __python_idx)
|
||||
if (NOT __python_idx EQUAL -1)
|
||||
list(REMOVE_ITEM OPENCV_MODULE_${full_modname}_WRAPPERS "python")
|
||||
list(APPEND OPENCV_MODULE_${full_modname}_WRAPPERS "python_bindings_generator" "python2" "python3")
|
||||
endif()
|
||||
unset(__python_idx)
|
||||
|
||||
ocv_list_unique(OPENCV_MODULE_${full_modname}_REQ_DEPS)
|
||||
ocv_list_unique(OPENCV_MODULE_${full_modname}_OPT_DEPS)
|
||||
ocv_list_unique(OPENCV_MODULE_${full_modname}_PRIVATE_REQ_DEPS)
|
||||
@@ -209,11 +200,6 @@ macro(ocv_add_module _name)
|
||||
set(OPENCV_MODULES_DISABLED_USER ${OPENCV_MODULES_DISABLED_USER} "${the_module}" CACHE INTERNAL "List of OpenCV modules explicitly disabled by user")
|
||||
endif()
|
||||
|
||||
# add reverse wrapper dependencies
|
||||
foreach (wrapper ${OPENCV_MODULE_${the_module}_WRAPPERS})
|
||||
ocv_add_dependencies(opencv_${wrapper} OPTIONAL ${the_module})
|
||||
endforeach()
|
||||
|
||||
# stop processing of current file
|
||||
ocv_cmake_hook(POST_ADD_MODULE)
|
||||
ocv_cmake_hook(POST_ADD_MODULE_${the_module})
|
||||
@@ -500,6 +486,21 @@ function(__ocv_resolve_dependencies)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# add reverse wrapper dependencies (BINDINDS)
|
||||
foreach(the_module ${OPENCV_MODULES_BUILD})
|
||||
foreach (wrapper ${OPENCV_MODULE_${the_module}_WRAPPERS})
|
||||
if(wrapper STREQUAL "python") # hack for python (BINDINDS)
|
||||
ocv_add_dependencies(opencv_python2 OPTIONAL ${the_module})
|
||||
ocv_add_dependencies(opencv_python3 OPTIONAL ${the_module})
|
||||
else()
|
||||
ocv_add_dependencies(opencv_${wrapper} OPTIONAL ${the_module})
|
||||
endif()
|
||||
if(DEFINED OPENCV_MODULE_opencv_${wrapper}_bindings_generator_CLASS)
|
||||
ocv_add_dependencies(opencv_${wrapper}_bindings_generator OPTIONAL ${the_module})
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# disable MODULES with unresolved dependencies
|
||||
set(has_changes ON)
|
||||
while(has_changes)
|
||||
|
||||
+32
-1
@@ -8,7 +8,20 @@ include(CMakeParseArguments)
|
||||
function(ocv_cmake_dump_vars)
|
||||
set(OPENCV_SUPPRESS_DEPRECATIONS 1) # suppress deprecation warnings from variable_watch() guards
|
||||
get_cmake_property(__variableNames VARIABLES)
|
||||
cmake_parse_arguments(DUMP "" "TOFILE" "" ${ARGN})
|
||||
cmake_parse_arguments(DUMP "FORCE" "TOFILE" "" ${ARGN})
|
||||
|
||||
# avoid generation of excessive logs with "--trace" or "--trace-expand" parameters
|
||||
# Note: `-DCMAKE_TRACE_MODE=1` should be passed to CMake through command line. It is not a CMake buildin variable for now (2020-12)
|
||||
# Use `cmake . -UCMAKE_TRACE_MODE` to remove this variable from cache
|
||||
if(CMAKE_TRACE_MODE AND NOT DUMP_FORCE)
|
||||
if(DUMP_TOFILE)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/${DUMP_TOFILE} "Skipped due to enabled CMAKE_TRACE_MODE")
|
||||
else()
|
||||
message(AUTHOR_WARNING "ocv_cmake_dump_vars() is skipped due to enabled CMAKE_TRACE_MODE")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(regex "${DUMP_UNPARSED_ARGUMENTS}")
|
||||
string(TOLOWER "${regex}" regex_lower)
|
||||
set(__VARS "")
|
||||
@@ -400,6 +413,24 @@ macro(ocv_clear_vars)
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
|
||||
# Clears passed variables with INTERNAL type from CMake cache
|
||||
macro(ocv_clear_internal_cache_vars)
|
||||
foreach(_var ${ARGN})
|
||||
get_property(_propertySet CACHE ${_var} PROPERTY TYPE SET)
|
||||
if(_propertySet)
|
||||
get_property(_type CACHE ${_var} PROPERTY TYPE)
|
||||
if(_type STREQUAL "INTERNAL")
|
||||
message("Cleaning INTERNAL cached variable: ${_var}")
|
||||
unset(${_var} CACHE)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
unset(_propertySet)
|
||||
unset(_type)
|
||||
endmacro()
|
||||
|
||||
|
||||
set(OCV_COMPILER_FAIL_REGEX
|
||||
"argument .* is not valid" # GCC 9+ (including support of unicode quotes)
|
||||
"command[- ]line option .* is valid for .* but not for C\\+\\+" # GNU
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
set(OPENCV_SKIP_LINK_AS_NEEDED 1)
|
||||
@@ -39,7 +39,6 @@ ALIASES += end_toggle="@htmlonly[block] </div> @endhtmlonly"
|
||||
ALIASES += prev_tutorial{1}="**Prev Tutorial:** \ref \1 \n"
|
||||
ALIASES += next_tutorial{1}="**Next Tutorial:** \ref \1 \n"
|
||||
ALIASES += youtube{1}="@htmlonly[block]<div align='center'><iframe title='Video' width='560' height='349' src='https://www.youtube.com/embed/\1?rel=0' frameborder='0' align='middle' allowfullscreen></iframe></div>@endhtmlonly"
|
||||
TCL_SUBST =
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
OPTIMIZE_FOR_FORTRAN = NO
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
getBlobFromImage = function(inputSize, mean, std, swapRB, image) {
|
||||
let mat;
|
||||
if (typeof(image) === 'string') {
|
||||
mat = cv.imread(image);
|
||||
} else {
|
||||
mat = image;
|
||||
}
|
||||
|
||||
let matC3 = new cv.Mat(mat.matSize[0], mat.matSize[1], cv.CV_8UC3);
|
||||
cv.cvtColor(mat, matC3, cv.COLOR_RGBA2BGR);
|
||||
let input = cv.blobFromImage(matC3, std, new cv.Size(inputSize[0], inputSize[1]),
|
||||
new cv.Scalar(mean[0], mean[1], mean[2]), swapRB);
|
||||
|
||||
matC3.delete();
|
||||
return input;
|
||||
}
|
||||
|
||||
loadLables = async function(labelsUrl) {
|
||||
let response = await fetch(labelsUrl);
|
||||
let label = await response.text();
|
||||
label = label.split('\n');
|
||||
return label;
|
||||
}
|
||||
|
||||
loadModel = async function(e) {
|
||||
return new Promise((resolve) => {
|
||||
let file = e.target.files[0];
|
||||
let path = file.name;
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(file);
|
||||
reader.onload = function(ev) {
|
||||
if (reader.readyState === 2) {
|
||||
let buffer = reader.result;
|
||||
let data = new Uint8Array(buffer);
|
||||
cv.FS_createDataFile('/', path, data, true, false, false);
|
||||
resolve(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getTopClasses = function(probs, labels, topK = 3) {
|
||||
probs = Array.from(probs);
|
||||
let indexes = probs.map((prob, index) => [prob, index]);
|
||||
let sorted = indexes.sort((a, b) => {
|
||||
if (a[0] === b[0]) {return 0;}
|
||||
return a[0] < b[0] ? -1 : 1;
|
||||
});
|
||||
sorted.reverse();
|
||||
let classes = [];
|
||||
for (let i = 0; i < topK; ++i) {
|
||||
let prob = sorted[i][0];
|
||||
let index = sorted[i][1];
|
||||
let c = {
|
||||
label: labels[index],
|
||||
prob: (prob * 100).toFixed(2)
|
||||
}
|
||||
classes.push(c);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
loadImageToCanvas = function(e, canvasId) {
|
||||
let files = e.target.files;
|
||||
let imgUrl = URL.createObjectURL(files[0]);
|
||||
let canvas = document.getElementById(canvasId);
|
||||
let ctx = canvas.getContext('2d');
|
||||
let img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.src = imgUrl;
|
||||
img.onload = function() {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
}
|
||||
|
||||
drawInfoTable = async function(jsonUrl, divId) {
|
||||
let response = await fetch(jsonUrl);
|
||||
let json = await response.json();
|
||||
|
||||
let appendix = document.getElementById(divId);
|
||||
for (key of Object.keys(json)) {
|
||||
let h3 = document.createElement('h3');
|
||||
h3.textContent = key + " model";
|
||||
appendix.appendChild(h3);
|
||||
|
||||
let table = document.createElement('table');
|
||||
let head_tr = document.createElement('tr');
|
||||
for (head of Object.keys(json[key][0])) {
|
||||
let th = document.createElement('th');
|
||||
th.textContent = head;
|
||||
th.style.border = "1px solid black";
|
||||
head_tr.appendChild(th);
|
||||
}
|
||||
table.appendChild(head_tr)
|
||||
|
||||
for (model of json[key]) {
|
||||
let tr = document.createElement('tr');
|
||||
for (params of Object.keys(model)) {
|
||||
let td = document.createElement('td');
|
||||
td.style.border = "1px solid black";
|
||||
if (params !== "modelUrl" && params !== "configUrl" && params !== "labelsUrl") {
|
||||
td.textContent = model[params];
|
||||
tr.appendChild(td);
|
||||
} else {
|
||||
let a = document.createElement('a');
|
||||
let link = document.createTextNode('link');
|
||||
a.append(link);
|
||||
a.href = model[params];
|
||||
td.appendChild(a);
|
||||
tr.appendChild(td);
|
||||
}
|
||||
}
|
||||
table.appendChild(tr);
|
||||
}
|
||||
table.style.width = "800px";
|
||||
table.style.borderCollapse = "collapse";
|
||||
appendix.appendChild(table);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Image Classification Example</title>
|
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Image Classification Example</h2>
|
||||
<p>
|
||||
This tutorial shows you how to write an image classification example with OpenCV.js.<br>
|
||||
To try the example you should click the <b>modelFile</b> button(and <b>configFile</b> button if needed) to upload inference model.
|
||||
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
|
||||
Then You should change the parameters in the first code snippet according to the uploaded model.
|
||||
Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
|
||||
</p>
|
||||
|
||||
<div class="control"><button id="tryIt" disabled>Try it</button></div>
|
||||
<div>
|
||||
<table cellpadding="0" cellspacing="0" width="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<canvas id="canvasInput" width="400" height="400"></canvas>
|
||||
</td>
|
||||
<td>
|
||||
<table style="visibility: hidden;" id="result">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col" width=300>Label</th>
|
||||
<th scope="col">Probability</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">1</th>
|
||||
<td id="label0" align="center"></td>
|
||||
<td id="prob0" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">2</th>
|
||||
<td id="label1" align="center"></td>
|
||||
<td id="prob1" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">3</th>
|
||||
<td id="label2" align="center"></td>
|
||||
<td id="prob2" align="center"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p id='status' align="left"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
modelFile <input type="file" id="modelFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
configFile <input type="file" id="configFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="err" id="errorMessage"></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Help function</h3>
|
||||
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
|
||||
<textarea class="code" rows="13" cols="100" id="codeEditor" spellcheck="false"></textarea>
|
||||
<p>2.Main loop in which will read the image from canvas and do inference once.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor1" spellcheck="false"></textarea>
|
||||
<p>3.Load labels from txt file and process it into an array.</p>
|
||||
<textarea class="code" rows="7" cols="100" id="codeEditor2" spellcheck="false"></textarea>
|
||||
<p>4.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
|
||||
<p>5.Fetch model file and save to emscripten file system once click the input button.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor4" spellcheck="false"></textarea>
|
||||
<p>6.The post-processing, including softmax if needed and get the top classes from the output vector.</p>
|
||||
<textarea class="code" rows="35" cols="100" id="codeEditor5" spellcheck="false"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="appendix">
|
||||
<h2>Model Info:</h2>
|
||||
</div>
|
||||
|
||||
<script src="utils.js" type="text/javascript"></script>
|
||||
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
|
||||
|
||||
<script id="codeSnippet" type="text/code-snippet">
|
||||
inputSize = [224,224];
|
||||
mean = [104, 117, 123];
|
||||
std = 1;
|
||||
swapRB = false;
|
||||
|
||||
// record if need softmax function for post-processing
|
||||
needSoftmax = false;
|
||||
|
||||
// url for label file, can from local or Internet
|
||||
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt";
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet">
|
||||
main = async function() {
|
||||
const labels = await loadLables(labelsUrl);
|
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
|
||||
let net = cv.readNet(configPath, modelPath);
|
||||
net.setInput(input);
|
||||
const start = performance.now();
|
||||
const result = net.forward();
|
||||
const time = performance.now()-start;
|
||||
const probs = softmax(result);
|
||||
const classes = getTopClasses(probs, labels);
|
||||
|
||||
updateResult(classes, time);
|
||||
input.delete();
|
||||
net.delete();
|
||||
result.delete();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet">
|
||||
softmax = function(result) {
|
||||
let arr = result.data32F;
|
||||
if (needSoftmax) {
|
||||
const maxNum = Math.max(...arr);
|
||||
const expSum = arr.map((num) => Math.exp(num - maxNum)).reduce((a, b) => a + b);
|
||||
return arr.map((value, index) => {
|
||||
return Math.exp(value - maxNum) / expSum;
|
||||
});
|
||||
} else {
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
let jsonUrl = "js_image_classification_model_info.json";
|
||||
drawInfoTable(jsonUrl, 'appendix');
|
||||
|
||||
let utils = new Utils('errorMessage');
|
||||
utils.loadCode('codeSnippet', 'codeEditor');
|
||||
utils.loadCode('codeSnippet1', 'codeEditor1');
|
||||
|
||||
let loadLablesCode = 'loadLables = ' + loadLables.toString();
|
||||
document.getElementById('codeEditor2').value = loadLablesCode;
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
|
||||
document.getElementById('codeEditor3').value = getBlobFromImageCode;
|
||||
let loadModelCode = 'loadModel = ' + loadModel.toString();
|
||||
document.getElementById('codeEditor4').value = loadModelCode;
|
||||
|
||||
utils.loadCode('codeSnippet5', 'codeEditor5');
|
||||
let getTopClassesCode = 'getTopClasses = ' + getTopClasses.toString();
|
||||
document.getElementById('codeEditor5').value += '\n' + '\n' + getTopClassesCode;
|
||||
|
||||
let canvas = document.getElementById('canvasInput');
|
||||
let ctx = canvas.getContext('2d');
|
||||
let img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.src = 'space_shuttle.jpg';
|
||||
img.onload = function() {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
let tryIt = document.getElementById('tryIt');
|
||||
tryIt.addEventListener('click', () => {
|
||||
initStatus();
|
||||
document.getElementById('status').innerHTML = 'Running function main()...';
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
if (modelPath === "") {
|
||||
document.getElementById('status').innerHTML = 'Runing failed.';
|
||||
utils.printError('Please upload model file by clicking the button first.');
|
||||
} else {
|
||||
setTimeout(main, 1);
|
||||
}
|
||||
});
|
||||
|
||||
let fileInput = document.getElementById('fileInput');
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
initStatus();
|
||||
loadImageToCanvas(e, 'canvasInput');
|
||||
});
|
||||
|
||||
let configPath = "";
|
||||
let configFile = document.getElementById('configFile');
|
||||
configFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
configPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
|
||||
});
|
||||
|
||||
let modelPath = "";
|
||||
let modelFile = document.getElementById('modelFile');
|
||||
modelFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
modelPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
|
||||
configPath = "";
|
||||
configFile.value = "";
|
||||
});
|
||||
|
||||
utils.loadOpenCv(() => {
|
||||
tryIt.removeAttribute('disabled');
|
||||
});
|
||||
|
||||
var main = async function() {};
|
||||
var softmax = function(result){};
|
||||
var getTopClasses = function(mat, labels, topK = 3){};
|
||||
|
||||
utils.executeCode('codeEditor1');
|
||||
utils.executeCode('codeEditor2');
|
||||
utils.executeCode('codeEditor3');
|
||||
utils.executeCode('codeEditor4');
|
||||
utils.executeCode('codeEditor5');
|
||||
|
||||
function updateResult(classes, time) {
|
||||
try{
|
||||
classes.forEach((c,i) => {
|
||||
let labelElement = document.getElementById('label'+i);
|
||||
let probElement = document.getElementById('prob'+i);
|
||||
labelElement.innerHTML = c.label;
|
||||
probElement.innerHTML = c.prob + '%';
|
||||
});
|
||||
let result = document.getElementById('result');
|
||||
result.style.visibility = 'visible';
|
||||
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
|
||||
<b>Inference time:</b> ${time.toFixed(2)} ms`;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function initStatus() {
|
||||
document.getElementById('status').innerHTML = '';
|
||||
document.getElementById('result').style.visibility = 'hidden';
|
||||
utils.clearError();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"caffe": [
|
||||
{
|
||||
"model": "alexnet",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"needSoftmax": "false",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
|
||||
"modelUrl": "http://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodel",
|
||||
"configUrl": "https://raw.githubusercontent.com/BVLC/caffe/master/models/bvlc_alexnet/deploy.prototxt"
|
||||
},
|
||||
{
|
||||
"model": "densenet",
|
||||
"mean": "127.5, 127.5, 127.5",
|
||||
"std": "0.007843",
|
||||
"swapRB": "false",
|
||||
"needSoftmax": "true",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
|
||||
"modelUrl": "https://drive.google.com/open?id=0B7ubpZO7HnlCcHlfNmJkU2VPelE",
|
||||
"configUrl": "https://raw.githubusercontent.com/shicai/DenseNet-Caffe/master/DenseNet_121.prototxt"
|
||||
},
|
||||
{
|
||||
"model": "googlenet",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"needSoftmax": "false",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
|
||||
"modelUrl": "http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel",
|
||||
"configUrl": "https://raw.githubusercontent.com/BVLC/caffe/master/models/bvlc_googlenet/deploy.prototxt"
|
||||
},
|
||||
{
|
||||
"model": "squeezenet",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"needSoftmax": "false",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
|
||||
"modelUrl": "https://raw.githubusercontent.com/forresti/SqueezeNet/master/SqueezeNet_v1.0/squeezenet_v1.0.caffemodel",
|
||||
"configUrl": "https://raw.githubusercontent.com/forresti/SqueezeNet/master/SqueezeNet_v1.0/deploy.prototxt"
|
||||
},
|
||||
{
|
||||
"model": "VGG",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"needSoftmax": "false",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt",
|
||||
"modelUrl": "http://www.robots.ox.ac.uk/~vgg/software/very_deep/caffe/VGG_ILSVRC_19_layers.caffemodel",
|
||||
"configUrl": "https://gist.githubusercontent.com/ksimonyan/3785162f95cd2d5fee77/raw/f02f8769e64494bcd3d7e97d5d747ac275825721/VGG_ILSVRC_19_layers_deploy.prototxt"
|
||||
}
|
||||
],
|
||||
"tensorflow": [
|
||||
{
|
||||
"model": "inception",
|
||||
"mean": "123, 117, 104",
|
||||
"std": "1",
|
||||
"swapRB": "true",
|
||||
"needSoftmax": "false",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/petewarden/tf_ios_makefile_example/master/data/imagenet_comp_graph_label_strings.txt",
|
||||
"modelUrl": "https://raw.githubusercontent.com/petewarden/tf_ios_makefile_example/master/data/tensorflow_inception_graph.pb"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Image Classification Example with Camera</title>
|
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Image Classification Example with Camera</h2>
|
||||
<p>
|
||||
This tutorial shows you how to write an image classification example with camera.<br>
|
||||
To try the example you should click the <b>modelFile</b> button(and <b>configFile</b> button if needed) to upload inference model.
|
||||
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
|
||||
Then You should change the parameters in the first code snippet according to the uploaded model.
|
||||
Finally click <b>Start/Stop</b> button to start or stop the camera capture.<br>
|
||||
</p>
|
||||
|
||||
<div class="control"><button id="startAndStop" disabled>Start</button></div>
|
||||
<div>
|
||||
<table cellpadding="0" cellspacing="0" width="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<video id="videoInput" width="400" height="400"></video>
|
||||
</td>
|
||||
<td>
|
||||
<table style="visibility: hidden;" id="result">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col" width=300>Label</th>
|
||||
<th scope="col">Probability</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">1</th>
|
||||
<td id="label0" align="center"></td>
|
||||
<td id="prob0" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">2</th>
|
||||
<td id="label1" align="center"></td>
|
||||
<td id="prob1" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">3</th>
|
||||
<td id="label2" align="center"></td>
|
||||
<td id="prob2" align="center"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p id='status' align="left"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
videoInput
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
modelFile <input type="file" id="modelFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
configFile <input type="file" id="configFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="err" id="errorMessage"></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Help function</h3>
|
||||
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
|
||||
<textarea class="code" rows="13" cols="100" id="codeEditor" spellcheck="false"></textarea>
|
||||
<p>2.The function to capture video from camera, and the main loop in which will do inference once.</p>
|
||||
<textarea class="code" rows="35" cols="100" id="codeEditor1" spellcheck="false"></textarea>
|
||||
<p>3.Load labels from txt file and process it into an array.</p>
|
||||
<textarea class="code" rows="7" cols="100" id="codeEditor2" spellcheck="false"></textarea>
|
||||
<p>4.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
|
||||
<p>5.Fetch model file and save to emscripten file system once click the input button.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor4" spellcheck="false"></textarea>
|
||||
<p>6.The post-processing, including softmax if needed and get the top classes from the output vector.</p>
|
||||
<textarea class="code" rows="35" cols="100" id="codeEditor5" spellcheck="false"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="appendix">
|
||||
<h2>Model Info:</h2>
|
||||
</div>
|
||||
|
||||
<script src="utils.js" type="text/javascript"></script>
|
||||
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
|
||||
|
||||
<script id="codeSnippet" type="text/code-snippet">
|
||||
inputSize = [224,224];
|
||||
mean = [104, 117, 123];
|
||||
std = 1;
|
||||
swapRB = false;
|
||||
|
||||
// record if need softmax function for post-processing
|
||||
needSoftmax = false;
|
||||
|
||||
// url for label file, can from local or Internet
|
||||
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/classification_classes_ILSVRC2012.txt";
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet">
|
||||
let frame = new cv.Mat(video.height, video.width, cv.CV_8UC4);
|
||||
let cap = new cv.VideoCapture(video);
|
||||
|
||||
main = async function(frame) {
|
||||
const labels = await loadLables(labelsUrl);
|
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, frame);
|
||||
let net = cv.readNet(configPath, modelPath);
|
||||
net.setInput(input);
|
||||
const start = performance.now();
|
||||
const result = net.forward();
|
||||
const time = performance.now()-start;
|
||||
const probs = softmax(result);
|
||||
const classes = getTopClasses(probs, labels);
|
||||
|
||||
updateResult(classes, time);
|
||||
setTimeout(processVideo, 0);
|
||||
input.delete();
|
||||
net.delete();
|
||||
result.delete();
|
||||
}
|
||||
|
||||
function processVideo() {
|
||||
try {
|
||||
if (!streaming) {
|
||||
return;
|
||||
}
|
||||
cap.read(frame);
|
||||
main(frame);
|
||||
} catch (err) {
|
||||
utils.printError(err);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(processVideo, 0);
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet">
|
||||
softmax = function(result) {
|
||||
let arr = result.data32F;
|
||||
if (needSoftmax) {
|
||||
const maxNum = Math.max(...arr);
|
||||
const expSum = arr.map((num) => Math.exp(num - maxNum)).reduce((a, b) => a + b);
|
||||
return arr.map((value, index) => {
|
||||
return Math.exp(value - maxNum) / expSum;
|
||||
});
|
||||
} else {
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
let jsonUrl = "js_image_classification_model_info.json";
|
||||
drawInfoTable(jsonUrl, 'appendix');
|
||||
|
||||
let utils = new Utils('errorMessage');
|
||||
utils.loadCode('codeSnippet', 'codeEditor');
|
||||
utils.loadCode('codeSnippet1', 'codeEditor1');
|
||||
|
||||
let loadLablesCode = 'loadLables = ' + loadLables.toString();
|
||||
document.getElementById('codeEditor2').value = loadLablesCode;
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
|
||||
document.getElementById('codeEditor3').value = getBlobFromImageCode;
|
||||
let loadModelCode = 'loadModel = ' + loadModel.toString();
|
||||
document.getElementById('codeEditor4').value = loadModelCode;
|
||||
|
||||
utils.loadCode('codeSnippet5', 'codeEditor5');
|
||||
let getTopClassesCode = 'getTopClasses = ' + getTopClasses.toString();
|
||||
document.getElementById('codeEditor5').value += '\n' + '\n' + getTopClassesCode;
|
||||
|
||||
let video = document.getElementById('videoInput');
|
||||
let streaming = false;
|
||||
let startAndStop = document.getElementById('startAndStop');
|
||||
startAndStop.addEventListener('click', () => {
|
||||
if (!streaming) {
|
||||
utils.clearError();
|
||||
utils.startCamera('qvga', onVideoStarted, 'videoInput');
|
||||
} else {
|
||||
utils.stopCamera();
|
||||
onVideoStopped();
|
||||
}
|
||||
});
|
||||
|
||||
let configPath = "";
|
||||
let configFile = document.getElementById('configFile');
|
||||
configFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
configPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
|
||||
});
|
||||
|
||||
let modelPath = "";
|
||||
let modelFile = document.getElementById('modelFile');
|
||||
modelFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
modelPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
|
||||
configPath = "";
|
||||
configFile.value = "";
|
||||
});
|
||||
|
||||
utils.loadOpenCv(() => {
|
||||
startAndStop.removeAttribute('disabled');
|
||||
|
||||
});
|
||||
|
||||
var main = async function(frame) {};
|
||||
var softmax = function(result){};
|
||||
var getTopClasses = function(mat, labels, topK = 3){};
|
||||
|
||||
utils.executeCode('codeEditor1');
|
||||
utils.executeCode('codeEditor2');
|
||||
utils.executeCode('codeEditor3');
|
||||
utils.executeCode('codeEditor4');
|
||||
utils.executeCode('codeEditor5');
|
||||
|
||||
function onVideoStarted() {
|
||||
streaming = true;
|
||||
startAndStop.innerText = 'Stop';
|
||||
videoInput.width = videoInput.videoWidth;
|
||||
videoInput.height = videoInput.videoHeight;
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
}
|
||||
|
||||
function onVideoStopped() {
|
||||
streaming = false;
|
||||
startAndStop.innerText = 'Start';
|
||||
initStatus();
|
||||
}
|
||||
|
||||
function updateResult(classes, time) {
|
||||
try{
|
||||
classes.forEach((c,i) => {
|
||||
let labelElement = document.getElementById('label'+i);
|
||||
let probElement = document.getElementById('prob'+i);
|
||||
labelElement.innerHTML = c.label;
|
||||
probElement.innerHTML = c.prob + '%';
|
||||
});
|
||||
let result = document.getElementById('result');
|
||||
result.style.visibility = 'visible';
|
||||
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
|
||||
<b>Inference time:</b> ${time.toFixed(2)} ms`;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function initStatus() {
|
||||
document.getElementById('status').innerHTML = '';
|
||||
document.getElementById('result').style.visibility = 'hidden';
|
||||
utils.clearError();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,387 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Object Detection Example</title>
|
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Object Detection Example</h2>
|
||||
<p>
|
||||
This tutorial shows you how to write an object detection example with OpenCV.js.<br>
|
||||
To try the example you should click the <b>modelFile</b> button(and <b>configFile</b> button if needed) to upload inference model.
|
||||
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
|
||||
Then You should change the parameters in the first code snippet according to the uploaded model.
|
||||
Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
|
||||
</p>
|
||||
|
||||
<div class="control"><button id="tryIt" disabled>Try it</button></div>
|
||||
<div>
|
||||
<table cellpadding="0" cellspacing="0" width="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<canvas id="canvasInput" width="400" height="400"></canvas>
|
||||
</td>
|
||||
<td>
|
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p id='status' align="left"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
modelFile <input type="file" id="modelFile" name="file">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
configFile <input type="file" id="configFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="err" id="errorMessage"></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Help function</h3>
|
||||
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
|
||||
<textarea class="code" rows="15" cols="100" id="codeEditor" spellcheck="false"></textarea>
|
||||
<p>2.Main loop in which will read the image from canvas and do inference once.</p>
|
||||
<textarea class="code" rows="16" cols="100" id="codeEditor1" spellcheck="false"></textarea>
|
||||
<p>3.Load labels from txt file and process it into an array.</p>
|
||||
<textarea class="code" rows="7" cols="100" id="codeEditor2" spellcheck="false"></textarea>
|
||||
<p>4.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
|
||||
<p>5.Fetch model file and save to emscripten file system once click the input button.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor4" spellcheck="false"></textarea>
|
||||
<p>6.The post-processing, including get boxes from output and draw boxes into the image.</p>
|
||||
<textarea class="code" rows="35" cols="100" id="codeEditor5" spellcheck="false"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="appendix">
|
||||
<h2>Model Info:</h2>
|
||||
</div>
|
||||
|
||||
<script src="utils.js" type="text/javascript"></script>
|
||||
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
|
||||
|
||||
<script id="codeSnippet" type="text/code-snippet">
|
||||
inputSize = [300, 300];
|
||||
mean = [127.5, 127.5, 127.5];
|
||||
std = 0.007843;
|
||||
swapRB = false;
|
||||
confThreshold = 0.5;
|
||||
nmsThreshold = 0.4;
|
||||
|
||||
// The type of output, can be YOLO or SSD
|
||||
outType = "SSD";
|
||||
|
||||
// url for label file, can from local or Internet
|
||||
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt";
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet">
|
||||
main = async function() {
|
||||
const labels = await loadLables(labelsUrl);
|
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
|
||||
let net = cv.readNet(configPath, modelPath);
|
||||
net.setInput(input);
|
||||
const start = performance.now();
|
||||
const result = net.forward();
|
||||
const time = performance.now()-start;
|
||||
const output = postProcess(result, labels);
|
||||
|
||||
updateResult(output, time);
|
||||
input.delete();
|
||||
net.delete();
|
||||
result.delete();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet">
|
||||
postProcess = function(result, labels) {
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
const outputWidth = canvasOutput.width;
|
||||
const outputHeight = canvasOutput.height;
|
||||
const resultData = result.data32F;
|
||||
|
||||
// Get the boxes(with class and confidence) from the output
|
||||
let boxes = [];
|
||||
switch(outType) {
|
||||
case "YOLO": {
|
||||
const vecNum = result.matSize[0];
|
||||
const vecLength = result.matSize[1];
|
||||
const classNum = vecLength - 5;
|
||||
|
||||
for (let i = 0; i < vecNum; ++i) {
|
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength);
|
||||
let scores = vector.slice(5, vecLength);
|
||||
let classId = scores.indexOf(Math.max(...scores));
|
||||
let confidence = scores[classId];
|
||||
if (confidence > confThreshold) {
|
||||
let center_x = Math.round(vector[0] * outputWidth);
|
||||
let center_y = Math.round(vector[1] * outputHeight);
|
||||
let width = Math.round(vector[2] * outputWidth);
|
||||
let height = Math.round(vector[3] * outputHeight);
|
||||
let left = Math.round(center_x - width / 2);
|
||||
let top = Math.round(center_y - height / 2);
|
||||
|
||||
let box = {
|
||||
scores: scores,
|
||||
classId: classId,
|
||||
confidence: confidence,
|
||||
bounding: [left, top, width, height],
|
||||
toDraw: true
|
||||
}
|
||||
boxes.push(box);
|
||||
}
|
||||
}
|
||||
|
||||
// NMS(Non Maximum Suppression) algorithm
|
||||
let boxNum = boxes.length;
|
||||
let tmp_boxes = [];
|
||||
let sorted_boxes = [];
|
||||
for (let c = 0; c < classNum; ++c) {
|
||||
for (let i = 0; i < boxes.length; ++i) {
|
||||
tmp_boxes[i] = [boxes[i], i];
|
||||
}
|
||||
sorted_boxes = tmp_boxes.sort((a, b) => { return (b[0].scores[c] - a[0].scores[c]); });
|
||||
for (let i = 0; i < boxNum; ++i) {
|
||||
if (sorted_boxes[i][0].scores[c] === 0) continue;
|
||||
else {
|
||||
for (let j = i + 1; j < boxNum; ++j) {
|
||||
if (IOU(sorted_boxes[i][0], sorted_boxes[j][0]) >= nmsThreshold) {
|
||||
boxes[sorted_boxes[j][1]].toDraw = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case "SSD": {
|
||||
const vecNum = result.matSize[2];
|
||||
const vecLength = 7;
|
||||
|
||||
for (let i = 0; i < vecNum; ++i) {
|
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength);
|
||||
let confidence = vector[2];
|
||||
if (confidence > confThreshold) {
|
||||
let left, top, right, bottom, width, height;
|
||||
left = Math.round(vector[3]);
|
||||
top = Math.round(vector[4]);
|
||||
right = Math.round(vector[5]);
|
||||
bottom = Math.round(vector[6]);
|
||||
width = right - left + 1;
|
||||
height = bottom - top + 1;
|
||||
if (width <= 2 || height <= 2) {
|
||||
left = Math.round(vector[3] * outputWidth);
|
||||
top = Math.round(vector[4] * outputHeight);
|
||||
right = Math.round(vector[5] * outputWidth);
|
||||
bottom = Math.round(vector[6] * outputHeight);
|
||||
width = right - left + 1;
|
||||
height = bottom - top + 1;
|
||||
}
|
||||
let box = {
|
||||
classId: vector[1] - 1,
|
||||
confidence: confidence,
|
||||
bounding: [left, top, width, height],
|
||||
toDraw: true
|
||||
}
|
||||
boxes.push(box);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
console.error(`Unsupported output type ${outType}`)
|
||||
}
|
||||
|
||||
// Draw the saved box into the image
|
||||
let image = cv.imread("canvasInput");
|
||||
let output = new cv.Mat(outputWidth, outputHeight, cv.CV_8UC3);
|
||||
cv.cvtColor(image, output, cv.COLOR_RGBA2RGB);
|
||||
let boxNum = boxes.length;
|
||||
for (let i = 0; i < boxNum; ++i) {
|
||||
if (boxes[i].toDraw) {
|
||||
drawBox(boxes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
|
||||
// Calculate the IOU(Intersection over Union) of two boxes
|
||||
function IOU(box1, box2) {
|
||||
let bounding1 = box1.bounding;
|
||||
let bounding2 = box2.bounding;
|
||||
let s1 = bounding1[2] * bounding1[3];
|
||||
let s2 = bounding2[2] * bounding2[3];
|
||||
|
||||
let left1 = bounding1[0];
|
||||
let right1 = left1 + bounding1[2];
|
||||
let left2 = bounding2[0];
|
||||
let right2 = left2 + bounding2[2];
|
||||
let overlapW = calOverlap([left1, right1], [left2, right2]);
|
||||
|
||||
let top1 = bounding2[1];
|
||||
let bottom1 = top1 + bounding1[3];
|
||||
let top2 = bounding2[1];
|
||||
let bottom2 = top2 + bounding2[3];
|
||||
let overlapH = calOverlap([top1, bottom1], [top2, bottom2]);
|
||||
|
||||
let overlapS = overlapW * overlapH;
|
||||
return overlapS / (s1 + s2 + overlapS);
|
||||
}
|
||||
|
||||
// Calculate the overlap range of two vector
|
||||
function calOverlap(range1, range2) {
|
||||
let min1 = range1[0];
|
||||
let max1 = range1[1];
|
||||
let min2 = range2[0];
|
||||
let max2 = range2[1];
|
||||
|
||||
if (min2 > min1 && min2 < max1) {
|
||||
return max1 - min2;
|
||||
} else if (max2 > min1 && max2 < max1) {
|
||||
return max2 - min1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw one predict box into the origin image
|
||||
function drawBox(box) {
|
||||
let bounding = box.bounding;
|
||||
let left = bounding[0];
|
||||
let top = bounding[1];
|
||||
let width = bounding[2];
|
||||
let height = bounding[3];
|
||||
|
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + height),
|
||||
new cv.Scalar(0, 255, 0));
|
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + 15),
|
||||
new cv.Scalar(255, 255, 255), cv.FILLED);
|
||||
let text = `${labels[box.classId]}: ${box.confidence.toFixed(4)}`;
|
||||
cv.putText(output, text, new cv.Point(left, top + 10), cv.FONT_HERSHEY_SIMPLEX, 0.3,
|
||||
new cv.Scalar(0, 0, 0));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
let jsonUrl = "js_object_detection_model_info.json";
|
||||
drawInfoTable(jsonUrl, 'appendix');
|
||||
|
||||
let utils = new Utils('errorMessage');
|
||||
utils.loadCode('codeSnippet', 'codeEditor');
|
||||
utils.loadCode('codeSnippet1', 'codeEditor1');
|
||||
|
||||
let loadLablesCode = 'loadLables = ' + loadLables.toString();
|
||||
document.getElementById('codeEditor2').value = loadLablesCode;
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
|
||||
document.getElementById('codeEditor3').value = getBlobFromImageCode;
|
||||
let loadModelCode = 'loadModel = ' + loadModel.toString();
|
||||
document.getElementById('codeEditor4').value = loadModelCode;
|
||||
|
||||
utils.loadCode('codeSnippet5', 'codeEditor5');
|
||||
|
||||
let canvas = document.getElementById('canvasInput');
|
||||
let ctx = canvas.getContext('2d');
|
||||
let img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.src = 'lena.png';
|
||||
img.onload = function() {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
let tryIt = document.getElementById('tryIt');
|
||||
tryIt.addEventListener('click', () => {
|
||||
initStatus();
|
||||
document.getElementById('status').innerHTML = 'Running function main()...';
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
if (modelPath === "") {
|
||||
document.getElementById('status').innerHTML = 'Runing failed.';
|
||||
utils.printError('Please upload model file by clicking the button first.');
|
||||
} else {
|
||||
setTimeout(main, 1);
|
||||
}
|
||||
});
|
||||
|
||||
let fileInput = document.getElementById('fileInput');
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
initStatus();
|
||||
loadImageToCanvas(e, 'canvasInput');
|
||||
});
|
||||
|
||||
let configPath = "";
|
||||
let configFile = document.getElementById('configFile');
|
||||
configFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
configPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
|
||||
});
|
||||
|
||||
let modelPath = "";
|
||||
let modelFile = document.getElementById('modelFile');
|
||||
modelFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
modelPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
|
||||
configPath = "";
|
||||
configFile.value = "";
|
||||
});
|
||||
|
||||
utils.loadOpenCv(() => {
|
||||
tryIt.removeAttribute('disabled');
|
||||
});
|
||||
|
||||
var main = async function() {};
|
||||
var postProcess = function(result, labels) {};
|
||||
|
||||
utils.executeCode('codeEditor1');
|
||||
utils.executeCode('codeEditor2');
|
||||
utils.executeCode('codeEditor3');
|
||||
utils.executeCode('codeEditor4');
|
||||
utils.executeCode('codeEditor5');
|
||||
|
||||
|
||||
function updateResult(output, time) {
|
||||
try{
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
canvasOutput.style.visibility = "visible";
|
||||
cv.imshow('canvasOutput', output);
|
||||
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
|
||||
<b>Inference time:</b> ${time.toFixed(2)} ms`;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function initStatus() {
|
||||
document.getElementById('status').innerHTML = '';
|
||||
document.getElementById('canvasOutput').style.visibility = "hidden";
|
||||
utils.clearError();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"caffe": [
|
||||
{
|
||||
"model": "mobilenet_SSD",
|
||||
"inputSize": "300, 300",
|
||||
"mean": "127.5, 127.5, 127.5",
|
||||
"std": "0.007843",
|
||||
"swapRB": "false",
|
||||
"outType": "SSD",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt",
|
||||
"modelUrl": "https://raw.githubusercontent.com/chuanqi305/MobileNet-SSD/master/mobilenet_iter_73000.caffemodel",
|
||||
"configUrl": "https://raw.githubusercontent.com/chuanqi305/MobileNet-SSD/master/deploy.prototxt"
|
||||
},
|
||||
{
|
||||
"model": "VGG_SSD",
|
||||
"inputSize": "300, 300",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"outType": "SSD",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt",
|
||||
"modelUrl": "https://drive.google.com/uc?id=0BzKzrI_SkD1_WVVTSmQxU0dVRzA&export=download",
|
||||
"configUrl": "https://drive.google.com/uc?id=0BzKzrI_SkD1_WVVTSmQxU0dVRzA&export=download"
|
||||
}
|
||||
],
|
||||
"darknet": [
|
||||
{
|
||||
"model": "yolov2_tiny",
|
||||
"inputSize": "416, 416",
|
||||
"mean": "0, 0, 0",
|
||||
"std": "0.00392",
|
||||
"swapRB": "false",
|
||||
"outType": "YOLO",
|
||||
"labelsUrl": "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_yolov3.txt",
|
||||
"modelUrl": "https://pjreddie.com/media/files/yolov2-tiny.weights",
|
||||
"configUrl": "https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov2-tiny.cfg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Object Detection Example with Camera</title>
|
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Object Detection Example with Camera </h2>
|
||||
<p>
|
||||
This tutorial shows you how to write an object detection example with camera.<br>
|
||||
To try the example you should click the <b>modelFile</b> button(and <b>configInput</b> button if needed) to upload inference model.
|
||||
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
|
||||
Then You should change the parameters in the first code snippet according to the uploaded model.
|
||||
Finally click <b>Start/Stop</b> button to start or stop the camera capture.<br>
|
||||
</p>
|
||||
|
||||
<div class="control"><button id="startAndStop" disabled>Start</button></div>
|
||||
<div>
|
||||
<table cellpadding="0" cellspacing="0" width="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<video id="videoInput" width="400" height="400"></video>
|
||||
</td>
|
||||
<td>
|
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
videoInput
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p id='status' align="left"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
modelFile <input type="file" id="modelFile" name="file">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
configFile <input type="file" id="configFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="err" id="errorMessage"></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Help function</h3>
|
||||
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
|
||||
<textarea class="code" rows="15" cols="100" id="codeEditor" spellcheck="false"></textarea>
|
||||
<p>2.The function to capture video from camera, and the main loop in which will do inference once.</p>
|
||||
<textarea class="code" rows="34" cols="100" id="codeEditor1" spellcheck="false"></textarea>
|
||||
<p>3.Load labels from txt file and process it into an array.</p>
|
||||
<textarea class="code" rows="7" cols="100" id="codeEditor2" spellcheck="false"></textarea>
|
||||
<p>4.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
|
||||
<p>5.Fetch model file and save to emscripten file system once click the input button.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor4" spellcheck="false"></textarea>
|
||||
<p>6.The post-processing, including get boxes from output and draw boxes into the image.</p>
|
||||
<textarea class="code" rows="35" cols="100" id="codeEditor5" spellcheck="false"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="appendix">
|
||||
<h2>Model Info:</h2>
|
||||
</div>
|
||||
|
||||
<script src="utils.js" type="text/javascript"></script>
|
||||
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
|
||||
|
||||
<script id="codeSnippet" type="text/code-snippet">
|
||||
inputSize = [300, 300];
|
||||
mean = [127.5, 127.5, 127.5];
|
||||
std = 0.007843;
|
||||
swapRB = false;
|
||||
confThreshold = 0.5;
|
||||
nmsThreshold = 0.4;
|
||||
|
||||
// the type of output, can be YOLO or SSD
|
||||
outType = "SSD";
|
||||
|
||||
// url for label file, can from local or Internet
|
||||
labelsUrl = "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/object_detection_classes_pascal_voc.txt";
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet">
|
||||
let frame = new cv.Mat(videoInput.height, videoInput.width, cv.CV_8UC4);
|
||||
let cap = new cv.VideoCapture(videoInput);
|
||||
|
||||
main = async function(frame) {
|
||||
const labels = await loadLables(labelsUrl);
|
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, frame);
|
||||
let net = cv.readNet(configPath, modelPath);
|
||||
net.setInput(input);
|
||||
const start = performance.now();
|
||||
const result = net.forward();
|
||||
const time = performance.now()-start;
|
||||
const output = postProcess(result, labels, frame);
|
||||
|
||||
updateResult(output, time);
|
||||
setTimeout(processVideo, 0);
|
||||
input.delete();
|
||||
net.delete();
|
||||
result.delete();
|
||||
}
|
||||
|
||||
function processVideo() {
|
||||
try {
|
||||
if (!streaming) {
|
||||
return;
|
||||
}
|
||||
cap.read(frame);
|
||||
main(frame);
|
||||
} catch (err) {
|
||||
utils.printError(err);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(processVideo, 0);
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet">
|
||||
postProcess = function(result, labels, frame) {
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
const outputWidth = canvasOutput.width;
|
||||
const outputHeight = canvasOutput.height;
|
||||
const resultData = result.data32F;
|
||||
|
||||
// Get the boxes(with class and confidence) from the output
|
||||
let boxes = [];
|
||||
switch(outType) {
|
||||
case "YOLO": {
|
||||
const vecNum = result.matSize[0];
|
||||
const vecLength = result.matSize[1];
|
||||
const classNum = vecLength - 5;
|
||||
|
||||
for (let i = 0; i < vecNum; ++i) {
|
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength);
|
||||
let scores = vector.slice(5, vecLength);
|
||||
let classId = scores.indexOf(Math.max(...scores));
|
||||
let confidence = scores[classId];
|
||||
if (confidence > confThreshold) {
|
||||
let center_x = Math.round(vector[0] * outputWidth);
|
||||
let center_y = Math.round(vector[1] * outputHeight);
|
||||
let width = Math.round(vector[2] * outputWidth);
|
||||
let height = Math.round(vector[3] * outputHeight);
|
||||
let left = Math.round(center_x - width / 2);
|
||||
let top = Math.round(center_y - height / 2);
|
||||
|
||||
let box = {
|
||||
scores: scores,
|
||||
classId: classId,
|
||||
confidence: confidence,
|
||||
bounding: [left, top, width, height],
|
||||
toDraw: true
|
||||
}
|
||||
boxes.push(box);
|
||||
}
|
||||
}
|
||||
|
||||
// NMS(Non Maximum Suppression) algorithm
|
||||
let boxNum = boxes.length;
|
||||
let tmp_boxes = [];
|
||||
let sorted_boxes = [];
|
||||
for (let c = 0; c < classNum; ++c) {
|
||||
for (let i = 0; i < boxes.length; ++i) {
|
||||
tmp_boxes[i] = [boxes[i], i];
|
||||
}
|
||||
sorted_boxes = tmp_boxes.sort((a, b) => { return (b[0].scores[c] - a[0].scores[c]); });
|
||||
for (let i = 0; i < boxNum; ++i) {
|
||||
if (sorted_boxes[i][0].scores[c] === 0) continue;
|
||||
else {
|
||||
for (let j = i + 1; j < boxNum; ++j) {
|
||||
if (IOU(sorted_boxes[i][0], sorted_boxes[j][0]) >= nmsThreshold) {
|
||||
boxes[sorted_boxes[j][1]].toDraw = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case "SSD": {
|
||||
const vecNum = result.matSize[2];
|
||||
const vecLength = 7;
|
||||
|
||||
for (let i = 0; i < vecNum; ++i) {
|
||||
let vector = resultData.slice(i*vecLength, (i+1)*vecLength);
|
||||
let confidence = vector[2];
|
||||
if (confidence > confThreshold) {
|
||||
let left, top, right, bottom, width, height;
|
||||
left = Math.round(vector[3]);
|
||||
top = Math.round(vector[4]);
|
||||
right = Math.round(vector[5]);
|
||||
bottom = Math.round(vector[6]);
|
||||
width = right - left + 1;
|
||||
height = bottom - top + 1;
|
||||
if (width <= 2 || height <= 2) {
|
||||
left = Math.round(vector[3] * outputWidth);
|
||||
top = Math.round(vector[4] * outputHeight);
|
||||
right = Math.round(vector[5] * outputWidth);
|
||||
bottom = Math.round(vector[6] * outputHeight);
|
||||
width = right - left + 1;
|
||||
height = bottom - top + 1;
|
||||
}
|
||||
let box = {
|
||||
classId: vector[1] - 1,
|
||||
confidence: confidence,
|
||||
bounding: [left, top, width, height],
|
||||
toDraw: true
|
||||
}
|
||||
boxes.push(box);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
console.error(`Unsupported output type ${outType}`)
|
||||
}
|
||||
|
||||
// Draw the saved box into the image
|
||||
let output = new cv.Mat(outputWidth, outputHeight, cv.CV_8UC3);
|
||||
cv.cvtColor(frame, output, cv.COLOR_RGBA2RGB);
|
||||
let boxNum = boxes.length;
|
||||
for (let i = 0; i < boxNum; ++i) {
|
||||
if (boxes[i].toDraw) {
|
||||
drawBox(boxes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
|
||||
// Calculate the IOU(Intersection over Union) of two boxes
|
||||
function IOU(box1, box2) {
|
||||
let bounding1 = box1.bounding;
|
||||
let bounding2 = box2.bounding;
|
||||
let s1 = bounding1[2] * bounding1[3];
|
||||
let s2 = bounding2[2] * bounding2[3];
|
||||
|
||||
let left1 = bounding1[0];
|
||||
let right1 = left1 + bounding1[2];
|
||||
let left2 = bounding2[0];
|
||||
let right2 = left2 + bounding2[2];
|
||||
let overlapW = calOverlap([left1, right1], [left2, right2]);
|
||||
|
||||
let top1 = bounding2[1];
|
||||
let bottom1 = top1 + bounding1[3];
|
||||
let top2 = bounding2[1];
|
||||
let bottom2 = top2 + bounding2[3];
|
||||
let overlapH = calOverlap([top1, bottom1], [top2, bottom2]);
|
||||
|
||||
let overlapS = overlapW * overlapH;
|
||||
return overlapS / (s1 + s2 + overlapS);
|
||||
}
|
||||
|
||||
// Calculate the overlap range of two vector
|
||||
function calOverlap(range1, range2) {
|
||||
let min1 = range1[0];
|
||||
let max1 = range1[1];
|
||||
let min2 = range2[0];
|
||||
let max2 = range2[1];
|
||||
|
||||
if (min2 > min1 && min2 < max1) {
|
||||
return max1 - min2;
|
||||
} else if (max2 > min1 && max2 < max1) {
|
||||
return max2 - min1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw one predict box into the origin image
|
||||
function drawBox(box) {
|
||||
let bounding = box.bounding;
|
||||
let left = bounding[0];
|
||||
let top = bounding[1];
|
||||
let width = bounding[2];
|
||||
let height = bounding[3];
|
||||
|
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + height),
|
||||
new cv.Scalar(0, 255, 0));
|
||||
cv.rectangle(output, new cv.Point(left, top), new cv.Point(left + width, top + 15),
|
||||
new cv.Scalar(255, 255, 255), cv.FILLED);
|
||||
let text = `${labels[box.classId]}: ${box.confidence.toFixed(4)}`;
|
||||
cv.putText(output, text, new cv.Point(left, top + 10), cv.FONT_HERSHEY_SIMPLEX, 0.3,
|
||||
new cv.Scalar(0, 0, 0));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
let jsonUrl = "js_object_detection_model_info.json";
|
||||
drawInfoTable(jsonUrl, 'appendix');
|
||||
|
||||
let utils = new Utils('errorMessage');
|
||||
utils.loadCode('codeSnippet', 'codeEditor');
|
||||
utils.loadCode('codeSnippet1', 'codeEditor1');
|
||||
|
||||
let loadLablesCode = 'loadLables = ' + loadLables.toString();
|
||||
document.getElementById('codeEditor2').value = loadLablesCode;
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
|
||||
document.getElementById('codeEditor3').value = getBlobFromImageCode;
|
||||
let loadModelCode = 'loadModel = ' + loadModel.toString();
|
||||
document.getElementById('codeEditor4').value = loadModelCode;
|
||||
|
||||
utils.loadCode('codeSnippet5', 'codeEditor5');
|
||||
|
||||
let videoInput = document.getElementById('videoInput');
|
||||
let streaming = false;
|
||||
let startAndStop = document.getElementById('startAndStop');
|
||||
startAndStop.addEventListener('click', () => {
|
||||
if (!streaming) {
|
||||
utils.clearError();
|
||||
utils.startCamera('qvga', onVideoStarted, 'videoInput');
|
||||
} else {
|
||||
utils.stopCamera();
|
||||
onVideoStopped();
|
||||
}
|
||||
});
|
||||
|
||||
let configPath = "";
|
||||
let configFile = document.getElementById('configFile');
|
||||
configFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
configPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
|
||||
});
|
||||
|
||||
let modelPath = "";
|
||||
let modelFile = document.getElementById('modelFile');
|
||||
modelFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
modelPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
|
||||
configPath = "";
|
||||
configFile.value = "";
|
||||
});
|
||||
|
||||
utils.loadOpenCv(() => {
|
||||
startAndStop.removeAttribute('disabled');
|
||||
});
|
||||
|
||||
var main = async function(frame) {};
|
||||
var postProcess = function(result, labels, frame) {};
|
||||
|
||||
utils.executeCode('codeEditor1');
|
||||
utils.executeCode('codeEditor2');
|
||||
utils.executeCode('codeEditor3');
|
||||
utils.executeCode('codeEditor4');
|
||||
utils.executeCode('codeEditor5');
|
||||
|
||||
function onVideoStarted() {
|
||||
streaming = true;
|
||||
startAndStop.innerText = 'Stop';
|
||||
videoInput.width = videoInput.videoWidth;
|
||||
videoInput.height = videoInput.videoHeight;
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
}
|
||||
|
||||
function onVideoStopped() {
|
||||
streaming = false;
|
||||
startAndStop.innerText = 'Start';
|
||||
initStatus();
|
||||
}
|
||||
|
||||
function updateResult(output, time) {
|
||||
try{
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
canvasOutput.style.visibility = "visible";
|
||||
cv.imshow('canvasOutput', output);
|
||||
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
|
||||
<b>Inference time:</b> ${time.toFixed(2)} ms`;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function initStatus() {
|
||||
document.getElementById('status').innerHTML = '';
|
||||
document.getElementById('canvasOutput').style.visibility = "hidden";
|
||||
utils.clearError();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,327 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Pose Estimation Example</title>
|
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Pose Estimation Example</h2>
|
||||
<p>
|
||||
This tutorial shows you how to write an pose estimation example with OpenCV.js.<br>
|
||||
To try the example you should click the <b>modelFile</b> button(and <b>configInput</b> button if needed) to upload inference model.
|
||||
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
|
||||
Then You should change the parameters in the first code snippet according to the uploaded model.
|
||||
Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
|
||||
</p>
|
||||
|
||||
<div class="control"><button id="tryIt" disabled>Try it</button></div>
|
||||
<div>
|
||||
<table cellpadding="0" cellspacing="0" width="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<canvas id="canvasInput" width="400" height="250"></canvas>
|
||||
</td>
|
||||
<td>
|
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="250"></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p id='status' align="left"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
modelFile <input type="file" id="modelFile" name="file">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
configFile <input type="file" id="configFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="err" id="errorMessage"></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Help function</h3>
|
||||
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
|
||||
<textarea class="code" rows="9" cols="100" id="codeEditor" spellcheck="false"></textarea>
|
||||
<p>2.Main loop in which will read the image from canvas and do inference once.</p>
|
||||
<textarea class="code" rows="15" cols="100" id="codeEditor1" spellcheck="false"></textarea>
|
||||
<p>3.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor2" spellcheck="false"></textarea>
|
||||
<p>4.Fetch model file and save to emscripten file system once click the input button.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
|
||||
<p>5.The pairs of keypoints of different dataset.</p>
|
||||
<textarea class="code" rows="30" cols="100" id="codeEditor4" spellcheck="false"></textarea>
|
||||
<p>6.The post-processing, including get the predicted points and draw lines into the image.</p>
|
||||
<textarea class="code" rows="30" cols="100" id="codeEditor5" spellcheck="false"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="appendix">
|
||||
<h2>Model Info:</h2>
|
||||
</div>
|
||||
|
||||
<script src="utils.js" type="text/javascript"></script>
|
||||
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
|
||||
|
||||
<script id="codeSnippet" type="text/code-snippet">
|
||||
inputSize = [368, 368];
|
||||
mean = [0, 0, 0];
|
||||
std = 0.00392;
|
||||
swapRB = false;
|
||||
threshold = 0.1;
|
||||
|
||||
// the pairs of keypoint, can be "COCO", "MPI" and "BODY_25"
|
||||
dataset = "COCO";
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet">
|
||||
main = async function() {
|
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
|
||||
let net = cv.readNet(configPath, modelPath);
|
||||
net.setInput(input);
|
||||
const start = performance.now();
|
||||
const result = net.forward();
|
||||
const time = performance.now()-start;
|
||||
const output = postProcess(result);
|
||||
|
||||
updateResult(output, time);
|
||||
input.delete();
|
||||
net.delete();
|
||||
result.delete();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet4" type="text/code-snippet">
|
||||
BODY_PARTS = {};
|
||||
POSE_PAIRS = [];
|
||||
|
||||
if (dataset === 'COCO') {
|
||||
BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
|
||||
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,
|
||||
"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14,
|
||||
"LEye": 15, "REar": 16, "LEar": 17, "Background": 18 };
|
||||
|
||||
POSE_PAIRS = [ ["Neck", "RShoulder"], ["Neck", "LShoulder"], ["RShoulder", "RElbow"],
|
||||
["RElbow", "RWrist"], ["LShoulder", "LElbow"], ["LElbow", "LWrist"],
|
||||
["Neck", "RHip"], ["RHip", "RKnee"], ["RKnee", "RAnkle"], ["Neck", "LHip"],
|
||||
["LHip", "LKnee"], ["LKnee", "LAnkle"], ["Neck", "Nose"], ["Nose", "REye"],
|
||||
["REye", "REar"], ["Nose", "LEye"], ["LEye", "LEar"] ]
|
||||
} else if (dataset === 'MPI') {
|
||||
BODY_PARTS = { "Head": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
|
||||
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,
|
||||
"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "Chest": 14,
|
||||
"Background": 15 }
|
||||
|
||||
POSE_PAIRS = [ ["Head", "Neck"], ["Neck", "RShoulder"], ["RShoulder", "RElbow"],
|
||||
["RElbow", "RWrist"], ["Neck", "LShoulder"], ["LShoulder", "LElbow"],
|
||||
["LElbow", "LWrist"], ["Neck", "Chest"], ["Chest", "RHip"], ["RHip", "RKnee"],
|
||||
["RKnee", "RAnkle"], ["Chest", "LHip"], ["LHip", "LKnee"], ["LKnee", "LAnkle"] ]
|
||||
} else if (dataset === 'BODY_25') {
|
||||
BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
|
||||
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "MidHip": 8, "RHip": 9,
|
||||
"RKnee": 10, "RAnkle": 11, "LHip": 12, "LKnee": 13, "LAnkle": 14,
|
||||
"REye": 15, "LEye": 16, "REar": 17, "LEar": 18, "LBigToe": 19,
|
||||
"LSmallToe": 20, "LHeel": 21, "RBigToe": 22, "RSmallToe": 23,
|
||||
"RHeel": 24, "Background": 25 }
|
||||
|
||||
POSE_PAIRS = [ ["Neck", "Nose"], ["Neck", "RShoulder"],
|
||||
["Neck", "LShoulder"], ["RShoulder", "RElbow"],
|
||||
["RElbow", "RWrist"], ["LShoulder", "LElbow"],
|
||||
["LElbow", "LWrist"], ["Nose", "REye"],
|
||||
["REye", "REar"], ["Neck", "LEye"],
|
||||
["LEye", "LEar"], ["Neck", "MidHip"],
|
||||
["MidHip", "RHip"], ["RHip", "RKnee"],
|
||||
["RKnee", "RAnkle"], ["RAnkle", "RBigToe"],
|
||||
["RBigToe", "RSmallToe"], ["RAnkle", "RHeel"],
|
||||
["MidHip", "LHip"], ["LHip", "LKnee"],
|
||||
["LKnee", "LAnkle"], ["LAnkle", "LBigToe"],
|
||||
["LBigToe", "LSmallToe"], ["LAnkle", "LHeel"] ]
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet5" type="text/code-snippet">
|
||||
postProcess = function(result) {
|
||||
const resultData = result.data32F;
|
||||
const matSize = result.matSize;
|
||||
const size1 = matSize[1];
|
||||
const size2 = matSize[2];
|
||||
const size3 = matSize[3];
|
||||
const mapSize = size2 * size3;
|
||||
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
const outputWidth = canvasOutput.width;
|
||||
const outputHeight = canvasOutput.height;
|
||||
|
||||
let image = cv.imread("canvasInput");
|
||||
let output = new cv.Mat(outputWidth, outputHeight, cv.CV_8UC3);
|
||||
cv.cvtColor(image, output, cv.COLOR_RGBA2RGB);
|
||||
|
||||
// get position of keypoints from output
|
||||
let points = [];
|
||||
for (let i = 0; i < Object.keys(BODY_PARTS).length; ++i) {
|
||||
heatMap = resultData.slice(i*mapSize, (i+1)*mapSize);
|
||||
|
||||
let maxIndex = 0;
|
||||
let maxConf = heatMap[0];
|
||||
for (index in heatMap) {
|
||||
if (heatMap[index] > heatMap[maxIndex]) {
|
||||
maxIndex = index;
|
||||
maxConf = heatMap[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (maxConf > threshold) {
|
||||
indexX = maxIndex % size3;
|
||||
indexY = maxIndex / size3;
|
||||
|
||||
x = outputWidth * indexX / size3;
|
||||
y = outputHeight * indexY / size2;
|
||||
|
||||
points[i] = [Math.round(x), Math.round(y)];
|
||||
}
|
||||
}
|
||||
|
||||
// draw the points and lines into the image
|
||||
for (pair of POSE_PAIRS) {
|
||||
partFrom = pair[0];
|
||||
partTo = pair[1];
|
||||
idFrom = BODY_PARTS[partFrom];
|
||||
idTo = BODY_PARTS[partTo];
|
||||
pointFrom = points[idFrom];
|
||||
pointTo = points[idTo];
|
||||
|
||||
if (points[idFrom] && points[idTo]) {
|
||||
cv.line(output, new cv.Point(pointFrom[0], pointFrom[1]),
|
||||
new cv.Point(pointTo[0], pointTo[1]), new cv.Scalar(0, 255, 0), 3);
|
||||
cv.ellipse(output, new cv.Point(pointFrom[0], pointFrom[1]), new cv.Size(3, 3), 0, 0, 360,
|
||||
new cv.Scalar(0, 0, 255), cv.FILLED);
|
||||
cv.ellipse(output, new cv.Point(pointTo[0], pointTo[1]), new cv.Size(3, 3), 0, 0, 360,
|
||||
new cv.Scalar(0, 0, 255), cv.FILLED);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
let jsonUrl = "js_pose_estimation_model_info.json";
|
||||
drawInfoTable(jsonUrl, 'appendix');
|
||||
|
||||
let utils = new Utils('errorMessage');
|
||||
utils.loadCode('codeSnippet', 'codeEditor');
|
||||
utils.loadCode('codeSnippet1', 'codeEditor1');
|
||||
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
|
||||
document.getElementById('codeEditor2').value = getBlobFromImageCode;
|
||||
let loadModelCode = 'loadModel = ' + loadModel.toString();
|
||||
document.getElementById('codeEditor3').value = loadModelCode;
|
||||
|
||||
utils.loadCode('codeSnippet4', 'codeEditor4');
|
||||
utils.loadCode('codeSnippet5', 'codeEditor5');
|
||||
|
||||
let canvas = document.getElementById('canvasInput');
|
||||
let ctx = canvas.getContext('2d');
|
||||
let img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.src = 'roi.jpg';
|
||||
img.onload = function() {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
let tryIt = document.getElementById('tryIt');
|
||||
tryIt.addEventListener('click', () => {
|
||||
initStatus();
|
||||
document.getElementById('status').innerHTML = 'Running function main()...';
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
if (modelPath === "") {
|
||||
document.getElementById('status').innerHTML = 'Runing failed.';
|
||||
utils.printError('Please upload model file by clicking the button first.');
|
||||
} else {
|
||||
setTimeout(main, 1);
|
||||
}
|
||||
});
|
||||
|
||||
let fileInput = document.getElementById('fileInput');
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
initStatus();
|
||||
loadImageToCanvas(e, 'canvasInput');
|
||||
});
|
||||
|
||||
let configPath = "";
|
||||
let configFile = document.getElementById('configFile');
|
||||
configFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
configPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
|
||||
});
|
||||
|
||||
let modelPath = "";
|
||||
let modelFile = document.getElementById('modelFile');
|
||||
modelFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
modelPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
|
||||
configPath = "";
|
||||
configFile.value = "";
|
||||
});
|
||||
|
||||
utils.loadOpenCv(() => {
|
||||
tryIt.removeAttribute('disabled');
|
||||
});
|
||||
|
||||
var main = async function() {};
|
||||
var postProcess = function(result) {};
|
||||
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
utils.executeCode('codeEditor2');
|
||||
utils.executeCode('codeEditor3');
|
||||
utils.executeCode('codeEditor4');
|
||||
utils.executeCode('codeEditor5');
|
||||
|
||||
function updateResult(output, time) {
|
||||
try{
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
canvasOutput.style.visibility = "visible";
|
||||
let resized = new cv.Mat(canvasOutput.width, canvasOutput.height, cv.CV_8UC4);
|
||||
cv.resize(output, resized, new cv.Size(canvasOutput.width, canvasOutput.height));
|
||||
cv.imshow('canvasOutput', resized);
|
||||
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
|
||||
<b>Inference time:</b> ${time.toFixed(2)} ms`;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function initStatus() {
|
||||
document.getElementById('status').innerHTML = '';
|
||||
document.getElementById('canvasOutput').style.visibility = "hidden";
|
||||
utils.clearError();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"caffe": [
|
||||
{
|
||||
"model": "body_25",
|
||||
"inputSize": "368, 368",
|
||||
"mean": "0, 0, 0",
|
||||
"std": "0.00392",
|
||||
"swapRB": "false",
|
||||
"dataset": "BODY_25",
|
||||
"modelUrl": "http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/body_25/pose_iter_584000.caffemodel",
|
||||
"configUrl": "https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/pose/body_25/pose_deploy.prototxt"
|
||||
},
|
||||
{
|
||||
"model": "coco",
|
||||
"inputSize": "368, 368",
|
||||
"mean": "0, 0, 0",
|
||||
"std": "0.00392",
|
||||
"swapRB": "false",
|
||||
"dataset": "COCO",
|
||||
"modelUrl": "http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/coco/pose_iter_440000.caffemodel",
|
||||
"configUrl": "https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/pose/coco/pose_deploy_linevec.prototxt"
|
||||
},
|
||||
{
|
||||
"model": "mpi",
|
||||
"inputSize": "368, 368",
|
||||
"mean": "0, 0, 0",
|
||||
"std": "0.00392",
|
||||
"swapRB": "false",
|
||||
"dataset": "MPI",
|
||||
"modelUrl": "http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/mpi/pose_iter_160000.caffemodel",
|
||||
"configUrl": "https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/pose/mpi/pose_deploy_linevec.prototxt"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Semantic Segmentation Example</title>
|
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Semantic Segmentation Example</h2>
|
||||
<p>
|
||||
This tutorial shows you how to write an semantic segmentation example with OpenCV.js.<br>
|
||||
To try the example you should click the <b>modelFile</b> button(and <b>configInput</b> button if needed) to upload inference model.
|
||||
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
|
||||
Then You should change the parameters in the first code snippet according to the uploaded model.
|
||||
Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
|
||||
</p>
|
||||
|
||||
<div class="control"><button id="tryIt" disabled>Try it</button></div>
|
||||
<div>
|
||||
<table cellpadding="0" cellspacing="0" width="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<canvas id="canvasInput" width="400" height="400"></canvas>
|
||||
</td>
|
||||
<td>
|
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p id='status' align="left"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
modelFile <input type="file" id="modelFile" name="file">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
configFile <input type="file" id="configFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="err" id="errorMessage"></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Help function</h3>
|
||||
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
|
||||
<textarea class="code" rows="5" cols="100" id="codeEditor" spellcheck="false"></textarea>
|
||||
<p>2.Main loop in which will read the image from canvas and do inference once.</p>
|
||||
<textarea class="code" rows="16" cols="100" id="codeEditor1" spellcheck="false"></textarea>
|
||||
<p>3.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor2" spellcheck="false"></textarea>
|
||||
<p>4.Fetch model file and save to emscripten file system once click the input button.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
|
||||
<p>5.The post-processing, including gengerate colors for different classes and argmax to get the classes for each pixel.</p>
|
||||
<textarea class="code" rows="34" cols="100" id="codeEditor4" spellcheck="false"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="appendix">
|
||||
<h2>Model Info:</h2>
|
||||
</div>
|
||||
|
||||
<script src="utils.js" type="text/javascript"></script>
|
||||
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
|
||||
|
||||
<script id="codeSnippet" type="text/code-snippet">
|
||||
inputSize = [513, 513];
|
||||
mean = [127.5, 127.5, 127.5];
|
||||
std = 0.007843;
|
||||
swapRB = false;
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet">
|
||||
main = async function() {
|
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
|
||||
let net = cv.readNet(configPath, modelPath);
|
||||
net.setInput(input);
|
||||
const start = performance.now();
|
||||
const result = net.forward();
|
||||
const time = performance.now()-start;
|
||||
const colors = generateColors(result);
|
||||
const output = argmax(result, colors);
|
||||
|
||||
updateResult(output, time);
|
||||
input.delete();
|
||||
net.delete();
|
||||
result.delete();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet4" type="text/code-snippet">
|
||||
generateColors = function(result) {
|
||||
const numClasses = result.matSize[1];
|
||||
let colors = [0,0,0];
|
||||
while(colors.length < numClasses*3){
|
||||
colors.push(Math.round((Math.random()*255 + colors[colors.length-3]) / 2));
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
argmax = function(result, colors) {
|
||||
const C = result.matSize[1];
|
||||
const H = result.matSize[2];
|
||||
const W = result.matSize[3];
|
||||
const resultData = result.data32F;
|
||||
const imgSize = H*W;
|
||||
|
||||
let classId = [];
|
||||
for (i = 0; i<imgSize; ++i) {
|
||||
let id = 0;
|
||||
for (j = 0; j < C; ++j) {
|
||||
if (resultData[j*imgSize+i] > resultData[id*imgSize+i]) {
|
||||
id = j;
|
||||
}
|
||||
}
|
||||
classId.push(colors[id*3]);
|
||||
classId.push(colors[id*3+1]);
|
||||
classId.push(colors[id*3+2]);
|
||||
classId.push(255);
|
||||
}
|
||||
|
||||
output = cv.matFromArray(H,W,cv.CV_8UC4,classId);
|
||||
return output;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
let jsonUrl = "js_semantic_segmentation_model_info.json";
|
||||
drawInfoTable(jsonUrl, 'appendix');
|
||||
|
||||
let utils = new Utils('errorMessage');
|
||||
utils.loadCode('codeSnippet', 'codeEditor');
|
||||
utils.loadCode('codeSnippet1', 'codeEditor1');
|
||||
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
|
||||
document.getElementById('codeEditor2').value = getBlobFromImageCode;
|
||||
let loadModelCode = 'loadModel = ' + loadModel.toString();
|
||||
document.getElementById('codeEditor3').value = loadModelCode;
|
||||
|
||||
utils.loadCode('codeSnippet4', 'codeEditor4');
|
||||
|
||||
let canvas = document.getElementById('canvasInput');
|
||||
let ctx = canvas.getContext('2d');
|
||||
let img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.src = 'roi.jpg';
|
||||
img.onload = function() {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
let tryIt = document.getElementById('tryIt');
|
||||
tryIt.addEventListener('click', () => {
|
||||
initStatus();
|
||||
document.getElementById('status').innerHTML = 'Running function main()...';
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
if (modelPath === "") {
|
||||
document.getElementById('status').innerHTML = 'Runing failed.';
|
||||
utils.printError('Please upload model file by clicking the button first.');
|
||||
} else {
|
||||
setTimeout(main, 1);
|
||||
}
|
||||
});
|
||||
|
||||
let fileInput = document.getElementById('fileInput');
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
initStatus();
|
||||
loadImageToCanvas(e, 'canvasInput');
|
||||
});
|
||||
|
||||
let configPath = "";
|
||||
let configFile = document.getElementById('configFile');
|
||||
configFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
configPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
|
||||
});
|
||||
|
||||
let modelPath = "";
|
||||
let modelFile = document.getElementById('modelFile');
|
||||
modelFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
modelPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
|
||||
configPath = "";
|
||||
configFile.value = "";
|
||||
});
|
||||
|
||||
utils.loadOpenCv(() => {
|
||||
tryIt.removeAttribute('disabled');
|
||||
});
|
||||
|
||||
var main = async function() {};
|
||||
var generateColors = function(result) {};
|
||||
var argmax = function(result, colors) {};
|
||||
|
||||
utils.executeCode('codeEditor1');
|
||||
utils.executeCode('codeEditor2');
|
||||
utils.executeCode('codeEditor3');
|
||||
utils.executeCode('codeEditor4');
|
||||
|
||||
function updateResult(output, time) {
|
||||
try{
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
canvasOutput.style.visibility = "visible";
|
||||
let resized = new cv.Mat(canvasOutput.width, canvasOutput.height, cv.CV_8UC4);
|
||||
cv.resize(output, resized, new cv.Size(canvasOutput.width, canvasOutput.height));
|
||||
cv.imshow('canvasOutput', resized);
|
||||
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
|
||||
<b>Inference time:</b> ${time.toFixed(2)} ms`;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function initStatus() {
|
||||
document.getElementById('status').innerHTML = '';
|
||||
document.getElementById('canvasOutput').style.visibility = "hidden";
|
||||
utils.clearError();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"tensorflow": [
|
||||
{
|
||||
"model": "deeplabv3",
|
||||
"inputSize": "513, 513",
|
||||
"mean": "127.5, 127.5, 127.5",
|
||||
"std": "0.007843",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://drive.google.com/uc?id=1v-hfGenaE9tiGOzo5qdgMNG_gqQ5-Xn4&export=download"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Style Transfer Example</title>
|
||||
<link href="js_example_style.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Style Transfer Example</h2>
|
||||
<p>
|
||||
This tutorial shows you how to write an style transfer example with OpenCV.js.<br>
|
||||
To try the example you should click the <b>modelFile</b> button(and <b>configFile</b> button if needed) to upload inference model.
|
||||
You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
|
||||
Then You should change the parameters in the first code snippet according to the uploaded model.
|
||||
Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
|
||||
</p>
|
||||
|
||||
<div class="control"><button id="tryIt" disabled>Try it</button></div>
|
||||
<div>
|
||||
<table cellpadding="0" cellspacing="0" width="0" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<canvas id="canvasInput" width="400" height="400"></canvas>
|
||||
</td>
|
||||
<td>
|
||||
<canvas id="canvasOutput" style="visibility: hidden;" width="400" height="400"></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p id='status' align="left"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
modelFile <input type="file" id="modelFile" name="file">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="caption">
|
||||
configFile <input type="file" id="configFile">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="err" id="errorMessage"></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Help function</h3>
|
||||
<p>1.The parameters for model inference which you can modify to investigate more models.</p>
|
||||
<textarea class="code" rows="5" cols="100" id="codeEditor" spellcheck="false"></textarea>
|
||||
<p>2.Main loop in which will read the image from canvas and do inference once.</p>
|
||||
<textarea class="code" rows="15" cols="100" id="codeEditor1" spellcheck="false"></textarea>
|
||||
<p>3.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor2" spellcheck="false"></textarea>
|
||||
<p>4.Fetch model file and save to emscripten file system once click the input button.</p>
|
||||
<textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
|
||||
<p>5.The post-processing, including scaling and reordering.</p>
|
||||
<textarea class="code" rows="21" cols="100" id="codeEditor4" spellcheck="false"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="appendix">
|
||||
<h2>Model Info:</h2>
|
||||
</div>
|
||||
|
||||
<script src="utils.js" type="text/javascript"></script>
|
||||
<script src="js_dnn_example_helper.js" type="text/javascript"></script>
|
||||
|
||||
<script id="codeSnippet" type="text/code-snippet">
|
||||
inputSize = [224, 224];
|
||||
mean = [104, 117, 123];
|
||||
std = 1;
|
||||
swapRB = false;
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet1" type="text/code-snippet">
|
||||
main = async function() {
|
||||
const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
|
||||
let net = cv.readNet(configPath, modelPath);
|
||||
net.setInput(input);
|
||||
const start = performance.now();
|
||||
const result = net.forward();
|
||||
const time = performance.now()-start;
|
||||
const output = postProcess(result);
|
||||
|
||||
updateResult(output, time);
|
||||
input.delete();
|
||||
net.delete();
|
||||
result.delete();
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="codeSnippet4" type="text/code-snippet">
|
||||
postProcess = function(result) {
|
||||
const resultData = result.data32F;
|
||||
const C = result.matSize[1];
|
||||
const H = result.matSize[2];
|
||||
const W = result.matSize[3];
|
||||
const mean = [104, 117, 123];
|
||||
|
||||
let normData = [];
|
||||
for (let h = 0; h < H; ++h) {
|
||||
for (let w = 0; w < W; ++w) {
|
||||
for (let c = 0; c < C; ++c) {
|
||||
normData.push(resultData[c*H*W + h*W + w] + mean[c]);
|
||||
}
|
||||
normData.push(255);
|
||||
}
|
||||
}
|
||||
|
||||
let output = new cv.matFromArray(H, W, cv.CV_8UC4, normData);
|
||||
return output;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
let jsonUrl = "js_style_transfer_model_info.json";
|
||||
drawInfoTable(jsonUrl, 'appendix');
|
||||
|
||||
let utils = new Utils('errorMessage');
|
||||
utils.loadCode('codeSnippet', 'codeEditor');
|
||||
utils.loadCode('codeSnippet1', 'codeEditor1');
|
||||
|
||||
let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
|
||||
document.getElementById('codeEditor2').value = getBlobFromImageCode;
|
||||
let loadModelCode = 'loadModel = ' + loadModel.toString();
|
||||
document.getElementById('codeEditor3').value = loadModelCode;
|
||||
|
||||
utils.loadCode('codeSnippet4', 'codeEditor4');
|
||||
|
||||
let canvas = document.getElementById('canvasInput');
|
||||
let ctx = canvas.getContext('2d');
|
||||
let img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.src = 'lena.png';
|
||||
img.onload = function() {
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
};
|
||||
|
||||
let tryIt = document.getElementById('tryIt');
|
||||
tryIt.addEventListener('click', () => {
|
||||
initStatus();
|
||||
document.getElementById('status').innerHTML = 'Running function main()...';
|
||||
utils.executeCode('codeEditor');
|
||||
utils.executeCode('codeEditor1');
|
||||
if (modelPath === "") {
|
||||
document.getElementById('status').innerHTML = 'Runing failed.';
|
||||
utils.printError('Please upload model file by clicking the button first.');
|
||||
} else {
|
||||
setTimeout(main, 1);
|
||||
}
|
||||
});
|
||||
|
||||
let fileInput = document.getElementById('fileInput');
|
||||
fileInput.addEventListener('change', (e) => {
|
||||
initStatus();
|
||||
loadImageToCanvas(e, 'canvasInput');
|
||||
});
|
||||
|
||||
let configPath = "";
|
||||
let configFile = document.getElementById('configFile');
|
||||
configFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
configPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
|
||||
});
|
||||
|
||||
let modelPath = "";
|
||||
let modelFile = document.getElementById('modelFile');
|
||||
modelFile.addEventListener('change', async (e) => {
|
||||
initStatus();
|
||||
modelPath = await loadModel(e);
|
||||
document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
|
||||
configPath = "";
|
||||
configFile.value = "";
|
||||
});
|
||||
|
||||
utils.loadOpenCv(() => {
|
||||
tryIt.removeAttribute('disabled');
|
||||
});
|
||||
|
||||
var main = async function() {};
|
||||
var postProcess = function(result) {};
|
||||
|
||||
utils.executeCode('codeEditor1');
|
||||
utils.executeCode('codeEditor2');
|
||||
utils.executeCode('codeEditor3');
|
||||
utils.executeCode('codeEditor4');
|
||||
|
||||
function updateResult(output, time) {
|
||||
try{
|
||||
let canvasOutput = document.getElementById('canvasOutput');
|
||||
canvasOutput.style.visibility = "visible";
|
||||
let resized = new cv.Mat(canvasOutput.width, canvasOutput.height, cv.CV_8UC4);
|
||||
cv.resize(output, resized, new cv.Size(canvasOutput.width, canvasOutput.height));
|
||||
cv.imshow('canvasOutput', resized);
|
||||
document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
|
||||
<b>Inference time:</b> ${time.toFixed(2)} ms`;
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function initStatus() {
|
||||
document.getElementById('status').innerHTML = '';
|
||||
document.getElementById('canvasOutput').style.visibility = "hidden";
|
||||
utils.clearError();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"torch": [
|
||||
{
|
||||
"model": "candy.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/candy.t7"
|
||||
},
|
||||
{
|
||||
"model": "composition_vii.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//eccv16/composition_vii.t7"
|
||||
},
|
||||
{
|
||||
"model": "feathers.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/feathers.t7"
|
||||
},
|
||||
{
|
||||
"model": "la_muse.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/la_muse.t7"
|
||||
},
|
||||
{
|
||||
"model": "mosaic.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/mosaic.t7"
|
||||
},
|
||||
{
|
||||
"model": "starry_night.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//eccv16/starry_night.t7"
|
||||
},
|
||||
{
|
||||
"model": "the_scream.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/the_scream.t7"
|
||||
},
|
||||
{
|
||||
"model": "the_wave.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//eccv16/the_wave.t7"
|
||||
},
|
||||
{
|
||||
"model": "udnie.t7",
|
||||
"inputSize": "224, 224",
|
||||
"mean": "104, 117, 123",
|
||||
"std": "1",
|
||||
"swapRB": "false",
|
||||
"modelUrl": "https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/udnie.t7"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,7 @@ function Utils(errorOutputId) { // eslint-disable-line no-unused-vars
|
||||
let script = document.createElement('script');
|
||||
script.setAttribute('async', '');
|
||||
script.setAttribute('type', 'text/javascript');
|
||||
script.addEventListener('load', () => {
|
||||
script.addEventListener('load', async () => {
|
||||
if (cv.getBuildInformation)
|
||||
{
|
||||
console.log(cv.getBuildInformation());
|
||||
@@ -16,9 +16,15 @@ function Utils(errorOutputId) { // eslint-disable-line no-unused-vars
|
||||
else
|
||||
{
|
||||
// WASM
|
||||
cv['onRuntimeInitialized']=()=>{
|
||||
if (cv instanceof Promise) {
|
||||
cv = await cv;
|
||||
console.log(cv.getBuildInformation());
|
||||
onloadCallback();
|
||||
} else {
|
||||
cv['onRuntimeInitialized']=()=>{
|
||||
console.log(cv.getBuildInformation());
|
||||
onloadCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Image Classification Example {#tutorial_js_image_classification}
|
||||
=======================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for image classification.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_image_classification.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
Image Classification Example with Camera {#tutorial_js_image_classification_with_camera}
|
||||
=======================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for image classification example with camera.
|
||||
|
||||
@note If you don't know how to capture video from camera, please review @ref tutorial_js_video_display.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_image_classification_with_camera.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
@@ -0,0 +1,13 @@
|
||||
Object Detection Example {#tutorial_js_object_detection}
|
||||
=======================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for object detection.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_object_detection.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
@@ -0,0 +1,13 @@
|
||||
Object Detection Example with Camera{#tutorial_js_object_detection_with_camera}
|
||||
=======================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for object detection with camera.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_object_detection_with_camera.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
@@ -0,0 +1,13 @@
|
||||
Pose Estimation Example {#tutorial_js_pose_estimation}
|
||||
=======================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for pose estimation.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_pose_estimation.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
@@ -0,0 +1,13 @@
|
||||
Semantic Segmentation Example {#tutorial_js_semantic_segmentation}
|
||||
=======================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for semantic segmentation.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_semantic_segmentation.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
@@ -0,0 +1,13 @@
|
||||
Style Transfer Example {#tutorial_js_style_transfer}
|
||||
=======================================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial you will learn how to use OpenCV.js dnn module for style transfer.
|
||||
|
||||
\htmlonly
|
||||
<iframe src="../../js_style_transfer.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
@@ -0,0 +1,30 @@
|
||||
Deep Neural Networks (dnn module) {#tutorial_js_table_of_contents_dnn}
|
||||
============
|
||||
|
||||
- @subpage tutorial_js_image_classification
|
||||
|
||||
Image classification example
|
||||
|
||||
- @subpage tutorial_js_image_classification_with_camera
|
||||
|
||||
Image classification example with camera
|
||||
|
||||
- @subpage tutorial_js_object_detection
|
||||
|
||||
Object detection example
|
||||
|
||||
- @subpage tutorial_js_object_detection_with_camera
|
||||
|
||||
Object detection example with camera
|
||||
|
||||
- @subpage tutorial_js_semantic_segmentation
|
||||
|
||||
Semantic segmentation example
|
||||
|
||||
- @subpage tutorial_js_style_transfer
|
||||
|
||||
Style transfer example
|
||||
|
||||
- @subpage tutorial_js_pose_estimation
|
||||
|
||||
Pose estimation example
|
||||
@@ -13,7 +13,7 @@ OpenCV.js: OpenCV for the JavaScript programmer
|
||||
|
||||
Web is the most ubiquitous open computing platform. With HTML5 standards implemented in every browser, web applications are able to render online video with HTML5 video tags, capture webcam video via WebRTC API, and access each pixel of a video frame via canvas API. With abundance of available multimedia content, web developers are in need of a wide array of image and vision processing algorithms in JavaScript to build innovative applications. This requirement is even more essential for emerging applications on the web, such as Web Virtual Reality (WebVR) and Augmented Reality (WebAR). All of these use cases demand efficient implementations of computation-intensive vision kernels on web.
|
||||
|
||||
[Emscripten](http://kripken.github.io/emscripten-site) is an LLVM-to-JavaScript compiler. It takes LLVM bitcode - which can be generated from C/C++ using clang, and compiles that into asm.js or WebAssembly that can execute directly inside the web browsers. . Asm.js is a highly optimizable, low-level subset of JavaScript. Asm.js enables ahead-of-time compilation and optimization in JavaScript engine that provide near-to-native execution speed. WebAssembly is a new portable, size- and load-time-efficient binary format suitable for compilation to the web. WebAssembly aims to execute at native speed. WebAssembly is currently being designed as an open standard by W3C.
|
||||
[Emscripten](https://emscripten.org/) is an LLVM-to-JavaScript compiler. It takes LLVM bitcode - which can be generated from C/C++ using clang, and compiles that into asm.js or WebAssembly that can execute directly inside the web browsers. . Asm.js is a highly optimizable, low-level subset of JavaScript. Asm.js enables ahead-of-time compilation and optimization in JavaScript engine that provide near-to-native execution speed. WebAssembly is a new portable, size- and load-time-efficient binary format suitable for compilation to the web. WebAssembly aims to execute at native speed. WebAssembly is currently being designed as an open standard by W3C.
|
||||
|
||||
OpenCV.js is a JavaScript binding for selected subset of OpenCV functions for the web platform. It allows emerging web applications with multimedia processing to benefit from the wide variety of vision functions available in OpenCV. OpenCV.js leverages Emscripten to compile OpenCV functions into asm.js or WebAssembly targets, and provides a JavaScript APIs for web application to access them. The future versions of the library will take advantage of acceleration APIs that are available on the Web such as SIMD and multi-threaded execution.
|
||||
|
||||
@@ -42,4 +42,4 @@ Below is the list of contributors of OpenCV.js bindings and tutorials.
|
||||
- Gang Song (GSoC student, Shanghai Jiao Tong University)
|
||||
- Wenyao Gan (Student intern, Shanghai Jiao Tong University)
|
||||
- Mohammad Reza Haghighat (Project initiator & sponsor, Intel Corporation)
|
||||
- Ningxin Hu (Students' supervisor, Intel Corporation)
|
||||
- Ningxin Hu (Students' supervisor, Intel Corporation)
|
||||
|
||||
@@ -7,12 +7,12 @@ You don't have to build your own copy if you simply want to start using it. Refe
|
||||
Installing Emscripten
|
||||
-----------------------------
|
||||
|
||||
[Emscripten](https://github.com/kripken/emscripten) is an LLVM-to-JavaScript compiler. We will use Emscripten to build OpenCV.js.
|
||||
[Emscripten](https://github.com/emscripten-core/emscripten) is an LLVM-to-JavaScript compiler. We will use Emscripten to build OpenCV.js.
|
||||
|
||||
@note
|
||||
While this describes installation of required tools from scratch, there's a section below also describing an alternative procedure to perform the same build using docker containers which is often easier.
|
||||
|
||||
To Install Emscripten, follow instructions of [Emscripten SDK](https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
|
||||
To Install Emscripten, follow instructions of [Emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html).
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
@@ -21,24 +21,29 @@ For example:
|
||||
./emsdk activate latest
|
||||
@endcode
|
||||
|
||||
@note
|
||||
To compile to [WebAssembly](http://webassembly.org), you need to install and activate [Binaryen](https://github.com/WebAssembly/binaryen) with the `emsdk` command. Please refer to [Developer's Guide](http://webassembly.org/getting-started/developers-guide/) for more details.
|
||||
|
||||
After install, ensure the `EMSCRIPTEN` environment is setup correctly.
|
||||
After install, ensure the `EMSDK` environment is setup correctly.
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
source ./emsdk_env.sh
|
||||
echo ${EMSCRIPTEN}
|
||||
echo ${EMSDK}
|
||||
@endcode
|
||||
|
||||
The version 1.39.16 of emscripten is verified for latest WebAssembly. Please check the version of emscripten to use the newest features of WebAssembly.
|
||||
Modern versions of Emscripten requires to use `emcmake` / `emmake` launchers:
|
||||
|
||||
@code{.bash}
|
||||
emcmake sh -c 'echo ${EMSCRIPTEN}'
|
||||
@endcode
|
||||
|
||||
|
||||
The version 2.0.10 of emscripten is verified for latest WebAssembly. Please check the version of Emscripten to use the newest features of WebAssembly.
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
./emsdk update
|
||||
./emsdk install 1.39.16
|
||||
./emsdk activate 1.39.16
|
||||
./emsdk install 2.0.10
|
||||
./emsdk activate 2.0.10
|
||||
@endcode
|
||||
|
||||
Obtaining OpenCV Source Code
|
||||
@@ -71,8 +76,7 @@ Building OpenCV.js from Source
|
||||
|
||||
For example, to build in `build_js` directory:
|
||||
@code{.bash}
|
||||
cd opencv
|
||||
python ./platforms/js/build_js.py build_js
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js
|
||||
@endcode
|
||||
|
||||
@note
|
||||
@@ -82,14 +86,14 @@ Building OpenCV.js from Source
|
||||
|
||||
For example, to build wasm version in `build_wasm` directory:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_wasm --build_wasm
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_wasm --build_wasm
|
||||
@endcode
|
||||
|
||||
-# [Optional] To build the OpenCV.js loader, append `--build_loader`.
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --build_loader
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js --build_loader
|
||||
@endcode
|
||||
|
||||
@note
|
||||
@@ -114,7 +118,7 @@ Building OpenCV.js from Source
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --build_doc
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js --build_doc
|
||||
@endcode
|
||||
|
||||
@note
|
||||
@@ -124,7 +128,14 @@ Building OpenCV.js from Source
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --build_test
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js --build_test
|
||||
@endcode
|
||||
|
||||
-# [optional] To enable OpenCV contrib modules append `--cmake_option="-DOPENCV_EXTRA_MODULES_PATH=/path/to/opencv_contrib/modules/"`
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --cmake_option="-DOPENCV_EXTRA_MODULES_PATH=opencv_contrib/modules"
|
||||
@endcode
|
||||
|
||||
Running OpenCV.js Tests
|
||||
@@ -186,7 +197,7 @@ node tests.js
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --build_wasm --threads
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js --build_wasm --threads
|
||||
@endcode
|
||||
|
||||
The default threads number is the logic core number of your device. You can use `cv.parallel_pthreads_set_threads_num(number)` to set threads number by yourself and use `cv.parallel_pthreads_get_threads_num()` to get the current threads number.
|
||||
@@ -198,7 +209,7 @@ node tests.js
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --build_wasm --simd
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js --build_wasm --simd
|
||||
@endcode
|
||||
|
||||
The simd optimization is experimental as wasm simd is still in development.
|
||||
@@ -222,7 +233,7 @@ node tests.js
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --build_wasm --simd --build_wasm_intrin_test
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js --build_wasm --simd --build_wasm_intrin_test
|
||||
@endcode
|
||||
|
||||
For wasm intrinsics tests, you can use the following function to test all the cases:
|
||||
@@ -250,7 +261,7 @@ node tests.js
|
||||
|
||||
For example:
|
||||
@code{.bash}
|
||||
python ./platforms/js/build_js.py build_js --build_perf
|
||||
emcmake python ./opencv/platforms/js/build_js.py build_js --build_perf
|
||||
@endcode
|
||||
|
||||
To run performance tests, launch a local web server in \<build_dir\>/bin folder. For example, node http-server which serves on `localhost:8080`.
|
||||
@@ -271,25 +282,25 @@ Building OpenCV.js with Docker
|
||||
|
||||
Alternatively, the same build can be can be accomplished using [docker](https://www.docker.com/) containers which is often easier and more reliable, particularly in non linux systems. You only need to install [docker](https://www.docker.com/) on your system and use a popular container that provides a clean well tested environment for emscripten builds like this, that already has latest versions of all the necessary tools installed.
|
||||
|
||||
So, make sure [docker](https://www.docker.com/) is installed in your system and running. The following shell script should work in linux and MacOS:
|
||||
So, make sure [docker](https://www.docker.com/) is installed in your system and running. The following shell script should work in Linux and MacOS:
|
||||
|
||||
@code{.bash}
|
||||
git clone https://github.com/opencv/opencv.git
|
||||
cd opencv
|
||||
docker run --rm --workdir /code -v "$PWD":/code "trzeci/emscripten:latest" python ./platforms/js/build_js.py build
|
||||
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk emcmake python3 ./dev/platforms/js/build_js.py build_js
|
||||
@endcode
|
||||
|
||||
In Windows use the following PowerShell command:
|
||||
|
||||
@code{.bash}
|
||||
docker run --rm --workdir /code -v "$(get-location):/code" "trzeci/emscripten:latest" python ./platforms/js/build_js.py build
|
||||
docker run --rm --workdir /src -v "$(get-location):/src" "emscripten/emsdk" emcmake python3 ./dev/platforms/js/build_js.py build_js
|
||||
@endcode
|
||||
|
||||
@warning
|
||||
The example uses latest version of emscripten. If the build fails you should try a version that is known to work fine which is `1.38.32` using the following command:
|
||||
The example uses latest version of emscripten. If the build fails you should try a version that is known to work fine which is `2.0.10` using the following command:
|
||||
|
||||
@code{.bash}
|
||||
docker run --rm --workdir /code -v "$PWD":/code "trzeci/emscripten:sdk-tag-1.38.32-64bit" python ./platforms/js/build_js.py build
|
||||
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk:2.0.10 emcmake python3 ./dev/platforms/js/build_js.py build_js
|
||||
@endcode
|
||||
|
||||
### Building the documentation with Docker
|
||||
@@ -297,10 +308,11 @@ docker run --rm --workdir /code -v "$PWD":/code "trzeci/emscripten:sdk-tag-1.38.
|
||||
To build the documentation `doxygen` needs to be installed. Create a file named `Dockerfile` with the following content:
|
||||
|
||||
```
|
||||
FROM trzeci/emscripten:sdk-tag-1.38.32-64bit
|
||||
FROM emscripten/emsdk:2.0.10
|
||||
|
||||
RUN apt-get update -y
|
||||
RUN apt-get install -y doxygen
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends doxygen \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
Then we build the docker image and name it `opencv-js-doc` with the following command (that needs to be run only once):
|
||||
@@ -312,5 +324,5 @@ docker build . -t opencv-js-doc
|
||||
Now run the build command again, this time using the new image and passing `--build_doc`:
|
||||
|
||||
@code{.bash}
|
||||
docker run --rm --workdir /code -v "$PWD":/code "opencv-js-doc" python ./platforms/js/build_js.py build --build_doc
|
||||
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) "opencv-js-doc" emcmake python3 ./dev/platforms/js/build_js.py build_js --build_doc
|
||||
@endcode
|
||||
|
||||
@@ -4,7 +4,7 @@ Using OpenCV.js {#tutorial_js_usage}
|
||||
Steps
|
||||
-----
|
||||
|
||||
In this tutorial, you will learn how to include and start to use `opencv.js` inside a web page. You can get a copy of `opencv.js` from `opencv-{VERSION_NUMBER}-docs.zip` in each [release](https://github.com/opencv/opencv/releases), or simply download the prebuilt script from the online documentations at "https://docs.opencv.org/{VERISON_NUMBER}/opencv.js" (For example, [https://docs.opencv.org/3.4.0/opencv.js](https://docs.opencv.org/3.4.0/opencv.js). Use `master` if you want the latest build). You can also build your own copy by following the tutorial on Build Opencv.js.
|
||||
In this tutorial, you will learn how to include and start to use `opencv.js` inside a web page. You can get a copy of `opencv.js` from `opencv-{VERSION_NUMBER}-docs.zip` in each [release](https://github.com/opencv/opencv/releases), or simply download the prebuilt script from the online documentations at "https://docs.opencv.org/{VERSION_NUMBER}/opencv.js" (For example, [https://docs.opencv.org/3.4.0/opencv.js](https://docs.opencv.org/3.4.0/opencv.js). Use `master` if you want the latest build). You can also build your own copy by following the tutorial on Build Opencv.js.
|
||||
|
||||
### Create a web page
|
||||
|
||||
@@ -129,7 +129,7 @@ function onOpenCvReady() {
|
||||
</html>
|
||||
@endcode
|
||||
|
||||
@note You have to call delete method of cv.Mat to free memory allocated in Emscripten's heap. Please refer to [Memory management of Emscripten](https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#memory-management) for details.
|
||||
@note You have to call delete method of cv.Mat to free memory allocated in Emscripten's heap. Please refer to [Memory management of Emscripten](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#memory-management) for details.
|
||||
|
||||
Try it
|
||||
------
|
||||
@@ -137,4 +137,4 @@ Try it
|
||||
<iframe src="../../js_setup_usage.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
\endhtmlonly
|
||||
|
||||
@@ -26,3 +26,7 @@ OpenCV.js Tutorials {#tutorial_js_root}
|
||||
|
||||
In this section you
|
||||
will object detection techniques like face detection etc.
|
||||
|
||||
- @subpage tutorial_js_table_of_contents_dnn
|
||||
|
||||
These tutorials show how to use dnn module in JavaScript
|
||||
|
||||
@@ -209,7 +209,7 @@ find the average error, we calculate the arithmetical mean of the errors calcula
|
||||
calibration images.
|
||||
@code{.py}
|
||||
mean_error = 0
|
||||
for i in xrange(len(objpoints)):
|
||||
for i in range(len(objpoints)):
|
||||
imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
|
||||
error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
|
||||
mean_error += error
|
||||
|
||||
@@ -79,7 +79,7 @@ from matplotlib import pyplot as plt
|
||||
img1 = cv.imread('myleft.jpg',0) #queryimage # left image
|
||||
img2 = cv.imread('myright.jpg',0) #trainimage # right image
|
||||
|
||||
sift = cv.SIFT()
|
||||
sift = cv.SIFT_create()
|
||||
|
||||
# find the keypoints and descriptors with SIFT
|
||||
kp1, des1 = sift.detectAndCompute(img1,None)
|
||||
@@ -93,14 +93,12 @@ search_params = dict(checks=50)
|
||||
flann = cv.FlannBasedMatcher(index_params,search_params)
|
||||
matches = flann.knnMatch(des1,des2,k=2)
|
||||
|
||||
good = []
|
||||
pts1 = []
|
||||
pts2 = []
|
||||
|
||||
# ratio test as per Lowe's paper
|
||||
for i,(m,n) in enumerate(matches):
|
||||
if m.distance < 0.8*n.distance:
|
||||
good.append(m)
|
||||
pts2.append(kp2[m.trainIdx].pt)
|
||||
pts1.append(kp1[m.queryIdx].pt)
|
||||
@endcode
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ automatically available with the platform (e.g. APPLE GCD) but chances are that
|
||||
have access to a parallel framework either directly or by enabling the option in CMake and rebuild the library.
|
||||
|
||||
The second (weak) precondition is more related to the task you want to achieve as not all computations
|
||||
are suitable / can be adatapted to be run in a parallel way. To remain simple, tasks that can be split
|
||||
are suitable / can be adapted to be run in a parallel way. To remain simple, tasks that can be split
|
||||
into multiple elementary operations with no memory dependency (no possible race condition) are easily
|
||||
parallelizable. Computer vision processing are often easily parallelizable as most of the time the processing of
|
||||
one pixel does not depend to the state of other pixels.
|
||||
|
||||
@@ -84,57 +84,198 @@ This tutorial's code is shown below. You can also download it
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
-# Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
||||
previous sections). Let's check the general structure of the C++ program:
|
||||
@add_toggle_cpp
|
||||
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
||||
previous sections). Let's check the general structure of the C++ program:
|
||||
|
||||
- Load an image (can be BGR or grayscale)
|
||||
- Create two windows (one for dilation output, the other for erosion)
|
||||
- Create a set of two Trackbars for each operation:
|
||||
- The first trackbar "Element" returns either **erosion_elem** or **dilation_elem**
|
||||
- The second trackbar "Kernel size" return **erosion_size** or **dilation_size** for the
|
||||
corresponding operation.
|
||||
- Every time we move any slider, the user's function **Erosion** or **Dilation** will be
|
||||
called and it will update the output image based on the current trackbar values.
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp main
|
||||
|
||||
Let's analyze these two functions:
|
||||
-# Load an image (can be BGR or grayscale)
|
||||
-# Create two windows (one for dilation output, the other for erosion)
|
||||
-# Create a set of two Trackbars for each operation:
|
||||
- The first trackbar "Element" returns either **erosion_elem** or **dilation_elem**
|
||||
- The second trackbar "Kernel size" return **erosion_size** or **dilation_size** for the
|
||||
corresponding operation.
|
||||
-# Call once erosion and dilation to show the initial image.
|
||||
|
||||
-# **erosion:**
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp erosion
|
||||
|
||||
- The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||
receives three arguments:
|
||||
- *src*: The source image
|
||||
- *erosion_dst*: The output image
|
||||
- *element*: This is the kernel we will use to perform the operation. If we do not
|
||||
specify, the default is a simple `3x3` matrix. Otherwise, we can specify its
|
||||
shape. For this, we need to use the function cv::getStructuringElement :
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp kernel
|
||||
Every time we move any slider, the user's function **Erosion** or **Dilation** will be
|
||||
called and it will update the output image based on the current trackbar values.
|
||||
|
||||
We can choose any of three shapes for our kernel:
|
||||
Let's analyze these two functions:
|
||||
|
||||
- Rectangular box: MORPH_RECT
|
||||
- Cross: MORPH_CROSS
|
||||
- Ellipse: MORPH_ELLIPSE
|
||||
#### The erosion function
|
||||
|
||||
Then, we just have to specify the size of our kernel and the *anchor point*. If not
|
||||
specified, it is assumed to be in the center.
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp erosion
|
||||
|
||||
- That is all. We are ready to perform the erosion of our image.
|
||||
@note Additionally, there is another parameter that allows you to perform multiple erosions
|
||||
(iterations) at once. However, We haven't used it in this simple tutorial. You can check out the
|
||||
reference for more details.
|
||||
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||
receives three arguments:
|
||||
- *src*: The source image
|
||||
- *erosion_dst*: The output image
|
||||
- *element*: This is the kernel we will use to perform the operation. If we do not
|
||||
specify, the default is a simple `3x3` matrix. Otherwise, we can specify its
|
||||
shape. For this, we need to use the function cv::getStructuringElement :
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp kernel
|
||||
|
||||
-# **dilation:**
|
||||
We can choose any of three shapes for our kernel:
|
||||
|
||||
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
|
||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
||||
to be used.
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp dilation
|
||||
- Rectangular box: MORPH_RECT
|
||||
- Cross: MORPH_CROSS
|
||||
- Ellipse: MORPH_ELLIPSE
|
||||
|
||||
Then, we just have to specify the size of our kernel and the *anchor point*. If not
|
||||
specified, it is assumed to be in the center.
|
||||
|
||||
That is all. We are ready to perform the erosion of our image.
|
||||
|
||||
#### The dilation function
|
||||
|
||||
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
|
||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
||||
to be used.
|
||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp dilation
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
||||
previous sections). Let's check however the general structure of the java class. There are 4 main
|
||||
parts in the java class:
|
||||
|
||||
- the class constructor which setups the window that will be filled with window components
|
||||
- the `addComponentsToPane` method, which fills out the window
|
||||
- the `update` method, which determines what happens when the user changes any value
|
||||
- the `main` method, which is the entry point of the program
|
||||
|
||||
In this tutorial we will focus on the `addComponentsToPane` and `update` methods. However, for completion the
|
||||
steps followed in the constructor are:
|
||||
|
||||
-# Load an image (can be BGR or grayscale)
|
||||
-# Create a window
|
||||
-# Add various control components with `addComponentsToPane`
|
||||
-# show the window
|
||||
|
||||
The components were added by the following method:
|
||||
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java components
|
||||
|
||||
In short we
|
||||
|
||||
-# create a panel for the sliders
|
||||
-# create a combo box for the element types
|
||||
-# create a slider for the kernel size
|
||||
-# create a combo box for the morphology function to use (erosion or dilation)
|
||||
|
||||
The action and state changed listeners added call at the end the `update` method which updates
|
||||
the image based on the current slider values. So every time we move any slider, the `update` method is triggered.
|
||||
|
||||
#### Updating the image
|
||||
|
||||
To update the image we used the following implementation:
|
||||
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java update
|
||||
|
||||
In other words we
|
||||
|
||||
-# get the structuring element the user chose
|
||||
-# execute the **erosion** or **dilation** function based on `doErosion`
|
||||
-# reload the image with the morphology applied
|
||||
-# repaint the frame
|
||||
|
||||
Let's analyze the `erode` and `dilate` methods:
|
||||
|
||||
#### The erosion method
|
||||
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java erosion
|
||||
|
||||
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||
receives three arguments:
|
||||
- *src*: The source image
|
||||
- *erosion_dst*: The output image
|
||||
- *element*: This is the kernel we will use to perform the operation. For specifying the shape, we need to use
|
||||
the function cv::getStructuringElement :
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java kernel
|
||||
|
||||
We can choose any of three shapes for our kernel:
|
||||
|
||||
- Rectangular box: CV_SHAPE_RECT
|
||||
- Cross: CV_SHAPE_CROSS
|
||||
- Ellipse: CV_SHAPE_ELLIPSE
|
||||
|
||||
Together with the shape we specify the size of our kernel and the *anchor point*. If the anchor point is not
|
||||
specified, it is assumed to be in the center.
|
||||
|
||||
That is all. We are ready to perform the erosion of our image.
|
||||
|
||||
#### The dilation function
|
||||
|
||||
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
|
||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
||||
to be used.
|
||||
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java dilation
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
||||
previous sections). Let's check the general structure of the python script:
|
||||
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py main
|
||||
|
||||
-# Load an image (can be BGR or grayscale)
|
||||
-# Create two windows (one for erosion output, the other for dilation) with a set of trackbars each
|
||||
- The first trackbar "Element" returns the value for the morphological type that will be mapped
|
||||
(1 = rectangle, 2 = cross, 3 = ellipse)
|
||||
- The second trackbar "Kernel size" returns the size of the element for the
|
||||
corresponding operation
|
||||
-# Call once erosion and dilation to show the initial image
|
||||
|
||||
Every time we move any slider, the user's function **erosion** or **dilation** will be
|
||||
called and it will update the output image based on the current trackbar values.
|
||||
|
||||
Let's analyze these two functions:
|
||||
|
||||
#### The erosion function
|
||||
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py erosion
|
||||
|
||||
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||
receives two arguments and returns the processed image:
|
||||
- *src*: The source image
|
||||
- *element*: The kernel we will use to perform the operation. We can specify its
|
||||
shape by using the function cv::getStructuringElement :
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py kernel
|
||||
|
||||
We can choose any of three shapes for our kernel:
|
||||
|
||||
- Rectangular box: MORPH_RECT
|
||||
- Cross: MORPH_CROSS
|
||||
- Ellipse: MORPH_ELLIPSE
|
||||
|
||||
Then, we just have to specify the size of our kernel and the *anchor point*. If the anchor point not
|
||||
specified, it is assumed to be in the center.
|
||||
|
||||
That is all. We are ready to perform the erosion of our image.
|
||||
|
||||
#### The dilation function
|
||||
|
||||
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
|
||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
||||
to be used.
|
||||
|
||||
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py dilation
|
||||
@end_toggle
|
||||
|
||||
@note Additionally, there are further parameters that allow you to perform multiple erosions/dilations
|
||||
(iterations) at once and also set the border type and value. However, We haven't used those
|
||||
in this simple tutorial. You can check out the reference for more details.
|
||||
|
||||
Results
|
||||
-------
|
||||
|
||||
Compile the code above and execute it with an image as argument. For instance, using this image:
|
||||
Compile the code above and execute it (or run the script if using python) with an image as argument.
|
||||
If you do not provide an image as argument the default sample image
|
||||
([LinuxLogo.jpg](https://github.com/opencv/opencv/tree/master/samples/data/LinuxLogo.jpg)) will be used.
|
||||
|
||||
For instance, using this image:
|
||||
|
||||

|
||||
|
||||
@@ -143,3 +284,4 @@ naturally. Try them out! You can even try to add a third Trackbar to control the
|
||||
iterations.
|
||||
|
||||

|
||||
(depending on the programming language the output might vary a little or be only 1 window)
|
||||
|
||||
@@ -48,12 +48,12 @@ cd /c/lib
|
||||
@code{.bash}
|
||||
#!/bin/bash -e
|
||||
myRepo=$(pwd)
|
||||
CMAKE_CONFIG_GENERATOR="Visual Studio 14 2015 Win64"
|
||||
CMAKE_GENERATOR_OPTIONS=-G"Visual Studio 16 2019"
|
||||
#CMAKE_GENERATOR_OPTIONS=-G"Visual Studio 15 2017 Win64"
|
||||
#CMAKE_GENERATOR_OPTIONS=(-G"Visual Studio 16 2019" -A x64) # CMake 3.14+ is required
|
||||
if [ ! -d "$myRepo/opencv" ]; then
|
||||
echo "cloning opencv"
|
||||
git clone https://github.com/opencv/opencv.git
|
||||
mkdir -p Build/opencv
|
||||
mkdir -p Install/opencv
|
||||
else
|
||||
cd opencv
|
||||
git pull --rebase
|
||||
@@ -62,16 +62,17 @@ fi
|
||||
if [ ! -d "$myRepo/opencv_contrib" ]; then
|
||||
echo "cloning opencv_contrib"
|
||||
git clone https://github.com/opencv/opencv_contrib.git
|
||||
mkdir -p Build/opencv_contrib
|
||||
else
|
||||
cd opencv_contrib
|
||||
git pull --rebase
|
||||
cd ..
|
||||
fi
|
||||
RepoSource=opencv
|
||||
pushd Build/$RepoSource
|
||||
CMAKE_OPTIONS='-DBUILD_PERF_TESTS:BOOL=OFF -DBUILD_TESTS:BOOL=OFF -DBUILD_DOCS:BOOL=OFF -DWITH_CUDA:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF -DINSTALL_CREATE_DISTRIB=ON'
|
||||
cmake -G"$CMAKE_CONFIG_GENERATOR" $CMAKE_OPTIONS -DOPENCV_EXTRA_MODULES_PATH="$myRepo"/opencv_contrib/modules -DCMAKE_INSTALL_PREFIX="$myRepo"/install/"$RepoSource" "$myRepo/$RepoSource"
|
||||
mkdir -p build_opencv
|
||||
pushd build_opencv
|
||||
CMAKE_OPTIONS=(-DBUILD_PERF_TESTS:BOOL=OFF -DBUILD_TESTS:BOOL=OFF -DBUILD_DOCS:BOOL=OFF -DWITH_CUDA:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF -DINSTALL_CREATE_DISTRIB=ON)
|
||||
set -x
|
||||
cmake "${CMAKE_GENERATOR_OPTIONS[@]}" "${CMAKE_OPTIONS[@]}" -DOPENCV_EXTRA_MODULES_PATH="$myRepo"/opencv_contrib/modules -DCMAKE_INSTALL_PREFIX="$myRepo/install/$RepoSource" "$myRepo/$RepoSource"
|
||||
echo "************************* $Source_DIR -->debug"
|
||||
cmake --build . --config debug
|
||||
echo "************************* $Source_DIR -->release"
|
||||
@@ -82,15 +83,15 @@ popd
|
||||
@endcode
|
||||
In this script I suppose you use VS 2015 in 64 bits
|
||||
@code{.bash}
|
||||
CMAKE_CONFIG_GENERATOR="Visual Studio 14 2015 Win64"
|
||||
CMAKE_GENERATOR_OPTIONS=-G"Visual Studio 14 2015 Win64"
|
||||
@endcode
|
||||
and opencv will be installed in c:/lib/install
|
||||
and opencv will be installed in c:/lib/install/opencv
|
||||
@code{.bash}
|
||||
-DCMAKE_INSTALL_PREFIX="$myRepo"/install/"$RepoSource" "$myRepo/$RepoSource"
|
||||
-DCMAKE_INSTALL_PREFIX="$myRepo/install/$RepoSource"
|
||||
@endcode
|
||||
with no Perf tests, no tests, no doc, no CUDA and no example
|
||||
@code{.bash}
|
||||
CMAKE_OPTIONS='-DBUILD_PERF_TESTS:BOOL=OFF -DBUILD_TESTS:BOOL=OFF -DBUILD_DOCS:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF'
|
||||
CMAKE_OPTIONS=(-DBUILD_PERF_TESTS:BOOL=OFF -DBUILD_TESTS:BOOL=OFF -DBUILD_DOCS:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF)
|
||||
@endcode
|
||||
-# In git command line enter following command :
|
||||
@code{.bash}
|
||||
|
||||
@@ -109,7 +109,7 @@ const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the n
|
||||
@code{.cpp}
|
||||
CV_FOURCC('P','I','M,'1') // this is an MPEG1 codec from the characters to integer
|
||||
@endcode
|
||||
If you pass for this argument minus one than a window will pop up at runtime that contains all
|
||||
If you pass for this argument minus one then a window will pop up at runtime that contains all
|
||||
the codec installed on your system and ask you to select the one to use:
|
||||
|
||||

|
||||
|
||||
@@ -39,3 +39,11 @@
|
||||
year={2013},
|
||||
publisher={IEEE}
|
||||
}
|
||||
|
||||
@inproceedings{Terzakis20,
|
||||
author = {Terzakis, George and Lourakis, Manolis},
|
||||
year = {2020},
|
||||
month = {09},
|
||||
pages = {},
|
||||
title = {A Consistently Fast and Globally Optimal Solution to the Perspective-n-Point Problem}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ respectively) by the same factor.
|
||||
|
||||
The joint rotation-translation matrix \f$[R|t]\f$ is the matrix product of a projective
|
||||
transformation and a homogeneous transformation. The 3-by-4 projective transformation maps 3D points
|
||||
represented in camera coordinates to 2D poins in the image plane and represented in normalized
|
||||
represented in camera coordinates to 2D points in the image plane and represented in normalized
|
||||
camera coordinates \f$x' = X_c / Z_c\f$ and \f$y' = Y_c / Z_c\f$:
|
||||
|
||||
\f[Z_c \begin{bmatrix}
|
||||
@@ -464,6 +464,7 @@ enum SolvePnPMethod {
|
||||
//!< - point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
//!< - point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
//!< - point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
SOLVEPNP_SQPNP = 8, //!< SQPnP: A Consistently Fast and Globally OptimalSolution to the Perspective-n-Point Problem @cite Terzakis20
|
||||
#ifndef CV_DOXYGEN
|
||||
SOLVEPNP_MAX_COUNT //!< Used for count
|
||||
#endif
|
||||
@@ -566,15 +567,15 @@ or vector\<Point2f\> .
|
||||
a vector\<Point2f\> .
|
||||
@param method Method used to compute a homography matrix. The following methods are possible:
|
||||
- **0** - a regular method using all the points, i.e., the least squares method
|
||||
- **RANSAC** - RANSAC-based robust method
|
||||
- **LMEDS** - Least-Median robust method
|
||||
- **RHO** - PROSAC-based robust method
|
||||
- @ref RANSAC - RANSAC-based robust method
|
||||
- @ref LMEDS - Least-Median robust method
|
||||
- @ref RHO - PROSAC-based robust method
|
||||
@param ransacReprojThreshold Maximum allowed reprojection error to treat a point pair as an inlier
|
||||
(used in the RANSAC and RHO methods only). That is, if
|
||||
\f[\| \texttt{dstPoints} _i - \texttt{convertPointsHomogeneous} ( \texttt{H} * \texttt{srcPoints} _i) \|_2 > \texttt{ransacReprojThreshold}\f]
|
||||
then the point \f$i\f$ is considered as an outlier. If srcPoints and dstPoints are measured in pixels,
|
||||
it usually makes sense to set this parameter somewhere in the range of 1 to 10.
|
||||
@param mask Optional output mask set by a robust method ( RANSAC or LMEDS ). Note that the input
|
||||
@param mask Optional output mask set by a robust method ( RANSAC or LMeDS ). Note that the input
|
||||
mask values are ignored.
|
||||
@param maxIters The maximum number of RANSAC iterations.
|
||||
@param confidence Confidence level, between 0 and 1.
|
||||
@@ -805,36 +806,39 @@ the model coordinate system to the camera coordinate system.
|
||||
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||
vectors, respectively, and further optimizes them.
|
||||
@param flags Method for solving a PnP problem:
|
||||
- **SOLVEPNP_ITERATIVE** Iterative method is based on a Levenberg-Marquardt optimization. In
|
||||
- @ref SOLVEPNP_ITERATIVE Iterative method is based on a Levenberg-Marquardt optimization. In
|
||||
this case the function finds such a pose that minimizes reprojection error, that is the sum
|
||||
of squared distances between the observed projections imagePoints and the projected (using
|
||||
@ref projectPoints ) objectPoints .
|
||||
- **SOLVEPNP_P3P** Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
- @ref SOLVEPNP_P3P Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of T. Ke, S. Roumeliotis
|
||||
- @ref SOLVEPNP_AP3P Method is based on the paper of T. Ke, S. Roumeliotis
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_EPNP** Method has been introduced by F. Moreno-Noguer, V. Lepetit and P. Fua in the
|
||||
- @ref SOLVEPNP_EPNP Method has been introduced by F. Moreno-Noguer, V. Lepetit and P. Fua in the
|
||||
paper "EPnP: Efficient Perspective-n-Point Camera Pose Estimation" (@cite lepetit2009epnp).
|
||||
- **SOLVEPNP_DLS** **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
- @ref SOLVEPNP_DLS **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
Method is based on the paper of J. Hesch and S. Roumeliotis.
|
||||
"A Direct Least-Squares (DLS) Method for PnP" (@cite hesch2011direct).
|
||||
- **SOLVEPNP_UPNP** **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
- @ref SOLVEPNP_UPNP **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
Method is based on the paper of A. Penate-Sanchez, J. Andrade-Cetto,
|
||||
F. Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Length
|
||||
Estimation" (@cite penate2013exhaustive). In this case the function also estimates the parameters \f$f_x\f$ and \f$f_y\f$
|
||||
assuming that both have the same value. Then the cameraMatrix is updated with the estimated
|
||||
focal length.
|
||||
- **SOLVEPNP_IPPE** Method is based on the paper of T. Collins and A. Bartoli.
|
||||
- @ref SOLVEPNP_IPPE Method is based on the paper of T. Collins and A. Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method requires coplanar object points.
|
||||
- **SOLVEPNP_IPPE_SQUARE** Method is based on the paper of Toby Collins and Adrien Bartoli.
|
||||
- @ref SOLVEPNP_IPPE_SQUARE Method is based on the paper of Toby Collins and Adrien Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method is suitable for marker pose estimation.
|
||||
It requires 4 coplanar object points defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
- @ref SOLVEPNP_SQPNP Method is based on the paper "A Consistently Fast and Globally Optimal Solution to the
|
||||
Perspective-n-Point Problem" by G. Terzakis and M.Lourakis (@cite Terzakis20). It requires 3 or more points.
|
||||
|
||||
|
||||
The function estimates the object pose given a set of object points, their corresponding image
|
||||
projections, as well as the camera intrinsic matrix and the distortion coefficients, see the figure below
|
||||
@@ -942,22 +946,23 @@ a 3D point expressed in the world frame into the camera frame:
|
||||
- Thus, given some data D = np.array(...) where D.shape = (N,M), in order to use a subset of
|
||||
it as, e.g., imagePoints, one must effectively copy it into a new array: imagePoints =
|
||||
np.ascontiguousarray(D[:,:2]).reshape((N,1,2))
|
||||
- The methods **SOLVEPNP_DLS** and **SOLVEPNP_UPNP** cannot be used as the current implementations are
|
||||
- The methods @ref SOLVEPNP_DLS and @ref SOLVEPNP_UPNP cannot be used as the current implementations are
|
||||
unstable and sometimes give completely wrong results. If you pass one of these two
|
||||
flags, **SOLVEPNP_EPNP** method will be used instead.
|
||||
- The minimum number of points is 4 in the general case. In the case of **SOLVEPNP_P3P** and **SOLVEPNP_AP3P**
|
||||
flags, @ref SOLVEPNP_EPNP method will be used instead.
|
||||
- The minimum number of points is 4 in the general case. In the case of @ref SOLVEPNP_P3P and @ref SOLVEPNP_AP3P
|
||||
methods, it is required to use exactly 4 points (the first 3 points are used to estimate all the solutions
|
||||
of the P3P problem, the last one is used to retain the best solution that minimizes the reprojection error).
|
||||
- With **SOLVEPNP_ITERATIVE** method and `useExtrinsicGuess=true`, the minimum number of points is 3 (3 points
|
||||
- With @ref SOLVEPNP_ITERATIVE method and `useExtrinsicGuess=true`, the minimum number of points is 3 (3 points
|
||||
are sufficient to compute a pose but there are up to 4 solutions). The initial solution should be close to the
|
||||
global solution to converge.
|
||||
- With **SOLVEPNP_IPPE** input points must be >= 4 and object points must be coplanar.
|
||||
- With **SOLVEPNP_IPPE_SQUARE** this is a special case suitable for marker pose estimation.
|
||||
- With @ref SOLVEPNP_IPPE input points must be >= 4 and object points must be coplanar.
|
||||
- With @ref SOLVEPNP_IPPE_SQUARE this is a special case suitable for marker pose estimation.
|
||||
Number of input points must be 4. Object points must be defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
- With @ref SOLVEPNP_SQPNP input points must be >= 3
|
||||
*/
|
||||
CV_EXPORTS_W bool solvePnP( InputArray objectPoints, InputArray imagePoints,
|
||||
InputArray cameraMatrix, InputArray distCoeffs,
|
||||
@@ -1026,9 +1031,9 @@ assumed.
|
||||
the model coordinate system to the camera coordinate system. A P3P problem has up to 4 solutions.
|
||||
@param tvecs Output translation vectors.
|
||||
@param flags Method for solving a P3P problem:
|
||||
- **SOLVEPNP_P3P** Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
- @ref SOLVEPNP_P3P Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of T. Ke and S. Roumeliotis.
|
||||
- @ref SOLVEPNP_AP3P Method is based on the paper of T. Ke and S. Roumeliotis.
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
|
||||
The function estimates the object pose given 3 object points, their corresponding image
|
||||
@@ -1128,39 +1133,39 @@ the model coordinate system to the camera coordinate system.
|
||||
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||
vectors, respectively, and further optimizes them.
|
||||
@param flags Method for solving a PnP problem:
|
||||
- **SOLVEPNP_ITERATIVE** Iterative method is based on a Levenberg-Marquardt optimization. In
|
||||
- @ref SOLVEPNP_ITERATIVE Iterative method is based on a Levenberg-Marquardt optimization. In
|
||||
this case the function finds such a pose that minimizes reprojection error, that is the sum
|
||||
of squared distances between the observed projections imagePoints and the projected (using
|
||||
projectPoints ) objectPoints .
|
||||
- **SOLVEPNP_P3P** Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
- @ref SOLVEPNP_P3P Method is based on the paper of X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang
|
||||
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_AP3P** Method is based on the paper of T. Ke, S. Roumeliotis
|
||||
- @ref SOLVEPNP_AP3P Method is based on the paper of T. Ke, S. Roumeliotis
|
||||
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||
In this case the function requires exactly four object and image points.
|
||||
- **SOLVEPNP_EPNP** Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the
|
||||
- @ref SOLVEPNP_EPNP Method has been introduced by F.Moreno-Noguer, V.Lepetit and P.Fua in the
|
||||
paper "EPnP: Efficient Perspective-n-Point Camera Pose Estimation" (@cite lepetit2009epnp).
|
||||
- **SOLVEPNP_DLS** **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
- @ref SOLVEPNP_DLS **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
Method is based on the paper of Joel A. Hesch and Stergios I. Roumeliotis.
|
||||
"A Direct Least-Squares (DLS) Method for PnP" (@cite hesch2011direct).
|
||||
- **SOLVEPNP_UPNP** **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
- @ref SOLVEPNP_UPNP **Broken implementation. Using this flag will fallback to EPnP.** \n
|
||||
Method is based on the paper of A.Penate-Sanchez, J.Andrade-Cetto,
|
||||
F.Moreno-Noguer. "Exhaustive Linearization for Robust Camera Pose and Focal Length
|
||||
Estimation" (@cite penate2013exhaustive). In this case the function also estimates the parameters \f$f_x\f$ and \f$f_y\f$
|
||||
assuming that both have the same value. Then the cameraMatrix is updated with the estimated
|
||||
focal length.
|
||||
- **SOLVEPNP_IPPE** Method is based on the paper of T. Collins and A. Bartoli.
|
||||
- @ref SOLVEPNP_IPPE Method is based on the paper of T. Collins and A. Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method requires coplanar object points.
|
||||
- **SOLVEPNP_IPPE_SQUARE** Method is based on the paper of Toby Collins and Adrien Bartoli.
|
||||
- @ref SOLVEPNP_IPPE_SQUARE Method is based on the paper of Toby Collins and Adrien Bartoli.
|
||||
"Infinitesimal Plane-Based Pose Estimation" (@cite Collins14). This method is suitable for marker pose estimation.
|
||||
It requires 4 coplanar object points defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
@param rvec Rotation vector used to initialize an iterative PnP refinement algorithm, when flag is SOLVEPNP_ITERATIVE
|
||||
@param rvec Rotation vector used to initialize an iterative PnP refinement algorithm, when flag is @ref SOLVEPNP_ITERATIVE
|
||||
and useExtrinsicGuess is set to true.
|
||||
@param tvec Translation vector used to initialize an iterative PnP refinement algorithm, when flag is SOLVEPNP_ITERATIVE
|
||||
@param tvec Translation vector used to initialize an iterative PnP refinement algorithm, when flag is @ref SOLVEPNP_ITERATIVE
|
||||
and useExtrinsicGuess is set to true.
|
||||
@param reprojectionError Optional vector of reprojection error, that is the RMS error
|
||||
(\f$ \text{RMSE} = \sqrt{\frac{\sum_{i}^{N} \left ( \hat{y_i} - y_i \right )^2}{N}} \f$) between the input image points
|
||||
@@ -1272,17 +1277,17 @@ a 3D point expressed in the world frame into the camera frame:
|
||||
- Thus, given some data D = np.array(...) where D.shape = (N,M), in order to use a subset of
|
||||
it as, e.g., imagePoints, one must effectively copy it into a new array: imagePoints =
|
||||
np.ascontiguousarray(D[:,:2]).reshape((N,1,2))
|
||||
- The methods **SOLVEPNP_DLS** and **SOLVEPNP_UPNP** cannot be used as the current implementations are
|
||||
- The methods @ref SOLVEPNP_DLS and @ref SOLVEPNP_UPNP cannot be used as the current implementations are
|
||||
unstable and sometimes give completely wrong results. If you pass one of these two
|
||||
flags, **SOLVEPNP_EPNP** method will be used instead.
|
||||
- The minimum number of points is 4 in the general case. In the case of **SOLVEPNP_P3P** and **SOLVEPNP_AP3P**
|
||||
flags, @ref SOLVEPNP_EPNP method will be used instead.
|
||||
- The minimum number of points is 4 in the general case. In the case of @ref SOLVEPNP_P3P and @ref SOLVEPNP_AP3P
|
||||
methods, it is required to use exactly 4 points (the first 3 points are used to estimate all the solutions
|
||||
of the P3P problem, the last one is used to retain the best solution that minimizes the reprojection error).
|
||||
- With **SOLVEPNP_ITERATIVE** method and `useExtrinsicGuess=true`, the minimum number of points is 3 (3 points
|
||||
- With @ref SOLVEPNP_ITERATIVE method and `useExtrinsicGuess=true`, the minimum number of points is 3 (3 points
|
||||
are sufficient to compute a pose but there are up to 4 solutions). The initial solution should be close to the
|
||||
global solution to converge.
|
||||
- With **SOLVEPNP_IPPE** input points must be >= 4 and object points must be coplanar.
|
||||
- With **SOLVEPNP_IPPE_SQUARE** this is a special case suitable for marker pose estimation.
|
||||
- With @ref SOLVEPNP_IPPE input points must be >= 4 and object points must be coplanar.
|
||||
- With @ref SOLVEPNP_IPPE_SQUARE this is a special case suitable for marker pose estimation.
|
||||
Number of input points must be 4. Object points must be defined in the following order:
|
||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
@@ -1322,13 +1327,13 @@ CV_EXPORTS_W Mat initCameraMatrix2D( InputArrayOfArrays objectPoints,
|
||||
( patternSize = cvSize(points_per_row,points_per_colum) = cvSize(columns,rows) ).
|
||||
@param corners Output array of detected corners.
|
||||
@param flags Various operation flags that can be zero or a combination of the following values:
|
||||
- **CALIB_CB_ADAPTIVE_THRESH** Use adaptive thresholding to convert the image to black
|
||||
- @ref CALIB_CB_ADAPTIVE_THRESH Use adaptive thresholding to convert the image to black
|
||||
and white, rather than a fixed threshold level (computed from the average image brightness).
|
||||
- **CALIB_CB_NORMALIZE_IMAGE** Normalize the image gamma with equalizeHist before
|
||||
- @ref CALIB_CB_NORMALIZE_IMAGE Normalize the image gamma with equalizeHist before
|
||||
applying fixed or adaptive thresholding.
|
||||
- **CALIB_CB_FILTER_QUADS** Use additional criteria (like contour area, perimeter,
|
||||
- @ref CALIB_CB_FILTER_QUADS Use additional criteria (like contour area, perimeter,
|
||||
square-like shape) to filter out false quads extracted at the contour retrieval stage.
|
||||
- **CALIB_CB_FAST_CHECK** Run a fast check on the image that looks for chessboard corners,
|
||||
- @ref CALIB_CB_FAST_CHECK Run a fast check on the image that looks for chessboard corners,
|
||||
and shortcut the call if none is found. This can drastically speed up the call in the
|
||||
degenerate condition when no chessboard is observed.
|
||||
|
||||
@@ -1443,11 +1448,12 @@ struct CV_EXPORTS_W_SIMPLE CirclesGridFinderParameters2 : public CirclesGridFind
|
||||
( patternSize = Size(points_per_row, points_per_colum) ).
|
||||
@param centers output array of detected centers.
|
||||
@param flags various operation flags that can be one of the following values:
|
||||
- **CALIB_CB_SYMMETRIC_GRID** uses symmetric pattern of circles.
|
||||
- **CALIB_CB_ASYMMETRIC_GRID** uses asymmetric pattern of circles.
|
||||
- **CALIB_CB_CLUSTERING** uses a special algorithm for grid detection. It is more robust to
|
||||
- @ref CALIB_CB_SYMMETRIC_GRID uses symmetric pattern of circles.
|
||||
- @ref CALIB_CB_ASYMMETRIC_GRID uses asymmetric pattern of circles.
|
||||
- @ref CALIB_CB_CLUSTERING uses a special algorithm for grid detection. It is more robust to
|
||||
perspective distortions but much more sensitive to background clutter.
|
||||
@param blobDetector feature detector that finds blobs like dark circles on light background.
|
||||
If `blobDetector` is NULL then `image` represents Point2f array of candidates.
|
||||
@param parameters struct for finding circles in a grid pattern.
|
||||
|
||||
The function attempts to determine whether the input image contains a grid of circles. If it is, the
|
||||
@@ -1458,7 +1464,7 @@ row). Otherwise, if the function fails to find all the corners or reorder them,
|
||||
Sample usage of detecting and drawing the centers of circles: :
|
||||
@code
|
||||
Size patternsize(7,7); //number of centers
|
||||
Mat gray = ....; //source image
|
||||
Mat gray = ...; //source image
|
||||
vector<Point2f> centers; //this will be filled by the detected centers
|
||||
|
||||
bool patternfound = findCirclesGrid(gray, patternsize, centers);
|
||||
@@ -1503,8 +1509,8 @@ respectively. In the old interface all the vectors of object points from differe
|
||||
concatenated together.
|
||||
@param imageSize Size of the image used only to initialize the camera intrinsic matrix.
|
||||
@param cameraMatrix Input/output 3x3 floating-point camera intrinsic matrix
|
||||
\f$\cameramatrix{A}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS
|
||||
and/or CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be
|
||||
\f$\cameramatrix{A}\f$ . If @ref CALIB_USE_INTRINSIC_GUESS
|
||||
and/or @ref CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be
|
||||
initialized before calling the function.
|
||||
@param distCoeffs Input/output vector of distortion coefficients
|
||||
\f$\distcoeffs\f$.
|
||||
@@ -1527,40 +1533,40 @@ parameters. Order of deviations values: \f$(R_0, T_0, \dotsc , R_{M - 1}, T_{M -
|
||||
the number of pattern views. \f$R_i, T_i\f$ are concatenated 1x3 vectors.
|
||||
@param perViewErrors Output vector of the RMS re-projection error estimated for each pattern view.
|
||||
@param flags Different flags that may be zero or a combination of the following values:
|
||||
- **CALIB_USE_INTRINSIC_GUESS** cameraMatrix contains valid initial values of
|
||||
- @ref CALIB_USE_INTRINSIC_GUESS cameraMatrix contains valid initial values of
|
||||
fx, fy, cx, cy that are optimized further. Otherwise, (cx, cy) is initially set to the image
|
||||
center ( imageSize is used), and focal distances are computed in a least-squares fashion.
|
||||
Note, that if intrinsic parameters are known, there is no need to use this function just to
|
||||
estimate extrinsic parameters. Use solvePnP instead.
|
||||
- **CALIB_FIX_PRINCIPAL_POINT** The principal point is not changed during the global
|
||||
- @ref CALIB_FIX_PRINCIPAL_POINT The principal point is not changed during the global
|
||||
optimization. It stays at the center or at a different location specified when
|
||||
CALIB_USE_INTRINSIC_GUESS is set too.
|
||||
- **CALIB_FIX_ASPECT_RATIO** The functions consider only fy as a free parameter. The
|
||||
@ref CALIB_USE_INTRINSIC_GUESS is set too.
|
||||
- @ref CALIB_FIX_ASPECT_RATIO The functions consider only fy as a free parameter. The
|
||||
ratio fx/fy stays the same as in the input cameraMatrix . When
|
||||
CALIB_USE_INTRINSIC_GUESS is not set, the actual input values of fx and fy are
|
||||
@ref CALIB_USE_INTRINSIC_GUESS is not set, the actual input values of fx and fy are
|
||||
ignored, only their ratio is computed and used further.
|
||||
- **CALIB_ZERO_TANGENT_DIST** Tangential distortion coefficients \f$(p_1, p_2)\f$ are set
|
||||
- @ref CALIB_ZERO_TANGENT_DIST Tangential distortion coefficients \f$(p_1, p_2)\f$ are set
|
||||
to zeros and stay zero.
|
||||
- **CALIB_FIX_K1,...,CALIB_FIX_K6** The corresponding radial distortion
|
||||
coefficient is not changed during the optimization. If CALIB_USE_INTRINSIC_GUESS is
|
||||
- @ref CALIB_FIX_K1,..., @ref CALIB_FIX_K6 The corresponding radial distortion
|
||||
coefficient is not changed during the optimization. If @ref CALIB_USE_INTRINSIC_GUESS is
|
||||
set, the coefficient from the supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||
- **CALIB_RATIONAL_MODEL** Coefficients k4, k5, and k6 are enabled. To provide the
|
||||
- @ref CALIB_RATIONAL_MODEL Coefficients k4, k5, and k6 are enabled. To provide the
|
||||
backward compatibility, this extra flag should be explicitly specified to make the
|
||||
calibration function use the rational model and return 8 coefficients. If the flag is not
|
||||
set, the function computes and returns only 5 distortion coefficients.
|
||||
- **CALIB_THIN_PRISM_MODEL** Coefficients s1, s2, s3 and s4 are enabled. To provide the
|
||||
- @ref CALIB_THIN_PRISM_MODEL Coefficients s1, s2, s3 and s4 are enabled. To provide the
|
||||
backward compatibility, this extra flag should be explicitly specified to make the
|
||||
calibration function use the thin prism model and return 12 coefficients. If the flag is not
|
||||
set, the function computes and returns only 5 distortion coefficients.
|
||||
- **CALIB_FIX_S1_S2_S3_S4** The thin prism distortion coefficients are not changed during
|
||||
the optimization. If CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
- @ref CALIB_FIX_S1_S2_S3_S4 The thin prism distortion coefficients are not changed during
|
||||
the optimization. If @ref CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||
- **CALIB_TILTED_MODEL** Coefficients tauX and tauY are enabled. To provide the
|
||||
- @ref CALIB_TILTED_MODEL Coefficients tauX and tauY are enabled. To provide the
|
||||
backward compatibility, this extra flag should be explicitly specified to make the
|
||||
calibration function use the tilted sensor model and return 14 coefficients. If the flag is not
|
||||
set, the function computes and returns only 5 distortion coefficients.
|
||||
- **CALIB_FIX_TAUX_TAUY** The coefficients of the tilted sensor model are not changed during
|
||||
the optimization. If CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
- @ref CALIB_FIX_TAUX_TAUY The coefficients of the tilted sensor model are not changed during
|
||||
the optimization. If @ref CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
||||
|
||||
@@ -1572,7 +1578,7 @@ points and their corresponding 2D projections in each view must be specified. Th
|
||||
by using an object with known geometry and easily detectable feature points. Such an object is
|
||||
called a calibration rig or calibration pattern, and OpenCV has built-in support for a chessboard as
|
||||
a calibration rig (see @ref findChessboardCorners). Currently, initialization of intrinsic
|
||||
parameters (when CALIB_USE_INTRINSIC_GUESS is not set) is only implemented for planar calibration
|
||||
parameters (when @ref CALIB_USE_INTRINSIC_GUESS is not set) is only implemented for planar calibration
|
||||
patterns (where Z-coordinates of the object points must be all zeros). 3D calibration rigs can also
|
||||
be used as long as initial cameraMatrix is provided.
|
||||
|
||||
@@ -1683,39 +1689,39 @@ second camera coordinate system.
|
||||
@param F Output fundamental matrix.
|
||||
@param perViewErrors Output vector of the RMS re-projection error estimated for each pattern view.
|
||||
@param flags Different flags that may be zero or a combination of the following values:
|
||||
- **CALIB_FIX_INTRINSIC** Fix cameraMatrix? and distCoeffs? so that only R, T, E, and F
|
||||
- @ref CALIB_FIX_INTRINSIC Fix cameraMatrix? and distCoeffs? so that only R, T, E, and F
|
||||
matrices are estimated.
|
||||
- **CALIB_USE_INTRINSIC_GUESS** Optimize some or all of the intrinsic parameters
|
||||
- @ref CALIB_USE_INTRINSIC_GUESS Optimize some or all of the intrinsic parameters
|
||||
according to the specified flags. Initial values are provided by the user.
|
||||
- **CALIB_USE_EXTRINSIC_GUESS** R and T contain valid initial values that are optimized further.
|
||||
- @ref CALIB_USE_EXTRINSIC_GUESS R and T contain valid initial values that are optimized further.
|
||||
Otherwise R and T are initialized to the median value of the pattern views (each dimension separately).
|
||||
- **CALIB_FIX_PRINCIPAL_POINT** Fix the principal points during the optimization.
|
||||
- **CALIB_FIX_FOCAL_LENGTH** Fix \f$f^{(j)}_x\f$ and \f$f^{(j)}_y\f$ .
|
||||
- **CALIB_FIX_ASPECT_RATIO** Optimize \f$f^{(j)}_y\f$ . Fix the ratio \f$f^{(j)}_x/f^{(j)}_y\f$
|
||||
- @ref CALIB_FIX_PRINCIPAL_POINT Fix the principal points during the optimization.
|
||||
- @ref CALIB_FIX_FOCAL_LENGTH Fix \f$f^{(j)}_x\f$ and \f$f^{(j)}_y\f$ .
|
||||
- @ref CALIB_FIX_ASPECT_RATIO Optimize \f$f^{(j)}_y\f$ . Fix the ratio \f$f^{(j)}_x/f^{(j)}_y\f$
|
||||
.
|
||||
- **CALIB_SAME_FOCAL_LENGTH** Enforce \f$f^{(0)}_x=f^{(1)}_x\f$ and \f$f^{(0)}_y=f^{(1)}_y\f$ .
|
||||
- **CALIB_ZERO_TANGENT_DIST** Set tangential distortion coefficients for each camera to
|
||||
- @ref CALIB_SAME_FOCAL_LENGTH Enforce \f$f^{(0)}_x=f^{(1)}_x\f$ and \f$f^{(0)}_y=f^{(1)}_y\f$ .
|
||||
- @ref CALIB_ZERO_TANGENT_DIST Set tangential distortion coefficients for each camera to
|
||||
zeros and fix there.
|
||||
- **CALIB_FIX_K1,...,CALIB_FIX_K6** Do not change the corresponding radial
|
||||
distortion coefficient during the optimization. If CALIB_USE_INTRINSIC_GUESS is set,
|
||||
- @ref CALIB_FIX_K1,..., @ref CALIB_FIX_K6 Do not change the corresponding radial
|
||||
distortion coefficient during the optimization. If @ref CALIB_USE_INTRINSIC_GUESS is set,
|
||||
the coefficient from the supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||
- **CALIB_RATIONAL_MODEL** Enable coefficients k4, k5, and k6. To provide the backward
|
||||
- @ref CALIB_RATIONAL_MODEL Enable coefficients k4, k5, and k6. To provide the backward
|
||||
compatibility, this extra flag should be explicitly specified to make the calibration
|
||||
function use the rational model and return 8 coefficients. If the flag is not set, the
|
||||
function computes and returns only 5 distortion coefficients.
|
||||
- **CALIB_THIN_PRISM_MODEL** Coefficients s1, s2, s3 and s4 are enabled. To provide the
|
||||
- @ref CALIB_THIN_PRISM_MODEL Coefficients s1, s2, s3 and s4 are enabled. To provide the
|
||||
backward compatibility, this extra flag should be explicitly specified to make the
|
||||
calibration function use the thin prism model and return 12 coefficients. If the flag is not
|
||||
set, the function computes and returns only 5 distortion coefficients.
|
||||
- **CALIB_FIX_S1_S2_S3_S4** The thin prism distortion coefficients are not changed during
|
||||
the optimization. If CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
- @ref CALIB_FIX_S1_S2_S3_S4 The thin prism distortion coefficients are not changed during
|
||||
the optimization. If @ref CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||
- **CALIB_TILTED_MODEL** Coefficients tauX and tauY are enabled. To provide the
|
||||
- @ref CALIB_TILTED_MODEL Coefficients tauX and tauY are enabled. To provide the
|
||||
backward compatibility, this extra flag should be explicitly specified to make the
|
||||
calibration function use the tilted sensor model and return 14 coefficients. If the flag is not
|
||||
set, the function computes and returns only 5 distortion coefficients.
|
||||
- **CALIB_FIX_TAUX_TAUY** The coefficients of the tilted sensor model are not changed during
|
||||
the optimization. If CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
- @ref CALIB_FIX_TAUX_TAUY The coefficients of the tilted sensor model are not changed during
|
||||
the optimization. If @ref CALIB_USE_INTRINSIC_GUESS is set, the coefficient from the
|
||||
supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
||||
|
||||
@@ -1763,10 +1769,10 @@ Besides the stereo-related information, the function can also perform a full cal
|
||||
the two cameras. However, due to the high dimensionality of the parameter space and noise in the
|
||||
input data, the function can diverge from the correct solution. If the intrinsic parameters can be
|
||||
estimated with high accuracy for each of the cameras individually (for example, using
|
||||
calibrateCamera ), you are recommended to do so and then pass CALIB_FIX_INTRINSIC flag to the
|
||||
calibrateCamera ), you are recommended to do so and then pass @ref CALIB_FIX_INTRINSIC flag to the
|
||||
function along with the computed intrinsic parameters. Otherwise, if all the parameters are
|
||||
estimated at once, it makes sense to restrict some parameters, for example, pass
|
||||
CALIB_SAME_FOCAL_LENGTH and CALIB_ZERO_TANGENT_DIST flags, which is usually a
|
||||
@ref CALIB_SAME_FOCAL_LENGTH and @ref CALIB_ZERO_TANGENT_DIST flags, which is usually a
|
||||
reasonable assumption.
|
||||
|
||||
Similarly to calibrateCamera, the function minimizes the total re-projection error for all the
|
||||
@@ -1816,7 +1822,7 @@ rectified first camera's image.
|
||||
camera, i.e. it projects points given in the rectified first camera coordinate system into the
|
||||
rectified second camera's image.
|
||||
@param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see @ref reprojectImageTo3D).
|
||||
@param flags Operation flags that may be zero or CALIB_ZERO_DISPARITY . If the flag is set,
|
||||
@param flags Operation flags that may be zero or @ref CALIB_ZERO_DISPARITY . If the flag is set,
|
||||
the function makes the principal points of each camera have the same pixel coordinates in the
|
||||
rectified views. And if the flag is not set, the function may still shift the images in the
|
||||
horizontal or vertical direction (depending on the orientation of epipolar lines) to maximize the
|
||||
@@ -1863,7 +1869,7 @@ coordinates. The function distinguishes the following two cases:
|
||||
\end{bmatrix} ,\f]
|
||||
|
||||
where \f$T_x\f$ is a horizontal shift between the cameras and \f$cx_1=cx_2\f$ if
|
||||
CALIB_ZERO_DISPARITY is set.
|
||||
@ref CALIB_ZERO_DISPARITY is set.
|
||||
|
||||
- **Vertical stereo**: the first and the second camera views are shifted relative to each other
|
||||
mainly in the vertical direction (and probably a bit in the horizontal direction too). The epipolar
|
||||
@@ -1882,7 +1888,7 @@ coordinates. The function distinguishes the following two cases:
|
||||
\end{bmatrix},\f]
|
||||
|
||||
where \f$T_y\f$ is a vertical shift between the cameras and \f$cy_1=cy_2\f$ if
|
||||
CALIB_ZERO_DISPARITY is set.
|
||||
@ref CALIB_ZERO_DISPARITY is set.
|
||||
|
||||
As you can see, the first three columns of P1 and P2 will effectively be the new "rectified" camera
|
||||
matrices. The matrices, together with R1 and R2 , can then be passed to initUndistortRectifyMap to
|
||||
@@ -2153,10 +2159,10 @@ CV_EXPORTS void convertPointsHomogeneous( InputArray src, OutputArray dst );
|
||||
floating-point (single or double precision).
|
||||
@param points2 Array of the second image points of the same size and format as points1 .
|
||||
@param method Method for computing a fundamental matrix.
|
||||
- **CV_FM_7POINT** for a 7-point algorithm. \f$N = 7\f$
|
||||
- **CV_FM_8POINT** for an 8-point algorithm. \f$N \ge 8\f$
|
||||
- **CV_FM_RANSAC** for the RANSAC algorithm. \f$N \ge 8\f$
|
||||
- **CV_FM_LMEDS** for the LMedS algorithm. \f$N \ge 8\f$
|
||||
- @ref FM_7POINT for a 7-point algorithm. \f$N = 7\f$
|
||||
- @ref FM_8POINT for an 8-point algorithm. \f$N \ge 8\f$
|
||||
- @ref FM_RANSAC for the RANSAC algorithm. \f$N \ge 8\f$
|
||||
- @ref FM_LMEDS for the LMedS algorithm. \f$N \ge 8\f$
|
||||
@param ransacReprojThreshold Parameter used only for RANSAC. It is the maximum distance from a point to an epipolar
|
||||
line in pixels, beyond which the point is considered an outlier and is not used for computing the
|
||||
final fundamental matrix. It can be set to something like 1-3, depending on the accuracy of the
|
||||
@@ -2225,8 +2231,8 @@ same camera intrinsic matrix. If this assumption does not hold for your use case
|
||||
to normalized image coordinates, which are valid for the identity camera intrinsic matrix. When
|
||||
passing these coordinates, pass the identity matrix for this parameter.
|
||||
@param method Method for computing an essential matrix.
|
||||
- **RANSAC** for the RANSAC algorithm.
|
||||
- **LMEDS** for the LMedS algorithm.
|
||||
- @ref RANSAC for the RANSAC algorithm.
|
||||
- @ref LMEDS for the LMedS algorithm.
|
||||
@param prob Parameter used for the RANSAC or LMedS methods only. It specifies a desirable level of
|
||||
confidence (probability) that the estimated matrix is correct.
|
||||
@param threshold Parameter used for RANSAC. It is the maximum distance from a point to an epipolar
|
||||
@@ -2258,8 +2264,8 @@ be floating-point (single or double precision).
|
||||
are feature points from cameras with same focal length and principal point.
|
||||
@param pp principal point of the camera.
|
||||
@param method Method for computing a fundamental matrix.
|
||||
- **RANSAC** for the RANSAC algorithm.
|
||||
- **LMEDS** for the LMedS algorithm.
|
||||
- @ref RANSAC for the RANSAC algorithm.
|
||||
- @ref LMEDS for the LMedS algorithm.
|
||||
@param threshold Parameter used for RANSAC. It is the maximum distance from a point to an epipolar
|
||||
line in pixels, beyond which the point is considered an outlier and is not used for computing the
|
||||
final fundamental matrix. It can be set to something like 1-3, depending on the accuracy of the
|
||||
@@ -2662,8 +2668,8 @@ b_2\\
|
||||
@param to Second input 2D point set containing \f$(x,y)\f$.
|
||||
@param inliers Output vector indicating which points are inliers (1-inlier, 0-outlier).
|
||||
@param method Robust method used to compute transformation. The following methods are possible:
|
||||
- cv::RANSAC - RANSAC-based robust method
|
||||
- cv::LMEDS - Least-Median robust method
|
||||
- @ref RANSAC - RANSAC-based robust method
|
||||
- @ref LMEDS - Least-Median robust method
|
||||
RANSAC is the default method.
|
||||
@param ransacReprojThreshold Maximum reprojection error in the RANSAC algorithm to consider
|
||||
a point as an inlier. Applies only to RANSAC.
|
||||
@@ -2708,8 +2714,8 @@ two 2D point sets.
|
||||
@param to Second input 2D point set.
|
||||
@param inliers Output vector indicating which points are inliers.
|
||||
@param method Robust method used to compute transformation. The following methods are possible:
|
||||
- cv::RANSAC - RANSAC-based robust method
|
||||
- cv::LMEDS - Least-Median robust method
|
||||
- @ref RANSAC - RANSAC-based robust method
|
||||
- @ref LMEDS - Least-Median robust method
|
||||
RANSAC is the default method.
|
||||
@param ransacReprojThreshold Maximum reprojection error in the RANSAC algorithm to consider
|
||||
a point as an inlier. Applies only to RANSAC.
|
||||
@@ -3006,7 +3012,8 @@ namespace fisheye
|
||||
CALIB_FIX_K3 = 1 << 6,
|
||||
CALIB_FIX_K4 = 1 << 7,
|
||||
CALIB_FIX_INTRINSIC = 1 << 8,
|
||||
CALIB_FIX_PRINCIPAL_POINT = 1 << 9
|
||||
CALIB_FIX_PRINCIPAL_POINT = 1 << 9,
|
||||
CALIB_ZERO_DISPARITY = 1 << 10
|
||||
};
|
||||
|
||||
/** @brief Projects points using fisheye model
|
||||
@@ -3139,7 +3146,7 @@ namespace fisheye
|
||||
@param image_size Size of the image used only to initialize the camera intrinsic matrix.
|
||||
@param K Output 3x3 floating-point camera intrinsic matrix
|
||||
\f$\cameramatrix{A}\f$ . If
|
||||
fisheye::CALIB_USE_INTRINSIC_GUESS/ is specified, some or all of fx, fy, cx, cy must be
|
||||
@ref fisheye::CALIB_USE_INTRINSIC_GUESS is specified, some or all of fx, fy, cx, cy must be
|
||||
initialized before calling the function.
|
||||
@param D Output vector of distortion coefficients \f$\distcoeffsfisheye\f$.
|
||||
@param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each pattern view.
|
||||
@@ -3149,17 +3156,17 @@ namespace fisheye
|
||||
position of the calibration pattern in the k-th pattern view (k=0.. *M* -1).
|
||||
@param tvecs Output vector of translation vectors estimated for each pattern view.
|
||||
@param flags Different flags that may be zero or a combination of the following values:
|
||||
- **fisheye::CALIB_USE_INTRINSIC_GUESS** cameraMatrix contains valid initial values of
|
||||
- @ref fisheye::CALIB_USE_INTRINSIC_GUESS cameraMatrix contains valid initial values of
|
||||
fx, fy, cx, cy that are optimized further. Otherwise, (cx, cy) is initially set to the image
|
||||
center ( imageSize is used), and focal distances are computed in a least-squares fashion.
|
||||
- **fisheye::CALIB_RECOMPUTE_EXTRINSIC** Extrinsic will be recomputed after each iteration
|
||||
- @ref fisheye::CALIB_RECOMPUTE_EXTRINSIC Extrinsic will be recomputed after each iteration
|
||||
of intrinsic optimization.
|
||||
- **fisheye::CALIB_CHECK_COND** The functions will check validity of condition number.
|
||||
- **fisheye::CALIB_FIX_SKEW** Skew coefficient (alpha) is set to zero and stay zero.
|
||||
- **fisheye::CALIB_FIX_K1..fisheye::CALIB_FIX_K4** Selected distortion coefficients
|
||||
- @ref fisheye::CALIB_CHECK_COND The functions will check validity of condition number.
|
||||
- @ref fisheye::CALIB_FIX_SKEW Skew coefficient (alpha) is set to zero and stay zero.
|
||||
- @ref fisheye::CALIB_FIX_K1,..., @ref fisheye::CALIB_FIX_K4 Selected distortion coefficients
|
||||
are set to zeros and stay zero.
|
||||
- **fisheye::CALIB_FIX_PRINCIPAL_POINT** The principal point is not changed during the global
|
||||
optimization. It stays at the center or at a different location specified when CALIB_USE_INTRINSIC_GUESS is set too.
|
||||
- @ref fisheye::CALIB_FIX_PRINCIPAL_POINT The principal point is not changed during the global
|
||||
optimization. It stays at the center or at a different location specified when @ref fisheye::CALIB_USE_INTRINSIC_GUESS is set too.
|
||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
||||
*/
|
||||
CV_EXPORTS_W double calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, const Size& image_size,
|
||||
@@ -3183,7 +3190,7 @@ optimization. It stays at the center or at a different location specified when C
|
||||
@param P2 Output 3x4 projection matrix in the new (rectified) coordinate systems for the second
|
||||
camera.
|
||||
@param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see reprojectImageTo3D ).
|
||||
@param flags Operation flags that may be zero or CALIB_ZERO_DISPARITY . If the flag is set,
|
||||
@param flags Operation flags that may be zero or @ref fisheye::CALIB_ZERO_DISPARITY . If the flag is set,
|
||||
the function makes the principal points of each camera have the same pixel coordinates in the
|
||||
rectified views. And if the flag is not set, the function may still shift the images in the
|
||||
horizontal or vertical direction (depending on the orientation of epipolar lines) to maximize the
|
||||
@@ -3209,7 +3216,7 @@ optimization. It stays at the center or at a different location specified when C
|
||||
observed by the second camera.
|
||||
@param K1 Input/output first camera intrinsic matrix:
|
||||
\f$\vecthreethree{f_x^{(j)}}{0}{c_x^{(j)}}{0}{f_y^{(j)}}{c_y^{(j)}}{0}{0}{1}\f$ , \f$j = 0,\, 1\f$ . If
|
||||
any of fisheye::CALIB_USE_INTRINSIC_GUESS , fisheye::CALIB_FIX_INTRINSIC are specified,
|
||||
any of @ref fisheye::CALIB_USE_INTRINSIC_GUESS , @ref fisheye::CALIB_FIX_INTRINSIC are specified,
|
||||
some or all of the matrix components must be initialized.
|
||||
@param D1 Input/output vector of distortion coefficients \f$\distcoeffsfisheye\f$ of 4 elements.
|
||||
@param K2 Input/output second camera intrinsic matrix. The parameter is similar to K1 .
|
||||
@@ -3219,16 +3226,16 @@ optimization. It stays at the center or at a different location specified when C
|
||||
@param R Output rotation matrix between the 1st and the 2nd camera coordinate systems.
|
||||
@param T Output translation vector between the coordinate systems of the cameras.
|
||||
@param flags Different flags that may be zero or a combination of the following values:
|
||||
- **fisheye::CALIB_FIX_INTRINSIC** Fix K1, K2? and D1, D2? so that only R, T matrices
|
||||
- @ref fisheye::CALIB_FIX_INTRINSIC Fix K1, K2? and D1, D2? so that only R, T matrices
|
||||
are estimated.
|
||||
- **fisheye::CALIB_USE_INTRINSIC_GUESS** K1, K2 contains valid initial values of
|
||||
- @ref fisheye::CALIB_USE_INTRINSIC_GUESS K1, K2 contains valid initial values of
|
||||
fx, fy, cx, cy that are optimized further. Otherwise, (cx, cy) is initially set to the image
|
||||
center (imageSize is used), and focal distances are computed in a least-squares fashion.
|
||||
- **fisheye::CALIB_RECOMPUTE_EXTRINSIC** Extrinsic will be recomputed after each iteration
|
||||
- @ref fisheye::CALIB_RECOMPUTE_EXTRINSIC Extrinsic will be recomputed after each iteration
|
||||
of intrinsic optimization.
|
||||
- **fisheye::CALIB_CHECK_COND** The functions will check validity of condition number.
|
||||
- **fisheye::CALIB_FIX_SKEW** Skew coefficient (alpha) is set to zero and stay zero.
|
||||
- **fisheye::CALIB_FIX_K1..4** Selected distortion coefficients are set to zeros and stay
|
||||
- @ref fisheye::CALIB_CHECK_COND The functions will check validity of condition number.
|
||||
- @ref fisheye::CALIB_FIX_SKEW Skew coefficient (alpha) is set to zero and stay zero.
|
||||
- @ref fisheye::CALIB_FIX_K1,..., @ref fisheye::CALIB_FIX_K4 Selected distortion coefficients are set to zeros and stay
|
||||
zero.
|
||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
||||
*/
|
||||
|
||||
@@ -2178,13 +2178,6 @@ void drawChessboardCorners( InputOutputArray image, Size patternSize,
|
||||
}
|
||||
}
|
||||
|
||||
static int quiet_error(int /*status*/, const char* /*func_name*/,
|
||||
const char* /*err_msg*/, const char* /*file_name*/,
|
||||
int /*line*/, void* /*userdata*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool findCirclesGrid(InputArray image, Size patternSize,
|
||||
OutputArray centers, int flags,
|
||||
const Ptr<FeatureDetector> &blobDetector,
|
||||
@@ -2205,15 +2198,22 @@ bool findCirclesGrid2(InputArray _image, Size patternSize,
|
||||
bool isSymmetricGrid = (flags & CALIB_CB_SYMMETRIC_GRID ) ? true : false;
|
||||
CV_Assert(isAsymmetricGrid ^ isSymmetricGrid);
|
||||
|
||||
Mat image = _image.getMat();
|
||||
std::vector<Point2f> centers;
|
||||
|
||||
std::vector<KeyPoint> keypoints;
|
||||
blobDetector->detect(image, keypoints);
|
||||
std::vector<Point2f> points;
|
||||
for (size_t i = 0; i < keypoints.size(); i++)
|
||||
if (blobDetector)
|
||||
{
|
||||
points.push_back (keypoints[i].pt);
|
||||
std::vector<KeyPoint> keypoints;
|
||||
blobDetector->detect(_image, keypoints);
|
||||
for (size_t i = 0; i < keypoints.size(); i++)
|
||||
{
|
||||
points.push_back(keypoints[i].pt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_CheckTypeEQ(_image.type(), CV_32FC2, "blobDetector must be provided or image must contains Point2f array (std::vector<Point2f>) with candidates");
|
||||
_image.copyTo(points);
|
||||
}
|
||||
|
||||
if(flags & CALIB_CB_ASYMMETRIC_GRID)
|
||||
@@ -2229,64 +2229,59 @@ bool findCirclesGrid2(InputArray _image, Size patternSize,
|
||||
return !centers.empty();
|
||||
}
|
||||
|
||||
bool isValid = false;
|
||||
const int attempts = 2;
|
||||
const size_t minHomographyPoints = 4;
|
||||
Mat H;
|
||||
for (int i = 0; i < attempts; i++)
|
||||
{
|
||||
centers.clear();
|
||||
CirclesGridFinder boxFinder(patternSize, points, parameters);
|
||||
bool isFound = false;
|
||||
#define BE_QUIET 1
|
||||
#if BE_QUIET
|
||||
void* oldCbkData;
|
||||
ErrorCallback oldCbk = redirectError(quiet_error, 0, &oldCbkData); // FIXIT not thread safe
|
||||
#endif
|
||||
try
|
||||
{
|
||||
isFound = boxFinder.findHoles();
|
||||
}
|
||||
catch (const cv::Exception &)
|
||||
{
|
||||
|
||||
}
|
||||
#if BE_QUIET
|
||||
redirectError(oldCbk, oldCbkData);
|
||||
#endif
|
||||
if (isFound)
|
||||
{
|
||||
switch(parameters.gridType)
|
||||
centers.clear();
|
||||
CirclesGridFinder boxFinder(patternSize, points, parameters);
|
||||
try
|
||||
{
|
||||
case CirclesGridFinderParameters::SYMMETRIC_GRID:
|
||||
boxFinder.getHoles(centers);
|
||||
break;
|
||||
case CirclesGridFinderParameters::ASYMMETRIC_GRID:
|
||||
boxFinder.getAsymmetricHoles(centers);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Unknown pattern type");
|
||||
bool isFound = boxFinder.findHoles();
|
||||
if (isFound)
|
||||
{
|
||||
switch(parameters.gridType)
|
||||
{
|
||||
case CirclesGridFinderParameters::SYMMETRIC_GRID:
|
||||
boxFinder.getHoles(centers);
|
||||
break;
|
||||
case CirclesGridFinderParameters::ASYMMETRIC_GRID:
|
||||
boxFinder.getAsymmetricHoles(centers);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Unknown pattern type");
|
||||
}
|
||||
|
||||
isValid = true;
|
||||
break; // done, return result
|
||||
}
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
CV_UNUSED(e);
|
||||
CV_LOG_DEBUG(NULL, "findCirclesGrid2: attempt=" << i << ": " << e.what());
|
||||
// nothing, next attempt
|
||||
}
|
||||
|
||||
if (i != 0)
|
||||
boxFinder.getHoles(centers);
|
||||
if (i != attempts - 1)
|
||||
{
|
||||
Mat orgPointsMat;
|
||||
transform(centers, orgPointsMat, H.inv());
|
||||
convertPointsFromHomogeneous(orgPointsMat, centers);
|
||||
if (centers.size() < minHomographyPoints)
|
||||
break;
|
||||
H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points);
|
||||
}
|
||||
Mat(centers).copyTo(_centers);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
boxFinder.getHoles(centers);
|
||||
if (i != attempts - 1)
|
||||
{
|
||||
if (centers.size() < minHomographyPoints)
|
||||
break;
|
||||
H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points);
|
||||
}
|
||||
if (!H.empty()) // undone rectification
|
||||
{
|
||||
Mat orgPointsMat;
|
||||
transform(centers, orgPointsMat, H.inv());
|
||||
convertPointsFromHomogeneous(orgPointsMat, centers);
|
||||
}
|
||||
Mat(centers).copyTo(_centers);
|
||||
return false;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
bool findCirclesGrid(InputArray _image, Size patternSize,
|
||||
|
||||
@@ -1622,7 +1622,7 @@ size_t CirclesGridFinder::getFirstCorner(std::vector<Point> &largeCornerIndices,
|
||||
int cornerIdx = 0;
|
||||
bool waitOutsider = true;
|
||||
|
||||
for(;;)
|
||||
for (size_t i = 0; i < cornersCount * 2; ++i)
|
||||
{
|
||||
if (waitOutsider)
|
||||
{
|
||||
@@ -1632,11 +1632,11 @@ size_t CirclesGridFinder::getFirstCorner(std::vector<Point> &largeCornerIndices,
|
||||
else
|
||||
{
|
||||
if (isInsider[(cornerIdx + 1) % cornersCount])
|
||||
break;
|
||||
return cornerIdx;
|
||||
}
|
||||
|
||||
cornerIdx = (cornerIdx + 1) % cornersCount;
|
||||
}
|
||||
|
||||
return cornerIdx;
|
||||
CV_Error(Error::StsNoConv, "isInsider array has the same values");
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "p3p.h"
|
||||
#include "ap3p.h"
|
||||
#include "ippe.hpp"
|
||||
#include "sqpnp.hpp"
|
||||
#include "opencv2/calib3d/calib3d_c.h"
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
|
||||
@@ -751,7 +752,8 @@ int solvePnPGeneric( InputArray _opoints, InputArray _ipoints,
|
||||
|
||||
Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
|
||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
||||
CV_Assert( ( (npoints >= 4) || (npoints == 3 && flags == SOLVEPNP_ITERATIVE && useExtrinsicGuess) )
|
||||
CV_Assert( ( (npoints >= 4) || (npoints == 3 && flags == SOLVEPNP_ITERATIVE && useExtrinsicGuess)
|
||||
|| (npoints >= 3 && flags == SOLVEPNP_SQPNP) )
|
||||
&& npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
|
||||
|
||||
opoints = opoints.reshape(3, npoints);
|
||||
@@ -936,6 +938,14 @@ int solvePnPGeneric( InputArray _opoints, InputArray _ipoints,
|
||||
}
|
||||
} catch (...) { }
|
||||
}
|
||||
else if (flags == SOLVEPNP_SQPNP)
|
||||
{
|
||||
Mat undistortedPoints;
|
||||
undistortPoints(ipoints, undistortedPoints, cameraMatrix, distCoeffs);
|
||||
|
||||
sqpnp::PoseSolver solver;
|
||||
solver.solve(opoints, undistortedPoints, vec_rvecs, vec_tvecs);
|
||||
}
|
||||
/*else if (flags == SOLVEPNP_DLS)
|
||||
{
|
||||
Mat undistortedPoints;
|
||||
@@ -963,7 +973,8 @@ int solvePnPGeneric( InputArray _opoints, InputArray _ipoints,
|
||||
vec_tvecs.push_back(tvec);
|
||||
}*/
|
||||
else
|
||||
CV_Error(CV_StsBadArg, "The flags argument must be one of SOLVEPNP_ITERATIVE, SOLVEPNP_P3P, SOLVEPNP_EPNP or SOLVEPNP_DLS");
|
||||
CV_Error(CV_StsBadArg, "The flags argument must be one of SOLVEPNP_ITERATIVE, SOLVEPNP_P3P, "
|
||||
"SOLVEPNP_EPNP, SOLVEPNP_DLS, SOLVEPNP_UPNP, SOLVEPNP_AP3P, SOLVEPNP_IPPE, SOLVEPNP_IPPE_SQUARE or SOLVEPNP_SQPNP");
|
||||
|
||||
CV_Assert(vec_rvecs.size() == vec_tvecs.size());
|
||||
|
||||
|
||||
@@ -0,0 +1,775 @@
|
||||
// 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
|
||||
|
||||
// This file is based on file issued with the following license:
|
||||
|
||||
/*
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2020, George Terzakis
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "sqpnp.hpp"
|
||||
|
||||
#include <opencv2/calib3d.hpp>
|
||||
|
||||
namespace cv {
|
||||
namespace sqpnp {
|
||||
|
||||
const double PoseSolver::RANK_TOLERANCE = 1e-7;
|
||||
const double PoseSolver::SQP_SQUARED_TOLERANCE = 1e-10;
|
||||
const double PoseSolver::SQP_DET_THRESHOLD = 1.001;
|
||||
const double PoseSolver::ORTHOGONALITY_SQUARED_ERROR_THRESHOLD = 1e-8;
|
||||
const double PoseSolver::EQUAL_VECTORS_SQUARED_DIFF = 1e-10;
|
||||
const double PoseSolver::EQUAL_SQUARED_ERRORS_DIFF = 1e-6;
|
||||
const double PoseSolver::POINT_VARIANCE_THRESHOLD = 1e-5;
|
||||
const double PoseSolver::SQRT3 = std::sqrt(3);
|
||||
const int PoseSolver::SQP_MAX_ITERATION = 15;
|
||||
|
||||
//No checking done here for overflow, since this is not public all call instances
|
||||
//are assumed to be valid
|
||||
template <typename tp, int snrows, int sncols,
|
||||
int dnrows, int dncols>
|
||||
void set(int row, int col, cv::Matx<tp, dnrows, dncols>& dest,
|
||||
const cv::Matx<tp, snrows, sncols>& source)
|
||||
{
|
||||
for (int y = 0; y < snrows; y++)
|
||||
{
|
||||
for (int x = 0; x < sncols; x++)
|
||||
{
|
||||
dest(row + y, col + x) = source(y, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PoseSolver::PoseSolver()
|
||||
: num_null_vectors_(-1),
|
||||
num_solutions_(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void PoseSolver::solve(InputArray objectPoints, InputArray imagePoints, OutputArrayOfArrays rvecs,
|
||||
OutputArrayOfArrays tvecs)
|
||||
{
|
||||
//Input checking
|
||||
int objType = objectPoints.getMat().type();
|
||||
CV_CheckType(objType, objType == CV_32FC3 || objType == CV_64FC3,
|
||||
"Type of objectPoints must be CV_32FC3 or CV_64FC3");
|
||||
|
||||
int imgType = imagePoints.getMat().type();
|
||||
CV_CheckType(imgType, imgType == CV_32FC2 || imgType == CV_64FC2,
|
||||
"Type of imagePoints must be CV_32FC2 or CV_64FC2");
|
||||
|
||||
CV_Assert(objectPoints.rows() == 1 || objectPoints.cols() == 1);
|
||||
CV_Assert(objectPoints.rows() >= 3 || objectPoints.cols() >= 3);
|
||||
CV_Assert(imagePoints.rows() == 1 || imagePoints.cols() == 1);
|
||||
CV_Assert(imagePoints.rows() * imagePoints.cols() == objectPoints.rows() * objectPoints.cols());
|
||||
|
||||
Mat _imagePoints;
|
||||
if (imgType == CV_32FC2)
|
||||
{
|
||||
imagePoints.getMat().convertTo(_imagePoints, CV_64F);
|
||||
}
|
||||
else
|
||||
{
|
||||
_imagePoints = imagePoints.getMat();
|
||||
}
|
||||
|
||||
Mat _objectPoints;
|
||||
if (objType == CV_32FC3)
|
||||
{
|
||||
objectPoints.getMat().convertTo(_objectPoints, CV_64F);
|
||||
}
|
||||
else
|
||||
{
|
||||
_objectPoints = objectPoints.getMat();
|
||||
}
|
||||
|
||||
num_null_vectors_ = -1;
|
||||
num_solutions_ = 0;
|
||||
|
||||
computeOmega(_objectPoints, _imagePoints);
|
||||
solveInternal();
|
||||
|
||||
int depthRot = rvecs.fixedType() ? rvecs.depth() : CV_64F;
|
||||
int depthTrans = tvecs.fixedType() ? tvecs.depth() : CV_64F;
|
||||
|
||||
rvecs.create(num_solutions_, 1, CV_MAKETYPE(depthRot, rvecs.fixedType() && rvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1));
|
||||
tvecs.create(num_solutions_, 1, CV_MAKETYPE(depthTrans, tvecs.fixedType() && tvecs.kind() == _InputArray::STD_VECTOR ? 3 : 1));
|
||||
|
||||
for (int i = 0; i < num_solutions_; i++)
|
||||
{
|
||||
|
||||
Mat rvec;
|
||||
Mat rotation = Mat(solutions_[i].r_hat).reshape(1, 3);
|
||||
Rodrigues(rotation, rvec);
|
||||
|
||||
rvecs.getMatRef(i) = rvec;
|
||||
tvecs.getMatRef(i) = Mat(solutions_[i].t);
|
||||
}
|
||||
}
|
||||
|
||||
void PoseSolver::computeOmega(InputArray objectPoints, InputArray imagePoints)
|
||||
{
|
||||
omega_ = cv::Matx<double, 9, 9>::zeros();
|
||||
cv::Matx<double, 3, 9> qa_sum = cv::Matx<double, 3, 9>::zeros();
|
||||
|
||||
cv::Point2d sum_img(0, 0);
|
||||
cv::Point3d sum_obj(0, 0, 0);
|
||||
double sq_norm_sum = 0;
|
||||
|
||||
Mat _imagePoints = imagePoints.getMat();
|
||||
Mat _objectPoints = objectPoints.getMat();
|
||||
|
||||
int n = _objectPoints.cols * _objectPoints.rows;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
const cv::Point2d& img_pt = _imagePoints.at<cv::Point2d>(i);
|
||||
const cv::Point3d& obj_pt = _objectPoints.at<cv::Point3d>(i);
|
||||
|
||||
sum_img += img_pt;
|
||||
sum_obj += obj_pt;
|
||||
|
||||
const double& x = img_pt.x, & y = img_pt.y;
|
||||
const double& X = obj_pt.x, & Y = obj_pt.y, & Z = obj_pt.z;
|
||||
double sq_norm = x * x + y * y;
|
||||
sq_norm_sum += sq_norm;
|
||||
|
||||
double X2 = X * X,
|
||||
XY = X * Y,
|
||||
XZ = X * Z,
|
||||
Y2 = Y * Y,
|
||||
YZ = Y * Z,
|
||||
Z2 = Z * Z;
|
||||
|
||||
omega_(0, 0) += X2;
|
||||
omega_(0, 1) += XY;
|
||||
omega_(0, 2) += XZ;
|
||||
omega_(1, 1) += Y2;
|
||||
omega_(1, 2) += YZ;
|
||||
omega_(2, 2) += Z2;
|
||||
|
||||
|
||||
//Populating this manually saves operations by only calculating upper triangle
|
||||
omega_(0, 6) += -x * X2; omega_(0, 7) += -x * XY; omega_(0, 8) += -x * XZ;
|
||||
omega_(1, 7) += -x * Y2; omega_(1, 8) += -x * YZ;
|
||||
omega_(2, 8) += -x * Z2;
|
||||
|
||||
omega_(3, 6) += -y * X2; omega_(3, 7) += -y * XY; omega_(3, 8) += -y * XZ;
|
||||
omega_(4, 7) += -y * Y2; omega_(4, 8) += -y * YZ;
|
||||
omega_(5, 8) += -y * Z2;
|
||||
|
||||
|
||||
omega_(6, 6) += sq_norm * X2; omega_(6, 7) += sq_norm * XY; omega_(6, 8) += sq_norm * XZ;
|
||||
omega_(7, 7) += sq_norm * Y2; omega_(7, 8) += sq_norm * YZ;
|
||||
omega_(8, 8) += sq_norm * Z2;
|
||||
|
||||
//Compute qa_sum
|
||||
qa_sum(0, 0) += X; qa_sum(0, 1) += Y; qa_sum(0, 2) += Z;
|
||||
qa_sum(1, 3) += X; qa_sum(1, 4) += Y; qa_sum(1, 5) += Z;
|
||||
|
||||
qa_sum(0, 6) += -x * X; qa_sum(0, 7) += -x * Y; qa_sum(0, 8) += -x * Z;
|
||||
qa_sum(1, 6) += -y * X; qa_sum(1, 7) += -y * Y; qa_sum(1, 8) += -y * Z;
|
||||
|
||||
qa_sum(2, 0) += -x * X; qa_sum(2, 1) += -x * Y; qa_sum(2, 2) += -x * Z;
|
||||
qa_sum(2, 3) += -y * X; qa_sum(2, 4) += -y * Y; qa_sum(2, 5) += -y * Z;
|
||||
|
||||
qa_sum(2, 6) += sq_norm * X; qa_sum(2, 7) += sq_norm * Y; qa_sum(2, 8) += sq_norm * Z;
|
||||
}
|
||||
|
||||
|
||||
omega_(1, 6) = omega_(0, 7); omega_(2, 6) = omega_(0, 8); omega_(2, 7) = omega_(1, 8);
|
||||
omega_(4, 6) = omega_(3, 7); omega_(5, 6) = omega_(3, 8); omega_(5, 7) = omega_(4, 8);
|
||||
omega_(7, 6) = omega_(6, 7); omega_(8, 6) = omega_(6, 8); omega_(8, 7) = omega_(7, 8);
|
||||
|
||||
|
||||
omega_(3, 3) = omega_(0, 0); omega_(3, 4) = omega_(0, 1); omega_(3, 5) = omega_(0, 2);
|
||||
omega_(4, 4) = omega_(1, 1); omega_(4, 5) = omega_(1, 2);
|
||||
omega_(5, 5) = omega_(2, 2);
|
||||
|
||||
//Mirror upper triangle to lower triangle
|
||||
for (int r = 0; r < 9; r++)
|
||||
{
|
||||
for (int c = 0; c < r; c++)
|
||||
{
|
||||
omega_(r, c) = omega_(c, r);
|
||||
}
|
||||
}
|
||||
|
||||
cv::Matx<double, 3, 3> q;
|
||||
q(0, 0) = n; q(0, 1) = 0; q(0, 2) = -sum_img.x;
|
||||
q(1, 0) = 0; q(1, 1) = n; q(1, 2) = -sum_img.y;
|
||||
q(2, 0) = -sum_img.x; q(2, 1) = -sum_img.y; q(2, 2) = sq_norm_sum;
|
||||
|
||||
double inv_n = 1.0 / n;
|
||||
double detQ = n * (n * sq_norm_sum - sum_img.y * sum_img.y - sum_img.x * sum_img.x);
|
||||
double point_coordinate_variance = detQ * inv_n * inv_n * inv_n;
|
||||
|
||||
CV_Assert(point_coordinate_variance >= POINT_VARIANCE_THRESHOLD);
|
||||
|
||||
Matx<double, 3, 3> q_inv;
|
||||
analyticalInverse3x3Symm(q, q_inv);
|
||||
|
||||
p_ = -q_inv * qa_sum;
|
||||
|
||||
omega_ += qa_sum.t() * p_;
|
||||
|
||||
cv::SVD omega_svd(omega_, cv::SVD::FULL_UV);
|
||||
s_ = omega_svd.w;
|
||||
u_ = cv::Mat(omega_svd.vt.t());
|
||||
|
||||
CV_Assert(s_(0) >= 1e-7);
|
||||
|
||||
while (s_(7 - num_null_vectors_) < RANK_TOLERANCE) num_null_vectors_++;
|
||||
|
||||
CV_Assert(++num_null_vectors_ <= 6);
|
||||
|
||||
point_mean_ = cv::Vec3d(sum_obj.x / n, sum_obj.y / n, sum_obj.z / n);
|
||||
}
|
||||
|
||||
void PoseSolver::solveInternal()
|
||||
{
|
||||
double min_sq_err = std::numeric_limits<double>::max();
|
||||
int num_eigen_points = num_null_vectors_ > 0 ? num_null_vectors_ : 1;
|
||||
|
||||
for (int i = 9 - num_eigen_points; i < 9; i++)
|
||||
{
|
||||
const cv::Matx<double, 9, 1> e = SQRT3 * u_.col(i);
|
||||
double orthogonality_sq_err = orthogonalityError(e);
|
||||
|
||||
SQPSolution solutions[2];
|
||||
|
||||
//If e is orthogonal, we can skip SQP
|
||||
if (orthogonality_sq_err < ORTHOGONALITY_SQUARED_ERROR_THRESHOLD)
|
||||
{
|
||||
solutions[0].r_hat = det3x3(e) * e;
|
||||
solutions[0].t = p_ * solutions[0].r_hat;
|
||||
checkSolution(solutions[0], min_sq_err);
|
||||
}
|
||||
else
|
||||
{
|
||||
Matx<double, 9, 1> r;
|
||||
nearestRotationMatrix(e, r);
|
||||
solutions[0] = runSQP(r);
|
||||
solutions[0].t = p_ * solutions[0].r_hat;
|
||||
checkSolution(solutions[0], min_sq_err);
|
||||
|
||||
nearestRotationMatrix(-e, r);
|
||||
solutions[1] = runSQP(r);
|
||||
solutions[1].t = p_ * solutions[1].r_hat;
|
||||
checkSolution(solutions[1], min_sq_err);
|
||||
}
|
||||
}
|
||||
|
||||
int c = 1;
|
||||
|
||||
while (min_sq_err > 3 * s_[9 - num_eigen_points - c] && 9 - num_eigen_points - c > 0)
|
||||
{
|
||||
int index = 9 - num_eigen_points - c;
|
||||
|
||||
const cv::Matx<double, 9, 1> e = u_.col(index);
|
||||
SQPSolution solutions[2];
|
||||
|
||||
Matx<double, 9, 1> r;
|
||||
nearestRotationMatrix(e, r);
|
||||
solutions[0] = runSQP(r);
|
||||
solutions[0].t = p_ * solutions[0].r_hat;
|
||||
checkSolution(solutions[0], min_sq_err);
|
||||
|
||||
nearestRotationMatrix(-e, r);
|
||||
solutions[1] = runSQP(r);
|
||||
solutions[1].t = p_ * solutions[1].r_hat;
|
||||
checkSolution(solutions[1], min_sq_err);
|
||||
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
PoseSolver::SQPSolution PoseSolver::runSQP(const cv::Matx<double, 9, 1>& r0)
|
||||
{
|
||||
cv::Matx<double, 9, 1> r = r0;
|
||||
|
||||
double delta_squared_norm = std::numeric_limits<double>::max();
|
||||
cv::Matx<double, 9, 1> delta;
|
||||
|
||||
int step = 0;
|
||||
while (delta_squared_norm > SQP_SQUARED_TOLERANCE && step++ < SQP_MAX_ITERATION)
|
||||
{
|
||||
solveSQPSystem(r, delta);
|
||||
r += delta;
|
||||
delta_squared_norm = cv::norm(delta, cv::NORM_L2SQR);
|
||||
}
|
||||
|
||||
SQPSolution solution;
|
||||
|
||||
double det_r = det3x3(r);
|
||||
if (det_r < 0)
|
||||
{
|
||||
r = -r;
|
||||
det_r = -det_r;
|
||||
}
|
||||
|
||||
if (det_r > SQP_DET_THRESHOLD)
|
||||
{
|
||||
nearestRotationMatrix(r, solution.r_hat);
|
||||
}
|
||||
else
|
||||
{
|
||||
solution.r_hat = r;
|
||||
}
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
void PoseSolver::solveSQPSystem(const cv::Matx<double, 9, 1>& r, cv::Matx<double, 9, 1>& delta)
|
||||
{
|
||||
double sqnorm_r1 = r(0) * r(0) + r(1) * r(1) + r(2) * r(2),
|
||||
sqnorm_r2 = r(3) * r(3) + r(4) * r(4) + r(5) * r(5),
|
||||
sqnorm_r3 = r(6) * r(6) + r(7) * r(7) + r(8) * r(8);
|
||||
double dot_r1r2 = r(0) * r(3) + r(1) * r(4) + r(2) * r(5),
|
||||
dot_r1r3 = r(0) * r(6) + r(1) * r(7) + r(2) * r(8),
|
||||
dot_r2r3 = r(3) * r(6) + r(4) * r(7) + r(5) * r(8);
|
||||
|
||||
cv::Matx<double, 9, 3> N;
|
||||
cv::Matx<double, 9, 6> H;
|
||||
cv::Matx<double, 6, 6> JH;
|
||||
|
||||
computeRowAndNullspace(r, H, N, JH);
|
||||
|
||||
cv::Matx<double, 6, 1> g;
|
||||
g(0) = 1 - sqnorm_r1; g(1) = 1 - sqnorm_r2; g(2) = 1 - sqnorm_r3; g(3) = -dot_r1r2; g(4) = -dot_r2r3; g(5) = -dot_r1r3;
|
||||
|
||||
cv::Matx<double, 6, 1> x;
|
||||
x(0) = g(0) / JH(0, 0);
|
||||
x(1) = g(1) / JH(1, 1);
|
||||
x(2) = g(2) / JH(2, 2);
|
||||
x(3) = (g(3) - JH(3, 0) * x(0) - JH(3, 1) * x(1)) / JH(3, 3);
|
||||
x(4) = (g(4) - JH(4, 1) * x(1) - JH(4, 2) * x(2) - JH(4, 3) * x(3)) / JH(4, 4);
|
||||
x(5) = (g(5) - JH(5, 0) * x(0) - JH(5, 2) * x(2) - JH(5, 3) * x(3) - JH(5, 4) * x(4)) / JH(5, 5);
|
||||
|
||||
delta = H * x;
|
||||
|
||||
|
||||
cv::Matx<double, 3, 9> nt_omega = N.t() * omega_;
|
||||
cv::Matx<double, 3, 3> W = nt_omega * N, W_inv;
|
||||
|
||||
analyticalInverse3x3Symm(W, W_inv);
|
||||
|
||||
cv::Matx<double, 3, 1> y = -W_inv * nt_omega * (delta + r);
|
||||
delta += N * y;
|
||||
}
|
||||
|
||||
bool PoseSolver::analyticalInverse3x3Symm(const cv::Matx<double, 3, 3>& Q,
|
||||
cv::Matx<double, 3, 3>& Qinv,
|
||||
const double& threshold)
|
||||
{
|
||||
// 1. Get the elements of the matrix
|
||||
double a = Q(0, 0),
|
||||
b = Q(1, 0), d = Q(1, 1),
|
||||
c = Q(2, 0), e = Q(2, 1), f = Q(2, 2);
|
||||
|
||||
// 2. Determinant
|
||||
double t2, t4, t7, t9, t12;
|
||||
t2 = e * e;
|
||||
t4 = a * d;
|
||||
t7 = b * b;
|
||||
t9 = b * c;
|
||||
t12 = c * c;
|
||||
double det = -t4 * f + a * t2 + t7 * f - 2.0 * t9 * e + t12 * d;
|
||||
|
||||
if (fabs(det) < threshold) return false;
|
||||
|
||||
// 3. Inverse
|
||||
double t15, t20, t24, t30;
|
||||
t15 = 1.0 / det;
|
||||
t20 = (-b * f + c * e) * t15;
|
||||
t24 = (b * e - c * d) * t15;
|
||||
t30 = (a * e - t9) * t15;
|
||||
Qinv(0, 0) = (-d * f + t2) * t15;
|
||||
Qinv(0, 1) = Qinv(1, 0) = -t20;
|
||||
Qinv(0, 2) = Qinv(2, 0) = -t24;
|
||||
Qinv(1, 1) = -(a * f - t12) * t15;
|
||||
Qinv(1, 2) = Qinv(2, 1) = t30;
|
||||
Qinv(2, 2) = -(t4 - t7) * t15;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
|
||||
cv::Matx<double, 9, 6>& H,
|
||||
cv::Matx<double, 9, 3>& N,
|
||||
cv::Matx<double, 6, 6>& K,
|
||||
const double& norm_threshold)
|
||||
{
|
||||
H = cv::Matx<double, 9, 6>::zeros();
|
||||
|
||||
// 1. q1
|
||||
double norm_r1 = sqrt(r(0) * r(0) + r(1) * r(1) + r(2) * r(2));
|
||||
double inv_norm_r1 = norm_r1 > 1e-5 ? 1.0 / norm_r1 : 0.0;
|
||||
H(0, 0) = r(0) * inv_norm_r1;
|
||||
H(1, 0) = r(1) * inv_norm_r1;
|
||||
H(2, 0) = r(2) * inv_norm_r1;
|
||||
K(0, 0) = 2 * norm_r1;
|
||||
|
||||
// 2. q2
|
||||
double norm_r2 = sqrt(r(3) * r(3) + r(4) * r(4) + r(5) * r(5));
|
||||
double inv_norm_r2 = 1.0 / norm_r2;
|
||||
H(3, 1) = r(3) * inv_norm_r2;
|
||||
H(4, 1) = r(4) * inv_norm_r2;
|
||||
H(5, 1) = r(5) * inv_norm_r2;
|
||||
K(1, 0) = 0;
|
||||
K(1, 1) = 2 * norm_r2;
|
||||
|
||||
// 3. q3 = (r3'*q2)*q2 - (r3'*q1)*q1 ; q3 = q3/norm(q3)
|
||||
double norm_r3 = sqrt(r(6) * r(6) + r(7) * r(7) + r(8) * r(8));
|
||||
double inv_norm_r3 = 1.0 / norm_r3;
|
||||
H(6, 2) = r(6) * inv_norm_r3;
|
||||
H(7, 2) = r(7) * inv_norm_r3;
|
||||
H(8, 2) = r(8) * inv_norm_r3;
|
||||
K(2, 0) = K(2, 1) = 0;
|
||||
K(2, 2) = 2 * norm_r3;
|
||||
|
||||
// 4. q4
|
||||
double dot_j4q1 = r(3) * H(0, 0) + r(4) * H(1, 0) + r(5) * H(2, 0),
|
||||
dot_j4q2 = r(0) * H(3, 1) + r(1) * H(4, 1) + r(2) * H(5, 1);
|
||||
|
||||
H(0, 3) = r(3) - dot_j4q1 * H(0, 0);
|
||||
H(1, 3) = r(4) - dot_j4q1 * H(1, 0);
|
||||
H(2, 3) = r(5) - dot_j4q1 * H(2, 0);
|
||||
H(3, 3) = r(0) - dot_j4q2 * H(3, 1);
|
||||
H(4, 3) = r(1) - dot_j4q2 * H(4, 1);
|
||||
H(5, 3) = r(2) - dot_j4q2 * H(5, 1);
|
||||
double inv_norm_j4 = 1.0 / sqrt(H(0, 3) * H(0, 3) + H(1, 3) * H(1, 3) + H(2, 3) * H(2, 3) +
|
||||
H(3, 3) * H(3, 3) + H(4, 3) * H(4, 3) + H(5, 3) * H(5, 3));
|
||||
|
||||
H(0, 3) *= inv_norm_j4;
|
||||
H(1, 3) *= inv_norm_j4;
|
||||
H(2, 3) *= inv_norm_j4;
|
||||
H(3, 3) *= inv_norm_j4;
|
||||
H(4, 3) *= inv_norm_j4;
|
||||
H(5, 3) *= inv_norm_j4;
|
||||
|
||||
K(3, 0) = r(3) * H(0, 0) + r(4) * H(1, 0) + r(5) * H(2, 0);
|
||||
K(3, 1) = r(0) * H(3, 1) + r(1) * H(4, 1) + r(2) * H(5, 1);
|
||||
K(3, 2) = 0;
|
||||
K(3, 3) = r(3) * H(0, 3) + r(4) * H(1, 3) + r(5) * H(2, 3) + r(0) * H(3, 3) + r(1) * H(4, 3) + r(2) * H(5, 3);
|
||||
|
||||
// 5. q5
|
||||
double dot_j5q2 = r(6) * H(3, 1) + r(7) * H(4, 1) + r(8) * H(5, 1);
|
||||
double dot_j5q3 = r(3) * H(6, 2) + r(4) * H(7, 2) + r(5) * H(8, 2);
|
||||
double dot_j5q4 = r(6) * H(3, 3) + r(7) * H(4, 3) + r(8) * H(5, 3);
|
||||
|
||||
H(0, 4) = -dot_j5q4 * H(0, 3);
|
||||
H(1, 4) = -dot_j5q4 * H(1, 3);
|
||||
H(2, 4) = -dot_j5q4 * H(2, 3);
|
||||
H(3, 4) = r(6) - dot_j5q2 * H(3, 1) - dot_j5q4 * H(3, 3);
|
||||
H(4, 4) = r(7) - dot_j5q2 * H(4, 1) - dot_j5q4 * H(4, 3);
|
||||
H(5, 4) = r(8) - dot_j5q2 * H(5, 1) - dot_j5q4 * H(5, 3);
|
||||
H(6, 4) = r(3) - dot_j5q3 * H(6, 2); H(7, 4) = r(4) - dot_j5q3 * H(7, 2); H(8, 4) = r(5) - dot_j5q3 * H(8, 2);
|
||||
|
||||
Matx<double, 9, 1> q4 = H.col(4);
|
||||
q4 /= cv::norm(q4);
|
||||
set<double, 9, 1, 9, 6>(0, 4, H, q4);
|
||||
|
||||
K(4, 0) = 0;
|
||||
K(4, 1) = r(6) * H(3, 1) + r(7) * H(4, 1) + r(8) * H(5, 1);
|
||||
K(4, 2) = r(3) * H(6, 2) + r(4) * H(7, 2) + r(5) * H(8, 2);
|
||||
K(4, 3) = r(6) * H(3, 3) + r(7) * H(4, 3) + r(8) * H(5, 3);
|
||||
K(4, 4) = r(6) * H(3, 4) + r(7) * H(4, 4) + r(8) * H(5, 4) + r(3) * H(6, 4) + r(4) * H(7, 4) + r(5) * H(8, 4);
|
||||
|
||||
|
||||
// 4. q6
|
||||
double dot_j6q1 = r(6) * H(0, 0) + r(7) * H(1, 0) + r(8) * H(2, 0);
|
||||
double dot_j6q3 = r(0) * H(6, 2) + r(1) * H(7, 2) + r(2) * H(8, 2);
|
||||
double dot_j6q4 = r(6) * H(0, 3) + r(7) * H(1, 3) + r(8) * H(2, 3);
|
||||
double dot_j6q5 = r(0) * H(6, 4) + r(1) * H(7, 4) + r(2) * H(8, 4) + r(6) * H(0, 4) + r(7) * H(1, 4) + r(8) * H(2, 4);
|
||||
|
||||
H(0, 5) = r(6) - dot_j6q1 * H(0, 0) - dot_j6q4 * H(0, 3) - dot_j6q5 * H(0, 4);
|
||||
H(1, 5) = r(7) - dot_j6q1 * H(1, 0) - dot_j6q4 * H(1, 3) - dot_j6q5 * H(1, 4);
|
||||
H(2, 5) = r(8) - dot_j6q1 * H(2, 0) - dot_j6q4 * H(2, 3) - dot_j6q5 * H(2, 4);
|
||||
|
||||
H(3, 5) = -dot_j6q5 * H(3, 4) - dot_j6q4 * H(3, 3);
|
||||
H(4, 5) = -dot_j6q5 * H(4, 4) - dot_j6q4 * H(4, 3);
|
||||
H(5, 5) = -dot_j6q5 * H(5, 4) - dot_j6q4 * H(5, 3);
|
||||
|
||||
H(6, 5) = r(0) - dot_j6q3 * H(6, 2) - dot_j6q5 * H(6, 4);
|
||||
H(7, 5) = r(1) - dot_j6q3 * H(7, 2) - dot_j6q5 * H(7, 4);
|
||||
H(8, 5) = r(2) - dot_j6q3 * H(8, 2) - dot_j6q5 * H(8, 4);
|
||||
|
||||
Matx<double, 9, 1> q5 = H.col(5);
|
||||
q5 /= cv::norm(q5);
|
||||
set<double, 9, 1, 9, 6>(0, 5, H, q5);
|
||||
|
||||
K(5, 0) = r(6) * H(0, 0) + r(7) * H(1, 0) + r(8) * H(2, 0);
|
||||
K(5, 1) = 0; K(5, 2) = r(0) * H(6, 2) + r(1) * H(7, 2) + r(2) * H(8, 2);
|
||||
K(5, 3) = r(6) * H(0, 3) + r(7) * H(1, 3) + r(8) * H(2, 3);
|
||||
K(5, 4) = r(6) * H(0, 4) + r(7) * H(1, 4) + r(8) * H(2, 4) + r(0) * H(6, 4) + r(1) * H(7, 4) + r(2) * H(8, 4);
|
||||
K(5, 5) = r(6) * H(0, 5) + r(7) * H(1, 5) + r(8) * H(2, 5) + r(0) * H(6, 5) + r(1) * H(7, 5) + r(2) * H(8, 5);
|
||||
|
||||
// Great! Now H is an orthogonalized, sparse basis of the Jacobian row space and K is filled.
|
||||
//
|
||||
// Now get a projector onto the null space H:
|
||||
const cv::Matx<double, 9, 9> Pn = cv::Matx<double, 9, 9>::eye() - (H * H.t());
|
||||
|
||||
// Now we need to pick 3 columns of P with non-zero norm (> 0.3) and some angle between them (> 0.3).
|
||||
//
|
||||
// Find the 3 columns of Pn with largest norms
|
||||
int index1 = 0,
|
||||
index2 = 0,
|
||||
index3 = 0;
|
||||
double max_norm1 = std::numeric_limits<double>::min();
|
||||
double min_dot12 = std::numeric_limits<double>::max();
|
||||
double min_dot1323 = std::numeric_limits<double>::max();
|
||||
|
||||
|
||||
double col_norms[9];
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
col_norms[i] = cv::norm(Pn.col(i));
|
||||
if (col_norms[i] >= norm_threshold)
|
||||
{
|
||||
if (max_norm1 < col_norms[i])
|
||||
{
|
||||
max_norm1 = col_norms[i];
|
||||
index1 = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Matx<double, 9, 1> v1 = Pn.col(index1);
|
||||
v1 /= max_norm1;
|
||||
set<double, 9, 1, 9, 3>(0, 0, N, v1);
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
if (i == index1) continue;
|
||||
if (col_norms[i] >= norm_threshold)
|
||||
{
|
||||
double cos_v1_x_col = fabs(Pn.col(i).dot(v1) / col_norms[i]);
|
||||
|
||||
if (cos_v1_x_col <= min_dot12)
|
||||
{
|
||||
index2 = i;
|
||||
min_dot12 = cos_v1_x_col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Matx<double, 9, 1> v2 = Pn.col(index2);
|
||||
Matx<double, 9, 1> n0 = N.col(0);
|
||||
v2 -= v2.dot(n0) * n0;
|
||||
v2 /= cv::norm(v2);
|
||||
set<double, 9, 1, 9, 3>(0, 1, N, v2);
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
if (i == index2 || i == index1) continue;
|
||||
if (col_norms[i] >= norm_threshold)
|
||||
{
|
||||
double cos_v1_x_col = fabs(Pn.col(i).dot(v1) / col_norms[i]);
|
||||
double cos_v2_x_col = fabs(Pn.col(i).dot(v2) / col_norms[i]);
|
||||
|
||||
if (cos_v1_x_col + cos_v2_x_col <= min_dot1323)
|
||||
{
|
||||
index3 = i;
|
||||
min_dot1323 = cos_v2_x_col + cos_v2_x_col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Matx<double, 9, 1> v3 = Pn.col(index3);
|
||||
Matx<double, 9, 1> n1 = N.col(1);
|
||||
v3 -= (v3.dot(n1)) * n1 - (v3.dot(n0)) * n0;
|
||||
v3 /= cv::norm(v3);
|
||||
set<double, 9, 1, 9, 3>(0, 2, N, v3);
|
||||
|
||||
}
|
||||
|
||||
// faster nearest rotation computation based on FOAM (see: http://users.ics.forth.gr/~lourakis/publ/2018_iros.pdf )
|
||||
/* Solve the nearest orthogonal approximation problem
|
||||
* i.e., given e, find R minimizing ||R-e||_F
|
||||
*
|
||||
* The computation borrows from Markley's FOAM algorithm
|
||||
* "Attitude Determination Using Vector Observations: A Fast Optimal Matrix Algorithm", J. Astronaut. Sci.
|
||||
*
|
||||
* See also M. Lourakis: "An Efficient Solution to Absolute Orientation", ICPR 2016
|
||||
*
|
||||
* Copyright (C) 2019 Manolis Lourakis (lourakis **at** ics forth gr)
|
||||
* Institute of Computer Science, Foundation for Research & Technology - Hellas
|
||||
* Heraklion, Crete, Greece.
|
||||
*/
|
||||
void PoseSolver::nearestRotationMatrix(const cv::Matx<double, 9, 1>& e,
|
||||
cv::Matx<double, 9, 1>& r)
|
||||
{
|
||||
int i;
|
||||
double l, lprev, det_e, e_sq, adj_e_sq, adj_e[9];
|
||||
|
||||
// e's adjoint
|
||||
adj_e[0] = e(4) * e(8) - e(5) * e(7); adj_e[1] = e(2) * e(7) - e(1) * e(8); adj_e[2] = e(1) * e(5) - e(2) * e(4);
|
||||
adj_e[3] = e(5) * e(6) - e(3) * e(8); adj_e[4] = e(0) * e(8) - e(2) * e(6); adj_e[5] = e(2) * e(3) - e(0) * e(5);
|
||||
adj_e[6] = e(3) * e(7) - e(4) * e(6); adj_e[7] = e(1) * e(6) - e(0) * e(7); adj_e[8] = e(0) * e(4) - e(1) * e(3);
|
||||
|
||||
// det(e), ||e||^2, ||adj(e)||^2
|
||||
det_e = e(0) * e(4) * e(8) - e(0) * e(5) * e(7) - e(1) * e(3) * e(8) + e(2) * e(3) * e(7) + e(1) * e(6) * e(5) - e(2) * e(6) * e(4);
|
||||
e_sq = e(0) * e(0) + e(1) * e(1) + e(2) * e(2) + e(3) * e(3) + e(4) * e(4) + e(5) * e(5) + e(6) * e(6) + e(7) * e(7) + e(8) * e(8);
|
||||
adj_e_sq = adj_e[0] * adj_e[0] + adj_e[1] * adj_e[1] + adj_e[2] * adj_e[2] + adj_e[3] * adj_e[3] + adj_e[4] * adj_e[4] + adj_e[5] * adj_e[5] + adj_e[6] * adj_e[6] + adj_e[7] * adj_e[7] + adj_e[8] * adj_e[8];
|
||||
|
||||
// compute l_max with Newton-Raphson from FOAM's characteristic polynomial, i.e. eq.(23) - (26)
|
||||
for (i = 200, l = 2.0, lprev = 0.0; fabs(l - lprev) > 1E-12 * fabs(lprev) && i > 0; --i) {
|
||||
double tmp, p, pp;
|
||||
|
||||
tmp = (l * l - e_sq);
|
||||
p = (tmp * tmp - 8.0 * l * det_e - 4.0 * adj_e_sq);
|
||||
pp = 8.0 * (0.5 * tmp * l - det_e);
|
||||
|
||||
lprev = l;
|
||||
l -= p / pp;
|
||||
}
|
||||
|
||||
// the rotation matrix equals ((l^2 + e_sq)*e + 2*l*adj(e') - 2*e*e'*e) / (l*(l*l-e_sq) - 2*det(e)), i.e. eq.(14) using (18), (19)
|
||||
{
|
||||
// compute (l^2 + e_sq)*e
|
||||
double tmp[9], e_et[9], denom;
|
||||
const double a = l * l + e_sq;
|
||||
|
||||
// e_et=e*e'
|
||||
e_et[0] = e(0) * e(0) + e(1) * e(1) + e(2) * e(2);
|
||||
e_et[1] = e(0) * e(3) + e(1) * e(4) + e(2) * e(5);
|
||||
e_et[2] = e(0) * e(6) + e(1) * e(7) + e(2) * e(8);
|
||||
|
||||
e_et[3] = e_et[1];
|
||||
e_et[4] = e(3) * e(3) + e(4) * e(4) + e(5) * e(5);
|
||||
e_et[5] = e(3) * e(6) + e(4) * e(7) + e(5) * e(8);
|
||||
|
||||
e_et[6] = e_et[2];
|
||||
e_et[7] = e_et[5];
|
||||
e_et[8] = e(6) * e(6) + e(7) * e(7) + e(8) * e(8);
|
||||
|
||||
// tmp=e_et*e
|
||||
tmp[0] = e_et[0] * e(0) + e_et[1] * e(3) + e_et[2] * e(6);
|
||||
tmp[1] = e_et[0] * e(1) + e_et[1] * e(4) + e_et[2] * e(7);
|
||||
tmp[2] = e_et[0] * e(2) + e_et[1] * e(5) + e_et[2] * e(8);
|
||||
|
||||
tmp[3] = e_et[3] * e(0) + e_et[4] * e(3) + e_et[5] * e(6);
|
||||
tmp[4] = e_et[3] * e(1) + e_et[4] * e(4) + e_et[5] * e(7);
|
||||
tmp[5] = e_et[3] * e(2) + e_et[4] * e(5) + e_et[5] * e(8);
|
||||
|
||||
tmp[6] = e_et[6] * e(0) + e_et[7] * e(3) + e_et[8] * e(6);
|
||||
tmp[7] = e_et[6] * e(1) + e_et[7] * e(4) + e_et[8] * e(7);
|
||||
tmp[8] = e_et[6] * e(2) + e_et[7] * e(5) + e_et[8] * e(8);
|
||||
|
||||
// compute R as (a*e + 2*(l*adj(e)' - tmp))*denom; note that adj(e')=adj(e)'
|
||||
denom = l * (l * l - e_sq) - 2.0 * det_e;
|
||||
denom = 1.0 / denom;
|
||||
r(0) = (a * e(0) + 2.0 * (l * adj_e[0] - tmp[0])) * denom;
|
||||
r(1) = (a * e(1) + 2.0 * (l * adj_e[3] - tmp[1])) * denom;
|
||||
r(2) = (a * e(2) + 2.0 * (l * adj_e[6] - tmp[2])) * denom;
|
||||
|
||||
r(3) = (a * e(3) + 2.0 * (l * adj_e[1] - tmp[3])) * denom;
|
||||
r(4) = (a * e(4) + 2.0 * (l * adj_e[4] - tmp[4])) * denom;
|
||||
r(5) = (a * e(5) + 2.0 * (l * adj_e[7] - tmp[5])) * denom;
|
||||
|
||||
r(6) = (a * e(6) + 2.0 * (l * adj_e[2] - tmp[6])) * denom;
|
||||
r(7) = (a * e(7) + 2.0 * (l * adj_e[5] - tmp[7])) * denom;
|
||||
r(8) = (a * e(8) + 2.0 * (l * adj_e[8] - tmp[8])) * denom;
|
||||
}
|
||||
}
|
||||
|
||||
double PoseSolver::det3x3(const cv::Matx<double, 9, 1>& e)
|
||||
{
|
||||
return e(0) * e(4) * e(8) + e(1) * e(5) * e(6) + e(2) * e(3) * e(7)
|
||||
- e(6) * e(4) * e(2) - e(7) * e(5) * e(0) - e(8) * e(3) * e(1);
|
||||
}
|
||||
|
||||
inline bool PoseSolver::positiveDepth(const SQPSolution& solution) const
|
||||
{
|
||||
const cv::Matx<double, 9, 1>& r = solution.r_hat;
|
||||
const cv::Matx<double, 3, 1>& t = solution.t;
|
||||
const cv::Vec3d& mean = point_mean_;
|
||||
return (r(6) * mean(0) + r(7) * mean(1) + r(8) * mean(2) + t(2) > 0);
|
||||
}
|
||||
|
||||
void PoseSolver::checkSolution(SQPSolution& solution, double& min_error)
|
||||
{
|
||||
if (positiveDepth(solution))
|
||||
{
|
||||
solution.sq_error = (omega_ * solution.r_hat).ddot(solution.r_hat);
|
||||
if (fabs(min_error - solution.sq_error) > EQUAL_SQUARED_ERRORS_DIFF)
|
||||
{
|
||||
if (min_error > solution.sq_error)
|
||||
{
|
||||
min_error = solution.sq_error;
|
||||
solutions_[0] = solution;
|
||||
num_solutions_ = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = 0; i < num_solutions_; i++)
|
||||
{
|
||||
if (cv::norm(solutions_[i].r_hat - solution.r_hat, cv::NORM_L2SQR) < EQUAL_VECTORS_SQUARED_DIFF)
|
||||
{
|
||||
if (solutions_[i].sq_error > solution.sq_error)
|
||||
{
|
||||
solutions_[i] = solution;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
solutions_[num_solutions_++] = solution;
|
||||
}
|
||||
if (min_error > solution.sq_error) min_error = solution.sq_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double PoseSolver::orthogonalityError(const cv::Matx<double, 9, 1>& e)
|
||||
{
|
||||
double sq_norm_e1 = e(0) * e(0) + e(1) * e(1) + e(2) * e(2);
|
||||
double sq_norm_e2 = e(3) * e(3) + e(4) * e(4) + e(5) * e(5);
|
||||
double sq_norm_e3 = e(6) * e(6) + e(7) * e(7) + e(8) * e(8);
|
||||
double dot_e1e2 = e(0) * e(3) + e(1) * e(4) + e(2) * e(5);
|
||||
double dot_e1e3 = e(0) * e(6) + e(1) * e(7) + e(2) * e(8);
|
||||
double dot_e2e3 = e(3) * e(6) + e(4) * e(7) + e(5) * e(8);
|
||||
|
||||
return (sq_norm_e1 - 1) * (sq_norm_e1 - 1) + (sq_norm_e2 - 1) * (sq_norm_e2 - 1) + (sq_norm_e3 - 1) * (sq_norm_e3 - 1) +
|
||||
2 * (dot_e1e2 * dot_e1e2 + dot_e1e3 * dot_e1e3 + dot_e2e3 * dot_e2e3);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
// 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
|
||||
|
||||
// This file is based on file issued with the following license:
|
||||
|
||||
/*
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2020, George Terzakis
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
*/
|
||||
|
||||
#ifndef OPENCV_CALIB3D_SQPNP_HPP
|
||||
#define OPENCV_CALIB3D_SQPNP_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
namespace cv {
|
||||
namespace sqpnp {
|
||||
|
||||
|
||||
class PoseSolver {
|
||||
public:
|
||||
/**
|
||||
* @brief PoseSolver constructor
|
||||
*/
|
||||
PoseSolver();
|
||||
|
||||
/**
|
||||
* @brief Finds the possible poses of a camera given a set of 3D points
|
||||
* and their corresponding 2D image projections. The poses are
|
||||
* sorted by lowest squared error (which corresponds to lowest
|
||||
* reprojection error).
|
||||
* @param objectPoints Array or vector of 3 or more 3D points defined in object coordinates.
|
||||
* 1xN/Nx1 3-channel (float or double) where N is the number of points.
|
||||
* @param imagePoints Array or vector of corresponding 2D points, 1xN/Nx1 2-channel.
|
||||
* @param rvec The output rotation solutions (up to 18 3x1 rotation vectors)
|
||||
* @param tvec The output translation solutions (up to 18 3x1 vectors)
|
||||
*/
|
||||
void solve(InputArray objectPoints, InputArray imagePoints, OutputArrayOfArrays rvec,
|
||||
OutputArrayOfArrays tvec);
|
||||
|
||||
private:
|
||||
struct SQPSolution
|
||||
{
|
||||
cv::Matx<double, 9, 1> r_hat;
|
||||
cv::Matx<double, 3, 1> t;
|
||||
double sq_error;
|
||||
};
|
||||
|
||||
/*
|
||||
* @brief Computes the 9x9 PSD Omega matrix and supporting matrices.
|
||||
* @param objectPoints Array or vector of 3 or more 3D points defined in object coordinates.
|
||||
* 1xN/Nx1 3-channel (float or double) where N is the number of points.
|
||||
* @param imagePoints Array or vector of corresponding 2D points, 1xN/Nx1 2-channel.
|
||||
*/
|
||||
void computeOmega(InputArray objectPoints, InputArray imagePoints);
|
||||
|
||||
/*
|
||||
* @brief Computes the 9x9 PSD Omega matrix and supporting matrices.
|
||||
*/
|
||||
void solveInternal();
|
||||
|
||||
/*
|
||||
* @brief Produces the distance from being orthogonal for a given 3x3 matrix
|
||||
* in row-major form.
|
||||
* @param e The vector to test representing a 3x3 matrix in row major form.
|
||||
* @return The distance the matrix is from being orthogonal.
|
||||
*/
|
||||
static double orthogonalityError(const cv::Matx<double, 9, 1>& e);
|
||||
|
||||
/*
|
||||
* @brief Processes a solution and sorts it by error.
|
||||
* @param solution The solution to evaluate.
|
||||
* @param min_error The current minimum error.
|
||||
*/
|
||||
void checkSolution(SQPSolution& solution, double& min_error);
|
||||
|
||||
/*
|
||||
* @brief Computes the determinant of a matrix stored in row-major format.
|
||||
* @param e Vector representing a 3x3 matrix stored in row-major format.
|
||||
* @return The determinant of the matrix.
|
||||
*/
|
||||
static double det3x3(const cv::Matx<double, 9, 1>& e);
|
||||
|
||||
/*
|
||||
* @brief Tests the cheirality for a given solution.
|
||||
* @param solution The solution to evaluate.
|
||||
*/
|
||||
inline bool positiveDepth(const SQPSolution& solution) const;
|
||||
|
||||
/*
|
||||
* @brief Determines the nearest rotation matrix to a given rotaiton matrix.
|
||||
* Input and output are 9x1 vector representing a vector stored in row-major
|
||||
* form.
|
||||
* @param e The input 3x3 matrix stored in a vector in row-major form.
|
||||
* @param r The nearest rotation matrix to the input e (again in row-major form).
|
||||
*/
|
||||
static void nearestRotationMatrix(const cv::Matx<double, 9, 1>& e,
|
||||
cv::Matx<double, 9, 1>& r);
|
||||
|
||||
/*
|
||||
* @brief Runs the sequential quadratic programming on orthogonal matrices.
|
||||
* @param r0 The start point of the solver.
|
||||
*/
|
||||
SQPSolution runSQP(const cv::Matx<double, 9, 1>& r0);
|
||||
|
||||
/*
|
||||
* @brief Steps down the gradient for the given matrix r to solve the SQP system.
|
||||
* @param r The current matrix step.
|
||||
* @param delta The next step down the gradient.
|
||||
*/
|
||||
void solveSQPSystem(const cv::Matx<double, 9, 1>& r, cv::Matx<double, 9, 1>& delta);
|
||||
|
||||
/*
|
||||
* @brief Analytically computes the inverse of a symmetric 3x3 matrix using the
|
||||
* lower triangle.
|
||||
* @param Q The matrix to invert.
|
||||
* @param Qinv The inverse of Q.
|
||||
* @param threshold The threshold to determine if Q is singular and non-invertible.
|
||||
*/
|
||||
bool analyticalInverse3x3Symm(const cv::Matx<double, 3, 3>& Q,
|
||||
cv::Matx<double, 3, 3>& Qinv,
|
||||
const double& threshold = 1e-8);
|
||||
|
||||
/*
|
||||
* @brief Computes the 3D null space and 6D normal space of the constraint Jacobian
|
||||
* at a 9D vector r (representing a rank-3 matrix). Note that K is lower
|
||||
* triangular so upper triangle is undefined.
|
||||
* @param r 9D vector representing a rank-3 matrix.
|
||||
* @param H 6D row space of the constraint Jacobian at r.
|
||||
* @param N 3D null space of the constraint Jacobian at r.
|
||||
* @param K The constraint Jacobian at r.
|
||||
* @param norm_threshold Threshold for column vector norm of Pn (the projection onto the null space
|
||||
* of the constraint Jacobian).
|
||||
*/
|
||||
void computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
|
||||
cv::Matx<double, 9, 6>& H,
|
||||
cv::Matx<double, 9, 3>& N,
|
||||
cv::Matx<double, 6, 6>& K,
|
||||
const double& norm_threshold = 0.1);
|
||||
|
||||
static const double RANK_TOLERANCE;
|
||||
static const double SQP_SQUARED_TOLERANCE;
|
||||
static const double SQP_DET_THRESHOLD;
|
||||
static const double ORTHOGONALITY_SQUARED_ERROR_THRESHOLD;
|
||||
static const double EQUAL_VECTORS_SQUARED_DIFF;
|
||||
static const double EQUAL_SQUARED_ERRORS_DIFF;
|
||||
static const double POINT_VARIANCE_THRESHOLD;
|
||||
static const int SQP_MAX_ITERATION;
|
||||
static const double SQRT3;
|
||||
|
||||
cv::Matx<double, 9, 9> omega_;
|
||||
cv::Vec<double, 9> s_;
|
||||
cv::Matx<double, 9, 9> u_;
|
||||
cv::Matx<double, 3, 9> p_;
|
||||
cv::Vec3d point_mean_;
|
||||
int num_null_vectors_;
|
||||
|
||||
SQPSolution solutions_[18];
|
||||
int num_solutions_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -487,5 +487,59 @@ TEST(Calib3d_CirclesPatternDetectorWithClustering, accuracy)
|
||||
ASSERT_LE(error, precise_success_error_level);
|
||||
}
|
||||
|
||||
TEST(Calib3d_AsymmetricCirclesPatternDetector, regression_18713)
|
||||
{
|
||||
float pts_[][2] = {
|
||||
{ 166.5, 107 }, { 146, 236 }, { 147, 92 }, { 184, 162 }, { 150, 185.5 },
|
||||
{ 215, 105 }, { 270.5, 186 }, { 159, 142 }, { 6, 205.5 }, { 32, 148.5 },
|
||||
{ 126, 163.5 }, { 181, 208.5 }, { 240.5, 62 }, { 84.5, 76.5 }, { 190, 120.5 },
|
||||
{ 10, 189 }, { 266, 104 }, { 307.5, 207.5 }, { 97, 184 }, { 116.5, 210 },
|
||||
{ 114, 139 }, { 84.5, 233 }, { 269.5, 139 }, { 136, 126.5 }, { 120, 107.5 },
|
||||
{ 129.5, 65.5 }, { 212.5, 140.5 }, { 204.5, 60.5 }, { 207.5, 241 }, { 61.5, 94.5 },
|
||||
{ 186.5, 61.5 }, { 220, 63 }, { 239, 120.5 }, { 212, 186 }, { 284, 87.5 },
|
||||
{ 62, 114.5 }, { 283, 61.5 }, { 238.5, 88.5 }, { 243, 159 }, { 245, 208 },
|
||||
{ 298.5, 158.5 }, { 57, 129 }, { 156.5, 63.5 }, { 192, 90.5 }, { 281, 235.5 },
|
||||
{ 172, 62.5 }, { 291.5, 119.5 }, { 90, 127 }, { 68.5, 166.5 }, { 108.5, 83.5 },
|
||||
{ 22, 176 }
|
||||
};
|
||||
Mat candidates(51, 1, CV_32FC2, (void*)pts_);
|
||||
Size patternSize(4, 9);
|
||||
|
||||
std::vector< Point2f > result;
|
||||
bool res = false;
|
||||
|
||||
// issue reports about hangs
|
||||
EXPECT_NO_THROW(res = findCirclesGrid(candidates, patternSize, result, CALIB_CB_ASYMMETRIC_GRID, Ptr<FeatureDetector>()/*blobDetector=NULL*/));
|
||||
EXPECT_FALSE(res);
|
||||
|
||||
if (cvtest::debugLevel > 0)
|
||||
{
|
||||
std::cout << Mat(candidates) << std::endl;
|
||||
std::cout << Mat(result) << std::endl;
|
||||
Mat img(Size(400, 300), CV_8UC3, Scalar::all(0));
|
||||
|
||||
std::vector< Point2f > centers;
|
||||
candidates.copyTo(centers);
|
||||
|
||||
for (size_t i = 0; i < centers.size(); i++)
|
||||
{
|
||||
const Point2f& pt = centers[i];
|
||||
//printf("{ %g, %g }, \n", pt.x, pt.y);
|
||||
circle(img, pt, 5, Scalar(0, 255, 0));
|
||||
}
|
||||
for (size_t i = 0; i < result.size(); i++)
|
||||
{
|
||||
const Point2f& pt = result[i];
|
||||
circle(img, pt, 10, Scalar(0, 0, 255));
|
||||
}
|
||||
imwrite("test_18713.png", img);
|
||||
if (cvtest::debugLevel >= 10)
|
||||
{
|
||||
imshow("result", img);
|
||||
waitKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
/* End of file. */
|
||||
|
||||
@@ -153,9 +153,8 @@ void CV_ChessboardSubpixelTest::run( int )
|
||||
|
||||
vector<Point2f> test_corners;
|
||||
bool result = findChessboardCorners(chessboard_image, pattern_size, test_corners, 15);
|
||||
if(!result)
|
||||
if (!result && cvtest::debugLevel > 0)
|
||||
{
|
||||
#if 0
|
||||
ts->printf(cvtest::TS::LOG, "Warning: chessboard was not detected! Writing image to test.png\n");
|
||||
ts->printf(cvtest::TS::LOG, "Size = %d, %d\n", pattern_size.width, pattern_size.height);
|
||||
ts->printf(cvtest::TS::LOG, "Intrinsic params: fx = %f, fy = %f, cx = %f, cy = %f\n",
|
||||
@@ -167,7 +166,9 @@ void CV_ChessboardSubpixelTest::run( int )
|
||||
distortion_coeffs_.at<double>(0, 4));
|
||||
|
||||
imwrite("test.png", chessboard_image);
|
||||
#endif
|
||||
}
|
||||
if (!result)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -390,6 +390,12 @@ TEST_F(fisheyeTest, EstimateUncertainties)
|
||||
|
||||
TEST_F(fisheyeTest, stereoRectify)
|
||||
{
|
||||
// For consistency purposes
|
||||
CV_StaticAssert(
|
||||
static_cast<int>(cv::CALIB_ZERO_DISPARITY) == static_cast<int>(cv::fisheye::CALIB_ZERO_DISPARITY),
|
||||
"For the purpose of continuity the following should be true: cv::CALIB_ZERO_DISPARITY == cv::fisheye::CALIB_ZERO_DISPARITY"
|
||||
);
|
||||
|
||||
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
|
||||
cv::Size calibration_size = this->imageSize, requested_size = calibration_size;
|
||||
@@ -402,7 +408,7 @@ TEST_F(fisheyeTest, stereoRectify)
|
||||
double balance = 0.0, fov_scale = 1.1;
|
||||
cv::Mat R1, R2, P1, P2, Q;
|
||||
cv::fisheye::stereoRectify(K1, D1, K2, D2, calibration_size, theR, theT, R1, R2, P1, P2, Q,
|
||||
cv::CALIB_ZERO_DISPARITY, requested_size, balance, fov_scale);
|
||||
cv::fisheye::CALIB_ZERO_DISPARITY, requested_size, balance, fov_scale);
|
||||
|
||||
// Collected with these CMake flags: -DWITH_IPP=OFF -DCV_ENABLE_INTRINSICS=OFF -DCV_DISABLE_OPTIMIZATION=ON -DCMAKE_BUILD_TYPE=Debug
|
||||
cv::Matx33d R1_ref(
|
||||
@@ -449,7 +455,10 @@ TEST_F(fisheyeTest, stereoRectify)
|
||||
<< "Q =" << std::endl << Q << std::endl;
|
||||
}
|
||||
|
||||
#if 1 // Debug code
|
||||
if (cvtest::debugLevel == 0)
|
||||
return;
|
||||
// DEBUG code is below
|
||||
|
||||
cv::Mat lmapx, lmapy, rmapx, rmapy;
|
||||
//rewrite for fisheye
|
||||
cv::fisheye::initUndistortRectifyMap(K1, D1, R1, P1, requested_size, CV_32F, lmapx, lmapy);
|
||||
@@ -482,7 +491,6 @@ TEST_F(fisheyeTest, stereoRectify)
|
||||
|
||||
cv::imwrite(cv::format("fisheye_rectification_AB_%03d.png", i), rectification);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(fisheyeTest, stereoCalibrate)
|
||||
|
||||
@@ -190,6 +190,8 @@ static std::string printMethod(int method)
|
||||
return "SOLVEPNP_IPPE";
|
||||
case 7:
|
||||
return "SOLVEPNP_IPPE_SQUARE";
|
||||
case 8:
|
||||
return "SOLVEPNP_SQPNP";
|
||||
default:
|
||||
return "Unknown value";
|
||||
}
|
||||
@@ -206,6 +208,7 @@ public:
|
||||
eps[SOLVEPNP_AP3P] = 1.0e-2;
|
||||
eps[SOLVEPNP_DLS] = 1.0e-2;
|
||||
eps[SOLVEPNP_UPNP] = 1.0e-2;
|
||||
eps[SOLVEPNP_SQPNP] = 1.0e-2;
|
||||
totalTestsCount = 10;
|
||||
pointsCount = 500;
|
||||
}
|
||||
@@ -436,6 +439,7 @@ public:
|
||||
eps[SOLVEPNP_UPNP] = 1.0e-6; //UPnP is remapped to EPnP, so we use the same threshold
|
||||
eps[SOLVEPNP_IPPE] = 1.0e-6;
|
||||
eps[SOLVEPNP_IPPE_SQUARE] = 1.0e-6;
|
||||
eps[SOLVEPNP_SQPNP] = 1.0e-6;
|
||||
|
||||
totalTestsCount = 1000;
|
||||
|
||||
|
||||
@@ -82,16 +82,24 @@ option(OPENCV_ENABLE_ALLOCATOR_STATS "Enable Allocator metrics" ON)
|
||||
|
||||
if(NOT OPENCV_ENABLE_ALLOCATOR_STATS)
|
||||
add_definitions(-DOPENCV_DISABLE_ALLOCATOR_STATS=1)
|
||||
else()
|
||||
elseif(HAVE_CXX11 OR DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE)
|
||||
if(NOT DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE)
|
||||
if(HAVE_ATOMIC_LONG_LONG AND OPENCV_ENABLE_ATOMIC_LONG_LONG)
|
||||
set(OPENCV_ALLOCATOR_STATS_COUNTER_TYPE "long long")
|
||||
if(MINGW)
|
||||
# command-line generation issue due to space in value, int/int64_t should be used instead
|
||||
# https://github.com/opencv/opencv/issues/16990
|
||||
message(STATUS "Consider adding OPENCV_ALLOCATOR_STATS_COUNTER_TYPE=int/int64_t according to your build configuration")
|
||||
else()
|
||||
set(OPENCV_ALLOCATOR_STATS_COUNTER_TYPE "long long")
|
||||
endif()
|
||||
else()
|
||||
set(OPENCV_ALLOCATOR_STATS_COUNTER_TYPE "int")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "Allocator metrics storage type: '${OPENCV_ALLOCATOR_STATS_COUNTER_TYPE}'")
|
||||
add_definitions("-DOPENCV_ALLOCATOR_STATS_COUNTER_TYPE=${OPENCV_ALLOCATOR_STATS_COUNTER_TYPE}")
|
||||
if(DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE)
|
||||
message(STATUS "Allocator metrics storage type: '${OPENCV_ALLOCATOR_STATS_COUNTER_TYPE}'")
|
||||
add_definitions("-DOPENCV_ALLOCATOR_STATS_COUNTER_TYPE=${OPENCV_ALLOCATOR_STATS_COUNTER_TYPE}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -202,6 +202,9 @@ enum CovarFlags {
|
||||
COVAR_COLS = 16
|
||||
};
|
||||
|
||||
//! @addtogroup core_cluster
|
||||
//! @{
|
||||
|
||||
//! k-Means flags
|
||||
enum KmeansFlags {
|
||||
/** Select random initial centers in each attempt.*/
|
||||
@@ -215,6 +218,8 @@ enum KmeansFlags {
|
||||
KMEANS_USE_INITIAL_LABELS = 1
|
||||
};
|
||||
|
||||
//! @} core_cluster
|
||||
|
||||
//! type of line
|
||||
enum LineTypes {
|
||||
FILLED = -1,
|
||||
@@ -236,12 +241,16 @@ enum HersheyFonts {
|
||||
FONT_ITALIC = 16 //!< flag for italic font
|
||||
};
|
||||
|
||||
//! @addtogroup core_array
|
||||
//! @{
|
||||
|
||||
enum ReduceTypes { REDUCE_SUM = 0, //!< the output is the sum of all rows/columns of the matrix.
|
||||
REDUCE_AVG = 1, //!< the output is the mean vector of all rows/columns of the matrix.
|
||||
REDUCE_MAX = 2, //!< the output is the maximum (column/row-wise) of all rows/columns of the matrix.
|
||||
REDUCE_MIN = 3 //!< the output is the minimum (column/row-wise) of all rows/columns of the matrix.
|
||||
};
|
||||
|
||||
//! @} core_array
|
||||
|
||||
/** @brief Swaps two matrices
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,12 @@ String dumpCString(const char* argument)
|
||||
return cv::format("String: %s", argument);
|
||||
}
|
||||
|
||||
CV_WRAP static inline
|
||||
String dumpString(const String& argument)
|
||||
{
|
||||
return cv::format("String: %s", argument.c_str());
|
||||
}
|
||||
|
||||
CV_WRAP static inline
|
||||
AsyncArray testAsyncArray(InputArray argument)
|
||||
{
|
||||
|
||||
@@ -2554,6 +2554,14 @@ inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \
|
||||
return _Tpvec(intrin(a.val, b.val)); \
|
||||
}
|
||||
|
||||
#define OPENCV_HAL_IMPL_WASM_BIN_FUNC_FALLBACK(_Tpvec, func, intrin) \
|
||||
inline _Tpvec func(const _Tpvec& a, const _Tpvec& b) \
|
||||
{ \
|
||||
fallback::_Tpvec a_(a); \
|
||||
fallback::_Tpvec b_(b); \
|
||||
return _Tpvec(fallback::func(a_, b_)); \
|
||||
}
|
||||
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float32x4, v_min, wasm_f32x4_min)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float32x4, v_max, wasm_f32x4_max)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_float64x2, v_min, wasm_f64x2_min)
|
||||
@@ -2644,8 +2652,14 @@ OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_sub_wrap, wasm_i8x16_sub)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_sub_wrap, wasm_i8x16_sub)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_sub_wrap, wasm_i16x8_sub)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_sub_wrap, wasm_i16x8_sub)
|
||||
#if (__EMSCRIPTEN_major__ * 1000000 + __EMSCRIPTEN_minor__ * 1000 + __EMSCRIPTEN_tiny__) >= (2000000)
|
||||
// details: https://github.com/opencv/opencv/issues/18097 ( https://github.com/emscripten-core/emscripten/issues/12018 )
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC_FALLBACK(v_uint8x16, v_mul_wrap, wasm_i8x16_mul)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC_FALLBACK(v_int8x16, v_mul_wrap, wasm_i8x16_mul)
|
||||
#else
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint8x16, v_mul_wrap, wasm_i8x16_mul)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int8x16, v_mul_wrap, wasm_i8x16_mul)
|
||||
#endif
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_uint16x8, v_mul_wrap, wasm_i16x8_mul)
|
||||
OPENCV_HAL_IMPL_WASM_BIN_FUNC(v_int16x8, v_mul_wrap, wasm_i16x8_mul)
|
||||
|
||||
|
||||
@@ -702,11 +702,16 @@ sub-matrices.
|
||||
-# Process "foreign" data using OpenCV (for example, when you implement a DirectShow\* filter or
|
||||
a processing module for gstreamer, and so on). For example:
|
||||
@code
|
||||
void process_video_frame(const unsigned char* pixels,
|
||||
int width, int height, int step)
|
||||
Mat process_video_frame(const unsigned char* pixels,
|
||||
int width, int height, int step)
|
||||
{
|
||||
Mat img(height, width, CV_8UC3, pixels, step);
|
||||
GaussianBlur(img, img, Size(7,7), 1.5, 1.5);
|
||||
// wrap input buffer
|
||||
Mat img(height, width, CV_8UC3, (unsigned char*)pixels, step);
|
||||
|
||||
Mat result;
|
||||
GaussianBlur(img, result, Size(7, 7), 1.5, 1.5);
|
||||
|
||||
return result;
|
||||
}
|
||||
@endcode
|
||||
-# Quickly initialize small matrices and/or get a super-fast element access.
|
||||
@@ -2432,20 +2437,11 @@ public:
|
||||
UMat(const UMat& m, const Rect& roi);
|
||||
UMat(const UMat& m, const Range* ranges);
|
||||
UMat(const UMat& m, const std::vector<Range>& ranges);
|
||||
|
||||
// FIXIT copyData=false is not implemented, drop this in favor of cv::Mat (OpenCV 5.0)
|
||||
//! builds matrix from std::vector with or without copying the data
|
||||
template<typename _Tp> explicit UMat(const std::vector<_Tp>& vec, bool copyData=false);
|
||||
|
||||
//! builds matrix from cv::Vec; the data is copied by default
|
||||
template<typename _Tp, int n> explicit UMat(const Vec<_Tp, n>& vec, bool copyData=true);
|
||||
//! builds matrix from cv::Matx; the data is copied by default
|
||||
template<typename _Tp, int m, int n> explicit UMat(const Matx<_Tp, m, n>& mtx, bool copyData=true);
|
||||
//! builds matrix from a 2D point
|
||||
template<typename _Tp> explicit UMat(const Point_<_Tp>& pt, bool copyData=true);
|
||||
//! builds matrix from a 3D point
|
||||
template<typename _Tp> explicit UMat(const Point3_<_Tp>& pt, bool copyData=true);
|
||||
//! builds matrix from comma initializer
|
||||
template<typename _Tp> explicit UMat(const MatCommaInitializer_<_Tp>& commaInitializer);
|
||||
|
||||
//! destructor - calls release()
|
||||
~UMat();
|
||||
//! assignment operators
|
||||
|
||||
@@ -720,7 +720,12 @@ public:
|
||||
|
||||
String name() const;
|
||||
String vendor() const;
|
||||
|
||||
/// See CL_PLATFORM_VERSION
|
||||
String version() const;
|
||||
int versionMajor() const;
|
||||
int versionMinor() const;
|
||||
|
||||
int deviceNumber() const;
|
||||
void getDevice(Device& device, int d) const;
|
||||
|
||||
|
||||
@@ -7,13 +7,11 @@
|
||||
|
||||
#include "./allocator_stats.hpp"
|
||||
|
||||
#ifdef CV_CXX11
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
//#define OPENCV_DISABLE_ALLOCATOR_STATS
|
||||
|
||||
namespace cv { namespace utils {
|
||||
#ifdef CV_CXX11
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE
|
||||
#if defined(__GNUC__) && (\
|
||||
@@ -28,6 +26,16 @@ namespace cv { namespace utils {
|
||||
#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE long long
|
||||
#endif
|
||||
|
||||
#else // CV_CXX11
|
||||
|
||||
#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE
|
||||
#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE int // CV_XADD supports int only
|
||||
#endif
|
||||
|
||||
#endif // CV_CXX11
|
||||
|
||||
namespace cv { namespace utils {
|
||||
|
||||
#ifdef CV__ALLOCATOR_STATS_LOG
|
||||
namespace {
|
||||
#endif
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#define CV_VERSION_MAJOR 3
|
||||
#define CV_VERSION_MINOR 4
|
||||
#define CV_VERSION_REVISION 13
|
||||
#define CV_VERSION_STATUS "-pre"
|
||||
#define CV_VERSION_STATUS ""
|
||||
|
||||
#define CVAUX_STR_EXP(__A) #__A
|
||||
#define CVAUX_STR(__A) CVAUX_STR_EXP(__A)
|
||||
|
||||
@@ -503,7 +503,7 @@ VSX_IMPL_CONV_EVEN_2_4(vec_uint4, vec_double2, vec_ctu, vec_ctuo)
|
||||
VSX_IMPL_CONV_2VARIANT(vec_int4, vec_float4, vec_cts, vec_cts)
|
||||
VSX_IMPL_CONV_2VARIANT(vec_float4, vec_int4, vec_ctf, vec_ctf)
|
||||
// define vec_cts for converting double precision to signed doubleword
|
||||
// which isn't combitable with xlc but its okay since Eigen only use it for gcc
|
||||
// which isn't compatible with xlc but its okay since Eigen only uses it for gcc
|
||||
VSX_IMPL_CONV_2VARIANT(vec_dword2, vec_double2, vec_cts, vec_ctsl)
|
||||
#endif // Eigen
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ static bool ocl_convertFp16( InputArray _src, OutputArray _dst, int sdepth, int
|
||||
sdepth == CV_32F ? "half" : "float",
|
||||
rowsPerWI,
|
||||
sdepth == CV_32F ? " -D FLOAT_TO_HALF " : "");
|
||||
ocl::Kernel k("convertFp16", ocl::core::halfconvert_oclsrc, build_opt);
|
||||
ocl::Kernel k(sdepth == CV_32F ? "convertFp16_FP32_to_FP16" : "convertFp16_FP16_to_FP32", ocl::core::halfconvert_oclsrc, build_opt);
|
||||
if (k.empty())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1032,8 +1032,7 @@ void flip( InputArray _src, OutputArray _dst, int flip_mode )
|
||||
}
|
||||
|
||||
if ((size.width == 1 && flip_mode > 0) ||
|
||||
(size.height == 1 && flip_mode == 0) ||
|
||||
(size.height == 1 && size.width == 1 && flip_mode < 0))
|
||||
(size.height == 1 && flip_mode == 0))
|
||||
{
|
||||
return _src.copyTo(_dst);
|
||||
}
|
||||
|
||||
@@ -62,11 +62,9 @@ static bool ipp_countNonZero( Mat &src, int &res )
|
||||
{
|
||||
CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
#if defined __APPLE__ || (defined _MSC_VER && defined _M_IX86)
|
||||
// see https://github.com/opencv/opencv/issues/17453
|
||||
if (src.dims <= 2 && src.step > 520000)
|
||||
if (src.dims <= 2 && src.step > 520000 && cv::ipp::getIppTopFeatures() == ippCPUID_SSE42)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if IPP_VERSION_X100 < 201801
|
||||
// Poor performance of SSE42
|
||||
|
||||
@@ -1537,7 +1537,7 @@ transform_8u( const uchar* src, uchar* dst, const float* m, int len, int scn, in
|
||||
static void
|
||||
transform_16u( const ushort* src, ushort* dst, const float* m, int len, int scn, int dcn )
|
||||
{
|
||||
#if CV_SIMD && !defined(__aarch64__) && !defined(_M_ARM64)
|
||||
#if CV_SIMD
|
||||
if( scn == 3 && dcn == 3 )
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
@@ -914,7 +914,7 @@ bool _InputArray::isContinuous(int i) const
|
||||
if( k == STD_ARRAY_MAT )
|
||||
{
|
||||
const Mat* vv = (const Mat*)obj;
|
||||
CV_Assert(i > 0 && i < sz.height);
|
||||
CV_Assert(i >= 0 && i < sz.height);
|
||||
return vv[i].isContinuous();
|
||||
}
|
||||
|
||||
@@ -948,21 +948,21 @@ bool _InputArray::isSubmatrix(int i) const
|
||||
if( k == STD_VECTOR_MAT )
|
||||
{
|
||||
const std::vector<Mat>& vv = *(const std::vector<Mat>*)obj;
|
||||
CV_Assert((size_t)i < vv.size());
|
||||
CV_Assert(i >= 0 && (size_t)i < vv.size());
|
||||
return vv[i].isSubmatrix();
|
||||
}
|
||||
|
||||
if( k == STD_ARRAY_MAT )
|
||||
{
|
||||
const Mat* vv = (const Mat*)obj;
|
||||
CV_Assert(i < sz.height);
|
||||
CV_Assert(i >= 0 && i < sz.height);
|
||||
return vv[i].isSubmatrix();
|
||||
}
|
||||
|
||||
if( k == STD_VECTOR_UMAT )
|
||||
{
|
||||
const std::vector<UMat>& vv = *(const std::vector<UMat>*)obj;
|
||||
CV_Assert((size_t)i < vv.size());
|
||||
CV_Assert(i >= 0 && (size_t)i < vv.size());
|
||||
return vv[i].isSubmatrix();
|
||||
}
|
||||
|
||||
@@ -993,9 +993,7 @@ size_t _InputArray::offset(int i) const
|
||||
if( k == STD_VECTOR_MAT )
|
||||
{
|
||||
const std::vector<Mat>& vv = *(const std::vector<Mat>*)obj;
|
||||
if( i < 0 )
|
||||
return 1;
|
||||
CV_Assert( i < (int)vv.size() );
|
||||
CV_Assert( i >= 0 && i < (int)vv.size() );
|
||||
|
||||
return (size_t)(vv[i].ptr() - vv[i].datastart);
|
||||
}
|
||||
@@ -1003,16 +1001,14 @@ size_t _InputArray::offset(int i) const
|
||||
if( k == STD_ARRAY_MAT )
|
||||
{
|
||||
const Mat* vv = (const Mat*)obj;
|
||||
if( i < 0 )
|
||||
return 1;
|
||||
CV_Assert( i < sz.height );
|
||||
CV_Assert( i >= 0 && i < sz.height );
|
||||
return (size_t)(vv[i].ptr() - vv[i].datastart);
|
||||
}
|
||||
|
||||
if( k == STD_VECTOR_UMAT )
|
||||
{
|
||||
const std::vector<UMat>& vv = *(const std::vector<UMat>*)obj;
|
||||
CV_Assert((size_t)i < vv.size());
|
||||
CV_Assert(i >= 0 && (size_t)i < vv.size());
|
||||
return vv[i].offset;
|
||||
}
|
||||
|
||||
@@ -1026,7 +1022,7 @@ size_t _InputArray::offset(int i) const
|
||||
if (k == STD_VECTOR_CUDA_GPU_MAT)
|
||||
{
|
||||
const std::vector<cuda::GpuMat>& vv = *(const std::vector<cuda::GpuMat>*)obj;
|
||||
CV_Assert((size_t)i < vv.size());
|
||||
CV_Assert(i >= 0 && (size_t)i < vv.size());
|
||||
return (size_t)(vv[i].data - vv[i].datastart);
|
||||
}
|
||||
|
||||
@@ -1056,25 +1052,21 @@ size_t _InputArray::step(int i) const
|
||||
if( k == STD_VECTOR_MAT )
|
||||
{
|
||||
const std::vector<Mat>& vv = *(const std::vector<Mat>*)obj;
|
||||
if( i < 0 )
|
||||
return 1;
|
||||
CV_Assert( i < (int)vv.size() );
|
||||
CV_Assert( i >= 0 && i < (int)vv.size() );
|
||||
return vv[i].step;
|
||||
}
|
||||
|
||||
if( k == STD_ARRAY_MAT )
|
||||
{
|
||||
const Mat* vv = (const Mat*)obj;
|
||||
if( i < 0 )
|
||||
return 1;
|
||||
CV_Assert( i < sz.height );
|
||||
CV_Assert( i >= 0 && i < sz.height );
|
||||
return vv[i].step;
|
||||
}
|
||||
|
||||
if( k == STD_VECTOR_UMAT )
|
||||
{
|
||||
const std::vector<UMat>& vv = *(const std::vector<UMat>*)obj;
|
||||
CV_Assert((size_t)i < vv.size());
|
||||
CV_Assert(i >= 0 && (size_t)i < vv.size());
|
||||
return vv[i].step;
|
||||
}
|
||||
|
||||
@@ -1086,7 +1078,7 @@ size_t _InputArray::step(int i) const
|
||||
if (k == STD_VECTOR_CUDA_GPU_MAT)
|
||||
{
|
||||
const std::vector<cuda::GpuMat>& vv = *(const std::vector<cuda::GpuMat>*)obj;
|
||||
CV_Assert((size_t)i < vv.size());
|
||||
CV_Assert(i >= 0 && (size_t)i < vv.size());
|
||||
return vv[i].step;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,10 +152,10 @@ float normL2Sqr_(const float* a, const float* b, int n)
|
||||
{
|
||||
v_float32 t0 = vx_load(a + j) - vx_load(b + j);
|
||||
v_float32 t1 = vx_load(a + j + v_float32::nlanes) - vx_load(b + j + v_float32::nlanes);
|
||||
v_float32 t2 = vx_load(a + j + 2 * v_float32::nlanes) - vx_load(b + j + 2 * v_float32::nlanes);
|
||||
v_float32 t3 = vx_load(a + j + 3 * v_float32::nlanes) - vx_load(b + j + 3 * v_float32::nlanes);
|
||||
v_d0 = v_muladd(t0, t0, v_d0);
|
||||
v_float32 t2 = vx_load(a + j + 2 * v_float32::nlanes) - vx_load(b + j + 2 * v_float32::nlanes);
|
||||
v_d1 = v_muladd(t1, t1, v_d1);
|
||||
v_float32 t3 = vx_load(a + j + 3 * v_float32::nlanes) - vx_load(b + j + 3 * v_float32::nlanes);
|
||||
v_d2 = v_muladd(t2, t2, v_d2);
|
||||
v_d3 = v_muladd(t3, t3, v_d3);
|
||||
}
|
||||
|
||||
+40
-10
@@ -1162,25 +1162,27 @@ Platform& Platform::getDefault()
|
||||
|
||||
/////////////////////////////////////// Device ////////////////////////////////////////////
|
||||
|
||||
// deviceVersion has format
|
||||
// Version has format:
|
||||
// OpenCL<space><major_version.minor_version><space><vendor-specific information>
|
||||
// by specification
|
||||
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clGetDeviceInfo.html
|
||||
// http://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clGetDeviceInfo.html
|
||||
static void parseDeviceVersion(const String &deviceVersion, int &major, int &minor)
|
||||
// https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/clGetPlatformInfo.html
|
||||
// https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/clGetPlatformInfo.html
|
||||
static void parseOpenCLVersion(const String &version, int &major, int &minor)
|
||||
{
|
||||
major = minor = 0;
|
||||
if (10 >= deviceVersion.length())
|
||||
if (10 >= version.length())
|
||||
return;
|
||||
const char *pstr = deviceVersion.c_str();
|
||||
const char *pstr = version.c_str();
|
||||
if (0 != strncmp(pstr, "OpenCL ", 7))
|
||||
return;
|
||||
size_t ppos = deviceVersion.find('.', 7);
|
||||
size_t ppos = version.find('.', 7);
|
||||
if (String::npos == ppos)
|
||||
return;
|
||||
String temp = deviceVersion.substr(7, ppos - 7);
|
||||
String temp = version.substr(7, ppos - 7);
|
||||
major = atoi(temp.c_str());
|
||||
temp = deviceVersion.substr(ppos + 1);
|
||||
temp = version.substr(ppos + 1);
|
||||
minor = atoi(temp.c_str());
|
||||
}
|
||||
|
||||
@@ -1203,7 +1205,7 @@ struct Device::Impl
|
||||
addressBits_ = getProp<cl_uint, int>(CL_DEVICE_ADDRESS_BITS);
|
||||
|
||||
String deviceVersion_ = getStrProp(CL_DEVICE_VERSION);
|
||||
parseDeviceVersion(deviceVersion_, deviceVersionMajor_, deviceVersionMinor_);
|
||||
parseOpenCLVersion(deviceVersion_, deviceVersionMajor_, deviceVersionMinor_);
|
||||
|
||||
size_t pos = 0;
|
||||
while (pos < extensions_.size())
|
||||
@@ -2949,6 +2951,15 @@ bool Kernel::empty() const
|
||||
return ptr() == 0;
|
||||
}
|
||||
|
||||
static cv::String dumpValue(size_t sz, const void* p)
|
||||
{
|
||||
if (sz == 4)
|
||||
return cv::format("%d / %uu / 0x%08x / %g", *(int*)p, *(int*)p, *(int*)p, *(float*)p);
|
||||
if (sz == 8)
|
||||
return cv::format("%lld / %lluu / 0x%16llx / %g", *(long long*)p, *(long long*)p, *(long long*)p, *(double*)p);
|
||||
return cv::format("%p", p);
|
||||
}
|
||||
|
||||
int Kernel::set(int i, const void* value, size_t sz)
|
||||
{
|
||||
if (!p || !p->handle)
|
||||
@@ -2959,7 +2970,7 @@ int Kernel::set(int i, const void* value, size_t sz)
|
||||
p->cleanupUMats();
|
||||
|
||||
cl_int retval = clSetKernelArg(p->handle, (cl_uint)i, sz, value);
|
||||
CV_OCL_DBG_CHECK_RESULT(retval, cv::format("clSetKernelArg('%s', arg_index=%d, size=%d, value=%p)", p->name.c_str(), (int)i, (int)sz, (void*)value).c_str());
|
||||
CV_OCL_DBG_CHECK_RESULT(retval, cv::format("clSetKernelArg('%s', arg_index=%d, size=%d, value=%s)", p->name.c_str(), (int)i, (int)sz, dumpValue(sz, value).c_str()).c_str());
|
||||
if (retval != CL_SUCCESS)
|
||||
return -1;
|
||||
return i+1;
|
||||
@@ -5958,6 +5969,9 @@ struct PlatformInfo::Impl
|
||||
refcount = 1;
|
||||
handle = *(cl_platform_id*)id;
|
||||
getDevices(devices, handle);
|
||||
|
||||
version_ = getStrProp(CL_PLATFORM_VERSION);
|
||||
parseOpenCLVersion(version_, versionMajor_, versionMinor_);
|
||||
}
|
||||
|
||||
String getStrProp(cl_platform_info prop) const
|
||||
@@ -5971,6 +5985,10 @@ struct PlatformInfo::Impl
|
||||
IMPLEMENT_REFCOUNTABLE();
|
||||
std::vector<cl_device_id> devices;
|
||||
cl_platform_id handle;
|
||||
|
||||
String version_;
|
||||
int versionMajor_;
|
||||
int versionMinor_;
|
||||
};
|
||||
|
||||
PlatformInfo::PlatformInfo()
|
||||
@@ -6033,7 +6051,19 @@ String PlatformInfo::vendor() const
|
||||
|
||||
String PlatformInfo::version() const
|
||||
{
|
||||
return p ? p->getStrProp(CL_PLATFORM_VERSION) : String();
|
||||
return p ? p->version_ : String();
|
||||
}
|
||||
|
||||
int PlatformInfo::versionMajor() const
|
||||
{
|
||||
CV_Assert(p);
|
||||
return p->versionMajor_;
|
||||
}
|
||||
|
||||
int PlatformInfo::versionMinor() const
|
||||
{
|
||||
CV_Assert(p);
|
||||
return p->versionMinor_;
|
||||
}
|
||||
|
||||
static void getPlatforms(std::vector<cl_platform_id>& platforms)
|
||||
|
||||
@@ -47,8 +47,17 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
__kernel void convertFp16(__global const uchar * srcptr, int src_step, int src_offset,
|
||||
__global uchar * dstptr, int dst_step, int dst_offset, int dst_rows, int dst_cols)
|
||||
__kernel void
|
||||
#ifdef FLOAT_TO_HALF
|
||||
convertFp16_FP32_to_FP16
|
||||
#else
|
||||
convertFp16_FP16_to_FP32
|
||||
#endif
|
||||
(
|
||||
__global const uchar * srcptr, int src_step, int src_offset,
|
||||
__global uchar * dstptr, int dst_step, int dst_offset,
|
||||
int dst_rows, int dst_cols
|
||||
)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y0 = get_global_id(1) * rowsPerWI;
|
||||
|
||||
@@ -1575,6 +1575,7 @@ void cv::ogl::render(const ogl::Arrays& arr, InputArray indices, int mode, Scala
|
||||
// CL-GL Interoperability
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
# include "opencv2/core/opencl/runtime/opencl_core.hpp"
|
||||
# include "opencv2/core/opencl/runtime/opencl_gl.hpp"
|
||||
# ifdef cl_khr_gl_sharing
|
||||
# define HAVE_OPENCL_OPENGL_SHARING
|
||||
@@ -1595,6 +1596,34 @@ void cv::ogl::render(const ogl::Arrays& arr, InputArray indices, int mode, Scala
|
||||
|
||||
namespace cv { namespace ogl {
|
||||
|
||||
#if defined(HAVE_OPENCL) && defined(HAVE_OPENGL) && defined(HAVE_OPENCL_OPENGL_SHARING)
|
||||
// Check to avoid crash in OpenCL runtime: https://github.com/opencv/opencv/issues/5209
|
||||
static void checkOpenCLVersion()
|
||||
{
|
||||
using namespace cv::ocl;
|
||||
const Device& device = Device::getDefault();
|
||||
//CV_Assert(!device.empty());
|
||||
cl_device_id dev = (cl_device_id)device.ptr();
|
||||
CV_Assert(dev);
|
||||
|
||||
cl_platform_id platform_id = 0;
|
||||
size_t sz = 0;
|
||||
|
||||
cl_int status = clGetDeviceInfo(dev, CL_DEVICE_PLATFORM, sizeof(platform_id), &platform_id, &sz);
|
||||
CV_Assert(status == CL_SUCCESS && sz == sizeof(cl_platform_id));
|
||||
CV_Assert(platform_id);
|
||||
|
||||
PlatformInfo pi(&platform_id);
|
||||
int versionMajor = pi.versionMajor();
|
||||
int versionMinor = pi.versionMinor();
|
||||
if (versionMajor < 1 || (versionMajor == 1 && versionMinor <= 1))
|
||||
CV_Error_(cv::Error::OpenCLApiCallError,
|
||||
("OpenCL: clCreateFromGLTexture requires OpenCL 1.2+ version: %d.%d - %s (%s)",
|
||||
versionMajor, versionMinor, pi.name().c_str(), pi.version().c_str())
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace ocl {
|
||||
|
||||
Context& initializeContextFromGL()
|
||||
@@ -1714,6 +1743,8 @@ void convertToGLTexture2D(InputArray src, Texture2D& texture)
|
||||
Context& ctx = Context::getDefault();
|
||||
cl_context context = (cl_context)ctx.ptr();
|
||||
|
||||
checkOpenCLVersion(); // clCreateFromGLTexture requires OpenCL 1.2
|
||||
|
||||
UMat u = src.getUMat();
|
||||
|
||||
// TODO Add support for roi
|
||||
@@ -1772,6 +1803,8 @@ void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst)
|
||||
Context& ctx = Context::getDefault();
|
||||
cl_context context = (cl_context)ctx.ptr();
|
||||
|
||||
checkOpenCLVersion(); // clCreateFromGLTexture requires OpenCL 1.2
|
||||
|
||||
// TODO Need to specify ACCESS_WRITE here somehow to prevent useless data copying!
|
||||
dst.create(texture.size(), textureType);
|
||||
UMat u = dst.getUMat();
|
||||
|
||||
@@ -2429,6 +2429,13 @@ public:
|
||||
ippTopFeatures = ippCPUID_SSE42;
|
||||
|
||||
pIppLibInfo = ippiGetLibVersion();
|
||||
|
||||
// workaround: https://github.com/opencv/opencv/issues/12959
|
||||
std::string ippName(pIppLibInfo->Name ? pIppLibInfo->Name : "");
|
||||
if (ippName.find("SSE4.2") != std::string::npos)
|
||||
{
|
||||
ippTopFeatures = ippCPUID_SSE42;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -2468,16 +2475,12 @@ int getIppFeatures()
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned long long getIppTopFeatures();
|
||||
|
||||
#ifdef HAVE_IPP
|
||||
unsigned long long getIppTopFeatures()
|
||||
{
|
||||
#ifdef HAVE_IPP
|
||||
return getIPPSingleton().ippTopFeatures;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void setIppStatus(int status, const char * const _funcname, const char * const _filename, int _line)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "opencv2/core/eigen.hpp"
|
||||
#endif
|
||||
|
||||
#include "opencv2/core/cuda.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
class Core_ReduceTest : public cvtest::BaseTest
|
||||
@@ -1984,6 +1986,157 @@ TEST(Core_InputArray, fetch_MatExpr)
|
||||
}
|
||||
|
||||
|
||||
#ifdef CV_CXX11
|
||||
class TestInputArrayRangeChecking {
|
||||
static const char *kind2str(cv::_InputArray ia)
|
||||
{
|
||||
switch (ia.kind())
|
||||
{
|
||||
#define C(x) case cv::_InputArray::x: return #x
|
||||
C(MAT);
|
||||
C(UMAT);
|
||||
C(EXPR);
|
||||
C(MATX);
|
||||
C(STD_VECTOR);
|
||||
C(STD_ARRAY);
|
||||
C(NONE);
|
||||
C(STD_VECTOR_VECTOR);
|
||||
C(STD_BOOL_VECTOR);
|
||||
C(STD_VECTOR_MAT);
|
||||
C(STD_ARRAY_MAT);
|
||||
C(STD_VECTOR_UMAT);
|
||||
C(CUDA_GPU_MAT);
|
||||
C(STD_VECTOR_CUDA_GPU_MAT);
|
||||
#undef C
|
||||
default:
|
||||
return "<unsupported>";
|
||||
}
|
||||
}
|
||||
|
||||
static void banner(cv::_InputArray ia, const char *label, const char *name)
|
||||
{
|
||||
std::cout << std::endl
|
||||
<< label << " = " << name << ", Kind: " << kind2str(ia)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
template<typename I, typename F>
|
||||
static void testA(I ia, F f, const char *mfname)
|
||||
{
|
||||
banner(ia, "f", mfname);
|
||||
EXPECT_THROW(f(ia, -1), cv::Exception)
|
||||
<< "f(ia, " << -1 << ") should throw cv::Exception";
|
||||
for (int i = 0; i < int(ia.size()); i++)
|
||||
{
|
||||
EXPECT_NO_THROW(f(ia, i))
|
||||
<< "f(ia, " << i << ") should not throw an exception";
|
||||
}
|
||||
EXPECT_THROW(f(ia, int(ia.size())), cv::Exception)
|
||||
<< "f(ia, " << ia.size() << ") should throw cv::Exception";
|
||||
}
|
||||
|
||||
template<typename I, typename F>
|
||||
static void testB(I ia, F f, const char *mfname)
|
||||
{
|
||||
banner(ia, "f", mfname);
|
||||
EXPECT_THROW(f(ia, -1), cv::Exception)
|
||||
<< "f(ia, " << -1 << ") should throw cv::Exception";
|
||||
for (int i = 0; i < int(ia.size()); i++)
|
||||
{
|
||||
EXPECT_NO_THROW(f(ia, i))
|
||||
<< "f(ia, " << i << ") should not throw an exception";
|
||||
}
|
||||
EXPECT_THROW(f(ia, int(ia.size())), cv::Exception)
|
||||
<< "f(ia, " << ia.size() << ") should throw cv::Exception";
|
||||
}
|
||||
|
||||
static void test_isContinuous()
|
||||
{
|
||||
auto f = [](cv::_InputArray ia, int i) { (void)ia.isContinuous(i); };
|
||||
|
||||
cv::Mat M;
|
||||
cv::UMat uM;
|
||||
|
||||
std::vector<cv::Mat> vec = {M, M};
|
||||
std::array<cv::Mat, 2> arr = {M, M};
|
||||
std::vector<cv::UMat> uvec = {uM, uM};
|
||||
|
||||
testA(vec, f, "isContinuous");
|
||||
testA(arr, f, "isContinuous");
|
||||
testA(uvec, f, "isContinuous");
|
||||
}
|
||||
|
||||
static void test_isSubmatrix()
|
||||
{
|
||||
auto f = [](cv::_InputArray ia, int i) { (void)ia.isSubmatrix(i); };
|
||||
|
||||
cv::Mat M;
|
||||
cv::UMat uM;
|
||||
|
||||
std::vector<cv::Mat> vec = {M, M};
|
||||
std::array<cv::Mat, 2> arr = {M, M};
|
||||
std::vector<cv::UMat> uvec = {uM, uM};
|
||||
|
||||
testA(vec, f, "isSubmatrix");
|
||||
testA(arr, f, "isSubmatrix");
|
||||
testA(uvec, f, "isSubmatrix");
|
||||
}
|
||||
|
||||
static void test_offset()
|
||||
{
|
||||
auto f = [](cv::_InputArray ia, int i) { return ia.offset(i); };
|
||||
|
||||
cv::Mat M;
|
||||
cv::UMat uM;
|
||||
cv::cuda::GpuMat gM;
|
||||
|
||||
std::vector<cv::Mat> vec = {M, M};
|
||||
std::array<cv::Mat, 2> arr = {M, M};
|
||||
std::vector<cv::UMat> uvec = {uM, uM};
|
||||
std::vector<cv::cuda::GpuMat> gvec = {gM, gM};
|
||||
|
||||
testB(vec, f, "offset");
|
||||
testB(arr, f, "offset");
|
||||
testB(uvec, f, "offset");
|
||||
testB(gvec, f, "offset");
|
||||
}
|
||||
|
||||
static void test_step()
|
||||
{
|
||||
auto f = [](cv::_InputArray ia, int i) { return ia.step(i); };
|
||||
|
||||
cv::Mat M;
|
||||
cv::UMat uM;
|
||||
cv::cuda::GpuMat gM;
|
||||
|
||||
std::vector<cv::Mat> vec = {M, M};
|
||||
std::array<cv::Mat, 2> arr = {M, M};
|
||||
std::vector<cv::UMat> uvec = {uM, uM};
|
||||
std::vector<cv::cuda::GpuMat> gvec = {gM, gM};
|
||||
|
||||
testB(vec, f, "step");
|
||||
testB(arr, f, "step");
|
||||
testB(uvec, f, "step");
|
||||
testB(gvec, f, "step");
|
||||
}
|
||||
|
||||
public:
|
||||
static void run()
|
||||
{
|
||||
test_isContinuous();
|
||||
test_isSubmatrix();
|
||||
test_offset();
|
||||
test_step();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Core_InputArray, range_checking)
|
||||
{
|
||||
TestInputArrayRangeChecking::run();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
TEST(Core_Vectors, issue_13078)
|
||||
{
|
||||
float floats_[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
|
||||
@@ -189,7 +189,7 @@ TEST(Core_OutputArrayCreate, _13772)
|
||||
TEST(Core_String, find_last_of__with__empty_string)
|
||||
{
|
||||
cv::String s;
|
||||
size_t p = s.find_last_of("q", 0);
|
||||
size_t p = s.find_last_of('q', 0);
|
||||
// npos is not exported: EXPECT_EQ(cv::String::npos, p);
|
||||
EXPECT_EQ(std::string::npos, p);
|
||||
}
|
||||
|
||||
@@ -248,8 +248,6 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
|
||||
int type;
|
||||
std::vector<size_t> kernel_size, strides;
|
||||
std::vector<size_t> pads_begin, pads_end;
|
||||
CV_DEPRECATED_EXTERNAL Size kernel, stride, pad;
|
||||
CV_DEPRECATED_EXTERNAL int pad_l, pad_t, pad_r, pad_b;
|
||||
bool globalPooling; //!< Flag is true if at least one of the axes is global pooled.
|
||||
std::vector<bool> isGlobalPooling;
|
||||
bool computeMaxIdx;
|
||||
|
||||
@@ -206,7 +206,7 @@ PERF_TEST_P_(DNNTestNetwork, YOLOv3)
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_OPENCL_FP16)
|
||||
throw SkipTestException("Test is disabled in OpenVINO 2020.4");
|
||||
#endif
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021010000) // nGraph compilation failure
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2021010000) // nGraph compilation failure
|
||||
if (target == DNN_TARGET_MYRIAD)
|
||||
throw SkipTestException("");
|
||||
#endif
|
||||
@@ -241,7 +241,7 @@ PERF_TEST_P_(DNNTestNetwork, YOLOv4_tiny)
|
||||
{
|
||||
if (backend == DNN_BACKEND_HALIDE)
|
||||
throw SkipTestException("");
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021010000) // nGraph compilation failure
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2021010000) // nGraph compilation failure
|
||||
if (target == DNN_TARGET_MYRIAD)
|
||||
throw SkipTestException("");
|
||||
#endif
|
||||
@@ -276,9 +276,9 @@ PERF_TEST_P_(DNNTestNetwork, Inception_v2_Faster_RCNN)
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
throw SkipTestException("Test is disabled in OpenVINO 2019R2");
|
||||
#endif
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021010000)
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && target == DNN_TARGET_MYRIAD)
|
||||
throw SkipTestException("Test is disabled in OpenVINO 2021.1 / MYRIAD");
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2021010000)
|
||||
if (target == DNN_TARGET_MYRIAD)
|
||||
throw SkipTestException("Test is disabled in OpenVINO 2021.1+ / MYRIAD");
|
||||
#endif
|
||||
if (backend == DNN_BACKEND_HALIDE ||
|
||||
(backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && target != DNN_TARGET_CPU) ||
|
||||
|
||||
@@ -181,6 +181,8 @@ message DetectionOutputParameter {
|
||||
optional float confidence_threshold = 9;
|
||||
// If prior boxes are normalized to [0, 1] or not.
|
||||
optional bool normalized_bbox = 10 [default = true];
|
||||
// OpenCV custom parameter
|
||||
optional bool clip = 1000 [default = false];
|
||||
}
|
||||
|
||||
message Datum {
|
||||
|
||||
@@ -620,7 +620,7 @@ namespace cv {
|
||||
// read section
|
||||
read_net = false;
|
||||
++layers_counter;
|
||||
const size_t layer_type_size = line.find("]") - 1;
|
||||
const size_t layer_type_size = line.find(']') - 1;
|
||||
CV_Assert(layer_type_size < line.size());
|
||||
std::string layer_type = line.substr(1, layer_type_size);
|
||||
net->layers_cfg[layers_counter]["layer_type"] = layer_type;
|
||||
|
||||
@@ -1370,15 +1370,15 @@ public:
|
||||
v_float32x4 r2 = v_load_aligned(rptr + vsz_a*2);
|
||||
v_float32x4 r3 = v_load_aligned(rptr + vsz_a*3);
|
||||
|
||||
vs00 += w0*r0;
|
||||
vs01 += w0*r1;
|
||||
vs02 += w0*r2;
|
||||
vs03 += w0*r3;
|
||||
vs00 = v_fma(w0, r0, vs00);
|
||||
vs01 = v_fma(w0, r1, vs01);
|
||||
vs02 = v_fma(w0, r2, vs02);
|
||||
vs03 = v_fma(w0, r3, vs03);
|
||||
|
||||
vs10 += w1*r0;
|
||||
vs11 += w1*r1;
|
||||
vs12 += w1*r2;
|
||||
vs13 += w1*r3;
|
||||
vs10 = v_fma(w1, r0, vs10);
|
||||
vs11 = v_fma(w1, r1, vs11);
|
||||
vs12 = v_fma(w1, r2, vs12);
|
||||
vs13 = v_fma(w1, r3, vs13);
|
||||
}
|
||||
s0 += v_reduce_sum4(vs00, vs01, vs02, vs03);
|
||||
s1 += v_reduce_sum4(vs10, vs11, vs12, vs13);
|
||||
@@ -1461,16 +1461,7 @@ public:
|
||||
umat_blobs.resize(n);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
if (use_half)
|
||||
{
|
||||
Mat matFP32;
|
||||
convertFp16(inputs[i + 1], matFP32);
|
||||
matFP32.copyTo(umat_blobs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
inputs[i + 1].copyTo(umat_blobs[i]);
|
||||
}
|
||||
inputs[i + 1].copyTo(umat_blobs[i]);
|
||||
}
|
||||
inputs.resize(1);
|
||||
}
|
||||
@@ -1481,7 +1472,10 @@ public:
|
||||
umat_blobs.resize(n);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
blobs[i].copyTo(umat_blobs[i]);
|
||||
if (use_half)
|
||||
convertFp16(blobs[i], umat_blobs[i]);
|
||||
else
|
||||
blobs[i].copyTo(umat_blobs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1537,14 +1531,20 @@ public:
|
||||
|
||||
if (fusedWeights)
|
||||
{
|
||||
weightsMat.copyTo(umat_blobs[0]);
|
||||
if (use_half)
|
||||
convertFp16(weightsMat, umat_blobs[0]);
|
||||
else
|
||||
weightsMat.copyTo(umat_blobs[0]);
|
||||
fusedWeights = false;
|
||||
}
|
||||
if (fusedBias)
|
||||
{
|
||||
if ( umat_blobs.size() < 2 )
|
||||
umat_blobs.resize(2);
|
||||
umat_blobs[1] = UMat(biasvec, true);
|
||||
if (use_half)
|
||||
convertFp16(Mat(biasvec, true), umat_blobs[1]);
|
||||
else
|
||||
Mat(biasvec, true).copyTo(umat_blobs[1]);
|
||||
convolutionOp->setBias(true);
|
||||
fusedBias = false;
|
||||
}
|
||||
@@ -2035,20 +2035,21 @@ public:
|
||||
|
||||
for( ; n <= nmax - 4; n += 4 )
|
||||
{
|
||||
v_float32x4 d0 = v_load(dst0 + n);
|
||||
v_float32x4 d1 = v_load(dst1 + n);
|
||||
v_float32x4 b0 = v_load(bptr0 + n);
|
||||
v_float32x4 b1 = v_load(bptr1 + n);
|
||||
v_float32x4 b2 = v_load(bptr2 + n);
|
||||
v_float32x4 b3 = v_load(bptr3 + n);
|
||||
v_float32x4 d0 = v_load(dst0 + n);
|
||||
v_float32x4 d1 = v_load(dst1 + n);
|
||||
d0 += b0*a00;
|
||||
d1 += b0*a01;
|
||||
d0 += b1*a10;
|
||||
d1 += b1*a11;
|
||||
d0 += b2*a20;
|
||||
d1 += b2*a21;
|
||||
d0 += b3*a30;
|
||||
d1 += b3*a31;
|
||||
// TODO try to improve pipeline width
|
||||
d0 = v_fma(b0, a00, d0);
|
||||
d1 = v_fma(b0, a01, d1);
|
||||
d0 = v_fma(b1, a10, d0);
|
||||
d1 = v_fma(b1, a11, d1);
|
||||
d0 = v_fma(b2, a20, d0);
|
||||
d1 = v_fma(b2, a21, d1);
|
||||
d0 = v_fma(b3, a30, d0);
|
||||
d1 = v_fma(b3, a31, d1);
|
||||
v_store(dst0 + n, d0);
|
||||
v_store(dst1 + n, d1);
|
||||
}
|
||||
@@ -2056,8 +2057,10 @@ public:
|
||||
|
||||
for( ; n < nmax; n++ )
|
||||
{
|
||||
float b0 = bptr0[n], b1 = bptr1[n];
|
||||
float b2 = bptr2[n], b3 = bptr3[n];
|
||||
float b0 = bptr0[n];
|
||||
float b1 = bptr1[n];
|
||||
float b2 = bptr2[n];
|
||||
float b3 = bptr3[n];
|
||||
float d0 = dst0[n] + alpha00*b0 + alpha10*b1 + alpha20*b2 + alpha30*b3;
|
||||
float d1 = dst1[n] + alpha01*b0 + alpha11*b1 + alpha21*b2 + alpha31*b3;
|
||||
dst0[n] = d0;
|
||||
|
||||
@@ -241,16 +241,18 @@ public:
|
||||
#if CV_SIMD128
|
||||
for( ; i <= nw - 4; i += 4, wptr += 4*wstep )
|
||||
{
|
||||
v_float32x4 vs0 = v_setall_f32(0.f), vs1 = v_setall_f32(0.f);
|
||||
v_float32x4 vs2 = v_setall_f32(0.f), vs3 = v_setall_f32(0.f);
|
||||
v_float32x4 vs0 = v_setall_f32(0.f);
|
||||
v_float32x4 vs1 = v_setall_f32(0.f);
|
||||
v_float32x4 vs2 = v_setall_f32(0.f);
|
||||
v_float32x4 vs3 = v_setall_f32(0.f);
|
||||
|
||||
for( k = 0; k < vecsize; k += 4 )
|
||||
{
|
||||
v_float32x4 v = v_load_aligned(sptr + k);
|
||||
vs0 += v*v_load_aligned(wptr + k);
|
||||
vs1 += v*v_load_aligned(wptr + wstep + k);
|
||||
vs2 += v*v_load_aligned(wptr + wstep*2 + k);
|
||||
vs3 += v*v_load_aligned(wptr + wstep*3 + k);
|
||||
vs0 = v_fma(v, v_load_aligned(wptr + k), vs0);
|
||||
vs1 = v_fma(v, v_load_aligned(wptr + wstep + k), vs1);
|
||||
vs2 = v_fma(v, v_load_aligned(wptr + wstep*2 + k), vs2);
|
||||
vs3 = v_fma(v, v_load_aligned(wptr + wstep*3 + k), vs3);
|
||||
}
|
||||
|
||||
v_float32x4 s = v_reduce_sum4(vs0, vs1, vs2, vs3);
|
||||
|
||||
@@ -68,6 +68,14 @@ using std::min;
|
||||
using namespace cv::dnn::ocl4dnn;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
#if 0 // size_t is not well supported in Halide operations
|
||||
typedef size_t HALIDE_DIFF_T;
|
||||
#else
|
||||
typedef int HALIDE_DIFF_T;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
@@ -85,8 +93,6 @@ public:
|
||||
computeMaxIdx = true;
|
||||
globalPooling = false;
|
||||
isGlobalPooling = std::vector<bool>(3, false);
|
||||
stride = Size(1, 1);
|
||||
pad_t = pad_l = pad_b = pad_r = 0;
|
||||
|
||||
hasDynamicShapes = params.get<bool>("has_dynamic_shapes", false);
|
||||
shapesInitialized = !hasDynamicShapes;
|
||||
@@ -108,16 +114,6 @@ public:
|
||||
|
||||
getPoolingKernelParams(params, kernel_size, isGlobalPooling, pads_begin, pads_end, strides, padMode);
|
||||
globalPooling = isGlobalPooling[0] || isGlobalPooling[1] || isGlobalPooling[2];
|
||||
if (kernel_size.size() == 2) {
|
||||
kernel = Size(kernel_size[1], kernel_size[0]);
|
||||
stride = Size(strides[1], strides[0]);
|
||||
pad = Size(pads_begin[1], pads_begin[0]);
|
||||
|
||||
pad_t = pads_begin[0];
|
||||
pad_l = pads_begin[1];
|
||||
pad_b = pads_end[0];
|
||||
pad_r = pads_end[1];
|
||||
}
|
||||
}
|
||||
else if (params.has("pooled_w") || params.has("pooled_h"))
|
||||
{
|
||||
@@ -165,17 +161,20 @@ public:
|
||||
finalKernel.push_back(isGlobalPooling[idx] ? inp[i] : kernel_size[idx]);
|
||||
}
|
||||
kernel_size = finalKernel;
|
||||
kernel = Size(kernel_size[1], kernel_size[0]);
|
||||
}
|
||||
|
||||
getConvPoolPaddings(inp, kernel_size, strides, padMode, pads_begin, pads_end);
|
||||
if (pads_begin.size() == 2) {
|
||||
pad_t = pads_begin[0];
|
||||
pad_l = pads_begin[1];
|
||||
pad_b = pads_end[0];
|
||||
pad_r = pads_end[1];
|
||||
|
||||
if (inputs[0].dims == 3)
|
||||
{
|
||||
//Pool1D
|
||||
kernel_size.erase(kernel_size.begin() + 1);
|
||||
strides.erase(strides.begin() + 1);
|
||||
pads_begin.erase(pads_begin.begin() + 1);
|
||||
pads_end.erase(pads_end.begin() + 1);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
poolOp.release();
|
||||
#endif
|
||||
@@ -191,9 +190,11 @@ public:
|
||||
return false;
|
||||
if (kernel_size.size() == 3)
|
||||
return preferableTarget == DNN_TARGET_CPU;
|
||||
if (kernel_size.size() == 1)
|
||||
return false;
|
||||
if (preferableTarget == DNN_TARGET_MYRIAD) {
|
||||
#if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2019R1)
|
||||
if (type == MAX && (pad_l == 1 && pad_t == 1) && stride == Size(2, 2) ) {
|
||||
if (type == MAX && (pads_begin[1] == 1 && pads_begin[0] == 1) && (strides[0] == 2 && strides[1] == 2)) {
|
||||
return !isMyriadX();
|
||||
}
|
||||
#endif
|
||||
@@ -205,19 +206,23 @@ public:
|
||||
#endif
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
return !computeMaxIdx && type != STOCHASTIC;
|
||||
return !computeMaxIdx && type != STOCHASTIC && kernel_size.size() > 1;
|
||||
}
|
||||
else if (backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE)
|
||||
else if (backendId == DNN_BACKEND_OPENCV)
|
||||
{
|
||||
if (kernel_size.size() == 3)
|
||||
return (backendId == DNN_BACKEND_OPENCV && preferableTarget == DNN_TARGET_CPU);
|
||||
if (kernel_size.empty() || kernel_size.size() == 2)
|
||||
return backendId == DNN_BACKEND_OPENCV ||
|
||||
(backendId == DNN_BACKEND_HALIDE && haveHalide() &&
|
||||
(type == MAX || (type == AVE && !pad_t && !pad_l && !pad_b && !pad_r)));
|
||||
return preferableTarget == DNN_TARGET_CPU;
|
||||
if (kernel_size.size() <= 2)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (backendId == DNN_BACKEND_HALIDE)
|
||||
{
|
||||
if (kernel_size.empty() || kernel_size.size() == 2)
|
||||
return haveHalide() &&
|
||||
(type == MAX || (type == AVE && !pads_begin[0] && !pads_begin[1] && !pads_end[0] && !pads_end[1]));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -237,12 +242,25 @@ public:
|
||||
|
||||
config.in_shape = shape(inputs[0]);
|
||||
config.out_shape = shape(outputs[0]);
|
||||
config.kernel = kernel;
|
||||
config.pad_l = pad_l;
|
||||
config.pad_t = pad_t;
|
||||
config.pad_r = pad_r;
|
||||
config.pad_b = pad_b;
|
||||
config.stride = stride;
|
||||
if (inputs[0].dims == 3)
|
||||
{
|
||||
//Pool1D
|
||||
config.kernel = Size(kernel_size[0], 1);
|
||||
config.stride = Size(strides[0], 1);
|
||||
config.pad_l = pads_begin[0];
|
||||
config.pad_t = 0;
|
||||
config.pad_r = pads_end[0];
|
||||
config.pad_b = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.kernel = Size(kernel_size[1], kernel_size[0]);
|
||||
config.stride = Size(strides[1], strides[0]);
|
||||
config.pad_l = pads_begin[1];
|
||||
config.pad_t = pads_begin[0];
|
||||
config.pad_r = pads_end[1];
|
||||
config.pad_b = pads_end[0];
|
||||
}
|
||||
config.channels = inputs[0].size[1];
|
||||
config.pool_method = type == MAX ? LIBDNN_POOLING_METHOD_MAX :
|
||||
(type == AVE ? LIBDNN_POOLING_METHOD_AVE :
|
||||
@@ -428,7 +446,6 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
public:
|
||||
const Mat* src, *rois;
|
||||
Mat *dst, *mask;
|
||||
Size kernel, stride;
|
||||
int pad_l, pad_t, pad_r, pad_b;
|
||||
bool avePoolPaddedArea;
|
||||
int nstripes;
|
||||
@@ -453,7 +470,7 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
CV_Assert_N(
|
||||
src.isContinuous(), dst.isContinuous(),
|
||||
src.type() == CV_32F, src.type() == dst.type(),
|
||||
src.dims == 4 || src.dims == 5, dst.dims == 4 || dst.dims == 5,
|
||||
src.dims == 3 || src.dims == 4 || src.dims == 5, dst.dims == 3 || dst.dims == 4 || dst.dims == 5,
|
||||
(((poolingType == ROI || poolingType == PSROI) &&
|
||||
dst.size[0] == rois.size[0]) || src.size[0] == dst.size[0]),
|
||||
poolingType == PSROI || src.size[1] == dst.size[1],
|
||||
@@ -461,6 +478,9 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
|
||||
PoolingInvoker p;
|
||||
|
||||
bool isPool1D = src.dims == 3;
|
||||
bool isPool3D = src.dims == 5;
|
||||
|
||||
p.src = &src;
|
||||
p.rois = &rois;
|
||||
p.dst = &dst;
|
||||
@@ -471,12 +491,10 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
p.pads_end = pads_end;
|
||||
|
||||
p.mask = &mask;
|
||||
p.kernel = Size(kernel_size[1], kernel_size[0]);
|
||||
p.stride = Size(strides[1], strides[0]);
|
||||
p.pad_l = pads_begin.back();
|
||||
p.pad_t = pads_begin[pads_begin.size() - 2];
|
||||
p.pad_t = isPool1D ? 0 : pads_begin[pads_begin.size() - 2];
|
||||
p.pad_r = pads_end.back();
|
||||
p.pad_b = pads_end[pads_end.size() - 2];
|
||||
p.pad_b = isPool1D ? 0 : pads_end[pads_end.size() - 2];
|
||||
|
||||
p.avePoolPaddedArea = avePoolPaddedArea;
|
||||
p.nstripes = nstripes;
|
||||
@@ -486,11 +504,11 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
|
||||
if( !computeMaxIdx )
|
||||
{
|
||||
int height = src.size[src.dims - 2];
|
||||
int height = isPool1D ? 1 : src.size[src.dims - 2];
|
||||
int width = src.size[src.dims - 1];
|
||||
|
||||
int kernel_d = (kernel_size.size() == 3) ? kernel_size[0] : 1;
|
||||
int kernel_h = kernel_size[kernel_size.size() - 2];
|
||||
int kernel_d = isPool3D ? kernel_size[0] : 1;
|
||||
int kernel_h = isPool1D ? 1 : kernel_size[kernel_size.size() - 2];
|
||||
int kernel_w = kernel_size.back();
|
||||
|
||||
p.ofsbuf.resize(kernel_d * kernel_h * kernel_w);
|
||||
@@ -510,13 +528,15 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
{
|
||||
int channels = dst->size[1];
|
||||
|
||||
bool isPool3D = src->dims == 5;
|
||||
bool isPool2D = src->dims == 4;
|
||||
int depth = !isPool2D? dst->size[2] : 1;
|
||||
int height = dst->size[dst->dims - 2];
|
||||
bool isPool1D = src->dims == 3;
|
||||
int depth = isPool3D? dst->size[2] : 1;
|
||||
int height = isPool1D? 1 : dst->size[dst->dims - 2];
|
||||
int width = dst->size[dst->dims - 1];
|
||||
|
||||
int inp_depth = !isPool2D? src->size[2] : 1;
|
||||
int inp_height = src->size[src->dims - 2];
|
||||
int inp_depth = isPool3D? src->size[2] : 1;
|
||||
int inp_height = isPool1D? 1 : src->size[src->dims - 2];
|
||||
int inp_width = src->size[src->dims - 1];
|
||||
|
||||
size_t total = dst->total();
|
||||
@@ -524,12 +544,12 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
size_t stripeStart = r.start*stripeSize;
|
||||
size_t stripeEnd = std::min(r.end*stripeSize, total);
|
||||
|
||||
int kernel_d = !isPool2D? kernel_size[0] : 1;
|
||||
int kernel_h = kernel_size[kernel_size.size() - 2];
|
||||
int kernel_d = isPool3D? kernel_size[0] : 1;
|
||||
int kernel_h = isPool1D? 1 : kernel_size[kernel_size.size() - 2];
|
||||
int kernel_w = kernel_size.back();
|
||||
|
||||
int stride_d = !isPool2D? strides[0] : 0;
|
||||
int stride_h = strides[strides.size() - 2];
|
||||
int stride_d = isPool3D? strides[0] : 0;
|
||||
int stride_h = isPool1D? 1 :strides[strides.size() - 2];
|
||||
int stride_w = strides.back();
|
||||
bool compMaxIdx = computeMaxIdx;
|
||||
|
||||
@@ -720,7 +740,24 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
}
|
||||
}
|
||||
else
|
||||
#else
|
||||
CV_UNUSED(isPool2D);
|
||||
#endif
|
||||
if( isPool1D )
|
||||
{
|
||||
const float* first = srcData + xstart;
|
||||
const float* last = srcData + xend;
|
||||
const float* max_elem = std::max_element(first, last);
|
||||
if (max_elem!=last)
|
||||
{
|
||||
dstData[x0] = *max_elem;
|
||||
if( compMaxIdx )
|
||||
{
|
||||
dstMaskData[x0] = std::distance(first, max_elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float max_val = -FLT_MAX;
|
||||
if( compMaxIdx )
|
||||
@@ -794,6 +831,14 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if( isPool1D )
|
||||
{
|
||||
const float* first = srcData + xstart;
|
||||
const float* last = srcData + xend;
|
||||
float sum_val = std::accumulate(first, last, 0.f);
|
||||
dstData[x0] = sum_val*inv_kernel_area;
|
||||
}
|
||||
else
|
||||
{
|
||||
float sum_val = 0.f;
|
||||
for (int d = dstart; d < dend; ++d) {
|
||||
@@ -907,20 +952,26 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
const int inWidth = inputBuffer.width();
|
||||
const int inHeight = inputBuffer.height();
|
||||
const HALIDE_DIFF_T kernelHeight = (HALIDE_DIFF_T)kernel_size[0];
|
||||
const HALIDE_DIFF_T kernelWidth = (HALIDE_DIFF_T)kernel_size[1];
|
||||
const HALIDE_DIFF_T strideHeight = (HALIDE_DIFF_T)strides[0];
|
||||
const HALIDE_DIFF_T strideWidth = (HALIDE_DIFF_T)strides[1];
|
||||
const HALIDE_DIFF_T paddingTop = (HALIDE_DIFF_T)pads_begin[0];
|
||||
const HALIDE_DIFF_T paddingLeft = (HALIDE_DIFF_T)pads_begin[1];
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::RDom r(0, kernel.width, 0, kernel.height);
|
||||
Halide::RDom r(0, kernelWidth, 0, kernelHeight);
|
||||
Halide::Expr kx, ky;
|
||||
if(pad_l || pad_t)
|
||||
if(paddingLeft || paddingTop)
|
||||
{
|
||||
kx = clamp(x * stride.width + r.x - pad_l, 0, inWidth - 1);
|
||||
ky = clamp(y * stride.height + r.y - pad_t, 0, inHeight - 1);
|
||||
kx = clamp(x * strideWidth + r.x - paddingLeft, 0, inWidth - 1);
|
||||
ky = clamp(y * strideHeight + r.y - paddingTop, 0, inHeight - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
kx = min(x * stride.width + r.x, inWidth - 1);
|
||||
ky = min(y * stride.height + r.y, inHeight - 1);
|
||||
kx = min(x * strideWidth + r.x, inWidth - 1);
|
||||
ky = min(y * strideHeight + r.y, inHeight - 1);
|
||||
}
|
||||
|
||||
// Halide::argmax returns tuple (r.x, r.y, max).
|
||||
@@ -928,17 +979,17 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
|
||||
// Compute offset from argmax in range [0, kernel_size).
|
||||
Halide::Expr max_index;
|
||||
if(pad_l || pad_t)
|
||||
if(paddingLeft || paddingTop)
|
||||
{
|
||||
max_index = clamp(y * stride.height + res[1] - pad_t,
|
||||
max_index = clamp(y * strideHeight + res[1] - paddingTop,
|
||||
0, inHeight - 1) * inWidth +
|
||||
clamp(x * stride.width + res[0] - pad_l,
|
||||
clamp(x * strideWidth + res[0] - paddingLeft,
|
||||
0, inWidth - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
max_index = min(y * stride.height + res[1], inHeight - 1) * inWidth +
|
||||
min(x * stride.width + res[0], inWidth - 1);
|
||||
max_index = min(y * strideHeight + res[1], inHeight - 1) * inWidth +
|
||||
min(x * strideWidth + res[0], inWidth - 1);
|
||||
}
|
||||
top(x, y, c, n) = { res[2], Halide::cast<float>(max_index) };
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
@@ -952,21 +1003,25 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
|
||||
const int inW = inputBuffer.width(), inH = inputBuffer.height();
|
||||
if ((inW - kernel.width) % stride.width || (inH - kernel.height) % stride.height)
|
||||
const HALIDE_DIFF_T kernelHeight = (HALIDE_DIFF_T)kernel_size[0];
|
||||
const HALIDE_DIFF_T kernelWidth = (HALIDE_DIFF_T)kernel_size[1];
|
||||
const HALIDE_DIFF_T strideHeight = (HALIDE_DIFF_T)strides[0];
|
||||
const HALIDE_DIFF_T strideWidth = (HALIDE_DIFF_T)strides[1];
|
||||
if ((inW - kernelWidth) % strideWidth || (inH - kernelHeight) % strideHeight)
|
||||
{
|
||||
CV_Error(cv::Error::StsNotImplemented,
|
||||
"Halide backend for average pooling with partial "
|
||||
"kernels is not implemented");
|
||||
}
|
||||
|
||||
const float norm = 1.0f / (kernel.width * kernel.height);
|
||||
const float norm = 1.0f / (kernelWidth * kernelHeight);
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::RDom r(0, kernel.width, 0, kernel.height);
|
||||
Halide::RDom r(0, kernelWidth, 0, kernelHeight);
|
||||
top(x, y, c, n) = sum(
|
||||
inputBuffer(x * stride.width + r.x,
|
||||
y * stride.height + r.y, c, n)) * norm;
|
||||
inputBuffer(x * strideWidth + r.x,
|
||||
y * strideHeight + r.y, c, n)) * norm;
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
@@ -1028,6 +1083,7 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
{
|
||||
CV_Assert(inputs.size() != 0);
|
||||
|
||||
bool isPool1D = inputs[0].size() == 3;
|
||||
std::vector<int> inpShape(inputs[0].begin() + 2, inputs[0].end());
|
||||
std::vector<int> outShape(inputs[0].begin(), inputs[0].begin() + 2);
|
||||
|
||||
@@ -1056,14 +1112,15 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
}
|
||||
else if (padMode.empty())
|
||||
{
|
||||
for (int i = 0; i < local_kernel.size(); i++) {
|
||||
int addedDims = isPool1D? inpShape.size() : local_kernel.size();
|
||||
for (int i = 0; i < addedDims; i++) {
|
||||
float dst = (float) (inpShape[i] + pads_begin[i] + pads_end[i] - local_kernel[i]) / strides[i];
|
||||
outShape.push_back(1 + (ceilMode ? ceil(dst) : floor(dst)));
|
||||
}
|
||||
|
||||
// If we have padding, ensure that the last pooling starts strictly
|
||||
// inside the image (instead of at the padding); otherwise clip the last.
|
||||
for (int i = 0; i < pads_end.size(); i++) {
|
||||
for (int i = 0; i < addedDims; i++) {
|
||||
if (pads_end[i] && (outShape[2 + i] - 1) * strides[i] >= inpShape[i] + pads_end[i]) {
|
||||
--outShape[2 + i];
|
||||
CV_Assert((outShape[2 + i] - 1) * strides[i] < inpShape[i] + pads_end[i]);
|
||||
@@ -1107,7 +1164,8 @@ virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inp
|
||||
{
|
||||
CV_UNUSED(inputs); // suppress unused variable warning
|
||||
long flops = 0;
|
||||
size_t karea = std::accumulate(kernel_size.begin(), kernel_size.end(),
|
||||
bool isPool1D = inputs[0].size() == 3;
|
||||
size_t karea = std::accumulate(kernel_size.begin(), isPool1D? kernel_size.begin() + 1 : kernel_size.end(),
|
||||
1, std::multiplies<size_t>());
|
||||
for(int i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
CV_Check(interpolation, interpolation == "nearest" || interpolation == "opencv_linear" || interpolation == "bilinear", "");
|
||||
|
||||
alignCorners = params.get<bool>("align_corners", false);
|
||||
halfPixelCenters = params.get<bool>("half_pixel_centers", false);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
@@ -114,7 +115,7 @@ public:
|
||||
|
||||
Mat& inp = inputs[0];
|
||||
Mat& out = outputs[0];
|
||||
if (interpolation == "nearest" || interpolation == "opencv_linear")
|
||||
if ((interpolation == "nearest" && !alignCorners && !halfPixelCenters) || interpolation == "opencv_linear" || (interpolation == "bilinear" && halfPixelCenters))
|
||||
{
|
||||
InterpolationFlags mode = interpolation == "nearest" ? INTER_NEAREST : INTER_LINEAR;
|
||||
for (size_t n = 0; n < inputs[0].size[0]; ++n)
|
||||
@@ -126,6 +127,54 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (interpolation == "nearest")
|
||||
{
|
||||
const int inpHeight = inp.size[2];
|
||||
const int inpWidth = inp.size[3];
|
||||
const int inpSpatialSize = inpHeight * inpWidth;
|
||||
const int outSpatialSize = outHeight * outWidth;
|
||||
const int numPlanes = inp.size[0] * inp.size[1];
|
||||
CV_Assert_N(inp.isContinuous(), out.isContinuous());
|
||||
|
||||
Mat inpPlanes = inp.reshape(1, numPlanes * inpHeight);
|
||||
Mat outPlanes = out.reshape(1, numPlanes * outHeight);
|
||||
|
||||
float heightOffset = 0.0f;
|
||||
float widthOffset = 0.0f;
|
||||
|
||||
if (halfPixelCenters)
|
||||
{
|
||||
heightOffset = 0.5f * scaleHeight;
|
||||
widthOffset = 0.5f * scaleWidth;
|
||||
}
|
||||
|
||||
for (int y = 0; y < outHeight; ++y)
|
||||
{
|
||||
float input_y = y * scaleHeight + heightOffset;
|
||||
int y0 = halfPixelCenters ? std::floor(input_y) : lroundf(input_y);
|
||||
y0 = std::min(y0, inpHeight - 1);
|
||||
|
||||
const float* inpData_row = inpPlanes.ptr<float>(y0);
|
||||
|
||||
for (int x = 0; x < outWidth; ++x)
|
||||
{
|
||||
float input_x = x * scaleWidth + widthOffset;
|
||||
int x0 = halfPixelCenters ? std::floor(input_x) : lroundf(input_x);
|
||||
x0 = std::min(x0, inpWidth - 1);
|
||||
|
||||
float* outData = outPlanes.ptr<float>(y, x);
|
||||
const float* inpData_row_c = inpData_row;
|
||||
|
||||
for (int c = 0; c < numPlanes; ++c)
|
||||
{
|
||||
*outData = inpData_row_c[x0];
|
||||
|
||||
inpData_row_c += inpSpatialSize;
|
||||
outData += outSpatialSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (interpolation == "bilinear")
|
||||
{
|
||||
const int inpHeight = inp.size[2];
|
||||
@@ -236,6 +285,7 @@ protected:
|
||||
String interpolation;
|
||||
float scaleWidth, scaleHeight;
|
||||
bool alignCorners;
|
||||
bool halfPixelCenters;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -274,8 +274,6 @@ class OCL4DNNConvSpatial
|
||||
int32_t group_;
|
||||
bool bias_term_;
|
||||
UMat swizzled_weights_umat;
|
||||
UMat weights_half;
|
||||
UMat bias_half;
|
||||
UMat bottom_data2_;
|
||||
|
||||
int32_t bottom_index_;
|
||||
|
||||
@@ -88,13 +88,13 @@ ocl::Image2D ocl4dnnGEMMCopyBufferToImage(UMat buffer, int offset,
|
||||
size_t global_copy[2];
|
||||
global_copy[0] = width;
|
||||
global_copy[1] = height;
|
||||
oclk_gemm_copy.set(0, ocl::KernelArg::PtrReadOnly(buffer));
|
||||
oclk_gemm_copy.set(1, image);
|
||||
oclk_gemm_copy.set(2, offset);
|
||||
oclk_gemm_copy.set(3, width);
|
||||
oclk_gemm_copy.set(4, height);
|
||||
oclk_gemm_copy.set(5, ld);
|
||||
oclk_gemm_copy.run(2, global_copy, NULL, false);
|
||||
oclk_gemm_copy
|
||||
.args(
|
||||
ocl::KernelArg::PtrReadOnly(buffer),
|
||||
image, offset,
|
||||
width, height,
|
||||
ld)
|
||||
.run(2, global_copy, NULL, false);
|
||||
}
|
||||
} else {
|
||||
if (!padding)
|
||||
@@ -112,13 +112,13 @@ ocl::Image2D ocl4dnnGEMMCopyBufferToImage(UMat buffer, int offset,
|
||||
global_copy[0] = padded_width;
|
||||
global_copy[1] = padded_height;
|
||||
|
||||
oclk_gemm_copy.set(0, ocl::KernelArg::PtrReadOnly(buffer));
|
||||
oclk_gemm_copy.set(1, image);
|
||||
oclk_gemm_copy.set(2, offset);
|
||||
oclk_gemm_copy.set(3, width);
|
||||
oclk_gemm_copy.set(4, height);
|
||||
oclk_gemm_copy.set(5, ld);
|
||||
|
||||
oclk_gemm_copy
|
||||
.args(
|
||||
ocl::KernelArg::PtrReadOnly(buffer),
|
||||
image, offset,
|
||||
width, height,
|
||||
ld)
|
||||
.run(2, global_copy, NULL, false);
|
||||
oclk_gemm_copy.run(2, global_copy, NULL, false);
|
||||
}
|
||||
}
|
||||
@@ -465,8 +465,12 @@ static bool ocl4dnnFastBufferGEMM(const CBLAS_TRANSPOSE TransA,
|
||||
kernel_name += "_float";
|
||||
}
|
||||
|
||||
bool isBetaZero = beta == 0;
|
||||
|
||||
String opts = format("-DTYPE=%d", halfPrecisionMode ? TYPE_HALF : TYPE_FLOAT);
|
||||
ocl::Kernel oclk_gemm_float(kernel_name.c_str(), ocl::dnn::gemm_buffer_oclsrc, opts);
|
||||
if (isBetaZero)
|
||||
opts += " -DZERO_BETA=1";
|
||||
|
||||
size_t local[2] = {};
|
||||
size_t global[2] = {};
|
||||
if (TransA == CblasNoTrans && TransB != CblasNoTrans && is_small_batch) {
|
||||
@@ -496,27 +500,37 @@ static bool ocl4dnnFastBufferGEMM(const CBLAS_TRANSPOSE TransA,
|
||||
local[1] = ly;
|
||||
}
|
||||
|
||||
int arg_idx = 0;
|
||||
oclk_gemm_float.set(arg_idx++, ocl::KernelArg::PtrReadOnly(A));
|
||||
oclk_gemm_float.set(arg_idx++, offA);
|
||||
oclk_gemm_float.set(arg_idx++, ocl::KernelArg::PtrReadOnly(B));
|
||||
oclk_gemm_float.set(arg_idx++, offB);
|
||||
oclk_gemm_float.set(arg_idx++, ocl::KernelArg::PtrWriteOnly(C));
|
||||
oclk_gemm_float.set(arg_idx++, offC);
|
||||
oclk_gemm_float.set(arg_idx++, M);
|
||||
oclk_gemm_float.set(arg_idx++, N);
|
||||
oclk_gemm_float.set(arg_idx++, K);
|
||||
oclk_gemm_float.set(arg_idx++, (float)alpha);
|
||||
oclk_gemm_float.set(arg_idx++, (float)beta);
|
||||
|
||||
bool ret = true;
|
||||
if (TransB == CblasNoTrans || TransA != CblasNoTrans) {
|
||||
if (TransB == CblasNoTrans || TransA != CblasNoTrans)
|
||||
{
|
||||
// _NN_
|
||||
int stride = 256;
|
||||
for (int start_index = 0; start_index < K; start_index += stride) {
|
||||
oclk_gemm_float.set(arg_idx, start_index);
|
||||
ret = oclk_gemm_float.run(2, global, local, false);
|
||||
ocl::Kernel oclk_gemm_float(kernel_name.c_str(), ocl::dnn::gemm_buffer_oclsrc, opts);
|
||||
oclk_gemm_float.args(
|
||||
ocl::KernelArg::PtrReadOnly(A), offA,
|
||||
ocl::KernelArg::PtrReadOnly(B), offB,
|
||||
isBetaZero ? ocl::KernelArg::PtrWriteOnly(C) : ocl::KernelArg::PtrReadWrite(C), offC,
|
||||
M, N, K,
|
||||
(float)alpha, (float)beta,
|
||||
start_index
|
||||
);
|
||||
ret &= oclk_gemm_float.run(2, global, local, false);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// _NT_
|
||||
//C.reshape(1,1).setTo(0xfe00 /*FP16 NAN*/); // stable one-line reproducer for https://github.com/opencv/opencv/issues/18937
|
||||
//C.reshape(1,1).setTo(0); // non-optimal fixup (and not accurate)
|
||||
ocl::Kernel oclk_gemm_float(kernel_name.c_str(), ocl::dnn::gemm_buffer_oclsrc, opts);
|
||||
oclk_gemm_float.args(
|
||||
ocl::KernelArg::PtrReadOnly(A), offA,
|
||||
ocl::KernelArg::PtrReadOnly(B), offB,
|
||||
isBetaZero ? ocl::KernelArg::PtrWriteOnly(C) : ocl::KernelArg::PtrReadWrite(C), offC,
|
||||
M, N, K,
|
||||
(float)alpha, (float)beta
|
||||
);
|
||||
ret = oclk_gemm_float.run(2, global, local, false);
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -588,16 +588,16 @@ bool OCL4DNNConvSpatial<Dtype>::Forward(const UMat& bottom,
|
||||
fused_eltwise_ = false;
|
||||
}
|
||||
|
||||
if (use_half_ && bias_half.empty() && !bias.empty())
|
||||
convertFp16(bias, bias_half);
|
||||
if (use_half_ && !bias.empty())
|
||||
CV_CheckTypeEQ(bias.type(), CV_16SC1, "");
|
||||
|
||||
if (use_half_ && weights_half.empty())
|
||||
convertFp16(weight, weights_half);
|
||||
if (use_half_)
|
||||
CV_CheckTypeEQ(weight.type(), CV_16SC1, "");
|
||||
|
||||
prepareKernel(bottom, top, weight, (use_half_) ? bias_half : bias, numImages);
|
||||
prepareKernel(bottom, top, weight, bias, numImages);
|
||||
if (bestKernelConfig.empty())
|
||||
return false;
|
||||
return convolve(bottom, top, weight, (use_half_) ? bias_half : bias, numImages, bestKernelConfig);
|
||||
return convolve(bottom, top, weight, bias, numImages, bestKernelConfig);
|
||||
}
|
||||
|
||||
template<typename Dtype>
|
||||
@@ -744,29 +744,26 @@ bool OCL4DNNConvSpatial<Dtype>::swizzleWeight(const UMat &weight,
|
||||
kernel_h_ * (int)alignSize(kernel_w_, 2),
|
||||
(use_half_) ? CV_16SC1 : CV_32FC1);
|
||||
|
||||
UMat swizzled_weights_tmp;
|
||||
if (use_half_)
|
||||
swizzled_weights_tmp.create(shape(swizzled_weights_umat), CV_32F);
|
||||
|
||||
if (!interleave) {
|
||||
cl_uint argIdx = 0;
|
||||
int32_t channels = channels_ / group_;
|
||||
|
||||
ocl::Kernel oclk_copy_weight(CL_KERNEL_SELECT("copyWeightsSwizzled"),
|
||||
cv::ocl::dnn::conv_spatial_helper_oclsrc);
|
||||
ocl::Kernel oclk_copy_weight(
|
||||
use_half_ ? "copyWeightsSwizzled_half" : "copyWeightsSwizzled_float",
|
||||
cv::ocl::dnn::conv_spatial_helper_oclsrc,
|
||||
use_half_ ? "-DHALF_SUPPORT=1 -DDtype=half" : "-DDtype=float"
|
||||
);
|
||||
if (oclk_copy_weight.empty())
|
||||
return false;
|
||||
|
||||
oclk_copy_weight.set(argIdx++, ocl::KernelArg::PtrReadOnly(weight));
|
||||
if (use_half_)
|
||||
oclk_copy_weight.set(argIdx++, ocl::KernelArg::PtrWriteOnly(swizzled_weights_tmp));
|
||||
else
|
||||
oclk_copy_weight.set(argIdx++, ocl::KernelArg::PtrWriteOnly(swizzled_weights_umat));
|
||||
oclk_copy_weight.set(argIdx++, kernel_w_);
|
||||
oclk_copy_weight.set(argIdx++, kernel_h_);
|
||||
oclk_copy_weight.set(argIdx++, channels);
|
||||
oclk_copy_weight.set(argIdx++, num_output_);
|
||||
oclk_copy_weight.set(argIdx++, swizzled_factor);
|
||||
oclk_copy_weight.args(
|
||||
ocl::KernelArg::PtrReadOnly(weight),
|
||||
ocl::KernelArg::PtrWriteOnly(swizzled_weights_umat),
|
||||
kernel_w_,
|
||||
kernel_h_,
|
||||
channels,
|
||||
num_output_,
|
||||
swizzled_factor
|
||||
);
|
||||
|
||||
size_t global_work_size_copy[3] = {
|
||||
(size_t) (alignSize(num_output_, swizzled_factor) * channels * kernel_w_ * kernel_h_), 1, 1 };
|
||||
@@ -778,13 +775,24 @@ bool OCL4DNNConvSpatial<Dtype>::swizzleWeight(const UMat &weight,
|
||||
}
|
||||
} else {
|
||||
// assumption: kernel dimension is 2
|
||||
Mat weightMat = weight.getMat(ACCESS_READ);
|
||||
Dtype* cpu_weight = (Dtype *)weightMat.ptr<float>();
|
||||
Mat weightMat;
|
||||
Mat swizzledWeightMat;
|
||||
UMat weight_tmp; // FP32 in half mode, TODO implement FP16 repack
|
||||
if (use_half_)
|
||||
swizzledWeightMat = swizzled_weights_tmp.getMat(ACCESS_WRITE);
|
||||
{
|
||||
CV_CheckTypeEQ(weight.type(), CV_16SC1, "");
|
||||
convertFp16(weight, weight_tmp);
|
||||
weightMat = weight_tmp.getMat(ACCESS_READ);
|
||||
swizzledWeightMat.create(shape(swizzled_weights_umat), CV_32F);
|
||||
}
|
||||
else
|
||||
{
|
||||
weightMat = weight.getMat(ACCESS_READ);
|
||||
swizzledWeightMat = swizzled_weights_umat.getMat(ACCESS_WRITE);
|
||||
}
|
||||
|
||||
CV_CheckTypeEQ(weightMat.type(), CV_32FC1, "");
|
||||
Dtype* cpu_weight = (Dtype *)weightMat.ptr<float>();
|
||||
Dtype* cpu_swizzled_weight = (Dtype *)swizzledWeightMat.ptr<float>();
|
||||
|
||||
int interleavedRows = (kernel_w_ / 2) * 2;
|
||||
@@ -792,26 +800,28 @@ bool OCL4DNNConvSpatial<Dtype>::swizzleWeight(const UMat &weight,
|
||||
int blockWidth = swizzled_factor; // should equal to simd size.
|
||||
int rowAlignment = 32;
|
||||
size_t interleaved_filter_size = M_ * kernel_w_ * kernel_h_ * channels_ * sizeof(Dtype);
|
||||
Dtype * tmpSwizzledWeight = reinterpret_cast<Dtype*>(malloc(interleaved_filter_size));
|
||||
CHECK_EQ(tmpSwizzledWeight != NULL, true) << "Failed to allocate temporary swizzled weight";
|
||||
cv::AutoBuffer<Dtype, 0> tmpSwizzledWeight(interleaved_filter_size);
|
||||
for (int od = 0; od < M_; od++)
|
||||
for (int id = 0; id < channels_; id++)
|
||||
for (int r = 0; r < kernel_h_; r++)
|
||||
for (int c = 0; c < kernel_w_; c++)
|
||||
tmpSwizzledWeight[((id * kernel_h_ + r)* kernel_w_ + c) * M_ + od] =
|
||||
cpu_weight[((od * channels_ + id) * kernel_h_ + r)*kernel_w_+c];
|
||||
|
||||
interleaveMatrix(cpu_swizzled_weight,
|
||||
tmpSwizzledWeight,
|
||||
tmpSwizzledWeight.data(),
|
||||
kernel_w_ * kernel_h_ * channels_, M_,
|
||||
interleavedRows,
|
||||
nonInterleavedRows,
|
||||
blockWidth,
|
||||
rowAlignment);
|
||||
free(tmpSwizzledWeight);
|
||||
}
|
||||
|
||||
if (use_half_)
|
||||
convertFp16(swizzled_weights_tmp, swizzled_weights_umat);
|
||||
// unmap OpenCL buffers
|
||||
weightMat.release();
|
||||
|
||||
if (use_half_)
|
||||
convertFp16(swizzledWeightMat, swizzled_weights_umat);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1104,10 +1114,7 @@ bool OCL4DNNConvSpatial<float>::convolve(const UMat &bottom, UMat &top,
|
||||
cl_uint argIdx = 0;
|
||||
setFusionArg(fused_activ_, fused_eltwise_, kernel, argIdx);
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bottom));
|
||||
if (use_half_)
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weights_half));
|
||||
else
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weight));
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weight));
|
||||
if (bias_term_)
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bias));
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrWriteOnly(top));
|
||||
@@ -1148,10 +1155,7 @@ bool OCL4DNNConvSpatial<float>::convolve(const UMat &bottom, UMat &top,
|
||||
setFusionArg(fused_activ_, fused_eltwise_, kernel, argIdx);
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bottom));
|
||||
kernel.set(argIdx++, image_offset);
|
||||
if (use_half_)
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weights_half));
|
||||
else
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weight));
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(weight));
|
||||
kernel.set(argIdx++, kernel_offset);
|
||||
if (bias_term_)
|
||||
kernel.set(argIdx++, ocl::KernelArg::PtrReadOnly(bias));
|
||||
@@ -1956,7 +1960,7 @@ void OCL4DNNConvSpatial<Dtype>::prepareKernel(const UMat &bottom, UMat &top,
|
||||
|
||||
UMat benchData(1, numImages * top_dim_, (use_half_) ? CV_16SC1 : CV_32FC1);
|
||||
|
||||
calculateBenchmark(bottom, benchData, (use_half_) ? weights_half : weight, bias, numImages);
|
||||
calculateBenchmark(bottom, benchData, weight, bias, numImages);
|
||||
|
||||
if (run_auto_tuning_ || force_auto_tuning_)
|
||||
{
|
||||
|
||||
@@ -51,18 +51,20 @@ template<typename Dtype>
|
||||
OCL4DNNPool<Dtype>::OCL4DNNPool(OCL4DNNPoolConfig config)
|
||||
{
|
||||
int dims = config.in_shape.size();
|
||||
int spatial_dims = 2;
|
||||
int spatial_dims = config.in_shape.size()-2;
|
||||
|
||||
channels_ = config.channels;
|
||||
pool_method_ = config.pool_method;
|
||||
avePoolPaddedArea = config.avePoolPaddedArea;
|
||||
computeMaxIdx = config.computeMaxIdx;
|
||||
use_half = config.use_half;
|
||||
kernel_shape_.push_back(config.kernel.height);
|
||||
kernel_shape_.push_back(config.kernel.width);
|
||||
stride_.push_back(config.stride.height);
|
||||
stride_.push_back(config.stride.width);
|
||||
|
||||
for (int i = 0; i < spatial_dims; ++i)
|
||||
{
|
||||
kernel_shape_.push_back(i == 0 ? config.kernel.height : config.kernel.width);
|
||||
stride_.push_back(i == 0 ? config.stride.height : config.stride.width);
|
||||
im_in_shape_.push_back(config.in_shape[dims - spatial_dims + i]);
|
||||
im_out_shape_.push_back(config.out_shape[dims - spatial_dims + i]);
|
||||
}
|
||||
@@ -75,10 +77,10 @@ OCL4DNNPool<Dtype>::OCL4DNNPool(OCL4DNNPoolConfig config)
|
||||
pad_l_ = config.pad_l;
|
||||
pad_r_ = config.pad_r;
|
||||
pad_b_ = config.pad_b;
|
||||
height_ = im_in_shape_[0];
|
||||
width_ = im_in_shape_[1];
|
||||
pooled_height_ = im_out_shape_[0];
|
||||
pooled_width_ = im_out_shape_[1];
|
||||
height_ = spatial_dims == 1? 1 : im_in_shape_[0];
|
||||
width_ = im_in_shape_.back();
|
||||
pooled_height_ = spatial_dims == 1? 1 : im_out_shape_[0];
|
||||
pooled_width_ = im_out_shape_.back();
|
||||
|
||||
count_ = 1;
|
||||
for (int i = 0; i < config.out_shape.size(); ++i)
|
||||
|
||||
@@ -500,14 +500,17 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
MatShape inpShape = outShapes[node_proto.input(0)];
|
||||
DictValue axes = layerParams.get("axes");
|
||||
bool keepdims = layerParams.get<int>("keepdims");
|
||||
MatShape targetShape = inpShape;
|
||||
MatShape targetShape;
|
||||
std::vector<bool> shouldDelete(inpShape.size(), false);
|
||||
for (int i = 0; i < axes.size(); i++) {
|
||||
int axis = clamp(axes.get<int>(i), inpShape.size());
|
||||
if (keepdims) {
|
||||
targetShape[axis] = 1;
|
||||
} else {
|
||||
targetShape.erase(targetShape.begin() + axis);
|
||||
}
|
||||
shouldDelete[axis] = true;
|
||||
}
|
||||
for (int axis = 0; axis < inpShape.size(); ++axis){
|
||||
if (!shouldDelete[axis])
|
||||
targetShape.push_back(inpShape[axis]);
|
||||
else if (keepdims)
|
||||
targetShape.push_back(1);
|
||||
}
|
||||
|
||||
if (inpShape.size() == 3 && axes.size() <= 2)
|
||||
@@ -1746,43 +1749,45 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
for (int i = 1; i < node_proto.input_size(); i++)
|
||||
CV_Assert(layer_id.find(node_proto.input(i)) == layer_id.end());
|
||||
|
||||
String interp_mode;
|
||||
if (layerParams.has("coordinate_transformation_mode"))
|
||||
interp_mode = layerParams.get<String>("coordinate_transformation_mode");
|
||||
else
|
||||
interp_mode = layerParams.get<String>("mode");
|
||||
CV_Assert_N(interp_mode != "tf_crop_and_resize", interp_mode != "tf_half_pixel_for_nn");
|
||||
{
|
||||
String interp_mode = layerParams.get<String>("coordinate_transformation_mode");
|
||||
CV_Assert_N(interp_mode != "tf_crop_and_resize", interp_mode != "tf_half_pixel_for_nn");
|
||||
|
||||
layerParams.set("align_corners", interp_mode == "align_corners");
|
||||
Mat shapes = getBlob(node_proto, node_proto.input_size() - 1);
|
||||
CV_CheckEQ(shapes.size[0], 4, "");
|
||||
CV_CheckEQ(shapes.size[1], 1, "");
|
||||
CV_CheckDepth(shapes.depth(), shapes.depth() == CV_32S || shapes.depth() == CV_32F, "");
|
||||
if (shapes.depth() == CV_32F)
|
||||
shapes.convertTo(shapes, CV_32S);
|
||||
int height = shapes.at<int>(2);
|
||||
int width = shapes.at<int>(3);
|
||||
if (hasDynamicShapes)
|
||||
{
|
||||
layerParams.set("zoom_factor_x", width);
|
||||
layerParams.set("zoom_factor_y", height);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node_proto.input_size() == 3) {
|
||||
IterShape_t shapeIt = outShapes.find(node_proto.input(0));
|
||||
CV_Assert(shapeIt != outShapes.end());
|
||||
MatShape scales = shapeIt->second;
|
||||
height *= scales[2];
|
||||
width *= scales[3];
|
||||
layerParams.set("align_corners", interp_mode == "align_corners");
|
||||
if (layerParams.get<String>("mode") == "linear")
|
||||
{
|
||||
layerParams.set("mode", interp_mode == "pytorch_half_pixel" ?
|
||||
"opencv_linear" : "bilinear");
|
||||
}
|
||||
layerParams.set("width", width);
|
||||
layerParams.set("height", height);
|
||||
}
|
||||
if (layerParams.get<String>("mode") == "linear" && framework_name == "pytorch")
|
||||
layerParams.set("mode", "opencv_linear");
|
||||
|
||||
if (layerParams.get<String>("mode") == "linear") {
|
||||
layerParams.set("mode", interp_mode == "pytorch_half_pixel" ?
|
||||
"opencv_linear" : "bilinear");
|
||||
// input = [X, scales], [X, roi, scales] or [x, roi, scales, sizes]
|
||||
int foundScaleId = hasDynamicShapes ? node_proto.input_size() - 1
|
||||
: node_proto.input_size() > 2 ? 2 : 1;
|
||||
|
||||
Mat scales = getBlob(node_proto, foundScaleId);
|
||||
if (scales.total() == 4)
|
||||
{
|
||||
layerParams.set("zoom_factor_y", scales.at<float>(2));
|
||||
layerParams.set("zoom_factor_x", scales.at<float>(3));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string& inputLast = node_proto.input(node_proto.input_size() - 1);
|
||||
if (constBlobs.find(inputLast) != constBlobs.end())
|
||||
{
|
||||
Mat shapes = getBlob(inputLast);
|
||||
CV_CheckEQ(shapes.size[0], 4, "");
|
||||
CV_CheckEQ(shapes.size[1], 1, "");
|
||||
CV_CheckDepth(shapes.depth(), shapes.depth() == CV_32S || shapes.depth() == CV_32F, "");
|
||||
if (shapes.depth() == CV_32F)
|
||||
shapes.convertTo(shapes, CV_32S);
|
||||
layerParams.set("width", shapes.at<int>(3));
|
||||
layerParams.set("height", shapes.at<int>(2));
|
||||
}
|
||||
}
|
||||
replaceLayerParam(layerParams, "mode", "interpolation");
|
||||
}
|
||||
@@ -1822,10 +1827,14 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
else
|
||||
{
|
||||
// scales as input
|
||||
Mat scales = getBlob(node_proto, 1);
|
||||
CV_Assert(scales.total() == 4);
|
||||
layerParams.set("zoom_factor_y", scales.at<float>(2));
|
||||
layerParams.set("zoom_factor_x", scales.at<float>(3));
|
||||
const std::string& input1 = node_proto.input(1);
|
||||
if (constBlobs.find(input1) != constBlobs.end())
|
||||
{
|
||||
Mat scales = getBlob(input1);
|
||||
CV_Assert(scales.total() == 4);
|
||||
layerParams.set("zoom_factor_y", scales.at<float>(2));
|
||||
layerParams.set("zoom_factor_x", scales.at<float>(3));
|
||||
}
|
||||
}
|
||||
replaceLayerParam(layerParams, "mode", "interpolation");
|
||||
}
|
||||
|
||||
@@ -28,10 +28,11 @@
|
||||
#define INF_ENGINE_RELEASE_2020_3 2020030000
|
||||
#define INF_ENGINE_RELEASE_2020_4 2020040000
|
||||
#define INF_ENGINE_RELEASE_2021_1 2021010000
|
||||
#define INF_ENGINE_RELEASE_2021_2 2021020000
|
||||
|
||||
#ifndef INF_ENGINE_RELEASE
|
||||
#warning("IE version have not been provided via command-line. Using 2021.1 by default")
|
||||
#define INF_ENGINE_RELEASE INF_ENGINE_RELEASE_2021_1
|
||||
#warning("IE version have not been provided via command-line. Using 2021.2 by default")
|
||||
#define INF_ENGINE_RELEASE INF_ENGINE_RELEASE_2021_2
|
||||
#endif
|
||||
|
||||
#define INF_ENGINE_VER_MAJOR_GT(ver) (((INF_ENGINE_RELEASE) / 10000) > ((ver) / 10000))
|
||||
|
||||
@@ -39,9 +39,14 @@
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifdef HALF_SUPPORT
|
||||
#ifdef cl_khr_fp16
|
||||
#pragma OPENCL EXTENSION cl_khr_fp16:enable
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONCAT(A,B) A##_##B
|
||||
#define TEMPLATE(name,type) CONCAT(name,type)
|
||||
#define Dtype float
|
||||
|
||||
__kernel void TEMPLATE(copyWeightsSwizzled, Dtype)
|
||||
(__global Dtype* weightIn,
|
||||
|
||||
@@ -90,6 +90,12 @@
|
||||
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
|
||||
#endif
|
||||
|
||||
#ifdef ZERO_BETA
|
||||
#define BETA_ZERO_CHECK(b0, v) (b0)
|
||||
#else
|
||||
#define BETA_ZERO_CHECK(b0, v) (v)
|
||||
#endif
|
||||
|
||||
#define VEC_SIZE 4
|
||||
#define LWG_HEIGHT 4
|
||||
#define TILE_M 8
|
||||
@@ -143,14 +149,14 @@ __kernel void TEMPLATE(gemm_buffer_NN, Dtype)(
|
||||
int row6 = mad24(global_y, TILE_M, 6) < M ? 6 : border;
|
||||
int row7 = mad24(global_y, TILE_M, 7) < M ? 7 : border;
|
||||
|
||||
Dtype4 dot00 = (start_index != 0) ? vload4(0, dst_write0) : beta * vload4(0, dst_write0);
|
||||
Dtype4 dot01 = (start_index != 0) ? vload4(0, dst_write0 + 1 * N) : beta * vload4(0, dst_write0 + 1 * N);
|
||||
Dtype4 dot02 = (start_index != 0) ? vload4(0, dst_write0 + 2 * N) : beta * vload4(0, dst_write0 + 2 * N);
|
||||
Dtype4 dot03 = (start_index != 0) ? vload4(0, dst_write0 + 3 * N) : beta * vload4(0, dst_write0 + 3 * N);
|
||||
Dtype4 dot04 = (start_index != 0) ? vload4(0, dst_write0 + 4 * N) : beta * vload4(0, dst_write0 + 4 * N);
|
||||
Dtype4 dot05 = (start_index != 0) ? vload4(0, dst_write0 + 5 * N) : beta * vload4(0, dst_write0 + 5 * N);
|
||||
Dtype4 dot06 = (start_index != 0) ? vload4(0, dst_write0 + 6 * N) : beta * vload4(0, dst_write0 + 6 * N);
|
||||
Dtype4 dot07 = (start_index != 0) ? vload4(0, dst_write0 + 7 * N) : beta * vload4(0, dst_write0 + 7 * N);
|
||||
Dtype4 dot00 = (start_index != 0) ? vload4(0, dst_write0) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0));
|
||||
Dtype4 dot01 = (start_index != 0) ? vload4(0, dst_write0 + 1 * N) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0 + 1 * N));
|
||||
Dtype4 dot02 = (start_index != 0) ? vload4(0, dst_write0 + 2 * N) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0 + 2 * N));
|
||||
Dtype4 dot03 = (start_index != 0) ? vload4(0, dst_write0 + 3 * N) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0 + 3 * N));
|
||||
Dtype4 dot04 = (start_index != 0) ? vload4(0, dst_write0 + 4 * N) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0 + 4 * N));
|
||||
Dtype4 dot05 = (start_index != 0) ? vload4(0, dst_write0 + 5 * N) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0 + 5 * N));
|
||||
Dtype4 dot06 = (start_index != 0) ? vload4(0, dst_write0 + 6 * N) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0 + 6 * N));
|
||||
Dtype4 dot07 = (start_index != 0) ? vload4(0, dst_write0 + 7 * N) : BETA_ZERO_CHECK((Dtype4)0, beta * vload4(0, dst_write0 + 7 * N));
|
||||
|
||||
int end_index = min(start_index + 256, K);
|
||||
int w = start_index;
|
||||
@@ -579,7 +585,7 @@ __kernel void TEMPLATE(gemm_buffer_NT, Dtype)(
|
||||
output = (local_x == 5) ? _dot.s5 : output; \
|
||||
output = (local_x == 6) ? _dot.s6 : output; \
|
||||
output = (local_x == 7) ? _dot.s7 : output; \
|
||||
dst_write0[0] = mad(output, alpha, beta * dst_write0[0]); \
|
||||
dst_write0[0] = BETA_ZERO_CHECK(alpha * output, mad(output, alpha, beta * dst_write0[0])); \
|
||||
dst_write0 += N;
|
||||
|
||||
if(global_x < N && global_y * 8 < M) {
|
||||
@@ -765,7 +771,7 @@ __kernel void TEMPLATE(gemm_buffer_NT, Dtype)(
|
||||
output = (local_x == 5) ? _dot.s5 : output; \
|
||||
output = (local_x == 6) ? _dot.s6 : output; \
|
||||
output = (local_x == 7) ? _dot.s7 : output; \
|
||||
dst_write0[0] = mad(output, alpha, beta * dst_write0[0]); \
|
||||
dst_write0[0] = BETA_ZERO_CHECK(alpha * output, mad(output, alpha, beta * dst_write0[0])); \
|
||||
dst_write0 += N;
|
||||
|
||||
if(global_x < N && global_y * 8 < M) {
|
||||
@@ -819,8 +825,9 @@ void TEMPLATE(gemm_buffer_NT_M_2_edgerows,Dtype)(
|
||||
const Dtype4 b1 = {srca_read1[i*4], srca_read1[(i*4+1)], srca_read1[(i*4+2)], srca_read1[(i*4+3)]};
|
||||
#pragma unroll
|
||||
for(int j = 0; j < rows; ++j) {
|
||||
dot0[j] += b0 * vload4(i, srcb_read + j * K);
|
||||
dot1[j] += b1 * vload4(i, srcb_read + j * K);
|
||||
Dtype4 a = vload4(i, srcb_read + j * K);
|
||||
dot0[j] += b0 * a;
|
||||
dot1[j] += b1 * a;
|
||||
}
|
||||
|
||||
i += get_local_size(0);
|
||||
@@ -859,11 +866,19 @@ void TEMPLATE(gemm_buffer_NT_M_2_edgerows,Dtype)(
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if(lid == 0) {
|
||||
#pragma unroll
|
||||
for(int j = 0; j < rows; ++j) {
|
||||
dstc0[(x_gid * 4 + j)] = alpha * work_each0[j] + beta * dstc0[(x_gid * 4 + j)];
|
||||
dstc1[(x_gid * 4 + j)] = alpha * work_each1[j] + beta * dstc1[(x_gid * 4 + j)];
|
||||
#ifdef ZERO_BETA
|
||||
Dtype a0 = alpha * work_each0[j];
|
||||
Dtype a1 = alpha * work_each1[j];
|
||||
#else
|
||||
Dtype a0 = alpha * work_each0[j] + beta * dstc0[(x_gid * 4 + j)];
|
||||
Dtype a1 = alpha * work_each1[j] + beta * dstc1[(x_gid * 4 + j)];
|
||||
#endif
|
||||
dstc0[(x_gid * 4 + j)] = a0;
|
||||
dstc1[(x_gid * 4 + j)] = a1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -952,9 +967,15 @@ __kernel void TEMPLATE(gemm_buffer_NT_M_2,Dtype)(
|
||||
}
|
||||
}
|
||||
|
||||
if(lid == 0) {
|
||||
if(lid == 0)
|
||||
{
|
||||
#ifdef ZERO_BETA
|
||||
dstc0[x_gid] = alpha * work0[0];
|
||||
dstc1[x_gid] = alpha * work1[0];
|
||||
#else
|
||||
dstc0[x_gid] = alpha * work0[0] + beta * dstc0[x_gid];
|
||||
dstc1[x_gid] = alpha * work1[0] + beta * dstc1[x_gid];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1058,10 +1079,17 @@ void TEMPLATE(gemm_buffer_NT_M_4_edgerows,Dtype)(
|
||||
if(lid == 0) {
|
||||
#pragma unroll
|
||||
for(int j = 0; j < rows; ++j) {
|
||||
#ifdef ZERO_BETA
|
||||
dstc0[(x_gid * 4 + j)] = alpha * work_each0[j];
|
||||
dstc1[(x_gid * 4 + j)] = alpha * work_each1[j];
|
||||
dstc2[(x_gid * 4 + j)] = alpha * work_each2[j];
|
||||
dstc3[(x_gid * 4 + j)] = alpha * work_each3[j];
|
||||
#else
|
||||
dstc0[(x_gid * 4 + j)] = alpha * work_each0[j] + beta * dstc0[(x_gid * 4 + j)];
|
||||
dstc1[(x_gid * 4 + j)] = alpha * work_each1[j] + beta * dstc1[(x_gid * 4 + j)];
|
||||
dstc2[(x_gid * 4 + j)] = alpha * work_each2[j] + beta * dstc2[(x_gid * 4 + j)];
|
||||
dstc3[(x_gid * 4 + j)] = alpha * work_each3[j] + beta * dstc3[(x_gid * 4 + j)];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1179,10 +1207,17 @@ __kernel void TEMPLATE(gemm_buffer_NT_M_4,Dtype)(
|
||||
}
|
||||
|
||||
if(lid == 0) {
|
||||
#ifdef ZERO_BETA
|
||||
dstc0[x_gid] = alpha * work0[0];
|
||||
dstc1[x_gid] = alpha * work1[0];
|
||||
dstc2[x_gid] = alpha * work2[0];
|
||||
dstc3[x_gid] = alpha * work3[0];
|
||||
#else
|
||||
dstc0[x_gid] = alpha * work0[0] + beta * dstc0[x_gid];
|
||||
dstc1[x_gid] = alpha * work1[0] + beta * dstc1[x_gid];
|
||||
dstc2[x_gid] = alpha * work2[0] + beta * dstc2[x_gid];
|
||||
dstc3[x_gid] = alpha * work3[0] + beta * dstc3[x_gid];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1320,6 +1355,16 @@ __kernel void TEMPLATE(gemm_buffer_NT_M_8,Dtype)(
|
||||
}
|
||||
|
||||
if(lid == 0) {
|
||||
#ifdef ZERO_BETA
|
||||
dstc0[x_gid] = alpha * work0[0];
|
||||
dstc1[x_gid] = alpha * work1[0];
|
||||
dstc2[x_gid] = alpha * work2[0];
|
||||
dstc3[x_gid] = alpha * work3[0];
|
||||
dstc4[x_gid] = alpha * work4[0];
|
||||
dstc5[x_gid] = alpha * work5[0];
|
||||
dstc6[x_gid] = alpha * work6[0];
|
||||
dstc7[x_gid] = alpha * work7[0];
|
||||
#else
|
||||
dstc0[x_gid] = alpha * work0[0] + beta * dstc0[x_gid];
|
||||
dstc1[x_gid] = alpha * work1[0] + beta * dstc1[x_gid];
|
||||
dstc2[x_gid] = alpha * work2[0] + beta * dstc2[x_gid];
|
||||
@@ -1328,6 +1373,7 @@ __kernel void TEMPLATE(gemm_buffer_NT_M_8,Dtype)(
|
||||
dstc5[x_gid] = alpha * work5[0] + beta * dstc5[x_gid];
|
||||
dstc6[x_gid] = alpha * work6[0] + beta * dstc6[x_gid];
|
||||
dstc7[x_gid] = alpha * work7[0] + beta * dstc7[x_gid];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#undef SLM_SIZE
|
||||
|
||||
@@ -389,7 +389,7 @@ Pin parsePin(const std::string &name)
|
||||
{
|
||||
Pin pin(name);
|
||||
|
||||
size_t delimiter_pos = name.find_first_of(":");
|
||||
size_t delimiter_pos = name.find_first_of(':');
|
||||
if (delimiter_pos != std::string::npos)
|
||||
{
|
||||
pin.name = name.substr(0, delimiter_pos);
|
||||
@@ -1962,6 +1962,9 @@ void TFImporter::populateNet(Net dstNet)
|
||||
if (hasLayerAttr(layer, "align_corners"))
|
||||
layerParams.set("align_corners", getLayerAttr(layer, "align_corners").b());
|
||||
|
||||
if (hasLayerAttr(layer, "half_pixel_centers"))
|
||||
layerParams.set("half_pixel_centers", getLayerAttr(layer, "half_pixel_centers").b());
|
||||
|
||||
int id = dstNet.addLayer(name, "Resize", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
|
||||
@@ -101,6 +101,9 @@ public:
|
||||
TEST_P(DNNTestNetwork, AlexNet)
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_MEMORY_1GB);
|
||||
if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE);
|
||||
|
||||
processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt",
|
||||
Size(227, 227), "prob",
|
||||
target == DNN_TARGET_OPENCL ? "dnn/halide_scheduler_opencl_alexnet.yml" :
|
||||
@@ -114,6 +117,9 @@ TEST_P(DNNTestNetwork, ResNet_50)
|
||||
(target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB),
|
||||
CV_TEST_TAG_DEBUG_LONG
|
||||
);
|
||||
if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE);
|
||||
|
||||
processNet("dnn/ResNet-50-model.caffemodel", "dnn/ResNet-50-deploy.prototxt",
|
||||
Size(224, 224), "prob",
|
||||
target == DNN_TARGET_OPENCL ? "dnn/halide_scheduler_opencl_resnet_50.yml" :
|
||||
@@ -123,6 +129,9 @@ TEST_P(DNNTestNetwork, ResNet_50)
|
||||
|
||||
TEST_P(DNNTestNetwork, SqueezeNet_v1_1)
|
||||
{
|
||||
if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE);
|
||||
|
||||
processNet("dnn/squeezenet_v1.1.caffemodel", "dnn/squeezenet_v1.1.prototxt",
|
||||
Size(227, 227), "prob",
|
||||
target == DNN_TARGET_OPENCL ? "dnn/halide_scheduler_opencl_squeezenet_v1_1.yml" :
|
||||
@@ -133,6 +142,9 @@ TEST_P(DNNTestNetwork, SqueezeNet_v1_1)
|
||||
TEST_P(DNNTestNetwork, GoogLeNet)
|
||||
{
|
||||
applyTestTag(target == DNN_TARGET_CPU ? "" : CV_TEST_TAG_MEMORY_512MB);
|
||||
if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE);
|
||||
|
||||
processNet("dnn/bvlc_googlenet.caffemodel", "dnn/bvlc_googlenet.prototxt",
|
||||
Size(224, 224), "prob");
|
||||
expectNoFallbacksFromIE(net);
|
||||
@@ -141,6 +153,9 @@ TEST_P(DNNTestNetwork, GoogLeNet)
|
||||
TEST_P(DNNTestNetwork, Inception_5h)
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_MEMORY_512MB);
|
||||
if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE);
|
||||
|
||||
double l1 = default_l1, lInf = default_lInf;
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && (target == DNN_TARGET_CPU || target == DNN_TARGET_OPENCL))
|
||||
{
|
||||
@@ -157,6 +172,9 @@ TEST_P(DNNTestNetwork, Inception_5h)
|
||||
TEST_P(DNNTestNetwork, ENet)
|
||||
{
|
||||
applyTestTag(target == DNN_TARGET_CPU ? "" : CV_TEST_TAG_MEMORY_512MB);
|
||||
if (backend == DNN_BACKEND_HALIDE) // Realization contains wrong number of Images (1) for realizing pipeline with 2 outputs
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_HALIDE);
|
||||
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
|
||||
@@ -625,7 +625,7 @@ TEST_P(Test_Darknet_nets, YOLOv4_tiny)
|
||||
target == DNN_TARGET_CPU ? CV_TEST_TAG_MEMORY_512MB : CV_TEST_TAG_MEMORY_1GB
|
||||
);
|
||||
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_EQ(2021010000) // nGraph compilation failure
|
||||
#if defined(INF_ENGINE_RELEASE) && INF_ENGINE_VER_MAJOR_GE(2021010000) // nGraph compilation failure
|
||||
if (target == DNN_TARGET_MYRIAD)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_VERSION);
|
||||
#endif
|
||||
|
||||
@@ -518,7 +518,12 @@ TEST_P(Test_ONNX_layers, Broadcast)
|
||||
|
||||
TEST_P(Test_ONNX_layers, DynamicResize)
|
||||
{
|
||||
testONNXModels("dynamic_resize", npy, 0, 0, false, true, 2);
|
||||
testONNXModels("dynamic_resize_9", npy, 0, 0, false, true, 2);
|
||||
testONNXModels("dynamic_resize_10", npy, 0, 0, false, true, 2);
|
||||
testONNXModels("dynamic_resize_11", npy, 0, 0, false, true, 2);
|
||||
testONNXModels("dynamic_resize_scale_9", npy, 0, 0, false, true, 2);
|
||||
testONNXModels("dynamic_resize_scale_10", npy, 0, 0, false, true, 2);
|
||||
testONNXModels("dynamic_resize_scale_11", npy, 0, 0, false, true, 2);
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Div)
|
||||
@@ -742,6 +747,84 @@ TEST_P(Test_ONNX_layers, DynamicAxes)
|
||||
testONNXModels("maxpooling_sigmoid_dynamic_axes");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, MaxPool1d)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||
}
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
}
|
||||
testONNXModels("maxpooling_1d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, MaxPoolSigmoid1d)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||
}
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
}
|
||||
testONNXModels("maxpooling_sigmoid_1d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, MaxPool1d_Twise)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||
}
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
}
|
||||
testONNXModels("two_maxpooling_1d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, AvePool1d)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||
}
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
}
|
||||
testONNXModels("average_pooling_1d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, PoolConv1d)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||
}
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
}
|
||||
testONNXModels("pool_conv_1d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, ConvResizePool1d)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||
}
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
{
|
||||
if (target == DNN_TARGET_MYRIAD) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD, CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
}
|
||||
testONNXModels("conv_resize_pool_1d");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Test_ONNX_layers, dnnBackendsAndTargets());
|
||||
|
||||
class Test_ONNX_nets : public Test_ONNX_layers
|
||||
|
||||
@@ -81,12 +81,12 @@ class Test_TensorFlow_layers : public DNNTestLayer
|
||||
{
|
||||
public:
|
||||
void runTensorFlowNet(const std::string& prefix, bool hasText = false,
|
||||
double l1 = 0.0, double lInf = 0.0, bool memoryLoad = false)
|
||||
double l1 = 0.0, double lInf = 0.0, bool memoryLoad = false, const std::string& groupPrefix = "")
|
||||
{
|
||||
std::string netPath = path(prefix + "_net.pb");
|
||||
std::string netConfig = (hasText ? path(prefix + "_net.pbtxt") : "");
|
||||
std::string netPath = path(prefix + groupPrefix + "_net.pb");
|
||||
std::string netConfig = (hasText ? path(prefix + groupPrefix + "_net.pbtxt") : "");
|
||||
std::string inpPath = path(prefix + "_in.npy");
|
||||
std::string outPath = path(prefix + "_out.npy");
|
||||
std::string outPath = path(prefix + groupPrefix + "_out.npy");
|
||||
|
||||
cv::Mat input = blobFromNPY(inpPath);
|
||||
cv::Mat ref = blobFromNPY(outPath);
|
||||
@@ -920,6 +920,19 @@ TEST_P(Test_TensorFlow_layers, resize_nearest_neighbor)
|
||||
runTensorFlowNet("keras_upsampling2d");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, resize_nearest_neighbor_align_corners)
|
||||
{
|
||||
runTensorFlowNet("resize_nearest_neighbor", false, 0.0, 0.0, false, "_align_corners");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, resize_nearest_neighbor_half_pixel)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
|
||||
runTensorFlowNet("resize_nearest_neighbor", false, 0.0, 0.0, false, "_half_pixel");
|
||||
}
|
||||
|
||||
TEST_P(Test_TensorFlow_layers, fused_resize_conv)
|
||||
{
|
||||
runTensorFlowNet("fused_resize_conv");
|
||||
@@ -975,10 +988,53 @@ TEST_P(Test_TensorFlow_layers, keras_mobilenet_head)
|
||||
runTensorFlowNet("keras_learning_phase");
|
||||
}
|
||||
|
||||
// TF case: align_corners=False, half_pixel_centers=False
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear)
|
||||
{
|
||||
runTensorFlowNet("resize_bilinear");
|
||||
}
|
||||
|
||||
// TF case: align_corners=True, half_pixel_centers=False
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear_align_corners)
|
||||
{
|
||||
runTensorFlowNet("resize_bilinear",
|
||||
false, 0.0, 0.0, false, // default parameters
|
||||
"_align_corners");
|
||||
}
|
||||
|
||||
// TF case: align_corners=False, half_pixel_centers=True
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear_half_pixel)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
|
||||
runTensorFlowNet("resize_bilinear", false, 0.0, 0.0, false, "_half_pixel");
|
||||
}
|
||||
|
||||
// TF case: align_corners=False, half_pixel_centers=False
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear_factor)
|
||||
{
|
||||
runTensorFlowNet("resize_bilinear_factor");
|
||||
}
|
||||
|
||||
// TF case: align_corners=False, half_pixel_centers=True
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear_factor_half_pixel)
|
||||
{
|
||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||
|
||||
runTensorFlowNet("resize_bilinear_factor", false, 0.0, 0.0, false, "_half_pixel");
|
||||
}
|
||||
|
||||
// TF case: align_corners=True, half_pixel_centers=False
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear_factor_align_corners)
|
||||
{
|
||||
runTensorFlowNet("resize_bilinear_factor", false, 0.0, 0.0, false, "_align_corners");
|
||||
}
|
||||
|
||||
// TF case: align_corners=False, half_pixel_centers=False
|
||||
TEST_P(Test_TensorFlow_layers, resize_bilinear_down)
|
||||
{
|
||||
runTensorFlowNet("resize_bilinear_down");
|
||||
}
|
||||
|
||||
|
||||
@@ -1025,15 +1025,20 @@ void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask,
|
||||
Mat imagePyramid, maskPyramid;
|
||||
UMat uimagePyramid, ulayerInfo;
|
||||
|
||||
int level_dy = image.rows + border*2;
|
||||
Point level_ofs(0,0);
|
||||
Size bufSize((cvRound(image.cols/getScale(0, firstLevel, scaleFactor)) + border*2 + 15) & -16, 0);
|
||||
float level0_inv_scale = 1.0f / getScale(0, firstLevel, scaleFactor);
|
||||
size_t level0_width = (size_t)cvRound(image.cols * level0_inv_scale);
|
||||
size_t level0_height = (size_t)cvRound(image.rows * level0_inv_scale);
|
||||
Size bufSize((int)alignSize(level0_width + border*2, 16), 0); // TODO change alignment to 64
|
||||
|
||||
int level_dy = (int)level0_height + border*2;
|
||||
Point level_ofs(0, 0);
|
||||
|
||||
for( level = 0; level < nLevels; level++ )
|
||||
{
|
||||
float scale = getScale(level, firstLevel, scaleFactor);
|
||||
layerScale[level] = scale;
|
||||
Size sz(cvRound(image.cols/scale), cvRound(image.rows/scale));
|
||||
float inv_scale = 1.0f / scale;
|
||||
Size sz(cvRound(image.cols * inv_scale), cvRound(image.rows * inv_scale));
|
||||
Size wholeSize(sz.width + border*2, sz.height + border*2);
|
||||
if( level_ofs.x + wholeSize.width > bufSize.width )
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ TEST(Features2D_ORB, _1996)
|
||||
ASSERT_EQ(0, roiViolations);
|
||||
}
|
||||
|
||||
TEST(Features2D_ORB, crash)
|
||||
TEST(Features2D_ORB, crash_5031)
|
||||
{
|
||||
cv::Mat image = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3);
|
||||
|
||||
@@ -123,4 +123,23 @@ TEST(Features2D_ORB, crash)
|
||||
ASSERT_NO_THROW(orb->compute(image, keypoints, descriptors));
|
||||
}
|
||||
|
||||
|
||||
TEST(Features2D_ORB, regression_16197)
|
||||
{
|
||||
Mat img(Size(72, 72), CV_8UC1, Scalar::all(0));
|
||||
Ptr<ORB> orbPtr = ORB::create();
|
||||
orbPtr->setNLevels(5);
|
||||
orbPtr->setFirstLevel(3);
|
||||
orbPtr->setScaleFactor(1.8);
|
||||
orbPtr->setPatchSize(8);
|
||||
orbPtr->setEdgeThreshold(8);
|
||||
|
||||
std::vector<KeyPoint> kps;
|
||||
Mat fv;
|
||||
|
||||
// exception in debug mode, crash in release
|
||||
ASSERT_NO_THROW(orbPtr->detectAndCompute(img, noArray(), kps, fv));
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -82,7 +82,7 @@ struct index_creator
|
||||
nnIndex = new LshIndex<Distance>(dataset, params, distance);
|
||||
break;
|
||||
default:
|
||||
throw FLANNException("Unknown index type");
|
||||
FLANN_THROW(cv::Error::StsBadArg, "Unknown index type");
|
||||
}
|
||||
|
||||
return nnIndex;
|
||||
@@ -111,7 +111,7 @@ struct index_creator<False,VectorSpace,Distance>
|
||||
nnIndex = new LshIndex<Distance>(dataset, params, distance);
|
||||
break;
|
||||
default:
|
||||
throw FLANNException("Unknown index type");
|
||||
FLANN_THROW(cv::Error::StsBadArg, "Unknown index type");
|
||||
}
|
||||
|
||||
return nnIndex;
|
||||
@@ -140,7 +140,7 @@ struct index_creator<False,False,Distance>
|
||||
nnIndex = new LshIndex<Distance>(dataset, params, distance);
|
||||
break;
|
||||
default:
|
||||
throw FLANNException("Unknown index type");
|
||||
FLANN_THROW(cv::Error::StsBadArg, "Unknown index type");
|
||||
}
|
||||
|
||||
return nnIndex;
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "general.h"
|
||||
#include "nn_index.h"
|
||||
#include "ground_truth.h"
|
||||
#include "index_testing.h"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user