diff --git a/modules/gpu/src/opencv2/gpu/device/transform.hpp b/modules/gpu/src/opencv2/gpu/device/transform.hpp index 79ed09439d..5ddf18edff 100644 --- a/modules/gpu/src/opencv2/gpu/device/transform.hpp +++ b/modules/gpu/src/opencv2/gpu/device/transform.hpp @@ -315,8 +315,8 @@ namespace cv { namespace gpu { - template struct TransformChooser; - template<> struct TransformChooser + template struct TransformDispatcher; + template<> struct TransformDispatcher { template static void call(const DevMem2D_& src, const DevMem2D_& dst, UnOp op, const Mask& mask, @@ -350,7 +350,7 @@ namespace cv cudaSafeCall( cudaThreadSynchronize() ); } }; - template<> struct TransformChooser + template<> struct TransformDispatcher { template static void call(const DevMem2D_& src, const DevMem2D_& dst, UnOp op, const Mask& mask, @@ -393,7 +393,7 @@ namespace cv static void transform_caller(const DevMem2D_& src, const DevMem2D_& dst, UnOp op, const Mask& mask, cudaStream_t stream = 0) { - TransformChooser::cn == 1 && device::VecTraits::cn == 1 && device::UnReadWriteTraits::shift != 1>::call(src, dst, op, mask, stream); + TransformDispatcher::cn == 1 && device::VecTraits::cn == 1 && device::UnReadWriteTraits::shift != 1>::call(src, dst, op, mask, stream); } template @@ -412,7 +412,7 @@ namespace cv static void transform_caller(const DevMem2D_& src1, const DevMem2D_& src2, const DevMem2D_& dst, BinOp op, const Mask& mask, cudaStream_t stream = 0) { - TransformChooser::cn == 1 && device::VecTraits::cn == 1 && device::VecTraits::cn == 1 && device::BinReadWriteTraits::shift != 1>::call(src1, src2, dst, op, mask, stream); + TransformDispatcher::cn == 1 && device::VecTraits::cn == 1 && device::VecTraits::cn == 1 && device::BinReadWriteTraits::shift != 1>::call(src1, src2, dst, op, mask, stream); } template diff --git a/modules/gpu/src/surf.cpp b/modules/gpu/src/surf.cpp index fc2313f24c..b5ab0d902d 100644 --- a/modules/gpu/src/surf.cpp +++ b/modules/gpu/src/surf.cpp @@ -101,7 +101,7 @@ namespace featureCounter(0), maxCounter(0) { - CV_Assert(img.type() == CV_8UC1); + CV_Assert(!img.empty() && img.type() == CV_8UC1); CV_Assert(mask.empty() || (mask.size() == img.size() && mask.type() == CV_8UC1)); CV_Assert(nOctaves > 0 && nIntervals > 2); CV_Assert(DeviceInfo().has(ATOMICS)); @@ -109,6 +109,8 @@ namespace max_features = static_cast(img.size().area() * featuresRatio); max_candidates = static_cast(1.5 * max_features); + CV_Assert(max_features > 0); + featuresBuffer.create(1, max_features, CV_32FC(6)); maxPosBuffer.create(1, max_candidates, CV_32SC4); @@ -202,7 +204,10 @@ namespace featureCounter = std::min(featureCounter, static_cast(max_features)); } - featuresBuffer.colRange(0, featureCounter).copyTo(keypoints); + if (featureCounter > 0) + featuresBuffer.colRange(0, featureCounter).copyTo(keypoints); + else + keypoints.release(); } void findOrientation(GpuMat& keypoints) @@ -252,83 +257,104 @@ int cv::gpu::SURF_GPU::descriptorSize() const void cv::gpu::SURF_GPU::uploadKeypoints(const vector& keypoints, GpuMat& keypointsGPU) { - Mat keypointsCPU(1, keypoints.size(), CV_32FC(6)); - - const KeyPoint* keypoints_ptr = &keypoints[0]; - KeyPoint_GPU* keypointsCPU_ptr = keypointsCPU.ptr(); - for (size_t i = 0; i < keypoints.size(); ++i, ++keypoints_ptr, ++keypointsCPU_ptr) + if (keypoints.empty()) + keypointsGPU.release(); + else { - const KeyPoint& kp = *keypoints_ptr; - KeyPoint_GPU& gkp = *keypointsCPU_ptr; + Mat keypointsCPU(1, keypoints.size(), CV_32FC(6)); - gkp.x = kp.pt.x; - gkp.y = kp.pt.y; + const KeyPoint* keypoints_ptr = &keypoints[0]; + KeyPoint_GPU* keypointsCPU_ptr = keypointsCPU.ptr(); + for (size_t i = 0; i < keypoints.size(); ++i, ++keypoints_ptr, ++keypointsCPU_ptr) + { + const KeyPoint& kp = *keypoints_ptr; + KeyPoint_GPU& gkp = *keypointsCPU_ptr; - gkp.size = kp.size; + gkp.x = kp.pt.x; + gkp.y = kp.pt.y; - gkp.octave = static_cast(kp.octave); - gkp.angle = kp.angle; - gkp.response = kp.response; - } + gkp.size = kp.size; - keypointsGPU.upload(keypointsCPU); + gkp.octave = static_cast(kp.octave); + gkp.angle = kp.angle; + gkp.response = kp.response; + } + + keypointsGPU.upload(keypointsCPU); + } } void cv::gpu::SURF_GPU::downloadKeypoints(const GpuMat& keypointsGPU, vector& keypoints) { - CV_Assert(keypointsGPU.type() == CV_32FC(6) && keypointsGPU.rows == 1); + if (keypointsGPU.empty()) + keypoints.clear(); + else + { + CV_Assert(keypointsGPU.type() == CV_32FC(6) && keypointsGPU.isContinuous()); - Mat keypointsCPU = keypointsGPU; - keypoints.resize(keypointsGPU.cols); + Mat keypointsCPU = keypointsGPU; + keypoints.resize(keypointsGPU.cols); - KeyPoint* keypoints_ptr = &keypoints[0]; - const KeyPoint_GPU* keypointsCPU_ptr = keypointsCPU.ptr(); - for (int i = 0; i < keypointsGPU.cols; ++i, ++keypoints_ptr, ++keypointsCPU_ptr) - { - KeyPoint& kp = *keypoints_ptr; - const KeyPoint_GPU& gkp = *keypointsCPU_ptr; + KeyPoint* keypoints_ptr = &keypoints[0]; + const KeyPoint_GPU* keypointsCPU_ptr = keypointsCPU.ptr(); + for (int i = 0; i < keypointsGPU.cols; ++i, ++keypoints_ptr, ++keypointsCPU_ptr) + { + KeyPoint& kp = *keypoints_ptr; + const KeyPoint_GPU& gkp = *keypointsCPU_ptr; - kp.pt.x = gkp.x; - kp.pt.y = gkp.y; + kp.pt.x = gkp.x; + kp.pt.y = gkp.y; - kp.size = gkp.size; + kp.size = gkp.size; - kp.octave = static_cast(gkp.octave); - kp.angle = gkp.angle; - kp.response = gkp.response; + kp.octave = static_cast(gkp.octave); + kp.angle = gkp.angle; + kp.response = gkp.response; + } } } void cv::gpu::SURF_GPU::downloadDescriptors(const GpuMat& descriptorsGPU, vector& descriptors) { - CV_Assert(descriptorsGPU.type() == CV_32F); + if (descriptorsGPU.empty()) + descriptors.clear(); + else + { + CV_Assert(descriptorsGPU.type() == CV_32F); - descriptors.resize(descriptorsGPU.rows * descriptorsGPU.cols); - Mat descriptorsCPU(descriptorsGPU.size(), CV_32F, &descriptors[0]); - descriptorsGPU.download(descriptorsCPU); + descriptors.resize(descriptorsGPU.rows * descriptorsGPU.cols); + Mat descriptorsCPU(descriptorsGPU.size(), CV_32F, &descriptors[0]); + descriptorsGPU.download(descriptorsCPU); + } } void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints) { - SURF_GPU_Invoker surf(*this, img, mask); + if (!img.empty()) + { + SURF_GPU_Invoker surf(*this, img, mask); - surf.detectKeypoints(keypoints); + surf.detectKeypoints(keypoints); - surf.findOrientation(keypoints); + surf.findOrientation(keypoints); + } } void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints, GpuMat& descriptors, bool useProvidedKeypoints, bool calcOrientation) { - SURF_GPU_Invoker surf(*this, img, mask); - - if (!useProvidedKeypoints) - surf.detectKeypoints(keypoints); - - if (calcOrientation) - surf.findOrientation(keypoints); + if (!img.empty()) + { + SURF_GPU_Invoker surf(*this, img, mask); + + if (!useProvidedKeypoints) + surf.detectKeypoints(keypoints); + + if (calcOrientation) + surf.findOrientation(keypoints); - surf.computeDescriptors(keypoints, descriptors, descriptorSize()); + surf.computeDescriptors(keypoints, descriptors, descriptorSize()); + } } void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector& keypoints) diff --git a/tests/gpu/CMakeLists.txt b/tests/gpu/CMakeLists.txt index ca0e601ef7..6e172c76ff 100644 --- a/tests/gpu/CMakeLists.txt +++ b/tests/gpu/CMakeLists.txt @@ -51,10 +51,10 @@ set_target_properties(${the_target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/" ) -add_dependencies(${the_target} opencv_ts opencv_gpu opencv_highgui opencv_imgproc) +add_dependencies(${the_target} opencv_ts opencv_gpu opencv_highgui opencv_imgproc opencv_calib3d) # Add the required libraries for linking: -target_link_libraries(${the_target} ${OPENCV_LINKER_LIBS} opencv_ts opencv_gpu opencv_highgui opencv_imgproc) +target_link_libraries(${the_target} ${OPENCV_LINKER_LIBS} opencv_ts opencv_gpu opencv_highgui opencv_imgproc opencv_calib3d) enable_testing() get_target_property(LOC ${the_target} LOCATION) diff --git a/tests/gpu/src/features2d.cpp b/tests/gpu/src/features2d.cpp new file mode 100644 index 0000000000..8a754478e9 --- /dev/null +++ b/tests/gpu/src/features2d.cpp @@ -0,0 +1,228 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "gputest.hpp" +#include + +using namespace cv; +using namespace cv::gpu; +using namespace std; + +const string FEATURES2D_DIR = "features2d"; +const string IMAGE_FILENAME = "aloe.png"; +const string VALID_FILE_NAME = "surf.xml.gz"; + +class CV_GPU_SURFTest : public CvTest +{ +public: + CV_GPU_SURFTest() : + CvTest( "GPU-SURF", "SURF_GPU") + { + } + +protected: + bool isSimilarKeypoints(const KeyPoint& p1, const KeyPoint& p2); + void compareKeypointSets(const vector& validKeypoints, const vector& calcKeypoints, + const Mat& validDescriptors, const Mat& calcDescriptors); + + void emptyDataTest(SURF_GPU& fdetector); + void regressionTest(SURF_GPU& fdetector); + + virtual void run(int); +}; + +void CV_GPU_SURFTest::emptyDataTest(SURF_GPU& fdetector) +{ + GpuMat image; + vector keypoints; + vector descriptors; + try + { + fdetector(image, GpuMat(), keypoints, descriptors); + } + catch(...) + { + ts->printf( CvTS::LOG, "detect() on empty image must not generate exception (1).\n" ); + ts->set_failed_test_info( CvTS::FAIL_INVALID_OUTPUT ); + } + + if( !keypoints.empty() ) + { + ts->printf( CvTS::LOG, "detect() on empty image must return empty keypoints vector (1).\n" ); + ts->set_failed_test_info( CvTS::FAIL_INVALID_OUTPUT ); + return; + } + + if( !descriptors.empty() ) + { + ts->printf( CvTS::LOG, "detect() on empty image must return empty descriptors vector (1).\n" ); + ts->set_failed_test_info( CvTS::FAIL_INVALID_OUTPUT ); + return; + } +} + +bool CV_GPU_SURFTest::isSimilarKeypoints(const KeyPoint& p1, const KeyPoint& p2) +{ + const float maxPtDif = 1.f; + const float maxSizeDif = 1.f; + const float maxAngleDif = 2.f; + const float maxResponseDif = 0.1f; + + float dist = (float)norm( p1.pt - p2.pt ); + return (dist < maxPtDif && + fabs(p1.size - p2.size) < maxSizeDif && + abs(p1.angle - p2.angle) < maxAngleDif && + abs(p1.response - p2.response) < maxResponseDif && + p1.octave == p2.octave && + p1.class_id == p2.class_id ); +} + +void CV_GPU_SURFTest::compareKeypointSets(const vector& validKeypoints, const vector& calcKeypoints, + const Mat& validDescriptors, const Mat& calcDescriptors) +{ + if (validKeypoints.size() != calcKeypoints.size()) + { + ts->printf(CvTS::LOG, "Keypoints sizes doesn't equal (validCount = %d, calcCount = %d).\n", + validKeypoints.size(), calcKeypoints.size()); + ts->set_failed_test_info(CvTS::FAIL_INVALID_OUTPUT); + return; + } + if (validDescriptors.size() != calcDescriptors.size()) + { + ts->printf(CvTS::LOG, "Descriptors sizes doesn't equal.\n"); + ts->set_failed_test_info(CvTS::FAIL_INVALID_OUTPUT); + return; + } + for (size_t v = 0; v < validKeypoints.size(); v++) + { + int nearestIdx = -1; + float minDist = std::numeric_limits::max(); + + for (size_t c = 0; c < calcKeypoints.size(); c++) + { + float curDist = (float)norm(calcKeypoints[c].pt - validKeypoints[v].pt); + if (curDist < minDist) + { + minDist = curDist; + nearestIdx = c; + } + } + + assert(minDist >= 0); + if (!isSimilarKeypoints(validKeypoints[v], calcKeypoints[nearestIdx])) + { + ts->set_failed_test_info( CvTS::FAIL_BAD_ACCURACY ); + return; + } + + if (norm(validDescriptors.row(v), calcDescriptors.row(nearestIdx), NORM_L2) > 1.0f) + { + ts->set_failed_test_info( CvTS::FAIL_BAD_ACCURACY ); + return; + } + } +} + +void CV_GPU_SURFTest::regressionTest(SURF_GPU& fdetector) +{ + string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; + string resFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + VALID_FILE_NAME; + + // Read the test image. + GpuMat image(imread(imgFilename, 0)); + if (image.empty()) + { + ts->printf( CvTS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); + return; + } + + FileStorage fs(resFilename, FileStorage::READ); + + // Compute keypoints. + GpuMat mask(image.size(), CV_8UC1, Scalar::all(1)); + mask(Range(0, image.rows / 2), Range(0, image.cols / 2)).setTo(Scalar::all(0)); + vector calcKeypoints; + GpuMat calcDespcriptors; + fdetector(image, mask, calcKeypoints, calcDespcriptors); + + if (fs.isOpened()) // Compare computed and valid keypoints. + { + // Read validation keypoints set. + vector validKeypoints; + Mat validDespcriptors; + read(fs["keypoints"], validKeypoints); + read(fs["descriptors"], validDespcriptors); + if (validKeypoints.empty() || validDespcriptors.empty()) + { + ts->printf(CvTS::LOG, "Validation file can not be read.\n"); + ts->set_failed_test_info(CvTS::FAIL_INVALID_TEST_DATA); + return; + } + + compareKeypointSets(validKeypoints, calcKeypoints, validDespcriptors, calcDespcriptors); + } + else // Write detector parameters and computed keypoints as validation data. + { + fs.open(resFilename, FileStorage::WRITE); + if (!fs.isOpened()) + { + ts->printf(CvTS::LOG, "File %s can not be opened to write.\n", resFilename.c_str()); + ts->set_failed_test_info(CvTS::FAIL_INVALID_TEST_DATA); + return; + } + else + { + write(fs, "keypoints", calcKeypoints); + write(fs, "descriptors", (Mat)calcDespcriptors); + } + } +} + +void CV_GPU_SURFTest::run( int /*start_from*/ ) +{ + SURF_GPU fdetector; + + emptyDataTest(fdetector); + regressionTest(fdetector); +} + +CV_GPU_SURFTest CV_GPU_SURF_test; diff --git a/tests/gpu/src/gputest.hpp b/tests/gpu/src/gputest.hpp index 360fba6ed6..50f388c533 100644 --- a/tests/gpu/src/gputest.hpp +++ b/tests/gpu/src/gputest.hpp @@ -52,6 +52,7 @@ #include #include #include +#include #include "cxts.h" /****************************************************************************************/ diff --git a/tests/gpu/src/imgproc_gpu.cpp b/tests/gpu/src/imgproc_gpu.cpp index 0414c48baf..10db183c27 100644 --- a/tests/gpu/src/imgproc_gpu.cpp +++ b/tests/gpu/src/imgproc_gpu.cpp @@ -912,6 +912,51 @@ struct CV_GpuNormTest : CvTest } }; +//////////////////////////////////////////////////////////////////////////////// +// reprojectImageTo3D +class CV_GpuReprojectImageTo3DTest : public CvTest +{ +public: + CV_GpuReprojectImageTo3DTest() : CvTest("GPU-ReprojectImageTo3D", "reprojectImageTo3D") {} + +protected: + void run(int) + { + Mat disp(320, 240, CV_8UC1); + + RNG rng(*ts->get_rng()); + rng.fill(disp, RNG::UNIFORM, Scalar(5), Scalar(30)); + + Mat Q(4, 4, CV_32FC1); + rng.fill(Q, RNG::UNIFORM, Scalar(0.1), Scalar(1)); + + Mat cpures; + GpuMat gpures; + + reprojectImageTo3D(disp, cpures, Q, false); + reprojectImageTo3D(GpuMat(disp), gpures, Q); + + Mat temp = gpures; + + for (int y = 0; y < cpures.rows; ++y) + { + const Vec3f* cpu_row = cpures.ptr(y); + const Vec4f* gpu_row = temp.ptr(y); + for (int x = 0; x < cpures.cols; ++x) + { + Vec3f a = cpu_row[x]; + Vec4f b = gpu_row[x]; + + if (fabs(a[0] - b[0]) > 1e-5 || fabs(a[1] - b[1]) > 1e-5 || fabs(a[2] - b[2]) > 1e-5) + { + ts->set_failed_test_info(CvTS::FAIL_INVALID_OUTPUT); + return; + } + } + } + } +}; + ///////////////////////////////////////////////////////////////////////////// /////////////////// tests registration ///////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -933,3 +978,4 @@ CV_GpuCornerHarrisTest CV_GpuCornerHarris_test; CV_GpuCornerMinEigenValTest CV_GpuCornerMinEigenVal_test; CV_GpuColumnSumTest CV_GpuColumnSum_test; CV_GpuNormTest CV_GpuNormTest_test; +CV_GpuReprojectImageTo3DTest CV_GpuReprojectImageTo3D_test;