/*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 "_cv.h" IPCVAPI_IMPL( CvStatus, icvUpdateMotionHistory_8u32f_C1IR, (const uchar * silIm, int silStep, float *mhiIm, int mhiStep, CvSize size, float timestamp, float mhi_duration), (silIm, silStep, mhiIm, mhiStep, size, timestamp, mhi_duration) ) { int x, y; /* function processes floating-point images using integer arithmetics */ Cv32suf v; int ts, delbound; int *mhi = (int *) mhiIm; v.f = timestamp; ts = v.i; if( !silIm || !mhiIm ) return CV_NULLPTR_ERR; if( size.height <= 0 || size.width <= 0 || silStep < size.width || mhiStep < size.width * CV_SIZEOF_FLOAT || (mhiStep & (CV_SIZEOF_FLOAT - 1)) != 0 ) return CV_BADSIZE_ERR; if( mhi_duration < 0 ) return CV_BADFACTOR_ERR; mhi_duration = timestamp - mhi_duration; v.f = mhi_duration; delbound = CV_TOGGLE_FLT( v.i ); mhiStep /= sizeof(mhi[0]); if( mhiStep == size.width && silStep == size.width ) { size.width *= size.height; size.height = 1; } if( delbound > 0 ) for( y = 0; y < size.height; y++, silIm += silStep, mhi += mhiStep ) for( x = 0; x < size.width; x++ ) { int val = mhi[x]; /* val = silIm[x] ? ts : val < delbound ? 0 : val; */ val &= (val < delbound) - 1; val ^= (ts ^ val) & ((silIm[x] == 0) - 1); mhi[x] = val; } else for( y = 0; y < size.height; y++, silIm += silStep, mhi += mhiStep ) for( x = 0; x < size.width; x++ ) { int val = mhi[x]; /* val = silIm[x] ? ts : val < delbound ? 0 : val; */ val &= (CV_TOGGLE_FLT( val ) < delbound) - 1; val ^= (ts ^ val) & ((silIm[x] == 0) - 1); mhi[x] = val; } return CV_OK; } /* motion templates */ CV_IMPL void cvUpdateMotionHistory( const void* silhouette, void* mhimg, double timestamp, double mhi_duration ) { CvSize size; CvMat silhstub, *silh = (CvMat*)silhouette; CvMat mhistub, *mhi = (CvMat*)mhimg; int mhi_step, silh_step; CV_FUNCNAME( "cvUpdateMHIByTime" ); __BEGIN__; CV_CALL( silh = cvGetMat( silh, &silhstub )); CV_CALL( mhi = cvGetMat( mhi, &mhistub )); if( !CV_IS_MASK_ARR( silh )) CV_ERROR( CV_StsBadMask, "" ); if( CV_MAT_CN( mhi->type ) > 1 ) CV_ERROR( CV_BadNumChannels, "" ); if( CV_MAT_DEPTH( mhi->type ) != CV_32F ) CV_ERROR( CV_BadDepth, "" ); if( !CV_ARE_SIZES_EQ( mhi, silh )) CV_ERROR( CV_StsUnmatchedSizes, "" ); size = cvGetMatSize( mhi ); mhi_step = mhi->step; silh_step = silh->step; if( CV_IS_MAT_CONT( mhi->type & silh->type )) { size.width *= size.height; mhi_step = silh_step = CV_STUB_STEP; size.height = 1; } IPPI_CALL( icvUpdateMotionHistory_8u32f_C1IR( (const uchar*)(silh->data.ptr), silh_step, mhi->data.fl, mhi_step, size, (float)timestamp, (float)mhi_duration )); __END__; } CV_IMPL void cvCalcMotionGradient( const CvArr* mhiimg, CvArr* maskimg, CvArr* orientation, double delta1, double delta2, int aperture_size ) { CvMat *dX_min = 0, *dY_max = 0; IplConvKernel* el = 0; CV_FUNCNAME( "cvCalcMotionGradient" ); __BEGIN__; CvMat mhistub, *mhi = (CvMat*)mhiimg; CvMat maskstub, *mask = (CvMat*)maskimg; CvMat orientstub, *orient = (CvMat*)orientation; CvMat dX_min_row, dY_max_row, orient_row, mask_row; CvSize size; int x, y; float gradient_epsilon = 1e-4f * aperture_size * aperture_size; float min_delta, max_delta; CV_CALL( mhi = cvGetMat( mhi, &mhistub )); CV_CALL( mask = cvGetMat( mask, &maskstub )); CV_CALL( orient = cvGetMat( orient, &orientstub )); if( !CV_IS_MASK_ARR( mask )) CV_ERROR( CV_StsBadMask, "" ); if( aperture_size < 3 || aperture_size > 7 || (aperture_size & 1) == 0 ) CV_ERROR( CV_StsOutOfRange, "aperture_size must be 3, 5 or 7" ); if( delta1 <= 0 || delta2 <= 0 ) CV_ERROR( CV_StsOutOfRange, "both delta's must be positive" ); if( CV_MAT_TYPE( mhi->type ) != CV_32FC1 || CV_MAT_TYPE( orient->type ) != CV_32FC1 ) CV_ERROR( CV_StsUnsupportedFormat, "MHI and orientation must be single-channel floating-point images" ); if( !CV_ARE_SIZES_EQ( mhi, mask ) || !CV_ARE_SIZES_EQ( orient, mhi )) CV_ERROR( CV_StsUnmatchedSizes, "" ); if( orient->data.ptr == mhi->data.ptr ) CV_ERROR( CV_StsInplaceNotSupported, "orientation image must be different from MHI" ); if( delta1 > delta2 ) { double t; CV_SWAP( delta1, delta2, t ); } size = cvGetMatSize( mhi ); min_delta = (float)delta1; max_delta = (float)delta2; CV_CALL( dX_min = cvCreateMat( mhi->rows, mhi->cols, CV_32F )); CV_CALL( dY_max = cvCreateMat( mhi->rows, mhi->cols, CV_32F )); /* calc Dx and Dy */ CV_CALL( cvSobel( mhi, dX_min, 1, 0, aperture_size )); CV_CALL( cvSobel( mhi, dY_max, 0, 1, aperture_size )); cvGetRow( dX_min, &dX_min_row, 0 ); cvGetRow( dY_max, &dY_max_row, 0 ); cvGetRow( orient, &orient_row, 0 ); cvGetRow( mask, &mask_row, 0 ); /* calc gradient */ for( y = 0; y < size.height; y++ ) { dX_min_row.data.ptr = dX_min->data.ptr + y*dX_min->step; dY_max_row.data.ptr = dY_max->data.ptr + y*dY_max->step; orient_row.data.ptr = orient->data.ptr + y*orient->step; mask_row.data.ptr = mask->data.ptr + y*mask->step; cvCartToPolar( &dX_min_row, &dY_max_row, 0, &orient_row, 1 ); /* make orientation zero where the gradient is very small */ for( x = 0; x < size.width; x++ ) { float dY = dY_max_row.data.fl[x]; float dX = dX_min_row.data.fl[x]; if( fabs(dX) < gradient_epsilon && fabs(dY) < gradient_epsilon ) { mask_row.data.ptr[x] = 0; orient_row.data.i[x] = 0; } else mask_row.data.ptr[x] = 1; } } CV_CALL( el = cvCreateStructuringElementEx( aperture_size, aperture_size, aperture_size/2, aperture_size/2, CV_SHAPE_RECT )); cvErode( mhi, dX_min, el ); cvDilate( mhi, dY_max, el ); /* mask off pixels which have little motion difference in their neighborhood */ for( y = 0; y < size.height; y++ ) { dX_min_row.data.ptr = dX_min->data.ptr + y*dX_min->step; dY_max_row.data.ptr = dY_max->data.ptr + y*dY_max->step; mask_row.data.ptr = mask->data.ptr + y*mask->step; orient_row.data.ptr = orient->data.ptr + y*orient->step; for( x = 0; x < size.width; x++ ) { float d0 = dY_max_row.data.fl[x] - dX_min_row.data.fl[x]; if( mask_row.data.ptr[x] == 0 || d0 < min_delta || max_delta < d0 ) { mask_row.data.ptr[x] = 0; orient_row.data.i[x] = 0; } } } __END__; cvReleaseMat( &dX_min ); cvReleaseMat( &dY_max ); cvReleaseStructuringElement( &el ); } CV_IMPL double cvCalcGlobalOrientation( const void* orientation, const void* maskimg, const void* mhiimg, double curr_mhi_timestamp, double mhi_duration ) { double angle = 0; int hist_size = 12; CvHistogram* hist = 0; CV_FUNCNAME( "cvCalcGlobalOrientation" ); __BEGIN__; CvMat mhistub, *mhi = (CvMat*)mhiimg; CvMat maskstub, *mask = (CvMat*)maskimg; CvMat orientstub, *orient = (CvMat*)orientation; void* _orient; float _ranges[] = { 0, 360 }; float* ranges = _ranges; int base_orient; double shift_orient = 0, shift_weight = 0, fbase_orient; double a, b; float delbound; CvMat mhi_row, mask_row, orient_row; int x, y, mhi_rows, mhi_cols; CV_CALL( mhi = cvGetMat( mhi, &mhistub )); CV_CALL( mask = cvGetMat( mask, &maskstub )); CV_CALL( orient = cvGetMat( orient, &orientstub )); if( !CV_IS_MASK_ARR( mask )) CV_ERROR( CV_StsBadMask, "" ); if( CV_MAT_TYPE( mhi->type ) != CV_32FC1 || CV_MAT_TYPE( orient->type ) != CV_32FC1 ) CV_ERROR( CV_StsUnsupportedFormat, "MHI and orientation must be single-channel floating-point images" ); if( !CV_ARE_SIZES_EQ( mhi, mask ) || !CV_ARE_SIZES_EQ( orient, mhi )) CV_ERROR( CV_StsUnmatchedSizes, "" ); if( mhi_duration <= 0 ) CV_ERROR( CV_StsOutOfRange, "MHI duration must be positive" ); if( orient->data.ptr == mhi->data.ptr ) CV_ERROR( CV_StsInplaceNotSupported, "orientation image must be different from MHI" ); // calculate histogram of different orientation values CV_CALL( hist = cvCreateHist( 1, &hist_size, CV_HIST_ARRAY, &ranges )); _orient = orient; cvCalcArrHist( &_orient, hist, 0, mask ); // find the maximum index (the dominant orientation) cvGetMinMaxHistValue( hist, 0, 0, 0, &base_orient ); base_orient *= 360/hist_size; // override timestamp with the maximum value in MHI cvMinMaxLoc( mhi, 0, &curr_mhi_timestamp, 0, 0, mask ); // find the shift relative to the dominant orientation as weighted sum of relative angles a = 254. / 255. / mhi_duration; b = 1. - curr_mhi_timestamp * a; fbase_orient = base_orient; delbound = (float)(curr_mhi_timestamp - mhi_duration); mhi_rows = mhi->rows; mhi_cols = mhi->cols; if( CV_IS_MAT_CONT( mhi->type & mask->type & orient->type )) { mhi_cols *= mhi_rows; mhi_rows = 1; } cvGetRow( mhi, &mhi_row, 0 ); cvGetRow( mask, &mask_row, 0 ); cvGetRow( orient, &orient_row, 0 ); /* a = 254/(255*dt) b = 1 - t*a = 1 - 254*t/(255*dur) = (255*dt - 254*t)/(255*dt) = (dt - (t - dt)*254)/(255*dt); -------------------------------------------------------- ax + b = 254*x/(255*dt) + (dt - (t - dt)*254)/(255*dt) = (254*x + dt - (t - dt)*254)/(255*dt) = ((x - (t - dt))*254 + dt)/(255*dt) = (((x - (t - dt))/dt)*254 + 1)/255 = (((x - low_time)/dt)*254 + 1)/255 */ for( y = 0; y < mhi_rows; y++ ) { mhi_row.data.ptr = mhi->data.ptr + mhi->step*y; mask_row.data.ptr = mask->data.ptr + mask->step*y; orient_row.data.ptr = orient->data.ptr + orient->step*y; for( x = 0; x < mhi_cols; x++ ) if( mask_row.data.ptr[x] != 0 && mhi_row.data.fl[x] > delbound ) { /* orient in 0..360, base_orient in 0..360 -> (rel_angle = orient - base_orient) in -360..360. rel_angle is translated to -180..180 */ double weight = mhi_row.data.fl[x] * a + b; int rel_angle = cvRound( orient_row.data.fl[x] - fbase_orient ); rel_angle += (rel_angle < -180 ? 360 : 0); rel_angle += (rel_angle > 180 ? -360 : 0); if( abs(rel_angle) < 90 ) { shift_orient += weight * rel_angle; shift_weight += weight; } } } // add the dominant orientation and the relative shift if( shift_weight == 0 ) shift_weight = 0.01; base_orient = base_orient + cvRound( shift_orient / shift_weight ); base_orient -= (base_orient < 360 ? 0 : 360); base_orient += (base_orient >= 0 ? 0 : 360); angle = base_orient; __END__; cvReleaseHist( &hist ); return angle; } CV_IMPL CvSeq* cvSegmentMotion( const CvArr* mhiimg, CvArr* segmask, CvMemStorage* storage, double timestamp, double seg_thresh ) { CvSeq* components = 0; CvMat* mask8u = 0; CV_FUNCNAME( "cvSegmentMotion" ); __BEGIN__; CvMat mhistub, *mhi = (CvMat*)mhiimg; CvMat maskstub, *mask = (CvMat*)segmask; Cv32suf v, comp_idx; int stub_val, ts; int x, y; if( !storage ) CV_ERROR( CV_StsNullPtr, "NULL memory storage" ); CV_CALL( mhi = cvGetMat( mhi, &mhistub )); CV_CALL( mask = cvGetMat( mask, &maskstub )); if( CV_MAT_TYPE( mhi->type ) != CV_32FC1 || CV_MAT_TYPE( mask->type ) != CV_32FC1 ) CV_ERROR( CV_BadDepth, "Both MHI and the destination mask" ); if( !CV_ARE_SIZES_EQ( mhi, mask )) CV_ERROR( CV_StsUnmatchedSizes, "" ); CV_CALL( mask8u = cvCreateMat( mhi->rows + 2, mhi->cols + 2, CV_8UC1 )); cvZero( mask8u ); cvZero( mask ); CV_CALL( components = cvCreateSeq( CV_SEQ_KIND_GENERIC, sizeof(CvSeq), sizeof(CvConnectedComp), storage )); v.f = (float)timestamp; ts = v.i; v.f = FLT_MAX*0.1f; stub_val = v.i; comp_idx.f = 1; for( y = 0; y < mhi->rows; y++ ) { int* mhi_row = (int*)(mhi->data.ptr + y*mhi->step); for( x = 0; x < mhi->cols; x++ ) { if( mhi_row[x] == 0 ) mhi_row[x] = stub_val; } } for( y = 0; y < mhi->rows; y++ ) { int* mhi_row = (int*)(mhi->data.ptr + y*mhi->step); uchar* mask8u_row = mask8u->data.ptr + (y+1)*mask8u->step + 1; for( x = 0; x < mhi->cols; x++ ) { if( mhi_row[x] == ts && mask8u_row[x] == 0 ) { CvConnectedComp comp; int x1, y1; CvScalar _seg_thresh = cvRealScalar(seg_thresh); CvPoint seed = cvPoint(x,y); CV_CALL( cvFloodFill( mhi, seed, cvRealScalar(0), _seg_thresh, _seg_thresh, &comp, CV_FLOODFILL_MASK_ONLY + 2*256 + 4, mask8u )); for( y1 = 0; y1 < comp.rect.height; y1++ ) { int* mask_row1 = (int*)(mask->data.ptr + (comp.rect.y + y1)*mask->step) + comp.rect.x; uchar* mask8u_row1 = mask8u->data.ptr + (comp.rect.y + y1+1)*mask8u->step + comp.rect.x+1; for( x1 = 0; x1 < comp.rect.width; x1++ ) { if( mask8u_row1[x1] > 1 ) { mask8u_row1[x1] = 1; mask_row1[x1] = comp_idx.i; } } } comp_idx.f++; cvSeqPush( components, &comp ); } } } for( y = 0; y < mhi->rows; y++ ) { int* mhi_row = (int*)(mhi->data.ptr + y*mhi->step); for( x = 0; x < mhi->cols; x++ ) { if( mhi_row[x] == stub_val ) mhi_row[x] = 0; } } __END__; cvReleaseMat( &mask8u ); return components; } /* End of file. */