mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
OpenCV 3.4.2
This commit is contained in:
commit
9e1b1e5389
2
3rdparty/carotene/hal/tegra_hal.hpp
vendored
2
3rdparty/carotene/hal/tegra_hal.hpp
vendored
@ -1531,7 +1531,7 @@ class TegraCvtColor_##name##_Invoker : public cv::ParallelLoopBody \
|
|||||||
public: \
|
public: \
|
||||||
TegraCvtColor_##name##_Invoker(const uchar * src_data_, size_t src_step_, uchar * dst_data_, size_t dst_step_, int width_, int height_) : \
|
TegraCvtColor_##name##_Invoker(const uchar * src_data_, size_t src_step_, uchar * dst_data_, size_t dst_step_, int width_, int height_) : \
|
||||||
cv::ParallelLoopBody(), src_data(src_data_), src_step(src_step_), dst_data(dst_data_), dst_step(dst_step_), width(width_), height(height_) {} \
|
cv::ParallelLoopBody(), src_data(src_data_), src_step(src_step_), dst_data(dst_data_), dst_step(dst_step_), width(width_), height(height_) {} \
|
||||||
virtual void operator()(const cv::Range& range) const \
|
virtual void operator()(const cv::Range& range) const CV_OVERRIDE \
|
||||||
{ \
|
{ \
|
||||||
CAROTENE_NS::func(CAROTENE_NS::Size2D(width, range.end-range.start), __VA_ARGS__); \
|
CAROTENE_NS::func(CAROTENE_NS::Size2D(width, range.end-range.start), __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
|
@ -1,6 +1,39 @@
|
|||||||
add_definitions(-D__OPENCV_BUILD=1)
|
add_definitions(-D__OPENCV_BUILD=1)
|
||||||
add_definitions(-D__OPENCV_APPS=1)
|
add_definitions(-D__OPENCV_APPS=1)
|
||||||
|
|
||||||
|
# Unified function for creating OpenCV applications:
|
||||||
|
# ocv_add_application(tgt [MODULES <m1> [<m2> ...]] SRCS <src1> [<src2> ...])
|
||||||
|
function(ocv_add_application the_target)
|
||||||
|
cmake_parse_arguments(APP "" "" "MODULES;SRCS" ${ARGN})
|
||||||
|
ocv_check_dependencies(${APP_MODULES})
|
||||||
|
if(NOT OCV_DEPENDENCIES_FOUND)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project(${the_target})
|
||||||
|
ocv_target_include_modules_recurse(${the_target} ${APP_MODULES})
|
||||||
|
ocv_target_include_directories(${the_target} PRIVATE "${OpenCV_SOURCE_DIR}/include/opencv")
|
||||||
|
ocv_add_executable(${the_target} ${APP_SRCS})
|
||||||
|
ocv_target_link_libraries(${the_target} ${APP_MODULES})
|
||||||
|
set_target_properties(${the_target} PROPERTIES
|
||||||
|
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
||||||
|
OUTPUT_NAME "${the_target}")
|
||||||
|
|
||||||
|
if(ENABLE_SOLUTION_FOLDERS)
|
||||||
|
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(INSTALL_CREATE_DISTRIB)
|
||||||
|
if(BUILD_SHARED_LIBS)
|
||||||
|
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
link_libraries(${OPENCV_LINKER_LIBS})
|
link_libraries(${OPENCV_LINKER_LIBS})
|
||||||
|
|
||||||
macro(ocv_add_app directory)
|
macro(ocv_add_app directory)
|
||||||
|
@ -1,36 +1,3 @@
|
|||||||
SET(OPENCV_ANNOTATION_DEPS opencv_core opencv_highgui opencv_imgproc opencv_imgcodecs opencv_videoio)
|
ocv_add_application(opencv_annotation
|
||||||
ocv_check_dependencies(${OPENCV_ANNOTATION_DEPS})
|
MODULES opencv_core opencv_highgui opencv_imgproc opencv_imgcodecs opencv_videoio
|
||||||
|
SRCS opencv_annotation.cpp)
|
||||||
if(NOT OCV_DEPENDENCIES_FOUND)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(annotation)
|
|
||||||
set(the_target opencv_annotation)
|
|
||||||
|
|
||||||
ocv_target_include_directories(${the_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${OpenCV_SOURCE_DIR}/include/opencv")
|
|
||||||
ocv_target_include_modules_recurse(${the_target} ${OPENCV_ANNOTATION_DEPS})
|
|
||||||
|
|
||||||
file(GLOB SRCS *.cpp)
|
|
||||||
|
|
||||||
set(annotation_files ${SRCS})
|
|
||||||
ocv_add_executable(${the_target} ${annotation_files})
|
|
||||||
ocv_target_link_libraries(${the_target} ${OPENCV_ANNOTATION_DEPS})
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES
|
|
||||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
|
||||||
OUTPUT_NAME "opencv_annotation")
|
|
||||||
|
|
||||||
if(ENABLE_SOLUTION_FOLDERS)
|
|
||||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(INSTALL_CREATE_DISTRIB)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
|
@ -1,38 +1,4 @@
|
|||||||
set(OPENCV_CREATESAMPLES_DEPS opencv_core opencv_imgproc opencv_objdetect opencv_imgcodecs opencv_highgui opencv_calib3d opencv_features2d opencv_videoio)
|
|
||||||
ocv_check_dependencies(${OPENCV_CREATESAMPLES_DEPS})
|
|
||||||
|
|
||||||
if(NOT OCV_DEPENDENCIES_FOUND)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(createsamples)
|
|
||||||
set(the_target opencv_createsamples)
|
|
||||||
|
|
||||||
ocv_target_include_directories(${the_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${OpenCV_SOURCE_DIR}/include/opencv")
|
|
||||||
ocv_target_include_modules_recurse(${the_target} ${OPENCV_CREATESAMPLES_DEPS})
|
|
||||||
|
|
||||||
file(GLOB SRCS *.cpp)
|
file(GLOB SRCS *.cpp)
|
||||||
file(GLOB HDRS *.h*)
|
ocv_add_application(opencv_createsamples
|
||||||
|
MODULES opencv_core opencv_imgproc opencv_objdetect opencv_imgcodecs opencv_highgui opencv_calib3d opencv_features2d opencv_videoio
|
||||||
set(createsamples_files ${SRCS} ${HDRS})
|
SRCS ${SRCS})
|
||||||
|
|
||||||
ocv_add_executable(${the_target} ${createsamples_files})
|
|
||||||
ocv_target_link_libraries(${the_target} ${OPENCV_CREATESAMPLES_DEPS})
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES
|
|
||||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
|
||||||
OUTPUT_NAME "opencv_createsamples")
|
|
||||||
|
|
||||||
if(ENABLE_SOLUTION_FOLDERS)
|
|
||||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(INSTALL_CREATE_DISTRIB)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
install(TARGETS ${the_target} OPTIONAL RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
|
@ -1,41 +1,6 @@
|
|||||||
set(OPENCV_INTERACTIVECALIBRATION_DEPS opencv_core opencv_imgproc opencv_features2d opencv_highgui opencv_calib3d opencv_videoio)
|
set(DEPS opencv_core opencv_imgproc opencv_features2d opencv_highgui opencv_calib3d opencv_videoio)
|
||||||
if(${BUILD_opencv_aruco})
|
if(${BUILD_opencv_aruco})
|
||||||
list(APPEND OPENCV_INTERACTIVECALIBRATION_DEPS opencv_aruco)
|
list(APPEND DEPS opencv_aruco)
|
||||||
endif()
|
endif()
|
||||||
ocv_check_dependencies(${OPENCV_INTERACTIVECALIBRATION_DEPS})
|
|
||||||
|
|
||||||
if(NOT OCV_DEPENDENCIES_FOUND)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(interactive-calibration)
|
|
||||||
set(the_target opencv_interactive-calibration)
|
|
||||||
|
|
||||||
ocv_target_include_directories(${the_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${OpenCV_SOURCE_DIR}/include/opencv")
|
|
||||||
ocv_target_include_modules_recurse(${the_target} ${OPENCV_INTERACTIVECALIBRATION_DEPS})
|
|
||||||
|
|
||||||
file(GLOB SRCS *.cpp)
|
file(GLOB SRCS *.cpp)
|
||||||
file(GLOB HDRS *.h*)
|
ocv_add_application(opencv_interactive-calibration MODULES ${DEPS} SRCS ${SRCS})
|
||||||
|
|
||||||
set(interactive-calibration_files ${SRCS} ${HDRS})
|
|
||||||
|
|
||||||
ocv_add_executable(${the_target} ${interactive-calibration_files})
|
|
||||||
ocv_target_link_libraries(${the_target} ${OPENCV_INTERACTIVECALIBRATION_DEPS})
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES
|
|
||||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
|
||||||
OUTPUT_NAME "opencv_interactive-calibration")
|
|
||||||
|
|
||||||
if(ENABLE_SOLUTION_FOLDERS)
|
|
||||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(INSTALL_CREATE_DISTRIB)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
install(TARGETS ${the_target} OPTIONAL RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
|
@ -1,42 +1,5 @@
|
|||||||
set(OPENCV_TRAINCASCADE_DEPS opencv_core opencv_imgproc opencv_objdetect opencv_imgcodecs opencv_highgui opencv_calib3d opencv_features2d)
|
ocv_warnings_disable(CMAKE_CXX_FLAGS -Woverloaded-virtual -Winconsistent-missing-override -Wsuggest-override)
|
||||||
ocv_check_dependencies(${OPENCV_TRAINCASCADE_DEPS})
|
|
||||||
|
|
||||||
if(NOT OCV_DEPENDENCIES_FOUND)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(traincascade)
|
|
||||||
set(the_target opencv_traincascade)
|
|
||||||
|
|
||||||
ocv_warnings_disable(CMAKE_CXX_FLAGS -Woverloaded-virtual
|
|
||||||
-Winconsistent-missing-override -Wsuggest-override
|
|
||||||
)
|
|
||||||
|
|
||||||
ocv_target_include_directories(${the_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${OpenCV_SOURCE_DIR}/include/opencv")
|
|
||||||
ocv_target_include_modules_recurse(${the_target} ${OPENCV_TRAINCASCADE_DEPS})
|
|
||||||
|
|
||||||
file(GLOB SRCS *.cpp)
|
file(GLOB SRCS *.cpp)
|
||||||
file(GLOB HDRS *.h*)
|
ocv_add_application(opencv_traincascade
|
||||||
|
MODULES opencv_core opencv_imgproc opencv_objdetect opencv_imgcodecs opencv_highgui opencv_calib3d opencv_features2d
|
||||||
set(traincascade_files ${SRCS} ${HDRS})
|
SRCS ${SRCS})
|
||||||
|
|
||||||
ocv_add_executable(${the_target} ${traincascade_files})
|
|
||||||
ocv_target_link_libraries(${the_target} ${OPENCV_TRAINCASCADE_DEPS})
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES
|
|
||||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
|
||||||
OUTPUT_NAME "opencv_traincascade")
|
|
||||||
|
|
||||||
if(ENABLE_SOLUTION_FOLDERS)
|
|
||||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(INSTALL_CREATE_DISTRIB)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
install(TARGETS ${the_target} OPTIONAL RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
|
@ -1,49 +1,5 @@
|
|||||||
set(OPENCV_APPLICATION_DEPS opencv_core)
|
ocv_add_application(opencv_version MODULES opencv_core SRCS opencv_version.cpp)
|
||||||
ocv_check_dependencies(${OPENCV_APPLICATION_DEPS})
|
|
||||||
if(NOT OCV_DEPENDENCIES_FOUND)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(opencv_version)
|
|
||||||
set(the_target opencv_version)
|
|
||||||
ocv_target_include_modules_recurse(${the_target} ${OPENCV_APPLICATION_DEPS})
|
|
||||||
ocv_add_executable(${the_target} opencv_version.cpp)
|
|
||||||
ocv_target_link_libraries(${the_target} ${OPENCV_APPLICATION_DEPS})
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES
|
|
||||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
|
||||||
OUTPUT_NAME "opencv_version")
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
|
||||||
|
|
||||||
if(INSTALL_CREATE_DISTRIB)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT libs)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
project(opencv_version_win32)
|
ocv_add_application(opencv_version_win32 MODULES opencv_core SRCS opencv_version.cpp)
|
||||||
set(the_target opencv_version_win32)
|
target_compile_definitions(opencv_version_win32 PRIVATE "OPENCV_WIN32_API=1")
|
||||||
ocv_target_include_modules_recurse(${the_target} ${OPENCV_APPLICATION_DEPS})
|
|
||||||
ocv_add_executable(${the_target} opencv_version.cpp)
|
|
||||||
ocv_target_link_libraries(${the_target} ${OPENCV_APPLICATION_DEPS})
|
|
||||||
target_compile_definitions(${the_target} PRIVATE "OPENCV_WIN32_API=1")
|
|
||||||
set_target_properties(${the_target} PROPERTIES
|
|
||||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
|
||||||
OUTPUT_NAME "opencv_version_win32")
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
|
||||||
|
|
||||||
if(INSTALL_CREATE_DISTRIB)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT libs)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT libs)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,36 +1,3 @@
|
|||||||
SET(OPENCV_VISUALISATION_DEPS opencv_core opencv_highgui opencv_imgproc opencv_videoio opencv_imgcodecs)
|
ocv_add_application(opencv_visualisation
|
||||||
ocv_check_dependencies(${OPENCV_VISUALISATION_DEPS})
|
MODULES opencv_core opencv_highgui opencv_imgproc opencv_videoio opencv_imgcodecs
|
||||||
|
SRCS opencv_visualisation.cpp)
|
||||||
if(NOT OCV_DEPENDENCIES_FOUND)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(visualisation)
|
|
||||||
set(the_target opencv_visualisation)
|
|
||||||
|
|
||||||
ocv_target_include_directories(${the_target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${OpenCV_SOURCE_DIR}/include/opencv")
|
|
||||||
ocv_target_include_modules_recurse(${the_target} ${OPENCV_VISUALISATION_DEPS})
|
|
||||||
|
|
||||||
file(GLOB SRCS *.cpp)
|
|
||||||
|
|
||||||
set(visualisation_files ${SRCS})
|
|
||||||
ocv_add_executable(${the_target} ${visualisation_files})
|
|
||||||
ocv_target_link_libraries(${the_target} ${OPENCV_VISUALISATION_DEPS})
|
|
||||||
|
|
||||||
set_target_properties(${the_target} PROPERTIES
|
|
||||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
|
|
||||||
OUTPUT_NAME "opencv_visualisation")
|
|
||||||
|
|
||||||
if(ENABLE_SOLUTION_FOLDERS)
|
|
||||||
set_target_properties(${the_target} PROPERTIES FOLDER "applications")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(INSTALL_CREATE_DISTRIB)
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} CONFIGURATIONS Release COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
install(TARGETS ${the_target} RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT dev)
|
|
||||||
endif()
|
|
||||||
|
@ -1624,7 +1624,7 @@ endif()
|
|||||||
|
|
||||||
macro(ocv_git_describe var_name path)
|
macro(ocv_git_describe var_name path)
|
||||||
if(GIT_FOUND)
|
if(GIT_FOUND)
|
||||||
execute_process(COMMAND "${GIT_EXECUTABLE}" describe --tags --tags --exact-match --dirty
|
execute_process(COMMAND "${GIT_EXECUTABLE}" describe --tags --exact-match --dirty
|
||||||
WORKING_DIRECTORY "${path}"
|
WORKING_DIRECTORY "${path}"
|
||||||
OUTPUT_VARIABLE ${var_name}
|
OUTPUT_VARIABLE ${var_name}
|
||||||
RESULT_VARIABLE GIT_RESULT
|
RESULT_VARIABLE GIT_RESULT
|
||||||
|
@ -4,32 +4,34 @@ Camera Calibration {#tutorial_py_calibration}
|
|||||||
Goal
|
Goal
|
||||||
----
|
----
|
||||||
|
|
||||||
In this section,
|
In this section, we will learn about
|
||||||
- We will learn about distortions in camera, intrinsic and extrinsic parameters of camera etc.
|
|
||||||
- We will learn to find these parameters, undistort images etc.
|
* types of distortion caused by cameras
|
||||||
|
* how to find the intrinsic and extrinsic properties of a camera
|
||||||
|
* how to undistort images based off these properties
|
||||||
|
|
||||||
Basics
|
Basics
|
||||||
------
|
------
|
||||||
|
|
||||||
Today's cheap pinhole cameras introduces a lot of distortion to images. Two major distortions are
|
Some pinhole cameras introduce significant distortion to images. Two major kinds of distortion are
|
||||||
radial distortion and tangential distortion.
|
radial distortion and tangential distortion.
|
||||||
|
|
||||||
Due to radial distortion, straight lines will appear curved. Its effect is more as we move away from
|
Radial distortion causes straight lines to appear curved. Radial distortion becomes larger the farther points are from
|
||||||
the center of image. For example, one image is shown below, where two edges of a chess board are
|
the center of the image. For example, one image is shown below in which two edges of a chess board are
|
||||||
marked with red lines. But you can see that border is not a straight line and doesn't match with the
|
marked with red lines. But, you can see that the border of the chess board is not a straight line and doesn't match with the
|
||||||
red line. All the expected straight lines are bulged out. Visit [Distortion
|
red line. All the expected straight lines are bulged out. Visit [Distortion
|
||||||
(optics)](http://en.wikipedia.org/wiki/Distortion_%28optics%29) for more details.
|
(optics)](http://en.wikipedia.org/wiki/Distortion_%28optics%29) for more details.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This distortion is represented as follows:
|
Radial distortion can be represented as follows:
|
||||||
|
|
||||||
\f[x_{distorted} = x( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\
|
\f[x_{distorted} = x( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6) \\
|
||||||
y_{distorted} = y( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6)\f]
|
y_{distorted} = y( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6)\f]
|
||||||
|
|
||||||
Similarly, another distortion is the tangential distortion which occurs because image taking lense
|
Similarly, tangential distortion occurs because the image-taking lense
|
||||||
is not aligned perfectly parallel to the imaging plane. So some areas in image may look nearer than
|
is not aligned perfectly parallel to the imaging plane. So, some areas in the image may look nearer than
|
||||||
expected. It is represented as below:
|
expected. The amount of tangential distortion can be represented as below:
|
||||||
|
|
||||||
\f[x_{distorted} = x + [ 2p_1xy + p_2(r^2+2x^2)] \\
|
\f[x_{distorted} = x + [ 2p_1xy + p_2(r^2+2x^2)] \\
|
||||||
y_{distorted} = y + [ p_1(r^2+ 2y^2)+ 2p_2xy]\f]
|
y_{distorted} = y + [ p_1(r^2+ 2y^2)+ 2p_2xy]\f]
|
||||||
@ -38,10 +40,9 @@ In short, we need to find five parameters, known as distortion coefficients give
|
|||||||
|
|
||||||
\f[Distortion \; coefficients=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3)\f]
|
\f[Distortion \; coefficients=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3)\f]
|
||||||
|
|
||||||
In addition to this, we need to find a few more information, like intrinsic and extrinsic parameters
|
In addition to this, we need to some other information, like the intrinsic and extrinsic parameters
|
||||||
of a camera. Intrinsic parameters are specific to a camera. It includes information like focal
|
of the camera. Intrinsic parameters are specific to a camera. They include information like focal
|
||||||
length (\f$f_x,f_y\f$), optical centers (\f$c_x, c_y\f$) etc. It is also called camera matrix. It depends on
|
length (\f$f_x,f_y\f$) and optical centers (\f$c_x, c_y\f$). The focal length and optical centers can be used to create a camera matrix, which can be used to remove distortion due to the lenses of a specific camera. The camera matrix is unique to a specific camera, so once calculated, it can be reused on other images taken by the same camera. It is expressed as a 3x3
|
||||||
the camera only, so once calculated, it can be stored for future purposes. It is expressed as a 3x3
|
|
||||||
matrix:
|
matrix:
|
||||||
|
|
||||||
\f[camera \; matrix = \left [ \begin{matrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix} \right ]\f]
|
\f[camera \; matrix = \left [ \begin{matrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{matrix} \right ]\f]
|
||||||
@ -49,20 +50,16 @@ matrix:
|
|||||||
Extrinsic parameters corresponds to rotation and translation vectors which translates a coordinates
|
Extrinsic parameters corresponds to rotation and translation vectors which translates a coordinates
|
||||||
of a 3D point to a coordinate system.
|
of a 3D point to a coordinate system.
|
||||||
|
|
||||||
For stereo applications, these distortions need to be corrected first. To find all these parameters,
|
For stereo applications, these distortions need to be corrected first. To find these parameters,
|
||||||
what we have to do is to provide some sample images of a well defined pattern (eg, chess board). We
|
we must provide some sample images of a well defined pattern (e.g. a chess board). We
|
||||||
find some specific points in it ( square corners in chess board). We know its coordinates in real
|
find some specific points of which we already know the relative positions (e.g. square corners in the chess board). We know the coordinates of these points in real world space and we know the coordinates in the image, so we can solve for the distortion coefficients. For better results, we need at least 10 test patterns.
|
||||||
world space and we know its coordinates in image. With these data, some mathematical problem is
|
|
||||||
solved in background to get the distortion coefficients. That is the summary of the whole story. For
|
|
||||||
better results, we need atleast 10 test patterns.
|
|
||||||
|
|
||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
As mentioned above, we need atleast 10 test patterns for camera calibration. OpenCV comes with some
|
As mentioned above, we need at least 10 test patterns for camera calibration. OpenCV comes with some
|
||||||
images of chess board (see samples/cpp/left01.jpg -- left14.jpg), so we will utilize it. For sake of
|
images of a chess board (see samples/data/left01.jpg -- left14.jpg), so we will utilize these. Consider an image of a chess board. The important input data needed for calibration of the camera
|
||||||
understanding, consider just one image of a chess board. Important input datas needed for camera
|
is the set of 3D real world points and the corresponding 2D coordinates of these points in the image. 2D image points
|
||||||
calibration is a set of 3D real world points and its corresponding 2D image points. 2D image points
|
|
||||||
are OK which we can easily find from the image. (These image points are locations where two black
|
are OK which we can easily find from the image. (These image points are locations where two black
|
||||||
squares touch each other in chess boards)
|
squares touch each other in chess boards)
|
||||||
|
|
||||||
@ -72,7 +69,7 @@ values. But for simplicity, we can say chess board was kept stationary at XY pla
|
|||||||
and camera was moved accordingly. This consideration helps us to find only X,Y values. Now for X,Y
|
and camera was moved accordingly. This consideration helps us to find only X,Y values. Now for X,Y
|
||||||
values, we can simply pass the points as (0,0), (1,0), (2,0), ... which denotes the location of
|
values, we can simply pass the points as (0,0), (1,0), (2,0), ... which denotes the location of
|
||||||
points. In this case, the results we get will be in the scale of size of chess board square. But if
|
points. In this case, the results we get will be in the scale of size of chess board square. But if
|
||||||
we know the square size, (say 30 mm), and we can pass the values as (0,0),(30,0),(60,0),..., we get
|
we know the square size, (say 30 mm), we can pass the values as (0,0), (30,0), (60,0), ... . Thus, we get
|
||||||
the results in mm. (In this case, we don't know square size since we didn't take those images, so we
|
the results in mm. (In this case, we don't know square size since we didn't take those images, so we
|
||||||
pass in terms of square size).
|
pass in terms of square size).
|
||||||
|
|
||||||
@ -80,23 +77,22 @@ pass in terms of square size).
|
|||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
So to find pattern in chess board, we use the function, **cv.findChessboardCorners()**. We also
|
So to find pattern in chess board, we can use the function, **cv.findChessboardCorners()**. We also
|
||||||
need to pass what kind of pattern we are looking, like 8x8 grid, 5x5 grid etc. In this example, we
|
need to pass what kind of pattern we are looking for, like 8x8 grid, 5x5 grid etc. In this example, we
|
||||||
use 7x6 grid. (Normally a chess board has 8x8 squares and 7x7 internal corners). It returns the
|
use 7x6 grid. (Normally a chess board has 8x8 squares and 7x7 internal corners). It returns the
|
||||||
corner points and retval which will be True if pattern is obtained. These corners will be placed in
|
corner points and retval which will be True if pattern is obtained. These corners will be placed in
|
||||||
an order (from left-to-right, top-to-bottom)
|
an order (from left-to-right, top-to-bottom)
|
||||||
|
|
||||||
@sa This function may not be able to find the required pattern in all the images. So one good option
|
@sa This function may not be able to find the required pattern in all the images. So, one good option
|
||||||
is to write the code such that, it starts the camera and check each frame for required pattern. Once
|
is to write the code such that, it starts the camera and check each frame for required pattern. Once
|
||||||
pattern is obtained, find the corners and store it in a list. Also provides some interval before
|
the pattern is obtained, find the corners and store it in a list. Also, provide some interval before
|
||||||
reading next frame so that we can adjust our chess board in different direction. Continue this
|
reading next frame so that we can adjust our chess board in different direction. Continue this
|
||||||
process until required number of good patterns are obtained. Even in the example provided here, we
|
process until the required number of good patterns are obtained. Even in the example provided here, we
|
||||||
are not sure out of 14 images given, how many are good. So we read all the images and take the good
|
are not sure how many images out of the 14 given are good. Thus, we must read all the images and take only the good
|
||||||
ones.
|
ones.
|
||||||
|
|
||||||
@sa Instead of chess board, we can use some circular grid, but then use the function
|
@sa Instead of chess board, we can alternatively use a circular grid. In this case, we must use the function
|
||||||
**cv.findCirclesGrid()** to find the pattern. It is said that less number of images are enough when
|
**cv.findCirclesGrid()** to find the pattern. Fewer images are sufficient to perform camera calibration using a circular grid.
|
||||||
using circular grid.
|
|
||||||
|
|
||||||
Once we find the corners, we can increase their accuracy using **cv.cornerSubPix()**. We can also
|
Once we find the corners, we can increase their accuracy using **cv.cornerSubPix()**. We can also
|
||||||
draw the pattern using **cv.drawChessboardCorners()**. All these steps are included in below code:
|
draw the pattern using **cv.drawChessboardCorners()**. All these steps are included in below code:
|
||||||
@ -146,22 +142,23 @@ One image with pattern drawn on it is shown below:
|
|||||||
|
|
||||||
### Calibration
|
### Calibration
|
||||||
|
|
||||||
So now we have our object points and image points we are ready to go for calibration. For that we
|
Now that we have our object points and image points, we are ready to go for calibration. We can
|
||||||
use the function, **cv.calibrateCamera()**. It returns the camera matrix, distortion coefficients,
|
use the function, **cv.calibrateCamera()** which returns the camera matrix, distortion coefficients,
|
||||||
rotation and translation vectors etc.
|
rotation and translation vectors etc.
|
||||||
@code{.py}
|
@code{.py}
|
||||||
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
|
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
### Undistortion
|
### Undistortion
|
||||||
|
|
||||||
We have got what we were trying. Now we can take an image and undistort it. OpenCV comes with two
|
Now, we can take an image and undistort it. OpenCV comes with two
|
||||||
methods, we will see both. But before that, we can refine the camera matrix based on a free scaling
|
methods for doing this. However first, we can refine the camera matrix based on a free scaling
|
||||||
parameter using **cv.getOptimalNewCameraMatrix()**. If the scaling parameter alpha=0, it returns
|
parameter using **cv.getOptimalNewCameraMatrix()**. If the scaling parameter alpha=0, it returns
|
||||||
undistorted image with minimum unwanted pixels. So it may even remove some pixels at image corners.
|
undistorted image with minimum unwanted pixels. So it may even remove some pixels at image corners.
|
||||||
If alpha=1, all pixels are retained with some extra black images. It also returns an image ROI which
|
If alpha=1, all pixels are retained with some extra black images. This function also returns an image ROI which
|
||||||
can be used to crop the result.
|
can be used to crop the result.
|
||||||
|
|
||||||
So we take a new image (left12.jpg in this case. That is the first image in this chapter)
|
So, we take a new image (left12.jpg in this case. That is the first image in this chapter)
|
||||||
@code{.py}
|
@code{.py}
|
||||||
img = cv.imread('left12.jpg')
|
img = cv.imread('left12.jpg')
|
||||||
h, w = img.shape[:2]
|
h, w = img.shape[:2]
|
||||||
@ -169,7 +166,7 @@ newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
|
|||||||
@endcode
|
@endcode
|
||||||
#### 1. Using **cv.undistort()**
|
#### 1. Using **cv.undistort()**
|
||||||
|
|
||||||
This is the shortest path. Just call the function and use ROI obtained above to crop the result.
|
This is the easiest way. Just call the function and use ROI obtained above to crop the result.
|
||||||
@code{.py}
|
@code{.py}
|
||||||
# undistort
|
# undistort
|
||||||
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
|
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
|
||||||
@ -181,7 +178,7 @@ cv.imwrite('calibresult.png', dst)
|
|||||||
@endcode
|
@endcode
|
||||||
#### 2. Using **remapping**
|
#### 2. Using **remapping**
|
||||||
|
|
||||||
This is curved path. First find a mapping function from distorted image to undistorted image. Then
|
This way is a little bit more difficult. First, find a mapping function from the distorted image to the undistorted image. Then
|
||||||
use the remap function.
|
use the remap function.
|
||||||
@code{.py}
|
@code{.py}
|
||||||
# undistort
|
# undistort
|
||||||
@ -193,23 +190,22 @@ x, y, w, h = roi
|
|||||||
dst = dst[y:y+h, x:x+w]
|
dst = dst[y:y+h, x:x+w]
|
||||||
cv.imwrite('calibresult.png', dst)
|
cv.imwrite('calibresult.png', dst)
|
||||||
@endcode
|
@endcode
|
||||||
Both the methods give the same result. See the result below:
|
Still, both the methods give the same result. See the result below:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
You can see in the result that all the edges are straight.
|
You can see in the result that all the edges are straight.
|
||||||
|
|
||||||
Now you can store the camera matrix and distortion coefficients using write functions in Numpy
|
Now you can store the camera matrix and distortion coefficients using write functions in NumPy
|
||||||
(np.savez, np.savetxt etc) for future uses.
|
(np.savez, np.savetxt etc) for future uses.
|
||||||
|
|
||||||
Re-projection Error
|
Re-projection Error
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Re-projection error gives a good estimation of just how exact is the found parameters. This should
|
Re-projection error gives a good estimation of just how exact the found parameters are. The closer the re-projection error is to zero, the more accurate the parameters we found are. Given the intrinsic, distortion, rotation and translation matrices,
|
||||||
be as close to zero as possible. Given the intrinsic, distortion, rotation and translation matrices,
|
we must first transform the object point to image point using **cv.projectPoints()**. Then, we can calculate
|
||||||
we first transform the object point to image point using **cv.projectPoints()**. Then we calculate
|
|
||||||
the absolute norm between what we got with our transformation and the corner finding algorithm. To
|
the absolute norm between what we got with our transformation and the corner finding algorithm. To
|
||||||
find the average error we calculate the arithmetical mean of the errors calculate for all the
|
find the average error, we calculate the arithmetical mean of the errors calculated for all the
|
||||||
calibration images.
|
calibration images.
|
||||||
@code{.py}
|
@code{.py}
|
||||||
mean_error = 0
|
mean_error = 0
|
||||||
|
@ -183,7 +183,7 @@ minimizes the **weighted within-class variance** given by the relation :
|
|||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
\f[q_1(t) = \sum_{i=1}^{t} P(i) \quad \& \quad q_1(t) = \sum_{i=t+1}^{I} P(i)\f]\f[\mu_1(t) = \sum_{i=1}^{t} \frac{iP(i)}{q_1(t)} \quad \& \quad \mu_2(t) = \sum_{i=t+1}^{I} \frac{iP(i)}{q_2(t)}\f]\f[\sigma_1^2(t) = \sum_{i=1}^{t} [i-\mu_1(t)]^2 \frac{P(i)}{q_1(t)} \quad \& \quad \sigma_2^2(t) = \sum_{i=t+1}^{I} [i-\mu_1(t)]^2 \frac{P(i)}{q_2(t)}\f]
|
\f[q_1(t) = \sum_{i=1}^{t} P(i) \quad \& \quad q_2(t) = \sum_{i=t+1}^{I} P(i)\f]\f[\mu_1(t) = \sum_{i=1}^{t} \frac{iP(i)}{q_1(t)} \quad \& \quad \mu_2(t) = \sum_{i=t+1}^{I} \frac{iP(i)}{q_2(t)}\f]\f[\sigma_1^2(t) = \sum_{i=1}^{t} [i-\mu_1(t)]^2 \frac{P(i)}{q_1(t)} \quad \& \quad \sigma_2^2(t) = \sum_{i=t+1}^{I} [i-\mu_2(t)]^2 \frac{P(i)}{q_2(t)}\f]
|
||||||
|
|
||||||
It actually finds a value of t which lies in between two peaks such that variances to both classes
|
It actually finds a value of t which lies in between two peaks such that variances to both classes
|
||||||
are minimum. It can be simply implemented in Python as follows:
|
are minimum. It can be simply implemented in Python as follows:
|
||||||
|
@ -7,8 +7,7 @@ Introduction
|
|||||||
In this tutorial we will learn how to use AKAZE @cite ANB13 local features to detect and match keypoints on
|
In this tutorial we will learn how to use AKAZE @cite ANB13 local features to detect and match keypoints on
|
||||||
two images.
|
two images.
|
||||||
We will find keypoints on a pair of images with given homography matrix, match them and count the
|
We will find keypoints on a pair of images with given homography matrix, match them and count the
|
||||||
|
number of inliers (i.e. matches that fit in the given homography).
|
||||||
number of inliers (i. e. matches that fit in the given homography).
|
|
||||||
|
|
||||||
You can find expanded version of this example here:
|
You can find expanded version of this example here:
|
||||||
<https://github.com/pablofdezalc/test_kaze_akaze_opencv>
|
<https://github.com/pablofdezalc/test_kaze_akaze_opencv>
|
||||||
@ -16,7 +15,7 @@ You can find expanded version of this example here:
|
|||||||
Data
|
Data
|
||||||
----
|
----
|
||||||
|
|
||||||
We are going to use images 1 and 3 from *Graffity* sequence of Oxford dataset.
|
We are going to use images 1 and 3 from *Graffiti* sequence of [Oxford dataset](http://www.robots.ox.ac.uk/~vgg/data/data-aff.html).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -27,107 +26,148 @@ Homography is given by a 3 by 3 matrix:
|
|||||||
3.4663091e-04 -1.4364524e-05 1.0000000e+00
|
3.4663091e-04 -1.4364524e-05 1.0000000e+00
|
||||||
@endcode
|
@endcode
|
||||||
You can find the images (*graf1.png*, *graf3.png*) and homography (*H1to3p.xml*) in
|
You can find the images (*graf1.png*, *graf3.png*) and homography (*H1to3p.xml*) in
|
||||||
*opencv/samples/cpp*.
|
*opencv/samples/data/*.
|
||||||
|
|
||||||
### Source Code
|
### Source Code
|
||||||
|
|
||||||
@include cpp/tutorial_code/features2D/AKAZE_match.cpp
|
@add_toggle_cpp
|
||||||
|
- **Downloadable code**: Click
|
||||||
|
[here](https://raw.githubusercontent.com/opencv/opencv/3.4/samples/cpp/tutorial_code/features2D/AKAZE_match.cpp)
|
||||||
|
|
||||||
|
- **Code at glance:**
|
||||||
|
@include samples/cpp/tutorial_code/features2D/AKAZE_match.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
- **Downloadable code**: Click
|
||||||
|
[here](https://raw.githubusercontent.com/opencv/opencv/3.4/samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java)
|
||||||
|
|
||||||
|
- **Code at glance:**
|
||||||
|
@include samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
- **Downloadable code**: Click
|
||||||
|
[here](https://raw.githubusercontent.com/opencv/opencv/3.4/samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py)
|
||||||
|
|
||||||
|
- **Code at glance:**
|
||||||
|
@include samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
### Explanation
|
### Explanation
|
||||||
|
|
||||||
-# **Load images and homography**
|
- **Load images and homography**
|
||||||
@code{.cpp}
|
|
||||||
Mat img1 = imread("graf1.png", IMREAD_GRAYSCALE);
|
|
||||||
Mat img2 = imread("graf3.png", IMREAD_GRAYSCALE);
|
|
||||||
|
|
||||||
Mat homography;
|
@add_toggle_cpp
|
||||||
FileStorage fs("H1to3p.xml", FileStorage::READ);
|
@snippet samples/cpp/tutorial_code/features2D/AKAZE_match.cpp load
|
||||||
fs.getFirstTopLevelNode() >> homography;
|
@end_toggle
|
||||||
@endcode
|
|
||||||
We are loading grayscale images here. Homography is stored in the xml created with FileStorage.
|
|
||||||
|
|
||||||
-# **Detect keypoints and compute descriptors using AKAZE**
|
@add_toggle_java
|
||||||
@code{.cpp}
|
@snippet samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java load
|
||||||
vector<KeyPoint> kpts1, kpts2;
|
@end_toggle
|
||||||
Mat desc1, desc2;
|
|
||||||
|
|
||||||
AKAZE akaze;
|
@add_toggle_python
|
||||||
akaze(img1, noArray(), kpts1, desc1);
|
@snippet samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py load
|
||||||
akaze(img2, noArray(), kpts2, desc2);
|
@end_toggle
|
||||||
@endcode
|
|
||||||
We create AKAZE object and use it's *operator()* functionality. Since we don't need the *mask*
|
|
||||||
parameter, *noArray()* is used.
|
|
||||||
|
|
||||||
-# **Use brute-force matcher to find 2-nn matches**
|
We are loading grayscale images here. Homography is stored in the xml created with FileStorage.
|
||||||
@code{.cpp}
|
|
||||||
BFMatcher matcher(NORM_HAMMING);
|
|
||||||
vector< vector<DMatch> > nn_matches;
|
|
||||||
matcher.knnMatch(desc1, desc2, nn_matches, 2);
|
|
||||||
@endcode
|
|
||||||
We use Hamming distance, because AKAZE uses binary descriptor by default.
|
|
||||||
|
|
||||||
-# **Use 2-nn matches to find correct keypoint matches**
|
- **Detect keypoints and compute descriptors using AKAZE**
|
||||||
@code{.cpp}
|
|
||||||
for(size_t i = 0; i < nn_matches.size(); i++) {
|
|
||||||
DMatch first = nn_matches[i][0];
|
|
||||||
float dist1 = nn_matches[i][0].distance;
|
|
||||||
float dist2 = nn_matches[i][1].distance;
|
|
||||||
|
|
||||||
if(dist1 < nn_match_ratio * dist2) {
|
@add_toggle_cpp
|
||||||
matched1.push_back(kpts1[first.queryIdx]);
|
@snippet samples/cpp/tutorial_code/features2D/AKAZE_match.cpp AKAZE
|
||||||
matched2.push_back(kpts2[first.trainIdx]);
|
@end_toggle
|
||||||
}
|
|
||||||
}
|
|
||||||
@endcode
|
|
||||||
If the closest match is *ratio* closer than the second closest one, then the match is correct.
|
|
||||||
|
|
||||||
-# **Check if our matches fit in the homography model**
|
@add_toggle_java
|
||||||
@code{.cpp}
|
@snippet samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java AKAZE
|
||||||
for(int i = 0; i < matched1.size(); i++) {
|
@end_toggle
|
||||||
Mat col = Mat::ones(3, 1, CV_64F);
|
|
||||||
col.at<double>(0) = matched1[i].pt.x;
|
|
||||||
col.at<double>(1) = matched1[i].pt.y;
|
|
||||||
|
|
||||||
col = homography * col;
|
@add_toggle_python
|
||||||
col /= col.at<double>(2);
|
@snippet samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py AKAZE
|
||||||
float dist = sqrt( pow(col.at<double>(0) - matched2[i].pt.x, 2) +
|
@end_toggle
|
||||||
pow(col.at<double>(1) - matched2[i].pt.y, 2));
|
|
||||||
|
|
||||||
if(dist < inlier_threshold) {
|
We create AKAZE and detect and compute AKAZE keypoints and descriptors. Since we don't need the *mask*
|
||||||
int new_i = inliers1.size();
|
parameter, *noArray()* is used.
|
||||||
inliers1.push_back(matched1[i]);
|
|
||||||
inliers2.push_back(matched2[i]);
|
|
||||||
good_matches.push_back(DMatch(new_i, new_i, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@endcode
|
|
||||||
If the distance from first keypoint's projection to the second keypoint is less than threshold,
|
|
||||||
then it it fits in the homography.
|
|
||||||
|
|
||||||
We create a new set of matches for the inliers, because it is required by the drawing function.
|
- **Use brute-force matcher to find 2-nn matches**
|
||||||
|
|
||||||
-# **Output results**
|
@add_toggle_cpp
|
||||||
@code{.cpp}
|
@snippet samples/cpp/tutorial_code/features2D/AKAZE_match.cpp 2-nn matching
|
||||||
Mat res;
|
@end_toggle
|
||||||
drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
|
|
||||||
imwrite("res.png", res);
|
|
||||||
...
|
|
||||||
@endcode
|
|
||||||
Here we save the resulting image and print some statistics.
|
|
||||||
|
|
||||||
### Results
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java 2-nn matching
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Found matches
|
@add_toggle_python
|
||||||
-------------
|
@snippet samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py 2-nn matching
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
We use Hamming distance, because AKAZE uses binary descriptor by default.
|
||||||
|
|
||||||
|
- **Use 2-nn matches and ratio criterion to find correct keypoint matches**
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/features2D/AKAZE_match.cpp ratio test filtering
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java ratio test filtering
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py ratio test filtering
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
If the closest match distance is significantly lower than the second closest one, then the match is correct (match is not ambiguous).
|
||||||
|
|
||||||
|
- **Check if our matches fit in the homography model**
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/features2D/AKAZE_match.cpp homography check
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java homography check
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py homography check
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
If the distance from first keypoint's projection to the second keypoint is less than threshold,
|
||||||
|
then it fits the homography model.
|
||||||
|
|
||||||
|
We create a new set of matches for the inliers, because it is required by the drawing function.
|
||||||
|
|
||||||
|
- **Output results**
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/features2D/AKAZE_match.cpp draw final matches
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/features2D/akaze_matching/AKAZEMatchDemo.java draw final matches
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/features2D/akaze_matching/AKAZE_match.py draw final matches
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
Here we save the resulting image and print some statistics.
|
||||||
|
|
||||||
|
Results
|
||||||
|
-------
|
||||||
|
|
||||||
|
### Found matches
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
A-KAZE Matching Results
|
Depending on your OpenCV version, you should get results coherent with:
|
||||||
-----------------------
|
|
||||||
@code{.none}
|
@code{.none}
|
||||||
Keypoints 1: 2943
|
Keypoints 1: 2943
|
||||||
Keypoints 2: 3511
|
Keypoints 2: 3511
|
||||||
Matches: 447
|
Matches: 447
|
||||||
Inliers: 308
|
Inliers: 308
|
||||||
Inlier Ratio: 0.689038}
|
Inlier Ratio: 0.689038
|
||||||
@endcode
|
@endcode
|
||||||
|
@ -98,6 +98,8 @@ OpenCV.
|
|||||||
|
|
||||||
- @subpage tutorial_akaze_matching
|
- @subpage tutorial_akaze_matching
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 3.0
|
*Compatibility:* \> OpenCV 3.0
|
||||||
|
|
||||||
*Author:* Fedor Morozov
|
*Author:* Fedor Morozov
|
||||||
|
@ -16,42 +16,152 @@ Theory
|
|||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
This tutorial code's is shown lines below. You can also download it from
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp).
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp).
|
||||||
@include samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp
|
@include samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java)
|
||||||
|
@include samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py)
|
||||||
|
@include samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Explanation / Result
|
Explanation / Result
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
-# Load the source image and check if it is loaded without any problem, then show it:
|
- Load the source image and check if it is loaded without any problem, then show it:
|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp load_image
|
|
||||||

|
|
||||||
|
|
||||||
-# Then if we have an image with a white background, it is good to transform it to black. This will help us to discriminate the foreground objects easier when we will apply the Distance Transform:
|
@add_toggle_cpp
|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp black_bg
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp load_image
|
||||||

|
@end_toggle
|
||||||
|
|
||||||
-# Afterwards we will sharpen our image in order to acute the edges of the foreground objects. We will apply a laplacian filter with a quite strong filter (an approximation of second derivative):
|
@add_toggle_java
|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp sharp
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java load_image
|
||||||

|
@end_toggle
|
||||||

|
|
||||||
|
|
||||||
-# Now we transform our new sharpened source image to a grayscale and a binary one, respectively:
|
@add_toggle_python
|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp bin
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py load_image
|
||||||

|
@end_toggle
|
||||||
|
|
||||||
-# We are ready now to apply the Distance Transform on the binary image. Moreover, we normalize the output image in order to be able visualize and threshold the result:
|

|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp dist
|
|
||||||

|
|
||||||
|
|
||||||
-# We threshold the *dist* image and then perform some morphology operation (i.e. dilation) in order to extract the peaks from the above image:
|
- Then if we have an image with a white background, it is good to transform it to black. This will help us to discriminate the foreground objects easier when we will apply the Distance Transform:
|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp peaks
|
|
||||||

|
|
||||||
|
|
||||||
-# From each blob then we create a seed/marker for the watershed algorithm with the help of the @ref cv::findContours function:
|
@add_toggle_cpp
|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp seeds
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp black_bg
|
||||||

|
@end_toggle
|
||||||
|
|
||||||
-# Finally, we can apply the watershed algorithm, and visualize the result:
|
@add_toggle_java
|
||||||
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp watershed
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java black_bg
|
||||||

|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py black_bg
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Afterwards we will sharpen our image in order to acute the edges of the foreground objects. We will apply a laplacian filter with a quite strong filter (an approximation of second derivative):
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp sharp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java sharp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py sharp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
- Now we transform our new sharpened source image to a grayscale and a binary one, respectively:
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp bin
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java bin
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py bin
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- We are ready now to apply the Distance Transform on the binary image. Moreover, we normalize the output image in order to be able visualize and threshold the result:
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp dist
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java dist
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py dist
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- We threshold the *dist* image and then perform some morphology operation (i.e. dilation) in order to extract the peaks from the above image:
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp peaks
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java peaks
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py peaks
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- From each blob then we create a seed/marker for the watershed algorithm with the help of the @ref cv::findContours function:
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp seeds
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java seeds
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py seeds
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Finally, we can apply the watershed algorithm, and visualize the result:
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp watershed
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java watershed
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py watershed
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|

|
||||||
|
@ -15,55 +15,167 @@ Theory
|
|||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
This tutorial code's is shown lines below. You can also download it from
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp)
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp)
|
||||||
@include samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp
|
@include samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java)
|
||||||
|
@include samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py)
|
||||||
|
@include samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Explanation
|
Explanation
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The main function is rather simple, as follows from the comments we do the following:
|
The main function is rather simple, as follows from the comments we do the following:
|
||||||
-# Open the image, convert it into grayscale and blur it to get rid of the noise.
|
- Open the image, convert it into grayscale and blur it to get rid of the noise.
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp setup
|
|
||||||
-# Create a window with header "Source" and display the source file in it.
|
@add_toggle_cpp
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp createWindow
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp setup
|
||||||
-# Create a trackbar on the source_window and assign a callback function to it
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java setup
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py setup
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
- Create a window with header "Source" and display the source file in it.
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp createWindow
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java createWindow
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py createWindow
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
- Create a trackbar on the `source_window` and assign a callback function to it.
|
||||||
In general callback functions are used to react to some kind of signal, in our
|
In general callback functions are used to react to some kind of signal, in our
|
||||||
case it's trackbar's state change.
|
case it's trackbar's state change.
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp taskbar
|
Explicit one-time call of `thresh_callback` is necessary to display
|
||||||
-# Explicit one-time call of `thresh_callback` is necessary to display
|
|
||||||
the "Contours" window simultaniously with the "Source" window.
|
the "Contours" window simultaniously with the "Source" window.
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp callback00
|
|
||||||
-# Wait for user to close the windows.
|
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp waitForIt
|
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp trackbar
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
The callback function `thresh_callback` does all the interesting job.
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java trackbar
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py trackbar
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
-# Writes to `threshold_output` the threshold of the grayscale picture (you can check out about thresholding @ref tutorial_threshold "here").
|
The callback function does all the interesting job.
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp threshold
|
|
||||||
-# Finds contours and saves them to the vectors `contour` and `hierarchy`.
|
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp findContours
|
|
||||||
-# For every found contour we now apply approximation to polygons
|
|
||||||
with accuracy +-3 and stating that the curve must me closed.
|
|
||||||
|
|
||||||
|
- Use @ref cv::Canny to detect edges in the images.
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp Canny
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java Canny
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py Canny
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
- Finds contours and saves them to the vectors `contour` and `hierarchy`.
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp findContours
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java findContours
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py findContours
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
- For every found contour we now apply approximation to polygons
|
||||||
|
with accuracy +-3 and stating that the curve must be closed.
|
||||||
After that we find a bounding rect for every polygon and save it to `boundRect`.
|
After that we find a bounding rect for every polygon and save it to `boundRect`.
|
||||||
|
|
||||||
At last we find a minimum enclosing circle for every polygon and
|
At last we find a minimum enclosing circle for every polygon and
|
||||||
save it to `center` and `radius` vectors.
|
save it to `center` and `radius` vectors.
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp allthework
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp allthework
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java allthework
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py allthework
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
We found everything we need, all we have to do is to draw.
|
We found everything we need, all we have to do is to draw.
|
||||||
|
|
||||||
-# Create new Mat of unsigned 8-bit chars, filled with zeros.
|
- Create new Mat of unsigned 8-bit chars, filled with zeros.
|
||||||
It will contain all the drawings we are going to make (rects and circles).
|
It will contain all the drawings we are going to make (rects and circles).
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp zeroMat
|
|
||||||
-# For every contour: pick a random color, draw the contour, the bounding rectangle and
|
@add_toggle_cpp
|
||||||
the minimal enclosing circle with it,
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp zeroMat
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp forContour
|
@end_toggle
|
||||||
-# Display the results: create a new window "Contours" and show everything we added to drawings on it.
|
|
||||||
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp showDrawings
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java zeroMat
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py zeroMat
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
- For every contour: pick a random color, draw the contour, the bounding rectangle and
|
||||||
|
the minimal enclosing circle with it.
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp forContour
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java forContour
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py forContour
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
- Display the results: create a new window "Contours" and show everything we added to drawings on it.
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
|
@snippet samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp showDrawings
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
@snippet samples/java/tutorial_code/ShapeDescriptors/bounding_rects_circles/GeneralContoursDemo1.java showDrawings
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
@snippet samples/python/tutorial_code/ShapeDescriptors/bounding_rects_circles/generalContours_demo1.py showDrawings
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Result
|
Result
|
||||||
------
|
------
|
||||||
|
@ -15,9 +15,23 @@ Theory
|
|||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
This tutorial code's is shown lines below. You can also download it from
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo2.cpp)
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo2.cpp)
|
||||||
@include samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo2.cpp
|
@include samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo2.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ShapeDescriptors/bounding_rotated_ellipses/GeneralContoursDemo2.java)
|
||||||
|
@include samples/java/tutorial_code/ShapeDescriptors/bounding_rotated_ellipses/GeneralContoursDemo2.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ShapeDescriptors/bounding_rotated_ellipses/generalContours_demo2.py)
|
||||||
|
@include samples/python/tutorial_code/ShapeDescriptors/bounding_rotated_ellipses/generalContours_demo2.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Explanation
|
Explanation
|
||||||
-----------
|
-----------
|
||||||
|
@ -15,9 +15,23 @@ Theory
|
|||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
This tutorial code's is shown lines below. You can also download it from
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/findContours_demo.cpp)
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/findContours_demo.cpp)
|
||||||
@include samples/cpp/tutorial_code/ShapeDescriptors/findContours_demo.cpp
|
@include samples/cpp/tutorial_code/ShapeDescriptors/findContours_demo.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ShapeDescriptors/find_contours/FindContoursDemo.java)
|
||||||
|
@include samples/java/tutorial_code/ShapeDescriptors/find_contours/FindContoursDemo.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ShapeDescriptors/find_contours/findContours_demo.py)
|
||||||
|
@include samples/python/tutorial_code/ShapeDescriptors/find_contours/findContours_demo.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Explanation
|
Explanation
|
||||||
-----------
|
-----------
|
||||||
|
@ -14,10 +14,23 @@ Theory
|
|||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
This tutorial code's is shown lines below. You can also download it from
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/hull_demo.cpp)
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/hull_demo.cpp)
|
||||||
|
|
||||||
@include samples/cpp/tutorial_code/ShapeDescriptors/hull_demo.cpp
|
@include samples/cpp/tutorial_code/ShapeDescriptors/hull_demo.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ShapeDescriptors/hull/HullDemo.java)
|
||||||
|
@include samples/java/tutorial_code/ShapeDescriptors/hull/HullDemo.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ShapeDescriptors/hull/hull_demo.py)
|
||||||
|
@include samples/python/tutorial_code/ShapeDescriptors/hull/hull_demo.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Explanation
|
Explanation
|
||||||
-----------
|
-----------
|
||||||
|
@ -16,9 +16,23 @@ Theory
|
|||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
This tutorial code's is shown lines below. You can also download it from
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp)
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp)
|
||||||
@include samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp
|
@include samples/cpp/tutorial_code/ShapeDescriptors/moments_demo.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ShapeDescriptors/moments/MomentsDemo.java)
|
||||||
|
@include samples/java/tutorial_code/ShapeDescriptors/moments/MomentsDemo.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ShapeDescriptors/moments/moments_demo.py)
|
||||||
|
@include samples/python/tutorial_code/ShapeDescriptors/moments/moments_demo.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Explanation
|
Explanation
|
||||||
-----------
|
-----------
|
||||||
|
@ -14,9 +14,23 @@ Theory
|
|||||||
Code
|
Code
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
This tutorial code's is shown lines below. You can also download it from
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/pointPolygonTest_demo.cpp)
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ShapeDescriptors/pointPolygonTest_demo.cpp)
|
||||||
@include samples/cpp/tutorial_code/ShapeDescriptors/pointPolygonTest_demo.cpp
|
@include samples/cpp/tutorial_code/ShapeDescriptors/pointPolygonTest_demo.cpp
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_java
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ShapeDescriptors/point_polygon_test/PointPolygonTestDemo.java)
|
||||||
|
@include samples/java/tutorial_code/ShapeDescriptors/point_polygon_test/PointPolygonTestDemo.java
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
|
@add_toggle_python
|
||||||
|
This tutorial code's is shown lines below. You can also download it from
|
||||||
|
[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ShapeDescriptors/point_polygon_test/pointPolygonTest_demo.py)
|
||||||
|
@include samples/python/tutorial_code/ShapeDescriptors/point_polygon_test/pointPolygonTest_demo.py
|
||||||
|
@end_toggle
|
||||||
|
|
||||||
Explanation
|
Explanation
|
||||||
-----------
|
-----------
|
||||||
|
@ -225,6 +225,8 @@ In this section you will learn about the image processing (manipulation) functio
|
|||||||
|
|
||||||
- @subpage tutorial_find_contours
|
- @subpage tutorial_find_contours
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Ana Huamán
|
*Author:* Ana Huamán
|
||||||
@ -233,6 +235,8 @@ In this section you will learn about the image processing (manipulation) functio
|
|||||||
|
|
||||||
- @subpage tutorial_hull
|
- @subpage tutorial_hull
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Ana Huamán
|
*Author:* Ana Huamán
|
||||||
@ -241,6 +245,8 @@ In this section you will learn about the image processing (manipulation) functio
|
|||||||
|
|
||||||
- @subpage tutorial_bounding_rects_circles
|
- @subpage tutorial_bounding_rects_circles
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Ana Huamán
|
*Author:* Ana Huamán
|
||||||
@ -249,6 +255,8 @@ In this section you will learn about the image processing (manipulation) functio
|
|||||||
|
|
||||||
- @subpage tutorial_bounding_rotated_ellipses
|
- @subpage tutorial_bounding_rotated_ellipses
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Ana Huamán
|
*Author:* Ana Huamán
|
||||||
@ -257,6 +265,8 @@ In this section you will learn about the image processing (manipulation) functio
|
|||||||
|
|
||||||
- @subpage tutorial_moments
|
- @subpage tutorial_moments
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Ana Huamán
|
*Author:* Ana Huamán
|
||||||
@ -265,6 +275,8 @@ In this section you will learn about the image processing (manipulation) functio
|
|||||||
|
|
||||||
- @subpage tutorial_point_polygon_test
|
- @subpage tutorial_point_polygon_test
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Ana Huamán
|
*Author:* Ana Huamán
|
||||||
@ -273,6 +285,8 @@ In this section you will learn about the image processing (manipulation) functio
|
|||||||
|
|
||||||
- @subpage tutorial_distance_transform
|
- @subpage tutorial_distance_transform
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Theodore Tsesmelis
|
*Author:* Theodore Tsesmelis
|
||||||
|
@ -1992,6 +1992,31 @@ CV_EXPORTS_W int decomposeHomographyMat(InputArray H,
|
|||||||
OutputArrayOfArrays translations,
|
OutputArrayOfArrays translations,
|
||||||
OutputArrayOfArrays normals);
|
OutputArrayOfArrays normals);
|
||||||
|
|
||||||
|
/** @brief Filters homography decompositions based on additional information.
|
||||||
|
|
||||||
|
@param rotations Vector of rotation matrices.
|
||||||
|
@param normals Vector of plane normal matrices.
|
||||||
|
@param beforePoints Vector of (rectified) visible reference points before the homography is applied
|
||||||
|
@param afterPoints Vector of (rectified) visible reference points after the homography is applied
|
||||||
|
@param possibleSolutions Vector of int indices representing the viable solution set after filtering
|
||||||
|
@param pointsMask optional Mat/Vector of 8u type representing the mask for the inliers as given by the findHomography function
|
||||||
|
|
||||||
|
This function is intended to filter the output of the decomposeHomographyMat based on additional
|
||||||
|
information as described in @cite Malis . The summary of the method: the decomposeHomographyMat function
|
||||||
|
returns 2 unique solutions and their "opposites" for a total of 4 solutions. If we have access to the
|
||||||
|
sets of points visible in the camera frame before and after the homography transformation is applied,
|
||||||
|
we can determine which are the true potential solutions and which are the opposites by verifying which
|
||||||
|
homographies are consistent with all visible reference points being in front of the camera. The inputs
|
||||||
|
are left unchanged; the filtered solution set is returned as indices into the existing one.
|
||||||
|
|
||||||
|
*/
|
||||||
|
CV_EXPORTS_W void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays rotations,
|
||||||
|
InputArrayOfArrays normals,
|
||||||
|
InputArray beforePoints,
|
||||||
|
InputArray afterPoints,
|
||||||
|
OutputArray possibleSolutions,
|
||||||
|
InputArray pointsMask = noArray());
|
||||||
|
|
||||||
/** @brief The base class for stereo correspondence algorithms.
|
/** @brief The base class for stereo correspondence algorithms.
|
||||||
*/
|
*/
|
||||||
class CV_EXPORTS_W StereoMatcher : public Algorithm
|
class CV_EXPORTS_W StereoMatcher : public Algorithm
|
||||||
|
@ -1,50 +1,51 @@
|
|||||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// This is a homography decomposition implementation contributed to OpenCV
|
// This is a homography decomposition implementation contributed to OpenCV
|
||||||
// by Samson Yilma. It implements the homography decomposition algorithm
|
// by Samson Yilma. It implements the homography decomposition algorithm
|
||||||
// described in the research report:
|
// described in the research report:
|
||||||
// Malis, E and Vargas, M, "Deeper understanding of the homography decomposition
|
// Malis, E and Vargas, M, "Deeper understanding of the homography decomposition
|
||||||
// for vision-based control", Research Report 6303, INRIA (2007)
|
// for vision-based control", Research Report 6303, INRIA (2007)
|
||||||
//
|
//
|
||||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||||
//
|
//
|
||||||
// By downloading, copying, installing or using the software you agree to this license.
|
// By downloading, copying, installing or using the software you agree to this license.
|
||||||
// If you do not agree to this license, do not download, install,
|
// If you do not agree to this license, do not download, install,
|
||||||
// copy or use the software.
|
// copy or use the software.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// License Agreement
|
// License Agreement
|
||||||
// For Open Source Computer Vision Library
|
// For Open Source Computer Vision Library
|
||||||
//
|
//
|
||||||
// Copyright (C) 2014, Samson Yilma (samson_yilma@yahoo.com), all rights reserved.
|
// Copyright (C) 2014, Samson Yilma (samson_yilma@yahoo.com), all rights reserved.
|
||||||
//
|
// Copyright (C) 2018, Intel Corporation, all rights reserved.
|
||||||
// Third party copyrights are property of their respective owners.
|
//
|
||||||
//
|
// Third party copyrights are property of their respective owners.
|
||||||
// Redistribution and use in source and binary forms, with or without modification,
|
//
|
||||||
// are permitted provided that the following conditions are met:
|
// Redistribution and use in source and binary forms, with or without modification,
|
||||||
//
|
// are permitted provided that the following conditions are met:
|
||||||
// * Redistribution's of source code must retain the above copyright notice,
|
//
|
||||||
// this list of conditions and the following disclaimer.
|
// * Redistribution's of source code must retain the above copyright notice,
|
||||||
//
|
// this list of conditions and the following disclaimer.
|
||||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
//
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||||
// and/or other materials provided with the distribution.
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
//
|
// and/or other materials provided with the distribution.
|
||||||
// * The name of the copyright holders may not be used to endorse or promote products
|
//
|
||||||
// derived from this software without specific prior written permission.
|
// * The name of the copyright holders may not be used to endorse or promote products
|
||||||
//
|
// derived from this software without specific prior written permission.
|
||||||
// This software is provided by the copyright holders and contributors "as is" and
|
//
|
||||||
// any express or implied warranties, including, but not limited to, the implied
|
// This software is provided by the copyright holders and contributors "as is" and
|
||||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
// any express or implied warranties, including, but not limited to, the implied
|
||||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||||
// indirect, incidental, special, exemplary, or consequential damages
|
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||||
// (including, but not limited to, procurement of substitute goods or services;
|
// indirect, incidental, special, exemplary, or consequential damages
|
||||||
// loss of use, data, or profits; or business interruption) however caused
|
// (including, but not limited to, procurement of substitute goods or services;
|
||||||
// and on any theory of liability, whether in contract, strict liability,
|
// loss of use, data, or profits; or business interruption) however caused
|
||||||
// or tort (including negligence or otherwise) arising in any way out of
|
// and on any theory of liability, whether in contract, strict liability,
|
||||||
// the use of this software, even if advised of the possibility of such damage.
|
// or tort (including negligence or otherwise) arising in any way out of
|
||||||
//
|
// the use of this software, even if advised of the possibility of such damage.
|
||||||
//M*/
|
//
|
||||||
|
//M*/
|
||||||
|
|
||||||
#include "precomp.hpp"
|
#include "precomp.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -489,4 +490,67 @@ int decomposeHomographyMat(InputArray _H,
|
|||||||
return nsols;
|
return nsols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays _rotations,
|
||||||
|
InputArrayOfArrays _normals,
|
||||||
|
InputArray _beforeRectifiedPoints,
|
||||||
|
InputArray _afterRectifiedPoints,
|
||||||
|
OutputArray _possibleSolutions,
|
||||||
|
InputArray _pointsMask)
|
||||||
|
{
|
||||||
|
CV_Assert(_beforeRectifiedPoints.type() == CV_32FC2 && _afterRectifiedPoints.type() == CV_32FC2);
|
||||||
|
CV_Assert(_pointsMask.empty() || _pointsMask.type() == CV_8U);
|
||||||
|
|
||||||
|
Mat beforeRectifiedPoints = _beforeRectifiedPoints.getMat();
|
||||||
|
Mat afterRectifiedPoints = _afterRectifiedPoints.getMat();
|
||||||
|
Mat pointsMask = _pointsMask.getMat();
|
||||||
|
int nsolutions = (int)_rotations.total();
|
||||||
|
int npoints = (int)beforeRectifiedPoints.total();
|
||||||
|
CV_Assert(pointsMask.empty() || pointsMask.checkVector(1, CV_8U) == npoints);
|
||||||
|
const uchar* pointsMaskPtr = pointsMask.data;
|
||||||
|
|
||||||
|
std::vector<uchar> solutionMask(nsolutions, (uchar)1);
|
||||||
|
std::vector<Mat> normals(nsolutions);
|
||||||
|
std::vector<Mat> rotnorm(nsolutions);
|
||||||
|
Mat R;
|
||||||
|
|
||||||
|
for( int i = 0; i < nsolutions; i++ )
|
||||||
|
{
|
||||||
|
_normals.getMat(i).convertTo(normals[i], CV_64F);
|
||||||
|
CV_Assert(normals[i].total() == 3);
|
||||||
|
_rotations.getMat(i).convertTo(R, CV_64F);
|
||||||
|
rotnorm[i] = R*normals[i];
|
||||||
|
CV_Assert(rotnorm[i].total() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for( int j = 0; j < npoints; j++ )
|
||||||
|
{
|
||||||
|
if( !pointsMaskPtr || pointsMaskPtr[j] )
|
||||||
|
{
|
||||||
|
Point2f prevPoint = beforeRectifiedPoints.at<Point2f>(j);
|
||||||
|
Point2f currPoint = afterRectifiedPoints.at<Point2f>(j);
|
||||||
|
|
||||||
|
for( int i = 0; i < nsolutions; i++ )
|
||||||
|
{
|
||||||
|
if( !solutionMask[i] )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const double* normal_i = normals[i].ptr<double>();
|
||||||
|
const double* rotnorm_i = rotnorm[i].ptr<double>();
|
||||||
|
double prevNormDot = normal_i[0]*prevPoint.x + normal_i[1]*prevPoint.y + normal_i[2];
|
||||||
|
double currNormDot = rotnorm_i[0]*currPoint.x + rotnorm_i[1]*currPoint.y + rotnorm_i[2];
|
||||||
|
|
||||||
|
if (prevNormDot <= 0 || currNormDot <= 0)
|
||||||
|
solutionMask[i] = (uchar)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> possibleSolutions;
|
||||||
|
for( int i = 0; i < nsolutions; i++ )
|
||||||
|
if( solutionMask[i] )
|
||||||
|
possibleSolutions.push_back(i);
|
||||||
|
|
||||||
|
Mat(possibleSolutions).copyTo(_possibleSolutions);
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace cv
|
} //namespace cv
|
||||||
|
575
modules/calib3d/test/test_filter_homography_decomp.cpp
Normal file
575
modules/calib3d/test/test_filter_homography_decomp.cpp
Normal file
@ -0,0 +1,575 @@
|
|||||||
|
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||||
|
//
|
||||||
|
// By downloading, copying, installing or using the software you agree to this license.
|
||||||
|
// If you do not agree to this license, do not download, install,
|
||||||
|
// copy or use the software.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// License Agreement
|
||||||
|
// For Open Source Computer Vision Library
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016, OpenCV Foundation, all rights reserved.
|
||||||
|
//
|
||||||
|
// Third party copyrights are property of their respective owners.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
// are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistribution's of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// * The name of the copyright holders may not be used to endorse or promote products
|
||||||
|
// derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// This software is provided by the copyright holders and contributors "as is" and
|
||||||
|
// any express or implied warranties, including, but not limited to, the implied
|
||||||
|
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||||
|
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||||
|
// indirect, incidental, special, exemplary, or consequential damages
|
||||||
|
// (including, but not limited to, procurement of substitute goods or services;
|
||||||
|
// loss of use, data, or profits; or business interruption) however caused
|
||||||
|
// and on any theory of liability, whether in contract, strict liability,
|
||||||
|
// or tort (including negligence or otherwise) arising in any way out of
|
||||||
|
// the use of this software, even if advised of the possibility of such damage.
|
||||||
|
//
|
||||||
|
//M*/
|
||||||
|
|
||||||
|
#include "test_precomp.hpp"
|
||||||
|
#include "opencv2/calib3d.hpp"
|
||||||
|
|
||||||
|
namespace opencv_test { namespace {
|
||||||
|
|
||||||
|
class CV_FilterHomographyDecompTest : public cvtest::BaseTest {
|
||||||
|
|
||||||
|
public:
|
||||||
|
CV_FilterHomographyDecompTest()
|
||||||
|
{
|
||||||
|
buildTestDataSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run(int)
|
||||||
|
{
|
||||||
|
vector<int> finalSolutions;
|
||||||
|
filterHomographyDecompByVisibleRefpoints(_rotations, _normals, _prevRectifiedPoints, _currRectifiedPoints, finalSolutions, _mask);
|
||||||
|
|
||||||
|
//there should be at least 2 solution
|
||||||
|
ASSERT_EQ(finalSolutions, _validSolutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void buildTestDataSet()
|
||||||
|
{
|
||||||
|
double rotationsArray[4][9] = {
|
||||||
|
{
|
||||||
|
0.98811084196540500,
|
||||||
|
-0.15276633082836735,
|
||||||
|
0.017303530150126534,
|
||||||
|
0.14161851662094097,
|
||||||
|
0.94821044891315664,
|
||||||
|
0.28432576443578628,
|
||||||
|
-0.059842791884259422,
|
||||||
|
-0.27849487021693553,
|
||||||
|
0.95857156619751127
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0.98811084196540500,
|
||||||
|
-0.15276633082836735,
|
||||||
|
0.017303530150126534,
|
||||||
|
0.14161851662094097,
|
||||||
|
0.94821044891315664,
|
||||||
|
0.28432576443578628,
|
||||||
|
-0.059842791884259422,
|
||||||
|
-0.27849487021693553,
|
||||||
|
0.95857156619751127
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0.95471096402077438,
|
||||||
|
-0.21080808634428211,
|
||||||
|
-0.20996886890771557,
|
||||||
|
0.20702063153797226,
|
||||||
|
0.97751379914116743,
|
||||||
|
-0.040115216641822840,
|
||||||
|
0.21370407880090386,
|
||||||
|
-0.0051694506925720751,
|
||||||
|
0.97688476468997820
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0.95471096402077438,
|
||||||
|
-0.21080808634428211,
|
||||||
|
-0.20996886890771557,
|
||||||
|
0.20702063153797226,
|
||||||
|
0.97751379914116743,
|
||||||
|
-0.040115216641822840,
|
||||||
|
0.21370407880090386,
|
||||||
|
-0.0051694506925720751,
|
||||||
|
0.97688476468997820
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
double normalsArray[4][3] = {
|
||||||
|
{
|
||||||
|
-0.023560516110791116,
|
||||||
|
0.085818414407956692,
|
||||||
|
0.99603217911325403
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0.023560516110791116,
|
||||||
|
-0.085818414407956692,
|
||||||
|
-0.99603217911325403
|
||||||
|
},
|
||||||
|
{
|
||||||
|
-0.62483547397726014,
|
||||||
|
-0.56011861446691769,
|
||||||
|
0.54391889853844289
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0.62483547397726014,
|
||||||
|
0.56011861446691769,
|
||||||
|
-0.54391889853844289
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uchar maskArray[514] =
|
||||||
|
{
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0,
|
||||||
|
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0,
|
||||||
|
1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
|
||||||
|
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0,
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1,
|
||||||
|
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1,
|
||||||
|
1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0,
|
||||||
|
0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
|
||||||
|
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
|
||||||
|
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0,
|
||||||
|
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const float currRectifiedPointArr[] =
|
||||||
|
{
|
||||||
|
-0.565732896f, -0.321162999f, -0.416198403f, -0.299646467f, -0.408354312f, -0.290387660f,
|
||||||
|
-0.386555284f, -0.287677139f, -0.348475337f, -0.276208878f, -0.415957332f, -0.266133875f,
|
||||||
|
-0.354961902f, -0.257545590f, -0.420189440f, -0.255190015f, -0.379785866f, -0.252570540f,
|
||||||
|
-0.144345313f, -0.249134675f, -0.162417486f, -0.223227784f, -0.129876539f, -0.219722182f,
|
||||||
|
-0.470801264f, -0.211166814f, 0.0992607549f, -0.209064797f, 0.123508267f, -0.196303099f,
|
||||||
|
-0.521849990f, -0.190706849f, -0.513497114f, -0.189186409f, -0.534959674f, -0.185138911f,
|
||||||
|
0.121614374f, -0.182721153f, 0.154205695f, -0.183763996f, -0.516449869f, -0.181606859f,
|
||||||
|
-0.523427486f, -0.180088669f, 0.149494573f, -0.179563865f, -0.552187204f, -0.172630817f,
|
||||||
|
-0.322249800f, -0.172333881f, 0.127574071f, -0.165683150f, 0.159817487f, -0.162389070f,
|
||||||
|
-0.578930736f, -0.160272732f, -0.600617707f, -0.155920163f, -0.249115735f, -0.154711768f,
|
||||||
|
-0.543279886f, -0.144873798f, -0.529992998f, -0.142433196f, 0.0554505363f, -0.142878756f,
|
||||||
|
-0.613355398f, -0.132748783f, 0.190059289f, -0.128930226f, -0.255682647f, -0.127393380f,
|
||||||
|
0.0299431719f, -0.125339776f, -0.282943249f, -0.118550651f, -0.0348402821f, -0.115398556f,
|
||||||
|
-0.0362741761f, -0.110100254f, -0.319089264f, -0.104354575f, -0.0401916653f, -0.0852083191f,
|
||||||
|
-0.372183621f, -0.0812346712f, -0.00707253255f, -0.0810251758f, 0.267345309f, -0.0787685066f,
|
||||||
|
0.258760840f, -0.0768160895f, -0.377273679f, -0.0763452053f, -0.0314898677f, -0.0743160769f,
|
||||||
|
0.223423928f, -0.0724818707f, 0.00284322398f, -0.0720727518f, 0.232531011f, -0.0682833865f,
|
||||||
|
0.282355100f, -0.0655683428f, -0.233353317f, -0.0613981225f, 0.290982842f, -0.0607336313f,
|
||||||
|
-0.0994169787f, -0.0376472026f, 0.257561266f, -0.0331368558f, 0.265076399f, -0.0320781991f,
|
||||||
|
0.0454338901f, -0.0238198638f, 0.0409987904f, -0.0186991505f, -0.502306283f, -0.0172236171f,
|
||||||
|
-0.464807063f, -0.0149533665f, -0.185798749f, -0.00540314987f, 0.182073534f, -0.000651287497f,
|
||||||
|
-0.435764432f, 0.00162558386f, -0.181552932f, 0.00792864431f, -0.700565279f, 0.0110246018f,
|
||||||
|
-0.144087434f, 0.0120453080f, -0.524990261f, 0.0138590708f, -0.182723984f, 0.0165519360f,
|
||||||
|
-0.217308879f, 0.0208590515f, 0.462978750f, 0.0247372910f, 0.0956632495f, 0.0323494300f,
|
||||||
|
0.0843820646f, 0.0424364135f, 0.122466311f, 0.0441578403f, -0.162433729f, 0.0528083183f,
|
||||||
|
0.0964344442f, 0.0624147579f, -0.271349967f, 0.0727724135f, -0.266336441f, 0.0719895661f,
|
||||||
|
0.0675768778f, 0.0848240927f, -0.689944625f, 0.0889045894f, -0.680990934f, 0.0903657600f,
|
||||||
|
-0.119472280f, 0.0930491239f, -0.124393739f, 0.0933082998f, -0.323403478f, 0.0937438533f,
|
||||||
|
-0.323273063f, 0.0969979763f, -0.352427900f, 0.101048596f, -0.327554941f, 0.104539163f,
|
||||||
|
-0.330044419f, 0.114519835f, 0.0235135648f, 0.118004657f, -0.671623945f, 0.130437061f,
|
||||||
|
-0.385111898f, 0.142786101f, -0.376281500f, 0.145800456f, -0.0169987213f, 0.148056105f,
|
||||||
|
-0.326495141f, 0.152596891f, -0.337120056f, 0.154522225f, -0.336885720f, 0.154304653f,
|
||||||
|
0.322089493f, 0.155130088f, -0.0713477954f, 0.163638428f, -0.0208650175f, 0.171433330f,
|
||||||
|
-0.380652726f, 0.172022790f, -0.0599780641f, 0.182294667f, 0.244408697f, 0.194245726f,
|
||||||
|
-0.101454332f, 0.198159069f, 0.257901788f, 0.200226694f, -0.0775909275f, 0.205242962f,
|
||||||
|
0.231870517f, 0.222396746f, -0.546760798f, 0.242291704f, -0.538914979f, 0.243761152f,
|
||||||
|
0.206653103f, 0.244874880f, -0.595693469f, 0.264329463f, -0.581023335f, 0.265664101f,
|
||||||
|
0.00444878871f, 0.267031074f, -0.573156178f, 0.271591753f, -0.543381274f, 0.271759123f,
|
||||||
|
0.00450209389f, 0.271335930f, -0.223618075f, 0.278416723f, 0.161934286f, 0.289435983f,
|
||||||
|
-0.199636295f, 0.296817899f, -0.250217140f, 0.299677849f, -0.258231103f, 0.314012855f,
|
||||||
|
-0.628315628f, 0.316889286f, 0.320948511f, 0.316358119f, -0.246845752f, 0.320511192f,
|
||||||
|
0.0687271580f, 0.321383297f, 0.0784438103f, 0.322898388f, 0.0946765989f, 0.325111747f,
|
||||||
|
-0.249674007f, 0.328731328f, -0.244633347f, 0.329467386f, -0.245841011f, 0.334985316f,
|
||||||
|
0.118609101f, 0.343532443f, 0.0497615598f, 0.348162144f, -0.221477821f, 0.349263757f,
|
||||||
|
0.0759577379f, 0.351840734f, 0.0504637137f, 0.373238713f, 0.0730970055f, 0.376537383f,
|
||||||
|
-0.204333842f, 0.381100655f, -0.557245076f, -0.339432925f, -0.402010202f, -0.288829565f,
|
||||||
|
-0.350465477f, -0.281259984f, -0.352995187f, -0.264569730f, -0.466762394f, -0.217114508f,
|
||||||
|
0.152002022f, -0.217566550f, 0.146226048f, -0.183914393f, 0.0949312001f, -0.177005857f,
|
||||||
|
-0.211882949f, -0.175594494f, -0.531562269f, -0.173924312f, -0.0727246776f, -0.167270422f,
|
||||||
|
0.0546481088f, -0.140193000f, -0.296819001f, -0.137850702f, -0.261863053f, -0.139540121f,
|
||||||
|
0.187967837f, -0.131033540f, 0.322852045f, -0.112108752f, -0.0432251953f, -0.102951847f,
|
||||||
|
-0.0453428440f, -0.0914504975f, -0.0182842426f, -0.0918859020f, 0.0140433423f, -0.0904538929f,
|
||||||
|
-0.377287626f, -0.0817026496f, 0.266108125f, -0.0797783583f, 0.257961422f, -0.0767710134f,
|
||||||
|
-0.495943695f, -0.0683977529f, 0.231466040f, -0.0675206482f, -0.240675926f, -0.0551427566f,
|
||||||
|
-0.482824773f, -0.0510699376f, -0.491354793f, -0.0414650664f, -0.0960614979f, -0.0377000235f,
|
||||||
|
-0.102409534f, -0.0369749814f, -0.471273214f, -0.0325376652f, -0.483320534f, -0.0174943600f,
|
||||||
|
-0.457503378f, -0.0152483145f, -0.178161725f, -0.0153892851f, -0.483233035f, -0.0106405178f,
|
||||||
|
-0.472914547f, -0.0105228210f, -0.166542307f, -0.00667150877f, 0.181261331f, -0.00449455017f,
|
||||||
|
-0.474292487f, -0.00428914558f, -0.185297221f, -0.00575157674f, -0.494381040f, -0.00278507406f,
|
||||||
|
-0.141748473f, -0.00289725070f, -0.487515569f, 0.000758233888f, 0.322646528f, 0.0197495818f,
|
||||||
|
0.142943904f, 0.0276249554f, -0.563232243f, 0.0306834858f, -0.555995941f, 0.0367121249f,
|
||||||
|
0.114935011f, 0.0496927276f, -0.152954608f, 0.0538645200f, -0.594885707f, 0.0562511310f,
|
||||||
|
0.0678326488f, 0.0756176412f, -0.667605639f, 0.0828208700f, -0.354470938f, 0.101424232f,
|
||||||
|
0.0228204262f, 0.120382607f, -0.639557123f, 0.124422595f, -0.690505445f, 0.126883239f,
|
||||||
|
-0.395509213f, 0.130242139f, -0.00618012529f, 0.139929801f, 0.175945997f, 0.140235618f,
|
||||||
|
0.198833048f, 0.167587668f, -0.334679037f, 0.177859858f, 0.236127406f, 0.192743436f,
|
||||||
|
0.283146858f, 0.204260647f, -0.0354267135f, 0.206209183f, 0.247388184f, 0.207016930f,
|
||||||
|
-0.0422560424f, 0.212493256f, 0.261681855f, 0.215763748f, 0.207528576f, 0.219807997f,
|
||||||
|
-0.300219178f, 0.221922547f, 0.206393883f, 0.245171010f, 0.239619836f, 0.244768366f,
|
||||||
|
-0.523026288f, 0.250639766f, -0.591975033f, 0.254252791f, 0.246785000f, 0.252878994f,
|
||||||
|
0.272995651f, 0.255815417f, 0.00825022161f, 0.265591830f, 0.192723796f, 0.266924977f,
|
||||||
|
-0.222951472f, 0.290150762f, -0.545146644f, 0.304910392f, 0.131736591f, 0.319247276f,
|
||||||
|
0.319435924f, 0.317917794f, 0.0687546134f, 0.321296155f, -0.255853772f, 0.327258259f,
|
||||||
|
0.0948092714f, 0.325284332f, 0.104488030f, 0.327628911f, -0.245483562f, 0.327617317f,
|
||||||
|
0.0647632629f, 0.363111496f, -0.382861346f, -0.287226975f, -0.354297429f, -0.278708905f,
|
||||||
|
-0.356116027f, -0.262691110f, -0.369049937f, -0.237850189f, -0.146217853f, -0.233530551f,
|
||||||
|
0.102752604f, -0.223108903f, 0.137545392f, -0.218163848f, 0.125815898f, -0.216970086f,
|
||||||
|
-0.557826996f, -0.194665924f, -0.533946335f, -0.184958249f, 0.0976954028f, -0.173691019f,
|
||||||
|
-0.240166873f, -0.160652772f, 0.166464865f, -0.154563308f, -0.0330923162f, -0.125799045f,
|
||||||
|
-0.290044904f, -0.118914597f, 0.00350888353f, -0.108661920f, -0.0109116854f, -0.106212743f,
|
||||||
|
-0.0298740193f, -0.102953635f, -0.287203342f, -0.0997403413f, -0.269498408f, -0.0981520712f,
|
||||||
|
-0.000815737061f, -0.0938294530f, 0.274663270f, -0.0844340026f, -0.371082008f, -0.0805466920f,
|
||||||
|
-0.368196100f, -0.0743779093f, 0.00675902702f, -0.0735078678f, 0.226267770f, -0.0744194537f,
|
||||||
|
-0.241736412f, -0.0630025938f, -0.408663541f, -0.0564615242f, 0.251640886f, -0.0519632548f,
|
||||||
|
0.249993712f, -0.0519672707f, -0.426033378f, -0.0365641154f, -0.467352122f, -0.0305716563f,
|
||||||
|
0.251341015f, -0.0268137120f, -0.443456501f, -0.0243669953f, -0.502199471f, -0.0151771074f,
|
||||||
|
-0.178487480f, -0.0155749097f, 0.178145915f, -0.00528379623f, -0.492981344f, -0.00174682145f,
|
||||||
|
-0.150337398f, 0.000692513015f, -0.457302928f, 0.00352234906f, 0.190587431f, 0.00151424226f,
|
||||||
|
-0.482671946f, 0.00682042213f, -0.158589542f, 0.0150188655f, -0.182223722f, 0.0145649035f,
|
||||||
|
0.107089065f, 0.0223725326f, 0.135399371f, 0.0275243558f, -0.552838683f, 0.0275048595f,
|
||||||
|
-0.432176501f, 0.0248741303f, -0.192510992f, 0.0281074084f, -0.553043425f, 0.0298770685f,
|
||||||
|
-0.684887648f, 0.0436144769f, 0.0850105733f, 0.0448755622f, -0.165784389f, 0.0439001285f,
|
||||||
|
0.102653719f, 0.0457992665f, 0.114853017f, 0.0504316092f, -0.647432685f, 0.0608204119f,
|
||||||
|
0.0828530043f, 0.0608987175f, 0.0894377902f, 0.0742467493f, 0.0702404827f, 0.0767309442f,
|
||||||
|
-0.613642335f, 0.0779517740f, -0.670592189f, 0.0849624202f, -0.395209312f, 0.0854151621f,
|
||||||
|
0.125186160f, 0.0919951499f, -0.359707922f, 0.102121405f, -0.354259193f, 0.101300709f,
|
||||||
|
0.0304000825f, 0.110619470f, -0.677573025f, 0.114422500f, 0.0305799693f, 0.121603437f,
|
||||||
|
-0.358950615f, 0.121660560f, -0.718753040f, 0.134569481f, 0.256451160f, 0.141883001f,
|
||||||
|
-0.0904129520f, 0.146879435f, -0.0184279438f, 0.148968369f, -0.356992692f, 0.160104826f,
|
||||||
|
-0.337676436f, 0.161766291f, 0.201174691f, 0.169025913f, -0.378423393f, 0.170933828f,
|
||||||
|
-0.601599216f, 0.174998865f, -0.0902864039f, 0.184311926f, -0.0584819093f, 0.184186250f,
|
||||||
|
0.294467270f, 0.182560727f, 0.250262231f, 0.186239958f, -0.326370239f, 0.191697389f,
|
||||||
|
-0.0980727375f, 0.196913749f, 0.253085673f, 0.201914877f, -0.0344332159f, 0.205900863f,
|
||||||
|
0.255287141f, 0.203029931f, -0.452713937f, 0.205191836f, 0.264822274f, 0.217408702f,
|
||||||
|
-0.0290334225f, 0.221684650f, -0.583990574f, 0.237398431f, -0.145020664f, 0.240374506f,
|
||||||
|
0.249667659f, 0.254706532f, 0.274279058f, 0.256447285f, -0.282936275f, 0.259140193f,
|
||||||
|
0.241211995f, 0.260401577f, -0.590560019f, 0.272659779f, -0.574947417f, 0.272671998f,
|
||||||
|
-0.224780366f, 0.279990941f, -0.525540829f, 0.287235677f, -0.247069210f, 0.298608154f,
|
||||||
|
-0.201292604f, 0.298156679f, 0.319822490f, 0.317605704f, -0.248013541f, 0.320789784f,
|
||||||
|
0.0957527757f, 0.326543272f, 0.105006196f, 0.328469753f, -0.264089525f, 0.332354158f,
|
||||||
|
-0.670460403f, 0.339870930f, 0.118318990f, 0.345167071f, 0.0737744719f, 0.353734553f,
|
||||||
|
0.0655663237f, 0.361025929f, -0.306805104f, 0.363820761f, 0.0524423867f, 0.371921480f,
|
||||||
|
0.0713953897f, 0.375074357f, -0.411387652f, -0.268335998f, -0.357590824f, -0.263346583f,
|
||||||
|
-0.407676578f, -0.253785878f, 0.0660323426f, -0.253718942f, -0.157670841f, -0.225629836f,
|
||||||
|
0.170453921f, -0.220800355f, -0.475751191f, -0.209005311f, -0.331408232f, -0.203059763f,
|
||||||
|
-0.173841938f, -0.199112654f, -0.503261328f, -0.193795130f, -0.532277644f, -0.190292686f,
|
||||||
|
-0.0972326621f, -0.191563144f, -0.0692789108f, -0.172031537f, -0.318824291f, -0.169072524f,
|
||||||
|
-0.576232314f, -0.162124678f, -0.0839322209f, -0.156304389f, -0.583625376f, -0.142171323f,
|
||||||
|
-0.0546422042f, -0.135338858f, 0.0501612425f, -0.132490858f, -0.645011544f, -0.111341864f,
|
||||||
|
-0.0925374180f, -0.0483307689f, -0.444242209f, -0.0263337940f, 0.0335495919f, -0.0281750113f,
|
||||||
|
0.274629444f, -0.0259516705f, 0.213774025f, -0.0240113474f, -0.194874078f, -0.0151330847f,
|
||||||
|
0.175111562f, -0.00868577976f, -0.185011521f, -0.000680683181f, 0.152071685f, 0.0204544198f,
|
||||||
|
0.321354061f, 0.0199794695f, -0.192160159f, 0.0275637116f, -0.189656645f, 0.0275667012f,
|
||||||
|
0.137452200f, 0.0298070628f, -0.194602579f, 0.0449027494f, -0.647751570f, 0.0625102371f,
|
||||||
|
0.124078721f, 0.0639316663f, 0.125849217f, 0.0762147456f, -0.614036798f, 0.0778791085f,
|
||||||
|
-0.684063017f, 0.0867959261f, -0.670344174f, 0.0846142769f, -0.127689242f, 0.0883567855f,
|
||||||
|
0.123796627f, 0.0907361880f, -0.356352538f, 0.101948388f, -0.388843179f, 0.110183217f,
|
||||||
|
0.0316384435f, 0.123791300f, -0.627986908f, 0.146491125f, -0.0747071728f, 0.158135459f,
|
||||||
|
-0.0235102437f, 0.168867558f, -0.0903210714f, 0.184088305f, 0.292073458f, 0.183571488f,
|
||||||
|
-0.0585953295f, 0.184784085f, -0.0317775607f, 0.218368888f, 0.209752038f, 0.223883361f,
|
||||||
|
-0.295424402f, 0.229150623f, -0.144439027f, 0.237902716f, -0.284140587f, 0.262761474f,
|
||||||
|
0.289083928f, 0.276900887f, 0.159017235f, 0.300793648f, -0.204925507f, 0.298536539f,
|
||||||
|
-0.544958472f, 0.305164427f, -0.261615157f, 0.306550682f, 0.0977220088f, 0.327949613f,
|
||||||
|
0.109876208f, 0.337665111f, -0.283918083f, 0.347385526f, 0.0436712503f, 0.350702018f,
|
||||||
|
0.114512287f, 0.367949426f, 0.106543839f, 0.375095814f, 0.505324781f, -0.272183985f,
|
||||||
|
0.0645913780f, -0.251512915f, -0.457196057f, -0.225893468f, -0.480293810f, -0.222602293f,
|
||||||
|
-0.138176888f, -0.209798917f, -0.110901751f, -0.198036820f, -0.196451947f, -0.191723794f,
|
||||||
|
-0.537742376f, -0.174413025f, -0.0650562346f, -0.174762890f, -0.567489207f, -0.165461496f,
|
||||||
|
0.0879585966f, -0.163023785f, -0.303777844f, -0.142031133f, 0.199195996f, -0.141861767f,
|
||||||
|
0.0491657220f, -0.132264882f, -0.497363061f, -0.107934952f, -0.000536393432f, -0.102828167f,
|
||||||
|
0.0155952247f, -0.0998895392f, -0.363601953f, -0.0897399634f, -0.224325985f, -0.0719678402f,
|
||||||
|
-0.0638299435f, -0.0646244809f, -0.108656809f, -0.0468749776f, -0.0865045264f, -0.0512534790f,
|
||||||
|
-0.469339728f, -0.0279338267f, 0.0578282699f, -0.0133374622f, -0.195265710f, -0.0115369316f,
|
||||||
|
0.296735317f, -0.0132813146f, 0.0664219409f, 0.0134935537f, 0.126060545f, 0.0333039127f,
|
||||||
|
0.139887005f, 0.0334976614f, -0.547339618f, 0.0433730707f, 0.0866046399f, 0.0527233221f,
|
||||||
|
0.131943896f, 0.0657638907f, -0.280056775f, 0.0685855150f, 0.0746403933f, 0.0795079395f,
|
||||||
|
0.125382811f, 0.0822770745f, -0.648187757f, 0.103887804f, -0.107411072f, 0.107508548f,
|
||||||
|
0.0155869983f, 0.108978622f, 0.0189307462f, 0.129617691f, 0.162685350f, 0.127225950f,
|
||||||
|
-0.0875291452f, 0.142281070f, 0.319728941f, 0.148827255f, -0.0259547811f, 0.169724479f,
|
||||||
|
0.259297132f, 0.190075457f, -0.467013776f, 0.212794706f, -0.315732479f, 0.219243437f,
|
||||||
|
-0.111042649f, 0.217940107f, 0.239550352f, 0.222786069f, 0.263966352f, 0.260309041f,
|
||||||
|
0.320023954f, -0.222228840f, -0.322707742f, -0.213004455f, -0.224977970f, -0.169595599f,
|
||||||
|
-0.605799317f, -0.142425537f, 0.0454332717f, -0.129945949f, 0.205748767f, -0.113405459f,
|
||||||
|
0.317985803f, -0.118630089f, 0.497755647f, -0.0962266177f, -0.393495560f, -0.0904672816f,
|
||||||
|
0.240035087f, -0.0737613589f, -0.212947786f, -0.0280145984f, 0.0674179196f, 0.0124880793f,
|
||||||
|
-0.545862198f, 0.0207057912f, -0.284409463f, 0.0626631007f, -0.107082598f, 0.0854173824f,
|
||||||
|
0.0578137375f, 0.0917839557f, 0.145844117f, 0.102937251f, 0.183878779f, 0.119614877f,
|
||||||
|
-0.626380265f, 0.140862882f, -0.0325521491f, 0.161834121f, -0.590211987f, 0.167720392f,
|
||||||
|
0.289599866f, 0.186565816f, -0.328821093f, 0.187714070f, -0.289086968f, 0.205165654f,
|
||||||
|
-0.445392698f, 0.215343162f, 0.173715711f, 0.273563296f, 0.284015119f, 0.270610362f,
|
||||||
|
0.0174398609f, 0.283809274f, -0.496335506f, -0.202981815f, 0.0389454551f, -0.166210428f,
|
||||||
|
-0.317301393f, -0.156280205f, -0.396320462f, -0.0949599668f, -0.213638976f, -0.0776446015f,
|
||||||
|
0.497601509f, -0.0928353444f, -0.260220319f, -0.0718628615f, -0.116495222f, -0.0543703064f,
|
||||||
|
-0.118132629f, -0.0156126227f, 0.0242815297f, 0.00629332382f, -0.537928998f, 0.00815516617f,
|
||||||
|
0.317720622f, 0.0271231923f, -0.582170665f, 0.0478387438f, -0.536856830f, 0.0466793887f,
|
||||||
|
-0.220819592f, 0.0433096550f, -0.246473342f, 0.0572598167f, 0.481240988f, 0.0503845438f,
|
||||||
|
-0.102453016f, 0.0649363101f, -0.149955124f, 0.0744054317f, -0.248215869f, 0.0916868672f,
|
||||||
|
-0.101221249f, 0.110788561f, -0.437672526f, 0.179065496f, -0.0383506976f, 0.183546484f,
|
||||||
|
-0.279600590f, 0.208760634f, 0.182261929f, 0.275244594f, 0.0253023170f, -0.170456246f,
|
||||||
|
-0.476852804f, -0.123630777f, -0.0803126246f, -0.0782076195f, -0.133338496f, -0.0659459904f,
|
||||||
|
-0.0822777376f, -0.00390591589f, 0.149250969f, 0.104314201f, 0.0418044887f, 0.149009049f,
|
||||||
|
-0.438308835f, 0.164682120f
|
||||||
|
};
|
||||||
|
|
||||||
|
const Point2f* currRectifiedPointArr_2f = (const Point2f*)currRectifiedPointArr;
|
||||||
|
vector<Point2f> currRectifiedPoints(currRectifiedPointArr_2f,
|
||||||
|
currRectifiedPointArr_2f + sizeof(currRectifiedPointArr) / sizeof(currRectifiedPointArr[0]) / 2);
|
||||||
|
|
||||||
|
_currRectifiedPoints.swap(currRectifiedPoints);
|
||||||
|
|
||||||
|
static const float prevRectifiedPointArr[] = {
|
||||||
|
-0.599324584f, -0.381164283f, -0.387985110f, -0.385367423f, -0.371437579f, -0.371891201f,
|
||||||
|
-0.340867460f, -0.370632380f, -0.289822906f, -0.364118159f, -0.372411519f, -0.335272551f,
|
||||||
|
-0.289586753f, -0.335766882f, -0.372335523f, -0.316857219f, -0.321099430f, -0.323233813f,
|
||||||
|
0.208661616f, -0.153931335f, -0.559897065f, 0.193362445f, 0.0181128159f, -0.325224668f,
|
||||||
|
-0.427504510f, 0.105302416f, 0.487470537f, -0.187071189f, 0.343267351f, -0.339755565f,
|
||||||
|
-0.477639943f, -0.204375938f, -0.466626763f, -0.204072326f, 0.340813518f, -0.347292691f,
|
||||||
|
0.342682719f, -0.320172101f, 0.383663863f, -0.327343374f, -0.467062414f, -0.193995550f,
|
||||||
|
-0.475603998f, -0.189820126f, 0.552475691f, 0.198386014f, -0.508027375f, -0.174297482f,
|
||||||
|
-0.211989403f, -0.217261642f, 0.180832058f, -0.127527758f, -0.112721168f, -0.125876635f,
|
||||||
|
-0.112387165f, -0.167135969f, -0.562491000f, -0.140186235f, 0.395156831f, -0.298828602f,
|
||||||
|
-0.485202312f, -0.135626689f, 0.148358017f, -0.195937276f, -0.248159677f, -0.254669130f,
|
||||||
|
-0.568366945f, -0.105187029f, -0.0714842379f, -0.0832463056f, -0.497599572f, -0.205334768f,
|
||||||
|
-0.0948727652f, 0.245045587f, 0.160857186f, 0.138075173f, 0.164952606f, -0.195109487f,
|
||||||
|
0.165254518f, -0.186554477f, -0.183777973f, -0.124357253f, 0.166813776f, -0.153241888f,
|
||||||
|
-0.241765827f, -0.0820638761f, 0.208661616f, -0.153931335f, 0.540147483f, -0.203156039f,
|
||||||
|
0.529201686f, -0.199348077f, -0.248159677f, -0.254669130f, 0.180369601f, -0.139303327f,
|
||||||
|
0.570952237f, -0.185722873f, 0.221771300f, -0.143187970f, 0.498627752f, -0.183768719f,
|
||||||
|
0.561214447f, -0.188666284f, -0.241409421f, -0.253560483f, 0.569648385f, -0.184499770f,
|
||||||
|
0.276665628f, -0.0881819800f, 0.533934176f, -0.142226711f, -0.299728751f, -0.330407321f,
|
||||||
|
0.270322412f, -0.256552309f, -0.255016476f, -0.0823200271f, -0.378096581f, 0.0264666155f,
|
||||||
|
-0.331565350f, 0.0210608803f, 0.0100810500f, -0.0213523544f, -0.248159677f, -0.254669130f,
|
||||||
|
0.249623299f, 0.164078355f, 0.0190342199f, -0.00415771967f, 0.604407132f, -0.259350061f,
|
||||||
|
0.0660026148f, -0.00787150953f, 0.605921566f, 0.114344336f, 0.0208173525f, 0.00527517078f,
|
||||||
|
-0.0200567022f, 0.0183092188f, -0.184784368f, -0.193566754f, -0.0125719802f, -0.344967902f,
|
||||||
|
0.343063682f, -0.0121044181f, 0.389022052f, -0.0171062462f, 0.163190305f, 0.200014487f,
|
||||||
|
0.362440646f, 0.0120019922f, -0.427743971f, 0.100272447f, -0.0714842379f, -0.0832463056f,
|
||||||
|
0.0664352402f, 0.0467514023f, -0.559897065f, 0.193362445f, -0.549086213f, 0.193808615f,
|
||||||
|
-0.241472989f, -0.253163874f, -0.241765827f, -0.0820638761f, -0.122216024f, 0.132651567f,
|
||||||
|
-0.122216024f, 0.132651567f, 0.515065968f, 0.205271944f, 0.180832058f, -0.127527758f,
|
||||||
|
-0.123633556f, 0.154476687f, -0.248159677f, -0.254669130f, 0.0208173525f, 0.00527517078f,
|
||||||
|
-0.483276874f, 0.191274792f, -0.167928949f, 0.200682297f, 0.232745290f, -0.211950779f,
|
||||||
|
-0.288701504f, -0.334238827f, -0.119621970f, 0.204155236f, -0.119621970f, 0.204155236f,
|
||||||
|
0.632996142f, 0.0804972649f, 0.189231426f, 0.164325386f, 0.249623299f, 0.164078355f,
|
||||||
|
0.0676716864f, 0.0479496233f, 0.207636267f, 0.184271768f, -0.300510556f, 0.358790994f,
|
||||||
|
-0.107678331f, 0.188473806f, 0.565983415f, 0.144723341f, 0.191329703f, 0.213909492f,
|
||||||
|
-0.0283227600f, -0.373237878f, -0.184958130f, 0.200373843f, 0.0346363746f, -0.0259889495f,
|
||||||
|
-0.112387165f, -0.167135969f, 0.251426309f, 0.210430339f, -0.477397382f, -0.131372169f,
|
||||||
|
-0.0667442903f, 0.0997460634f, 0.251426309f, 0.210430339f, -0.317926824f, 0.375238001f,
|
||||||
|
-0.0621999837f, 0.280056626f, 0.0443522707f, 0.321513236f, 0.471269101f, 0.260774940f,
|
||||||
|
-0.107678331f, 0.188473806f, 0.0208210852f, 0.350526422f, 0.0157474391f, 0.367335707f,
|
||||||
|
0.632996142f, 0.0804972649f, 0.646697879f, 0.265504390f, 0.0295150280f, 0.371205181f,
|
||||||
|
0.376071006f, 0.313471258f, -0.379525930f, 0.364357829f, -0.00628023129f, -0.0373278372f,
|
||||||
|
0.0291138459f, 0.381194293f, 0.0358079821f, 0.381886899f, 0.0344478637f, 0.386993408f,
|
||||||
|
0.433862329f, 0.328515977f, 0.359724253f, 0.345606029f, 0.0651357397f, 0.397334814f,
|
||||||
|
0.388413996f, 0.344747871f, -0.140228778f, 0.216103494f, 0.389989913f, 0.372472703f,
|
||||||
|
0.444995403f, 0.300240308f, -0.606455386f, 0.100793049f, -0.362332910f, -0.371920794f,
|
||||||
|
-0.478956074f, 0.234040022f, -0.289441198f, -0.344822973f, -0.0714842379f, -0.0832463056f,
|
||||||
|
0.375879139f, -0.374975592f, 0.376526117f, -0.326493502f, 0.313251913f, -0.306372881f,
|
||||||
|
-0.0577337518f, 0.0893306211f, -0.483683407f, -0.179540694f, -0.0763650239f, -0.258294433f,
|
||||||
|
0.276665628f, -0.0881819800f, -0.167122558f, -0.175508693f, -0.164081737f, 0.176902041f,
|
||||||
|
0.276665628f, -0.0881819800f, 0.602967978f, -0.260941893f, 0.158573851f, -0.178748295f,
|
||||||
|
0.159815103f, -0.160761341f, 0.194283918f, -0.165657878f, 0.231515527f, -0.172808051f,
|
||||||
|
-0.247000366f, 0.277822912f, 0.538969517f, -0.204621449f, 0.531404376f, -0.198565826f,
|
||||||
|
-0.388338953f, -0.0433262810f, 0.499413073f, -0.181929186f, -0.237337112f, 0.0934364349f,
|
||||||
|
-0.368045300f, -0.0204487685f, -0.374767631f, -0.00678646797f, -0.0667242110f, -0.248651102f,
|
||||||
|
-0.248159677f, -0.254669130f, -0.345217139f, -0.00101677026f, -0.353382975f, 0.0210586078f,
|
||||||
|
-0.322639942f, 0.0211628731f, 0.0184581745f, -0.0366852731f, 0.0259528626f, -0.0136881955f,
|
||||||
|
-0.339446336f, 0.0286702402f, 0.0335014127f, -0.0271516014f, 0.465966076f, 0.0830826238f,
|
||||||
|
-0.337860256f, 0.0362124667f, 0.188271523f, -0.146541893f, -0.298272073f, -0.323130161f,
|
||||||
|
0.0643569306f, -0.0264105909f, -0.353804410f, 0.0433940105f, 0.618646920f, -0.0855877250f,
|
||||||
|
0.411329508f, -0.0414552018f, -0.427743971f, 0.100272447f, -0.247000366f, 0.277822912f,
|
||||||
|
0.381912649f, -0.00914942939f, 0.0664352402f, 0.0467514023f, 0.138687640f, -0.114854909f,
|
||||||
|
-0.0170480162f, -0.372787565f, -0.535477102f, 0.183755845f, -0.155668780f, 0.144164801f,
|
||||||
|
-0.427504510f, 0.105302416f, -0.484430760f, 0.227277100f, -0.361284673f, -0.373513311f,
|
||||||
|
-0.316764563f, 0.331503242f, -0.0230990555f, 0.314180285f, 0.101539977f, -0.256640851f,
|
||||||
|
-0.210743994f, -0.111771651f, -0.560086846f, 0.151153624f, 0.542884171f, 0.141691014f,
|
||||||
|
0.596041858f, 0.144990161f, 0.239398748f, 0.207432285f, 0.557545543f, 0.155783832f,
|
||||||
|
0.233033463f, 0.214694947f, 0.572789013f, 0.162068501f, 0.512761712f, 0.176260322f,
|
||||||
|
0.287076950f, 0.0868823677f, 0.515065968f, 0.205271944f, 0.552475691f, 0.198386014f,
|
||||||
|
-0.301232725f, 0.347804308f, -0.379525930f, 0.364357829f, 0.561403453f, 0.206571117f,
|
||||||
|
0.590792358f, 0.206283644f, -0.428855836f, 0.100270294f, 0.300039053f, -0.283949375f,
|
||||||
|
0.0481642894f, 0.334260821f, -0.173260480f, -0.167126089f, 0.444995403f, 0.300240308f,
|
||||||
|
0.646697879f, 0.265504390f, 0.375487208f, 0.314186513f, 0.0217850581f, 0.381838262f,
|
||||||
|
0.404422343f, 0.313856274f, 0.417644382f, 0.314869910f, 0.0358079821f, 0.381886899f,
|
||||||
|
0.378262609f, 0.358303785f, -0.336999178f, -0.367679387f, -0.295442462f, -0.365161836f,
|
||||||
|
-0.293496192f, -0.342732310f, -0.298767596f, -0.303165644f, -0.0111337993f, -0.342149645f,
|
||||||
|
0.310648471f, -0.374146342f, 0.359467417f, -0.373746723f, 0.340779394f, -0.369219989f,
|
||||||
|
-0.527450860f, -0.203896046f, -0.490746915f, -0.194764644f, 0.314866364f, -0.300261766f,
|
||||||
|
-0.0298556220f, 0.0591949411f, 0.319549739f, 0.0552458987f, 0.163977623f, -0.209844783f,
|
||||||
|
-0.149107113f, -0.149005055f, 0.212483421f, -0.191198543f, 0.197611198f, -0.187811792f,
|
||||||
|
0.174361721f, -0.179897651f, 0.0387913659f, -0.0366905928f, -0.122265801f, -0.126270071f,
|
||||||
|
0.211038783f, -0.172842503f, 0.246728286f, 0.134398326f, -0.0577337518f, 0.0893306211f,
|
||||||
|
-0.415295422f, 0.105914228f, -0.292730510f, 0.0379575789f, 0.489636958f, -0.194117576f,
|
||||||
|
-0.254337519f, 0.0937413648f, 0.336177140f, 0.305443168f, 0.526942134f, -0.164069965f,
|
||||||
|
0.524966419f, -0.165161178f, -0.379173398f, 0.332068861f, -0.340792000f, 0.00105464540f,
|
||||||
|
0.525632977f, -0.134992197f, -0.308774501f, 0.00290521770f, -0.375407755f, 0.0294080544f,
|
||||||
|
0.0178439785f, -0.0365749858f, -0.255016476f, -0.0823200271f, -0.359951973f, 0.0446678996f,
|
||||||
|
0.0564084686f, -0.0197724514f, -0.315141559f, 0.0424463004f, 0.292196661f, 0.279810339f,
|
||||||
|
-0.345294952f, 0.0533128195f, 0.0458479226f, -0.00109126628f, 0.0179449394f, 0.00371767790f,
|
||||||
|
0.365872562f, -0.0412087664f, 0.403013051f, -0.0416624695f, -0.0714842379f, -0.0832463056f,
|
||||||
|
-0.209011748f, 0.133690849f, 0.0122421598f, 0.0230175443f, -0.0577337518f, 0.0893306211f,
|
||||||
|
-0.572846889f, 0.141102776f, 0.345340014f, -0.0111671211f, 0.0479373708f, 0.0379454680f,
|
||||||
|
0.363291621f, -0.00829032529f, 0.381912649f, -0.00914942939f, -0.521542430f, 0.151489466f,
|
||||||
|
0.345966965f, 0.0110620018f, 0.354562849f, 0.0254590791f, 0.334322065f, 0.0310698878f,
|
||||||
|
-0.00463629747f, -0.0357710384f, -0.538667142f, 0.185365483f, -0.209011748f, 0.133690849f,
|
||||||
|
0.398122877f, 0.0403857268f, -0.160881191f, 0.145009249f, -0.155668780f, 0.144164801f,
|
||||||
|
-0.0714842379f, -0.0832463056f, -0.536377013f, 0.221241340f, -0.0632879063f, -0.247039422f,
|
||||||
|
-0.155869946f, 0.169341147f, 0.578685045f, -0.223878756f, 0.557447612f, 0.0768704116f,
|
||||||
|
-0.188812047f, 0.228197843f, 0.246747240f, 0.136472240f, -0.142677084f, 0.213736445f,
|
||||||
|
-0.118143238f, 0.208306640f, -0.388338953f, -0.0433262810f, -0.163515776f, 0.231573820f,
|
||||||
|
-0.0738375857f, -0.256104171f, 0.173092276f, 0.191535592f, 0.208548918f, 0.185476139f,
|
||||||
|
-0.392410189f, 0.0686017647f, 0.555366814f, 0.130478472f, -0.101943128f, -0.113997340f,
|
||||||
|
0.0716935173f, 0.340265751f, 0.561738014f, 0.148283109f, 0.242452115f, 0.205116034f,
|
||||||
|
0.561738014f, 0.148283109f, -0.427743971f, 0.100272447f, 0.578137994f, 0.163653031f,
|
||||||
|
0.251277626f, 0.223055005f, -0.376505047f, 0.343530416f, -0.0714842379f, -0.0832463056f,
|
||||||
|
0.567448437f, 0.207419440f, 0.590792358f, 0.206283644f, 0.578685045f, -0.223878756f,
|
||||||
|
0.0635343120f, -0.00499309227f, -0.370767444f, 0.384881169f, -0.485191971f, -0.120962359f,
|
||||||
|
0.512761712f, 0.176260322f, -0.375972956f, 0.0288736783f, -0.147176415f, -0.185790271f,
|
||||||
|
0.0752977654f, 0.339190871f, 0.646697879f, 0.265504390f, 0.0282997675f, 0.373214334f,
|
||||||
|
0.410353780f, 0.316089481f, 0.417644382f, 0.314869910f, 0.0147482762f, 0.389459789f,
|
||||||
|
-0.182916895f, -0.140514761f, 0.433515042f, 0.330774426f, 0.388069838f, 0.347381502f,
|
||||||
|
0.378925055f, 0.357438952f, 0.247128293f, -0.116897359f, -0.0230906308f, 0.314556211f,
|
||||||
|
0.388534039f, 0.370789021f, -0.368050814f, -0.339653373f, -0.292694926f, -0.341653705f,
|
||||||
|
-0.353774697f, -0.320387989f, 0.599263310f, -0.264537901f, -0.0213720929f, -0.326088905f,
|
||||||
|
-0.571947694f, 0.141147330f, -0.0577337518f, 0.0893306211f, 0.108424753f, -0.267108470f,
|
||||||
|
-0.0317604132f, -0.0458168685f, -0.0967136100f, 0.242639020f, -0.486509413f, -0.204596937f,
|
||||||
|
0.239178345f, -0.219647482f, 0.108424753f, -0.267108470f, -0.280393064f, -0.283867925f,
|
||||||
|
-0.533659995f, -0.151733354f, 0.0880429000f, -0.240412414f, -0.534965396f, -0.124174178f,
|
||||||
|
0.142445788f, -0.118948005f, 0.0947291106f, 0.0767719224f, -0.597055852f, -0.0692315027f,
|
||||||
|
-0.254337519f, 0.0937413648f, -0.308869720f, 0.00354974205f, -0.409894019f, -0.0694356859f,
|
||||||
|
0.556049764f, -0.137727231f, -0.0317604132f, -0.0458168685f, -0.524152219f, 0.239541322f,
|
||||||
|
0.108424753f, -0.267108470f, 0.0143662402f, -0.0164190196f, 0.150936082f, 0.128616557f,
|
||||||
|
0.618646920f, -0.0855877250f, 0.0122421598f, 0.0230175443f, 0.0122421598f, 0.0230175443f,
|
||||||
|
-0.188812047f, 0.228197843f, 0.00441747159f, -0.297387213f, -0.520719767f, 0.152393058f,
|
||||||
|
0.392849416f, 0.00738697406f, 0.400074363f, 0.0185570847f, -0.161484867f, -0.192373112f,
|
||||||
|
-0.554901838f, 0.190730989f, -0.538667142f, 0.185365483f, -0.0667442903f, 0.0997460634f,
|
||||||
|
0.399885803f, 0.0410231315f, -0.159816831f, 0.145826310f, -0.193316415f, 0.161277503f,
|
||||||
|
-0.0678345188f, 0.287081748f, -0.383089483f, -0.283330113f, -0.538667142f, 0.185365483f,
|
||||||
|
0.245664895f, 0.162005231f, 0.173092276f, 0.191535592f, 0.601281762f, 0.120500855f,
|
||||||
|
0.208548918f, 0.185476139f, 0.246893004f, 0.220670119f, 0.516039073f, 0.178782418f,
|
||||||
|
-0.254337519f, 0.0937413648f, -0.254337519f, 0.0937413648f, -0.0230990555f, 0.314180285f,
|
||||||
|
0.610029638f, 0.227215171f, -0.254337519f, 0.0937413648f, 0.0697976872f, 0.343245506f,
|
||||||
|
0.538969517f, -0.204621449f, 0.00916308723f, 0.359826297f, 0.410353780f, 0.316089481f,
|
||||||
|
0.423950195f, 0.324112266f, 0.166566655f, 0.145402640f, 0.354594171f, 0.350193948f,
|
||||||
|
0.433712035f, 0.356235564f, 0.425307065f, 0.364637494f, 0.166924104f, -0.152513608f,
|
||||||
|
0.594130874f, -0.268246830f, -0.0843627378f, -0.0962528363f, 0.108424753f, -0.267108470f,
|
||||||
|
0.00760878995f, -0.304247797f, -0.471018314f, -0.178305879f, -0.0817007348f, -0.0933016762f,
|
||||||
|
0.232274890f, 0.154553935f, 0.108424753f, -0.267108470f, -0.525787771f, -0.161353886f,
|
||||||
|
-0.206048280f, 0.241006181f, -0.178062543f, -0.184703678f, 0.105906568f, 0.268231422f,
|
||||||
|
-0.0817007348f, -0.0933016762f, 0.490914792f, 0.276718110f, -0.176861435f, -0.153617889f,
|
||||||
|
0.0387795344f, 0.0457828715f, 0.456206828f, -0.250739783f, 0.0982551053f, 0.104225174f,
|
||||||
|
0.142445788f, -0.118948005f, 0.108424753f, -0.267108470f, -0.0817007348f, -0.0933016762f,
|
||||||
|
-0.340707630f, 0.00498990202f, 0.0947291106f, 0.0767719224f, 0.169802040f, 0.203134149f,
|
||||||
|
0.577375948f, -0.125099033f, 0.318376005f, -0.0486588739f, 0.388697982f, -0.0351444185f,
|
||||||
|
0.406605273f, -0.0364143848f, 0.274859309f, 0.0776181892f, 0.349759877f, -7.70174083e-05f,
|
||||||
|
0.402967423f, 0.00697830878f, 0.105906568f, 0.268231422f, 0.338973522f, 0.0359939188f,
|
||||||
|
0.394951165f, 0.0322254188f, -0.503028810f, 0.203627899f, -0.0840740278f, -0.234684706f,
|
||||||
|
0.108424753f, -0.267108470f, 0.286642373f, 0.103878126f, -0.0817007348f, -0.0933016762f,
|
||||||
|
0.332983583f, -0.0356097035f, 0.628004134f, 0.0766527727f, -0.112659439f, -0.196833044f,
|
||||||
|
0.568797410f, 0.136423931f, 0.456206828f, -0.250739783f, -0.254337519f, 0.0937413648f,
|
||||||
|
-0.206692874f, -0.210832119f, 0.550912619f, 0.171586066f, 0.581267595f, 0.213235661f,
|
||||||
|
0.334484309f, 0.303876013f, -0.469516128f, 0.0883551016f, 0.133899942f, 0.106862970f,
|
||||||
|
-0.560961962f, -0.114681393f, -0.0840740278f, -0.234684706f, 0.459386230f, -0.236088052f,
|
||||||
|
0.594130874f, -0.268246830f, -0.124856450f, 0.193096936f, -0.469516128f, 0.0883551016f,
|
||||||
|
0.514290810f, -0.193822652f, 0.158255994f, 0.233290926f, 0.317973822f, -0.0477817170f,
|
||||||
|
-0.0817007348f, -0.0933016762f, -0.0702776462f, -0.0671426803f, 0.440836668f, -0.100193374f,
|
||||||
|
0.326240778f, 0.0523138903f, -0.279556662f, -0.283929169f, -0.485202312f, -0.135626689f,
|
||||||
|
-0.467358112f, 0.246376559f, 0.232274890f, 0.154553935f, 0.258349210f, -0.269529581f,
|
||||||
|
0.600620329f, 0.126268178f, -0.0985416993f, 0.245674044f, -0.279264033f, -0.0990248993f,
|
||||||
|
0.108424753f, -0.267108470f, 0.259638488f, -0.100053802f, 0.605106652f, 0.223564968f,
|
||||||
|
0.129683495f, -0.100376993f, -0.0953388065f, 0.112722203f, -0.440420747f, -0.0396305211f,
|
||||||
|
-0.0181254297f, 0.0439292751f, -0.0878356919f, 0.0847257674f, -0.271582603f, 0.126064256f,
|
||||||
|
-0.183777973f, -0.124357253f, 0.431088895f, 0.0680654719f, -0.469516128f, 0.0883551016f,
|
||||||
|
-0.445174575f, 0.133306518f, -0.0878356919f, 0.0847257674f, -0.279039949f, 0.0810008645f,
|
||||||
|
0.612402737f, -0.0826834291f, -0.454494953f, 0.122878648f, 0.244000912f, -0.264438629f,
|
||||||
|
0.142445788f, -0.118948005f, 0.129683495f, -0.100376993f, -0.210078895f, 0.131698489f,
|
||||||
|
-0.277847171f, 0.0665081516f, 0.431088895f, 0.0680654719f, 0.252345473f, 0.0688349009f,
|
||||||
|
0.133899942f, 0.106862970f, 0.133899942f, 0.106862970f, -0.486509413f, -0.204596937f,
|
||||||
|
-0.0940247625f, 0.0698821172f, 0.133899942f, 0.106862970f, -0.440420747f, -0.0396305211f,
|
||||||
|
-0.0878356919f, 0.0847257674f, -0.0954068601f, -0.0968973264f, -0.277847171f, 0.0665081516f,
|
||||||
|
-0.277847171f, 0.0665081516f, 0.266677618f, 0.111257851f, 0.292424291f, -0.230888903f,
|
||||||
|
-0.0954068601f, -0.0968973264f
|
||||||
|
};
|
||||||
|
|
||||||
|
const Point2f* prevRectifiedPointArr_2f = (const Point2f*)prevRectifiedPointArr;
|
||||||
|
vector<Point2f> prevRectifiedPoints(prevRectifiedPointArr_2f, prevRectifiedPointArr_2f +
|
||||||
|
sizeof(prevRectifiedPointArr) / sizeof(prevRectifiedPointArr[0]) / 2);
|
||||||
|
|
||||||
|
_prevRectifiedPoints.swap(prevRectifiedPoints);
|
||||||
|
|
||||||
|
int validSolutionArr[2] = { 0, 2 };
|
||||||
|
|
||||||
|
vector<int> validSolutions(validSolutionArr, validSolutionArr +
|
||||||
|
sizeof(validSolutionArr) / sizeof(validSolutionArr[0]));
|
||||||
|
|
||||||
|
_validSolutions.swap(validSolutions);
|
||||||
|
|
||||||
|
vector<Mat> rotations;
|
||||||
|
vector<Mat> normals;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < (sizeof(rotationsArray) / sizeof(*rotationsArray)); i++) {
|
||||||
|
Mat tempRotMat = Mat(Matx33d(
|
||||||
|
rotationsArray[i][0],
|
||||||
|
rotationsArray[i][1],
|
||||||
|
rotationsArray[i][2],
|
||||||
|
rotationsArray[i][3],
|
||||||
|
rotationsArray[i][4],
|
||||||
|
rotationsArray[i][5],
|
||||||
|
rotationsArray[i][6],
|
||||||
|
rotationsArray[i][7],
|
||||||
|
rotationsArray[i][8]
|
||||||
|
));
|
||||||
|
|
||||||
|
Mat tempNormMat = Mat(Matx31d(
|
||||||
|
normalsArray[i][0],
|
||||||
|
normalsArray[i][1],
|
||||||
|
normalsArray[i][2]
|
||||||
|
));
|
||||||
|
|
||||||
|
rotations.push_back(tempRotMat);
|
||||||
|
normals.push_back(tempNormMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
_rotations.swap(rotations);
|
||||||
|
_normals.swap(normals);
|
||||||
|
|
||||||
|
_mask = Mat(514, 1, CV_8U, maskArray).clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidResult(const vector<int>& solutions)
|
||||||
|
{
|
||||||
|
return (solutions == _validSolutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<int> _validSolutions;
|
||||||
|
vector<Point2f> _prevRectifiedPoints, _currRectifiedPoints;
|
||||||
|
Mat _mask;
|
||||||
|
vector<Mat> _rotations, _normals;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Calib3d_FilterDecomposeHomography, regression) { CV_FilterHomographyDecompTest test; test.safe_run(); }
|
||||||
|
|
||||||
|
}}
|
@ -463,9 +463,14 @@ static bool ipp_Mat_setTo_Mat(Mat &dst, Mat &_val, Mat &mask)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (dst.depth() == CV_32F)
|
if (dst.depth() == CV_32F)
|
||||||
|
{
|
||||||
for (int i = 0; i < (int)(_val.total()); i++)
|
for (int i = 0; i < (int)(_val.total()); i++)
|
||||||
if (_val.at<double>(i) < iwTypeGetMin(ipp32f) || _val.at<double>(i) > iwTypeGetMax(ipp32f))
|
{
|
||||||
|
float v = (float)(_val.at<double>(i)); // cast to float
|
||||||
|
if (cvIsNaN(v) || cvIsInf(v)) // accept finite numbers only
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(dst.dims <= 2)
|
if(dst.dims <= 2)
|
||||||
{
|
{
|
||||||
|
@ -1612,6 +1612,32 @@ TEST(Mat, regression_7873_mat_vector_initialize)
|
|||||||
ASSERT_EQ(2, sub_mat.size[2]);
|
ASSERT_EQ(2, sub_mat.size[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Mat, regression_10507_mat_setTo)
|
||||||
|
{
|
||||||
|
Size sz(6, 4);
|
||||||
|
Mat test_mask(sz, CV_8UC1, cv::Scalar::all(255));
|
||||||
|
test_mask.at<uchar>(1,0) = 0;
|
||||||
|
test_mask.at<uchar>(0,1) = 0;
|
||||||
|
for (int cn = 1; cn <= 4; cn++)
|
||||||
|
{
|
||||||
|
cv::Mat A(sz, CV_MAKE_TYPE(CV_32F, cn), cv::Scalar::all(5));
|
||||||
|
A.setTo(cv::Scalar::all(std::numeric_limits<float>::quiet_NaN()), test_mask);
|
||||||
|
int nans = 0;
|
||||||
|
for (int y = 0; y < A.rows; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < A.cols; x++)
|
||||||
|
{
|
||||||
|
for (int c = 0; c < cn; c++)
|
||||||
|
{
|
||||||
|
float v = A.ptr<float>(y, x)[c];
|
||||||
|
nans += (v == v) ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(nans, cn * (sz.area() - 2)) << "A=" << A << std::endl << "mask=" << test_mask << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CV_CXX_STD_ARRAY
|
#ifdef CV_CXX_STD_ARRAY
|
||||||
TEST(Core_Mat_array, outputArray_create_getMat)
|
TEST(Core_Mat_array, outputArray_create_getMat)
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,8 @@ ocv_add_dispatched_file_force_all("layers/layers_common" AVX AVX2 AVX512_SKX)
|
|||||||
|
|
||||||
ocv_add_module(dnn opencv_core opencv_imgproc WRAP python matlab java js)
|
ocv_add_module(dnn opencv_core opencv_imgproc WRAP python matlab java js)
|
||||||
|
|
||||||
ocv_option(OPENCV_DNN_OPENCL "Build with OpenCL support" HAVE_OPENCL)
|
ocv_option(OPENCV_DNN_OPENCL "Build with OpenCL support" HAVE_OPENCL AND NOT APPLE)
|
||||||
|
|
||||||
if(OPENCV_DNN_OPENCL AND HAVE_OPENCL)
|
if(OPENCV_DNN_OPENCL AND HAVE_OPENCL)
|
||||||
add_definitions(-DCV_OCL4DNN=1)
|
add_definitions(-DCV_OCL4DNN=1)
|
||||||
else()
|
else()
|
||||||
|
@ -361,6 +361,23 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
|
|||||||
static Ptr<PermuteLayer> create(const LayerParams& params);
|
static Ptr<PermuteLayer> create(const LayerParams& params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permute channels of 4-dimensional input blob.
|
||||||
|
* @param group Number of groups to split input channels and pick in turns
|
||||||
|
* into output blob.
|
||||||
|
*
|
||||||
|
* \f[ groupSize = \frac{number\ of\ channels}{group} \f]
|
||||||
|
* \f[ output(n, c, h, w) = input(n, groupSize \times (c \% group) + \lfloor \frac{c}{group} \rfloor, h, w) \f]
|
||||||
|
* Read more at https://arxiv.org/pdf/1707.01083.pdf
|
||||||
|
*/
|
||||||
|
class CV_EXPORTS ShuffleChannelLayer : public Layer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Ptr<Layer> create(const LayerParams& params);
|
||||||
|
|
||||||
|
int group;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Adds extra values for specific axes.
|
* @brief Adds extra values for specific axes.
|
||||||
* @param paddings Vector of paddings in format
|
* @param paddings Vector of paddings in format
|
||||||
@ -575,6 +592,17 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
|
|||||||
static Ptr<ResizeLayer> create(const LayerParams& params);
|
static Ptr<ResizeLayer> create(const LayerParams& params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Bilinear resize layer from https://github.com/cdmh/deeplab-public
|
||||||
|
*
|
||||||
|
* It differs from @ref ResizeLayer in output shape and resize scales computations.
|
||||||
|
*/
|
||||||
|
class CV_EXPORTS InterpLayer : public Layer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Ptr<Layer> create(const LayerParams& params);
|
||||||
|
};
|
||||||
|
|
||||||
class CV_EXPORTS ProposalLayer : public Layer
|
class CV_EXPORTS ProposalLayer : public Layer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -144,7 +144,8 @@ PERF_TEST_P_(DNNTestNetwork, SSD)
|
|||||||
PERF_TEST_P_(DNNTestNetwork, OpenFace)
|
PERF_TEST_P_(DNNTestNetwork, OpenFace)
|
||||||
{
|
{
|
||||||
if (backend == DNN_BACKEND_HALIDE ||
|
if (backend == DNN_BACKEND_HALIDE ||
|
||||||
backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU)
|
(backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16) ||
|
||||||
|
(backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD))
|
||||||
throw SkipTestException("");
|
throw SkipTestException("");
|
||||||
processNet("dnn/openface_nn4.small2.v1.t7", "", "",
|
processNet("dnn/openface_nn4.small2.v1.t7", "", "",
|
||||||
Mat(cv::Size(96, 96), CV_32FC3));
|
Mat(cv::Size(96, 96), CV_32FC3));
|
||||||
@ -248,6 +249,15 @@ PERF_TEST_P_(DNNTestNetwork, EAST_text_detection)
|
|||||||
processNet("dnn/frozen_east_text_detection.pb", "", "", Mat(cv::Size(320, 320), CV_32FC3));
|
processNet("dnn/frozen_east_text_detection.pb", "", "", Mat(cv::Size(320, 320), CV_32FC3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PERF_TEST_P_(DNNTestNetwork, FastNeuralStyle_eccv16)
|
||||||
|
{
|
||||||
|
if (backend == DNN_BACKEND_HALIDE ||
|
||||||
|
(backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) ||
|
||||||
|
(backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD))
|
||||||
|
throw SkipTestException("");
|
||||||
|
processNet("dnn/fast_neural_style_eccv16_starry_night.t7", "", "", Mat(cv::Size(320, 240), CV_32FC3));
|
||||||
|
}
|
||||||
|
|
||||||
const tuple<DNNBackend, DNNTarget> testCases[] = {
|
const tuple<DNNBackend, DNNTarget> testCases[] = {
|
||||||
#ifdef HAVE_HALIDE
|
#ifdef HAVE_HALIDE
|
||||||
tuple<DNNBackend, DNNTarget>(DNN_BACKEND_HALIDE, DNN_TARGET_CPU),
|
tuple<DNNBackend, DNNTarget>(DNN_BACKEND_HALIDE, DNN_TARGET_CPU),
|
||||||
|
@ -212,6 +212,44 @@ namespace cv {
|
|||||||
fused_layer_names.push_back(last_layer);
|
fused_layer_names.push_back(last_layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAvgpool()
|
||||||
|
{
|
||||||
|
cv::dnn::LayerParams avgpool_param;
|
||||||
|
avgpool_param.set<cv::String>("pool", "ave");
|
||||||
|
avgpool_param.set<bool>("global_pooling", true);
|
||||||
|
avgpool_param.name = "Pooling-name";
|
||||||
|
avgpool_param.type = "Pooling";
|
||||||
|
darknet::LayerParameter lp;
|
||||||
|
|
||||||
|
std::string layer_name = cv::format("avgpool_%d", layer_id);
|
||||||
|
lp.layer_name = layer_name;
|
||||||
|
lp.layer_type = avgpool_param.type;
|
||||||
|
lp.layerParams = avgpool_param;
|
||||||
|
lp.bottom_indexes.push_back(last_layer);
|
||||||
|
last_layer = layer_name;
|
||||||
|
net->layers.push_back(lp);
|
||||||
|
layer_id++;
|
||||||
|
fused_layer_names.push_back(last_layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSoftmax()
|
||||||
|
{
|
||||||
|
cv::dnn::LayerParams softmax_param;
|
||||||
|
softmax_param.name = "Softmax-name";
|
||||||
|
softmax_param.type = "Softmax";
|
||||||
|
darknet::LayerParameter lp;
|
||||||
|
|
||||||
|
std::string layer_name = cv::format("softmax_%d", layer_id);
|
||||||
|
lp.layer_name = layer_name;
|
||||||
|
lp.layer_type = softmax_param.type;
|
||||||
|
lp.layerParams = softmax_param;
|
||||||
|
lp.bottom_indexes.push_back(last_layer);
|
||||||
|
last_layer = layer_name;
|
||||||
|
net->layers.push_back(lp);
|
||||||
|
layer_id++;
|
||||||
|
fused_layer_names.push_back(last_layer);
|
||||||
|
}
|
||||||
|
|
||||||
void setConcat(int number_of_inputs, int *input_indexes)
|
void setConcat(int number_of_inputs, int *input_indexes)
|
||||||
{
|
{
|
||||||
cv::dnn::LayerParams concat_param;
|
cv::dnn::LayerParams concat_param;
|
||||||
@ -541,6 +579,17 @@ namespace cv {
|
|||||||
int pad = getParam<int>(layer_params, "pad", 0);
|
int pad = getParam<int>(layer_params, "pad", 0);
|
||||||
setParams.setMaxpool(kernel_size, pad, stride);
|
setParams.setMaxpool(kernel_size, pad, stride);
|
||||||
}
|
}
|
||||||
|
else if (layer_type == "avgpool")
|
||||||
|
{
|
||||||
|
setParams.setAvgpool();
|
||||||
|
}
|
||||||
|
else if (layer_type == "softmax")
|
||||||
|
{
|
||||||
|
int groups = getParam<int>(layer_params, "groups", 1);
|
||||||
|
if (groups != 1)
|
||||||
|
CV_Error(Error::StsNotImplemented, "Softmax from Darknet with groups != 1");
|
||||||
|
setParams.setSoftmax();
|
||||||
|
}
|
||||||
else if (layer_type == "route")
|
else if (layer_type == "route")
|
||||||
{
|
{
|
||||||
std::string bottom_layers = getParam<std::string>(layer_params, "layers", "");
|
std::string bottom_layers = getParam<std::string>(layer_params, "layers", "");
|
||||||
|
@ -66,6 +66,15 @@ static bool DNN_DISABLE_MEMORY_OPTIMIZATIONS = utils::getConfigurationParameterB
|
|||||||
static bool DNN_OPENCL_ALLOW_ALL_DEVICES = utils::getConfigurationParameterBool("OPENCV_DNN_OPENCL_ALLOW_ALL_DEVICES", false);
|
static bool DNN_OPENCL_ALLOW_ALL_DEVICES = utils::getConfigurationParameterBool("OPENCV_DNN_OPENCL_ALLOW_ALL_DEVICES", false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int PARAM_DNN_BACKEND_DEFAULT = (int)utils::getConfigurationParameterSizeT("OPENCV_DNN_BACKEND_DEFAULT",
|
||||||
|
#ifdef HAVE_INF_ENGINE
|
||||||
|
(size_t)DNN_BACKEND_INFERENCE_ENGINE
|
||||||
|
#else
|
||||||
|
(size_t)DNN_BACKEND_OPENCV
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::map;
|
using std::map;
|
||||||
using std::make_pair;
|
using std::make_pair;
|
||||||
@ -851,11 +860,8 @@ struct Net::Impl
|
|||||||
CV_TRACE_FUNCTION();
|
CV_TRACE_FUNCTION();
|
||||||
|
|
||||||
if (preferableBackend == DNN_BACKEND_DEFAULT)
|
if (preferableBackend == DNN_BACKEND_DEFAULT)
|
||||||
#ifdef HAVE_INF_ENGINE
|
preferableBackend = (Backend)PARAM_DNN_BACKEND_DEFAULT;
|
||||||
preferableBackend = DNN_BACKEND_INFERENCE_ENGINE;
|
|
||||||
#else
|
|
||||||
preferableBackend = DNN_BACKEND_OPENCV;
|
|
||||||
#endif
|
|
||||||
CV_Assert(preferableBackend != DNN_BACKEND_OPENCV ||
|
CV_Assert(preferableBackend != DNN_BACKEND_OPENCV ||
|
||||||
preferableTarget == DNN_TARGET_CPU ||
|
preferableTarget == DNN_TARGET_CPU ||
|
||||||
preferableTarget == DNN_TARGET_OPENCL ||
|
preferableTarget == DNN_TARGET_OPENCL ||
|
||||||
@ -982,52 +988,26 @@ struct Net::Impl
|
|||||||
ld.inputBlobsId[inNum] = from;
|
ld.inputBlobsId[inNum] = from;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void splitPin(const String &pinAlias, String &layerName, String &outName)
|
|
||||||
{
|
|
||||||
size_t delimPos = pinAlias.find('.');
|
|
||||||
layerName = pinAlias.substr(0, delimPos);
|
|
||||||
outName = (delimPos == String::npos) ? String() : pinAlias.substr(delimPos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int resolvePinOutputName(LayerData &ld, const String &outName)
|
int resolvePinOutputName(LayerData &ld, const String &outName)
|
||||||
{
|
{
|
||||||
if (outName.empty())
|
if (outName.empty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (std::isdigit(outName[0]))
|
|
||||||
{
|
|
||||||
char *lastChar;
|
|
||||||
long inum = std::strtol(outName.c_str(), &lastChar, 10);
|
|
||||||
|
|
||||||
if (*lastChar == 0)
|
|
||||||
{
|
|
||||||
CV_Assert(inum == (int)inum);
|
|
||||||
return (int)inum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ld.getLayerInstance()->outputNameToIndex(outName);
|
return ld.getLayerInstance()->outputNameToIndex(outName);
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerPin getPinByAlias(const String &pinAlias)
|
LayerPin getPinByAlias(const String &layerName)
|
||||||
{
|
{
|
||||||
LayerPin pin;
|
LayerPin pin;
|
||||||
String layerName, outName;
|
|
||||||
splitPin(pinAlias, layerName, outName);
|
|
||||||
|
|
||||||
pin.lid = (layerName.empty()) ? 0 : getLayerId(layerName);
|
pin.lid = (layerName.empty()) ? 0 : getLayerId(layerName);
|
||||||
|
|
||||||
if (pin.lid >= 0)
|
if (pin.lid >= 0)
|
||||||
pin.oid = resolvePinOutputName(getLayerData(pin.lid), outName);
|
pin.oid = resolvePinOutputName(getLayerData(pin.lid), layerName);
|
||||||
|
|
||||||
return pin;
|
return pin;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LayerPin> getLayerOutPins(const String &pinAlias)
|
std::vector<LayerPin> getLayerOutPins(const String &layerName)
|
||||||
{
|
{
|
||||||
String layerName, outName;
|
|
||||||
splitPin(pinAlias, layerName, outName);
|
|
||||||
|
|
||||||
int lid = (layerName.empty()) ? 0 : getLayerId(layerName);
|
int lid = (layerName.empty()) ? 0 : getLayerId(layerName);
|
||||||
|
|
||||||
std::vector<LayerPin> pins;
|
std::vector<LayerPin> pins;
|
||||||
@ -1466,7 +1446,7 @@ struct Net::Impl
|
|||||||
// TODO: OpenCL target support more fusion styles.
|
// TODO: OpenCL target support more fusion styles.
|
||||||
if ( preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget) &&
|
if ( preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget) &&
|
||||||
(!cv::ocl::useOpenCL() || (ld.layerInstance->type != "Convolution" &&
|
(!cv::ocl::useOpenCL() || (ld.layerInstance->type != "Convolution" &&
|
||||||
ld.layerInstance->type != "MVN")) )
|
ld.layerInstance->type != "MVN" && ld.layerInstance->type != "Pooling")) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Ptr<Layer>& currLayer = ld.layerInstance;
|
Ptr<Layer>& currLayer = ld.layerInstance;
|
||||||
@ -2013,11 +1993,17 @@ Net Net::readFromModelOptimizer(const String& xml, const String& bin)
|
|||||||
backendNode->net = Ptr<InfEngineBackendNet>(new InfEngineBackendNet(ieNet));
|
backendNode->net = Ptr<InfEngineBackendNet>(new InfEngineBackendNet(ieNet));
|
||||||
for (auto& it : ieNet.getOutputsInfo())
|
for (auto& it : ieNet.getOutputsInfo())
|
||||||
{
|
{
|
||||||
|
Ptr<Layer> cvLayer(new InfEngineBackendLayer(it.second));
|
||||||
|
InferenceEngine::CNNLayerPtr ieLayer = ieNet.getLayerByName(it.first.c_str());
|
||||||
|
CV_Assert(ieLayer);
|
||||||
|
|
||||||
LayerParams lp;
|
LayerParams lp;
|
||||||
int lid = cvNet.addLayer(it.first, "", lp);
|
int lid = cvNet.addLayer(it.first, "", lp);
|
||||||
|
|
||||||
LayerData& ld = cvNet.impl->layers[lid];
|
LayerData& ld = cvNet.impl->layers[lid];
|
||||||
ld.layerInstance = Ptr<Layer>(new InfEngineBackendLayer(it.second));
|
cvLayer->name = it.first;
|
||||||
|
cvLayer->type = ieLayer->type;
|
||||||
|
ld.layerInstance = cvLayer;
|
||||||
ld.backendNodes[DNN_BACKEND_INFERENCE_ENGINE] = backendNode;
|
ld.backendNodes[DNN_BACKEND_INFERENCE_ENGINE] = backendNode;
|
||||||
|
|
||||||
for (int i = 0; i < inputsNames.size(); ++i)
|
for (int i = 0; i < inputsNames.size(); ++i)
|
||||||
@ -2038,12 +2024,6 @@ int Net::addLayer(const String &name, const String &type, LayerParams ¶ms)
|
|||||||
{
|
{
|
||||||
CV_TRACE_FUNCTION();
|
CV_TRACE_FUNCTION();
|
||||||
|
|
||||||
if (name.find('.') != String::npos)
|
|
||||||
{
|
|
||||||
CV_Error(Error::StsBadArg, "Added layer name \"" + name + "\" must not contain dot symbol");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (impl->getLayerId(name) >= 0)
|
if (impl->getLayerId(name) >= 0)
|
||||||
{
|
{
|
||||||
CV_Error(Error::StsBadArg, "Layer \"" + name + "\" already into net");
|
CV_Error(Error::StsBadArg, "Layer \"" + name + "\" already into net");
|
||||||
@ -2683,7 +2663,7 @@ int Layer::inputNameToIndex(String)
|
|||||||
|
|
||||||
int Layer::outputNameToIndex(const String&)
|
int Layer::outputNameToIndex(const String&)
|
||||||
{
|
{
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Layer::supportBackend(int backendId)
|
bool Layer::supportBackend(int backendId)
|
||||||
|
@ -84,6 +84,7 @@ void initializeLayerFactory()
|
|||||||
CV_DNN_REGISTER_LAYER_CLASS(Reshape, ReshapeLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Reshape, ReshapeLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(Flatten, FlattenLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Flatten, FlattenLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(Resize, ResizeLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Resize, ResizeLayer);
|
||||||
|
CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(CropAndResize, CropAndResizeLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(CropAndResize, CropAndResizeLayer);
|
||||||
|
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(Convolution, ConvolutionLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Convolution, ConvolutionLayer);
|
||||||
@ -115,6 +116,7 @@ void initializeLayerFactory()
|
|||||||
CV_DNN_REGISTER_LAYER_CLASS(Crop, CropLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Crop, CropLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(Eltwise, EltwiseLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Eltwise, EltwiseLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(Permute, PermuteLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Permute, PermuteLayer);
|
||||||
|
CV_DNN_REGISTER_LAYER_CLASS(ShuffleChannel, ShuffleChannelLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(PriorBox, PriorBoxLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(PriorBox, PriorBoxLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(PriorBoxClustered, PriorBoxLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(PriorBoxClustered, PriorBoxLayer);
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(Reorg, ReorgLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Reorg, ReorgLayer);
|
||||||
|
@ -96,6 +96,46 @@ public:
|
|||||||
shift = bias_;
|
shift = bias_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool tryFuse(Ptr<Layer>& top) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
Mat w, b;
|
||||||
|
top->getScaleShift(w, b);
|
||||||
|
if (w.empty() && b.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int numChannels = weights_.total();
|
||||||
|
const int numFusedWeights = w.total();
|
||||||
|
const int numFusedBias = b.total();
|
||||||
|
|
||||||
|
if ((numFusedWeights != numChannels && numFusedWeights != 1 && !w.empty()) ||
|
||||||
|
(numFusedBias != numChannels && numFusedBias != 1 && !b.empty()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!w.empty())
|
||||||
|
{
|
||||||
|
w = w.reshape(1, 1);
|
||||||
|
if (numFusedWeights == 1)
|
||||||
|
{
|
||||||
|
multiply(weights_, w.at<float>(0), weights_);
|
||||||
|
multiply(bias_, w.at<float>(0), bias_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
multiply(weights_, w, weights_);
|
||||||
|
multiply(bias_, w, bias_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!b.empty())
|
||||||
|
{
|
||||||
|
b = b.reshape(1, 1);
|
||||||
|
if (numFusedBias == 1)
|
||||||
|
add(bias_, b.at<float>(0), bias_);
|
||||||
|
else
|
||||||
|
add(bias_, b.reshape(1, 1), bias_);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||||
const int requiredOutputs,
|
const int requiredOutputs,
|
||||||
std::vector<MatShape> &outputs,
|
std::vector<MatShape> &outputs,
|
||||||
|
@ -81,9 +81,10 @@ public:
|
|||||||
|
|
||||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||||
{
|
{
|
||||||
return backendId == DNN_BACKEND_OPENCV ||
|
if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
|
||||||
backendId == DNN_BACKEND_HALIDE && haveHalide() ||
|
return preferableTarget != DNN_TARGET_MYRIAD || type != "Deconvolution" || adjustPad == Size();
|
||||||
backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine();
|
else
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void finalize(const std::vector<Mat*> &inputs, std::vector<Mat> &outputs) CV_OVERRIDE
|
void finalize(const std::vector<Mat*> &inputs, std::vector<Mat> &outputs) CV_OVERRIDE
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
// Copyright (C) 2018, Intel Corporation, all rights reserved.
|
||||||
|
// Third party copyrights are property of their respective owners.
|
||||||
#include "../precomp.hpp"
|
#include "../precomp.hpp"
|
||||||
#include "layers_common.hpp"
|
#include "layers_common.hpp"
|
||||||
|
|
||||||
|
@ -115,9 +115,7 @@ public:
|
|||||||
|
|
||||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||||
{
|
{
|
||||||
return backendId == DNN_BACKEND_OPENCV ||
|
return func.supportBackend(backendId, this->preferableTarget);
|
||||||
backendId == DNN_BACKEND_HALIDE && haveHalide() ||
|
|
||||||
backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Ptr<BackendNode> tryAttach(const Ptr<BackendNode>& node) CV_OVERRIDE
|
virtual Ptr<BackendNode> tryAttach(const Ptr<BackendNode>& node) CV_OVERRIDE
|
||||||
@ -238,6 +236,12 @@ struct ReLUFunctor
|
|||||||
|
|
||||||
explicit ReLUFunctor(float slope_=1.f) : slope(slope_) {}
|
explicit ReLUFunctor(float slope_=1.f) : slope(slope_) {}
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
|
||||||
|
backendId == DNN_BACKEND_INFERENCE_ENGINE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
float s = slope;
|
float s = slope;
|
||||||
@ -353,6 +357,12 @@ struct ReLU6Functor
|
|||||||
CV_Assert(minValue <= maxValue);
|
CV_Assert(minValue <= maxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
|
||||||
|
backendId == DNN_BACKEND_INFERENCE_ENGINE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||||
@ -445,6 +455,12 @@ struct TanHFunctor
|
|||||||
{
|
{
|
||||||
typedef TanHLayer Layer;
|
typedef TanHLayer Layer;
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
|
||||||
|
backendId == DNN_BACKEND_INFERENCE_ENGINE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||||
@ -509,6 +525,12 @@ struct SigmoidFunctor
|
|||||||
{
|
{
|
||||||
typedef SigmoidLayer Layer;
|
typedef SigmoidLayer Layer;
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE ||
|
||||||
|
backendId == DNN_BACKEND_INFERENCE_ENGINE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||||
@ -575,6 +597,11 @@ struct ELUFunctor
|
|||||||
|
|
||||||
explicit ELUFunctor() {}
|
explicit ELUFunctor() {}
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||||
@ -638,6 +665,11 @@ struct AbsValFunctor
|
|||||||
{
|
{
|
||||||
typedef AbsLayer Layer;
|
typedef AbsLayer Layer;
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||||
@ -701,6 +733,11 @@ struct BNLLFunctor
|
|||||||
{
|
{
|
||||||
typedef BNLLLayer Layer;
|
typedef BNLLLayer Layer;
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||||
@ -751,6 +788,14 @@ struct PowerFunctor
|
|||||||
explicit PowerFunctor(float power_ = 1.f, float scale_ = 1.f, float shift_ = 0.f)
|
explicit PowerFunctor(float power_ = 1.f, float scale_ = 1.f, float shift_ = 0.f)
|
||||||
: power(power_), scale(scale_), shift(shift_) {}
|
: power(power_), scale(scale_), shift(shift_) {}
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int targetId)
|
||||||
|
{
|
||||||
|
if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
|
||||||
|
return (targetId != DNN_TARGET_OPENCL && targetId != DNN_TARGET_OPENCL_FP16) || power == 1.0;
|
||||||
|
else
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
float a = scale, b = shift, p = power;
|
float a = scale, b = shift, p = power;
|
||||||
@ -853,6 +898,11 @@ struct ChannelsPReLUFunctor
|
|||||||
scale_umat = scale.getUMat(ACCESS_READ);
|
scale_umat = scale.getUMat(ACCESS_READ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool supportBackend(int backendId, int)
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
|
||||||
|
}
|
||||||
|
|
||||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||||
{
|
{
|
||||||
CV_Assert(scale.isContinuous() && scale.type() == CV_32F);
|
CV_Assert(scale.isContinuous() && scale.type() == CV_32F);
|
||||||
|
@ -310,7 +310,6 @@ public:
|
|||||||
innerProductOp = Ptr<OCL4DNNInnerProduct<float> >(new OCL4DNNInnerProduct<float>(config));
|
innerProductOp = Ptr<OCL4DNNInnerProduct<float> >(new OCL4DNNInnerProduct<float>(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
UMat biasOnesMat = UMat::ones(outerSize, 1, umat_blobs[0].type());
|
|
||||||
for (size_t i = 0; i < inputs.size(); i++)
|
for (size_t i = 0; i < inputs.size(); i++)
|
||||||
{
|
{
|
||||||
MatShape inshape, outshape;
|
MatShape inshape, outshape;
|
||||||
@ -320,7 +319,6 @@ public:
|
|||||||
UMat srcMat, dstMat;
|
UMat srcMat, dstMat;
|
||||||
srcMat = inputs[i].reshape(1, inshape.size(), &inshape[0]);
|
srcMat = inputs[i].reshape(1, inshape.size(), &inshape[0]);
|
||||||
dstMat = outputs[i].reshape(1, outshape.size(), &outshape[0]);
|
dstMat = outputs[i].reshape(1, outshape.size(), &outshape[0]);
|
||||||
dstMat.setTo(0.0f);
|
|
||||||
|
|
||||||
if (!innerProductOp->Forward(srcMat, (use_half) ? half_blobs[0] : umat_blobs[0],
|
if (!innerProductOp->Forward(srcMat, (use_half) ? half_blobs[0] : umat_blobs[0],
|
||||||
(bias) ? (use_half ? half_blobs[1] : umat_blobs[1]) : UMat(),
|
(bias) ? (use_half ? half_blobs[1] : umat_blobs[1]) : UMat(),
|
||||||
@ -332,6 +330,7 @@ public:
|
|||||||
|
|
||||||
if (!use_half && bias && (outerSize > 1))
|
if (!use_half && bias && (outerSize > 1))
|
||||||
{
|
{
|
||||||
|
UMat biasOnesMat = UMat::ones(outerSize, 1, umat_blobs[0].type());
|
||||||
UMat& biases = umat_blobs[1];
|
UMat& biases = umat_blobs[1];
|
||||||
cv::gemm(biasOnesMat, biases, 1, dstMat, 1, dstMat, 0);
|
cv::gemm(biasOnesMat, biases, 1, dstMat, 1, dstMat, 0);
|
||||||
}
|
}
|
||||||
@ -354,6 +353,7 @@ public:
|
|||||||
|
|
||||||
if (bias)
|
if (bias)
|
||||||
{
|
{
|
||||||
|
UMat biasOnesMat = UMat::ones(outerSize, 1, umat_blobs[0].type());
|
||||||
UMat& biases = umat_blobs[1];
|
UMat& biases = umat_blobs[1];
|
||||||
cv::gemm(biasOnesMat, biases, 1, dstMat, 1, dstMat, 0);
|
cv::gemm(biasOnesMat, biases, 1, dstMat, 1, dstMat, 0);
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,7 @@ public:
|
|||||||
(type == AVE ? LIBDNN_POOLING_METHOD_AVE :
|
(type == AVE ? LIBDNN_POOLING_METHOD_AVE :
|
||||||
LIBDNN_POOLING_METHOD_STO);
|
LIBDNN_POOLING_METHOD_STO);
|
||||||
config.avePoolPaddedArea = avePoolPaddedArea;
|
config.avePoolPaddedArea = avePoolPaddedArea;
|
||||||
|
config.computeMaxIdx = computeMaxIdx;
|
||||||
config.use_half = use_half;
|
config.use_half = use_half;
|
||||||
poolOp = Ptr<OCL4DNNPool<float> >(new OCL4DNNPool<float>(config));
|
poolOp = Ptr<OCL4DNNPool<float> >(new OCL4DNNPool<float>(config));
|
||||||
}
|
}
|
||||||
|
@ -82,17 +82,26 @@ static void computeShapeByReshapeMask(const MatShape &srcShape,
|
|||||||
{
|
{
|
||||||
if (matched)
|
if (matched)
|
||||||
{
|
{
|
||||||
if (i == 0 || total(srcShape, i, srcRange.end) != maskTotal)
|
if (total(srcShape, i, srcRange.end) != maskTotal)
|
||||||
{
|
{
|
||||||
srcRange.start = i + 1;
|
srcRange.start = i + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else if (i == 0)
|
||||||
|
{
|
||||||
|
srcRange.start = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
matched = total(srcShape, i, srcRange.end) == maskTotal;
|
matched = total(srcShape, i, srcRange.end) == maskTotal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while (total(srcShape, srcRange.start, srcRange.end) != maskTotal && srcRange.start > 0)
|
||||||
|
{
|
||||||
|
srcRange.start -= 1;
|
||||||
|
}
|
||||||
CV_Assert(total(srcShape, srcRange.start, srcRange.end) == maskTotal);
|
CV_Assert(total(srcShape, srcRange.start, srcRange.end) == maskTotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace cv { namespace dnn {
|
namespace cv { namespace dnn {
|
||||||
|
|
||||||
class ResizeLayerImpl CV_FINAL : public ResizeLayer
|
class ResizeLayerImpl : public ResizeLayer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ResizeLayerImpl(const LayerParams& params)
|
ResizeLayerImpl(const LayerParams& params)
|
||||||
@ -33,7 +33,7 @@ public:
|
|||||||
interpolation = params.get<String>("interpolation");
|
interpolation = params.get<String>("interpolation");
|
||||||
CV_Assert(interpolation == "nearest" || interpolation == "bilinear");
|
CV_Assert(interpolation == "nearest" || interpolation == "bilinear");
|
||||||
|
|
||||||
alignCorners = params.get<bool>("align_corners", false);
|
bool alignCorners = params.get<bool>("align_corners", false);
|
||||||
if (alignCorners)
|
if (alignCorners)
|
||||||
CV_Error(Error::StsNotImplemented, "Resize with align_corners=true is not implemented");
|
CV_Error(Error::StsNotImplemented, "Resize with align_corners=true is not implemented");
|
||||||
}
|
}
|
||||||
@ -53,8 +53,10 @@ public:
|
|||||||
|
|
||||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||||
{
|
{
|
||||||
return backendId == DNN_BACKEND_OPENCV ||
|
if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
|
||||||
backendId == DNN_BACKEND_INFERENCE_ENGINE && haveInfEngine() && interpolation == "nearest";
|
return interpolation == "nearest" && preferableTarget != DNN_TARGET_MYRIAD;
|
||||||
|
else
|
||||||
|
return backendId == DNN_BACKEND_OPENCV;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void finalize(const std::vector<Mat*>& inputs, std::vector<Mat> &outputs) CV_OVERRIDE
|
virtual void finalize(const std::vector<Mat*>& inputs, std::vector<Mat> &outputs) CV_OVERRIDE
|
||||||
@ -64,6 +66,8 @@ public:
|
|||||||
outHeight = outputs[0].size[2];
|
outHeight = outputs[0].size[2];
|
||||||
outWidth = outputs[0].size[3];
|
outWidth = outputs[0].size[3];
|
||||||
}
|
}
|
||||||
|
scaleHeight = static_cast<float>(inputs[0]->size[2]) / outHeight;
|
||||||
|
scaleWidth = static_cast<float>(inputs[0]->size[3]) / outWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
|
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
|
||||||
@ -101,8 +105,6 @@ public:
|
|||||||
const int inpWidth = inp.size[3];
|
const int inpWidth = inp.size[3];
|
||||||
const int inpSpatialSize = inpHeight * inpWidth;
|
const int inpSpatialSize = inpHeight * inpWidth;
|
||||||
const int outSpatialSize = outHeight * outWidth;
|
const int outSpatialSize = outHeight * outWidth;
|
||||||
const float heightScale = static_cast<float>(inpHeight) / (outHeight);
|
|
||||||
const float widthScale = static_cast<float>(inpWidth) / (outWidth);
|
|
||||||
const int numPlanes = inp.size[0] * inp.size[1];
|
const int numPlanes = inp.size[0] * inp.size[1];
|
||||||
CV_Assert(inp.isContinuous(), out.isContinuous());
|
CV_Assert(inp.isContinuous(), out.isContinuous());
|
||||||
|
|
||||||
@ -110,13 +112,13 @@ public:
|
|||||||
Mat outPlanes = out.reshape(1, numPlanes * outHeight);
|
Mat outPlanes = out.reshape(1, numPlanes * outHeight);
|
||||||
for (int y = 0; y < outHeight; ++y)
|
for (int y = 0; y < outHeight; ++y)
|
||||||
{
|
{
|
||||||
float input_y = y * heightScale;
|
float input_y = y * scaleHeight;
|
||||||
int y0 = static_cast<int>(input_y);
|
int y0 = static_cast<int>(input_y);
|
||||||
const float* inpData_row0 = inpPlanes.ptr<float>(y0);
|
const float* inpData_row0 = inpPlanes.ptr<float>(y0);
|
||||||
const float* inpData_row1 = inpPlanes.ptr<float>(std::min(y0 + 1, inpHeight - 1));
|
const float* inpData_row1 = inpPlanes.ptr<float>(std::min(y0 + 1, inpHeight - 1));
|
||||||
for (int x = 0; x < outWidth; ++x)
|
for (int x = 0; x < outWidth; ++x)
|
||||||
{
|
{
|
||||||
float input_x = x * widthScale;
|
float input_x = x * scaleWidth;
|
||||||
int x0 = static_cast<int>(input_x);
|
int x0 = static_cast<int>(input_x);
|
||||||
int x1 = std::min(x0 + 1, inpWidth - 1);
|
int x1 = std::min(x0 + 1, inpWidth - 1);
|
||||||
|
|
||||||
@ -160,10 +162,10 @@ public:
|
|||||||
return Ptr<BackendNode>();
|
return Ptr<BackendNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
int outWidth, outHeight, zoomFactorWidth, zoomFactorHeight;
|
int outWidth, outHeight, zoomFactorWidth, zoomFactorHeight;
|
||||||
String interpolation;
|
String interpolation;
|
||||||
bool alignCorners;
|
float scaleWidth, scaleHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -172,5 +174,44 @@ Ptr<ResizeLayer> ResizeLayer::create(const LayerParams& params)
|
|||||||
return Ptr<ResizeLayer>(new ResizeLayerImpl(params));
|
return Ptr<ResizeLayer>(new ResizeLayerImpl(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InterpLayerImpl CV_FINAL : public ResizeLayerImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InterpLayerImpl(const LayerParams& params) : ResizeLayerImpl(params) {}
|
||||||
|
|
||||||
|
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||||
|
const int requiredOutputs,
|
||||||
|
std::vector<MatShape> &outputs,
|
||||||
|
std::vector<MatShape> &internals) const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
CV_Assert(inputs.size() == 1, inputs[0].size() == 4);
|
||||||
|
outputs.resize(1, inputs[0]);
|
||||||
|
outputs[0][2] = outHeight > 0 ? outHeight : (1 + zoomFactorHeight * (outputs[0][2] - 1));
|
||||||
|
outputs[0][3] = outWidth > 0 ? outWidth : (1 + zoomFactorWidth * (outputs[0][3] - 1));
|
||||||
|
// We can work in-place (do nothing) if input shape == output shape.
|
||||||
|
return (outputs[0][2] == inputs[0][2]) && (outputs[0][3] == inputs[0][3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void finalize(const std::vector<Mat*>& inputs, std::vector<Mat> &outputs) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
if (!outWidth && !outHeight)
|
||||||
|
{
|
||||||
|
outHeight = outputs[0].size[2];
|
||||||
|
outWidth = outputs[0].size[3];
|
||||||
|
}
|
||||||
|
int inpHeight = inputs[0]->size[2];
|
||||||
|
int inpWidth = inputs[0]->size[3];
|
||||||
|
scaleHeight = (outHeight > 1) ? (static_cast<float>(inpHeight - 1) / (outHeight - 1)) : 0.f;
|
||||||
|
scaleWidth = (outWidth > 1) ? (static_cast<float>(inpWidth - 1) / (outWidth - 1)) : 0.f;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ptr<Layer> InterpLayer::create(const LayerParams& params)
|
||||||
|
{
|
||||||
|
LayerParams lp(params);
|
||||||
|
lp.set("interpolation", "bilinear");
|
||||||
|
return Ptr<Layer>(new InterpLayerImpl(lp));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dnn
|
} // namespace dnn
|
||||||
} // namespace cv
|
} // namespace cv
|
||||||
|
104
modules/dnn/src/layers/shuffle_channel_layer.cpp
Normal file
104
modules/dnn/src/layers/shuffle_channel_layer.cpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
// Copyright (C) 2018, Intel Corporation, all rights reserved.
|
||||||
|
// Third party copyrights are property of their respective owners.
|
||||||
|
#include "../precomp.hpp"
|
||||||
|
|
||||||
|
namespace cv { namespace dnn {
|
||||||
|
|
||||||
|
class ShuffleChannelLayerImpl CV_FINAL : public ShuffleChannelLayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShuffleChannelLayerImpl(const LayerParams& params)
|
||||||
|
{
|
||||||
|
group = params.get<int>("group", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||||
|
const int requiredOutputs,
|
||||||
|
std::vector<MatShape> &outputs,
|
||||||
|
std::vector<MatShape> &internals) const CV_OVERRIDE
|
||||||
|
{
|
||||||
|
CV_Assert(inputs.size() == 1 && inputs[0].size() == 4);
|
||||||
|
CV_Assert(inputs[0][1] % group == 0);
|
||||||
|
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||||
|
return group == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void finalize(const std::vector<Mat*>& inputs, std::vector<Mat> &outputs) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
if (group != 1)
|
||||||
|
{
|
||||||
|
LayerParams lp;
|
||||||
|
float order[] = {0, 2, 1, 3};
|
||||||
|
lp.set("order", DictValue::arrayInt(&order[0], 4));
|
||||||
|
permute = PermuteLayer::create(lp);
|
||||||
|
|
||||||
|
Mat inp = *inputs[0];
|
||||||
|
Mat out = outputs[0];
|
||||||
|
|
||||||
|
permuteInpShape.resize(4);
|
||||||
|
permuteInpShape[0] = inp.size[0];
|
||||||
|
permuteInpShape[1] = group;
|
||||||
|
permuteInpShape[2] = inp.size[1] / group;
|
||||||
|
permuteInpShape[3] = inp.size[2]*inp.size[3];
|
||||||
|
|
||||||
|
permuteOutShape.resize(4);
|
||||||
|
permuteOutShape[0] = permuteInpShape[0];
|
||||||
|
permuteOutShape[1] = permuteInpShape[2];
|
||||||
|
permuteOutShape[2] = permuteInpShape[1];
|
||||||
|
permuteOutShape[3] = permuteInpShape[3];
|
||||||
|
|
||||||
|
inp = inp.reshape(1, permuteInpShape);
|
||||||
|
out = out.reshape(1, permuteOutShape);
|
||||||
|
|
||||||
|
std::vector<Mat*> permuteInputs(1, &inp);
|
||||||
|
std::vector<Mat> permuteOutputs(1, out);
|
||||||
|
permute->finalize(permuteInputs, permuteOutputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
CV_TRACE_FUNCTION();
|
||||||
|
CV_TRACE_ARG_VALUE(name, "name", name.c_str());
|
||||||
|
|
||||||
|
Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
CV_TRACE_FUNCTION();
|
||||||
|
CV_TRACE_ARG_VALUE(name, "name", name.c_str());
|
||||||
|
|
||||||
|
Mat inp = *inputs[0];
|
||||||
|
Mat out = outputs[0];
|
||||||
|
if (inp.data != out.data)
|
||||||
|
{
|
||||||
|
if (!permute.empty())
|
||||||
|
{
|
||||||
|
inp = inp.reshape(1, permuteInpShape);
|
||||||
|
out = out.reshape(1, permuteOutShape);
|
||||||
|
std::vector<Mat*> permuteInputs(1, &inp);
|
||||||
|
std::vector<Mat> permuteOutputs(1, out);
|
||||||
|
permute->forward(permuteInputs, permuteOutputs, internals);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
inp.copyTo(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ptr<PermuteLayer> permute;
|
||||||
|
std::vector<int> permuteInpShape, permuteOutShape;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ptr<Layer> ShuffleChannelLayer::create(const LayerParams& params)
|
||||||
|
{
|
||||||
|
return Ptr<Layer>(new ShuffleChannelLayerImpl(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dnn
|
||||||
|
} // namespace cv
|
@ -41,6 +41,7 @@
|
|||||||
//M*/
|
//M*/
|
||||||
|
|
||||||
#include "../precomp.hpp"
|
#include "../precomp.hpp"
|
||||||
|
#include "../op_inf_engine.hpp"
|
||||||
#include "layers_common.hpp"
|
#include "layers_common.hpp"
|
||||||
#include <opencv2/dnn/shape_utils.hpp>
|
#include <opencv2/dnn/shape_utils.hpp>
|
||||||
|
|
||||||
@ -107,6 +108,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return backendId == DNN_BACKEND_OPENCV ||
|
||||||
|
backendId == DNN_BACKEND_INFERENCE_ENGINE && sliceRanges.size() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||||
const int requiredOutputs,
|
const int requiredOutputs,
|
||||||
std::vector<MatShape> &outputs,
|
std::vector<MatShape> &outputs,
|
||||||
@ -247,6 +254,29 @@ public:
|
|||||||
inpMat(sliceRanges[i]).copyTo(outputs[i]);
|
inpMat(sliceRanges[i]).copyTo(outputs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual Ptr<BackendNode> initInfEngine(const std::vector<Ptr<BackendWrapper> >& inputs) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
#ifdef HAVE_INF_ENGINE
|
||||||
|
InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]);
|
||||||
|
InferenceEngine::LayerParams lp;
|
||||||
|
lp.name = name;
|
||||||
|
lp.type = "Crop";
|
||||||
|
lp.precision = InferenceEngine::Precision::FP32;
|
||||||
|
std::shared_ptr<InferenceEngine::CropLayer> ieLayer(new InferenceEngine::CropLayer(lp));
|
||||||
|
|
||||||
|
CV_Assert(sliceRanges.size() == 1);
|
||||||
|
for (int i = sliceRanges[0].size() - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
ieLayer->axis.push_back(i);
|
||||||
|
ieLayer->offset.push_back(sliceRanges[0][i].start);
|
||||||
|
ieLayer->dim.push_back(sliceRanges[0][i].end - sliceRanges[0][i].start);
|
||||||
|
}
|
||||||
|
return Ptr<BackendNode>(new InfEngineBackendNode(ieLayer));
|
||||||
|
|
||||||
|
#endif // HAVE_INF_ENGINE
|
||||||
|
return Ptr<BackendNode>();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ptr<SliceLayer> SliceLayer::create(const LayerParams& params)
|
Ptr<SliceLayer> SliceLayer::create(const LayerParams& params)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -306,6 +306,7 @@ class OCL4DNNConvSpatial
|
|||||||
std::string kernel_name_;
|
std::string kernel_name_;
|
||||||
std::string cache_path_;
|
std::string cache_path_;
|
||||||
bool use_cache_path_; // true if cache_path_ directory exists
|
bool use_cache_path_; // true if cache_path_ directory exists
|
||||||
|
bool run_auto_tuning_;
|
||||||
bool force_auto_tuning_;
|
bool force_auto_tuning_;
|
||||||
int32_t kernel_index_;
|
int32_t kernel_index_;
|
||||||
std::vector< cv::Ptr<kernelConfig> > kernelQueue;
|
std::vector< cv::Ptr<kernelConfig> > kernelQueue;
|
||||||
@ -351,6 +352,7 @@ struct OCL4DNNPoolConfig
|
|||||||
pool_method(LIBDNN_POOLING_METHOD_MAX),
|
pool_method(LIBDNN_POOLING_METHOD_MAX),
|
||||||
global_pooling(false),
|
global_pooling(false),
|
||||||
avePoolPaddedArea(true),
|
avePoolPaddedArea(true),
|
||||||
|
computeMaxIdx(true),
|
||||||
use_half(false)
|
use_half(false)
|
||||||
{}
|
{}
|
||||||
MatShape in_shape;
|
MatShape in_shape;
|
||||||
@ -364,6 +366,7 @@ struct OCL4DNNPoolConfig
|
|||||||
ocl4dnnPoolingMethod_t pool_method; // = LIBDNN_POOLING_METHOD_MAX;
|
ocl4dnnPoolingMethod_t pool_method; // = LIBDNN_POOLING_METHOD_MAX;
|
||||||
bool global_pooling; // = false;
|
bool global_pooling; // = false;
|
||||||
bool avePoolPaddedArea;
|
bool avePoolPaddedArea;
|
||||||
|
bool computeMaxIdx;
|
||||||
bool use_half;
|
bool use_half;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -398,6 +401,7 @@ class OCL4DNNPool
|
|||||||
int32_t pooled_height_;
|
int32_t pooled_height_;
|
||||||
int32_t pooled_width_;
|
int32_t pooled_width_;
|
||||||
bool avePoolPaddedArea;
|
bool avePoolPaddedArea;
|
||||||
|
bool computeMaxIdx;
|
||||||
bool use_half;
|
bool use_half;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
#include "../include/math_functions.hpp"
|
#include "../include/math_functions.hpp"
|
||||||
#include "../include/default_kernel_config.hpp"
|
#include "../include/default_kernel_config.hpp"
|
||||||
#include "opencv2/dnn/shape_utils.hpp"
|
#include "opencv2/dnn/shape_utils.hpp"
|
||||||
|
#include "opencv2/core/utils/logger.hpp"
|
||||||
|
|
||||||
#if defined WIN32 || defined _WIN32
|
#if defined WIN32 || defined _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -67,6 +68,69 @@ typedef std::map<std::string, std::string> kernel_hash_t;
|
|||||||
static kernel_hash_t kernelConfigMap;
|
static kernel_hash_t kernelConfigMap;
|
||||||
static bool defaultConfigLoaded = false;
|
static bool defaultConfigLoaded = false;
|
||||||
|
|
||||||
|
static std::string sanitize(const std::string& s)
|
||||||
|
{
|
||||||
|
std::string s_ = s;
|
||||||
|
for (size_t i = 0; i < s_.size(); i++)
|
||||||
|
{
|
||||||
|
char c = s_[i];
|
||||||
|
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'))
|
||||||
|
{
|
||||||
|
s_[i] = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO add hash?
|
||||||
|
// s_ = s_ + cv::format("_%08llx", crc64((uchar*)s.c_str(), s.size()));
|
||||||
|
return s_;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initializeGlobalBuiltinConfigurations(const std::string& cache_path)
|
||||||
|
{
|
||||||
|
CV_Assert(defaultConfigLoaded == false);
|
||||||
|
CV_Assert(kernelConfigMap.empty());
|
||||||
|
|
||||||
|
/* fp32 config */
|
||||||
|
size_t numConfigs = sizeof(default_kernel_config_intel_fp32) /
|
||||||
|
sizeof(default_kernel_config_intel_fp32[0]) / 2;
|
||||||
|
for (size_t i = 0; i < numConfigs; i++)
|
||||||
|
{
|
||||||
|
std::string key = std::string("Intel(R) Corporation_") + default_kernel_config_intel_fp32[2 * i];
|
||||||
|
if (!cache_path.empty())
|
||||||
|
{
|
||||||
|
std::string cacheFile = cache_path + sanitize(key);
|
||||||
|
std::ifstream cachedKernel(cacheFile.c_str());
|
||||||
|
if (cachedKernel)
|
||||||
|
continue; // external configuration found, skip builtin
|
||||||
|
}
|
||||||
|
std::pair<std::string, std::string> entry(
|
||||||
|
key,
|
||||||
|
default_kernel_config_intel_fp32[2 * i + 1]);
|
||||||
|
kernelConfigMap.insert(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fp16 config */
|
||||||
|
numConfigs = sizeof(default_kernel_config_intel_fp16) /
|
||||||
|
sizeof(default_kernel_config_intel_fp16[0]) / 2;
|
||||||
|
for (size_t i = 0; i < numConfigs; i++)
|
||||||
|
{
|
||||||
|
std::string key = std::string("Intel(R) Corporation_") + default_kernel_config_intel_fp16[2 * i];
|
||||||
|
if (!cache_path.empty())
|
||||||
|
{
|
||||||
|
std::string cacheFile = cache_path + sanitize(key);
|
||||||
|
std::ifstream cachedKernel(cacheFile.c_str());
|
||||||
|
if (cachedKernel)
|
||||||
|
continue; // external configuration found, skip builtin
|
||||||
|
}
|
||||||
|
std::pair<std::string, std::string> entry(
|
||||||
|
key,
|
||||||
|
default_kernel_config_intel_fp16[2 * i + 1]);
|
||||||
|
kernelConfigMap.insert(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfigLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename Dtype>
|
template<typename Dtype>
|
||||||
OCL4DNNConvSpatial<Dtype>::OCL4DNNConvSpatial(OCL4DNNConvConfig config)
|
OCL4DNNConvSpatial<Dtype>::OCL4DNNConvSpatial(OCL4DNNConvConfig config)
|
||||||
{
|
{
|
||||||
@ -139,9 +203,8 @@ OCL4DNNConvSpatial<Dtype>::OCL4DNNConvSpatial(OCL4DNNConvConfig config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
force_auto_tuning_ =
|
run_auto_tuning_ = use_cache_path_ && !utils::getConfigurationParameterBool("OPENCV_OCL4DNN_DISABLE_AUTO_TUNING", false);
|
||||||
(use_cache_path_ && !utils::getConfigurationParameterBool("OPENCV_OCL4DNN_DISABLE_AUTO_TUNING", false))
|
force_auto_tuning_ = utils::getConfigurationParameterBool("OPENCV_OCL4DNN_FORCE_AUTO_TUNING", false);
|
||||||
|| utils::getConfigurationParameterBool("OPENCV_OCL4DNN_FORCE_AUTO_TUNING", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Dtype>
|
template<typename Dtype>
|
||||||
@ -272,40 +335,38 @@ void OCL4DNNConvSpatial<Dtype>::setupKernelDetails(int32_t kernelType,
|
|||||||
|
|
||||||
// options
|
// options
|
||||||
options_ << " -cl-fast-relaxed-math -D KERNEL_IDLF -D convolve_simd=" << kernel_name_;
|
options_ << " -cl-fast-relaxed-math -D KERNEL_IDLF -D convolve_simd=" << kernel_name_;
|
||||||
|
options_ << " -cl-mad-enable";
|
||||||
if (clOptionSupport("-cl-no-subgroup-ifp"))
|
if (clOptionSupport("-cl-no-subgroup-ifp"))
|
||||||
options_ << " -cl-no-subgroup-ifp ";
|
options_ << " -cl-no-subgroup-ifp ";
|
||||||
|
|
||||||
// defs
|
// defs
|
||||||
int32_t output_width = output_w_;
|
|
||||||
int32_t output_height = output_h_;
|
|
||||||
int32_t output_block_width = blockM;
|
int32_t output_block_width = blockM;
|
||||||
int32_t output_block_height = blockK;
|
int32_t output_block_height = blockK;
|
||||||
const int32_t last_block_width = (output_width % output_block_width == 0) ?
|
int tile_x = (output_block_width - 1) * stride_w_ + kernel_w_ * dilation_w_;
|
||||||
output_block_width : output_width % output_block_width;
|
int tile_y = (output_block_height - 1) * stride_h_ + kernel_h_ * dilation_h_;
|
||||||
const int32_t last_block_height = (output_height % output_block_height == 0) ?
|
int invec_size = tile_y;
|
||||||
output_block_height : output_height % output_block_height;
|
|
||||||
int tile_x = alignSize((output_block_width - 1) * stride_w_ + kernel_w_ * dilation_w_, 4);
|
|
||||||
int tile_y = (output_block_height -1) * stride_h_ + kernel_h_ * dilation_h_;
|
|
||||||
int tile_y_stride = (4 * simd_size) / tile_x;
|
|
||||||
int invec_size = divUp(tile_y, tile_y_stride);
|
|
||||||
|
|
||||||
addDef("SIMD_SIZE", simd_size);
|
addDef("SIMD_SIZE", simd_size);
|
||||||
addDef("filter_qualifier", "__global");
|
|
||||||
addDef("OUT_BLOCK_WIDTH", output_block_width);
|
addDef("OUT_BLOCK_WIDTH", output_block_width);
|
||||||
addDef("OUT_BLOCK_HEIGHT", output_block_height);
|
addDef("OUT_BLOCK_HEIGHT", output_block_height);
|
||||||
addDef("LAST_BLOCK_WIDTH", last_block_width);
|
|
||||||
addDef("LAST_BLOCK_HEIGHT", last_block_height);
|
|
||||||
addDef("INPUT_DEPTH", channels_ / group_);
|
addDef("INPUT_DEPTH", channels_ / group_);
|
||||||
addDef("TOTAL_INPUT_DEPTH_SIZE", channels_);
|
addDef("TOTAL_INPUT_DEPTH_SIZE", channels_);
|
||||||
addDef("TOTAL_OUTPUT_DEPTH", num_output_);
|
addDef("TOTAL_OUTPUT_DEPTH", num_output_);
|
||||||
addDef("NUM_FILTERS", M_);
|
addDef("NUM_FILTERS", M_);
|
||||||
addDef("TILE_X", tile_x);
|
addDef("TILE_X", tile_x);
|
||||||
addDef("TILE_Y", tile_y);
|
addDef("TILE_Y", tile_y);
|
||||||
addDef("TILE_Y_STRIDE", tile_y_stride);
|
|
||||||
addDef("INVEC_SIZE", invec_size);
|
addDef("INVEC_SIZE", invec_size);
|
||||||
addDef("ALIGNED_NUM_FILTERS", (int)alignSize(M_, simd_size));
|
addDef("ALIGNED_NUM_FILTERS", (int)alignSize(M_, simd_size));
|
||||||
addDef("OUT_BLOCK_SIZE", (output_block_width*output_block_height));
|
addDef("OUT_BLOCK_SIZE", (output_block_width*output_block_height));
|
||||||
addDef("APPLY_BIAS", bias_term_);
|
addDef("APPLY_BIAS", bias_term_);
|
||||||
|
addDef("WEIGHT_PREF", ((kernel_w_ * kernel_h_) == 1) ? 1 : 8);
|
||||||
|
addDef("INPUT_PITCH", (width_ * height_));
|
||||||
|
addDef("OUTPUT_PITCH", (output_w_ * output_h_));
|
||||||
|
addDef("LEFT_FILTERS", ((int)alignSize(M_, simd_size) - M_));
|
||||||
|
addDef("INPUT_WIDTH", width_);
|
||||||
|
addDef("INPUT_HEIGHT", height_);
|
||||||
|
addDef("FILTERS_IN_GROUP", ((int)alignSize(M_, simd_size) / simd_size));
|
||||||
|
|
||||||
setFusionDefine(fused_activ_, fused_eltwise_);
|
setFusionDefine(fused_activ_, fused_eltwise_);
|
||||||
|
|
||||||
src_ = cv::ocl::dnn::conv_layer_spatial_oclsrc;
|
src_ = cv::ocl::dnn::conv_layer_spatial_oclsrc;
|
||||||
@ -528,13 +589,6 @@ void OCL4DNNConvSpatial<Dtype>::calculateBenchmark(const UMat &bottom, UMat &ver
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define dbg
|
|
||||||
#ifdef dbg
|
|
||||||
#define dbgPrint(x) (x)
|
|
||||||
#else
|
|
||||||
#define dbgPrint(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// For large enough input size, we do not need to tune kernels for different
|
// For large enough input size, we do not need to tune kernels for different
|
||||||
// size. The reason is with large input size, there will be enough work items
|
// size. The reason is with large input size, there will be enough work items
|
||||||
// to feed al the EUs.
|
// to feed al the EUs.
|
||||||
@ -545,6 +599,7 @@ void OCL4DNNConvSpatial<Dtype>::calculateBenchmark(const UMat &bottom, UMat &ver
|
|||||||
template<typename Dtype>
|
template<typename Dtype>
|
||||||
void OCL4DNNConvSpatial<Dtype>::generateKey()
|
void OCL4DNNConvSpatial<Dtype>::generateKey()
|
||||||
{
|
{
|
||||||
|
std::string precision = (use_half_) ? "FP16" : "FP32";
|
||||||
std::stringstream keyBuilder;
|
std::stringstream keyBuilder;
|
||||||
// FIXME: to support fuse?
|
// FIXME: to support fuse?
|
||||||
keyBuilder << "k" << kernel_w_ << "x" << kernel_h_ << "_"
|
keyBuilder << "k" << kernel_w_ << "x" << kernel_h_ << "_"
|
||||||
@ -558,21 +613,12 @@ void OCL4DNNConvSpatial<Dtype>::generateKey()
|
|||||||
<< "num" << num_ << "_"
|
<< "num" << num_ << "_"
|
||||||
<< "M" << M_ << "_"
|
<< "M" << M_ << "_"
|
||||||
<< "activ" << fused_activ_ << "_"
|
<< "activ" << fused_activ_ << "_"
|
||||||
<< "eltwise" << fused_eltwise_;
|
<< "eltwise" << fused_eltwise_ << "_"
|
||||||
|
<< precision;
|
||||||
|
|
||||||
|
|
||||||
key_ = ocl::Device::getDefault().vendorName() + "_EU" + cv::format("%d", ocl::Device::getDefault().maxComputeUnits()) + "_" + keyBuilder.str();
|
key_ = ocl::Device::getDefault().vendorName() + "_EU" + cv::format("%d", ocl::Device::getDefault().maxComputeUnits()) + "_" + keyBuilder.str();
|
||||||
key_sanitized_ = key_;
|
key_sanitized_ = sanitize(key_);
|
||||||
for (size_t i = 0; i < key_sanitized_.size(); i++)
|
|
||||||
{
|
|
||||||
char c = key_sanitized_[i];
|
|
||||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'))
|
|
||||||
{
|
|
||||||
key_sanitized_[i] = '_';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO add hash?
|
|
||||||
// key_sanitized_ = key_sanitized_ + cv::format("_%08llx", crc64((uchar*)key_.c_str(), key_.size()));
|
|
||||||
short_key_ = keyBuilder.str();
|
short_key_ = keyBuilder.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,11 +633,6 @@ std::string OCL4DNNConvSpatial<Dtype>::generateSpecificKey(int32_t type, int32_t
|
|||||||
<< "_" << blockHeight
|
<< "_" << blockHeight
|
||||||
<< "_" << blockDepth;
|
<< "_" << blockDepth;
|
||||||
|
|
||||||
if (!use_half_)
|
|
||||||
keyBuilder << "_float";
|
|
||||||
else
|
|
||||||
keyBuilder << "_half";
|
|
||||||
|
|
||||||
return keyBuilder.str();
|
return keyBuilder.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1135,7 +1176,7 @@ float OCL4DNNConvSpatial<float>::timedConvolve(const UMat &bottom, UMat &top,
|
|||||||
cv::ocl::Timer timer(queue);
|
cv::ocl::Timer timer(queue);
|
||||||
timer.start();
|
timer.start();
|
||||||
bool res = true;;
|
bool res = true;;
|
||||||
dbgPrint(std::cout << "Benchmarking kernel: " << config->kernelName << std::endl);
|
CV_LOG_INFO(NULL, "Benchmarking kernel: " << config->kernelName);
|
||||||
tuned_ = true;
|
tuned_ = true;
|
||||||
int loop_cnt = 4;
|
int loop_cnt = 4;
|
||||||
for (int i = 0; i < loop_cnt; i++) {
|
for (int i = 0; i < loop_cnt; i++) {
|
||||||
@ -1152,7 +1193,6 @@ float OCL4DNNConvSpatial<float>::timedConvolve(const UMat &bottom, UMat &top,
|
|||||||
}
|
}
|
||||||
|
|
||||||
float elapsedTime = timer.durationNS() * 1e-6 / loop_cnt;
|
float elapsedTime = timer.durationNS() * 1e-6 / loop_cnt;
|
||||||
#ifdef dbg
|
|
||||||
double out_w = output_w_;
|
double out_w = output_w_;
|
||||||
double out_h = output_h_;
|
double out_h = output_h_;
|
||||||
double out_z = M_;
|
double out_z = M_;
|
||||||
@ -1160,16 +1200,8 @@ float OCL4DNNConvSpatial<float>::timedConvolve(const UMat &bottom, UMat &top,
|
|||||||
double k_h = kernel_h_;
|
double k_h = kernel_h_;
|
||||||
double k_z = channels_;
|
double k_z = channels_;
|
||||||
double totalFlops = ((k_w*k_h*k_z -1)*2)*(out_w*out_h*out_z)*num_;
|
double totalFlops = ((k_w*k_h*k_z -1)*2)*(out_w*out_h*out_z)*num_;
|
||||||
std::cout << "\tEstimated Gflops:" << (totalFlops * 1e-9)
|
CV_LOG_INFO(NULL, "\tEstimated Gflops:" << (totalFlops * 1e-9));
|
||||||
<< std::endl;
|
CV_LOG_INFO(NULL, "\tEstimated GFLOPS/S: " << ((totalFlops * 1e-9)*(1000.0/elapsedTime)));
|
||||||
std::cout << "\tEstimated GFLOPS/S: " << ((totalFlops * 1e-9)*(1000.0/elapsedTime))
|
|
||||||
<< std::endl;
|
|
||||||
#if 0
|
|
||||||
std::cout << "Estimated utilization: " <<
|
|
||||||
((((totalFlops/1000)/1000)/1000)*(1000.0/elapsedTime))/880.0
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
return elapsedTime;
|
return elapsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1225,18 +1257,18 @@ bool OCL4DNNConvSpatial<float>::verifyResult(const UMat &bottom,
|
|||||||
if (use_half_ && error_factor > 0.1 * fabs(verify_data[offset]) &&
|
if (use_half_ && error_factor > 0.1 * fabs(verify_data[offset]) &&
|
||||||
error_factor > 0.04 && !(fabs(verify_data[offset]) < 1.e-3 && error_factor < 1.e-4))
|
error_factor > 0.04 && !(fabs(verify_data[offset]) < 1.e-3 && error_factor < 1.e-4))
|
||||||
{
|
{
|
||||||
dbgPrint(printf("test verification failed @ image %d group %d"
|
CV_LOG_ERROR(NULL, "test verification failed @ image " << n << " group " << g
|
||||||
"out_ch %d h %d w %d got %G expected %G\n",
|
<< " out_ch " << out_ch << " h " << h << " w " << w
|
||||||
n, g, out_ch, h, w, data[offset], verify_data[offset]));
|
<< " got " << data[offset] << " expected " << verify_data[offset]);
|
||||||
verificationFail = 1;
|
verificationFail = 1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
else if (!use_half_ && error_factor > 0.1 * fabs(verify_data[offset]) &&
|
else if (!use_half_ && error_factor > 0.1 * fabs(verify_data[offset]) &&
|
||||||
!(fabs(verify_data[offset]) < 1.e-3 && error_factor < 1.e-4))
|
!(fabs(verify_data[offset]) < 1.e-3 && error_factor < 1.e-4))
|
||||||
{
|
{
|
||||||
dbgPrint(printf("test verification failed @ image %d group %d"
|
CV_LOG_ERROR(NULL, "test verification failed @ image " << n << " group " << g
|
||||||
"out_ch %d h %d w %d got %G expected %G\n",
|
<< " out_ch " << out_ch << " h " << h << " w " << w
|
||||||
n, g, out_ch, h, w, data[offset], verify_data[offset]));
|
<< " got " << data[offset] << " expected " << verify_data[offset]);
|
||||||
verificationFail = 1;
|
verificationFail = 1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1517,17 +1549,11 @@ void OCL4DNNConvSpatial<float>::generate_idlf_tuneritems(std::vector< cv::Ptr<tu
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int actual_tile_x = kernel_w_ * dilation_w_ + (blockM - 1) * stride_w_ ;
|
int actual_tile_x = kernel_w_ * dilation_w_ + (blockM - 1) * stride_w_ ;
|
||||||
int tile_x = alignSize(actual_tile_x, 4);
|
int tile_x = alignSize(actual_tile_x, simd_size);
|
||||||
int tile_y = kernel_h_ * dilation_h_ + (blockK - 1) * stride_h_;
|
if (tile_x > simd_size)
|
||||||
if (tile_x > (4 * simd_size))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((blockM * blockK + divUp(tile_x * tile_y, simd_size)) > block_size_max)
|
if (blockM * blockK > block_size_max)
|
||||||
return;
|
|
||||||
|
|
||||||
int tile_y_stride = (4 * simd_size) / tile_x;
|
|
||||||
int invec_size = divUp(tile_y, tile_y_stride);
|
|
||||||
if (invec_size > 4)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tunerItems.push_back(makePtr<tunerParam>(KERNEL_TYPE_INTEL_IDLF, blockM, blockK, simd_size));
|
tunerItems.push_back(makePtr<tunerParam>(KERNEL_TYPE_INTEL_IDLF, blockM, blockK, simd_size));
|
||||||
@ -1570,11 +1596,7 @@ void OCL4DNNConvSpatial<float>::generateTunerItems(std::vector< cv::Ptr<tunerPar
|
|||||||
for (uint32_t height = height_max; height > 0; height--)
|
for (uint32_t height = height_max; height > 0; height--)
|
||||||
{
|
{
|
||||||
generate_idlf_tuneritems(tunerItems, width, height, simd_size);
|
generate_idlf_tuneritems(tunerItems, width, height, simd_size);
|
||||||
if (tunerItems.size() >= 8 && height == 2)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (tunerItems.size() >= 12 && width == 2)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1661,35 +1683,31 @@ void OCL4DNNConvSpatial<float>::setupConvolution(const UMat &bottom,
|
|||||||
if (kernelQueue[x]->tested == false) {
|
if (kernelQueue[x]->tested == false) {
|
||||||
bool verified = verifyResult(bottom, top, weight, bias, numImages, kernelQueue[x], verifyTop);
|
bool verified = verifyResult(bottom, top, weight, bias, numImages, kernelQueue[x], verifyTop);
|
||||||
if (verified == false) {
|
if (verified == false) {
|
||||||
dbgPrint(std::cout << "Kernel "
|
CV_LOG_ERROR(NULL, "Kernel " << kernelQueue[x]->kernelName << " failed verification");
|
||||||
<< kernelQueue[x]->kernelName
|
CV_LOG_ERROR(NULL, "kernelQueue[x]->workItem_output[0]: "
|
||||||
<< " failed verification" << std::endl);
|
<< kernelQueue[x]->workItem_output[0] << " "
|
||||||
dbgPrint(std::cout << "kernelQueue[x]->workItem_output[0]: "
|
<< "kernelQueue[x]->workItem_output[1]: "
|
||||||
<< kernelQueue[x]->workItem_output[0] << " "
|
<< kernelQueue[x]->workItem_output[1] << " "
|
||||||
<< "kernelQueue[x]->workItem_output[1]: "
|
<< "kernelQueue[x]->workItem_output[2]: "
|
||||||
<< kernelQueue[x]->workItem_output[1] << " "
|
<< kernelQueue[x]->workItem_output[2] << " "
|
||||||
<< "kernelQueue[x]->workItem_output[2]: "
|
<< "kernelQueue[x]->kernelType: "
|
||||||
<< kernelQueue[x]->workItem_output[2] << " "
|
<< kernelQueue[x]->kernelType << " "
|
||||||
<< "kernelQueue[x]->kernelType: "
|
<< "kernelQueue[x]->global_work_size[0]: "
|
||||||
<< kernelQueue[x]->kernelType << " "
|
<< kernelQueue[x]->global_work_size[0] << " "
|
||||||
<< "kernelQueue[x]->global_work_size[0]: "
|
<< "kernelQueue[x]->global_work_size[1]: "
|
||||||
<< kernelQueue[x]->global_work_size[0] << " "
|
<< kernelQueue[x]->global_work_size[1] << " "
|
||||||
<< "kernelQueue[x]->global_work_size[1]: "
|
<< "kernelQueue[x]->global_work_size[2]: "
|
||||||
<< kernelQueue[x]->global_work_size[1] << " "
|
<< kernelQueue[x]->global_work_size[2] << " "
|
||||||
<< "kernelQueue[x]->global_work_size[2]: "
|
<< "kernelQueue[x]->local_work_size[0]: "
|
||||||
<< kernelQueue[x]->global_work_size[2] << " "
|
<< kernelQueue[x]->local_work_size[0] << " "
|
||||||
<< "kernelQueue[x]->local_work_size[0]: "
|
<< "kernelQueue[x]->local_work_size[1]: "
|
||||||
<< kernelQueue[x]->local_work_size[0] << " "
|
<< kernelQueue[x]->local_work_size[1] << " "
|
||||||
<< "kernelQueue[x]->local_work_size[1]: "
|
<< "kernelQueue[x]->local_work_size[2]: "
|
||||||
<< kernelQueue[x]->local_work_size[1] << " "
|
<< kernelQueue[x]->local_work_size[2] << " "
|
||||||
<< "kernelQueue[x]->local_work_size[2]: "
|
<< kernelQueue[x]->swizzle_weights << " "
|
||||||
<< kernelQueue[x]->local_work_size[2] << " "
|
<< kernelQueue[x]->use_null_local);
|
||||||
<< kernelQueue[x]->swizzle_weights << " "
|
|
||||||
<< kernelQueue[x]->use_null_local << std::endl);
|
|
||||||
} else {
|
} else {
|
||||||
dbgPrint(std::cout << "Kernel "
|
CV_LOG_INFO(NULL, "Kernel " << kernelQueue[x]->kernelName << " pass verification");
|
||||||
<< kernelQueue[x]->kernelName
|
|
||||||
<< " pass verification" << std::endl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1718,19 +1736,28 @@ void OCL4DNNConvSpatial<float>::setupConvolution(const UMat &bottom,
|
|||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
kernelQueue[fastestKernel]->tested = true;
|
kernelQueue[fastestKernel]->tested = true;
|
||||||
dbgPrint(std::cout << "Kernel " <<
|
CV_LOG_ERROR(NULL, "Kernel " << kernelQueue[fastestKernel]->kernelName <<
|
||||||
kernelQueue[fastestKernel]->kernelName <<
|
" failed verification");
|
||||||
" failed verification" << std::endl);
|
|
||||||
failures++;
|
failures++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (verification) {
|
if (verification) {
|
||||||
dbgPrint(std::cout << "Kernel <" << kernelQueue[kernel_index_]->kernelName <<
|
CV_LOG_INFO(NULL, "Kernel <" << kernelQueue[kernel_index_]->kernelName <<
|
||||||
"> passed verification" << std::endl);
|
"> passed verification");
|
||||||
dbgPrint(std::cout << "Convolution Time:" << kernelQueue[kernel_index_]->executionTime << std::endl);
|
CV_LOG_INFO(NULL, "Convolution Time:" << kernelQueue[kernel_index_]->executionTime);
|
||||||
|
double out_w = output_w_;
|
||||||
|
double out_h = output_h_;
|
||||||
|
double out_z = M_;
|
||||||
|
double k_w = kernel_w_;
|
||||||
|
double k_h = kernel_h_;
|
||||||
|
double k_z = channels_;
|
||||||
|
float elapsedTime = kernelQueue[kernel_index_]->executionTime;
|
||||||
|
double totalFlops = ((k_w*k_h*k_z -1)*2)*(out_w*out_h*out_z)*num_;
|
||||||
|
CV_LOG_INFO(NULL, "\tEstimated Gflops:" << (totalFlops * 1e-9));
|
||||||
|
CV_LOG_INFO(NULL, "\tEstimated GFLOPS/S: " << ((totalFlops * 1e-9)*(1000.0/elapsedTime)));
|
||||||
} else {
|
} else {
|
||||||
dbgPrint(std::cout << "fallback to basic kernel" << std::endl);
|
CV_LOG_INFO(NULL, "fallback to basic kernel");
|
||||||
options_.str(""); options_.clear(); // clear contents and state flags
|
options_.str(""); options_.clear(); // clear contents and state flags
|
||||||
createBasicKernel(1, 1, 1);
|
createBasicKernel(1, 1, 1);
|
||||||
kernel_index_ = kernelQueue.size() - 1;
|
kernel_index_ = kernelQueue.size() - 1;
|
||||||
@ -1798,14 +1825,14 @@ void OCL4DNNConvSpatial<Dtype>::prepareKernel(const UMat &bottom, UMat &top,
|
|||||||
if (loadCachedConfig()) // check in-memory cache
|
if (loadCachedConfig()) // check in-memory cache
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (loadTunedConfig()) // check external storage
|
if (loadTunedConfig()) // check external storage
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UMat benchData(1, numImages * top_dim_, (use_half_) ? CV_16SC1 : CV_32FC1);
|
UMat benchData(1, numImages * top_dim_, (use_half_) ? CV_16SC1 : CV_32FC1);
|
||||||
|
|
||||||
calculateBenchmark(bottom, benchData, (use_half_) ? weights_half : weight, bias, numImages);
|
calculateBenchmark(bottom, benchData, (use_half_) ? weights_half : weight, bias, numImages);
|
||||||
|
|
||||||
if (force_auto_tuning_)
|
if (run_auto_tuning_ || force_auto_tuning_)
|
||||||
{
|
{
|
||||||
setupConvolution(bottom, top, weight, bias, numImages, benchData);
|
setupConvolution(bottom, top, weight, bias, numImages, benchData);
|
||||||
}
|
}
|
||||||
@ -1820,18 +1847,8 @@ template<typename Dtype>
|
|||||||
bool OCL4DNNConvSpatial<Dtype>::loadCachedConfig()
|
bool OCL4DNNConvSpatial<Dtype>::loadCachedConfig()
|
||||||
{
|
{
|
||||||
cv::AutoLock lock(kernelConfigMutex);
|
cv::AutoLock lock(kernelConfigMutex);
|
||||||
if (!defaultConfigLoaded)
|
if (!defaultConfigLoaded && !force_auto_tuning_)
|
||||||
{
|
initializeGlobalBuiltinConfigurations((use_cache_path_ && !cache_path_.empty()) ? (cache_path_ + '/') : std::string());
|
||||||
const size_t numConfigs = sizeof(default_kernel_config_intel)/sizeof(default_kernel_config_intel[0])/2;
|
|
||||||
for (size_t i = 0; i < numConfigs; i++)
|
|
||||||
{
|
|
||||||
std::pair<std::string, std::string> entry(
|
|
||||||
std::string("Intel(R) Corporation_") + default_kernel_config_intel[2 * i],
|
|
||||||
default_kernel_config_intel[2 * i + 1]);
|
|
||||||
kernelConfigMap.insert(entry);
|
|
||||||
}
|
|
||||||
defaultConfigLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel_hash_t::iterator it = kernelConfigMap.find(key_);
|
kernel_hash_t::iterator it = kernelConfigMap.find(key_);
|
||||||
if (it != kernelConfigMap.end())
|
if (it != kernelConfigMap.end())
|
||||||
@ -1904,9 +1921,12 @@ bool OCL4DNNConvSpatial<Dtype>::setupKernelByConfig(int x, int y, int z, int typ
|
|||||||
template<typename Dtype>
|
template<typename Dtype>
|
||||||
bool OCL4DNNConvSpatial<Dtype>::loadTunedConfig()
|
bool OCL4DNNConvSpatial<Dtype>::loadTunedConfig()
|
||||||
{
|
{
|
||||||
|
if (force_auto_tuning_)
|
||||||
|
return false; // don't load results from external storage
|
||||||
|
|
||||||
if (!use_cache_path_)
|
if (!use_cache_path_)
|
||||||
{
|
{
|
||||||
if (cache_path_.empty() && !force_auto_tuning_)
|
if (cache_path_.empty())
|
||||||
{
|
{
|
||||||
static int warn_ = 0;
|
static int warn_ = 0;
|
||||||
if (!warn_)
|
if (!warn_)
|
||||||
|
@ -56,6 +56,7 @@ OCL4DNNPool<Dtype>::OCL4DNNPool(OCL4DNNPoolConfig config)
|
|||||||
channels_ = config.channels;
|
channels_ = config.channels;
|
||||||
pool_method_ = config.pool_method;
|
pool_method_ = config.pool_method;
|
||||||
avePoolPaddedArea = config.avePoolPaddedArea;
|
avePoolPaddedArea = config.avePoolPaddedArea;
|
||||||
|
computeMaxIdx = config.computeMaxIdx;
|
||||||
use_half = config.use_half;
|
use_half = config.use_half;
|
||||||
|
|
||||||
for (int i = 0; i < spatial_dims; ++i)
|
for (int i = 0; i < spatial_dims; ++i)
|
||||||
@ -97,7 +98,7 @@ bool OCL4DNNPool<Dtype>::Forward(const UMat& bottom,
|
|||||||
UMat& top_mask)
|
UMat& top_mask)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
size_t global[] = { 128 * 128 };
|
size_t global[] = { (size_t)count_ };
|
||||||
size_t local[] = { 128 };
|
size_t local[] = { 128 };
|
||||||
|
|
||||||
// support 2D case
|
// support 2D case
|
||||||
@ -105,8 +106,7 @@ bool OCL4DNNPool<Dtype>::Forward(const UMat& bottom,
|
|||||||
{
|
{
|
||||||
case LIBDNN_POOLING_METHOD_MAX:
|
case LIBDNN_POOLING_METHOD_MAX:
|
||||||
{
|
{
|
||||||
bool haveMask = !top_mask.empty();
|
String kname = computeMaxIdx ? "max_pool_forward_mask" : "max_pool_forward";
|
||||||
String kname = haveMask ? "max_pool_forward_mask" : "max_pool_forward";
|
|
||||||
kname += (use_half) ? "_half" : "_float";
|
kname += (use_half) ? "_half" : "_float";
|
||||||
ocl::Kernel oclk_max_pool_forward(
|
ocl::Kernel oclk_max_pool_forward(
|
||||||
kname.c_str(),
|
kname.c_str(),
|
||||||
@ -118,7 +118,7 @@ bool OCL4DNNPool<Dtype>::Forward(const UMat& bottom,
|
|||||||
kernel_w_, kernel_h_,
|
kernel_w_, kernel_h_,
|
||||||
stride_w_, stride_h_,
|
stride_w_, stride_h_,
|
||||||
pad_w_, pad_h_,
|
pad_w_, pad_h_,
|
||||||
haveMask ? " -D HAVE_MASK=1" : ""
|
computeMaxIdx ? " -D HAVE_MASK=1" : ""
|
||||||
));
|
));
|
||||||
|
|
||||||
if (oclk_max_pool_forward.empty())
|
if (oclk_max_pool_forward.empty())
|
||||||
|
@ -206,8 +206,6 @@ __kernel void ConvolveBasic(
|
|||||||
|
|
||||||
#elif defined KERNEL_IDLF
|
#elif defined KERNEL_IDLF
|
||||||
|
|
||||||
#define VLOAD4(_v, _p) do { _v = vload4(0, _p); } while(0)
|
|
||||||
|
|
||||||
// Each work-item computes a OUT_BLOCK_WIDTH * OUT_BLOCK_HEIGHT region of one output map.
|
// Each work-item computes a OUT_BLOCK_WIDTH * OUT_BLOCK_HEIGHT region of one output map.
|
||||||
// Each work-group (which will be mapped to 1 SIMD16/SIMD8 EU thread) will compute 16/8 different feature maps, but each feature map is for the same region of the input image.
|
// Each work-group (which will be mapped to 1 SIMD16/SIMD8 EU thread) will compute 16/8 different feature maps, but each feature map is for the same region of the input image.
|
||||||
// NDRange: (output_width+pad)/ OUT_BLOCK_WIDTH, (output_height+pad)/OUT_BLOCK_HEIGHT, NUM_FILTERS/OUT_BLOCK_DEPTH
|
// NDRange: (output_width+pad)/ OUT_BLOCK_WIDTH, (output_height+pad)/OUT_BLOCK_HEIGHT, NUM_FILTERS/OUT_BLOCK_DEPTH
|
||||||
@ -219,190 +217,123 @@ __kernel void
|
|||||||
convolve_simd(
|
convolve_simd(
|
||||||
ELTWISE_DATA_ARG
|
ELTWISE_DATA_ARG
|
||||||
FUSED_ARG
|
FUSED_ARG
|
||||||
__global Dtype* inputs_base,
|
__global Dtype* inputs,
|
||||||
filter_qualifier Dtype* weights_base,
|
__global Dtype* weights,
|
||||||
BIAS_KERNEL_ARG
|
BIAS_KERNEL_ARG
|
||||||
__global Dtype* outputs_base,
|
__global Dtype* outputs,
|
||||||
const ushort input_width,
|
const ushort input_width,
|
||||||
const ushort input_height,
|
const ushort input_height,
|
||||||
const ushort output_width,
|
const ushort output_width,
|
||||||
const ushort output_height)
|
const ushort output_height)
|
||||||
{
|
{
|
||||||
__global Dtype* outputs = outputs_base;
|
|
||||||
__global Dtype* inputs = inputs_base;
|
|
||||||
filter_qualifier Dtype* weights = weights_base;
|
|
||||||
unsigned int oc = get_global_id(0) * OUT_BLOCK_WIDTH; // oc = Output Column
|
unsigned int oc = get_global_id(0) * OUT_BLOCK_WIDTH; // oc = Output Column
|
||||||
unsigned int or = get_global_id(1) * OUT_BLOCK_HEIGHT;// or = Output Row
|
unsigned int or = get_global_id(1) * OUT_BLOCK_HEIGHT; // or = Output Row
|
||||||
unsigned int fm = get_global_id(2);// fm = Feature Map = od = Output Depth
|
unsigned int fm = get_global_id(2); // fm = Feature Map = od = Output Depth
|
||||||
unsigned int fmg = get_group_id(2);
|
unsigned int fmg = get_group_id(2);
|
||||||
unsigned int lid = get_local_id(2);
|
unsigned int lid = get_local_id(2);
|
||||||
|
|
||||||
Dtype out[OUT_BLOCK_WIDTH * OUT_BLOCK_HEIGHT];
|
Dtype out[OUT_BLOCK_WIDTH * OUT_BLOCK_HEIGHT] = { 0.0f };
|
||||||
|
|
||||||
int in_addr;
|
|
||||||
|
|
||||||
// find weights address of given neuron (lid is index)
|
// find weights address of given neuron (lid is index)
|
||||||
unsigned int weight_addr = (fmg % (ALIGNED_NUM_FILTERS/SIMD_SIZE)) * INPUT_DEPTH * KERNEL_WIDTH * KERNEL_HEIGHT * SIMD_SIZE + lid;
|
unsigned int weight_addr = (fmg % FILTERS_IN_GROUP) *
|
||||||
|
INPUT_DEPTH * KERNEL_WIDTH * KERNEL_HEIGHT * SIMD_SIZE + lid;
|
||||||
|
|
||||||
for(int i=0;i<OUT_BLOCK_SIZE;i++) {
|
unsigned int num_in_batch = fm / ALIGNED_NUM_FILTERS;
|
||||||
out[i]=0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int num_in_batch = ( fm ) / ALIGNED_NUM_FILTERS;
|
unsigned int input_batch_offset = num_in_batch * INPUT_PITCH * TOTAL_INPUT_DEPTH_SIZE;
|
||||||
|
|
||||||
unsigned int input_batch_offset = num_in_batch * input_height * input_width * TOTAL_INPUT_DEPTH_SIZE;
|
int curr_y = or * STRIDE_Y;
|
||||||
|
int curr_x = oc * STRIDE_X + lid;
|
||||||
int curr_local_y = ( lid / ( TILE_X / 4 ) );
|
|
||||||
int curr_local_x = ( lid % ( TILE_X / 4 ) ) * 4;
|
|
||||||
int curr_y = or * STRIDE_Y + curr_local_y;
|
|
||||||
int curr_x = oc * STRIDE_X + curr_local_x;
|
|
||||||
#if INPUT_PAD_W != 0 || INPUT_PAD_H != 0 || INPUT_PAD_BOTTOM != 0 || INPUT_PAD_RIGHT != 0
|
#if INPUT_PAD_W != 0 || INPUT_PAD_H != 0 || INPUT_PAD_BOTTOM != 0 || INPUT_PAD_RIGHT != 0
|
||||||
int saved_y = curr_y;
|
int saved_y = curr_y;
|
||||||
#endif
|
#endif
|
||||||
in_addr = input_batch_offset
|
int in_addr = input_batch_offset
|
||||||
+ (curr_y - INPUT_PAD_H) * input_width // y tile offset
|
+ (curr_y - INPUT_PAD_H) * INPUT_WIDTH // y tile offset
|
||||||
+ curr_x - INPUT_PAD_W; // x tile offset
|
+ curr_x - INPUT_PAD_W; // x tile offset
|
||||||
union {
|
|
||||||
Dtype4 in_vec[INVEC_SIZE];
|
Dtype in_buf[INVEC_SIZE];
|
||||||
Dtype in_array[INVEC_SIZE * 4];
|
|
||||||
} in_buf;
|
|
||||||
|
|
||||||
for(int kd = 0; kd < INPUT_DEPTH; kd++)
|
for(int kd = 0; kd < INPUT_DEPTH; kd++)
|
||||||
{
|
{
|
||||||
int in_offset = in_addr;
|
int in_offset = in_addr;
|
||||||
int reg = 0;
|
__attribute__((opencl_unroll_hint(INVEC_SIZE)))
|
||||||
LOOP(INVEC_SIZE, reg,
|
for (int reg = 0; reg < INVEC_SIZE; reg++)
|
||||||
{
|
{
|
||||||
if (curr_local_y + reg * TILE_Y_STRIDE < TILE_Y || INVEC_SIZE * TILE_Y_STRIDE <= (TILE_Y + 2) || reg < INVEC_SIZE - 1) {
|
in_buf[reg] = inputs[in_offset];
|
||||||
#if INPUT_PAD_W != 0 || INPUT_PAD_H != 0 || INPUT_PAD_BOTTOM != 0 || INPUT_PAD_RIGHT != 0
|
#if INPUT_PAD_W != 0 || INPUT_PAD_H != 0 || INPUT_PAD_BOTTOM != 0 || INPUT_PAD_RIGHT != 0
|
||||||
if (curr_y >= INPUT_PAD_H && curr_y < input_height + INPUT_PAD_H && curr_x + 3 >= INPUT_PAD_W && curr_x < input_width + INPUT_PAD_W) {
|
if (!(curr_y >= INPUT_PAD_H && curr_y < INPUT_HEIGHT + INPUT_PAD_H &&
|
||||||
if (curr_x < INPUT_PAD_W) {
|
curr_x >= INPUT_PAD_W && curr_x < INPUT_WIDTH + INPUT_PAD_W))
|
||||||
in_buf.in_vec[reg].s0 = 0;
|
{
|
||||||
if (curr_x + 1 >= INPUT_PAD_W && curr_x + 1 < input_width + INPUT_PAD_W)
|
in_buf[reg] = 0;
|
||||||
in_buf.in_vec[reg].s1 = *(inputs + in_offset + 1);
|
|
||||||
else
|
|
||||||
in_buf.in_vec[reg].s1 = 0;
|
|
||||||
if (curr_x + 2 >= INPUT_PAD_W && curr_x + 2 < input_width + INPUT_PAD_W)
|
|
||||||
in_buf.in_vec[reg].s2 = *(inputs + in_offset + 2);
|
|
||||||
else
|
|
||||||
in_buf.in_vec[reg].s2 = 0;
|
|
||||||
if (curr_x + 3 < input_width + INPUT_PAD_W)
|
|
||||||
in_buf.in_vec[reg].s3 = *(inputs + in_offset + 3);
|
|
||||||
else
|
|
||||||
in_buf.in_vec[reg].s3 = 0;
|
|
||||||
} else {
|
|
||||||
VLOAD4(in_buf.in_vec[reg], inputs + in_offset);
|
|
||||||
if (curr_x + 1 >= input_width + INPUT_PAD_W)
|
|
||||||
in_buf.in_vec[reg].s1 = 0;
|
|
||||||
if (curr_x + 2 >= input_width + INPUT_PAD_W)
|
|
||||||
in_buf.in_vec[reg].s2 = 0;
|
|
||||||
if (curr_x + 3 >= input_width + INPUT_PAD_W)
|
|
||||||
in_buf.in_vec[reg].s3 = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
in_buf.in_vec[reg] = 0;
|
|
||||||
}
|
}
|
||||||
curr_y += TILE_Y_STRIDE;
|
|
||||||
#else
|
|
||||||
VLOAD4(in_buf.in_vec[reg], inputs + in_offset);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
curr_y += 1;
|
||||||
in_offset += input_width * TILE_Y_STRIDE;
|
in_offset += INPUT_WIDTH;
|
||||||
});
|
}
|
||||||
in_addr += input_height * input_width;
|
|
||||||
|
in_addr += INPUT_PITCH;
|
||||||
|
|
||||||
#if INPUT_PAD_W != 0 || INPUT_PAD_H != 0 || INPUT_PAD_BOTTOM != 0 || INPUT_PAD_RIGHT != 0
|
#if INPUT_PAD_W != 0 || INPUT_PAD_H != 0 || INPUT_PAD_BOTTOM != 0 || INPUT_PAD_RIGHT != 0
|
||||||
curr_y = saved_y;
|
curr_y = saved_y;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if KERNEL_WIDTH * KERNEL_HEIGHT != 1
|
Dtype weight_buf[WEIGHT_PREF];
|
||||||
#define WEIGHT_PREF 8
|
|
||||||
#else
|
|
||||||
#define WEIGHT_PREF 1
|
|
||||||
#endif
|
|
||||||
union {
|
|
||||||
Dtype w[WEIGHT_PREF];
|
|
||||||
#if KERNEL_WIDTH * KERNEL_HEIGHT != 1
|
|
||||||
INT_TYPE8 ui8;
|
|
||||||
#endif
|
|
||||||
} weight_buf;
|
|
||||||
int w_idx=0;
|
int w_idx=0;
|
||||||
|
|
||||||
unsigned int orig_weight_addr = weight_addr;
|
for (int i = 0; i < WEIGHT_PREF; i++)
|
||||||
#if KERNEL_WIDTH * KERNEL_HEIGHT != 1
|
{
|
||||||
weight_buf.ui8 = SUB_GROUP_BLOCK_READ8((__global INT_TYPE *)&weights[weight_addr]);
|
weight_buf[i] = weights[weight_addr];
|
||||||
weight_addr += SIMD_SIZE * WEIGHT_PREF;
|
weight_addr += SIMD_SIZE;
|
||||||
#else
|
}
|
||||||
weight_buf.w[0] = as_Dtype(SUB_GROUP_BLOCK_READ((__global INT_TYPE *)&weights[weight_addr]));
|
|
||||||
weight_addr += SIMD_SIZE * 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BLOCK_IN(n) sub_group_broadcast( in_buf.in_array[((n)%4) + ((n) / (TILE_Y_STRIDE * TILE_X)) * 4], (((n) % (TILE_Y_STRIDE * TILE_X))/4))
|
#define BLOCK_IN(n, c) intel_sub_group_shuffle(in_buf[n], (c))
|
||||||
|
|
||||||
int kr = 0; // kr = Kernel Row
|
int kr = 0; // kr = Kernel Row
|
||||||
LOOP(KERNEL_HEIGHT, kr,// LOOP is a macro that unrolls the loop.
|
LOOP(KERNEL_HEIGHT, kr,// LOOP is a macro that unrolls the loop.
|
||||||
|
{
|
||||||
|
int kc = 0; // kc = Kernel Column
|
||||||
|
LOOP(KERNEL_WIDTH, kc,
|
||||||
{
|
{
|
||||||
int kc = 0; // kc = Kernel Column
|
for (int br=0; br < OUT_BLOCK_HEIGHT; br++)
|
||||||
LOOP(KERNEL_WIDTH, kc,
|
{
|
||||||
{
|
for(int bc=0; bc < OUT_BLOCK_WIDTH; bc++)
|
||||||
for(int br=0; br < OUT_BLOCK_HEIGHT; br++) {
|
{
|
||||||
for(int bc=0; bc < OUT_BLOCK_WIDTH; bc++) {
|
Dtype input = BLOCK_IN((br * STRIDE_Y + kr * DILATION_Y), bc * STRIDE_X + kc * DILATION_X);
|
||||||
Dtype input = BLOCK_IN((br * STRIDE_Y + kr * DILATION_Y) * TILE_X + bc * STRIDE_X + kc * DILATION_X);
|
out[br * OUT_BLOCK_WIDTH + bc] = mad(weight_buf[w_idx % WEIGHT_PREF], input, out[br * OUT_BLOCK_WIDTH + bc]);
|
||||||
out[br * OUT_BLOCK_WIDTH + bc] = mad(weight_buf.w[w_idx % WEIGHT_PREF], input, out[br * OUT_BLOCK_WIDTH + bc]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#if KERNEL_WIDTH * KERNEL_HEIGHT > WEIGHT_PREF
|
}
|
||||||
// We assume KERNEL_W is equal to KERNEL_H here.
|
weight_buf[w_idx % WEIGHT_PREF] = weights[weight_addr];
|
||||||
if ((w_idx + 1) % WEIGHT_PREF == 0
|
weight_addr += SIMD_SIZE;
|
||||||
#if KERNEL_WIDTH * KERNEL_HEIGHT % 8 != 0
|
++w_idx;
|
||||||
&& ((w_idx + 1) <= (KERNEL_WIDTH * KERNEL_HEIGHT - WEIGHT_PREF))
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
weight_buf.ui8 = SUB_GROUP_BLOCK_READ8((__global INT_TYPE *)&weights[weight_addr]);
|
|
||||||
weight_addr += SIMD_SIZE * WEIGHT_PREF; // weights must be stored in just the right SIMD swizzled format for this to work, see host code for details.
|
|
||||||
}
|
|
||||||
#if KERNEL_WIDTH*KERNEL_HEIGHT % 8 == 0
|
|
||||||
// need to do nothing
|
|
||||||
#else
|
|
||||||
else if ((w_idx + 1) % WEIGHT_PREF == 0 && ((w_idx + 1) > (KERNEL_WIDTH * KERNEL_HEIGHT - WEIGHT_PREF)))
|
|
||||||
#if KERNEL_WIDTH * KERNEL_HEIGHT % 8 == 1
|
|
||||||
weight_buf.w[0] = weights[weight_addr];
|
|
||||||
#elif KERNEL_WIDTH * KERNEL_HEIGHT % 8 == 2
|
|
||||||
weight_buf.ui8.s01 = SUB_GROUP_BLOCK_READ2((__global INT_TYPE *)&weights[weight_addr]);
|
|
||||||
#elif KERNEL_WIDTH * KERNEL_HEIGHT % 8 <= 4
|
|
||||||
weight_buf.ui8.s0123 = SUB_GROUP_BLOCK_READ4((__global INT_TYPE *)&weights[weight_addr]);
|
|
||||||
#else
|
|
||||||
weight_buf.ui8 = SUB_GROUP_BLOCK_READ8((__global INT_TYPE *)&weights[weight_addr]);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
++w_idx;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
weight_addr = orig_weight_addr + KERNEL_WIDTH * KERNEL_HEIGHT * SIMD_SIZE;
|
});
|
||||||
|
weight_addr -= WEIGHT_PREF * SIMD_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
// dead code to work around possible compiler bug.
|
|
||||||
if (ALIGNED_NUM_FILTERS != NUM_FILTERS && fm > 0xfffffffeul) {
|
|
||||||
outputs[0] = BLOCK_IN(fm % SIMD_SIZE);
|
|
||||||
}
|
|
||||||
fm = fm % ALIGNED_NUM_FILTERS;
|
fm = fm % ALIGNED_NUM_FILTERS;
|
||||||
|
|
||||||
if ((ALIGNED_NUM_FILTERS == NUM_FILTERS || fm < NUM_FILTERS)) {
|
#if LEFT_FILTERS > 0
|
||||||
unsigned int out_addr = ( num_in_batch * TOTAL_OUTPUT_DEPTH + fm ) * output_width * output_height;
|
if (fm < NUM_FILTERS)
|
||||||
out_addr += or * output_width + oc;
|
#endif
|
||||||
// we need this address calculation for biases because we support views and batching
|
{
|
||||||
#if APPLY_BIAS
|
unsigned int out_addr = (num_in_batch * TOTAL_OUTPUT_DEPTH + fm) * OUTPUT_PITCH;
|
||||||
Dtype bias = biases_base[fm];
|
out_addr += or * output_width + oc;
|
||||||
#else
|
// we need this address calculation for biases because we support views and batching
|
||||||
Dtype bias = 0;
|
#if APPLY_BIAS
|
||||||
|
Dtype bias = biases_base[fm];
|
||||||
|
#else
|
||||||
|
Dtype bias = 0;
|
||||||
#endif
|
#endif
|
||||||
for(unsigned int r = 0; r < OUT_BLOCK_HEIGHT; r++) {
|
|
||||||
if (r + or >= output_height) break;
|
|
||||||
for(unsigned int c = 0; c < OUT_BLOCK_WIDTH; c++) {
|
|
||||||
if (c + oc >= output_width) break;
|
|
||||||
// this does a scattered write to SIMD_SIZE different feature maps, so that data within one map is contiguous, thus ready for input to next layer.
|
|
||||||
ACTIVATION_FUNCTION(outputs, out_addr + r * output_width + c, bias + out[r * OUT_BLOCK_WIDTH + c], fm);
|
|
||||||
|
|
||||||
|
for(unsigned int r = 0; r < OUT_BLOCK_HEIGHT; r++)
|
||||||
|
{
|
||||||
|
if (r + or >= output_height) break;
|
||||||
|
for(unsigned int c = 0; c < OUT_BLOCK_WIDTH; c++)
|
||||||
|
{
|
||||||
|
if (c + oc >= output_width) break;
|
||||||
|
// this does a scattered write to SIMD_SIZE different feature maps,
|
||||||
|
// so that data within one map is contiguous, thus ready for input to next layer.
|
||||||
|
ACTIVATION_FUNCTION(outputs, out_addr + r * output_width + c, bias + out[r * OUT_BLOCK_WIDTH + c], fm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,36 +65,40 @@ __kernel void
|
|||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
for (int index = get_global_id(0); index < nthreads;
|
int index = get_global_id(0);
|
||||||
index += get_global_size(0))
|
if (index >= nthreads)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int pw = index % pooled_width;
|
||||||
|
const int xx = index / pooled_width;
|
||||||
|
const int ph = xx % pooled_height;
|
||||||
|
const int ch = xx / pooled_height;
|
||||||
|
int hstart = ph * STRIDE_H - PAD_H;
|
||||||
|
int wstart = pw * STRIDE_W - PAD_W;
|
||||||
|
Dtype maxval = -FLT_MAX;
|
||||||
|
int maxidx = -1;
|
||||||
|
int in_offset = ch * height * width;
|
||||||
|
for (int h = 0; h < KERNEL_H; ++h)
|
||||||
{
|
{
|
||||||
const int pw = index % pooled_width;
|
int off_y = hstart + h;
|
||||||
const int ph = (index / pooled_width) % pooled_height;
|
if (off_y >= 0 && off_y < height)
|
||||||
const int c = (index / pooled_width / pooled_height) % channels;
|
{
|
||||||
const int n = index / pooled_width / pooled_height / channels;
|
for (int w = 0; w < KERNEL_W; ++w)
|
||||||
int hstart = ph * STRIDE_H - PAD_H;
|
{
|
||||||
int wstart = pw * STRIDE_W - PAD_W;
|
int off_x = wstart + w;
|
||||||
const int hend = min(hstart + KERNEL_H, height);
|
if (off_x >= 0 && off_x < width)
|
||||||
const int wend = min(wstart + KERNEL_W, width);
|
{
|
||||||
hstart = max(hstart, (int)0);
|
Dtype val = bottom_data[in_offset + off_y * width + off_x];
|
||||||
wstart = max(wstart, (int)0);
|
maxidx = (val > maxval) ? (off_y * width + off_x) : maxidx;
|
||||||
Dtype maxval = -FLT_MAX;
|
maxval = fmax(val, maxval);
|
||||||
int maxidx = -1;
|
|
||||||
__global const Dtype* bottom_slice = bottom_data
|
|
||||||
+ (n * channels + c) * height * width;
|
|
||||||
for (int h = hstart; h < hend; ++h) {
|
|
||||||
for (int w = wstart; w < wend; ++w) {
|
|
||||||
if (bottom_slice[h * width + w] > maxval) {
|
|
||||||
maxidx = h * width + w;
|
|
||||||
maxval = bottom_slice[maxidx];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
top_data[index] = maxval;
|
|
||||||
#ifdef HAVE_MASK
|
|
||||||
mask[index] = maxidx;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
top_data[index] = maxval;
|
||||||
|
#ifdef HAVE_MASK
|
||||||
|
mask[index] = maxidx;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined KERNEL_AVE_POOL
|
#elif defined KERNEL_AVE_POOL
|
||||||
@ -105,43 +109,42 @@ __kernel void TEMPLATE(ave_pool_forward, Dtype)(
|
|||||||
const int pooled_height, const int pooled_width,
|
const int pooled_height, const int pooled_width,
|
||||||
__global Dtype* top_data)
|
__global Dtype* top_data)
|
||||||
{
|
{
|
||||||
for (int index = get_global_id(0); index < nthreads;
|
int index = get_global_id(0);
|
||||||
index += get_global_size(0))
|
if (index >= nthreads)
|
||||||
{
|
return;
|
||||||
{
|
|
||||||
const int pw = index % pooled_width;
|
const int pw = index % pooled_width;
|
||||||
const int ph = (index / pooled_width) % pooled_height;
|
const int xx = index / pooled_width;
|
||||||
const int c = (index / pooled_width / pooled_height) % channels;
|
const int ph = xx % pooled_height;
|
||||||
const int n = index / pooled_width / pooled_height / channels;
|
const int ch = xx / pooled_height;
|
||||||
int hstart = ph * STRIDE_H - PAD_H;
|
int hstart = ph * STRIDE_H - PAD_H;
|
||||||
int wstart = pw * STRIDE_W - PAD_W;
|
int wstart = pw * STRIDE_W - PAD_W;
|
||||||
int hend = min(hstart + KERNEL_H, height + PAD_H);
|
int hend = min(hstart + KERNEL_H, height + PAD_H);
|
||||||
int wend = min(wstart + KERNEL_W, width + PAD_W);
|
int wend = min(wstart + KERNEL_W, width + PAD_W);
|
||||||
int pool_size;
|
int pool_size;
|
||||||
#ifdef AVE_POOL_PADDING_AREA
|
#ifdef AVE_POOL_PADDING_AREA
|
||||||
pool_size = (hend - hstart) * (wend - wstart);
|
pool_size = (hend - hstart) * (wend - wstart);
|
||||||
hstart = max(hstart, (int)0);
|
hstart = max(hstart, (int)0);
|
||||||
wstart = max(wstart, (int)0);
|
wstart = max(wstart, (int)0);
|
||||||
hend = min(hend, height);
|
hend = min(hend, height);
|
||||||
wend = min(wend, width);
|
wend = min(wend, width);
|
||||||
#else
|
#else
|
||||||
hstart = max(hstart, (int)0);
|
hstart = max(hstart, (int)0);
|
||||||
wstart = max(wstart, (int)0);
|
wstart = max(wstart, (int)0);
|
||||||
hend = min(hend, height);
|
hend = min(hend, height);
|
||||||
wend = min(wend, width);
|
wend = min(wend, width);
|
||||||
pool_size = (hend - hstart) * (wend - wstart);
|
pool_size = (hend - hstart) * (wend - wstart);
|
||||||
#endif
|
#endif
|
||||||
Dtype aveval = 0;
|
Dtype aveval = 0;
|
||||||
__global const Dtype* bottom_slice = bottom_data
|
int in_offset = ch * height * width;
|
||||||
+ (n * channels + c) * height * width;
|
for (int h = hstart; h < hend; ++h)
|
||||||
for (int h = hstart; h < hend; ++h) {
|
{
|
||||||
for (int w = wstart; w < wend; ++w) {
|
for (int w = wstart; w < wend; ++w)
|
||||||
aveval += bottom_slice[h * width + w];
|
{
|
||||||
}
|
aveval += bottom_data[in_offset + h * width + w];
|
||||||
}
|
|
||||||
top_data[index] = aveval / pool_size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
top_data[index] = aveval / pool_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined KERNEL_STO_POOL
|
#elif defined KERNEL_STO_POOL
|
||||||
|
@ -18,6 +18,7 @@ Implementation of Tensorflow models parser
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
#include "tf_graph_simplifier.hpp"
|
#include "tf_graph_simplifier.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -50,7 +51,8 @@ enum DataLayout
|
|||||||
{
|
{
|
||||||
DATA_LAYOUT_NHWC,
|
DATA_LAYOUT_NHWC,
|
||||||
DATA_LAYOUT_NCHW,
|
DATA_LAYOUT_NCHW,
|
||||||
DATA_LAYOUT_UNKNOWN
|
DATA_LAYOUT_UNKNOWN,
|
||||||
|
DATA_LAYOUT_PLANAR // 2-dimensional outputs (matmul, flatten, reshape to 2d)
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<std::pair<String, int> > StrIntVector;
|
typedef std::vector<std::pair<String, int> > StrIntVector;
|
||||||
@ -245,16 +247,53 @@ const tensorflow::AttrValue& getLayerAttr(const tensorflow::NodeDef &layer, cons
|
|||||||
return layer.attr().at(name);
|
return layer.attr().at(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int getDataLayout(const tensorflow::NodeDef& layer)
|
||||||
|
{
|
||||||
|
if (hasLayerAttr(layer, "data_format"))
|
||||||
|
{
|
||||||
|
std::string format = getLayerAttr(layer, "data_format").s();
|
||||||
|
if (format == "NHWC" || format == "channels_last")
|
||||||
|
return DATA_LAYOUT_NHWC;
|
||||||
|
else if (format == "NCHW" || format == "channels_first")
|
||||||
|
return DATA_LAYOUT_NCHW;
|
||||||
|
else
|
||||||
|
CV_Error(Error::StsParseError, "Unknown data_format value: " + format);
|
||||||
|
}
|
||||||
|
return DATA_LAYOUT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string getNodeName(const std::string& tensorName)
|
||||||
|
{
|
||||||
|
return tensorName.substr(0, tensorName.rfind(':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int getDataLayout(const std::string& layerName,
|
||||||
|
const std::map<String, int>& data_layouts)
|
||||||
|
{
|
||||||
|
std::map<String, int>::const_iterator it = data_layouts.find(getNodeName(layerName));
|
||||||
|
return it != data_layouts.end() ? it->second : DATA_LAYOUT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
void setStrides(LayerParams &layerParams, const tensorflow::NodeDef &layer)
|
void setStrides(LayerParams &layerParams, const tensorflow::NodeDef &layer)
|
||||||
{
|
{
|
||||||
if (hasLayerAttr(layer, "strides"))
|
if (hasLayerAttr(layer, "strides"))
|
||||||
{
|
{
|
||||||
const tensorflow::AttrValue& val = getLayerAttr(layer, "strides");
|
const tensorflow::AttrValue& val = getLayerAttr(layer, "strides");
|
||||||
|
int dimX, dimY, dimC;
|
||||||
|
int layout = getDataLayout(layer);
|
||||||
|
if (layout == DATA_LAYOUT_NCHW)
|
||||||
|
{
|
||||||
|
dimC = 1; dimY = 2; dimX = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dimY = 1; dimX = 2; dimC = 3;
|
||||||
|
}
|
||||||
if (val.list().i_size() != 4 ||
|
if (val.list().i_size() != 4 ||
|
||||||
val.list().i(0) != 1 || val.list().i(3) != 1)
|
val.list().i(0) != 1 || val.list().i(dimC) != 1)
|
||||||
CV_Error(Error::StsError, "Unsupported strides");
|
CV_Error(Error::StsError, "Unsupported strides");
|
||||||
layerParams.set("stride_h", static_cast<int>(val.list().i(1)));
|
layerParams.set("stride_h", static_cast<int>(val.list().i(dimY)));
|
||||||
layerParams.set("stride_w", static_cast<int>(val.list().i(2)));
|
layerParams.set("stride_w", static_cast<int>(val.list().i(dimX)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,11 +316,21 @@ void setKSize(LayerParams &layerParams, const tensorflow::NodeDef &layer)
|
|||||||
if (hasLayerAttr(layer, "ksize"))
|
if (hasLayerAttr(layer, "ksize"))
|
||||||
{
|
{
|
||||||
const tensorflow::AttrValue& val = getLayerAttr(layer, "ksize");
|
const tensorflow::AttrValue& val = getLayerAttr(layer, "ksize");
|
||||||
|
int dimX, dimY, dimC;
|
||||||
|
int layout = getDataLayout(layer);
|
||||||
|
if (layout == DATA_LAYOUT_NCHW)
|
||||||
|
{
|
||||||
|
dimC = 1; dimY = 2; dimX = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dimY = 1; dimX = 2; dimC = 3;
|
||||||
|
}
|
||||||
if (val.list().i_size() != 4 ||
|
if (val.list().i_size() != 4 ||
|
||||||
val.list().i(0) != 1 || val.list().i(3) != 1)
|
val.list().i(0) != 1 || val.list().i(dimC) != 1)
|
||||||
CV_Error(Error::StsError, "Unsupported ksize");
|
CV_Error(Error::StsError, "Unsupported ksize");
|
||||||
layerParams.set("kernel_h", static_cast<int>(val.list().i(1)));
|
layerParams.set("kernel_h", static_cast<int>(val.list().i(dimY)));
|
||||||
layerParams.set("kernel_w", static_cast<int>(val.list().i(2)));
|
layerParams.set("kernel_w", static_cast<int>(val.list().i(dimX)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -375,6 +424,8 @@ private:
|
|||||||
// and may be used to build the network using binary format only as a weights storage.
|
// and may be used to build the network using binary format only as a weights storage.
|
||||||
// This approach is similar to Caffe's `.prorotxt` and `.caffemodel`.
|
// This approach is similar to Caffe's `.prorotxt` and `.caffemodel`.
|
||||||
tensorflow::GraphDef netTxt;
|
tensorflow::GraphDef netTxt;
|
||||||
|
|
||||||
|
std::vector<String> netInputsNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
TFImporter::TFImporter(const char *model, const char *config)
|
TFImporter::TFImporter(const char *model, const char *config)
|
||||||
@ -442,7 +493,14 @@ void TFImporter::connect(const std::map<String, int>& layers_name_id_map, Net& n
|
|||||||
std::map<String, int>::const_iterator it = layers_name_id_map.find(outPin.name);
|
std::map<String, int>::const_iterator it = layers_name_id_map.find(outPin.name);
|
||||||
if (it == layers_name_id_map.end())
|
if (it == layers_name_id_map.end())
|
||||||
CV_Error(Error::StsError, "Input layer not found: " + outPin.name);
|
CV_Error(Error::StsError, "Input layer not found: " + outPin.name);
|
||||||
network.connect(it->second, outPin.blobIndex, input_layer_id, input_blob_id);
|
|
||||||
|
std::vector<String>::iterator inpNameIt = std::find(netInputsNames.begin(), netInputsNames.end(), outPin.name);
|
||||||
|
int blobIndex;
|
||||||
|
if (inpNameIt == netInputsNames.end())
|
||||||
|
blobIndex = outPin.blobIndex;
|
||||||
|
else
|
||||||
|
blobIndex = inpNameIt - netInputsNames.begin();
|
||||||
|
network.connect(it->second, blobIndex, input_layer_id, input_blob_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TFImporter::connectToAllBlobs(const std::map<String, int>& layer_id, Net& network, const Pin& outPin,
|
void TFImporter::connectToAllBlobs(const std::map<String, int>& layer_id, Net& network, const Pin& outPin,
|
||||||
@ -560,39 +618,38 @@ static void addConstNodes(tensorflow::GraphDef& net, std::map<String, int>& cons
|
|||||||
|
|
||||||
// If all inputs of specific layer have the same data layout we can say that
|
// If all inputs of specific layer have the same data layout we can say that
|
||||||
// this layer's output has this data layout too. Returns DATA_LAYOUT_UNKNOWN otherwise.
|
// this layer's output has this data layout too. Returns DATA_LAYOUT_UNKNOWN otherwise.
|
||||||
static int predictOutputDataLayout(const tensorflow::NodeDef& layer, const std::map<String, int>& data_layouts)
|
static int predictOutputDataLayout(const tensorflow::GraphDef& net,
|
||||||
|
const tensorflow::NodeDef& layer,
|
||||||
|
const std::map<String, int>& data_layouts)
|
||||||
{
|
{
|
||||||
if (hasLayerAttr(layer, "data_format"))
|
int layout = getDataLayout(layer);
|
||||||
{
|
if (layout != DATA_LAYOUT_UNKNOWN)
|
||||||
std::string format = getLayerAttr(layer, "data_format").s();
|
return layout;
|
||||||
if (format == "NHWC" || format == "channels_last")
|
|
||||||
return DATA_LAYOUT_NHWC;
|
|
||||||
else if (format == "NCHW" || format == "channels_first")
|
|
||||||
return DATA_LAYOUT_NCHW;
|
|
||||||
else
|
|
||||||
CV_Error(Error::StsParseError, "Unknown data_format value: " + format);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine layout by layer's inputs
|
// Determine layout by layer's inputs
|
||||||
int layout = DATA_LAYOUT_UNKNOWN;
|
|
||||||
std::map<String, int>::const_iterator it;
|
std::map<String, int>::const_iterator it;
|
||||||
for (int i = 0, n = layer.input_size(); i < n; ++i)
|
for (int i = 0, n = layer.input_size(); i < n; ++i)
|
||||||
{
|
{
|
||||||
it = data_layouts.find(layer.input(i).substr(0, layer.input(i).rfind(':')));
|
it = data_layouts.find(getNodeName(layer.input(i)));
|
||||||
if (it != data_layouts.end())
|
if (it != data_layouts.end())
|
||||||
{
|
{
|
||||||
if (it->second == DATA_LAYOUT_UNKNOWN)
|
if (layout != DATA_LAYOUT_UNKNOWN)
|
||||||
return DATA_LAYOUT_UNKNOWN;
|
|
||||||
else if (it->second != layout)
|
|
||||||
{
|
{
|
||||||
if (layout == DATA_LAYOUT_UNKNOWN)
|
if (it->second != layout && it->second != DATA_LAYOUT_UNKNOWN)
|
||||||
layout = it->second;
|
|
||||||
else
|
|
||||||
return DATA_LAYOUT_UNKNOWN;
|
return DATA_LAYOUT_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
layout = it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return layout;
|
|
||||||
|
if (layout != DATA_LAYOUT_UNKNOWN)
|
||||||
|
return layout;
|
||||||
|
|
||||||
|
// Determine layout by layer's consumers recursively.
|
||||||
|
it = data_layouts.find(layer.name());
|
||||||
|
CV_Assert(it != data_layouts.end());
|
||||||
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TFImporter::populateNet(Net dstNet)
|
void TFImporter::populateNet(Net dstNet)
|
||||||
@ -610,6 +667,52 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
int layersSize = net.node_size();
|
int layersSize = net.node_size();
|
||||||
|
|
||||||
std::map<String, int> data_layouts;
|
std::map<String, int> data_layouts;
|
||||||
|
// Pre-fill data layouts where they are set explicitly.
|
||||||
|
// Assuming that nodes are in topological order
|
||||||
|
for (int i = net.node_size() - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
const tensorflow::NodeDef& layer = net.node(i);
|
||||||
|
std::string name = layer.name();
|
||||||
|
|
||||||
|
int layout = getDataLayout(layer);
|
||||||
|
std::map<String, int>::iterator it = data_layouts.find(name);
|
||||||
|
if (it != data_layouts.end())
|
||||||
|
{
|
||||||
|
if (layout != DATA_LAYOUT_UNKNOWN)
|
||||||
|
{
|
||||||
|
if (it->second == DATA_LAYOUT_UNKNOWN)
|
||||||
|
it->second = layout;
|
||||||
|
else if (it->second != layout)
|
||||||
|
{
|
||||||
|
it->second = DATA_LAYOUT_UNKNOWN;
|
||||||
|
layout = DATA_LAYOUT_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
layout = it->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
data_layouts[name] = layout;
|
||||||
|
|
||||||
|
// Specify input layers to have the same data layout.
|
||||||
|
for (int j = 0; j < layer.input_size(); ++j)
|
||||||
|
{
|
||||||
|
name = getNodeName(layer.input(j));
|
||||||
|
it = data_layouts.find(name);
|
||||||
|
if (it != data_layouts.end())
|
||||||
|
{
|
||||||
|
if (layout != DATA_LAYOUT_UNKNOWN)
|
||||||
|
{
|
||||||
|
if (it->second == DATA_LAYOUT_UNKNOWN)
|
||||||
|
it->second = layout;
|
||||||
|
else if (it->second != layout)
|
||||||
|
it->second = DATA_LAYOUT_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
data_layouts[name] = layout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// find all Const layers for params
|
// find all Const layers for params
|
||||||
std::map<String, int> value_id;
|
std::map<String, int> value_id;
|
||||||
@ -628,7 +731,8 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
if(layers_to_ignore.find(name) != layers_to_ignore.end())
|
if(layers_to_ignore.find(name) != layers_to_ignore.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
data_layouts[name] = predictOutputDataLayout(layer, data_layouts);
|
int predictedLayout = predictOutputDataLayout(net, layer, data_layouts);
|
||||||
|
data_layouts[name] = predictedLayout;
|
||||||
|
|
||||||
if (type == "Conv2D" || type == "SpaceToBatchND" || type == "DepthwiseConv2dNative")
|
if (type == "Conv2D" || type == "SpaceToBatchND" || type == "DepthwiseConv2dNative")
|
||||||
{
|
{
|
||||||
@ -733,7 +837,8 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
// one input only
|
// one input only
|
||||||
connect(layer_id, dstNet, parsePin(input), id, 0);
|
connect(layer_id, dstNet, parsePin(input), id, 0);
|
||||||
|
|
||||||
if (data_layouts[name] == DATA_LAYOUT_UNKNOWN)
|
|
||||||
|
if (getDataLayout(name, data_layouts) == DATA_LAYOUT_UNKNOWN)
|
||||||
data_layouts[name] = DATA_LAYOUT_NHWC;
|
data_layouts[name] = DATA_LAYOUT_NHWC;
|
||||||
}
|
}
|
||||||
else if (type == "BiasAdd" || type == "Add")
|
else if (type == "BiasAdd" || type == "Add")
|
||||||
@ -778,7 +883,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
Pin inp = parsePin(layer.input(ii));
|
Pin inp = parsePin(layer.input(ii));
|
||||||
if (layer_id.find(inp.name) == layer_id.end())
|
if (layer_id.find(inp.name) == layer_id.end())
|
||||||
CV_Error(Error::StsError, "Input layer not found: " + inp.name);
|
CV_Error(Error::StsError, "Input layer not found: " + inp.name);
|
||||||
dstNet.connect(layer_id.at(inp.name), inp.blobIndex, id, ii);
|
connect(layer_id, dstNet, inp, id, ii);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -852,14 +957,15 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
// one input only
|
// one input only
|
||||||
int input_blob_index = kernel_blob_index == 0 ? 1 : 0;
|
int input_blob_index = kernel_blob_index == 0 ? 1 : 0;
|
||||||
connect(layer_id, dstNet, parsePin(layer.input(input_blob_index)), id, 0);
|
connect(layer_id, dstNet, parsePin(layer.input(input_blob_index)), id, 0);
|
||||||
data_layouts[name] = DATA_LAYOUT_UNKNOWN;
|
data_layouts[name] = DATA_LAYOUT_PLANAR;
|
||||||
}
|
}
|
||||||
else if (type == "Reshape")
|
else if (type == "Reshape")
|
||||||
{
|
{
|
||||||
Pin inpId = parsePin(layer.input(0));
|
Pin inpId = parsePin(layer.input(0));
|
||||||
Mat newShape = getTensorContent(getConstBlob(layer, value_id, 1));
|
Mat newShape = getTensorContent(getConstBlob(layer, value_id, 1));
|
||||||
|
|
||||||
if (newShape.total() != 4 && data_layouts[layer.input(0)] == DATA_LAYOUT_NHWC)
|
int inpLayout = getDataLayout(layer.input(0), data_layouts);
|
||||||
|
if (newShape.total() != 4 && inpLayout == DATA_LAYOUT_NHWC)
|
||||||
{
|
{
|
||||||
LayerParams permLP;
|
LayerParams permLP;
|
||||||
int order[] = {0, 2, 3, 1}; // From OpenCV's NCHW to NHWC.
|
int order[] = {0, 2, 3, 1}; // From OpenCV's NCHW to NHWC.
|
||||||
@ -872,7 +978,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
connect(layer_id, dstNet, inpId, permId, 0);
|
connect(layer_id, dstNet, inpId, permId, 0);
|
||||||
inpId = Pin(permName);
|
inpId = Pin(permName);
|
||||||
}
|
}
|
||||||
else if (newShape.total() == 4 && data_layouts[layer.input(0)] == DATA_LAYOUT_NHWC)
|
else if (newShape.total() == 4 && inpLayout == DATA_LAYOUT_NHWC)
|
||||||
{
|
{
|
||||||
// NHWC->NCHW
|
// NHWC->NCHW
|
||||||
std::swap(*newShape.ptr<int32_t>(0, 2), *newShape.ptr<int32_t>(0, 3));
|
std::swap(*newShape.ptr<int32_t>(0, 2), *newShape.ptr<int32_t>(0, 3));
|
||||||
@ -885,11 +991,12 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
|
|
||||||
// one input only
|
// one input only
|
||||||
connect(layer_id, dstNet, inpId, id, 0);
|
connect(layer_id, dstNet, inpId, id, 0);
|
||||||
|
data_layouts[name] = newShape.total() == 2 ? DATA_LAYOUT_PLANAR : DATA_LAYOUT_UNKNOWN;
|
||||||
}
|
}
|
||||||
else if (type == "Flatten" || type == "Squeeze")
|
else if (type == "Flatten" || type == "Squeeze")
|
||||||
{
|
{
|
||||||
Pin inpId = parsePin(layer.input(0));
|
Pin inpId = parsePin(layer.input(0));
|
||||||
int inpLayout = data_layouts[layer.input(0)];
|
int inpLayout = getDataLayout(layer.input(0), data_layouts);
|
||||||
if (type == "Squeeze")
|
if (type == "Squeeze")
|
||||||
{
|
{
|
||||||
CV_Assert(hasLayerAttr(layer, "squeeze_dims"));
|
CV_Assert(hasLayerAttr(layer, "squeeze_dims"));
|
||||||
@ -923,7 +1030,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
int id = dstNet.addLayer(name, "Flatten", layerParams);
|
int id = dstNet.addLayer(name, "Flatten", layerParams);
|
||||||
layer_id[name] = id;
|
layer_id[name] = id;
|
||||||
connect(layer_id, dstNet, inpId, id, 0);
|
connect(layer_id, dstNet, inpId, id, 0);
|
||||||
data_layouts[name] = DATA_LAYOUT_UNKNOWN;
|
data_layouts[name] = DATA_LAYOUT_PLANAR;
|
||||||
}
|
}
|
||||||
else if (type == "Transpose")
|
else if (type == "Transpose")
|
||||||
{
|
{
|
||||||
@ -934,7 +1041,8 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
{
|
{
|
||||||
// Only NHWC <-> NCHW permutations are allowed. OpenCV is always
|
// Only NHWC <-> NCHW permutations are allowed. OpenCV is always
|
||||||
// keep NCHW layout this way.
|
// keep NCHW layout this way.
|
||||||
if (data_layouts[layer.input(0)] == DATA_LAYOUT_NHWC)
|
int inpLayout = getDataLayout(layer.input(0), data_layouts);
|
||||||
|
if (inpLayout == DATA_LAYOUT_NHWC)
|
||||||
{
|
{
|
||||||
if (permData[0] == 0 && permData[1] == 3 && permData[2] == 1 && permData[3] == 2)
|
if (permData[0] == 0 && permData[1] == 3 && permData[2] == 1 && permData[3] == 2)
|
||||||
{
|
{
|
||||||
@ -951,7 +1059,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
else
|
else
|
||||||
CV_Error(Error::StsParseError, "Only NHWC <-> NCHW permutations are allowed.");
|
CV_Error(Error::StsParseError, "Only NHWC <-> NCHW permutations are allowed.");
|
||||||
}
|
}
|
||||||
else if (data_layouts[layer.input(0)] == DATA_LAYOUT_NCHW)
|
else if (inpLayout == DATA_LAYOUT_NCHW)
|
||||||
{
|
{
|
||||||
if (permData[0] == 0 && permData[1] == 2 && permData[2] == 3 && permData[3] == 1)
|
if (permData[0] == 0 && permData[1] == 2 && permData[2] == 3 && permData[3] == 1)
|
||||||
{
|
{
|
||||||
@ -1013,7 +1121,10 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
{
|
{
|
||||||
int axisId = (type == "Concat" ? 0 : layer.input_size() - 1);
|
int axisId = (type == "Concat" ? 0 : layer.input_size() - 1);
|
||||||
int axis = getConstBlob(layer, value_id, axisId).int_val().Get(0);
|
int axis = getConstBlob(layer, value_id, axisId).int_val().Get(0);
|
||||||
layerParams.set("axis", 0 <= axis && axis < 4 ? toNCHW(axis) : axis);
|
|
||||||
|
if (getDataLayout(name, data_layouts) == DATA_LAYOUT_NHWC)
|
||||||
|
axis = toNCHW(axis);
|
||||||
|
layerParams.set("axis", axis);
|
||||||
|
|
||||||
int id = dstNet.addLayer(name, "Concat", layerParams);
|
int id = dstNet.addLayer(name, "Concat", layerParams);
|
||||||
layer_id[name] = id;
|
layer_id[name] = id;
|
||||||
@ -1028,7 +1139,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
Pin inp = parsePin(layer.input(ii));
|
Pin inp = parsePin(layer.input(ii));
|
||||||
if (layer_id.find(inp.name) == layer_id.end())
|
if (layer_id.find(inp.name) == layer_id.end())
|
||||||
CV_Error(Error::StsError, "Input layer not found: " + inp.name);
|
CV_Error(Error::StsError, "Input layer not found: " + inp.name);
|
||||||
dstNet.connect(layer_id.at(inp.name), inp.blobIndex, id, ii - from);
|
connect(layer_id, dstNet, inp, id, ii - from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == "MaxPool")
|
else if (type == "MaxPool")
|
||||||
@ -1060,10 +1171,12 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
}
|
}
|
||||||
else if (type == "Placeholder")
|
else if (type == "Placeholder")
|
||||||
{
|
{
|
||||||
std::vector<String> netInputs(1);
|
if (!hasLayerAttr(layer, "dtype") ||
|
||||||
netInputs[0] = name;
|
getLayerAttr(layer, "dtype").type() != tensorflow::DT_BOOL) // If input is not a train/test flag.
|
||||||
layer_id[name] = 0;
|
{
|
||||||
dstNet.setInputsNames(netInputs);
|
netInputsNames.push_back(name);
|
||||||
|
layer_id[name] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (type == "Split") {
|
else if (type == "Split") {
|
||||||
// TODO: determining axis index remapping by input dimensions order of input blob
|
// TODO: determining axis index remapping by input dimensions order of input blob
|
||||||
@ -1094,7 +1207,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
CV_Assert(!begins.empty(), !sizes.empty(), begins.type() == CV_32SC1,
|
CV_Assert(!begins.empty(), !sizes.empty(), begins.type() == CV_32SC1,
|
||||||
sizes.type() == CV_32SC1);
|
sizes.type() == CV_32SC1);
|
||||||
|
|
||||||
if (begins.total() == 4 && data_layouts[name] == DATA_LAYOUT_NHWC)
|
if (begins.total() == 4 && getDataLayout(name, data_layouts) == DATA_LAYOUT_NHWC)
|
||||||
{
|
{
|
||||||
// Swap NHWC parameters' order to NCHW.
|
// Swap NHWC parameters' order to NCHW.
|
||||||
std::swap(*begins.ptr<int32_t>(0, 2), *begins.ptr<int32_t>(0, 3));
|
std::swap(*begins.ptr<int32_t>(0, 2), *begins.ptr<int32_t>(0, 3));
|
||||||
@ -1201,7 +1314,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
Pin inp = parsePin(layer.input(ii));
|
Pin inp = parsePin(layer.input(ii));
|
||||||
if (layer_id.find(inp.name) == layer_id.end())
|
if (layer_id.find(inp.name) == layer_id.end())
|
||||||
CV_Error(Error::StsError, "Input layer not found: " + inp.name);
|
CV_Error(Error::StsError, "Input layer not found: " + inp.name);
|
||||||
dstNet.connect(layer_id.at(inp.name), inp.blobIndex, id, ii);
|
connect(layer_id, dstNet, inp, id, ii);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1494,7 +1607,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
CV_Assert(reductionIndices.type() == CV_32SC1);
|
CV_Assert(reductionIndices.type() == CV_32SC1);
|
||||||
|
|
||||||
const int numAxes = reductionIndices.total();
|
const int numAxes = reductionIndices.total();
|
||||||
if (data_layouts[name] == DATA_LAYOUT_NHWC)
|
if (getDataLayout(name, data_layouts) == DATA_LAYOUT_NHWC)
|
||||||
for (int i = 0; i < numAxes; ++i)
|
for (int i = 0; i < numAxes; ++i)
|
||||||
reductionIndices.at<int>(i) = toNCHW(reductionIndices.at<int>(i));
|
reductionIndices.at<int>(i) = toNCHW(reductionIndices.at<int>(i));
|
||||||
|
|
||||||
@ -1641,6 +1754,27 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
connect(layer_id, dstNet, Pin(name), flattenId, 0);
|
connect(layer_id, dstNet, Pin(name), flattenId, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (type == "ClipByValue")
|
||||||
|
{
|
||||||
|
// op: "ClipByValue"
|
||||||
|
// input: "input"
|
||||||
|
// input: "mix"
|
||||||
|
// input: "max"
|
||||||
|
CV_Assert(layer.input_size() == 3);
|
||||||
|
|
||||||
|
Mat minValue = getTensorContent(getConstBlob(layer, value_id, 1));
|
||||||
|
Mat maxValue = getTensorContent(getConstBlob(layer, value_id, 2));
|
||||||
|
CV_Assert(minValue.total() == 1, minValue.type() == CV_32F,
|
||||||
|
maxValue.total() == 1, maxValue.type() == CV_32F);
|
||||||
|
|
||||||
|
layerParams.set("min_value", minValue.at<float>(0));
|
||||||
|
layerParams.set("max_value", maxValue.at<float>(0));
|
||||||
|
|
||||||
|
int id = dstNet.addLayer(name, "ReLU6", layerParams);
|
||||||
|
layer_id[name] = id;
|
||||||
|
|
||||||
|
connect(layer_id, dstNet, parsePin(layer.input(0)), id, 0);
|
||||||
|
}
|
||||||
else if (type == "Abs" || type == "Tanh" || type == "Sigmoid" ||
|
else if (type == "Abs" || type == "Tanh" || type == "Sigmoid" ||
|
||||||
type == "Relu" || type == "Elu" ||
|
type == "Relu" || type == "Elu" ||
|
||||||
type == "Identity" || type == "Relu6")
|
type == "Identity" || type == "Relu6")
|
||||||
@ -1698,6 +1832,7 @@ void TFImporter::populateNet(Net dstNet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dstNet.setInputsNames(netInputsNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -592,8 +592,8 @@ struct TorchImporter
|
|||||||
DictValue dimParam = scalarParams.get("size");
|
DictValue dimParam = scalarParams.get("size");
|
||||||
layerParams.set("dim", dimParam);
|
layerParams.set("dim", dimParam);
|
||||||
|
|
||||||
if (scalarParams.has("batchMode") && scalarParams.get<bool>("batchMode"))
|
int axis = (int)scalarParams.get<bool>("batchMode", true);
|
||||||
layerParams.set("axis", 1);
|
layerParams.set("axis", axis);
|
||||||
|
|
||||||
curModule->modules.push_back(newModule);
|
curModule->modules.push_back(newModule);
|
||||||
}
|
}
|
||||||
|
@ -182,11 +182,9 @@ TEST_P(DNNTestNetwork, MobileNet_SSD_Caffe)
|
|||||||
throw SkipTestException("");
|
throw SkipTestException("");
|
||||||
Mat sample = imread(findDataFile("dnn/street.png", false));
|
Mat sample = imread(findDataFile("dnn/street.png", false));
|
||||||
Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false);
|
Mat inp = blobFromImage(sample, 1.0f / 127.5, Size(300, 300), Scalar(127.5, 127.5, 127.5), false);
|
||||||
float l1 = (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) ? 0.0007 : 0.0;
|
float diffScores = (target == DNN_TARGET_OPENCL_FP16) ? 6e-3 : 0.0;
|
||||||
float lInf = (backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) ? 0.011 : 0.0;
|
|
||||||
|
|
||||||
processNet("dnn/MobileNetSSD_deploy.caffemodel", "dnn/MobileNetSSD_deploy.prototxt",
|
processNet("dnn/MobileNetSSD_deploy.caffemodel", "dnn/MobileNetSSD_deploy.prototxt",
|
||||||
inp, "detection_out", "", l1, lInf);
|
inp, "detection_out", "", diffScores);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(DNNTestNetwork, MobileNet_SSD_v1_TensorFlow)
|
TEST_P(DNNTestNetwork, MobileNet_SSD_v1_TensorFlow)
|
||||||
@ -256,7 +254,8 @@ TEST_P(DNNTestNetwork, OpenPose_pose_mpi_faster_4_stages)
|
|||||||
TEST_P(DNNTestNetwork, OpenFace)
|
TEST_P(DNNTestNetwork, OpenFace)
|
||||||
{
|
{
|
||||||
if (backend == DNN_BACKEND_HALIDE ||
|
if (backend == DNN_BACKEND_HALIDE ||
|
||||||
backend == DNN_BACKEND_INFERENCE_ENGINE && target != DNN_TARGET_CPU)
|
(backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_OPENCL_FP16) ||
|
||||||
|
(backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD))
|
||||||
throw SkipTestException("");
|
throw SkipTestException("");
|
||||||
processNet("dnn/openface_nn4.small2.v1.t7", "", Size(96, 96), "");
|
processNet("dnn/openface_nn4.small2.v1.t7", "", Size(96, 96), "");
|
||||||
}
|
}
|
||||||
@ -296,6 +295,20 @@ TEST_P(DNNTestNetwork, DenseNet_121)
|
|||||||
processNet("dnn/DenseNet_121.caffemodel", "dnn/DenseNet_121.prototxt", Size(224, 224), "", "caffe");
|
processNet("dnn/DenseNet_121.caffemodel", "dnn/DenseNet_121.prototxt", Size(224, 224), "", "caffe");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(DNNTestNetwork, FastNeuralStyle_eccv16)
|
||||||
|
{
|
||||||
|
if (backend == DNN_BACKEND_HALIDE ||
|
||||||
|
(backend == DNN_BACKEND_OPENCV && target == DNN_TARGET_OPENCL_FP16) ||
|
||||||
|
(backend == DNN_BACKEND_INFERENCE_ENGINE && target == DNN_TARGET_MYRIAD))
|
||||||
|
throw SkipTestException("");
|
||||||
|
Mat img = imread(findDataFile("dnn/googlenet_1.png", false));
|
||||||
|
Mat inp = blobFromImage(img, 1.0, Size(320, 240), Scalar(103.939, 116.779, 123.68), false, false);
|
||||||
|
// Output image has values in range [-143.526, 148.539].
|
||||||
|
float l1 = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.3 : 4e-5;
|
||||||
|
float lInf = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 7.0 : 2e-3;
|
||||||
|
processNet("dnn/fast_neural_style_eccv16_starry_night.t7", "", inp, "", "", l1, lInf);
|
||||||
|
}
|
||||||
|
|
||||||
const tuple<DNNBackend, DNNTarget> testCases[] = {
|
const tuple<DNNBackend, DNNTarget> testCases[] = {
|
||||||
#ifdef HAVE_HALIDE
|
#ifdef HAVE_HALIDE
|
||||||
tuple<DNNBackend, DNNTarget>(DNN_BACKEND_HALIDE, DNN_TARGET_CPU),
|
tuple<DNNBackend, DNNTarget>(DNN_BACKEND_HALIDE, DNN_TARGET_CPU),
|
||||||
|
@ -157,7 +157,8 @@ static inline bool checkMyriadTarget()
|
|||||||
net.addLayerToPrev("testLayer", "Identity", lp);
|
net.addLayerToPrev("testLayer", "Identity", lp);
|
||||||
net.setPreferableBackend(cv::dnn::DNN_BACKEND_INFERENCE_ENGINE);
|
net.setPreferableBackend(cv::dnn::DNN_BACKEND_INFERENCE_ENGINE);
|
||||||
net.setPreferableTarget(cv::dnn::DNN_TARGET_MYRIAD);
|
net.setPreferableTarget(cv::dnn::DNN_TARGET_MYRIAD);
|
||||||
net.setInput(cv::Mat::zeros(1, 1, CV_32FC1));
|
static int inpDims[] = {1, 2, 3, 4};
|
||||||
|
net.setInput(cv::Mat(4, &inpDims[0], CV_32FC1, cv::Scalar(0)));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
net.forward();
|
net.forward();
|
||||||
|
@ -135,8 +135,6 @@ TEST_P(Test_Darknet_nets, YoloVoc)
|
|||||||
{
|
{
|
||||||
int backendId = get<0>(GetParam());
|
int backendId = get<0>(GetParam());
|
||||||
int targetId = get<1>(GetParam());
|
int targetId = get<1>(GetParam());
|
||||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE && targetId == DNN_TARGET_MYRIAD)
|
|
||||||
throw SkipTestException("");
|
|
||||||
std::vector<cv::String> outNames(1, "detection_out");
|
std::vector<cv::String> outNames(1, "detection_out");
|
||||||
|
|
||||||
std::vector<int> classIds(3);
|
std::vector<int> classIds(3);
|
||||||
@ -145,7 +143,7 @@ TEST_P(Test_Darknet_nets, YoloVoc)
|
|||||||
classIds[0] = 6; confidences[0] = 0.750469f; boxes[0] = Rect2d(0.577374, 0.127391, 0.325575, 0.173418); // a car
|
classIds[0] = 6; confidences[0] = 0.750469f; boxes[0] = Rect2d(0.577374, 0.127391, 0.325575, 0.173418); // a car
|
||||||
classIds[1] = 1; confidences[1] = 0.780879f; boxes[1] = Rect2d(0.270762, 0.264102, 0.461713, 0.48131); // a bicycle
|
classIds[1] = 1; confidences[1] = 0.780879f; boxes[1] = Rect2d(0.270762, 0.264102, 0.461713, 0.48131); // a bicycle
|
||||||
classIds[2] = 11; confidences[2] = 0.901615f; boxes[2] = Rect2d(0.1386, 0.338509, 0.282737, 0.60028); // a dog
|
classIds[2] = 11; confidences[2] = 0.901615f; boxes[2] = Rect2d(0.1386, 0.338509, 0.282737, 0.60028); // a dog
|
||||||
double scoreDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 7e-3 : 8e-5;
|
double scoreDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 1e-2 : 8e-5;
|
||||||
double iouDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 0.013 : 3e-5;
|
double iouDiff = (targetId == DNN_TARGET_OPENCL_FP16 || targetId == DNN_TARGET_MYRIAD) ? 0.013 : 3e-5;
|
||||||
testDarknetModel("yolo-voc.cfg", "yolo-voc.weights", outNames,
|
testDarknetModel("yolo-voc.cfg", "yolo-voc.weights", outNames,
|
||||||
classIds, confidences, boxes, backendId, targetId, scoreDiff, iouDiff);
|
classIds, confidences, boxes, backendId, targetId, scoreDiff, iouDiff);
|
||||||
@ -230,4 +228,9 @@ TEST(Test_Darknet, upsample)
|
|||||||
testDarknetLayer("upsample");
|
testDarknetLayer("upsample");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Test_Darknet, avgpool_softmax)
|
||||||
|
{
|
||||||
|
testDarknetLayer("avgpool_softmax");
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
@ -201,6 +201,13 @@ TEST(Layer_Test_Reshape, Accuracy)
|
|||||||
testReshape(MatShape(inp, inp + 4), MatShape(out, out + 2), 0, -1,
|
testReshape(MatShape(inp, inp + 4), MatShape(out, out + 2), 0, -1,
|
||||||
MatShape(mask, mask + 2));
|
MatShape(mask, mask + 2));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
int inp[] = {1, 2, 3};
|
||||||
|
int out[] = {3, 1, 2};
|
||||||
|
int mask[] = {3, 1, 2};
|
||||||
|
testReshape(MatShape(inp, inp + 3), MatShape(out, out + 3), 0, -1,
|
||||||
|
MatShape(mask, mask + 3));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Layer_Test_BatchNorm, Accuracy)
|
TEST(Layer_Test_BatchNorm, Accuracy)
|
||||||
@ -925,6 +932,10 @@ TEST(Layer_Test_Convolution_DLDT, Accuracy)
|
|||||||
Mat out = net.forward();
|
Mat out = net.forward();
|
||||||
|
|
||||||
normAssert(outDefault, out);
|
normAssert(outDefault, out);
|
||||||
|
|
||||||
|
std::vector<int> outLayers = net.getUnconnectedOutLayers();
|
||||||
|
ASSERT_EQ(net.getLayer(outLayers[0])->name, "output_merge");
|
||||||
|
ASSERT_EQ(net.getLayer(outLayers[0])->type, "Concat");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Create a .prototxt file with the following network:
|
// 1. Create a .prototxt file with the following network:
|
||||||
@ -1137,11 +1148,96 @@ private:
|
|||||||
int outWidth, outHeight, zoomFactor;
|
int outWidth, outHeight, zoomFactor;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(Layer_Test_Interp, Accuracy)
|
TEST(Layer_Test_Interp_custom, Accuracy)
|
||||||
{
|
{
|
||||||
CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer);
|
CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer);
|
||||||
testLayerUsingCaffeModels("layer_interp", DNN_TARGET_CPU, false, false);
|
testLayerUsingCaffeModels("layer_interp", DNN_TARGET_CPU, false, false);
|
||||||
LayerFactory::unregisterLayer("Interp");
|
LayerFactory::unregisterLayer("Interp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Layer_Test_Interp, Accuracy)
|
||||||
|
{
|
||||||
|
testLayerUsingCaffeModels("layer_interp", DNN_TARGET_CPU, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Layer_Test_PoolingIndices, Accuracy)
|
||||||
|
{
|
||||||
|
Net net;
|
||||||
|
|
||||||
|
LayerParams lp;
|
||||||
|
lp.set("pool", "max");
|
||||||
|
lp.set("kernel_w", 2);
|
||||||
|
lp.set("kernel_h", 2);
|
||||||
|
lp.set("stride_w", 2);
|
||||||
|
lp.set("stride_h", 2);
|
||||||
|
lp.set("pad_w", 0);
|
||||||
|
lp.set("pad_h", 0);
|
||||||
|
lp.name = "testLayer.name"; // This test also checks that OpenCV lets use names with dots.
|
||||||
|
lp.type = "Pooling";
|
||||||
|
net.addLayerToPrev(lp.name, lp.type, lp);
|
||||||
|
|
||||||
|
Mat inp(10, 10, CV_8U);
|
||||||
|
randu(inp, 0, 255);
|
||||||
|
|
||||||
|
Mat maxValues(5, 5, CV_32F, Scalar(-1)), indices(5, 5, CV_32F, Scalar(-1));
|
||||||
|
for (int y = 0; y < 10; ++y)
|
||||||
|
{
|
||||||
|
int dstY = y / 2;
|
||||||
|
for (int x = 0; x < 10; ++x)
|
||||||
|
{
|
||||||
|
int dstX = x / 2;
|
||||||
|
uint8_t val = inp.at<uint8_t>(y, x);
|
||||||
|
if ((float)inp.at<uint8_t>(y, x) > maxValues.at<float>(dstY, dstX))
|
||||||
|
{
|
||||||
|
maxValues.at<float>(dstY, dstX) = val;
|
||||||
|
indices.at<float>(dstY, dstX) = y * 10 + x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
net.setPreferableBackend(DNN_BACKEND_OPENCV);
|
||||||
|
net.setInput(blobFromImage(inp));
|
||||||
|
|
||||||
|
std::vector<Mat> outputs;
|
||||||
|
net.forward(outputs, lp.name);
|
||||||
|
normAssert(maxValues, outputs[0].reshape(1, 5));
|
||||||
|
normAssert(indices, outputs[1].reshape(1, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<tuple<Vec4i, int> > Layer_Test_ShuffleChannel;
|
||||||
|
TEST_P(Layer_Test_ShuffleChannel, Accuracy)
|
||||||
|
{
|
||||||
|
Vec4i inpShapeVec = get<0>(GetParam());
|
||||||
|
int group = get<1>(GetParam());
|
||||||
|
ASSERT_EQ(inpShapeVec[1] % group, 0);
|
||||||
|
const int groupSize = inpShapeVec[1] / group;
|
||||||
|
|
||||||
|
Net net;
|
||||||
|
LayerParams lp;
|
||||||
|
lp.set("group", group);
|
||||||
|
lp.type = "ShuffleChannel";
|
||||||
|
lp.name = "testLayer";
|
||||||
|
net.addLayerToPrev(lp.name, lp.type, lp);
|
||||||
|
|
||||||
|
const int inpShape[] = {inpShapeVec[0], inpShapeVec[1], inpShapeVec[2], inpShapeVec[3]};
|
||||||
|
Mat inp(4, inpShape, CV_32F);
|
||||||
|
randu(inp, 0, 255);
|
||||||
|
|
||||||
|
net.setInput(inp);
|
||||||
|
Mat out = net.forward();
|
||||||
|
|
||||||
|
for (int n = 0; n < inpShapeVec[0]; ++n)
|
||||||
|
{
|
||||||
|
for (int c = 0; c < inpShapeVec[1]; ++c)
|
||||||
|
{
|
||||||
|
Mat outChannel = getPlane(out, n, c);
|
||||||
|
Mat inpChannel = getPlane(inp, n, groupSize * (c % group) + c / group);
|
||||||
|
normAssert(outChannel, inpChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_ShuffleChannel, Combine(
|
||||||
|
/*input shape*/ Values(Vec4i(1, 6, 5, 7), Vec4i(3, 12, 1, 4)),
|
||||||
|
/*group*/ Values(1, 2, 3, 6)
|
||||||
|
));
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
@ -127,6 +127,7 @@ TEST_P(Test_TensorFlow_layers, conv)
|
|||||||
runTensorFlowNet("atrous_conv2d_same", targetId);
|
runTensorFlowNet("atrous_conv2d_same", targetId);
|
||||||
runTensorFlowNet("depthwise_conv2d", targetId);
|
runTensorFlowNet("depthwise_conv2d", targetId);
|
||||||
runTensorFlowNet("keras_atrous_conv2d_same", targetId);
|
runTensorFlowNet("keras_atrous_conv2d_same", targetId);
|
||||||
|
runTensorFlowNet("conv_pool_nchw", targetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Test_TensorFlow_layers, padding)
|
TEST_P(Test_TensorFlow_layers, padding)
|
||||||
@ -142,9 +143,10 @@ TEST_P(Test_TensorFlow_layers, eltwise_add_mul)
|
|||||||
runTensorFlowNet("eltwise_add_mul", GetParam());
|
runTensorFlowNet("eltwise_add_mul", GetParam());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Test_TensorFlow_layers, pad_and_concat)
|
TEST_P(Test_TensorFlow_layers, concat)
|
||||||
{
|
{
|
||||||
runTensorFlowNet("pad_and_concat", GetParam());
|
runTensorFlowNet("pad_and_concat", GetParam());
|
||||||
|
runTensorFlowNet("concat_axis_1", GetParam());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Test_TensorFlow_layers, batch_norm)
|
TEST_P(Test_TensorFlow_layers, batch_norm)
|
||||||
@ -196,6 +198,7 @@ TEST_P(Test_TensorFlow_layers, reshape)
|
|||||||
{
|
{
|
||||||
int targetId = GetParam();
|
int targetId = GetParam();
|
||||||
runTensorFlowNet("shift_reshape_no_reorder", targetId);
|
runTensorFlowNet("shift_reshape_no_reorder", targetId);
|
||||||
|
runTensorFlowNet("reshape_no_reorder", targetId);
|
||||||
runTensorFlowNet("reshape_reduce", targetId);
|
runTensorFlowNet("reshape_reduce", targetId);
|
||||||
runTensorFlowNet("flatten", targetId, true);
|
runTensorFlowNet("flatten", targetId, true);
|
||||||
runTensorFlowNet("unfused_flatten", targetId);
|
runTensorFlowNet("unfused_flatten", targetId);
|
||||||
@ -415,6 +418,7 @@ TEST(Test_TensorFlow, softmax)
|
|||||||
TEST(Test_TensorFlow, relu6)
|
TEST(Test_TensorFlow, relu6)
|
||||||
{
|
{
|
||||||
runTensorFlowNet("keras_relu6");
|
runTensorFlowNet("keras_relu6");
|
||||||
|
runTensorFlowNet("keras_relu6", DNN_TARGET_CPU, /*hasText*/ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Test_TensorFlow, keras_mobilenet_head)
|
TEST(Test_TensorFlow, keras_mobilenet_head)
|
||||||
@ -439,4 +443,20 @@ TEST(Test_TensorFlow, resize_bilinear)
|
|||||||
runTensorFlowNet("resize_bilinear_factor");
|
runTensorFlowNet("resize_bilinear_factor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Test_TensorFlow, two_inputs)
|
||||||
|
{
|
||||||
|
Net net = readNet(path("two_inputs_net.pbtxt"));
|
||||||
|
net.setPreferableBackend(DNN_BACKEND_OPENCV);
|
||||||
|
|
||||||
|
Mat firstInput(2, 3, CV_32FC1), secondInput(2, 3, CV_32FC1);
|
||||||
|
randu(firstInput, -1, 1);
|
||||||
|
randu(secondInput, -1, 1);
|
||||||
|
|
||||||
|
net.setInput(firstInput, "first_input");
|
||||||
|
net.setInput(secondInput, "second_input");
|
||||||
|
Mat out = net.forward();
|
||||||
|
|
||||||
|
normAssert(out, firstInput + secondInput);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ static void runTorchNet(String prefix, int targetId = DNN_TARGET_CPU, String out
|
|||||||
if (outLayerName.empty())
|
if (outLayerName.empty())
|
||||||
outLayerName = net.getLayerNames().back();
|
outLayerName = net.getLayerNames().back();
|
||||||
|
|
||||||
net.setInput(inp, "0");
|
net.setInput(inp);
|
||||||
std::vector<Mat> outBlobs;
|
std::vector<Mat> outBlobs;
|
||||||
net.forward(outBlobs, outLayerName);
|
net.forward(outBlobs, outLayerName);
|
||||||
normAssert(outRef, outBlobs[0]);
|
normAssert(outRef, outBlobs[0]);
|
||||||
@ -296,6 +296,7 @@ TEST_P(Test_Torch_nets, FastNeuralStyle_accuracy)
|
|||||||
Mat inputBlob = blobFromImage(img, 1.0, Size(), Scalar(103.939, 116.779, 123.68), false);
|
Mat inputBlob = blobFromImage(img, 1.0, Size(), Scalar(103.939, 116.779, 123.68), false);
|
||||||
|
|
||||||
net.setInput(inputBlob);
|
net.setInput(inputBlob);
|
||||||
|
net.setPreferableBackend(DNN_BACKEND_OPENCV);
|
||||||
Mat out = net.forward();
|
Mat out = net.forward();
|
||||||
|
|
||||||
// Deprocessing.
|
// Deprocessing.
|
||||||
|
@ -175,8 +175,6 @@ bool SunRasterDecoder::readData( Mat& img )
|
|||||||
|
|
||||||
AutoBuffer<uchar> _src(src_pitch + 32);
|
AutoBuffer<uchar> _src(src_pitch + 32);
|
||||||
uchar* src = _src;
|
uchar* src = _src;
|
||||||
AutoBuffer<uchar> _bgr(m_width*3 + 32);
|
|
||||||
uchar* bgr = _bgr;
|
|
||||||
|
|
||||||
if( !color && m_maptype == RMT_EQUAL_RGB )
|
if( !color && m_maptype == RMT_EQUAL_RGB )
|
||||||
CvtPaletteToGray( m_palette, gray_palette, 1 << m_bpp );
|
CvtPaletteToGray( m_palette, gray_palette, 1 << m_bpp );
|
||||||
@ -340,16 +338,18 @@ bad_decoding_end:
|
|||||||
case 24:
|
case 24:
|
||||||
for( y = 0; y < m_height; y++, data += step )
|
for( y = 0; y < m_height; y++, data += step )
|
||||||
{
|
{
|
||||||
m_strm.getBytes( color ? data : bgr, src_pitch );
|
m_strm.getBytes(src, src_pitch );
|
||||||
|
|
||||||
if( color )
|
if( color )
|
||||||
{
|
{
|
||||||
if( m_type == RAS_FORMAT_RGB )
|
if( m_type == RAS_FORMAT_RGB )
|
||||||
icvCvt_RGB2BGR_8u_C3R( data, 0, data, 0, cvSize(m_width,1) );
|
icvCvt_RGB2BGR_8u_C3R(src, 0, data, 0, cvSize(m_width,1) );
|
||||||
|
else
|
||||||
|
memcpy(data, src, std::min(step, (size_t)src_pitch));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
icvCvt_BGR2Gray_8u_C3C1R( bgr, 0, data, 0, cvSize(m_width,1),
|
icvCvt_BGR2Gray_8u_C3C1R(src, 0, data, 0, cvSize(m_width,1),
|
||||||
m_type == RAS_FORMAT_RGB ? 2 : 0 );
|
m_type == RAS_FORMAT_RGB ? 2 : 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -670,6 +670,14 @@ public:
|
|||||||
void groupRectangles(std::vector<cv::Rect>& rectList, std::vector<double>& weights, int groupThreshold, double eps) const;
|
void groupRectangles(std::vector<cv::Rect>& rectList, std::vector<double>& weights, int groupThreshold, double eps) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @brief Detect QR code in image and return minimum area of quadrangle that describes QR code.
|
||||||
|
@param in Matrix of the type CV_8UC1 containing an image where QR code are detected.
|
||||||
|
@param points Output vector of vertices of a quadrangle of minimal area that describes QR code.
|
||||||
|
@param eps_x Epsilon neighborhood, which allows you to determine the horizontal pattern of the scheme 1:1:3:1:1 according to QR code standard.
|
||||||
|
@param eps_y Epsilon neighborhood, which allows you to determine the vertical pattern of the scheme 1:1:3:1:1 according to QR code standard.
|
||||||
|
*/
|
||||||
|
CV_EXPORTS bool detectQRCode(InputArray in, std::vector<Point> &points, double eps_x = 0.2, double eps_y = 0.1);
|
||||||
|
|
||||||
//! @} objdetect
|
//! @} objdetect
|
||||||
|
|
||||||
}
|
}
|
||||||
|
775
modules/objdetect/src/qrcode.cpp
Normal file
775
modules/objdetect/src/qrcode.cpp
Normal file
@ -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.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2018, Intel Corporation, all rights reserved.
|
||||||
|
// Third party copyrights are property of their respective owners.
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include "opencv2/objdetect.hpp"
|
||||||
|
// #include "opencv2/calib3d.hpp"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace cv
|
||||||
|
{
|
||||||
|
class QRDecode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void init(Mat src, double eps_vertical_ = 0.19, double eps_horizontal_ = 0.09);
|
||||||
|
void binarization();
|
||||||
|
bool localization();
|
||||||
|
bool transformation();
|
||||||
|
Mat getBinBarcode() { return bin_barcode; }
|
||||||
|
Mat getLocalizationBarcode() { return local_barcode; }
|
||||||
|
Mat getTransformationBarcode() { return transform_barcode; }
|
||||||
|
std::vector<Point> getTransformationPoints() { return transformation_points; }
|
||||||
|
Mat getStraightBarcode() { return straight_barcode; }
|
||||||
|
protected:
|
||||||
|
std::vector<Vec3d> searchVerticalLines();
|
||||||
|
std::vector<Vec3d> separateHorizontalLines(std::vector<Vec3d> list_lines);
|
||||||
|
std::vector<Vec3d> pointClustering(std::vector<Vec3d> list_lines);
|
||||||
|
void fixationPoints(std::vector<Point> &local_point, std::vector<double> &local_len);
|
||||||
|
Point getTransformationPoint(Point left, Point center, double cos_angle_rotation,
|
||||||
|
bool right_rotate = true);
|
||||||
|
Point intersectionLines(Point a1, Point a2, Point b1, Point b2);
|
||||||
|
std::vector<Point> getQuadrilateral(std::vector<Point> angle_list);
|
||||||
|
double getQuadrilateralArea(Point a, Point b, Point c, Point d);
|
||||||
|
double getCosVectors(Point a, Point b, Point c);
|
||||||
|
|
||||||
|
Mat barcode, bin_barcode, local_barcode, transform_barcode, straight_barcode;
|
||||||
|
std::vector<Point> localization_points, transformation_points;
|
||||||
|
std::vector<double> localization_length;
|
||||||
|
double experimental_area;
|
||||||
|
|
||||||
|
double eps_vertical, eps_horizontal;
|
||||||
|
std::vector<Vec3d> result;
|
||||||
|
std::vector<double> test_lines;
|
||||||
|
uint8_t next_pixel, future_pixel;
|
||||||
|
double length, weight;
|
||||||
|
};
|
||||||
|
|
||||||
|
void QRDecode::init(Mat src, double eps_vertical_, double eps_horizontal_)
|
||||||
|
{
|
||||||
|
barcode = src;
|
||||||
|
eps_vertical = eps_vertical_;
|
||||||
|
eps_horizontal = eps_horizontal_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRDecode::binarization()
|
||||||
|
{
|
||||||
|
Mat filter_barcode;
|
||||||
|
GaussianBlur(barcode, filter_barcode, Size(3, 3), 0);
|
||||||
|
threshold(filter_barcode, bin_barcode, 0, 255, THRESH_BINARY + THRESH_OTSU);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QRDecode::localization()
|
||||||
|
{
|
||||||
|
cvtColor(bin_barcode, local_barcode, COLOR_GRAY2RGB);
|
||||||
|
Point begin, end;
|
||||||
|
|
||||||
|
std::vector<Vec3d> list_lines_x = searchVerticalLines();
|
||||||
|
std::vector<Vec3d> list_lines_y = separateHorizontalLines(list_lines_x);
|
||||||
|
std::vector<Vec3d> result_point = pointClustering(list_lines_y);
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
localization_points.push_back(
|
||||||
|
Point(static_cast<int>(result_point[i][0]),
|
||||||
|
static_cast<int>(result_point[i][1] + result_point[i][2])));
|
||||||
|
localization_length.push_back(result_point[i][2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixationPoints(localization_points, localization_length);
|
||||||
|
|
||||||
|
|
||||||
|
if (localization_points.size() != 3) { return false; }
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vec3d> QRDecode::searchVerticalLines()
|
||||||
|
{
|
||||||
|
result.clear();
|
||||||
|
int temp_length = 0;
|
||||||
|
|
||||||
|
for (int x = 0; x < bin_barcode.rows; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < bin_barcode.cols; y++)
|
||||||
|
{
|
||||||
|
if (bin_barcode.at<uint8_t>(x, y) > 0) { continue; }
|
||||||
|
|
||||||
|
// --------------- Search vertical lines --------------- //
|
||||||
|
|
||||||
|
test_lines.clear();
|
||||||
|
future_pixel = 255;
|
||||||
|
|
||||||
|
for (int i = x; i < bin_barcode.rows - 1; i++)
|
||||||
|
{
|
||||||
|
next_pixel = bin_barcode.at<uint8_t>(i + 1, y);
|
||||||
|
temp_length++;
|
||||||
|
if (next_pixel == future_pixel)
|
||||||
|
{
|
||||||
|
future_pixel = 255 - future_pixel;
|
||||||
|
test_lines.push_back(temp_length);
|
||||||
|
temp_length = 0;
|
||||||
|
if (test_lines.size() == 5) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------- Compute vertical lines --------------- //
|
||||||
|
|
||||||
|
if (test_lines.size() == 5)
|
||||||
|
{
|
||||||
|
length = 0.0; weight = 0.0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < test_lines.size(); i++) { length += test_lines[i]; }
|
||||||
|
|
||||||
|
for (size_t i = 0; i < test_lines.size(); i++)
|
||||||
|
{
|
||||||
|
if (i == 2) { weight += abs((test_lines[i] / length) - 3.0/7.0); }
|
||||||
|
else { weight += abs((test_lines[i] / length) - 1.0/7.0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weight < eps_vertical)
|
||||||
|
{
|
||||||
|
Vec3d line;
|
||||||
|
line[0] = x; line[1] = y, line[2] = length;
|
||||||
|
result.push_back(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vec3d> QRDecode::separateHorizontalLines(std::vector<Vec3d> list_lines)
|
||||||
|
{
|
||||||
|
result.clear();
|
||||||
|
int temp_length = 0;
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
for (size_t pnt = 0; pnt < list_lines.size(); pnt++)
|
||||||
|
{
|
||||||
|
x = static_cast<int>(list_lines[pnt][0] + list_lines[pnt][2] / 2);
|
||||||
|
y = static_cast<int>(list_lines[pnt][1]);
|
||||||
|
|
||||||
|
// --------------- Search horizontal up-lines --------------- //
|
||||||
|
test_lines.clear();
|
||||||
|
future_pixel = 255;
|
||||||
|
|
||||||
|
for (int j = y; j < bin_barcode.cols - 1; j++)
|
||||||
|
{
|
||||||
|
next_pixel = bin_barcode.at<uint8_t>(x, j + 1);
|
||||||
|
temp_length++;
|
||||||
|
if (next_pixel == future_pixel)
|
||||||
|
{
|
||||||
|
future_pixel = 255 - future_pixel;
|
||||||
|
test_lines.push_back(temp_length);
|
||||||
|
temp_length = 0;
|
||||||
|
if (test_lines.size() == 3) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------- Search horizontal down-lines --------------- //
|
||||||
|
future_pixel = 255;
|
||||||
|
|
||||||
|
for (int j = y; j >= 1; j--)
|
||||||
|
{
|
||||||
|
next_pixel = bin_barcode.at<uint8_t>(x, j - 1);
|
||||||
|
temp_length++;
|
||||||
|
if (next_pixel == future_pixel)
|
||||||
|
{
|
||||||
|
future_pixel = 255 - future_pixel;
|
||||||
|
test_lines.push_back(temp_length);
|
||||||
|
temp_length = 0;
|
||||||
|
if (test_lines.size() == 6) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------- Compute horizontal lines --------------- //
|
||||||
|
|
||||||
|
if (test_lines.size() == 6)
|
||||||
|
{
|
||||||
|
length = 0.0; weight = 0.0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < test_lines.size(); i++) { length += test_lines[i]; }
|
||||||
|
|
||||||
|
for (size_t i = 0; i < test_lines.size(); i++)
|
||||||
|
{
|
||||||
|
if (i % 3 == 0) { weight += abs((test_lines[i] / length) - 3.0/14.0); }
|
||||||
|
else { weight += abs((test_lines[i] / length) - 1.0/ 7.0); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(weight < eps_horizontal)
|
||||||
|
{
|
||||||
|
result.push_back(list_lines[pnt]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vec3d> QRDecode::pointClustering(std::vector<Vec3d> list_lines)
|
||||||
|
{
|
||||||
|
std::vector<Vec3d> centers;
|
||||||
|
std::vector<Point> clusters[3];
|
||||||
|
double weight_clusters[3] = {0.0, 0.0, 0.0};
|
||||||
|
Point basis[3], temp_pnt;
|
||||||
|
double temp_norm = 0.0, temp_compute_norm, distance[3];
|
||||||
|
|
||||||
|
basis[0] = Point(static_cast<int>(list_lines[0][1]), static_cast<int>(list_lines[0][0]));
|
||||||
|
for (size_t i = 1; i < list_lines.size(); i++)
|
||||||
|
{
|
||||||
|
temp_pnt = Point(static_cast<int>(list_lines[i][1]), static_cast<int>(list_lines[i][0]));
|
||||||
|
temp_compute_norm = norm(basis[0] - temp_pnt);
|
||||||
|
if (temp_norm < temp_compute_norm)
|
||||||
|
{
|
||||||
|
basis[1] = temp_pnt;
|
||||||
|
temp_norm = temp_compute_norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 1; i < list_lines.size(); i++)
|
||||||
|
{
|
||||||
|
temp_pnt = Point(static_cast<int>(list_lines[i][1]), static_cast<int>(list_lines[i][0]));
|
||||||
|
temp_compute_norm = norm(basis[0] - temp_pnt) + norm(basis[1] - temp_pnt);
|
||||||
|
if (temp_norm < temp_compute_norm)
|
||||||
|
{
|
||||||
|
basis[2] = temp_pnt;
|
||||||
|
temp_norm = temp_compute_norm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < list_lines.size(); i++)
|
||||||
|
{
|
||||||
|
temp_pnt = Point(static_cast<int>(list_lines[i][1]), static_cast<int>(list_lines[i][0]));
|
||||||
|
distance[0] = norm(basis[0] - temp_pnt);
|
||||||
|
distance[1] = norm(basis[1] - temp_pnt);
|
||||||
|
distance[2] = norm(basis[2] - temp_pnt);
|
||||||
|
if (distance[0] < distance[1] && distance[0] < distance[2])
|
||||||
|
{
|
||||||
|
clusters[0].push_back(temp_pnt);
|
||||||
|
weight_clusters[0] += list_lines[i][2];
|
||||||
|
}
|
||||||
|
else if (distance[1] < distance[0] && distance[1] < distance[2])
|
||||||
|
{
|
||||||
|
clusters[1].push_back(temp_pnt);
|
||||||
|
weight_clusters[1] += list_lines[i][2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clusters[2].push_back(temp_pnt);
|
||||||
|
weight_clusters[2] += list_lines[i][2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
basis[i] = Point(0, 0);
|
||||||
|
for (size_t j = 0; j < clusters[i].size(); j++) { basis[i] += clusters[i][j]; }
|
||||||
|
basis[i] = basis[i] / static_cast<int>(clusters[i].size());
|
||||||
|
weight = weight_clusters[i] / (2 * clusters[i].size());
|
||||||
|
centers.push_back(Vec3d(basis[i].x, basis[i].y, weight));
|
||||||
|
}
|
||||||
|
|
||||||
|
return centers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRDecode::fixationPoints(std::vector<Point> &local_point, std::vector<double> &local_len)
|
||||||
|
{
|
||||||
|
double cos_angles[3], norm_triangl[3];
|
||||||
|
|
||||||
|
norm_triangl[0] = norm(local_point[1] - local_point[2]);
|
||||||
|
norm_triangl[1] = norm(local_point[0] - local_point[2]);
|
||||||
|
norm_triangl[2] = norm(local_point[1] - local_point[0]);
|
||||||
|
|
||||||
|
cos_angles[0] = (pow(norm_triangl[1], 2) + pow(norm_triangl[2], 2) - pow(norm_triangl[0], 2))
|
||||||
|
/ (2 * norm_triangl[1] * norm_triangl[2]);
|
||||||
|
cos_angles[1] = (pow(norm_triangl[0], 2) + pow(norm_triangl[2], 2) - pow(norm_triangl[1], 2))
|
||||||
|
/ (2 * norm_triangl[0] * norm_triangl[2]);
|
||||||
|
cos_angles[2] = (pow(norm_triangl[0], 2) + pow(norm_triangl[1], 2) - pow(norm_triangl[2], 2))
|
||||||
|
/ (2 * norm_triangl[0] * norm_triangl[1]);
|
||||||
|
|
||||||
|
int i_min_cos =
|
||||||
|
(cos_angles[0] < cos_angles[1] && cos_angles[0] < cos_angles[2]) ? 0 :
|
||||||
|
(cos_angles[1] < cos_angles[0] && cos_angles[1] < cos_angles[2]) ? 1 : 2;
|
||||||
|
|
||||||
|
Point temp_pnt;
|
||||||
|
double tmp_len;
|
||||||
|
temp_pnt = local_point[0];
|
||||||
|
tmp_len = local_len[0];
|
||||||
|
local_point[0] = local_point[i_min_cos];
|
||||||
|
local_len[0] = local_len[i_min_cos];
|
||||||
|
local_point[i_min_cos] = temp_pnt;
|
||||||
|
local_len[i_min_cos] = tmp_len;
|
||||||
|
|
||||||
|
Mat vector_mult(Size(3, 3), CV_32FC1);
|
||||||
|
vector_mult.at<float>(0, 0) = 1;
|
||||||
|
vector_mult.at<float>(1, 0) = 1;
|
||||||
|
vector_mult.at<float>(2, 0) = 1;
|
||||||
|
vector_mult.at<float>(0, 1) = static_cast<float>((local_point[1] - local_point[0]).x);
|
||||||
|
vector_mult.at<float>(1, 1) = static_cast<float>((local_point[1] - local_point[0]).y);
|
||||||
|
vector_mult.at<float>(0, 2) = static_cast<float>((local_point[2] - local_point[0]).x);
|
||||||
|
vector_mult.at<float>(1, 2) = static_cast<float>((local_point[2] - local_point[0]).y);
|
||||||
|
double res_vect_mult = determinant(vector_mult);
|
||||||
|
if (res_vect_mult < 0)
|
||||||
|
{
|
||||||
|
temp_pnt = local_point[1];
|
||||||
|
tmp_len = local_len[1];
|
||||||
|
local_point[1] = local_point[2];
|
||||||
|
local_len[1] = local_len[2];
|
||||||
|
local_point[2] = temp_pnt;
|
||||||
|
local_len[2] = tmp_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QRDecode::transformation()
|
||||||
|
{
|
||||||
|
cvtColor(bin_barcode, transform_barcode, COLOR_GRAY2RGB);
|
||||||
|
if (localization_points.size() != 3) { return false; }
|
||||||
|
|
||||||
|
Point red = localization_points[0];
|
||||||
|
Point green = localization_points[1];
|
||||||
|
Point blue = localization_points[2];
|
||||||
|
Point adj_b_r_pnt, adj_r_b_pnt, adj_g_r_pnt, adj_r_g_pnt;
|
||||||
|
Point line_r_b_pnt, line_r_g_pnt, norm_r_b_pnt, norm_r_g_pnt;
|
||||||
|
adj_b_r_pnt = getTransformationPoint(blue, red, -1);
|
||||||
|
adj_r_b_pnt = getTransformationPoint(red, blue, -1);
|
||||||
|
adj_g_r_pnt = getTransformationPoint(green, red, -1);
|
||||||
|
adj_r_g_pnt = getTransformationPoint(red, green, -1);
|
||||||
|
line_r_b_pnt = getTransformationPoint(red, blue, -0.91);
|
||||||
|
line_r_g_pnt = getTransformationPoint(red, green, -0.91);
|
||||||
|
norm_r_b_pnt = getTransformationPoint(red, blue, 0.0, true);
|
||||||
|
norm_r_g_pnt = getTransformationPoint(red, green, 0.0, false);
|
||||||
|
|
||||||
|
transformation_points.push_back(intersectionLines(
|
||||||
|
adj_r_g_pnt, line_r_g_pnt, adj_r_b_pnt, line_r_b_pnt));
|
||||||
|
transformation_points.push_back(intersectionLines(
|
||||||
|
adj_b_r_pnt, norm_r_g_pnt, adj_r_g_pnt, line_r_g_pnt));
|
||||||
|
transformation_points.push_back(intersectionLines(
|
||||||
|
norm_r_b_pnt, adj_g_r_pnt, adj_b_r_pnt, norm_r_g_pnt));
|
||||||
|
transformation_points.push_back(intersectionLines(
|
||||||
|
norm_r_b_pnt, adj_g_r_pnt, adj_r_b_pnt, line_r_b_pnt));
|
||||||
|
|
||||||
|
experimental_area = getQuadrilateralArea(transformation_points[0],
|
||||||
|
transformation_points[1],
|
||||||
|
transformation_points[2],
|
||||||
|
transformation_points[3]);
|
||||||
|
std::vector<Point> quadrilateral = getQuadrilateral(transformation_points);
|
||||||
|
transformation_points = quadrilateral;
|
||||||
|
|
||||||
|
int max_length_norm = -1;
|
||||||
|
size_t transform_size = transformation_points.size();
|
||||||
|
for (size_t i = 0; i < transform_size; i++)
|
||||||
|
{
|
||||||
|
int len_norm = static_cast<int>(norm(transformation_points[i % transform_size] -
|
||||||
|
transformation_points[(i + 1) % transform_size]));
|
||||||
|
if (max_length_norm < len_norm) { max_length_norm = len_norm; }
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Point> perspective_points;
|
||||||
|
perspective_points.push_back(Point(0, 0));
|
||||||
|
perspective_points.push_back(Point(0, max_length_norm));
|
||||||
|
perspective_points.push_back(Point(max_length_norm, max_length_norm));
|
||||||
|
perspective_points.push_back(Point(max_length_norm, 0));
|
||||||
|
|
||||||
|
// warpPerspective(bin_barcode, straight_barcode,
|
||||||
|
// findHomography(transformation_points, perspective_points),
|
||||||
|
// Size(max_length_norm, max_length_norm));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point QRDecode::getTransformationPoint(Point left, Point center, double cos_angle_rotation,
|
||||||
|
bool right_rotate)
|
||||||
|
{
|
||||||
|
Point temp_pnt, prev_pnt(0, 0), next_pnt, start_pnt(center);
|
||||||
|
double temp_delta, min_delta;
|
||||||
|
int steps = 0;
|
||||||
|
|
||||||
|
future_pixel = 255;
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
min_delta = std::numeric_limits<double>::max();
|
||||||
|
for (int i = -1; i < 2; i++)
|
||||||
|
{
|
||||||
|
for (int j = -1; j < 2; j++)
|
||||||
|
{
|
||||||
|
if (i == 0 && j == 0) { continue; }
|
||||||
|
temp_pnt = Point(start_pnt.x + i, start_pnt.y + j);
|
||||||
|
temp_delta = abs(getCosVectors(left, center, temp_pnt) - cos_angle_rotation);
|
||||||
|
if (temp_delta < min_delta && prev_pnt != temp_pnt)
|
||||||
|
{
|
||||||
|
next_pnt = temp_pnt;
|
||||||
|
min_delta = temp_delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_pnt = start_pnt;
|
||||||
|
start_pnt = next_pnt;
|
||||||
|
next_pixel = bin_barcode.at<uint8_t>(start_pnt.y, start_pnt.x);
|
||||||
|
if (next_pixel == future_pixel)
|
||||||
|
{
|
||||||
|
future_pixel = 255 - future_pixel;
|
||||||
|
steps++;
|
||||||
|
if (steps == 3) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cos_angle_rotation == 0.0)
|
||||||
|
{
|
||||||
|
Mat vector_mult(Size(3, 3), CV_32FC1);
|
||||||
|
vector_mult.at<float>(0, 0) = 1;
|
||||||
|
vector_mult.at<float>(1, 0) = 1;
|
||||||
|
vector_mult.at<float>(2, 0) = 1;
|
||||||
|
vector_mult.at<float>(0, 1) = static_cast<float>((left - center).x);
|
||||||
|
vector_mult.at<float>(1, 1) = static_cast<float>((left - center).y);
|
||||||
|
vector_mult.at<float>(0, 2) = static_cast<float>((left - start_pnt).x);
|
||||||
|
vector_mult.at<float>(1, 2) = static_cast<float>((left - start_pnt).y);
|
||||||
|
double res_vect_mult = determinant(vector_mult);
|
||||||
|
if (( right_rotate && res_vect_mult < 0) ||
|
||||||
|
(!right_rotate && res_vect_mult > 0))
|
||||||
|
{
|
||||||
|
start_pnt = getTransformationPoint(start_pnt, center, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start_pnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point QRDecode::intersectionLines(Point a1, Point a2, Point b1, Point b2)
|
||||||
|
{
|
||||||
|
Point result_square_angle(
|
||||||
|
static_cast<int>(
|
||||||
|
static_cast<double>
|
||||||
|
((a1.x * a2.y - a1.y * a2.x) * (b1.x - b2.x) -
|
||||||
|
(b1.x * b2.y - b1.y * b2.x) * (a1.x - a2.x)) /
|
||||||
|
((a1.x - a2.x) * (b1.y - b2.y) -
|
||||||
|
(a1.y - a2.y) * (b1.x - b2.x))),
|
||||||
|
static_cast<int>(
|
||||||
|
static_cast<double>
|
||||||
|
((a1.x * a2.y - a1.y * a2.x) * (b1.y - b2.y) -
|
||||||
|
(b1.x * b2.y - b1.y * b2.x) * (a1.y - a2.y)) /
|
||||||
|
((a1.x - a2.x) * (b1.y - b2.y) -
|
||||||
|
(a1.y - a2.y) * (b1.x - b2.x)))
|
||||||
|
);
|
||||||
|
return result_square_angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Point> QRDecode::getQuadrilateral(std::vector<Point> angle_list)
|
||||||
|
{
|
||||||
|
size_t angle_size = angle_list.size();
|
||||||
|
uint8_t value, mask_value;
|
||||||
|
Mat mask(bin_barcode.rows + 2, bin_barcode.cols + 2, CV_8UC1);
|
||||||
|
for (size_t i = 0; i < angle_size; i++)
|
||||||
|
{
|
||||||
|
LineIterator line_iter(bin_barcode, angle_list[ i % angle_size],
|
||||||
|
angle_list[(i + 1) % angle_size]);
|
||||||
|
for(int j = 0; j < line_iter.count; j++, ++line_iter)
|
||||||
|
{
|
||||||
|
value = bin_barcode.at<uint8_t>(line_iter.pos());
|
||||||
|
mask_value = mask.at<uint8_t>(line_iter.pos() + Point(1, 1));
|
||||||
|
if (value == 0 && mask_value == 0)
|
||||||
|
{
|
||||||
|
floodFill(bin_barcode, mask, line_iter.pos(), 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<Point> locations;
|
||||||
|
Mat mask_roi = mask(Range(1, bin_barcode.rows - 1),
|
||||||
|
Range(1, bin_barcode.cols - 1));
|
||||||
|
|
||||||
|
cv::findNonZero(mask_roi, locations);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < angle_list.size(); i++)
|
||||||
|
{
|
||||||
|
locations.push_back(angle_list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::vector<Point> > hull(1), approx_hull(1);
|
||||||
|
convexHull(Mat(locations), hull[0]);
|
||||||
|
int hull_size = static_cast<int>(hull[0].size());
|
||||||
|
|
||||||
|
Point min_pnt;
|
||||||
|
|
||||||
|
std::vector<Point> min_abc;
|
||||||
|
double min_abs_cos_abc, abs_cos_abc;
|
||||||
|
for (int count = 0; count < 4; count++)
|
||||||
|
{
|
||||||
|
min_abs_cos_abc = std::numeric_limits<double>::max();
|
||||||
|
for (int i = 0; i < hull_size; i++)
|
||||||
|
{
|
||||||
|
Point a = hull[0][ i % hull_size];
|
||||||
|
Point b = hull[0][(i + 1) % hull_size];
|
||||||
|
Point c = hull[0][(i + 2) % hull_size];
|
||||||
|
abs_cos_abc = abs(getCosVectors(a, b, c));
|
||||||
|
|
||||||
|
bool flag_detect = true;
|
||||||
|
for (size_t j = 0; j < min_abc.size(); j++)
|
||||||
|
{
|
||||||
|
if (min_abc[j] == b) { flag_detect = false; break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag_detect && (abs_cos_abc < min_abs_cos_abc))
|
||||||
|
{
|
||||||
|
min_pnt = b;
|
||||||
|
min_abs_cos_abc = abs_cos_abc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
min_abc.push_back(min_pnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int min_abc_size = static_cast<int>(min_abc.size());
|
||||||
|
std::vector<int> index_min_abc(min_abc_size);
|
||||||
|
for (int i = 0; i < min_abc_size; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < hull_size; j++)
|
||||||
|
{
|
||||||
|
if (hull[0][j] == min_abc[i]) { index_min_abc[i] = j; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Point> result_hull_point(angle_size);
|
||||||
|
double min_norm, temp_norm;
|
||||||
|
for (size_t i = 0; i < angle_size; i++)
|
||||||
|
{
|
||||||
|
min_norm = std::numeric_limits<double>::max();
|
||||||
|
Point closest_pnt;
|
||||||
|
for (int j = 0; j < min_abc_size; j++)
|
||||||
|
{
|
||||||
|
if (min_norm > norm(hull[0][index_min_abc[j]] - angle_list[i]))
|
||||||
|
{
|
||||||
|
min_norm = norm(hull[0][index_min_abc[j]] - angle_list[i]);
|
||||||
|
closest_pnt = hull[0][index_min_abc[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result_hull_point[i] = closest_pnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_line[2] = {0, 0}, finish_line[2] = {0, 0}, unstable_pnt = 0;
|
||||||
|
for (int i = 0; i < hull_size; i++)
|
||||||
|
{
|
||||||
|
if (result_hull_point[3] == hull[0][i]) { start_line[0] = i; }
|
||||||
|
if (result_hull_point[2] == hull[0][i]) { finish_line[0] = start_line[1] = i; }
|
||||||
|
if (result_hull_point[1] == hull[0][i]) { finish_line[1] = i; }
|
||||||
|
if (result_hull_point[0] == hull[0][i]) { unstable_pnt = i; }
|
||||||
|
}
|
||||||
|
|
||||||
|
int index_hull, extra_index_hull, next_index_hull, extra_next_index_hull, count_points;
|
||||||
|
Point result_side_begin[4], result_side_end[4];
|
||||||
|
|
||||||
|
min_norm = std::numeric_limits<double>::max();
|
||||||
|
index_hull = start_line[0];
|
||||||
|
count_points = abs(start_line[0] - finish_line[0]);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (count_points > hull_size / 2) { next_index_hull = index_hull + 1; }
|
||||||
|
else { next_index_hull = index_hull - 1; }
|
||||||
|
|
||||||
|
if (next_index_hull == hull_size) { next_index_hull = 0; }
|
||||||
|
if (next_index_hull == -1) { next_index_hull = hull_size - 1; }
|
||||||
|
|
||||||
|
Point angle_closest_pnt = norm(hull[0][index_hull] - angle_list[2]) >
|
||||||
|
norm(hull[0][index_hull] - angle_list[3]) ? angle_list[3] : angle_list[2];
|
||||||
|
|
||||||
|
Point intrsc_line_hull =
|
||||||
|
intersectionLines(hull[0][index_hull], hull[0][next_index_hull],
|
||||||
|
angle_list[2], angle_list[3]);
|
||||||
|
temp_norm = getCosVectors(hull[0][index_hull], intrsc_line_hull, angle_closest_pnt);
|
||||||
|
if (min_norm > temp_norm &&
|
||||||
|
norm(hull[0][index_hull] - hull[0][next_index_hull]) >
|
||||||
|
norm(angle_list[2] - angle_list[3]) / 10)
|
||||||
|
{
|
||||||
|
min_norm = temp_norm;
|
||||||
|
result_side_begin[0] = hull[0][index_hull];
|
||||||
|
result_side_end[0] = hull[0][next_index_hull];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
index_hull = next_index_hull;
|
||||||
|
}
|
||||||
|
while(index_hull != finish_line[0]);
|
||||||
|
|
||||||
|
if (min_norm == std::numeric_limits<double>::max())
|
||||||
|
{
|
||||||
|
result_side_begin[0] = angle_list[2];
|
||||||
|
result_side_end[0] = angle_list[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
min_norm = std::numeric_limits<double>::max();
|
||||||
|
index_hull = start_line[1];
|
||||||
|
count_points = abs(start_line[1] - finish_line[1]);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (count_points > hull_size / 2) { next_index_hull = index_hull + 1; }
|
||||||
|
else { next_index_hull = index_hull - 1; }
|
||||||
|
|
||||||
|
if (next_index_hull == hull_size) { next_index_hull = 0; }
|
||||||
|
if (next_index_hull == -1) { next_index_hull = hull_size - 1; }
|
||||||
|
|
||||||
|
Point angle_closest_pnt = norm(hull[0][index_hull] - angle_list[1]) >
|
||||||
|
norm(hull[0][index_hull] - angle_list[2]) ? angle_list[2] : angle_list[1];
|
||||||
|
|
||||||
|
Point intrsc_line_hull =
|
||||||
|
intersectionLines(hull[0][index_hull], hull[0][next_index_hull],
|
||||||
|
angle_list[1], angle_list[2]);
|
||||||
|
temp_norm = getCosVectors(hull[0][index_hull], intrsc_line_hull, angle_closest_pnt);
|
||||||
|
if (min_norm > temp_norm &&
|
||||||
|
norm(hull[0][index_hull] - hull[0][next_index_hull]) >
|
||||||
|
norm(angle_list[1] - angle_list[2]) / 20)
|
||||||
|
{
|
||||||
|
min_norm = temp_norm;
|
||||||
|
result_side_begin[1] = hull[0][index_hull];
|
||||||
|
result_side_end[1] = hull[0][next_index_hull];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
index_hull = next_index_hull;
|
||||||
|
}
|
||||||
|
while(index_hull != finish_line[1]);
|
||||||
|
|
||||||
|
if (min_norm == std::numeric_limits<double>::max())
|
||||||
|
{
|
||||||
|
result_side_begin[1] = angle_list[1];
|
||||||
|
result_side_end[1] = angle_list[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
double test_norm[4] = { 0.0, 0.0, 0.0, 0.0 };
|
||||||
|
int test_index[4];
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
test_index[i] = (i < 2) ? static_cast<int>(start_line[0])
|
||||||
|
: static_cast<int>(finish_line[1]);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
next_index_hull = ((i + 1) % 2 != 0) ? test_index[i] + 1 : test_index[i] - 1;
|
||||||
|
if (next_index_hull == hull_size) { next_index_hull = 0; }
|
||||||
|
if (next_index_hull == -1) { next_index_hull = hull_size - 1; }
|
||||||
|
test_norm[i] += norm(hull[0][next_index_hull] - hull[0][unstable_pnt]);
|
||||||
|
test_index[i] = next_index_hull;
|
||||||
|
}
|
||||||
|
while(test_index[i] != unstable_pnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Point> result_angle_list(4), test_result_angle_list(4);
|
||||||
|
double min_area = std::numeric_limits<double>::max(), test_area;
|
||||||
|
index_hull = start_line[0];
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (test_norm[0] < test_norm[1]) { next_index_hull = index_hull + 1; }
|
||||||
|
else { next_index_hull = index_hull - 1; }
|
||||||
|
|
||||||
|
if (next_index_hull == hull_size) { next_index_hull = 0; }
|
||||||
|
if (next_index_hull == -1) { next_index_hull = hull_size - 1; }
|
||||||
|
|
||||||
|
extra_index_hull = finish_line[1];
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (test_norm[2] < test_norm[3]) { extra_next_index_hull = extra_index_hull + 1; }
|
||||||
|
else { extra_next_index_hull = extra_index_hull - 1; }
|
||||||
|
|
||||||
|
if (extra_next_index_hull == hull_size) { extra_next_index_hull = 0; }
|
||||||
|
if (extra_next_index_hull == -1) { extra_next_index_hull = hull_size - 1; }
|
||||||
|
|
||||||
|
test_result_angle_list[0]
|
||||||
|
= intersectionLines(result_side_begin[0], result_side_end[0],
|
||||||
|
result_side_begin[1], result_side_end[1]);
|
||||||
|
test_result_angle_list[1]
|
||||||
|
= intersectionLines(result_side_begin[1], result_side_end[1],
|
||||||
|
hull[0][extra_index_hull], hull[0][extra_next_index_hull]);
|
||||||
|
test_result_angle_list[2]
|
||||||
|
= intersectionLines(hull[0][extra_index_hull], hull[0][extra_next_index_hull],
|
||||||
|
hull[0][index_hull], hull[0][next_index_hull]);
|
||||||
|
test_result_angle_list[3]
|
||||||
|
= intersectionLines(hull[0][index_hull], hull[0][next_index_hull],
|
||||||
|
result_side_begin[0], result_side_end[0]);
|
||||||
|
test_area = getQuadrilateralArea(test_result_angle_list[0],
|
||||||
|
test_result_angle_list[1],
|
||||||
|
test_result_angle_list[2],
|
||||||
|
test_result_angle_list[3]);
|
||||||
|
if (min_area > test_area)
|
||||||
|
{
|
||||||
|
min_area = test_area;
|
||||||
|
for (size_t i = 0; i < test_result_angle_list.size(); i++)
|
||||||
|
{
|
||||||
|
result_angle_list[i] = test_result_angle_list[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_index_hull = extra_next_index_hull;
|
||||||
|
}
|
||||||
|
while(extra_index_hull != unstable_pnt);
|
||||||
|
|
||||||
|
index_hull = next_index_hull;
|
||||||
|
}
|
||||||
|
while(index_hull != unstable_pnt);
|
||||||
|
|
||||||
|
if (norm(result_angle_list[0] - angle_list[2]) >
|
||||||
|
norm(angle_list[2] - angle_list[1]) / 3) { result_angle_list[0] = angle_list[2]; }
|
||||||
|
|
||||||
|
if (norm(result_angle_list[1] - angle_list[1]) >
|
||||||
|
norm(angle_list[1] - angle_list[0]) / 3) { result_angle_list[1] = angle_list[1]; }
|
||||||
|
|
||||||
|
if (norm(result_angle_list[2] - angle_list[0]) >
|
||||||
|
norm(angle_list[0] - angle_list[3]) / 3) { result_angle_list[2] = angle_list[0]; }
|
||||||
|
|
||||||
|
if (norm(result_angle_list[3] - angle_list[3]) >
|
||||||
|
norm(angle_list[3] - angle_list[2]) / 3) { result_angle_list[3] = angle_list[3]; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return result_angle_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// b __________ c
|
||||||
|
// / |
|
||||||
|
// / |
|
||||||
|
// / S |
|
||||||
|
// / |
|
||||||
|
// a --------------- d
|
||||||
|
|
||||||
|
double QRDecode::getQuadrilateralArea(Point a, Point b, Point c, Point d)
|
||||||
|
{
|
||||||
|
double length_sides[4], perimeter = 0.0, result_area = 1.0;
|
||||||
|
length_sides[0] = norm(a - b); length_sides[1] = norm(b - c);
|
||||||
|
length_sides[2] = norm(c - d); length_sides[3] = norm(d - a);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) { perimeter += length_sides[i]; }
|
||||||
|
perimeter /= 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
result_area *= (perimeter - length_sides[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result_area = sqrt(result_area);
|
||||||
|
|
||||||
|
return result_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
// / | b
|
||||||
|
// / |
|
||||||
|
// / |
|
||||||
|
// a/ | c
|
||||||
|
|
||||||
|
double QRDecode::getCosVectors(Point a, Point b, Point c)
|
||||||
|
{
|
||||||
|
return ((a - b).x * (c - b).x + (a - b).y * (c - b).y) / (norm(a - b) * norm(c - b));
|
||||||
|
}
|
||||||
|
|
||||||
|
CV_EXPORTS bool detectQRCode(InputArray in, std::vector<Point> &points, double eps_x, double eps_y)
|
||||||
|
{
|
||||||
|
CV_Assert(in.isMat());
|
||||||
|
CV_Assert(in.getMat().type() == CV_8UC1);
|
||||||
|
QRDecode qrdec;
|
||||||
|
qrdec.init(in.getMat(), eps_x, eps_y);
|
||||||
|
qrdec.binarization();
|
||||||
|
if (!qrdec.localization()) { return false; }
|
||||||
|
if (!qrdec.transformation()) { return false; }
|
||||||
|
points = qrdec.getTransformationPoints();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
modules/objdetect/test/test_qrcode.cpp
Normal file
74
modules/objdetect/test/test_qrcode.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||||
|
//
|
||||||
|
// By downloading, copying, installing or using the software you agree to this license.
|
||||||
|
// If you do not agree to this license, do not download, install,
|
||||||
|
// copy or use the software.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Intel License Agreement
|
||||||
|
// For Open Source Computer Vision Library
|
||||||
|
//
|
||||||
|
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||||
|
// Third party copyrights are property of their respective owners.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
// are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistribution's of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||||
|
// derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// This software is provided by the copyright holders and contributors "as is" and
|
||||||
|
// any express or implied warranties, including, but not limited to, the implied
|
||||||
|
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||||
|
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||||
|
// indirect, incidental, special, exemplary, or consequential damages
|
||||||
|
// (including, but not limited to, procurement of substitute goods or services;
|
||||||
|
// loss of use, data, or profits; or business interruption) however caused
|
||||||
|
// and on any theory of liability, whether in contract, strict liability,
|
||||||
|
// or tort (including negligence or otherwise) arising in any way out of
|
||||||
|
// the use of this software, even if advised of the possibility of such damage.
|
||||||
|
//
|
||||||
|
//M*/
|
||||||
|
|
||||||
|
#include "test_precomp.hpp"
|
||||||
|
|
||||||
|
namespace opencv_test { namespace {
|
||||||
|
|
||||||
|
TEST(Objdetect_QRCode, regression)
|
||||||
|
{
|
||||||
|
String root = cvtest::TS::ptr()->get_data_path() + "qrcode/";
|
||||||
|
// String cascades[] =
|
||||||
|
// {
|
||||||
|
// root + "haarcascade_frontalface_alt.xml",
|
||||||
|
// root + "lbpcascade_frontalface.xml",
|
||||||
|
// String()
|
||||||
|
// };
|
||||||
|
|
||||||
|
// vector<Rect> objects;
|
||||||
|
// RNG rng((uint64)-1);
|
||||||
|
|
||||||
|
// for( int i = 0; !cascades[i].empty(); i++ )
|
||||||
|
// {
|
||||||
|
// printf("%d. %s\n", i, cascades[i].c_str());
|
||||||
|
// CascadeClassifier cascade(cascades[i]);
|
||||||
|
// for( int j = 0; j < 100; j++ )
|
||||||
|
// {
|
||||||
|
// int width = rng.uniform(1, 100);
|
||||||
|
// int height = rng.uniform(1, 100);
|
||||||
|
// Mat img(height, width, CV_8U);
|
||||||
|
// randu(img, 0, 256);
|
||||||
|
// cascade.detectMultiScale(img, objects);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
}} // namespace
|
@ -88,10 +88,6 @@ void cv::seamlessClone(InputArray _src, InputArray _dst, InputArray _mask, Point
|
|||||||
|
|
||||||
int minxd = p.x - lenx/2;
|
int minxd = p.x - lenx/2;
|
||||||
int minyd = p.y - leny/2;
|
int minyd = p.y - leny/2;
|
||||||
int maxxd = minxd + lenx;
|
|
||||||
int maxyd = minyd + leny;
|
|
||||||
|
|
||||||
CV_Assert(minxd >= 0 && minyd >= 0 && maxxd <= dest.rows && maxyd <= dest.cols);
|
|
||||||
|
|
||||||
Rect roi_d(minxd,minyd,lenx,leny);
|
Rect roi_d(minxd,minyd,lenx,leny);
|
||||||
Rect roi_s(minx,miny,lenx,leny);
|
Rect roi_s(minx,miny,lenx,leny);
|
||||||
|
@ -916,7 +916,7 @@ bool pyopencv_to(PyObject* obj, String& value, const char* name)
|
|||||||
(void)name;
|
(void)name;
|
||||||
if(!obj || obj == Py_None)
|
if(!obj || obj == Py_None)
|
||||||
return true;
|
return true;
|
||||||
char* str = PyString_AsString(obj);
|
const char* str = PyString_AsString(obj);
|
||||||
if(!str)
|
if(!str)
|
||||||
return false;
|
return false;
|
||||||
value = String(str);
|
value = String(str);
|
||||||
|
@ -250,7 +250,9 @@ when fullAffine=false.
|
|||||||
@sa
|
@sa
|
||||||
estimateAffine2D, estimateAffinePartial2D, getAffineTransform, getPerspectiveTransform, findHomography
|
estimateAffine2D, estimateAffinePartial2D, getAffineTransform, getPerspectiveTransform, findHomography
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W Mat estimateRigidTransform( InputArray src, InputArray dst, bool fullAffine );
|
CV_EXPORTS_W Mat estimateRigidTransform( InputArray src, InputArray dst, bool fullAffine);
|
||||||
|
CV_EXPORTS_W Mat estimateRigidTransform( InputArray src, InputArray dst, bool fullAffine, int ransacMaxIters, double ransacGoodRatio,
|
||||||
|
int ransacSize0);
|
||||||
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -1412,7 +1412,7 @@ namespace cv
|
|||||||
{
|
{
|
||||||
|
|
||||||
static void
|
static void
|
||||||
getRTMatrix( const Point2f* a, const Point2f* b,
|
getRTMatrix( const std::vector<Point2f> a, const std::vector<Point2f> b,
|
||||||
int count, Mat& M, bool fullAffine )
|
int count, Mat& M, bool fullAffine )
|
||||||
{
|
{
|
||||||
CV_Assert( M.isContinuous() );
|
CV_Assert( M.isContinuous() );
|
||||||
@ -1488,6 +1488,12 @@ getRTMatrix( const Point2f* a, const Point2f* b,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullAffine )
|
cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullAffine )
|
||||||
|
{
|
||||||
|
return estimateRigidTransform(src1, src2, fullAffine, 500, 0.5, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullAffine, int ransacMaxIters, double ransacGoodRatio,
|
||||||
|
const int ransacSize0)
|
||||||
{
|
{
|
||||||
CV_INSTRUMENT_REGION()
|
CV_INSTRUMENT_REGION()
|
||||||
|
|
||||||
@ -1495,9 +1501,6 @@ cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullA
|
|||||||
|
|
||||||
const int COUNT = 15;
|
const int COUNT = 15;
|
||||||
const int WIDTH = 160, HEIGHT = 120;
|
const int WIDTH = 160, HEIGHT = 120;
|
||||||
const int RANSAC_MAX_ITERS = 500;
|
|
||||||
const int RANSAC_SIZE0 = 3;
|
|
||||||
const double RANSAC_GOOD_RATIO = 0.5;
|
|
||||||
|
|
||||||
std::vector<Point2f> pA, pB;
|
std::vector<Point2f> pA, pB;
|
||||||
std::vector<int> good_idx;
|
std::vector<int> good_idx;
|
||||||
@ -1509,6 +1512,12 @@ cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullA
|
|||||||
RNG rng((uint64)-1);
|
RNG rng((uint64)-1);
|
||||||
int good_count = 0;
|
int good_count = 0;
|
||||||
|
|
||||||
|
if( ransacSize0 < 3 )
|
||||||
|
CV_Error( Error::StsBadArg, "ransacSize0 should have value bigger than 2.");
|
||||||
|
|
||||||
|
if( ransacGoodRatio > 1 || ransacGoodRatio < 0)
|
||||||
|
CV_Error( Error::StsBadArg, "ransacGoodRatio should have value between 0 and 1");
|
||||||
|
|
||||||
if( A.size() != B.size() )
|
if( A.size() != B.size() )
|
||||||
CV_Error( Error::StsUnmatchedSizes, "Both input images must have the same size" );
|
CV_Error( Error::StsUnmatchedSizes, "Both input images must have the same size" );
|
||||||
|
|
||||||
@ -1597,23 +1606,23 @@ cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullA
|
|||||||
|
|
||||||
good_idx.resize(count);
|
good_idx.resize(count);
|
||||||
|
|
||||||
if( count < RANSAC_SIZE0 )
|
if( count < ransacSize0 )
|
||||||
return Mat();
|
return Mat();
|
||||||
|
|
||||||
Rect brect = boundingRect(pB);
|
Rect brect = boundingRect(pB);
|
||||||
|
|
||||||
|
std::vector<Point2f> a(ransacSize0);
|
||||||
|
std::vector<Point2f> b(ransacSize0);
|
||||||
|
|
||||||
// RANSAC stuff:
|
// RANSAC stuff:
|
||||||
// 1. find the consensus
|
// 1. find the consensus
|
||||||
for( k = 0; k < RANSAC_MAX_ITERS; k++ )
|
for( k = 0; k < ransacMaxIters; k++ )
|
||||||
{
|
{
|
||||||
int idx[RANSAC_SIZE0];
|
std::vector<int> idx(ransacSize0);
|
||||||
Point2f a[RANSAC_SIZE0];
|
// choose random 3 non-complanar points from A & B
|
||||||
Point2f b[RANSAC_SIZE0];
|
for( i = 0; i < ransacSize0; i++ )
|
||||||
|
|
||||||
// choose random 3 non-coplanar points from A & B
|
|
||||||
for( i = 0; i < RANSAC_SIZE0; i++ )
|
|
||||||
{
|
{
|
||||||
for( k1 = 0; k1 < RANSAC_MAX_ITERS; k1++ )
|
for( k1 = 0; k1 < ransacMaxIters; k1++ )
|
||||||
{
|
{
|
||||||
idx[i] = rng.uniform(0, count);
|
idx[i] = rng.uniform(0, count);
|
||||||
|
|
||||||
@ -1633,7 +1642,7 @@ cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullA
|
|||||||
if( j < i )
|
if( j < i )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( i+1 == RANSAC_SIZE0 )
|
if( i+1 == ransacSize0 )
|
||||||
{
|
{
|
||||||
// additional check for non-complanar vectors
|
// additional check for non-complanar vectors
|
||||||
a[0] = pA[idx[0]];
|
a[0] = pA[idx[0]];
|
||||||
@ -1657,11 +1666,11 @@ cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullA
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( k1 >= RANSAC_MAX_ITERS )
|
if( k1 >= ransacMaxIters )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( i < RANSAC_SIZE0 )
|
if( i < ransacSize0 )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// estimate the transformation using 3 points
|
// estimate the transformation using 3 points
|
||||||
@ -1675,11 +1684,11 @@ cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullA
|
|||||||
good_idx[good_count++] = i;
|
good_idx[good_count++] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( good_count >= count*RANSAC_GOOD_RATIO )
|
if( good_count >= count*ransacGoodRatio )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( k >= RANSAC_MAX_ITERS )
|
if( k >= ransacMaxIters )
|
||||||
return Mat();
|
return Mat();
|
||||||
|
|
||||||
if( good_count < count )
|
if( good_count < count )
|
||||||
@ -1692,7 +1701,7 @@ cv::Mat cv::estimateRigidTransform( InputArray src1, InputArray src2, bool fullA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getRTMatrix( &pA[0], &pB[0], good_count, M, fullAffine );
|
getRTMatrix( pA, pB, good_count, M, fullAffine );
|
||||||
M.at<double>(0, 2) /= scale;
|
M.at<double>(0, 2) /= scale;
|
||||||
M.at<double>(1, 2) /= scale;
|
M.at<double>(1, 2) /= scale;
|
||||||
|
|
||||||
|
@ -3220,6 +3220,11 @@ double VideoCapture_DShow::getProperty(int propIdx) const
|
|||||||
return g_VI.getFourcc(m_index);
|
return g_VI.getFourcc(m_index);
|
||||||
case CV_CAP_PROP_FPS:
|
case CV_CAP_PROP_FPS:
|
||||||
return g_VI.getFPS(m_index);
|
return g_VI.getFPS(m_index);
|
||||||
|
case CV_CAP_PROP_AUTOFOCUS:
|
||||||
|
// Flags indicate whether or not autofocus is enabled
|
||||||
|
if (g_VI.getVideoSettingCamera(m_index, CameraControl_Focus, min_value, max_value, stepping_delta, current_value, flags, defaultValue))
|
||||||
|
return (double)flags;
|
||||||
|
return -1;
|
||||||
|
|
||||||
// video filter properties
|
// video filter properties
|
||||||
case CV_CAP_PROP_BRIGHTNESS:
|
case CV_CAP_PROP_BRIGHTNESS:
|
||||||
@ -3284,6 +3289,7 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CV_CAP_PROP_FPS:
|
case CV_CAP_PROP_FPS:
|
||||||
|
{
|
||||||
int fps = cvRound(propVal);
|
int fps = cvRound(propVal);
|
||||||
if (fps != g_VI.getFPS(m_index))
|
if (fps != g_VI.getFPS(m_index))
|
||||||
{
|
{
|
||||||
@ -3297,6 +3303,19 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal)
|
|||||||
return g_VI.isDeviceSetup(m_index);
|
return g_VI.isDeviceSetup(m_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CV_CAP_PROP_AUTOFOCUS:
|
||||||
|
{
|
||||||
|
// Flags are required to toggle autofocus or not, but the setProperty interface does not support multiple parameters
|
||||||
|
bool enabled = cvRound(propVal) == 1;
|
||||||
|
long minFocus, maxFocus, delta, currentFocus, flags, defaultValue;
|
||||||
|
if (!g_VI.getVideoSettingCamera(m_index, CameraControl_Focus, minFocus, maxFocus, delta, currentFocus, flags, defaultValue))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return g_VI.setVideoSettingCamera(m_index, CameraControl_Focus, currentFocus, enabled ? CameraControl_Flags_Auto | CameraControl_Flags_Manual : CameraControl_Flags_Manual, enabled ? true : false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (handled)
|
if (handled)
|
||||||
{
|
{
|
||||||
// a stream setting
|
// a stream setting
|
||||||
|
@ -83,6 +83,21 @@
|
|||||||
#pragma comment(lib, "Mfreadwrite")
|
#pragma comment(lib, "Mfreadwrite")
|
||||||
#ifdef HAVE_DXVA
|
#ifdef HAVE_DXVA
|
||||||
#pragma comment(lib, "d3d11")
|
#pragma comment(lib, "d3d11")
|
||||||
|
// MFCreateDXGIDeviceManager() is available since Win8 only.
|
||||||
|
// To avoid OpenCV loading failure on Win7 use dynamic detection of this symbol.
|
||||||
|
// Details: https://github.com/opencv/opencv/issues/11858
|
||||||
|
typedef HRESULT (WINAPI *FN_MFCreateDXGIDeviceManager)(UINT *resetToken, IMFDXGIDeviceManager **ppDeviceManager);
|
||||||
|
static bool pMFCreateDXGIDeviceManager_initialized = false;
|
||||||
|
static FN_MFCreateDXGIDeviceManager pMFCreateDXGIDeviceManager = NULL;
|
||||||
|
static void init_MFCreateDXGIDeviceManager()
|
||||||
|
{
|
||||||
|
HMODULE h = LoadLibraryA("mfplat.dll");
|
||||||
|
if (h)
|
||||||
|
{
|
||||||
|
pMFCreateDXGIDeviceManager = (FN_MFCreateDXGIDeviceManager)GetProcAddress(h, "MFCreateDXGIDeviceManager");
|
||||||
|
}
|
||||||
|
pMFCreateDXGIDeviceManager_initialized = true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#if (WINVER >= 0x0602) // Available since Win 8
|
#if (WINVER >= 0x0602) // Available since Win 8
|
||||||
#pragma comment(lib, "MinCore_Downlevel")
|
#pragma comment(lib, "MinCore_Downlevel")
|
||||||
@ -93,6 +108,8 @@
|
|||||||
|
|
||||||
#include <comdef.h>
|
#include <comdef.h>
|
||||||
|
|
||||||
|
#include <shlwapi.h> // QISearch
|
||||||
|
|
||||||
struct IMFMediaType;
|
struct IMFMediaType;
|
||||||
struct IMFActivate;
|
struct IMFActivate;
|
||||||
struct IMFMediaSource;
|
struct IMFMediaSource;
|
||||||
@ -101,38 +118,6 @@ struct IMFAttributes;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef _DEBUG
|
|
||||||
void DPOprintOut(const wchar_t *format, ...)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
wchar_t *p = NULL;
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
if (::IsDebuggerPresent())
|
|
||||||
{
|
|
||||||
WCHAR szMsg[512];
|
|
||||||
::StringCchVPrintfW(szMsg, sizeof(szMsg) / sizeof(szMsg[0]), format, args);
|
|
||||||
::OutputDebugStringW(szMsg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (wcscmp(format, L"%i"))
|
|
||||||
{
|
|
||||||
i = va_arg(args, int);
|
|
||||||
}
|
|
||||||
if (wcscmp(format, L"%s"))
|
|
||||||
{
|
|
||||||
p = va_arg(args, wchar_t *);
|
|
||||||
}
|
|
||||||
wprintf(format, i, p);
|
|
||||||
}
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
#define DebugPrintOut(...) DPOprintOut(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define DebugPrintOut(...) void()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class ComPtr
|
class ComPtr
|
||||||
{
|
{
|
||||||
@ -154,89 +139,36 @@ public:
|
|||||||
|
|
||||||
T** operator&()
|
T** operator&()
|
||||||
{
|
{
|
||||||
assert(p == NULL);
|
CV_Assert(p == NULL);
|
||||||
return p.operator&();
|
return p.operator&();
|
||||||
}
|
}
|
||||||
T* operator->() const
|
T* operator->() const
|
||||||
{
|
{
|
||||||
assert(p != NULL);
|
CV_Assert(p != NULL);
|
||||||
return p.operator->();
|
return p.operator->();
|
||||||
}
|
}
|
||||||
bool operator!() const
|
|
||||||
{
|
|
||||||
return p.operator==(NULL);
|
|
||||||
}
|
|
||||||
bool operator==(_In_opt_ T* pT) const
|
|
||||||
{
|
|
||||||
return p.operator==(pT);
|
|
||||||
}
|
|
||||||
bool operator!=(_In_opt_ T* pT) const
|
|
||||||
{
|
|
||||||
return p.operator!=(pT);
|
|
||||||
}
|
|
||||||
operator bool()
|
operator bool()
|
||||||
{
|
{
|
||||||
return p.operator!=(NULL);
|
return p.operator!=(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
T* const* GetAddressOf() const
|
|
||||||
{
|
|
||||||
return &p;
|
|
||||||
}
|
|
||||||
|
|
||||||
T** GetAddressOf()
|
|
||||||
{
|
|
||||||
return &p;
|
|
||||||
}
|
|
||||||
|
|
||||||
T** ReleaseAndGetAddressOf()
|
|
||||||
{
|
|
||||||
p.Release();
|
|
||||||
return &p;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* Get() const
|
T* Get() const
|
||||||
{
|
{
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing interface (does not AddRef)
|
void Release()
|
||||||
void Attach(_In_opt_ T* p2)
|
|
||||||
{
|
{
|
||||||
p.Attach(p2);
|
if (p)
|
||||||
}
|
p.Release();
|
||||||
// Detach the interface (does not Release)
|
|
||||||
T* Detach()
|
|
||||||
{
|
|
||||||
return p.Detach();
|
|
||||||
}
|
|
||||||
_Check_return_ HRESULT CopyTo(_Deref_out_opt_ T** ppT)
|
|
||||||
{
|
|
||||||
assert(ppT != NULL);
|
|
||||||
if (ppT == NULL)
|
|
||||||
return E_POINTER;
|
|
||||||
*ppT = p;
|
|
||||||
if (p != NULL)
|
|
||||||
p->AddRef();
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset()
|
|
||||||
{
|
|
||||||
p.Release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// query for U interface
|
// query for U interface
|
||||||
template<typename U>
|
template<typename U>
|
||||||
HRESULT As(_Inout_ U** lp) const
|
HRESULT As(_Out_ ComPtr<U>& lp) const
|
||||||
{
|
{
|
||||||
return p->QueryInterface(__uuidof(U), reinterpret_cast<void**>(lp));
|
lp.Release();
|
||||||
}
|
return p->QueryInterface(__uuidof(U), reinterpret_cast<void**>((T**)&lp));
|
||||||
// query for U interface
|
|
||||||
template<typename U>
|
|
||||||
HRESULT As(_Out_ ComPtr<U>* lp) const
|
|
||||||
{
|
|
||||||
return p->QueryInterface(__uuidof(U), reinterpret_cast<void**>(lp->ReleaseAndGetAddressOf()));
|
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
_COM_SMARTPTR_TYPEDEF(T, __uuidof(T));
|
_COM_SMARTPTR_TYPEDEF(T, __uuidof(T));
|
||||||
@ -680,6 +612,77 @@ void MediaType::Clear()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SourceReaderCB : public IMFSourceReaderCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SourceReaderCB() :
|
||||||
|
m_nRefCount(1), m_hEvent(CreateEvent(NULL, FALSE, FALSE, NULL)), m_bEOS(FALSE), m_hrStatus(S_OK), m_dwStreamIndex(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// IUnknown methods
|
||||||
|
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4838)
|
||||||
|
#endif
|
||||||
|
static const QITAB qit[] =
|
||||||
|
{
|
||||||
|
QITABENT(SourceReaderCB, IMFSourceReaderCallback),
|
||||||
|
{ 0 },
|
||||||
|
};
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
return QISearch(this, qit, iid, ppv);
|
||||||
|
}
|
||||||
|
STDMETHODIMP_(ULONG) AddRef() CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&m_nRefCount);
|
||||||
|
}
|
||||||
|
STDMETHODIMP_(ULONG) Release() CV_OVERRIDE
|
||||||
|
{
|
||||||
|
ULONG uCount = InterlockedDecrement(&m_nRefCount);
|
||||||
|
if (uCount == 0)
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
return uCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) CV_OVERRIDE;
|
||||||
|
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
STDMETHODIMP OnFlush(DWORD) CV_OVERRIDE
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT Wait(DWORD dwMilliseconds, _ComPtr<IMFSample>& videoSample, BOOL& pbEOS);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Destructor is private. Caller should call Release.
|
||||||
|
virtual ~SourceReaderCB()
|
||||||
|
{
|
||||||
|
CV_LOG_WARNING(NULL, "terminating async callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
long m_nRefCount; // Reference count.
|
||||||
|
cv::Mutex m_mutex;
|
||||||
|
HANDLE m_hEvent;
|
||||||
|
BOOL m_bEOS;
|
||||||
|
HRESULT m_hrStatus;
|
||||||
|
|
||||||
|
_ComPtr<IMFSourceReader> m_reader;
|
||||||
|
DWORD m_dwStreamIndex;
|
||||||
|
_ComPtr<IMFSample> m_lastSample;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/******* Capturing video from camera or file via Microsoft Media Foundation **********/
|
/******* Capturing video from camera or file via Microsoft Media Foundation **********/
|
||||||
class CvCapture_MSMF : public cv::IVideoCapture
|
class CvCapture_MSMF : public cv::IVideoCapture
|
||||||
{
|
{
|
||||||
@ -689,8 +692,6 @@ public:
|
|||||||
MODE_HW = 1
|
MODE_HW = 1
|
||||||
} MSMFCapture_Mode;
|
} MSMFCapture_Mode;
|
||||||
CvCapture_MSMF();
|
CvCapture_MSMF();
|
||||||
CvCapture_MSMF(int);
|
|
||||||
CvCapture_MSMF(const cv::String&);
|
|
||||||
virtual ~CvCapture_MSMF();
|
virtual ~CvCapture_MSMF();
|
||||||
virtual bool open(int);
|
virtual bool open(int);
|
||||||
virtual bool open(const cv::String&);
|
virtual bool open(const cv::String&);
|
||||||
@ -728,6 +729,7 @@ protected:
|
|||||||
_ComPtr<IMFSample> videoSample;
|
_ComPtr<IMFSample> videoSample;
|
||||||
LONGLONG sampleTime;
|
LONGLONG sampleTime;
|
||||||
bool isOpen;
|
bool isOpen;
|
||||||
|
_ComPtr<IMFSourceReaderCallback> readCallback; // non-NULL for "live" streams (camera capture)
|
||||||
};
|
};
|
||||||
|
|
||||||
CvCapture_MSMF::CvCapture_MSMF():
|
CvCapture_MSMF::CvCapture_MSMF():
|
||||||
@ -752,8 +754,6 @@ CvCapture_MSMF::CvCapture_MSMF():
|
|||||||
{
|
{
|
||||||
configureHW(true);
|
configureHW(true);
|
||||||
}
|
}
|
||||||
CvCapture_MSMF::CvCapture_MSMF(int index) : CvCapture_MSMF() { open(index); }
|
|
||||||
CvCapture_MSMF::CvCapture_MSMF(const cv::String& _filename) : CvCapture_MSMF() { open(_filename); }
|
|
||||||
|
|
||||||
CvCapture_MSMF::~CvCapture_MSMF()
|
CvCapture_MSMF::~CvCapture_MSMF()
|
||||||
{
|
{
|
||||||
@ -766,13 +766,12 @@ void CvCapture_MSMF::close()
|
|||||||
if (isOpen)
|
if (isOpen)
|
||||||
{
|
{
|
||||||
isOpen = false;
|
isOpen = false;
|
||||||
if (videoSample)
|
videoSample.Release();
|
||||||
videoSample.Reset();
|
videoFileSource.Release();
|
||||||
if (videoFileSource)
|
|
||||||
videoFileSource.Reset();
|
|
||||||
camid = -1;
|
camid = -1;
|
||||||
filename = "";
|
filename.clear();
|
||||||
}
|
}
|
||||||
|
readCallback.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CvCapture_MSMF::configureHW(bool enable)
|
bool CvCapture_MSMF::configureHW(bool enable)
|
||||||
@ -780,6 +779,10 @@ bool CvCapture_MSMF::configureHW(bool enable)
|
|||||||
#ifdef HAVE_DXVA
|
#ifdef HAVE_DXVA
|
||||||
if ((enable && D3DMgr && D3DDev) || (!enable && !D3DMgr && !D3DDev))
|
if ((enable && D3DMgr && D3DDev) || (!enable && !D3DMgr && !D3DDev))
|
||||||
return true;
|
return true;
|
||||||
|
if (!pMFCreateDXGIDeviceManager_initialized)
|
||||||
|
init_MFCreateDXGIDeviceManager();
|
||||||
|
if (enable && !pMFCreateDXGIDeviceManager)
|
||||||
|
return false;
|
||||||
|
|
||||||
bool reopen = isOpen;
|
bool reopen = isOpen;
|
||||||
int prevcam = camid;
|
int prevcam = camid;
|
||||||
@ -791,7 +794,7 @@ bool CvCapture_MSMF::configureHW(bool enable)
|
|||||||
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0,
|
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0,
|
||||||
D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 };
|
D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 };
|
||||||
if (SUCCEEDED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
|
if (SUCCEEDED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
|
||||||
levels, sizeof(levels) / sizeof(*levels), D3D11_SDK_VERSION, D3DDev.GetAddressOf(), NULL, NULL)))
|
levels, sizeof(levels) / sizeof(*levels), D3D11_SDK_VERSION, &D3DDev, NULL, NULL)))
|
||||||
{
|
{
|
||||||
// NOTE: Getting ready for multi-threaded operation
|
// NOTE: Getting ready for multi-threaded operation
|
||||||
_ComPtr<ID3D11Multithread> D3DDevMT;
|
_ComPtr<ID3D11Multithread> D3DDevMT;
|
||||||
@ -799,29 +802,29 @@ bool CvCapture_MSMF::configureHW(bool enable)
|
|||||||
if (SUCCEEDED(D3DDev->QueryInterface(IID_PPV_ARGS(&D3DDevMT))))
|
if (SUCCEEDED(D3DDev->QueryInterface(IID_PPV_ARGS(&D3DDevMT))))
|
||||||
{
|
{
|
||||||
D3DDevMT->SetMultithreadProtected(TRUE);
|
D3DDevMT->SetMultithreadProtected(TRUE);
|
||||||
D3DDevMT.Reset();
|
D3DDevMT.Release();
|
||||||
if (SUCCEEDED(MFCreateDXGIDeviceManager(&mgrRToken, D3DMgr.GetAddressOf())))
|
if (SUCCEEDED(pMFCreateDXGIDeviceManager(&mgrRToken, &D3DMgr)))
|
||||||
{
|
{
|
||||||
if (SUCCEEDED(D3DMgr->ResetDevice(D3DDev.Get(), mgrRToken)))
|
if (SUCCEEDED(D3DMgr->ResetDevice(D3DDev.Get(), mgrRToken)))
|
||||||
{
|
{
|
||||||
captureMode = MODE_HW;
|
captureMode = MODE_HW;
|
||||||
return reopen ? camid >= 0 ? open(prevcam) : open(prevfile.c_str()) : true;
|
return reopen ? (prevcam >= 0 ? open(prevcam) : open(prevfile.c_str())) : true;
|
||||||
}
|
}
|
||||||
D3DMgr.Reset();
|
D3DMgr.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
D3DDev.Reset();
|
D3DDev.Release();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (D3DMgr)
|
if (D3DMgr)
|
||||||
D3DMgr.Reset();
|
D3DMgr.Release();
|
||||||
if (D3DDev)
|
if (D3DDev)
|
||||||
D3DDev.Reset();
|
D3DDev.Release();
|
||||||
captureMode = MODE_SW;
|
captureMode = MODE_SW;
|
||||||
return reopen ? camid >= 0 ? open(prevcam) : open(prevfile.c_str()) : true;
|
return reopen ? (prevcam >= 0 ? open(prevcam) : open(prevfile.c_str())) : true;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
return !enable;
|
return !enable;
|
||||||
@ -941,9 +944,10 @@ bool CvCapture_MSMF::configureOutput(UINT32 width, UINT32 height, double prefFra
|
|||||||
bool CvCapture_MSMF::open(int _index)
|
bool CvCapture_MSMF::open(int _index)
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
|
if (_index < 0)
|
||||||
|
return false;
|
||||||
_ComPtr<IMFAttributes> msAttr = NULL;
|
_ComPtr<IMFAttributes> msAttr = NULL;
|
||||||
if (SUCCEEDED(MFCreateAttributes(msAttr.GetAddressOf(), 1)) &&
|
if (SUCCEEDED(MFCreateAttributes(&msAttr, 1)) &&
|
||||||
SUCCEEDED(msAttr->SetGUID(
|
SUCCEEDED(msAttr->SetGUID(
|
||||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
||||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
|
||||||
@ -955,7 +959,6 @@ bool CvCapture_MSMF::open(int _index)
|
|||||||
{
|
{
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
_index = std::min(std::max(0, _index), (int)count - 1);
|
|
||||||
for (int ind = 0; ind < (int)count; ind++)
|
for (int ind = 0; ind < (int)count; ind++)
|
||||||
{
|
{
|
||||||
if (ind == _index && ppDevices[ind])
|
if (ind == _index && ppDevices[ind])
|
||||||
@ -965,15 +968,23 @@ bool CvCapture_MSMF::open(int _index)
|
|||||||
_ComPtr<IMFAttributes> srAttr;
|
_ComPtr<IMFAttributes> srAttr;
|
||||||
if (SUCCEEDED(ppDevices[ind]->ActivateObject(__uuidof(IMFMediaSource), (void**)&mSrc)) && mSrc &&
|
if (SUCCEEDED(ppDevices[ind]->ActivateObject(__uuidof(IMFMediaSource), (void**)&mSrc)) && mSrc &&
|
||||||
SUCCEEDED(MFCreateAttributes(&srAttr, 10)) &&
|
SUCCEEDED(MFCreateAttributes(&srAttr, 10)) &&
|
||||||
SUCCEEDED(srAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true)) &&
|
SUCCEEDED(srAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE)) &&
|
||||||
SUCCEEDED(srAttr->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, false)) &&
|
SUCCEEDED(srAttr->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE)) &&
|
||||||
SUCCEEDED(srAttr->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, false)) &&
|
SUCCEEDED(srAttr->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, FALSE)) &&
|
||||||
SUCCEEDED(srAttr->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, true)))
|
SUCCEEDED(srAttr->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE)))
|
||||||
{
|
{
|
||||||
#ifdef HAVE_DXVA
|
#ifdef HAVE_DXVA
|
||||||
if (D3DMgr)
|
if (D3DMgr)
|
||||||
srAttr->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, D3DMgr.Get());
|
srAttr->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, D3DMgr.Get());
|
||||||
#endif
|
#endif
|
||||||
|
readCallback = ComPtr<IMFSourceReaderCallback>(new SourceReaderCB());
|
||||||
|
HRESULT hr = srAttr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, (IMFSourceReaderCallback*)readCallback.Get());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
readCallback.Release();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(MFCreateSourceReaderFromMediaSource(mSrc.Get(), srAttr.Get(), &videoFileSource)))
|
if (SUCCEEDED(MFCreateSourceReaderFromMediaSource(mSrc.Get(), srAttr.Get(), &videoFileSource)))
|
||||||
{
|
{
|
||||||
isOpen = true;
|
isOpen = true;
|
||||||
@ -1045,14 +1056,116 @@ bool CvCapture_MSMF::open(const cv::String& _filename)
|
|||||||
return isOpen;
|
return isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT SourceReaderCB::Wait(DWORD dwMilliseconds, _ComPtr<IMFSample>& videoSample, BOOL& bEOS)
|
||||||
|
{
|
||||||
|
bEOS = FALSE;
|
||||||
|
|
||||||
|
DWORD dwResult = WaitForSingleObject(m_hEvent, dwMilliseconds);
|
||||||
|
if (dwResult == WAIT_TIMEOUT)
|
||||||
|
{
|
||||||
|
return E_PENDING;
|
||||||
|
}
|
||||||
|
else if (dwResult != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
bEOS = m_bEOS;
|
||||||
|
if (!bEOS)
|
||||||
|
{
|
||||||
|
cv::AutoLock lock(m_mutex);
|
||||||
|
videoSample = m_lastSample;
|
||||||
|
CV_Assert(videoSample);
|
||||||
|
m_lastSample.Release();
|
||||||
|
ResetEvent(m_hEvent); // event is auto-reset, but we need this forced reset due time gap between wait() and mutex hold.
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_hrStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP SourceReaderCB::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample)
|
||||||
|
{
|
||||||
|
CV_UNUSED(llTimestamp);
|
||||||
|
|
||||||
|
HRESULT hr = 0;
|
||||||
|
cv::AutoLock lock(m_mutex);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hrStatus))
|
||||||
|
{
|
||||||
|
if (pSample)
|
||||||
|
{
|
||||||
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): got frame at " << llTimestamp);
|
||||||
|
IMFSample* prev = m_lastSample.Get();
|
||||||
|
if (prev)
|
||||||
|
{
|
||||||
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): drop frame (not processed)");
|
||||||
|
}
|
||||||
|
m_lastSample = pSample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CV_LOG_WARNING(NULL, "videoio(MSMF): OnReadSample() is called with error status: " << hrStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
|
||||||
|
{
|
||||||
|
// Reached the end of the stream.
|
||||||
|
m_bEOS = true;
|
||||||
|
}
|
||||||
|
m_hrStatus = hrStatus;
|
||||||
|
|
||||||
|
if (FAILED(hr = m_reader->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
|
||||||
|
{
|
||||||
|
CV_LOG_WARNING(NULL, "videoio(MSMF): async ReadSample() call is failed with error status: " << hr);
|
||||||
|
m_bEOS = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pSample || m_bEOS)
|
||||||
|
{
|
||||||
|
SetEvent(m_hEvent);
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CvCapture_MSMF::grabFrame()
|
bool CvCapture_MSMF::grabFrame()
|
||||||
{
|
{
|
||||||
CV_TRACE_FUNCTION();
|
CV_TRACE_FUNCTION();
|
||||||
if (isOpen)
|
if (readCallback) // async "live" capture mode
|
||||||
|
{
|
||||||
|
HRESULT hr = 0;
|
||||||
|
SourceReaderCB* reader = ((SourceReaderCB*)readCallback.Get());
|
||||||
|
if (!reader->m_reader)
|
||||||
|
{
|
||||||
|
// Initiate capturing with async callback
|
||||||
|
reader->m_reader = videoFileSource;
|
||||||
|
reader->m_dwStreamIndex = dwStreamIndex;
|
||||||
|
if (FAILED(hr = videoFileSource->ReadSample(dwStreamIndex, 0, NULL, NULL, NULL, NULL)))
|
||||||
|
{
|
||||||
|
CV_LOG_ERROR(NULL, "videoio(MSMF): can't grab frame - initial async ReadSample() call failed: " << hr);
|
||||||
|
reader->m_reader = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BOOL bEOS = false;
|
||||||
|
if (FAILED(hr = reader->Wait(10000, videoSample, bEOS))) // 10 sec
|
||||||
|
{
|
||||||
|
CV_LOG_WARNING(NULL, "videoio(MSMF): can't grab frame. Error: " << hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bEOS)
|
||||||
|
{
|
||||||
|
CV_LOG_WARNING(NULL, "videoio(MSMF): EOS signal. Capture stream is lost");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (isOpen)
|
||||||
{
|
{
|
||||||
DWORD streamIndex, flags;
|
DWORD streamIndex, flags;
|
||||||
if (videoSample)
|
videoSample.Release();
|
||||||
videoSample.Reset();
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
@ -1074,7 +1187,7 @@ bool CvCapture_MSMF::grabFrame()
|
|||||||
break;
|
break;
|
||||||
if (flags & MF_SOURCE_READERF_STREAMTICK)
|
if (flags & MF_SOURCE_READERF_STREAMTICK)
|
||||||
{
|
{
|
||||||
DebugPrintOut(L"\tStream tick detected. Retrying to grab the frame\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream tick detected. Retrying to grab the frame");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1082,38 +1195,38 @@ bool CvCapture_MSMF::grabFrame()
|
|||||||
{
|
{
|
||||||
if (streamIndex != dwStreamIndex)
|
if (streamIndex != dwStreamIndex)
|
||||||
{
|
{
|
||||||
DebugPrintOut(L"\tWrong stream readed. Abort capturing\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): Wrong stream readed. Abort capturing");
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
else if (flags & MF_SOURCE_READERF_ERROR)
|
else if (flags & MF_SOURCE_READERF_ERROR)
|
||||||
{
|
{
|
||||||
DebugPrintOut(L"\tStream reading error. Abort capturing\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream reading error. Abort capturing");
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
else if (flags & MF_SOURCE_READERF_ALLEFFECTSREMOVED)
|
else if (flags & MF_SOURCE_READERF_ALLEFFECTSREMOVED)
|
||||||
{
|
{
|
||||||
DebugPrintOut(L"\tStream decoding error. Abort capturing\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream decoding error. Abort capturing");
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
else if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
|
else if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
|
||||||
{
|
{
|
||||||
sampleTime += frameStep;
|
sampleTime += frameStep;
|
||||||
DebugPrintOut(L"\tEnd of stream detected\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): End of stream detected");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sampleTime += frameStep;
|
sampleTime += frameStep;
|
||||||
if (flags & MF_SOURCE_READERF_NEWSTREAM)
|
if (flags & MF_SOURCE_READERF_NEWSTREAM)
|
||||||
{
|
{
|
||||||
DebugPrintOut(L"\tNew stream detected\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): New stream detected");
|
||||||
}
|
}
|
||||||
if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
|
if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
|
||||||
{
|
{
|
||||||
DebugPrintOut(L"\tStream native media type changed\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream native media type changed");
|
||||||
}
|
}
|
||||||
if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
|
if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
|
||||||
{
|
{
|
||||||
DebugPrintOut(L"\tStream current media type changed\n");
|
CV_LOG_DEBUG(NULL, "videoio(MSMF): Stream current media type changed");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1155,7 +1268,7 @@ bool CvCapture_MSMF::retrieveFrame(int, cv::OutputArray frame)
|
|||||||
_ComPtr<IMF2DBuffer> buffer2d;
|
_ComPtr<IMF2DBuffer> buffer2d;
|
||||||
if (convertFormat)
|
if (convertFormat)
|
||||||
{
|
{
|
||||||
if (SUCCEEDED(buf.As<IMF2DBuffer>(&buffer2d)))
|
if (SUCCEEDED(buf.As<IMF2DBuffer>(buffer2d)))
|
||||||
{
|
{
|
||||||
CV_TRACE_REGION_NEXT("lock2d");
|
CV_TRACE_REGION_NEXT("lock2d");
|
||||||
if (SUCCEEDED(buffer2d->Lock2D(&ptr, &pitch)))
|
if (SUCCEEDED(buffer2d->Lock2D(&ptr, &pitch)))
|
||||||
@ -1236,8 +1349,7 @@ bool CvCapture_MSMF::setTime(double time, bool rough)
|
|||||||
if (SUCCEEDED(videoFileSource->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, &var)) &&
|
if (SUCCEEDED(videoFileSource->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, &var)) &&
|
||||||
var.vt == VT_UI4 && var.ulVal & MFMEDIASOURCE_CAN_SEEK)
|
var.vt == VT_UI4 && var.ulVal & MFMEDIASOURCE_CAN_SEEK)
|
||||||
{
|
{
|
||||||
if (videoSample)
|
videoSample.Release();
|
||||||
videoSample.Reset();
|
|
||||||
bool useGrabbing = time > 0 && !rough && !(var.ulVal & MFMEDIASOURCE_HAS_SLOW_SEEK);
|
bool useGrabbing = time > 0 && !rough && !(var.ulVal & MFMEDIASOURCE_HAS_SLOW_SEEK);
|
||||||
PropVariantClear(&var);
|
PropVariantClear(&var);
|
||||||
sampleTime = (useGrabbing && time >= frameStep) ? (LONGLONG)floor(time + 0.5) - frameStep : (LONGLONG)floor(time + 0.5);
|
sampleTime = (useGrabbing && time >= frameStep) ? (LONGLONG)floor(time + 0.5) - frameStep : (LONGLONG)floor(time + 0.5);
|
||||||
@ -1248,7 +1360,7 @@ bool CvCapture_MSMF::setTime(double time, bool rough)
|
|||||||
if (resOK && useGrabbing)
|
if (resOK && useGrabbing)
|
||||||
{
|
{
|
||||||
LONGLONG timeborder = (LONGLONG)floor(time + 0.5) - frameStep / 2;
|
LONGLONG timeborder = (LONGLONG)floor(time + 0.5) - frameStep / 2;
|
||||||
do { resOK = grabFrame(); videoSample.Reset(); } while (resOK && sampleTime < timeborder);
|
do { resOK = grabFrame(); videoSample.Release(); } while (resOK && sampleTime < timeborder);
|
||||||
}
|
}
|
||||||
return resOK;
|
return resOK;
|
||||||
}
|
}
|
||||||
@ -1801,17 +1913,25 @@ bool CvCapture_MSMF::setProperty( int property_id, double value )
|
|||||||
|
|
||||||
cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF( int index )
|
cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF( int index )
|
||||||
{
|
{
|
||||||
cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>(index);
|
cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>();
|
||||||
if (capture && capture->isOpened())
|
if (capture)
|
||||||
return capture;
|
{
|
||||||
|
capture->open(index);
|
||||||
|
if (capture->isOpened())
|
||||||
|
return capture;
|
||||||
|
}
|
||||||
return cv::Ptr<cv::IVideoCapture>();
|
return cv::Ptr<cv::IVideoCapture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF (const cv::String& filename)
|
cv::Ptr<cv::IVideoCapture> cv::cvCreateCapture_MSMF (const cv::String& filename)
|
||||||
{
|
{
|
||||||
cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>(filename);
|
cv::Ptr<CvCapture_MSMF> capture = cv::makePtr<CvCapture_MSMF>();
|
||||||
if (capture && capture->isOpened())
|
if (capture)
|
||||||
return capture;
|
{
|
||||||
|
capture->open(filename);
|
||||||
|
if (capture->isOpened())
|
||||||
|
return capture;
|
||||||
|
}
|
||||||
return cv::Ptr<cv::IVideoCapture>();
|
return cv::Ptr<cv::IVideoCapture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1825,8 +1945,6 @@ class CvVideoWriter_MSMF : public cv::IVideoWriter
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CvVideoWriter_MSMF();
|
CvVideoWriter_MSMF();
|
||||||
CvVideoWriter_MSMF(const cv::String& filename, int fourcc,
|
|
||||||
double fps, cv::Size frameSize, bool isColor);
|
|
||||||
virtual ~CvVideoWriter_MSMF();
|
virtual ~CvVideoWriter_MSMF();
|
||||||
virtual bool open(const cv::String& filename, int fourcc,
|
virtual bool open(const cv::String& filename, int fourcc,
|
||||||
double fps, cv::Size frameSize, bool isColor);
|
double fps, cv::Size frameSize, bool isColor);
|
||||||
@ -1863,7 +1981,6 @@ CvVideoWriter_MSMF::CvVideoWriter_MSMF():
|
|||||||
initiated(false)
|
initiated(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
CvVideoWriter_MSMF::CvVideoWriter_MSMF(const cv::String& filename, int fourcc, double fps, cv::Size frameSize, bool isColor) : CvVideoWriter_MSMF() { open(filename, fourcc, fps, frameSize, isColor); }
|
|
||||||
|
|
||||||
CvVideoWriter_MSMF::~CvVideoWriter_MSMF()
|
CvVideoWriter_MSMF::~CvVideoWriter_MSMF()
|
||||||
{
|
{
|
||||||
@ -1990,7 +2107,7 @@ void CvVideoWriter_MSMF::close()
|
|||||||
{
|
{
|
||||||
initiated = false;
|
initiated = false;
|
||||||
sinkWriter->Finalize();
|
sinkWriter->Finalize();
|
||||||
sinkWriter.Reset();
|
sinkWriter.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2034,9 +2151,13 @@ void CvVideoWriter_MSMF::write(cv::InputArray img)
|
|||||||
cv::Ptr<cv::IVideoWriter> cv::cvCreateVideoWriter_MSMF( const cv::String& filename, int fourcc,
|
cv::Ptr<cv::IVideoWriter> cv::cvCreateVideoWriter_MSMF( const cv::String& filename, int fourcc,
|
||||||
double fps, cv::Size frameSize, int isColor )
|
double fps, cv::Size frameSize, int isColor )
|
||||||
{
|
{
|
||||||
cv::Ptr<CvVideoWriter_MSMF> writer = cv::makePtr<CvVideoWriter_MSMF>(filename, fourcc, fps, frameSize, isColor != 0);
|
cv::Ptr<CvVideoWriter_MSMF> writer = cv::makePtr<CvVideoWriter_MSMF>();
|
||||||
if (writer && writer->isOpened())
|
if (writer)
|
||||||
return writer;
|
{
|
||||||
|
writer->open(filename, fourcc, fps, frameSize, isColor != 0);
|
||||||
|
if (writer->isOpened())
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
return cv::Ptr<cv::IVideoWriter>();
|
return cv::Ptr<cv::IVideoWriter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,35 @@
|
|||||||
#include "PS1080.h"
|
#include "PS1080.h"
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static cv::Mutex initOpenNI2Mutex;
|
||||||
|
|
||||||
|
struct OpenNI2Initializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void init()
|
||||||
|
{
|
||||||
|
cv::AutoLock al(initOpenNI2Mutex);
|
||||||
|
static OpenNI2Initializer initializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenNI2Initializer()
|
||||||
|
{
|
||||||
|
// Initialize and configure the context.
|
||||||
|
openni::Status status = openni::OpenNI::initialize();
|
||||||
|
if (status != openni::STATUS_OK)
|
||||||
|
{
|
||||||
|
CV_Error(CV_StsError, std::string("Failed to initialize:") + openni::OpenNI::getExtendedError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~OpenNI2Initializer()
|
||||||
|
{
|
||||||
|
openni::OpenNI::shutdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class CvCapture_OpenNI2 : public CvCapture
|
class CvCapture_OpenNI2 : public CvCapture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -107,6 +136,8 @@ protected:
|
|||||||
|
|
||||||
static openni::VideoMode defaultStreamOutputMode(int stream);
|
static openni::VideoMode defaultStreamOutputMode(int stream);
|
||||||
|
|
||||||
|
CvCapture_OpenNI2(int index, const char * filename);
|
||||||
|
|
||||||
IplImage* retrieveDepthMap();
|
IplImage* retrieveDepthMap();
|
||||||
IplImage* retrievePointCloudMap();
|
IplImage* retrievePointCloudMap();
|
||||||
IplImage* retrieveDisparityMap();
|
IplImage* retrieveDisparityMap();
|
||||||
@ -116,8 +147,8 @@ protected:
|
|||||||
IplImage* retrieveGrayImage();
|
IplImage* retrieveGrayImage();
|
||||||
IplImage* retrieveIrImage();
|
IplImage* retrieveIrImage();
|
||||||
|
|
||||||
openni::Status toggleStream(int stream, bool toggle);
|
void toggleStream(int stream, bool toggle);
|
||||||
bool readCamerasParams();
|
void readCamerasParams();
|
||||||
|
|
||||||
double getDepthGeneratorProperty(int propIdx) const;
|
double getDepthGeneratorProperty(int propIdx) const;
|
||||||
bool setDepthGeneratorProperty(int propIdx, double propVal);
|
bool setDepthGeneratorProperty(int propIdx, double propVal);
|
||||||
@ -131,12 +162,11 @@ protected:
|
|||||||
// OpenNI context
|
// OpenNI context
|
||||||
openni::Device device;
|
openni::Device device;
|
||||||
bool isContextOpened;
|
bool isContextOpened;
|
||||||
openni::Recorder recorder;
|
|
||||||
|
|
||||||
// Data generators with its metadata
|
// Data generators with its metadata
|
||||||
openni::VideoStream streams[CV_MAX_NUM_STREAMS];
|
std::vector<openni::VideoStream> streams;
|
||||||
openni::VideoFrameRef streamFrames[CV_MAX_NUM_STREAMS];
|
std::vector<openni::VideoFrameRef> streamFrames;
|
||||||
cv::Mat streamImages[CV_MAX_NUM_STREAMS];
|
std::vector<cv::Mat> streamImages;
|
||||||
|
|
||||||
int maxBufferSize, maxTimeDuration; // for approx sync
|
int maxBufferSize, maxTimeDuration; // for approx sync
|
||||||
bool isCircleBuffer;
|
bool isCircleBuffer;
|
||||||
@ -191,80 +221,103 @@ openni::VideoMode CvCapture_OpenNI2::defaultStreamOutputMode(int stream)
|
|||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
CvCapture_OpenNI2::CvCapture_OpenNI2( int index )
|
|
||||||
|
CvCapture_OpenNI2::CvCapture_OpenNI2(int index) :
|
||||||
|
CvCapture_OpenNI2(index, nullptr)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
CvCapture_OpenNI2::CvCapture_OpenNI2(const char * filename) :
|
||||||
|
CvCapture_OpenNI2(-1, filename)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
CvCapture_OpenNI2::CvCapture_OpenNI2(int index, const char * filename) :
|
||||||
|
device(),
|
||||||
|
isContextOpened(false),
|
||||||
|
streams(CV_MAX_NUM_STREAMS),
|
||||||
|
streamFrames(CV_MAX_NUM_STREAMS),
|
||||||
|
streamImages(CV_MAX_NUM_STREAMS),
|
||||||
|
maxBufferSize(DEFAULT_MAX_BUFFER_SIZE),
|
||||||
|
maxTimeDuration(DEFAULT_MAX_TIME_DURATION),
|
||||||
|
isCircleBuffer(DEFAULT_IS_CIRCLE_BUFFER),
|
||||||
|
baseline(0),
|
||||||
|
depthFocalLength_VGA(0),
|
||||||
|
shadowValue(0),
|
||||||
|
noSampleValue(0),
|
||||||
|
outputMaps(outputMapsTypesCount)
|
||||||
{
|
{
|
||||||
const char* deviceURI = openni::ANY_DEVICE;
|
|
||||||
openni::Status status;
|
|
||||||
int deviceType = DEVICE_DEFAULT;
|
|
||||||
|
|
||||||
noSampleValue = shadowValue = 0;
|
|
||||||
|
|
||||||
isContextOpened = false;
|
|
||||||
maxBufferSize = DEFAULT_MAX_BUFFER_SIZE;
|
|
||||||
isCircleBuffer = DEFAULT_IS_CIRCLE_BUFFER;
|
|
||||||
maxTimeDuration = DEFAULT_MAX_TIME_DURATION;
|
|
||||||
|
|
||||||
if( index >= 10 )
|
|
||||||
{
|
|
||||||
deviceType = index / 10;
|
|
||||||
index %= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize and configure the context.
|
// Initialize and configure the context.
|
||||||
status = openni::OpenNI::initialize();
|
OpenNI2Initializer::init();
|
||||||
|
|
||||||
if (status != openni::STATUS_OK)
|
const char* deviceURI = openni::ANY_DEVICE;
|
||||||
|
bool needColor = true;
|
||||||
|
bool needIR = true;
|
||||||
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsError, cv::format("Failed to initialize:", openni::OpenNI::getExtendedError()));
|
int deviceType = DEVICE_DEFAULT;
|
||||||
return;
|
if (index >= 10)
|
||||||
}
|
{
|
||||||
|
deviceType = index / 10;
|
||||||
// find appropriate device URI
|
index %= 10;
|
||||||
openni::Array<openni::DeviceInfo> ldevs;
|
}
|
||||||
if (index > 0)
|
// Asus XTION and Occipital Structure Sensor do not have an image generator
|
||||||
{
|
needColor = (deviceType != DEVICE_ASUS_XTION);
|
||||||
openni::OpenNI::enumerateDevices(&ldevs);
|
|
||||||
deviceURI = ldevs[index].getUri();
|
// find appropriate device URI
|
||||||
|
openni::Array<openni::DeviceInfo> ldevs;
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
openni::OpenNI::enumerateDevices(&ldevs);
|
||||||
|
if (index < ldevs.getSize())
|
||||||
|
deviceURI = ldevs[index].getUri();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CV_Error(CV_StsError, "OpenCVKinect2: Device index exceeds the number of available OpenNI devices");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deviceURI = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openni::Status status;
|
||||||
status = device.open(deviceURI);
|
status = device.open(deviceURI);
|
||||||
if( status != openni::STATUS_OK )
|
|
||||||
{
|
|
||||||
CV_Error(CV_StsError, cv::format("OpenCVKinect: Device open failed see: %s\n", openni::OpenNI::getExtendedError()));
|
|
||||||
openni::OpenNI::shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = toggleStream(CV_DEPTH_STREAM, true);
|
|
||||||
// Asus XTION and Occipital Structure Sensor do not have an image generator
|
|
||||||
if (deviceType != DEVICE_ASUS_XTION)
|
|
||||||
status = openni::Status(status | toggleStream(CV_COLOR_STREAM, true));
|
|
||||||
if (status != openni::STATUS_OK)
|
if (status != openni::STATUS_OK)
|
||||||
{
|
{
|
||||||
openni::OpenNI::shutdown();
|
CV_Error(CV_StsError, std::string("OpenCVKinect2: Failed to open device: ") + openni::OpenNI::getExtendedError());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!readCamerasParams())
|
toggleStream(CV_DEPTH_STREAM, true);
|
||||||
{
|
if (needColor)
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::CvCapture_OpenNI2 : Could not read cameras parameters\n"));
|
toggleStream(CV_COLOR_STREAM, true);
|
||||||
return;
|
if (needIR)
|
||||||
}
|
toggleStream(CV_IR_STREAM, true);
|
||||||
|
|
||||||
|
|
||||||
outputMaps.resize( outputMapsTypesCount );
|
|
||||||
|
|
||||||
isContextOpened = true;
|
|
||||||
|
|
||||||
setProperty(CV_CAP_PROP_OPENNI_REGISTRATION, 1.0);
|
setProperty(CV_CAP_PROP_OPENNI_REGISTRATION, 1.0);
|
||||||
|
|
||||||
|
// default for Kinect2 camera
|
||||||
|
setProperty(CV_CAP_PROP_OPENNI2_MIRROR, 0.0);
|
||||||
|
|
||||||
|
isContextOpened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
openni::Status CvCapture_OpenNI2::toggleStream(int stream, bool toggle)
|
CvCapture_OpenNI2::~CvCapture_OpenNI2()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < streams.size(); ++i)
|
||||||
|
{
|
||||||
|
streamFrames[i].release();
|
||||||
|
streams[i].stop();
|
||||||
|
streams[i].destroy();
|
||||||
|
}
|
||||||
|
device.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CvCapture_OpenNI2::toggleStream(int stream, bool toggle)
|
||||||
{
|
{
|
||||||
openni::Status status;
|
openni::Status status;
|
||||||
|
|
||||||
// for logging
|
// for logging
|
||||||
static const char* stream_names[CV_MAX_NUM_STREAMS] = {
|
static const std::string stream_names[CV_MAX_NUM_STREAMS] = {
|
||||||
"depth",
|
"depth",
|
||||||
"color",
|
"color",
|
||||||
"IR"
|
"IR"
|
||||||
@ -280,140 +333,92 @@ openni::Status CvCapture_OpenNI2::toggleStream(int stream, bool toggle)
|
|||||||
{
|
{
|
||||||
// already opened
|
// already opened
|
||||||
if (streams[stream].isValid())
|
if (streams[stream].isValid())
|
||||||
return openni::STATUS_OK;
|
return;
|
||||||
|
|
||||||
// open stream
|
// open stream
|
||||||
status = streams[stream].create(device, stream_sensor_types[stream]);
|
status = streams[stream].create(device, stream_sensor_types[stream]);
|
||||||
if (status == openni::STATUS_OK)
|
if (status == openni::STATUS_OK)
|
||||||
{
|
{
|
||||||
// set video mode
|
// try to set up default stream mode (if available)
|
||||||
status = streams[stream].setVideoMode(defaultStreamOutputMode(stream)); // xn::DepthGenerator supports VGA only! (Jan 2011)
|
const openni::Array<openni::VideoMode>& vm = streams[stream].getSensorInfo().getSupportedVideoModes();
|
||||||
if (status != openni::STATUS_OK)
|
openni::VideoMode dm = defaultStreamOutputMode(stream);
|
||||||
|
for (int i = 0; i < vm.getSize(); i++)
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::CvCapture_OpenNI2 : Couldn't set %s stream output mode: %s\n",
|
if (vm[i].getPixelFormat() == dm.getPixelFormat() &&
|
||||||
stream_names[stream],
|
vm[i].getResolutionX() == dm.getResolutionX() &&
|
||||||
openni::OpenNI::getExtendedError()));
|
vm[i].getResolutionY() == dm.getResolutionY() &&
|
||||||
streams[stream].destroy();
|
vm[i].getFps() == dm.getFps())
|
||||||
return status;
|
{
|
||||||
|
status = streams[stream].setVideoMode(defaultStreamOutputMode(stream));
|
||||||
|
if (status != openni::STATUS_OK)
|
||||||
|
{
|
||||||
|
streams[stream].destroy();
|
||||||
|
CV_Error(CV_StsError, std::string("OpenCVKinect2 : Couldn't set ") +
|
||||||
|
stream_names[stream] + std::string(" stream output mode: ") +
|
||||||
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start stream
|
// start stream
|
||||||
status = streams[stream].start();
|
status = streams[stream].start();
|
||||||
if (status != openni::STATUS_OK)
|
if (status != openni::STATUS_OK)
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::CvCapture_OpenNI2 : Couldn't start %s stream: %s\n",
|
|
||||||
stream_names[stream],
|
|
||||||
openni::OpenNI::getExtendedError()));
|
|
||||||
streams[stream].destroy();
|
streams[stream].destroy();
|
||||||
return status;
|
CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::CvCapture_OpenNI2 : Couldn't start ") +
|
||||||
|
stream_names[stream] + std::string(" stream: ") +
|
||||||
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::CvCapture_OpenNI2 : Couldn't find %s stream:: %s\n",
|
CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::CvCapture_OpenNI2 : Couldn't find ") +
|
||||||
stream_names[stream],
|
stream_names[stream] + " stream: " +
|
||||||
openni::OpenNI::getExtendedError()));
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (streams[stream].isValid()) // want to close stream
|
else if (streams[stream].isValid()) // want to close stream
|
||||||
{
|
{
|
||||||
streams[stream].stop();
|
//FIX for libfreenect2
|
||||||
streams[stream].destroy();
|
//which stops the whole device when stopping only one stream
|
||||||
}
|
|
||||||
|
|
||||||
return openni::STATUS_OK;
|
//streams[stream].stop();
|
||||||
|
//streams[stream].destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CvCapture_OpenNI2::CvCapture_OpenNI2(const char * filename)
|
|
||||||
{
|
|
||||||
openni::Status status;
|
|
||||||
|
|
||||||
isContextOpened = false;
|
void CvCapture_OpenNI2::readCamerasParams()
|
||||||
maxBufferSize = DEFAULT_MAX_BUFFER_SIZE;
|
|
||||||
isCircleBuffer = DEFAULT_IS_CIRCLE_BUFFER;
|
|
||||||
maxTimeDuration = DEFAULT_MAX_TIME_DURATION;
|
|
||||||
|
|
||||||
// Initialize and configure the context.
|
|
||||||
status = openni::OpenNI::initialize();
|
|
||||||
|
|
||||||
if (status != openni::STATUS_OK)
|
|
||||||
{
|
|
||||||
CV_Error(CV_StsError, cv::format("Failed to initialize:", openni::OpenNI::getExtendedError()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open file
|
|
||||||
status = device.open(filename);
|
|
||||||
if( status != openni::STATUS_OK )
|
|
||||||
{
|
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::CvCapture_OpenNI2 : Failed to open input file (%s): %s\n", filename, openni::OpenNI::getExtendedError()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = openni::Status(toggleStream(CV_DEPTH_STREAM, true) | toggleStream(CV_COLOR_STREAM, true));
|
|
||||||
if (status != openni::STATUS_OK)
|
|
||||||
{
|
|
||||||
openni::OpenNI::shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !readCamerasParams() )
|
|
||||||
{
|
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::CvCapture_OpenNI2 : Could not read cameras parameters\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputMaps.resize( outputMapsTypesCount );
|
|
||||||
|
|
||||||
isContextOpened = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CvCapture_OpenNI2::~CvCapture_OpenNI2()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < CV_MAX_NUM_STREAMS; ++i)
|
|
||||||
{
|
|
||||||
streamFrames[i].release();
|
|
||||||
streams[i].stop();
|
|
||||||
streams[i].destroy();
|
|
||||||
}
|
|
||||||
device.close();
|
|
||||||
openni::OpenNI::shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CvCapture_OpenNI2::readCamerasParams()
|
|
||||||
{
|
{
|
||||||
double pixelSize = 0;
|
double pixelSize = 0;
|
||||||
if (streams[CV_DEPTH_STREAM].getProperty<double>(XN_STREAM_PROPERTY_ZERO_PLANE_PIXEL_SIZE, &pixelSize) != openni::STATUS_OK)
|
if (streams[CV_DEPTH_STREAM].getProperty<double>(XN_STREAM_PROPERTY_ZERO_PLANE_PIXEL_SIZE, &pixelSize) != openni::STATUS_OK)
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::readCamerasParams : Could not read pixel size!\n"));
|
CV_Error(CV_StsError, "CvCapture_OpenNI2::readCamerasParams : Could not read pixel size!" +
|
||||||
return false;
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// pixel size @ VGA = pixel size @ SXGA x 2
|
// pixel size @ VGA = pixel size @ SXGA x 2
|
||||||
pixelSize *= 2.0; // in mm
|
pixelSize *= 2.0; // in mm
|
||||||
|
|
||||||
// focal length of IR camera in pixels for VGA resolution
|
// focal length of IR camera in pixels for VGA resolution
|
||||||
int zeroPlanDistance; // in mm
|
unsigned long long zeroPlaneDistance; // in mm
|
||||||
if (streams[CV_DEPTH_STREAM].getProperty(XN_STREAM_PROPERTY_ZERO_PLANE_DISTANCE, &zeroPlanDistance) != openni::STATUS_OK)
|
if (streams[CV_DEPTH_STREAM].getProperty(XN_STREAM_PROPERTY_ZERO_PLANE_DISTANCE, &zeroPlaneDistance) != openni::STATUS_OK)
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::readCamerasParams : Could not read virtual plane distance!\n"));
|
CV_Error(CV_StsError, "CvCapture_OpenNI2::readCamerasParams : Could not read virtual plane distance!" +
|
||||||
return false;
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (streams[CV_DEPTH_STREAM].getProperty<double>(XN_STREAM_PROPERTY_EMITTER_DCMOS_DISTANCE, &baseline) != openni::STATUS_OK)
|
if (streams[CV_DEPTH_STREAM].getProperty<double>(XN_STREAM_PROPERTY_EMITTER_DCMOS_DISTANCE, &baseline) != openni::STATUS_OK)
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::readCamerasParams : Could not read base line!\n"));
|
CV_Error(CV_StsError, "CvCapture_OpenNI2::readCamerasParams : Could not read base line!" +
|
||||||
return false;
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// baseline from cm -> mm
|
// baseline from cm -> mm
|
||||||
baseline *= 10;
|
baseline *= 10;
|
||||||
|
|
||||||
// focal length from mm -> pixels (valid for 640x480)
|
// focal length from mm -> pixels (valid for 640x480)
|
||||||
depthFocalLength_VGA = (int)((double)zeroPlanDistance / (double)pixelSize);
|
depthFocalLength_VGA = (int)((double)zeroPlaneDistance / (double)pixelSize);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double CvCapture_OpenNI2::getProperty( int propIdx ) const
|
double CvCapture_OpenNI2::getProperty( int propIdx ) const
|
||||||
@ -500,7 +505,7 @@ double CvCapture_OpenNI2::getCommonProperty( int propIdx ) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default :
|
default :
|
||||||
CV_Error( CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for getting.\n", propIdx) );
|
CV_Error( CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for getting.", propIdx) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return propValue;
|
return propValue;
|
||||||
@ -525,14 +530,20 @@ bool CvCapture_OpenNI2::setCommonProperty( int propIdx, double propValue )
|
|||||||
// There is a set of properties that correspond to depth generator by default
|
// There is a set of properties that correspond to depth generator by default
|
||||||
// (is they are pass without particular generator flag).
|
// (is they are pass without particular generator flag).
|
||||||
case CV_CAP_PROP_OPENNI_REGISTRATION:
|
case CV_CAP_PROP_OPENNI_REGISTRATION:
|
||||||
isSet = setDepthGeneratorProperty( propIdx, propValue );
|
isSet = setDepthGeneratorProperty(propIdx, propValue);
|
||||||
break;
|
break;
|
||||||
case CV_CAP_PROP_OPENNI2_SYNC:
|
case CV_CAP_PROP_OPENNI2_SYNC:
|
||||||
isSet = device.setDepthColorSyncEnabled(propValue > 0.0) == openni::STATUS_OK;
|
isSet = device.setDepthColorSyncEnabled(propValue > 0.0) == openni::STATUS_OK;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CV_CAP_PROP_FRAME_WIDTH:
|
||||||
|
case CV_CAP_PROP_FRAME_HEIGHT:
|
||||||
|
case CV_CAP_PROP_AUTOFOCUS:
|
||||||
|
isSet = false;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
CV_Error( CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for setting.\n", propIdx) );
|
CV_Error(CV_StsBadArg, cv::format("Such parameter (propIdx=%d) isn't supported for setting.", propIdx));
|
||||||
}
|
}
|
||||||
|
|
||||||
return isSet;
|
return isSet;
|
||||||
@ -565,9 +576,13 @@ double CvCapture_OpenNI2::getDepthGeneratorProperty( int propIdx ) const
|
|||||||
propValue = streams[CV_DEPTH_STREAM].getMaxPixelValue();
|
propValue = streams[CV_DEPTH_STREAM].getMaxPixelValue();
|
||||||
break;
|
break;
|
||||||
case CV_CAP_PROP_OPENNI_BASELINE :
|
case CV_CAP_PROP_OPENNI_BASELINE :
|
||||||
|
if(baseline <= 0)
|
||||||
|
const_cast<CvCapture_OpenNI2*>(this)->readCamerasParams();
|
||||||
propValue = baseline;
|
propValue = baseline;
|
||||||
break;
|
break;
|
||||||
case CV_CAP_PROP_OPENNI_FOCAL_LENGTH :
|
case CV_CAP_PROP_OPENNI_FOCAL_LENGTH :
|
||||||
|
if(depthFocalLength_VGA <= 0)
|
||||||
|
const_cast<CvCapture_OpenNI2*>(this)->readCamerasParams();
|
||||||
propValue = (double)depthFocalLength_VGA;
|
propValue = (double)depthFocalLength_VGA;
|
||||||
break;
|
break;
|
||||||
case CV_CAP_PROP_OPENNI_REGISTRATION :
|
case CV_CAP_PROP_OPENNI_REGISTRATION :
|
||||||
@ -580,7 +595,7 @@ double CvCapture_OpenNI2::getDepthGeneratorProperty( int propIdx ) const
|
|||||||
propValue = streamFrames[CV_DEPTH_STREAM].getFrameIndex();
|
propValue = streamFrames[CV_DEPTH_STREAM].getFrameIndex();
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for getting.\n", propIdx) );
|
CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for getting.", propIdx) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return propValue;
|
return propValue;
|
||||||
@ -594,7 +609,10 @@ bool CvCapture_OpenNI2::setDepthGeneratorProperty( int propIdx, double propValue
|
|||||||
{
|
{
|
||||||
case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT:
|
case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT:
|
||||||
if (isContextOpened)
|
if (isContextOpened)
|
||||||
isSet = toggleStream(CV_DEPTH_STREAM, propValue > 0.0) == openni::STATUS_OK;
|
{
|
||||||
|
toggleStream(CV_DEPTH_STREAM, propValue > 0.0);
|
||||||
|
isSet = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CV_CAP_PROP_OPENNI_REGISTRATION:
|
case CV_CAP_PROP_OPENNI_REGISTRATION:
|
||||||
{
|
{
|
||||||
@ -612,12 +630,13 @@ bool CvCapture_OpenNI2::setDepthGeneratorProperty( int propIdx, double propValue
|
|||||||
{
|
{
|
||||||
openni::Status status = device.setImageRegistrationMode(mode);
|
openni::Status status = device.setImageRegistrationMode(mode);
|
||||||
if( status != openni::STATUS_OK )
|
if( status != openni::STATUS_OK )
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::setDepthGeneratorProperty : %s\n", openni::OpenNI::getExtendedError()));
|
CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setDepthGeneratorProperty: ") +
|
||||||
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
else
|
else
|
||||||
isSet = true;
|
isSet = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::setDepthGeneratorProperty : Unsupported viewpoint.\n"));
|
CV_Error(CV_StsError, "CvCapture_OpenNI2::setDepthGeneratorProperty: Unsupported viewpoint.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
isSet = true;
|
isSet = true;
|
||||||
@ -627,14 +646,15 @@ bool CvCapture_OpenNI2::setDepthGeneratorProperty( int propIdx, double propValue
|
|||||||
{
|
{
|
||||||
openni::Status status = device.setImageRegistrationMode(openni::IMAGE_REGISTRATION_OFF);
|
openni::Status status = device.setImageRegistrationMode(openni::IMAGE_REGISTRATION_OFF);
|
||||||
if( status != openni::STATUS_OK )
|
if( status != openni::STATUS_OK )
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::setDepthGeneratorProperty : %s\n", openni::OpenNI::getExtendedError()));
|
CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setDepthGeneratorProperty: ") +
|
||||||
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
else
|
else
|
||||||
isSet = true;
|
isSet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for setting.\n", propIdx) );
|
CV_Error( CV_StsBadArg, cv::format("Depth generator does not support such parameter (propIdx=%d) for setting.", propIdx) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return isSet;
|
return isSet;
|
||||||
@ -668,7 +688,7 @@ double CvCapture_OpenNI2::getImageGeneratorProperty( int propIdx ) const
|
|||||||
propValue = (double)streamFrames[CV_COLOR_STREAM].getFrameIndex();
|
propValue = (double)streamFrames[CV_COLOR_STREAM].getFrameIndex();
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.\n", propIdx) );
|
CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.", propIdx) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return propValue;
|
return propValue;
|
||||||
@ -682,7 +702,10 @@ bool CvCapture_OpenNI2::setImageGeneratorProperty(int propIdx, double propValue)
|
|||||||
{
|
{
|
||||||
case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT:
|
case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT:
|
||||||
if (isContextOpened)
|
if (isContextOpened)
|
||||||
isSet = toggleStream(CV_COLOR_STREAM, propValue > 0.0) == openni::STATUS_OK;
|
{
|
||||||
|
toggleStream(CV_COLOR_STREAM, propValue > 0.0);
|
||||||
|
isSet = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CV_CAP_PROP_OPENNI_OUTPUT_MODE :
|
case CV_CAP_PROP_OPENNI_OUTPUT_MODE :
|
||||||
{
|
{
|
||||||
@ -713,18 +736,19 @@ bool CvCapture_OpenNI2::setImageGeneratorProperty(int propIdx, double propValue)
|
|||||||
mode.setFps(60);
|
mode.setFps(60);
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
CV_Error( CV_StsBadArg, "Unsupported image generator output mode.\n");
|
CV_Error( CV_StsBadArg, "Unsupported image generator output mode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
openni::Status status = streams[CV_COLOR_STREAM].setVideoMode( mode );
|
openni::Status status = streams[CV_COLOR_STREAM].setVideoMode( mode );
|
||||||
if( status != openni::STATUS_OK )
|
if( status != openni::STATUS_OK )
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::setImageGeneratorProperty : %s\n", openni::OpenNI::getExtendedError()));
|
CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setImageGeneratorProperty: ") +
|
||||||
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
else
|
else
|
||||||
isSet = true;
|
isSet = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.\n", propIdx) );
|
CV_Error( CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.", propIdx) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return isSet;
|
return isSet;
|
||||||
@ -758,7 +782,7 @@ double CvCapture_OpenNI2::getIrGeneratorProperty(int propIdx) const
|
|||||||
propValue = (double)streamFrames[CV_IR_STREAM].getFrameIndex();
|
propValue = (double)streamFrames[CV_IR_STREAM].getFrameIndex();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
CV_Error(CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.\n", propIdx));
|
CV_Error(CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for getting.", propIdx));
|
||||||
}
|
}
|
||||||
|
|
||||||
return propValue;
|
return propValue;
|
||||||
@ -772,7 +796,10 @@ bool CvCapture_OpenNI2::setIrGeneratorProperty(int propIdx, double propValue)
|
|||||||
{
|
{
|
||||||
case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT:
|
case CV_CAP_PROP_OPENNI_GENERATOR_PRESENT:
|
||||||
if (isContextOpened)
|
if (isContextOpened)
|
||||||
isSet = toggleStream(CV_IR_STREAM, propValue > 0.0) == openni::STATUS_OK;
|
{
|
||||||
|
toggleStream(CV_IR_STREAM, propValue > 0.0);
|
||||||
|
isSet = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CV_CAP_PROP_OPENNI_OUTPUT_MODE:
|
case CV_CAP_PROP_OPENNI_OUTPUT_MODE:
|
||||||
{
|
{
|
||||||
@ -803,18 +830,19 @@ bool CvCapture_OpenNI2::setIrGeneratorProperty(int propIdx, double propValue)
|
|||||||
mode.setFps(60);
|
mode.setFps(60);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
CV_Error(CV_StsBadArg, "Unsupported image generator output mode.\n");
|
CV_Error(CV_StsBadArg, "Unsupported image generator output mode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
openni::Status status = streams[CV_IR_STREAM].setVideoMode(mode);
|
openni::Status status = streams[CV_IR_STREAM].setVideoMode(mode);
|
||||||
if (status != openni::STATUS_OK)
|
if (status != openni::STATUS_OK)
|
||||||
CV_Error(CV_StsError, cv::format("CvCapture_OpenNI2::setImageGeneratorProperty : %s\n", openni::OpenNI::getExtendedError()));
|
CV_Error(CV_StsError, std::string("CvCapture_OpenNI2::setImageGeneratorProperty: ") +
|
||||||
|
std::string(openni::OpenNI::getExtendedError()));
|
||||||
else
|
else
|
||||||
isSet = true;
|
isSet = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
CV_Error(CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.\n", propIdx));
|
CV_Error(CV_StsBadArg, cv::format("Image generator does not support such parameter (propIdx=%d) for setting.", propIdx));
|
||||||
}
|
}
|
||||||
|
|
||||||
return isSet;
|
return isSet;
|
||||||
@ -931,10 +959,12 @@ IplImage* CvCapture_OpenNI2::retrieveDisparityMap()
|
|||||||
if (!streamFrames[CV_DEPTH_STREAM].isValid())
|
if (!streamFrames[CV_DEPTH_STREAM].isValid())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
readCamerasParams();
|
||||||
|
|
||||||
cv::Mat disp32;
|
cv::Mat disp32;
|
||||||
computeDisparity_32F(streamFrames[CV_DEPTH_STREAM], disp32, baseline, depthFocalLength_VGA, noSampleValue, shadowValue);
|
computeDisparity_32F(streamFrames[CV_DEPTH_STREAM], disp32, baseline, depthFocalLength_VGA, noSampleValue, shadowValue);
|
||||||
|
|
||||||
disp32.convertTo( outputMaps[CV_CAP_OPENNI_DISPARITY_MAP].mat, CV_8UC1 );
|
disp32.convertTo(outputMaps[CV_CAP_OPENNI_DISPARITY_MAP].mat, CV_8UC1);
|
||||||
|
|
||||||
return outputMaps[CV_CAP_OPENNI_DISPARITY_MAP].getIplImagePtr();
|
return outputMaps[CV_CAP_OPENNI_DISPARITY_MAP].getIplImagePtr();
|
||||||
}
|
}
|
||||||
@ -944,6 +974,8 @@ IplImage* CvCapture_OpenNI2::retrieveDisparityMap_32F()
|
|||||||
if (!streamFrames[CV_DEPTH_STREAM].isValid())
|
if (!streamFrames[CV_DEPTH_STREAM].isValid())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
readCamerasParams();
|
||||||
|
|
||||||
computeDisparity_32F(streamFrames[CV_DEPTH_STREAM], outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].mat, baseline, depthFocalLength_VGA, noSampleValue, shadowValue);
|
computeDisparity_32F(streamFrames[CV_DEPTH_STREAM], outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].mat, baseline, depthFocalLength_VGA, noSampleValue, shadowValue);
|
||||||
|
|
||||||
return outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].getIplImagePtr();
|
return outputMaps[CV_CAP_OPENNI_DISPARITY_MAP_32F].getIplImagePtr();
|
||||||
@ -966,7 +998,7 @@ inline void getBGRImageFromMetaData( const openni::VideoFrameRef& imageMetaData,
|
|||||||
{
|
{
|
||||||
cv::Mat bufferImage;
|
cv::Mat bufferImage;
|
||||||
if( imageMetaData.getVideoMode().getPixelFormat() != openni::PIXEL_FORMAT_RGB888 )
|
if( imageMetaData.getVideoMode().getPixelFormat() != openni::PIXEL_FORMAT_RGB888 )
|
||||||
CV_Error( CV_StsUnsupportedFormat, "Unsupported format of grabbed image\n" );
|
CV_Error( CV_StsUnsupportedFormat, "Unsupported format of grabbed image." );
|
||||||
|
|
||||||
bgrImage.create(imageMetaData.getHeight(), imageMetaData.getWidth(), CV_8UC3);
|
bgrImage.create(imageMetaData.getHeight(), imageMetaData.getWidth(), CV_8UC3);
|
||||||
bufferImage.create(imageMetaData.getHeight(), imageMetaData.getWidth(), CV_8UC3);
|
bufferImage.create(imageMetaData.getHeight(), imageMetaData.getWidth(), CV_8UC3);
|
||||||
@ -989,7 +1021,7 @@ inline void getGrayImageFromMetaData(const openni::VideoFrameRef& imageMetaData,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CV_Error(CV_StsUnsupportedFormat, "Unsupported format of grabbed image\n");
|
CV_Error(CV_StsUnsupportedFormat, "Unsupported format of grabbed image.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,12 +75,12 @@ static const struct VideoBackendInfo builtin_backends[] =
|
|||||||
#ifdef HAVE_MSMF
|
#ifdef HAVE_MSMF
|
||||||
DECLARE_BACKEND(CAP_MSMF, "MSMF", MODE_CAPTURE_ALL | MODE_WRITER),
|
DECLARE_BACKEND(CAP_MSMF, "MSMF", MODE_CAPTURE_ALL | MODE_WRITER),
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_VFW
|
|
||||||
DECLARE_BACKEND(CAP_VFW, "VFW", MODE_CAPTURE_ALL | MODE_WRITER),
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_DSHOW
|
#ifdef HAVE_DSHOW
|
||||||
DECLARE_BACKEND(CAP_DSHOW, "DSHOW", MODE_CAPTURE_BY_INDEX),
|
DECLARE_BACKEND(CAP_DSHOW, "DSHOW", MODE_CAPTURE_BY_INDEX),
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_VFW
|
||||||
|
DECLARE_BACKEND(CAP_VFW, "VFW", MODE_CAPTURE_ALL | MODE_WRITER),
|
||||||
|
#endif
|
||||||
|
|
||||||
// Linux, some Unix
|
// Linux, some Unix
|
||||||
#if defined HAVE_CAMV4L2
|
#if defined HAVE_CAMV4L2
|
||||||
|
@ -4,444 +4,373 @@
|
|||||||
#include "opencv2/highgui.hpp"
|
#include "opencv2/highgui.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
class GStreamerPipeline
|
//================================================================================
|
||||||
|
|
||||||
|
template<typename M>
|
||||||
|
inline typename M::mapped_type getValue(const M &dict, const typename M::key_type &key, const string & errorMessage)
|
||||||
{
|
{
|
||||||
public:
|
typename M::const_iterator it = dict.find(key);
|
||||||
// Preprocessing arguments command line
|
if (it == dict.end())
|
||||||
GStreamerPipeline(int argc, char *argv[])
|
|
||||||
{
|
{
|
||||||
const string keys =
|
CV_Error(Error::StsBadArg, errorMessage);
|
||||||
"{h help usage ? | | print help messages }"
|
|
||||||
"{m mode | | coding mode (supported: encode, decode) }"
|
|
||||||
"{p pipeline |default | pipeline name (supported: 'default', 'gst-basic', 'gst-vaapi', 'gst-libav', 'ffmpeg') }"
|
|
||||||
"{cd codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }"
|
|
||||||
"{f file path | | path to file }"
|
|
||||||
"{vr resolution |720p | video resolution for encoding (supported: '720p', '1080p', '4k') }"
|
|
||||||
"{fps |30 | fix frame per second for encoding (supported: fps > 0) }"
|
|
||||||
"{fm fast | | fast measure fps }";
|
|
||||||
cmd_parser = new CommandLineParser(argc, argv, keys);
|
|
||||||
cmd_parser->about("This program shows how to read a video file with GStreamer pipeline with OpenCV.");
|
|
||||||
|
|
||||||
if (cmd_parser->has("help"))
|
|
||||||
{
|
|
||||||
cmd_parser->printMessage();
|
|
||||||
CV_Error(Error::StsBadArg, "Called help.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fast_measure = cmd_parser->has("fast"); // fast measure fps
|
|
||||||
fix_fps = cmd_parser->get<int>("fps"); // fixed frame per second
|
|
||||||
pipeline = cmd_parser->get<string>("pipeline"), // gstreamer pipeline type
|
|
||||||
mode = cmd_parser->get<string>("mode"), // coding mode
|
|
||||||
codec = cmd_parser->get<string>("codec"), // codec type
|
|
||||||
file_name = cmd_parser->get<string>("file"), // path to videofile
|
|
||||||
resolution = cmd_parser->get<string>("resolution"); // video resolution
|
|
||||||
|
|
||||||
size_t found = file_name.rfind(".");
|
|
||||||
if (found != string::npos)
|
|
||||||
{
|
|
||||||
container = file_name.substr(found + 1); // container type
|
|
||||||
}
|
|
||||||
else { CV_Error(Error::StsBadArg, "Can not parse container extension."); }
|
|
||||||
|
|
||||||
if (!cmd_parser->check())
|
|
||||||
{
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
CV_Error(Error::StsBadArg, "Failed parse arguments.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
~GStreamerPipeline() { delete cmd_parser; }
|
inline map<string, Size> sizeByResolution()
|
||||||
|
{
|
||||||
|
map<string, Size> res;
|
||||||
|
res["720p"] = Size(1280, 720);
|
||||||
|
res["1080p"] = Size(1920, 1080);
|
||||||
|
res["4k"] = Size(3840, 2160);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// Start pipeline
|
inline map<string, int> fourccByCodec()
|
||||||
int run()
|
{
|
||||||
|
map<string, int> res;
|
||||||
|
res["h264"] = VideoWriter::fourcc('H','2','6','4');
|
||||||
|
res["h265"] = VideoWriter::fourcc('H','E','V','C');
|
||||||
|
res["mpeg2"] = VideoWriter::fourcc('M','P','E','G');
|
||||||
|
res["mpeg4"] = VideoWriter::fourcc('M','P','4','2');
|
||||||
|
res["mjpeg"] = VideoWriter::fourcc('M','J','P','G');
|
||||||
|
res["vp8"] = VideoWriter::fourcc('V','P','8','0');
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> defaultEncodeElementByCodec()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["h264"] = "x264enc";
|
||||||
|
res["h265"] = "x265enc";
|
||||||
|
res["mpeg2"] = "mpeg2enc";
|
||||||
|
res["mjpeg"] = "jpegenc";
|
||||||
|
res["vp8"] = "vp8enc";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> VAAPIEncodeElementByCodec()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["h264"] = "parsebin ! vaapih264enc";
|
||||||
|
res["h265"] = "parsebin ! vaapih265enc";
|
||||||
|
res["mpeg2"] = "parsebin ! vaapimpeg2enc";
|
||||||
|
res["mjpeg"] = "parsebin ! vaapijpegenc";
|
||||||
|
res["vp8"] = "parsebin ! vaapivp8enc";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> mfxDecodeElementByCodec()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["h264"] = "parsebin ! mfxh264dec";
|
||||||
|
res["h265"] = "parsebin ! mfxhevcdec";
|
||||||
|
res["mpeg2"] = "parsebin ! mfxmpeg2dec";
|
||||||
|
res["mjpeg"] = "parsebin ! mfxjpegdec";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> mfxEncodeElementByCodec()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["h264"] = "mfxh264enc";
|
||||||
|
res["h265"] = "mfxhevcenc";
|
||||||
|
res["mpeg2"] = "mfxmpeg2enc";
|
||||||
|
res["mjpeg"] = "mfxjpegenc";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> libavDecodeElementByCodec()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["h264"] = "parsebin ! avdec_h264";
|
||||||
|
res["h265"] = "parsebin ! avdec_h265";
|
||||||
|
res["mpeg2"] = "parsebin ! avdec_mpeg2video";
|
||||||
|
res["mpeg4"] = "parsebin ! avdec_mpeg4";
|
||||||
|
res["mjpeg"] = "parsebin ! avdec_mjpeg";
|
||||||
|
res["vp8"] = "parsebin ! avdec_vp8";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> libavEncodeElementByCodec()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["h264"] = "avenc_h264";
|
||||||
|
res["h265"] = "avenc_h265";
|
||||||
|
res["mpeg2"] = "avenc_mpeg2video";
|
||||||
|
res["mpeg4"] = "avenc_mpeg4";
|
||||||
|
res["mjpeg"] = "avenc_mjpeg";
|
||||||
|
res["vp8"] = "avenc_vp8";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> demuxPluginByContainer()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["avi"] = "avidemux";
|
||||||
|
res["mp4"] = "qtdemux";
|
||||||
|
res["mov"] = "qtdemux";
|
||||||
|
res["mkv"] = "matroskademux";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline map<string, string> muxPluginByContainer()
|
||||||
|
{
|
||||||
|
map<string, string> res;
|
||||||
|
res["avi"] = "avimux";
|
||||||
|
res["mp4"] = "qtmux";
|
||||||
|
res["mov"] = "qtmux";
|
||||||
|
res["mkv"] = "matroskamux";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//================================================================================
|
||||||
|
|
||||||
|
inline string containerByName(const string &name)
|
||||||
|
{
|
||||||
|
size_t found = name.rfind(".");
|
||||||
|
if (found != string::npos)
|
||||||
{
|
{
|
||||||
if (mode == "decode") { if (createDecodePipeline() < 0) return -1; }
|
return name.substr(found + 1); // container type
|
||||||
else if (mode == "encode") { if (createEncodePipeline() < 0) return -1; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported mode: " << mode << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
cout << "_____________________________________" << endl;
|
|
||||||
cout << "Pipeline " << mode << ":" << endl;
|
|
||||||
cout << stream_pipeline.str() << endl;
|
|
||||||
// Choose a show video or only measure fps
|
|
||||||
cout << "_____________________________________" << endl;
|
|
||||||
cout << "Start measure frame per seconds (fps)" << endl;
|
|
||||||
cout << "Loading ..." << endl;
|
|
||||||
|
|
||||||
vector<double> tick_counts;
|
|
||||||
|
|
||||||
cout << "Start " << mode << ": " << file_name;
|
|
||||||
cout << " (" << pipeline << ")" << endl;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int64 temp_count_tick = 0;
|
|
||||||
if (mode == "decode")
|
|
||||||
{
|
|
||||||
Mat frame;
|
|
||||||
temp_count_tick = getTickCount();
|
|
||||||
cap >> frame;
|
|
||||||
temp_count_tick = getTickCount() - temp_count_tick;
|
|
||||||
if (frame.empty()) { break; }
|
|
||||||
}
|
|
||||||
else if (mode == "encode")
|
|
||||||
{
|
|
||||||
Mat element;
|
|
||||||
while(!cap.grab());
|
|
||||||
cap.retrieve(element);
|
|
||||||
temp_count_tick = getTickCount();
|
|
||||||
wrt << element;
|
|
||||||
temp_count_tick = getTickCount() - temp_count_tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
tick_counts.push_back(static_cast<double>(temp_count_tick));
|
|
||||||
if (((mode == "decode") && fast_measure && (tick_counts.size() > 1e3)) ||
|
|
||||||
((mode == "encode") && (tick_counts.size() > 3e3)) ||
|
|
||||||
((mode == "encode") && fast_measure && (tick_counts.size() > 1e2)))
|
|
||||||
{ break; }
|
|
||||||
|
|
||||||
}
|
|
||||||
double time_fps = sum(tick_counts)[0] / getTickFrequency();
|
|
||||||
|
|
||||||
if (tick_counts.size() != 0)
|
|
||||||
{
|
|
||||||
cout << "Finished: " << tick_counts.size() << " in " << time_fps <<" sec ~ " ;
|
|
||||||
cout << tick_counts.size() / time_fps <<" fps " << endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Failed " << mode << ": " << file_name;
|
|
||||||
cout << " (" << pipeline << ")" << endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
// Free video resource
|
//================================================================================
|
||||||
void close()
|
|
||||||
|
inline Ptr<VideoCapture> createCapture(const string &backend, const string &file_name, const string &codec)
|
||||||
|
{
|
||||||
|
if (backend == "gst-default")
|
||||||
{
|
{
|
||||||
cap.release();
|
cout << "Created GStreamer capture ( " << file_name << " )" << endl;
|
||||||
wrt.release();
|
return makePtr<VideoCapture>(file_name, CAP_GSTREAMER);
|
||||||
}
|
}
|
||||||
|
else if (backend.find("gst") == 0)
|
||||||
private:
|
|
||||||
// Choose the constructed GStreamer pipeline for decode
|
|
||||||
int createDecodePipeline()
|
|
||||||
{
|
{
|
||||||
if (pipeline == "default") {
|
ostringstream line;
|
||||||
cap = VideoCapture(file_name, CAP_GSTREAMER);
|
line << "filesrc location=\"" << file_name << "\"";
|
||||||
}
|
line << " ! ";
|
||||||
else if (pipeline.find("gst") == 0)
|
line << getValue(demuxPluginByContainer(), containerByName(file_name), "Invalid container");
|
||||||
{
|
line << " ! ";
|
||||||
stream_pipeline << "filesrc location=\"" << file_name << "\"";
|
if (backend.find("basic") == 4)
|
||||||
stream_pipeline << " ! " << getGstMuxPlugin();
|
line << "decodebin";
|
||||||
|
else if (backend.find("vaapi") == 4)
|
||||||
if (pipeline.find("basic") == 4)
|
line << "vaapidecodebin";
|
||||||
{
|
else if (backend.find("libav") == 4)
|
||||||
stream_pipeline << getGstDefaultCodePlugin();
|
line << getValue(libavDecodeElementByCodec(), codec, "Invalid codec");
|
||||||
}
|
else if (backend.find("mfx") == 4)
|
||||||
else if (pipeline.find("vaapi1710") == 4)
|
line << getValue(mfxDecodeElementByCodec(), codec, "Invalid or unsupported codec");
|
||||||
{
|
|
||||||
stream_pipeline << getGstVaapiCodePlugin();
|
|
||||||
}
|
|
||||||
else if (pipeline.find("libav") == 4)
|
|
||||||
{
|
|
||||||
stream_pipeline << getGstAvCodePlugin();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_pipeline << " ! videoconvert n-threads=" << getNumThreads();
|
|
||||||
stream_pipeline << " ! appsink sync=false";
|
|
||||||
cap = VideoCapture(stream_pipeline.str(), CAP_GSTREAMER);
|
|
||||||
}
|
|
||||||
else if (pipeline == "ffmpeg")
|
|
||||||
{
|
|
||||||
cap = VideoCapture(file_name, CAP_FFMPEG);
|
|
||||||
stream_pipeline << "default pipeline for ffmpeg" << endl;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
return Ptr<VideoCapture>();
|
||||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
line << " ! videoconvert n-threads=" << getNumThreads();
|
||||||
cmd_parser->printErrors();
|
line << " ! appsink sync=false";
|
||||||
return -1;
|
cout << "Created GStreamer capture ( " << line.str() << " )" << endl;
|
||||||
}
|
return makePtr<VideoCapture>(line.str(), CAP_GSTREAMER);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
else if (backend == "ffmpeg")
|
||||||
// Choose the constructed GStreamer pipeline for encode
|
|
||||||
int createEncodePipeline()
|
|
||||||
{
|
{
|
||||||
if (checkConfiguration() < 0) return -1;
|
cout << "Created FFmpeg capture ( " << file_name << " )" << endl;
|
||||||
ostringstream test_pipeline;
|
return makePtr<VideoCapture>(file_name, CAP_FFMPEG);
|
||||||
test_pipeline << "videotestsrc pattern=smpte";
|
|
||||||
test_pipeline << " ! video/x-raw, " << getVideoSettings();
|
|
||||||
test_pipeline << " ! appsink sync=false";
|
|
||||||
cap = VideoCapture(test_pipeline.str(), CAP_GSTREAMER);
|
|
||||||
|
|
||||||
if (pipeline == "default") {
|
|
||||||
wrt = VideoWriter(file_name, CAP_GSTREAMER, getFourccCode(), fix_fps, fix_size, true);
|
|
||||||
}
|
|
||||||
else if (pipeline.find("gst") == 0)
|
|
||||||
{
|
|
||||||
stream_pipeline << "appsrc ! videoconvert n-threads=" << getNumThreads() << " ! ";
|
|
||||||
|
|
||||||
if (pipeline.find("basic") == 4)
|
|
||||||
{
|
|
||||||
stream_pipeline << getGstDefaultCodePlugin();
|
|
||||||
}
|
|
||||||
else if (pipeline.find("vaapi1710") == 4)
|
|
||||||
{
|
|
||||||
stream_pipeline << getGstVaapiCodePlugin();
|
|
||||||
}
|
|
||||||
else if (pipeline.find("libav") == 4)
|
|
||||||
{
|
|
||||||
stream_pipeline << getGstAvCodePlugin();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_pipeline << " ! " << getGstMuxPlugin();
|
|
||||||
stream_pipeline << " ! filesink location=\"" << file_name << "\"";
|
|
||||||
wrt = VideoWriter(stream_pipeline.str(), CAP_GSTREAMER, 0, fix_fps, fix_size, true);
|
|
||||||
}
|
|
||||||
else if (pipeline == "ffmpeg")
|
|
||||||
{
|
|
||||||
wrt = VideoWriter(file_name, CAP_FFMPEG, getFourccCode(), fix_fps, fix_size, true);
|
|
||||||
stream_pipeline << "default pipeline for ffmpeg" << endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported pipeline: " << pipeline << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return Ptr<VideoCapture>();
|
||||||
|
}
|
||||||
|
|
||||||
// Choose video resolution for encoding
|
inline Ptr<VideoCapture> createSynthSource(Size sz, unsigned fps)
|
||||||
string getVideoSettings()
|
{
|
||||||
|
ostringstream line;
|
||||||
|
line << "videotestsrc pattern=smpte";
|
||||||
|
line << " ! video/x-raw";
|
||||||
|
line << ",width=" << sz.width << ",height=" << sz.height;
|
||||||
|
if (fps > 0)
|
||||||
|
line << ",framerate=" << fps << "/1";
|
||||||
|
line << " ! appsink sync=false";
|
||||||
|
cout << "Created synthetic video source ( " << line.str() << " )" << endl;
|
||||||
|
return makePtr<VideoCapture>(line.str(), CAP_GSTREAMER);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Ptr<VideoWriter> createWriter(const string &backend, const string &file_name, const string &codec, Size sz, unsigned fps)
|
||||||
|
{
|
||||||
|
if (backend == "gst-default")
|
||||||
{
|
{
|
||||||
ostringstream video_size;
|
cout << "Created GStreamer writer ( " << file_name << ", FPS=" << fps << ", Size=" << sz << ")" << endl;
|
||||||
if (fix_fps > 0) { video_size << "framerate=" << fix_fps << "/1, "; }
|
return makePtr<VideoWriter>(file_name, CAP_GSTREAMER, getValue(fourccByCodec(), codec, "Invalid codec"), fps, sz, true);
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported fps (< 0): " << fix_fps << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resolution == "720p") { fix_size = Size(1280, 720); }
|
|
||||||
else if (resolution == "1080p") { fix_size = Size(1920, 1080); }
|
|
||||||
else if (resolution == "4k") { fix_size = Size(3840, 2160); }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported video resolution: " << resolution << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
video_size << "width=" << fix_size.width << ", height=" << fix_size.height;
|
|
||||||
return video_size.str();
|
|
||||||
}
|
}
|
||||||
|
else if (backend.find("gst") == 0)
|
||||||
// Choose a video container
|
|
||||||
string getGstMuxPlugin()
|
|
||||||
{
|
{
|
||||||
ostringstream plugin;
|
ostringstream line;
|
||||||
if (container == "avi") { plugin << "avi"; }
|
line << "appsrc ! videoconvert n-threads=" << getNumThreads() << " ! ";
|
||||||
else if (container == "mp4") { plugin << "qt"; }
|
if (backend.find("basic") == 4)
|
||||||
else if (container == "mov") { plugin << "qt"; }
|
line << getValue(defaultEncodeElementByCodec(), codec, "Invalid codec");
|
||||||
else if (container == "mkv") { plugin << "matroska"; }
|
else if (backend.find("vaapi") == 4)
|
||||||
|
line << getValue(VAAPIEncodeElementByCodec(), codec, "Invalid codec");
|
||||||
|
else if (backend.find("libav") == 4)
|
||||||
|
line << getValue(libavEncodeElementByCodec(), codec, "Invalid codec");
|
||||||
|
else if (backend.find("mfx") == 4)
|
||||||
|
line << getValue(mfxEncodeElementByCodec(), codec, "Invalid codec");
|
||||||
else
|
else
|
||||||
{
|
return Ptr<VideoWriter>();
|
||||||
cout << "Unsupported container: " << container << endl;
|
line << " ! ";
|
||||||
cmd_parser->printErrors();
|
line << getValue(muxPluginByContainer(), containerByName(file_name), "Invalid container");
|
||||||
return string();
|
line << " ! ";
|
||||||
}
|
line << "filesink location=\"" << file_name << "\"";
|
||||||
|
cout << "Created GStreamer writer ( " << line.str() << " )" << endl;
|
||||||
if (mode == "decode") { plugin << "demux"; }
|
return makePtr<VideoWriter>(line.str(), CAP_GSTREAMER, 0, fps, sz, true);
|
||||||
else if (mode == "encode") { plugin << "mux"; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported mode: " << mode << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugin.str();
|
|
||||||
}
|
}
|
||||||
|
else if (backend == "ffmpeg")
|
||||||
// Choose a libav codec
|
|
||||||
string getGstAvCodePlugin()
|
|
||||||
{
|
{
|
||||||
ostringstream plugin;
|
cout << "Created FFMpeg writer ( " << file_name << ", FPS=" << fps << ", Size=" << sz << " )" << endl;
|
||||||
if (mode == "decode")
|
return makePtr<VideoWriter>(file_name, CAP_FFMPEG, getValue(fourccByCodec(), codec, "Invalid codec"), fps, sz, true);
|
||||||
{
|
|
||||||
if (codec == "h264") { plugin << "h264parse ! "; }
|
|
||||||
else if (codec == "h265") { plugin << "h265parse ! "; }
|
|
||||||
plugin << "avdec_";
|
|
||||||
}
|
|
||||||
else if (mode == "encode") { plugin << "avenc_"; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported mode: " << mode << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codec == "h264") { plugin << "h264"; }
|
|
||||||
else if (codec == "h265") { plugin << "h265"; }
|
|
||||||
else if (codec == "mpeg2") { plugin << "mpeg2video"; }
|
|
||||||
else if (codec == "mpeg4") { plugin << "mpeg4"; }
|
|
||||||
else if (codec == "mjpeg") { plugin << "mjpeg"; }
|
|
||||||
else if (codec == "vp8") { plugin << "vp8"; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported libav codec: " << codec << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
return plugin.str();
|
|
||||||
}
|
}
|
||||||
|
return Ptr<VideoWriter>();
|
||||||
|
}
|
||||||
|
|
||||||
// Choose a vaapi codec
|
//================================================================================
|
||||||
string getGstVaapiCodePlugin()
|
|
||||||
{
|
|
||||||
ostringstream plugin;
|
|
||||||
if (mode == "decode")
|
|
||||||
{
|
|
||||||
plugin << "vaapidecodebin";
|
|
||||||
if (container == "mkv") { plugin << " ! autovideoconvert"; }
|
|
||||||
else { plugin << " ! video/x-raw, format=YV12"; }
|
|
||||||
}
|
|
||||||
else if (mode == "encode")
|
|
||||||
{
|
|
||||||
if (codec == "h264") { plugin << "vaapih264enc"; }
|
|
||||||
else if (codec == "h265") { plugin << "vaapih265enc"; }
|
|
||||||
else if (codec == "mpeg2") { plugin << "vaapimpeg2enc"; }
|
|
||||||
else if (codec == "mjpeg") { plugin << "vaapijpegenc"; }
|
|
||||||
else if (codec == "vp8") { plugin << "vaapivp8enc"; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported vaapi codec: " << codec << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported mode: " << resolution << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
return plugin.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Choose a default codec
|
|
||||||
string getGstDefaultCodePlugin()
|
|
||||||
{
|
|
||||||
ostringstream plugin;
|
|
||||||
if (mode == "decode")
|
|
||||||
{
|
|
||||||
plugin << " ! decodebin";
|
|
||||||
}
|
|
||||||
else if (mode == "encode")
|
|
||||||
{
|
|
||||||
if (codec == "h264") { plugin << "x264enc"; }
|
|
||||||
else if (codec == "h265") { plugin << "x265enc"; }
|
|
||||||
else if (codec == "mpeg2") { plugin << "mpeg2enc"; }
|
|
||||||
else if (codec == "mjpeg") { plugin << "jpegenc"; }
|
|
||||||
else if (codec == "vp8") { plugin << "vp8enc"; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported default codec: " << codec << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported mode: " << resolution << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
return plugin.str();
|
|
||||||
}
|
|
||||||
// Get fourcc for codec
|
|
||||||
int getFourccCode()
|
|
||||||
{
|
|
||||||
if (codec == "h264") { return VideoWriter::fourcc('H','2','6','4'); }
|
|
||||||
else if (codec == "h265") { return VideoWriter::fourcc('H','E','V','C'); }
|
|
||||||
else if (codec == "mpeg2") { return VideoWriter::fourcc('M','P','E','G'); }
|
|
||||||
else if (codec == "mpeg4") { return VideoWriter::fourcc('M','P','4','2'); }
|
|
||||||
else if (codec == "mjpeg") { return VideoWriter::fourcc('M','J','P','G'); }
|
|
||||||
else if (codec == "vp8") { return VideoWriter::fourcc('V','P','8','0'); }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unsupported ffmpeg codec: " << codec << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check bad configuration
|
|
||||||
int checkConfiguration()
|
|
||||||
{
|
|
||||||
if ((codec == "mpeg2" && getGstMuxPlugin() == "qtmux") ||
|
|
||||||
(codec == "h265" && getGstMuxPlugin() == "avimux") ||
|
|
||||||
(pipeline == "gst-libav" && (codec == "h264" || codec == "h265")) ||
|
|
||||||
(pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="4k") ||
|
|
||||||
(pipeline == "gst-vaapi1710" && codec=="mpeg2" && resolution=="1080p" && fix_fps > 30))
|
|
||||||
{
|
|
||||||
cout << "Unsupported configuration" << endl;
|
|
||||||
cmd_parser->printErrors();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fast_measure; // fast measure fps
|
|
||||||
string pipeline, // gstreamer pipeline type
|
|
||||||
container, // container type
|
|
||||||
mode, // coding mode
|
|
||||||
codec, // codec type
|
|
||||||
file_name, // path to videofile
|
|
||||||
resolution; // video resolution
|
|
||||||
int fix_fps; // fixed frame per second
|
|
||||||
Size fix_size; // fixed frame size
|
|
||||||
VideoWriter wrt;
|
|
||||||
VideoCapture cap;
|
|
||||||
ostringstream stream_pipeline;
|
|
||||||
CommandLineParser* cmd_parser;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
const string keys =
|
||||||
|
"{h help usage ? | | print help messages }"
|
||||||
|
"{m mode |decode | coding mode (supported: encode, decode) }"
|
||||||
|
"{b backend |default | video backend (supported: 'gst-default', 'gst-basic', 'gst-vaapi', 'gst-libav', 'gst-mfx', 'ffmpeg') }"
|
||||||
|
"{c codec |h264 | codec name (supported: 'h264', 'h265', 'mpeg2', 'mpeg4', 'mjpeg', 'vp8') }"
|
||||||
|
"{f file path | | path to file }"
|
||||||
|
"{r resolution |720p | video resolution for encoding (supported: '720p', '1080p', '4k') }"
|
||||||
|
"{fps |30 | fix frame per second for encoding (supported: fps > 0) }"
|
||||||
|
"{fast | | fast measure fps }";
|
||||||
|
CommandLineParser cmd_parser(argc, argv, keys);
|
||||||
|
cmd_parser.about("This program measures performance of video encoding and decoding using different backends OpenCV.");
|
||||||
|
if (cmd_parser.has("help"))
|
||||||
|
{
|
||||||
|
cmd_parser.printMessage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
bool fast_measure = cmd_parser.has("fast"); // fast measure fps
|
||||||
|
unsigned fix_fps = cmd_parser.get<unsigned>("fps"); // fixed frame per second
|
||||||
|
string backend = cmd_parser.get<string>("backend"); // video backend
|
||||||
|
string mode = cmd_parser.get<string>("mode"); // coding mode
|
||||||
|
string codec = cmd_parser.get<string>("codec"); // codec type
|
||||||
|
string file_name = cmd_parser.get<string>("file"); // path to videofile
|
||||||
|
string resolution = cmd_parser.get<string>("resolution"); // video resolution
|
||||||
|
if (!cmd_parser.check())
|
||||||
|
{
|
||||||
|
cmd_parser.printErrors();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (mode != "encode" && mode != "decode")
|
||||||
|
{
|
||||||
|
cout << "Unsupported mode: " << mode << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cout << "Mode: " << mode << ", Backend: " << backend << ", File: " << file_name << ", Codec: " << codec << endl;
|
||||||
|
|
||||||
|
TickMeter total;
|
||||||
|
Ptr<VideoCapture> cap;
|
||||||
|
Ptr<VideoWriter> wrt;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
GStreamerPipeline pipe(argc, argv);
|
if (mode == "decode")
|
||||||
return pipe.run();
|
{
|
||||||
|
cap = createCapture(backend, file_name, codec);
|
||||||
|
if (!cap)
|
||||||
|
{
|
||||||
|
cout << "Failed to create video capture" << endl;
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
if (!cap->isOpened())
|
||||||
|
{
|
||||||
|
cout << "Capture is not opened" << endl;
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mode == "encode")
|
||||||
|
{
|
||||||
|
Size sz = getValue(sizeByResolution(), resolution, "Invalid resolution");
|
||||||
|
cout << "FPS: " << fix_fps << ", Frame size: " << sz << endl;
|
||||||
|
cap = createSynthSource(sz, fix_fps);
|
||||||
|
wrt = createWriter(backend, file_name, codec, sz, fix_fps);
|
||||||
|
if (!cap || !wrt)
|
||||||
|
{
|
||||||
|
cout << "Failed to create synthetic video source or video writer" << endl;
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
if (!cap->isOpened() || !wrt->isOpened())
|
||||||
|
{
|
||||||
|
cout << "Synthetic video source or video writer is not opened" << endl;
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(const Exception& e)
|
catch (...)
|
||||||
{
|
{
|
||||||
cerr << e.what() << endl;
|
cout << "Unsupported parameters" << endl;
|
||||||
return 1;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TickMeter tick;
|
||||||
|
Mat frame;
|
||||||
|
Mat element;
|
||||||
|
total.start();
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
if (mode == "decode")
|
||||||
|
{
|
||||||
|
tick.start();
|
||||||
|
if (!cap->grab())
|
||||||
|
{
|
||||||
|
cout << "No more frames - break" << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!cap->retrieve(frame))
|
||||||
|
{
|
||||||
|
cout << "Failed to retrieve frame - break" << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (frame.empty())
|
||||||
|
{
|
||||||
|
cout << "Empty frame received - break" << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tick.stop();
|
||||||
|
}
|
||||||
|
else if (mode == "encode")
|
||||||
|
{
|
||||||
|
int limit = 100;
|
||||||
|
while (!cap->grab() && --limit != 0)
|
||||||
|
{
|
||||||
|
cout << "Skipping empty input frame - " << limit << endl;
|
||||||
|
}
|
||||||
|
cap->retrieve(element);
|
||||||
|
tick.start();
|
||||||
|
*wrt << element;
|
||||||
|
tick.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fast_measure && tick.getCounter() >= 1000)
|
||||||
|
{
|
||||||
|
cout << "Fast mode frame limit reached - break" << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mode == "encode" && tick.getCounter() >= 1000)
|
||||||
|
{
|
||||||
|
cout << "Encode frame limit reached - break" << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total.stop();
|
||||||
|
if (tick.getCounter() == 0)
|
||||||
|
{
|
||||||
|
cout << "No frames have been processed" << endl;
|
||||||
|
return -10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double res_fps = tick.getCounter() / tick.getTimeSec();
|
||||||
|
cout << tick.getCounter() << " frames in " << tick.getTimeSec() << " sec ~ " << res_fps << " FPS" << " (total time: " << total.getTimeSec() << " sec)" << endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
/**
|
/**
|
||||||
* @function Watershed_and_Distance_Transform.cpp
|
|
||||||
* @brief Sample code showing how to segment overlapping objects using Laplacian filtering, in addition to Watershed and Distance Transformation
|
* @brief Sample code showing how to segment overlapping objects using Laplacian filtering, in addition to Watershed and Distance Transformation
|
||||||
* @author OpenCV Team
|
* @author OpenCV Team
|
||||||
*/
|
*/
|
||||||
@ -12,43 +11,47 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
int main()
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
//! [load_image]
|
//! [load_image]
|
||||||
// Load the image
|
// Load the image
|
||||||
Mat src = imread("../data/cards.png");
|
CommandLineParser parser( argc, argv, "{@input | ../data/cards.png | input image}" );
|
||||||
|
Mat src = imread( parser.get<String>( "@input" ) );
|
||||||
// Check if everything was fine
|
if( src.empty() )
|
||||||
if (!src.data)
|
{
|
||||||
|
cout << "Could not open or find the image!\n" << endl;
|
||||||
|
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Show source image
|
// Show source image
|
||||||
imshow("Source Image", src);
|
imshow("Source Image", src);
|
||||||
//! [load_image]
|
//! [load_image]
|
||||||
|
|
||||||
//! [black_bg]
|
//! [black_bg]
|
||||||
// Change the background from white to black, since that will help later to extract
|
// Change the background from white to black, since that will help later to extract
|
||||||
// better results during the use of Distance Transform
|
// better results during the use of Distance Transform
|
||||||
for( int x = 0; x < src.rows; x++ ) {
|
for ( int i = 0; i < src.rows; i++ ) {
|
||||||
for( int y = 0; y < src.cols; y++ ) {
|
for ( int j = 0; j < src.cols; j++ ) {
|
||||||
if ( src.at<Vec3b>(x, y) == Vec3b(255,255,255) ) {
|
if ( src.at<Vec3b>(i, j) == Vec3b(255,255,255) )
|
||||||
src.at<Vec3b>(x, y)[0] = 0;
|
{
|
||||||
src.at<Vec3b>(x, y)[1] = 0;
|
src.at<Vec3b>(i, j)[0] = 0;
|
||||||
src.at<Vec3b>(x, y)[2] = 0;
|
src.at<Vec3b>(i, j)[1] = 0;
|
||||||
}
|
src.at<Vec3b>(i, j)[2] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show output image
|
// Show output image
|
||||||
imshow("Black Background Image", src);
|
imshow("Black Background Image", src);
|
||||||
//! [black_bg]
|
//! [black_bg]
|
||||||
|
|
||||||
//! [sharp]
|
//! [sharp]
|
||||||
// Create a kernel that we will use for accuting/sharpening our image
|
// Create a kernel that we will use to sharpen our image
|
||||||
Mat kernel = (Mat_<float>(3,3) <<
|
Mat kernel = (Mat_<float>(3,3) <<
|
||||||
1, 1, 1,
|
1, 1, 1,
|
||||||
1, -8, 1,
|
1, -8, 1,
|
||||||
1, 1, 1); // an approximation of second derivative, a quite strong kernel
|
1, 1, 1); // an approximation of second derivative, a quite strong kernel
|
||||||
|
|
||||||
// do the laplacian filtering as it is
|
// do the laplacian filtering as it is
|
||||||
// well, we need to convert everything in something more deeper then CV_8U
|
// well, we need to convert everything in something more deeper then CV_8U
|
||||||
@ -57,8 +60,8 @@ int main()
|
|||||||
// BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
|
// BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
|
||||||
// so the possible negative number will be truncated
|
// so the possible negative number will be truncated
|
||||||
Mat imgLaplacian;
|
Mat imgLaplacian;
|
||||||
Mat sharp = src; // copy source image to another temporary one
|
filter2D(src, imgLaplacian, CV_32F, kernel);
|
||||||
filter2D(sharp, imgLaplacian, CV_32F, kernel);
|
Mat sharp;
|
||||||
src.convertTo(sharp, CV_32F);
|
src.convertTo(sharp, CV_32F);
|
||||||
Mat imgResult = sharp - imgLaplacian;
|
Mat imgResult = sharp - imgLaplacian;
|
||||||
|
|
||||||
@ -68,41 +71,39 @@ int main()
|
|||||||
|
|
||||||
// imshow( "Laplace Filtered Image", imgLaplacian );
|
// imshow( "Laplace Filtered Image", imgLaplacian );
|
||||||
imshow( "New Sharped Image", imgResult );
|
imshow( "New Sharped Image", imgResult );
|
||||||
//! [sharp]
|
//! [sharp]
|
||||||
|
|
||||||
src = imgResult; // copy back
|
//! [bin]
|
||||||
|
|
||||||
//! [bin]
|
|
||||||
// Create binary image from source image
|
// Create binary image from source image
|
||||||
Mat bw;
|
Mat bw;
|
||||||
cvtColor(src, bw, COLOR_BGR2GRAY);
|
cvtColor(imgResult, bw, COLOR_BGR2GRAY);
|
||||||
threshold(bw, bw, 40, 255, THRESH_BINARY | THRESH_OTSU);
|
threshold(bw, bw, 40, 255, THRESH_BINARY | THRESH_OTSU);
|
||||||
imshow("Binary Image", bw);
|
imshow("Binary Image", bw);
|
||||||
//! [bin]
|
//! [bin]
|
||||||
|
|
||||||
//! [dist]
|
//! [dist]
|
||||||
// Perform the distance transform algorithm
|
// Perform the distance transform algorithm
|
||||||
Mat dist;
|
Mat dist;
|
||||||
distanceTransform(bw, dist, DIST_L2, 3);
|
distanceTransform(bw, dist, DIST_L2, 3);
|
||||||
|
|
||||||
// Normalize the distance image for range = {0.0, 1.0}
|
// Normalize the distance image for range = {0.0, 1.0}
|
||||||
// so we can visualize and threshold it
|
// so we can visualize and threshold it
|
||||||
normalize(dist, dist, 0, 1., NORM_MINMAX);
|
normalize(dist, dist, 0, 1.0, NORM_MINMAX);
|
||||||
imshow("Distance Transform Image", dist);
|
imshow("Distance Transform Image", dist);
|
||||||
//! [dist]
|
//! [dist]
|
||||||
|
|
||||||
//! [peaks]
|
//! [peaks]
|
||||||
// Threshold to obtain the peaks
|
// Threshold to obtain the peaks
|
||||||
// This will be the markers for the foreground objects
|
// This will be the markers for the foreground objects
|
||||||
threshold(dist, dist, .4, 1., THRESH_BINARY);
|
threshold(dist, dist, 0.4, 1.0, THRESH_BINARY);
|
||||||
|
|
||||||
// Dilate a bit the dist image
|
// Dilate a bit the dist image
|
||||||
Mat kernel1 = Mat::ones(3, 3, CV_8UC1);
|
Mat kernel1 = Mat::ones(3, 3, CV_8U);
|
||||||
dilate(dist, dist, kernel1);
|
dilate(dist, dist, kernel1);
|
||||||
imshow("Peaks", dist);
|
imshow("Peaks", dist);
|
||||||
//! [peaks]
|
//! [peaks]
|
||||||
|
|
||||||
//! [seeds]
|
//! [seeds]
|
||||||
// Create the CV_8U version of the distance image
|
// Create the CV_8U version of the distance image
|
||||||
// It is needed for findContours()
|
// It is needed for findContours()
|
||||||
Mat dist_8u;
|
Mat dist_8u;
|
||||||
@ -113,34 +114,36 @@ int main()
|
|||||||
findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
|
findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
// Create the marker image for the watershed algorithm
|
// Create the marker image for the watershed algorithm
|
||||||
Mat markers = Mat::zeros(dist.size(), CV_32SC1);
|
Mat markers = Mat::zeros(dist.size(), CV_32S);
|
||||||
|
|
||||||
// Draw the foreground markers
|
// Draw the foreground markers
|
||||||
for (size_t i = 0; i < contours.size(); i++)
|
for (size_t i = 0; i < contours.size(); i++)
|
||||||
drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i)+1), -1);
|
{
|
||||||
|
drawContours(markers, contours, static_cast<int>(i), Scalar(static_cast<int>(i)+1), -1);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the background marker
|
// Draw the background marker
|
||||||
circle(markers, Point(5,5), 3, CV_RGB(255,255,255), -1);
|
circle(markers, Point(5,5), 3, Scalar(255), -1);
|
||||||
imshow("Markers", markers*10000);
|
imshow("Markers", markers*10000);
|
||||||
//! [seeds]
|
//! [seeds]
|
||||||
|
|
||||||
//! [watershed]
|
//! [watershed]
|
||||||
// Perform the watershed algorithm
|
// Perform the watershed algorithm
|
||||||
watershed(src, markers);
|
watershed(imgResult, markers);
|
||||||
|
|
||||||
Mat mark = Mat::zeros(markers.size(), CV_8UC1);
|
Mat mark;
|
||||||
markers.convertTo(mark, CV_8UC1);
|
markers.convertTo(mark, CV_8U);
|
||||||
bitwise_not(mark, mark);
|
bitwise_not(mark, mark);
|
||||||
// imshow("Markers_v2", mark); // uncomment this if you want to see how the mark
|
// imshow("Markers_v2", mark); // uncomment this if you want to see how the mark
|
||||||
// image looks like at that point
|
// image looks like at that point
|
||||||
|
|
||||||
// Generate random colors
|
// Generate random colors
|
||||||
vector<Vec3b> colors;
|
vector<Vec3b> colors;
|
||||||
for (size_t i = 0; i < contours.size(); i++)
|
for (size_t i = 0; i < contours.size(); i++)
|
||||||
{
|
{
|
||||||
int b = theRNG().uniform(0, 255);
|
int b = theRNG().uniform(0, 256);
|
||||||
int g = theRNG().uniform(0, 255);
|
int g = theRNG().uniform(0, 256);
|
||||||
int r = theRNG().uniform(0, 255);
|
int r = theRNG().uniform(0, 256);
|
||||||
|
|
||||||
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
|
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
|
||||||
}
|
}
|
||||||
@ -155,16 +158,16 @@ int main()
|
|||||||
{
|
{
|
||||||
int index = markers.at<int>(i,j);
|
int index = markers.at<int>(i,j);
|
||||||
if (index > 0 && index <= static_cast<int>(contours.size()))
|
if (index > 0 && index <= static_cast<int>(contours.size()))
|
||||||
|
{
|
||||||
dst.at<Vec3b>(i,j) = colors[index-1];
|
dst.at<Vec3b>(i,j) = colors[index-1];
|
||||||
else
|
}
|
||||||
dst.at<Vec3b>(i,j) = Vec3b(0,0,0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visualize the final image
|
// Visualize the final image
|
||||||
imshow("Final Result", dst);
|
imshow("Final Result", dst);
|
||||||
//! [watershed]
|
//! [watershed]
|
||||||
|
|
||||||
waitKey(0);
|
waitKey();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Mat src; Mat src_gray;
|
Mat src_gray;
|
||||||
int thresh = 100;
|
int thresh = 100;
|
||||||
int max_thresh = 255;
|
|
||||||
RNG rng(12345);
|
RNG rng(12345);
|
||||||
|
|
||||||
/// Function header
|
/// Function header
|
||||||
@ -25,34 +24,31 @@ void thresh_callback(int, void* );
|
|||||||
*/
|
*/
|
||||||
int main( int argc, char** argv )
|
int main( int argc, char** argv )
|
||||||
{
|
{
|
||||||
/// Load source image
|
/// Load source image
|
||||||
String imageName("../data/happyfish.jpg"); // by default
|
CommandLineParser parser( argc, argv, "{@input | ../data/HappyFish.jpg | input image}" );
|
||||||
if (argc > 1)
|
Mat src = imread( parser.get<String>( "@input" ) );
|
||||||
{
|
if( src.empty() )
|
||||||
imageName = argv[1];
|
{
|
||||||
}
|
cout << "Could not open or find the image!\n" << endl;
|
||||||
src = imread(imageName, IMREAD_COLOR);
|
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (src.empty())
|
/// Convert image to gray and blur it
|
||||||
{
|
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
||||||
cerr << "No image supplied ..." << endl;
|
blur( src_gray, src_gray, Size(3,3) );
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert image to gray and blur it
|
/// Create Window
|
||||||
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
const char* source_window = "Source";
|
||||||
blur( src_gray, src_gray, Size(3,3) );
|
namedWindow( source_window );
|
||||||
|
imshow( source_window, src );
|
||||||
|
|
||||||
/// Create Window
|
const int max_thresh = 255;
|
||||||
const char* source_window = "Source";
|
createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
|
||||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
thresh_callback( 0, 0 );
|
||||||
imshow( source_window, src );
|
|
||||||
|
|
||||||
createTrackbar( " Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );
|
waitKey();
|
||||||
thresh_callback( 0, 0 );
|
return 0;
|
||||||
|
|
||||||
waitKey(0);
|
|
||||||
return(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,24 +56,23 @@ int main( int argc, char** argv )
|
|||||||
*/
|
*/
|
||||||
void thresh_callback(int, void* )
|
void thresh_callback(int, void* )
|
||||||
{
|
{
|
||||||
Mat canny_output;
|
/// Detect edges using Canny
|
||||||
vector<vector<Point> > contours;
|
Mat canny_output;
|
||||||
vector<Vec4i> hierarchy;
|
Canny( src_gray, canny_output, thresh, thresh*2 );
|
||||||
|
|
||||||
/// Detect edges using canny
|
/// Find contours
|
||||||
Canny( src_gray, canny_output, thresh, thresh*2, 3 );
|
vector<vector<Point> > contours;
|
||||||
/// Find contours
|
vector<Vec4i> hierarchy;
|
||||||
findContours( canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
|
findContours( canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE );
|
||||||
|
|
||||||
/// Draw contours
|
/// Draw contours
|
||||||
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
|
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
|
||||||
for( size_t i = 0; i< contours.size(); i++ )
|
for( size_t i = 0; i< contours.size(); i++ )
|
||||||
{
|
{
|
||||||
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
|
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
|
||||||
drawContours( drawing, contours, (int)i, color, 2, 8, hierarchy, 0, Point() );
|
drawContours( drawing, contours, (int)i, color, 2, LINE_8, hierarchy, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show in a window
|
/// Show in a window
|
||||||
namedWindow( "Contours", WINDOW_AUTOSIZE );
|
imshow( "Contours", drawing );
|
||||||
imshow( "Contours", drawing );
|
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Mat src; Mat src_gray;
|
Mat src_gray;
|
||||||
int thresh = 100;
|
int thresh = 100;
|
||||||
int max_thresh = 255;
|
|
||||||
RNG rng(12345);
|
RNG rng(12345);
|
||||||
|
|
||||||
/// Function header
|
/// Function header
|
||||||
@ -25,42 +24,37 @@ void thresh_callback(int, void* );
|
|||||||
*/
|
*/
|
||||||
int main( int argc, char** argv )
|
int main( int argc, char** argv )
|
||||||
{
|
{
|
||||||
//![setup]
|
//! [setup]
|
||||||
/// Load source image
|
/// Load source image
|
||||||
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
||||||
src = imread( parser.get<String>( "@input" ), IMREAD_COLOR );
|
Mat src = imread( parser.get<String>( "@input" ) );
|
||||||
if( src.empty() )
|
if( src.empty() )
|
||||||
{
|
{
|
||||||
cout << "Could not open or find the image!\n" << endl;
|
cout << "Could not open or find the image!\n" << endl;
|
||||||
cout << "usage: " << argv[0] << " <Input image>" << endl;
|
cout << "usage: " << argv[0] << " <Input image>" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert image to gray and blur it
|
/// Convert image to gray and blur it
|
||||||
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
||||||
blur( src_gray, src_gray, Size(3,3) );
|
blur( src_gray, src_gray, Size(3,3) );
|
||||||
//![setup]
|
//! [setup]
|
||||||
|
|
||||||
//![createWindow]
|
//! [createWindow]
|
||||||
/// Create Window
|
/// Create Window
|
||||||
const char* source_window = "Source";
|
const char* source_window = "Source";
|
||||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
namedWindow( source_window );
|
||||||
imshow( source_window, src );
|
imshow( source_window, src );
|
||||||
//![createWindow]
|
//! [createWindow]
|
||||||
|
|
||||||
//![taskbar]
|
//! [trackbar]
|
||||||
createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
|
const int max_thresh = 255;
|
||||||
//![taskbar]
|
createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
|
||||||
|
thresh_callback( 0, 0 );
|
||||||
|
//! [trackbar]
|
||||||
|
|
||||||
//![callback00]
|
waitKey();
|
||||||
thresh_callback( 0, 0 );
|
return 0;
|
||||||
//![callback00]
|
|
||||||
|
|
||||||
//![waitForIt]
|
|
||||||
waitKey(0);
|
|
||||||
//![waitForIt]
|
|
||||||
|
|
||||||
return(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,53 +62,50 @@ int main( int argc, char** argv )
|
|||||||
*/
|
*/
|
||||||
void thresh_callback(int, void* )
|
void thresh_callback(int, void* )
|
||||||
{
|
{
|
||||||
Mat threshold_output;
|
//! [Canny]
|
||||||
vector<vector<Point> > contours;
|
/// Detect edges using Canny
|
||||||
vector<Vec4i> hierarchy;
|
Mat canny_output;
|
||||||
|
Canny( src_gray, canny_output, thresh, thresh*2 );
|
||||||
|
//! [Canny]
|
||||||
|
|
||||||
//![threshold]
|
//! [findContours]
|
||||||
/// Detect edges using Threshold
|
/// Find contours
|
||||||
threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
|
vector<vector<Point> > contours;
|
||||||
//![threshold]
|
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
|
||||||
|
//! [findContours]
|
||||||
|
|
||||||
//![findContours]
|
//! [allthework]
|
||||||
/// Find contours
|
/// Approximate contours to polygons + get bounding rects and circles
|
||||||
findContours( threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
|
vector<vector<Point> > contours_poly( contours.size() );
|
||||||
//![findContours]
|
vector<Rect> boundRect( contours.size() );
|
||||||
|
vector<Point2f>centers( contours.size() );
|
||||||
|
vector<float>radius( contours.size() );
|
||||||
|
|
||||||
/// Approximate contours to polygons + get bounding rects and circles
|
for( size_t i = 0; i < contours.size(); i++ )
|
||||||
vector<vector<Point> > contours_poly( contours.size() );
|
{
|
||||||
vector<Rect> boundRect( contours.size() );
|
approxPolyDP( contours[i], contours_poly[i], 3, true );
|
||||||
vector<Point2f>center( contours.size() );
|
boundRect[i] = boundingRect( contours_poly[i] );
|
||||||
vector<float>radius( contours.size() );
|
minEnclosingCircle( contours_poly[i], centers[i], radius[i] );
|
||||||
|
}
|
||||||
|
//! [allthework]
|
||||||
|
|
||||||
//![allthework]
|
//! [zeroMat]
|
||||||
for( size_t i = 0; i < contours.size(); i++ )
|
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
|
||||||
{
|
//! [zeroMat]
|
||||||
approxPolyDP( contours[i], contours_poly[i], 3, true );
|
|
||||||
boundRect[i] = boundingRect( contours_poly[i] );
|
|
||||||
minEnclosingCircle( contours_poly[i], center[i], radius[i] );
|
|
||||||
}
|
|
||||||
//![allthework]
|
|
||||||
|
|
||||||
//![zeroMat]
|
//! [forContour]
|
||||||
/// Draw polygonal contour + bonding rects + circles
|
/// Draw polygonal contour + bonding rects + circles
|
||||||
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
|
for( size_t i = 0; i< contours.size(); i++ )
|
||||||
//![zeroMat]
|
{
|
||||||
|
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
|
||||||
|
drawContours( drawing, contours_poly, (int)i, color );
|
||||||
|
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2 );
|
||||||
|
circle( drawing, centers[i], (int)radius[i], color, 2 );
|
||||||
|
}
|
||||||
|
//! [forContour]
|
||||||
|
|
||||||
//![forContour]
|
//! [showDrawings]
|
||||||
for( size_t i = 0; i< contours.size(); i++ )
|
/// Show in a window
|
||||||
{
|
imshow( "Contours", drawing );
|
||||||
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
|
//! [showDrawings]
|
||||||
drawContours( drawing, contours_poly, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );
|
|
||||||
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
|
|
||||||
circle( drawing, center[i], (int)radius[i], color, 2, 8, 0 );
|
|
||||||
}
|
|
||||||
//![forContour]
|
|
||||||
|
|
||||||
//![showDrawings]
|
|
||||||
/// Show in a window
|
|
||||||
namedWindow( "Contours", WINDOW_AUTOSIZE );
|
|
||||||
imshow( "Contours", drawing );
|
|
||||||
//![showDrawings]
|
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Mat src; Mat src_gray;
|
Mat src_gray;
|
||||||
int thresh = 100;
|
int thresh = 100;
|
||||||
int max_thresh = 255;
|
|
||||||
RNG rng(12345);
|
RNG rng(12345);
|
||||||
|
|
||||||
/// Function header
|
/// Function header
|
||||||
@ -25,30 +24,31 @@ void thresh_callback(int, void* );
|
|||||||
*/
|
*/
|
||||||
int main( int argc, char** argv )
|
int main( int argc, char** argv )
|
||||||
{
|
{
|
||||||
/// Load source image and convert it to gray
|
/// Load source image and convert it to gray
|
||||||
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
||||||
src = imread( parser.get<String>( "@input" ), IMREAD_COLOR );
|
Mat src = imread( parser.get<String>( "@input" ) );
|
||||||
if( src.empty() )
|
if( src.empty() )
|
||||||
{
|
{
|
||||||
cout << "Could not open or find the image!\n" << endl;
|
cout << "Could not open or find the image!\n" << endl;
|
||||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert image to gray and blur it
|
/// Convert image to gray and blur it
|
||||||
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
||||||
blur( src_gray, src_gray, Size(3,3) );
|
blur( src_gray, src_gray, Size(3,3) );
|
||||||
|
|
||||||
/// Create Window
|
/// Create Window
|
||||||
const char* source_window = "Source";
|
const char* source_window = "Source";
|
||||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
namedWindow( source_window );
|
||||||
imshow( source_window, src );
|
imshow( source_window, src );
|
||||||
|
|
||||||
createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
|
const int max_thresh = 255;
|
||||||
thresh_callback( 0, 0 );
|
createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
|
||||||
|
thresh_callback( 0, 0 );
|
||||||
|
|
||||||
waitKey(0);
|
waitKey();
|
||||||
return(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,41 +56,43 @@ int main( int argc, char** argv )
|
|||||||
*/
|
*/
|
||||||
void thresh_callback(int, void* )
|
void thresh_callback(int, void* )
|
||||||
{
|
{
|
||||||
Mat threshold_output;
|
/// Detect edges using Canny
|
||||||
vector<vector<Point> > contours;
|
Mat canny_output;
|
||||||
vector<Vec4i> hierarchy;
|
Canny( src_gray, canny_output, thresh, thresh*2 );
|
||||||
|
/// Find contours
|
||||||
|
vector<vector<Point> > contours;
|
||||||
|
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
|
||||||
|
|
||||||
/// Detect edges using Threshold
|
/// Find the rotated rectangles and ellipses for each contour
|
||||||
threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
|
vector<RotatedRect> minRect( contours.size() );
|
||||||
/// Find contours
|
vector<RotatedRect> minEllipse( contours.size() );
|
||||||
findContours( threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
|
for( size_t i = 0; i < contours.size(); i++ )
|
||||||
|
{
|
||||||
|
minRect[i] = minAreaRect( contours[i] );
|
||||||
|
if( contours[i].size() > 5 )
|
||||||
|
{
|
||||||
|
minEllipse[i] = fitEllipse( contours[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the rotated rectangles and ellipses for each contour
|
/// Draw contours + rotated rects + ellipses
|
||||||
vector<RotatedRect> minRect( contours.size() );
|
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
|
||||||
vector<RotatedRect> minEllipse( contours.size() );
|
for( size_t i = 0; i< contours.size(); i++ )
|
||||||
|
{
|
||||||
|
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
|
||||||
|
// contour
|
||||||
|
drawContours( drawing, contours, (int)i, color );
|
||||||
|
// ellipse
|
||||||
|
ellipse( drawing, minEllipse[i], color, 2 );
|
||||||
|
// rotated rectangle
|
||||||
|
Point2f rect_points[4];
|
||||||
|
minRect[i].points( rect_points );
|
||||||
|
for ( int j = 0; j < 4; j++ )
|
||||||
|
{
|
||||||
|
line( drawing, rect_points[j], rect_points[(j+1)%4], color );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for( size_t i = 0; i < contours.size(); i++ )
|
/// Show in a window
|
||||||
{ minRect[i] = minAreaRect( contours[i] );
|
imshow( "Contours", drawing );
|
||||||
if( contours[i].size() > 5 )
|
|
||||||
{ minEllipse[i] = fitEllipse( contours[i] ); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw contours + rotated rects + ellipses
|
|
||||||
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
|
|
||||||
for( size_t i = 0; i< contours.size(); i++ )
|
|
||||||
{
|
|
||||||
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
|
|
||||||
// contour
|
|
||||||
drawContours( drawing, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );
|
|
||||||
// ellipse
|
|
||||||
ellipse( drawing, minEllipse[i], color, 2, 8 );
|
|
||||||
// rotated rectangle
|
|
||||||
Point2f rect_points[4]; minRect[i].points( rect_points );
|
|
||||||
for( int j = 0; j < 4; j++ )
|
|
||||||
line( drawing, rect_points[j], rect_points[(j+1)%4], color, 1, 8 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show in a window
|
|
||||||
namedWindow( "Contours", WINDOW_AUTOSIZE );
|
|
||||||
imshow( "Contours", drawing );
|
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Mat src; Mat src_gray;
|
Mat src_gray;
|
||||||
int thresh = 100;
|
int thresh = 100;
|
||||||
int max_thresh = 255;
|
|
||||||
RNG rng(12345);
|
RNG rng(12345);
|
||||||
|
|
||||||
/// Function header
|
/// Function header
|
||||||
@ -25,30 +24,31 @@ void thresh_callback(int, void* );
|
|||||||
*/
|
*/
|
||||||
int main( int argc, char** argv )
|
int main( int argc, char** argv )
|
||||||
{
|
{
|
||||||
/// Load source image and convert it to gray
|
/// Load source image and convert it to gray
|
||||||
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
||||||
src = imread( parser.get<String>( "@input" ), IMREAD_COLOR );
|
Mat src = imread( parser.get<String>( "@input" ) );
|
||||||
if( src.empty() )
|
if( src.empty() )
|
||||||
{
|
{
|
||||||
cout << "Could not open or find the image!\n" << endl;
|
cout << "Could not open or find the image!\n" << endl;
|
||||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert image to gray and blur it
|
/// Convert image to gray and blur it
|
||||||
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
||||||
blur( src_gray, src_gray, Size(3,3) );
|
blur( src_gray, src_gray, Size(3,3) );
|
||||||
|
|
||||||
/// Create Window
|
/// Create Window
|
||||||
const char* source_window = "Source";
|
const char* source_window = "Source";
|
||||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
namedWindow( source_window );
|
||||||
imshow( source_window, src );
|
imshow( source_window, src );
|
||||||
|
|
||||||
createTrackbar( " Threshold:", "Source", &thresh, max_thresh, thresh_callback );
|
const int max_thresh = 255;
|
||||||
thresh_callback( 0, 0 );
|
createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
|
||||||
|
thresh_callback( 0, 0 );
|
||||||
|
|
||||||
waitKey(0);
|
waitKey();
|
||||||
return(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,31 +56,30 @@ int main( int argc, char** argv )
|
|||||||
*/
|
*/
|
||||||
void thresh_callback(int, void* )
|
void thresh_callback(int, void* )
|
||||||
{
|
{
|
||||||
Mat threshold_output;
|
/// Detect edges using Canny
|
||||||
vector<vector<Point> > contours;
|
Mat canny_output;
|
||||||
vector<Vec4i> hierarchy;
|
Canny( src_gray, canny_output, thresh, thresh*2 );
|
||||||
|
|
||||||
/// Detect edges using Threshold
|
/// Find contours
|
||||||
threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
|
vector<vector<Point> > contours;
|
||||||
|
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
|
||||||
|
|
||||||
/// Find contours
|
/// Find the convex hull object for each contour
|
||||||
findContours( threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
|
vector<vector<Point> >hull( contours.size() );
|
||||||
|
for( size_t i = 0; i < contours.size(); i++ )
|
||||||
|
{
|
||||||
|
convexHull( contours[i], hull[i] );
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the convex hull object for each contour
|
/// Draw contours + hull results
|
||||||
vector<vector<Point> >hull( contours.size() );
|
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
|
||||||
for( size_t i = 0; i < contours.size(); i++ )
|
for( size_t i = 0; i< contours.size(); i++ )
|
||||||
{ convexHull( contours[i], hull[i], false ); }
|
{
|
||||||
|
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
|
||||||
|
drawContours( drawing, contours, (int)i, color );
|
||||||
|
drawContours( drawing, hull, (int)i, color );
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw contours + hull results
|
/// Show in a window
|
||||||
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
|
imshow( "Hull demo", drawing );
|
||||||
for( size_t i = 0; i< contours.size(); i++ )
|
|
||||||
{
|
|
||||||
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
|
|
||||||
drawContours( drawing, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );
|
|
||||||
drawContours( drawing, hull, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point() );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show in a window
|
|
||||||
namedWindow( "Hull demo", WINDOW_AUTOSIZE );
|
|
||||||
imshow( "Hull demo", drawing );
|
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@
|
|||||||
#include "opencv2/highgui.hpp"
|
#include "opencv2/highgui.hpp"
|
||||||
#include "opencv2/imgproc.hpp"
|
#include "opencv2/imgproc.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Mat src; Mat src_gray;
|
Mat src_gray;
|
||||||
int thresh = 100;
|
int thresh = 100;
|
||||||
int max_thresh = 255;
|
|
||||||
RNG rng(12345);
|
RNG rng(12345);
|
||||||
|
|
||||||
/// Function header
|
/// Function header
|
||||||
@ -25,31 +25,32 @@ void thresh_callback(int, void* );
|
|||||||
*/
|
*/
|
||||||
int main( int argc, char** argv )
|
int main( int argc, char** argv )
|
||||||
{
|
{
|
||||||
/// Load source image and convert it to gray
|
/// Load source image
|
||||||
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
CommandLineParser parser( argc, argv, "{@input | ../data/stuff.jpg | input image}" );
|
||||||
src = imread( parser.get<String>( "@input" ), IMREAD_COLOR );
|
Mat src = imread( parser.get<String>( "@input" ) );
|
||||||
|
|
||||||
if( src.empty() )
|
if( src.empty() )
|
||||||
{
|
{
|
||||||
cout << "Could not open or find the image!\n" << endl;
|
cout << "Could not open or find the image!\n" << endl;
|
||||||
cout << "usage: " << argv[0] << " <Input image>" << endl;
|
cout << "usage: " << argv[0] << " <Input image>" << endl;
|
||||||
exit(0);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert image to gray and blur it
|
/// Convert image to gray and blur it
|
||||||
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
||||||
blur( src_gray, src_gray, Size(3,3) );
|
blur( src_gray, src_gray, Size(3,3) );
|
||||||
|
|
||||||
/// Create Window
|
/// Create Window
|
||||||
const char* source_window = "Source";
|
const char* source_window = "Source";
|
||||||
namedWindow( source_window, WINDOW_AUTOSIZE );
|
namedWindow( source_window );
|
||||||
imshow( source_window, src );
|
imshow( source_window, src );
|
||||||
|
|
||||||
createTrackbar( " Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );
|
const int max_thresh = 255;
|
||||||
thresh_callback( 0, 0 );
|
createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
|
||||||
|
thresh_callback( 0, 0 );
|
||||||
|
|
||||||
waitKey(0);
|
waitKey();
|
||||||
return(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,44 +58,47 @@ int main( int argc, char** argv )
|
|||||||
*/
|
*/
|
||||||
void thresh_callback(int, void* )
|
void thresh_callback(int, void* )
|
||||||
{
|
{
|
||||||
Mat canny_output;
|
/// Detect edges using canny
|
||||||
vector<vector<Point> > contours;
|
Mat canny_output;
|
||||||
|
Canny( src_gray, canny_output, thresh, thresh*2, 3 );
|
||||||
|
/// Find contours
|
||||||
|
vector<vector<Point> > contours;
|
||||||
|
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
|
||||||
|
|
||||||
/// Detect edges using canny
|
/// Get the moments
|
||||||
Canny( src_gray, canny_output, thresh, thresh*2, 3 );
|
vector<Moments> mu(contours.size() );
|
||||||
/// Find contours
|
for( size_t i = 0; i < contours.size(); i++ )
|
||||||
findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
|
{
|
||||||
|
mu[i] = moments( contours[i] );
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the moments
|
/// Get the mass centers
|
||||||
vector<Moments> mu(contours.size() );
|
vector<Point2f> mc( contours.size() );
|
||||||
for( size_t i = 0; i < contours.size(); i++ )
|
for( size_t i = 0; i < contours.size(); i++ )
|
||||||
{ mu[i] = moments( contours[i], false ); }
|
{
|
||||||
|
//add 1e-5 to avoid division by zero
|
||||||
|
mc[i] = Point2f( static_cast<float>(mu[i].m10 / (mu[i].m00 + 1e-5)),
|
||||||
|
static_cast<float>(mu[i].m01 / (mu[i].m00 + 1e-5)) );
|
||||||
|
cout << "mc[" << i << "]=" << mc[i] << endl;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the mass centers:
|
/// Draw contours
|
||||||
vector<Point2f> mc( contours.size() );
|
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
|
||||||
for( size_t i = 0; i < contours.size(); i++ )
|
for( size_t i = 0; i< contours.size(); i++ )
|
||||||
{ mc[i] = Point2f( static_cast<float>(mu[i].m10/mu[i].m00) , static_cast<float>(mu[i].m01/mu[i].m00) ); }
|
{
|
||||||
|
Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
|
||||||
|
drawContours( drawing, contours, (int)i, color, 2 );
|
||||||
|
circle( drawing, mc[i], 4, color, -1 );
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw contours
|
/// Show in a window
|
||||||
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
|
imshow( "Contours", drawing );
|
||||||
for( size_t i = 0; i< contours.size(); i++ )
|
|
||||||
{
|
|
||||||
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
|
|
||||||
drawContours( drawing, contours, (int)i, color, 2, LINE_8 );
|
|
||||||
circle( drawing, mc[i], 4, color, -1, 8, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show in a window
|
/// Calculate the area with the moments 00 and compare with the result of the OpenCV function
|
||||||
namedWindow( "Contours", WINDOW_AUTOSIZE );
|
cout << "\t Info: Area and Contour Length \n";
|
||||||
imshow( "Contours", drawing );
|
for( size_t i = 0; i < contours.size(); i++ )
|
||||||
|
{
|
||||||
/// Calculate the area with the moments 00 and compare with the result of the OpenCV function
|
cout << " * Contour[" << i << "] - Area (M_00) = " << std::fixed << std::setprecision(2) << mu[i].m00
|
||||||
printf("\t Info: Area and Contour Length \n");
|
<< " - Area OpenCV: " << contourArea(contours[i]) << " - Length: " << arcLength( contours[i], true ) << endl;
|
||||||
for( size_t i = 0; i< contours.size(); i++ )
|
}
|
||||||
{
|
|
||||||
printf(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f \n", (int)i, mu[i].m00, contourArea(contours[i]), arcLength( contours[i], true ) );
|
|
||||||
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
|
|
||||||
drawContours( drawing, contours, (int)i, color, 2, LINE_8 );
|
|
||||||
circle( drawing, mc[i], 4, color, -1, 8, 0 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,60 +16,71 @@ using namespace std;
|
|||||||
*/
|
*/
|
||||||
int main( void )
|
int main( void )
|
||||||
{
|
{
|
||||||
/// Create an image
|
/// Create an image
|
||||||
const int r = 100;
|
const int r = 100;
|
||||||
Mat src = Mat::zeros( Size( 4*r, 4*r ), CV_8UC1 );
|
Mat src = Mat::zeros( Size( 4*r, 4*r ), CV_8U );
|
||||||
|
|
||||||
/// Create a sequence of points to make a contour:
|
/// Create a sequence of points to make a contour
|
||||||
vector<Point2f> vert(6);
|
vector<Point2f> vert(6);
|
||||||
|
vert[0] = Point( 3*r/2, static_cast<int>(1.34*r) );
|
||||||
|
vert[1] = Point( 1*r, 2*r );
|
||||||
|
vert[2] = Point( 3*r/2, static_cast<int>(2.866*r) );
|
||||||
|
vert[3] = Point( 5*r/2, static_cast<int>(2.866*r) );
|
||||||
|
vert[4] = Point( 3*r, 2*r );
|
||||||
|
vert[5] = Point( 5*r/2, static_cast<int>(1.34*r) );
|
||||||
|
|
||||||
vert[0] = Point( 3*r/2, static_cast<int>(1.34*r) );
|
/// Draw it in src
|
||||||
vert[1] = Point( 1*r, 2*r );
|
for( int i = 0; i < 6; i++ )
|
||||||
vert[2] = Point( 3*r/2, static_cast<int>(2.866*r) );
|
{
|
||||||
vert[3] = Point( 5*r/2, static_cast<int>(2.866*r) );
|
line( src, vert[i], vert[(i+1)%6], Scalar( 255 ), 3 );
|
||||||
vert[4] = Point( 3*r, 2*r );
|
}
|
||||||
vert[5] = Point( 5*r/2, static_cast<int>(1.34*r) );
|
|
||||||
|
|
||||||
/// Draw it in src
|
/// Get the contours
|
||||||
for( int j = 0; j < 6; j++ )
|
vector<vector<Point> > contours;
|
||||||
{ line( src, vert[j], vert[(j+1)%6], Scalar( 255 ), 3, 8 ); }
|
findContours( src, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
/// Get the contours
|
/// Calculate the distances to the contour
|
||||||
vector<vector<Point> > contours;
|
Mat raw_dist( src.size(), CV_32F );
|
||||||
|
for( int i = 0; i < src.rows; i++ )
|
||||||
|
{
|
||||||
|
for( int j = 0; j < src.cols; j++ )
|
||||||
|
{
|
||||||
|
raw_dist.at<float>(i,j) = (float)pointPolygonTest( contours[0], Point2f((float)j, (float)i), true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
findContours( src, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
|
double minVal, maxVal;
|
||||||
|
minMaxLoc( raw_dist, &minVal, &maxVal );
|
||||||
|
minVal = abs(minVal);
|
||||||
|
maxVal = abs(maxVal);
|
||||||
|
|
||||||
/// Calculate the distances to the contour
|
/// Depicting the distances graphically
|
||||||
Mat raw_dist( src.size(), CV_32FC1 );
|
Mat drawing = Mat::zeros( src.size(), CV_8UC3 );
|
||||||
|
for( int i = 0; i < src.rows; i++ )
|
||||||
for( int j = 0; j < src.rows; j++ )
|
{
|
||||||
{ for( int i = 0; i < src.cols; i++ )
|
for( int j = 0; j < src.cols; j++ )
|
||||||
{ raw_dist.at<float>(j,i) = (float)pointPolygonTest( contours[0], Point2f((float)i,(float)j), true ); }
|
{
|
||||||
}
|
if( raw_dist.at<float>(i,j) < 0 )
|
||||||
|
{
|
||||||
double minVal; double maxVal;
|
drawing.at<Vec3b>(i,j)[0] = (uchar)(255 - abs(raw_dist.at<float>(i,j)) * 255 / minVal);
|
||||||
minMaxLoc( raw_dist, &minVal, &maxVal, 0, 0, Mat() );
|
}
|
||||||
minVal = abs(minVal); maxVal = abs(maxVal);
|
else if( raw_dist.at<float>(i,j) > 0 )
|
||||||
|
{
|
||||||
/// Depicting the distances graphically
|
drawing.at<Vec3b>(i,j)[2] = (uchar)(255 - raw_dist.at<float>(i,j) * 255 / maxVal);
|
||||||
Mat drawing = Mat::zeros( src.size(), CV_8UC3 );
|
}
|
||||||
|
|
||||||
for( int j = 0; j < src.rows; j++ )
|
|
||||||
{ for( int i = 0; i < src.cols; i++ )
|
|
||||||
{
|
|
||||||
if( raw_dist.at<float>(j,i) < 0 )
|
|
||||||
{ drawing.at<Vec3b>(j,i)[0] = (uchar)(255 - abs(raw_dist.at<float>(j,i))*255/minVal); }
|
|
||||||
else if( raw_dist.at<float>(j,i) > 0 )
|
|
||||||
{ drawing.at<Vec3b>(j,i)[2] = (uchar)(255 - raw_dist.at<float>(j,i)*255/maxVal); }
|
|
||||||
else
|
else
|
||||||
{ drawing.at<Vec3b>(j,i)[0] = 255; drawing.at<Vec3b>(j,i)[1] = 255; drawing.at<Vec3b>(j,i)[2] = 255; }
|
{
|
||||||
}
|
drawing.at<Vec3b>(i,j)[0] = 255;
|
||||||
}
|
drawing.at<Vec3b>(i,j)[1] = 255;
|
||||||
|
drawing.at<Vec3b>(i,j)[2] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Show your results
|
/// Show your results
|
||||||
imshow( "Source", src );
|
imshow( "Source", src );
|
||||||
imshow( "Distance", drawing );
|
imshow( "Distance", drawing );
|
||||||
|
|
||||||
waitKey(0);
|
waitKey();
|
||||||
return(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,12 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
const float inlier_threshold = 2.5f; // Distance threshold to identify inliers
|
const float inlier_threshold = 2.5f; // Distance threshold to identify inliers with homography check
|
||||||
const float nn_match_ratio = 0.8f; // Nearest neighbor matching ratio
|
const float nn_match_ratio = 0.8f; // Nearest neighbor matching ratio
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
//! [load]
|
||||||
CommandLineParser parser(argc, argv,
|
CommandLineParser parser(argc, argv,
|
||||||
"{@img1 | ../data/graf1.png | input image 1}"
|
"{@img1 | ../data/graf1.png | input image 1}"
|
||||||
"{@img2 | ../data/graf3.png | input image 2}"
|
"{@img2 | ../data/graf3.png | input image 2}"
|
||||||
@ -21,20 +22,25 @@ int main(int argc, char* argv[])
|
|||||||
Mat homography;
|
Mat homography;
|
||||||
FileStorage fs(parser.get<String>("@homography"), FileStorage::READ);
|
FileStorage fs(parser.get<String>("@homography"), FileStorage::READ);
|
||||||
fs.getFirstTopLevelNode() >> homography;
|
fs.getFirstTopLevelNode() >> homography;
|
||||||
|
//! [load]
|
||||||
|
|
||||||
|
//! [AKAZE]
|
||||||
vector<KeyPoint> kpts1, kpts2;
|
vector<KeyPoint> kpts1, kpts2;
|
||||||
Mat desc1, desc2;
|
Mat desc1, desc2;
|
||||||
|
|
||||||
Ptr<AKAZE> akaze = AKAZE::create();
|
Ptr<AKAZE> akaze = AKAZE::create();
|
||||||
akaze->detectAndCompute(img1, noArray(), kpts1, desc1);
|
akaze->detectAndCompute(img1, noArray(), kpts1, desc1);
|
||||||
akaze->detectAndCompute(img2, noArray(), kpts2, desc2);
|
akaze->detectAndCompute(img2, noArray(), kpts2, desc2);
|
||||||
|
//! [AKAZE]
|
||||||
|
|
||||||
|
//! [2-nn matching]
|
||||||
BFMatcher matcher(NORM_HAMMING);
|
BFMatcher matcher(NORM_HAMMING);
|
||||||
vector< vector<DMatch> > nn_matches;
|
vector< vector<DMatch> > nn_matches;
|
||||||
matcher.knnMatch(desc1, desc2, nn_matches, 2);
|
matcher.knnMatch(desc1, desc2, nn_matches, 2);
|
||||||
|
//! [2-nn matching]
|
||||||
|
|
||||||
vector<KeyPoint> matched1, matched2, inliers1, inliers2;
|
//! [ratio test filtering]
|
||||||
vector<DMatch> good_matches;
|
vector<KeyPoint> matched1, matched2;
|
||||||
for(size_t i = 0; i < nn_matches.size(); i++) {
|
for(size_t i = 0; i < nn_matches.size(); i++) {
|
||||||
DMatch first = nn_matches[i][0];
|
DMatch first = nn_matches[i][0];
|
||||||
float dist1 = nn_matches[i][0].distance;
|
float dist1 = nn_matches[i][0].distance;
|
||||||
@ -45,8 +51,12 @@ int main(int argc, char* argv[])
|
|||||||
matched2.push_back(kpts2[first.trainIdx]);
|
matched2.push_back(kpts2[first.trainIdx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//! [ratio test filtering]
|
||||||
|
|
||||||
for(unsigned i = 0; i < matched1.size(); i++) {
|
//! [homography check]
|
||||||
|
vector<DMatch> good_matches;
|
||||||
|
vector<KeyPoint> inliers1, inliers2;
|
||||||
|
for(size_t i = 0; i < matched1.size(); i++) {
|
||||||
Mat col = Mat::ones(3, 1, CV_64F);
|
Mat col = Mat::ones(3, 1, CV_64F);
|
||||||
col.at<double>(0) = matched1[i].pt.x;
|
col.at<double>(0) = matched1[i].pt.x;
|
||||||
col.at<double>(1) = matched1[i].pt.y;
|
col.at<double>(1) = matched1[i].pt.y;
|
||||||
@ -63,12 +73,14 @@ int main(int argc, char* argv[])
|
|||||||
good_matches.push_back(DMatch(new_i, new_i, 0));
|
good_matches.push_back(DMatch(new_i, new_i, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//! [homography check]
|
||||||
|
|
||||||
|
//! [draw final matches]
|
||||||
Mat res;
|
Mat res;
|
||||||
drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
|
drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
|
||||||
imwrite("akaze_result.png", res);
|
imwrite("akaze_result.png", res);
|
||||||
|
|
||||||
double inlier_ratio = inliers1.size() * 1.0 / matched1.size();
|
double inlier_ratio = inliers1.size() / (double) matched1.size();
|
||||||
cout << "A-KAZE Matching Results" << endl;
|
cout << "A-KAZE Matching Results" << endl;
|
||||||
cout << "*******************************" << endl;
|
cout << "*******************************" << endl;
|
||||||
cout << "# Keypoints 1: \t" << kpts1.size() << endl;
|
cout << "# Keypoints 1: \t" << kpts1.size() << endl;
|
||||||
@ -80,6 +92,7 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
imshow("result", res);
|
imshow("result", res);
|
||||||
waitKey();
|
waitKey();
|
||||||
|
//! [draw final matches]
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ int main( int argc, char* argv[] )
|
|||||||
std::vector<DMatch> good_matches;
|
std::vector<DMatch> good_matches;
|
||||||
for (size_t i = 0; i < knn_matches.size(); i++)
|
for (size_t i = 0; i < knn_matches.size(); i++)
|
||||||
{
|
{
|
||||||
if (knn_matches[i].size() > 1 && knn_matches[i][0].distance / knn_matches[i][1].distance <= ratio_thresh)
|
if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
|
||||||
{
|
{
|
||||||
good_matches.push_back(knn_matches[i][0]);
|
good_matches.push_back(knn_matches[i][0]);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ int main( int argc, char* argv[] )
|
|||||||
std::vector<DMatch> good_matches;
|
std::vector<DMatch> good_matches;
|
||||||
for (size_t i = 0; i < knn_matches.size(); i++)
|
for (size_t i = 0; i < knn_matches.size(); i++)
|
||||||
{
|
{
|
||||||
if (knn_matches[i].size() > 1 && knn_matches[i][0].distance / knn_matches[i][1].distance <= ratio_thresh)
|
if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
|
||||||
{
|
{
|
||||||
good_matches.push_back(knn_matches[i][0]);
|
good_matches.push_back(knn_matches[i][0]);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ int main(int, char**)
|
|||||||
//--- INITIALIZE VIDEOCAPTURE
|
//--- INITIALIZE VIDEOCAPTURE
|
||||||
VideoCapture cap;
|
VideoCapture cap;
|
||||||
// open the default camera using default API
|
// open the default camera using default API
|
||||||
cap.open(0);
|
// cap.open(0);
|
||||||
// OR advance usage: select any API backend
|
// OR advance usage: select any API backend
|
||||||
int deviceID = 0; // 0 = open default camera
|
int deviceID = 0; // 0 = open default camera
|
||||||
int apiID = cv::CAP_ANY; // 0 = autodetect default API
|
int apiID = cv::CAP_ANY; // 0 = autodetect default API
|
||||||
|
@ -11,7 +11,7 @@ int main(int, char**)
|
|||||||
{
|
{
|
||||||
Mat frame;
|
Mat frame;
|
||||||
cout << "Opening camera..." << endl;
|
cout << "Opening camera..." << endl;
|
||||||
VideoCapture capture(-1); // open the first available camera
|
VideoCapture capture(0); // open the first camera
|
||||||
if (!capture.isOpened())
|
if (!capture.isOpened())
|
||||||
{
|
{
|
||||||
cerr << "ERROR: Can't initialize camera capture" << endl;
|
cerr << "ERROR: Can't initialize camera capture" << endl;
|
||||||
|
@ -9,7 +9,7 @@ truck
|
|||||||
boat
|
boat
|
||||||
traffic light
|
traffic light
|
||||||
fire hydrant
|
fire hydrant
|
||||||
|
street sign
|
||||||
stop sign
|
stop sign
|
||||||
parking meter
|
parking meter
|
||||||
bench
|
bench
|
||||||
@ -23,11 +23,11 @@ elephant
|
|||||||
bear
|
bear
|
||||||
zebra
|
zebra
|
||||||
giraffe
|
giraffe
|
||||||
|
hat
|
||||||
backpack
|
backpack
|
||||||
umbrella
|
umbrella
|
||||||
|
shoe
|
||||||
|
eye glasses
|
||||||
handbag
|
handbag
|
||||||
tie
|
tie
|
||||||
suitcase
|
suitcase
|
||||||
@ -42,7 +42,7 @@ skateboard
|
|||||||
surfboard
|
surfboard
|
||||||
tennis racket
|
tennis racket
|
||||||
bottle
|
bottle
|
||||||
|
plate
|
||||||
wine glass
|
wine glass
|
||||||
cup
|
cup
|
||||||
fork
|
fork
|
||||||
@ -63,12 +63,12 @@ chair
|
|||||||
couch
|
couch
|
||||||
potted plant
|
potted plant
|
||||||
bed
|
bed
|
||||||
|
mirror
|
||||||
dining table
|
dining table
|
||||||
|
window
|
||||||
|
desk
|
||||||
toilet
|
toilet
|
||||||
|
door
|
||||||
tv
|
tv
|
||||||
laptop
|
laptop
|
||||||
mouse
|
mouse
|
||||||
@ -80,7 +80,7 @@ oven
|
|||||||
toaster
|
toaster
|
||||||
sink
|
sink
|
||||||
refrigerator
|
refrigerator
|
||||||
|
blender
|
||||||
book
|
book
|
||||||
clock
|
clock
|
||||||
vase
|
vase
|
||||||
|
80
samples/data/dnn/object_detection_classes_yolov3.txt
Normal file
80
samples/data/dnn/object_detection_classes_yolov3.txt
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
person
|
||||||
|
bicycle
|
||||||
|
car
|
||||||
|
motorcycle
|
||||||
|
airplane
|
||||||
|
bus
|
||||||
|
train
|
||||||
|
truck
|
||||||
|
boat
|
||||||
|
traffic light
|
||||||
|
fire hydrant
|
||||||
|
stop sign
|
||||||
|
parking meter
|
||||||
|
bench
|
||||||
|
bird
|
||||||
|
cat
|
||||||
|
dog
|
||||||
|
horse
|
||||||
|
sheep
|
||||||
|
cow
|
||||||
|
elephant
|
||||||
|
bear
|
||||||
|
zebra
|
||||||
|
giraffe
|
||||||
|
backpack
|
||||||
|
umbrella
|
||||||
|
handbag
|
||||||
|
tie
|
||||||
|
suitcase
|
||||||
|
frisbee
|
||||||
|
skis
|
||||||
|
snowboard
|
||||||
|
sports ball
|
||||||
|
kite
|
||||||
|
baseball bat
|
||||||
|
baseball glove
|
||||||
|
skateboard
|
||||||
|
surfboard
|
||||||
|
tennis racket
|
||||||
|
bottle
|
||||||
|
wine glass
|
||||||
|
cup
|
||||||
|
fork
|
||||||
|
knife
|
||||||
|
spoon
|
||||||
|
bowl
|
||||||
|
banana
|
||||||
|
apple
|
||||||
|
sandwich
|
||||||
|
orange
|
||||||
|
broccoli
|
||||||
|
carrot
|
||||||
|
hot dog
|
||||||
|
pizza
|
||||||
|
donut
|
||||||
|
cake
|
||||||
|
chair
|
||||||
|
couch
|
||||||
|
potted plant
|
||||||
|
bed
|
||||||
|
dining table
|
||||||
|
toilet
|
||||||
|
tv
|
||||||
|
laptop
|
||||||
|
mouse
|
||||||
|
remote
|
||||||
|
keyboard
|
||||||
|
cell phone
|
||||||
|
microwave
|
||||||
|
oven
|
||||||
|
toaster
|
||||||
|
sink
|
||||||
|
refrigerator
|
||||||
|
book
|
||||||
|
clock
|
||||||
|
vase
|
||||||
|
scissors
|
||||||
|
teddy bear
|
||||||
|
hair drier
|
||||||
|
toothbrush
|
@ -22,6 +22,7 @@ const char* keys =
|
|||||||
"{ height | -1 | Preprocess input image by resizing to a specific height. }"
|
"{ height | -1 | Preprocess input image by resizing to a specific height. }"
|
||||||
"{ rgb | | Indicate that model works with RGB input images instead BGR ones. }"
|
"{ rgb | | Indicate that model works with RGB input images instead BGR ones. }"
|
||||||
"{ thr | .5 | Confidence threshold. }"
|
"{ thr | .5 | Confidence threshold. }"
|
||||||
|
"{ thr | .4 | Non-maximum suppression threshold. }"
|
||||||
"{ backend | 0 | Choose one of computation backends: "
|
"{ backend | 0 | Choose one of computation backends: "
|
||||||
"0: automatically (by default), "
|
"0: automatically (by default), "
|
||||||
"1: Halide language (http://halide-lang.org/), "
|
"1: Halide language (http://halide-lang.org/), "
|
||||||
@ -37,7 +38,7 @@ const char* keys =
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace dnn;
|
using namespace dnn;
|
||||||
|
|
||||||
float confThreshold;
|
float confThreshold, nmsThreshold;
|
||||||
std::vector<std::string> classes;
|
std::vector<std::string> classes;
|
||||||
|
|
||||||
void postprocess(Mat& frame, const std::vector<Mat>& out, Net& net);
|
void postprocess(Mat& frame, const std::vector<Mat>& out, Net& net);
|
||||||
@ -59,6 +60,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
confThreshold = parser.get<float>("thr");
|
confThreshold = parser.get<float>("thr");
|
||||||
|
nmsThreshold = parser.get<float>("nms");
|
||||||
float scale = parser.get<float>("scale");
|
float scale = parser.get<float>("scale");
|
||||||
Scalar mean = parser.get<Scalar>("mean");
|
Scalar mean = parser.get<Scalar>("mean");
|
||||||
bool swapRB = parser.get<bool>("rgb");
|
bool swapRB = parser.get<bool>("rgb");
|
||||||
@ -144,6 +146,9 @@ void postprocess(Mat& frame, const std::vector<Mat>& outs, Net& net)
|
|||||||
static std::vector<int> outLayers = net.getUnconnectedOutLayers();
|
static std::vector<int> outLayers = net.getUnconnectedOutLayers();
|
||||||
static std::string outLayerType = net.getLayer(outLayers[0])->type;
|
static std::string outLayerType = net.getLayer(outLayers[0])->type;
|
||||||
|
|
||||||
|
std::vector<int> classIds;
|
||||||
|
std::vector<float> confidences;
|
||||||
|
std::vector<Rect> boxes;
|
||||||
if (net.getLayer(0)->outputNameToIndex("im_info") != -1) // Faster-RCNN or R-FCN
|
if (net.getLayer(0)->outputNameToIndex("im_info") != -1) // Faster-RCNN or R-FCN
|
||||||
{
|
{
|
||||||
// Network produces output blob with a shape 1x1xNx7 where N is a number of
|
// Network produces output blob with a shape 1x1xNx7 where N is a number of
|
||||||
@ -160,8 +165,11 @@ void postprocess(Mat& frame, const std::vector<Mat>& outs, Net& net)
|
|||||||
int top = (int)data[i + 4];
|
int top = (int)data[i + 4];
|
||||||
int right = (int)data[i + 5];
|
int right = (int)data[i + 5];
|
||||||
int bottom = (int)data[i + 6];
|
int bottom = (int)data[i + 6];
|
||||||
int classId = (int)(data[i + 1]) - 1; // Skip 0th background class id.
|
int width = right - left + 1;
|
||||||
drawPred(classId, confidence, left, top, right, bottom, frame);
|
int height = bottom - top + 1;
|
||||||
|
classIds.push_back((int)(data[i + 1]) - 1); // Skip 0th background class id.
|
||||||
|
boxes.push_back(Rect(left, top, width, height));
|
||||||
|
confidences.push_back(confidence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,16 +189,16 @@ void postprocess(Mat& frame, const std::vector<Mat>& outs, Net& net)
|
|||||||
int top = (int)(data[i + 4] * frame.rows);
|
int top = (int)(data[i + 4] * frame.rows);
|
||||||
int right = (int)(data[i + 5] * frame.cols);
|
int right = (int)(data[i + 5] * frame.cols);
|
||||||
int bottom = (int)(data[i + 6] * frame.rows);
|
int bottom = (int)(data[i + 6] * frame.rows);
|
||||||
int classId = (int)(data[i + 1]) - 1; // Skip 0th background class id.
|
int width = right - left + 1;
|
||||||
drawPred(classId, confidence, left, top, right, bottom, frame);
|
int height = bottom - top + 1;
|
||||||
|
classIds.push_back((int)(data[i + 1]) - 1); // Skip 0th background class id.
|
||||||
|
boxes.push_back(Rect(left, top, width, height));
|
||||||
|
confidences.push_back(confidence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (outLayerType == "Region")
|
else if (outLayerType == "Region")
|
||||||
{
|
{
|
||||||
std::vector<int> classIds;
|
|
||||||
std::vector<float> confidences;
|
|
||||||
std::vector<Rect> boxes;
|
|
||||||
for (size_t i = 0; i < outs.size(); ++i)
|
for (size_t i = 0; i < outs.size(); ++i)
|
||||||
{
|
{
|
||||||
// Network produces output blob with a shape NxC where N is a number of
|
// Network produces output blob with a shape NxC where N is a number of
|
||||||
@ -218,18 +226,19 @@ void postprocess(Mat& frame, const std::vector<Mat>& outs, Net& net)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<int> indices;
|
|
||||||
NMSBoxes(boxes, confidences, confThreshold, 0.4f, indices);
|
|
||||||
for (size_t i = 0; i < indices.size(); ++i)
|
|
||||||
{
|
|
||||||
int idx = indices[i];
|
|
||||||
Rect box = boxes[idx];
|
|
||||||
drawPred(classIds[idx], confidences[idx], box.x, box.y,
|
|
||||||
box.x + box.width, box.y + box.height, frame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CV_Error(Error::StsNotImplemented, "Unknown output layer type: " + outLayerType);
|
CV_Error(Error::StsNotImplemented, "Unknown output layer type: " + outLayerType);
|
||||||
|
|
||||||
|
std::vector<int> indices;
|
||||||
|
NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
|
||||||
|
for (size_t i = 0; i < indices.size(); ++i)
|
||||||
|
{
|
||||||
|
int idx = indices[i];
|
||||||
|
Rect box = boxes[idx];
|
||||||
|
drawPred(classIds[idx], confidences[idx], box.x, box.y,
|
||||||
|
box.x + box.width, box.y + box.height, frame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
|
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
|
||||||
|
@ -31,6 +31,7 @@ parser.add_argument('--height', type=int,
|
|||||||
parser.add_argument('--rgb', action='store_true',
|
parser.add_argument('--rgb', action='store_true',
|
||||||
help='Indicate that model works with RGB input images instead BGR ones.')
|
help='Indicate that model works with RGB input images instead BGR ones.')
|
||||||
parser.add_argument('--thr', type=float, default=0.5, help='Confidence threshold')
|
parser.add_argument('--thr', type=float, default=0.5, help='Confidence threshold')
|
||||||
|
parser.add_argument('--nms', type=float, default=0.4, help='Non-maximum suppression threshold')
|
||||||
parser.add_argument('--backend', choices=backends, default=cv.dnn.DNN_BACKEND_DEFAULT, type=int,
|
parser.add_argument('--backend', choices=backends, default=cv.dnn.DNN_BACKEND_DEFAULT, type=int,
|
||||||
help="Choose one of computation backends: "
|
help="Choose one of computation backends: "
|
||||||
"%d: automatically (by default), "
|
"%d: automatically (by default), "
|
||||||
@ -57,6 +58,7 @@ net.setPreferableBackend(args.backend)
|
|||||||
net.setPreferableTarget(args.target)
|
net.setPreferableTarget(args.target)
|
||||||
|
|
||||||
confThreshold = args.thr
|
confThreshold = args.thr
|
||||||
|
nmsThreshold = args.nms
|
||||||
|
|
||||||
def getOutputsNames(net):
|
def getOutputsNames(net):
|
||||||
layersNames = net.getLayerNames()
|
layersNames = net.getLayerNames()
|
||||||
@ -86,36 +88,43 @@ def postprocess(frame, outs):
|
|||||||
lastLayerId = net.getLayerId(layerNames[-1])
|
lastLayerId = net.getLayerId(layerNames[-1])
|
||||||
lastLayer = net.getLayer(lastLayerId)
|
lastLayer = net.getLayer(lastLayerId)
|
||||||
|
|
||||||
|
classIds = []
|
||||||
|
confidences = []
|
||||||
|
boxes = []
|
||||||
if net.getLayer(0).outputNameToIndex('im_info') != -1: # Faster-RCNN or R-FCN
|
if net.getLayer(0).outputNameToIndex('im_info') != -1: # Faster-RCNN or R-FCN
|
||||||
# Network produces output blob with a shape 1x1xNx7 where N is a number of
|
# Network produces output blob with a shape 1x1xNx7 where N is a number of
|
||||||
# detections and an every detection is a vector of values
|
# detections and an every detection is a vector of values
|
||||||
# [batchId, classId, confidence, left, top, right, bottom]
|
# [batchId, classId, confidence, left, top, right, bottom]
|
||||||
assert(len(outs) == 1)
|
for out in outs:
|
||||||
out = outs[0]
|
for detection in out[0, 0]:
|
||||||
for detection in out[0, 0]:
|
confidence = detection[2]
|
||||||
confidence = detection[2]
|
if confidence > confThreshold:
|
||||||
if confidence > confThreshold:
|
left = int(detection[3])
|
||||||
left = int(detection[3])
|
top = int(detection[4])
|
||||||
top = int(detection[4])
|
right = int(detection[5])
|
||||||
right = int(detection[5])
|
bottom = int(detection[6])
|
||||||
bottom = int(detection[6])
|
width = right - left + 1
|
||||||
classId = int(detection[1]) - 1 # Skip background label
|
height = bottom - top + 1
|
||||||
drawPred(classId, confidence, left, top, right, bottom)
|
classIds.append(int(detection[1]) - 1) # Skip background label
|
||||||
|
confidences.append(float(confidence))
|
||||||
|
boxes.append([left, top, width, height])
|
||||||
elif lastLayer.type == 'DetectionOutput':
|
elif lastLayer.type == 'DetectionOutput':
|
||||||
# Network produces output blob with a shape 1x1xNx7 where N is a number of
|
# Network produces output blob with a shape 1x1xNx7 where N is a number of
|
||||||
# detections and an every detection is a vector of values
|
# detections and an every detection is a vector of values
|
||||||
# [batchId, classId, confidence, left, top, right, bottom]
|
# [batchId, classId, confidence, left, top, right, bottom]
|
||||||
assert(len(outs) == 1)
|
for out in outs:
|
||||||
out = outs[0]
|
for detection in out[0, 0]:
|
||||||
for detection in out[0, 0]:
|
confidence = detection[2]
|
||||||
confidence = detection[2]
|
if confidence > confThreshold:
|
||||||
if confidence > confThreshold:
|
left = int(detection[3] * frameWidth)
|
||||||
left = int(detection[3] * frameWidth)
|
top = int(detection[4] * frameHeight)
|
||||||
top = int(detection[4] * frameHeight)
|
right = int(detection[5] * frameWidth)
|
||||||
right = int(detection[5] * frameWidth)
|
bottom = int(detection[6] * frameHeight)
|
||||||
bottom = int(detection[6] * frameHeight)
|
width = right - left + 1
|
||||||
classId = int(detection[1]) - 1 # Skip background label
|
height = bottom - top + 1
|
||||||
drawPred(classId, confidence, left, top, right, bottom)
|
classIds.append(int(detection[1]) - 1) # Skip background label
|
||||||
|
confidences.append(float(confidence))
|
||||||
|
boxes.append([left, top, width, height])
|
||||||
elif lastLayer.type == 'Region':
|
elif lastLayer.type == 'Region':
|
||||||
# Network produces output blob with a shape NxC where N is a number of
|
# Network produces output blob with a shape NxC where N is a number of
|
||||||
# detected objects and C is a number of classes + 4 where the first 4
|
# detected objects and C is a number of classes + 4 where the first 4
|
||||||
@ -138,15 +147,19 @@ def postprocess(frame, outs):
|
|||||||
classIds.append(classId)
|
classIds.append(classId)
|
||||||
confidences.append(float(confidence))
|
confidences.append(float(confidence))
|
||||||
boxes.append([left, top, width, height])
|
boxes.append([left, top, width, height])
|
||||||
indices = cv.dnn.NMSBoxes(boxes, confidences, confThreshold, 0.4)
|
else:
|
||||||
for i in indices:
|
print('Unknown output layer type: ' + lastLayer.type)
|
||||||
i = i[0]
|
exit()
|
||||||
box = boxes[i]
|
|
||||||
left = box[0]
|
indices = cv.dnn.NMSBoxes(boxes, confidences, confThreshold, nmsThreshold)
|
||||||
top = box[1]
|
for i in indices:
|
||||||
width = box[2]
|
i = i[0]
|
||||||
height = box[3]
|
box = boxes[i]
|
||||||
drawPred(classIds[i], confidences[i], left, top, left + width, top + height)
|
left = box[0]
|
||||||
|
top = box[1]
|
||||||
|
width = box[2]
|
||||||
|
height = box[3]
|
||||||
|
drawPred(classIds[i], confidences[i], left, top, left + width, top + height)
|
||||||
|
|
||||||
# Process inputs
|
# Process inputs
|
||||||
winName = 'Deep learning object detection in OpenCV'
|
winName = 'Deep learning object detection in OpenCV'
|
||||||
|
@ -0,0 +1,219 @@
|
|||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfPoint;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @brief Sample code showing how to segment overlapping objects using Laplacian filtering, in addition to Watershed
|
||||||
|
* and Distance Transformation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ImageSegmentation {
|
||||||
|
public void run(String[] args) {
|
||||||
|
//! [load_image]
|
||||||
|
// Load the image
|
||||||
|
String filename = args.length > 0 ? args[0] : "../data/cards.png";
|
||||||
|
Mat srcOriginal = Imgcodecs.imread(filename);
|
||||||
|
if (srcOriginal.empty()) {
|
||||||
|
System.err.println("Cannot read image: " + filename);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show source image
|
||||||
|
HighGui.imshow("Source Image", srcOriginal);
|
||||||
|
//! [load_image]
|
||||||
|
|
||||||
|
//! [black_bg]
|
||||||
|
// Change the background from white to black, since that will help later to
|
||||||
|
// extract
|
||||||
|
// better results during the use of Distance Transform
|
||||||
|
Mat src = srcOriginal.clone();
|
||||||
|
byte[] srcData = new byte[(int) (src.total() * src.channels())];
|
||||||
|
src.get(0, 0, srcData);
|
||||||
|
for (int i = 0; i < src.rows(); i++) {
|
||||||
|
for (int j = 0; j < src.cols(); j++) {
|
||||||
|
if (srcData[(i * src.cols() + j) * 3] == (byte) 255 && srcData[(i * src.cols() + j) * 3 + 1] == (byte) 255
|
||||||
|
&& srcData[(i * src.cols() + j) * 3 + 2] == (byte) 255) {
|
||||||
|
srcData[(i * src.cols() + j) * 3] = 0;
|
||||||
|
srcData[(i * src.cols() + j) * 3 + 1] = 0;
|
||||||
|
srcData[(i * src.cols() + j) * 3 + 2] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src.put(0, 0, srcData);
|
||||||
|
|
||||||
|
// Show output image
|
||||||
|
HighGui.imshow("Black Background Image", src);
|
||||||
|
//! [black_bg]
|
||||||
|
|
||||||
|
//! [sharp]
|
||||||
|
// Create a kernel that we will use to sharpen our image
|
||||||
|
Mat kernel = new Mat(3, 3, CvType.CV_32F);
|
||||||
|
// an approximation of second derivative, a quite strong kernel
|
||||||
|
float[] kernelData = new float[(int) (kernel.total() * kernel.channels())];
|
||||||
|
kernelData[0] = 1; kernelData[1] = 1; kernelData[2] = 1;
|
||||||
|
kernelData[3] = 1; kernelData[4] = -8; kernelData[5] = 1;
|
||||||
|
kernelData[6] = 1; kernelData[7] = 1; kernelData[8] = 1;
|
||||||
|
kernel.put(0, 0, kernelData);
|
||||||
|
|
||||||
|
// do the laplacian filtering as it is
|
||||||
|
// well, we need to convert everything in something more deeper then CV_8U
|
||||||
|
// because the kernel has some negative values,
|
||||||
|
// and we can expect in general to have a Laplacian image with negative values
|
||||||
|
// BUT a 8bits unsigned int (the one we are working with) can contain values
|
||||||
|
// from 0 to 255
|
||||||
|
// so the possible negative number will be truncated
|
||||||
|
Mat imgLaplacian = new Mat();
|
||||||
|
Imgproc.filter2D(src, imgLaplacian, CvType.CV_32F, kernel);
|
||||||
|
Mat sharp = new Mat();
|
||||||
|
src.convertTo(sharp, CvType.CV_32F);
|
||||||
|
Mat imgResult = new Mat();
|
||||||
|
Core.subtract(sharp, imgLaplacian, imgResult);
|
||||||
|
|
||||||
|
// convert back to 8bits gray scale
|
||||||
|
imgResult.convertTo(imgResult, CvType.CV_8UC3);
|
||||||
|
imgLaplacian.convertTo(imgLaplacian, CvType.CV_8UC3);
|
||||||
|
|
||||||
|
// imshow( "Laplace Filtered Image", imgLaplacian );
|
||||||
|
HighGui.imshow("New Sharped Image", imgResult);
|
||||||
|
//! [sharp]
|
||||||
|
|
||||||
|
//! [bin]
|
||||||
|
// Create binary image from source image
|
||||||
|
Mat bw = new Mat();
|
||||||
|
Imgproc.cvtColor(imgResult, bw, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.threshold(bw, bw, 40, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
|
||||||
|
HighGui.imshow("Binary Image", bw);
|
||||||
|
//! [bin]
|
||||||
|
|
||||||
|
//! [dist]
|
||||||
|
// Perform the distance transform algorithm
|
||||||
|
Mat dist = new Mat();
|
||||||
|
Imgproc.distanceTransform(bw, dist, Imgproc.DIST_L2, 3);
|
||||||
|
|
||||||
|
// Normalize the distance image for range = {0.0, 1.0}
|
||||||
|
// so we can visualize and threshold it
|
||||||
|
Core.normalize(dist, dist, 0.0, 1.0, Core.NORM_MINMAX);
|
||||||
|
Mat distDisplayScaled = new Mat();
|
||||||
|
Core.multiply(dist, new Scalar(255), distDisplayScaled);
|
||||||
|
Mat distDisplay = new Mat();
|
||||||
|
distDisplayScaled.convertTo(distDisplay, CvType.CV_8U);
|
||||||
|
HighGui.imshow("Distance Transform Image", distDisplay);
|
||||||
|
//! [dist]
|
||||||
|
|
||||||
|
//! [peaks]
|
||||||
|
// Threshold to obtain the peaks
|
||||||
|
// This will be the markers for the foreground objects
|
||||||
|
Imgproc.threshold(dist, dist, 0.4, 1.0, Imgproc.THRESH_BINARY);
|
||||||
|
|
||||||
|
// Dilate a bit the dist image
|
||||||
|
Mat kernel1 = Mat.ones(3, 3, CvType.CV_8U);
|
||||||
|
Imgproc.dilate(dist, dist, kernel1);
|
||||||
|
Mat distDisplay2 = new Mat();
|
||||||
|
dist.convertTo(distDisplay2, CvType.CV_8U);
|
||||||
|
Core.multiply(distDisplay2, new Scalar(255), distDisplay2);
|
||||||
|
HighGui.imshow("Peaks", distDisplay2);
|
||||||
|
//! [peaks]
|
||||||
|
|
||||||
|
//! [seeds]
|
||||||
|
// Create the CV_8U version of the distance image
|
||||||
|
// It is needed for findContours()
|
||||||
|
Mat dist_8u = new Mat();
|
||||||
|
dist.convertTo(dist_8u, CvType.CV_8U);
|
||||||
|
|
||||||
|
// Find total markers
|
||||||
|
List<MatOfPoint> contours = new ArrayList<>();
|
||||||
|
Mat hierarchy = new Mat();
|
||||||
|
Imgproc.findContours(dist_8u, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
|
// Create the marker image for the watershed algorithm
|
||||||
|
Mat markers = Mat.zeros(dist.size(), CvType.CV_32S);
|
||||||
|
|
||||||
|
// Draw the foreground markers
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
Imgproc.drawContours(markers, contours, i, new Scalar(i + 1), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the background marker
|
||||||
|
Mat markersScaled = new Mat();
|
||||||
|
markers.convertTo(markersScaled, CvType.CV_32F);
|
||||||
|
Core.normalize(markersScaled, markersScaled, 0.0, 255.0, Core.NORM_MINMAX);
|
||||||
|
Imgproc.circle(markersScaled, new Point(5, 5), 3, new Scalar(255, 255, 255), -1);
|
||||||
|
Mat markersDisplay = new Mat();
|
||||||
|
markersScaled.convertTo(markersDisplay, CvType.CV_8U);
|
||||||
|
HighGui.imshow("Markers", markersDisplay);
|
||||||
|
Imgproc.circle(markers, new Point(5, 5), 3, new Scalar(255, 255, 255), -1);
|
||||||
|
//! [seeds]
|
||||||
|
|
||||||
|
//! [watershed]
|
||||||
|
// Perform the watershed algorithm
|
||||||
|
Imgproc.watershed(imgResult, markers);
|
||||||
|
|
||||||
|
Mat mark = Mat.zeros(markers.size(), CvType.CV_8U);
|
||||||
|
markers.convertTo(mark, CvType.CV_8UC1);
|
||||||
|
Core.bitwise_not(mark, mark);
|
||||||
|
// imshow("Markers_v2", mark); // uncomment this if you want to see how the mark
|
||||||
|
// image looks like at that point
|
||||||
|
|
||||||
|
// Generate random colors
|
||||||
|
Random rng = new Random(12345);
|
||||||
|
List<Scalar> colors = new ArrayList<>(contours.size());
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
int b = rng.nextInt(256);
|
||||||
|
int g = rng.nextInt(256);
|
||||||
|
int r = rng.nextInt(256);
|
||||||
|
|
||||||
|
colors.add(new Scalar(b, g, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the result image
|
||||||
|
Mat dst = Mat.zeros(markers.size(), CvType.CV_8UC3);
|
||||||
|
byte[] dstData = new byte[(int) (dst.total() * dst.channels())];
|
||||||
|
dst.get(0, 0, dstData);
|
||||||
|
|
||||||
|
// Fill labeled objects with random colors
|
||||||
|
int[] markersData = new int[(int) (markers.total() * markers.channels())];
|
||||||
|
markers.get(0, 0, markersData);
|
||||||
|
for (int i = 0; i < markers.rows(); i++) {
|
||||||
|
for (int j = 0; j < markers.cols(); j++) {
|
||||||
|
int index = markersData[i * markers.cols() + j];
|
||||||
|
if (index > 0 && index <= contours.size()) {
|
||||||
|
dstData[(i * dst.cols() + j) * 3 + 0] = (byte) colors.get(index - 1).val[0];
|
||||||
|
dstData[(i * dst.cols() + j) * 3 + 1] = (byte) colors.get(index - 1).val[1];
|
||||||
|
dstData[(i * dst.cols() + j) * 3 + 2] = (byte) colors.get(index - 1).val[2];
|
||||||
|
} else {
|
||||||
|
dstData[(i * dst.cols() + j) * 3 + 0] = 0;
|
||||||
|
dstData[(i * dst.cols() + j) * 3 + 1] = 0;
|
||||||
|
dstData[(i * dst.cols() + j) * 3 + 2] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst.put(0, 0, dstData);
|
||||||
|
|
||||||
|
// Visualize the final image
|
||||||
|
HighGui.imshow("Final Result", dst);
|
||||||
|
//! [watershed]
|
||||||
|
|
||||||
|
HighGui.waitKey();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ImageSegmentationDemo {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
new ImageSegmentation().run(args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,179 @@
|
|||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfPoint;
|
||||||
|
import org.opencv.core.MatOfPoint2f;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Rect;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
class GeneralContours1 {
|
||||||
|
private Mat srcGray = new Mat();
|
||||||
|
private JFrame frame;
|
||||||
|
private JLabel imgSrcLabel;
|
||||||
|
private JLabel imgContoursLabel;
|
||||||
|
private static final int MAX_THRESHOLD = 255;
|
||||||
|
private int threshold = 100;
|
||||||
|
private Random rng = new Random(12345);
|
||||||
|
|
||||||
|
public GeneralContours1(String[] args) {
|
||||||
|
//! [setup]
|
||||||
|
/// Load source image
|
||||||
|
String filename = args.length > 0 ? args[0] : "../data/stuff.jpg";
|
||||||
|
Mat src = Imgcodecs.imread(filename);
|
||||||
|
if (src.empty()) {
|
||||||
|
System.err.println("Cannot read image: " + filename);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert image to gray and blur it
|
||||||
|
Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.blur(srcGray, srcGray, new Size(3, 3));
|
||||||
|
//! [setup]
|
||||||
|
|
||||||
|
//! [createWindow]
|
||||||
|
// Create and set up the window.
|
||||||
|
frame = new JFrame("Creating Bounding boxes and circles for contours demo");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
// Set up the content pane.
|
||||||
|
Image img = HighGui.toBufferedImage(src);
|
||||||
|
addComponentsToPane(frame.getContentPane(), img);
|
||||||
|
//! [createWindow]
|
||||||
|
// Use the content pane's default BorderLayout. No need for
|
||||||
|
// setLayout(new BorderLayout());
|
||||||
|
// Display the window.
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addComponentsToPane(Container pane, Image img) {
|
||||||
|
if (!(pane.getLayout() instanceof BorderLayout)) {
|
||||||
|
pane.add(new JLabel("Container doesn't use BorderLayout!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPanel sliderPanel = new JPanel();
|
||||||
|
sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
//! [trackbar]
|
||||||
|
sliderPanel.add(new JLabel("Canny threshold: "));
|
||||||
|
JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold);
|
||||||
|
slider.setMajorTickSpacing(20);
|
||||||
|
slider.setMinorTickSpacing(10);
|
||||||
|
slider.setPaintTicks(true);
|
||||||
|
slider.setPaintLabels(true);
|
||||||
|
slider.addChangeListener(new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
JSlider source = (JSlider) e.getSource();
|
||||||
|
threshold = source.getValue();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//! [trackbar]
|
||||||
|
sliderPanel.add(slider);
|
||||||
|
pane.add(sliderPanel, BorderLayout.PAGE_START);
|
||||||
|
|
||||||
|
JPanel imgPanel = new JPanel();
|
||||||
|
imgSrcLabel = new JLabel(new ImageIcon(img));
|
||||||
|
imgPanel.add(imgSrcLabel);
|
||||||
|
|
||||||
|
Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U);
|
||||||
|
imgContoursLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg)));
|
||||||
|
imgPanel.add(imgContoursLabel);
|
||||||
|
|
||||||
|
pane.add(imgPanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
//! [Canny]
|
||||||
|
/// Detect edges using Canny
|
||||||
|
Mat cannyOutput = new Mat();
|
||||||
|
Imgproc.Canny(srcGray, cannyOutput, threshold, threshold * 2);
|
||||||
|
//! [Canny]
|
||||||
|
|
||||||
|
//! [findContours]
|
||||||
|
/// Find contours
|
||||||
|
List<MatOfPoint> contours = new ArrayList<>();
|
||||||
|
Mat hierarchy = new Mat();
|
||||||
|
Imgproc.findContours(cannyOutput, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
|
||||||
|
//! [findContours]
|
||||||
|
|
||||||
|
//! [allthework]
|
||||||
|
/// Approximate contours to polygons + get bounding rects and circles
|
||||||
|
MatOfPoint2f[] contoursPoly = new MatOfPoint2f[contours.size()];
|
||||||
|
Rect[] boundRect = new Rect[contours.size()];
|
||||||
|
Point[] centers = new Point[contours.size()];
|
||||||
|
float[][] radius = new float[contours.size()][1];
|
||||||
|
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
contoursPoly[i] = new MatOfPoint2f();
|
||||||
|
Imgproc.approxPolyDP(new MatOfPoint2f(contours.get(i).toArray()), contoursPoly[i], 3, true);
|
||||||
|
boundRect[i] = Imgproc.boundingRect(new MatOfPoint(contoursPoly[i].toArray()));
|
||||||
|
centers[i] = new Point();
|
||||||
|
Imgproc.minEnclosingCircle(contoursPoly[i], centers[i], radius[i]);
|
||||||
|
}
|
||||||
|
//! [allthework]
|
||||||
|
|
||||||
|
//! [zeroMat]
|
||||||
|
Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);
|
||||||
|
//! [zeroMat]
|
||||||
|
//! [forContour]
|
||||||
|
/// Draw polygonal contour + bonding rects + circles
|
||||||
|
List<MatOfPoint> contoursPolyList = new ArrayList<>(contoursPoly.length);
|
||||||
|
for (MatOfPoint2f poly : contoursPoly) {
|
||||||
|
contoursPolyList.add(new MatOfPoint(poly.toArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
|
||||||
|
Imgproc.drawContours(drawing, contoursPolyList, i, color);
|
||||||
|
Imgproc.rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2);
|
||||||
|
Imgproc.circle(drawing, centers[i], (int) radius[i][0], color, 2);
|
||||||
|
}
|
||||||
|
//! [forContour]
|
||||||
|
|
||||||
|
//! [showDrawings]
|
||||||
|
/// Show in a window
|
||||||
|
imgContoursLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(drawing)));
|
||||||
|
frame.repaint();
|
||||||
|
//! [showDrawings]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GeneralContoursDemo1 {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
// Schedule a job for the event dispatch thread:
|
||||||
|
// creating and showing this application's GUI.
|
||||||
|
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new GeneralContours1(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,176 @@
|
|||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfPoint;
|
||||||
|
import org.opencv.core.MatOfPoint2f;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.RotatedRect;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
class GeneralContours2 {
|
||||||
|
private Mat srcGray = new Mat();
|
||||||
|
private JFrame frame;
|
||||||
|
private JLabel imgSrcLabel;
|
||||||
|
private JLabel imgContoursLabel;
|
||||||
|
private static final int MAX_THRESHOLD = 255;
|
||||||
|
private int threshold = 100;
|
||||||
|
private Random rng = new Random(12345);
|
||||||
|
|
||||||
|
public GeneralContours2(String[] args) {
|
||||||
|
//! [setup]
|
||||||
|
/// Load source image
|
||||||
|
String filename = args.length > 0 ? args[0] : "../data/stuff.jpg";
|
||||||
|
Mat src = Imgcodecs.imread(filename);
|
||||||
|
if (src.empty()) {
|
||||||
|
System.err.println("Cannot read image: " + filename);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert image to gray and blur it
|
||||||
|
Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.blur(srcGray, srcGray, new Size(3, 3));
|
||||||
|
//! [setup]
|
||||||
|
|
||||||
|
//! [createWindow]
|
||||||
|
// Create and set up the window.
|
||||||
|
frame = new JFrame("Creating Bounding rotated boxes and ellipses for contours demo");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
// Set up the content pane.
|
||||||
|
Image img = HighGui.toBufferedImage(src);
|
||||||
|
addComponentsToPane(frame.getContentPane(), img);
|
||||||
|
//! [createWindow]
|
||||||
|
// Use the content pane's default BorderLayout. No need for
|
||||||
|
// setLayout(new BorderLayout());
|
||||||
|
// Display the window.
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addComponentsToPane(Container pane, Image img) {
|
||||||
|
if (!(pane.getLayout() instanceof BorderLayout)) {
|
||||||
|
pane.add(new JLabel("Container doesn't use BorderLayout!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPanel sliderPanel = new JPanel();
|
||||||
|
sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
//! [trackbar]
|
||||||
|
sliderPanel.add(new JLabel("Canny threshold: "));
|
||||||
|
JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold);
|
||||||
|
slider.setMajorTickSpacing(20);
|
||||||
|
slider.setMinorTickSpacing(10);
|
||||||
|
slider.setPaintTicks(true);
|
||||||
|
slider.setPaintLabels(true);
|
||||||
|
slider.addChangeListener(new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
JSlider source = (JSlider) e.getSource();
|
||||||
|
threshold = source.getValue();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//! [trackbar]
|
||||||
|
sliderPanel.add(slider);
|
||||||
|
pane.add(sliderPanel, BorderLayout.PAGE_START);
|
||||||
|
|
||||||
|
JPanel imgPanel = new JPanel();
|
||||||
|
imgSrcLabel = new JLabel(new ImageIcon(img));
|
||||||
|
imgPanel.add(imgSrcLabel);
|
||||||
|
|
||||||
|
Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U);
|
||||||
|
imgContoursLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg)));
|
||||||
|
imgPanel.add(imgContoursLabel);
|
||||||
|
|
||||||
|
pane.add(imgPanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
//! [Canny]
|
||||||
|
/// Detect edges using Canny
|
||||||
|
Mat cannyOutput = new Mat();
|
||||||
|
Imgproc.Canny(srcGray, cannyOutput, threshold, threshold * 2);
|
||||||
|
//! [Canny]
|
||||||
|
|
||||||
|
//! [findContours]
|
||||||
|
/// Find contours
|
||||||
|
List<MatOfPoint> contours = new ArrayList<>();
|
||||||
|
Mat hierarchy = new Mat();
|
||||||
|
Imgproc.findContours(cannyOutput, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
|
||||||
|
//! [findContours]
|
||||||
|
|
||||||
|
/// Find the rotated rectangles and ellipses for each contour
|
||||||
|
RotatedRect[] minRect = new RotatedRect[contours.size()];
|
||||||
|
RotatedRect[] minEllipse = new RotatedRect[contours.size()];
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
minRect[i] = Imgproc.minAreaRect(new MatOfPoint2f(contours.get(i).toArray()));
|
||||||
|
minEllipse[i] = new RotatedRect();
|
||||||
|
if (contours.get(i).rows() > 5) {
|
||||||
|
minEllipse[i] = Imgproc.fitEllipse(new MatOfPoint2f(contours.get(i).toArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! [zeroMat]
|
||||||
|
/// Draw contours + rotated rects + ellipses
|
||||||
|
Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);
|
||||||
|
//! [zeroMat]
|
||||||
|
//! [forContour]
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
|
||||||
|
// contour
|
||||||
|
Imgproc.drawContours(drawing, contours, i, color);
|
||||||
|
// ellipse
|
||||||
|
Imgproc.ellipse(drawing, minEllipse[i], color, 2);
|
||||||
|
// rotated rectangle
|
||||||
|
Point[] rectPoints = new Point[4];
|
||||||
|
minRect[i].points(rectPoints);
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
Imgproc.line(drawing, rectPoints[j], rectPoints[(j+1) % 4], color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! [forContour]
|
||||||
|
|
||||||
|
//! [showDrawings]
|
||||||
|
/// Show in a window
|
||||||
|
imgContoursLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(drawing)));
|
||||||
|
frame.repaint();
|
||||||
|
//! [showDrawings]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GeneralContoursDemo2 {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
// Schedule a job for the event dispatch thread:
|
||||||
|
// creating and showing this application's GUI.
|
||||||
|
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new GeneralContours2(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfPoint;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
class FindContours {
|
||||||
|
private Mat srcGray = new Mat();
|
||||||
|
private JFrame frame;
|
||||||
|
private JLabel imgSrcLabel;
|
||||||
|
private JLabel imgContoursLabel;
|
||||||
|
private static final int MAX_THRESHOLD = 255;
|
||||||
|
private int threshold = 100;
|
||||||
|
private Random rng = new Random(12345);
|
||||||
|
|
||||||
|
public FindContours(String[] args) {
|
||||||
|
/// Load source image
|
||||||
|
String filename = args.length > 0 ? args[0] : "../data/HappyFish.jpg";
|
||||||
|
Mat src = Imgcodecs.imread(filename);
|
||||||
|
if (src.empty()) {
|
||||||
|
System.err.println("Cannot read image: " + filename);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert image to gray and blur it
|
||||||
|
Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.blur(srcGray, srcGray, new Size(3, 3));
|
||||||
|
|
||||||
|
// Create and set up the window.
|
||||||
|
frame = new JFrame("Finding contours in your image demo");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
// Set up the content pane.
|
||||||
|
Image img = HighGui.toBufferedImage(src);
|
||||||
|
addComponentsToPane(frame.getContentPane(), img);
|
||||||
|
// Use the content pane's default BorderLayout. No need for
|
||||||
|
// setLayout(new BorderLayout());
|
||||||
|
// Display the window.
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addComponentsToPane(Container pane, Image img) {
|
||||||
|
if (!(pane.getLayout() instanceof BorderLayout)) {
|
||||||
|
pane.add(new JLabel("Container doesn't use BorderLayout!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPanel sliderPanel = new JPanel();
|
||||||
|
sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
sliderPanel.add(new JLabel("Canny threshold: "));
|
||||||
|
JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold);
|
||||||
|
slider.setMajorTickSpacing(20);
|
||||||
|
slider.setMinorTickSpacing(10);
|
||||||
|
slider.setPaintTicks(true);
|
||||||
|
slider.setPaintLabels(true);
|
||||||
|
slider.addChangeListener(new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
JSlider source = (JSlider) e.getSource();
|
||||||
|
threshold = source.getValue();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sliderPanel.add(slider);
|
||||||
|
pane.add(sliderPanel, BorderLayout.PAGE_START);
|
||||||
|
|
||||||
|
JPanel imgPanel = new JPanel();
|
||||||
|
imgSrcLabel = new JLabel(new ImageIcon(img));
|
||||||
|
imgPanel.add(imgSrcLabel);
|
||||||
|
|
||||||
|
Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U);
|
||||||
|
imgContoursLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg)));
|
||||||
|
imgPanel.add(imgContoursLabel);
|
||||||
|
|
||||||
|
pane.add(imgPanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
/// Detect edges using Canny
|
||||||
|
Mat cannyOutput = new Mat();
|
||||||
|
Imgproc.Canny(srcGray, cannyOutput, threshold, threshold * 2);
|
||||||
|
|
||||||
|
/// Find contours
|
||||||
|
List<MatOfPoint> contours = new ArrayList<>();
|
||||||
|
Mat hierarchy = new Mat();
|
||||||
|
Imgproc.findContours(cannyOutput, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
|
/// Draw contours
|
||||||
|
Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
|
||||||
|
Imgproc.drawContours(drawing, contours, i, color, 2, Core.LINE_8, hierarchy, 0, new Point());
|
||||||
|
}
|
||||||
|
|
||||||
|
imgContoursLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(drawing)));
|
||||||
|
frame.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FindContoursDemo {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
// Schedule a job for the event dispatch thread:
|
||||||
|
// creating and showing this application's GUI.
|
||||||
|
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new FindContours(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
154
samples/java/tutorial_code/ShapeDescriptors/hull/HullDemo.java
Normal file
154
samples/java/tutorial_code/ShapeDescriptors/hull/HullDemo.java
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfInt;
|
||||||
|
import org.opencv.core.MatOfPoint;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
class Hull {
|
||||||
|
private Mat srcGray = new Mat();
|
||||||
|
private JFrame frame;
|
||||||
|
private JLabel imgSrcLabel;
|
||||||
|
private JLabel imgContoursLabel;
|
||||||
|
private static final int MAX_THRESHOLD = 255;
|
||||||
|
private int threshold = 100;
|
||||||
|
private Random rng = new Random(12345);
|
||||||
|
|
||||||
|
public Hull(String[] args) {
|
||||||
|
/// Load source image
|
||||||
|
String filename = args.length > 0 ? args[0] : "../data/stuff.jpg";
|
||||||
|
Mat src = Imgcodecs.imread(filename);
|
||||||
|
if (src.empty()) {
|
||||||
|
System.err.println("Cannot read image: " + filename);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert image to gray and blur it
|
||||||
|
Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.blur(srcGray, srcGray, new Size(3, 3));
|
||||||
|
|
||||||
|
// Create and set up the window.
|
||||||
|
frame = new JFrame("Convex Hull demo");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
// Set up the content pane.
|
||||||
|
Image img = HighGui.toBufferedImage(src);
|
||||||
|
addComponentsToPane(frame.getContentPane(), img);
|
||||||
|
// Use the content pane's default BorderLayout. No need for
|
||||||
|
// setLayout(new BorderLayout());
|
||||||
|
// Display the window.
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addComponentsToPane(Container pane, Image img) {
|
||||||
|
if (!(pane.getLayout() instanceof BorderLayout)) {
|
||||||
|
pane.add(new JLabel("Container doesn't use BorderLayout!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPanel sliderPanel = new JPanel();
|
||||||
|
sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
sliderPanel.add(new JLabel("Canny threshold: "));
|
||||||
|
JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold);
|
||||||
|
slider.setMajorTickSpacing(20);
|
||||||
|
slider.setMinorTickSpacing(10);
|
||||||
|
slider.setPaintTicks(true);
|
||||||
|
slider.setPaintLabels(true);
|
||||||
|
slider.addChangeListener(new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
JSlider source = (JSlider) e.getSource();
|
||||||
|
threshold = source.getValue();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sliderPanel.add(slider);
|
||||||
|
pane.add(sliderPanel, BorderLayout.PAGE_START);
|
||||||
|
|
||||||
|
JPanel imgPanel = new JPanel();
|
||||||
|
imgSrcLabel = new JLabel(new ImageIcon(img));
|
||||||
|
imgPanel.add(imgSrcLabel);
|
||||||
|
|
||||||
|
Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U);
|
||||||
|
imgContoursLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg)));
|
||||||
|
imgPanel.add(imgContoursLabel);
|
||||||
|
|
||||||
|
pane.add(imgPanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
/// Detect edges using Canny
|
||||||
|
Mat cannyOutput = new Mat();
|
||||||
|
Imgproc.Canny(srcGray, cannyOutput, threshold, threshold * 2);
|
||||||
|
|
||||||
|
/// Find contours
|
||||||
|
List<MatOfPoint> contours = new ArrayList<>();
|
||||||
|
Mat hierarchy = new Mat();
|
||||||
|
Imgproc.findContours(cannyOutput, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
|
/// Find the convex hull object for each contour
|
||||||
|
List<MatOfPoint> hullList = new ArrayList<>();
|
||||||
|
for (MatOfPoint contour : contours) {
|
||||||
|
MatOfInt hull = new MatOfInt();
|
||||||
|
Imgproc.convexHull(contour, hull);
|
||||||
|
|
||||||
|
Point[] contourArray = contour.toArray();
|
||||||
|
Point[] hullPoints = new Point[hull.rows()];
|
||||||
|
List<Integer> hullContourIdxList = hull.toList();
|
||||||
|
for (int i = 0; i < hullContourIdxList.size(); i++) {
|
||||||
|
hullPoints[i] = contourArray[hullContourIdxList.get(i)];
|
||||||
|
}
|
||||||
|
hullList.add(new MatOfPoint(hullPoints));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw contours + hull results
|
||||||
|
Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
|
||||||
|
Imgproc.drawContours(drawing, contours, i, color);
|
||||||
|
Imgproc.drawContours(drawing, hullList, i, color );
|
||||||
|
}
|
||||||
|
|
||||||
|
imgContoursLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(drawing)));
|
||||||
|
frame.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HullDemo {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
// Schedule a job for the event dispatch thread:
|
||||||
|
// creating and showing this application's GUI.
|
||||||
|
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new Hull(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfPoint;
|
||||||
|
import org.opencv.core.MatOfPoint2f;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
import org.opencv.imgproc.Moments;
|
||||||
|
|
||||||
|
class MomentsClass {
|
||||||
|
private Mat srcGray = new Mat();
|
||||||
|
private JFrame frame;
|
||||||
|
private JLabel imgSrcLabel;
|
||||||
|
private JLabel imgContoursLabel;
|
||||||
|
private static final int MAX_THRESHOLD = 255;
|
||||||
|
private int threshold = 100;
|
||||||
|
private Random rng = new Random(12345);
|
||||||
|
|
||||||
|
public MomentsClass(String[] args) {
|
||||||
|
//! [setup]
|
||||||
|
/// Load source image
|
||||||
|
String filename = args.length > 0 ? args[0] : "../data/stuff.jpg";
|
||||||
|
Mat src = Imgcodecs.imread(filename);
|
||||||
|
if (src.empty()) {
|
||||||
|
System.err.println("Cannot read image: " + filename);
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert image to gray and blur it
|
||||||
|
Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);
|
||||||
|
Imgproc.blur(srcGray, srcGray, new Size(3, 3));
|
||||||
|
//! [setup]
|
||||||
|
|
||||||
|
//! [createWindow]
|
||||||
|
// Create and set up the window.
|
||||||
|
frame = new JFrame("Image Moments demo");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
// Set up the content pane.
|
||||||
|
Image img = HighGui.toBufferedImage(src);
|
||||||
|
addComponentsToPane(frame.getContentPane(), img);
|
||||||
|
//! [createWindow]
|
||||||
|
// Use the content pane's default BorderLayout. No need for
|
||||||
|
// setLayout(new BorderLayout());
|
||||||
|
// Display the window.
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addComponentsToPane(Container pane, Image img) {
|
||||||
|
if (!(pane.getLayout() instanceof BorderLayout)) {
|
||||||
|
pane.add(new JLabel("Container doesn't use BorderLayout!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JPanel sliderPanel = new JPanel();
|
||||||
|
sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
//! [trackbar]
|
||||||
|
sliderPanel.add(new JLabel("Canny threshold: "));
|
||||||
|
JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold);
|
||||||
|
slider.setMajorTickSpacing(20);
|
||||||
|
slider.setMinorTickSpacing(10);
|
||||||
|
slider.setPaintTicks(true);
|
||||||
|
slider.setPaintLabels(true);
|
||||||
|
slider.addChangeListener(new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
JSlider source = (JSlider) e.getSource();
|
||||||
|
threshold = source.getValue();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//! [trackbar]
|
||||||
|
sliderPanel.add(slider);
|
||||||
|
pane.add(sliderPanel, BorderLayout.PAGE_START);
|
||||||
|
|
||||||
|
JPanel imgPanel = new JPanel();
|
||||||
|
imgSrcLabel = new JLabel(new ImageIcon(img));
|
||||||
|
imgPanel.add(imgSrcLabel);
|
||||||
|
|
||||||
|
Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U);
|
||||||
|
imgContoursLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg)));
|
||||||
|
imgPanel.add(imgContoursLabel);
|
||||||
|
|
||||||
|
pane.add(imgPanel, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
//! [Canny]
|
||||||
|
/// Detect edges using Canny
|
||||||
|
Mat cannyOutput = new Mat();
|
||||||
|
Imgproc.Canny(srcGray, cannyOutput, threshold, threshold * 2);
|
||||||
|
//! [Canny]
|
||||||
|
|
||||||
|
//! [findContours]
|
||||||
|
/// Find contours
|
||||||
|
List<MatOfPoint> contours = new ArrayList<>();
|
||||||
|
Mat hierarchy = new Mat();
|
||||||
|
Imgproc.findContours(cannyOutput, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
|
||||||
|
//! [findContours]
|
||||||
|
|
||||||
|
/// Get the moments
|
||||||
|
List<Moments> mu = new ArrayList<>(contours.size());
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
mu.add(Imgproc.moments(contours.get(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the mass centers
|
||||||
|
List<Point> mc = new ArrayList<>(contours.size());
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
//add 1e-5 to avoid division by zero
|
||||||
|
mc.add(new Point(mu.get(i).m10 / (mu.get(i).m00 + 1e-5), mu.get(i).m01 / (mu.get(i).m00 + 1e-5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//! [zeroMat]
|
||||||
|
/// Draw contours
|
||||||
|
Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);
|
||||||
|
//! [zeroMat]
|
||||||
|
//! [forContour]
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
|
||||||
|
Imgproc.drawContours(drawing, contours, i, color, 2);
|
||||||
|
Imgproc.circle(drawing, mc.get(i), 4, color, -1);
|
||||||
|
}
|
||||||
|
//! [forContour]
|
||||||
|
|
||||||
|
//! [showDrawings]
|
||||||
|
/// Show in a window
|
||||||
|
imgContoursLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(drawing)));
|
||||||
|
frame.repaint();
|
||||||
|
//! [showDrawings]
|
||||||
|
|
||||||
|
/// Calculate the area with the moments 00 and compare with the result of the OpenCV function
|
||||||
|
System.out.println("\t Info: Area and Contour Length \n");
|
||||||
|
for (int i = 0; i < contours.size(); i++) {
|
||||||
|
System.out.format(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f\n", i,
|
||||||
|
mu.get(i).m00, Imgproc.contourArea(contours.get(i)),
|
||||||
|
Imgproc.arcLength(new MatOfPoint2f(contours.get(i).toArray()), true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MomentsDemo {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
// Schedule a job for the event dispatch thread:
|
||||||
|
// creating and showing this application's GUI.
|
||||||
|
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
new MomentsClass(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.Core.MinMaxLocResult;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfPoint;
|
||||||
|
import org.opencv.core.MatOfPoint2f;
|
||||||
|
import org.opencv.core.Point;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.core.Size;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
class PointPolygonTest {
|
||||||
|
public void run() {
|
||||||
|
/// Create an image
|
||||||
|
int r = 100;
|
||||||
|
Mat src = Mat.zeros(new Size(4 * r, 4 * r), CvType.CV_8U);
|
||||||
|
|
||||||
|
/// Create a sequence of points to make a contour
|
||||||
|
List<Point> vert = new ArrayList<>(6);
|
||||||
|
vert.add(new Point(3 * r / 2, 1.34 * r));
|
||||||
|
vert.add(new Point(1 * r, 2 * r));
|
||||||
|
vert.add(new Point(3 * r / 2, 2.866 * r));
|
||||||
|
vert.add(new Point(5 * r / 2, 2.866 * r));
|
||||||
|
vert.add(new Point(3 * r, 2 * r));
|
||||||
|
vert.add(new Point(5 * r / 2, 1.34 * r));
|
||||||
|
|
||||||
|
/// Draw it in src
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
Imgproc.line(src, vert.get(i), vert.get((i + 1) % 6), new Scalar(255), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the contours
|
||||||
|
List<MatOfPoint> contours = new ArrayList<>();
|
||||||
|
Mat hierarchy = new Mat();
|
||||||
|
Imgproc.findContours(src, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
|
||||||
|
|
||||||
|
/// Calculate the distances to the contour
|
||||||
|
Mat rawDist = new Mat(src.size(), CvType.CV_32F);
|
||||||
|
float[] rawDistData = new float[(int) (rawDist.total() * rawDist.channels())];
|
||||||
|
for (int i = 0; i < src.rows(); i++) {
|
||||||
|
for (int j = 0; j < src.cols(); j++) {
|
||||||
|
rawDistData[i * src.cols() + j] = (float) Imgproc
|
||||||
|
.pointPolygonTest(new MatOfPoint2f(contours.get(0).toArray()), new Point(j, i), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawDist.put(0, 0, rawDistData);
|
||||||
|
|
||||||
|
MinMaxLocResult res = Core.minMaxLoc(rawDist);
|
||||||
|
double minVal = Math.abs(res.minVal);
|
||||||
|
double maxVal = Math.abs(res.maxVal);
|
||||||
|
|
||||||
|
/// Depicting the distances graphically
|
||||||
|
Mat drawing = Mat.zeros(src.size(), CvType.CV_8UC3);
|
||||||
|
byte[] drawingData = new byte[(int) (drawing.total() * drawing.channels())];
|
||||||
|
for (int i = 0; i < src.rows(); i++) {
|
||||||
|
for (int j = 0; j < src.cols(); j++) {
|
||||||
|
if (rawDistData[i * src.cols() + j] < 0) {
|
||||||
|
drawingData[(i * src.cols() + j) * 3] =
|
||||||
|
(byte) (255 - Math.abs(rawDistData[i * src.cols() + j]) * 255 / minVal);
|
||||||
|
} else if (rawDistData[i * src.cols() + j] > 0) {
|
||||||
|
drawingData[(i * src.cols() + j) * 3 + 2] =
|
||||||
|
(byte) (255 - rawDistData[i * src.cols() + j] * 255 / maxVal);
|
||||||
|
} else {
|
||||||
|
drawingData[(i * src.cols() + j) * 3] = (byte) 255;
|
||||||
|
drawingData[(i * src.cols() + j) * 3 + 1] = (byte) 255;
|
||||||
|
drawingData[(i * src.cols() + j) * 3 + 2] = (byte) 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawing.put(0, 0, drawingData);
|
||||||
|
|
||||||
|
/// Show your results
|
||||||
|
HighGui.imshow("Source", src);
|
||||||
|
HighGui.imshow("Distance", drawing);
|
||||||
|
|
||||||
|
HighGui.waitKey();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PointPolygonTestDemo {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
new PointPolygonTest().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import org.opencv.core.Core;
|
||||||
|
import org.opencv.core.CvType;
|
||||||
|
import org.opencv.core.DMatch;
|
||||||
|
import org.opencv.core.KeyPoint;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.MatOfDMatch;
|
||||||
|
import org.opencv.core.MatOfKeyPoint;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
|
import org.opencv.features2d.AKAZE;
|
||||||
|
import org.opencv.features2d.DescriptorMatcher;
|
||||||
|
import org.opencv.features2d.Features2d;
|
||||||
|
import org.opencv.highgui.HighGui;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
class AKAZEMatch {
|
||||||
|
public void run(String[] args) {
|
||||||
|
//! [load]
|
||||||
|
String filename1 = args.length > 2 ? args[0] : "../data/graf1.png";
|
||||||
|
String filename2 = args.length > 2 ? args[1] : "../data/graf3.png";
|
||||||
|
String filename3 = args.length > 2 ? args[2] : "../data/H1to3p.xml";
|
||||||
|
Mat img1 = Imgcodecs.imread(filename1, Imgcodecs.IMREAD_GRAYSCALE);
|
||||||
|
Mat img2 = Imgcodecs.imread(filename2, Imgcodecs.IMREAD_GRAYSCALE);
|
||||||
|
if (img1.empty() || img2.empty()) {
|
||||||
|
System.err.println("Cannot read images!");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = new File(filename3);
|
||||||
|
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder documentBuilder;
|
||||||
|
Document document;
|
||||||
|
Mat homography = new Mat(3, 3, CvType.CV_64F);
|
||||||
|
double[] homographyData = new double[(int) (homography.total()*homography.channels())];
|
||||||
|
try {
|
||||||
|
documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||||
|
document = documentBuilder.parse(file);
|
||||||
|
String homographyStr = document.getElementsByTagName("data").item(0).getTextContent();
|
||||||
|
String[] splited = homographyStr.split("\\s+");
|
||||||
|
int idx = 0;
|
||||||
|
for (String s : splited) {
|
||||||
|
if (!s.isEmpty()) {
|
||||||
|
homographyData[idx] = Double.parseDouble(s);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ParserConfigurationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(0);
|
||||||
|
} catch (SAXException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(0);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
homography.put(0, 0, homographyData);
|
||||||
|
//! [load]
|
||||||
|
|
||||||
|
//! [AKAZE]
|
||||||
|
AKAZE akaze = AKAZE.create();
|
||||||
|
MatOfKeyPoint kpts1 = new MatOfKeyPoint(), kpts2 = new MatOfKeyPoint();
|
||||||
|
Mat desc1 = new Mat(), desc2 = new Mat();
|
||||||
|
akaze.detectAndCompute(img1, new Mat(), kpts1, desc1);
|
||||||
|
akaze.detectAndCompute(img2, new Mat(), kpts2, desc2);
|
||||||
|
//! [AKAZE]
|
||||||
|
|
||||||
|
//! [2-nn matching]
|
||||||
|
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
|
||||||
|
List<MatOfDMatch> knnMatches = new ArrayList<>();
|
||||||
|
matcher.knnMatch(desc1, desc2, knnMatches, 2);
|
||||||
|
//! [2-nn matching]
|
||||||
|
|
||||||
|
//! [ratio test filtering]
|
||||||
|
float ratioThreshold = 0.8f; // Nearest neighbor matching ratio
|
||||||
|
List<KeyPoint> listOfMatched1 = new ArrayList<>();
|
||||||
|
List<KeyPoint> listOfMatched2 = new ArrayList<>();
|
||||||
|
List<KeyPoint> listOfKeypoints1 = kpts1.toList();
|
||||||
|
List<KeyPoint> listOfKeypoints2 = kpts2.toList();
|
||||||
|
for (int i = 0; i < knnMatches.size(); i++) {
|
||||||
|
DMatch[] matches = knnMatches.get(i).toArray();
|
||||||
|
float dist1 = matches[0].distance;
|
||||||
|
float dist2 = matches[1].distance;
|
||||||
|
if (dist1 < ratioThreshold * dist2) {
|
||||||
|
listOfMatched1.add(listOfKeypoints1.get(matches[0].queryIdx));
|
||||||
|
listOfMatched2.add(listOfKeypoints2.get(matches[0].trainIdx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! [ratio test filtering]
|
||||||
|
|
||||||
|
//! [homography check]
|
||||||
|
double inlierThreshold = 2.5; // Distance threshold to identify inliers with homography check
|
||||||
|
List<KeyPoint> listOfInliers1 = new ArrayList<>();
|
||||||
|
List<KeyPoint> listOfInliers2 = new ArrayList<>();
|
||||||
|
List<DMatch> listOfGoodMatches = new ArrayList<>();
|
||||||
|
for (int i = 0; i < listOfMatched1.size(); i++) {
|
||||||
|
Mat col = new Mat(3, 1, CvType.CV_64F);
|
||||||
|
double[] colData = new double[(int) (col.total() * col.channels())];
|
||||||
|
colData[0] = listOfMatched1.get(i).pt.x;
|
||||||
|
colData[1] = listOfMatched1.get(i).pt.y;
|
||||||
|
colData[2] = 1.0;
|
||||||
|
col.put(0, 0, colData);
|
||||||
|
|
||||||
|
Mat colRes = new Mat();
|
||||||
|
Core.gemm(homography, col, 1.0, new Mat(), 0.0, colRes);
|
||||||
|
colRes.get(0, 0, colData);
|
||||||
|
Core.multiply(colRes, new Scalar(1.0 / colData[2]), col);
|
||||||
|
col.get(0, 0, colData);
|
||||||
|
|
||||||
|
double dist = Math.sqrt(Math.pow(colData[0] - listOfMatched2.get(i).pt.x, 2) +
|
||||||
|
Math.pow(colData[1] - listOfMatched2.get(i).pt.y, 2));
|
||||||
|
|
||||||
|
if (dist < inlierThreshold) {
|
||||||
|
listOfGoodMatches.add(new DMatch(listOfInliers1.size(), listOfInliers2.size(), 0));
|
||||||
|
listOfInliers1.add(listOfMatched1.get(i));
|
||||||
|
listOfInliers2.add(listOfMatched2.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//! [homography check]
|
||||||
|
|
||||||
|
//! [draw final matches]
|
||||||
|
Mat res = new Mat();
|
||||||
|
MatOfKeyPoint inliers1 = new MatOfKeyPoint(listOfInliers1.toArray(new KeyPoint[listOfInliers1.size()]));
|
||||||
|
MatOfKeyPoint inliers2 = new MatOfKeyPoint(listOfInliers2.toArray(new KeyPoint[listOfInliers2.size()]));
|
||||||
|
MatOfDMatch goodMatches = new MatOfDMatch(listOfGoodMatches.toArray(new DMatch[listOfGoodMatches.size()]));
|
||||||
|
Features2d.drawMatches(img1, inliers1, img2, inliers2, goodMatches, res);
|
||||||
|
Imgcodecs.imwrite("akaze_result.png", res);
|
||||||
|
|
||||||
|
double inlierRatio = listOfInliers1.size() / (double) listOfMatched1.size();
|
||||||
|
System.out.println("A-KAZE Matching Results");
|
||||||
|
System.out.println("*******************************");
|
||||||
|
System.out.println("# Keypoints 1: \t" + listOfKeypoints1.size());
|
||||||
|
System.out.println("# Keypoints 2: \t" + listOfKeypoints2.size());
|
||||||
|
System.out.println("# Matches: \t" + listOfMatched1.size());
|
||||||
|
System.out.println("# Inliers: \t" + listOfInliers1.size());
|
||||||
|
System.out.println("# Inliers Ratio: \t" + inlierRatio);
|
||||||
|
|
||||||
|
HighGui.imshow("result", res);
|
||||||
|
HighGui.waitKey();
|
||||||
|
//! [draw final matches]
|
||||||
|
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AKAZEMatchDemo {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Load the native OpenCV library
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
|
||||||
|
new AKAZEMatch().run(args);
|
||||||
|
}
|
||||||
|
}
|
@ -42,12 +42,12 @@ class SURFFLANNMatching {
|
|||||||
matcher.knnMatch(descriptors1, descriptors2, knnMatches, 2);
|
matcher.knnMatch(descriptors1, descriptors2, knnMatches, 2);
|
||||||
|
|
||||||
//-- Filter matches using the Lowe's ratio test
|
//-- Filter matches using the Lowe's ratio test
|
||||||
float ratio_thresh = 0.7f;
|
float ratioThresh = 0.7f;
|
||||||
List<DMatch> listOfGoodMatches = new ArrayList<>();
|
List<DMatch> listOfGoodMatches = new ArrayList<>();
|
||||||
for (int i = 0; i < knnMatches.size(); i++) {
|
for (int i = 0; i < knnMatches.size(); i++) {
|
||||||
if (knnMatches.get(i).rows() > 1) {
|
if (knnMatches.get(i).rows() > 1) {
|
||||||
DMatch[] matches = knnMatches.get(i).toArray();
|
DMatch[] matches = knnMatches.get(i).toArray();
|
||||||
if (matches[0].distance / matches[1].distance <= ratio_thresh) {
|
if (matches[0].distance < ratioThresh * matches[1].distance) {
|
||||||
listOfGoodMatches.add(matches[0]);
|
listOfGoodMatches.add(matches[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,12 +48,12 @@ class SURFFLANNMatchingHomography {
|
|||||||
matcher.knnMatch(descriptorsObject, descriptorsScene, knnMatches, 2);
|
matcher.knnMatch(descriptorsObject, descriptorsScene, knnMatches, 2);
|
||||||
|
|
||||||
//-- Filter matches using the Lowe's ratio test
|
//-- Filter matches using the Lowe's ratio test
|
||||||
float ratio_thresh = 0.75f;
|
float ratioThresh = 0.75f;
|
||||||
List<DMatch> listOfGoodMatches = new ArrayList<>();
|
List<DMatch> listOfGoodMatches = new ArrayList<>();
|
||||||
for (int i = 0; i < knnMatches.size(); i++) {
|
for (int i = 0; i < knnMatches.size(); i++) {
|
||||||
if (knnMatches.get(i).rows() > 1) {
|
if (knnMatches.get(i).rows() > 1) {
|
||||||
DMatch[] matches = knnMatches.get(i).toArray();
|
DMatch[] matches = knnMatches.get(i).toArray();
|
||||||
if (matches[0].distance / matches[1].distance <= ratio_thresh) {
|
if (matches[0].distance < ratioThresh * matches[1].distance) {
|
||||||
listOfGoodMatches.add(matches[0]);
|
listOfGoodMatches.add(matches[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
|||||||
import org.opencv.core.Core;
|
import org.opencv.core.Core;
|
||||||
import org.opencv.core.CvType;
|
import org.opencv.core.CvType;
|
||||||
import org.opencv.core.Mat;
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.core.Scalar;
|
||||||
import org.opencv.imgcodecs.Imgcodecs;
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
import org.opencv.photo.CalibrateDebevec;
|
import org.opencv.photo.CalibrateDebevec;
|
||||||
import org.opencv.photo.MergeDebevec;
|
import org.opencv.photo.MergeDebevec;
|
||||||
@ -70,7 +71,7 @@ class HDRImaging {
|
|||||||
|
|
||||||
//! [Tonemap HDR image]
|
//! [Tonemap HDR image]
|
||||||
Mat ldr = new Mat();
|
Mat ldr = new Mat();
|
||||||
TonemapDurand tonemap = Photo.createTonemapDurand();
|
TonemapDurand tonemap = Photo.createTonemapDurand(2.2f, 4.0f, 1.0f, 2.0f, 2.0f);
|
||||||
tonemap.process(hdr, ldr);
|
tonemap.process(hdr, ldr);
|
||||||
//! [Tonemap HDR image]
|
//! [Tonemap HDR image]
|
||||||
|
|
||||||
@ -81,8 +82,8 @@ class HDRImaging {
|
|||||||
//! [Perform exposure fusion]
|
//! [Perform exposure fusion]
|
||||||
|
|
||||||
//! [Write results]
|
//! [Write results]
|
||||||
fusion = fusion.mul(fusion, 255);
|
Core.multiply(fusion, new Scalar(255,255,255), fusion);
|
||||||
ldr = ldr.mul(ldr, 255);
|
Core.multiply(ldr, new Scalar(255,255,255), ldr);
|
||||||
Imgcodecs.imwrite("fusion.png", fusion);
|
Imgcodecs.imwrite("fusion.png", fusion);
|
||||||
Imgcodecs.imwrite("ldr.png", ldr);
|
Imgcodecs.imwrite("ldr.png", ldr);
|
||||||
Imgcodecs.imwrite("hdr.hdr", hdr);
|
Imgcodecs.imwrite("hdr.hdr", hdr);
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
|
||||||
|
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
|
||||||
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS // eliminate build warning
|
#define CL_USE_DEPRECATED_OPENCL_2_0_APIS // eliminate build warning
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
@ -0,0 +1,138 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
import cv2 as cv
|
||||||
|
import numpy as np
|
||||||
|
import argparse
|
||||||
|
import random as rng
|
||||||
|
|
||||||
|
rng.seed(12345)
|
||||||
|
|
||||||
|
## [load_image]
|
||||||
|
# Load the image
|
||||||
|
parser = argparse.ArgumentParser(description='Code for Image Segmentation with Distance Transform and Watershed Algorithm.\
|
||||||
|
Sample code showing how to segment overlapping objects using Laplacian filtering, \
|
||||||
|
in addition to Watershed and Distance Transformation')
|
||||||
|
parser.add_argument('--input', help='Path to input image.', default='../data/cards.png')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
src = cv.imread(args.input)
|
||||||
|
if src is None:
|
||||||
|
print('Could not open or find the image:', args.input)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
# Show source image
|
||||||
|
cv.imshow('Source Image', src)
|
||||||
|
## [load_image]
|
||||||
|
|
||||||
|
## [black_bg]
|
||||||
|
# Change the background from white to black, since that will help later to extract
|
||||||
|
# better results during the use of Distance Transform
|
||||||
|
src[np.all(src == 255, axis=2)] = 0
|
||||||
|
|
||||||
|
# Show output image
|
||||||
|
cv.imshow('Black Background Image', src)
|
||||||
|
## [black_bg]
|
||||||
|
|
||||||
|
## [sharp]
|
||||||
|
# Create a kernel that we will use to sharpen our image
|
||||||
|
# an approximation of second derivative, a quite strong kernel
|
||||||
|
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
|
||||||
|
|
||||||
|
# do the laplacian filtering as it is
|
||||||
|
# well, we need to convert everything in something more deeper then CV_8U
|
||||||
|
# because the kernel has some negative values,
|
||||||
|
# and we can expect in general to have a Laplacian image with negative values
|
||||||
|
# BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
|
||||||
|
# so the possible negative number will be truncated
|
||||||
|
imgLaplacian = cv.filter2D(src, cv.CV_32F, kernel)
|
||||||
|
sharp = np.float32(src)
|
||||||
|
imgResult = sharp - imgLaplacian
|
||||||
|
|
||||||
|
# convert back to 8bits gray scale
|
||||||
|
imgResult = np.clip(imgResult, 0, 255)
|
||||||
|
imgResult = imgResult.astype('uint8')
|
||||||
|
imgLaplacian = np.clip(imgLaplacian, 0, 255)
|
||||||
|
imgLaplacian = np.uint8(imgLaplacian)
|
||||||
|
|
||||||
|
#cv.imshow('Laplace Filtered Image', imgLaplacian)
|
||||||
|
cv.imshow('New Sharped Image', imgResult)
|
||||||
|
## [sharp]
|
||||||
|
|
||||||
|
## [bin]
|
||||||
|
# Create binary image from source image
|
||||||
|
bw = cv.cvtColor(imgResult, cv.COLOR_BGR2GRAY)
|
||||||
|
_, bw = cv.threshold(bw, 40, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
|
||||||
|
cv.imshow('Binary Image', bw)
|
||||||
|
## [bin]
|
||||||
|
|
||||||
|
## [dist]
|
||||||
|
# Perform the distance transform algorithm
|
||||||
|
dist = cv.distanceTransform(bw, cv.DIST_L2, 3)
|
||||||
|
|
||||||
|
# Normalize the distance image for range = {0.0, 1.0}
|
||||||
|
# so we can visualize and threshold it
|
||||||
|
cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX)
|
||||||
|
cv.imshow('Distance Transform Image', dist)
|
||||||
|
## [dist]
|
||||||
|
|
||||||
|
## [peaks]
|
||||||
|
# Threshold to obtain the peaks
|
||||||
|
# This will be the markers for the foreground objects
|
||||||
|
_, dist = cv.threshold(dist, 0.4, 1.0, cv.THRESH_BINARY)
|
||||||
|
|
||||||
|
# Dilate a bit the dist image
|
||||||
|
kernel1 = np.ones((3,3), dtype=np.uint8)
|
||||||
|
dist = cv.dilate(dist, kernel1)
|
||||||
|
cv.imshow('Peaks', dist)
|
||||||
|
## [peaks]
|
||||||
|
|
||||||
|
## [seeds]
|
||||||
|
# Create the CV_8U version of the distance image
|
||||||
|
# It is needed for findContours()
|
||||||
|
dist_8u = dist.astype('uint8')
|
||||||
|
|
||||||
|
# Find total markers
|
||||||
|
_, contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
|
# Create the marker image for the watershed algorithm
|
||||||
|
markers = np.zeros(dist.shape, dtype=np.int32)
|
||||||
|
|
||||||
|
# Draw the foreground markers
|
||||||
|
for i in range(len(contours)):
|
||||||
|
cv.drawContours(markers, contours, i, (i+1), -1)
|
||||||
|
|
||||||
|
# Draw the background marker
|
||||||
|
cv.circle(markers, (5,5), 3, (255,255,255), -1)
|
||||||
|
cv.imshow('Markers', markers*10000)
|
||||||
|
## [seeds]
|
||||||
|
|
||||||
|
## [watershed]
|
||||||
|
# Perform the watershed algorithm
|
||||||
|
cv.watershed(imgResult, markers)
|
||||||
|
|
||||||
|
#mark = np.zeros(markers.shape, dtype=np.uint8)
|
||||||
|
mark = markers.astype('uint8')
|
||||||
|
mark = cv.bitwise_not(mark)
|
||||||
|
# uncomment this if you want to see how the mark
|
||||||
|
# image looks like at that point
|
||||||
|
#cv.imshow('Markers_v2', mark)
|
||||||
|
|
||||||
|
# Generate random colors
|
||||||
|
colors = []
|
||||||
|
for contour in contours:
|
||||||
|
colors.append((rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)))
|
||||||
|
|
||||||
|
# Create the result image
|
||||||
|
dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Fill labeled objects with random colors
|
||||||
|
for i in range(markers.shape[0]):
|
||||||
|
for j in range(markers.shape[1]):
|
||||||
|
index = markers[i,j]
|
||||||
|
if index > 0 and index <= len(contours):
|
||||||
|
dst[i,j,:] = colors[index-1]
|
||||||
|
|
||||||
|
# Visualize the final image
|
||||||
|
cv.imshow('Final Result', dst)
|
||||||
|
## [watershed]
|
||||||
|
|
||||||
|
cv.waitKey()
|
@ -0,0 +1,82 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
import cv2 as cv
|
||||||
|
import numpy as np
|
||||||
|
import argparse
|
||||||
|
import random as rng
|
||||||
|
|
||||||
|
rng.seed(12345)
|
||||||
|
|
||||||
|
def thresh_callback(val):
|
||||||
|
threshold = val
|
||||||
|
|
||||||
|
## [Canny]
|
||||||
|
# Detect edges using Canny
|
||||||
|
canny_output = cv.Canny(src_gray, threshold, threshold * 2)
|
||||||
|
## [Canny]
|
||||||
|
|
||||||
|
## [findContours]
|
||||||
|
# Find contours
|
||||||
|
_, contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
|
||||||
|
## [findContours]
|
||||||
|
|
||||||
|
## [allthework]
|
||||||
|
# Approximate contours to polygons + get bounding rects and circles
|
||||||
|
contours_poly = [None]*len(contours)
|
||||||
|
boundRect = [None]*len(contours)
|
||||||
|
centers = [None]*len(contours)
|
||||||
|
radius = [None]*len(contours)
|
||||||
|
for i in range(len(contours)):
|
||||||
|
contours_poly[i] = cv.approxPolyDP(contours[i], 3, True)
|
||||||
|
boundRect[i] = cv.boundingRect(contours_poly[i])
|
||||||
|
centers[i], radius[i] = cv.minEnclosingCircle(contours_poly[i])
|
||||||
|
## [allthework]
|
||||||
|
|
||||||
|
## [zeroMat]
|
||||||
|
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
|
||||||
|
## [zeroMat]
|
||||||
|
|
||||||
|
## [forContour]
|
||||||
|
# Draw polygonal contour + bonding rects + circles
|
||||||
|
for i in range(len(contours)):
|
||||||
|
color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
|
||||||
|
cv.drawContours(drawing, contours_poly, i, color)
|
||||||
|
cv.rectangle(drawing, (int(boundRect[i][0]), int(boundRect[i][1])), \
|
||||||
|
(int(boundRect[i][0]+boundRect[i][2]), int(boundRect[i][1]+boundRect[i][3])), color, 2)
|
||||||
|
cv.circle(drawing, (int(centers[i][0]), int(centers[i][1])), int(radius[i]), color, 2)
|
||||||
|
## [forContour]
|
||||||
|
|
||||||
|
## [showDrawings]
|
||||||
|
# Show in a window
|
||||||
|
cv.imshow('Contours', drawing)
|
||||||
|
## [showDrawings]
|
||||||
|
|
||||||
|
## [setup]
|
||||||
|
# Load source image
|
||||||
|
parser = argparse.ArgumentParser(description='Code for Creating Bounding boxes and circles for contours tutorial.')
|
||||||
|
parser.add_argument('--input', help='Path to input image.', default='../data/stuff.jpg')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
src = cv.imread(args.input)
|
||||||
|
if src is None:
|
||||||
|
print('Could not open or find the image:', args.input)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
# Convert image to gray and blur it
|
||||||
|
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
|
||||||
|
src_gray = cv.blur(src_gray, (3,3))
|
||||||
|
## [setup]
|
||||||
|
|
||||||
|
## [createWindow]
|
||||||
|
# Create Window
|
||||||
|
source_window = 'Source'
|
||||||
|
cv.namedWindow(source_window)
|
||||||
|
cv.imshow(source_window, src)
|
||||||
|
## [createWindow]
|
||||||
|
## [trackbar]
|
||||||
|
max_thresh = 255
|
||||||
|
thresh = 100 # initial threshold
|
||||||
|
cv.createTrackbar('Canny thresh:', source_window, thresh, max_thresh, thresh_callback)
|
||||||
|
thresh_callback(thresh)
|
||||||
|
## [trackbar]
|
||||||
|
|
||||||
|
cv.waitKey()
|
@ -0,0 +1,82 @@
|
|||||||
|
from __future__ import print_function
|
||||||
|
import cv2 as cv
|
||||||
|
import numpy as np
|
||||||
|
import argparse
|
||||||
|
import random as rng
|
||||||
|
|
||||||
|
rng.seed(12345)
|
||||||
|
|
||||||
|
def thresh_callback(val):
|
||||||
|
threshold = val
|
||||||
|
|
||||||
|
## [Canny]
|
||||||
|
# Detect edges using Canny
|
||||||
|
canny_output = cv.Canny(src_gray, threshold, threshold * 2)
|
||||||
|
## [Canny]
|
||||||
|
|
||||||
|
## [findContours]
|
||||||
|
# Find contours
|
||||||
|
_, contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
|
||||||
|
## [findContours]
|
||||||
|
|
||||||
|
# Find the rotated rectangles and ellipses for each contour
|
||||||
|
minRect = [None]*len(contours)
|
||||||
|
minEllipse = [None]*len(contours)
|
||||||
|
for i in range(len(contours)):
|
||||||
|
minRect[i] = cv.minAreaRect(contours[i])
|
||||||
|
if contours[i].shape[0] > 5:
|
||||||
|
minEllipse[i] = cv.fitEllipse(contours[i])
|
||||||
|
|
||||||
|
# Draw contours + rotated rects + ellipses
|
||||||
|
## [zeroMat]
|
||||||
|
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
|
||||||
|
## [zeroMat]
|
||||||
|
## [forContour]
|
||||||
|
for i in range(len(contours)):
|
||||||
|
color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
|
||||||
|
# contour
|
||||||
|
cv.drawContours(drawing, contours, i, color)
|
||||||
|
# ellipse
|
||||||
|
if contours[i].shape[0] > 5:
|
||||||
|
cv.ellipse(drawing, minEllipse[i], color, 2)
|
||||||
|
# rotated rectangle
|
||||||
|
box = cv.boxPoints(minRect[i])
|
||||||
|
box = np.intp(box) #np.intp: Integer used for indexing (same as C ssize_t; normally either int32 or int64)
|
||||||
|
cv.drawContours(drawing, [box], 0, color)
|
||||||
|
## [forContour]
|
||||||
|
|
||||||
|
## [showDrawings]
|
||||||
|
# Show in a window
|
||||||
|
cv.imshow('Contours', drawing)
|
||||||
|
## [showDrawings]
|
||||||
|
|
||||||
|
## [setup]
|
||||||
|
# Load source image
|
||||||
|
parser = argparse.ArgumentParser(description='Code for Creating Bounding rotated boxes and ellipses for contours tutorial.')
|
||||||
|
parser.add_argument('--input', help='Path to input image.', default='../data/stuff.jpg')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
src = cv.imread(args.input)
|
||||||
|
if src is None:
|
||||||
|
print('Could not open or find the image:', args.input)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
# Convert image to gray and blur it
|
||||||
|
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
|
||||||
|
src_gray = cv.blur(src_gray, (3,3))
|
||||||
|
## [setup]
|
||||||
|
|
||||||
|
## [createWindow]
|
||||||
|
# Create Window
|
||||||
|
source_window = 'Source'
|
||||||
|
cv.namedWindow(source_window)
|
||||||
|
cv.imshow(source_window, src)
|
||||||
|
## [createWindow]
|
||||||
|
## [trackbar]
|
||||||
|
max_thresh = 255
|
||||||
|
thresh = 100 # initial threshold
|
||||||
|
cv.createTrackbar('Canny Thresh:', source_window, thresh, max_thresh, thresh_callback)
|
||||||
|
thresh_callback(thresh)
|
||||||
|
## [trackbar]
|
||||||
|
|
||||||
|
cv.waitKey()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user