surf.cpp 16.4 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 72 73 74 75
    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);

    void icvInterpolateKeypoint_gpu(const PtrStepf& det, const int4* maxPosBuffer, unsigned int maxCounter, KeyPoint_GPU* featuresBuffer, unsigned int* featureCounter);

    void icvCalcOrientation_gpu(const KeyPoint_GPU* featureBuffer, int nFeatures, KeyPoint_GPU* keypoints, unsigned int* keypointCounter);

Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
76 77 78 79 80 81 82
    void compute_descriptors_gpu(const DevMem2Df& descriptors, const KeyPoint_GPU* features, int nFeatures);
}}}

using namespace cv::gpu::surf;

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

89
            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
90

91
            maxPosBuffer(surf.maxPosBuffer), featuresBuffer(surf.featuresBuffer), keypointsBuffer(surf.keypointsBuffer),
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
92 93 94

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

95 96 97
            use_mask(!mask.empty()),

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

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

108 109 110 111
            CV_Assert(maxKeypoints > 0);
            
            cudaSafeCall( cudaMalloc((void**)&d_counters, (nOctaves + 2) * sizeof(unsigned int)) );
            cudaSafeCall( cudaMemset(d_counters, 0, (nOctaves + 2) * sizeof(unsigned int)) );
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
112

113 114 115 116 117 118 119
            uploadConstant("cv::gpu::surf::c_max_candidates",    maxCandidates);
            uploadConstant("cv::gpu::surf::c_max_features",      maxFeatures);
            uploadConstant("cv::gpu::surf::c_max_keypoints",     maxKeypoints);
            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
120

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

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

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

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

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

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

        void detectKeypoints(GpuMat& keypoints)
        {
147 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);
            ensureSizeIsEnough(1, maxFeatures, CV_32FC(6), featuresBuffer);
152

153
            for (int octave = 0; octave < nOctaves; ++octave)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
154
            {
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
                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);

                icvFindMaximaInLayer_gpu(det, trace, maxPosBuffer.ptr<int4>(), d_counters + 2 + octave,
                    img_rows, img_cols, octave, use_mask, nOctaveLayers);

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

171 172
                if (maxCounter > 0)
                {
173 174
                    icvInterpolateKeypoint_gpu(det, maxPosBuffer.ptr<int4>(), maxCounter, 
                        featuresBuffer.ptr<KeyPoint_GPU>(), d_counters);
175
                }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
176
            }
177 178 179
            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
180

181 182 183 184 185 186 187 188 189
            if (!upright)
                findOrientation(featuresBuffer.colRange(0, featureCounter), keypoints);
            else
            {
                if (featureCounter > 0)
                    featuresBuffer.colRange(0, featureCounter).copyTo(keypoints);
                else
                    keypoints.release();
            }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
190 191
        }

192
        void findOrientation(const GpuMat& features, GpuMat& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
193
        {
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
            if (features.cols > 0)
            {
                ensureSizeIsEnough(1, maxKeypoints, CV_32FC(6), keypointsBuffer);

                icvCalcOrientation_gpu(features.ptr<KeyPoint_GPU>(), features.cols, keypointsBuffer.ptr<KeyPoint_GPU>(), 
                    d_counters + 1);

                unsigned int keypointsCounter;
                cudaSafeCall( cudaMemcpy(&keypointsCounter, d_counters + 1, sizeof(unsigned int), cudaMemcpyDeviceToHost) );
                keypointsCounter = std::min(keypointsCounter, static_cast<unsigned int>(maxKeypoints));

                if (keypointsCounter > 0)
                    keypointsBuffer.colRange(0, keypointsCounter).copyTo(keypoints);
                else
                    keypoints.release();
            }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
210 211 212 213 214 215 216
        }

        void computeDescriptors(const GpuMat& keypoints, GpuMat& descriptors, int descriptorSize)
        {
            if (keypoints.cols > 0)
            {
                descriptors.create(keypoints.cols, descriptorSize, CV_32F);
217
                compute_descriptors_gpu(descriptors, keypoints.ptr<KeyPoint_GPU>(), keypoints.cols);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
218 219 220 221 222
            }
        }

    private:
        GpuMat& sum;
223 224
        GpuMat& mask1;
        GpuMat& maskSum;
225 226 227 228
        GpuMat& intBuffer;

        GpuMat& det;
        GpuMat& trace;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
229 230 231

        GpuMat& maxPosBuffer;
        GpuMat& featuresBuffer;
232
        GpuMat& keypointsBuffer;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
233 234

        int img_cols, img_rows;
235 236

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

238 239
        bool upright;

240 241 242
        int maxCandidates;
        int maxFeatures;
        int maxKeypoints;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
243

244
        unsigned int* d_counters;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
245 246 247
    };
}

248 249 250 251 252 253 254
cv::gpu::SURF_GPU::SURF_GPU()
{
    hessianThreshold = 100;
    extended = 1;
    nOctaves = 4;
    nOctaveLayers = 2;
    keypointsRatio = 0.01f;
255
    upright = false;
256 257
}

258
cv::gpu::SURF_GPU::SURF_GPU(double _threshold, int _nOctaves, int _nOctaveLayers, bool _extended, float _keypointsRatio, bool _upright)
259 260 261 262 263 264
{
    hessianThreshold = _threshold;
    extended = _extended;
    nOctaves = _nOctaves;
    nOctaveLayers = _nOctaveLayers;
    keypointsRatio = _keypointsRatio;
265
    upright = _upright;
266 267
}

Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
268 269 270 271 272 273 274
int cv::gpu::SURF_GPU::descriptorSize() const
{
    return extended ? 128 : 64;
}

void cv::gpu::SURF_GPU::uploadKeypoints(const vector<KeyPoint>& keypoints, GpuMat& keypointsGPU)
{
275 276 277
    if (keypoints.empty())
        keypointsGPU.release();
    else
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
278
    {
279
        Mat keypointsCPU(1, keypoints.size(), CV_32FC(6));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
280

281
        for (size_t i = 0; i < keypoints.size(); ++i)
282
        {
283 284
            const KeyPoint& kp = keypoints[i];
            KeyPoint_GPU& gkp = keypointsCPU.ptr<KeyPoint_GPU>()[i];
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
285

286 287
            gkp.x = kp.pt.x;
            gkp.y = kp.pt.y;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
288

289 290
            gkp.laplacian = 1.0f;

291
            gkp.size = kp.size;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
292

293 294
            gkp.dir = kp.angle;
            gkp.hessian = kp.response;
295 296 297 298
        }

        keypointsGPU.upload(keypointsCPU);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
299 300
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
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;
    }

    int getPointOctave(const KeyPoint_GPU& kpt, const CvSURFParams& params)
    {
        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)
            {
                float diff = std::abs(kpt.size - (float)calcSize(octave, layer));
                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
339 340
void cv::gpu::SURF_GPU::downloadKeypoints(const GpuMat& keypointsGPU, vector<KeyPoint>& keypoints)
{
341 342 343 344 345
    if (keypointsGPU.empty())
        keypoints.clear();
    else
    {
        CV_Assert(keypointsGPU.type() == CV_32FC(6) && keypointsGPU.isContinuous());
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
346

347 348
        Mat keypointsCPU = keypointsGPU;
        keypoints.resize(keypointsGPU.cols);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
349

350
        for (int i = 0; i < keypointsGPU.cols; ++i)
351
        {
352 353
            KeyPoint& kp = keypoints[i];
            const KeyPoint_GPU& gkp = keypointsCPU.ptr<KeyPoint_GPU>()[i];
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
354

355 356
            kp.pt.x = gkp.x;
            kp.pt.y = gkp.y;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
357

358
            kp.size = gkp.size;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
359

360 361 362 363 364 365 366
            kp.angle = gkp.dir;

            kp.response = gkp.hessian;

            kp.octave = getPointOctave(gkp, *this);

            kp.class_id = static_cast<int>(gkp.laplacian);
367
        }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
368 369 370 371 372
    }
}

void cv::gpu::SURF_GPU::downloadDescriptors(const GpuMat& descriptorsGPU, vector<float>& descriptors)
{
373 374 375 376 377
    if (descriptorsGPU.empty())
        descriptors.clear();
    else
    {
        CV_Assert(descriptorsGPU.type() == CV_32F);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
378

379 380 381 382
        descriptors.resize(descriptorsGPU.rows * descriptorsGPU.cols);
        Mat descriptorsCPU(descriptorsGPU.size(), CV_32F, &descriptors[0]);
        descriptorsGPU.download(descriptorsCPU);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
383 384
}

385
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
386
{
387 388 389
    if (!img.empty())
    {
        SURF_GPU_Invoker surf(*this, img, mask);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
390

391 392
        surf.detectKeypoints(keypoints);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
393 394
}

395
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints, GpuMat& descriptors, 
396
                                   bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
397
{
398 399 400
    if (!img.empty())
    {
        SURF_GPU_Invoker surf(*this, img, mask);
401
    
402 403
        if (!useProvidedKeypoints)
            surf.detectKeypoints(keypoints);
404
        else if (!upright)
405 406 407 408 409
        {
            GpuMat keypointsBuf;
            surf.findOrientation(keypoints, keypointsBuf);
            keypointsBuf.copyTo(keypoints);
        }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
410

411 412
        surf.computeDescriptors(keypoints, descriptors, descriptorSize());
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
413 414
}

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

419
    (*this)(img, mask, keypointsGPU);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
420 421 422 423

    downloadKeypoints(keypointsGPU, keypoints);
}

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

    if (useProvidedKeypoints)
        uploadKeypoints(keypoints, keypointsGPU);    

432
    (*this)(img, mask, keypointsGPU, descriptors, useProvidedKeypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
433 434 435 436

    downloadKeypoints(keypointsGPU, keypoints);
}

437
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector<KeyPoint>& keypoints, 
438
    vector<float>& descriptors, bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
439 440 441
{
    GpuMat descriptorsGPU;

442
    (*this)(img, mask, keypoints, descriptorsGPU, useProvidedKeypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
443 444 445 446 447

    downloadDescriptors(descriptorsGPU, descriptors);
}

#endif /* !defined (HAVE_CUDA) */