mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 23:33:05 +04:00
Compare commits
228 Commits
| 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 | |||
| 301c078d19 | |||
| 9485113923 | |||
| 4f3130f562 | |||
| a783bf4a93 | |||
| f7e8dc770a | |||
| 34909d97b4 | |||
| 474a67231c | |||
| b645fc10a3 | |||
| 8e1e08ee38 | |||
| d266fee8bb | |||
| 8357e865bc | |||
| fe9a8ebea2 | |||
| 2b558a3787 | |||
| 72d06080c6 | |||
| 3cdf926454 | |||
| 025a9647af | |||
| 32e7ef8a3d | |||
| 2b82f8f12c | |||
| 3a184ae677 | |||
| 564d1a0f79 | |||
| 05c011e842 | |||
| 61144f935e | |||
| d23435baac | |||
| 41c2669476 | |||
| ed3591ed1f | |||
| 5dae278652 | |||
| 30d91e8ed6 | |||
| c69417149d | |||
| 08271e5591 | |||
| a104e7c593 | |||
| c3e7a23da5 | |||
| 083edb201c | |||
| df7bf9a048 | |||
| bed5debca6 | |||
| caa1658a4c | |||
| bb5b628cce | |||
| 127a44f2b9 | |||
| ad71a1633c | |||
| fb69620a27 | |||
| a6e15b2f57 | |||
| 39ecd96b09 | |||
| 21a8d9569d | |||
| 24d60da553 | |||
| 039795b405 | |||
| 716450ceb5 | |||
| 97eaddd93b | |||
| e3ce0fbee3 | |||
| 364702b1c9 | |||
| 0f7b2eb79f | |||
| 4a0760719e | |||
| d011383a3d | |||
| 0ec94630d4 | |||
| c71f2714c6 | |||
| 71462d9f99 | |||
| 61a8cf8ba7 | |||
| 8869d3212f | |||
| aac7c5465b | |||
| 22ee5c0c4d | |||
| 7f3d8de26f | |||
| 331b73c8e4 | |||
| 9138c98b25 | |||
| 456af21d8b | |||
| c82417697a | |||
| bd19f991a5 | |||
| e87ba1d317 | |||
| f9d1f5196a | |||
| b5717f82a0 | |||
| bc6a70c689 | |||
| 1fb6c6e6e5 | |||
| d2dbc9d7a0 | |||
| 8c4d415412 | |||
| 25163eb008 | |||
| 96e8b83d41 | |||
| de93782fab | |||
| 7ed82aea38 | |||
| da555a2c9b | |||
| dfb9832a25 |
Vendored
+2
-2
@@ -27,7 +27,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(carotene_objs OBJECT
|
add_library(carotene_objs OBJECT EXCLUDE_FROM_ALL
|
||||||
${carotene_headers}
|
${carotene_headers}
|
||||||
${carotene_sources}
|
${carotene_sources}
|
||||||
)
|
)
|
||||||
@@ -41,4 +41,4 @@ if(WITH_NEON)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# we add dummy file to fix XCode build
|
# we add dummy file to fix XCode build
|
||||||
add_library(carotene STATIC EXCLUDE_FROM_ALL "$<TARGET_OBJECTS:carotene_objs>" "${CAROTENE_SOURCE_DIR}/dummy.cpp")
|
add_library(carotene STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} "$<TARGET_OBJECTS:carotene_objs>" "${CAROTENE_SOURCE_DIR}/dummy.cpp")
|
||||||
|
|||||||
Vendored
+2
-2
@@ -14,7 +14,7 @@ if(NOT DEFINED CPUFEATURES_SOURCES)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(${CPUFEATURES_INCLUDE_DIRS})
|
include_directories(${CPUFEATURES_INCLUDE_DIRS})
|
||||||
add_library(${OPENCV_CPUFEATURES_TARGET_NAME} STATIC ${CPUFEATURES_SOURCES})
|
add_library(${OPENCV_CPUFEATURES_TARGET_NAME} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${CPUFEATURES_SOURCES})
|
||||||
|
|
||||||
set_target_properties(${OPENCV_CPUFEATURES_TARGET_NAME}
|
set_target_properties(${OPENCV_CPUFEATURES_TARGET_NAME}
|
||||||
PROPERTIES OUTPUT_NAME cpufeatures
|
PROPERTIES OUTPUT_NAME cpufeatures
|
||||||
@@ -29,7 +29,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${OPENCV_CPUFEATURES_TARGET_NAME} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${OPENCV_CPUFEATURES_TARGET_NAME} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(cpufeatures LICENSE README.md)
|
ocv_install_3rdparty_licenses(cpufeatures LICENSE README.md)
|
||||||
|
|||||||
Vendored
+2
-2
@@ -17,7 +17,7 @@ file(GLOB lib_hdrs ${IPP_IW_PATH}/include/*.h ${IPP_IW_PATH}/include/iw/*.h ${IP
|
|||||||
# Define the library target:
|
# Define the library target:
|
||||||
# ----------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
add_library(${IPP_IW_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs})
|
add_library(${IPP_IW_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs} ${lib_hdrs})
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
if(CV_GCC OR CV_CLANG OR CV_ICC)
|
if(CV_GCC OR CV_CLANG OR CV_ICC)
|
||||||
@@ -41,5 +41,5 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${IPP_IW_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${IPP_IW_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
Vendored
+2
-2
@@ -37,7 +37,7 @@ set(ITT_SRCS
|
|||||||
src/ittnotify/jitprofiling.c
|
src/ittnotify/jitprofiling.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(${ITT_LIBRARY} STATIC ${ITT_SRCS} ${ITT_PUBLIC_HDRS} ${ITT_PRIVATE_HDRS})
|
add_library(${ITT_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${ITT_SRCS} ${ITT_PUBLIC_HDRS} ${ITT_PRIVATE_HDRS})
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
if(HAVE_DL_LIBRARY)
|
if(HAVE_DL_LIBRARY)
|
||||||
@@ -60,7 +60,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${ITT_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${ITT_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(ittnotify src/ittnotify/LICENSE.BSD src/ittnotify/LICENSE.GPL)
|
ocv_install_3rdparty_licenses(ittnotify src/ittnotify/LICENSE.BSD src/ittnotify/LICENSE.GPL)
|
||||||
|
|||||||
Vendored
+2
-2
@@ -17,7 +17,7 @@ file(GLOB lib_ext_hdrs jasper/*.h)
|
|||||||
# Define the library target:
|
# Define the library target:
|
||||||
# ----------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
add_library(${JASPER_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs} ${lib_ext_hdrs})
|
add_library(${JASPER_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs} ${lib_hdrs} ${lib_ext_hdrs})
|
||||||
|
|
||||||
if(WIN32 AND NOT MINGW)
|
if(WIN32 AND NOT MINGW)
|
||||||
add_definitions(-DJAS_WIN_MSVC_BUILD)
|
add_definitions(-DJAS_WIN_MSVC_BUILD)
|
||||||
@@ -46,7 +46,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${JASPER_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${JASPER_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(jasper LICENSE README copyright)
|
ocv_install_3rdparty_licenses(jasper LICENSE README copyright)
|
||||||
|
|||||||
+4
-4
@@ -4,9 +4,9 @@ ocv_warnings_disable(CMAKE_C_FLAGS -Wunused-parameter -Wsign-compare -Wshorten-6
|
|||||||
|
|
||||||
set(VERSION_MAJOR 2)
|
set(VERSION_MAJOR 2)
|
||||||
set(VERSION_MINOR 0)
|
set(VERSION_MINOR 0)
|
||||||
set(VERSION_REVISION 5)
|
set(VERSION_REVISION 6)
|
||||||
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION})
|
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION})
|
||||||
set(LIBJPEG_TURBO_VERSION_NUMBER 2000005)
|
set(LIBJPEG_TURBO_VERSION_NUMBER 2000006)
|
||||||
|
|
||||||
string(TIMESTAMP BUILD "opencv-${OPENCV_VERSION}-libjpeg-turbo")
|
string(TIMESTAMP BUILD "opencv-${OPENCV_VERSION}-libjpeg-turbo")
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
@@ -106,7 +106,7 @@ set(JPEG_SOURCES ${JPEG_SOURCES} jsimd_none.c)
|
|||||||
|
|
||||||
ocv_list_add_prefix(JPEG_SOURCES src/)
|
ocv_list_add_prefix(JPEG_SOURCES src/)
|
||||||
|
|
||||||
add_library(${JPEG_LIBRARY} STATIC ${JPEG_SOURCES} ${SIMD_OBJS})
|
add_library(${JPEG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${JPEG_SOURCES} ${SIMD_OBJS})
|
||||||
|
|
||||||
set_target_properties(${JPEG_LIBRARY}
|
set_target_properties(${JPEG_LIBRARY}
|
||||||
PROPERTIES OUTPUT_NAME ${JPEG_LIBRARY}
|
PROPERTIES OUTPUT_NAME ${JPEG_LIBRARY}
|
||||||
@@ -121,7 +121,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${JPEG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${JPEG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(libjpeg-turbo README.md LICENSE.md README.ijg)
|
ocv_install_3rdparty_licenses(libjpeg-turbo README.md LICENSE.md README.ijg)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -91,7 +91,7 @@ best of our understanding.
|
|||||||
The Modified (3-clause) BSD License
|
The Modified (3-clause) BSD License
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
Copyright (C)2009-2019 D. R. Commander. All Rights Reserved.
|
Copyright (C)2009-2020 D. R. Commander. All Rights Reserved.
|
||||||
Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
|
Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|||||||
Vendored
+8
-14
@@ -223,12 +223,12 @@ https://www.iso.org/standard/54989.html and http://www.itu.int/rec/T-REC-T.871.
|
|||||||
A PDF file of the older JFIF 1.02 specification is available at
|
A PDF file of the older JFIF 1.02 specification is available at
|
||||||
http://www.w3.org/Graphics/JPEG/jfif3.pdf.
|
http://www.w3.org/Graphics/JPEG/jfif3.pdf.
|
||||||
|
|
||||||
The TIFF 6.0 file format specification can be obtained by FTP from
|
The TIFF 6.0 file format specification can be obtained from
|
||||||
ftp://ftp.sgi.com/graphics/tiff/TIFF6.ps.gz. The JPEG incorporation scheme
|
http://mirrors.ctan.org/graphics/tiff/TIFF6.ps.gz. The JPEG incorporation
|
||||||
found in the TIFF 6.0 spec of 3-June-92 has a number of serious problems.
|
scheme found in the TIFF 6.0 spec of 3-June-92 has a number of serious
|
||||||
IJG does not recommend use of the TIFF 6.0 design (TIFF Compression tag 6).
|
problems. IJG does not recommend use of the TIFF 6.0 design (TIFF Compression
|
||||||
Instead, we recommend the JPEG design proposed by TIFF Technical Note #2
|
tag 6). Instead, we recommend the JPEG design proposed by TIFF Technical Note
|
||||||
(Compression tag 7). Copies of this Note can be obtained from
|
#2 (Compression tag 7). Copies of this Note can be obtained from
|
||||||
http://www.ijg.org/files/. It is expected that the next revision
|
http://www.ijg.org/files/. It is expected that the next revision
|
||||||
of the TIFF spec will replace the 6.0 JPEG design with the Note's design.
|
of the TIFF spec will replace the 6.0 JPEG design with the Note's design.
|
||||||
Although IJG's own code does not support TIFF/JPEG, the free libtiff library
|
Although IJG's own code does not support TIFF/JPEG, the free libtiff library
|
||||||
@@ -243,14 +243,8 @@ The most recent released version can always be found there in
|
|||||||
directory "files".
|
directory "files".
|
||||||
|
|
||||||
The JPEG FAQ (Frequently Asked Questions) article is a source of some
|
The JPEG FAQ (Frequently Asked Questions) article is a source of some
|
||||||
general information about JPEG.
|
general information about JPEG. It is available at
|
||||||
It is available on the World Wide Web at http://www.faqs.org/faqs/jpeg-faq/
|
http://www.faqs.org/faqs/jpeg-faq.
|
||||||
and other news.answers archive sites, including the official news.answers
|
|
||||||
archive at rtfm.mit.edu: ftp://rtfm.mit.edu/pub/usenet/news.answers/jpeg-faq/.
|
|
||||||
If you don't have Web or FTP access, send e-mail to mail-server@rtfm.mit.edu
|
|
||||||
with body
|
|
||||||
send usenet/news.answers/jpeg-faq/part1
|
|
||||||
send usenet/news.answers/jpeg-faq/part2
|
|
||||||
|
|
||||||
|
|
||||||
FILE FORMAT COMPATIBILITY
|
FILE FORMAT COMPATIBILITY
|
||||||
|
|||||||
Vendored
+11
-10
@@ -2,7 +2,7 @@ Background
|
|||||||
==========
|
==========
|
||||||
|
|
||||||
libjpeg-turbo is a JPEG image codec that uses SIMD instructions to accelerate
|
libjpeg-turbo is a JPEG image codec that uses SIMD instructions to accelerate
|
||||||
baseline JPEG compression and decompression on x86, x86-64, ARM, PowerPC, and
|
baseline JPEG compression and decompression on x86, x86-64, Arm, PowerPC, and
|
||||||
MIPS systems, as well as progressive JPEG compression on x86 and x86-64
|
MIPS systems, as well as progressive JPEG compression on x86 and x86-64
|
||||||
systems. On such systems, libjpeg-turbo is generally 2-6x as fast as libjpeg,
|
systems. On such systems, libjpeg-turbo is generally 2-6x as fast as libjpeg,
|
||||||
all else being equal. On other types of systems, libjpeg-turbo can still
|
all else being equal. On other types of systems, libjpeg-turbo can still
|
||||||
@@ -179,8 +179,8 @@ supported and which aren't.
|
|||||||
|
|
||||||
NOTE: As of this writing, extensive research has been conducted into the
|
NOTE: As of this writing, extensive research has been conducted into the
|
||||||
usefulness of DCT scaling as a means of data reduction and SmartScale as a
|
usefulness of DCT scaling as a means of data reduction and SmartScale as a
|
||||||
means of quality improvement. The reader is invited to peruse the research at
|
means of quality improvement. Readers are invited to peruse the research at
|
||||||
<http://www.libjpeg-turbo.org/About/SmartScale> and draw his/her own conclusions,
|
<http://www.libjpeg-turbo.org/About/SmartScale> and draw their own conclusions,
|
||||||
but it is the general belief of our project that these features have not
|
but it is the general belief of our project that these features have not
|
||||||
demonstrated sufficient usefulness to justify inclusion in libjpeg-turbo.
|
demonstrated sufficient usefulness to justify inclusion in libjpeg-turbo.
|
||||||
|
|
||||||
@@ -287,12 +287,13 @@ following reasons:
|
|||||||
(and slightly faster) floating point IDCT algorithm introduced in libjpeg
|
(and slightly faster) floating point IDCT algorithm introduced in libjpeg
|
||||||
v8a as opposed to the algorithm used in libjpeg v6b. It should be noted,
|
v8a as opposed to the algorithm used in libjpeg v6b. It should be noted,
|
||||||
however, that this algorithm basically brings the accuracy of the floating
|
however, that this algorithm basically brings the accuracy of the floating
|
||||||
point IDCT in line with the accuracy of the slow integer IDCT. The floating
|
point IDCT in line with the accuracy of the accurate integer IDCT. The
|
||||||
point DCT/IDCT algorithms are mainly a legacy feature, and they do not
|
floating point DCT/IDCT algorithms are mainly a legacy feature, and they do
|
||||||
produce significantly more accuracy than the slow integer algorithms (to put
|
not produce significantly more accuracy than the accurate integer algorithms
|
||||||
numbers on this, the typical difference in PNSR between the two algorithms
|
(to put numbers on this, the typical difference in PNSR between the two
|
||||||
is less than 0.10 dB, whereas changing the quality level by 1 in the upper
|
algorithms is less than 0.10 dB, whereas changing the quality level by 1 in
|
||||||
range of the quality scale is typically more like a 1.0 dB difference.)
|
the upper range of the quality scale is typically more like a 1.0 dB
|
||||||
|
difference.)
|
||||||
|
|
||||||
- If the floating point algorithms in libjpeg-turbo are not implemented using
|
- If the floating point algorithms in libjpeg-turbo are not implemented using
|
||||||
SIMD instructions on a particular platform, then the accuracy of the
|
SIMD instructions on a particular platform, then the accuracy of the
|
||||||
@@ -340,7 +341,7 @@ The algorithm used by the SIMD-accelerated quantization function cannot produce
|
|||||||
correct results whenever the fast integer forward DCT is used along with a JPEG
|
correct results whenever the fast integer forward DCT is used along with a JPEG
|
||||||
quality of 98-100. Thus, libjpeg-turbo must use the non-SIMD quantization
|
quality of 98-100. Thus, libjpeg-turbo must use the non-SIMD quantization
|
||||||
function in those cases. This causes performance to drop by as much as 40%.
|
function in those cases. This causes performance to drop by as much as 40%.
|
||||||
It is therefore strongly advised that you use the slow integer forward DCT
|
It is therefore strongly advised that you use the accurate integer forward DCT
|
||||||
whenever encoding images with a JPEG quality of 98 or higher.
|
whenever encoding images with a JPEG quality of 98 or higher.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
@@ -34,10 +34,10 @@
|
|||||||
* memory footprint by 64k, which is important for some mobile applications
|
* memory footprint by 64k, which is important for some mobile applications
|
||||||
* that create many isolated instances of libjpeg-turbo (web browsers, for
|
* that create many isolated instances of libjpeg-turbo (web browsers, for
|
||||||
* instance.) This may improve performance on some mobile platforms as well.
|
* instance.) This may improve performance on some mobile platforms as well.
|
||||||
* This feature is enabled by default only on ARM processors, because some x86
|
* This feature is enabled by default only on Arm processors, because some x86
|
||||||
* chips have a slow implementation of bsr, and the use of clz/bsr cannot be
|
* chips have a slow implementation of bsr, and the use of clz/bsr cannot be
|
||||||
* shown to have a significant performance impact even on the x86 chips that
|
* shown to have a significant performance impact even on the x86 chips that
|
||||||
* have a fast implementation of it. When building for ARMv6, you can
|
* have a fast implementation of it. When building for Armv6, you can
|
||||||
* explicitly disable the use of clz/bsr by adding -mthumb to the compiler
|
* explicitly disable the use of clz/bsr by adding -mthumb to the compiler
|
||||||
* flags (this defines __thumb__).
|
* flags (this defines __thumb__).
|
||||||
*/
|
*/
|
||||||
|
|||||||
Vendored
+4
-1
@@ -1,8 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* jcinit.c
|
* jcinit.c
|
||||||
*
|
*
|
||||||
|
* This file was part of the Independent JPEG Group's software:
|
||||||
* Copyright (C) 1991-1997, Thomas G. Lane.
|
* Copyright (C) 1991-1997, Thomas G. Lane.
|
||||||
* This file is part of the Independent JPEG Group's software.
|
* libjpeg-turbo Modifications:
|
||||||
|
* Copyright (C) 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
#define JPEG_INTERNALS
|
#define JPEG_INTERNALS
|
||||||
#include "jinclude.h"
|
#include "jinclude.h"
|
||||||
#include "jpeglib.h"
|
#include "jpeglib.h"
|
||||||
|
#include "jpegcomp.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Vendored
+2
-2
@@ -43,10 +43,10 @@
|
|||||||
* memory footprint by 64k, which is important for some mobile applications
|
* memory footprint by 64k, which is important for some mobile applications
|
||||||
* that create many isolated instances of libjpeg-turbo (web browsers, for
|
* that create many isolated instances of libjpeg-turbo (web browsers, for
|
||||||
* instance.) This may improve performance on some mobile platforms as well.
|
* instance.) This may improve performance on some mobile platforms as well.
|
||||||
* This feature is enabled by default only on ARM processors, because some x86
|
* This feature is enabled by default only on Arm processors, because some x86
|
||||||
* chips have a slow implementation of bsr, and the use of clz/bsr cannot be
|
* chips have a slow implementation of bsr, and the use of clz/bsr cannot be
|
||||||
* shown to have a significant performance impact even on the x86 chips that
|
* shown to have a significant performance impact even on the x86 chips that
|
||||||
* have a fast implementation of it. When building for ARMv6, you can
|
* have a fast implementation of it. When building for Armv6, you can
|
||||||
* explicitly disable the use of clz/bsr by adding -mthumb to the compiler
|
* explicitly disable the use of clz/bsr by adding -mthumb to the compiler
|
||||||
* flags (this defines __thumb__).
|
* flags (this defines __thumb__).
|
||||||
*/
|
*/
|
||||||
|
|||||||
Vendored
+3
-2
@@ -4,8 +4,8 @@
|
|||||||
* This file was part of the Independent JPEG Group's software:
|
* This file was part of the Independent JPEG Group's software:
|
||||||
* Copyright (C) 1995-1998, Thomas G. Lane.
|
* Copyright (C) 1995-1998, Thomas G. Lane.
|
||||||
* Modified 2000-2009 by Guido Vollbeding.
|
* Modified 2000-2009 by Guido Vollbeding.
|
||||||
* It was modified by The libjpeg-turbo Project to include only code relevant
|
* libjpeg-turbo Modifications:
|
||||||
* to libjpeg-turbo.
|
* Copyright (C) 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#define JPEG_INTERNALS
|
#define JPEG_INTERNALS
|
||||||
#include "jinclude.h"
|
#include "jinclude.h"
|
||||||
#include "jpeglib.h"
|
#include "jpeglib.h"
|
||||||
|
#include "jpegcomp.h"
|
||||||
|
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
|
|||||||
+36
-9
@@ -4,7 +4,7 @@
|
|||||||
* This file was part of the Independent JPEG Group's software:
|
* This file was part of the Independent JPEG Group's software:
|
||||||
* Copyright (C) 1994-1996, Thomas G. Lane.
|
* Copyright (C) 1994-1996, Thomas G. Lane.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2010, 2015-2018, D. R. Commander.
|
* Copyright (C) 2010, 2015-2018, 2020, D. R. Commander.
|
||||||
* Copyright (C) 2015, Google, Inc.
|
* Copyright (C) 2015, Google, Inc.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
#include "jinclude.h"
|
#include "jinclude.h"
|
||||||
#include "jdmainct.h"
|
#include "jdmainct.h"
|
||||||
#include "jdcoefct.h"
|
#include "jdcoefct.h"
|
||||||
|
#include "jdmaster.h"
|
||||||
|
#include "jdmerge.h"
|
||||||
#include "jdsample.h"
|
#include "jdsample.h"
|
||||||
#include "jmemsys.h"
|
#include "jmemsys.h"
|
||||||
|
|
||||||
@@ -316,6 +318,8 @@ LOCAL(void)
|
|||||||
read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
||||||
{
|
{
|
||||||
JDIMENSION n;
|
JDIMENSION n;
|
||||||
|
my_master_ptr master = (my_master_ptr)cinfo->master;
|
||||||
|
JSAMPARRAY scanlines = NULL;
|
||||||
void (*color_convert) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
void (*color_convert) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
||||||
JDIMENSION input_row, JSAMPARRAY output_buf,
|
JDIMENSION input_row, JSAMPARRAY output_buf,
|
||||||
int num_rows) = NULL;
|
int num_rows) = NULL;
|
||||||
@@ -332,8 +336,13 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
|||||||
cinfo->cquantize->color_quantize = noop_quantize;
|
cinfo->cquantize->color_quantize = noop_quantize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (master->using_merged_upsample && cinfo->max_v_samp_factor == 2) {
|
||||||
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
|
scanlines = &upsample->spare_row;
|
||||||
|
}
|
||||||
|
|
||||||
for (n = 0; n < num_lines; n++)
|
for (n = 0; n < num_lines; n++)
|
||||||
jpeg_read_scanlines(cinfo, NULL, 1);
|
jpeg_read_scanlines(cinfo, scanlines, 1);
|
||||||
|
|
||||||
if (color_convert)
|
if (color_convert)
|
||||||
cinfo->cconvert->color_convert = color_convert;
|
cinfo->cconvert->color_convert = color_convert;
|
||||||
@@ -353,6 +362,12 @@ increment_simple_rowgroup_ctr(j_decompress_ptr cinfo, JDIMENSION rows)
|
|||||||
{
|
{
|
||||||
JDIMENSION rows_left;
|
JDIMENSION rows_left;
|
||||||
my_main_ptr main_ptr = (my_main_ptr)cinfo->main;
|
my_main_ptr main_ptr = (my_main_ptr)cinfo->main;
|
||||||
|
my_master_ptr master = (my_master_ptr)cinfo->master;
|
||||||
|
|
||||||
|
if (master->using_merged_upsample && cinfo->max_v_samp_factor == 2) {
|
||||||
|
read_and_discard_scanlines(cinfo, rows);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Increment the counter to the next row group after the skipped rows. */
|
/* Increment the counter to the next row group after the skipped rows. */
|
||||||
main_ptr->rowgroup_ctr += rows / cinfo->max_v_samp_factor;
|
main_ptr->rowgroup_ctr += rows / cinfo->max_v_samp_factor;
|
||||||
@@ -382,21 +397,27 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
|||||||
{
|
{
|
||||||
my_main_ptr main_ptr = (my_main_ptr)cinfo->main;
|
my_main_ptr main_ptr = (my_main_ptr)cinfo->main;
|
||||||
my_coef_ptr coef = (my_coef_ptr)cinfo->coef;
|
my_coef_ptr coef = (my_coef_ptr)cinfo->coef;
|
||||||
|
my_master_ptr master = (my_master_ptr)cinfo->master;
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
||||||
JDIMENSION i, x;
|
JDIMENSION i, x;
|
||||||
int y;
|
int y;
|
||||||
JDIMENSION lines_per_iMCU_row, lines_left_in_iMCU_row, lines_after_iMCU_row;
|
JDIMENSION lines_per_iMCU_row, lines_left_in_iMCU_row, lines_after_iMCU_row;
|
||||||
JDIMENSION lines_to_skip, lines_to_read;
|
JDIMENSION lines_to_skip, lines_to_read;
|
||||||
|
|
||||||
|
/* Two-pass color quantization is not supported. */
|
||||||
|
if (cinfo->quantize_colors && cinfo->two_pass_quantize)
|
||||||
|
ERREXIT(cinfo, JERR_NOTIMPL);
|
||||||
|
|
||||||
if (cinfo->global_state != DSTATE_SCANNING)
|
if (cinfo->global_state != DSTATE_SCANNING)
|
||||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||||
|
|
||||||
/* Do not skip past the bottom of the image. */
|
/* Do not skip past the bottom of the image. */
|
||||||
if (cinfo->output_scanline + num_lines >= cinfo->output_height) {
|
if (cinfo->output_scanline + num_lines >= cinfo->output_height) {
|
||||||
|
num_lines = cinfo->output_height - cinfo->output_scanline;
|
||||||
cinfo->output_scanline = cinfo->output_height;
|
cinfo->output_scanline = cinfo->output_height;
|
||||||
(*cinfo->inputctl->finish_input_pass) (cinfo);
|
(*cinfo->inputctl->finish_input_pass) (cinfo);
|
||||||
cinfo->inputctl->eoi_reached = TRUE;
|
cinfo->inputctl->eoi_reached = TRUE;
|
||||||
return cinfo->output_height - cinfo->output_scanline;
|
return num_lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_lines == 0)
|
if (num_lines == 0)
|
||||||
@@ -445,8 +466,10 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
|||||||
main_ptr->buffer_full = FALSE;
|
main_ptr->buffer_full = FALSE;
|
||||||
main_ptr->rowgroup_ctr = 0;
|
main_ptr->rowgroup_ctr = 0;
|
||||||
main_ptr->context_state = CTX_PREPARE_FOR_IMCU;
|
main_ptr->context_state = CTX_PREPARE_FOR_IMCU;
|
||||||
upsample->next_row_out = cinfo->max_v_samp_factor;
|
if (!master->using_merged_upsample) {
|
||||||
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
upsample->next_row_out = cinfo->max_v_samp_factor;
|
||||||
|
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Skipping is much simpler when context rows are not required. */
|
/* Skipping is much simpler when context rows are not required. */
|
||||||
@@ -458,8 +481,10 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
|||||||
cinfo->output_scanline += lines_left_in_iMCU_row;
|
cinfo->output_scanline += lines_left_in_iMCU_row;
|
||||||
main_ptr->buffer_full = FALSE;
|
main_ptr->buffer_full = FALSE;
|
||||||
main_ptr->rowgroup_ctr = 0;
|
main_ptr->rowgroup_ctr = 0;
|
||||||
upsample->next_row_out = cinfo->max_v_samp_factor;
|
if (!master->using_merged_upsample) {
|
||||||
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
upsample->next_row_out = cinfo->max_v_samp_factor;
|
||||||
|
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +519,8 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
|||||||
cinfo->output_iMCU_row += lines_to_skip / lines_per_iMCU_row;
|
cinfo->output_iMCU_row += lines_to_skip / lines_per_iMCU_row;
|
||||||
increment_simple_rowgroup_ctr(cinfo, lines_to_read);
|
increment_simple_rowgroup_ctr(cinfo, lines_to_read);
|
||||||
}
|
}
|
||||||
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
if (!master->using_merged_upsample)
|
||||||
|
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
||||||
return num_lines;
|
return num_lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,7 +561,8 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
|
|||||||
* bit odd, since "rows_to_go" seems to be redundantly keeping track of
|
* bit odd, since "rows_to_go" seems to be redundantly keeping track of
|
||||||
* output_scanline.
|
* output_scanline.
|
||||||
*/
|
*/
|
||||||
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
if (!master->using_merged_upsample)
|
||||||
|
upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
|
||||||
|
|
||||||
/* Always skip the requested number of lines. */
|
/* Always skip the requested number of lines. */
|
||||||
return num_lines;
|
return num_lines;
|
||||||
|
|||||||
+5
-3
@@ -6,7 +6,7 @@
|
|||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright 2009 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
* Copyright 2009 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
||||||
* Copyright (C) 2010, 2015-2016, D. R. Commander.
|
* Copyright (C) 2010, 2015-2016, D. R. Commander.
|
||||||
* Copyright (C) 2015, Google, Inc.
|
* Copyright (C) 2015, 2020, Google, Inc.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -495,11 +495,13 @@ decompress_smooth_data(j_decompress_ptr cinfo, JSAMPIMAGE output_buf)
|
|||||||
if (first_row && block_row == 0)
|
if (first_row && block_row == 0)
|
||||||
prev_block_row = buffer_ptr;
|
prev_block_row = buffer_ptr;
|
||||||
else
|
else
|
||||||
prev_block_row = buffer[block_row - 1];
|
prev_block_row = buffer[block_row - 1] +
|
||||||
|
cinfo->master->first_MCU_col[ci];
|
||||||
if (last_row && block_row == block_rows - 1)
|
if (last_row && block_row == block_rows - 1)
|
||||||
next_block_row = buffer_ptr;
|
next_block_row = buffer_ptr;
|
||||||
else
|
else
|
||||||
next_block_row = buffer[block_row + 1];
|
next_block_row = buffer[block_row + 1] +
|
||||||
|
cinfo->master->first_MCU_col[ci];
|
||||||
/* We fetch the surrounding DC values using a sliding-register approach.
|
/* We fetch the surrounding DC values using a sliding-register approach.
|
||||||
* Initialize all nine here so as to do the right thing on narrow pics.
|
* Initialize all nine here so as to do the right thing on narrow pics.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Vendored
+4
-5
@@ -571,11 +571,10 @@ ycck_cmyk_convert(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
* RGB565 conversion
|
* RGB565 conversion
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PACK_SHORT_565_LE(r, g, b) ((((r) << 8) & 0xF800) | \
|
#define PACK_SHORT_565_LE(r, g, b) \
|
||||||
(((g) << 3) & 0x7E0) | ((b) >> 3))
|
((((r) << 8) & 0xF800) | (((g) << 3) & 0x7E0) | ((b) >> 3))
|
||||||
#define PACK_SHORT_565_BE(r, g, b) (((r) & 0xF8) | ((g) >> 5) | \
|
#define PACK_SHORT_565_BE(r, g, b) \
|
||||||
(((g) << 11) & 0xE000) | \
|
(((r) & 0xF8) | ((g) >> 5) | (((g) << 11) & 0xE000) | (((b) << 5) & 0x1F00))
|
||||||
(((b) << 5) & 0x1F00))
|
|
||||||
|
|
||||||
#define PACK_TWO_PIXELS_LE(l, r) ((r << 16) | l)
|
#define PACK_TWO_PIXELS_LE(l, r) ((r << 16) | l)
|
||||||
#define PACK_TWO_PIXELS_BE(l, r) ((l << 16) | r)
|
#define PACK_TWO_PIXELS_BE(l, r) ((l << 16) | r)
|
||||||
|
|||||||
Vendored
+13
-42
@@ -5,7 +5,7 @@
|
|||||||
* Copyright (C) 1994-1996, Thomas G. Lane.
|
* Copyright (C) 1994-1996, Thomas G. Lane.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright 2009 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
* Copyright 2009 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
||||||
* Copyright (C) 2009, 2011, 2014-2015, D. R. Commander.
|
* Copyright (C) 2009, 2011, 2014-2015, 2020, D. R. Commander.
|
||||||
* Copyright (C) 2013, Linaro Limited.
|
* Copyright (C) 2013, Linaro Limited.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
@@ -40,41 +40,13 @@
|
|||||||
#define JPEG_INTERNALS
|
#define JPEG_INTERNALS
|
||||||
#include "jinclude.h"
|
#include "jinclude.h"
|
||||||
#include "jpeglib.h"
|
#include "jpeglib.h"
|
||||||
|
#include "jdmerge.h"
|
||||||
#include "jsimd.h"
|
#include "jsimd.h"
|
||||||
#include "jconfigint.h"
|
#include "jconfigint.h"
|
||||||
|
|
||||||
#ifdef UPSAMPLE_MERGING_SUPPORTED
|
#ifdef UPSAMPLE_MERGING_SUPPORTED
|
||||||
|
|
||||||
|
|
||||||
/* Private subobject */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
struct jpeg_upsampler pub; /* public fields */
|
|
||||||
|
|
||||||
/* Pointer to routine to do actual upsampling/conversion of one row group */
|
|
||||||
void (*upmethod) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|
||||||
JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf);
|
|
||||||
|
|
||||||
/* Private state for YCC->RGB conversion */
|
|
||||||
int *Cr_r_tab; /* => table for Cr to R conversion */
|
|
||||||
int *Cb_b_tab; /* => table for Cb to B conversion */
|
|
||||||
JLONG *Cr_g_tab; /* => table for Cr to G conversion */
|
|
||||||
JLONG *Cb_g_tab; /* => table for Cb to G conversion */
|
|
||||||
|
|
||||||
/* For 2:1 vertical sampling, we produce two output rows at a time.
|
|
||||||
* We need a "spare" row buffer to hold the second output row if the
|
|
||||||
* application provides just a one-row buffer; we also use the spare
|
|
||||||
* to discard the dummy last row if the image height is odd.
|
|
||||||
*/
|
|
||||||
JSAMPROW spare_row;
|
|
||||||
boolean spare_full; /* T if spare buffer is occupied */
|
|
||||||
|
|
||||||
JDIMENSION out_row_width; /* samples per output row */
|
|
||||||
JDIMENSION rows_to_go; /* counts rows remaining in image */
|
|
||||||
} my_upsampler;
|
|
||||||
|
|
||||||
typedef my_upsampler *my_upsample_ptr;
|
|
||||||
|
|
||||||
#define SCALEBITS 16 /* speediest right-shift on some machines */
|
#define SCALEBITS 16 /* speediest right-shift on some machines */
|
||||||
#define ONE_HALF ((JLONG)1 << (SCALEBITS - 1))
|
#define ONE_HALF ((JLONG)1 << (SCALEBITS - 1))
|
||||||
#define FIX(x) ((JLONG)((x) * (1L << SCALEBITS) + 0.5))
|
#define FIX(x) ((JLONG)((x) * (1L << SCALEBITS) + 0.5))
|
||||||
@@ -189,7 +161,7 @@ typedef my_upsampler *my_upsample_ptr;
|
|||||||
LOCAL(void)
|
LOCAL(void)
|
||||||
build_ycc_rgb_table(j_decompress_ptr cinfo)
|
build_ycc_rgb_table(j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
int i;
|
int i;
|
||||||
JLONG x;
|
JLONG x;
|
||||||
SHIFT_TEMPS
|
SHIFT_TEMPS
|
||||||
@@ -232,7 +204,7 @@ build_ycc_rgb_table(j_decompress_ptr cinfo)
|
|||||||
METHODDEF(void)
|
METHODDEF(void)
|
||||||
start_pass_merged_upsample(j_decompress_ptr cinfo)
|
start_pass_merged_upsample(j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
|
|
||||||
/* Mark the spare buffer empty */
|
/* Mark the spare buffer empty */
|
||||||
upsample->spare_full = FALSE;
|
upsample->spare_full = FALSE;
|
||||||
@@ -254,7 +226,7 @@ merged_2v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)
|
JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)
|
||||||
/* 2:1 vertical sampling case: may need a spare row. */
|
/* 2:1 vertical sampling case: may need a spare row. */
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
JSAMPROW work_ptrs[2];
|
JSAMPROW work_ptrs[2];
|
||||||
JDIMENSION num_rows; /* number of rows returned to caller */
|
JDIMENSION num_rows; /* number of rows returned to caller */
|
||||||
|
|
||||||
@@ -305,7 +277,7 @@ merged_1v_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)
|
JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)
|
||||||
/* 1:1 vertical sampling case: much easier, never need a spare row. */
|
/* 1:1 vertical sampling case: much easier, never need a spare row. */
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
|
|
||||||
/* Just do the upsampling. */
|
/* Just do the upsampling. */
|
||||||
(*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr,
|
(*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr,
|
||||||
@@ -420,11 +392,10 @@ h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
* RGB565 conversion
|
* RGB565 conversion
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PACK_SHORT_565_LE(r, g, b) ((((r) << 8) & 0xF800) | \
|
#define PACK_SHORT_565_LE(r, g, b) \
|
||||||
(((g) << 3) & 0x7E0) | ((b) >> 3))
|
((((r) << 8) & 0xF800) | (((g) << 3) & 0x7E0) | ((b) >> 3))
|
||||||
#define PACK_SHORT_565_BE(r, g, b) (((r) & 0xF8) | ((g) >> 5) | \
|
#define PACK_SHORT_565_BE(r, g, b) \
|
||||||
(((g) << 11) & 0xE000) | \
|
(((r) & 0xF8) | ((g) >> 5) | (((g) << 11) & 0xE000) | (((b) << 5) & 0x1F00))
|
||||||
(((b) << 5) & 0x1F00))
|
|
||||||
|
|
||||||
#define PACK_TWO_PIXELS_LE(l, r) ((r << 16) | l)
|
#define PACK_TWO_PIXELS_LE(l, r) ((r << 16) | l)
|
||||||
#define PACK_TWO_PIXELS_BE(l, r) ((l << 16) | r)
|
#define PACK_TWO_PIXELS_BE(l, r) ((l << 16) | r)
|
||||||
@@ -566,11 +537,11 @@ h2v2_merged_upsample_565D(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
GLOBAL(void)
|
GLOBAL(void)
|
||||||
jinit_merged_upsampler(j_decompress_ptr cinfo)
|
jinit_merged_upsampler(j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample;
|
my_merged_upsample_ptr upsample;
|
||||||
|
|
||||||
upsample = (my_upsample_ptr)
|
upsample = (my_merged_upsample_ptr)
|
||||||
(*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
|
(*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
|
||||||
sizeof(my_upsampler));
|
sizeof(my_merged_upsampler));
|
||||||
cinfo->upsample = (struct jpeg_upsampler *)upsample;
|
cinfo->upsample = (struct jpeg_upsampler *)upsample;
|
||||||
upsample->pub.start_pass = start_pass_merged_upsample;
|
upsample->pub.start_pass = start_pass_merged_upsample;
|
||||||
upsample->pub.need_context_rows = FALSE;
|
upsample->pub.need_context_rows = FALSE;
|
||||||
|
|||||||
Vendored
+47
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* jdmerge.h
|
||||||
|
*
|
||||||
|
* This file was part of the Independent JPEG Group's software:
|
||||||
|
* Copyright (C) 1994-1996, Thomas G. Lane.
|
||||||
|
* libjpeg-turbo Modifications:
|
||||||
|
* Copyright (C) 2020, D. R. Commander.
|
||||||
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define JPEG_INTERNALS
|
||||||
|
#include "jpeglib.h"
|
||||||
|
|
||||||
|
#ifdef UPSAMPLE_MERGING_SUPPORTED
|
||||||
|
|
||||||
|
|
||||||
|
/* Private subobject */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct jpeg_upsampler pub; /* public fields */
|
||||||
|
|
||||||
|
/* Pointer to routine to do actual upsampling/conversion of one row group */
|
||||||
|
void (*upmethod) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
||||||
|
JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf);
|
||||||
|
|
||||||
|
/* Private state for YCC->RGB conversion */
|
||||||
|
int *Cr_r_tab; /* => table for Cr to R conversion */
|
||||||
|
int *Cb_b_tab; /* => table for Cb to B conversion */
|
||||||
|
JLONG *Cr_g_tab; /* => table for Cr to G conversion */
|
||||||
|
JLONG *Cb_g_tab; /* => table for Cb to G conversion */
|
||||||
|
|
||||||
|
/* For 2:1 vertical sampling, we produce two output rows at a time.
|
||||||
|
* We need a "spare" row buffer to hold the second output row if the
|
||||||
|
* application provides just a one-row buffer; we also use the spare
|
||||||
|
* to discard the dummy last row if the image height is odd.
|
||||||
|
*/
|
||||||
|
JSAMPROW spare_row;
|
||||||
|
boolean spare_full; /* T if spare buffer is occupied */
|
||||||
|
|
||||||
|
JDIMENSION out_row_width; /* samples per output row */
|
||||||
|
JDIMENSION rows_to_go; /* counts rows remaining in image */
|
||||||
|
} my_merged_upsampler;
|
||||||
|
|
||||||
|
typedef my_merged_upsampler *my_merged_upsample_ptr;
|
||||||
|
|
||||||
|
#endif /* UPSAMPLE_MERGING_SUPPORTED */
|
||||||
+5
-5
@@ -5,7 +5,7 @@
|
|||||||
* Copyright (C) 1994-1996, Thomas G. Lane.
|
* Copyright (C) 1994-1996, Thomas G. Lane.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2013, Linaro Limited.
|
* Copyright (C) 2013, Linaro Limited.
|
||||||
* Copyright (C) 2014-2015, 2018, D. R. Commander.
|
* Copyright (C) 2014-2015, 2018, 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -19,7 +19,7 @@ h2v1_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
JDIMENSION in_row_group_ctr,
|
JDIMENSION in_row_group_ctr,
|
||||||
JSAMPARRAY output_buf)
|
JSAMPARRAY output_buf)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
register int y, cred, cgreen, cblue;
|
register int y, cred, cgreen, cblue;
|
||||||
int cb, cr;
|
int cb, cr;
|
||||||
register JSAMPROW outptr;
|
register JSAMPROW outptr;
|
||||||
@@ -90,7 +90,7 @@ h2v1_merged_upsample_565D_internal(j_decompress_ptr cinfo,
|
|||||||
JDIMENSION in_row_group_ctr,
|
JDIMENSION in_row_group_ctr,
|
||||||
JSAMPARRAY output_buf)
|
JSAMPARRAY output_buf)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
register int y, cred, cgreen, cblue;
|
register int y, cred, cgreen, cblue;
|
||||||
int cb, cr;
|
int cb, cr;
|
||||||
register JSAMPROW outptr;
|
register JSAMPROW outptr;
|
||||||
@@ -163,7 +163,7 @@ h2v2_merged_upsample_565_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
JDIMENSION in_row_group_ctr,
|
JDIMENSION in_row_group_ctr,
|
||||||
JSAMPARRAY output_buf)
|
JSAMPARRAY output_buf)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
register int y, cred, cgreen, cblue;
|
register int y, cred, cgreen, cblue;
|
||||||
int cb, cr;
|
int cb, cr;
|
||||||
register JSAMPROW outptr0, outptr1;
|
register JSAMPROW outptr0, outptr1;
|
||||||
@@ -259,7 +259,7 @@ h2v2_merged_upsample_565D_internal(j_decompress_ptr cinfo,
|
|||||||
JDIMENSION in_row_group_ctr,
|
JDIMENSION in_row_group_ctr,
|
||||||
JSAMPARRAY output_buf)
|
JSAMPARRAY output_buf)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
register int y, cred, cgreen, cblue;
|
register int y, cred, cgreen, cblue;
|
||||||
int cb, cr;
|
int cb, cr;
|
||||||
register JSAMPROW outptr0, outptr1;
|
register JSAMPROW outptr0, outptr1;
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
* This file was part of the Independent JPEG Group's software:
|
* This file was part of the Independent JPEG Group's software:
|
||||||
* Copyright (C) 1994-1996, Thomas G. Lane.
|
* Copyright (C) 1994-1996, Thomas G. Lane.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2011, 2015, D. R. Commander.
|
* Copyright (C) 2011, 2015, 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -25,7 +25,7 @@ h2v1_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
JDIMENSION in_row_group_ctr,
|
JDIMENSION in_row_group_ctr,
|
||||||
JSAMPARRAY output_buf)
|
JSAMPARRAY output_buf)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
register int y, cred, cgreen, cblue;
|
register int y, cred, cgreen, cblue;
|
||||||
int cb, cr;
|
int cb, cr;
|
||||||
register JSAMPROW outptr;
|
register JSAMPROW outptr;
|
||||||
@@ -97,7 +97,7 @@ h2v2_merged_upsample_internal(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
|
|||||||
JDIMENSION in_row_group_ctr,
|
JDIMENSION in_row_group_ctr,
|
||||||
JSAMPARRAY output_buf)
|
JSAMPARRAY output_buf)
|
||||||
{
|
{
|
||||||
my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
|
my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
|
||||||
register int y, cred, cgreen, cblue;
|
register int y, cred, cgreen, cblue;
|
||||||
int cb, cr;
|
int cb, cr;
|
||||||
register JSAMPROW outptr0, outptr1;
|
register JSAMPROW outptr0, outptr1;
|
||||||
|
|||||||
Vendored
+3
-2
@@ -3,8 +3,8 @@
|
|||||||
*
|
*
|
||||||
* This file was part of the Independent JPEG Group's software:
|
* This file was part of the Independent JPEG Group's software:
|
||||||
* Copyright (C) 1995-1997, Thomas G. Lane.
|
* Copyright (C) 1995-1997, Thomas G. Lane.
|
||||||
* It was modified by The libjpeg-turbo Project to include only code relevant
|
* libjpeg-turbo Modifications:
|
||||||
* to libjpeg-turbo.
|
* Copyright (C) 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
#define JPEG_INTERNALS
|
#define JPEG_INTERNALS
|
||||||
#include "jinclude.h"
|
#include "jinclude.h"
|
||||||
#include "jpeglib.h"
|
#include "jpeglib.h"
|
||||||
|
#include "jpegcomp.h"
|
||||||
|
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
|
|||||||
+2
-2
@@ -4,11 +4,11 @@
|
|||||||
* This file was part of the Independent JPEG Group's software:
|
* This file was part of the Independent JPEG Group's software:
|
||||||
* Copyright (C) 1991-1996, Thomas G. Lane.
|
* Copyright (C) 1991-1996, Thomas G. Lane.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2015, D. R. Commander.
|
* Copyright (C) 2015, 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
* This file contains a slow-but-accurate integer implementation of the
|
* This file contains a slower but more accurate integer implementation of the
|
||||||
* forward DCT (Discrete Cosine Transform).
|
* forward DCT (Discrete Cosine Transform).
|
||||||
*
|
*
|
||||||
* A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
|
* A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT
|
||||||
|
|||||||
+2
-2
@@ -5,11 +5,11 @@
|
|||||||
* Copyright (C) 1991-1998, Thomas G. Lane.
|
* Copyright (C) 1991-1998, Thomas G. Lane.
|
||||||
* Modification developed 2002-2009 by Guido Vollbeding.
|
* Modification developed 2002-2009 by Guido Vollbeding.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2015, D. R. Commander.
|
* Copyright (C) 2015, 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
* This file contains a slow-but-accurate integer implementation of the
|
* This file contains a slower but more accurate integer implementation of the
|
||||||
* inverse DCT (Discrete Cosine Transform). In the IJG code, this routine
|
* inverse DCT (Discrete Cosine Transform). In the IJG code, this routine
|
||||||
* must also perform dequantization of the input coefficients.
|
* must also perform dequantization of the input coefficients.
|
||||||
*
|
*
|
||||||
|
|||||||
+4
-4
@@ -5,7 +5,7 @@
|
|||||||
* Copyright (C) 1991-1997, Thomas G. Lane.
|
* Copyright (C) 1991-1997, Thomas G. Lane.
|
||||||
* Modified 1997-2009 by Guido Vollbeding.
|
* Modified 1997-2009 by Guido Vollbeding.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2009, 2011, 2014-2015, 2018, D. R. Commander.
|
* Copyright (C) 2009, 2011, 2014-2015, 2018, 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -273,9 +273,9 @@ typedef int boolean;
|
|||||||
|
|
||||||
/* Capability options common to encoder and decoder: */
|
/* Capability options common to encoder and decoder: */
|
||||||
|
|
||||||
#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */
|
#define DCT_ISLOW_SUPPORTED /* accurate integer method */
|
||||||
#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */
|
#define DCT_IFAST_SUPPORTED /* less accurate int method [legacy feature] */
|
||||||
#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */
|
#define DCT_FLOAT_SUPPORTED /* floating-point method [legacy feature] */
|
||||||
|
|
||||||
/* Encoder capability options: */
|
/* Encoder capability options: */
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* jpegcomp.h
|
* jpegcomp.h
|
||||||
*
|
*
|
||||||
* Copyright (C) 2010, D. R. Commander.
|
* Copyright (C) 2010, 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#define _min_DCT_v_scaled_size min_DCT_v_scaled_size
|
#define _min_DCT_v_scaled_size min_DCT_v_scaled_size
|
||||||
#define _jpeg_width jpeg_width
|
#define _jpeg_width jpeg_width
|
||||||
#define _jpeg_height jpeg_height
|
#define _jpeg_height jpeg_height
|
||||||
|
#define JERR_ARITH_NOTIMPL JERR_NOT_COMPILED
|
||||||
#else
|
#else
|
||||||
#define _DCT_scaled_size DCT_scaled_size
|
#define _DCT_scaled_size DCT_scaled_size
|
||||||
#define _DCT_h_scaled_size DCT_scaled_size
|
#define _DCT_h_scaled_size DCT_scaled_size
|
||||||
|
|||||||
Vendored
+4
-4
@@ -5,7 +5,7 @@
|
|||||||
* Copyright (C) 1991-1998, Thomas G. Lane.
|
* Copyright (C) 1991-1998, Thomas G. Lane.
|
||||||
* Modified 2002-2009 by Guido Vollbeding.
|
* Modified 2002-2009 by Guido Vollbeding.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2009-2011, 2013-2014, 2016-2017, D. R. Commander.
|
* Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, D. R. Commander.
|
||||||
* Copyright (C) 2015, Google, Inc.
|
* Copyright (C) 2015, Google, Inc.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
@@ -244,9 +244,9 @@ typedef enum {
|
|||||||
/* DCT/IDCT algorithm options. */
|
/* DCT/IDCT algorithm options. */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
JDCT_ISLOW, /* slow but accurate integer algorithm */
|
JDCT_ISLOW, /* accurate integer method */
|
||||||
JDCT_IFAST, /* faster, less accurate integer method */
|
JDCT_IFAST, /* less accurate integer method [legacy feature] */
|
||||||
JDCT_FLOAT /* floating-point: accurate, fast on fast HW */
|
JDCT_FLOAT /* floating-point method [legacy feature] */
|
||||||
} J_DCT_METHOD;
|
} J_DCT_METHOD;
|
||||||
|
|
||||||
#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */
|
#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */
|
||||||
|
|||||||
Vendored
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
* This file was part of the Independent JPEG Group's software:
|
* This file was part of the Independent JPEG Group's software:
|
||||||
* Copyright (C) 1991-1996, Thomas G. Lane.
|
* Copyright (C) 1991-1996, Thomas G. Lane.
|
||||||
* libjpeg-turbo Modifications:
|
* libjpeg-turbo Modifications:
|
||||||
* Copyright (C) 2009, 2014-2015, D. R. Commander.
|
* Copyright (C) 2009, 2014-2015, 2020, D. R. Commander.
|
||||||
* For conditions of distribution and use, see the accompanying README.ijg
|
* For conditions of distribution and use, see the accompanying README.ijg
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
@@ -1145,7 +1145,7 @@ start_pass_2_quant(j_decompress_ptr cinfo, boolean is_pre_scan)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Only F-S dithering or no dithering is supported. */
|
/* Only F-S dithering or no dithering is supported. */
|
||||||
/* If user asks for ordered dither, give him F-S. */
|
/* If user asks for ordered dither, give them F-S. */
|
||||||
if (cinfo->dither_mode != JDITHER_NONE)
|
if (cinfo->dither_mode != JDITHER_NONE)
|
||||||
cinfo->dither_mode = JDITHER_FS;
|
cinfo->dither_mode = JDITHER_FS;
|
||||||
|
|
||||||
@@ -1263,7 +1263,7 @@ jinit_2pass_quantizer(j_decompress_ptr cinfo)
|
|||||||
cquantize->sv_colormap = NULL;
|
cquantize->sv_colormap = NULL;
|
||||||
|
|
||||||
/* Only F-S dithering or no dithering is supported. */
|
/* Only F-S dithering or no dithering is supported. */
|
||||||
/* If user asks for ordered dither, give him F-S. */
|
/* If user asks for ordered dither, give them F-S. */
|
||||||
if (cinfo->dither_mode != JDITHER_NONE)
|
if (cinfo->dither_mode != JDITHER_NONE)
|
||||||
cinfo->dither_mode = JDITHER_FS;
|
cinfo->dither_mode = JDITHER_FS;
|
||||||
|
|
||||||
|
|||||||
+8
-6
@@ -30,23 +30,25 @@
|
|||||||
* NOTE: It is our convention to place the authors in the following order:
|
* NOTE: It is our convention to place the authors in the following order:
|
||||||
* - libjpeg-turbo authors (2009-) in descending order of the date of their
|
* - libjpeg-turbo authors (2009-) in descending order of the date of their
|
||||||
* most recent contribution to the project, then in ascending order of the
|
* most recent contribution to the project, then in ascending order of the
|
||||||
* date of their first contribution to the project
|
* date of their first contribution to the project, then in alphabetical
|
||||||
|
* order
|
||||||
* - Upstream authors in descending order of the date of the first inclusion of
|
* - Upstream authors in descending order of the date of the first inclusion of
|
||||||
* their code
|
* their code
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define JCOPYRIGHT \
|
#define JCOPYRIGHT \
|
||||||
"Copyright (C) 2009-2020 D. R. Commander\n" \
|
"Copyright (C) 2009-2020 D. R. Commander\n" \
|
||||||
"Copyright (C) 2011-2016 Siarhei Siamashka\n" \
|
"Copyright (C) 2015, 2020 Google, Inc.\n" \
|
||||||
|
"Copyright (C) 2019 Arm Limited\n" \
|
||||||
"Copyright (C) 2015-2016, 2018 Matthieu Darbois\n" \
|
"Copyright (C) 2015-2016, 2018 Matthieu Darbois\n" \
|
||||||
|
"Copyright (C) 2011-2016 Siarhei Siamashka\n" \
|
||||||
"Copyright (C) 2015 Intel Corporation\n" \
|
"Copyright (C) 2015 Intel Corporation\n" \
|
||||||
"Copyright (C) 2015 Google, Inc.\n" \
|
"Copyright (C) 2013-2014 Linaro Limited\n" \
|
||||||
"Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \
|
"Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \
|
||||||
"Copyright (C) 2013 Linaro Limited\n" \
|
"Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \
|
||||||
"Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \
|
"Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \
|
||||||
"Copyright (C) 2009 Pierre Ossman for Cendio AB\n" \
|
|
||||||
"Copyright (C) 1999-2006 MIYASAKA Masaru\n" \
|
"Copyright (C) 1999-2006 MIYASAKA Masaru\n" \
|
||||||
"Copyright (C) 1991-2016 Thomas G. Lane, Guido Vollbeding"
|
"Copyright (C) 1991-2017 Thomas G. Lane, Guido Vollbeding"
|
||||||
|
|
||||||
#define JCOPYRIGHT_SHORT \
|
#define JCOPYRIGHT_SHORT \
|
||||||
"Copyright (C) 1991-2020 The libjpeg-turbo Project and many others"
|
"Copyright (C) 1991-2020 The libjpeg-turbo Project and many others"
|
||||||
|
|||||||
Vendored
+2
-2
@@ -19,7 +19,7 @@ endif()
|
|||||||
# Define the library target:
|
# Define the library target:
|
||||||
# ----------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
add_library(${JPEG_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs})
|
add_library(${JPEG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs} ${lib_hdrs})
|
||||||
|
|
||||||
if(CV_GCC OR CV_CLANG)
|
if(CV_GCC OR CV_CLANG)
|
||||||
set_source_files_properties(jcdctmgr.c PROPERTIES COMPILE_FLAGS "-O1")
|
set_source_files_properties(jcdctmgr.c PROPERTIES COMPILE_FLAGS "-O1")
|
||||||
@@ -42,7 +42,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${JPEG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${JPEG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(libjpeg README)
|
ocv_install_3rdparty_licenses(libjpeg README)
|
||||||
|
|||||||
Vendored
+2
-2
@@ -74,7 +74,7 @@ if(MSVC)
|
|||||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
|
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
||||||
add_library(${PNG_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs})
|
add_library(${PNG_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs} ${lib_hdrs})
|
||||||
target_link_libraries(${PNG_LIBRARY} ${ZLIB_LIBRARIES})
|
target_link_libraries(${PNG_LIBRARY} ${ZLIB_LIBRARIES})
|
||||||
|
|
||||||
ocv_warnings_disable(CMAKE_C_FLAGS -Wundef -Wcast-align -Wimplicit-fallthrough -Wunused-parameter -Wsign-compare)
|
ocv_warnings_disable(CMAKE_C_FLAGS -Wundef -Wcast-align -Wimplicit-fallthrough -Wunused-parameter -Wsign-compare)
|
||||||
@@ -92,7 +92,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${PNG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${PNG_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(libpng LICENSE README)
|
ocv_install_3rdparty_licenses(libpng LICENSE README)
|
||||||
|
|||||||
Vendored
+2
-2
@@ -462,7 +462,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4456 /wd4457 /wd4312) # vs2015
|
|||||||
|
|
||||||
ocv_warnings_disable(CMAKE_C_FLAGS /wd4267 /wd4244 /wd4018 /wd4311 /wd4312)
|
ocv_warnings_disable(CMAKE_C_FLAGS /wd4267 /wd4244 /wd4018 /wd4311 /wd4312)
|
||||||
|
|
||||||
add_library(${TIFF_LIBRARY} STATIC ${lib_srcs})
|
add_library(${TIFF_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs})
|
||||||
target_link_libraries(${TIFF_LIBRARY} ${ZLIB_LIBRARIES})
|
target_link_libraries(${TIFF_LIBRARY} ${ZLIB_LIBRARIES})
|
||||||
|
|
||||||
set_target_properties(${TIFF_LIBRARY}
|
set_target_properties(${TIFF_LIBRARY}
|
||||||
@@ -479,7 +479,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${TIFF_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${TIFF_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(libtiff COPYRIGHT)
|
ocv_install_3rdparty_licenses(libtiff COPYRIGHT)
|
||||||
|
|||||||
Vendored
+2
-2
@@ -34,7 +34,7 @@ endif()
|
|||||||
|
|
||||||
add_definitions(-DWEBP_USE_THREAD)
|
add_definitions(-DWEBP_USE_THREAD)
|
||||||
|
|
||||||
add_library(${WEBP_LIBRARY} STATIC ${lib_srcs} ${lib_hdrs})
|
add_library(${WEBP_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs} ${lib_hdrs})
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
target_link_libraries(${WEBP_LIBRARY} ${CPUFEATURES_LIBRARIES})
|
target_link_libraries(${WEBP_LIBRARY} ${CPUFEATURES_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
@@ -59,6 +59,6 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${WEBP_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${WEBP_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
-2
@@ -125,7 +125,7 @@ if(MSVC AND CV_ICC)
|
|||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Qrestrict")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Qrestrict")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(IlmImf STATIC ${lib_hdrs} ${lib_srcs})
|
add_library(IlmImf STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_hdrs} ${lib_srcs})
|
||||||
target_link_libraries(IlmImf ${ZLIB_LIBRARIES})
|
target_link_libraries(IlmImf ${ZLIB_LIBRARIES})
|
||||||
|
|
||||||
set_target_properties(IlmImf
|
set_target_properties(IlmImf
|
||||||
@@ -142,7 +142,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(IlmImf EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(IlmImf EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(openexr LICENSE AUTHORS.ilmbase AUTHORS.openexr)
|
ocv_install_3rdparty_licenses(openexr LICENSE AUTHORS.ilmbase AUTHORS.openexr)
|
||||||
|
|||||||
Vendored
+8
-2
@@ -140,7 +140,8 @@ append_if_exist(Protobuf_SRCS
|
|||||||
${PROTOBUF_ROOT}/src/google/protobuf/wrappers.pb.cc
|
${PROTOBUF_ROOT}/src/google/protobuf/wrappers.pb.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(libprotobuf STATIC ${Protobuf_SRCS})
|
include_directories(BEFORE "${PROTOBUF_ROOT}/src") # ensure using if own headers: https://github.com/opencv/opencv/issues/13328
|
||||||
|
add_library(libprotobuf STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${Protobuf_SRCS})
|
||||||
target_include_directories(libprotobuf SYSTEM PUBLIC $<BUILD_INTERFACE:${PROTOBUF_ROOT}/src>)
|
target_include_directories(libprotobuf SYSTEM PUBLIC $<BUILD_INTERFACE:${PROTOBUF_ROOT}/src>)
|
||||||
set_target_properties(libprotobuf
|
set_target_properties(libprotobuf
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
@@ -152,11 +153,16 @@ set_target_properties(libprotobuf
|
|||||||
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
|
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")
|
get_protobuf_version(Protobuf_VERSION "${PROTOBUF_ROOT}/src")
|
||||||
set(Protobuf_VERSION ${Protobuf_VERSION} CACHE INTERNAL "" FORCE)
|
set(Protobuf_VERSION ${Protobuf_VERSION} CACHE INTERNAL "" FORCE)
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(libprotobuf EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(libprotobuf EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(protobuf LICENSE README.md)
|
ocv_install_3rdparty_licenses(protobuf LICENSE README.md)
|
||||||
|
|||||||
Vendored
+2
-2
@@ -8,7 +8,7 @@ ocv_include_directories(${CURR_INCLUDE_DIR})
|
|||||||
file(GLOB_RECURSE quirc_headers RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "include/*.h")
|
file(GLOB_RECURSE quirc_headers RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "include/*.h")
|
||||||
file(GLOB_RECURSE quirc_sources RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "src/*.c")
|
file(GLOB_RECURSE quirc_sources RELATIVE "${CMAKE_CURRENT_LIST_DIR}" "src/*.c")
|
||||||
|
|
||||||
add_library(${PROJECT_NAME} STATIC ${quirc_headers} ${quirc_sources})
|
add_library(${PROJECT_NAME} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${quirc_headers} ${quirc_sources})
|
||||||
ocv_warnings_disable(CMAKE_C_FLAGS -Wunused-variable -Wshadow)
|
ocv_warnings_disable(CMAKE_C_FLAGS -Wunused-variable -Wshadow)
|
||||||
|
|
||||||
set_target_properties(${PROJECT_NAME}
|
set_target_properties(${PROJECT_NAME}
|
||||||
@@ -24,7 +24,7 @@ if(ENABLE_SOLUTION_FOLDERS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BUILD_SHARED_LIBS)
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
ocv_install_target(${PROJECT_NAME} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
ocv_install_target(${PROJECT_NAME} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(${PROJECT_NAME} LICENSE)
|
ocv_install_3rdparty_licenses(${PROJECT_NAME} LICENSE)
|
||||||
|
|||||||
Vendored
+2
-1
@@ -108,7 +108,7 @@ set(tbb_version_file "version_string.ver")
|
|||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${tbb_version_file}.cmakein" "${CMAKE_CURRENT_BINARY_DIR}/${tbb_version_file}" @ONLY)
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${tbb_version_file}.cmakein" "${CMAKE_CURRENT_BINARY_DIR}/${tbb_version_file}" @ONLY)
|
||||||
list(APPEND TBB_SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/${tbb_version_file}")
|
list(APPEND TBB_SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/${tbb_version_file}")
|
||||||
|
|
||||||
add_library(tbb ${TBB_SOURCE_FILES})
|
add_library(tbb ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${TBB_SOURCE_FILES})
|
||||||
target_compile_definitions(tbb PUBLIC
|
target_compile_definitions(tbb PUBLIC
|
||||||
TBB_USE_GCC_BUILTINS=1
|
TBB_USE_GCC_BUILTINS=1
|
||||||
__TBB_GCC_BUILTIN_ATOMICS_PRESENT=1
|
__TBB_GCC_BUILTIN_ATOMICS_PRESENT=1
|
||||||
@@ -165,6 +165,7 @@ ocv_install_target(tbb EXPORT OpenCVModules
|
|||||||
RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs
|
RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs
|
||||||
LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs
|
LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs
|
||||||
ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev
|
ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev
|
||||||
|
OPTIONAL
|
||||||
)
|
)
|
||||||
|
|
||||||
ocv_install_3rdparty_licenses(tbb "${tbb_src_dir}/LICENSE" "${tbb_src_dir}/README")
|
ocv_install_3rdparty_licenses(tbb "${tbb_src_dir}/LICENSE" "${tbb_src_dir}/README")
|
||||||
|
|||||||
Vendored
+1
-1
@@ -76,7 +76,7 @@ set(ZLIB_SRCS
|
|||||||
zutil.c
|
zutil.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(${ZLIB_LIBRARY} STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
|
add_library(${ZLIB_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
|
||||||
set_target_properties(${ZLIB_LIBRARY} PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
|
set_target_properties(${ZLIB_LIBRARY} PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
|
||||||
|
|
||||||
ocv_warnings_disable(CMAKE_C_FLAGS -Wshorten-64-to-32 -Wattributes -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshift-negative-value
|
ocv_warnings_disable(CMAKE_C_FLAGS -Wshorten-64-to-32 -Wattributes -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshift-negative-value
|
||||||
|
|||||||
+6
-1
@@ -32,6 +32,11 @@ endif()
|
|||||||
#
|
#
|
||||||
# Configure CMake policies
|
# Configure CMake policies
|
||||||
#
|
#
|
||||||
|
|
||||||
|
if(POLICY CMP0025)
|
||||||
|
cmake_policy(SET CMP0025 NEW) # CMAKE_CXX_COMPILER_ID=AppleClang
|
||||||
|
endif()
|
||||||
|
|
||||||
if(POLICY CMP0026)
|
if(POLICY CMP0026)
|
||||||
cmake_policy(SET CMP0026 NEW)
|
cmake_policy(SET CMP0026 NEW)
|
||||||
endif()
|
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_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_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)
|
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_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) )
|
OCV_OPTION(ENABLE_VFPV3 "Enable VFPv3-D32 instructions" OFF IF (CV_GCC OR CV_CLANG) AND (ARM OR AARCH64 OR IOS) )
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ bool calib::parametersController::loadFromFile(const std::string &inputFileName)
|
|||||||
|
|
||||||
if(!reader.isOpened()) {
|
if(!reader.isOpened()) {
|
||||||
std::cerr << "Warning: Unable to open " << inputFileName <<
|
std::cerr << "Warning: Unable to open " << inputFileName <<
|
||||||
" Applicatioin stated with default advanced parameters" << std::endl;
|
" Application started with default advanced parameters" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ if(CV_GCC OR CV_CLANG)
|
|||||||
add_extra_compiler_option(-Wshadow)
|
add_extra_compiler_option(-Wshadow)
|
||||||
add_extra_compiler_option(-Wsign-promo)
|
add_extra_compiler_option(-Wsign-promo)
|
||||||
add_extra_compiler_option(-Wuninitialized)
|
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))
|
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)
|
add_extra_compiler_option(-Wno-psabi)
|
||||||
endif()
|
endif()
|
||||||
@@ -151,7 +150,7 @@ if(CV_GCC OR CV_CLANG)
|
|||||||
if(CV_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
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+
|
add_extra_compiler_option(-Wno-missing-field-initializers) # GCC 4.x emits warnings about {}, fixed in GCC 5+
|
||||||
endif()
|
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-enum-enum-conversion)
|
||||||
add_extra_compiler_option(-Wno-deprecated-anon-enum-enum-conversion)
|
add_extra_compiler_option(-Wno-deprecated-anon-enum-enum-conversion)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -135,9 +135,9 @@ endif()
|
|||||||
|
|
||||||
if(INF_ENGINE_TARGET)
|
if(INF_ENGINE_TARGET)
|
||||||
if(NOT INF_ENGINE_RELEASE)
|
if(NOT INF_ENGINE_RELEASE)
|
||||||
message(WARNING "InferenceEngine version has not been set, 2020.4 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()
|
endif()
|
||||||
set(INF_ENGINE_RELEASE "2020040000" 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
|
set_target_properties(${INF_ENGINE_TARGET} PROPERTIES
|
||||||
INTERFACE_COMPILE_DEFINITIONS "HAVE_INF_ENGINE=1;INF_ENGINE_RELEASE=${INF_ENGINE_RELEASE}"
|
INTERFACE_COMPILE_DEFINITIONS "HAVE_INF_ENGINE=1;INF_ENGINE_RELEASE=${INF_ENGINE_RELEASE}"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
if(BUILD_ZLIB)
|
if(BUILD_ZLIB)
|
||||||
ocv_clear_vars(ZLIB_FOUND)
|
ocv_clear_vars(ZLIB_FOUND)
|
||||||
else()
|
else()
|
||||||
|
ocv_clear_internal_cache_vars(ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
|
||||||
find_package(ZLIB "${MIN_VER_ZLIB}")
|
find_package(ZLIB "${MIN_VER_ZLIB}")
|
||||||
if(ZLIB_FOUND AND ANDROID)
|
if(ZLIB_FOUND AND ANDROID)
|
||||||
if(ZLIB_LIBRARIES MATCHES "/usr/(lib|lib32|lib64)/libz.so$")
|
if(ZLIB_LIBRARIES MATCHES "/usr/(lib|lib32|lib64)/libz.so$")
|
||||||
@@ -15,11 +16,12 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT ZLIB_FOUND)
|
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")
|
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})
|
set(ZLIB_LIBRARIES ${ZLIB_LIBRARY})
|
||||||
|
|
||||||
ocv_parse_header2(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h" ZLIB_VERSION)
|
ocv_parse_header2(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h" ZLIB_VERSION)
|
||||||
@@ -30,23 +32,25 @@ if(WITH_JPEG)
|
|||||||
if(BUILD_JPEG)
|
if(BUILD_JPEG)
|
||||||
ocv_clear_vars(JPEG_FOUND)
|
ocv_clear_vars(JPEG_FOUND)
|
||||||
else()
|
else()
|
||||||
|
ocv_clear_internal_cache_vars(JPEG_LIBRARY JPEG_INCLUDE_DIR)
|
||||||
include(FindJPEG)
|
include(FindJPEG)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT JPEG_FOUND)
|
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)
|
if(NOT BUILD_JPEG_TURBO_DISABLE)
|
||||||
set(JPEG_LIBRARY libjpeg-turbo)
|
set(JPEG_LIBRARY libjpeg-turbo CACHE INTERNAL "")
|
||||||
set(JPEG_LIBRARIES ${JPEG_LIBRARY})
|
set(JPEG_LIBRARIES ${JPEG_LIBRARY})
|
||||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libjpeg-turbo")
|
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()
|
else()
|
||||||
set(JPEG_LIBRARY libjpeg)
|
set(JPEG_LIBRARY libjpeg CACHE INTERNAL "")
|
||||||
set(JPEG_LIBRARIES ${JPEG_LIBRARY})
|
set(JPEG_LIBRARIES ${JPEG_LIBRARY})
|
||||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libjpeg")
|
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()
|
endif()
|
||||||
|
set(JPEG_INCLUDE_DIRS "${JPEG_INCLUDE_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
macro(ocv_detect_jpeg_version header_file)
|
macro(ocv_detect_jpeg_version header_file)
|
||||||
@@ -74,6 +78,7 @@ if(WITH_TIFF)
|
|||||||
if(BUILD_TIFF)
|
if(BUILD_TIFF)
|
||||||
ocv_clear_vars(TIFF_FOUND)
|
ocv_clear_vars(TIFF_FOUND)
|
||||||
else()
|
else()
|
||||||
|
ocv_clear_internal_cache_vars(TIFF_LIBRARY TIFF_INCLUDE_DIR)
|
||||||
include(FindTIFF)
|
include(FindTIFF)
|
||||||
if(TIFF_FOUND)
|
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)
|
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)
|
if(NOT TIFF_FOUND)
|
||||||
ocv_clear_vars(TIFF_LIBRARY TIFF_LIBRARIES TIFF_INCLUDE_DIR)
|
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})
|
set(TIFF_LIBRARIES ${TIFF_LIBRARY})
|
||||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libtiff")
|
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)
|
ocv_parse_header("${${TIFF_LIBRARY}_SOURCE_DIR}/tiff.h" TIFF_VERSION_LINES TIFF_VERSION_CLASSIC TIFF_VERSION_BIG TIFF_VERSION TIFF_BIGTIFF_VERSION)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -117,6 +122,7 @@ if(WITH_WEBP)
|
|||||||
if(BUILD_WEBP)
|
if(BUILD_WEBP)
|
||||||
ocv_clear_vars(WEBP_FOUND WEBP_LIBRARY WEBP_LIBRARIES WEBP_INCLUDE_DIR)
|
ocv_clear_vars(WEBP_FOUND WEBP_LIBRARY WEBP_LIBRARIES WEBP_INCLUDE_DIR)
|
||||||
else()
|
else()
|
||||||
|
ocv_clear_internal_cache_vars(WEBP_LIBRARY WEBP_INCLUDE_DIR)
|
||||||
include(cmake/OpenCVFindWebP.cmake)
|
include(cmake/OpenCVFindWebP.cmake)
|
||||||
if(WEBP_FOUND)
|
if(WEBP_FOUND)
|
||||||
set(HAVE_WEBP 1)
|
set(HAVE_WEBP 1)
|
||||||
@@ -128,12 +134,12 @@ endif()
|
|||||||
if(WITH_WEBP AND NOT WEBP_FOUND
|
if(WITH_WEBP AND NOT WEBP_FOUND
|
||||||
AND (NOT ANDROID OR HAVE_CPUFEATURES)
|
AND (NOT ANDROID OR HAVE_CPUFEATURES)
|
||||||
)
|
)
|
||||||
|
ocv_clear_vars(WEBP_LIBRARY WEBP_INCLUDE_DIR)
|
||||||
set(WEBP_LIBRARY libwebp)
|
set(WEBP_LIBRARY libwebp CACHE INTERNAL "")
|
||||||
set(WEBP_LIBRARIES ${WEBP_LIBRARY})
|
set(WEBP_LIBRARIES ${WEBP_LIBRARY})
|
||||||
|
|
||||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libwebp")
|
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)
|
set(HAVE_WEBP 1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -164,10 +170,10 @@ if(WITH_JASPER)
|
|||||||
if(NOT JASPER_FOUND)
|
if(NOT JASPER_FOUND)
|
||||||
ocv_clear_vars(JASPER_LIBRARY JASPER_LIBRARIES JASPER_INCLUDE_DIR)
|
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})
|
set(JASPER_LIBRARIES ${JASPER_LIBRARY})
|
||||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libjasper")
|
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()
|
endif()
|
||||||
|
|
||||||
set(HAVE_JASPER YES)
|
set(HAVE_JASPER YES)
|
||||||
@@ -182,6 +188,7 @@ if(WITH_PNG)
|
|||||||
if(BUILD_PNG)
|
if(BUILD_PNG)
|
||||||
ocv_clear_vars(PNG_FOUND)
|
ocv_clear_vars(PNG_FOUND)
|
||||||
else()
|
else()
|
||||||
|
ocv_clear_internal_cache_vars(PNG_LIBRARY PNG_INCLUDE_DIR)
|
||||||
include(FindPNG)
|
include(FindPNG)
|
||||||
if(PNG_FOUND)
|
if(PNG_FOUND)
|
||||||
include(CheckIncludeFile)
|
include(CheckIncludeFile)
|
||||||
@@ -197,10 +204,10 @@ if(WITH_PNG)
|
|||||||
if(NOT PNG_FOUND)
|
if(NOT PNG_FOUND)
|
||||||
ocv_clear_vars(PNG_LIBRARY PNG_LIBRARIES PNG_INCLUDE_DIR PNG_PNG_INCLUDE_DIR HAVE_LIBPNG_PNG_H PNG_DEFINITIONS)
|
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})
|
set(PNG_LIBRARIES ${PNG_LIBRARY})
|
||||||
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/libpng")
|
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 "")
|
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)
|
ocv_parse_header("${PNG_INCLUDE_DIR}/png.h" PNG_VERSION_LINES PNG_LIBPNG_VER_MAJOR PNG_LIBPNG_VER_MINOR PNG_LIBPNG_VER_RELEASE)
|
||||||
endif()
|
endif()
|
||||||
@@ -213,6 +220,7 @@ endif()
|
|||||||
if(WITH_OPENEXR)
|
if(WITH_OPENEXR)
|
||||||
ocv_clear_vars(HAVE_OPENEXR)
|
ocv_clear_vars(HAVE_OPENEXR)
|
||||||
if(NOT BUILD_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")
|
include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindOpenEXR.cmake")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -242,7 +250,7 @@ if(WITH_GDAL)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WITH_GDCM)
|
if(WITH_GDCM)
|
||||||
find_package(GDCM QUIET)
|
find_package(GDCM QUIET)
|
||||||
if(NOT GDCM_FOUND)
|
if(NOT GDCM_FOUND)
|
||||||
set(HAVE_GDCM NO)
|
set(HAVE_GDCM NO)
|
||||||
|
|||||||
@@ -51,7 +51,15 @@ endif(WITH_CUDA)
|
|||||||
|
|
||||||
# --- Eigen ---
|
# --- Eigen ---
|
||||||
if(WITH_EIGEN AND NOT HAVE_EIGEN)
|
if(WITH_EIGEN AND NOT HAVE_EIGEN)
|
||||||
find_package(Eigen3 QUIET)
|
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)
|
||||||
|
find_package(Eigen3 QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(Eigen3_FOUND)
|
if(Eigen3_FOUND)
|
||||||
if(TARGET Eigen3::Eigen)
|
if(TARGET Eigen3::Eigen)
|
||||||
|
|||||||
+87
-65
@@ -3,7 +3,14 @@
|
|||||||
# installation/package
|
# installation/package
|
||||||
#
|
#
|
||||||
# Parameters:
|
# 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:
|
# On return this will define:
|
||||||
#
|
#
|
||||||
@@ -13,12 +20,6 @@
|
|||||||
# MKL_LIBRARIES - MKL libraries that are used by OpenCV
|
# 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)
|
macro(mkl_fail)
|
||||||
set(HAVE_MKL OFF)
|
set(HAVE_MKL OFF)
|
||||||
set(MKL_ROOT_DIR "${MKL_ROOT_DIR}" CACHE PATH "Path to MKL directory")
|
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)
|
set(MKL_VERSION_STR "${MKL_VERSION_MAJOR}.${MKL_VERSION_MINOR}.${MKL_VERSION_UPDATE}" CACHE STRING "MKL version" FORCE)
|
||||||
endmacro()
|
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)
|
if(NOT MKL_ROOT_DIR AND DEFINED MKL_INCLUDE_DIR AND EXISTS "${MKL_INCLUDE_DIR}/mkl.h")
|
||||||
OCV_OPTION(MKL_WITH_TBB "Use MKL with TBB multithreading" OFF)#ON IF WITH_TBB)
|
file(TO_CMAKE_PATH "${MKL_INCLUDE_DIR}" MKL_INCLUDE_DIR)
|
||||||
OCV_OPTION(MKL_WITH_OPENMP "Use MKL with OpenMP multithreading" OFF)#ON IF WITH_OPENMP)
|
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()
|
endif()
|
||||||
|
|
||||||
#check current MKL_ROOT_DIR
|
|
||||||
if(NOT MKL_ROOT_DIR OR NOT EXISTS "${MKL_ROOT_DIR}/include/mkl.h")
|
if(NOT MKL_ROOT_DIR OR NOT EXISTS "${MKL_ROOT_DIR}/include/mkl.h")
|
||||||
set(mkl_root_paths "${MKL_ROOT_DIR}")
|
mkl_fail()
|
||||||
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})
|
|
||||||
endif()
|
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
|
if(NOT MKL_ROOT_DIR
|
||||||
OR NOT EXISTS "${MKL_ROOT_DIR}"
|
OR NOT EXISTS "${MKL_ROOT_DIR}"
|
||||||
OR NOT EXISTS "${MKL_INCLUDE_DIRS}"
|
OR NOT EXISTS "${MKL_INCLUDE_DIR}"
|
||||||
OR NOT EXISTS "${MKL_INCLUDE_DIRS}/mkl_version.h"
|
OR NOT EXISTS "${MKL_INCLUDE_DIR}/mkl_version.h"
|
||||||
)
|
)
|
||||||
mkl_fail()
|
mkl_fail()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
get_mkl_version(${MKL_INCLUDE_DIRS}/mkl_version.h)
|
get_mkl_version(${MKL_INCLUDE_DIR}/mkl_version.h)
|
||||||
|
|
||||||
#determine arch
|
#determine arch
|
||||||
if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
|
if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
|
||||||
@@ -95,52 +103,66 @@ else()
|
|||||||
set(MKL_ARCH_SUFFIX "c")
|
set(MKL_ARCH_SUFFIX "c")
|
||||||
endif()
|
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_LIB_FIND_PATHS} ${MKL_ROOT_DIR}/lib)
|
||||||
set(mkl_lib_find_paths
|
foreach(MKL_ARCH ${MKL_ARCH_LIST})
|
||||||
${MKL_ROOT_DIR}/lib)
|
list(APPEND mkl_lib_find_paths
|
||||||
foreach(MKL_ARCH ${MKL_ARCH_LIST})
|
${MKL_ROOT_DIR}/lib/${MKL_ARCH}
|
||||||
list(APPEND mkl_lib_find_paths
|
${MKL_ROOT_DIR}/${MKL_ARCH}
|
||||||
${MKL_ROOT_DIR}/lib/${MKL_ARCH}
|
)
|
||||||
${MKL_ROOT_DIR}/../tbb/lib/${MKL_ARCH}
|
endforeach()
|
||||||
${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)
|
# https://software.intel.com/content/www/us/en/develop/articles/a-new-linking-model-single-dynamic-library-mkl_rt-since-intel-mkl-103.html
|
||||||
list(APPEND mkl_lib_list mkl_tbb_thread tbb)
|
set(mkl_lib_list "mkl_rt")
|
||||||
elseif(MKL_WITH_OPENMP)
|
|
||||||
if(MSVC)
|
elseif(NOT (MKL_VERSION_STR VERSION_LESS "11.3.0"))
|
||||||
list(APPEND mkl_lib_list mkl_intel_thread libiomp5md)
|
|
||||||
else()
|
foreach(MKL_ARCH ${MKL_ARCH_LIST})
|
||||||
list(APPEND mkl_lib_list mkl_gnu_thread)
|
list(APPEND mkl_lib_find_paths
|
||||||
endif()
|
${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()
|
else()
|
||||||
list(APPEND mkl_lib_list mkl_sequential)
|
list(APPEND mkl_lib_list mkl_gnu_thread)
|
||||||
endif()
|
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()
|
else()
|
||||||
message(STATUS "MKL version ${MKL_VERSION_STR} is not supported")
|
message(STATUS "MKL version ${MKL_VERSION_STR} is not supported")
|
||||||
mkl_fail()
|
mkl_fail()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(MKL_LIBRARIES "")
|
if(NOT MKL_LIBRARIES)
|
||||||
foreach(lib ${mkl_lib_list})
|
set(MKL_LIBRARIES "")
|
||||||
find_library(${lib} NAMES ${lib} ${lib}_dll HINTS ${mkl_lib_find_paths})
|
foreach(lib ${mkl_lib_list})
|
||||||
mark_as_advanced(${lib})
|
set(lib_var_name MKL_LIBRARY_${lib})
|
||||||
if(NOT ${lib})
|
find_library(${lib_var_name} NAMES ${lib} ${lib}_dll HINTS ${mkl_lib_find_paths})
|
||||||
mkl_fail()
|
mark_as_advanced(${lib_var_name})
|
||||||
|
if(NOT ${lib_var_name})
|
||||||
|
mkl_fail()
|
||||||
endif()
|
endif()
|
||||||
list(APPEND MKL_LIBRARIES ${${lib}})
|
list(APPEND MKL_LIBRARIES ${${lib_var_name}})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
message(STATUS "Found MKL ${MKL_VERSION_STR} at: ${MKL_ROOT_DIR}")
|
message(STATUS "Found MKL ${MKL_VERSION_STR} at: ${MKL_ROOT_DIR}")
|
||||||
set(HAVE_MKL ON)
|
set(HAVE_MKL ON)
|
||||||
set(MKL_ROOT_DIR "${MKL_ROOT_DIR}" CACHE PATH "Path to MKL directory")
|
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_INCLUDE_DIRS "${MKL_INCLUDE_DIR}")
|
||||||
set(MKL_LIBRARIES "${MKL_LIBRARIES}" CACHE STRING "MKL libraries")
|
set(MKL_LIBRARIES "${MKL_LIBRARIES}")
|
||||||
if(UNIX AND NOT MKL_LIBRARIES_DONT_HACK)
|
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
|
#it's ugly but helps to avoid cyclic lib problem
|
||||||
set(MKL_LIBRARIES ${MKL_LIBRARIES} ${MKL_LIBRARIES} ${MKL_LIBRARIES} "-lpthread" "-lm" "-ldl")
|
set(MKL_LIBRARIES ${MKL_LIBRARIES} ${MKL_LIBRARIES} ${MKL_LIBRARIES} "-lpthread" "-lm" "-ldl")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ SET(Open_BLAS_INCLUDE_SEARCH_PATHS
|
|||||||
)
|
)
|
||||||
|
|
||||||
SET(Open_BLAS_LIB_SEARCH_PATHS
|
SET(Open_BLAS_LIB_SEARCH_PATHS
|
||||||
$ENV{OpenBLAS}cd
|
$ENV{OpenBLAS}
|
||||||
$ENV{OpenBLAS}/lib
|
$ENV{OpenBLAS}/lib
|
||||||
$ENV{OpenBLAS_HOME}
|
$ENV{OpenBLAS_HOME}
|
||||||
$ENV{OpenBLAS_HOME}/lib
|
$ENV{OpenBLAS_HOME}/lib
|
||||||
|
|||||||
+15
-14
@@ -98,15 +98,6 @@ macro(ocv_add_dependencies full_modname)
|
|||||||
endforeach()
|
endforeach()
|
||||||
unset(__depsvar)
|
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}_REQ_DEPS)
|
||||||
ocv_list_unique(OPENCV_MODULE_${full_modname}_OPT_DEPS)
|
ocv_list_unique(OPENCV_MODULE_${full_modname}_OPT_DEPS)
|
||||||
ocv_list_unique(OPENCV_MODULE_${full_modname}_PRIVATE_REQ_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")
|
set(OPENCV_MODULES_DISABLED_USER ${OPENCV_MODULES_DISABLED_USER} "${the_module}" CACHE INTERNAL "List of OpenCV modules explicitly disabled by user")
|
||||||
endif()
|
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
|
# stop processing of current file
|
||||||
ocv_cmake_hook(POST_ADD_MODULE)
|
ocv_cmake_hook(POST_ADD_MODULE)
|
||||||
ocv_cmake_hook(POST_ADD_MODULE_${the_module})
|
ocv_cmake_hook(POST_ADD_MODULE_${the_module})
|
||||||
@@ -500,6 +486,21 @@ function(__ocv_resolve_dependencies)
|
|||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
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
|
# disable MODULES with unresolved dependencies
|
||||||
set(has_changes ON)
|
set(has_changes ON)
|
||||||
while(has_changes)
|
while(has_changes)
|
||||||
|
|||||||
+38
-1
@@ -8,7 +8,20 @@ include(CMakeParseArguments)
|
|||||||
function(ocv_cmake_dump_vars)
|
function(ocv_cmake_dump_vars)
|
||||||
set(OPENCV_SUPPRESS_DEPRECATIONS 1) # suppress deprecation warnings from variable_watch() guards
|
set(OPENCV_SUPPRESS_DEPRECATIONS 1) # suppress deprecation warnings from variable_watch() guards
|
||||||
get_cmake_property(__variableNames VARIABLES)
|
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}")
|
set(regex "${DUMP_UNPARSED_ARGUMENTS}")
|
||||||
string(TOLOWER "${regex}" regex_lower)
|
string(TOLOWER "${regex}" regex_lower)
|
||||||
set(__VARS "")
|
set(__VARS "")
|
||||||
@@ -400,6 +413,24 @@ macro(ocv_clear_vars)
|
|||||||
endforeach()
|
endforeach()
|
||||||
endmacro()
|
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
|
set(OCV_COMPILER_FAIL_REGEX
|
||||||
"argument .* is not valid" # GCC 9+ (including support of unicode quotes)
|
"argument .* is not valid" # GCC 9+ (including support of unicode quotes)
|
||||||
"command[- ]line option .* is valid for .* but not for C\\+\\+" # GNU
|
"command[- ]line option .* is valid for .* but not for C\\+\\+" # GNU
|
||||||
@@ -1890,3 +1921,9 @@ function(ocv_update_file filepath content)
|
|||||||
file(WRITE "${filepath}" "${content}")
|
file(WRITE "${filepath}" "${content}")
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
if(NOT BUILD_SHARED_LIBS AND (CMAKE_VERSION VERSION_LESS "3.14.0"))
|
||||||
|
ocv_update(OPENCV_3RDPARTY_EXCLUDE_FROM_ALL "") # avoid CMake warnings: https://gitlab.kitware.com/cmake/cmake/-/issues/18938
|
||||||
|
else()
|
||||||
|
ocv_update(OPENCV_3RDPARTY_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL")
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
set(OPENCV_SKIP_LINK_AS_NEEDED 1)
|
||||||
+15
-1
@@ -130,9 +130,23 @@ if(DOXYGEN_FOUND)
|
|||||||
set(tutorial_js_path "${CMAKE_CURRENT_SOURCE_DIR}/js_tutorials")
|
set(tutorial_js_path "${CMAKE_CURRENT_SOURCE_DIR}/js_tutorials")
|
||||||
set(example_path "${CMAKE_SOURCE_DIR}/samples")
|
set(example_path "${CMAKE_SOURCE_DIR}/samples")
|
||||||
|
|
||||||
|
set(doxygen_image_path
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/images
|
||||||
|
${paths_doc}
|
||||||
|
${tutorial_path}
|
||||||
|
${tutorial_py_path}
|
||||||
|
${tutorial_js_path}
|
||||||
|
${paths_tutorial}
|
||||||
|
#${OpenCV_SOURCE_DIR}/samples/data # TODO: need to resolve ambiguous conflicts first
|
||||||
|
${OpenCV_SOURCE_DIR}
|
||||||
|
${OpenCV_SOURCE_DIR}/modules # <opencv>/modules
|
||||||
|
${OPENCV_EXTRA_MODULES_PATH} # <opencv_contrib>/modules
|
||||||
|
${OPENCV_DOCS_EXTRA_IMAGE_PATH} # custom variable for user modules
|
||||||
|
)
|
||||||
|
|
||||||
# set export variables
|
# set export variables
|
||||||
string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_INPUT_LIST "${rootfile} ; ${faqfile} ; ${paths_include} ; ${paths_hal_interface} ; ${paths_doc} ; ${tutorial_path} ; ${tutorial_py_path} ; ${tutorial_js_path} ; ${paths_tutorial} ; ${tutorial_contrib_root}")
|
string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_INPUT_LIST "${rootfile} ; ${faqfile} ; ${paths_include} ; ${paths_hal_interface} ; ${paths_doc} ; ${tutorial_path} ; ${tutorial_py_path} ; ${tutorial_js_path} ; ${paths_tutorial} ; ${tutorial_contrib_root}")
|
||||||
string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_IMAGE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/images ; ${paths_doc} ; ${tutorial_path} ; ${tutorial_py_path} ; ${tutorial_js_path} ; ${paths_tutorial}")
|
string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_IMAGE_PATH "${doxygen_image_path}")
|
||||||
string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_EXCLUDE_LIST "${CMAKE_DOXYGEN_EXCLUDE_LIST}")
|
string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_EXCLUDE_LIST "${CMAKE_DOXYGEN_EXCLUDE_LIST}")
|
||||||
string(REPLACE ";" " " CMAKE_DOXYGEN_ENABLED_SECTIONS "${CMAKE_DOXYGEN_ENABLED_SECTIONS}")
|
string(REPLACE ";" " " CMAKE_DOXYGEN_ENABLED_SECTIONS "${CMAKE_DOXYGEN_ENABLED_SECTIONS}")
|
||||||
# TODO: remove paths_doc from EXAMPLE_PATH after face module tutorials/samples moved to separate folders
|
# TODO: remove paths_doc from EXAMPLE_PATH after face module tutorials/samples moved to separate folders
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ ALIASES += end_toggle="@htmlonly[block] </div> @endhtmlonly"
|
|||||||
ALIASES += prev_tutorial{1}="**Prev Tutorial:** \ref \1 \n"
|
ALIASES += prev_tutorial{1}="**Prev Tutorial:** \ref \1 \n"
|
||||||
ALIASES += next_tutorial{1}="**Next 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"
|
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_FOR_C = NO
|
||||||
OPTIMIZE_OUTPUT_JAVA = NO
|
OPTIMIZE_OUTPUT_JAVA = NO
|
||||||
OPTIMIZE_FOR_FORTRAN = 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');
|
let script = document.createElement('script');
|
||||||
script.setAttribute('async', '');
|
script.setAttribute('async', '');
|
||||||
script.setAttribute('type', 'text/javascript');
|
script.setAttribute('type', 'text/javascript');
|
||||||
script.addEventListener('load', () => {
|
script.addEventListener('load', async () => {
|
||||||
if (cv.getBuildInformation)
|
if (cv.getBuildInformation)
|
||||||
{
|
{
|
||||||
console.log(cv.getBuildInformation());
|
console.log(cv.getBuildInformation());
|
||||||
@@ -16,9 +16,15 @@ function Utils(errorOutputId) { // eslint-disable-line no-unused-vars
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// WASM
|
// WASM
|
||||||
cv['onRuntimeInitialized']=()=>{
|
if (cv instanceof Promise) {
|
||||||
|
cv = await cv;
|
||||||
console.log(cv.getBuildInformation());
|
console.log(cv.getBuildInformation());
|
||||||
onloadCallback();
|
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.
|
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.
|
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)
|
- Gang Song (GSoC student, Shanghai Jiao Tong University)
|
||||||
- Wenyao Gan (Student intern, Shanghai Jiao Tong University)
|
- Wenyao Gan (Student intern, Shanghai Jiao Tong University)
|
||||||
- Mohammad Reza Haghighat (Project initiator & sponsor, Intel Corporation)
|
- 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
|
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
|
@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.
|
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:
|
For example:
|
||||||
@code{.bash}
|
@code{.bash}
|
||||||
@@ -21,15 +21,29 @@ For example:
|
|||||||
./emsdk activate latest
|
./emsdk activate latest
|
||||||
@endcode
|
@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:
|
For example:
|
||||||
@code{.bash}
|
@code{.bash}
|
||||||
source ./emsdk_env.sh
|
source ./emsdk_env.sh
|
||||||
echo ${EMSCRIPTEN}
|
echo ${EMSDK}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
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 2.0.10
|
||||||
|
./emsdk activate 2.0.10
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
Obtaining OpenCV Source Code
|
Obtaining OpenCV Source Code
|
||||||
@@ -62,8 +76,7 @@ Building OpenCV.js from Source
|
|||||||
|
|
||||||
For example, to build in `build_js` directory:
|
For example, to build in `build_js` directory:
|
||||||
@code{.bash}
|
@code{.bash}
|
||||||
cd opencv
|
emcmake python ./opencv/platforms/js/build_js.py build_js
|
||||||
python ./platforms/js/build_js.py build_js
|
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
@note
|
@note
|
||||||
@@ -73,14 +86,39 @@ Building OpenCV.js from Source
|
|||||||
|
|
||||||
For example, to build wasm version in `build_wasm` directory:
|
For example, to build wasm version in `build_wasm` directory:
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
|
|
||||||
|
-# [Optional] To build the OpenCV.js loader, append `--build_loader`.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
@code{.bash}
|
||||||
|
emcmake python ./opencv/platforms/js/build_js.py build_js --build_loader
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@note
|
||||||
|
The loader is implemented as a js file in the path `<opencv_js_dir>/bin/loader.js`. The loader utilizes the [WebAssembly Feature Detection](https://github.com/GoogleChromeLabs/wasm-feature-detect) to detect the features of the broswer and load corresponding OpenCV.js automatically. To use it, you need to use the UMD version of [WebAssembly Feature Detection](https://github.com/GoogleChromeLabs/wasm-feature-detect) and introduce the `loader.js` in your Web application.
|
||||||
|
|
||||||
|
Example Code:
|
||||||
|
@code{.javascipt}
|
||||||
|
// Set paths configuration
|
||||||
|
let pathsConfig = {
|
||||||
|
wasm: "../../build_wasm/opencv.js",
|
||||||
|
threads: "../../build_mt/opencv.js",
|
||||||
|
simd: "../../build_simd/opencv.js",
|
||||||
|
threadsSimd: "../../build_mtSIMD/opencv.js",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load OpenCV.js and use the pathsConfiguration and main function as the params.
|
||||||
|
loadOpenCV(pathsConfig, main);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
|
||||||
-# [optional] To build documents, append `--build_doc` option.
|
-# [optional] To build documents, append `--build_doc` option.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
|
|
||||||
@note
|
@note
|
||||||
@@ -90,7 +128,14 @@ Building OpenCV.js from Source
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
|
|
||||||
Running OpenCV.js Tests
|
Running OpenCV.js Tests
|
||||||
@@ -152,7 +197,7 @@ node tests.js
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
@code{.bash}
|
@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
|
@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.
|
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.
|
||||||
@@ -164,7 +209,7 @@ node tests.js
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
|
|
||||||
The simd optimization is experimental as wasm simd is still in development.
|
The simd optimization is experimental as wasm simd is still in development.
|
||||||
@@ -188,7 +233,7 @@ node tests.js
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
|
|
||||||
For wasm intrinsics tests, you can use the following function to test all the cases:
|
For wasm intrinsics tests, you can use the following function to test all the cases:
|
||||||
@@ -216,7 +261,7 @@ node tests.js
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
@code{.bash}
|
@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
|
@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`.
|
To run performance tests, launch a local web server in \<build_dir\>/bin folder. For example, node http-server which serves on `localhost:8080`.
|
||||||
@@ -237,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.
|
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}
|
@code{.bash}
|
||||||
git clone https://github.com/opencv/opencv.git
|
git clone https://github.com/opencv/opencv.git
|
||||||
cd opencv
|
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
|
@endcode
|
||||||
|
|
||||||
In Windows use the following PowerShell command:
|
In Windows use the following PowerShell command:
|
||||||
|
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
|
|
||||||
@warning
|
@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}
|
@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
|
@endcode
|
||||||
|
|
||||||
### Building the documentation with Docker
|
### Building the documentation with Docker
|
||||||
@@ -263,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:
|
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 update \
|
||||||
RUN apt-get install -y doxygen
|
&& 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):
|
Then we build the docker image and name it `opencv-js-doc` with the following command (that needs to be run only once):
|
||||||
@@ -278,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`:
|
Now run the build command again, this time using the new image and passing `--build_doc`:
|
||||||
|
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Using OpenCV.js {#tutorial_js_usage}
|
|||||||
Steps
|
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
|
### Create a web page
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ function onOpenCvReady() {
|
|||||||
</html>
|
</html>
|
||||||
@endcode
|
@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
|
Try it
|
||||||
------
|
------
|
||||||
@@ -137,4 +137,4 @@ Try it
|
|||||||
<iframe src="../../js_setup_usage.html" width="100%"
|
<iframe src="../../js_setup_usage.html" width="100%"
|
||||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||||
</iframe>
|
</iframe>
|
||||||
\endhtmlonly
|
\endhtmlonly
|
||||||
|
|||||||
@@ -26,3 +26,7 @@ OpenCV.js Tutorials {#tutorial_js_root}
|
|||||||
|
|
||||||
In this section you
|
In this section you
|
||||||
will object detection techniques like face detection etc.
|
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
|
||||||
|
|||||||
@@ -92,11 +92,11 @@ def main():
|
|||||||
dest="square_size", type=float)
|
dest="square_size", type=float)
|
||||||
parser.add_argument("-R", "--radius_rate", help="circles_radius = square_size/radius_rate", default="5.0",
|
parser.add_argument("-R", "--radius_rate", help="circles_radius = square_size/radius_rate", default="5.0",
|
||||||
action="store", dest="radius_rate", type=float)
|
action="store", dest="radius_rate", type=float)
|
||||||
parser.add_argument("-w", "--page_width", help="page width in units", default="216", action="store",
|
parser.add_argument("-w", "--page_width", help="page width in units", default=argparse.SUPPRESS, action="store",
|
||||||
dest="page_width", type=float)
|
dest="page_width", type=float)
|
||||||
parser.add_argument("-h", "--page_height", help="page height in units", default="279", action="store",
|
parser.add_argument("-h", "--page_height", help="page height in units", default=argparse.SUPPRESS, action="store",
|
||||||
dest="page_width", type=float)
|
dest="page_height", type=float)
|
||||||
parser.add_argument("-a", "--page_size", help="page size, supersedes -h -w arguments", default="A4", action="store",
|
parser.add_argument("-a", "--page_size", help="page size, superseded if -h and -w are set", default="A4", action="store",
|
||||||
dest="page_size", choices=["A0", "A1", "A2", "A3", "A4", "A5"])
|
dest="page_size", choices=["A0", "A1", "A2", "A3", "A4", "A5"])
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -111,12 +111,16 @@ def main():
|
|||||||
units = args.units
|
units = args.units
|
||||||
square_size = args.square_size
|
square_size = args.square_size
|
||||||
radius_rate = args.radius_rate
|
radius_rate = args.radius_rate
|
||||||
page_size = args.page_size
|
if 'page_width' and 'page_height' in args:
|
||||||
# page size dict (ISO standard, mm) for easy lookup. format - size: [width, height]
|
page_width = args.page_width
|
||||||
page_sizes = {"A0": [840, 1188], "A1": [594, 840], "A2": [420, 594], "A3": [297, 420], "A4": [210, 297],
|
page_height = args.page_height
|
||||||
"A5": [148, 210]}
|
else:
|
||||||
page_width = page_sizes[page_size.upper()][0]
|
page_size = args.page_size
|
||||||
page_height = page_sizes[page_size.upper()][1]
|
# page size dict (ISO standard, mm) for easy lookup. format - size: [width, height]
|
||||||
|
page_sizes = {"A0": [840, 1188], "A1": [594, 840], "A2": [420, 594], "A3": [297, 420], "A4": [210, 297],
|
||||||
|
"A5": [148, 210]}
|
||||||
|
page_width = page_sizes[page_size][0]
|
||||||
|
page_height = page_sizes[page_size][1]
|
||||||
pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height)
|
pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height)
|
||||||
# dict for easy lookup of pattern type
|
# dict for easy lookup of pattern type
|
||||||
mp = {"circles": pm.make_circles_pattern, "acircles": pm.make_acircles_pattern,
|
mp = {"circles": pm.make_circles_pattern, "acircles": pm.make_acircles_pattern,
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ find the average error, we calculate the arithmetical mean of the errors calcula
|
|||||||
calibration images.
|
calibration images.
|
||||||
@code{.py}
|
@code{.py}
|
||||||
mean_error = 0
|
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)
|
imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
|
||||||
error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
|
error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
|
||||||
mean_error += error
|
mean_error += error
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ from matplotlib import pyplot as plt
|
|||||||
img1 = cv.imread('myleft.jpg',0) #queryimage # left image
|
img1 = cv.imread('myleft.jpg',0) #queryimage # left image
|
||||||
img2 = cv.imread('myright.jpg',0) #trainimage # right image
|
img2 = cv.imread('myright.jpg',0) #trainimage # right image
|
||||||
|
|
||||||
sift = cv.SIFT()
|
sift = cv.SIFT_create()
|
||||||
|
|
||||||
# find the keypoints and descriptors with SIFT
|
# find the keypoints and descriptors with SIFT
|
||||||
kp1, des1 = sift.detectAndCompute(img1,None)
|
kp1, des1 = sift.detectAndCompute(img1,None)
|
||||||
@@ -93,14 +93,12 @@ search_params = dict(checks=50)
|
|||||||
flann = cv.FlannBasedMatcher(index_params,search_params)
|
flann = cv.FlannBasedMatcher(index_params,search_params)
|
||||||
matches = flann.knnMatch(des1,des2,k=2)
|
matches = flann.knnMatch(des1,des2,k=2)
|
||||||
|
|
||||||
good = []
|
|
||||||
pts1 = []
|
pts1 = []
|
||||||
pts2 = []
|
pts2 = []
|
||||||
|
|
||||||
# ratio test as per Lowe's paper
|
# ratio test as per Lowe's paper
|
||||||
for i,(m,n) in enumerate(matches):
|
for i,(m,n) in enumerate(matches):
|
||||||
if m.distance < 0.8*n.distance:
|
if m.distance < 0.8*n.distance:
|
||||||
good.append(m)
|
|
||||||
pts2.append(kp2[m.trainIdx].pt)
|
pts2.append(kp2[m.trainIdx].pt)
|
||||||
pts1.append(kp1[m.queryIdx].pt)
|
pts1.append(kp1[m.queryIdx].pt)
|
||||||
@endcode
|
@endcode
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 18 KiB |
@@ -48,7 +48,7 @@ titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
|
|||||||
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
|
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
|
||||||
|
|
||||||
for i in xrange(6):
|
for i in xrange(6):
|
||||||
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
|
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray',vmin=0,vmax=255)
|
||||||
plt.title(titles[i])
|
plt.title(titles[i])
|
||||||
plt.xticks([]),plt.yticks([])
|
plt.xticks([]),plt.yticks([])
|
||||||
|
|
||||||
|
|||||||
+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.
|
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
|
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
|
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
|
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.
|
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
|
Explanation
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
-# Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
|
@add_toggle_cpp
|
||||||
previous sections). Let's check the general structure of the C++ program:
|
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)
|
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp main
|
||||||
- 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.
|
|
||||||
|
|
||||||
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
|
Every time we move any slider, the user's function **Erosion** or **Dilation** will be
|
||||||
receives three arguments:
|
called and it will update the output image based on the current trackbar values.
|
||||||
- *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
|
|
||||||
|
|
||||||
We can choose any of three shapes for our kernel:
|
Let's analyze these two functions:
|
||||||
|
|
||||||
- Rectangular box: MORPH_RECT
|
#### The erosion function
|
||||||
- Cross: MORPH_CROSS
|
|
||||||
- Ellipse: MORPH_ELLIPSE
|
|
||||||
|
|
||||||
Then, we just have to specify the size of our kernel and the *anchor point*. If not
|
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp erosion
|
||||||
specified, it is assumed to be in the center.
|
|
||||||
|
|
||||||
- That is all. We are ready to perform the erosion of our image.
|
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
|
||||||
@note Additionally, there is another parameter that allows you to perform multiple erosions
|
receives three arguments:
|
||||||
(iterations) at once. However, We haven't used it in this simple tutorial. You can check out the
|
- *src*: The source image
|
||||||
reference for more details.
|
- *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**.
|
- Rectangular box: MORPH_RECT
|
||||||
Here we also have the option of defining our kernel, its anchor point and the size of the operator
|
- Cross: MORPH_CROSS
|
||||||
to be used.
|
- Ellipse: MORPH_ELLIPSE
|
||||||
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp dilation
|
|
||||||
|
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
|
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.
|
iterations.
|
||||||
|
|
||||||

|

|
||||||
|
(depending on the programming language the output might vary a little or be only 1 window)
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ Open your Doxyfile using your favorite text editor and search for the key
|
|||||||
`TAGFILES`. Change it as follows:
|
`TAGFILES`. Change it as follows:
|
||||||
|
|
||||||
@code
|
@code
|
||||||
TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.12
|
TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.13
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
If you had other definitions already, you can append the line using a `\`:
|
If you had other definitions already, you can append the line using a `\`:
|
||||||
|
|
||||||
@code
|
@code
|
||||||
TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \
|
TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \
|
||||||
./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.12
|
./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.13
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
Doxygen can now use the information from the tag file to link to the OpenCV
|
Doxygen can now use the information from the tag file to link to the OpenCV
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ cd /c/lib
|
|||||||
@code{.bash}
|
@code{.bash}
|
||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
myRepo=$(pwd)
|
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
|
if [ ! -d "$myRepo/opencv" ]; then
|
||||||
echo "cloning opencv"
|
echo "cloning opencv"
|
||||||
git clone https://github.com/opencv/opencv.git
|
git clone https://github.com/opencv/opencv.git
|
||||||
mkdir -p Build/opencv
|
|
||||||
mkdir -p Install/opencv
|
|
||||||
else
|
else
|
||||||
cd opencv
|
cd opencv
|
||||||
git pull --rebase
|
git pull --rebase
|
||||||
@@ -62,16 +62,17 @@ fi
|
|||||||
if [ ! -d "$myRepo/opencv_contrib" ]; then
|
if [ ! -d "$myRepo/opencv_contrib" ]; then
|
||||||
echo "cloning opencv_contrib"
|
echo "cloning opencv_contrib"
|
||||||
git clone https://github.com/opencv/opencv_contrib.git
|
git clone https://github.com/opencv/opencv_contrib.git
|
||||||
mkdir -p Build/opencv_contrib
|
|
||||||
else
|
else
|
||||||
cd opencv_contrib
|
cd opencv_contrib
|
||||||
git pull --rebase
|
git pull --rebase
|
||||||
cd ..
|
cd ..
|
||||||
fi
|
fi
|
||||||
RepoSource=opencv
|
RepoSource=opencv
|
||||||
pushd Build/$RepoSource
|
mkdir -p 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'
|
pushd build_opencv
|
||||||
cmake -G"$CMAKE_CONFIG_GENERATOR" $CMAKE_OPTIONS -DOPENCV_EXTRA_MODULES_PATH="$myRepo"/opencv_contrib/modules -DCMAKE_INSTALL_PREFIX="$myRepo"/install/"$RepoSource" "$myRepo/$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)
|
||||||
|
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"
|
echo "************************* $Source_DIR -->debug"
|
||||||
cmake --build . --config debug
|
cmake --build . --config debug
|
||||||
echo "************************* $Source_DIR -->release"
|
echo "************************* $Source_DIR -->release"
|
||||||
@@ -82,15 +83,15 @@ popd
|
|||||||
@endcode
|
@endcode
|
||||||
In this script I suppose you use VS 2015 in 64 bits
|
In this script I suppose you use VS 2015 in 64 bits
|
||||||
@code{.bash}
|
@code{.bash}
|
||||||
CMAKE_CONFIG_GENERATOR="Visual Studio 14 2015 Win64"
|
CMAKE_GENERATOR_OPTIONS=-G"Visual Studio 14 2015 Win64"
|
||||||
@endcode
|
@endcode
|
||||||
and opencv will be installed in c:/lib/install
|
and opencv will be installed in c:/lib/install/opencv
|
||||||
@code{.bash}
|
@code{.bash}
|
||||||
-DCMAKE_INSTALL_PREFIX="$myRepo"/install/"$RepoSource" "$myRepo/$RepoSource"
|
-DCMAKE_INSTALL_PREFIX="$myRepo/install/$RepoSource"
|
||||||
@endcode
|
@endcode
|
||||||
with no Perf tests, no tests, no doc, no CUDA and no example
|
with no Perf tests, no tests, no doc, no CUDA and no example
|
||||||
@code{.bash}
|
@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
|
@endcode
|
||||||
-# In git command line enter following command :
|
-# In git command line enter following command :
|
||||||
@code{.bash}
|
@code{.bash}
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ In this tutorial you will learn how to:
|
|||||||
-# Create and update the background model by using @ref cv::BackgroundSubtractor class;
|
-# Create and update the background model by using @ref cv::BackgroundSubtractor class;
|
||||||
-# Get and show the foreground mask by using @ref cv::imshow ;
|
-# Get and show the foreground mask by using @ref cv::imshow ;
|
||||||
|
|
||||||
Code
|
### Code
|
||||||
----
|
|
||||||
|
|
||||||
In the following you can find the source code. We will let the user choose to process either a video
|
In the following you can find the source code. We will let the user choose to process either a video
|
||||||
file or a sequence of images.
|
file or a sequence of images.
|
||||||
|
|||||||
@@ -126,13 +126,12 @@ captRefrnc.set(CAP_PROP_POS_FRAMES, 10); // go to the 10th frame of the video
|
|||||||
For properties you can read and change look into the documentation of the @ref cv::VideoCapture::get and
|
For properties you can read and change look into the documentation of the @ref cv::VideoCapture::get and
|
||||||
@ref cv::VideoCapture::set functions.
|
@ref cv::VideoCapture::set functions.
|
||||||
|
|
||||||
Image similarity - PSNR and SSIM
|
### Image similarity - PSNR and SSIM
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
We want to check just how imperceptible our video converting operation went, therefore we need a
|
We want to check just how imperceptible our video converting operation went, therefore we need a
|
||||||
system to check frame by frame the similarity or differences. The most common algorithm used for
|
system to check frame by frame the similarity or differences. The most common algorithm used for
|
||||||
this is the PSNR (aka **Peak signal-to-noise ratio**). The simplest definition of this starts out
|
this is the PSNR (aka **Peak signal-to-noise ratio**). The simplest definition of this starts out
|
||||||
from the *mean squad error*. Let there be two images: I1 and I2; with a two dimensional size i and
|
from the *mean squared error*. Let there be two images: I1 and I2; with a two dimensional size i and
|
||||||
j, composed of c number of channels.
|
j, composed of c number of channels.
|
||||||
|
|
||||||
\f[MSE = \frac{1}{c*i*j} \sum{(I_1-I_2)^2}\f]
|
\f[MSE = \frac{1}{c*i*j} \sum{(I_1-I_2)^2}\f]
|
||||||
@@ -145,15 +144,15 @@ Here the \f$MAX_I\f$ is the maximum valid value for a pixel. In case of the simp
|
|||||||
per pixel per channel this is 255. When two images are the same the MSE will give zero, resulting in
|
per pixel per channel this is 255. When two images are the same the MSE will give zero, resulting in
|
||||||
an invalid divide by zero operation in the PSNR formula. In this case the PSNR is undefined and as
|
an invalid divide by zero operation in the PSNR formula. In this case the PSNR is undefined and as
|
||||||
we'll need to handle this case separately. The transition to a logarithmic scale is made because the
|
we'll need to handle this case separately. The transition to a logarithmic scale is made because the
|
||||||
pixel values have a very wide dynamic range. All this translated to OpenCV and a C++ function looks
|
pixel values have a very wide dynamic range. All this translated to OpenCV and a function looks
|
||||||
like:
|
like:
|
||||||
|
|
||||||
@add_toggle_cpp
|
@add_toggle_cpp
|
||||||
@include cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp get-psnr
|
@snippet cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp get-psnr
|
||||||
@end_toggle
|
@end_toggle
|
||||||
|
|
||||||
@add_toggle_python
|
@add_toggle_python
|
||||||
@include samples/python/tutorial_code/videoio/video-input-psnr-ssim.py get-psnr
|
@snippet samples/python/tutorial_code/videoio/video-input-psnr-ssim.py get-psnr
|
||||||
@end_toggle
|
@end_toggle
|
||||||
|
|
||||||
Typically result values are anywhere between 30 and 50 for video compression, where higher is
|
Typically result values are anywhere between 30 and 50 for video compression, where higher is
|
||||||
@@ -172,11 +171,11 @@ implementation below.
|
|||||||
Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004." article.
|
Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004." article.
|
||||||
|
|
||||||
@add_toggle_cpp
|
@add_toggle_cpp
|
||||||
@include cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp get-mssim
|
@snippet samples/cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp get-mssim
|
||||||
@end_toggle
|
@end_toggle
|
||||||
|
|
||||||
@add_toggle_python
|
@add_toggle_python
|
||||||
@include samples/python/tutorial_code/videoio/video-input-psnr-ssim.py get-mssim
|
@snippet samples/python/tutorial_code/videoio/video-input-psnr-ssim.py get-mssim
|
||||||
@end_toggle
|
@end_toggle
|
||||||
|
|
||||||
This will return a similarity index for each channel of the image. This value is between zero and
|
This will return a similarity index for each channel of the image. This value is between zero and
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ specialized video writing libraries such as *FFMpeg* or codecs as *HuffYUV*, *Co
|
|||||||
an alternative, create the video track with OpenCV and expand it with sound tracks or convert it to
|
an alternative, create the video track with OpenCV and expand it with sound tracks or convert it to
|
||||||
other formats by using video manipulation programs such as *VirtualDub* or *AviSynth*.
|
other formats by using video manipulation programs such as *VirtualDub* or *AviSynth*.
|
||||||
|
|
||||||
The *VideoWriter* class
|
The VideoWriter class
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
The content written here builds on the assumption you
|
The content written here builds on the assumption you
|
||||||
@@ -109,7 +109,7 @@ const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the n
|
|||||||
@code{.cpp}
|
@code{.cpp}
|
||||||
CV_FOURCC('P','I','M,'1') // this is an MPEG1 codec from the characters to integer
|
CV_FOURCC('P','I','M,'1') // this is an MPEG1 codec from the characters to integer
|
||||||
@endcode
|
@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:
|
the codec installed on your system and ask you to select the one to use:
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -39,3 +39,11 @@
|
|||||||
year={2013},
|
year={2013},
|
||||||
publisher={IEEE}
|
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
|
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
|
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$:
|
camera coordinates \f$x' = X_c / Z_c\f$ and \f$y' = Y_c / Z_c\f$:
|
||||||
|
|
||||||
\f[Z_c \begin{bmatrix}
|
\f[Z_c \begin{bmatrix}
|
||||||
@@ -464,6 +464,7 @@ enum SolvePnPMethod {
|
|||||||
//!< - point 1: [ squareLength / 2, squareLength / 2, 0]
|
//!< - point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||||
//!< - point 2: [ squareLength / 2, -squareLength / 2, 0]
|
//!< - point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||||
//!< - point 3: [-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
|
#ifndef CV_DOXYGEN
|
||||||
SOLVEPNP_MAX_COUNT //!< Used for count
|
SOLVEPNP_MAX_COUNT //!< Used for count
|
||||||
#endif
|
#endif
|
||||||
@@ -566,15 +567,15 @@ or vector\<Point2f\> .
|
|||||||
a vector\<Point2f\> .
|
a vector\<Point2f\> .
|
||||||
@param method Method used to compute a homography matrix. The following methods are possible:
|
@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
|
- **0** - a regular method using all the points, i.e., the least squares method
|
||||||
- **RANSAC** - RANSAC-based robust method
|
- @ref RANSAC - RANSAC-based robust method
|
||||||
- **LMEDS** - Least-Median robust method
|
- @ref LMEDS - Least-Median robust method
|
||||||
- **RHO** - PROSAC-based robust method
|
- @ref RHO - PROSAC-based robust method
|
||||||
@param ransacReprojThreshold Maximum allowed reprojection error to treat a point pair as an inlier
|
@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
|
(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]
|
\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,
|
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.
|
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.
|
mask values are ignored.
|
||||||
@param maxIters The maximum number of RANSAC iterations.
|
@param maxIters The maximum number of RANSAC iterations.
|
||||||
@param confidence Confidence level, between 0 and 1.
|
@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
|
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||||
vectors, respectively, and further optimizes them.
|
vectors, respectively, and further optimizes them.
|
||||||
@param flags Method for solving a PnP problem:
|
@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
|
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
|
of squared distances between the observed projections imagePoints and the projected (using
|
||||||
@ref projectPoints ) objectPoints .
|
@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).
|
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||||
In this case the function requires exactly four object and image points.
|
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).
|
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||||
In this case the function requires exactly four object and image points.
|
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).
|
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.
|
Method is based on the paper of J. Hesch and S. Roumeliotis.
|
||||||
"A Direct Least-Squares (DLS) Method for PnP" (@cite hesch2011direct).
|
"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,
|
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
|
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$
|
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
|
assuming that both have the same value. Then the cameraMatrix is updated with the estimated
|
||||||
focal length.
|
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.
|
"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.
|
"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:
|
It requires 4 coplanar object points defined in the following order:
|
||||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||||
- point 3: [-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
|
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
|
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
|
- 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 =
|
it as, e.g., imagePoints, one must effectively copy it into a new array: imagePoints =
|
||||||
np.ascontiguousarray(D[:,:2]).reshape((N,1,2))
|
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
|
unstable and sometimes give completely wrong results. If you pass one of these two
|
||||||
flags, **SOLVEPNP_EPNP** method will be used instead.
|
flags, @ref 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**
|
- 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
|
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).
|
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
|
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.
|
global solution to converge.
|
||||||
- With **SOLVEPNP_IPPE** input points must be >= 4 and object points must be coplanar.
|
- With @ref 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_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:
|
Number of input points must be 4. Object points must be defined in the following order:
|
||||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||||
- point 3: [-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,
|
CV_EXPORTS_W bool solvePnP( InputArray objectPoints, InputArray imagePoints,
|
||||||
InputArray cameraMatrix, InputArray distCoeffs,
|
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.
|
the model coordinate system to the camera coordinate system. A P3P problem has up to 4 solutions.
|
||||||
@param tvecs Output translation vectors.
|
@param tvecs Output translation vectors.
|
||||||
@param flags Method for solving a P3P problem:
|
@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).
|
"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).
|
"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
|
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
|
the provided rvec and tvec values as initial approximations of the rotation and translation
|
||||||
vectors, respectively, and further optimizes them.
|
vectors, respectively, and further optimizes them.
|
||||||
@param flags Method for solving a PnP problem:
|
@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
|
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
|
of squared distances between the observed projections imagePoints and the projected (using
|
||||||
projectPoints ) objectPoints .
|
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).
|
"Complete Solution Classification for the Perspective-Three-Point Problem" (@cite gao2003complete).
|
||||||
In this case the function requires exactly four object and image points.
|
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).
|
"An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (@cite Ke17).
|
||||||
In this case the function requires exactly four object and image points.
|
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).
|
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.
|
Method is based on the paper of Joel A. Hesch and Stergios I. Roumeliotis.
|
||||||
"A Direct Least-Squares (DLS) Method for PnP" (@cite hesch2011direct).
|
"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,
|
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
|
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$
|
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
|
assuming that both have the same value. Then the cameraMatrix is updated with the estimated
|
||||||
focal length.
|
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.
|
"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.
|
"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:
|
It requires 4 coplanar object points defined in the following order:
|
||||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||||
- point 3: [-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.
|
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.
|
and useExtrinsicGuess is set to true.
|
||||||
@param reprojectionError Optional vector of reprojection error, that is the RMS error
|
@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
|
(\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
|
- 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 =
|
it as, e.g., imagePoints, one must effectively copy it into a new array: imagePoints =
|
||||||
np.ascontiguousarray(D[:,:2]).reshape((N,1,2))
|
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
|
unstable and sometimes give completely wrong results. If you pass one of these two
|
||||||
flags, **SOLVEPNP_EPNP** method will be used instead.
|
flags, @ref 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**
|
- 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
|
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).
|
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
|
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.
|
global solution to converge.
|
||||||
- With **SOLVEPNP_IPPE** input points must be >= 4 and object points must be coplanar.
|
- With @ref 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_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:
|
Number of input points must be 4. Object points must be defined in the following order:
|
||||||
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
- point 0: [-squareLength / 2, squareLength / 2, 0]
|
||||||
- point 1: [ 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) ).
|
( patternSize = cvSize(points_per_row,points_per_colum) = cvSize(columns,rows) ).
|
||||||
@param corners Output array of detected corners.
|
@param corners Output array of detected corners.
|
||||||
@param flags Various operation flags that can be zero or a combination of the following values:
|
@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).
|
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.
|
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.
|
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
|
and shortcut the call if none is found. This can drastically speed up the call in the
|
||||||
degenerate condition when no chessboard is observed.
|
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) ).
|
( patternSize = Size(points_per_row, points_per_colum) ).
|
||||||
@param centers output array of detected centers.
|
@param centers output array of detected centers.
|
||||||
@param flags various operation flags that can be one of the following values:
|
@param flags various operation flags that can be one of the following values:
|
||||||
- **CALIB_CB_SYMMETRIC_GRID** uses symmetric pattern of circles.
|
- @ref CALIB_CB_SYMMETRIC_GRID uses symmetric pattern of circles.
|
||||||
- **CALIB_CB_ASYMMETRIC_GRID** uses asymmetric pattern of circles.
|
- @ref 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_CLUSTERING uses a special algorithm for grid detection. It is more robust to
|
||||||
perspective distortions but much more sensitive to background clutter.
|
perspective distortions but much more sensitive to background clutter.
|
||||||
@param blobDetector feature detector that finds blobs like dark circles on light background.
|
@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.
|
@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
|
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: :
|
Sample usage of detecting and drawing the centers of circles: :
|
||||||
@code
|
@code
|
||||||
Size patternsize(7,7); //number of centers
|
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
|
vector<Point2f> centers; //this will be filled by the detected centers
|
||||||
|
|
||||||
bool patternfound = findCirclesGrid(gray, patternsize, 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.
|
concatenated together.
|
||||||
@param imageSize Size of the image used only to initialize the camera intrinsic matrix.
|
@param imageSize Size of the image used only to initialize the camera intrinsic matrix.
|
||||||
@param cameraMatrix Input/output 3x3 floating-point camera intrinsic matrix
|
@param cameraMatrix Input/output 3x3 floating-point camera intrinsic matrix
|
||||||
\f$\cameramatrix{A}\f$ . If CV\_CALIB\_USE\_INTRINSIC\_GUESS
|
\f$\cameramatrix{A}\f$ . If @ref CALIB_USE_INTRINSIC_GUESS
|
||||||
and/or CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be
|
and/or @ref CALIB_FIX_ASPECT_RATIO are specified, some or all of fx, fy, cx, cy must be
|
||||||
initialized before calling the function.
|
initialized before calling the function.
|
||||||
@param distCoeffs Input/output vector of distortion coefficients
|
@param distCoeffs Input/output vector of distortion coefficients
|
||||||
\f$\distcoeffs\f$.
|
\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.
|
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 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:
|
@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
|
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.
|
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
|
Note, that if intrinsic parameters are known, there is no need to use this function just to
|
||||||
estimate extrinsic parameters. Use solvePnP instead.
|
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
|
optimization. It stays at the center or at a different location specified when
|
||||||
CALIB_USE_INTRINSIC_GUESS is set too.
|
@ref CALIB_USE_INTRINSIC_GUESS is set too.
|
||||||
- **CALIB_FIX_ASPECT_RATIO** The functions consider only fy as a free parameter. The
|
- @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
|
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.
|
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.
|
to zeros and stay zero.
|
||||||
- **CALIB_FIX_K1,...,CALIB_FIX_K6** The corresponding radial distortion
|
- @ref CALIB_FIX_K1,..., @ref CALIB_FIX_K6 The corresponding radial distortion
|
||||||
coefficient is not changed during the optimization. If CALIB_USE_INTRINSIC_GUESS is
|
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.
|
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
|
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
|
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.
|
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
|
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
|
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.
|
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
|
- @ref 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
|
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.
|
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
|
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
|
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.
|
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
|
- @ref 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
|
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.
|
supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
@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
|
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
|
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
|
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
|
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.
|
be used as long as initial cameraMatrix is provided.
|
||||||
|
|
||||||
@@ -1683,39 +1689,39 @@ second camera coordinate system.
|
|||||||
@param F Output fundamental matrix.
|
@param F Output fundamental matrix.
|
||||||
@param perViewErrors Output vector of the RMS re-projection error estimated for each pattern view.
|
@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:
|
@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.
|
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.
|
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).
|
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.
|
- @ref 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$ .
|
- @ref 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_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$ .
|
- @ref 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_ZERO_TANGENT_DIST Set tangential distortion coefficients for each camera to
|
||||||
zeros and fix there.
|
zeros and fix there.
|
||||||
- **CALIB_FIX_K1,...,CALIB_FIX_K6** Do not change the corresponding radial
|
- @ref CALIB_FIX_K1,..., @ref CALIB_FIX_K6 Do not change the corresponding radial
|
||||||
distortion coefficient during the optimization. If CALIB_USE_INTRINSIC_GUESS is set,
|
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.
|
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
|
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 use the rational model and return 8 coefficients. If the flag is not set, the
|
||||||
function computes and returns only 5 distortion coefficients.
|
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
|
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
|
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.
|
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
|
- @ref 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
|
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.
|
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
|
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
|
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.
|
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
|
- @ref 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
|
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.
|
supplied distCoeffs matrix is used. Otherwise, it is set to 0.
|
||||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
@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
|
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
|
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
|
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
|
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
|
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.
|
reasonable assumption.
|
||||||
|
|
||||||
Similarly to calibrateCamera, the function minimizes the total re-projection error for all the
|
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
|
camera, i.e. it projects points given in the rectified first camera coordinate system into the
|
||||||
rectified second camera's image.
|
rectified second camera's image.
|
||||||
@param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see @ref reprojectImageTo3D).
|
@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
|
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
|
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
|
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]
|
\end{bmatrix} ,\f]
|
||||||
|
|
||||||
where \f$T_x\f$ is a horizontal shift between the cameras and \f$cx_1=cx_2\f$ if
|
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
|
- **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
|
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]
|
\end{bmatrix},\f]
|
||||||
|
|
||||||
where \f$T_y\f$ is a vertical shift between the cameras and \f$cy_1=cy_2\f$ if
|
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
|
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
|
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).
|
floating-point (single or double precision).
|
||||||
@param points2 Array of the second image points of the same size and format as points1 .
|
@param points2 Array of the second image points of the same size and format as points1 .
|
||||||
@param method Method for computing a fundamental matrix.
|
@param method Method for computing a fundamental matrix.
|
||||||
- **CV_FM_7POINT** for a 7-point algorithm. \f$N = 7\f$
|
- @ref FM_7POINT for a 7-point algorithm. \f$N = 7\f$
|
||||||
- **CV_FM_8POINT** for an 8-point algorithm. \f$N \ge 8\f$
|
- @ref FM_8POINT for an 8-point algorithm. \f$N \ge 8\f$
|
||||||
- **CV_FM_RANSAC** for the RANSAC algorithm. \f$N \ge 8\f$
|
- @ref 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_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
|
@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
|
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
|
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
|
to normalized image coordinates, which are valid for the identity camera intrinsic matrix. When
|
||||||
passing these coordinates, pass the identity matrix for this parameter.
|
passing these coordinates, pass the identity matrix for this parameter.
|
||||||
@param method Method for computing an essential matrix.
|
@param method Method for computing an essential matrix.
|
||||||
- **RANSAC** for the RANSAC algorithm.
|
- @ref RANSAC for the RANSAC algorithm.
|
||||||
- **LMEDS** for the LMedS algorithm.
|
- @ref LMEDS for the LMedS algorithm.
|
||||||
@param prob Parameter used for the RANSAC or LMedS methods only. It specifies a desirable level of
|
@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.
|
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
|
@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.
|
are feature points from cameras with same focal length and principal point.
|
||||||
@param pp principal point of the camera.
|
@param pp principal point of the camera.
|
||||||
@param method Method for computing a fundamental matrix.
|
@param method Method for computing a fundamental matrix.
|
||||||
- **RANSAC** for the RANSAC algorithm.
|
- @ref RANSAC for the RANSAC algorithm.
|
||||||
- **LMEDS** for the LMedS algorithm.
|
- @ref LMEDS for the LMedS algorithm.
|
||||||
@param threshold Parameter used for RANSAC. It is the maximum distance from a point to an epipolar
|
@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
|
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
|
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 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 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:
|
@param method Robust method used to compute transformation. The following methods are possible:
|
||||||
- cv::RANSAC - RANSAC-based robust method
|
- @ref RANSAC - RANSAC-based robust method
|
||||||
- cv::LMEDS - Least-Median robust method
|
- @ref LMEDS - Least-Median robust method
|
||||||
RANSAC is the default method.
|
RANSAC is the default method.
|
||||||
@param ransacReprojThreshold Maximum reprojection error in the RANSAC algorithm to consider
|
@param ransacReprojThreshold Maximum reprojection error in the RANSAC algorithm to consider
|
||||||
a point as an inlier. Applies only to RANSAC.
|
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 to Second input 2D point set.
|
||||||
@param inliers Output vector indicating which points are inliers.
|
@param inliers Output vector indicating which points are inliers.
|
||||||
@param method Robust method used to compute transformation. The following methods are possible:
|
@param method Robust method used to compute transformation. The following methods are possible:
|
||||||
- cv::RANSAC - RANSAC-based robust method
|
- @ref RANSAC - RANSAC-based robust method
|
||||||
- cv::LMEDS - Least-Median robust method
|
- @ref LMEDS - Least-Median robust method
|
||||||
RANSAC is the default method.
|
RANSAC is the default method.
|
||||||
@param ransacReprojThreshold Maximum reprojection error in the RANSAC algorithm to consider
|
@param ransacReprojThreshold Maximum reprojection error in the RANSAC algorithm to consider
|
||||||
a point as an inlier. Applies only to RANSAC.
|
a point as an inlier. Applies only to RANSAC.
|
||||||
@@ -3006,7 +3012,8 @@ namespace fisheye
|
|||||||
CALIB_FIX_K3 = 1 << 6,
|
CALIB_FIX_K3 = 1 << 6,
|
||||||
CALIB_FIX_K4 = 1 << 7,
|
CALIB_FIX_K4 = 1 << 7,
|
||||||
CALIB_FIX_INTRINSIC = 1 << 8,
|
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
|
/** @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 image_size Size of the image used only to initialize the camera intrinsic matrix.
|
||||||
@param K Output 3x3 floating-point camera intrinsic matrix
|
@param K Output 3x3 floating-point camera intrinsic matrix
|
||||||
\f$\cameramatrix{A}\f$ . If
|
\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.
|
initialized before calling the function.
|
||||||
@param D Output vector of distortion coefficients \f$\distcoeffsfisheye\f$.
|
@param D Output vector of distortion coefficients \f$\distcoeffsfisheye\f$.
|
||||||
@param rvecs Output vector of rotation vectors (see Rodrigues ) estimated for each pattern view.
|
@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).
|
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 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:
|
@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
|
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.
|
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.
|
of intrinsic optimization.
|
||||||
- **fisheye::CALIB_CHECK_COND** The functions will check validity of condition number.
|
- @ref 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.
|
- @ref 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_FIX_K1,..., @ref fisheye::CALIB_FIX_K4 Selected distortion coefficients
|
||||||
are set to zeros and stay zero.
|
are set to zeros and stay zero.
|
||||||
- **fisheye::CALIB_FIX_PRINCIPAL_POINT** The principal point is not changed during the global
|
- @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 CALIB_USE_INTRINSIC_GUESS is set too.
|
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.
|
@param criteria Termination criteria for the iterative optimization algorithm.
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W double calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, const Size& image_size,
|
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
|
@param P2 Output 3x4 projection matrix in the new (rectified) coordinate systems for the second
|
||||||
camera.
|
camera.
|
||||||
@param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see reprojectImageTo3D ).
|
@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
|
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
|
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
|
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.
|
observed by the second camera.
|
||||||
@param K1 Input/output first camera intrinsic matrix:
|
@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
|
\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.
|
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 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 .
|
@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 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 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:
|
@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.
|
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
|
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.
|
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.
|
of intrinsic optimization.
|
||||||
- **fisheye::CALIB_CHECK_COND** The functions will check validity of condition number.
|
- @ref 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.
|
- @ref 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_FIX_K1,..., @ref fisheye::CALIB_FIX_K4 Selected distortion coefficients are set to zeros and stay
|
||||||
zero.
|
zero.
|
||||||
@param criteria Termination criteria for the iterative optimization algorithm.
|
@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,
|
bool findCirclesGrid(InputArray image, Size patternSize,
|
||||||
OutputArray centers, int flags,
|
OutputArray centers, int flags,
|
||||||
const Ptr<FeatureDetector> &blobDetector,
|
const Ptr<FeatureDetector> &blobDetector,
|
||||||
@@ -2205,15 +2198,22 @@ bool findCirclesGrid2(InputArray _image, Size patternSize,
|
|||||||
bool isSymmetricGrid = (flags & CALIB_CB_SYMMETRIC_GRID ) ? true : false;
|
bool isSymmetricGrid = (flags & CALIB_CB_SYMMETRIC_GRID ) ? true : false;
|
||||||
CV_Assert(isAsymmetricGrid ^ isSymmetricGrid);
|
CV_Assert(isAsymmetricGrid ^ isSymmetricGrid);
|
||||||
|
|
||||||
Mat image = _image.getMat();
|
|
||||||
std::vector<Point2f> centers;
|
std::vector<Point2f> centers;
|
||||||
|
|
||||||
std::vector<KeyPoint> keypoints;
|
|
||||||
blobDetector->detect(image, keypoints);
|
|
||||||
std::vector<Point2f> points;
|
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)
|
if(flags & CALIB_CB_ASYMMETRIC_GRID)
|
||||||
@@ -2229,64 +2229,59 @@ bool findCirclesGrid2(InputArray _image, Size patternSize,
|
|||||||
return !centers.empty();
|
return !centers.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isValid = false;
|
||||||
const int attempts = 2;
|
const int attempts = 2;
|
||||||
const size_t minHomographyPoints = 4;
|
const size_t minHomographyPoints = 4;
|
||||||
Mat H;
|
Mat H;
|
||||||
for (int i = 0; i < attempts; i++)
|
for (int i = 0; i < attempts; i++)
|
||||||
{
|
{
|
||||||
centers.clear();
|
centers.clear();
|
||||||
CirclesGridFinder boxFinder(patternSize, points, parameters);
|
CirclesGridFinder boxFinder(patternSize, points, parameters);
|
||||||
bool isFound = false;
|
try
|
||||||
#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)
|
|
||||||
{
|
{
|
||||||
case CirclesGridFinderParameters::SYMMETRIC_GRID:
|
bool isFound = boxFinder.findHoles();
|
||||||
boxFinder.getHoles(centers);
|
if (isFound)
|
||||||
break;
|
{
|
||||||
case CirclesGridFinderParameters::ASYMMETRIC_GRID:
|
switch(parameters.gridType)
|
||||||
boxFinder.getAsymmetricHoles(centers);
|
{
|
||||||
break;
|
case CirclesGridFinderParameters::SYMMETRIC_GRID:
|
||||||
default:
|
boxFinder.getHoles(centers);
|
||||||
CV_Error(Error::StsBadArg, "Unknown pattern type");
|
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;
|
if (centers.size() < minHomographyPoints)
|
||||||
transform(centers, orgPointsMat, H.inv());
|
break;
|
||||||
convertPointsFromHomogeneous(orgPointsMat, centers);
|
H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points);
|
||||||
}
|
}
|
||||||
Mat(centers).copyTo(_centers);
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boxFinder.getHoles(centers);
|
if (!H.empty()) // undone rectification
|
||||||
if (i != attempts - 1)
|
{
|
||||||
{
|
Mat orgPointsMat;
|
||||||
if (centers.size() < minHomographyPoints)
|
transform(centers, orgPointsMat, H.inv());
|
||||||
break;
|
convertPointsFromHomogeneous(orgPointsMat, centers);
|
||||||
H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Mat(centers).copyTo(_centers);
|
Mat(centers).copyTo(_centers);
|
||||||
return false;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool findCirclesGrid(InputArray _image, Size patternSize,
|
bool findCirclesGrid(InputArray _image, Size patternSize,
|
||||||
|
|||||||
@@ -1622,7 +1622,7 @@ size_t CirclesGridFinder::getFirstCorner(std::vector<Point> &largeCornerIndices,
|
|||||||
int cornerIdx = 0;
|
int cornerIdx = 0;
|
||||||
bool waitOutsider = true;
|
bool waitOutsider = true;
|
||||||
|
|
||||||
for(;;)
|
for (size_t i = 0; i < cornersCount * 2; ++i)
|
||||||
{
|
{
|
||||||
if (waitOutsider)
|
if (waitOutsider)
|
||||||
{
|
{
|
||||||
@@ -1632,11 +1632,11 @@ size_t CirclesGridFinder::getFirstCorner(std::vector<Point> &largeCornerIndices,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (isInsider[(cornerIdx + 1) % cornersCount])
|
if (isInsider[(cornerIdx + 1) % cornersCount])
|
||||||
break;
|
return cornerIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
cornerIdx = (cornerIdx + 1) % cornersCount;
|
cornerIdx = (cornerIdx + 1) % cornersCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cornerIdx;
|
CV_Error(Error::StsNoConv, "isInsider array has the same values");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
#include "p3p.h"
|
#include "p3p.h"
|
||||||
#include "ap3p.h"
|
#include "ap3p.h"
|
||||||
#include "ippe.hpp"
|
#include "ippe.hpp"
|
||||||
|
#include "sqpnp.hpp"
|
||||||
#include "opencv2/calib3d/calib3d_c.h"
|
#include "opencv2/calib3d/calib3d_c.h"
|
||||||
#include <opencv2/core/utils/logger.hpp>
|
#include <opencv2/core/utils/logger.hpp>
|
||||||
|
|
||||||
@@ -751,7 +752,8 @@ int solvePnPGeneric( InputArray _opoints, InputArray _ipoints,
|
|||||||
|
|
||||||
Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
|
Mat opoints = _opoints.getMat(), ipoints = _ipoints.getMat();
|
||||||
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
|
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)) );
|
&& npoints == std::max(ipoints.checkVector(2, CV_32F), ipoints.checkVector(2, CV_64F)) );
|
||||||
|
|
||||||
opoints = opoints.reshape(3, npoints);
|
opoints = opoints.reshape(3, npoints);
|
||||||
@@ -936,6 +938,14 @@ int solvePnPGeneric( InputArray _opoints, InputArray _ipoints,
|
|||||||
}
|
}
|
||||||
} catch (...) { }
|
} 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)
|
/*else if (flags == SOLVEPNP_DLS)
|
||||||
{
|
{
|
||||||
Mat undistortedPoints;
|
Mat undistortedPoints;
|
||||||
@@ -963,7 +973,8 @@ int solvePnPGeneric( InputArray _opoints, InputArray _ipoints,
|
|||||||
vec_tvecs.push_back(tvec);
|
vec_tvecs.push_back(tvec);
|
||||||
}*/
|
}*/
|
||||||
else
|
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());
|
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);
|
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
|
}} // namespace
|
||||||
/* End of file. */
|
/* End of file. */
|
||||||
|
|||||||
@@ -153,9 +153,8 @@ void CV_ChessboardSubpixelTest::run( int )
|
|||||||
|
|
||||||
vector<Point2f> test_corners;
|
vector<Point2f> test_corners;
|
||||||
bool result = findChessboardCorners(chessboard_image, pattern_size, test_corners, 15);
|
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, "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, "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",
|
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));
|
distortion_coeffs_.at<double>(0, 4));
|
||||||
|
|
||||||
imwrite("test.png", chessboard_image);
|
imwrite("test.png", chessboard_image);
|
||||||
#endif
|
}
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -390,6 +390,12 @@ TEST_F(fisheyeTest, EstimateUncertainties)
|
|||||||
|
|
||||||
TEST_F(fisheyeTest, stereoRectify)
|
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");
|
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||||
|
|
||||||
cv::Size calibration_size = this->imageSize, requested_size = calibration_size;
|
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;
|
double balance = 0.0, fov_scale = 1.1;
|
||||||
cv::Mat R1, R2, P1, P2, Q;
|
cv::Mat R1, R2, P1, P2, Q;
|
||||||
cv::fisheye::stereoRectify(K1, D1, K2, D2, calibration_size, theR, theT, 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
|
// Collected with these CMake flags: -DWITH_IPP=OFF -DCV_ENABLE_INTRINSICS=OFF -DCV_DISABLE_OPTIMIZATION=ON -DCMAKE_BUILD_TYPE=Debug
|
||||||
cv::Matx33d R1_ref(
|
cv::Matx33d R1_ref(
|
||||||
@@ -449,7 +455,10 @@ TEST_F(fisheyeTest, stereoRectify)
|
|||||||
<< "Q =" << std::endl << Q << std::endl;
|
<< "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;
|
cv::Mat lmapx, lmapy, rmapx, rmapy;
|
||||||
//rewrite for fisheye
|
//rewrite for fisheye
|
||||||
cv::fisheye::initUndistortRectifyMap(K1, D1, R1, P1, requested_size, CV_32F, lmapx, lmapy);
|
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);
|
cv::imwrite(cv::format("fisheye_rectification_AB_%03d.png", i), rectification);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(fisheyeTest, stereoCalibrate)
|
TEST_F(fisheyeTest, stereoCalibrate)
|
||||||
|
|||||||
@@ -190,6 +190,8 @@ static std::string printMethod(int method)
|
|||||||
return "SOLVEPNP_IPPE";
|
return "SOLVEPNP_IPPE";
|
||||||
case 7:
|
case 7:
|
||||||
return "SOLVEPNP_IPPE_SQUARE";
|
return "SOLVEPNP_IPPE_SQUARE";
|
||||||
|
case 8:
|
||||||
|
return "SOLVEPNP_SQPNP";
|
||||||
default:
|
default:
|
||||||
return "Unknown value";
|
return "Unknown value";
|
||||||
}
|
}
|
||||||
@@ -206,6 +208,7 @@ public:
|
|||||||
eps[SOLVEPNP_AP3P] = 1.0e-2;
|
eps[SOLVEPNP_AP3P] = 1.0e-2;
|
||||||
eps[SOLVEPNP_DLS] = 1.0e-2;
|
eps[SOLVEPNP_DLS] = 1.0e-2;
|
||||||
eps[SOLVEPNP_UPNP] = 1.0e-2;
|
eps[SOLVEPNP_UPNP] = 1.0e-2;
|
||||||
|
eps[SOLVEPNP_SQPNP] = 1.0e-2;
|
||||||
totalTestsCount = 10;
|
totalTestsCount = 10;
|
||||||
pointsCount = 500;
|
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_UPNP] = 1.0e-6; //UPnP is remapped to EPnP, so we use the same threshold
|
||||||
eps[SOLVEPNP_IPPE] = 1.0e-6;
|
eps[SOLVEPNP_IPPE] = 1.0e-6;
|
||||||
eps[SOLVEPNP_IPPE_SQUARE] = 1.0e-6;
|
eps[SOLVEPNP_IPPE_SQUARE] = 1.0e-6;
|
||||||
|
eps[SOLVEPNP_SQPNP] = 1.0e-6;
|
||||||
|
|
||||||
totalTestsCount = 1000;
|
totalTestsCount = 1000;
|
||||||
|
|
||||||
|
|||||||
@@ -82,16 +82,24 @@ option(OPENCV_ENABLE_ALLOCATOR_STATS "Enable Allocator metrics" ON)
|
|||||||
|
|
||||||
if(NOT OPENCV_ENABLE_ALLOCATOR_STATS)
|
if(NOT OPENCV_ENABLE_ALLOCATOR_STATS)
|
||||||
add_definitions(-DOPENCV_DISABLE_ALLOCATOR_STATS=1)
|
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(NOT DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE)
|
||||||
if(HAVE_ATOMIC_LONG_LONG AND OPENCV_ENABLE_ATOMIC_LONG_LONG)
|
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()
|
else()
|
||||||
set(OPENCV_ALLOCATOR_STATS_COUNTER_TYPE "int")
|
set(OPENCV_ALLOCATOR_STATS_COUNTER_TYPE "int")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Allocator metrics storage type: '${OPENCV_ALLOCATOR_STATS_COUNTER_TYPE}'")
|
if(DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE)
|
||||||
add_definitions("-DOPENCV_ALLOCATOR_STATS_COUNTER_TYPE=${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()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user