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(); }
62
void cv::gpu::SURF_GPU::releaseMemory() { throw_nogpu(); }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
63 64 65

#else /* !defined (HAVE_CUDA) */

66
namespace cv { namespace gpu { namespace device
67
{
68 69 70 71
    namespace surf
    {
        void loadGlobalConstants(int maxCandidates, int maxFeatures, int img_rows, int img_cols, int nOctaveLayers, float hessianThreshold);
        void loadOctaveConstants(int octave, int layer_rows, int layer_cols);
72

73 74 75
        void bindImgTex(DevMem2Db img);
        void bindSumTex(DevMem2D_<unsigned int> sum);
        void bindMaskSumTex(DevMem2D_<unsigned int> maskSum);
76

77
        void icvCalcLayerDetAndTrace_gpu(const PtrStepf& det, const PtrStepf& trace, int img_rows, int img_cols, int octave, int nOctaveLayers);
78

79 80
        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);
81

82
        void icvInterpolateKeypoint_gpu(const PtrStepf& det, const int4* maxPosBuffer, unsigned int maxCounter,
83
            float* featureX, float* featureY, int* featureLaplacian, int* featureOctave, float* featureSize, float* featureHessian,
84
            unsigned int* featureCounter);
85

86
        void icvCalcOrientation_gpu(const float* featureX, const float* featureY, const float* featureSize, float* featureDir, int nFeatures);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
87

88
        void compute_descriptors_gpu(const DevMem2Df& descriptors,
89 90 91
            const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures);
    }
}}}
92

93
using namespace ::cv::gpu::device::surf;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
94 95 96

namespace
{
97 98 99 100 101 102 103 104 105 106 107 108 109 110
    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;
    }
111

112
    class SURF_GPU_Invoker
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
113 114
    {
    public:
115
        SURF_GPU_Invoker(SURF_GPU& surf, const GpuMat& img, const GpuMat& mask) :
116
            surf_(surf),
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
117
            img_cols(img.cols), img_rows(img.rows),
118
            use_mask(!mask.empty())
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
119
        {
120
            CV_Assert(!img.empty() && img.type() == CV_8UC1);
121
            CV_Assert(mask.empty() || (mask.size() == img.size() && mask.type() == CV_8UC1));
122
            CV_Assert(surf_.nOctaves > 0 && surf_.nOctaveLayers > 0);
123 124 125

            if (!TargetArchs::builtWith(GLOBAL_ATOMICS) || !DeviceInfo().supports(GLOBAL_ATOMICS))
                CV_Error(CV_StsNotImplemented, "The device doesn't support global atomics");
126

127
            const int min_size = calcSize(surf_.nOctaves - 1, 0);
128 129
            CV_Assert(img_rows - min_size >= 0);
            CV_Assert(img_cols - min_size >= 0);
130

131 132 133
            const int layer_rows = img_rows >> (surf_.nOctaves - 1);
            const int layer_cols = img_cols >> (surf_.nOctaves - 1);
            const int min_margin = ((calcSize((surf_.nOctaves - 1), 2) >> 1) >> (surf_.nOctaves - 1)) + 1;
134 135
            CV_Assert(layer_rows - 2 * min_margin > 0);
            CV_Assert(layer_cols - 2 * min_margin > 0);
136

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

140
            CV_Assert(maxFeatures > 0);
141

142
            counters.create(1, surf_.nOctaves + 1, CV_32SC1);
143
            counters.setTo(Scalar::all(0));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
144

145
            loadGlobalConstants(maxCandidates, maxFeatures, img_rows, img_cols, surf_.nOctaveLayers, static_cast<float>(surf_.hessianThreshold));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
146

147
            bindImgTex(img);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
148

149 150
            integralBuffered(img, surf_.sum, surf_.intBuffer);
            bindSumTex(surf_.sum);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
151

152 153
            if (use_mask)
            {
154 155 156
                min(mask, 1.0, surf_.mask1);
                integralBuffered(surf_.mask1, surf_.maskSum, surf_.intBuffer);
                bindMaskSumTex(surf_.maskSum);
157
            }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
158 159 160 161
        }

        void detectKeypoints(GpuMat& keypoints)
        {
162 163
            ensureSizeIsEnough(img_rows * (surf_.nOctaveLayers + 2), img_cols, CV_32FC1, surf_.det);
            ensureSizeIsEnough(img_rows * (surf_.nOctaveLayers + 2), img_cols, CV_32FC1, surf_.trace);
164

165
            ensureSizeIsEnough(1, maxCandidates, CV_32SC4, surf_.maxPosBuffer);
166
            ensureSizeIsEnough(SURF_GPU::ROWS_COUNT, maxFeatures, CV_32FC1, keypoints);
167
            keypoints.setTo(Scalar::all(0));
168

169
            for (int octave = 0; octave < surf_.nOctaves; ++octave)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
170
            {
171 172 173
                const int layer_rows = img_rows >> octave;
                const int layer_cols = img_cols >> octave;

174
                loadOctaveConstants(octave, layer_rows, layer_cols);
175

176
                icvCalcLayerDetAndTrace_gpu(surf_.det, surf_.trace, img_rows, img_cols, octave, surf_.nOctaveLayers);
177

178 179
                icvFindMaximaInLayer_gpu(surf_.det, surf_.trace, surf_.maxPosBuffer.ptr<int4>(), counters.ptr<unsigned int>() + 1 + octave,
                    img_rows, img_cols, octave, use_mask, surf_.nOctaveLayers);
180 181

                unsigned int maxCounter;
182
                cudaSafeCall( cudaMemcpy(&maxCounter, counters.ptr<unsigned int>() + 1 + octave, sizeof(unsigned int), cudaMemcpyDeviceToHost) );
183
                maxCounter = std::min(maxCounter, static_cast<unsigned int>(maxCandidates));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
184

185 186
                if (maxCounter > 0)
                {
187
                    icvInterpolateKeypoint_gpu(surf_.det, surf_.maxPosBuffer.ptr<int4>(), maxCounter,
188
                        keypoints.ptr<float>(SURF_GPU::X_ROW), keypoints.ptr<float>(SURF_GPU::Y_ROW),
189 190
                        keypoints.ptr<int>(SURF_GPU::LAPLACIAN_ROW), keypoints.ptr<int>(SURF_GPU::OCTAVE_ROW),
                        keypoints.ptr<float>(SURF_GPU::SIZE_ROW), keypoints.ptr<float>(SURF_GPU::HESSIAN_ROW),
191
                        counters.ptr<unsigned int>());
192
                }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
193
            }
194
            unsigned int featureCounter;
195
            cudaSafeCall( cudaMemcpy(&featureCounter, counters.ptr<unsigned int>(), sizeof(unsigned int), cudaMemcpyDeviceToHost) );
196
            featureCounter = std::min(featureCounter, static_cast<unsigned int>(maxFeatures));
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
197

198 199
            keypoints.cols = featureCounter;

200
            if (surf_.upright)
201
                keypoints.row(SURF_GPU::ANGLE_ROW).setTo(Scalar::all(360.0 - 90.0));
202
            else
203
                findOrientation(keypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
204 205
        }

206
        void findOrientation(GpuMat& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
207
        {
208 209
            const int nFeatures = keypoints.cols;
            if (nFeatures > 0)
210
            {
211 212
                icvCalcOrientation_gpu(keypoints.ptr<float>(SURF_GPU::X_ROW), keypoints.ptr<float>(SURF_GPU::Y_ROW),
                    keypoints.ptr<float>(SURF_GPU::SIZE_ROW), keypoints.ptr<float>(SURF_GPU::ANGLE_ROW), nFeatures);
213
            }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
214 215 216 217
        }

        void computeDescriptors(const GpuMat& keypoints, GpuMat& descriptors, int descriptorSize)
        {
218 219
            const int nFeatures = keypoints.cols;
            if (nFeatures > 0)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
220
            {
221
                ensureSizeIsEnough(nFeatures, descriptorSize, CV_32F, descriptors);
222 223
                compute_descriptors_gpu(descriptors, keypoints.ptr<float>(SURF_GPU::X_ROW), keypoints.ptr<float>(SURF_GPU::Y_ROW),
                    keypoints.ptr<float>(SURF_GPU::SIZE_ROW), keypoints.ptr<float>(SURF_GPU::ANGLE_ROW), nFeatures);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
224 225 226 227
            }
        }

    private:
228
        SURF_GPU& surf_;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
229 230

        int img_cols, img_rows;
231 232

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

234 235
        int maxCandidates;
        int maxFeatures;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
236

237
        GpuMat counters;
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
238 239 240
    };
}

241 242 243
cv::gpu::SURF_GPU::SURF_GPU()
{
    hessianThreshold = 100;
244
    extended = true;
245 246 247
    nOctaves = 4;
    nOctaveLayers = 2;
    keypointsRatio = 0.01f;
248
    upright = false;
249 250
}

251
cv::gpu::SURF_GPU::SURF_GPU(double _threshold, int _nOctaves, int _nOctaveLayers, bool _extended, float _keypointsRatio, bool _upright)
252 253 254 255 256 257
{
    hessianThreshold = _threshold;
    extended = _extended;
    nOctaves = _nOctaves;
    nOctaveLayers = _nOctaveLayers;
    keypointsRatio = _keypointsRatio;
258
    upright = _upright;
259 260
}

Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
261 262 263 264 265 266 267
int cv::gpu::SURF_GPU::descriptorSize() const
{
    return extended ? 128 : 64;
}

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

274 275 276 277 278 279 280
        float* kp_x = keypointsCPU.ptr<float>(SURF_GPU::X_ROW);
        float* kp_y = keypointsCPU.ptr<float>(SURF_GPU::Y_ROW);
        int* kp_laplacian = keypointsCPU.ptr<int>(SURF_GPU::LAPLACIAN_ROW);
        int* kp_octave = keypointsCPU.ptr<int>(SURF_GPU::OCTAVE_ROW);
        float* kp_size = keypointsCPU.ptr<float>(SURF_GPU::SIZE_ROW);
        float* kp_dir = keypointsCPU.ptr<float>(SURF_GPU::ANGLE_ROW);
        float* kp_hessian = keypointsCPU.ptr<float>(SURF_GPU::HESSIAN_ROW);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
281

282
        for (size_t i = 0, size = keypoints.size(); i < size; ++i)
283
        {
284
            const KeyPoint& kp = keypoints[i];
285 286
            kp_x[i] = kp.pt.x;
            kp_y[i] = kp.pt.y;
287
            kp_octave[i] = kp.octave;
288 289 290 291
            kp_size[i] = kp.size;
            kp_dir[i] = kp.angle;
            kp_hessian[i] = kp.response;
            kp_laplacian[i] = 1;
292 293 294 295
        }

        keypointsGPU.upload(keypointsCPU);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
296 297 298 299
}

void cv::gpu::SURF_GPU::downloadKeypoints(const GpuMat& keypointsGPU, vector<KeyPoint>& keypoints)
{
300 301 302
    const int nFeatures = keypointsGPU.cols;

    if (nFeatures == 0)
303 304 305
        keypoints.clear();
    else
    {
306
        CV_Assert(keypointsGPU.type() == CV_32FC1 && keypointsGPU.rows == ROWS_COUNT);
307

308
        Mat keypointsCPU(keypointsGPU);
309

310
        keypoints.resize(nFeatures);
311

312 313 314 315 316 317 318
        float* kp_x = keypointsCPU.ptr<float>(SURF_GPU::X_ROW);
        float* kp_y = keypointsCPU.ptr<float>(SURF_GPU::Y_ROW);
        int* kp_laplacian = keypointsCPU.ptr<int>(SURF_GPU::LAPLACIAN_ROW);
        int* kp_octave = keypointsCPU.ptr<int>(SURF_GPU::OCTAVE_ROW);
        float* kp_size = keypointsCPU.ptr<float>(SURF_GPU::SIZE_ROW);
        float* kp_dir = keypointsCPU.ptr<float>(SURF_GPU::ANGLE_ROW);
        float* kp_hessian = keypointsCPU.ptr<float>(SURF_GPU::HESSIAN_ROW);
319 320

        for (int i = 0; i < nFeatures; ++i)
321
        {
322
            KeyPoint& kp = keypoints[i];
323 324 325
            kp.pt.x = kp_x[i];
            kp.pt.y = kp_y[i];
            kp.class_id = kp_laplacian[i];
326
            kp.octave = kp_octave[i];
327 328 329
            kp.size = kp_size[i];
            kp.angle = kp_dir[i];
            kp.response = kp_hessian[i];
330
        }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
331 332 333 334 335
    }
}

void cv::gpu::SURF_GPU::downloadDescriptors(const GpuMat& descriptorsGPU, vector<float>& descriptors)
{
336 337 338 339 340
    if (descriptorsGPU.empty())
        descriptors.clear();
    else
    {
        CV_Assert(descriptorsGPU.type() == CV_32F);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
341

342 343 344 345
        descriptors.resize(descriptorsGPU.rows * descriptorsGPU.cols);
        Mat descriptorsCPU(descriptorsGPU.size(), CV_32F, &descriptors[0]);
        descriptorsGPU.download(descriptorsCPU);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
346 347
}

348
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
349
{
350 351 352
    if (!img.empty())
    {
        SURF_GPU_Invoker surf(*this, img, mask);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
353

354 355
        surf.detectKeypoints(keypoints);
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
356 357
}

358
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints, GpuMat& descriptors,
359
                                   bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
360
{
361 362 363
    if (!img.empty())
    {
        SURF_GPU_Invoker surf(*this, img, mask);
364

365 366
        if (!useProvidedKeypoints)
            surf.detectKeypoints(keypoints);
367
        else if (!upright)
368
        {
369
            surf.findOrientation(keypoints);
370
        }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
371

372 373
        surf.computeDescriptors(keypoints, descriptors, descriptorSize());
    }
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
374 375
}

376
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector<KeyPoint>& keypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
377 378 379
{
    GpuMat keypointsGPU;

380
    (*this)(img, mask, keypointsGPU);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
381 382 383 384

    downloadKeypoints(keypointsGPU, keypoints);
}

385
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector<KeyPoint>& keypoints,
386
    GpuMat& descriptors, bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
387 388 389 390
{
    GpuMat keypointsGPU;

    if (useProvidedKeypoints)
391
        uploadKeypoints(keypoints, keypointsGPU);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
392

393
    (*this)(img, mask, keypointsGPU, descriptors, useProvidedKeypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
394 395 396 397

    downloadKeypoints(keypointsGPU, keypoints);
}

398
void cv::gpu::SURF_GPU::operator()(const GpuMat& img, const GpuMat& mask, vector<KeyPoint>& keypoints,
399
    vector<float>& descriptors, bool useProvidedKeypoints)
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
400 401 402
{
    GpuMat descriptorsGPU;

403
    (*this)(img, mask, keypoints, descriptorsGPU, useProvidedKeypoints);
Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
404 405 406 407

    downloadDescriptors(descriptorsGPU, descriptors);
}

408
void cv::gpu::SURF_GPU::releaseMemory()
409
{
410
    sum.release();
411 412 413 414 415 416 417 418
    mask1.release();
    maskSum.release();
    intBuffer.release();
    det.release();
    trace.release();
    maxPosBuffer.release();
}

Vladislav Vinogradov's avatar
Vladislav Vinogradov committed
419
#endif /* !defined (HAVE_CUDA) */