OpenCV 3.4.2

This commit is contained in:
Alexander Alekhin 2018-07-04 14:05:47 +03:00
commit 9e1b1e5389
107 changed files with 7390 additions and 2477 deletions

View File

@ -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__); \
} \ } \

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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.
![image](images/calib_radial.jpg) ![image](images/calib_radial.jpg)
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:
![image](images/calib_result.jpg) ![image](images/calib_result.jpg)
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

View File

@ -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:

View File

@ -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).
![](images/graf.png) ![](images/graf.png)
@ -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
![](images/res.png) ![](images/res.png)
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

View File

@ -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

View File

@ -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
![](images/source.jpeg)
-# 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
![](images/black_bg.jpeg) @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
![](images/laplace.jpeg) @end_toggle
![](images/sharp.jpeg)
-# 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
![](images/bin.jpeg) @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: ![](images/source.jpeg)
@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp dist
![](images/dist_transf.jpeg)
-# 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
![](images/peaks.jpeg)
-# 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
![](images/markers.jpeg) @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
![](images/final.jpeg) @end_toggle
@add_toggle_python
@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py black_bg
@end_toggle
![](images/black_bg.jpeg)
- 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
![](images/laplace.jpeg)
![](images/sharp.jpeg)
- 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
![](images/bin.jpeg)
- 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
![](images/dist_transf.jpeg)
- 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
![](images/peaks.jpeg)
- 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
![](images/markers.jpeg)
- 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
![](images/final.jpeg)

View File

@ -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
------ ------

View File

@ -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
----------- -----------

View File

@ -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
----------- -----------

View File

@ -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
----------- -----------

View File

@ -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
----------- -----------

View File

@ -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
----------- -----------

View File

@ -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

View File

@ -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

View File

@ -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

View 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(); }
}}

View File

@ -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)
{ {

View File

@ -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)
{ {

View File

@ -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()

View File

@ -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:

View File

@ -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),

View File

@ -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", "");

View File

@ -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 &params)
{ {
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)

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -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);
} }

View File

@ -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));
} }

View File

@ -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);
} }

View File

@ -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

View 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

View File

@ -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

View File

@ -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;
}; };

View File

@ -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_)

View File

@ -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())

View File

@ -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);
} }
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View File

@ -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),

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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);
}
} }

View File

@ -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.

View File

@ -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 );
} }
} }

View File

@ -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
} }

View 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;
}
}

View 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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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>();
} }

View File

@ -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.");
} }
} }

View File

@ -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

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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 );
} }

View File

@ -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]
} }

View File

@ -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 );
} }

View File

@ -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 );
} }

View File

@ -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 );
}
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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]);
} }

View File

@ -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]);
} }

View File

@ -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

View File

@ -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;

View File

@ -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

View 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

View File

@ -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)

View File

@ -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'

View File

@ -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);
}
}

View File

@ -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);
}
});
}
}

View File

@ -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);
}
});
}
}

View File

@ -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);
}
});
}
}

View 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);
}
});
}
}

View File

@ -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);
}
});
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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]);
} }
} }

View File

@ -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]);
} }
} }

View File

@ -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);

View File

@ -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__

View File

@ -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()

View File

@ -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()

View File

@ -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