hough.cpp 16.8 KB
Newer Older
Suenghoon Park's avatar
Suenghoon Park 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
/*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 materials 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 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"

using namespace cv;
using namespace cv::ocl;

48
#if !defined (HAVE_OPENCL)
Suenghoon Park's avatar
Suenghoon Park committed
49 50 51 52 53 54 55

void cv::ocl::HoughCircles(const oclMat&, oclMat&, int, float, float, int, int, int, int, int) { throw_nogpu(); }
void cv::ocl::HoughCircles(const oclMat&, oclMat&, HoughCirclesBuf&, int, float, float, int, int, int, int, int) { throw_nogpu(); }
void cv::ocl::HoughCirclesDownload(const oclMat&, OutputArray) { throw_nogpu(); }

#else /* !defined (HAVE_OPENCL) */

56
#define MUL_UP(a, b) ((a)/(b)+1)*(b)
Suenghoon Park's avatar
Suenghoon Park committed
57

58
namespace cv { namespace ocl {
Suenghoon Park's avatar
Suenghoon Park committed
59
    ///////////////////////////OpenCL kernel strings///////////////////////////
60
    extern const char *imgproc_hough;
Suenghoon Park's avatar
Suenghoon Park committed
61 62 63 64 65 66 67
}}



//////////////////////////////////////////////////////////
// common functions

68
namespace
Suenghoon Park's avatar
Suenghoon Park committed
69
{
70
    int buildPointList_gpu(const oclMat& src, oclMat& list)
Suenghoon Park's avatar
Suenghoon Park committed
71 72 73 74 75
    {
        const int PIXELS_PER_THREAD = 16;

        int totalCount = 0;
        int err = CL_SUCCESS;
Andrey Kamaev's avatar
Andrey Kamaev committed
76
        cl_mem counter = clCreateBuffer((cl_context)src.clCxt->oclContext(),
77
                                        CL_MEM_COPY_HOST_PTR,
Suenghoon Park's avatar
Suenghoon Park committed
78
                                        sizeof(int),
79
                                        &totalCount,
Suenghoon Park's avatar
Suenghoon Park committed
80 81 82 83 84 85 86 87
                                        &err);
        openCLSafeCall(err);

        const size_t blkSizeX = 32;
        const size_t blkSizeY = 4;
        size_t localThreads[3] = { blkSizeX, blkSizeY, 1 };

        const int PIXELS_PER_BLOCK = blkSizeX * PIXELS_PER_THREAD;
88
        const size_t glbSizeX = src.cols % (PIXELS_PER_BLOCK) == 0 ? src.cols : MUL_UP(src.cols, PIXELS_PER_BLOCK);
89
        const size_t glbSizeY = src.rows % blkSizeY == 0 ? src.rows : MUL_UP(src.rows, blkSizeY);
Suenghoon Park's avatar
Suenghoon Park committed
90 91
        size_t globalThreads[3] = { glbSizeX, glbSizeY, 1 };

92 93 94 95 96 97 98
        std::vector<std::pair<size_t , const void *> > args;
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&src.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&src.cols ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&src.rows ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&src.step ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&list.data ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&counter ));
Suenghoon Park's avatar
Suenghoon Park committed
99

100
        openCLExecuteKernel(src.clCxt, &imgproc_hough, "buildPointList", globalThreads, localThreads, args, -1, -1);
Andrey Kamaev's avatar
Andrey Kamaev committed
101
        openCLSafeCall(clEnqueueReadBuffer((cl_command_queue)src.clCxt->oclCommandQueue(), counter, CL_TRUE, 0, sizeof(int), &totalCount, 0, NULL, NULL));
Suenghoon Park's avatar
Suenghoon Park committed
102
        openCLSafeCall(clReleaseMemObject(counter));
103

Suenghoon Park's avatar
Suenghoon Park committed
104
        return totalCount;
105 106
    }
}
Suenghoon Park's avatar
Suenghoon Park committed
107 108 109 110

//////////////////////////////////////////////////////////
// HoughCircles

111
namespace
112 113 114 115 116
{
    void circlesAccumCenters_gpu(const oclMat& list, int count, const oclMat& dx, const oclMat& dy, oclMat& accum, int minRadius, int maxRadius, float idp)
    {
        const size_t blkSizeX = 256;
        size_t localThreads[3] = { 256, 1, 1 };
117

118 119 120 121 122 123
        const size_t glbSizeX = count % blkSizeX == 0 ? count : MUL_UP(count, blkSizeX);
        size_t globalThreads[3] = { glbSizeX, 1, 1 };

        const int width  = accum.cols - 2;
        const int height = accum.rows - 2;

124 125 126 127 128 129 130 131 132 133 134 135 136 137
        std::vector<std::pair<size_t , const void *> > args;
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&list.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&count ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&dx.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&dx.step ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&dy.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&dy.step ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&accum.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&accum.step ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&width ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&height ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&minRadius));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&maxRadius));
        args.push_back( std::make_pair( sizeof(cl_float), (void *)&idp));
138

139
        openCLExecuteKernel(accum.clCxt, &imgproc_hough, "circlesAccumCenters", globalThreads, localThreads, args, -1, -1);
140 141 142 143 144 145
    }

    int buildCentersList_gpu(const oclMat& accum, oclMat& centers, int threshold)
    {
        int totalCount = 0;
        int err = CL_SUCCESS;
Andrey Kamaev's avatar
Andrey Kamaev committed
146
        cl_mem counter = clCreateBuffer((cl_context)accum.clCxt->oclContext(),
147
                                        CL_MEM_COPY_HOST_PTR,
148
                                        sizeof(int),
149
                                        &totalCount,
150
                                        &err);
151
        openCLSafeCall(err);
152 153 154

        const size_t blkSizeX = 32;
        const size_t blkSizeY = 8;
155 156
        size_t localThreads[3] = { blkSizeX, blkSizeY, 1 };

157 158 159 160
        const size_t glbSizeX = (accum.cols - 2) % blkSizeX == 0 ? accum.cols - 2 : MUL_UP(accum.cols - 2, blkSizeX);
        const size_t glbSizeY = (accum.rows - 2) % blkSizeY == 0 ? accum.rows - 2 : MUL_UP(accum.rows - 2, blkSizeY);
        size_t globalThreads[3] = { glbSizeX, glbSizeY, 1 };

161 162 163 164 165 166 167 168
        std::vector<std::pair<size_t , const void *> > args;
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&accum.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&accum.cols ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&accum.rows ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&accum.step ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&centers.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&threshold ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&counter ));
169 170

        openCLExecuteKernel(accum.clCxt, &imgproc_hough, "buildCentersList", globalThreads, localThreads, args, -1, -1);
171

Andrey Kamaev's avatar
Andrey Kamaev committed
172
        openCLSafeCall(clEnqueueReadBuffer((cl_command_queue)accum.clCxt->oclCommandQueue(), counter, CL_TRUE, 0, sizeof(int), &totalCount, 0, NULL, NULL));
173
        openCLSafeCall(clReleaseMemObject(counter));
174

175 176 177 178 179 180 181 182 183 184
        return totalCount;
    }

    int circlesAccumRadius_gpu(const oclMat& centers, int centersCount,
                               const oclMat& list, int count,
                               oclMat& circles, int maxCircles,
                               float dp, int minRadius, int maxRadius, int threshold)
    {
        int totalCount = 0;
        int err = CL_SUCCESS;
Andrey Kamaev's avatar
Andrey Kamaev committed
185
        cl_mem counter = clCreateBuffer((cl_context)circles.clCxt->oclContext(),
186
                                        CL_MEM_COPY_HOST_PTR,
187
                                        sizeof(int),
188
                                        &totalCount,
189
                                        &err);
190
        openCLSafeCall(err);
191

Andrey Kamaev's avatar
Andrey Kamaev committed
192
        const size_t blkSizeX = circles.clCxt->maxWorkGroupSize();
193 194 195 196 197 198 199 200
        size_t localThreads[3] = { blkSizeX, 1, 1 };

        const size_t glbSizeX = centersCount * blkSizeX;
        size_t globalThreads[3] = { glbSizeX, 1, 1 };

        const int histSize = maxRadius - minRadius + 1;
        size_t smemSize = (histSize + 2) * sizeof(int);

201 202 203 204 205 206 207 208 209 210 211 212 213
        std::vector<std::pair<size_t , const void *> > args;
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&centers.data ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&list.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&count ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&circles.data ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&maxCircles ));
        args.push_back( std::make_pair( sizeof(cl_float), (void *)&dp ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&minRadius ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&maxRadius ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&histSize ));
        args.push_back( std::make_pair( sizeof(cl_int)  , (void *)&threshold ));
        args.push_back( std::make_pair( smemSize        , (void *)NULL ));
        args.push_back( std::make_pair( sizeof(cl_mem)  , (void *)&counter ));
214 215 216

        CV_Assert(circles.offset == 0);

217
        openCLExecuteKernel(circles.clCxt, &imgproc_hough, "circlesAccumRadius", globalThreads, localThreads, args, -1, -1);
218

Andrey Kamaev's avatar
Andrey Kamaev committed
219
        openCLSafeCall(clEnqueueReadBuffer((cl_command_queue)circles.clCxt->oclCommandQueue(), counter, CL_TRUE, 0, sizeof(int), &totalCount, 0, NULL, NULL));
220

221 222
        openCLSafeCall(clReleaseMemObject(counter));

223
        totalCount = std::min(totalCount, maxCircles);
224 225 226 227 228

        return totalCount;
    }


229
} // namespace
230 231


Suenghoon Park's avatar
Suenghoon Park committed
232 233 234 235 236 237 238 239 240 241 242 243 244

void cv::ocl::HoughCircles(const oclMat& src, oclMat& circles, int method, float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles)
{
    HoughCirclesBuf buf;
    HoughCircles(src, circles, buf, method, dp, minDist, cannyThreshold, votesThreshold, minRadius, maxRadius, maxCircles);
}

void cv::ocl::HoughCircles(const oclMat& src, oclMat& circles, HoughCirclesBuf& buf, int method,
                           float dp, float minDist, int cannyThreshold, int votesThreshold, int minRadius, int maxRadius, int maxCircles)
{
    CV_Assert(src.type() == CV_8UC1);
    CV_Assert(src.cols < std::numeric_limits<unsigned short>::max());
    CV_Assert(src.rows < std::numeric_limits<unsigned short>::max());
245
    CV_Assert(method == HOUGH_GRADIENT);
Suenghoon Park's avatar
Suenghoon Park committed
246 247 248 249 250
    CV_Assert(dp > 0);
    CV_Assert(minRadius > 0 && maxRadius > minRadius);
    CV_Assert(cannyThreshold > 0);
    CV_Assert(votesThreshold > 0);
    CV_Assert(maxCircles > 0);
251

Suenghoon Park's avatar
Suenghoon Park committed
252 253 254 255
    const float idp = 1.0f / dp;

    cv::ocl::Canny(src, buf.cannyBuf, buf.edges, std::max(cannyThreshold / 2, 1), cannyThreshold);

256
    ensureSizeIsEnough(1, src.size().area(), CV_32SC1, buf.srcPoints);
257
    const int pointsCount = buildPointList_gpu(buf.edges, buf.srcPoints);
Suenghoon Park's avatar
Suenghoon Park committed
258 259 260 261 262 263
    if (pointsCount == 0)
    {
        circles.release();
        return;
    }

264 265
    ensureSizeIsEnough(cvCeil(src.rows * idp) + 2, cvCeil(src.cols * idp) + 2, CV_32SC1, buf.accum);
    buf.accum.setTo(Scalar::all(0));
Suenghoon Park's avatar
Suenghoon Park committed
266

267
    circlesAccumCenters_gpu(buf.srcPoints, pointsCount, buf.cannyBuf.dx, buf.cannyBuf.dy, buf.accum, minRadius, maxRadius, idp);
Suenghoon Park's avatar
Suenghoon Park committed
268

269 270
    ensureSizeIsEnough(1, src.size().area(), CV_32SC1, buf.centers);
    int centersCount = buildCentersList_gpu(buf.accum, buf.centers, votesThreshold);
271 272 273 274 275
    if (centersCount == 0)
    {
        circles.release();
        return;
    }
Suenghoon Park's avatar
Suenghoon Park committed
276

277 278 279 280 281 282 283 284 285
    if (minDist > 1)
    {
        cv::AutoBuffer<unsigned int> oldBuf_(centersCount);
        cv::AutoBuffer<unsigned int> newBuf_(centersCount);
        int newCount = 0;

        unsigned int* oldBuf = oldBuf_;
        unsigned int* newBuf = newBuf_;

Andrey Kamaev's avatar
Andrey Kamaev committed
286
        openCLSafeCall(clEnqueueReadBuffer((cl_command_queue)buf.centers.clCxt->oclCommandQueue(),
287 288 289 290 291 292 293
                                           (cl_mem)buf.centers.data,
                                           CL_TRUE,
                                           0,
                                           centersCount * sizeof(unsigned int),
                                           oldBuf,
                                           0,
                                           NULL,
294 295
                                           NULL));

Suenghoon Park's avatar
Suenghoon Park committed
296

297 298 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
        const int cellSize = cvRound(minDist);
        const int gridWidth = (src.cols + cellSize - 1) / cellSize;
        const int gridHeight = (src.rows + cellSize - 1) / cellSize;

        std::vector< std::vector<unsigned int> > grid(gridWidth * gridHeight);

        const float minDist2 = minDist * minDist;

        for (int i = 0; i < centersCount; ++i)
        {
            unsigned int p = oldBuf[i];
            const int px = p & 0xFFFF;
            const int py = (p >> 16) & 0xFFFF;

            bool good = true;

            int xCell = static_cast<int>(px / cellSize);
            int yCell = static_cast<int>(py / cellSize);

            int x1 = xCell - 1;
            int y1 = yCell - 1;
            int x2 = xCell + 1;
            int y2 = yCell + 1;

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

            for (int yy = y1; yy <= y2; ++yy)
            {
                for (int xx = x1; xx <= x2; ++xx)
                {
331
                    std::vector<unsigned int>& m = grid[yy * gridWidth + xx];
332 333 334 335 336 337

                    for(size_t j = 0; j < m.size(); ++j)
                    {
                        const int val = m[j];
                        const int jx = val & 0xFFFF;
                        const int jy = (val >> 16) & 0xFFFF;
338

339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
                        float dx = (float)(px - jx);
                        float dy = (float)(py - jy);

                        if (dx * dx + dy * dy < minDist2)
                        {
                            good = false;
                            goto break_out;
                        }
                    }
                }
            }

            break_out:

            if(good)
            {
                grid[yCell * gridWidth + xCell].push_back(p);
                newBuf[newCount++] = p;
            }
        }

Andrey Kamaev's avatar
Andrey Kamaev committed
360
        openCLSafeCall(clEnqueueWriteBuffer((cl_command_queue)buf.centers.clCxt->oclCommandQueue(),
361 362 363 364 365 366 367
                                            (cl_mem)buf.centers.data,
                                            CL_TRUE,
                                            0,
                                            newCount * sizeof(unsigned int),
                                            newBuf,
                                            0,
                                            0,
368
                                            0));
369 370
        centersCount = newCount;
    }
Suenghoon Park's avatar
Suenghoon Park committed
371

372
    ensureSizeIsEnough(1, maxCircles, CV_32FC3, circles);
Suenghoon Park's avatar
Suenghoon Park committed
373

374
    const int circlesCount = circlesAccumRadius_gpu(buf.centers, centersCount,
375 376 377
                                                           buf.srcPoints, pointsCount,
                                                           circles, maxCircles,
                                                           dp, minRadius, maxRadius, votesThreshold);
Suenghoon Park's avatar
Suenghoon Park committed
378

379 380 381 382
    if (circlesCount > 0)
        circles.cols = circlesCount;
    else
        circles.release();
Suenghoon Park's avatar
Suenghoon Park committed
383 384 385 386
}

void cv::ocl::HoughCirclesDownload(const oclMat& d_circles, cv::OutputArray h_circles_)
{
387
    // FIX ME: garbage values are copied!
388
    CV_Error(Error::StsNotImplemented, "HoughCirclesDownload is not implemented");
389

Suenghoon Park's avatar
Suenghoon Park committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403
    if (d_circles.empty())
    {
        h_circles_.release();
        return;
    }

    CV_Assert(d_circles.rows == 1 && d_circles.type() == CV_32FC3);

    h_circles_.create(1, d_circles.cols, CV_32FC3);
    Mat h_circles = h_circles_.getMat();
    d_circles.download(h_circles);
}

#endif /* !defined (HAVE_OPENCL) */