surf.cpp 16.6 KB
Newer Older
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
/*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 GpuMaterials 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 bpied warranties, including, but not limited to, the bpied
// 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::gpu;
using namespace std;

#if !defined (HAVE_CUDA)

51
cv::gpu::SURF_GPU::SURF_GPU() { throw_nogpu(); }
52
cv::gpu::SURF_GPU::SURF_GPU(double, int, int, bool, float, bool) { throw_nogpu(); }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
53 54 55 56
int cv::gpu::SURF_GPU::descriptorSize() const { throw_nogpu(); return 0;}
void cv::gpu::SURF_GPU::uploadKeypoints(const vector<KeyPoint>&, GpuMat&) { throw_nogpu(); }
void cv::gpu::SURF_GPU::downloadKeypoints(const GpuMat&, vector<KeyPoint>&) { throw_nogpu(); }
void cv::gpu::SURF_GPU::downloadDescriptors(const GpuMat&, vector<float>&) { throw_nogpu(); }
57
void cv::gpu::SURF_GPU::operator()(const GpuMat&, const GpuMat&, GpuMat&) { throw_nogpu(); }
58
void cv::gpu::SURF_GPU::operator()(const GpuMat&, const GpuMat&, GpuMat&, GpuMat&, bool) { throw_nogpu(); }
59
void cv::gpu::SURF_GPU::operator()(const GpuMat&, const GpuMat&, vector<KeyPoint>&) { throw_nogpu(); }
60 61
void cv::gpu::SURF_GPU::operator()(const GpuMat&, const GpuMat&, vector<KeyPoint>&, GpuMat&, bool) { throw_nogpu(); }
void cv::gpu::SURF_GPU::operator()(const GpuMat&, const GpuMat&, vector<KeyPoint>&, vector<float>&, bool) { throw_nogpu(); }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
62 63 64 65

#else /* !defined (HAVE_CUDA) */

namespace cv { namespace gpu { namespace surf
66
{
67 68 69 70 71
    void icvCalcLayerDetAndTrace_gpu(const PtrStepf& det, const PtrStepf& trace, int img_rows, int img_cols, int octave, int nOctaveLayers);

    void icvFindMaximaInLayer_gpu(const PtrStepf& det, const PtrStepf& trace, int4* maxPosBuffer, unsigned int* maxCounter,
        int img_rows, int img_cols, int octave, bool use_mask, int nLayers);

72 73 74
    void icvInterpolateKeypoint_gpu(const PtrStepf& det, const int4* maxPosBuffer, unsigned int maxCounter, 
        float* featureX, float* featureY, int* featureLaplacian, float* featureSize, float* featureHessian, 
        unsigned int* featureCounter);
75

76
    void icvCalcOrientation_gpu(const float* featureX, const float* featureY, const float* featureSize, float* featureDir, int nFeatures);
77

78 79
    void compute_descriptors_gpu(const DevMem2Df& descriptors, 
        const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
80 81 82 83 84 85
}}}

using namespace cv::gpu::surf;

namespace
{
86
    class SURF_GPU_Invoker : private CvSURFParams
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
87 88
    {
    public:
89 90
        SURF_GPU_Invoker(SURF_GPU& surf, const GpuMat& img, const GpuMat& mask) :
            CvSURFParams(surf),
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
91

92
            sum(surf.sum), mask1(surf.mask1), maskSum(surf.maskSum), intBuffer(surf.intBuffer), det(surf.det), trace(surf.trace),
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
93

94
            maxPosBuffer(surf.maxPosBuffer), 
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
95 96 97

            img_cols(img.cols), img_rows(img.rows),

98 99 100
            use_mask(!mask.empty()),

            upright(surf.upright)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
101
        {
102
            CV_Assert(!img.empty() && img.type() == CV_8UC1);
103
            CV_Assert(mask.empty() || (mask.size() == img.size() && mask.type() == CV_8UC1));
104 105
            CV_Assert(nOctaves > 0 && nOctaveLayers > 0);
            CV_Assert(TargetArchs::builtWith(GLOBAL_ATOMICS) && DeviceInfo().supports(GLOBAL_ATOMICS));
106

107
            maxFeatures = min(static_cast<int>(img.size().area() * surf.keypointsRatio), 65535);
108
            maxCandidates = min(static_cast<int>(1.5 * maxFeatures), 65535);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
109

110
            CV_Assert(maxFeatures > 0);
111
            
112 113
            cudaSafeCall( cudaMalloc((void**)&d_counters, (nOctaves + 1) * sizeof(unsigned int)) );
            cudaSafeCall( cudaMemset(d_counters, 0, (nOctaves + 1) * sizeof(unsigned int)) );
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
114

115 116 117 118 119 120
            uploadConstant("cv::gpu::surf::c_max_candidates",    maxCandidates);
            uploadConstant("cv::gpu::surf::c_max_features",      maxFeatures);
            uploadConstant("cv::gpu::surf::c_img_rows",          img_rows);
            uploadConstant("cv::gpu::surf::c_img_cols",          img_cols);
            uploadConstant("cv::gpu::surf::c_nOctaveLayers",     nOctaveLayers);
            uploadConstant("cv::gpu::surf::c_hessianThreshold",  static_cast<float>(hessianThreshold));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
121

122
            bindTexture("cv::gpu::surf::imgTex", (DevMem2D)img);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
123

124 125
            integralBuffered(img, sum, intBuffer);
            bindTexture("cv::gpu::surf::sumTex", (DevMem2D_<unsigned int>)sum);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
126

127 128
            if (use_mask)
            {
129
                min(mask, 1.0, mask1);
130 131 132 133
                integralBuffered(mask1, maskSum, intBuffer);

                bindTexture("cv::gpu::surf::maskSumTex", (DevMem2D_<unsigned int>)maskSum);
            }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
134 135 136 137
        }

        ~SURF_GPU_Invoker()
        {
138 139 140
            cudaSafeCall( cudaFree(d_counters) );

            unbindTexture("cv::gpu::surf::imgTex");
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
141
            unbindTexture("cv::gpu::surf::sumTex");
142 143
            if (use_mask)
                unbindTexture("cv::gpu::surf::maskSumTex");
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
144 145 146 147
        }

        void detectKeypoints(GpuMat& keypoints)
        {
148 149 150 151
            ensureSizeIsEnough(img_rows * (nOctaveLayers + 2), img_cols, CV_32FC1, det);
            ensureSizeIsEnough(img_rows * (nOctaveLayers + 2), img_cols, CV_32FC1, trace);
            
            ensureSizeIsEnough(1, maxCandidates, CV_32SC4, maxPosBuffer);
152 153
            ensureSizeIsEnough(SURF_GPU::SF_FEATURE_STRIDE, maxFeatures, CV_32FC1, keypoints);
            keypoints.setTo(Scalar::all(0));
154

155
            for (int octave = 0; octave < nOctaves; ++octave)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
156
            {
157 158 159 160 161 162 163 164 165
                const int layer_rows = img_rows >> octave;
                const int layer_cols = img_cols >> octave;

                uploadConstant("cv::gpu::surf::c_octave",     octave);
                uploadConstant("cv::gpu::surf::c_layer_rows", layer_rows);
                uploadConstant("cv::gpu::surf::c_layer_cols", layer_cols);

                icvCalcLayerDetAndTrace_gpu(det, trace, img_rows, img_cols, octave, nOctaveLayers);

166
                icvFindMaximaInLayer_gpu(det, trace, maxPosBuffer.ptr<int4>(), d_counters + 1 + octave,
167 168 169
                    img_rows, img_cols, octave, use_mask, nOctaveLayers);

                unsigned int maxCounter;
170
                cudaSafeCall( cudaMemcpy(&maxCounter, d_counters + 1 + octave, sizeof(unsigned int), cudaMemcpyDeviceToHost) );
171
                maxCounter = std::min(maxCounter, static_cast<unsigned int>(maxCandidates));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
172

173 174
                if (maxCounter > 0)
                {
175
                    icvInterpolateKeypoint_gpu(det, maxPosBuffer.ptr<int4>(), maxCounter, 
176 177 178
                        keypoints.ptr<float>(SURF_GPU::SF_X), keypoints.ptr<float>(SURF_GPU::SF_Y),
                        keypoints.ptr<int>(SURF_GPU::SF_LAPLACIAN), keypoints.ptr<float>(SURF_GPU::SF_SIZE),
                        keypoints.ptr<float>(SURF_GPU::SF_HESSIAN), d_counters);
179
                }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
180
            }
181 182 183
            unsigned int featureCounter;
            cudaSafeCall( cudaMemcpy(&featureCounter, d_counters, sizeof(unsigned int), cudaMemcpyDeviceToHost) );
            featureCounter = std::min(featureCounter, static_cast<unsigned int>(maxFeatures));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
184

185 186
            keypoints.cols = featureCounter;

187
            if (!upright)
188
                findOrientation(keypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
189 190
        }

191
        void findOrientation(GpuMat& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
192
        {
193 194
            const int nFeatures = keypoints.cols;
            if (nFeatures > 0)
195
            {
196 197
                icvCalcOrientation_gpu(keypoints.ptr<float>(SURF_GPU::SF_X), keypoints.ptr<float>(SURF_GPU::SF_Y),
                    keypoints.ptr<float>(SURF_GPU::SF_SIZE), keypoints.ptr<float>(SURF_GPU::SF_DIR), nFeatures);
198
            }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
199 200 201 202
        }

        void computeDescriptors(const GpuMat& keypoints, GpuMat& descriptors, int descriptorSize)
        {
203 204
            const int nFeatures = keypoints.cols;
            if (nFeatures > 0)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
205
            {
206 207 208
                descriptors.create(nFeatures, descriptorSize, CV_32F);
                compute_descriptors_gpu(descriptors, keypoints.ptr<float>(SURF_GPU::SF_X), keypoints.ptr<float>(SURF_GPU::SF_Y),
                    keypoints.ptr<float>(SURF_GPU::SF_SIZE), keypoints.ptr<float>(SURF_GPU::SF_DIR), nFeatures);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
209 210 211 212 213
            }
        }

    private:
        GpuMat& sum;
214 215
        GpuMat& mask1;
        GpuMat& maskSum;
216 217 218 219
        GpuMat& intBuffer;

        GpuMat& det;
        GpuMat& trace;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
220 221 222 223

        GpuMat& maxPosBuffer;

        int img_cols, img_rows;
224 225

        bool use_mask;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
226

227 228
        bool upright;

229 230
        int maxCandidates;
        int maxFeatures;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
231

232
        unsigned int* d_counters;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
233 234 235
    };
}

236 237 238 239 240 241 242
cv::gpu::SURF_GPU::SURF_GPU()
{
    hessianThreshold = 100;
    extended = 1;
    nOctaves = 4;
    nOctaveLayers = 2;
    keypointsRatio = 0.01f;
243
    upright = false;
244 245
}

246
cv::gpu::SURF_GPU::SURF_GPU(double _threshold, int _nOctaves, int _nOctaveLayers, bool _extended, float _keypointsRatio, bool _upright)
247 248 249 250 251 252
{
    hessianThreshold = _threshold;
    extended = _extended;
    nOctaves = _nOctaves;
    nOctaveLayers = _nOctaveLayers;
    keypointsRatio = _keypointsRatio;
253
    upright = _upright;
254 255
}

Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
256 257 258 259 260 261 262
int cv::gpu::SURF_GPU::descriptorSize() const
{
    return extended ? 128 : 64;
}

void cv::gpu::SURF_GPU::uploadKeypoints(const vector<KeyPoint>& keypoints, GpuMat& keypointsGPU)
{
263 264 265
    if (keypoints.empty())
        keypointsGPU.release();
    else
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
266
    {
267 268 269 270 271 272 273 274
        Mat keypointsCPU(SURF_GPU::SF_FEATURE_STRIDE, keypoints.size(), CV_32FC1);

        float* kp_x = keypointsCPU.ptr<float>(SURF_GPU::SF_X);
        float* kp_y = keypointsCPU.ptr<float>(SURF_GPU::SF_Y);
        int* kp_laplacian = keypointsCPU.ptr<int>(SURF_GPU::SF_LAPLACIAN);
        float* kp_size = keypointsCPU.ptr<float>(SURF_GPU::SF_SIZE);
        float* kp_dir = keypointsCPU.ptr<float>(SURF_GPU::SF_DIR);
        float* kp_hessian = keypointsCPU.ptr<float>(SURF_GPU::SF_HESSIAN);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
275

276
        for (size_t i = 0, size = keypoints.size(); i < size; ++i)
277
        {
278
            const KeyPoint& kp = keypoints[i];
279 280 281 282 283 284
            kp_x[i] = kp.pt.x;
            kp_y[i] = kp.pt.y;
            kp_size[i] = kp.size;
            kp_dir[i] = kp.angle;
            kp_hessian[i] = kp.response;
            kp_laplacian[i] = 1;
285 286 287 288
        }

        keypointsGPU.upload(keypointsCPU);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
289 290
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
namespace
{
    int calcSize(int octave, int layer)
    {
        /* Wavelet size at first layer of first octave. */
        const int HAAR_SIZE0 = 9;

        /* Wavelet size increment between layers. This should be an even number,
         such that the wavelet sizes in an octave are either all even or all odd.
         This ensures that when looking for the neighbours of a sample, the layers
         above and below are aligned correctly. */
        const int HAAR_SIZE_INC = 6;

        return (HAAR_SIZE0 + HAAR_SIZE_INC * layer) << octave;
    }

307
    int getPointOctave(float size, const CvSURFParams& params)
308 309 310 311 312 313 314
    {
        int best_octave = 0;
        float min_diff = numeric_limits<float>::max();
        for (int octave = 1; octave < params.nOctaves; ++octave)
        {
            for (int layer = 0; layer < params.nOctaveLayers; ++layer)
            {
315
                float diff = std::abs(size - (float)calcSize(octave, layer));
316 317 318 319 320 321 322 323 324 325 326 327 328
                if (min_diff > diff)
                {
                    min_diff = diff;
                    best_octave = octave;
                    if (min_diff == 0)
                        return best_octave;
                }
            }
        }
        return best_octave;
    }
}

Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
329 330
void cv::gpu::SURF_GPU::downloadKeypoints(const GpuMat& keypointsGPU, vector<KeyPoint>& keypoints)
{
331 332 333
    const int nFeatures = keypointsGPU.cols;

    if (nFeatures == 0)
334 335 336
        keypoints.clear();
    else
    {
337 338
        CV_Assert(keypointsGPU.type() == CV_32FC1 && keypointsGPU.rows == SF_FEATURE_STRIDE);
        
339
        Mat keypointsCPU = keypointsGPU;
340 341
        
        keypoints.resize(nFeatures);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
342

343 344 345 346 347 348 349 350
        float* kp_x = keypointsCPU.ptr<float>(SF_X);
        float* kp_y = keypointsCPU.ptr<float>(SF_Y);
        int* kp_laplacian = keypointsCPU.ptr<int>(SF_LAPLACIAN);
        float* kp_size = keypointsCPU.ptr<float>(SF_SIZE);
        float* kp_dir = keypointsCPU.ptr<float>(SF_DIR);
        float* kp_hessian = keypointsCPU.ptr<float>(SF_HESSIAN);

        for (int i = 0; i < nFeatures; ++i)
351
        {
352
            KeyPoint& kp = keypoints[i];
353 354 355 356 357 358 359
            kp.pt.x = kp_x[i];
            kp.pt.y = kp_y[i];
            kp.class_id = kp_laplacian[i];
            kp.size = kp_size[i];
            kp.angle = kp_dir[i];
            kp.response = kp_hessian[i];
            kp.octave = getPointOctave(kp.size, *this);
360
        }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
361 362 363 364 365
    }
}

void cv::gpu::SURF_GPU::downloadDescriptors(const GpuMat& descriptorsGPU, vector<float>& descriptors)
{
366 367 368 369 370
    if (descriptorsGPU.empty())
        descriptors.clear();
    else
    {
        CV_Assert(descriptorsGPU.type() == CV_32F);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
371

372 373 374 375
        descriptors.resize(descriptorsGPU.rows * descriptorsGPU.cols);
        Mat descriptorsCPU(descriptorsGPU.size(), CV_32F, &descriptors[0]);
        descriptorsGPU.download(descriptorsCPU);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
376 377
}

378
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
379
{
380 381 382
    if (!img.empty())
    {
        SURF_GPU_Invoker surf(*this, img, mask);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
383

384 385
        surf.detectKeypoints(keypoints);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
386 387
}

388
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints, GpuMat& descriptors, 
389
                                   bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
390
{
391 392 393
    if (!img.empty())
    {
        SURF_GPU_Invoker surf(*this, img, mask);
394
    
395 396
        if (!useProvidedKeypoints)
            surf.detectKeypoints(keypoints);
397
        else if (!upright)
398
        {
399
            surf.findOrientation(keypoints);
400
        }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
401

402 403
        surf.computeDescriptors(keypoints, descriptors, descriptorSize());
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
404 405
}

406
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector<KeyPoint>& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
407 408 409
{
    GpuMat keypointsGPU;

410
    (*this)(img, mask, keypointsGPU);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
411 412 413 414

    downloadKeypoints(keypointsGPU, keypoints);
}

415
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector<KeyPoint>& keypoints, 
416
    GpuMat& descriptors, bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
417 418 419 420 421 422
{
    GpuMat keypointsGPU;

    if (useProvidedKeypoints)
        uploadKeypoints(keypoints, keypointsGPU);    

423
    (*this)(img, mask, keypointsGPU, descriptors, useProvidedKeypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
424 425 426 427

    downloadKeypoints(keypointsGPU, keypoints);
}

428
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector<KeyPoint>& keypoints, 
429
    vector<float>& descriptors, bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
430 431 432
{
    GpuMat descriptorsGPU;

433
    (*this)(img, mask, keypoints, descriptorsGPU, useProvidedKeypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
434 435 436 437 438

    downloadDescriptors(descriptorsGPU, descriptors);
}

#endif /* !defined (HAVE_CUDA) */