gftt.cpp 13.3 KB
Newer Older
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
/*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) 2010-2012, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Peng Xiao, pengxiao@outlook.com
//
// 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
Andrey Pavlenko's avatar
Andrey Pavlenko committed
28
//     and/or other materials provided with the distribution.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
//
//   * 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 implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
46
#include "opencl_kernels.hpp"
47 48 49 50

using namespace cv;
using namespace cv::ocl;

51
// currently sort procedure on the host is more efficient
52 53
static bool use_cpu_sorter = true;

54 55
// compact structure for corners
struct DefCorner
56
{
57 58 59 60 61 62 63 64
    float eig;  //eigenvalue of corner
    short x;    //x coordinate of corner point
    short y;    //y coordinate of corner point
} ;

// compare procedure for corner
//it is used for sort on the host side
struct DefCornerCompare
65
{
66
    bool operator()(const DefCorner a, const DefCorner b) const
67
    {
68
        return a.eig > b.eig;
69 70 71
    }
};

72 73
// sort corner point using opencl bitonicosrt implementation
static void sortCorners_caller(oclMat& corners, const int count)
74
{
75 76 77 78 79 80 81 82 83
    Context * cxt = Context::getContext();
    int     GS = count/2;
    int     LS = min(255,GS);
    size_t  globalThreads[3] = {GS, 1, 1};
    size_t  localThreads[3]  = {LS, 1, 1};

    // 2^numStages should be equal to count or the output is invalid
    int numStages = 0;
    for(int i = count; i > 1; i >>= 1)
84
    {
85
        ++numStages;
86
    }
87 88 89 90 91 92
    const int argc = 4;
    std::vector< std::pair<size_t, const void *> > args(argc);
    std::string kernelname = "sortCorners_bitonicSort";
    args[0] = std::make_pair(sizeof(cl_mem), (void *)&corners.data);
    args[1] = std::make_pair(sizeof(cl_int), (void *)&count);
    for(int stage = 0; stage < numStages; ++stage)
93
    {
94 95 96 97 98 99
        args[2] = std::make_pair(sizeof(cl_int), (void *)&stage);
        for(int passOfStage = 0; passOfStage < stage + 1; ++passOfStage)
        {
            args[3] = std::make_pair(sizeof(cl_int), (void *)&passOfStage);
            openCLExecuteKernel(cxt, &imgproc_gftt, kernelname, globalThreads, localThreads, args, -1, -1);
        }
100
    }
101
}
102

103
// find corners on matrix and put it into array
krodyush's avatar
krodyush committed
104
static void findCorners_caller(
105 106 107 108 109 110
    const oclMat&   eig_mat,        //input matrix worth eigenvalues
    oclMat&         eigMinMax,      //input with min and max values of eigenvalues
    const float     qualityLevel,
    const oclMat&   mask,
    oclMat&         corners,        //output array with detected corners
    oclMat&         counter)        //output value with number of detected corners, have to be 0 before call
111
{
112
    string  opt;
113 114 115 116 117 118
    Context * cxt = Context::getContext();

    std::vector< std::pair<size_t, const void*> > args;

    const int mask_strip = mask.step / mask.elemSize1();

119
    args.push_back(make_pair( sizeof(cl_mem),   (void*)&(eig_mat.data)));
120

121 122
    int src_pitch = (int)eig_mat.step;
    args.push_back(make_pair( sizeof(cl_int),   (void*)&src_pitch ));
123 124 125
    args.push_back(make_pair( sizeof(cl_mem),   (void*)&mask.data ));
    args.push_back(make_pair( sizeof(cl_mem),   (void*)&corners.data ));
    args.push_back(make_pair( sizeof(cl_int),   (void*)&mask_strip));
126 127 128 129 130 131 132 133
    args.push_back(make_pair( sizeof(cl_mem),   (void*)&eigMinMax.data ));
    args.push_back(make_pair( sizeof(cl_float), (void*)&qualityLevel ));
    args.push_back(make_pair( sizeof(cl_int),   (void*)&eig_mat.rows ));
    args.push_back(make_pair( sizeof(cl_int),   (void*)&eig_mat.cols ));
    args.push_back(make_pair( sizeof(cl_int),   (void*)&corners.cols ));
    args.push_back(make_pair( sizeof(cl_mem),   (void*)&counter.data ));

    size_t globalThreads[3] = {eig_mat.cols, eig_mat.rows, 1};
134
    size_t localThreads[3]  = {16, 16, 1};
135 136
    if(!mask.empty())
        opt += " -D WITH_MASK=1";
137

138 139 140 141 142 143 144 145 146 147 148 149 150 151
     openCLExecuteKernel(cxt, &imgproc_gftt, "findCorners", globalThreads, localThreads, args, -1, -1, opt.c_str());
}


static void minMaxEig_caller(const oclMat &src, oclMat &dst, oclMat & tozero)
{
    size_t groupnum = src.clCxt->getDeviceInfo().maxComputeUnits;
    CV_Assert(groupnum != 0);

    int dbsize = groupnum * 2 * src.elemSize();
    ensureSizeIsEnough(1, dbsize, CV_8UC1, dst);

    cl_mem dst_data = reinterpret_cast<cl_mem>(dst.data);

Ilya Lavrenov's avatar
Ilya Lavrenov committed
152 153 154
    int vElemSize = src.elemSize1();
    int src_step = src.step / vElemSize, src_offset = src.offset / vElemSize;
    int total = src.size().area();
155

Ilya Lavrenov's avatar
Ilya Lavrenov committed
156 157
    {
        // first parallel pass
158 159
        vector<pair<size_t , const void *> > args;
        args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data));
Ilya Lavrenov's avatar
Ilya Lavrenov committed
160
        args.push_back( make_pair( sizeof(cl_int) , (void *)&src_step));
161
        args.push_back( make_pair( sizeof(cl_int) , (void *)&src_offset));
Ilya Lavrenov's avatar
Ilya Lavrenov committed
162 163 164
        args.push_back( make_pair( sizeof(cl_int) , (void *)&src.rows ));
        args.push_back( make_pair( sizeof(cl_int) , (void *)&src.cols ));
        args.push_back( make_pair( sizeof(cl_int) , (void *)&total));
165
        args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum));
Ilya Lavrenov's avatar
Ilya Lavrenov committed
166
        args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst_data ));
167 168 169
        size_t globalThreads[3] = {groupnum * 256, 1, 1};
        size_t localThreads[3] = {256, 1, 1};
        openCLExecuteKernel(src.clCxt, &arithm_minMax, "arithm_op_minMax", globalThreads, localThreads,
Ilya Lavrenov's avatar
Ilya Lavrenov committed
170
                            args, -1, -1, "-D T=float -D DEPTH_5 -D vlen=1");
171 172
    }

Ilya Lavrenov's avatar
Ilya Lavrenov committed
173 174
    {
        // run final "serial" kernel to find accumulate results from threads and reset corner counter
175 176 177 178 179 180 181 182 183
        vector<pair<size_t , const void *> > args;
        args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst_data ));
        args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum ));
        args.push_back( make_pair( sizeof(cl_mem) , (void *)&tozero.data ));
        size_t globalThreads[3] = {1, 1, 1};
        size_t localThreads[3] = {1, 1, 1};
        openCLExecuteKernel(src.clCxt, &imgproc_gftt, "arithm_op_minMax_final", globalThreads, localThreads,
                            args, -1, -1);
    }
184 185 186 187 188 189 190 191 192 193
}

void cv::ocl::GoodFeaturesToTrackDetector_OCL::operator ()(const oclMat& image, oclMat& corners, const oclMat& mask)
{
    CV_Assert(qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0);
    CV_Assert(mask.empty() || (mask.type() == CV_8UC1 && mask.size() == image.size()));

    ensureSizeIsEnough(image.size(), CV_32F, eig_);

    if (useHarrisDetector)
194
        cornerHarris_dxdy(image, eig_, Dx_, Dy_, blockSize, 3, harrisK);
195 196 197
    else
        cornerMinEigenVal_dxdy(image, eig_, Dx_, Dy_, blockSize, 3);

198 199 200 201
    ensureSizeIsEnough(1,1, CV_32SC1, counter_);

    // find max eigenvalue and reset detected counters
    minMaxEig_caller(eig_,eig_minmax_,counter_);
202

203 204
    // allocate buffer for kernels
    int corner_array_size = std::max(1024, static_cast<int>(image.size().area() * 0.05));
205

206 207 208
    if(!use_cpu_sorter)
    {   // round to 2^n
        unsigned int n=1;
209
        for(n=1;n<(unsigned int)corner_array_size;n<<=1) ;
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
        corner_array_size = (int)n;

        ensureSizeIsEnough(1, corner_array_size , CV_32FC2, tmpCorners_);

        // set to 0 to be able use bitonic sort on whole 2^n array
        tmpCorners_.setTo(0);
    }
    else
    {
        ensureSizeIsEnough(1, corner_array_size , CV_32FC2, tmpCorners_);
    }

    int total = tmpCorners_.cols; // by default the number of corner is full array
    vector<DefCorner>   tmp(tmpCorners_.cols); // input buffer with corner for HOST part of algorithm

    //find points with high eigenvalue and put it into the output array
    findCorners_caller(
        eig_,
        eig_minmax_,
        static_cast<float>(qualityLevel),
230 231
        mask,
        tmpCorners_,
232 233 234 235 236 237 238 239 240 241 242 243 244
        counter_);

    if(!use_cpu_sorter)
    {// sort detected corners on deivce side
        sortCorners_caller(tmpCorners_, corner_array_size);
    }
    else
    {// send non-blocking request to read real non-zero number of corners to sort it on the HOST side
        openCLVerifyCall(clEnqueueReadBuffer(getClCommandQueue(counter_.clCxt), (cl_mem)counter_.data, CL_FALSE, 0,sizeof(int), &total, 0, NULL, NULL));
    }

    //blocking read whole corners array (sorted or not sorted)
    openCLReadBuffer(tmpCorners_.clCxt,(cl_mem)tmpCorners_.data,&tmp[0],tmpCorners_.cols*sizeof(DefCorner));
245 246

    if (total == 0)
247
    {// check for trivial case
248 249 250
        corners.release();
        return;
    }
251

252
    if(use_cpu_sorter)
253 254 255
    {// sort detected corners on cpu side.
        tmp.resize(total);
        cv::sort(tmp,DefCornerCompare());
256
    }
257

258 259 260 261 262 263 264 265
    //estimate maximal size of final output array
    int total_max = maxCorners > 0 ? std::min(maxCorners, total) : total;
    int D2 = (int)ceil(minDistance * minDistance);
    // allocate output buffer
    vector<Point2f> tmp2;
    tmp2.reserve(total_max);


266
    if (minDistance < 1)
267 268 269 270 271
    {// we have not distance restriction. then just copy with conversion maximal allowed points into output array
        for(int i=0;i<total_max && tmp[i].eig>0.0f;++i)
        {
            tmp2.push_back(Point2f(tmp[i].x,tmp[i].y));
        }
272 273
    }
    else
274
    {// we have distance restriction. then start coping to output array from the first element and check distance for each next one
275 276 277 278
        const int cell_size = cvRound(minDistance);
        const int grid_width = (image.cols + cell_size - 1) / cell_size;
        const int grid_height = (image.rows + cell_size - 1) / cell_size;

279
        std::vector< std::vector<Point2i> > grid(grid_width * grid_height);
280

281
        for (int i = 0; i < total ; ++i)
282
        {
283 284 285 286
            DefCorner p = tmp[i];

            if(p.eig<=0.0f)
                break; // condition to stop that is needed for GPU bitonic sort usage.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307

            bool good = true;

            int x_cell = static_cast<int>(p.x / cell_size);
            int y_cell = static_cast<int>(p.y / cell_size);

            int x1 = x_cell - 1;
            int y1 = y_cell - 1;
            int x2 = x_cell + 1;
            int y2 = y_cell + 1;

            // boundary check
            x1 = std::max(0, x1);
            y1 = std::max(0, y1);
            x2 = std::min(grid_width - 1, x2);
            y2 = std::min(grid_height - 1, y2);

            for (int yy = y1; yy <= y2; yy++)
            {
                for (int xx = x1; xx <= x2; xx++)
                {
308 309 310 311
                    vector<Point2i>& m = grid[yy * grid_width + xx];
                    if (m.empty())
                        continue;
                    for(size_t j = 0; j < m.size(); j++)
312
                    {
313 314 315 316
                        int dx = p.x - m[j].x;
                        int dy = p.y - m[j].y;

                        if (dx * dx + dy * dy < D2)
317
                        {
318 319
                            good = false;
                            goto break_out_;
320 321 322 323 324
                        }
                    }
                }
            }

325
            break_out_:
326 327 328

            if(good)
            {
329
                grid[y_cell * grid_width + x_cell].push_back(Point2i(p.x,p.y));
330

331
                tmp2.push_back(Point2f(p.x,p.y));
332 333 334 335 336 337 338

                if (maxCorners > 0 && tmp2.size() == static_cast<size_t>(maxCorners))
                    break;
            }
        }

    }
339 340 341 342 343
    int final_size = static_cast<int>(tmp2.size());
    if(final_size>0)
        corners.upload(Mat(1, final_size, CV_32FC2, &tmp2[0]));
    else
        corners.release();
344 345 346 347 348 349
}
void cv::ocl::GoodFeaturesToTrackDetector_OCL::downloadPoints(const oclMat &points, vector<Point2f> &points_v)
{
    CV_DbgAssert(points.type() == CV_32FC2);
    points_v.resize(points.cols);
    openCLSafeCall(clEnqueueReadBuffer(
350
        *(cl_command_queue*)getClCommandQueuePtr(),
351 352 353 354 355 356 357
        reinterpret_cast<cl_mem>(points.data),
        CL_TRUE,
        0,
        points.cols * sizeof(Point2f),
        &points_v[0],
        0,
        NULL,
358 359
        NULL));
}