moments.cpp 27.1 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/*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.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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"
Alexander Alekhin's avatar
Alexander Alekhin committed
42
#include "opencl_kernels_imgproc.hpp"
43

44 45 46
namespace cv
{

47
// The function calculates center of gravity and the central second order moments
48
static void completeMomentState( Moments* moments )
49 50 51
{
    double cx = 0, cy = 0;
    double mu20, mu11, mu02;
52
    double inv_m00 = 0.0;
53 54 55 56
    assert( moments != 0 );

    if( fabs(moments->m00) > DBL_EPSILON )
    {
57
        inv_m00 = 1. / moments->m00;
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
        cx = moments->m10 * inv_m00;
        cy = moments->m01 * inv_m00;
    }

    // mu20 = m20 - m10*cx
    mu20 = moments->m20 - moments->m10 * cx;
    // mu11 = m11 - m10*cy
    mu11 = moments->m11 - moments->m10 * cy;
    // mu02 = m02 - m01*cy
    mu02 = moments->m02 - moments->m01 * cy;

    moments->mu20 = mu20;
    moments->mu11 = mu11;
    moments->mu02 = mu02;

    // mu30 = m30 - cx*(3*mu20 + cx*m10)
    moments->mu30 = moments->m30 - cx * (3 * mu20 + cx * moments->m10);
    mu11 += mu11;
    // mu21 = m21 - cx*(2*mu11 + cx*m01) - cy*mu20
    moments->mu21 = moments->m21 - cx * (mu11 + cx * moments->m01) - cy * mu20;
    // mu12 = m12 - cy*(2*mu11 + cy*m10) - cx*mu02
    moments->mu12 = moments->m12 - cy * (mu11 + cy * moments->m10) - cx * mu02;
    // mu03 = m03 - cy*(3*mu02 + cy*m01)
    moments->mu03 = moments->m03 - cy * (3 * mu02 + cy * moments->m01);
82 83 84 85 86 87 88 89


    double inv_sqrt_m00 = std::sqrt(std::abs(inv_m00));
    double s2 = inv_m00*inv_m00, s3 = s2*inv_sqrt_m00;

    moments->nu20 = moments->mu20*s2; moments->nu11 = moments->mu11*s2; moments->nu02 = moments->mu02*s2;
    moments->nu30 = moments->mu30*s3; moments->nu21 = moments->mu21*s3; moments->nu12 = moments->mu12*s3; moments->nu03 = moments->mu03*s3;

90 91 92
}


93
static Moments contourMoments( const Mat& contour )
94
{
95 96 97
    Moments m;
    int lpt = contour.checkVector(2);
    int is_float = contour.depth() == CV_32F;
98 99
    const Point* ptsi = contour.ptr<Point>();
    const Point2f* ptsf = contour.ptr<Point2f>();
100

101
    CV_Assert( contour.depth() == CV_32S || contour.depth() == CV_32F );
102

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    if( lpt == 0 )
        return m;

    double a00 = 0, a10 = 0, a01 = 0, a20 = 0, a11 = 0, a02 = 0, a30 = 0, a21 = 0, a12 = 0, a03 = 0;
    double xi, yi, xi2, yi2, xi_1, yi_1, xi_12, yi_12, dxy, xii_1, yii_1;

    if( !is_float )
    {
        xi_1 = ptsi[lpt-1].x;
        yi_1 = ptsi[lpt-1].y;
    }
    else
    {
        xi_1 = ptsf[lpt-1].x;
        yi_1 = ptsf[lpt-1].y;
    }
119

120 121
    xi_12 = xi_1 * xi_1;
    yi_12 = yi_1 * yi_1;
122

123 124
    for( int i = 0; i < lpt; i++ )
    {
125 126
        if( !is_float )
        {
127 128
            xi = ptsi[i].x;
            yi = ptsi[i].y;
129 130 131
        }
        else
        {
132 133
            xi = ptsf[i].x;
            yi = ptsf[i].y;
134
        }
Andrey Kamaev's avatar
Andrey Kamaev committed
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
        xi2 = xi * xi;
        yi2 = yi * yi;
        dxy = xi_1 * yi - xi * yi_1;
        xii_1 = xi_1 + xi;
        yii_1 = yi_1 + yi;

        a00 += dxy;
        a10 += dxy * xii_1;
        a01 += dxy * yii_1;
        a20 += dxy * (xi_1 * xii_1 + xi2);
        a11 += dxy * (xi_1 * (yii_1 + yi_1) + xi * (yii_1 + yi));
        a02 += dxy * (yi_1 * yii_1 + yi2);
        a30 += dxy * xii_1 * (xi_12 + xi2);
        a03 += dxy * yii_1 * (yi_12 + yi2);
        a21 += dxy * (xi_12 * (3 * yi_1 + yi) + 2 * xi * xi_1 * yii_1 +
                   xi2 * (yi_1 + 3 * yi));
        a12 += dxy * (yi_12 * (3 * xi_1 + xi) + 2 * yi * yi_1 * xii_1 +
                   yi2 * (xi_1 + 3 * xi));
        xi_1 = xi;
        yi_1 = yi;
        xi_12 = xi2;
        yi_12 = yi2;
    }
159

160 161 162
    if( fabs(a00) > FLT_EPSILON )
    {
        double db1_2, db1_6, db1_12, db1_24, db1_20, db1_60;
163

164
        if( a00 > 0 )
165
        {
166 167 168 169 170 171
            db1_2 = 0.5;
            db1_6 = 0.16666666666666666666666666666667;
            db1_12 = 0.083333333333333333333333333333333;
            db1_24 = 0.041666666666666666666666666666667;
            db1_20 = 0.05;
            db1_60 = 0.016666666666666666666666666666667;
172
        }
173
        else
174
        {
175 176 177 178 179 180
            db1_2 = -0.5;
            db1_6 = -0.16666666666666666666666666666667;
            db1_12 = -0.083333333333333333333333333333333;
            db1_24 = -0.041666666666666666666666666666667;
            db1_20 = -0.05;
            db1_60 = -0.016666666666666666666666666666667;
181
        }
182 183 184 185 186 187 188 189 190 191 192 193 194 195

        // spatial moments
        m.m00 = a00 * db1_2;
        m.m10 = a10 * db1_6;
        m.m01 = a01 * db1_6;
        m.m20 = a20 * db1_12;
        m.m11 = a11 * db1_24;
        m.m02 = a02 * db1_12;
        m.m30 = a30 * db1_20;
        m.m21 = a21 * db1_60;
        m.m12 = a12 * db1_60;
        m.m03 = a03 * db1_20;

        completeMomentState( &m );
196
    }
197
    return m;
198 199 200 201 202 203 204 205
}


/****************************************************************************************\
*                                Spatial Raster Moments                                  *
\****************************************************************************************/

template<typename T, typename WT, typename MT>
Ilya Lavrenov's avatar
Ilya Lavrenov committed
206
struct MomentsInTile_SIMD
207
{
208
    int operator() (const T *, int, WT &, WT &, WT &, MT &)
209
    {
210
        return 0;
211
    }
212
};
213 214 215

#if CV_SSE2

216
template <>
Ilya Lavrenov's avatar
Ilya Lavrenov committed
217
struct MomentsInTile_SIMD<uchar, int, int>
218
{
Ilya Lavrenov's avatar
Ilya Lavrenov committed
219
    MomentsInTile_SIMD()
220 221 222
    {
        useSIMD = checkHardwareSupport(CV_CPU_SSE2);
    }
Andrey Kamaev's avatar
Andrey Kamaev committed
223

224
    int operator() (const uchar * ptr, int len, int & x0, int & x1, int & x2, int & x3)
225
    {
226
        int x = 0;
Andrey Kamaev's avatar
Andrey Kamaev committed
227

228 229 230
        if( useSIMD )
        {
            __m128i dx = _mm_set1_epi16(8);
231
            __m128i z = _mm_setzero_si128(), qx0 = z, qx1 = z, qx2 = z, qx3 = z, qx = _mm_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7);
Andrey Kamaev's avatar
Andrey Kamaev committed
232

233
            for( ; x <= len - 8; x += 8 )
234 235 236
            {
                __m128i p = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(ptr + x)), z);
                __m128i sx = _mm_mullo_epi16(qx, qx);
Ilya Lavrenov's avatar
Ilya Lavrenov committed
237

238
                qx0 = _mm_add_epi16(qx0, p);
239 240
                qx1 = _mm_add_epi32(qx1, _mm_madd_epi16(p, qx));
                qx2 = _mm_add_epi32(qx2, _mm_madd_epi16(p, sx));
Ilya Lavrenov's avatar
Ilya Lavrenov committed
241
                qx3 = _mm_add_epi32(qx3, _mm_madd_epi16( _mm_mullo_epi16(p, qx), sx));
Andrey Kamaev's avatar
Andrey Kamaev committed
242

243 244
                qx = _mm_add_epi16(qx, dx);
            }
245

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
            __m128i qx01_lo = _mm_unpacklo_epi32(qx0, qx1);
            __m128i qx23_lo = _mm_unpacklo_epi32(qx2, qx3);
            __m128i qx01_hi = _mm_unpackhi_epi32(qx0, qx1);
            __m128i qx23_hi = _mm_unpackhi_epi32(qx2, qx3);
            qx01_lo = _mm_add_epi32(qx01_lo, qx01_hi);
            qx23_lo = _mm_add_epi32(qx23_lo, qx23_hi);
            __m128i qx0123_lo = _mm_unpacklo_epi64(qx01_lo, qx23_lo);
            __m128i qx0123_hi = _mm_unpackhi_epi64(qx01_lo, qx23_lo);
            qx0123_lo = _mm_add_epi32(qx0123_lo, qx0123_hi);
            _mm_store_si128((__m128i*)buf, qx0123_lo);

            x0 = (buf[0] & 0xffff) + (buf[0] >> 16);
            x1 = buf[1];
            x2 = buf[2];
            x3 = buf[3];
261
        }
Andrey Kamaev's avatar
Andrey Kamaev committed
262

263 264 265
        return x;
    }

Ilya Lavrenov's avatar
Ilya Lavrenov committed
266
    int CV_DECL_ALIGNED(16) buf[4];
267 268 269
    bool useSIMD;
};

Ilya Lavrenov's avatar
Ilya Lavrenov committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 331 332 333 334 335
#elif CV_NEON

template <>
struct MomentsInTile_SIMD<uchar, int, int>
{
    MomentsInTile_SIMD()
    {
        ushort CV_DECL_ALIGNED(8) init[4] = { 0, 1, 2, 3 };
        qx_init = vld1_u16(init);
        v_step = vdup_n_u16(4);
    }

    int operator() (const uchar * ptr, int len, int & x0, int & x1, int & x2, int & x3)
    {
        int x = 0;

        uint32x4_t v_z = vdupq_n_u32(0), v_x0 = v_z, v_x1 = v_z,
            v_x2 = v_z, v_x3 = v_z;
        uint16x4_t qx = qx_init;

        for( ; x <= len - 8; x += 8 )
        {
            uint16x8_t v_src = vmovl_u8(vld1_u8(ptr + x));

            // first part
            uint32x4_t v_qx = vmovl_u16(qx);
            uint16x4_t v_p = vget_low_u16(v_src);
            uint32x4_t v_px = vmull_u16(qx, v_p);

            v_x0 = vaddw_u16(v_x0, v_p);
            v_x1 = vaddq_u32(v_x1, v_px);
            v_px = vmulq_u32(v_px, v_qx);
            v_x2 = vaddq_u32(v_x2, v_px);
            v_x3 = vaddq_u32(v_x3, vmulq_u32(v_px, v_qx));
            qx = vadd_u16(qx, v_step);

            // second part
            v_qx = vmovl_u16(qx);
            v_p = vget_high_u16(v_src);
            v_px = vmull_u16(qx, v_p);

            v_x0 = vaddw_u16(v_x0, v_p);
            v_x1 = vaddq_u32(v_x1, v_px);
            v_px = vmulq_u32(v_px, v_qx);
            v_x2 = vaddq_u32(v_x2, v_px);
            v_x3 = vaddq_u32(v_x3, vmulq_u32(v_px, v_qx));

            qx = vadd_u16(qx, v_step);
        }

        vst1q_u32(buf, v_x0);
        x0 = buf[0] + buf[1] + buf[2] + buf[3];
        vst1q_u32(buf, v_x1);
        x1 = buf[0] + buf[1] + buf[2] + buf[3];
        vst1q_u32(buf, v_x2);
        x2 = buf[0] + buf[1] + buf[2] + buf[3];
        vst1q_u32(buf, v_x3);
        x3 = buf[0] + buf[1] + buf[2] + buf[3];

        return x;
    }

    uint CV_DECL_ALIGNED(16) buf[4];
    uint16x4_t qx_init, v_step;
};

336 337 338 339 340
#endif

#if CV_SSE4_1

template <>
Ilya Lavrenov's avatar
Ilya Lavrenov committed
341
struct MomentsInTile_SIMD<ushort, int, int64>
342
{
Ilya Lavrenov's avatar
Ilya Lavrenov committed
343
    MomentsInTile_SIMD()
344 345 346 347 348 349 350 351 352 353
    {
        useSIMD = checkHardwareSupport(CV_CPU_SSE4_1);
    }

    int operator() (const ushort * ptr, int len, int & x0, int & x1, int & x2, int64 & x3)
    {
        int x = 0;

        if (useSIMD)
        {
354 355
            __m128i v_delta = _mm_set1_epi32(4), v_zero = _mm_setzero_si128(), v_x0 = v_zero,
                v_x1 = v_zero, v_x2 = v_zero, v_x3 = v_zero, v_ix0 = _mm_setr_epi32(0, 1, 2, 3);
356

357
            for( ; x <= len - 4; x += 4 )
358
            {
359 360
                __m128i v_src = _mm_loadl_epi64((const __m128i *)(ptr + x));
                v_src = _mm_unpacklo_epi16(v_src, v_zero);
361

362 363
                v_x0 = _mm_add_epi32(v_x0, v_src);
                v_x1 = _mm_add_epi32(v_x1, _mm_mullo_epi32(v_src, v_ix0));
364

365 366
                __m128i v_ix1 = _mm_mullo_epi32(v_ix0, v_ix0);
                v_x2 = _mm_add_epi32(v_x2, _mm_mullo_epi32(v_src, v_ix1));
367

368 369 370
                v_ix1 = _mm_mullo_epi32(v_ix0, v_ix1);
                v_src = _mm_mullo_epi32(v_src, v_ix1);
                v_x3 = _mm_add_epi64(v_x3, _mm_add_epi64(_mm_unpacklo_epi32(v_src, v_zero), _mm_unpackhi_epi32(v_src, v_zero)));
371 372 373 374

                v_ix0 = _mm_add_epi32(v_ix0, v_delta);
            }

375 376 377 378 379 380 381 382 383
            __m128i v_x01_lo = _mm_unpacklo_epi32(v_x0, v_x1);
            __m128i v_x22_lo = _mm_unpacklo_epi32(v_x2, v_x2);
            __m128i v_x01_hi = _mm_unpackhi_epi32(v_x0, v_x1);
            __m128i v_x22_hi = _mm_unpackhi_epi32(v_x2, v_x2);
            v_x01_lo = _mm_add_epi32(v_x01_lo, v_x01_hi);
            v_x22_lo = _mm_add_epi32(v_x22_lo, v_x22_hi);
            __m128i v_x0122_lo = _mm_unpacklo_epi64(v_x01_lo, v_x22_lo);
            __m128i v_x0122_hi = _mm_unpackhi_epi64(v_x01_lo, v_x22_lo);
            v_x0122_lo = _mm_add_epi32(v_x0122_lo, v_x0122_hi);
384
            _mm_store_si128((__m128i*)buf64, v_x3);
385 386 387 388 389
            _mm_store_si128((__m128i*)buf, v_x0122_lo);

            x0 = buf[0];
            x1 = buf[1];
            x2 = buf[2];
390 391 392 393 394 395
            x3 = buf64[0] + buf64[1];
        }

        return x;
    }

Ilya Lavrenov's avatar
Ilya Lavrenov committed
396 397
    int CV_DECL_ALIGNED(16) buf[4];
    int64 CV_DECL_ALIGNED(16) buf64[2];
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
    bool useSIMD;
};

#endif

template<typename T, typename WT, typename MT>
#if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9
// Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60196
__attribute__((optimize("no-tree-vectorize")))
#endif
static void momentsInTile( const Mat& img, double* moments )
{
    Size size = img.size();
    int x, y;
    MT mom[10] = {0,0,0,0,0,0,0,0,0,0};
Ilya Lavrenov's avatar
Ilya Lavrenov committed
413
    MomentsInTile_SIMD<T, WT, MT> vop;
414 415 416

    for( y = 0; y < size.height; y++ )
    {
417
        const T* ptr = img.ptr<T>(y);
418 419 420 421
        WT x0 = 0, x1 = 0, x2 = 0;
        MT x3 = 0;
        x = vop(ptr, size.width, x0, x1, x2, x3);

422 423 424 425
        for( ; x < size.width; x++ )
        {
            WT p = ptr[x];
            WT xp = x * p, xxp;
Andrey Kamaev's avatar
Andrey Kamaev committed
426

427 428
            x0 += p;
            x1 += xp;
429
            xxp = xp * x;
430 431 432
            x2 += xxp;
            x3 += xxp * x;
        }
Andrey Kamaev's avatar
Andrey Kamaev committed
433

434
        WT py = y * x0, sy = y*y;
Andrey Kamaev's avatar
Andrey Kamaev committed
435

436 437 438 439 440 441 442 443 444 445 446
        mom[9] += ((MT)py) * sy;  // m03
        mom[8] += ((MT)x1) * sy;  // m12
        mom[7] += ((MT)x2) * y;  // m21
        mom[6] += x3;             // m30
        mom[5] += x0 * sy;        // m02
        mom[4] += x1 * y;         // m11
        mom[3] += x2;             // m20
        mom[2] += py;             // m01
        mom[1] += x1;             // m10
        mom[0] += x0;             // m00
    }
Andrey Kamaev's avatar
Andrey Kamaev committed
447

448
    for( x = 0; x < 10; x++ )
449 450 451
        moments[x] = (double)mom[x];
}

452
typedef void (*MomentsInTileFunc)(const Mat& img, double* moments);
453

454
Moments::Moments()
455
{
456 457 458 459 460 461 462 463 464 465 466
    m00 = m10 = m01 = m20 = m11 = m02 = m30 = m21 = m12 = m03 =
    mu20 = mu11 = mu02 = mu30 = mu21 = mu12 = mu03 =
    nu20 = nu11 = nu02 = nu30 = nu21 = nu12 = nu03 = 0.;
}

Moments::Moments( double _m00, double _m10, double _m01, double _m20, double _m11,
                  double _m02, double _m30, double _m21, double _m12, double _m03 )
{
    m00 = _m00; m10 = _m10; m01 = _m01;
    m20 = _m20; m11 = _m11; m02 = _m02;
    m30 = _m30; m21 = _m21; m12 = _m12; m03 = _m03;
467

468 469
    double cx = 0, cy = 0, inv_m00 = 0;
    if( std::abs(m00) > DBL_EPSILON )
470
    {
471 472
        inv_m00 = 1./m00;
        cx = m10*inv_m00; cy = m01*inv_m00;
473 474
    }

475 476 477
    mu20 = m20 - m10*cx;
    mu11 = m11 - m10*cy;
    mu02 = m02 - m01*cy;
478

479 480 481 482
    mu30 = m30 - cx*(3*mu20 + cx*m10);
    mu21 = m21 - cx*(2*mu11 + cx*m01) - cy*mu20;
    mu12 = m12 - cy*(2*mu11 + cy*m10) - cx*mu02;
    mu03 = m03 - cy*(3*mu02 + cy*m01);
483

484 485
    double inv_sqrt_m00 = std::sqrt(std::abs(inv_m00));
    double s2 = inv_m00*inv_m00, s3 = s2*inv_sqrt_m00;
486

487 488 489
    nu20 = mu20*s2; nu11 = mu11*s2; nu02 = mu02*s2;
    nu30 = mu30*s3; nu21 = mu21*s3; nu12 = mu12*s3; nu03 = mu03*s3;
}
490

Ilya Lavrenov's avatar
Ilya Lavrenov committed
491 492
#ifdef HAVE_OPENCL

493
static bool ocl_moments( InputArray _src, Moments& m, bool binary)
494
{
495
    const int TILE_SIZE = 32;
496
    const int K = 10;
497 498 499 500 501 502

    ocl::Kernel k = ocl::Kernel("moments", ocl::imgproc::moments_oclsrc,
        format("-D TILE_SIZE=%d%s",
        TILE_SIZE,
        binary ? " -D OP_MOMENTS_BINARY" : ""));

503 504
    if( k.empty() )
        return false;
505

506 507
    UMat src = _src.getUMat();
    Size sz = src.size();
508 509
    int xtiles = (sz.width + TILE_SIZE-1)/TILE_SIZE;
    int ytiles = (sz.height + TILE_SIZE-1)/TILE_SIZE;
510 511
    int ntiles = xtiles*ytiles;
    UMat umbuf(1, ntiles*K, CV_32S);
512

513 514
    size_t globalsize[] = {(size_t)xtiles, std::max((size_t)TILE_SIZE, (size_t)sz.height)};
    size_t localsize[] = {1, TILE_SIZE};
515 516
    bool ok = k.args(ocl::KernelArg::ReadOnly(src),
                     ocl::KernelArg::PtrWriteOnly(umbuf),
517
                     xtiles).run(2, globalsize, localsize, true);
518 519
    if(!ok)
        return false;
520
    Mat mbuf = umbuf.getMat(ACCESS_READ);
521 522
    for( int i = 0; i < ntiles; i++ )
    {
523
        double x = (i % xtiles)*TILE_SIZE, y = (i / xtiles)*TILE_SIZE;
524 525
        const int* mom = mbuf.ptr<int>() + i*K;
        double xm = x * mom[0], ym = y * mom[0];
526

527
        // accumulate moments computed in each tile
528

529 530
        // + m00 ( = m00' )
        m.m00 += mom[0];
531

532 533
        // + m10 ( = m10' + x*m00' )
        m.m10 += mom[1] + xm;
534

535 536
        // + m01 ( = m01' + y*m00' )
        m.m01 += mom[2] + ym;
537

538 539
        // + m20 ( = m20' + 2*x*m10' + x*x*m00' )
        m.m20 += mom[3] + x * (mom[1] * 2 + xm);
540

541 542
        // + m11 ( = m11' + x*m01' + y*m10' + x*y*m00' )
        m.m11 += mom[4] + x * (mom[2] + ym) + y * mom[1];
543

544 545
        // + m02 ( = m02' + 2*y*m01' + y*y*m00' )
        m.m02 += mom[5] + y * (mom[2] * 2 + ym);
546

547 548
        // + m30 ( = m30' + 3*x*m20' + 3*x*x*m10' + x*x*x*m00' )
        m.m30 += mom[6] + x * (3. * mom[3] + x * (3. * mom[1] + xm));
549

550 551
        // + m21 ( = m21' + x*(2*m11' + 2*y*m10' + x*m01' + x*y*m00') + y*m20')
        m.m21 += mom[7] + x * (2 * (mom[4] + y * mom[1]) + x * (mom[2] + ym)) + y * mom[3];
552

553 554
        // + m12 ( = m12' + y*(2*m11' + 2*x*m01' + y*m10' + x*y*m00') + x*m02')
        m.m12 += mom[8] + y * (2 * (mom[4] + x * mom[2]) + y * (mom[1] + xm)) + x * mom[5];
555

556 557 558
        // + m03 ( = m03' + 3*y*m02' + 3*y*y*m01' + y*y*y*m00' )
        m.m03 += mom[9] + y * (3. * mom[5] + y * (3. * mom[2] + ym));
    }
559

560 561
    completeMomentState( &m );

562 563
    return true;
}
564

Ilya Lavrenov's avatar
Ilya Lavrenov committed
565 566
#endif

567 568 569 570 571 572 573 574
#ifdef HAVE_IPP
typedef IppStatus (CV_STDCALL * ippiMoments)(const void* pSrc, int srcStep, IppiSize roiSize, IppiMomentState_64f* pCtx);

static bool ipp_moments(Mat &src, Moments &m )
{
#if IPP_VERSION_X100 >= 900
    CV_INSTRUMENT_REGION_IPP()

575 576 577 578 579 580
#if IPP_VERSION_X100 < 201801
    // Degradations for CV_8UC1
    if(src.type() == CV_8UC1)
        return false;
#endif

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
    IppiSize  roi      = { src.cols, src.rows };
    IppiPoint point    = { 0, 0 };
    int       type     = src.type();
    IppStatus ippStatus;

    IppAutoBuffer<IppiMomentState_64f> state;
    int stateSize = 0;

    ippiMoments ippiMoments64f =
        (type == CV_8UC1)?(ippiMoments)ippiMoments64f_8u_C1R:
        (type == CV_16UC1)?(ippiMoments)ippiMoments64f_16u_C1R:
        (type == CV_32FC1)?(ippiMoments)ippiMoments64f_32f_C1R:
        NULL;
    if(!ippiMoments64f)
        return false;

    ippStatus = ippiMomentGetStateSize_64f(ippAlgHintAccurate, &stateSize);
    if(ippStatus < 0)
        return false;

    if(!state.allocate(stateSize) && stateSize)
        return false;

    ippStatus = ippiMomentInit_64f(state, ippAlgHintAccurate);
    if(ippStatus < 0)
        return false;

    ippStatus = CV_INSTRUMENT_FUN_IPP(ippiMoments64f, src.ptr<Ipp8u>(), (int)src.step, roi, state);
    if(ippStatus < 0)
        return false;

    ippStatus = ippiGetSpatialMoment_64f(state, 0, 0, 0, point, &m.m00);
    if(ippStatus < 0)
        return false;
    ippiGetSpatialMoment_64f(state, 1, 0, 0, point, &m.m10);
    ippiGetSpatialMoment_64f(state, 0, 1, 0, point, &m.m01);
    ippiGetSpatialMoment_64f(state, 2, 0, 0, point, &m.m20);
    ippiGetSpatialMoment_64f(state, 1, 1, 0, point, &m.m11);
    ippiGetSpatialMoment_64f(state, 0, 2, 0, point, &m.m02);
    ippiGetSpatialMoment_64f(state, 3, 0, 0, point, &m.m30);
    ippiGetSpatialMoment_64f(state, 2, 1, 0, point, &m.m21);
    ippiGetSpatialMoment_64f(state, 1, 2, 0, point, &m.m12);
    ippiGetSpatialMoment_64f(state, 0, 3, 0, point, &m.m03);

    ippStatus = ippiGetCentralMoment_64f(state, 2, 0, 0, &m.mu20);
    if(ippStatus < 0)
        return false;
    ippiGetCentralMoment_64f(state, 1, 1, 0, &m.mu11);
    ippiGetCentralMoment_64f(state, 0, 2, 0, &m.mu02);
    ippiGetCentralMoment_64f(state, 3, 0, 0, &m.mu30);
    ippiGetCentralMoment_64f(state, 2, 1, 0, &m.mu21);
    ippiGetCentralMoment_64f(state, 1, 2, 0, &m.mu12);
    ippiGetCentralMoment_64f(state, 0, 3, 0, &m.mu03);

    ippStatus = ippiGetNormalizedCentralMoment_64f(state, 2, 0, 0, &m.nu20);
    if(ippStatus < 0)
        return false;
    ippiGetNormalizedCentralMoment_64f(state, 1, 1, 0, &m.nu11);
    ippiGetNormalizedCentralMoment_64f(state, 0, 2, 0, &m.nu02);
    ippiGetNormalizedCentralMoment_64f(state, 3, 0, 0, &m.nu30);
    ippiGetNormalizedCentralMoment_64f(state, 2, 1, 0, &m.nu21);
    ippiGetNormalizedCentralMoment_64f(state, 1, 2, 0, &m.nu12);
    ippiGetNormalizedCentralMoment_64f(state, 0, 3, 0, &m.nu03);

    return true;
#else
    CV_UNUSED(src); CV_UNUSED(m);
    return false;
#endif
650
}
651
#endif
652

653
}
654 655 656

cv::Moments cv::moments( InputArray _src, bool binary )
{
657 658
    CV_INSTRUMENT_REGION()

659 660 661 662
    const int TILE_SIZE = 32;
    MomentsInTileFunc func = 0;
    uchar nzbuf[TILE_SIZE*TILE_SIZE];
    Moments m;
Ilya Lavrenov's avatar
Ilya Lavrenov committed
663
    int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
664
    Size size = _src.size();
665 666

    if( size.width <= 0 || size.height <= 0 )
667
        return m;
668

Ilya Lavrenov's avatar
Ilya Lavrenov committed
669
#ifdef HAVE_OPENCL
670
    CV_OCL_RUN_(type == CV_8UC1 && _src.isUMat(), ocl_moments(_src, m, binary), m);
Ilya Lavrenov's avatar
Ilya Lavrenov committed
671
#endif
672

673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
    Mat mat = _src.getMat();
    if( mat.checkVector(2) >= 0 && (depth == CV_32F || depth == CV_32S))
        return contourMoments(mat);

    if( cn > 1 )
        CV_Error( CV_StsBadArg, "Invalid image type (must be single-channel)" );

    CV_IPP_RUN(!binary, ipp_moments(mat, m), m);

    if( binary || depth == CV_8U )
        func = momentsInTile<uchar, int, int>;
    else if( depth == CV_16U )
        func = momentsInTile<ushort, int, int64>;
    else if( depth == CV_16S )
        func = momentsInTile<short, int, int64>;
    else if( depth == CV_32F )
        func = momentsInTile<float, double, double>;
    else if( depth == CV_64F )
        func = momentsInTile<double, double, double>;
    else
        CV_Error( CV_StsUnsupportedFormat, "" );
694

695
    Mat src0(mat);
Andrey Kamaev's avatar
Andrey Kamaev committed
696

697 698 699 700
    for( int y = 0; y < size.height; y += TILE_SIZE )
    {
        Size tileSize;
        tileSize.height = std::min(TILE_SIZE, size.height - y);
701

702
        for( int x = 0; x < size.width; x += TILE_SIZE )
703
        {
704 705
            tileSize.width = std::min(TILE_SIZE, size.width - x);
            Mat src(src0, cv::Rect(x, y, tileSize.width, tileSize.height));
706

707
            if( binary )
708
            {
709 710 711 712
                cv::Mat tmp(tileSize, CV_8U, nzbuf);
                cv::compare( src, 0, tmp, CV_CMP_NE );
                src = tmp;
            }
Andrey Kamaev's avatar
Andrey Kamaev committed
713

714 715
            double mom[10];
            func( src, mom );
Andrey Kamaev's avatar
Andrey Kamaev committed
716

717 718 719 720 721 722
            if(binary)
            {
                double s = 1./255;
                for( int k = 0; k < 10; k++ )
                    mom[k] *= s;
            }
723

724
            double xm = x * mom[0], ym = y * mom[0];
725

726
            // accumulate moments computed in each tile
727

728 729
            // + m00 ( = m00' )
            m.m00 += mom[0];
730

731 732
            // + m10 ( = m10' + x*m00' )
            m.m10 += mom[1] + xm;
733

734 735
            // + m01 ( = m01' + y*m00' )
            m.m01 += mom[2] + ym;
736

737 738
            // + m20 ( = m20' + 2*x*m10' + x*x*m00' )
            m.m20 += mom[3] + x * (mom[1] * 2 + xm);
739

740 741
            // + m11 ( = m11' + x*m01' + y*m10' + x*y*m00' )
            m.m11 += mom[4] + x * (mom[2] + ym) + y * mom[1];
742

743 744
            // + m02 ( = m02' + 2*y*m01' + y*y*m00' )
            m.m02 += mom[5] + y * (mom[2] * 2 + ym);
745

746 747
            // + m30 ( = m30' + 3*x*m20' + 3*x*x*m10' + x*x*x*m00' )
            m.m30 += mom[6] + x * (3. * mom[3] + x * (3. * mom[1] + xm));
Andrey Kamaev's avatar
Andrey Kamaev committed
748

749 750
            // + m21 ( = m21' + x*(2*m11' + 2*y*m10' + x*m01' + x*y*m00') + y*m20')
            m.m21 += mom[7] + x * (2 * (mom[4] + y * mom[1]) + x * (mom[2] + ym)) + y * mom[3];
751

752 753
            // + m12 ( = m12' + y*(2*m11' + 2*x*m01' + y*m10' + x*y*m00') + x*m02')
            m.m12 += mom[8] + y * (2 * (mom[4] + x * mom[2]) + y * (mom[1] + xm)) + x * mom[5];
754

755 756
            // + m03 ( = m03' + 3*y*m02' + 3*y*y*m01' + y*y*y*m00' )
            m.m03 += mom[9] + y * (3. * mom[5] + y * (3. * mom[2] + ym));
757 758
        }
    }
759

760 761
    completeMomentState( &m );
    return m;
762 763 764
}


765
void cv::HuMoments( const Moments& m, double hu[7] )
766
{
767 768
    CV_INSTRUMENT_REGION()

769 770
    double t0 = m.nu30 + m.nu12;
    double t1 = m.nu21 + m.nu03;
771 772 773

    double q0 = t0 * t0, q1 = t1 * t1;

774 775 776
    double n4 = 4 * m.nu11;
    double s = m.nu20 + m.nu02;
    double d = m.nu20 - m.nu02;
777

778 779 780 781
    hu[0] = s;
    hu[1] = d * d + n4 * m.nu11;
    hu[3] = q0 + q1;
    hu[5] = d * (q0 - q1) + n4 * t0 * t1;
782 783 784 785

    t0 *= q0 - 3 * q1;
    t1 *= 3 * q0 - q1;

786 787
    q0 = m.nu30 - 3 * m.nu12;
    q1 = 3 * m.nu21 - m.nu03;
788

789 790 791 792 793 794 795
    hu[2] = q0 * q0 + q1 * q1;
    hu[4] = q0 * t0 + q1 * t1;
    hu[6] = q1 * t0 - q0 * t1;
}

void cv::HuMoments( const Moments& m, OutputArray _hu )
{
796 797
    CV_INSTRUMENT_REGION()

798 799 800
    _hu.create(7, 1, CV_64F);
    Mat hu = _hu.getMat();
    CV_Assert( hu.isContinuous() );
801
    HuMoments(m, hu.ptr<double>());
802 803 804 805 806 807 808 809 810 811 812 813 814 815
}


CV_IMPL void cvMoments( const CvArr* arr, CvMoments* moments, int binary )
{
    const IplImage* img = (const IplImage*)arr;
    cv::Mat src;
    if( CV_IS_IMAGE(arr) && img->roi && img->roi->coi > 0 )
        cv::extractImageCOI(arr, src, img->roi->coi-1);
    else
        src = cv::cvarrToMat(arr);
    cv::Moments m = cv::moments(src, binary != 0);
    CV_Assert( moments != 0 );
    *moments = m;
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
}


CV_IMPL double cvGetSpatialMoment( CvMoments * moments, int x_order, int y_order )
{
    int order = x_order + y_order;

    if( !moments )
        CV_Error( CV_StsNullPtr, "" );
    if( (x_order | y_order) < 0 || order > 3 )
        CV_Error( CV_StsOutOfRange, "" );

    return (&(moments->m00))[order + (order >> 1) + (order > 2) * 2 + y_order];
}


CV_IMPL double cvGetCentralMoment( CvMoments * moments, int x_order, int y_order )
{
    int order = x_order + y_order;

    if( !moments )
        CV_Error( CV_StsNullPtr, "" );
    if( (x_order | y_order) < 0 || order > 3 )
        CV_Error( CV_StsOutOfRange, "" );

    return order >= 2 ? (&(moments->m00))[4 + order * 3 + y_order] :
842
    order == 0 ? moments->m00 : 0;
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
}


CV_IMPL double cvGetNormalizedCentralMoment( CvMoments * moments, int x_order, int y_order )
{
    int order = x_order + y_order;

    double mu = cvGetCentralMoment( moments, x_order, y_order );
    double m00s = moments->inv_sqrt_m00;

    while( --order >= 0 )
        mu *= m00s;
    return mu * m00s * m00s;
}


859
CV_IMPL void cvGetHuMoments( CvMoments * mState, CvHuMoments * HuState )
860
{
861 862
    if( !mState || !HuState )
        CV_Error( CV_StsNullPtr, "" );
Andrey Kamaev's avatar
Andrey Kamaev committed
863

864
    double m00s = mState->inv_sqrt_m00, m00 = m00s * m00s, s2 = m00 * m00, s3 = s2 * m00s;
Andrey Kamaev's avatar
Andrey Kamaev committed
865

866 867 868 869 870
    double nu20 = mState->mu20 * s2,
    nu11 = mState->mu11 * s2,
    nu02 = mState->mu02 * s2,
    nu30 = mState->mu30 * s3,
    nu21 = mState->mu21 * s3, nu12 = mState->mu12 * s3, nu03 = mState->mu03 * s3;
871

872 873
    double t0 = nu30 + nu12;
    double t1 = nu21 + nu03;
874 875 876

    double q0 = t0 * t0, q1 = t1 * t1;

877 878 879
    double n4 = 4 * nu11;
    double s = nu20 + nu02;
    double d = nu20 - nu02;
880

881 882 883 884
    HuState->hu1 = s;
    HuState->hu2 = d * d + n4 * nu11;
    HuState->hu4 = q0 + q1;
    HuState->hu6 = d * (q0 - q1) + n4 * t0 * t1;
885 886 887 888

    t0 *= q0 - 3 * q1;
    t1 *= 3 * q0 - q1;

889 890
    q0 = nu30 - 3 * nu12;
    q1 = 3 * nu21 - nu03;
891

892 893 894
    HuState->hu3 = q0 * q0 + q1 * q1;
    HuState->hu5 = q0 * t0 + q1 * t1;
    HuState->hu7 = q1 * t0 - q0 * t1;
895 896 897 898
}


/* End of file. */