vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
if(IOS OR WINRT OR (NOT HAVE_CUDA AND NOT BUILD_CUDA_STUBS))
|
||||
ocv_module_disable(cudaoptflow)
|
||||
endif()
|
||||
|
||||
set(the_description "CUDA-accelerated Optical Flow")
|
||||
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127 /wd4324 /wd4512 -Wundef -Wmissing-declarations -Wshadow -Wstrict-aliasing)
|
||||
|
||||
ocv_define_module(cudaoptflow opencv_video opencv_optflow opencv_cudaarithm opencv_cudawarping opencv_cudaimgproc opencv_cudafeatures2d OPTIONAL opencv_cudalegacy WRAP python)
|
||||
|
||||
if(ENABLE_CUDA_FIRST_CLASS_LANGUAGE)
|
||||
ocv_target_link_libraries(${the_module} PRIVATE CUDA::cudart${CUDA_LIB_EXT})
|
||||
endif()
|
||||
|
||||
if(NOT CUDA_VERSION VERSION_LESS "10.0")
|
||||
set(NVIDIA_OPTICAL_FLOW_2_0_HEADERS_COMMIT "edb50da3cf849840d680249aa6dbef248ebce2ca")
|
||||
set(NVIDIA_OPTICAL_FLOW_2_0_HEADERS_MD5 "a73cd48b18dcc0cc8933b30796074191")
|
||||
set(NVIDIA_OPTICAL_FLOW_2_0_HEADERS_PATH "${OpenCV_BINARY_DIR}/3rdparty/NVIDIAOpticalFlowSDK_2_0_Headers")
|
||||
ocv_download(FILENAME "${NVIDIA_OPTICAL_FLOW_2_0_HEADERS_COMMIT}.zip"
|
||||
HASH ${NVIDIA_OPTICAL_FLOW_2_0_HEADERS_MD5}
|
||||
URL "https://github.com/NVIDIA/NVIDIAOpticalFlowSDK/archive/"
|
||||
DESTINATION_DIR "${NVIDIA_OPTICAL_FLOW_2_0_HEADERS_PATH}"
|
||||
STATUS NVIDIA_OPTICAL_FLOW_2_0_HEADERS_DOWNLOAD_SUCCESS
|
||||
ID "NVIDIA_OPTICAL_FLOW"
|
||||
UNPACK RELATIVE_URL)
|
||||
|
||||
if(NOT NVIDIA_OPTICAL_FLOW_2_0_HEADERS_DOWNLOAD_SUCCESS)
|
||||
message(STATUS "Failed to download NVIDIA_Optical_Flow_2_0 Headers")
|
||||
else()
|
||||
message(STATUS "Building with NVIDIA Optical Flow API 2.0")
|
||||
add_definitions(-DHAVE_NVIDIA_OPTFLOW=2)
|
||||
ocv_include_directories(SYSTEM "${NVIDIA_OPTICAL_FLOW_2_0_HEADERS_PATH}/NVIDIAOpticalFlowSDK-${NVIDIA_OPTICAL_FLOW_2_0_HEADERS_COMMIT}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,594 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_CUDAOPTFLOW_HPP
|
||||
#define OPENCV_CUDAOPTFLOW_HPP
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error cudaoptflow.hpp header must be compiled as C++
|
||||
#endif
|
||||
|
||||
#include "opencv2/core/cuda.hpp"
|
||||
|
||||
/**
|
||||
@addtogroup cuda
|
||||
@{
|
||||
@defgroup cudaoptflow Optical Flow
|
||||
@}
|
||||
*/
|
||||
|
||||
namespace cv { namespace cuda {
|
||||
|
||||
//! @addtogroup cudaoptflow
|
||||
//! @{
|
||||
|
||||
//
|
||||
// Interface
|
||||
//
|
||||
|
||||
/** @brief Base interface for dense optical flow algorithms.
|
||||
*/
|
||||
class CV_EXPORTS_W DenseOpticalFlow : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Calculates a dense optical flow.
|
||||
|
||||
@param I0 first input image.
|
||||
@param I1 second input image of the same size and the same type as I0.
|
||||
@param flow computed flow image that has the same size as I0 and type CV_32FC2.
|
||||
@param stream Stream for the asynchronous version.
|
||||
*/
|
||||
CV_WRAP virtual void calc(InputArray I0, InputArray I1, InputOutputArray flow, Stream& stream = Stream::Null()) = 0;
|
||||
};
|
||||
|
||||
/** @brief Base interface for sparse optical flow algorithms.
|
||||
*/
|
||||
class CV_EXPORTS_W SparseOpticalFlow : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Calculates a sparse optical flow.
|
||||
|
||||
@param prevImg First input image.
|
||||
@param nextImg Second input image of the same size and the same type as prevImg.
|
||||
@param prevPts Vector of 2D points for which the flow needs to be found.
|
||||
@param nextPts Output vector of 2D points containing the calculated new positions of input features in the second image.
|
||||
@param status Output status vector. Each element of the vector is set to 1 if the
|
||||
flow for the corresponding features has been found. Otherwise, it is set to 0.
|
||||
@param err Optional output vector that contains error response for each point (inverse confidence).
|
||||
@param stream Stream for the asynchronous version.
|
||||
*/
|
||||
CV_WRAP virtual void calc(InputArray prevImg, InputArray nextImg,
|
||||
InputArray prevPts, InputOutputArray nextPts,
|
||||
OutputArray status,
|
||||
OutputArray err = cv::noArray(),
|
||||
Stream& stream = Stream::Null()) = 0;
|
||||
};
|
||||
/** @brief Base Interface for optical flow algorithms using NVIDIA Optical Flow SDK.
|
||||
*/
|
||||
class CV_EXPORTS_W NvidiaHWOpticalFlow : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Calculates Optical Flow using NVIDIA Optical Flow SDK.
|
||||
|
||||
* NVIDIA GPUs starting with Turing contain a dedicated hardware accelerator for computing optical flow vectors between pairs of images.
|
||||
* The optical flow hardware accelerator generates block-based optical flow vectors.
|
||||
* The size of the block depends on hardware in use, and can be queried using the function getGridSize().
|
||||
* The block-based flow vectors generated by the hardware can be converted to dense representation (i.e. per-pixel flow vectors) using upSampler() helper function, if needed.
|
||||
* The flow vectors are stored in CV_16SC2 format with x and y components of each flow vector in 16-bit signed fixed point representation S10.5.
|
||||
|
||||
@param inputImage Input image.
|
||||
@param referenceImage Reference image of the same size and the same type as input image.
|
||||
@param flow A buffer consisting of inputImage.Size() / getGridSize() flow vectors in CV_16SC2 format.
|
||||
@param stream It is highly recommended that CUDA streams for pre and post processing of optical flow vectors should be set once per session in create() function as a part of optical flow session creation.
|
||||
This parameter is left here for backward compatibility and may be removed in the future.
|
||||
Default value is NULL stream;
|
||||
@param hint Hint buffer if client provides external hints. Must have same size as flow buffer.
|
||||
Caller can provide flow vectors as hints for optical flow calculation.
|
||||
@param cost Cost buffer contains numbers indicating the confidence associated with each of the generated flow vectors.
|
||||
Higher the cost, lower the confidence. Cost buffer is of type CV_32SC1.
|
||||
|
||||
@note
|
||||
- Client must use critical sections around each calc() function if calling it from multiple threads.
|
||||
*/
|
||||
CV_WRAP virtual void calc(
|
||||
InputArray inputImage,
|
||||
InputArray referenceImage,
|
||||
InputOutputArray flow,
|
||||
Stream& stream = Stream::Null(),
|
||||
InputArray hint = cv::noArray(),
|
||||
OutputArray cost = cv::noArray()) = 0;
|
||||
|
||||
/** @brief Releases all buffers, contexts and device pointers.
|
||||
*/
|
||||
CV_WRAP virtual void collectGarbage() = 0;
|
||||
|
||||
/** @brief Returns grid size of output buffer as per the hardware's capability.
|
||||
*/
|
||||
CV_WRAP virtual int getGridSize() const = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// BroxOpticalFlow
|
||||
//
|
||||
|
||||
/** @brief Class computing the optical flow for two images using Brox et al Optical Flow algorithm (@cite Brox2004).
|
||||
*/
|
||||
class CV_EXPORTS_W BroxOpticalFlow : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual double getFlowSmoothness() const = 0;
|
||||
CV_WRAP virtual void setFlowSmoothness(double alpha) = 0;
|
||||
|
||||
CV_WRAP virtual double getGradientConstancyImportance() const = 0;
|
||||
CV_WRAP virtual void setGradientConstancyImportance(double gamma) = 0;
|
||||
|
||||
CV_WRAP virtual double getPyramidScaleFactor() const = 0;
|
||||
CV_WRAP virtual void setPyramidScaleFactor(double scale_factor) = 0;
|
||||
|
||||
//! number of lagged non-linearity iterations (inner loop)
|
||||
CV_WRAP virtual int getInnerIterations() const = 0;
|
||||
CV_WRAP virtual void setInnerIterations(int inner_iterations) = 0;
|
||||
|
||||
//! number of warping iterations (number of pyramid levels)
|
||||
CV_WRAP virtual int getOuterIterations() const = 0;
|
||||
CV_WRAP virtual void setOuterIterations(int outer_iterations) = 0;
|
||||
|
||||
//! number of linear system solver iterations
|
||||
CV_WRAP virtual int getSolverIterations() const = 0;
|
||||
CV_WRAP virtual void setSolverIterations(int solver_iterations) = 0;
|
||||
|
||||
CV_WRAP static Ptr<BroxOpticalFlow> create(
|
||||
double alpha = 0.197,
|
||||
double gamma = 50.0,
|
||||
double scale_factor = 0.8,
|
||||
int inner_iterations = 5,
|
||||
int outer_iterations = 150,
|
||||
int solver_iterations = 10);
|
||||
};
|
||||
|
||||
//
|
||||
// PyrLKOpticalFlow
|
||||
//
|
||||
|
||||
/** @brief Class used for calculating a sparse optical flow.
|
||||
|
||||
The class can calculate an optical flow for a sparse feature set using the
|
||||
iterative Lucas-Kanade method with pyramids.
|
||||
|
||||
@sa calcOpticalFlowPyrLK
|
||||
|
||||
@note
|
||||
- An example of the Lucas Kanade optical flow algorithm can be found at
|
||||
opencv_source_code/samples/gpu/pyrlk_optical_flow.cpp
|
||||
*/
|
||||
class CV_EXPORTS_W SparsePyrLKOpticalFlow : public SparseOpticalFlow
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual Size getWinSize() const = 0;
|
||||
CV_WRAP virtual void setWinSize(Size winSize) = 0;
|
||||
|
||||
CV_WRAP virtual int getMaxLevel() const = 0;
|
||||
CV_WRAP virtual void setMaxLevel(int maxLevel) = 0;
|
||||
|
||||
CV_WRAP virtual int getNumIters() const = 0;
|
||||
CV_WRAP virtual void setNumIters(int iters) = 0;
|
||||
|
||||
CV_WRAP virtual bool getUseInitialFlow() const = 0;
|
||||
CV_WRAP virtual void setUseInitialFlow(bool useInitialFlow) = 0;
|
||||
|
||||
CV_WRAP static Ptr<cuda::SparsePyrLKOpticalFlow> create(
|
||||
Size winSize = Size(21, 21),
|
||||
int maxLevel = 3,
|
||||
int iters = 30,
|
||||
bool useInitialFlow = false);
|
||||
};
|
||||
|
||||
/** @brief Class used for calculating a dense optical flow.
|
||||
|
||||
The class can calculate an optical flow for a dense optical flow using the
|
||||
iterative Lucas-Kanade method with pyramids.
|
||||
*/
|
||||
class CV_EXPORTS_W DensePyrLKOpticalFlow : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual Size getWinSize() const = 0;
|
||||
CV_WRAP virtual void setWinSize(Size winSize) = 0;
|
||||
|
||||
CV_WRAP virtual int getMaxLevel() const = 0;
|
||||
CV_WRAP virtual void setMaxLevel(int maxLevel) = 0;
|
||||
|
||||
CV_WRAP virtual int getNumIters() const = 0;
|
||||
CV_WRAP virtual void setNumIters(int iters) = 0;
|
||||
|
||||
CV_WRAP virtual bool getUseInitialFlow() const = 0;
|
||||
CV_WRAP virtual void setUseInitialFlow(bool useInitialFlow) = 0;
|
||||
|
||||
CV_WRAP static Ptr<DensePyrLKOpticalFlow> create(
|
||||
Size winSize = Size(13, 13),
|
||||
int maxLevel = 3,
|
||||
int iters = 30,
|
||||
bool useInitialFlow = false);
|
||||
};
|
||||
|
||||
//
|
||||
// FarnebackOpticalFlow
|
||||
//
|
||||
|
||||
/** @brief Class computing a dense optical flow using the Gunnar Farneback's algorithm.
|
||||
*/
|
||||
class CV_EXPORTS_W FarnebackOpticalFlow : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
CV_WRAP virtual int getNumLevels() const = 0;
|
||||
CV_WRAP virtual void setNumLevels(int numLevels) = 0;
|
||||
|
||||
CV_WRAP virtual double getPyrScale() const = 0;
|
||||
CV_WRAP virtual void setPyrScale(double pyrScale) = 0;
|
||||
|
||||
CV_WRAP virtual bool getFastPyramids() const = 0;
|
||||
CV_WRAP virtual void setFastPyramids(bool fastPyramids) = 0;
|
||||
|
||||
CV_WRAP virtual int getWinSize() const = 0;
|
||||
CV_WRAP virtual void setWinSize(int winSize) = 0;
|
||||
|
||||
CV_WRAP virtual int getNumIters() const = 0;
|
||||
CV_WRAP virtual void setNumIters(int numIters) = 0;
|
||||
|
||||
CV_WRAP virtual int getPolyN() const = 0;
|
||||
CV_WRAP virtual void setPolyN(int polyN) = 0;
|
||||
|
||||
CV_WRAP virtual double getPolySigma() const = 0;
|
||||
CV_WRAP virtual void setPolySigma(double polySigma) = 0;
|
||||
|
||||
CV_WRAP virtual int getFlags() const = 0;
|
||||
CV_WRAP virtual void setFlags(int flags) = 0;
|
||||
|
||||
CV_WRAP static Ptr<cuda::FarnebackOpticalFlow> create(
|
||||
int numLevels = 5,
|
||||
double pyrScale = 0.5,
|
||||
bool fastPyramids = false,
|
||||
int winSize = 13,
|
||||
int numIters = 10,
|
||||
int polyN = 5,
|
||||
double polySigma = 1.1,
|
||||
int flags = 0);
|
||||
};
|
||||
|
||||
//
|
||||
// OpticalFlowDual_TVL1
|
||||
//
|
||||
|
||||
/** @brief Implementation of the Zach, Pock and Bischof Dual TV-L1 Optical Flow method.
|
||||
*
|
||||
* @note C. Zach, T. Pock and H. Bischof, "A Duality Based Approach for Realtime TV-L1 Optical Flow".
|
||||
* @note Javier Sanchez, Enric Meinhardt-Llopis and Gabriele Facciolo. "TV-L1 Optical Flow Estimation".
|
||||
*/
|
||||
class CV_EXPORTS_W OpticalFlowDual_TVL1 : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Time step of the numerical scheme.
|
||||
*/
|
||||
CV_WRAP virtual double getTau() const = 0;
|
||||
CV_WRAP virtual void setTau(double tau) = 0;
|
||||
|
||||
/**
|
||||
* Weight parameter for the data term, attachment parameter.
|
||||
* This is the most relevant parameter, which determines the smoothness of the output.
|
||||
* The smaller this parameter is, the smoother the solutions we obtain.
|
||||
* It depends on the range of motions of the images, so its value should be adapted to each image sequence.
|
||||
*/
|
||||
CV_WRAP virtual double getLambda() const = 0;
|
||||
CV_WRAP virtual void setLambda(double lambda) = 0;
|
||||
|
||||
/**
|
||||
* Weight parameter for (u - v)^2, tightness parameter.
|
||||
* It serves as a link between the attachment and the regularization terms.
|
||||
* In theory, it should have a small value in order to maintain both parts in correspondence.
|
||||
* The method is stable for a large range of values of this parameter.
|
||||
*/
|
||||
CV_WRAP virtual double getGamma() const = 0;
|
||||
CV_WRAP virtual void setGamma(double gamma) = 0;
|
||||
|
||||
/**
|
||||
* parameter used for motion estimation. It adds a variable allowing for illumination variations
|
||||
* Set this parameter to 1. if you have varying illumination.
|
||||
* See: Chambolle et al, A First-Order Primal-Dual Algorithm for Convex Problems with Applications to Imaging
|
||||
* Journal of Mathematical imaging and vision, may 2011 Vol 40 issue 1, pp 120-145
|
||||
*/
|
||||
CV_WRAP virtual double getTheta() const = 0;
|
||||
CV_WRAP virtual void setTheta(double theta) = 0;
|
||||
|
||||
/**
|
||||
* Number of scales used to create the pyramid of images.
|
||||
*/
|
||||
CV_WRAP virtual int getNumScales() const = 0;
|
||||
CV_WRAP virtual void setNumScales(int nscales) = 0;
|
||||
|
||||
/**
|
||||
* Number of warpings per scale.
|
||||
* Represents the number of times that I1(x+u0) and grad( I1(x+u0) ) are computed per scale.
|
||||
* This is a parameter that assures the stability of the method.
|
||||
* It also affects the running time, so it is a compromise between speed and accuracy.
|
||||
*/
|
||||
CV_WRAP virtual int getNumWarps() const = 0;
|
||||
CV_WRAP virtual void setNumWarps(int warps) = 0;
|
||||
|
||||
/**
|
||||
* Stopping criterion threshold used in the numerical scheme, which is a trade-off between precision and running time.
|
||||
* A small value will yield more accurate solutions at the expense of a slower convergence.
|
||||
*/
|
||||
CV_WRAP virtual double getEpsilon() const = 0;
|
||||
CV_WRAP virtual void setEpsilon(double epsilon) = 0;
|
||||
|
||||
/**
|
||||
* Stopping criterion iterations number used in the numerical scheme.
|
||||
*/
|
||||
CV_WRAP virtual int getNumIterations() const = 0;
|
||||
CV_WRAP virtual void setNumIterations(int iterations) = 0;
|
||||
|
||||
CV_WRAP virtual double getScaleStep() const = 0;
|
||||
CV_WRAP virtual void setScaleStep(double scaleStep) = 0;
|
||||
|
||||
CV_WRAP virtual bool getUseInitialFlow() const = 0;
|
||||
CV_WRAP virtual void setUseInitialFlow(bool useInitialFlow) = 0;
|
||||
|
||||
CV_WRAP static Ptr<OpticalFlowDual_TVL1> create(
|
||||
double tau = 0.25,
|
||||
double lambda = 0.15,
|
||||
double theta = 0.3,
|
||||
int nscales = 5,
|
||||
int warps = 5,
|
||||
double epsilon = 0.01,
|
||||
int iterations = 300,
|
||||
double scaleStep = 0.8,
|
||||
double gamma = 0.0,
|
||||
bool useInitialFlow = false);
|
||||
};
|
||||
|
||||
//
|
||||
// NvidiaOpticalFlow
|
||||
//
|
||||
|
||||
/** @brief Class for computing the optical flow vectors between two images using NVIDIA Optical Flow hardware and Optical Flow SDK 1.0.
|
||||
@note
|
||||
- A sample application demonstrating the use of NVIDIA Optical Flow can be found at
|
||||
opencv_contrib_source_code/modules/cudaoptflow/samples/nvidia_optical_flow.cpp
|
||||
- An example application comparing accuracy and performance of NVIDIA Optical Flow with other optical flow algorithms in OpenCV can be found at
|
||||
opencv_contrib_source_code/modules/cudaoptflow/samples/optical_flow.cpp
|
||||
*/
|
||||
|
||||
class CV_EXPORTS_W NvidiaOpticalFlow_1_0 : public NvidiaHWOpticalFlow
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Supported optical flow performance levels.
|
||||
*/
|
||||
enum NVIDIA_OF_PERF_LEVEL
|
||||
{
|
||||
NV_OF_PERF_LEVEL_UNDEFINED,
|
||||
NV_OF_PERF_LEVEL_SLOW = 5, /**< Slow perf level results in lowest performance and best quality */
|
||||
NV_OF_PERF_LEVEL_MEDIUM = 10, /**< Medium perf level results in low performance and medium quality */
|
||||
NV_OF_PERF_LEVEL_FAST = 20, /**< Fast perf level results in high performance and low quality */
|
||||
NV_OF_PERF_LEVEL_MAX
|
||||
};
|
||||
|
||||
/** @brief The NVIDIA optical flow hardware generates flow vectors at granularity gridSize, which can be queried via function getGridSize().
|
||||
* Upsampler() helper function converts the hardware-generated flow vectors to dense representation (1 flow vector for each pixel)
|
||||
* using nearest neighbour upsampling method.
|
||||
|
||||
@param flow Buffer of type CV_16FC2 containing flow vectors generated by calc().
|
||||
@param imageSize Size of the input image in pixels for which these flow vectors were generated.
|
||||
@param gridSize Granularity of the optical flow vectors returned by calc() function. Can be queried using getGridSize().
|
||||
@param upsampledFlow Buffer of type CV_32FC2, containing upsampled flow vectors, each flow vector for 1 pixel, in the pitch-linear layout.
|
||||
*/
|
||||
CV_WRAP virtual void upSampler(InputArray flow, cv::Size imageSize,
|
||||
int gridSize, InputOutputArray upsampledFlow) = 0;
|
||||
|
||||
/** @brief Instantiate NVIDIA Optical Flow
|
||||
|
||||
@param imageSize Size of input image in pixels.
|
||||
@param perfPreset Optional parameter. Refer [NV OF SDK documentation](https://developer.nvidia.com/opticalflow-sdk) for details about presets.
|
||||
Defaults to NV_OF_PERF_LEVEL_SLOW.
|
||||
@param enableTemporalHints Optional parameter. Flag to enable temporal hints. When set to true, the hardware uses the flow vectors
|
||||
generated in previous call to calc() as internal hints for the current call to calc().
|
||||
Useful when computing flow vectors between successive video frames. Defaults to false.
|
||||
@param enableExternalHints Optional Parameter. Flag to enable passing external hints buffer to calc(). Defaults to false.
|
||||
@param enableCostBuffer Optional Parameter. Flag to enable cost buffer output from calc(). Defaults to false.
|
||||
@param gpuId Optional parameter to select the GPU ID on which the optical flow should be computed. Useful in multi-GPU systems. Defaults to 0.
|
||||
@param inputStream Optical flow algorithm may optionally involve cuda preprocessing on the input buffers.
|
||||
The input cuda stream can be used to pipeline and synchronize the cuda preprocessing tasks with OF HW engine.
|
||||
If input stream is not set, the execute function will use default stream which is NULL stream;
|
||||
@param outputStream Optical flow algorithm may optionally involve cuda post processing on the output flow vectors.
|
||||
The output cuda stream can be used to pipeline and synchronize the cuda post processing tasks with OF HW engine.
|
||||
If output stream is not set, the execute function will use default stream which is NULL stream;
|
||||
*/
|
||||
CV_WRAP static Ptr<NvidiaOpticalFlow_1_0> create(
|
||||
cv::Size imageSize,
|
||||
cv::cuda::NvidiaOpticalFlow_1_0::NVIDIA_OF_PERF_LEVEL perfPreset
|
||||
= cv::cuda::NvidiaOpticalFlow_1_0::NV_OF_PERF_LEVEL_SLOW,
|
||||
bool enableTemporalHints = false,
|
||||
bool enableExternalHints = false,
|
||||
bool enableCostBuffer = false,
|
||||
int gpuId = 0,
|
||||
Stream& inputStream = Stream::Null(),
|
||||
Stream& outputStream = Stream::Null());
|
||||
};
|
||||
|
||||
/** @brief Class for computing the optical flow vectors between two images using NVIDIA Optical Flow hardware and Optical Flow SDK 2.0.
|
||||
@note
|
||||
- A sample application demonstrating the use of NVIDIA Optical Flow can be found at
|
||||
opencv_contrib_source_code/modules/cudaoptflow/samples/nvidia_optical_flow.cpp
|
||||
- An example application comparing accuracy and performance of NVIDIA Optical Flow with other optical flow algorithms in OpenCV can be found at
|
||||
opencv_contrib_source_code/modules/cudaoptflow/samples/optical_flow.cpp
|
||||
*/
|
||||
|
||||
class CV_EXPORTS_W NvidiaOpticalFlow_2_0 : public NvidiaHWOpticalFlow
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Supported optical flow performance levels.
|
||||
*/
|
||||
enum NVIDIA_OF_PERF_LEVEL
|
||||
{
|
||||
NV_OF_PERF_LEVEL_UNDEFINED,
|
||||
NV_OF_PERF_LEVEL_SLOW = 5, /**< Slow perf level results in lowest performance and best quality */
|
||||
NV_OF_PERF_LEVEL_MEDIUM = 10, /**< Medium perf level results in low performance and medium quality */
|
||||
NV_OF_PERF_LEVEL_FAST = 20, /**< Fast perf level results in high performance and low quality */
|
||||
NV_OF_PERF_LEVEL_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* Supported grid size for output buffer.
|
||||
*/
|
||||
enum NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE
|
||||
{
|
||||
NV_OF_OUTPUT_VECTOR_GRID_SIZE_UNDEFINED,
|
||||
NV_OF_OUTPUT_VECTOR_GRID_SIZE_1 = 1, /**< Output buffer grid size is 1x1 */
|
||||
NV_OF_OUTPUT_VECTOR_GRID_SIZE_2 = 2, /**< Output buffer grid size is 2x2 */
|
||||
NV_OF_OUTPUT_VECTOR_GRID_SIZE_4 = 4, /**< Output buffer grid size is 4x4 */
|
||||
NV_OF_OUTPUT_VECTOR_GRID_SIZE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* Supported grid size for hint buffer.
|
||||
*/
|
||||
enum NVIDIA_OF_HINT_VECTOR_GRID_SIZE
|
||||
{
|
||||
NV_OF_HINT_VECTOR_GRID_SIZE_UNDEFINED,
|
||||
NV_OF_HINT_VECTOR_GRID_SIZE_1 = 1, /**< Hint buffer grid size is 1x1.*/
|
||||
NV_OF_HINT_VECTOR_GRID_SIZE_2 = 2, /**< Hint buffer grid size is 2x2.*/
|
||||
NV_OF_HINT_VECTOR_GRID_SIZE_4 = 4, /**< Hint buffer grid size is 4x4.*/
|
||||
NV_OF_HINT_VECTOR_GRID_SIZE_8 = 8, /**< Hint buffer grid size is 8x8.*/
|
||||
NV_OF_HINT_VECTOR_GRID_SIZE_MAX
|
||||
};
|
||||
|
||||
/** @brief convertToFloat() helper function converts the hardware-generated flow vectors to floating point representation (1 flow vector for gridSize).
|
||||
* gridSize can be queried via function getGridSize().
|
||||
|
||||
@param flow Buffer of type CV_16FC2 containing flow vectors generated by calc().
|
||||
@param floatFlow Buffer of type CV_32FC2, containing flow vectors in floating point representation, each flow vector for 1 pixel per gridSize, in the pitch-linear layout.
|
||||
*/
|
||||
CV_WRAP virtual void convertToFloat(InputArray flow, InputOutputArray floatFlow) = 0;
|
||||
|
||||
/** @brief Instantiate NVIDIA Optical Flow
|
||||
|
||||
@param imageSize Size of input image in pixels.
|
||||
@param perfPreset Optional parameter. Refer [NV OF SDK documentation](https://developer.nvidia.com/opticalflow-sdk) for details about presets.
|
||||
Defaults to NV_OF_PERF_LEVEL_SLOW.
|
||||
@param outputGridSize Optional parameter. Refer [NV OF SDK documentation](https://developer.nvidia.com/opticalflow-sdk) for details about output grid sizes.
|
||||
Defaults to NV_OF_OUTPUT_VECTOR_GRID_SIZE_1.
|
||||
@param hintGridSize Optional parameter. Refer [NV OF SDK documentation](https://developer.nvidia.com/opticalflow-sdk) for details about hint grid sizes.
|
||||
Defaults to NV_OF_HINT_VECTOR_GRID_SIZE_1.
|
||||
@param enableTemporalHints Optional parameter. Flag to enable temporal hints. When set to true, the hardware uses the flow vectors
|
||||
generated in previous call to calc() as internal hints for the current call to calc().
|
||||
Useful when computing flow vectors between successive video frames. Defaults to false.
|
||||
@param enableExternalHints Optional Parameter. Flag to enable passing external hints buffer to calc(). Defaults to false.
|
||||
@param enableCostBuffer Optional Parameter. Flag to enable cost buffer output from calc(). Defaults to false.
|
||||
@param gpuId Optional parameter to select the GPU ID on which the optical flow should be computed. Useful in multi-GPU systems. Defaults to 0.
|
||||
@param inputStream Optical flow algorithm may optionally involve cuda preprocessing on the input buffers.
|
||||
The input cuda stream can be used to pipeline and synchronize the cuda preprocessing tasks with OF HW engine.
|
||||
If input stream is not set, the execute function will use default stream which is NULL stream;
|
||||
@param outputStream Optical flow algorithm may optionally involve cuda post processing on the output flow vectors.
|
||||
The output cuda stream can be used to pipeline and synchronize the cuda post processing tasks with OF HW engine.
|
||||
If output stream is not set, the execute function will use default stream which is NULL stream;
|
||||
*/
|
||||
CV_WRAP static Ptr<NvidiaOpticalFlow_2_0> create(
|
||||
cv::Size imageSize,
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL perfPreset
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NV_OF_PERF_LEVEL_SLOW,
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE outputGridSize
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NV_OF_OUTPUT_VECTOR_GRID_SIZE_1,
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE hintGridSize
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NV_OF_HINT_VECTOR_GRID_SIZE_1,
|
||||
bool enableTemporalHints = false,
|
||||
bool enableExternalHints = false,
|
||||
bool enableCostBuffer = false,
|
||||
int gpuId = 0,
|
||||
Stream& inputStream = Stream::Null(),
|
||||
Stream& outputStream = Stream::Null());
|
||||
|
||||
/** @brief Instantiate NVIDIA Optical Flow with ROI Feature
|
||||
|
||||
@param imageSize Size of input image in pixels.
|
||||
@param roiData Pointer to ROI data.
|
||||
@param perfPreset Optional parameter. Refer [NV OF SDK documentation](https://developer.nvidia.com/opticalflow-sdk) for details about presets.
|
||||
Defaults to NV_OF_PERF_LEVEL_SLOW.
|
||||
@param outputGridSize Optional parameter. Refer [NV OF SDK documentation](https://developer.nvidia.com/opticalflow-sdk) for details about output grid sizes.
|
||||
Defaults to NV_OF_OUTPUT_VECTOR_GRID_SIZE_1.
|
||||
@param hintGridSize Optional parameter. Refer [NV OF SDK documentation](https://developer.nvidia.com/opticalflow-sdk) for details about hint grid sizes.
|
||||
Defaults to NV_OF_HINT_VECTOR_GRID_SIZE_1.
|
||||
@param enableTemporalHints Optional parameter. Flag to enable temporal hints. When set to true, the hardware uses the flow vectors
|
||||
generated in previous call to calc() as internal hints for the current call to calc().
|
||||
Useful when computing flow vectors between successive video frames. Defaults to false.
|
||||
@param enableExternalHints Optional Parameter. Flag to enable passing external hints buffer to calc(). Defaults to false.
|
||||
@param enableCostBuffer Optional Parameter. Flag to enable cost buffer output from calc(). Defaults to false.
|
||||
@param gpuId Optional parameter to select the GPU ID on which the optical flow should be computed. Useful in multi-GPU systems. Defaults to 0.
|
||||
@param inputStream Optical flow algorithm may optionally involve cuda preprocessing on the input buffers.
|
||||
The input cuda stream can be used to pipeline and synchronize the cuda preprocessing tasks with OF HW engine.
|
||||
If input stream is not set, the execute function will use default stream which is NULL stream;
|
||||
@param outputStream Optical flow algorithm may optionally involve cuda post processing on the output flow vectors.
|
||||
The output cuda stream can be used to pipeline and synchronize the cuda post processing tasks with OF HW engine.
|
||||
If output stream is not set, the execute function will use default stream which is NULL stream;
|
||||
*/
|
||||
CV_WRAP static Ptr<NvidiaOpticalFlow_2_0> create(
|
||||
cv::Size imageSize,
|
||||
std::vector<Rect> roiData,
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL perfPreset
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NV_OF_PERF_LEVEL_SLOW,
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE outputGridSize
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NV_OF_OUTPUT_VECTOR_GRID_SIZE_1,
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE hintGridSize
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NV_OF_HINT_VECTOR_GRID_SIZE_1,
|
||||
bool enableTemporalHints = false,
|
||||
bool enableExternalHints = false,
|
||||
bool enableCostBuffer = false,
|
||||
int gpuId = 0,
|
||||
Stream& inputStream = Stream::Null(),
|
||||
Stream& outputStream = Stream::Null());
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}} // namespace cv { namespace cuda {
|
||||
|
||||
#endif /* OPENCV_CUDAOPTFLOW_HPP */
|
||||
@@ -0,0 +1,36 @@
|
||||
import os
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
|
||||
from tests_common import NewOpenCVTests, unittest
|
||||
|
||||
class nvidiaopticalflow_test(NewOpenCVTests):
|
||||
def setUp(self):
|
||||
super(nvidiaopticalflow_test, self).setUp()
|
||||
if not cv.cuda.getCudaEnabledDeviceCount():
|
||||
self.skipTest("No CUDA-capable device is detected")
|
||||
|
||||
@unittest.skipIf('OPENCV_TEST_DATA_PATH' not in os.environ,
|
||||
"OPENCV_TEST_DATA_PATH is not defined")
|
||||
def test_calc(self):
|
||||
frame1 = os.environ['OPENCV_TEST_DATA_PATH'] + '/gpu/opticalflow/frame0.png'
|
||||
frame2 = os.environ['OPENCV_TEST_DATA_PATH'] + '/gpu/opticalflow/frame1.png'
|
||||
|
||||
npMat1 = cv.cvtColor(cv.imread(frame1),cv.COLOR_BGR2GRAY)
|
||||
npMat2 = cv.cvtColor(cv.imread(frame2),cv.COLOR_BGR2GRAY)
|
||||
|
||||
cuMat1 = cv.cuda_GpuMat(npMat1)
|
||||
cuMat2 = cv.cuda_GpuMat(npMat2)
|
||||
try:
|
||||
nvof = cv.cuda_NvidiaOpticalFlow_1_0.create((npMat1.shape[1], npMat1.shape[0]), 5, False, False, False, 0)
|
||||
flow, cost = nvof.calc(cuMat1, cuMat2, None)
|
||||
self.assertTrue(flow.size()[1] > 0 and flow.size()[0] > 0)
|
||||
flowUpSampled = nvof.upSampler(flow, (npMat1.shape[1], npMat1.shape[0]), nvof.getGridSize(), None)
|
||||
nvof.collectGarbage()
|
||||
self.assertTrue(flowUpSampled.size()[1] > 0 and flowUpSampled.size()[0] > 0)
|
||||
except cv.error as e:
|
||||
if e.code == cv.Error.StsBadFunc or e.code == cv.Error.StsBadArg or e.code == cv.Error.StsNullPtr:
|
||||
self.skipTest("Algorithm is not supported in the current environment")
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,47 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
using namespace perf;
|
||||
|
||||
CV_PERF_TEST_CUDA_MAIN(cudaoptflow)
|
||||
@@ -0,0 +1,434 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
typedef pair<string, string> pair_string;
|
||||
|
||||
DEF_PARAM_TEST_1(ImagePair, pair_string);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// BroxOpticalFlow
|
||||
|
||||
PERF_TEST_P(ImagePair, BroxOpticalFlow,
|
||||
Values<pair_string>(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")))
|
||||
{
|
||||
declare.time(300);
|
||||
|
||||
cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
frame0.convertTo(frame0, CV_32FC1, 1.0 / 255.0);
|
||||
frame1.convertTo(frame1, CV_32FC1, 1.0 / 255.0);
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_frame0(frame0);
|
||||
const cv::cuda::GpuMat d_frame1(frame1);
|
||||
cv::cuda::GpuMat flow;
|
||||
|
||||
cv::Ptr<cv::cuda::BroxOpticalFlow> d_alg =
|
||||
cv::cuda::BroxOpticalFlow::create(0.197 /*alpha*/, 50.0 /*gamma*/, 0.8 /*scale_factor*/,
|
||||
10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/);
|
||||
|
||||
TEST_CYCLE() d_alg->calc(d_frame0, d_frame1, flow);
|
||||
|
||||
cv::cuda::GpuMat flows[2];
|
||||
cv::cuda::split(flow, flows);
|
||||
|
||||
cv::cuda::GpuMat u = flows[0];
|
||||
cv::cuda::GpuMat v = flows[1];
|
||||
|
||||
CUDA_SANITY_CHECK(u, 1e-1);
|
||||
CUDA_SANITY_CHECK(v, 1e-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
FAIL_NO_CPU();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// PyrLKOpticalFlowSparse
|
||||
|
||||
DEF_PARAM_TEST(ImagePair_Gray_NPts_WinSz_Levels_Iters, pair_string, bool, int, int, int, int);
|
||||
|
||||
PERF_TEST_P(ImagePair_Gray_NPts_WinSz_Levels_Iters, PyrLKOpticalFlowSparse,
|
||||
Combine(Values<pair_string>(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")),
|
||||
Bool(),
|
||||
Values(8000),
|
||||
Values(21),
|
||||
Values(1, 3),
|
||||
Values(1, 30)))
|
||||
{
|
||||
declare.time(20.0);
|
||||
|
||||
const pair_string imagePair = GET_PARAM(0);
|
||||
const bool useGray = GET_PARAM(1);
|
||||
const int points = GET_PARAM(2);
|
||||
const int winSize = GET_PARAM(3);
|
||||
const int levels = GET_PARAM(4);
|
||||
const int iters = GET_PARAM(5);
|
||||
|
||||
cv::Mat frame0 = readImage(imagePair.first, useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage(imagePair.second, useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Mat gray_frame;
|
||||
if (useGray)
|
||||
gray_frame = frame0;
|
||||
else
|
||||
cv::cvtColor(frame0, gray_frame, cv::COLOR_BGR2GRAY);
|
||||
|
||||
cv::Mat pts;
|
||||
cv::goodFeaturesToTrack(gray_frame, pts, points, 0.01, 0.0);
|
||||
|
||||
frame0.convertTo(frame0, CV_32F);
|
||||
frame1.convertTo(frame1, CV_32F);
|
||||
if(!useGray)
|
||||
{
|
||||
cv::cvtColor(frame0, frame0, cv::COLOR_BGR2BGRA);
|
||||
cv::cvtColor(frame1, frame1, cv::COLOR_BGR2BGRA);
|
||||
}
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_pts(pts.reshape(2, 1));
|
||||
|
||||
cv::Ptr<cv::cuda::SparsePyrLKOpticalFlow> d_pyrLK =
|
||||
cv::cuda::SparsePyrLKOpticalFlow::create(cv::Size(winSize, winSize),
|
||||
levels - 1,
|
||||
iters);
|
||||
|
||||
const cv::cuda::GpuMat d_frame0(frame0);
|
||||
const cv::cuda::GpuMat d_frame1(frame1);
|
||||
cv::cuda::GpuMat nextPts;
|
||||
cv::cuda::GpuMat status;
|
||||
|
||||
TEST_CYCLE() d_pyrLK->calc(d_frame0, d_frame1, d_pts, nextPts, status);
|
||||
|
||||
CUDA_SANITY_CHECK(nextPts);
|
||||
CUDA_SANITY_CHECK(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::Mat nextPts;
|
||||
cv::Mat status;
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
cv::calcOpticalFlowPyrLK(frame0, frame1, pts, nextPts, status, cv::noArray(),
|
||||
cv::Size(winSize, winSize), levels - 1,
|
||||
cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, iters, 0.01));
|
||||
}
|
||||
|
||||
CPU_SANITY_CHECK(nextPts);
|
||||
CPU_SANITY_CHECK(status);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// PyrLKOpticalFlowDense
|
||||
|
||||
DEF_PARAM_TEST(ImagePair_WinSz_Levels_Iters, pair_string, int, int, int);
|
||||
|
||||
PERF_TEST_P(ImagePair_WinSz_Levels_Iters, PyrLKOpticalFlowDense,
|
||||
Combine(Values<pair_string>(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")),
|
||||
Values(3, 5, 7, 9, 13, 17, 21),
|
||||
Values(1, 3),
|
||||
Values(1, 10)))
|
||||
{
|
||||
declare.time(30);
|
||||
|
||||
const pair_string imagePair = GET_PARAM(0);
|
||||
const int winSize = GET_PARAM(1);
|
||||
const int levels = GET_PARAM(2);
|
||||
const int iters = GET_PARAM(3);
|
||||
|
||||
const cv::Mat frame0 = readImage(imagePair.first, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
const cv::Mat frame1 = readImage(imagePair.second, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_frame0(frame0);
|
||||
const cv::cuda::GpuMat d_frame1(frame1);
|
||||
cv::cuda::GpuMat flow;
|
||||
|
||||
cv::Ptr<cv::cuda::DensePyrLKOpticalFlow> d_pyrLK =
|
||||
cv::cuda::DensePyrLKOpticalFlow::create(cv::Size(winSize, winSize),
|
||||
levels - 1,
|
||||
iters);
|
||||
|
||||
TEST_CYCLE() d_pyrLK->calc(d_frame0, d_frame1, flow);
|
||||
|
||||
cv::cuda::GpuMat flows[2];
|
||||
cv::cuda::split(flow, flows);
|
||||
|
||||
cv::cuda::GpuMat u = flows[0];
|
||||
cv::cuda::GpuMat v = flows[1];
|
||||
|
||||
// Sanity test fails on Maxwell and CUDA 7.0
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
else
|
||||
{
|
||||
FAIL_NO_CPU();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// FarnebackOpticalFlow
|
||||
|
||||
PERF_TEST_P(ImagePair, FarnebackOpticalFlow,
|
||||
Values<pair_string>(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")))
|
||||
{
|
||||
declare.time(10);
|
||||
|
||||
const cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
const cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
const int numLevels = 5;
|
||||
const double pyrScale = 0.5;
|
||||
const int winSize = 13;
|
||||
const int numIters = 10;
|
||||
const int polyN = 5;
|
||||
const double polySigma = 1.1;
|
||||
const int flags = 0;
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_frame0(frame0);
|
||||
const cv::cuda::GpuMat d_frame1(frame1);
|
||||
cv::cuda::GpuMat flow;
|
||||
|
||||
cv::Ptr<cv::cuda::FarnebackOpticalFlow> d_farneback =
|
||||
cv::cuda::FarnebackOpticalFlow::create(numLevels, pyrScale, false, winSize,
|
||||
numIters, polyN, polySigma, flags);
|
||||
|
||||
TEST_CYCLE() d_farneback->calc(d_frame0, d_frame1, flow);
|
||||
|
||||
cv::cuda::GpuMat flows[2];
|
||||
cv::cuda::split(flow, flows);
|
||||
|
||||
cv::cuda::GpuMat u = flows[0];
|
||||
cv::cuda::GpuMat v = flows[1];
|
||||
|
||||
CUDA_SANITY_CHECK(u, 1e-4);
|
||||
CUDA_SANITY_CHECK(v, 1e-4);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::Mat flow;
|
||||
|
||||
TEST_CYCLE() cv::calcOpticalFlowFarneback(frame0, frame1, flow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags);
|
||||
|
||||
CPU_SANITY_CHECK(flow);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// OpticalFlowDual_TVL1
|
||||
|
||||
PERF_TEST_P(ImagePair, OpticalFlowDual_TVL1,
|
||||
Values<pair_string>(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")))
|
||||
{
|
||||
declare.time(20);
|
||||
|
||||
const cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
const cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_frame0(frame0);
|
||||
const cv::cuda::GpuMat d_frame1(frame1);
|
||||
cv::cuda::GpuMat flow;
|
||||
|
||||
cv::Ptr<cv::cuda::OpticalFlowDual_TVL1> d_alg =
|
||||
cv::cuda::OpticalFlowDual_TVL1::create();
|
||||
|
||||
TEST_CYCLE() d_alg->calc(d_frame0, d_frame1, flow);
|
||||
|
||||
cv::cuda::GpuMat flows[2];
|
||||
cv::cuda::split(flow, flows);
|
||||
|
||||
cv::cuda::GpuMat u = flows[0];
|
||||
cv::cuda::GpuMat v = flows[1];
|
||||
|
||||
CUDA_SANITY_CHECK(u, 1e-1);
|
||||
CUDA_SANITY_CHECK(v, 1e-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::Mat flow;
|
||||
|
||||
cv::Ptr<cv::optflow::DualTVL1OpticalFlow> alg = cv::optflow::createOptFlow_DualTVL1();
|
||||
alg->setMedianFiltering(1);
|
||||
alg->setInnerIterations(1);
|
||||
alg->setOuterIterations(300);
|
||||
TEST_CYCLE() alg->calc(frame0, frame1, flow);
|
||||
|
||||
CPU_SANITY_CHECK(flow);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// NvidiaOpticalFlow_1_0
|
||||
|
||||
PERF_TEST_P(ImagePair, NvidiaOpticalFlow_1_0,
|
||||
Values<pair_string>(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")))
|
||||
{
|
||||
declare.time(10);
|
||||
|
||||
const cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
const cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
Stream inputStream;
|
||||
Stream outputStream;
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_frame0(frame0);
|
||||
const cv::cuda::GpuMat d_frame1(frame1);
|
||||
cv::cuda::GpuMat d_flow;
|
||||
cv::Ptr<cv::cuda::NvidiaOpticalFlow_1_0> d_nvof;
|
||||
try
|
||||
{
|
||||
d_nvof = cv::cuda::NvidiaOpticalFlow_1_0::create(frame0.size(),
|
||||
cv::cuda::NvidiaOpticalFlow_1_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_FAST,
|
||||
false, false, false, 0, inputStream, outputStream);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if(e.code == Error::StsBadFunc || e.code == Error::StsBadArg || e.code == Error::StsNullPtr)
|
||||
throw SkipTestException("Current configuration is not supported");
|
||||
throw;
|
||||
}
|
||||
|
||||
TEST_CYCLE() d_nvof->calc(d_frame0, d_frame1, d_flow);
|
||||
|
||||
cv::cuda::GpuMat flow[2];
|
||||
cv::cuda::split(d_flow, flow);
|
||||
|
||||
cv::cuda::GpuMat u = flow[0];
|
||||
cv::cuda::GpuMat v = flow[1];
|
||||
|
||||
CUDA_SANITY_CHECK(u, 1e-10);
|
||||
CUDA_SANITY_CHECK(v, 1e-10);
|
||||
|
||||
d_nvof->collectGarbage();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// NvidiaOpticalFlow_2_0
|
||||
|
||||
PERF_TEST_P(ImagePair, NvidiaOpticalFlow_2_0,
|
||||
Values<pair_string>(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")))
|
||||
{
|
||||
declare.time(10);
|
||||
|
||||
const cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
const cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
const cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE outGridSize
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE::NV_OF_OUTPUT_VECTOR_GRID_SIZE_1;
|
||||
const cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE hintGridSize
|
||||
= cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE::NV_OF_HINT_VECTOR_GRID_SIZE_1;
|
||||
Stream inputStream;
|
||||
Stream outputStream;
|
||||
|
||||
if (PERF_RUN_CUDA())
|
||||
{
|
||||
const cv::cuda::GpuMat d_frame0(frame0);
|
||||
const cv::cuda::GpuMat d_frame1(frame1);
|
||||
cv::cuda::GpuMat d_flow;
|
||||
cv::Ptr<cv::cuda::NvidiaOpticalFlow_2_0> d_nvof;
|
||||
try
|
||||
{
|
||||
d_nvof = cv::cuda::NvidiaOpticalFlow_2_0::create(frame0.size(),
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_FAST, outGridSize, hintGridSize,
|
||||
false, false, false, 0, inputStream, outputStream);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if (e.code == Error::StsBadFunc || e.code == Error::StsBadArg || e.code == Error::StsNullPtr)
|
||||
throw SkipTestException("Current configuration is not supported");
|
||||
throw;
|
||||
}
|
||||
|
||||
TEST_CYCLE() d_nvof->calc(d_frame0, d_frame1, d_flow);
|
||||
|
||||
cv::cuda::GpuMat flow[2];
|
||||
cv::cuda::split(d_flow, flow);
|
||||
|
||||
cv::cuda::GpuMat u = flow[0];
|
||||
cv::cuda::GpuMat v = flow[1];
|
||||
|
||||
CUDA_SANITY_CHECK(u, 1e-10);
|
||||
CUDA_SANITY_CHECK(v, 1e-10);
|
||||
|
||||
d_nvof->collectGarbage();
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,59 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#ifndef __OPENCV_PERF_PRECOMP_HPP__
|
||||
#define __OPENCV_PERF_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/ts/cuda_perf.hpp"
|
||||
|
||||
#include "opencv2/cudaoptflow.hpp"
|
||||
#include "opencv2/cudaarithm.hpp"
|
||||
#include "opencv2/cudafeatures2d.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
using namespace perf;
|
||||
using namespace testing;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,337 @@
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/cudaoptflow.hpp"
|
||||
#include "opencv2/cudaarithm.hpp"
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
//this function is taken from opencv/samples/gpu/optical_flow.cpp
|
||||
inline bool isFlowCorrect(Point2f u)
|
||||
{
|
||||
return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9;
|
||||
}
|
||||
|
||||
//this function is taken from opencv/samples/gpu/optical_flow.cpp
|
||||
static Vec3b computeColor(float fx, float fy)
|
||||
{
|
||||
static bool first = true;
|
||||
|
||||
// relative lengths of color transitions:
|
||||
// these are chosen based on perceptual similarity
|
||||
// (e.g. one can distinguish more shades between red and yellow
|
||||
// than between yellow and green)
|
||||
const int RY = 15;
|
||||
const int YG = 6;
|
||||
const int GC = 4;
|
||||
const int CB = 11;
|
||||
const int BM = 13;
|
||||
const int MR = 6;
|
||||
const int NCOLS = RY + YG + GC + CB + BM + MR;
|
||||
static Vec3i colorWheel[NCOLS];
|
||||
|
||||
if (first)
|
||||
{
|
||||
int k = 0;
|
||||
|
||||
for (int i = 0; i < RY; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255, 255 * i / RY, 0);
|
||||
|
||||
for (int i = 0; i < YG; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);
|
||||
|
||||
for (int i = 0; i < GC; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(0, 255, 255 * i / GC);
|
||||
|
||||
for (int i = 0; i < CB; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);
|
||||
|
||||
for (int i = 0; i < BM; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255 * i / BM, 0, 255);
|
||||
|
||||
for (int i = 0; i < MR; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
const float rad = sqrt(fx * fx + fy * fy);
|
||||
const float a = atan2(-fy, -fx) / (float)CV_PI;
|
||||
|
||||
const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
|
||||
const int k0 = static_cast<int>(fk);
|
||||
const int k1 = (k0 + 1) % NCOLS;
|
||||
const float f = fk - k0;
|
||||
|
||||
Vec3b pix;
|
||||
|
||||
for (int b = 0; b < 3; b++)
|
||||
{
|
||||
const float col0 = colorWheel[k0][b] / 255.0f;
|
||||
const float col1 = colorWheel[k1][b] / 255.0f;
|
||||
|
||||
float col = (1 - f) * col0 + f * col1;
|
||||
|
||||
if (rad <= 1)
|
||||
col = 1 - rad * (1 - col); // increase saturation with radius
|
||||
else
|
||||
col *= .75; // out of range
|
||||
|
||||
pix[2 - b] = static_cast<uchar>(255.0 * col);
|
||||
}
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
||||
//this function is taken from opencv/samples/gpu/optical_flow.cpp
|
||||
static void drawOpticalFlow(const Mat_<float>& flowx, const Mat_<float>& flowy
|
||||
, Mat& dst, float maxmotion = -1)
|
||||
{
|
||||
dst.create(flowx.size(), CV_8UC3);
|
||||
dst.setTo(Scalar::all(0));
|
||||
|
||||
// determine motion range:
|
||||
float maxrad = maxmotion;
|
||||
|
||||
if (maxmotion <= 0)
|
||||
{
|
||||
maxrad = 1;
|
||||
for (int y = 0; y < flowx.rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < flowx.cols; ++x)
|
||||
{
|
||||
Point2f u(flowx(y, x), flowy(y, x));
|
||||
|
||||
if (!isFlowCorrect(u))
|
||||
continue;
|
||||
|
||||
maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < flowx.rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < flowx.cols; ++x)
|
||||
{
|
||||
Point2f u(flowx(y, x), flowy(y, x));
|
||||
|
||||
if (isFlowCorrect(u))
|
||||
dst.at<Vec3b>(y, x) = computeColor(u.x / maxrad, u.y / maxrad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ROI config file format.
|
||||
numrois 3
|
||||
roi0 640 96 1152 192
|
||||
roi1 640 64 896 864
|
||||
roi2 640 960 256 32
|
||||
*/
|
||||
bool parseROI(std::string ROIFileName, std::vector<Rect>& roiData)
|
||||
{
|
||||
std::string str;
|
||||
uint32_t nRois = 0;
|
||||
std::ifstream hRoiFile;
|
||||
hRoiFile.open(ROIFileName, std::ios::in);
|
||||
|
||||
if (hRoiFile.is_open())
|
||||
{
|
||||
while (std::getline(hRoiFile, str))
|
||||
{
|
||||
std::istringstream iss(str);
|
||||
std::vector<std::string> tokens{ std::istream_iterator<std::string>{iss},
|
||||
std::istream_iterator<std::string>{} };
|
||||
|
||||
if (tokens.size() == 0) continue; // if empty line, coninue
|
||||
|
||||
transform(tokens[0].begin(), tokens[0].end(), tokens[0].begin(), ::tolower);
|
||||
if (tokens[0] == "numrois")
|
||||
{
|
||||
nRois = atoi(tokens[1].data());
|
||||
}
|
||||
else if (tokens[0].rfind("roi", 0) == 0)
|
||||
{
|
||||
cv::Rect roi;
|
||||
roi.x = atoi(tokens[1].data());
|
||||
roi.y = atoi(tokens[2].data());
|
||||
roi.width = atoi(tokens[3].data());
|
||||
roi.height = atoi(tokens[4].data());
|
||||
roiData.push_back(roi);
|
||||
}
|
||||
else if (tokens[0].rfind("#", 0) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unidentified keyword in roi config file " << tokens[0] << std::endl;
|
||||
hRoiFile.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unable to open ROI file " << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (nRois != roiData.size())
|
||||
{
|
||||
std::cout << "NumRois(" << nRois << ")and specified roi rects (" << roiData.size() << ")are not matching " << std::endl;
|
||||
hRoiFile.close();
|
||||
return false;
|
||||
}
|
||||
hRoiFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::unordered_map<std::string, NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL> presetMap = {
|
||||
{ "slow", NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_SLOW },
|
||||
{ "medium", NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_MEDIUM },
|
||||
{ "fast", NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_FAST } };
|
||||
|
||||
std::unordered_map<int, NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE> outputGridSize = {
|
||||
{ 1, NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE::NV_OF_OUTPUT_VECTOR_GRID_SIZE_1 },
|
||||
{ 2, NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE::NV_OF_OUTPUT_VECTOR_GRID_SIZE_2 },
|
||||
{ 4, NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE::NV_OF_OUTPUT_VECTOR_GRID_SIZE_4 } };
|
||||
|
||||
std::unordered_map<int, NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE> hintGridSize = {
|
||||
{ 1, NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE::NV_OF_HINT_VECTOR_GRID_SIZE_1 },
|
||||
{ 2, NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE::NV_OF_HINT_VECTOR_GRID_SIZE_2 },
|
||||
{ 4, NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE::NV_OF_HINT_VECTOR_GRID_SIZE_4 },
|
||||
{ 8, NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE::NV_OF_HINT_VECTOR_GRID_SIZE_8 } };
|
||||
|
||||
try
|
||||
{
|
||||
CommandLineParser cmd(argc, argv,
|
||||
"{ l left | ../data/basketball1.png | specify left image }"
|
||||
"{ r right | ../data/basketball2.png | specify right image }"
|
||||
"{ g gpuid | 0 | cuda device index}"
|
||||
"{ p preset | slow | perf preset for OF algo [ options : slow, medium, fast ]}"
|
||||
"{ og outputGridSize | 1 | Output grid size of OF vector [ options : 1, 2, 4 ]}"
|
||||
"{ hg hintGridSize | 1 | Hint grid size of OF vector [ options : 1, 2, 4, 8 ]}"
|
||||
"{ o output | OpenCVNvOF.flo | output flow vector file in middlebury format}"
|
||||
"{ rc roiConfigFile | | Region of Interest config file }"
|
||||
"{ th enableTemporalHints | false | Enable temporal hints}"
|
||||
"{ eh enableExternalHints | false | Enable external hints}"
|
||||
"{ cb enableCostBuffer | false | Enable output cost buffer}"
|
||||
"{ h help | | print help message }");
|
||||
|
||||
cmd.about("Nvidia's optical flow sample.");
|
||||
if (cmd.has("help") || !cmd.check())
|
||||
{
|
||||
cmd.printMessage();
|
||||
cmd.printErrors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string pathL = cmd.get<std::string>("left");
|
||||
std::string pathR = cmd.get<std::string>("right");
|
||||
std::string preset = cmd.get<std::string>("preset");
|
||||
std::string output = cmd.get<std::string>("output");
|
||||
std::string roiConfiFile = cmd.get<std::string>("roiConfigFile");
|
||||
bool enableExternalHints = cmd.get<bool>("enableExternalHints");
|
||||
bool enableTemporalHints = cmd.get<bool>("enableTemporalHints");
|
||||
bool enableCostBuffer = cmd.get<bool>("enableCostBuffer");
|
||||
int gpuId = cmd.get<int>("gpuid");
|
||||
int outputBufferGridSize = cmd.get<int>("outputGridSize");
|
||||
int hintBufferGridSize = cmd.get<int>("hintGridSize");
|
||||
|
||||
if (pathL.empty()) std::cout << "Specify left image path" << std::endl;
|
||||
if (pathR.empty()) std::cout << "Specify right image path" << std::endl;
|
||||
if (preset.empty()) std::cout << "Specify perf preset for OpticalFlow algo" << std::endl;
|
||||
if (pathL.empty() || pathR.empty()) return 0;
|
||||
|
||||
auto p = presetMap.find(preset);
|
||||
if (p == presetMap.end())
|
||||
{
|
||||
std::cout << "Invalid preset level : " << preset << std::endl;
|
||||
return 0;
|
||||
}
|
||||
NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL perfPreset = p->second;
|
||||
|
||||
auto o = outputGridSize.find(outputBufferGridSize);
|
||||
if (o == outputGridSize.end())
|
||||
{
|
||||
std::cout << "Invalid output grid size: " << outputBufferGridSize << std::endl;
|
||||
return 0;
|
||||
}
|
||||
NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE outBufGridSize = o->second;
|
||||
|
||||
NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE hintBufGridSize =
|
||||
NvidiaOpticalFlow_2_0::NV_OF_HINT_VECTOR_GRID_SIZE_UNDEFINED;
|
||||
if (enableExternalHints)
|
||||
{
|
||||
auto h = hintGridSize.find(hintBufferGridSize);
|
||||
if (h == hintGridSize.end())
|
||||
{
|
||||
std::cout << "Invalid hint grid size: " << hintBufferGridSize << std::endl;
|
||||
return 0;
|
||||
}
|
||||
hintBufGridSize = h->second;
|
||||
}
|
||||
|
||||
std::vector<Rect> roiData;
|
||||
|
||||
if (!roiConfiFile.empty())
|
||||
{
|
||||
if (!parseROI(roiConfiFile, roiData))
|
||||
{
|
||||
std::cout << "Wrong Region of Interest config file, proceeding without ROI" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Mat frameL = imread(pathL, IMREAD_GRAYSCALE);
|
||||
Mat frameR = imread(pathR, IMREAD_GRAYSCALE);
|
||||
if (frameL.empty()) std::cout << "Can't open '" << pathL << "'" << std::endl;
|
||||
if (frameR.empty()) std::cout << "Can't open '" << pathR << "'" << std::endl;
|
||||
if (frameL.empty() || frameR.empty()) return -1;
|
||||
|
||||
Ptr<NvidiaOpticalFlow_2_0> nvof = NvidiaOpticalFlow_2_0::create(
|
||||
frameL.size(), roiData, perfPreset, outBufGridSize, hintBufGridSize,
|
||||
enableTemporalHints, enableExternalHints, enableCostBuffer, gpuId);
|
||||
|
||||
Mat flowx, flowy, flowxy, floatFlow, image;
|
||||
|
||||
nvof->calc(frameL, frameR, flowxy);
|
||||
|
||||
nvof->convertToFloat(flowxy, floatFlow);
|
||||
|
||||
if (!output.empty())
|
||||
{
|
||||
if (!writeOpticalFlow(output, floatFlow))
|
||||
std::cout << "Failed to save Flow Vector" << std::endl;
|
||||
else
|
||||
std::cout << "Flow vector saved as '" << output << "'" << std::endl;
|
||||
}
|
||||
|
||||
Mat planes[] = { flowx, flowy };
|
||||
split(floatFlow, planes);
|
||||
flowx = planes[0]; flowy = planes[1];
|
||||
|
||||
drawOpticalFlow(flowx, flowy, image, 10);
|
||||
|
||||
imshow("Colorize image", image);
|
||||
waitKey(0);
|
||||
nvof->collectGarbage();
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::cout << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/cudaoptflow.hpp"
|
||||
#include "opencv2/cudaarithm.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
inline bool isFlowCorrect(Point2f u)
|
||||
{
|
||||
return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9;
|
||||
}
|
||||
|
||||
static Vec3b computeColor(float fx, float fy)
|
||||
{
|
||||
static bool first = true;
|
||||
|
||||
// relative lengths of color transitions:
|
||||
// these are chosen based on perceptual similarity
|
||||
// (e.g. one can distinguish more shades between red and yellow
|
||||
// than between yellow and green)
|
||||
const int RY = 15;
|
||||
const int YG = 6;
|
||||
const int GC = 4;
|
||||
const int CB = 11;
|
||||
const int BM = 13;
|
||||
const int MR = 6;
|
||||
const int NCOLS = RY + YG + GC + CB + BM + MR;
|
||||
static Vec3i colorWheel[NCOLS];
|
||||
|
||||
if (first)
|
||||
{
|
||||
int k = 0;
|
||||
|
||||
for (int i = 0; i < RY; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255, 255 * i / RY, 0);
|
||||
|
||||
for (int i = 0; i < YG; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);
|
||||
|
||||
for (int i = 0; i < GC; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(0, 255, 255 * i / GC);
|
||||
|
||||
for (int i = 0; i < CB; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);
|
||||
|
||||
for (int i = 0; i < BM; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255 * i / BM, 0, 255);
|
||||
|
||||
for (int i = 0; i < MR; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
const float rad = sqrt(fx * fx + fy * fy);
|
||||
const float a = atan2(-fy, -fx) / (float)CV_PI;
|
||||
|
||||
const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
|
||||
const int k0 = static_cast<int>(fk);
|
||||
const int k1 = (k0 + 1) % NCOLS;
|
||||
const float f = fk - k0;
|
||||
|
||||
Vec3b pix;
|
||||
|
||||
for (int b = 0; b < 3; b++)
|
||||
{
|
||||
const float col0 = colorWheel[k0][b] / 255.0f;
|
||||
const float col1 = colorWheel[k1][b] / 255.0f;
|
||||
|
||||
float col = (1 - f) * col0 + f * col1;
|
||||
|
||||
if (rad <= 1)
|
||||
col = 1 - rad * (1 - col); // increase saturation with radius
|
||||
else
|
||||
col *= .75; // out of range
|
||||
|
||||
pix[2 - b] = static_cast<uchar>(255.0 * col);
|
||||
}
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
||||
static void drawOpticalFlow(const Mat_<float>& flowx, const Mat_<float>& flowy, Mat& dst, float maxmotion = -1)
|
||||
{
|
||||
dst.create(flowx.size(), CV_8UC3);
|
||||
dst.setTo(Scalar::all(0));
|
||||
|
||||
// determine motion range:
|
||||
float maxrad = maxmotion;
|
||||
|
||||
if (maxmotion <= 0)
|
||||
{
|
||||
maxrad = 1;
|
||||
for (int y = 0; y < flowx.rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < flowx.cols; ++x)
|
||||
{
|
||||
Point2f u(flowx(y, x), flowy(y, x));
|
||||
|
||||
if (!isFlowCorrect(u))
|
||||
continue;
|
||||
|
||||
maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < flowx.rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < flowx.cols; ++x)
|
||||
{
|
||||
Point2f u(flowx(y, x), flowy(y, x));
|
||||
|
||||
if (isFlowCorrect(u))
|
||||
dst.at<Vec3b>(y, x) = computeColor(u.x / maxrad, u.y / maxrad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void showFlow(const char* name, const GpuMat& d_flow)
|
||||
{
|
||||
GpuMat planes[2];
|
||||
cuda::split(d_flow, planes);
|
||||
|
||||
Mat flowx(planes[0]);
|
||||
Mat flowy(planes[1]);
|
||||
|
||||
Mat out;
|
||||
drawOpticalFlow(flowx, flowy, out, 10);
|
||||
|
||||
imshow(name, out);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
string filename1, filename2;
|
||||
if (argc < 3)
|
||||
{
|
||||
cerr << "Usage : " << argv[0] << " <frame0> <frame1>" << endl;
|
||||
filename1 = "../data/basketball1.png";
|
||||
filename2 = "../data/basketball2.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
filename1 = argv[1];
|
||||
filename2 = argv[2];
|
||||
}
|
||||
|
||||
Mat frame0 = imread(filename1, IMREAD_GRAYSCALE);
|
||||
Mat frame1 = imread(filename2, IMREAD_GRAYSCALE);
|
||||
|
||||
if (frame0.empty())
|
||||
{
|
||||
cerr << "Can't open image [" << filename1 << "]" << endl;
|
||||
return -1;
|
||||
}
|
||||
if (frame1.empty())
|
||||
{
|
||||
cerr << "Can't open image [" << filename2 << "]" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frame1.size() != frame0.size())
|
||||
{
|
||||
cerr << "Images should be of equal sizes" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
GpuMat d_frame0(frame0);
|
||||
GpuMat d_frame1(frame1);
|
||||
|
||||
GpuMat d_flow(frame0.size(), CV_32FC2), d_flowxy;
|
||||
|
||||
Stream inputStream, outputStream;
|
||||
|
||||
Ptr<cuda::BroxOpticalFlow> brox = cuda::BroxOpticalFlow::create(0.197f, 50.0f, 0.8f, 10, 77, 10);
|
||||
Ptr<cuda::DensePyrLKOpticalFlow> lk = cuda::DensePyrLKOpticalFlow::create(Size(7, 7));
|
||||
Ptr<cuda::FarnebackOpticalFlow> farn = cuda::FarnebackOpticalFlow::create();
|
||||
Ptr<cuda::OpticalFlowDual_TVL1> tvl1 = cuda::OpticalFlowDual_TVL1::create();
|
||||
Ptr<cuda::NvidiaOpticalFlow_1_0> nvof_1_0 = cuda::NvidiaOpticalFlow_1_0::create(frame0.size(),
|
||||
NvidiaOpticalFlow_1_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_FAST, false, false, false, 0, inputStream, outputStream);
|
||||
Ptr<cuda::NvidiaOpticalFlow_2_0> nvof_2_0 = cuda::NvidiaOpticalFlow_2_0::create(frame0.size(),
|
||||
NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_FAST, NvidiaOpticalFlow_2_0::NVIDIA_OF_OUTPUT_VECTOR_GRID_SIZE::NV_OF_OUTPUT_VECTOR_GRID_SIZE_1,
|
||||
NvidiaOpticalFlow_2_0::NVIDIA_OF_HINT_VECTOR_GRID_SIZE::NV_OF_HINT_VECTOR_GRID_SIZE_UNDEFINED, false, false, false, 0, inputStream, outputStream);
|
||||
|
||||
{
|
||||
GpuMat d_frame0f;
|
||||
GpuMat d_frame1f;
|
||||
|
||||
d_frame0.convertTo(d_frame0f, CV_32F, 1.0 / 255.0);
|
||||
d_frame1.convertTo(d_frame1f, CV_32F, 1.0 / 255.0);
|
||||
|
||||
const int64 start = getTickCount();
|
||||
|
||||
brox->calc(d_frame0f, d_frame1f, d_flow);
|
||||
|
||||
const double timeSec = (getTickCount() - start) / getTickFrequency();
|
||||
cout << "Brox : " << timeSec << " sec" << endl;
|
||||
|
||||
showFlow("Brox", d_flow);
|
||||
}
|
||||
|
||||
{
|
||||
const int64 start = getTickCount();
|
||||
|
||||
lk->calc(d_frame0, d_frame1, d_flow);
|
||||
|
||||
const double timeSec = (getTickCount() - start) / getTickFrequency();
|
||||
cout << "LK : " << timeSec << " sec" << endl;
|
||||
|
||||
showFlow("LK", d_flow);
|
||||
}
|
||||
|
||||
{
|
||||
const int64 start = getTickCount();
|
||||
|
||||
farn->calc(d_frame0, d_frame1, d_flow);
|
||||
|
||||
const double timeSec = (getTickCount() - start) / getTickFrequency();
|
||||
cout << "Farn : " << timeSec << " sec" << endl;
|
||||
|
||||
showFlow("Farn", d_flow);
|
||||
}
|
||||
|
||||
{
|
||||
const int64 start = getTickCount();
|
||||
|
||||
tvl1->calc(d_frame0, d_frame1, d_flow);
|
||||
|
||||
const double timeSec = (getTickCount() - start) / getTickFrequency();
|
||||
cout << "TVL1 : " << timeSec << " sec" << endl;
|
||||
|
||||
showFlow("TVL1", d_flow);
|
||||
}
|
||||
|
||||
{
|
||||
//The timing displayed below includes the time taken to copy the input buffers to the OF CUDA input buffers
|
||||
//and to copy the output buffers from the OF CUDA output buffer to the output buffer.
|
||||
//Hence it is expected to be more than what is displayed in the NVIDIA Optical Flow SDK documentation.
|
||||
const int64 start = getTickCount();
|
||||
|
||||
nvof_1_0->calc(d_frame0, d_frame1, d_flowxy);
|
||||
|
||||
const double timeSec = (getTickCount() - start) / getTickFrequency();
|
||||
cout << "NVIDIAOpticalFlow_1_0 : " << timeSec << " sec" << endl;
|
||||
|
||||
nvof_1_0->upSampler(d_flowxy, frame0.size(), nvof_1_0->getGridSize(), d_flow);
|
||||
|
||||
showFlow("NVIDIAOpticalFlow_1_0", d_flow);
|
||||
nvof_1_0->collectGarbage();
|
||||
}
|
||||
|
||||
{
|
||||
//The timing displayed below includes the time taken to copy the input buffers to the OF CUDA input buffers
|
||||
//and to copy the output buffers from the OF CUDA output buffer to the output buffer.
|
||||
//Hence it is expected to be more than what is displayed in the NVIDIA Optical Flow SDK documentation.
|
||||
const int64 start = getTickCount();
|
||||
|
||||
nvof_2_0->calc(d_frame0, d_frame1, d_flowxy);
|
||||
|
||||
const double timeSec = (getTickCount() - start) / getTickFrequency();
|
||||
cout << "NVIDIAOpticalFlow_2_0 : " << timeSec << " sec" << endl;
|
||||
|
||||
nvof_2_0->convertToFloat(d_flowxy, d_flow);
|
||||
|
||||
showFlow("NVIDIAOpticalFlow_2_0", d_flow);
|
||||
nvof_2_0->collectGarbage();
|
||||
}
|
||||
|
||||
imshow("Frame 0", frame0);
|
||||
imshow("Frame 1", frame1);
|
||||
waitKey();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
#if !defined (HAVE_CUDA) || !defined (HAVE_OPENCV_CUDALEGACY) || defined (CUDA_DISABLER)
|
||||
|
||||
Ptr<BroxOpticalFlow> cv::cuda::BroxOpticalFlow::create(double, double, double, int, int, int) { throw_no_cuda(); return Ptr<BroxOpticalFlow>(); }
|
||||
|
||||
#else
|
||||
|
||||
namespace {
|
||||
|
||||
class BroxOpticalFlowImpl : public BroxOpticalFlow
|
||||
{
|
||||
public:
|
||||
BroxOpticalFlowImpl(double alpha, double gamma, double scale_factor,
|
||||
int inner_iterations, int outer_iterations, int solver_iterations) :
|
||||
alpha_(alpha), gamma_(gamma), scale_factor_(scale_factor),
|
||||
inner_iterations_(inner_iterations), outer_iterations_(outer_iterations),
|
||||
solver_iterations_(solver_iterations)
|
||||
{
|
||||
}
|
||||
|
||||
virtual String getDefaultName() const { return "DenseOpticalFlow.BroxOpticalFlow"; }
|
||||
|
||||
virtual void calc(InputArray I0, InputArray I1, InputOutputArray flow, Stream& stream);
|
||||
|
||||
virtual double getFlowSmoothness() const { return alpha_; }
|
||||
virtual void setFlowSmoothness(double alpha) { alpha_ = static_cast<float>(alpha); }
|
||||
|
||||
virtual double getGradientConstancyImportance() const { return gamma_; }
|
||||
virtual void setGradientConstancyImportance(double gamma) { gamma_ = static_cast<float>(gamma); }
|
||||
|
||||
virtual double getPyramidScaleFactor() const { return scale_factor_; }
|
||||
virtual void setPyramidScaleFactor(double scale_factor) { scale_factor_ = static_cast<float>(scale_factor); }
|
||||
|
||||
//! number of lagged non-linearity iterations (inner loop)
|
||||
virtual int getInnerIterations() const { return inner_iterations_; }
|
||||
virtual void setInnerIterations(int inner_iterations) { inner_iterations_ = inner_iterations; }
|
||||
|
||||
//! number of warping iterations (number of pyramid levels)
|
||||
virtual int getOuterIterations() const { return outer_iterations_; }
|
||||
virtual void setOuterIterations(int outer_iterations) { outer_iterations_ = outer_iterations; }
|
||||
|
||||
//! number of linear system solver iterations
|
||||
virtual int getSolverIterations() const { return solver_iterations_; }
|
||||
virtual void setSolverIterations(int solver_iterations) { solver_iterations_ = solver_iterations; }
|
||||
|
||||
private:
|
||||
//! flow smoothness
|
||||
float alpha_;
|
||||
|
||||
//! gradient constancy importance
|
||||
float gamma_;
|
||||
|
||||
//! pyramid scale factor
|
||||
float scale_factor_;
|
||||
|
||||
//! number of lagged non-linearity iterations (inner loop)
|
||||
int inner_iterations_;
|
||||
|
||||
//! number of warping iterations (number of pyramid levels)
|
||||
int outer_iterations_;
|
||||
|
||||
//! number of linear system solver iterations
|
||||
int solver_iterations_;
|
||||
};
|
||||
|
||||
static size_t getBufSize(const NCVBroxOpticalFlowDescriptor& desc,
|
||||
const NCVMatrix<Ncv32f>& frame0, const NCVMatrix<Ncv32f>& frame1,
|
||||
NCVMatrix<Ncv32f>& u, NCVMatrix<Ncv32f>& v,
|
||||
size_t textureAlignment)
|
||||
{
|
||||
NCVMemStackAllocator gpuCounter(static_cast<Ncv32u>(textureAlignment));
|
||||
|
||||
ncvSafeCall( NCVBroxOpticalFlow(desc, gpuCounter, frame0, frame1, u, v, 0) );
|
||||
|
||||
return gpuCounter.maxSize();
|
||||
}
|
||||
|
||||
static void outputHandler(const String &msg)
|
||||
{
|
||||
CV_Error(cv::Error::GpuApiCallError, msg.c_str());
|
||||
}
|
||||
|
||||
void BroxOpticalFlowImpl::calc(InputArray _I0, InputArray _I1, InputOutputArray _flow, Stream& stream)
|
||||
{
|
||||
const GpuMat frame0 = _I0.getGpuMat();
|
||||
const GpuMat frame1 = _I1.getGpuMat();
|
||||
|
||||
CV_Assert( frame0.type() == CV_32FC1 );
|
||||
CV_Assert( frame1.size() == frame0.size() && frame1.type() == frame0.type() );
|
||||
|
||||
ncvSetDebugOutputHandler(outputHandler);
|
||||
|
||||
BufferPool pool(stream);
|
||||
GpuMat u = pool.getBuffer(frame0.size(), CV_32FC1);
|
||||
GpuMat v = pool.getBuffer(frame0.size(), CV_32FC1);
|
||||
|
||||
NCVBroxOpticalFlowDescriptor desc;
|
||||
desc.alpha = alpha_;
|
||||
desc.gamma = gamma_;
|
||||
desc.scale_factor = scale_factor_;
|
||||
desc.number_of_inner_iterations = inner_iterations_;
|
||||
desc.number_of_outer_iterations = outer_iterations_;
|
||||
desc.number_of_solver_iterations = solver_iterations_;
|
||||
|
||||
NCVMemSegment frame0MemSeg;
|
||||
frame0MemSeg.begin.memtype = NCVMemoryTypeDevice;
|
||||
frame0MemSeg.begin.ptr = const_cast<uchar*>(frame0.data);
|
||||
frame0MemSeg.size = frame0.step * frame0.rows;
|
||||
|
||||
NCVMemSegment frame1MemSeg;
|
||||
frame1MemSeg.begin.memtype = NCVMemoryTypeDevice;
|
||||
frame1MemSeg.begin.ptr = const_cast<uchar*>(frame1.data);
|
||||
frame1MemSeg.size = frame1.step * frame1.rows;
|
||||
|
||||
NCVMemSegment uMemSeg;
|
||||
uMemSeg.begin.memtype = NCVMemoryTypeDevice;
|
||||
uMemSeg.begin.ptr = u.ptr();
|
||||
uMemSeg.size = u.step * u.rows;
|
||||
|
||||
NCVMemSegment vMemSeg;
|
||||
vMemSeg.begin.memtype = NCVMemoryTypeDevice;
|
||||
vMemSeg.begin.ptr = v.ptr();
|
||||
vMemSeg.size = v.step * v.rows;
|
||||
|
||||
DeviceInfo devInfo;
|
||||
size_t textureAlignment = devInfo.textureAlignment();
|
||||
|
||||
NCVMatrixReuse<Ncv32f> frame0Mat(frame0MemSeg, static_cast<Ncv32u>(textureAlignment), frame0.cols, frame0.rows, static_cast<Ncv32u>(frame0.step));
|
||||
NCVMatrixReuse<Ncv32f> frame1Mat(frame1MemSeg, static_cast<Ncv32u>(textureAlignment), frame1.cols, frame1.rows, static_cast<Ncv32u>(frame1.step));
|
||||
NCVMatrixReuse<Ncv32f> uMat(uMemSeg, static_cast<Ncv32u>(textureAlignment), u.cols, u.rows, static_cast<Ncv32u>(u.step));
|
||||
NCVMatrixReuse<Ncv32f> vMat(vMemSeg, static_cast<Ncv32u>(textureAlignment), v.cols, v.rows, static_cast<Ncv32u>(v.step));
|
||||
|
||||
size_t bufSize = getBufSize(desc, frame0Mat, frame1Mat, uMat, vMat, textureAlignment);
|
||||
GpuMat buf = pool.getBuffer(1, static_cast<int>(bufSize), CV_8UC1);
|
||||
|
||||
NCVMemStackAllocator gpuAllocator(NCVMemoryTypeDevice, bufSize, static_cast<Ncv32u>(textureAlignment), buf.ptr());
|
||||
|
||||
ncvSafeCall( NCVBroxOpticalFlow(desc, gpuAllocator, frame0Mat, frame1Mat, uMat, vMat, StreamAccessor::getStream(stream)) );
|
||||
|
||||
GpuMat flows[] = {u, v};
|
||||
cuda::merge(flows, 2, _flow, stream);
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<BroxOpticalFlow> cv::cuda::BroxOpticalFlow::create(double alpha, double gamma, double scale_factor, int inner_iterations, int outer_iterations, int solver_iterations)
|
||||
{
|
||||
return makePtr<BroxOpticalFlowImpl>(alpha, gamma, scale_factor, inner_iterations, outer_iterations, solver_iterations);
|
||||
}
|
||||
|
||||
#endif /* HAVE_CUDA */
|
||||
@@ -0,0 +1,656 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#if !defined CUDA_DISABLER
|
||||
|
||||
#include "opencv2/core/cuda/common.hpp"
|
||||
#include "opencv2/core/cuda/border_interpolate.hpp"
|
||||
|
||||
#define tx threadIdx.x
|
||||
#define ty threadIdx.y
|
||||
#define bx blockIdx.x
|
||||
#define by blockIdx.y
|
||||
#define bdx blockDim.x
|
||||
#define bdy blockDim.y
|
||||
|
||||
#define BORDER_SIZE 5
|
||||
#define MAX_KSIZE_HALF 100
|
||||
|
||||
namespace cv { namespace cuda { namespace device { namespace optflow_farneback
|
||||
{
|
||||
__constant__ float c_g[8];
|
||||
__constant__ float c_xg[8];
|
||||
__constant__ float c_xxg[8];
|
||||
__constant__ float c_ig11, c_ig03, c_ig33, c_ig55;
|
||||
|
||||
|
||||
template <int polyN>
|
||||
__global__ void polynomialExpansion(
|
||||
const int height, const int width, const PtrStepf src, PtrStepf dst)
|
||||
{
|
||||
const int y = by * bdy + ty;
|
||||
const int x = bx * (bdx - 2*polyN) + tx - polyN;
|
||||
|
||||
if (y < height)
|
||||
{
|
||||
extern __shared__ float smem[];
|
||||
volatile float *row = smem + tx;
|
||||
int xWarped = ::min(::max(x, 0), width - 1);
|
||||
|
||||
row[0] = src(y, xWarped) * c_g[0];
|
||||
row[bdx] = 0.f;
|
||||
row[2*bdx] = 0.f;
|
||||
|
||||
for (int k = 1; k <= polyN; ++k)
|
||||
{
|
||||
float t0 = src(::max(y - k, 0), xWarped);
|
||||
float t1 = src(::min(y + k, height - 1), xWarped);
|
||||
|
||||
row[0] += c_g[k] * (t0 + t1);
|
||||
row[bdx] += c_xg[k] * (t1 - t0);
|
||||
row[2*bdx] += c_xxg[k] * (t0 + t1);
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
if (tx >= polyN && tx + polyN < bdx && x < width)
|
||||
{
|
||||
float b1 = c_g[0] * row[0];
|
||||
float b3 = c_g[0] * row[bdx];
|
||||
float b5 = c_g[0] * row[2*bdx];
|
||||
float b2 = 0, b4 = 0, b6 = 0;
|
||||
|
||||
for (int k = 1; k <= polyN; ++k)
|
||||
{
|
||||
b1 += (row[k] + row[-k]) * c_g[k];
|
||||
b4 += (row[k] + row[-k]) * c_xxg[k];
|
||||
b2 += (row[k] - row[-k]) * c_xg[k];
|
||||
b3 += (row[k + bdx] + row[-k + bdx]) * c_g[k];
|
||||
b6 += (row[k + bdx] - row[-k + bdx]) * c_xg[k];
|
||||
b5 += (row[k + 2*bdx] + row[-k + 2*bdx]) * c_g[k];
|
||||
}
|
||||
|
||||
dst(y, xWarped) = b3*c_ig11;
|
||||
dst(height + y, xWarped) = b2*c_ig11;
|
||||
dst(2*height + y, xWarped) = b1*c_ig03 + b5*c_ig33;
|
||||
dst(3*height + y, xWarped) = b1*c_ig03 + b4*c_ig33;
|
||||
dst(4*height + y, xWarped) = b6*c_ig55;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setPolynomialExpansionConsts(
|
||||
int polyN, const float *g, const float *xg, const float *xxg,
|
||||
float ig11, float ig03, float ig33, float ig55)
|
||||
{
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_g, g, (polyN + 1) * sizeof(*g)));
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_xg, xg, (polyN + 1) * sizeof(*xg)));
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_xxg, xxg, (polyN + 1) * sizeof(*xxg)));
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_ig11, &ig11, sizeof(ig11)));
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_ig03, &ig03, sizeof(ig03)));
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_ig33, &ig33, sizeof(ig33)));
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_ig55, &ig55, sizeof(ig55)));
|
||||
}
|
||||
|
||||
|
||||
void polynomialExpansionGpu(const PtrStepSzf &src, int polyN, PtrStepSzf dst, cudaStream_t stream)
|
||||
{
|
||||
dim3 block(256);
|
||||
dim3 grid(divUp(src.cols, block.x - 2*polyN), src.rows);
|
||||
int smem = 3 * block.x * sizeof(float);
|
||||
|
||||
if (polyN == 5)
|
||||
polynomialExpansion<5><<<grid, block, smem, stream>>>(src.rows, src.cols, src, dst);
|
||||
else if (polyN == 7)
|
||||
polynomialExpansion<7><<<grid, block, smem, stream>>>(src.rows, src.cols, src, dst);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
|
||||
__constant__ float c_border[BORDER_SIZE + 1];
|
||||
|
||||
__global__ void updateMatrices(
|
||||
const int height, const int width, const PtrStepf flowx, const PtrStepf flowy,
|
||||
const PtrStepf R0, const PtrStepf R1, PtrStepf M)
|
||||
{
|
||||
const int y = by * bdy + ty;
|
||||
const int x = bx * bdx + tx;
|
||||
|
||||
if (y < height && x < width)
|
||||
{
|
||||
float dx = flowx(y, x);
|
||||
float dy = flowy(y, x);
|
||||
float fx = x + dx;
|
||||
float fy = y + dy;
|
||||
|
||||
int x1 = floorf(fx);
|
||||
int y1 = floorf(fy);
|
||||
fx -= x1; fy -= y1;
|
||||
|
||||
float r2, r3, r4, r5, r6;
|
||||
|
||||
if (x1 >= 0 && y1 >= 0 && x1 < width - 1 && y1 < height - 1)
|
||||
{
|
||||
float a00 = (1.f - fx) * (1.f - fy);
|
||||
float a01 = fx * (1.f - fy);
|
||||
float a10 = (1.f - fx) * fy;
|
||||
float a11 = fx * fy;
|
||||
|
||||
r2 = a00 * R1(y1, x1) +
|
||||
a01 * R1(y1, x1 + 1) +
|
||||
a10 * R1(y1 + 1, x1) +
|
||||
a11 * R1(y1 + 1, x1 + 1);
|
||||
|
||||
r3 = a00 * R1(height + y1, x1) +
|
||||
a01 * R1(height + y1, x1 + 1) +
|
||||
a10 * R1(height + y1 + 1, x1) +
|
||||
a11 * R1(height + y1 + 1, x1 + 1);
|
||||
|
||||
r4 = a00 * R1(2*height + y1, x1) +
|
||||
a01 * R1(2*height + y1, x1 + 1) +
|
||||
a10 * R1(2*height + y1 + 1, x1) +
|
||||
a11 * R1(2*height + y1 + 1, x1 + 1);
|
||||
|
||||
r5 = a00 * R1(3*height + y1, x1) +
|
||||
a01 * R1(3*height + y1, x1 + 1) +
|
||||
a10 * R1(3*height + y1 + 1, x1) +
|
||||
a11 * R1(3*height + y1 + 1, x1 + 1);
|
||||
|
||||
r6 = a00 * R1(4*height + y1, x1) +
|
||||
a01 * R1(4*height + y1, x1 + 1) +
|
||||
a10 * R1(4*height + y1 + 1, x1) +
|
||||
a11 * R1(4*height + y1 + 1, x1 + 1);
|
||||
|
||||
r4 = (R0(2*height + y, x) + r4) * 0.5f;
|
||||
r5 = (R0(3*height + y, x) + r5) * 0.5f;
|
||||
r6 = (R0(4*height + y, x) + r6) * 0.25f;
|
||||
}
|
||||
else
|
||||
{
|
||||
r2 = r3 = 0.f;
|
||||
r4 = R0(2*height + y, x);
|
||||
r5 = R0(3*height + y, x);
|
||||
r6 = R0(4*height + y, x) * 0.5f;
|
||||
}
|
||||
|
||||
r2 = (R0(y, x) - r2) * 0.5f;
|
||||
r3 = (R0(height + y, x) - r3) * 0.5f;
|
||||
|
||||
r2 += r4*dy + r6*dx;
|
||||
r3 += r6*dy + r5*dx;
|
||||
|
||||
float scale =
|
||||
c_border[::min(x, BORDER_SIZE)] *
|
||||
c_border[::min(y, BORDER_SIZE)] *
|
||||
c_border[::min(width - x - 1, BORDER_SIZE)] *
|
||||
c_border[::min(height - y - 1, BORDER_SIZE)];
|
||||
|
||||
r2 *= scale; r3 *= scale; r4 *= scale;
|
||||
r5 *= scale; r6 *= scale;
|
||||
|
||||
M(y, x) = r4*r4 + r6*r6;
|
||||
M(height + y, x) = (r4 + r5)*r6;
|
||||
M(2*height + y, x) = r5*r5 + r6*r6;
|
||||
M(3*height + y, x) = r4*r2 + r6*r3;
|
||||
M(4*height + y, x) = r6*r2 + r5*r3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setUpdateMatricesConsts()
|
||||
{
|
||||
static const float border[BORDER_SIZE + 1] = {0.14f, 0.14f, 0.4472f, 0.4472f, 0.4472f, 1.f};
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_border, border, (BORDER_SIZE + 1) * sizeof(*border)));
|
||||
}
|
||||
|
||||
|
||||
void updateMatricesGpu(
|
||||
const PtrStepSzf flowx, const PtrStepSzf flowy, const PtrStepSzf R0, const PtrStepSzf R1,
|
||||
PtrStepSzf M, cudaStream_t stream)
|
||||
{
|
||||
dim3 block(32, 8);
|
||||
dim3 grid(divUp(flowx.cols, block.x), divUp(flowx.rows, block.y));
|
||||
|
||||
updateMatrices<<<grid, block, 0, stream>>>(flowx.rows, flowx.cols, flowx, flowy, R0, R1, M);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
|
||||
__global__ void updateFlow(
|
||||
const int height, const int width, const PtrStepf M, PtrStepf flowx, PtrStepf flowy)
|
||||
{
|
||||
const int y = by * bdy + ty;
|
||||
const int x = bx * bdx + tx;
|
||||
|
||||
if (y < height && x < width)
|
||||
{
|
||||
float g11 = M(y, x);
|
||||
float g12 = M(height + y, x);
|
||||
float g22 = M(2*height + y, x);
|
||||
float h1 = M(3*height + y, x);
|
||||
float h2 = M(4*height + y, x);
|
||||
|
||||
float detInv = 1.f / (g11*g22 - g12*g12 + 1e-3f);
|
||||
|
||||
flowx(y, x) = (g11*h2 - g12*h1) * detInv;
|
||||
flowy(y, x) = (g22*h1 - g12*h2) * detInv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void updateFlowGpu(const PtrStepSzf M, PtrStepSzf flowx, PtrStepSzf flowy, cudaStream_t stream)
|
||||
{
|
||||
dim3 block(32, 8);
|
||||
dim3 grid(divUp(flowx.cols, block.x), divUp(flowx.rows, block.y));
|
||||
|
||||
updateFlow<<<grid, block, 0, stream>>>(flowx.rows, flowx.cols, M, flowx, flowy);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
|
||||
/*__global__ void boxFilter(
|
||||
const int height, const int width, const PtrStepf src,
|
||||
const int ksizeHalf, const float boxAreaInv, PtrStepf dst)
|
||||
{
|
||||
const int y = by * bdy + ty;
|
||||
const int x = bx * bdx + tx;
|
||||
|
||||
extern __shared__ float smem[];
|
||||
volatile float *row = smem + ty * (bdx + 2*ksizeHalf);
|
||||
|
||||
if (y < height)
|
||||
{
|
||||
// Vertical pass
|
||||
for (int i = tx; i < bdx + 2*ksizeHalf; i += bdx)
|
||||
{
|
||||
int xExt = int(bx * bdx) + i - ksizeHalf;
|
||||
xExt = ::min(::max(xExt, 0), width - 1);
|
||||
|
||||
row[i] = src(y, xExt);
|
||||
for (int j = 1; j <= ksizeHalf; ++j)
|
||||
row[i] += src(::max(y - j, 0), xExt) + src(::min(y + j, height - 1), xExt);
|
||||
}
|
||||
|
||||
if (x < width)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Horizontal passs
|
||||
row += tx + ksizeHalf;
|
||||
float res = row[0];
|
||||
for (int i = 1; i <= ksizeHalf; ++i)
|
||||
res += row[-i] + row[i];
|
||||
dst(y, x) = res * boxAreaInv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void boxFilterGpu(const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, cudaStream_t stream)
|
||||
{
|
||||
dim3 block(256);
|
||||
dim3 grid(divUp(src.cols, block.x), divUp(src.rows, block.y));
|
||||
int smem = (block.x + 2*ksizeHalf) * block.y * sizeof(float);
|
||||
|
||||
float boxAreaInv = 1.f / ((1 + 2*ksizeHalf) * (1 + 2*ksizeHalf));
|
||||
boxFilter<<<grid, block, smem, stream>>>(src.rows, src.cols, src, ksizeHalf, boxAreaInv, dst);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}*/
|
||||
|
||||
|
||||
__global__ void boxFilter5(
|
||||
const int height, const int width, const PtrStepf src,
|
||||
const int ksizeHalf, const float boxAreaInv, PtrStepf dst)
|
||||
{
|
||||
const int y = by * bdy + ty;
|
||||
const int x = bx * bdx + tx;
|
||||
|
||||
extern __shared__ float smem[];
|
||||
|
||||
const int smw = bdx + 2*ksizeHalf; // shared memory "width"
|
||||
volatile float *row = smem + 5 * ty * smw;
|
||||
|
||||
if (y < height)
|
||||
{
|
||||
// Vertical pass
|
||||
for (int i = tx; i < bdx + 2*ksizeHalf; i += bdx)
|
||||
{
|
||||
int xExt = int(bx * bdx) + i - ksizeHalf;
|
||||
xExt = ::min(::max(xExt, 0), width - 1);
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] = src(k*height + y, xExt);
|
||||
|
||||
for (int j = 1; j <= ksizeHalf; ++j)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] +=
|
||||
src(k*height + ::max(y - j, 0), xExt) +
|
||||
src(k*height + ::min(y + j, height - 1), xExt);
|
||||
}
|
||||
|
||||
if (x < width)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Horizontal passs
|
||||
|
||||
row += tx + ksizeHalf;
|
||||
float res[5];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] = row[k*smw];
|
||||
|
||||
for (int i = 1; i <= ksizeHalf; ++i)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] += row[k*smw - i] + row[k*smw + i];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
dst(k*height + y, x) = res[k] * boxAreaInv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void boxFilter5Gpu(const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, cudaStream_t stream)
|
||||
{
|
||||
int height = src.rows / 5;
|
||||
int width = src.cols;
|
||||
|
||||
dim3 block(256);
|
||||
dim3 grid(divUp(width, block.x), divUp(height, block.y));
|
||||
int smem = (block.x + 2*ksizeHalf) * 5 * block.y * sizeof(float);
|
||||
|
||||
float boxAreaInv = 1.f / ((1 + 2*ksizeHalf) * (1 + 2*ksizeHalf));
|
||||
boxFilter5<<<grid, block, smem, stream>>>(height, width, src, ksizeHalf, boxAreaInv, dst);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
|
||||
void boxFilter5Gpu_CC11(const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, cudaStream_t stream)
|
||||
{
|
||||
int height = src.rows / 5;
|
||||
int width = src.cols;
|
||||
|
||||
dim3 block(128);
|
||||
dim3 grid(divUp(width, block.x), divUp(height, block.y));
|
||||
int smem = (block.x + 2*ksizeHalf) * 5 * block.y * sizeof(float);
|
||||
|
||||
float boxAreaInv = 1.f / ((1 + 2*ksizeHalf) * (1 + 2*ksizeHalf));
|
||||
boxFilter5<<<grid, block, smem, stream>>>(height, width, src, ksizeHalf, boxAreaInv, dst);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
|
||||
__constant__ float c_gKer[MAX_KSIZE_HALF + 1];
|
||||
|
||||
template <typename Border>
|
||||
__global__ void gaussianBlur(
|
||||
const int height, const int width, const PtrStepf src, const int ksizeHalf,
|
||||
const Border b, PtrStepf dst)
|
||||
{
|
||||
const int y = by * bdy + ty;
|
||||
const int x = bx * bdx + tx;
|
||||
|
||||
extern __shared__ float smem[];
|
||||
volatile float *row = smem + ty * (bdx + 2*ksizeHalf);
|
||||
|
||||
if (y < height)
|
||||
{
|
||||
// Vertical pass
|
||||
for (int i = tx; i < bdx + 2*ksizeHalf; i += bdx)
|
||||
{
|
||||
int xExt = int(bx * bdx) + i - ksizeHalf;
|
||||
xExt = b.idx_col(xExt);
|
||||
row[i] = src(y, xExt) * c_gKer[0];
|
||||
for (int j = 1; j <= ksizeHalf; ++j)
|
||||
row[i] +=
|
||||
(src(b.idx_row_low(y - j), xExt) +
|
||||
src(b.idx_row_high(y + j), xExt)) * c_gKer[j];
|
||||
}
|
||||
|
||||
if (x < width)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Horizontal pass
|
||||
row += tx + ksizeHalf;
|
||||
float res = row[0] * c_gKer[0];
|
||||
for (int i = 1; i <= ksizeHalf; ++i)
|
||||
res += (row[-i] + row[i]) * c_gKer[i];
|
||||
dst(y, x) = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setGaussianBlurKernel(const float *gKer, int ksizeHalf)
|
||||
{
|
||||
cudaSafeCall(cudaMemcpyToSymbol(c_gKer, gKer, (ksizeHalf + 1) * sizeof(*gKer)));
|
||||
}
|
||||
|
||||
|
||||
template <typename Border>
|
||||
void gaussianBlurCaller(const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, cudaStream_t stream)
|
||||
{
|
||||
int height = src.rows;
|
||||
int width = src.cols;
|
||||
|
||||
dim3 block(256);
|
||||
dim3 grid(divUp(width, block.x), divUp(height, block.y));
|
||||
int smem = (block.x + 2*ksizeHalf) * block.y * sizeof(float);
|
||||
Border b(height, width);
|
||||
|
||||
gaussianBlur<<<grid, block, smem, stream>>>(height, width, src, ksizeHalf, b, dst);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
|
||||
void gaussianBlurGpu(
|
||||
const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, int borderMode, cudaStream_t stream)
|
||||
{
|
||||
typedef void (*caller_t)(const PtrStepSzf, int, PtrStepSzf, cudaStream_t);
|
||||
|
||||
static const caller_t callers[] =
|
||||
{
|
||||
0 /*gaussianBlurCaller<BrdConstant<float> >*/,
|
||||
gaussianBlurCaller<BrdReplicate<float> >,
|
||||
0 /*gaussianBlurCaller<BrdReflect<float> >*/,
|
||||
0 /*gaussianBlurCaller<BrdWrap<float> >*/,
|
||||
gaussianBlurCaller<BrdReflect101<float> >
|
||||
};
|
||||
|
||||
callers[borderMode](src, ksizeHalf, dst, stream);
|
||||
}
|
||||
|
||||
|
||||
template <typename Border>
|
||||
__global__ void gaussianBlur5(
|
||||
const int height, const int width, const PtrStepf src, const int ksizeHalf,
|
||||
const Border b, PtrStepf dst)
|
||||
{
|
||||
const int y = by * bdy + ty;
|
||||
const int x = bx * bdx + tx;
|
||||
|
||||
extern __shared__ float smem[];
|
||||
|
||||
const int smw = bdx + 2*ksizeHalf; // shared memory "width"
|
||||
volatile float *row = smem + 5 * ty * smw;
|
||||
|
||||
if (y < height)
|
||||
{
|
||||
// Vertical pass
|
||||
for (int i = tx; i < bdx + 2*ksizeHalf; i += bdx)
|
||||
{
|
||||
int xExt = int(bx * bdx) + i - ksizeHalf;
|
||||
xExt = b.idx_col(xExt);
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] = src(k*height + y, xExt) * c_gKer[0];
|
||||
|
||||
for (int j = 1; j <= ksizeHalf; ++j)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] +=
|
||||
(src(k*height + b.idx_row_low(y - j), xExt) +
|
||||
src(k*height + b.idx_row_high(y + j), xExt)) * c_gKer[j];
|
||||
}
|
||||
|
||||
if (x < width)
|
||||
{
|
||||
__syncthreads();
|
||||
|
||||
// Horizontal pass
|
||||
|
||||
row += tx + ksizeHalf;
|
||||
float res[5];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] = row[k*smw] * c_gKer[0];
|
||||
|
||||
for (int i = 1; i <= ksizeHalf; ++i)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] += (row[k*smw - i] + row[k*smw + i]) * c_gKer[i];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
dst(k*height + y, x) = res[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename Border, int blockDimX>
|
||||
void gaussianBlur5Caller(
|
||||
const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, cudaStream_t stream)
|
||||
{
|
||||
int height = src.rows / 5;
|
||||
int width = src.cols;
|
||||
|
||||
dim3 block(blockDimX);
|
||||
dim3 grid(divUp(width, block.x), divUp(height, block.y));
|
||||
int smem = (block.x + 2*ksizeHalf) * 5 * block.y * sizeof(float);
|
||||
Border b(height, width);
|
||||
|
||||
gaussianBlur5<<<grid, block, smem, stream>>>(height, width, src, ksizeHalf, b, dst);
|
||||
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
|
||||
void gaussianBlur5Gpu(
|
||||
const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, int borderMode, cudaStream_t stream)
|
||||
{
|
||||
typedef void (*caller_t)(const PtrStepSzf, int, PtrStepSzf, cudaStream_t);
|
||||
|
||||
static const caller_t callers[] =
|
||||
{
|
||||
0 /*gaussianBlur5Caller<BrdConstant<float>,256>*/,
|
||||
gaussianBlur5Caller<BrdReplicate<float>,256>,
|
||||
0 /*gaussianBlur5Caller<BrdReflect<float>,256>*/,
|
||||
0 /*gaussianBlur5Caller<BrdWrap<float>,256>*/,
|
||||
gaussianBlur5Caller<BrdReflect101<float>,256>
|
||||
};
|
||||
|
||||
callers[borderMode](src, ksizeHalf, dst, stream);
|
||||
}
|
||||
|
||||
void gaussianBlur5Gpu_CC11(
|
||||
const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, int borderMode, cudaStream_t stream)
|
||||
{
|
||||
typedef void (*caller_t)(const PtrStepSzf, int, PtrStepSzf, cudaStream_t);
|
||||
|
||||
static const caller_t callers[] =
|
||||
{
|
||||
0 /*gaussianBlur5Caller<BrdConstant<float>,128>*/,
|
||||
gaussianBlur5Caller<BrdReplicate<float>,128>,
|
||||
0 /*gaussianBlur5Caller<BrdReflect<float>,128>*/,
|
||||
0 /*gaussianBlur5Caller<BrdWrap<float>,128>*/,
|
||||
gaussianBlur5Caller<BrdReflect101<float>,128>
|
||||
};
|
||||
|
||||
callers[borderMode](src, ksizeHalf, dst, stream);
|
||||
}
|
||||
|
||||
}}}} // namespace cv { namespace cuda { namespace cudev { namespace optflow_farneback
|
||||
|
||||
|
||||
#endif /* CUDA_DISABLER */
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//M*/
|
||||
#if !defined CUDA_DISABLER
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
|
||||
#define BLOCKDIM_X 32
|
||||
#define BLOCKDIM_Y 16
|
||||
|
||||
// data required to do 2x upsampling. Same can be used for 4x upsampling also
|
||||
#define SMEM_COLS ((BLOCKDIM_X)/2)
|
||||
#define SMEM_ROWS ((BLOCKDIM_Y)/2)
|
||||
|
||||
#if defined(__CUDACC_VER_MAJOR__) && (10 <= __CUDACC_VER_MAJOR__)
|
||||
namespace cv { namespace cuda { namespace device { namespace optflow_nvidia
|
||||
{
|
||||
static const char *_cudaGetErrorEnum(cudaError_t error) { return cudaGetErrorName(error); }
|
||||
|
||||
template <typename T>
|
||||
void check(T result, char const *const func, const char *const file,
|
||||
int const line) {
|
||||
if (result) {
|
||||
fprintf(stderr, "CUDA error at %s:%d code=%d(%s) \"%s\" \n", file, line,
|
||||
static_cast<uint32_t>(result), _cudaGetErrorEnum(result), func);
|
||||
// Make sure we call CUDA Device Reset before exiting
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
#define checkCudaErrors(val) check((val), #val, __FILE__, __LINE__)
|
||||
|
||||
template <class T>
|
||||
static __device__ void ReadDevPtrData(void* devptr, uint32_t x0, uint32_t y0, uint32_t src_w, uint32_t src_h, uint32_t src_pitch,
|
||||
T src[][SMEM_COLS], uint32_t i, uint32_t j)
|
||||
{
|
||||
uint32_t shift = (sizeof(T) == sizeof(int32_t)) ? 2 : 1;
|
||||
src[j][i] = *(T*)((uint8_t*)devptr + y0 * src_pitch + (x0 << shift));
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
__global__ void NearestNeighborFlowKernel(cudaSurfaceObject_t srcSurf, void* srcDevPtr, uint32_t src_w, uint32_t src_pitch, uint32_t src_h,
|
||||
cudaSurfaceObject_t dstSurf, void* dstDevPtr, uint32_t dst_w, uint32_t dst_pitch, uint32_t dst_h,
|
||||
uint32_t nScaleFactor)
|
||||
{
|
||||
int x = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
int y = blockDim.y * blockIdx.y + threadIdx.y;
|
||||
|
||||
int x0 = x / nScaleFactor;
|
||||
int y0 = y / nScaleFactor;
|
||||
|
||||
__shared__ short2 src[SMEM_ROWS][SMEM_COLS];
|
||||
|
||||
int i = threadIdx.x / nScaleFactor;
|
||||
int j = threadIdx.y / nScaleFactor;
|
||||
|
||||
if ((x % nScaleFactor == 0) && (y % nScaleFactor == 0))
|
||||
{
|
||||
ReadDevPtrData<short2>(srcDevPtr, x0, y0, src_w, src_h, src_pitch, src, i, j);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (x < dst_w && y < dst_h)
|
||||
{
|
||||
if (dstDevPtr == NULL)
|
||||
{
|
||||
surf2Dwrite<short2>(src[j][i], dstSurf, x * sizeof(short2), y, cudaBoundaryModeClamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
*(short2*)((uint8_t*)dstDevPtr + y * dst_pitch + (x << 2)) = src[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlowUpsample(void* srcDevPtr, uint32_t nSrcWidth, uint32_t nSrcPitch, uint32_t nSrcHeight,
|
||||
void* dstDevPtr, uint32_t nDstWidth, uint32_t nDstPitch, uint32_t nDstHeight,
|
||||
uint32_t nScaleFactor)
|
||||
{
|
||||
|
||||
dim3 blockDim(BLOCKDIM_X, BLOCKDIM_Y);
|
||||
dim3 gridDim((nDstWidth + blockDim.x - 1) / blockDim.x, (nDstHeight + blockDim.y - 1) / blockDim.y);
|
||||
NearestNeighborFlowKernel <<<gridDim, blockDim >>> (0, srcDevPtr, nSrcWidth, nSrcPitch, nSrcHeight,
|
||||
0, dstDevPtr, nDstWidth, nDstPitch, nDstHeight,
|
||||
nScaleFactor);
|
||||
|
||||
checkCudaErrors(cudaGetLastError());
|
||||
}}}}}
|
||||
#endif //defined(__CUDACC_VER_MAJOR__) && (10 <= __CUDACC_VER_MAJOR__)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,962 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#if !defined CUDA_DISABLER
|
||||
|
||||
#include "opencv2/core/cuda/common.hpp"
|
||||
#include "opencv2/core/cuda/utility.hpp"
|
||||
#include "opencv2/core/cuda/functional.hpp"
|
||||
#include "opencv2/core/cuda/limits.hpp"
|
||||
#include "opencv2/core/cuda/vec_math.hpp"
|
||||
#include "opencv2/core/cuda/reduce.hpp"
|
||||
#include "opencv2/core/cuda/filters.hpp"
|
||||
#include "opencv2/core/cuda/border_interpolate.hpp"
|
||||
#include <opencv2/cudev/ptr2d/texture.hpp>
|
||||
|
||||
using namespace cv::cuda;
|
||||
using namespace cv::cuda::device;
|
||||
|
||||
namespace pyrlk
|
||||
{
|
||||
__constant__ int c_winSize_x;
|
||||
__constant__ int c_winSize_y;
|
||||
__constant__ int c_halfWin_x;
|
||||
__constant__ int c_halfWin_y;
|
||||
__constant__ int c_iters;
|
||||
|
||||
__device__ __forceinline__ void accum(float& dst, const float& val)
|
||||
{
|
||||
dst += val;
|
||||
}
|
||||
__device__ __forceinline__ void accum(float& dst, const float2& val)
|
||||
{
|
||||
dst += val.x + val.y;
|
||||
}
|
||||
__device__ __forceinline__ void accum(float& dst, const float3& val)
|
||||
{
|
||||
dst += val.x + val.y + val.z;
|
||||
}
|
||||
__device__ __forceinline__ void accum(float& dst, const float4& val)
|
||||
{
|
||||
dst += val.x + val.y + val.z + val.w;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ float abs_(float a)
|
||||
{
|
||||
return ::fabsf(a);
|
||||
}
|
||||
__device__ __forceinline__ float4 abs_(const float4& a)
|
||||
{
|
||||
return abs(a);
|
||||
}
|
||||
__device__ __forceinline__ float2 abs_(const float2& a)
|
||||
{
|
||||
return abs(a);
|
||||
}
|
||||
__device__ __forceinline__ float3 abs_(const float3& a)
|
||||
{
|
||||
return abs(a);
|
||||
}
|
||||
|
||||
|
||||
template<typename T> __device__ __forceinline__ typename TypeVec<float, 1>::vec_type ToFloat(const typename TypeVec<T, 1>::vec_type& other)
|
||||
{
|
||||
return other;
|
||||
}
|
||||
template<typename T> __device__ __forceinline__ typename TypeVec<float, 2>::vec_type ToFloat(const typename TypeVec<T, 2>::vec_type& other)
|
||||
{
|
||||
typename TypeVec<float, 2>::vec_type ret;
|
||||
ret.x = other.x;
|
||||
ret.y = other.y;
|
||||
return ret;
|
||||
}
|
||||
template<typename T> __device__ __forceinline__ typename TypeVec<float, 3>::vec_type ToFloat(const typename TypeVec<T, 3>::vec_type& other)
|
||||
{
|
||||
typename TypeVec<float, 3>::vec_type ret;
|
||||
ret.x = other.x;
|
||||
ret.y = other.y;
|
||||
ret.z = other.z;
|
||||
return ret;
|
||||
}
|
||||
template<typename T> __device__ __forceinline__ typename TypeVec<float, 4>::vec_type ToFloat(const typename TypeVec<T, 4>::vec_type& other)
|
||||
{
|
||||
typename TypeVec<float, 4>::vec_type ret;
|
||||
ret.x = other.x;
|
||||
ret.y = other.y;
|
||||
ret.z = other.z;
|
||||
ret.w = other.w;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct DenormalizationFactor
|
||||
{
|
||||
static __device__ __forceinline__ float factor()
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DenormalizationFactor<uchar>
|
||||
{
|
||||
static __device__ __forceinline__ float factor()
|
||||
{
|
||||
return 255.0f;
|
||||
}
|
||||
};
|
||||
|
||||
template <int cn, int PATCH_X, int PATCH_Y, bool calcErr, typename T, class Ptr2D>
|
||||
__global__ void sparseKernel(const Ptr2D texI, const Ptr2D texJ, const float2* prevPts, float2* nextPts, uchar* status, float* err, const int level, const int rows, const int cols)
|
||||
{
|
||||
#if __CUDA_ARCH__ <= 110
|
||||
const int BLOCK_SIZE = 128;
|
||||
#else
|
||||
const int BLOCK_SIZE = 256;
|
||||
#endif
|
||||
|
||||
__shared__ float smem1[BLOCK_SIZE];
|
||||
__shared__ float smem2[BLOCK_SIZE];
|
||||
__shared__ float smem3[BLOCK_SIZE];
|
||||
|
||||
const unsigned int tid = threadIdx.y * blockDim.x + threadIdx.x;
|
||||
|
||||
float2 prevPt = prevPts[blockIdx.x];
|
||||
prevPt.x *= (1.0f / (1 << level));
|
||||
prevPt.y *= (1.0f / (1 << level));
|
||||
|
||||
if (prevPt.x < 0 || prevPt.x >= cols || prevPt.y < 0 || prevPt.y >= rows)
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[blockIdx.x] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
prevPt.x -= c_halfWin_x;
|
||||
prevPt.y -= c_halfWin_y;
|
||||
|
||||
// extract the patch from the first image, compute covariation matrix of derivatives
|
||||
|
||||
float A11 = 0;
|
||||
float A12 = 0;
|
||||
float A22 = 0;
|
||||
|
||||
typedef typename TypeVec<float, cn>::vec_type work_type;
|
||||
|
||||
work_type I_patch [PATCH_Y][PATCH_X];
|
||||
work_type dIdx_patch[PATCH_Y][PATCH_X];
|
||||
work_type dIdy_patch[PATCH_Y][PATCH_X];
|
||||
|
||||
for (int yBase = threadIdx.y, i = 0; yBase < c_winSize_y; yBase += blockDim.y, ++i)
|
||||
{
|
||||
for (int xBase = threadIdx.x, j = 0; xBase < c_winSize_x; xBase += blockDim.x, ++j)
|
||||
{
|
||||
float x = prevPt.x + xBase + 0.5f;
|
||||
float y = prevPt.y + yBase + 0.5f;
|
||||
|
||||
I_patch[i][j] = texI(y, x);
|
||||
|
||||
// Scharr Deriv
|
||||
work_type dIdx = 3.0f * texI(y - 1, x + 1) + 10.0f * texI(y, x + 1) + 3.0f * texI(y + 1, x + 1) -
|
||||
(3.0f * texI(y - 1, x - 1) + 10.0f * texI(y, x - 1) + 3.0f * texI(y + 1, x - 1));
|
||||
|
||||
work_type dIdy = 3.0f * texI(y + 1, x - 1) + 10.0f * texI(y + 1, x) + 3.0f * texI(y + 1, x + 1) -
|
||||
(3.0f * texI(y - 1, x - 1) + 10.0f * texI(y - 1, x) + 3.0f * texI(y - 1, x + 1));
|
||||
|
||||
dIdx_patch[i][j] = dIdx;
|
||||
dIdy_patch[i][j] = dIdy;
|
||||
|
||||
accum(A11, dIdx * dIdx);
|
||||
accum(A12, dIdx * dIdy);
|
||||
accum(A22, dIdy * dIdy);
|
||||
}
|
||||
}
|
||||
|
||||
reduce<BLOCK_SIZE>(smem_tuple(smem1, smem2, smem3), thrust::tie(A11, A12, A22), tid, thrust::make_tuple(plus<float>(), plus<float>(), plus<float>()));
|
||||
|
||||
#if __CUDA_ARCH__ >= 300
|
||||
if (tid == 0)
|
||||
{
|
||||
smem1[0] = A11;
|
||||
smem2[0] = A12;
|
||||
smem3[0] = A22;
|
||||
}
|
||||
#endif
|
||||
|
||||
__syncthreads();
|
||||
|
||||
A11 = smem1[0];
|
||||
A12 = smem2[0];
|
||||
A22 = smem3[0];
|
||||
|
||||
float D = A11 * A22 - A12 * A12;
|
||||
|
||||
if (D < numeric_limits<float>::epsilon())
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[blockIdx.x] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
D = 1.f / D;
|
||||
|
||||
A11 *= D;
|
||||
A12 *= D;
|
||||
A22 *= D;
|
||||
|
||||
float2 nextPt = nextPts[blockIdx.x];
|
||||
nextPt.x *= 2.f;
|
||||
nextPt.y *= 2.f;
|
||||
|
||||
nextPt.x -= c_halfWin_x;
|
||||
nextPt.y -= c_halfWin_y;
|
||||
|
||||
for (int k = 0; k < c_iters; ++k)
|
||||
{
|
||||
if (nextPt.x < -c_halfWin_x || nextPt.x >= cols || nextPt.y < -c_halfWin_y || nextPt.y >= rows)
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[blockIdx.x] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float b1 = 0;
|
||||
float b2 = 0;
|
||||
|
||||
for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i)
|
||||
{
|
||||
for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j)
|
||||
{
|
||||
work_type I_val = I_patch[i][j];
|
||||
|
||||
work_type J_val = texJ(nextPt.y + y + 0.5f, nextPt.x + x + 0.5f);
|
||||
|
||||
work_type diff = (J_val - I_val) * 32.0f;
|
||||
|
||||
accum(b1, diff * dIdx_patch[i][j]);
|
||||
accum(b2, diff * dIdy_patch[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
reduce<BLOCK_SIZE>(smem_tuple(smem1, smem2), thrust::tie(b1, b2), tid, thrust::make_tuple(plus<float>(), plus<float>()));
|
||||
|
||||
#if __CUDA_ARCH__ >= 300
|
||||
if (tid == 0)
|
||||
{
|
||||
smem1[0] = b1;
|
||||
smem2[0] = b2;
|
||||
}
|
||||
#endif
|
||||
|
||||
__syncthreads();
|
||||
|
||||
b1 = smem1[0];
|
||||
b2 = smem2[0];
|
||||
|
||||
float2 delta;
|
||||
delta.x = A12 * b2 - A22 * b1;
|
||||
delta.y = A12 * b1 - A11 * b2;
|
||||
|
||||
nextPt.x += delta.x;
|
||||
nextPt.y += delta.y;
|
||||
|
||||
if (::fabs(delta.x) < 0.01f && ::fabs(delta.y) < 0.01f)
|
||||
break;
|
||||
}
|
||||
|
||||
float errval = 0;
|
||||
if (calcErr)
|
||||
{
|
||||
for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i)
|
||||
{
|
||||
for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j)
|
||||
{
|
||||
work_type I_val = I_patch[i][j];
|
||||
|
||||
work_type J_val = texJ(nextPt.y + y + 0.5f, nextPt.x + x + 0.5f);
|
||||
|
||||
work_type diff = J_val - I_val;
|
||||
|
||||
accum(errval, abs_(diff));
|
||||
}
|
||||
}
|
||||
|
||||
reduce<BLOCK_SIZE>(smem1, errval, tid, plus<float>());
|
||||
}
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
nextPt.x += c_halfWin_x;
|
||||
nextPt.y += c_halfWin_y;
|
||||
|
||||
nextPts[blockIdx.x] = nextPt;
|
||||
|
||||
if (calcErr)
|
||||
err[blockIdx.x] = static_cast<float>(errval) / (::min(cn, 3) * c_winSize_x * c_winSize_y) * DenormalizationFactor<T>::factor();
|
||||
}
|
||||
}
|
||||
|
||||
// Kernel, uses non texture fetches
|
||||
template <int PATCH_X, int PATCH_Y, bool calcErr, int cn, typename T, typename Ptr2D>
|
||||
__global__ void sparseKernel_(Ptr2D I, Ptr2D J, const float2* prevPts, float2* nextPts, uchar* status, float* err, const int level, const int rows, const int cols)
|
||||
{
|
||||
#if __CUDA_ARCH__ <= 110
|
||||
const int BLOCK_SIZE = 128;
|
||||
#else
|
||||
const int BLOCK_SIZE = 256;
|
||||
#endif
|
||||
|
||||
__shared__ float smem1[BLOCK_SIZE];
|
||||
__shared__ float smem2[BLOCK_SIZE];
|
||||
__shared__ float smem3[BLOCK_SIZE];
|
||||
|
||||
const unsigned int tid = threadIdx.y * blockDim.x + threadIdx.x;
|
||||
|
||||
float2 prevPt = prevPts[blockIdx.x];
|
||||
prevPt.x *= (1.0f / (1 << level));
|
||||
prevPt.y *= (1.0f / (1 << level));
|
||||
|
||||
if (prevPt.x < 0 || prevPt.x >= cols || prevPt.y < 0 || prevPt.y >= rows)
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[blockIdx.x] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
prevPt.x -= c_halfWin_x;
|
||||
prevPt.y -= c_halfWin_y;
|
||||
|
||||
// extract the patch from the first image, compute covariation matrix of derivatives
|
||||
|
||||
float A11 = 0;
|
||||
float A12 = 0;
|
||||
float A22 = 0;
|
||||
|
||||
typedef typename TypeVec<float, cn>::vec_type work_type;
|
||||
|
||||
work_type I_patch[PATCH_Y][PATCH_X];
|
||||
work_type dIdx_patch[PATCH_Y][PATCH_X];
|
||||
work_type dIdy_patch[PATCH_Y][PATCH_X];
|
||||
|
||||
for (int yBase = threadIdx.y, i = 0; yBase < c_winSize_y; yBase += blockDim.y, ++i)
|
||||
{
|
||||
for (int xBase = threadIdx.x, j = 0; xBase < c_winSize_x; xBase += blockDim.x, ++j)
|
||||
{
|
||||
float x = prevPt.x + xBase + 0.5f;
|
||||
float y = prevPt.y + yBase + 0.5f;
|
||||
|
||||
I_patch[i][j] = ToFloat<T>(I(y, x));
|
||||
|
||||
// Scharr Deriv
|
||||
|
||||
work_type dIdx = 3.0f * I(y - 1, x + 1) + 10.0f * I(y, x + 1) + 3.0f * I(y + 1, x + 1) -
|
||||
(3.0f * I(y - 1, x - 1) + 10.0f * I(y, x - 1) + 3.0f * I(y + 1 , x - 1));
|
||||
|
||||
work_type dIdy = 3.0f * I(y + 1, x - 1) + 10.0f * I(y + 1, x) + 3.0f * I(y+1, x + 1) -
|
||||
(3.0f * I(y - 1, x - 1) + 10.0f * I(y-1, x) + 3.0f * I(y - 1, x + 1));
|
||||
|
||||
dIdx_patch[i][j] = dIdx;
|
||||
dIdy_patch[i][j] = dIdy;
|
||||
|
||||
accum(A11, dIdx * dIdx);
|
||||
accum(A12, dIdx * dIdy);
|
||||
accum(A22, dIdy * dIdy);
|
||||
}
|
||||
}
|
||||
|
||||
reduce<BLOCK_SIZE>(smem_tuple(smem1, smem2, smem3), thrust::tie(A11, A12, A22), tid, thrust::make_tuple(plus<float>(), plus<float>(), plus<float>()));
|
||||
|
||||
#if __CUDA_ARCH__ >= 300
|
||||
if (tid == 0)
|
||||
{
|
||||
smem1[0] = A11;
|
||||
smem2[0] = A12;
|
||||
smem3[0] = A22;
|
||||
}
|
||||
#endif
|
||||
|
||||
__syncthreads();
|
||||
|
||||
A11 = smem1[0];
|
||||
A12 = smem2[0];
|
||||
A22 = smem3[0];
|
||||
|
||||
float D = A11 * A22 - A12 * A12;
|
||||
|
||||
if (D < numeric_limits<float>::epsilon())
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[blockIdx.x] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
D = 1.f / D;
|
||||
|
||||
A11 *= D;
|
||||
A12 *= D;
|
||||
A22 *= D;
|
||||
|
||||
float2 nextPt = nextPts[blockIdx.x];
|
||||
nextPt.x *= 2.f;
|
||||
nextPt.y *= 2.f;
|
||||
|
||||
nextPt.x -= c_halfWin_x;
|
||||
nextPt.y -= c_halfWin_y;
|
||||
|
||||
for (int k = 0; k < c_iters; ++k)
|
||||
{
|
||||
if (nextPt.x < -c_halfWin_x || nextPt.x >= cols || nextPt.y < -c_halfWin_y || nextPt.y >= rows)
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[blockIdx.x] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float b1 = 0;
|
||||
float b2 = 0;
|
||||
|
||||
for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i)
|
||||
{
|
||||
for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j)
|
||||
{
|
||||
work_type I_val = I_patch[i][j];
|
||||
work_type J_val = ToFloat<T>(J(nextPt.y + y + 0.5f, nextPt.x + x + 0.5f));
|
||||
|
||||
work_type diff = (J_val - I_val) * 32.0f;
|
||||
|
||||
accum(b1, diff * dIdx_patch[i][j]);
|
||||
accum(b2, diff * dIdy_patch[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
reduce<BLOCK_SIZE>(smem_tuple(smem1, smem2), thrust::tie(b1, b2), tid, thrust::make_tuple(plus<float>(), plus<float>()));
|
||||
|
||||
#if __CUDA_ARCH__ >= 300
|
||||
if (tid == 0)
|
||||
{
|
||||
smem1[0] = b1;
|
||||
smem2[0] = b2;
|
||||
}
|
||||
#endif
|
||||
|
||||
__syncthreads();
|
||||
|
||||
b1 = smem1[0];
|
||||
b2 = smem2[0];
|
||||
|
||||
float2 delta;
|
||||
delta.x = A12 * b2 - A22 * b1;
|
||||
delta.y = A12 * b1 - A11 * b2;
|
||||
|
||||
nextPt.x += delta.x;
|
||||
nextPt.y += delta.y;
|
||||
|
||||
if (::fabs(delta.x) < 0.01f && ::fabs(delta.y) < 0.01f)
|
||||
break;
|
||||
}
|
||||
|
||||
float errval = 0;
|
||||
if (calcErr)
|
||||
{
|
||||
for (int y = threadIdx.y, i = 0; y < c_winSize_y; y += blockDim.y, ++i)
|
||||
{
|
||||
for (int x = threadIdx.x, j = 0; x < c_winSize_x; x += blockDim.x, ++j)
|
||||
{
|
||||
work_type I_val = I_patch[i][j];
|
||||
work_type J_val = ToFloat<T>(J(nextPt.y + y + 0.5f, nextPt.x + x + 0.5f));
|
||||
|
||||
work_type diff = J_val - I_val;
|
||||
|
||||
accum(errval, abs_(diff));
|
||||
}
|
||||
}
|
||||
|
||||
reduce<BLOCK_SIZE>(smem1, errval, tid, plus<float>());
|
||||
}
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
nextPt.x += c_halfWin_x;
|
||||
nextPt.y += c_halfWin_y;
|
||||
|
||||
nextPts[blockIdx.x] = nextPt;
|
||||
|
||||
if (calcErr)
|
||||
err[blockIdx.x] = static_cast<float>(errval) / (::min(cn, 3)*c_winSize_x * c_winSize_y);
|
||||
}
|
||||
} // __global__ void sparseKernel_
|
||||
|
||||
// Specialization for non float data, cudaFilterModeLinear only compatible with cudaReadModeNormalizedFloat.
|
||||
template<int cn, class T> class TextureLinear : public cv::cudev::Texture<typename TypeVec<T, cn>::vec_type, typename TypeVec<float, cn>::vec_type> {
|
||||
public:
|
||||
typedef typename TypeVec<T, cn>::vec_type elem_type;
|
||||
typedef typename TypeVec<float, cn>::vec_type ret_type;
|
||||
__host__ TextureLinear(PtrStepSz<elem_type> src, const bool normalizedCoords = false, const cudaTextureAddressMode addressMode = cudaAddressModeClamp) :
|
||||
cv::cudev::Texture<elem_type, ret_type>(src, normalizedCoords, cudaFilterModeLinear, addressMode, cudaReadModeNormalizedFloat)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for float data, cudaReadModeNormalizedFloat only compatible with cudaReadModeElementType.
|
||||
template<int cn> class TextureLinear<cn, float> : public cv::cudev::Texture<typename TypeVec<float, cn>::vec_type, typename TypeVec<float, cn>::vec_type>
|
||||
{
|
||||
public:
|
||||
typedef typename TypeVec<float, cn>::vec_type float_type;
|
||||
__host__ TextureLinear(PtrStepSz<float_type> src, const bool normalizedCoords = false, const cudaTextureAddressMode addressMode = cudaAddressModeClamp) :
|
||||
cv::cudev::Texture <float_type, float_type>(src, normalizedCoords, cudaFilterModeLinear, addressMode, cudaReadModeElementType)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <int cn, int PATCH_X, int PATCH_Y, typename T> class sparse_caller
|
||||
{
|
||||
public:
|
||||
static void call(PtrStepSz<typename TypeVec<T, cn>::vec_type> I, PtrStepSz<typename TypeVec<T, cn>::vec_type> J, int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, cudaStream_t stream)
|
||||
{
|
||||
typedef typename TypeVec<T, cn>::vec_type dType;
|
||||
typedef typename TypeVec<float, cn>::vec_type rType;
|
||||
TextureLinear<cn,T> texI(I);
|
||||
TextureLinear<cn,T> texJ(J);
|
||||
dim3 grid(ptcount);
|
||||
if (level == 0 && err)
|
||||
sparseKernel<cn, PATCH_X, PATCH_Y, true, T, cv::cudev::TexturePtr<dType,rType>><<<grid, block, 0, stream>>>(texI, texJ, prevPts, nextPts, status, err, level, rows, cols);
|
||||
else
|
||||
sparseKernel<cn, PATCH_X, PATCH_Y, false, T, cv::cudev::TexturePtr<dType, rType>><<<grid, block, 0, stream>>>(texI, texJ, prevPts, nextPts, status, err, level, rows, cols);
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
};
|
||||
// Specialization to use non texture path because for some reason the texture path keeps failing accuracy tests
|
||||
template<int PATCH_X, int PATCH_Y> class sparse_caller<1, PATCH_X, PATCH_Y, unsigned short>
|
||||
{
|
||||
public:
|
||||
typedef typename TypeVec<unsigned short, 1>::vec_type work_type;
|
||||
typedef PtrStepSz<work_type> Ptr2D;
|
||||
typedef BrdConstant<work_type> BrdType;
|
||||
typedef BorderReader<Ptr2D, BrdType> Reader;
|
||||
typedef LinearFilter<Reader> Filter;
|
||||
static void call(Ptr2D I, Ptr2D J, int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, cudaStream_t stream)
|
||||
{
|
||||
dim3 grid(ptcount);
|
||||
if (level == 0 && err)
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, true, 1, unsigned short> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, false, 1, unsigned short> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
};
|
||||
// Specialization for int because the texture path keeps failing
|
||||
template<int PATCH_X, int PATCH_Y> class sparse_caller<1, PATCH_X, PATCH_Y, int>
|
||||
{
|
||||
public:
|
||||
typedef typename TypeVec<int, 1>::vec_type work_type;
|
||||
typedef PtrStepSz<work_type> Ptr2D;
|
||||
typedef BrdConstant<work_type> BrdType;
|
||||
typedef BorderReader<Ptr2D, BrdType> Reader;
|
||||
typedef LinearFilter<Reader> Filter;
|
||||
static void call(Ptr2D I, Ptr2D J, int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, cudaStream_t stream)
|
||||
{
|
||||
dim3 grid(ptcount);
|
||||
if (level == 0 && err)
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, true, 1, int> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, false, 1, int> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
};
|
||||
template<int PATCH_X, int PATCH_Y> class sparse_caller<4, PATCH_X, PATCH_Y, int>
|
||||
{
|
||||
public:
|
||||
typedef typename TypeVec<int, 4>::vec_type work_type;
|
||||
typedef PtrStepSz<work_type> Ptr2D;
|
||||
typedef BrdConstant<work_type> BrdType;
|
||||
typedef BorderReader<Ptr2D, BrdType> Reader;
|
||||
typedef LinearFilter<Reader> Filter;
|
||||
static void call(Ptr2D I, Ptr2D J, int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, cudaStream_t stream)
|
||||
{
|
||||
dim3 grid(ptcount);
|
||||
if (level == 0 && err)
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, true, 4, int> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, false, 4, int> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
};
|
||||
using namespace cv::cuda::device;
|
||||
template <int PATCH_X, int PATCH_Y, typename T> class sparse_caller<3, PATCH_X, PATCH_Y, T>
|
||||
{
|
||||
public:
|
||||
typedef typename TypeVec<T, 3>::vec_type work_type;
|
||||
typedef PtrStepSz<work_type> Ptr2D;
|
||||
typedef BrdConstant<work_type> BrdType;
|
||||
typedef BorderReader<Ptr2D, BrdType> Reader;
|
||||
typedef LinearFilter<Reader> Filter;
|
||||
static void call(Ptr2D I, Ptr2D J, int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, cudaStream_t stream)
|
||||
{
|
||||
dim3 grid(ptcount);
|
||||
if (level == 0 && err)
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, true, 3, T> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
sparseKernel_<PATCH_X, PATCH_Y, false, 3, T> <<<grid, block, 0, stream >>>(
|
||||
Filter(Reader(I, BrdType(rows, cols))),
|
||||
Filter(Reader(J, BrdType(rows, cols))),
|
||||
prevPts, nextPts, status, err, level, rows, cols);
|
||||
}
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <bool calcErr, class Ptr2D>
|
||||
__global__ void denseKernel(const Ptr2D texI, const Ptr2D texJ, PtrStepf u, PtrStepf v, const PtrStepf prevU, const PtrStepf prevV, PtrStepf err, const int rows, const int cols)
|
||||
{
|
||||
extern __shared__ int smem[];
|
||||
|
||||
const int patchWidth = blockDim.x + 2 * c_halfWin_x;
|
||||
const int patchHeight = blockDim.y + 2 * c_halfWin_y;
|
||||
|
||||
int* I_patch = smem;
|
||||
int* dIdx_patch = I_patch + patchWidth * patchHeight;
|
||||
int* dIdy_patch = dIdx_patch + patchWidth * patchHeight;
|
||||
|
||||
const int xBase = blockIdx.x * blockDim.x;
|
||||
const int yBase = blockIdx.y * blockDim.y;
|
||||
|
||||
for (int i = threadIdx.y; i < patchHeight; i += blockDim.y)
|
||||
{
|
||||
for (int j = threadIdx.x; j < patchWidth; j += blockDim.x)
|
||||
{
|
||||
float x = xBase - c_halfWin_x + j + 0.5f;
|
||||
float y = yBase - c_halfWin_y + i + 0.5f;
|
||||
|
||||
I_patch[i * patchWidth + j] = texI(y, x);
|
||||
|
||||
// Scharr Deriv
|
||||
|
||||
dIdx_patch[i * patchWidth + j] = 3 * texI(y - 1, x + 1) + 10 * texI(y, x + 1) + 3 * texI(y + 1, x + 1) -
|
||||
(3 * texI(y - 1, x - 1) + 10 * texI(y, x - 1) + 3 * texI(y + 1, x - 1));
|
||||
|
||||
dIdy_patch[i * patchWidth + j] = 3 * texI(y + 1, x - 1) + 10 * texI(y + 1,x) + 3 * texI(y+ 1, x + 1) -
|
||||
(3 * texI(y - 1, x - 1) + 10 * texI(y - 1,x) + 3 * texI(y - 1, x + 1));
|
||||
}
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
const int x = xBase + threadIdx.x;
|
||||
const int y = yBase + threadIdx.y;
|
||||
|
||||
if (x >= cols || y >= rows)
|
||||
return;
|
||||
|
||||
|
||||
int A11i = 0;
|
||||
int A12i = 0;
|
||||
int A22i = 0;
|
||||
|
||||
for (int i = 0; i < c_winSize_y; ++i)
|
||||
{
|
||||
for (int j = 0; j < c_winSize_x; ++j)
|
||||
{
|
||||
int dIdx = dIdx_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)];
|
||||
int dIdy = dIdy_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)];
|
||||
|
||||
A11i += dIdx * dIdx;
|
||||
A12i += dIdx * dIdy;
|
||||
A22i += dIdy * dIdy;
|
||||
}
|
||||
}
|
||||
|
||||
float A11 = A11i;
|
||||
float A12 = A12i;
|
||||
float A22 = A22i;
|
||||
|
||||
float D = A11 * A22 - A12 * A12;
|
||||
|
||||
if (D < numeric_limits<float>::epsilon())
|
||||
{
|
||||
if (calcErr)
|
||||
err(y, x) = numeric_limits<float>::max();
|
||||
return;
|
||||
}
|
||||
|
||||
D = 1.f / D;
|
||||
|
||||
A11 *= D;
|
||||
A12 *= D;
|
||||
A22 *= D;
|
||||
|
||||
float2 nextPt;
|
||||
nextPt.x = x + prevU(y/2, x/2) * 2.0f;
|
||||
nextPt.y = y + prevV(y/2, x/2) * 2.0f;
|
||||
|
||||
for (int k = 0; k < c_iters; ++k)
|
||||
{
|
||||
if (nextPt.x < 0 || nextPt.x >= cols || nextPt.y < 0 || nextPt.y >= rows)
|
||||
{
|
||||
if (calcErr)
|
||||
err(y, x) = numeric_limits<float>::max();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int b1 = 0;
|
||||
int b2 = 0;
|
||||
|
||||
for (int i = 0; i < c_winSize_y; ++i)
|
||||
{
|
||||
for (int j = 0; j < c_winSize_x; ++j)
|
||||
{
|
||||
int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j];
|
||||
int J = texJ(nextPt.y - c_halfWin_y + i + 0.5f, nextPt.x - c_halfWin_x + j + 0.5f);
|
||||
|
||||
int diff = (J - I) * 32;
|
||||
|
||||
int dIdx = dIdx_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)];
|
||||
int dIdy = dIdy_patch[(threadIdx.y + i) * patchWidth + (threadIdx.x + j)];
|
||||
|
||||
b1 += diff * dIdx;
|
||||
b2 += diff * dIdy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float2 delta;
|
||||
delta.x = A12 * b2 - A22 * b1;
|
||||
delta.y = A12 * b1 - A11 * b2;
|
||||
|
||||
nextPt.x += delta.x;
|
||||
nextPt.y += delta.y;
|
||||
|
||||
if (::fabs(delta.x) < 0.01f && ::fabs(delta.y) < 0.01f)
|
||||
break;
|
||||
}
|
||||
|
||||
u(y, x) = nextPt.x - x;
|
||||
v(y, x) = nextPt.y - y;
|
||||
|
||||
if (calcErr)
|
||||
{
|
||||
int errval = 0;
|
||||
|
||||
for (int i = 0; i < c_winSize_y; ++i)
|
||||
{
|
||||
for (int j = 0; j < c_winSize_x; ++j)
|
||||
{
|
||||
int I = I_patch[(threadIdx.y + i) * patchWidth + threadIdx.x + j];
|
||||
|
||||
int J = texJ(nextPt.y - c_halfWin_y + i + 0.5f, nextPt.x - c_halfWin_x + j + 0.5f);
|
||||
|
||||
errval += ::abs(J - I);
|
||||
}
|
||||
}
|
||||
|
||||
err(y, x) = static_cast<float>(errval) / (c_winSize_x * c_winSize_y);
|
||||
}
|
||||
}
|
||||
|
||||
void loadWinSize(int* winSize, int* halfWinSize, cudaStream_t stream)
|
||||
{
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_winSize_x, winSize, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_winSize_y, winSize + 1, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_halfWin_x, halfWinSize, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_halfWin_y, halfWinSize + 1, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
}
|
||||
|
||||
void loadIters(int* iters, cudaStream_t stream)
|
||||
{
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_iters, iters, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
}
|
||||
|
||||
void loadConstants(int2 winSize_, int iters_, cudaStream_t stream)
|
||||
{
|
||||
static int2 winSize = make_int2(0,0);
|
||||
if(winSize.x != winSize_.x || winSize.y != winSize_.y)
|
||||
{
|
||||
winSize = winSize_;
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_winSize_x, &winSize.x, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_winSize_y, &winSize.y, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
}
|
||||
|
||||
static int2 halfWin = make_int2(0,0);
|
||||
int2 half = make_int2((winSize.x - 1) / 2, (winSize.y - 1) / 2);
|
||||
if(halfWin.x != half.x || halfWin.y != half.y)
|
||||
{
|
||||
halfWin = half;
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_halfWin_x, &halfWin.x, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_halfWin_y, &halfWin.y, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
}
|
||||
|
||||
static int iters = 0;
|
||||
if(iters != iters_)
|
||||
{
|
||||
iters = iters_;
|
||||
cudaSafeCall( cudaMemcpyToSymbolAsync(c_iters, &iters, sizeof(int), 0, cudaMemcpyHostToDevice, stream) );
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int cn> struct pyrLK_caller
|
||||
{
|
||||
static void sparse(PtrStepSz<typename TypeVec<T, cn>::vec_type> I, PtrStepSz<typename TypeVec<T, cn>::vec_type> J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, dim3 patch, cudaStream_t stream)
|
||||
{
|
||||
typedef void(*func_t)(PtrStepSz<typename TypeVec<T, cn>::vec_type> I, PtrStepSz<typename TypeVec<T, cn>::vec_type> J,
|
||||
int rows, int cols, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, cudaStream_t stream);
|
||||
|
||||
static const func_t funcs[5][5] =
|
||||
{
|
||||
{ sparse_caller<cn, 1, 1,T>::call, sparse_caller<cn, 2, 1,T>::call, sparse_caller<cn, 3, 1,T>::call, sparse_caller<cn, 4, 1,T>::call, sparse_caller<cn, 5, 1,T>::call },
|
||||
{ sparse_caller<cn, 1, 2,T>::call, sparse_caller<cn, 2, 2,T>::call, sparse_caller<cn, 3, 2,T>::call, sparse_caller<cn, 4, 2,T>::call, sparse_caller<cn, 5, 2,T>::call },
|
||||
{ sparse_caller<cn, 1, 3,T>::call, sparse_caller<cn, 2, 3,T>::call, sparse_caller<cn, 3, 3,T>::call, sparse_caller<cn, 4, 3,T>::call, sparse_caller<cn, 5, 3,T>::call },
|
||||
{ sparse_caller<cn, 1, 4,T>::call, sparse_caller<cn, 2, 4,T>::call, sparse_caller<cn, 3, 4,T>::call, sparse_caller<cn, 4, 4,T>::call, sparse_caller<cn, 5, 4,T>::call },
|
||||
{ sparse_caller<cn, 1, 5,T>::call, sparse_caller<cn, 2, 5,T>::call, sparse_caller<cn, 3, 5,T>::call, sparse_caller<cn, 4, 5,T>::call, sparse_caller<cn, 5, 5,T>::call }
|
||||
};
|
||||
|
||||
funcs[patch.y - 1][patch.x - 1](I, J, I.rows, I.cols, prevPts, nextPts, status, err, ptcount,
|
||||
level, block, stream);
|
||||
}
|
||||
static void dense(PtrStepSz<T> I, PtrStepSz<T> J, PtrStepSzf u, PtrStepSzf v, PtrStepSzf prevU, PtrStepSzf prevV, PtrStepSzf err, int2 winSize, cudaStream_t stream)
|
||||
{
|
||||
dim3 block(16, 16);
|
||||
dim3 grid(divUp(I.cols, block.x), divUp(I.rows, block.y));
|
||||
TextureLinear<1, T> texI(I);
|
||||
TextureLinear<1, T> texJ(J);
|
||||
int2 halfWin = make_int2((winSize.x - 1) / 2, (winSize.y - 1) / 2);
|
||||
const int patchWidth = block.x + 2 * halfWin.x;
|
||||
const int patchHeight = block.y + 2 * halfWin.y;
|
||||
size_t smem_size = 3 * patchWidth * patchHeight * sizeof(int);
|
||||
|
||||
if (err.data)
|
||||
{
|
||||
denseKernel<true, cv::cudev::TexturePtr<T,float>><<<grid, block, smem_size, stream>>>(texI, texJ, u, v, prevU, prevV, err, I.rows, I.cols);
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
denseKernel<false, cv::cudev::TexturePtr<T, float>><<<grid, block, smem_size, stream>>>(texI, texJ, u, v, prevU, prevV, PtrStepf(), I.rows, I.cols);
|
||||
cudaSafeCall(cudaGetLastError());
|
||||
}
|
||||
|
||||
if (stream == 0)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
};
|
||||
|
||||
template class pyrLK_caller<unsigned char,1>;
|
||||
template class pyrLK_caller<unsigned short,1>;
|
||||
template class pyrLK_caller<int,1>;
|
||||
template class pyrLK_caller<float,1>;
|
||||
|
||||
template class pyrLK_caller<unsigned char, 3>;
|
||||
template class pyrLK_caller<unsigned short, 3>;
|
||||
template class pyrLK_caller<int, 3>;
|
||||
template class pyrLK_caller<float, 3>;
|
||||
|
||||
template class pyrLK_caller<unsigned char, 4>;
|
||||
template class pyrLK_caller<unsigned short, 4>;
|
||||
template class pyrLK_caller<int, 4>;
|
||||
template class pyrLK_caller<float, 4>;
|
||||
}
|
||||
|
||||
#endif /* CUDA_DISABLER */
|
||||
@@ -0,0 +1,366 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#if !defined CUDA_DISABLER
|
||||
|
||||
#include "opencv2/core/cuda/common.hpp"
|
||||
#include "opencv2/core/cuda/border_interpolate.hpp"
|
||||
#include "opencv2/core/cuda/limits.hpp"
|
||||
#include "opencv2/core/cuda.hpp"
|
||||
#include <opencv2/cudev/ptr2d/texture.hpp>
|
||||
|
||||
using namespace cv::cuda;
|
||||
using namespace cv::cuda::device;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// centeredGradient
|
||||
|
||||
namespace tvl1flow
|
||||
{
|
||||
__global__ void centeredGradientKernel(const PtrStepSzf src, PtrStepf dx, PtrStepf dy)
|
||||
{
|
||||
const int x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (x >= src.cols || y >= src.rows)
|
||||
return;
|
||||
|
||||
dx(y, x) = 0.5f * (src(y, ::min(x + 1, src.cols - 1)) - src(y, ::max(x - 1, 0)));
|
||||
dy(y, x) = 0.5f * (src(::min(y + 1, src.rows - 1), x) - src(::max(y - 1, 0), x));
|
||||
}
|
||||
|
||||
void centeredGradient(PtrStepSzf src, PtrStepSzf dx, PtrStepSzf dy, cudaStream_t stream)
|
||||
{
|
||||
const dim3 block(32, 8);
|
||||
const dim3 grid(divUp(src.cols, block.x), divUp(src.rows, block.y));
|
||||
|
||||
centeredGradientKernel<<<grid, block, 0, stream>>>(src, dx, dy);
|
||||
cudaSafeCall( cudaGetLastError() );
|
||||
|
||||
if (!stream)
|
||||
cudaSafeCall( cudaDeviceSynchronize() );
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// warpBackward
|
||||
|
||||
namespace tvl1flow
|
||||
{
|
||||
static __device__ __forceinline__ float bicubicCoeff(float x_)
|
||||
{
|
||||
float x = fabsf(x_);
|
||||
if (x <= 1.0f)
|
||||
{
|
||||
return x * x * (1.5f * x - 2.5f) + 1.0f;
|
||||
}
|
||||
else if (x < 2.0f)
|
||||
{
|
||||
return x * (x * (-0.5f * x + 2.5f) - 4.0f) + 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void warpBackwardKernel(
|
||||
const PtrStepSzf I0, const cv::cudev::TexturePtr<float> I1, const cv::cudev::TexturePtr<float> I1x, const cv::cudev::TexturePtr<float> I1y, const PtrStepf u1, const PtrStepf u2,
|
||||
PtrStepf I1w, PtrStepf I1wx, PtrStepf I1wy, PtrStepf grad, PtrStepf rho)
|
||||
{
|
||||
const int x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (x >= I0.cols || y >= I0.rows)
|
||||
return;
|
||||
|
||||
const float u1Val = u1(y, x);
|
||||
const float u2Val = u2(y, x);
|
||||
|
||||
const float wx = x + u1Val;
|
||||
const float wy = y + u2Val;
|
||||
|
||||
const int xmin = ::ceilf(wx - 2.0f);
|
||||
const int xmax = ::floorf(wx + 2.0f);
|
||||
|
||||
const int ymin = ::ceilf(wy - 2.0f);
|
||||
const int ymax = ::floorf(wy + 2.0f);
|
||||
|
||||
float sum = 0.0f;
|
||||
float sumx = 0.0f;
|
||||
float sumy = 0.0f;
|
||||
float wsum = 0.0f;
|
||||
|
||||
for (int cy = ymin; cy <= ymax; ++cy)
|
||||
{
|
||||
for (int cx = xmin; cx <= xmax; ++cx)
|
||||
{
|
||||
const float w = bicubicCoeff(wx - cx) * bicubicCoeff(wy - cy);
|
||||
sum += w * I1(cy, cx);
|
||||
sumx += w * I1x(cy, cx);
|
||||
sumy += w * I1y(cy, cx);
|
||||
wsum += w;
|
||||
}
|
||||
}
|
||||
|
||||
const float coeff = 1.0f / wsum;
|
||||
|
||||
const float I1wVal = sum * coeff;
|
||||
const float I1wxVal = sumx * coeff;
|
||||
const float I1wyVal = sumy * coeff;
|
||||
|
||||
I1w(y, x) = I1wVal;
|
||||
I1wx(y, x) = I1wxVal;
|
||||
I1wy(y, x) = I1wyVal;
|
||||
|
||||
const float Ix2 = I1wxVal * I1wxVal;
|
||||
const float Iy2 = I1wyVal * I1wyVal;
|
||||
|
||||
// store the |Grad(I1)|^2
|
||||
grad(y, x) = Ix2 + Iy2;
|
||||
|
||||
// compute the constant part of the rho function
|
||||
const float I0Val = I0(y, x);
|
||||
rho(y, x) = I1wVal - I1wxVal * u1Val - I1wyVal * u2Val - I0Val;
|
||||
}
|
||||
|
||||
void warpBackward(PtrStepSzf I0, PtrStepSzf I1, PtrStepSzf I1x, PtrStepSzf I1y,
|
||||
PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf I1w, PtrStepSzf I1wx,
|
||||
PtrStepSzf I1wy, PtrStepSzf grad, PtrStepSzf rho,
|
||||
cudaStream_t stream)
|
||||
{
|
||||
cv::cudev::Texture<float> texI1(I1);
|
||||
cv::cudev::Texture<float> texI1x(I1x);
|
||||
cv::cudev::Texture<float> texI1y(I1y);
|
||||
const dim3 block(32, 8);
|
||||
const dim3 grid(divUp(I0.cols, block.x), divUp(I0.rows, block.y));
|
||||
warpBackwardKernel<<<grid, block, 0, stream>>>(I0, texI1, texI1x, texI1y , u1, u2, I1w, I1wx, I1wy, grad, rho);
|
||||
if (!stream)
|
||||
cudaSafeCall(cudaDeviceSynchronize());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// estimateU
|
||||
|
||||
namespace tvl1flow
|
||||
{
|
||||
__device__ float divergence(const PtrStepf& v1, const PtrStepf& v2, int y, int x)
|
||||
{
|
||||
if (x > 0 && y > 0)
|
||||
{
|
||||
const float v1x = v1(y, x) - v1(y, x - 1);
|
||||
const float v2y = v2(y, x) - v2(y - 1, x);
|
||||
return v1x + v2y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y > 0)
|
||||
return v1(y, 0) + v2(y, 0) - v2(y - 1, 0);
|
||||
else
|
||||
{
|
||||
if (x > 0)
|
||||
return v1(0, x) - v1(0, x - 1) + v2(0, x);
|
||||
else
|
||||
return v1(0, 0) + v2(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void estimateUKernel(const PtrStepSzf I1wx, const PtrStepf I1wy,
|
||||
const PtrStepf grad, const PtrStepf rho_c,
|
||||
const PtrStepf p11, const PtrStepf p12,
|
||||
const PtrStepf p21, const PtrStepf p22,
|
||||
const PtrStepf p31, const PtrStepf p32,
|
||||
PtrStepf u1, PtrStepf u2, PtrStepf u3, PtrStepf error,
|
||||
const float l_t, const float theta, const float gamma, const bool calcError)
|
||||
{
|
||||
const int x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (x >= I1wx.cols || y >= I1wx.rows)
|
||||
return;
|
||||
|
||||
const float I1wxVal = I1wx(y, x);
|
||||
const float I1wyVal = I1wy(y, x);
|
||||
const float gradVal = grad(y, x);
|
||||
const float u1OldVal = u1(y, x);
|
||||
const float u2OldVal = u2(y, x);
|
||||
const float u3OldVal = gamma ? u3(y, x) : 0;
|
||||
|
||||
const float rho = rho_c(y, x) + (I1wxVal * u1OldVal + I1wyVal * u2OldVal + gamma * u3OldVal);
|
||||
|
||||
// estimate the values of the variable (v1, v2) (thresholding operator TH)
|
||||
|
||||
float d1 = 0.0f;
|
||||
float d2 = 0.0f;
|
||||
float d3 = 0.0f;
|
||||
|
||||
if (rho < -l_t * gradVal)
|
||||
{
|
||||
d1 = l_t * I1wxVal;
|
||||
d2 = l_t * I1wyVal;
|
||||
if (gamma)
|
||||
d3 = l_t * gamma;
|
||||
}
|
||||
else if (rho > l_t * gradVal)
|
||||
{
|
||||
d1 = -l_t * I1wxVal;
|
||||
d2 = -l_t * I1wyVal;
|
||||
if (gamma)
|
||||
d3 = -l_t * gamma;
|
||||
}
|
||||
else if (gradVal > numeric_limits<float>::epsilon())
|
||||
{
|
||||
const float fi = -rho / gradVal;
|
||||
d1 = fi * I1wxVal;
|
||||
d2 = fi * I1wyVal;
|
||||
if (gamma)
|
||||
d3 = fi * gamma;
|
||||
}
|
||||
|
||||
const float v1 = u1OldVal + d1;
|
||||
const float v2 = u2OldVal + d2;
|
||||
const float v3 = u3OldVal + d3;
|
||||
|
||||
// compute the divergence of the dual variable (p1, p2)
|
||||
|
||||
const float div_p1 = divergence(p11, p12, y, x);
|
||||
const float div_p2 = divergence(p21, p22, y, x);
|
||||
const float div_p3 = gamma ? divergence(p31, p32, y, x) : 0;
|
||||
|
||||
// estimate the values of the optical flow (u1, u2)
|
||||
|
||||
const float u1NewVal = v1 + theta * div_p1;
|
||||
const float u2NewVal = v2 + theta * div_p2;
|
||||
const float u3NewVal = gamma ? v3 + theta * div_p3 : 0;
|
||||
|
||||
u1(y, x) = u1NewVal;
|
||||
u2(y, x) = u2NewVal;
|
||||
if (gamma)
|
||||
u3(y, x) = u3NewVal;
|
||||
|
||||
if (calcError)
|
||||
{
|
||||
const float n1 = (u1OldVal - u1NewVal) * (u1OldVal - u1NewVal);
|
||||
const float n2 = (u2OldVal - u2NewVal) * (u2OldVal - u2NewVal);
|
||||
error(y, x) = n1 + n2;
|
||||
}
|
||||
}
|
||||
|
||||
void estimateU(PtrStepSzf I1wx, PtrStepSzf I1wy,
|
||||
PtrStepSzf grad, PtrStepSzf rho_c,
|
||||
PtrStepSzf p11, PtrStepSzf p12, PtrStepSzf p21, PtrStepSzf p22, PtrStepSzf p31, PtrStepSzf p32,
|
||||
PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf u3, PtrStepSzf error,
|
||||
float l_t, float theta, float gamma, bool calcError,
|
||||
cudaStream_t stream)
|
||||
{
|
||||
const dim3 block(32, 8);
|
||||
const dim3 grid(divUp(I1wx.cols, block.x), divUp(I1wx.rows, block.y));
|
||||
|
||||
estimateUKernel<<<grid, block, 0, stream>>>(I1wx, I1wy, grad, rho_c, p11, p12, p21, p22, p31, p32, u1, u2, u3, error, l_t, theta, gamma, calcError);
|
||||
cudaSafeCall( cudaGetLastError() );
|
||||
|
||||
if (!stream)
|
||||
cudaSafeCall( cudaDeviceSynchronize() );
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// estimateDualVariables
|
||||
|
||||
namespace tvl1flow
|
||||
{
|
||||
__global__ void estimateDualVariablesKernel(const PtrStepSzf u1, const PtrStepf u2, const PtrStepSzf u3,
|
||||
PtrStepf p11, PtrStepf p12, PtrStepf p21, PtrStepf p22, PtrStepf p31, PtrStepf p32, const float taut, const float gamma)
|
||||
{
|
||||
const int x = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
const int y = blockIdx.y * blockDim.y + threadIdx.y;
|
||||
|
||||
if (x >= u1.cols || y >= u1.rows)
|
||||
return;
|
||||
|
||||
const float u1x = u1(y, ::min(x + 1, u1.cols - 1)) - u1(y, x);
|
||||
const float u1y = u1(::min(y + 1, u1.rows - 1), x) - u1(y, x);
|
||||
|
||||
const float u2x = u2(y, ::min(x + 1, u1.cols - 1)) - u2(y, x);
|
||||
const float u2y = u2(::min(y + 1, u1.rows - 1), x) - u2(y, x);
|
||||
|
||||
const float u3x = gamma ? u3(y, ::min(x + 1, u1.cols - 1)) - u3(y, x) : 0;
|
||||
const float u3y = gamma ? u3(::min(y + 1, u1.rows - 1), x) - u3(y, x) : 0;
|
||||
|
||||
const float g1 = ::hypotf(u1x, u1y);
|
||||
const float g2 = ::hypotf(u2x, u2y);
|
||||
const float g3 = gamma ? ::hypotf(u3x, u3y) : 0;
|
||||
|
||||
const float ng1 = 1.0f + taut * g1;
|
||||
const float ng2 = 1.0f + taut * g2;
|
||||
const float ng3 = gamma ? 1.0f + taut * g3 : 0;
|
||||
|
||||
p11(y, x) = (p11(y, x) + taut * u1x) / ng1;
|
||||
p12(y, x) = (p12(y, x) + taut * u1y) / ng1;
|
||||
p21(y, x) = (p21(y, x) + taut * u2x) / ng2;
|
||||
p22(y, x) = (p22(y, x) + taut * u2y) / ng2;
|
||||
if (gamma)
|
||||
{
|
||||
p31(y, x) = (p31(y, x) + taut * u3x) / ng3;
|
||||
p32(y, x) = (p32(y, x) + taut * u3y) / ng3;
|
||||
}
|
||||
}
|
||||
|
||||
void estimateDualVariables(PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf u3,
|
||||
PtrStepSzf p11, PtrStepSzf p12, PtrStepSzf p21, PtrStepSzf p22, PtrStepSzf p31, PtrStepSzf p32,
|
||||
float taut, float gamma,
|
||||
cudaStream_t stream)
|
||||
{
|
||||
const dim3 block(32, 8);
|
||||
const dim3 grid(divUp(u1.cols, block.x), divUp(u1.rows, block.y));
|
||||
|
||||
estimateDualVariablesKernel<<<grid, block, 0, stream>>>(u1, u2, u3, p11, p12, p21, p22, p31, p32, taut, gamma);
|
||||
cudaSafeCall( cudaGetLastError() );
|
||||
|
||||
if (!stream)
|
||||
cudaSafeCall( cudaDeviceSynchronize() );
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined CUDA_DISABLER
|
||||
@@ -0,0 +1,492 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
#if !defined HAVE_CUDA || defined(CUDA_DISABLER)
|
||||
|
||||
Ptr<cv::cuda::FarnebackOpticalFlow> cv::cuda::FarnebackOpticalFlow::create(int, double, bool, int, int, int, double, int) { throw_no_cuda(); return Ptr<FarnebackOpticalFlow>(); }
|
||||
|
||||
#else
|
||||
|
||||
#define MIN_SIZE 32
|
||||
|
||||
// CUDA resize() is fast, but it differs from the CPU analog. Disabling this flag
|
||||
// leads to an inefficient code. It's for debug purposes only.
|
||||
#define ENABLE_CUDA_RESIZE 1
|
||||
|
||||
namespace cv { namespace cuda { namespace device { namespace optflow_farneback
|
||||
{
|
||||
void setPolynomialExpansionConsts(
|
||||
int polyN, const float *g, const float *xg, const float *xxg,
|
||||
float ig11, float ig03, float ig33, float ig55);
|
||||
|
||||
void polynomialExpansionGpu(const PtrStepSzf &src, int polyN, PtrStepSzf dst, cudaStream_t stream);
|
||||
|
||||
void setUpdateMatricesConsts();
|
||||
|
||||
void updateMatricesGpu(
|
||||
const PtrStepSzf flowx, const PtrStepSzf flowy, const PtrStepSzf R0, const PtrStepSzf R1,
|
||||
PtrStepSzf M, cudaStream_t stream);
|
||||
|
||||
void updateFlowGpu(
|
||||
const PtrStepSzf M, PtrStepSzf flowx, PtrStepSzf flowy, cudaStream_t stream);
|
||||
|
||||
void boxFilter5Gpu(const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, cudaStream_t stream);
|
||||
|
||||
void boxFilter5Gpu_CC11(const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, cudaStream_t stream);
|
||||
|
||||
void setGaussianBlurKernel(const float *gKer, int ksizeHalf);
|
||||
|
||||
void gaussianBlurGpu(
|
||||
const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, int borderType, cudaStream_t stream);
|
||||
|
||||
void gaussianBlur5Gpu(
|
||||
const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, int borderType, cudaStream_t stream);
|
||||
|
||||
void gaussianBlur5Gpu_CC11(
|
||||
const PtrStepSzf src, int ksizeHalf, PtrStepSzf dst, int borderType, cudaStream_t stream);
|
||||
|
||||
}}}}
|
||||
|
||||
namespace
|
||||
{
|
||||
class FarnebackOpticalFlowImpl : public cv::cuda::FarnebackOpticalFlow
|
||||
{
|
||||
public:
|
||||
FarnebackOpticalFlowImpl(int numLevels, double pyrScale, bool fastPyramids, int winSize,
|
||||
int numIters, int polyN, double polySigma, int flags) :
|
||||
numLevels_(numLevels), pyrScale_(pyrScale), fastPyramids_(fastPyramids), winSize_(winSize),
|
||||
numIters_(numIters), polyN_(polyN), polySigma_(polySigma), flags_(flags)
|
||||
{
|
||||
}
|
||||
|
||||
virtual int getNumLevels() const { return numLevels_; }
|
||||
virtual void setNumLevels(int numLevels) { numLevels_ = numLevels; }
|
||||
|
||||
virtual double getPyrScale() const { return pyrScale_; }
|
||||
virtual void setPyrScale(double pyrScale) { pyrScale_ = pyrScale; }
|
||||
|
||||
virtual bool getFastPyramids() const { return fastPyramids_; }
|
||||
virtual void setFastPyramids(bool fastPyramids) { fastPyramids_ = fastPyramids; }
|
||||
|
||||
virtual int getWinSize() const { return winSize_; }
|
||||
virtual void setWinSize(int winSize) { winSize_ = winSize; }
|
||||
|
||||
virtual int getNumIters() const { return numIters_; }
|
||||
virtual void setNumIters(int numIters) { numIters_ = numIters; }
|
||||
|
||||
virtual int getPolyN() const { return polyN_; }
|
||||
virtual void setPolyN(int polyN) { polyN_ = polyN; }
|
||||
|
||||
virtual double getPolySigma() const { return polySigma_; }
|
||||
virtual void setPolySigma(double polySigma) { polySigma_ = polySigma; }
|
||||
|
||||
virtual int getFlags() const { return flags_; }
|
||||
virtual void setFlags(int flags) { flags_ = flags; }
|
||||
|
||||
virtual void calc(InputArray I0, InputArray I1, InputOutputArray flow, Stream& stream);
|
||||
|
||||
virtual String getDefaultName() const { return "DenseOpticalFlow.FarnebackOpticalFlow"; }
|
||||
|
||||
private:
|
||||
int numLevels_;
|
||||
double pyrScale_;
|
||||
bool fastPyramids_;
|
||||
int winSize_;
|
||||
int numIters_;
|
||||
int polyN_;
|
||||
double polySigma_;
|
||||
int flags_;
|
||||
Event sourceStreamComplete;
|
||||
|
||||
private:
|
||||
void prepareGaussian(
|
||||
int n, double sigma, float *g, float *xg, float *xxg,
|
||||
double &ig11, double &ig03, double &ig33, double &ig55);
|
||||
|
||||
void setPolynomialExpansionConsts(int n, double sigma);
|
||||
|
||||
void updateFlow_boxFilter(
|
||||
const GpuMat& R0, const GpuMat& R1, GpuMat& flowx, GpuMat &flowy,
|
||||
GpuMat& M, GpuMat &bufM, int blockSize, bool updateMatrices, Stream streams[]);
|
||||
|
||||
void updateFlow_gaussianBlur(
|
||||
const GpuMat& R0, const GpuMat& R1, GpuMat& flowx, GpuMat& flowy,
|
||||
GpuMat& M, GpuMat &bufM, int blockSize, bool updateMatrices, Stream streams[]);
|
||||
|
||||
void calcImpl(const GpuMat &frame0, const GpuMat &frame1, GpuMat &flowx, GpuMat &flowy, Stream &stream);
|
||||
|
||||
GpuMat frames_[2];
|
||||
GpuMat pyrLevel_[2], M_, bufM_, R_[2], blurredFrame_[2];
|
||||
std::vector<GpuMat> pyramid0_, pyramid1_;
|
||||
};
|
||||
|
||||
void FarnebackOpticalFlowImpl::calc(InputArray _frame0, InputArray _frame1, InputOutputArray _flow, Stream& stream)
|
||||
{
|
||||
const GpuMat frame0 = _frame0.getGpuMat();
|
||||
const GpuMat frame1 = _frame1.getGpuMat();
|
||||
GpuMat flow = _flow.getGpuMat();
|
||||
|
||||
CV_Assert(frame0.channels() == 1 && frame1.channels() == 1);
|
||||
CV_Assert(frame0.size() == frame1.size());
|
||||
|
||||
GpuMat flowx, flowy;
|
||||
|
||||
// If flag is set, check for integrity; if not set, allocate memory space
|
||||
if (flags_ & OPTFLOW_USE_INITIAL_FLOW)
|
||||
{
|
||||
CV_Assert(flow.size() == frame0.size() && flow.channels() == 2 &&
|
||||
flow.depth() == CV_32F);
|
||||
|
||||
std::vector<cuda::GpuMat> _flows(2);
|
||||
cuda::split(flow, _flows, stream);
|
||||
flowx = _flows[0];
|
||||
flowy = _flows[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
flowx.create(frame0.size(), CV_32FC1);
|
||||
flowy.create(frame0.size(), CV_32FC1);
|
||||
}
|
||||
|
||||
calcImpl(frame0, frame1, flowx, flowy, stream);
|
||||
|
||||
GpuMat flows[] = {flowx, flowy};
|
||||
cuda::merge(flows, 2, _flow, stream);
|
||||
}
|
||||
|
||||
GpuMat allocMatFromBuf(int rows, int cols, int type, GpuMat& mat)
|
||||
{
|
||||
if (!mat.empty() && mat.type() == type && mat.rows >= rows && mat.cols >= cols)
|
||||
return mat(Rect(0, 0, cols, rows));
|
||||
|
||||
return mat = GpuMat(rows, cols, type);
|
||||
}
|
||||
|
||||
void FarnebackOpticalFlowImpl::prepareGaussian(
|
||||
int n, double sigma, float *g, float *xg, float *xxg,
|
||||
double &ig11, double &ig03, double &ig33, double &ig55)
|
||||
{
|
||||
double s = 0.;
|
||||
for (int x = -n; x <= n; x++)
|
||||
{
|
||||
g[x] = (float)std::exp(-x*x/(2*sigma*sigma));
|
||||
s += g[x];
|
||||
}
|
||||
|
||||
s = 1./s;
|
||||
for (int x = -n; x <= n; x++)
|
||||
{
|
||||
g[x] = (float)(g[x]*s);
|
||||
xg[x] = (float)(x*g[x]);
|
||||
xxg[x] = (float)(x*x*g[x]);
|
||||
}
|
||||
|
||||
Mat_<double> G(6, 6);
|
||||
G.setTo(0);
|
||||
|
||||
for (int y = -n; y <= n; y++)
|
||||
{
|
||||
for (int x = -n; x <= n; x++)
|
||||
{
|
||||
G(0,0) += g[y]*g[x];
|
||||
G(1,1) += g[y]*g[x]*x*x;
|
||||
G(3,3) += g[y]*g[x]*x*x*x*x;
|
||||
G(5,5) += g[y]*g[x]*x*x*y*y;
|
||||
}
|
||||
}
|
||||
|
||||
//G[0][0] = 1.;
|
||||
G(2,2) = G(0,3) = G(0,4) = G(3,0) = G(4,0) = G(1,1);
|
||||
G(4,4) = G(3,3);
|
||||
G(3,4) = G(4,3) = G(5,5);
|
||||
|
||||
// invG:
|
||||
// [ x e e ]
|
||||
// [ y ]
|
||||
// [ y ]
|
||||
// [ e z ]
|
||||
// [ e z ]
|
||||
// [ u ]
|
||||
Mat_<double> invG = G.inv(DECOMP_CHOLESKY);
|
||||
|
||||
ig11 = invG(1,1);
|
||||
ig03 = invG(0,3);
|
||||
ig33 = invG(3,3);
|
||||
ig55 = invG(5,5);
|
||||
}
|
||||
|
||||
void FarnebackOpticalFlowImpl::setPolynomialExpansionConsts(int n, double sigma)
|
||||
{
|
||||
std::vector<float> buf(n*6 + 3);
|
||||
float* g = &buf[0] + n;
|
||||
float* xg = g + n*2 + 1;
|
||||
float* xxg = xg + n*2 + 1;
|
||||
|
||||
if (sigma < FLT_EPSILON)
|
||||
sigma = n*0.3;
|
||||
|
||||
double ig11, ig03, ig33, ig55;
|
||||
prepareGaussian(n, sigma, g, xg, xxg, ig11, ig03, ig33, ig55);
|
||||
|
||||
device::optflow_farneback::setPolynomialExpansionConsts(n, g, xg, xxg, static_cast<float>(ig11), static_cast<float>(ig03), static_cast<float>(ig33), static_cast<float>(ig55));
|
||||
}
|
||||
|
||||
void FarnebackOpticalFlowImpl::updateFlow_boxFilter(
|
||||
const GpuMat& R0, const GpuMat& R1, GpuMat& flowx, GpuMat &flowy,
|
||||
GpuMat& M, GpuMat &bufM, int blockSize, bool updateMatrices, Stream streams[])
|
||||
{
|
||||
if (deviceSupports(FEATURE_SET_COMPUTE_12))
|
||||
device::optflow_farneback::boxFilter5Gpu(M, blockSize/2, bufM, StreamAccessor::getStream(streams[0]));
|
||||
else
|
||||
device::optflow_farneback::boxFilter5Gpu_CC11(M, blockSize/2, bufM, StreamAccessor::getStream(streams[0]));
|
||||
swap(M, bufM);
|
||||
|
||||
for (int i = 1; i < 5; ++i)
|
||||
streams[i].waitForCompletion();
|
||||
device::optflow_farneback::updateFlowGpu(M, flowx, flowy, StreamAccessor::getStream(streams[0]));
|
||||
|
||||
if (updateMatrices)
|
||||
device::optflow_farneback::updateMatricesGpu(flowx, flowy, R0, R1, M, StreamAccessor::getStream(streams[0]));
|
||||
}
|
||||
|
||||
void FarnebackOpticalFlowImpl::updateFlow_gaussianBlur(
|
||||
const GpuMat& R0, const GpuMat& R1, GpuMat& flowx, GpuMat& flowy,
|
||||
GpuMat& M, GpuMat &bufM, int blockSize, bool updateMatrices, Stream streams[])
|
||||
{
|
||||
if (deviceSupports(FEATURE_SET_COMPUTE_12))
|
||||
device::optflow_farneback::gaussianBlur5Gpu(
|
||||
M, blockSize/2, bufM, BORDER_REPLICATE, StreamAccessor::getStream(streams[0]));
|
||||
else
|
||||
device::optflow_farneback::gaussianBlur5Gpu_CC11(
|
||||
M, blockSize/2, bufM, BORDER_REPLICATE, StreamAccessor::getStream(streams[0]));
|
||||
swap(M, bufM);
|
||||
|
||||
device::optflow_farneback::updateFlowGpu(M, flowx, flowy, StreamAccessor::getStream(streams[0]));
|
||||
|
||||
if (updateMatrices)
|
||||
device::optflow_farneback::updateMatricesGpu(flowx, flowy, R0, R1, M, StreamAccessor::getStream(streams[0]));
|
||||
}
|
||||
|
||||
void FarnebackOpticalFlowImpl::calcImpl(const GpuMat &frame0, const GpuMat &frame1, GpuMat &flowx, GpuMat &flowy, Stream &stream)
|
||||
{
|
||||
CV_Assert(polyN_ == 5 || polyN_ == 7);
|
||||
CV_Assert(!fastPyramids_ || std::abs(pyrScale_ - 0.5) < 1e-6);
|
||||
|
||||
Stream streams[5];
|
||||
if (stream)
|
||||
{
|
||||
streams[0] = stream;
|
||||
sourceStreamComplete.record();
|
||||
}
|
||||
|
||||
Size size = frame0.size();
|
||||
GpuMat prevFlowX, prevFlowY, curFlowX, curFlowY;
|
||||
|
||||
GpuMat flowx0 = flowx;
|
||||
GpuMat flowy0 = flowy;
|
||||
|
||||
// Crop unnecessary levels
|
||||
double scale = 1;
|
||||
int numLevelsCropped = 0;
|
||||
for (; numLevelsCropped < numLevels_; numLevelsCropped++)
|
||||
{
|
||||
scale *= pyrScale_;
|
||||
if (size.width*scale < MIN_SIZE || size.height*scale < MIN_SIZE)
|
||||
break;
|
||||
}
|
||||
|
||||
frame0.convertTo(frames_[0], CV_32F, streams[0]);
|
||||
if (stream)
|
||||
streams[1].waitEvent(sourceStreamComplete);
|
||||
frame1.convertTo(frames_[1], CV_32F, streams[1]);
|
||||
|
||||
if (fastPyramids_)
|
||||
{
|
||||
// Build Gaussian pyramids using pyrDown()
|
||||
pyramid0_.resize(numLevelsCropped + 1);
|
||||
pyramid1_.resize(numLevelsCropped + 1);
|
||||
pyramid0_[0] = frames_[0];
|
||||
pyramid1_[0] = frames_[1];
|
||||
for (int i = 1; i <= numLevelsCropped; ++i)
|
||||
{
|
||||
cuda::pyrDown(pyramid0_[i - 1], pyramid0_[i], streams[0]);
|
||||
cuda::pyrDown(pyramid1_[i - 1], pyramid1_[i], streams[1]);
|
||||
}
|
||||
}
|
||||
|
||||
setPolynomialExpansionConsts(polyN_, polySigma_);
|
||||
device::optflow_farneback::setUpdateMatricesConsts();
|
||||
|
||||
for (int k = numLevelsCropped; k >= 0; k--)
|
||||
{
|
||||
streams[0].waitForCompletion();
|
||||
|
||||
scale = 1;
|
||||
for (int i = 0; i < k; i++)
|
||||
scale *= pyrScale_;
|
||||
|
||||
double sigma = (1./scale - 1) * 0.5;
|
||||
int smoothSize = cvRound(sigma*5) | 1;
|
||||
smoothSize = std::max(smoothSize, 3);
|
||||
|
||||
int width = cvRound(size.width*scale);
|
||||
int height = cvRound(size.height*scale);
|
||||
|
||||
if (fastPyramids_)
|
||||
{
|
||||
width = pyramid0_[k].cols;
|
||||
height = pyramid0_[k].rows;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
{
|
||||
curFlowX.create(height, width, CV_32F);
|
||||
curFlowY.create(height, width, CV_32F);
|
||||
}
|
||||
else
|
||||
{
|
||||
curFlowX = flowx0;
|
||||
curFlowY = flowy0;
|
||||
}
|
||||
|
||||
if (!prevFlowX.data)
|
||||
{
|
||||
if (flags_ & OPTFLOW_USE_INITIAL_FLOW)
|
||||
{
|
||||
cuda::resize(flowx0, curFlowX, Size(width, height), 0, 0, INTER_LINEAR, streams[0]);
|
||||
cuda::resize(flowy0, curFlowY, Size(width, height), 0, 0, INTER_LINEAR, streams[1]);
|
||||
curFlowX.convertTo(curFlowX, curFlowX.depth(), scale, streams[0]);
|
||||
curFlowY.convertTo(curFlowY, curFlowY.depth(), scale, streams[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
curFlowX.setTo(0, streams[0]);
|
||||
curFlowY.setTo(0, streams[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cuda::resize(prevFlowX, curFlowX, Size(width, height), 0, 0, INTER_LINEAR, streams[0]);
|
||||
cuda::resize(prevFlowY, curFlowY, Size(width, height), 0, 0, INTER_LINEAR, streams[1]);
|
||||
curFlowX.convertTo(curFlowX, curFlowX.depth(), 1./pyrScale_, streams[0]);
|
||||
curFlowY.convertTo(curFlowY, curFlowY.depth(), 1./pyrScale_, streams[1]);
|
||||
}
|
||||
|
||||
GpuMat M = allocMatFromBuf(5*height, width, CV_32F, M_);
|
||||
GpuMat bufM = allocMatFromBuf(5*height, width, CV_32F, bufM_);
|
||||
GpuMat R[2] =
|
||||
{
|
||||
allocMatFromBuf(5*height, width, CV_32F, R_[0]),
|
||||
allocMatFromBuf(5*height, width, CV_32F, R_[1])
|
||||
};
|
||||
|
||||
if (fastPyramids_)
|
||||
{
|
||||
device::optflow_farneback::polynomialExpansionGpu(pyramid0_[k], polyN_, R[0], StreamAccessor::getStream(streams[0]));
|
||||
device::optflow_farneback::polynomialExpansionGpu(pyramid1_[k], polyN_, R[1], StreamAccessor::getStream(streams[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
GpuMat blurredFrame[2] =
|
||||
{
|
||||
allocMatFromBuf(size.height, size.width, CV_32F, blurredFrame_[0]),
|
||||
allocMatFromBuf(size.height, size.width, CV_32F, blurredFrame_[1])
|
||||
};
|
||||
GpuMat pyrLevel[2] =
|
||||
{
|
||||
allocMatFromBuf(height, width, CV_32F, pyrLevel_[0]),
|
||||
allocMatFromBuf(height, width, CV_32F, pyrLevel_[1])
|
||||
};
|
||||
|
||||
Mat g = getGaussianKernel(smoothSize, sigma, CV_32F);
|
||||
device::optflow_farneback::setGaussianBlurKernel(g.ptr<float>(smoothSize/2), smoothSize/2);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
device::optflow_farneback::gaussianBlurGpu(
|
||||
frames_[i], smoothSize/2, blurredFrame[i], BORDER_REFLECT101, StreamAccessor::getStream(streams[i]));
|
||||
cuda::resize(blurredFrame[i], pyrLevel[i], Size(width, height), 0.0, 0.0, INTER_LINEAR, streams[i]);
|
||||
device::optflow_farneback::polynomialExpansionGpu(pyrLevel[i], polyN_, R[i], StreamAccessor::getStream(streams[i]));
|
||||
}
|
||||
}
|
||||
|
||||
streams[1].waitForCompletion();
|
||||
device::optflow_farneback::updateMatricesGpu(curFlowX, curFlowY, R[0], R[1], M, StreamAccessor::getStream(streams[0]));
|
||||
|
||||
if (flags_ & OPTFLOW_FARNEBACK_GAUSSIAN)
|
||||
{
|
||||
Mat g = getGaussianKernel(winSize_, winSize_/2*0.3f, CV_32F);
|
||||
device::optflow_farneback::setGaussianBlurKernel(g.ptr<float>(winSize_/2), winSize_/2);
|
||||
}
|
||||
for (int i = 0; i < numIters_; i++)
|
||||
{
|
||||
if (flags_ & OPTFLOW_FARNEBACK_GAUSSIAN)
|
||||
updateFlow_gaussianBlur(R[0], R[1], curFlowX, curFlowY, M, bufM, winSize_, i < numIters_-1, streams);
|
||||
else
|
||||
updateFlow_boxFilter(R[0], R[1], curFlowX, curFlowY, M, bufM, winSize_, i < numIters_-1, streams);
|
||||
}
|
||||
|
||||
prevFlowX = curFlowX;
|
||||
prevFlowY = curFlowY;
|
||||
}
|
||||
|
||||
flowx = curFlowX;
|
||||
flowy = curFlowY;
|
||||
|
||||
if (!stream)
|
||||
streams[0].waitForCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<cv::cuda::FarnebackOpticalFlow> cv::cuda::FarnebackOpticalFlow::create(int numLevels, double pyrScale, bool fastPyramids, int winSize,
|
||||
int numIters, int polyN, double polySigma, int flags)
|
||||
{
|
||||
return makePtr<FarnebackOpticalFlowImpl>(numLevels, pyrScale, fastPyramids, winSize,
|
||||
numIters, polyN, polySigma, flags);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_PRECOMP_H__
|
||||
#define __OPENCV_PRECOMP_H__
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "opencv2/cudaoptflow.hpp"
|
||||
#include "opencv2/cudaarithm.hpp"
|
||||
#include "opencv2/cudawarping.hpp"
|
||||
#include "opencv2/cudaimgproc.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
|
||||
#include "opencv2/core/private.cuda.hpp"
|
||||
#if defined HAVE_CUDA
|
||||
#include "opencv2/core/cuda/vec_traits.hpp"
|
||||
#endif
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCV_CUDALEGACY
|
||||
# include "opencv2/cudalegacy/private.hpp"
|
||||
#endif
|
||||
|
||||
#endif /* __OPENCV_PRECOMP_H__ */
|
||||
@@ -0,0 +1,408 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER)
|
||||
|
||||
Ptr<cv::cuda::SparsePyrLKOpticalFlow> cv::cuda::SparsePyrLKOpticalFlow::create(Size, int, int, bool) { throw_no_cuda(); return Ptr<SparsePyrLKOpticalFlow>(); }
|
||||
|
||||
Ptr<cv::cuda::DensePyrLKOpticalFlow> cv::cuda::DensePyrLKOpticalFlow::create(Size, int, int, bool) { throw_no_cuda(); return Ptr<DensePyrLKOpticalFlow>(); }
|
||||
|
||||
#else /* !defined (HAVE_CUDA) */
|
||||
|
||||
namespace pyrlk
|
||||
{
|
||||
void loadConstants(int* winSize, int iters, cudaStream_t stream);
|
||||
void loadWinSize(int* winSize, int* halfWinSize, cudaStream_t stream);
|
||||
void loadIters(int* iters, cudaStream_t stream);
|
||||
template<typename T, int cn> struct pyrLK_caller
|
||||
{
|
||||
static void sparse(PtrStepSz<typename device::TypeVec<T, cn>::vec_type> I, PtrStepSz<typename device::TypeVec<T, cn>::vec_type> J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, dim3 patch, cudaStream_t stream);
|
||||
|
||||
static void dense(PtrStepSzf I, PtrStepSzf J, PtrStepSzf u, PtrStepSzf v, PtrStepSzf prevU, PtrStepSzf prevV,
|
||||
PtrStepSzf err, int2 winSize, cudaStream_t stream);
|
||||
};
|
||||
|
||||
template<typename T, int cn> void dispatcher(GpuMat I, GpuMat J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, dim3 patch, cudaStream_t stream)
|
||||
{
|
||||
pyrLK_caller<T, cn>::sparse(I, J, prevPts, nextPts, status, err, ptcount, level, block, patch, stream);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class PyrLKOpticalFlowBase
|
||||
{
|
||||
public:
|
||||
PyrLKOpticalFlowBase(Size winSize, int maxLevel, int iters, bool useInitialFlow);
|
||||
|
||||
void sparse(const GpuMat& prevImg, const GpuMat& nextImg, const GpuMat& prevPts, GpuMat& nextPts,
|
||||
GpuMat& status, GpuMat* err, Stream& stream);
|
||||
|
||||
void sparse(std::vector<GpuMat>& prevPyr, std::vector<GpuMat>& nextPyr, const GpuMat& prevPts, GpuMat& nextPts,
|
||||
GpuMat& status, GpuMat* err, Stream& stream);
|
||||
|
||||
void dense(const GpuMat& prevImg, const GpuMat& nextImg, GpuMat& u, GpuMat& v, Stream& stream);
|
||||
|
||||
protected:
|
||||
int winSize_[2];
|
||||
int halfWinSize_[2];
|
||||
int maxLevel_;
|
||||
int iters_;
|
||||
bool useInitialFlow_;
|
||||
void buildImagePyramid(const GpuMat& prevImg, std::vector<GpuMat>& prevPyr, const GpuMat& nextImg, std::vector<GpuMat>& nextPyr, Stream stream);
|
||||
private:
|
||||
friend class SparsePyrLKOpticalFlowImpl;
|
||||
std::vector<GpuMat> prevPyr_;
|
||||
std::vector<GpuMat> nextPyr_;
|
||||
};
|
||||
|
||||
PyrLKOpticalFlowBase::PyrLKOpticalFlowBase(Size winSize, int maxLevel, int iters, bool useInitialFlow) :
|
||||
maxLevel_(maxLevel), iters_(iters), useInitialFlow_(useInitialFlow)
|
||||
{
|
||||
winSize_[0] = winSize.width;
|
||||
winSize_[1] = winSize.height;
|
||||
halfWinSize_[0] = (winSize.width - 1) / 2;
|
||||
halfWinSize_[1] = (winSize.height - 1) / 2;
|
||||
pyrlk::loadWinSize(winSize_, halfWinSize_, 0);
|
||||
pyrlk::loadIters(&iters_, 0);
|
||||
}
|
||||
|
||||
void calcPatchSize(Size winSize, dim3& block, dim3& patch)
|
||||
{
|
||||
if (winSize.width > 32 && winSize.width > 2 * winSize.height)
|
||||
{
|
||||
block.x = deviceSupports(FEATURE_SET_COMPUTE_12) ? 32 : 16;
|
||||
block.y = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
block.x = 16;
|
||||
block.y = deviceSupports(FEATURE_SET_COMPUTE_12) ? 16 : 8;
|
||||
}
|
||||
|
||||
patch.x = (winSize.width + block.x - 1) / block.x;
|
||||
patch.y = (winSize.height + block.y - 1) / block.y;
|
||||
|
||||
block.z = patch.z = 1;
|
||||
}
|
||||
|
||||
void PyrLKOpticalFlowBase::buildImagePyramid(const GpuMat& prevImg, std::vector<GpuMat>& prevPyr, const GpuMat& nextImg, std::vector<GpuMat>& nextPyr, Stream stream)
|
||||
{
|
||||
prevPyr.resize(maxLevel_ + 1);
|
||||
nextPyr.resize(maxLevel_ + 1);
|
||||
|
||||
int cn = prevImg.channels();
|
||||
|
||||
CV_Assert(cn == 1 || cn == 3 || cn == 4);
|
||||
|
||||
prevPyr[0] = prevImg;
|
||||
nextPyr[0] = nextImg;
|
||||
|
||||
for (int level = 1; level <= maxLevel_; ++level)
|
||||
{
|
||||
cuda::pyrDown(prevPyr[level - 1], prevPyr[level], stream);
|
||||
cuda::pyrDown(nextPyr[level - 1], nextPyr[level], stream);
|
||||
}
|
||||
}
|
||||
void PyrLKOpticalFlowBase::sparse(std::vector<GpuMat>& prevPyr, std::vector<GpuMat>& nextPyr, const GpuMat& prevPts, GpuMat& nextPts,
|
||||
GpuMat& status, GpuMat* err, Stream& stream)
|
||||
{
|
||||
CV_Assert(prevPyr.size() && nextPyr.size() && "Pyramid needs to at least contain the original matrix as the first element");
|
||||
CV_Assert(prevPyr[0].size() == nextPyr[0].size());
|
||||
CV_Assert(prevPts.rows == 1 && prevPts.type() == CV_32FC2);
|
||||
CV_Assert(maxLevel_ >= 0);
|
||||
CV_Assert(winSize_[0] > 2 && winSize_[1] > 2);
|
||||
if (useInitialFlow_)
|
||||
CV_Assert(nextPts.size() == prevPts.size() && nextPts.type() == prevPts.type());
|
||||
else
|
||||
ensureSizeIsEnough(1, prevPts.cols, prevPts.type(), nextPts);
|
||||
|
||||
GpuMat temp1 = (useInitialFlow_ ? nextPts : prevPts).reshape(1);
|
||||
GpuMat temp2 = nextPts.reshape(1);
|
||||
cuda::multiply(temp1, Scalar::all(1.0 / (1 << maxLevel_) / 2.0), temp2, 1, -1, stream);
|
||||
|
||||
|
||||
ensureSizeIsEnough(1, prevPts.cols, CV_8UC1, status);
|
||||
status.setTo(Scalar::all(1), stream);
|
||||
|
||||
if (err)
|
||||
ensureSizeIsEnough(1, prevPts.cols, CV_32FC1, *err);
|
||||
|
||||
if (prevPyr.size() != size_t(maxLevel_ + 1) || nextPyr.size() != size_t(maxLevel_ + 1))
|
||||
{
|
||||
buildImagePyramid(prevPyr[0], prevPyr, nextPyr[0], nextPyr, stream);
|
||||
}
|
||||
|
||||
dim3 block, patch;
|
||||
calcPatchSize(Size(winSize_[0], winSize_[1]), block, patch);
|
||||
CV_Assert(patch.x > 0 && patch.x < 6 && patch.y > 0 && patch.y < 6);
|
||||
cudaStream_t stream_ = StreamAccessor::getStream(stream);
|
||||
pyrlk::loadWinSize(winSize_, halfWinSize_, stream_);
|
||||
pyrlk::loadIters(&iters_, stream_);
|
||||
|
||||
const int cn = prevPyr[0].channels();
|
||||
const int type = prevPyr[0].depth();
|
||||
|
||||
typedef void(*func_t)(GpuMat I, GpuMat J, const float2* prevPts, float2* nextPts, uchar* status, float* err, int ptcount,
|
||||
int level, dim3 block, dim3 patch, cudaStream_t stream);
|
||||
|
||||
// Current int datatype is disabled due to pyrDown not implementing it
|
||||
// while ushort does work, it has significantly worse performance, and thus doesn't pass accuracy tests.
|
||||
static const func_t funcs[6][4] =
|
||||
{
|
||||
{ pyrlk::dispatcher<uchar, 1> , /*pyrlk::dispatcher<uchar, 2>*/ 0, pyrlk::dispatcher<uchar, 3> , pyrlk::dispatcher<uchar, 4> },
|
||||
{ /*pyrlk::dispatcher<char, 1>*/ 0, /*pyrlk::dispatcher<char, 2>*/ 0, /*pyrlk::dispatcher<char, 3>*/ 0 , /*pyrlk::dispatcher<char, 4>*/ 0 },
|
||||
{ pyrlk::dispatcher<ushort, 1> , /*pyrlk::dispatcher<ushort, 2>*/0, pyrlk::dispatcher<ushort, 3> , pyrlk::dispatcher<ushort, 4> },
|
||||
{ /*pyrlk::dispatcher<short, 1>*/ 0, /*pyrlk::dispatcher<short, 2>*/ 0, /*pyrlk::dispatcher<short, 3>*/ 0 , /*pyrlk::dispatcher<short, 4>*/0 },
|
||||
{ pyrlk::dispatcher<int, 1> , /*pyrlk::dispatcher<int, 2>*/ 0, pyrlk::dispatcher<int, 3> , pyrlk::dispatcher<int, 4> },
|
||||
{ pyrlk::dispatcher<float, 1> , /*pyrlk::dispatcher<float, 2>*/ 0, pyrlk::dispatcher<float, 3> , pyrlk::dispatcher<float, 4> }
|
||||
};
|
||||
|
||||
func_t func = funcs[type][cn-1];
|
||||
CV_Assert(func != NULL && "Datatype not implemented");
|
||||
for (int level = maxLevel_; level >= 0; level--)
|
||||
{
|
||||
func(prevPyr[level], nextPyr[level],
|
||||
prevPts.ptr<float2>(), nextPts.ptr<float2>(),
|
||||
status.ptr(), level == 0 && err ? err->ptr<float>() : 0,
|
||||
prevPts.cols, level, block, patch,
|
||||
stream_);
|
||||
}
|
||||
}
|
||||
|
||||
void PyrLKOpticalFlowBase::sparse(const GpuMat& prevImg, const GpuMat& nextImg, const GpuMat& prevPts, GpuMat& nextPts, GpuMat& status, GpuMat* err, Stream& stream)
|
||||
{
|
||||
if (prevPts.empty())
|
||||
{
|
||||
nextPts.release();
|
||||
status.release();
|
||||
if (err) err->release();
|
||||
return;
|
||||
}
|
||||
CV_Assert( prevImg.channels() == 1 || prevImg.channels() == 3 || prevImg.channels() == 4 );
|
||||
CV_Assert( prevImg.size() == nextImg.size() && prevImg.type() == nextImg.type() );
|
||||
|
||||
// build the image pyramids.
|
||||
buildImagePyramid(prevImg, prevPyr_, nextImg, nextPyr_, stream);
|
||||
|
||||
sparse(prevPyr_, nextPyr_, prevPts, nextPts, status, err, stream);
|
||||
|
||||
}
|
||||
|
||||
void PyrLKOpticalFlowBase::dense(const GpuMat& prevImg, const GpuMat& nextImg, GpuMat& u, GpuMat& v, Stream& stream)
|
||||
{
|
||||
CV_Assert( prevImg.type() == CV_8UC1 );
|
||||
CV_Assert( prevImg.size() == nextImg.size() && prevImg.type() == nextImg.type() );
|
||||
CV_Assert( maxLevel_ >= 0 );
|
||||
CV_Assert( winSize_[0] > 2 && winSize_[1] > 2 );
|
||||
|
||||
// build the image pyramids.
|
||||
|
||||
prevPyr_.resize(maxLevel_ + 1);
|
||||
nextPyr_.resize(maxLevel_ + 1);
|
||||
|
||||
//prevPyr_[0] = prevImg;
|
||||
|
||||
prevImg.convertTo(prevPyr_[0], CV_32F, stream);
|
||||
nextImg.convertTo(nextPyr_[0], CV_32F, stream);
|
||||
|
||||
for (int level = 1; level <= maxLevel_; ++level)
|
||||
{
|
||||
cuda::pyrDown(prevPyr_[level - 1], prevPyr_[level], stream);
|
||||
cuda::pyrDown(nextPyr_[level - 1], nextPyr_[level], stream);
|
||||
}
|
||||
|
||||
BufferPool pool(stream);
|
||||
|
||||
GpuMat uPyr[] = {
|
||||
pool.getBuffer(prevImg.size(), CV_32FC1),
|
||||
pool.getBuffer(prevImg.size(), CV_32FC1),
|
||||
};
|
||||
GpuMat vPyr[] = {
|
||||
pool.getBuffer(prevImg.size(), CV_32FC1),
|
||||
pool.getBuffer(prevImg.size(), CV_32FC1),
|
||||
};
|
||||
|
||||
uPyr[0].setTo(Scalar::all(0), stream);
|
||||
vPyr[0].setTo(Scalar::all(0), stream);
|
||||
uPyr[1].setTo(Scalar::all(0), stream);
|
||||
vPyr[1].setTo(Scalar::all(0), stream);
|
||||
cudaStream_t stream_ = StreamAccessor::getStream(stream);
|
||||
pyrlk::loadWinSize(winSize_, halfWinSize_, stream_);
|
||||
pyrlk::loadIters(&iters_, stream_);
|
||||
int2 winSize2i = make_int2(winSize_[0], winSize_[1]);
|
||||
//pyrlk::loadConstants(winSize2i, iters_, StreamAccessor::getStream(stream));
|
||||
|
||||
int idx = 0;
|
||||
|
||||
for (int level = maxLevel_; level >= 0; level--)
|
||||
{
|
||||
int idx2 = (idx + 1) & 1;
|
||||
|
||||
pyrlk::pyrLK_caller<float,1>::dense(prevPyr_[level], nextPyr_[level],
|
||||
uPyr[idx], vPyr[idx], uPyr[idx2], vPyr[idx2],
|
||||
PtrStepSzf(), winSize2i,
|
||||
stream_);
|
||||
|
||||
if (level > 0)
|
||||
idx = idx2;
|
||||
}
|
||||
|
||||
uPyr[idx].copyTo(u, stream);
|
||||
vPyr[idx].copyTo(v, stream);
|
||||
}
|
||||
|
||||
class SparsePyrLKOpticalFlowImpl : public cv::cuda::SparsePyrLKOpticalFlow, private PyrLKOpticalFlowBase
|
||||
{
|
||||
public:
|
||||
SparsePyrLKOpticalFlowImpl(Size winSize, int maxLevel, int iters, bool useInitialFlow) :
|
||||
PyrLKOpticalFlowBase(winSize, maxLevel, iters, useInitialFlow)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Size getWinSize() const { return cv::Size(winSize_[0], winSize_[1]); }
|
||||
virtual void setWinSize(Size winSize) {
|
||||
winSize_[0] = winSize.width;
|
||||
winSize_[1] = winSize.height;
|
||||
halfWinSize_[0] = (winSize.width - 1) / 2;
|
||||
halfWinSize_[1] = (winSize.height -1) / 2;
|
||||
}
|
||||
|
||||
virtual int getMaxLevel() const { return maxLevel_; }
|
||||
virtual void setMaxLevel(int maxLevel) { maxLevel_ = maxLevel; }
|
||||
|
||||
virtual int getNumIters() const { return iters_; }
|
||||
virtual void setNumIters(int iters) { iters_ = iters; }
|
||||
|
||||
virtual bool getUseInitialFlow() const { return useInitialFlow_; }
|
||||
virtual void setUseInitialFlow(bool useInitialFlow) { useInitialFlow_ = useInitialFlow; }
|
||||
|
||||
virtual void calc(InputArray _prevImg, InputArray _nextImg,
|
||||
InputArray _prevPts, InputOutputArray _nextPts,
|
||||
OutputArray _status,
|
||||
OutputArray _err,
|
||||
Stream& stream)
|
||||
{
|
||||
const GpuMat prevPts = _prevPts.getGpuMat();
|
||||
GpuMat& nextPts = _nextPts.getGpuMatRef();
|
||||
GpuMat& status = _status.getGpuMatRef();
|
||||
GpuMat* err = _err.needed() ? &(_err.getGpuMatRef()) : NULL;
|
||||
if (_prevImg.kind() == _InputArray::STD_VECTOR_CUDA_GPU_MAT && _nextImg.kind() == _InputArray::STD_VECTOR_CUDA_GPU_MAT)
|
||||
{
|
||||
std::vector<GpuMat> prevPyr, nextPyr;
|
||||
_prevImg.getGpuMatVector(prevPyr);
|
||||
_nextImg.getGpuMatVector(nextPyr);
|
||||
sparse(prevPyr, nextPyr, prevPts, nextPts, status, err, stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
const GpuMat prevImg = _prevImg.getGpuMat();
|
||||
const GpuMat nextImg = _nextImg.getGpuMat();
|
||||
sparse(prevImg, nextImg, prevPts, nextPts, status, err, stream);
|
||||
}
|
||||
}
|
||||
|
||||
virtual String getDefaultName() const { return "SparseOpticalFlow.SparsePyrLKOpticalFlow"; }
|
||||
};
|
||||
|
||||
class DensePyrLKOpticalFlowImpl : public DensePyrLKOpticalFlow, private PyrLKOpticalFlowBase
|
||||
{
|
||||
public:
|
||||
DensePyrLKOpticalFlowImpl(Size winSize, int maxLevel, int iters, bool useInitialFlow) :
|
||||
PyrLKOpticalFlowBase(winSize, maxLevel, iters, useInitialFlow)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Size getWinSize() const { return cv::Size(winSize_[0], winSize_[1]); }
|
||||
virtual void setWinSize(Size winSize) {
|
||||
winSize_[0] = winSize.width;
|
||||
winSize_[1] = winSize.height;
|
||||
halfWinSize_[0] = (winSize.width - 1) / 2;
|
||||
halfWinSize_[1] = (winSize.height -1) / 2;
|
||||
}
|
||||
|
||||
virtual int getMaxLevel() const { return maxLevel_; }
|
||||
virtual void setMaxLevel(int maxLevel) { maxLevel_ = maxLevel; }
|
||||
|
||||
virtual int getNumIters() const { return iters_; }
|
||||
virtual void setNumIters(int iters) { iters_ = iters; }
|
||||
|
||||
virtual bool getUseInitialFlow() const { return useInitialFlow_; }
|
||||
virtual void setUseInitialFlow(bool useInitialFlow) { useInitialFlow_ = useInitialFlow; }
|
||||
|
||||
virtual void calc(InputArray _prevImg, InputArray _nextImg, InputOutputArray _flow, Stream& stream)
|
||||
{
|
||||
const GpuMat prevImg = _prevImg.getGpuMat();
|
||||
const GpuMat nextImg = _nextImg.getGpuMat();
|
||||
|
||||
BufferPool pool(stream);
|
||||
GpuMat u = pool.getBuffer(prevImg.size(), CV_32FC1);
|
||||
GpuMat v = pool.getBuffer(prevImg.size(), CV_32FC1);
|
||||
|
||||
dense(prevImg, nextImg, u, v, stream);
|
||||
|
||||
GpuMat flows[] = {u, v};
|
||||
cuda::merge(flows, 2, _flow, stream);
|
||||
}
|
||||
|
||||
virtual String getDefaultName() const { return "DenseOpticalFlow.DensePyrLKOpticalFlow"; }
|
||||
};
|
||||
}
|
||||
|
||||
Ptr<cv::cuda::SparsePyrLKOpticalFlow> cv::cuda::SparsePyrLKOpticalFlow::create(Size winSize, int maxLevel, int iters, bool useInitialFlow)
|
||||
{
|
||||
return makePtr<SparsePyrLKOpticalFlowImpl>(winSize, maxLevel, iters, useInitialFlow);
|
||||
}
|
||||
|
||||
Ptr<cv::cuda::DensePyrLKOpticalFlow> cv::cuda::DensePyrLKOpticalFlow::create(Size winSize, int maxLevel, int iters, bool useInitialFlow)
|
||||
{
|
||||
return makePtr<DensePyrLKOpticalFlowImpl>(winSize, maxLevel, iters, useInitialFlow);
|
||||
}
|
||||
|
||||
#endif /* !defined (HAVE_CUDA) */
|
||||
@@ -0,0 +1,393 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#if !defined HAVE_CUDA || defined(CUDA_DISABLER)
|
||||
|
||||
cv::Ptr<cv::cuda::OpticalFlowDual_TVL1> cv::cuda::OpticalFlowDual_TVL1::create(double, double, double, int, int, double, int, double, double, bool)
|
||||
{
|
||||
throw_no_cuda();
|
||||
return Ptr<cv::cuda::OpticalFlowDual_TVL1>();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::cuda;
|
||||
|
||||
namespace tvl1flow
|
||||
{
|
||||
void centeredGradient(PtrStepSzf src, PtrStepSzf dx, PtrStepSzf dy, cudaStream_t stream);
|
||||
void warpBackward(PtrStepSzf I0, PtrStepSzf I1, PtrStepSzf I1x, PtrStepSzf I1y,
|
||||
PtrStepSzf u1, PtrStepSzf u2,
|
||||
PtrStepSzf I1w, PtrStepSzf I1wx, PtrStepSzf I1wy,
|
||||
PtrStepSzf grad, PtrStepSzf rho,
|
||||
cudaStream_t stream);
|
||||
void estimateU(PtrStepSzf I1wx, PtrStepSzf I1wy,
|
||||
PtrStepSzf grad, PtrStepSzf rho_c,
|
||||
PtrStepSzf p11, PtrStepSzf p12, PtrStepSzf p21, PtrStepSzf p22, PtrStepSzf p31, PtrStepSzf p32,
|
||||
PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf u3, PtrStepSzf error,
|
||||
float l_t, float theta, float gamma, bool calcError,
|
||||
cudaStream_t stream);
|
||||
void estimateDualVariables(PtrStepSzf u1, PtrStepSzf u2, PtrStepSzf u3,
|
||||
PtrStepSzf p11, PtrStepSzf p12, PtrStepSzf p21, PtrStepSzf p22, PtrStepSzf p31, PtrStepSzf p32,
|
||||
float taut, float gamma,
|
||||
cudaStream_t stream);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class OpticalFlowDual_TVL1_Impl : public OpticalFlowDual_TVL1
|
||||
{
|
||||
public:
|
||||
OpticalFlowDual_TVL1_Impl(double tau, double lambda, double theta, int nscales, int warps, double epsilon,
|
||||
int iterations, double scaleStep, double gamma, bool useInitialFlow) :
|
||||
tau_(tau), lambda_(lambda), gamma_(gamma), theta_(theta), nscales_(nscales), warps_(warps),
|
||||
epsilon_(epsilon), iterations_(iterations), scaleStep_(scaleStep), useInitialFlow_(useInitialFlow)
|
||||
{
|
||||
}
|
||||
|
||||
virtual double getTau() const { return tau_; }
|
||||
virtual void setTau(double tau) { tau_ = tau; }
|
||||
|
||||
virtual double getLambda() const { return lambda_; }
|
||||
virtual void setLambda(double lambda) { lambda_ = lambda; }
|
||||
|
||||
virtual double getGamma() const { return gamma_; }
|
||||
virtual void setGamma(double gamma) { gamma_ = gamma; }
|
||||
|
||||
virtual double getTheta() const { return theta_; }
|
||||
virtual void setTheta(double theta) { theta_ = theta; }
|
||||
|
||||
virtual int getNumScales() const { return nscales_; }
|
||||
virtual void setNumScales(int nscales) { nscales_ = nscales; }
|
||||
|
||||
virtual int getNumWarps() const { return warps_; }
|
||||
virtual void setNumWarps(int warps) { warps_ = warps; }
|
||||
|
||||
virtual double getEpsilon() const { return epsilon_; }
|
||||
virtual void setEpsilon(double epsilon) { epsilon_ = epsilon; }
|
||||
|
||||
virtual int getNumIterations() const { return iterations_; }
|
||||
virtual void setNumIterations(int iterations) { iterations_ = iterations; }
|
||||
|
||||
virtual double getScaleStep() const { return scaleStep_; }
|
||||
virtual void setScaleStep(double scaleStep) { scaleStep_ = scaleStep; }
|
||||
|
||||
virtual bool getUseInitialFlow() const { return useInitialFlow_; }
|
||||
virtual void setUseInitialFlow(bool useInitialFlow) { useInitialFlow_ = useInitialFlow; }
|
||||
|
||||
virtual void calc(InputArray I0, InputArray I1, InputOutputArray flow, Stream& stream);
|
||||
|
||||
virtual String getDefaultName() const { return "DenseOpticalFlow.OpticalFlowDual_TVL1"; }
|
||||
|
||||
|
||||
private:
|
||||
double tau_;
|
||||
double lambda_;
|
||||
double gamma_;
|
||||
double theta_;
|
||||
int nscales_;
|
||||
int warps_;
|
||||
double epsilon_;
|
||||
int iterations_;
|
||||
double scaleStep_;
|
||||
bool useInitialFlow_;
|
||||
|
||||
private:
|
||||
void calcImpl(const GpuMat& I0, const GpuMat& I1, GpuMat& flowx, GpuMat& flowy, Stream& stream);
|
||||
void procOneScale(const GpuMat& I0, const GpuMat& I1, GpuMat& u1, GpuMat& u2, GpuMat& u3, Stream& stream);
|
||||
|
||||
std::vector<GpuMat> I0s;
|
||||
std::vector<GpuMat> I1s;
|
||||
std::vector<GpuMat> u1s;
|
||||
std::vector<GpuMat> u2s;
|
||||
std::vector<GpuMat> u3s;
|
||||
|
||||
GpuMat I1x_buf;
|
||||
GpuMat I1y_buf;
|
||||
|
||||
GpuMat I1w_buf;
|
||||
GpuMat I1wx_buf;
|
||||
GpuMat I1wy_buf;
|
||||
|
||||
GpuMat grad_buf;
|
||||
GpuMat rho_c_buf;
|
||||
|
||||
GpuMat p11_buf;
|
||||
GpuMat p12_buf;
|
||||
GpuMat p21_buf;
|
||||
GpuMat p22_buf;
|
||||
GpuMat p31_buf;
|
||||
GpuMat p32_buf;
|
||||
|
||||
GpuMat diff_buf;
|
||||
|
||||
GpuMat diff_sum_dev;
|
||||
Mat diff_sum_host;
|
||||
};
|
||||
|
||||
void OpticalFlowDual_TVL1_Impl::calc(InputArray _frame0, InputArray _frame1, InputOutputArray _flow, Stream& stream)
|
||||
{
|
||||
const GpuMat frame0 = _frame0.getGpuMat();
|
||||
const GpuMat frame1 = _frame1.getGpuMat();
|
||||
|
||||
BufferPool pool(stream);
|
||||
GpuMat flowx = pool.getBuffer(frame0.size(), CV_32FC1);
|
||||
GpuMat flowy = pool.getBuffer(frame0.size(), CV_32FC1);
|
||||
|
||||
calcImpl(frame0, frame1, flowx, flowy, stream);
|
||||
|
||||
GpuMat flows[] = {flowx, flowy};
|
||||
cuda::merge(flows, 2, _flow, stream);
|
||||
}
|
||||
|
||||
void OpticalFlowDual_TVL1_Impl::calcImpl(const GpuMat& I0, const GpuMat& I1, GpuMat& flowx, GpuMat& flowy, Stream& stream)
|
||||
{
|
||||
CV_Assert( I0.type() == CV_8UC1 || I0.type() == CV_32FC1 );
|
||||
CV_Assert( I0.size() == I1.size() );
|
||||
CV_Assert( I0.type() == I1.type() );
|
||||
CV_Assert( !useInitialFlow_ || (flowx.size() == I0.size() && flowx.type() == CV_32FC1 && flowy.size() == flowx.size() && flowy.type() == flowx.type()) );
|
||||
CV_Assert( nscales_ > 0 );
|
||||
|
||||
// allocate memory for the pyramid structure
|
||||
I0s.resize(nscales_);
|
||||
I1s.resize(nscales_);
|
||||
u1s.resize(nscales_);
|
||||
u2s.resize(nscales_);
|
||||
u3s.resize(nscales_);
|
||||
|
||||
I0.convertTo(I0s[0], CV_32F, I0.depth() == CV_8U ? 1.0 : 255.0, stream);
|
||||
I1.convertTo(I1s[0], CV_32F, I1.depth() == CV_8U ? 1.0 : 255.0, stream);
|
||||
|
||||
if (!useInitialFlow_)
|
||||
{
|
||||
flowx.create(I0.size(), CV_32FC1);
|
||||
flowy.create(I0.size(), CV_32FC1);
|
||||
}
|
||||
|
||||
u1s[0] = flowx;
|
||||
u2s[0] = flowy;
|
||||
if (gamma_)
|
||||
{
|
||||
u3s[0].create(I0.size(), CV_32FC1);
|
||||
}
|
||||
|
||||
I1x_buf.create(I0.size(), CV_32FC1);
|
||||
I1y_buf.create(I0.size(), CV_32FC1);
|
||||
|
||||
I1w_buf.create(I0.size(), CV_32FC1);
|
||||
I1wx_buf.create(I0.size(), CV_32FC1);
|
||||
I1wy_buf.create(I0.size(), CV_32FC1);
|
||||
|
||||
grad_buf.create(I0.size(), CV_32FC1);
|
||||
rho_c_buf.create(I0.size(), CV_32FC1);
|
||||
|
||||
p11_buf.create(I0.size(), CV_32FC1);
|
||||
p12_buf.create(I0.size(), CV_32FC1);
|
||||
p21_buf.create(I0.size(), CV_32FC1);
|
||||
p22_buf.create(I0.size(), CV_32FC1);
|
||||
if (gamma_)
|
||||
{
|
||||
p31_buf.create(I0.size(), CV_32FC1);
|
||||
p32_buf.create(I0.size(), CV_32FC1);
|
||||
}
|
||||
diff_buf.create(I0.size(), CV_32FC1);
|
||||
|
||||
// create the scales
|
||||
for (int s = 1; s < nscales_; ++s)
|
||||
{
|
||||
cuda::resize(I0s[s-1], I0s[s], Size(), scaleStep_, scaleStep_, INTER_LINEAR, stream);
|
||||
cuda::resize(I1s[s-1], I1s[s], Size(), scaleStep_, scaleStep_, INTER_LINEAR, stream);
|
||||
|
||||
if (I0s[s].cols < 16 || I0s[s].rows < 16)
|
||||
{
|
||||
nscales_ = s;
|
||||
break;
|
||||
}
|
||||
|
||||
if (useInitialFlow_)
|
||||
{
|
||||
cuda::resize(u1s[s-1], u1s[s], Size(), scaleStep_, scaleStep_, INTER_LINEAR, stream);
|
||||
cuda::resize(u2s[s-1], u2s[s], Size(), scaleStep_, scaleStep_, INTER_LINEAR, stream);
|
||||
|
||||
cuda::multiply(u1s[s], Scalar::all(scaleStep_), u1s[s], 1, -1, stream);
|
||||
cuda::multiply(u2s[s], Scalar::all(scaleStep_), u2s[s], 1, -1, stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
u1s[s].create(I0s[s].size(), CV_32FC1);
|
||||
u2s[s].create(I0s[s].size(), CV_32FC1);
|
||||
}
|
||||
if (gamma_)
|
||||
{
|
||||
u3s[s].create(I0s[s].size(), CV_32FC1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!useInitialFlow_)
|
||||
{
|
||||
u1s[nscales_-1].setTo(Scalar::all(0), stream);
|
||||
u2s[nscales_-1].setTo(Scalar::all(0), stream);
|
||||
}
|
||||
if (gamma_)
|
||||
{
|
||||
u3s[nscales_ - 1].setTo(Scalar::all(0), stream);
|
||||
}
|
||||
|
||||
// pyramidal structure for computing the optical flow
|
||||
for (int s = nscales_ - 1; s >= 0; --s)
|
||||
{
|
||||
// compute the optical flow at the current scale
|
||||
procOneScale(I0s[s], I1s[s], u1s[s], u2s[s], u3s[s], stream);
|
||||
|
||||
// if this was the last scale, finish now
|
||||
if (s == 0)
|
||||
break;
|
||||
|
||||
// otherwise, upsample the optical flow
|
||||
|
||||
// zoom the optical flow for the next finer scale
|
||||
cuda::resize(u1s[s], u1s[s - 1], I0s[s - 1].size(), 0, 0, INTER_LINEAR, stream);
|
||||
cuda::resize(u2s[s], u2s[s - 1], I0s[s - 1].size(), 0, 0, INTER_LINEAR, stream);
|
||||
if (gamma_)
|
||||
{
|
||||
cuda::resize(u3s[s], u3s[s - 1], I0s[s - 1].size(), 0, 0, INTER_LINEAR, stream);
|
||||
}
|
||||
|
||||
// scale the optical flow with the appropriate zoom factor
|
||||
cuda::multiply(u1s[s - 1], Scalar::all(1/scaleStep_), u1s[s - 1], 1, -1, stream);
|
||||
cuda::multiply(u2s[s - 1], Scalar::all(1/scaleStep_), u2s[s - 1], 1, -1, stream);
|
||||
}
|
||||
}
|
||||
|
||||
void OpticalFlowDual_TVL1_Impl::procOneScale(const GpuMat& I0, const GpuMat& I1, GpuMat& u1, GpuMat& u2, GpuMat& u3, Stream& _stream)
|
||||
{
|
||||
using namespace tvl1flow;
|
||||
|
||||
cudaStream_t stream = StreamAccessor::getStream(_stream);
|
||||
|
||||
const double scaledEpsilon = epsilon_ * epsilon_ * I0.size().area();
|
||||
|
||||
CV_DbgAssert( I1.size() == I0.size() );
|
||||
CV_DbgAssert( I1.type() == I0.type() );
|
||||
CV_DbgAssert( u1.size() == I0.size() );
|
||||
CV_DbgAssert( u2.size() == u1.size() );
|
||||
|
||||
GpuMat I1x = I1x_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat I1y = I1y_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
centeredGradient(I1, I1x, I1y, stream);
|
||||
|
||||
GpuMat I1w = I1w_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat I1wx = I1wx_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat I1wy = I1wy_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
|
||||
GpuMat grad = grad_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat rho_c = rho_c_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
|
||||
GpuMat p11 = p11_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat p12 = p12_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat p21 = p21_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat p22 = p22_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
GpuMat p31, p32;
|
||||
if (gamma_)
|
||||
{
|
||||
p31 = p31_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
p32 = p32_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
}
|
||||
p11.setTo(Scalar::all(0), _stream);
|
||||
p12.setTo(Scalar::all(0), _stream);
|
||||
p21.setTo(Scalar::all(0), _stream);
|
||||
p22.setTo(Scalar::all(0), _stream);
|
||||
if (gamma_)
|
||||
{
|
||||
p31.setTo(Scalar::all(0), _stream);
|
||||
p32.setTo(Scalar::all(0), _stream);
|
||||
}
|
||||
|
||||
GpuMat diff = diff_buf(Rect(0, 0, I0.cols, I0.rows));
|
||||
|
||||
const float l_t = static_cast<float>(lambda_ * theta_);
|
||||
const float taut = static_cast<float>(tau_ / theta_);
|
||||
|
||||
for (int warpings = 0; warpings < warps_; ++warpings)
|
||||
{
|
||||
warpBackward(I0, I1, I1x, I1y, u1, u2, I1w, I1wx, I1wy, grad, rho_c, stream);
|
||||
|
||||
double error = std::numeric_limits<double>::max();
|
||||
double prevError = 0.0;
|
||||
for (int n = 0; error > scaledEpsilon && n < iterations_; ++n)
|
||||
{
|
||||
// some tweaks to make sum operation less frequently
|
||||
bool calcError = (epsilon_ > 0) && (n & 0x1) && (prevError < scaledEpsilon);
|
||||
estimateU(I1wx, I1wy, grad, rho_c, p11, p12, p21, p22, p31, p32, u1, u2, u3, diff, l_t, static_cast<float>(theta_), gamma_, calcError, stream);
|
||||
if (calcError)
|
||||
{
|
||||
cuda::calcSum(diff, diff_sum_dev, cv::noArray(), _stream);
|
||||
diff_sum_dev.download(diff_sum_host, _stream);
|
||||
_stream.waitForCompletion();
|
||||
|
||||
error = diff_sum_host.at<double>(0,0);
|
||||
prevError = error;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = std::numeric_limits<double>::max();
|
||||
prevError -= scaledEpsilon;
|
||||
}
|
||||
|
||||
estimateDualVariables(u1, u2, u3, p11, p12, p21, p22, p31, p32, taut, gamma_, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<OpticalFlowDual_TVL1> cv::cuda::OpticalFlowDual_TVL1::create(
|
||||
double tau, double lambda, double theta, int nscales, int warps,
|
||||
double epsilon, int iterations, double scaleStep, double gamma, bool useInitialFlow)
|
||||
{
|
||||
return makePtr<OpticalFlowDual_TVL1_Impl>(tau, lambda, theta, nscales, warps,
|
||||
epsilon, iterations, scaleStep, gamma, useInitialFlow);
|
||||
}
|
||||
|
||||
#endif // !defined HAVE_CUDA || defined(CUDA_DISABLER)
|
||||
@@ -0,0 +1,45 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
CV_CUDA_TEST_MAIN("gpu")
|
||||
@@ -0,0 +1,707 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
#ifdef HAVE_CUDA
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// BroxOpticalFlow
|
||||
|
||||
//#define BROX_DUMP
|
||||
|
||||
struct BroxOpticalFlow : testing::TestWithParam<cv::cuda::DeviceInfo>
|
||||
{
|
||||
cv::cuda::DeviceInfo devInfo;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
devInfo = GetParam();
|
||||
|
||||
cv::cuda::setDevice(devInfo.deviceID());
|
||||
}
|
||||
};
|
||||
|
||||
CUDA_TEST_P(BroxOpticalFlow, Regression)
|
||||
{
|
||||
cv::Mat frame0 = readImageType("opticalflow/frame0.png", CV_32FC1);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImageType("opticalflow/frame1.png", CV_32FC1);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Ptr<cv::cuda::BroxOpticalFlow> brox =
|
||||
cv::cuda::BroxOpticalFlow::create(0.197 /*alpha*/, 50.0 /*gamma*/, 0.8 /*scale_factor*/,
|
||||
10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/);
|
||||
|
||||
cv::cuda::GpuMat flow;
|
||||
brox->calc(loadMat(frame0), loadMat(frame1), flow);
|
||||
|
||||
cv::cuda::GpuMat flows[2];
|
||||
cv::cuda::split(flow, flows);
|
||||
|
||||
cv::cuda::GpuMat u = flows[0];
|
||||
cv::cuda::GpuMat v = flows[1];
|
||||
|
||||
std::string fname(cvtest::TS::ptr()->get_data_path());
|
||||
if (devInfo.majorVersion() >= 2)
|
||||
fname += "opticalflow/brox_optical_flow_cc20.bin";
|
||||
else
|
||||
fname += "opticalflow/brox_optical_flow.bin";
|
||||
|
||||
#ifndef BROX_DUMP
|
||||
std::ifstream f(fname.c_str(), std::ios_base::binary);
|
||||
|
||||
int rows, cols;
|
||||
|
||||
f.read((char*) &rows, sizeof(rows));
|
||||
f.read((char*) &cols, sizeof(cols));
|
||||
|
||||
cv::Mat u_gold(rows, cols, CV_32FC1);
|
||||
|
||||
for (int i = 0; i < u_gold.rows; ++i)
|
||||
f.read(u_gold.ptr<char>(i), u_gold.cols * sizeof(float));
|
||||
|
||||
cv::Mat v_gold(rows, cols, CV_32FC1);
|
||||
|
||||
for (int i = 0; i < v_gold.rows; ++i)
|
||||
f.read(v_gold.ptr<char>(i), v_gold.cols * sizeof(float));
|
||||
|
||||
EXPECT_MAT_SIMILAR(u_gold, u, 1e-3);
|
||||
EXPECT_MAT_SIMILAR(v_gold, v, 1e-3);
|
||||
#else
|
||||
std::ofstream f(fname.c_str(), std::ios_base::binary);
|
||||
|
||||
f.write((char*) &u.rows, sizeof(u.rows));
|
||||
f.write((char*) &u.cols, sizeof(u.cols));
|
||||
|
||||
cv::Mat h_u(u);
|
||||
cv::Mat h_v(v);
|
||||
|
||||
for (int i = 0; i < u.rows; ++i)
|
||||
f.write(h_u.ptr<char>(i), u.cols * sizeof(float));
|
||||
|
||||
for (int i = 0; i < v.rows; ++i)
|
||||
f.write(h_v.ptr<char>(i), v.cols * sizeof(float));
|
||||
#endif
|
||||
}
|
||||
|
||||
CUDA_TEST_P(BroxOpticalFlow, OpticalFlowNan)
|
||||
{
|
||||
cv::Mat frame0 = readImageType("opticalflow/frame0.png", CV_32FC1);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImageType("opticalflow/frame1.png", CV_32FC1);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Mat r_frame0, r_frame1;
|
||||
cv::resize(frame0, r_frame0, cv::Size(1380,1000));
|
||||
cv::resize(frame1, r_frame1, cv::Size(1380,1000));
|
||||
|
||||
cv::Ptr<cv::cuda::BroxOpticalFlow> brox =
|
||||
cv::cuda::BroxOpticalFlow::create(0.197 /*alpha*/, 50.0 /*gamma*/, 0.8 /*scale_factor*/,
|
||||
10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/);
|
||||
|
||||
cv::cuda::GpuMat flow;
|
||||
brox->calc(loadMat(frame0), loadMat(frame1), flow);
|
||||
|
||||
cv::cuda::GpuMat flows[2];
|
||||
cv::cuda::split(flow, flows);
|
||||
|
||||
cv::cuda::GpuMat u = flows[0];
|
||||
cv::cuda::GpuMat v = flows[1];
|
||||
|
||||
cv::Mat h_u, h_v;
|
||||
u.download(h_u);
|
||||
v.download(h_v);
|
||||
|
||||
EXPECT_TRUE(cv::checkRange(h_u));
|
||||
EXPECT_TRUE(cv::checkRange(h_v));
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, BroxOpticalFlow, ALL_DEVICES);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// PyrLKOpticalFlow
|
||||
|
||||
namespace
|
||||
{
|
||||
IMPLEMENT_PARAM_CLASS(Chan, int)
|
||||
IMPLEMENT_PARAM_CLASS(DataType, int)
|
||||
}
|
||||
|
||||
PARAM_TEST_CASE(PyrLKOpticalFlow, cv::cuda::DeviceInfo, Chan, DataType)
|
||||
{
|
||||
cv::cuda::DeviceInfo devInfo;
|
||||
int channels;
|
||||
int dataType;
|
||||
virtual void SetUp()
|
||||
{
|
||||
devInfo = GET_PARAM(0);
|
||||
channels = GET_PARAM(1);
|
||||
dataType = GET_PARAM(2);
|
||||
cv::cuda::setDevice(devInfo.deviceID());
|
||||
}
|
||||
};
|
||||
|
||||
CUDA_TEST_P(PyrLKOpticalFlow, Sparse)
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/frame0.png", channels == 1 ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/frame1.png", channels == 1 ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Mat gray_frame;
|
||||
if (channels == 1)
|
||||
gray_frame = frame0;
|
||||
else
|
||||
cv::cvtColor(frame0, gray_frame, cv::COLOR_BGR2GRAY);
|
||||
|
||||
std::vector<cv::Point2f> pts;
|
||||
cv::goodFeaturesToTrack(gray_frame, pts, 1000, 0.01, 0.0);
|
||||
|
||||
cv::cuda::GpuMat d_pts;
|
||||
cv::Mat pts_mat(1, (int) pts.size(), CV_32FC2, (void*) &pts[0]);
|
||||
d_pts.upload(pts_mat);
|
||||
|
||||
cv::Ptr<cv::cuda::SparsePyrLKOpticalFlow> pyrLK =
|
||||
cv::cuda::SparsePyrLKOpticalFlow::create();
|
||||
|
||||
std::vector<cv::Point2f> nextPts_gold;
|
||||
std::vector<unsigned char> status_gold;
|
||||
cv::calcOpticalFlowPyrLK(frame0, frame1, pts, nextPts_gold, status_gold, cv::noArray());
|
||||
|
||||
|
||||
cv::cuda::GpuMat d_nextPts;
|
||||
cv::cuda::GpuMat d_status;
|
||||
cv::Mat converted0, converted1;
|
||||
if(channels == 4)
|
||||
{
|
||||
cv::cvtColor(frame0, frame0, cv::COLOR_BGR2BGRA);
|
||||
cv::cvtColor(frame1, frame1, cv::COLOR_BGR2BGRA);
|
||||
}
|
||||
frame0.convertTo(converted0, dataType);
|
||||
frame1.convertTo(converted1, dataType);
|
||||
|
||||
pyrLK->calc(loadMat(converted0), loadMat(converted1), d_pts, d_nextPts, d_status);
|
||||
|
||||
std::vector<cv::Point2f> nextPts(d_nextPts.cols);
|
||||
cv::Mat nextPts_mat(1, d_nextPts.cols, CV_32FC2, (void*)&nextPts[0]);
|
||||
d_nextPts.download(nextPts_mat);
|
||||
|
||||
std::vector<unsigned char> status(d_status.cols);
|
||||
cv::Mat status_mat(1, d_status.cols, CV_8UC1, (void*)&status[0]);
|
||||
d_status.download(status_mat);
|
||||
|
||||
ASSERT_EQ(nextPts_gold.size(), nextPts.size());
|
||||
ASSERT_EQ(status_gold.size(), status.size());
|
||||
|
||||
size_t mistmatch = 0;
|
||||
for (size_t i = 0; i < nextPts.size(); ++i)
|
||||
{
|
||||
cv::Point2i a = nextPts[i];
|
||||
cv::Point2i b = nextPts_gold[i];
|
||||
|
||||
if (status[i] != status_gold[i])
|
||||
{
|
||||
++mistmatch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status[i])
|
||||
{
|
||||
bool eq = std::abs(a.x - b.x) <= 1 && std::abs(a.y - b.y) <= 1;
|
||||
|
||||
if (!eq)
|
||||
++mistmatch;
|
||||
}
|
||||
}
|
||||
|
||||
double bad_ratio = static_cast<double>(mistmatch) / nextPts.size();
|
||||
|
||||
ASSERT_LE(bad_ratio, 0.01);
|
||||
|
||||
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, PyrLKOpticalFlow, testing::Combine(
|
||||
ALL_DEVICES,
|
||||
testing::Values(Chan(1), Chan(3), Chan(4)),
|
||||
testing::Values(DataType(CV_8U), DataType(CV_16U), DataType(CV_32S), DataType(CV_32F))));
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// FarnebackOpticalFlow
|
||||
|
||||
namespace
|
||||
{
|
||||
IMPLEMENT_PARAM_CLASS(PyrScale, double)
|
||||
IMPLEMENT_PARAM_CLASS(PolyN, int)
|
||||
CV_FLAGS(FarnebackOptFlowFlags, 0, OPTFLOW_FARNEBACK_GAUSSIAN)
|
||||
IMPLEMENT_PARAM_CLASS(UseInitFlow, bool)
|
||||
}
|
||||
|
||||
PARAM_TEST_CASE(FarnebackOpticalFlow, cv::cuda::DeviceInfo, PyrScale, PolyN, FarnebackOptFlowFlags, UseInitFlow)
|
||||
{
|
||||
cv::cuda::DeviceInfo devInfo;
|
||||
double pyrScale;
|
||||
int polyN;
|
||||
int flags;
|
||||
bool useInitFlow;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
devInfo = GET_PARAM(0);
|
||||
pyrScale = GET_PARAM(1);
|
||||
polyN = GET_PARAM(2);
|
||||
flags = GET_PARAM(3);
|
||||
useInitFlow = GET_PARAM(4);
|
||||
|
||||
cv::cuda::setDevice(devInfo.deviceID());
|
||||
}
|
||||
};
|
||||
|
||||
CUDA_TEST_P(FarnebackOpticalFlow, Accuracy)
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/rubberwhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/rubberwhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
double polySigma = polyN <= 5 ? 1.1 : 1.5;
|
||||
|
||||
cv::Ptr<cv::cuda::FarnebackOpticalFlow> farn =
|
||||
cv::cuda::FarnebackOpticalFlow::create();
|
||||
farn->setPyrScale(pyrScale);
|
||||
farn->setPolyN(polyN);
|
||||
farn->setPolySigma(polySigma);
|
||||
farn->setFlags(flags);
|
||||
|
||||
cv::cuda::GpuMat d_flow;
|
||||
farn->calc(loadMat(frame0), loadMat(frame1), d_flow);
|
||||
|
||||
cv::Mat flow;
|
||||
if (useInitFlow)
|
||||
{
|
||||
d_flow.download(flow);
|
||||
|
||||
farn->setFlags(farn->getFlags() | cv::OPTFLOW_USE_INITIAL_FLOW);
|
||||
farn->calc(loadMat(frame0), loadMat(frame1), d_flow);
|
||||
}
|
||||
|
||||
cv::calcOpticalFlowFarneback(
|
||||
frame0, frame1, flow, farn->getPyrScale(), farn->getNumLevels(), farn->getWinSize(),
|
||||
farn->getNumIters(), farn->getPolyN(), farn->getPolySigma(), farn->getFlags());
|
||||
|
||||
// Relax test limit when the flag is set
|
||||
if (farn->getFlags() & cv::OPTFLOW_FARNEBACK_GAUSSIAN)
|
||||
{
|
||||
EXPECT_MAT_SIMILAR(flow, d_flow, 2e-2);
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_MAT_SIMILAR(flow, d_flow, 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, FarnebackOpticalFlow, testing::Combine(
|
||||
ALL_DEVICES,
|
||||
testing::Values(PyrScale(0.3), PyrScale(0.5), PyrScale(0.8)),
|
||||
testing::Values(PolyN(5), PolyN(7)),
|
||||
testing::Values(FarnebackOptFlowFlags(0), FarnebackOptFlowFlags(cv::OPTFLOW_FARNEBACK_GAUSSIAN)),
|
||||
testing::Values(UseInitFlow(false), UseInitFlow(true))));
|
||||
|
||||
|
||||
PARAM_TEST_CASE(FarnebackOpticalFlowAsync, cv::cuda::DeviceInfo, PyrScale, PolyN, FarnebackOptFlowFlags)
|
||||
{
|
||||
cv::cuda::DeviceInfo devInfo;
|
||||
double pyrScale;
|
||||
int polyN;
|
||||
int flags;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
devInfo = GET_PARAM(0);
|
||||
pyrScale = GET_PARAM(1);
|
||||
polyN = GET_PARAM(2);
|
||||
flags = GET_PARAM(3);
|
||||
|
||||
cv::cuda::setDevice(devInfo.deviceID());
|
||||
}
|
||||
};
|
||||
|
||||
CUDA_TEST_P(FarnebackOpticalFlowAsync, Accuracy)
|
||||
{
|
||||
cv::Mat frame0Mat = readImage("opticalflow/rubberwhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0Mat.empty());
|
||||
|
||||
cv::Mat frame1Mat = readImage("opticalflow/rubberwhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1Mat.empty());
|
||||
|
||||
cv::Ptr<cv::cuda::FarnebackOpticalFlow> farn = cv::cuda::FarnebackOpticalFlow::create();
|
||||
farn->setPyrScale(pyrScale);
|
||||
farn->setPolyN(polyN);
|
||||
farn->setPolySigma(1.1);
|
||||
farn->setFlags(flags);
|
||||
|
||||
Stream sourceStream;
|
||||
HostMem dummyHost(4000, 4000, CV_8UC3), frame0(frame0Mat), frame1(frame1Mat);
|
||||
GpuMat d_flow, dummyDevice(dummyHost.size(), dummyHost.type()), frame0Device(frame0.size(), frame0.type()), frame1Device(frame1.size(), frame1.type());
|
||||
|
||||
// initialize and warm up CUDA kernels to ensure this doesn't occur during the test
|
||||
farn->calc(loadMat(frame0Mat), loadMat(frame1Mat), d_flow);
|
||||
d_flow.setTo(0);
|
||||
|
||||
frame0Device.upload(frame0, sourceStream);
|
||||
// place extra work in sourceStream to test internal stream synchronization by delaying the upload of frame1 that stream, see https://github.com/opencv/opencv/issues/24540
|
||||
dummyDevice.upload(dummyHost, sourceStream);
|
||||
frame1Device.upload(frame1, sourceStream);
|
||||
farn->calc(frame0Device, frame1Device, d_flow, sourceStream);
|
||||
|
||||
Mat flow;
|
||||
cv::calcOpticalFlowFarneback(
|
||||
frame0, frame1, flow, farn->getPyrScale(), farn->getNumLevels(), farn->getWinSize(),
|
||||
farn->getNumIters(), farn->getPolyN(), farn->getPolySigma(), farn->getFlags());
|
||||
EXPECT_MAT_SIMILAR(flow, d_flow, 1e-4);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, FarnebackOpticalFlowAsync, testing::Combine(
|
||||
ALL_DEVICES,
|
||||
testing::Values(PyrScale(0.3)),
|
||||
testing::Values(PolyN(5)),
|
||||
testing::Values(FarnebackOptFlowFlags(0))));
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// OpticalFlowDual_TVL1
|
||||
|
||||
namespace
|
||||
{
|
||||
IMPLEMENT_PARAM_CLASS(Gamma, double)
|
||||
}
|
||||
|
||||
PARAM_TEST_CASE(OpticalFlowDual_TVL1, cv::cuda::DeviceInfo, Gamma)
|
||||
{
|
||||
cv::cuda::DeviceInfo devInfo;
|
||||
double gamma;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
devInfo = GET_PARAM(0);
|
||||
gamma = GET_PARAM(1);
|
||||
|
||||
cv::cuda::setDevice(devInfo.deviceID());
|
||||
}
|
||||
};
|
||||
|
||||
CUDA_TEST_P(OpticalFlowDual_TVL1, Accuracy)
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/rubberwhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/rubberwhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Ptr<cv::cuda::OpticalFlowDual_TVL1> d_alg =
|
||||
cv::cuda::OpticalFlowDual_TVL1::create();
|
||||
d_alg->setNumIterations(10);
|
||||
d_alg->setGamma(gamma);
|
||||
|
||||
cv::cuda::GpuMat d_flow;
|
||||
d_alg->calc(loadMat(frame0), loadMat(frame1), d_flow);
|
||||
|
||||
cv::Ptr<cv::optflow::DualTVL1OpticalFlow> alg = cv::optflow::createOptFlow_DualTVL1();
|
||||
alg->setMedianFiltering(1);
|
||||
alg->setInnerIterations(1);
|
||||
alg->setOuterIterations(d_alg->getNumIterations());
|
||||
alg->setGamma(gamma);
|
||||
|
||||
cv::Mat flow;
|
||||
alg->calc(frame0, frame1, flow);
|
||||
|
||||
EXPECT_MAT_SIMILAR(flow, d_flow, 4e-3);
|
||||
}
|
||||
|
||||
class TVL1AsyncParallelLoopBody : public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
TVL1AsyncParallelLoopBody(const cv::cuda::GpuMat& d_img1_, const cv::cuda::GpuMat& d_img2_, cv::cuda::GpuMat* d_flow_, int iterations_, double gamma_)
|
||||
: d_img1(d_img1_), d_img2(d_img2_), d_flow(d_flow_), iterations(iterations_), gamma(gamma_) {}
|
||||
~TVL1AsyncParallelLoopBody() {}
|
||||
void operator()(const cv::Range& r) const
|
||||
{
|
||||
for (int i = r.start; i < r.end; i++) {
|
||||
cv::cuda::Stream stream;
|
||||
cv::Ptr<cv::cuda::OpticalFlowDual_TVL1> d_alg = cv::cuda::OpticalFlowDual_TVL1::create();
|
||||
d_alg->setNumIterations(iterations);
|
||||
d_alg->setGamma(gamma);
|
||||
d_alg->calc(d_img1, d_img2, d_flow[i], stream);
|
||||
stream.waitForCompletion();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
const cv::cuda::GpuMat& d_img1;
|
||||
const cv::cuda::GpuMat& d_img2;
|
||||
cv::cuda::GpuMat* d_flow;
|
||||
int iterations;
|
||||
double gamma;
|
||||
};
|
||||
|
||||
#define NUM_STREAMS 16
|
||||
|
||||
CUDA_TEST_P(OpticalFlowDual_TVL1, Async)
|
||||
{
|
||||
if (!supportFeature(devInfo, cv::cuda::FEATURE_SET_COMPUTE_30))
|
||||
{
|
||||
throw SkipTestException("CUDA device doesn't support texture objects");
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/rubberwhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/rubberwhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
const int iterations = 10;
|
||||
|
||||
// Synchronous call
|
||||
cv::Ptr<cv::cuda::OpticalFlowDual_TVL1> d_alg =
|
||||
cv::cuda::OpticalFlowDual_TVL1::create();
|
||||
d_alg->setNumIterations(iterations);
|
||||
d_alg->setGamma(gamma);
|
||||
|
||||
cv::cuda::GpuMat d_flow_gold;
|
||||
d_alg->calc(loadMat(frame0), loadMat(frame1), d_flow_gold);
|
||||
|
||||
// Asynchronous call
|
||||
cv::cuda::GpuMat d_flow[NUM_STREAMS];
|
||||
cv::parallel_for_(cv::Range(0, NUM_STREAMS), TVL1AsyncParallelLoopBody(loadMat(frame0), loadMat(frame1), d_flow, iterations, gamma));
|
||||
|
||||
// Compare the results of synchronous call and asynchronous call
|
||||
for (int i = 0; i < NUM_STREAMS; i++)
|
||||
EXPECT_MAT_NEAR(d_flow_gold, d_flow[i], 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, OpticalFlowDual_TVL1, testing::Combine(
|
||||
ALL_DEVICES,
|
||||
testing::Values(Gamma(0.0), Gamma(1.0))));
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// NvidiaOpticalFlow_1_0
|
||||
|
||||
struct NvidiaOpticalFlow_1_0 : testing::TestWithParam<cv::cuda::DeviceInfo>
|
||||
{
|
||||
cv::cuda::DeviceInfo devInfo;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
devInfo = GetParam();
|
||||
|
||||
cv::cuda::setDevice(devInfo.deviceID());
|
||||
}
|
||||
};
|
||||
|
||||
CUDA_TEST_P(NvidiaOpticalFlow_1_0, Regression)
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/frame0.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/frame1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Ptr<cv::cuda::NvidiaOpticalFlow_1_0> d_nvof;
|
||||
try
|
||||
{
|
||||
d_nvof = cv::cuda::NvidiaOpticalFlow_1_0::create(frame0.size(),
|
||||
cv::cuda::NvidiaOpticalFlow_1_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_SLOW);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if (e.code == Error::StsBadFunc || e.code == Error::StsBadArg || e.code == Error::StsNullPtr)
|
||||
throw SkipTestException("Current configuration is not supported");
|
||||
throw;
|
||||
}
|
||||
const int gridSize = d_nvof->getGridSize();
|
||||
|
||||
Mat flow, upsampledFlow;
|
||||
d_nvof->calc(loadMat(frame0), loadMat(frame1), flow);
|
||||
d_nvof->upSampler(flow, frame0.size(), gridSize, upsampledFlow);
|
||||
|
||||
std::string fname(cvtest::TS::ptr()->get_data_path());
|
||||
fname += "opticalflow/nvofGolden.flo";
|
||||
cv::Mat golden = cv::readOpticalFlow(fname.c_str());
|
||||
ASSERT_FALSE(golden.empty());
|
||||
|
||||
EXPECT_MAT_SIMILAR(golden, upsampledFlow, 1e-10);
|
||||
d_nvof->collectGarbage();
|
||||
}
|
||||
|
||||
CUDA_TEST_P(NvidiaOpticalFlow_1_0, OpticalFlowNan)
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/rubberwhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/rubberwhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Mat r_frame0, r_frame1;
|
||||
|
||||
cv::Ptr<cv::cuda::NvidiaOpticalFlow_1_0> d_nvof;
|
||||
try
|
||||
{
|
||||
d_nvof = cv::cuda::NvidiaOpticalFlow_1_0::create(frame0.size(),
|
||||
cv::cuda::NvidiaOpticalFlow_1_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_SLOW);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if (e.code == Error::StsBadFunc || e.code == Error::StsBadArg || e.code == Error::StsNullPtr)
|
||||
throw SkipTestException("Current configuration is not supported");
|
||||
throw;
|
||||
}
|
||||
|
||||
Mat flow, flowx, flowy;
|
||||
d_nvof->calc(loadMat(frame0), loadMat(frame1), flow);
|
||||
|
||||
Mat planes[] = { flowx, flowy };
|
||||
split(flow, planes);
|
||||
flowx = planes[0]; flowy = planes[1];
|
||||
|
||||
EXPECT_TRUE(cv::checkRange(flowx));
|
||||
EXPECT_TRUE(cv::checkRange(flowy));
|
||||
d_nvof->collectGarbage();
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, NvidiaOpticalFlow_1_0, ALL_DEVICES);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// NvidiaOpticalFlow_2_0
|
||||
|
||||
struct NvidiaOpticalFlow_2_0 : testing::TestWithParam<cv::cuda::DeviceInfo>
|
||||
{
|
||||
cv::cuda::DeviceInfo devInfo;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
devInfo = GetParam();
|
||||
|
||||
cv::cuda::setDevice(devInfo.deviceID());
|
||||
}
|
||||
};
|
||||
|
||||
CUDA_TEST_P(NvidiaOpticalFlow_2_0, Regression)
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/frame0.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/frame1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Ptr<cv::cuda::NvidiaOpticalFlow_2_0> d_nvof;
|
||||
try
|
||||
{
|
||||
d_nvof = cv::cuda::NvidiaOpticalFlow_2_0::create(frame0.size(),
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_SLOW);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if (e.code == Error::StsBadFunc || e.code == Error::StsBadArg || e.code == Error::StsNullPtr)
|
||||
throw SkipTestException("Current configuration is not supported");
|
||||
throw;
|
||||
}
|
||||
|
||||
Mat flow, upsampledFlow;
|
||||
d_nvof->calc(loadMat(frame0), loadMat(frame1), flow);
|
||||
d_nvof->convertToFloat(flow, upsampledFlow);
|
||||
|
||||
std::string fname(cvtest::TS::ptr()->get_data_path());
|
||||
fname += "opticalflow/nvofGolden_2.flo";
|
||||
cv::Mat golden = cv::readOpticalFlow(fname.c_str());
|
||||
ASSERT_FALSE(golden.empty());
|
||||
|
||||
EXPECT_MAT_SIMILAR(golden, upsampledFlow, 1e-10);
|
||||
}
|
||||
|
||||
CUDA_TEST_P(NvidiaOpticalFlow_2_0, OpticalFlowNan)
|
||||
{
|
||||
cv::Mat frame0 = readImage("opticalflow/rubberwhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("opticalflow/rubberwhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
cv::Mat r_frame0, r_frame1;
|
||||
|
||||
cv::Ptr<cv::cuda::NvidiaOpticalFlow_2_0> d_nvof;
|
||||
try
|
||||
{
|
||||
d_nvof = cv::cuda::NvidiaOpticalFlow_2_0::create(frame0.size(),
|
||||
cv::cuda::NvidiaOpticalFlow_2_0::NVIDIA_OF_PERF_LEVEL::NV_OF_PERF_LEVEL_SLOW);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
if (e.code == Error::StsBadFunc || e.code == Error::StsBadArg || e.code == Error::StsNullPtr)
|
||||
throw SkipTestException("Current configuration is not supported");
|
||||
throw;
|
||||
}
|
||||
|
||||
Mat flow, flowx, flowy;
|
||||
d_nvof->calc(loadMat(frame0), loadMat(frame1), flow);
|
||||
|
||||
Mat planes[] = { flowx, flowy };
|
||||
split(flow, planes);
|
||||
flowx = planes[0]; flowy = planes[1];
|
||||
|
||||
EXPECT_TRUE(cv::checkRange(flowx));
|
||||
EXPECT_TRUE(cv::checkRange(flowy));
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, NvidiaOpticalFlow_2_0, ALL_DEVICES);
|
||||
|
||||
}} // namespace
|
||||
#endif // HAVE_CUDA
|
||||
@@ -0,0 +1,57 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#ifndef __OPENCV_TEST_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/ts/cuda_test.hpp"
|
||||
|
||||
#include "opencv2/cudaoptflow.hpp"
|
||||
#include "opencv2/cudaarithm.hpp"
|
||||
#include "opencv2/cudaimgproc.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
|
||||
#include "cvconfig.h"
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user