From d7d6360fceb5881d596be95b03568d4dcdb7236d Mon Sep 17 00:00:00 2001 From: cudawarped Date: Wed, 29 Jan 2020 09:54:42 +0000 Subject: [PATCH] Merge pull request #2396 from cudawarped:fix_python_cudawarping_cudaarithm Add python bindings to cudaobjdetect, cudawarping and cudaarithm * Overload cudawarping functions to generate correct python bindings. Add python wrapper to convolution funciton. * Added shift and hog. * Moved cuda python tests to this repo and added python bindings to SURF. * Fix SURF documentation and allow meanshiftsegmention to create GpuMat internaly if not passed for python bindings consistency. * Add correct cuda SURF test case. * Fix python mog and mog2 python bindings, add tests and correct cudawarping documentation. * Updated KeyPoints in cuda::ORB::Convert python wrapper to be an output argument. * Add changes suggested by alalek * Added changes suggested by asmorkalov --- .../cudaarithm/include/opencv2/cudaarithm.hpp | 10 +- .../misc/python/test/test_cudaarithm.py | 178 ++++++++++++++++++ .../cudabgsegm/include/opencv2/cudabgsegm.hpp | 12 +- .../misc/python/test/test_cudabgsegm.py | 43 +++++ .../misc/python/test/test_cudacodec.py | 55 ++++++ .../include/opencv2/cudafeatures2d.hpp | 2 +- .../misc/python/test/test_cudafeatures2d.py | 47 +++++ .../misc/python/test/test_cudafilters.py | 43 +++++ .../misc/python/test/test_cudaimgproc.py | 93 +++++++++ modules/cudaimgproc/src/mssegmentation.cpp | 10 +- .../include/opencv2/cudaobjdetect.hpp | 32 ++++ .../misc/python/test/test_cudaobjdetect.py | 38 ++++ .../include/opencv2/cudawarping.hpp | 52 ++++- .../misc/python/test/test_cudawarping.py | 76 ++++++++ .../include/opencv2/xfeatures2d/cuda.hpp | 56 +++++- .../misc/python/test/test_cuda_xfeatures2d.py | 48 +++++ modules/xfeatures2d/src/surf.cuda.cpp | 8 + 17 files changed, 780 insertions(+), 23 deletions(-) create mode 100644 modules/cudaarithm/misc/python/test/test_cudaarithm.py create mode 100644 modules/cudabgsegm/misc/python/test/test_cudabgsegm.py create mode 100644 modules/cudacodec/misc/python/test/test_cudacodec.py create mode 100644 modules/cudafeatures2d/misc/python/test/test_cudafeatures2d.py create mode 100644 modules/cudafilters/misc/python/test/test_cudafilters.py create mode 100644 modules/cudaimgproc/misc/python/test/test_cudaimgproc.py create mode 100644 modules/cudaobjdetect/misc/python/test/test_cudaobjdetect.py create mode 100644 modules/cudawarping/misc/python/test/test_cudawarping.py create mode 100644 modules/xfeatures2d/misc/python/test/test_cuda_xfeatures2d.py diff --git a/modules/cudaarithm/include/opencv2/cudaarithm.hpp b/modules/cudaarithm/include/opencv2/cudaarithm.hpp index c843e8fbb..4aa6582dc 100644 --- a/modules/cudaarithm/include/opencv2/cudaarithm.hpp +++ b/modules/cudaarithm/include/opencv2/cudaarithm.hpp @@ -274,6 +274,10 @@ CV_EXPORTS_W void bitwise_xor(InputArray src1, InputArray src2, OutputArray dst, */ CV_EXPORTS void rshift(InputArray src, Scalar_ val, OutputArray dst, Stream& stream = Stream::Null()); +CV_WRAP inline void rshift(InputArray src, Scalar val, OutputArray dst, Stream& stream = Stream::Null()) { + rshift(src, Scalar_(val), dst, stream); +} + /** @brief Performs pixel by pixel right left of an image by a constant value. @param src Source matrix. Supports 1, 3 and 4 channels images with CV_8U , CV_16U or CV_32S @@ -284,6 +288,10 @@ depth. */ CV_EXPORTS void lshift(InputArray src, Scalar_ val, OutputArray dst, Stream& stream = Stream::Null()); +CV_WRAP inline void lshift(InputArray src, Scalar val, OutputArray dst, Stream& stream = Stream::Null()) { + lshift(src, Scalar_(val), dst, stream); +} + /** @brief Computes the per-element minimum of two matrices (or a matrix and a scalar). @param src1 First source matrix or scalar. @@ -858,7 +866,7 @@ public: @param ccorr Flags to evaluate cross-correlation instead of convolution. @param stream Stream for the asynchronous version. */ - virtual void convolve(InputArray image, InputArray templ, OutputArray result, bool ccorr = false, Stream& stream = Stream::Null()) = 0; + CV_WRAP virtual void convolve(InputArray image, InputArray templ, OutputArray result, bool ccorr = false, Stream& stream = Stream::Null()) = 0; }; /** @brief Creates implementation for cuda::Convolution . diff --git a/modules/cudaarithm/misc/python/test/test_cudaarithm.py b/modules/cudaarithm/misc/python/test/test_cudaarithm.py new file mode 100644 index 000000000..b068fae44 --- /dev/null +++ b/modules/cudaarithm/misc/python/test/test_cudaarithm.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudaarithm_test(NewOpenCVTests): + def setUp(self): + super(cudaarithm_test, self).setUp() + if not cv.cuda.getCudaEnabledDeviceCount(): + self.skipTest("No CUDA-capable device is detected") + + def test_cudaarithm(self): + npMat = (np.random.random((128, 128, 3)) * 255).astype(np.uint8) + + cuMat = cv.cuda_GpuMat(npMat) + cuMatDst = cv.cuda_GpuMat(cuMat.size(),cuMat.type()) + cuMatB = cv.cuda_GpuMat(cuMat.size(),cv.CV_8UC1) + cuMatG = cv.cuda_GpuMat(cuMat.size(),cv.CV_8UC1) + cuMatR = cv.cuda_GpuMat(cuMat.size(),cv.CV_8UC1) + + self.assertTrue(np.allclose(cv.cuda.merge(cv.cuda.split(cuMat)),npMat)) + + cv.cuda.split(cuMat,[cuMatB,cuMatG,cuMatR]) + cv.cuda.merge([cuMatB,cuMatG,cuMatR],cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),npMat)) + + shift = (np.random.random((cuMat.channels(),)) * 8).astype(np.uint8).tolist() + self.assertTrue(np.allclose(cv.cuda.rshift(cuMat,shift).download(),npMat >> shift)) + cv.cuda.rshift(cuMat,shift,cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),npMat >> shift)) + + self.assertTrue(np.allclose(cv.cuda.lshift(cuMat,shift).download(),(npMat << shift).astype('uint8'))) + cv.cuda.lshift(cuMat,shift,cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),(npMat << shift).astype('uint8'))) + + def test_arithmetic(self): + npMat1 = np.random.random((128, 128, 3)) - 0.5 + npMat2 = np.random.random((128, 128, 3)) - 0.5 + + cuMat1 = cv.cuda_GpuMat() + cuMat2 = cv.cuda_GpuMat() + cuMat1.upload(npMat1) + cuMat2.upload(npMat2) + cuMatDst = cv.cuda_GpuMat(cuMat1.size(),cuMat1.type()) + + self.assertTrue(np.allclose(cv.cuda.add(cuMat1, cuMat2).download(), + cv.add(npMat1, npMat2))) + + cv.cuda.add(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.add(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.subtract(cuMat1, cuMat2).download(), + cv.subtract(npMat1, npMat2))) + + cv.cuda.subtract(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.subtract(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.multiply(cuMat1, cuMat2).download(), + cv.multiply(npMat1, npMat2))) + + cv.cuda.multiply(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.multiply(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.divide(cuMat1, cuMat2).download(), + cv.divide(npMat1, npMat2))) + + cv.cuda.divide(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.divide(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.absdiff(cuMat1, cuMat2).download(), + cv.absdiff(npMat1, npMat2))) + + cv.cuda.absdiff(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.absdiff(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.compare(cuMat1, cuMat2, cv.CMP_GE).download(), + cv.compare(npMat1, npMat2, cv.CMP_GE))) + + cuMatDst1 = cv.cuda_GpuMat(cuMat1.size(),cv.CV_8UC3) + cv.cuda.compare(cuMat1, cuMat2, cv.CMP_GE, cuMatDst1) + self.assertTrue(np.allclose(cuMatDst1.download(),cv.compare(npMat1, npMat2, cv.CMP_GE))) + + self.assertTrue(np.allclose(cv.cuda.abs(cuMat1).download(), + np.abs(npMat1))) + + cv.cuda.abs(cuMat1, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),np.abs(npMat1))) + + self.assertTrue(np.allclose(cv.cuda.sqrt(cv.cuda.sqr(cuMat1)).download(), + cv.cuda.abs(cuMat1).download())) + + cv.cuda.sqr(cuMat1, cuMatDst) + cv.cuda.sqrt(cuMatDst, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.cuda.abs(cuMat1).download())) + + self.assertTrue(np.allclose(cv.cuda.log(cv.cuda.exp(cuMat1)).download(), + npMat1)) + + cv.cuda.exp(cuMat1, cuMatDst) + cv.cuda.log(cuMatDst, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),npMat1)) + + self.assertTrue(np.allclose(cv.cuda.pow(cuMat1, 2).download(), + cv.pow(npMat1, 2))) + + cv.cuda.pow(cuMat1, 2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.pow(npMat1, 2))) + + def test_logical(self): + npMat1 = (np.random.random((128, 128)) * 255).astype(np.uint8) + npMat2 = (np.random.random((128, 128)) * 255).astype(np.uint8) + + cuMat1 = cv.cuda_GpuMat() + cuMat2 = cv.cuda_GpuMat() + cuMat1.upload(npMat1) + cuMat2.upload(npMat2) + cuMatDst = cv.cuda_GpuMat(cuMat1.size(),cuMat1.type()) + + self.assertTrue(np.allclose(cv.cuda.bitwise_or(cuMat1, cuMat2).download(), + cv.bitwise_or(npMat1, npMat2))) + + cv.cuda.bitwise_or(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_or(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.bitwise_and(cuMat1, cuMat2).download(), + cv.bitwise_and(npMat1, npMat2))) + + cv.cuda.bitwise_and(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_and(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.bitwise_xor(cuMat1, cuMat2).download(), + cv.bitwise_xor(npMat1, npMat2))) + + cv.cuda.bitwise_xor(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_xor(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.bitwise_not(cuMat1).download(), + cv.bitwise_not(npMat1))) + + cv.cuda.bitwise_not(cuMat1, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.bitwise_not(npMat1))) + + self.assertTrue(np.allclose(cv.cuda.min(cuMat1, cuMat2).download(), + cv.min(npMat1, npMat2))) + + cv.cuda.min(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.min(npMat1, npMat2))) + + self.assertTrue(np.allclose(cv.cuda.max(cuMat1, cuMat2).download(), + cv.max(npMat1, npMat2))) + + cv.cuda.max(cuMat1, cuMat2, cuMatDst) + self.assertTrue(np.allclose(cuMatDst.download(),cv.max(npMat1, npMat2))) + + def test_convolution(self): + npMat = (np.random.random((128, 128)) * 255).astype(np.float32) + npDims = np.array(npMat.shape) + kernel = (np.random.random((3, 3)) * 1).astype(np.float32) + kernelDims = np.array(kernel.shape) + iS = (kernelDims/2).astype(int) + iE = npDims - kernelDims + iS + + cuMat = cv.cuda_GpuMat(npMat) + cuKernel= cv.cuda_GpuMat(kernel) + cuMatDst = cv.cuda_GpuMat(tuple(npDims - kernelDims + 1), cuMat.type()) + conv = cv.cuda.createConvolution() + + self.assertTrue(np.allclose(conv.convolve(cuMat,cuKernel,ccorr=True).download(), + cv.filter2D(npMat,-1,kernel,anchor=(-1,-1))[iS[0]:iE[0]+1,iS[1]:iE[1]+1])) + + conv.convolve(cuMat,cuKernel,cuMatDst,True) + self.assertTrue(np.allclose(cuMatDst.download(), + cv.filter2D(npMat,-1,kernel,anchor=(-1,-1))[iS[0]:iE[0]+1,iS[1]:iE[1]+1])) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudabgsegm/include/opencv2/cudabgsegm.hpp b/modules/cudabgsegm/include/opencv2/cudabgsegm.hpp index 1c051e4a5..eb5467e9b 100644 --- a/modules/cudabgsegm/include/opencv2/cudabgsegm.hpp +++ b/modules/cudabgsegm/include/opencv2/cudabgsegm.hpp @@ -85,7 +85,11 @@ public: CV_WRAP virtual void apply(InputArray image, OutputArray fgmask, double learningRate, Stream& stream) = 0; using cv::BackgroundSubtractor::getBackgroundImage; - CV_WRAP virtual void getBackgroundImage(OutputArray backgroundImage, Stream& stream) const = 0; + virtual void getBackgroundImage(OutputArray backgroundImage, Stream& stream) const = 0; + + CV_WRAP inline void getBackgroundImage(CV_OUT GpuMat& backgroundImage, Stream& stream) { + getBackgroundImage(OutputArray(backgroundImage), stream); + } CV_WRAP virtual int getHistory() const = 0; CV_WRAP virtual void setHistory(int nframes) = 0; @@ -131,7 +135,11 @@ public: CV_WRAP virtual void apply(InputArray image, OutputArray fgmask, double learningRate, Stream& stream) = 0; - CV_WRAP virtual void getBackgroundImage(OutputArray backgroundImage, Stream& stream) const = 0; + virtual void getBackgroundImage(OutputArray backgroundImage, Stream& stream) const = 0; + + CV_WRAP inline void getBackgroundImage(CV_OUT GpuMat &backgroundImage, Stream& stream) { + getBackgroundImage(OutputArray(backgroundImage), stream); + } }; /** @brief Creates MOG2 Background Subtractor diff --git a/modules/cudabgsegm/misc/python/test/test_cudabgsegm.py b/modules/cudabgsegm/misc/python/test/test_cudabgsegm.py new file mode 100644 index 000000000..a6e383ac0 --- /dev/null +++ b/modules/cudabgsegm/misc/python/test/test_cudabgsegm.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudabgsegm_test(NewOpenCVTests): + def setUp(self): + super(cudabgsegm_test, self).setUp() + if not cv.cuda.getCudaEnabledDeviceCount(): + self.skipTest("No CUDA-capable device is detected") + + def test_cudabgsegm(self): + lr = 0.05 + sz = (128,128,1) + npMat = (np.random.random(sz) * 255).astype(np.uint8) + cuMat = cv.cuda_GpuMat(npMat) + cuMatBg = cv.cuda_GpuMat(cuMat.size(),cuMat.type()) + cuMatFg = cv.cuda_GpuMat(cuMat.size(),cuMat.type()) + + mog = cv.cuda.createBackgroundSubtractorMOG() + mog.apply(cuMat, lr, cv.cuda.Stream_Null(), cuMatFg) + mog.getBackgroundImage(cv.cuda.Stream_Null(),cuMatBg) + self.assertTrue(sz[:2] == cuMatFg.size() == cuMatBg.size()) + self.assertTrue(sz[2] == cuMatFg.channels() == cuMatBg.channels()) + self.assertTrue(cv.CV_8UC1 == cuMatFg.type() == cuMatBg.type()) + mog = cv.cuda.createBackgroundSubtractorMOG() + self.assertTrue(np.allclose(cuMatFg.download(),mog.apply(cuMat, lr, cv.cuda.Stream_Null()).download())) + self.assertTrue(np.allclose(cuMatBg.download(),mog.getBackgroundImage(cv.cuda.Stream_Null()).download())) + + mog2 = cv.cuda.createBackgroundSubtractorMOG2() + mog2.apply(cuMat, lr, cv.cuda.Stream_Null(), cuMatFg) + mog2.getBackgroundImage(cv.cuda.Stream_Null(),cuMatBg) + self.assertTrue(sz[:2] == cuMatFg.size() == cuMatBg.size()) + self.assertTrue(sz[2] == cuMatFg.channels() == cuMatBg.channels()) + self.assertTrue(cv.CV_8UC1 == cuMatFg.type() == cuMatBg.type()) + mog2 = cv.cuda.createBackgroundSubtractorMOG2() + self.assertTrue(np.allclose(cuMatFg.download(),mog2.apply(cuMat, lr, cv.cuda.Stream_Null()).download())) + self.assertTrue(np.allclose(cuMatBg.download(),mog2.getBackgroundImage(cv.cuda.Stream_Null()).download())) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudacodec/misc/python/test/test_cudacodec.py b/modules/cudacodec/misc/python/test/test_cudacodec.py new file mode 100644 index 000000000..3bb8feaa0 --- /dev/null +++ b/modules/cudacodec/misc/python/test/test_cudacodec.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudacodec_test(NewOpenCVTests): + def setUp(self): + super(cudacodec_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_reader(self): + #Test the functionality but not the results of the video reader + + vid_path = os.environ['OPENCV_TEST_DATA_PATH'] + '/cv/video/1920x1080.avi' + try: + reader = cv.cudacodec.createVideoReader(vid_path) + ret, gpu_mat = reader.nextFrame() + self.assertTrue(ret) + self.assertTrue('GpuMat' in str(type(gpu_mat)), msg=type(gpu_mat)) + #TODO: print(cv.utils.dumpInputArray(gpu_mat)) # - no support for GpuMat + + # not checking output, therefore sepearate tests for different signatures is unecessary + ret, _gpu_mat2 = reader.nextFrame(gpu_mat) + #TODO: self.assertTrue(gpu_mat == gpu_mat2) + self.assertTrue(ret) + except cv.error as e: + notSupported = (e.code == cv.Error.StsNotImplemented or e.code == cv.Error.StsUnsupportedFormat or e.code == cv.Error.GPU_API_CALL_ERROR) + self.assertTrue(notSupported) + if e.code == cv.Error.StsNotImplemented: + self.skipTest("NVCUVID is not installed") + elif e.code == cv.Error.StsUnsupportedFormat: + self.skipTest("GPU hardware video decoder missing or video format not supported") + elif e.code == cv.Error.GPU_API_CALL_ERRROR: + self.skipTest("GPU hardware video decoder is missing") + else: + self.skipTest(e.err) + + def test_writer_existence(self): + #Test at least the existence of wrapped functions for now + + try: + _writer = cv.cudacodec.createVideoWriter("tmp", (128, 128), 30) + except cv.error as e: + self.assertEqual(e.code, cv.Error.StsNotImplemented) + self.skipTest("NVCUVENC is not installed") + + self.assertTrue(True) #It is sufficient that no exceptions have been there + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudafeatures2d/include/opencv2/cudafeatures2d.hpp b/modules/cudafeatures2d/include/opencv2/cudafeatures2d.hpp index 840780a27..d9cff6de8 100644 --- a/modules/cudafeatures2d/include/opencv2/cudafeatures2d.hpp +++ b/modules/cudafeatures2d/include/opencv2/cudafeatures2d.hpp @@ -414,7 +414,7 @@ public: /** Converts keypoints array from internal representation to standard vector. */ CV_WRAP virtual void convert(InputArray gpu_keypoints, - std::vector& keypoints) = 0; + CV_OUT std::vector& keypoints) = 0; }; // diff --git a/modules/cudafeatures2d/misc/python/test/test_cudafeatures2d.py b/modules/cudafeatures2d/misc/python/test/test_cudafeatures2d.py new file mode 100644 index 000000000..9c17da796 --- /dev/null +++ b/modules/cudafeatures2d/misc/python/test/test_cudafeatures2d.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudafeatures2d_test(NewOpenCVTests): + def setUp(self): + super(cudafeatures2d_test, self).setUp() + if not cv.cuda.getCudaEnabledDeviceCount(): + self.skipTest("No CUDA-capable device is detected") + + def test_cudafeatures2d(self): + npMat1 = self.get_sample("samples/data/right01.jpg") + npMat2 = self.get_sample("samples/data/right02.jpg") + + cuMat1 = cv.cuda_GpuMat() + cuMat2 = cv.cuda_GpuMat() + cuMat1.upload(npMat1) + cuMat2.upload(npMat2) + + cuMat1 = cv.cuda.cvtColor(cuMat1, cv.COLOR_RGB2GRAY) + cuMat2 = cv.cuda.cvtColor(cuMat2, cv.COLOR_RGB2GRAY) + + fast = cv.cuda_FastFeatureDetector.create() + _kps = fast.detectAsync(cuMat1) + + orb = cv.cuda_ORB.create() + _kps1, descs1 = orb.detectAndComputeAsync(cuMat1, None) + _kps2, descs2 = orb.detectAndComputeAsync(cuMat2, None) + + self.assertTrue(len(orb.convert(_kps1)) == _kps1.size()[0]) + self.assertTrue(len(orb.convert(_kps2)) == _kps2.size()[0]) + + bf = cv.cuda_DescriptorMatcher.createBFMatcher(cv.NORM_HAMMING) + matches = bf.match(descs1, descs2) + self.assertGreater(len(matches), 0) + matches = bf.knnMatch(descs1, descs2, 2) + self.assertGreater(len(matches), 0) + matches = bf.radiusMatch(descs1, descs2, 0.1) + self.assertGreater(len(matches), 0) + + self.assertTrue(True) #It is sufficient that no exceptions have been there + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudafilters/misc/python/test/test_cudafilters.py b/modules/cudafilters/misc/python/test/test_cudafilters.py new file mode 100644 index 000000000..8ee74dba5 --- /dev/null +++ b/modules/cudafilters/misc/python/test/test_cudafilters.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudafilters_test(NewOpenCVTests): + def setUp(self): + super(cudafilters_test, self).setUp() + if not cv.cuda.getCudaEnabledDeviceCount(): + self.skipTest("No CUDA-capable device is detected") + + def test_existence(self): + #Test at least the existence of wrapped functions for now + + _filter = cv.cuda.createBoxFilter(cv.CV_8UC1, -1, (3, 3)) + _filter = cv.cuda.createLinearFilter(cv.CV_8UC4, -1, np.eye(3)) + _filter = cv.cuda.createLaplacianFilter(cv.CV_16UC1, -1, ksize=3) + _filter = cv.cuda.createSeparableLinearFilter(cv.CV_8UC1, -1, np.eye(3), np.eye(3)) + _filter = cv.cuda.createDerivFilter(cv.CV_8UC1, -1, 1, 1, 3) + _filter = cv.cuda.createSobelFilter(cv.CV_8UC1, -1, 1, 1) + _filter = cv.cuda.createScharrFilter(cv.CV_8UC1, -1, 1, 0) + _filter = cv.cuda.createGaussianFilter(cv.CV_8UC1, -1, (3, 3), 16) + _filter = cv.cuda.createMorphologyFilter(cv.MORPH_DILATE, cv.CV_32FC1, np.eye(3)) + _filter = cv.cuda.createBoxMaxFilter(cv.CV_8UC1, (3, 3)) + _filter = cv.cuda.createBoxMinFilter(cv.CV_8UC1, (3, 3)) + _filter = cv.cuda.createRowSumFilter(cv.CV_8UC1, cv.CV_32FC1, 3) + _filter = cv.cuda.createColumnSumFilter(cv.CV_8UC1, cv.CV_32FC1, 3) + _filter = cv.cuda.createMedianFilter(cv.CV_8UC1, 3) + + self.assertTrue(True) #It is sufficient that no exceptions have been there + + def test_laplacian(self): + npMat = (np.random.random((128, 128)) * 255).astype(np.uint16) + cuMat = cv.cuda_GpuMat() + cuMat.upload(npMat) + + self.assertTrue(np.allclose(cv.cuda.createLaplacianFilter(cv.CV_16UC1, -1, ksize=3).apply(cuMat).download(), + cv.Laplacian(npMat, cv.CV_16UC1, ksize=3))) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudaimgproc/misc/python/test/test_cudaimgproc.py b/modules/cudaimgproc/misc/python/test/test_cudaimgproc.py new file mode 100644 index 000000000..0548cbcd8 --- /dev/null +++ b/modules/cudaimgproc/misc/python/test/test_cudaimgproc.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudaimgproc_test(NewOpenCVTests): + def setUp(self): + super(cudaimgproc_test, self).setUp() + if not cv.cuda.getCudaEnabledDeviceCount(): + self.skipTest("No CUDA-capable device is detected") + + def test_cudaimgproc(self): + npC1 = (np.random.random((128, 128)) * 255).astype(np.uint8) + npC3 = (np.random.random((128, 128, 3)) * 255).astype(np.uint8) + npC4 = (np.random.random((128, 128, 4)) * 255).astype(np.uint8) + cuC1 = cv.cuda_GpuMat() + cuC3 = cv.cuda_GpuMat() + cuC4 = cv.cuda_GpuMat() + cuC1.upload(npC1) + cuC3.upload(npC3) + cuC4.upload(npC4) + + cv.cuda.cvtColor(cuC3, cv.COLOR_RGB2HSV) + cv.cuda.demosaicing(cuC1, cv.cuda.COLOR_BayerGR2BGR_MHT) + cv.cuda.gammaCorrection(cuC3) + cv.cuda.alphaComp(cuC4, cuC4, cv.cuda.ALPHA_XOR) + cv.cuda.calcHist(cuC1) + cv.cuda.equalizeHist(cuC1) + cv.cuda.evenLevels(3, 0, 255) + cv.cuda.meanShiftFiltering(cuC4, 10, 5) + cv.cuda.meanShiftProc(cuC4, 10, 5) + cv.cuda.bilateralFilter(cuC3, 3, 16, 3) + cv.cuda.blendLinear + + cuRes = cv.cuda.meanShiftSegmentation(cuC4, 10, 5, 5) + cuDst = cv.cuda_GpuMat(cuC4.size(),cuC4.type()) + cv.cuda.meanShiftSegmentation(cuC4, 10, 5, 5, cuDst) + self.assertTrue(np.allclose(cuRes.download(),cuDst.download())) + + clahe = cv.cuda.createCLAHE() + clahe.apply(cuC1, cv.cuda_Stream.Null()) + + histLevels = cv.cuda.histEven(cuC3, 20, 0, 255) + cv.cuda.histRange(cuC1, histLevels) + + detector = cv.cuda.createCannyEdgeDetector(0, 100) + detector.detect(cuC1) + + detector = cv.cuda.createHoughLinesDetector(3, np.pi / 180, 20) + detector.detect(cuC1) + + detector = cv.cuda.createHoughSegmentDetector(3, np.pi / 180, 20, 5) + detector.detect(cuC1) + + detector = cv.cuda.createHoughCirclesDetector(3, 20, 10, 10, 20, 100) + detector.detect(cuC1) + + detector = cv.cuda.createGeneralizedHoughBallard() + #BUG: detect accept only Mat! + #Even if generate_gpumat_decls is set to True, it only wraps overload CUDA functions. + #The problem is that Mat and GpuMat are not fully compatible to enable system-wide overloading + #detector.detect(cuC1, cuC1, cuC1) + + detector = cv.cuda.createGeneralizedHoughGuil() + #BUG: same as above.. + #detector.detect(cuC1, cuC1, cuC1) + + detector = cv.cuda.createHarrisCorner(cv.CV_8UC1, 15, 5, 1) + detector.compute(cuC1) + + detector = cv.cuda.createMinEigenValCorner(cv.CV_8UC1, 15, 5, 1) + detector.compute(cuC1) + + detector = cv.cuda.createGoodFeaturesToTrackDetector(cv.CV_8UC1) + detector.detect(cuC1) + + matcher = cv.cuda.createTemplateMatching(cv.CV_8UC1, cv.TM_CCOEFF_NORMED) + matcher.match(cuC3, cuC3) + + self.assertTrue(True) #It is sufficient that no exceptions have been there + + def test_cvtColor(self): + npMat = (np.random.random((128, 128, 3)) * 255).astype(np.uint8) + cuMat = cv.cuda_GpuMat() + cuMat.upload(npMat) + + self.assertTrue(np.allclose(cv.cuda.cvtColor(cuMat, cv.COLOR_BGR2HSV).download(), + cv.cvtColor(npMat, cv.COLOR_BGR2HSV))) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudaimgproc/src/mssegmentation.cpp b/modules/cudaimgproc/src/mssegmentation.cpp index de8ed8414..2e7a6c10b 100644 --- a/modules/cudaimgproc/src/mssegmentation.cpp +++ b/modules/cudaimgproc/src/mssegmentation.cpp @@ -388,7 +388,15 @@ void cv::cuda::meanShiftSegmentation(InputArray _src, OutputArray _dst, int sp, dstcol[3] = 255; } } - dst.copyTo(_dst); + + if (_dst.kind() == _InputArray::CUDA_GPU_MAT) + { + GpuMat dstGpuMat = getOutputMat(_dst, src.size(), src.type(), stream); + dstGpuMat.upload(dst, stream); + } + else { + dst.copyTo(_dst); + } } #endif // #if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) diff --git a/modules/cudaobjdetect/include/opencv2/cudaobjdetect.hpp b/modules/cudaobjdetect/include/opencv2/cudaobjdetect.hpp index 7d11bd70c..cb7a3fe2e 100644 --- a/modules/cudaobjdetect/include/opencv2/cudaobjdetect.hpp +++ b/modules/cudaobjdetect/include/opencv2/cudaobjdetect.hpp @@ -162,6 +162,22 @@ public: std::vector& found_locations, std::vector* confidences = NULL) = 0; + CV_WRAP inline void detect(InputArray img, + CV_OUT std::vector& found_locations, + CV_OUT std::vector& confidences) { + detect(img, found_locations, &confidences); + } + + /** @brief Performs object detection without a multi-scale window. + + @param img Source image. CV_8UC1 and CV_8UC4 types are supported for now. + @param found_locations Left-top corner points of detected objects boundaries. + */ + CV_WRAP inline void detectWithoutConf(InputArray img, + CV_OUT std::vector& found_locations) { + detect(img, found_locations, NULL); + } + /** @brief Performs object detection with a multi-scale window. @param img Source image. See cuda::HOGDescriptor::detect for type limitations. @@ -172,6 +188,22 @@ public: std::vector& found_locations, std::vector* confidences = NULL) = 0; + CV_WRAP inline void detectMultiScale(InputArray img, + CV_OUT std::vector& found_locations, + CV_OUT std::vector& confidences) { + detectMultiScale(img, found_locations, &confidences); + } + + /** @brief Performs object detection with a multi-scale window. + + @param img Source image. See cuda::HOGDescriptor::detect for type limitations. + @param found_locations Detected objects boundaries. + */ + CV_WRAP inline void detectMultiScaleWithoutConf(InputArray img, + CV_OUT std::vector& found_locations) { + detectMultiScale(img, found_locations, NULL); + } + /** @brief Returns block descriptors computed for the whole image. @param img Source image. See cuda::HOGDescriptor::detect for type limitations. diff --git a/modules/cudaobjdetect/misc/python/test/test_cudaobjdetect.py b/modules/cudaobjdetect/misc/python/test/test_cudaobjdetect.py new file mode 100644 index 000000000..b11d77257 --- /dev/null +++ b/modules/cudaobjdetect/misc/python/test/test_cudaobjdetect.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class cudaobjdetect_test(NewOpenCVTests): + def setUp(self): + super(cudaobjdetect_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_hog(self): + img_path = os.environ['OPENCV_TEST_DATA_PATH'] + '/gpu/caltech/image_00000009_0.png' + npMat = cv.cvtColor(cv.imread(img_path),cv.COLOR_BGR2BGRA) + + cuMat = cv.cuda_GpuMat(npMat) + cuHog = cv.cuda.HOG_create() + cuHog.setSVMDetector(cuHog.getDefaultPeopleDetector()) + + loc, conf = cuHog.detect(cuMat) + self.assertTrue(len(loc) == len(conf) and len(loc) > 0 and len(loc[0]) == 2) + + loc = cuHog.detectWithoutConf(cuMat) + self.assertTrue(len(loc) > 0 and len(loc[0]) == 2) + + loc = cuHog.detectMultiScaleWithoutConf(cuMat) + self.assertTrue(len(loc) > 0 and len(loc[0]) == 4) + + cuHog.setGroupThreshold(0) + loc, conf = cuHog.detectMultiScale(cuMat) + self.assertTrue(len(loc) == len(conf) and len(loc) > 0 and len(loc[0]) == 4) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/cudawarping/include/opencv2/cudawarping.hpp b/modules/cudawarping/include/opencv2/cudawarping.hpp index aa00ac009..45cca1ccf 100644 --- a/modules/cudawarping/include/opencv2/cudawarping.hpp +++ b/modules/cudawarping/include/opencv2/cudawarping.hpp @@ -112,7 +112,7 @@ CV_EXPORTS_W void resize(InputArray src, OutputArray dst, Size dsize, double fx= @param src Source image. CV_8U , CV_16U , CV_32S , or CV_32F depth and 1, 3, or 4 channels are supported. @param dst Destination image with the same type as src . The size is dsize . -@param M *2x3* transformation matrix. +@param M *2x3* Mat or UMat transformation matrix. @param dsize Size of the destination image. @param flags Combination of interpolation methods (see resize) and the optional flag WARP_INVERSE_MAP specifying that M is an inverse transformation ( dst=\>src ). Only @@ -123,12 +123,22 @@ INTER_NEAREST , INTER_LINEAR , and INTER_CUBIC interpolation methods are support @sa warpAffine */ -CV_EXPORTS_W void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, +CV_EXPORTS void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar(), Stream& stream = Stream::Null()); +CV_WRAP inline void warpAffine(InputArray src, OutputArray dst, UMat M, Size dsize, int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar(), Stream& stream = Stream::Null()) { + warpAffine(src, dst, InputArray(M), dsize, flags, borderMode, borderValue, stream); +} + +CV_WRAP inline void warpAffine(InputArray src, OutputArray dst, Mat M, Size dsize, int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar(), Stream& stream = Stream::Null()) { + warpAffine(src, dst, InputArray(M), dsize, flags, borderMode, borderValue, stream); +} + /** @brief Builds transformation maps for affine transformation. -@param M *2x3* transformation matrix. +@param M *2x3* Mat or UMat transformation matrix. @param inverse Flag specifying that M is an inverse transformation ( dst=\>src ). @param dsize Size of the destination image. @param xmap X values with CV_32FC1 type. @@ -137,14 +147,22 @@ CV_EXPORTS_W void warpAffine(InputArray src, OutputArray dst, InputArray M, Size @sa cuda::warpAffine , cuda::remap */ -CV_EXPORTS_W void buildWarpAffineMaps(InputArray M, bool inverse, Size dsize, OutputArray xmap, OutputArray ymap, Stream& stream = Stream::Null()); +CV_EXPORTS void buildWarpAffineMaps(InputArray M, bool inverse, Size dsize, OutputArray xmap, OutputArray ymap, Stream& stream = Stream::Null()); + +CV_WRAP inline void buildWarpAffineMaps(UMat M, bool inverse, Size dsize, CV_OUT GpuMat& xmap, CV_OUT GpuMat& ymap, Stream& stream = Stream::Null()) { + buildWarpAffineMaps(InputArray(M), inverse, dsize, OutputArray(xmap), OutputArray(ymap), stream); +} + +CV_WRAP inline void buildWarpAffineMaps(Mat M, bool inverse, Size dsize, CV_OUT GpuMat& xmap, CV_OUT GpuMat& ymap, Stream& stream = Stream::Null()) { + buildWarpAffineMaps(InputArray(M), inverse, dsize, OutputArray(xmap), OutputArray(ymap), stream); +} /** @brief Applies a perspective transformation to an image. @param src Source image. CV_8U , CV_16U , CV_32S , or CV_32F depth and 1, 3, or 4 channels are supported. @param dst Destination image with the same type as src . The size is dsize . -@param M *3x3* transformation matrix. +@param M *3x3* Mat or UMat transformation matrix. @param dsize Size of the destination image. @param flags Combination of interpolation methods (see resize ) and the optional flag WARP_INVERSE_MAP specifying that M is the inverse transformation ( dst =\> src ). Only @@ -155,12 +173,22 @@ INTER_NEAREST , INTER_LINEAR , and INTER_CUBIC interpolation methods are support @sa warpPerspective */ -CV_EXPORTS_W void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, +CV_EXPORTS void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar(), Stream& stream = Stream::Null()); +CV_WRAP inline void warpPerspective(InputArray src, OutputArray dst, UMat M, Size dsize, int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar(), Stream& stream = Stream::Null()) { + warpPerspective(src, dst, InputArray(M), dsize, flags, borderMode, borderValue, stream); +} + +CV_WRAP inline void warpPerspective(InputArray src, OutputArray dst, Mat M, Size dsize, int flags = INTER_LINEAR, + int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar(), Stream& stream = Stream::Null()) { + warpPerspective(src, dst, InputArray(M), dsize, flags, borderMode, borderValue, stream); +} + /** @brief Builds transformation maps for perspective transformation. -@param M *3x3* transformation matrix. +@param M *3x3* Mat or UMat transformation matrix. @param inverse Flag specifying that M is an inverse transformation ( dst=\>src ). @param dsize Size of the destination image. @param xmap X values with CV_32FC1 type. @@ -169,7 +197,15 @@ CV_EXPORTS_W void warpPerspective(InputArray src, OutputArray dst, InputArray M, @sa cuda::warpPerspective , cuda::remap */ -CV_EXPORTS_W void buildWarpPerspectiveMaps(InputArray M, bool inverse, Size dsize, OutputArray xmap, OutputArray ymap, Stream& stream = Stream::Null()); +CV_EXPORTS void buildWarpPerspectiveMaps(InputArray M, bool inverse, Size dsize, OutputArray xmap, OutputArray ymap, Stream& stream = Stream::Null()); + +CV_WRAP inline void buildWarpPerspectiveMaps(UMat M, bool inverse, Size dsize, CV_OUT GpuMat& xmap, CV_OUT GpuMat& ymap, Stream& stream = Stream::Null()) { + buildWarpPerspectiveMaps(InputArray(M), inverse, dsize, OutputArray(xmap), OutputArray(ymap), stream); +} + +CV_WRAP inline void buildWarpPerspectiveMaps(Mat M, bool inverse, Size dsize, CV_OUT GpuMat& xmap, CV_OUT GpuMat& ymap, Stream& stream = Stream::Null()) { + buildWarpPerspectiveMaps(InputArray(M), inverse, dsize, OutputArray(xmap), OutputArray(ymap), stream); +} /** @brief Rotates an image around the origin (0,0) and then shifts it. diff --git a/modules/cudawarping/misc/python/test/test_cudawarping.py b/modules/cudawarping/misc/python/test/test_cudawarping.py new file mode 100644 index 000000000..65c6a3119 --- /dev/null +++ b/modules/cudawarping/misc/python/test/test_cudawarping.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +def create_affine_transform_matrix(size,angle): + return np.array([[np.cos(angle), -np.sin(angle), size[1]/2], [np.sin(angle), np.cos(angle), 0]]) +def create_perspective_transform_matrix(size,angle): + return np.vstack([create_affine_transform_matrix(size,angle),[0, 0, 1]]) + +class cudawarping_test(NewOpenCVTests): + def setUp(self): + super(cudawarping_test, self).setUp() + if not cv.cuda.getCudaEnabledDeviceCount(): + self.skipTest("No CUDA-capable device is detected") + + def test_resize(self): + dstSz = (256,256) + interp = cv.INTER_NEAREST + npMat = (np.random.random((128,128,3))*255).astype(np.uint8) + + cuMat = cv.cuda_GpuMat(npMat) + cuMatDst = cv.cuda_GpuMat(dstSz,cuMat.type()) + + self.assertTrue(np.allclose(cv.cuda.resize(cuMat,dstSz,interpolation=interp).download(), + cv.resize(npMat,dstSz,interpolation=interp))) + + cv.cuda.resize(cuMat,dstSz,cuMatDst,interpolation=interp) + self.assertTrue(np.allclose(cuMatDst.download(),cv.resize(npMat,dstSz,interpolation=interp))) + + def test_warp(self): + npMat = (np.random.random((128,128,3))*255).astype(np.uint8) + size = npMat.shape[:2] + M1 = create_affine_transform_matrix(size,np.pi/2) + + cuMat = cv.cuda_GpuMat(npMat) + cuMatDst = cv.cuda_GpuMat(size,cuMat.type()) + + borderType = cv.BORDER_REFLECT101 + self.assertTrue(np.allclose(cv.cuda.warpAffine(cuMat,M1,size,borderMode=borderType).download(), + cv.warpAffine(npMat,M1,size, borderMode=borderType))) + cv.cuda.warpAffine(cuMat,M1,size,cuMatDst,borderMode=borderType) + self.assertTrue(np.allclose(cuMatDst.download(),cv.warpAffine(npMat,M1,size,borderMode=borderType))) + + interpolation = cv.INTER_NEAREST + flags = interpolation | cv.WARP_INVERSE_MAP + dst_gold = cv.warpAffine(npMat, M1, size, flags = flags) + cuMaps = cv.cuda.buildWarpAffineMaps(M1,True,size) + dst = cv.remap(npMat, cuMaps[0].download(), cuMaps[1].download(),interpolation) + self.assertTrue(np.allclose(dst,dst_gold)) + + xmap = cv.cuda_GpuMat(size,cv.CV_32FC1) + ymap = cv.cuda_GpuMat(size,cv.CV_32FC1) + cv.cuda.buildWarpAffineMaps(M1,True,size,xmap,ymap) + dst = cv.remap(npMat, xmap.download(), ymap.download(),interpolation) + self.assertTrue(np.allclose(dst,dst_gold)) + + M2 = create_perspective_transform_matrix(size,np.pi/2) + np.allclose(cv.cuda.warpPerspective(cuMat,M2,size,borderMode=borderType).download(), + cv.warpPerspective(npMat,M2,size,borderMode=borderType)) + cv.cuda.warpPerspective(cuMat,M2,size,cuMatDst,borderMode=borderType) + self.assertTrue(np.allclose(cuMatDst.download(),cv.warpPerspective(npMat,M2,size,borderMode=borderType))) + + dst_gold = cv.warpPerspective(npMat, M2, size, flags = flags) + cuMaps = cv.cuda.buildWarpPerspectiveMaps(M2,True,size) + dst = cv.remap(npMat, cuMaps[0].download(), cuMaps[1].download(),interpolation) + self.assertTrue(np.allclose(dst,dst_gold)) + + cv.cuda.buildWarpPerspectiveMaps(M2,True,size,xmap,ymap) + dst = cv.remap(npMat, xmap.download(), ymap.download(),interpolation) + self.assertTrue(np.allclose(dst,dst_gold)) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp index 16039a5a7..ea4a32381 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp @@ -83,7 +83,7 @@ between function calls. opencv_source_code/samples/gpu/surf_keypoint_matcher.cpp */ -class CV_EXPORTS SURF_CUDA +class CV_EXPORTS_W SURF_CUDA { public: enum KeypointLayout @@ -104,15 +104,28 @@ public: explicit SURF_CUDA(double _hessianThreshold, int _nOctaves=4, int _nOctaveLayers=2, bool _extended=false, float _keypointsRatio=0.01f, bool _upright = false); + /** + @param _hessianThreshold Threshold for hessian keypoint detector used in SURF. + @param _nOctaves Number of pyramid octaves the keypoint detector will use. + @param _nOctaveLayers Number of octave layers within each octave. + @param _extended Extended descriptor flag (true - use extended 128-element descriptors; false - use + 64-element descriptors). + @param _keypointsRatio + @param _upright Up-right or rotated features flag (true - do not compute orientation of features; + false - compute orientation). + */ + CV_WRAP static Ptr create(double _hessianThreshold, int _nOctaves = 4, + int _nOctaveLayers = 2, bool _extended = false, float _keypointsRatio = 0.01f, bool _upright = false); + //! returns the descriptor size in float's (64 or 128) - int descriptorSize() const; + CV_WRAP int descriptorSize() const; //! returns the default norm type - int defaultNorm() const; + CV_WRAP int defaultNorm() const; //! upload host keypoints to device memory void uploadKeypoints(const std::vector& keypoints, GpuMat& keypointsGPU); //! download keypoints from device to host memory - void downloadKeypoints(const GpuMat& keypointsGPU, std::vector& keypoints); + CV_WRAP void downloadKeypoints(const GpuMat& keypointsGPU, CV_OUT std::vector& keypoints); //! download descriptors from device to host memory void downloadDescriptors(const GpuMat& descriptorsGPU, std::vector& descriptors); @@ -133,24 +146,47 @@ public: void operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints, GpuMat& descriptors, bool useProvidedKeypoints = false); + /** @brief Finds the keypoints using fast hessian detector used in SURF + + @param img Source image, currently supports only CV_8UC1 images. + @param mask A mask image same size as src and of type CV_8UC1. + @param keypoints Detected keypoints. + */ + CV_WRAP inline void detect(const GpuMat& img, const GpuMat& mask, CV_OUT GpuMat& keypoints) { + (*this)(img, mask, keypoints); + } + void operator()(const GpuMat& img, const GpuMat& mask, std::vector& keypoints); void operator()(const GpuMat& img, const GpuMat& mask, std::vector& keypoints, GpuMat& descriptors, bool useProvidedKeypoints = false); + /** @brief Finds the keypoints and computes their descriptors using fast hessian detector used in SURF + + @param img Source image, currently supports only CV_8UC1 images. + @param mask A mask image same size as src and of type CV_8UC1. + @param keypoints Detected keypoints. + @param descriptors Keypoint descriptors. + @param useProvidedKeypoints Compute descriptors for the user-provided keypoints and recompute keypoints direction. + */ + CV_WRAP inline void detectWithDescriptors(const GpuMat& img, const GpuMat& mask, CV_OUT GpuMat& keypoints, CV_OUT GpuMat& descriptors, + bool useProvidedKeypoints = false) { + (*this)(img, mask, keypoints, descriptors, useProvidedKeypoints); + } + void operator()(const GpuMat& img, const GpuMat& mask, std::vector& keypoints, std::vector& descriptors, bool useProvidedKeypoints = false); void releaseMemory(); // SURF parameters - double hessianThreshold; - int nOctaves; - int nOctaveLayers; - bool extended; - bool upright; + CV_PROP double hessianThreshold; + CV_PROP int nOctaves; + CV_PROP int nOctaveLayers; + CV_PROP bool extended; + CV_PROP bool upright; //! max keypoints = min(keypointsRatio * img.size().area(), 65535) - float keypointsRatio; + CV_PROP float keypointsRatio; GpuMat sum, mask1, maskSum; diff --git a/modules/xfeatures2d/misc/python/test/test_cuda_xfeatures2d.py b/modules/xfeatures2d/misc/python/test/test_cuda_xfeatures2d.py new file mode 100644 index 000000000..16c8e5651 --- /dev/null +++ b/modules/xfeatures2d/misc/python/test/test_cuda_xfeatures2d.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +import os +import cv2 as cv +import numpy as np + +from tests_common import NewOpenCVTests, unittest + +class xfeatures2d_test(NewOpenCVTests): + def setUp(self): + super(xfeatures2d_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_surf(self): + img_path = os.environ['OPENCV_TEST_DATA_PATH'] + "/gpu/features2d/aloe.png" + hessianThreshold = 100 + nOctaves = 3 + nOctaveLayers = 2 + extended = False + keypointsRatio = 0.05 + upright = False + + npMat = cv.cvtColor(cv.imread(img_path),cv.COLOR_BGR2GRAY) + cuMat = cv.cuda_GpuMat(npMat) + + try: + cuSurf = cv.cuda_SURF_CUDA.create(hessianThreshold,nOctaves,nOctaveLayers,extended,keypointsRatio,upright) + surf = cv.xfeatures2d_SURF.create(hessianThreshold,nOctaves,nOctaveLayers,extended,upright) + except cv.error as e: + self.assertEqual(e.code, cv.Error.StsNotImplemented) + self.skipTest("OPENCV_ENABLE_NONFREE is not enabled in this build.") + + cuKeypoints = cuSurf.detect(cuMat,cv.cuda_GpuMat()) + keypointsHost = cuSurf.downloadKeypoints(cuKeypoints) + keypoints = surf.detect(npMat) + self.assertTrue(len(keypointsHost) == len(keypoints)) + + cuKeypoints, cuDescriptors = cuSurf.detectWithDescriptors(cuMat,cv.cuda_GpuMat(),cuKeypoints,useProvidedKeypoints=True) + keypointsHost = cuSurf.downloadKeypoints(cuKeypoints) + descriptorsHost = cuDescriptors.download() + keypoints, descriptors = surf.compute(npMat,keypoints) + + self.assertTrue(len(keypointsHost) == len(keypoints) and descriptorsHost.shape == descriptors.shape) + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() \ No newline at end of file diff --git a/modules/xfeatures2d/src/surf.cuda.cpp b/modules/xfeatures2d/src/surf.cuda.cpp index 36be1780a..7864a166a 100644 --- a/modules/xfeatures2d/src/surf.cuda.cpp +++ b/modules/xfeatures2d/src/surf.cuda.cpp @@ -54,6 +54,7 @@ using namespace cv::cuda; cv::cuda::SURF_CUDA::SURF_CUDA() { throw_no_nonfree } cv::cuda::SURF_CUDA::SURF_CUDA(double, int, int, bool, float, bool) { throw_no_nonfree } +Ptr cv::cuda::SURF_CUDA::create(double, int, int, bool, float, bool) { throw_no_nonfree } int cv::cuda::SURF_CUDA::descriptorSize() const { throw_no_nonfree } int cv::cuda::SURF_CUDA::defaultNorm() const { throw_no_nonfree } void cv::cuda::SURF_CUDA::uploadKeypoints(const std::vector&, GpuMat&) { throw_no_nonfree } @@ -71,6 +72,7 @@ void cv::cuda::SURF_CUDA::releaseMemory() { throw_no_nonfree } cv::cuda::SURF_CUDA::SURF_CUDA() { throw_no_cuda(); } cv::cuda::SURF_CUDA::SURF_CUDA(double, int, int, bool, float, bool) { throw_no_cuda(); } +Ptr cv::cuda::SURF_CUDA::create(double, int, int, bool, float, bool) { throw_no_cuda(); } int cv::cuda::SURF_CUDA::descriptorSize() const { throw_no_cuda(); } int cv::cuda::SURF_CUDA::defaultNorm() const { throw_no_cuda(); } void cv::cuda::SURF_CUDA::uploadKeypoints(const std::vector&, GpuMat&) { throw_no_cuda(); } @@ -451,6 +453,12 @@ void cv::cuda::SURF_CUDA::releaseMemory() maxPosBuffer.release(); } +Ptr cv::cuda::SURF_CUDA::create(double _hessianThreshold, int _nOctaves, + int _nOctaveLayers, bool _extended, float _keypointsRatio, bool _upright) +{ + return makePtr(_hessianThreshold, _nOctaves, _nOctaveLayers, _extended, _keypointsRatio, _upright); +} + #endif // !defined (OPENCV_ENABLE_NONFREE) #endif