/*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" /* Finds L1 norm between two blocks. */ static int icvCmpBlocksL1_8u_C1( const uchar * vec1, const uchar * vec2, int len ) { int i, sum = 0; for( i = 0; i <= len - 4; i += 4 ) { int t0 = abs(vec1[i] - vec2[i]); int t1 = abs(vec1[i + 1] - vec2[i + 1]); int t2 = abs(vec1[i + 2] - vec2[i + 2]); int t3 = abs(vec1[i + 3] - vec2[i + 3]); sum += t0 + t1 + t2 + t3; } for( ; i < len; i++ ) { int t0 = abs(vec1[i] - vec2[i]); sum += t0; } return sum; } static void icvCopyBM_8u_C1R( const uchar* src, int src_step, uchar* dst, int dst_step, CvSize size ) { for( ; size.height--; src += src_step, dst += dst_step ) memcpy( dst, src, size.width ); } /*F/////////////////////////////////////////////////////////////////////////////////////// // Name: icvCalcOpticalFlowBM_8u32fR // Purpose: calculate Optical flow for 2 images using block matching algorithm // Context: // Parameters: // imgA, // pointer to first frame ROI // imgB, // pointer to second frame ROI // imgStep, // full width of input images in bytes // imgSize, // size of the image // blockSize, // size of basic blocks which are compared // shiftSize, // coordinates increments. // maxRange, // size of the scanned neighborhood. // usePrevious, // flag of using previous velocity field // velocityX, // pointer to ROI of horizontal and // velocityY, // vertical components of optical flow // velStep); // full width of velocity frames in bytes // Returns: CV_OK or error code // Notes: //F*/ #define SMALL_DIFF 2 #define BIG_DIFF 128 static CvStatus CV_STDCALL icvCalcOpticalFlowBM_8u32fR( uchar * imgA, uchar * imgB, int imgStep, CvSize imgSize, CvSize blockSize, CvSize shiftSize, CvSize maxRange, int usePrev, float *velocityX, float *velocityY, int velStep ) { const float back = 1.f / (float) (1 << 16); /* scanning scheme coordinates */ CvPoint *ss = 0; int ss_count = 0; int stand_accept_level = blockSize.height * blockSize.width * SMALL_DIFF; int stand_escape_level = blockSize.height * blockSize.width * BIG_DIFF; int i, j; int *int_velocityX = (int *) velocityX; int *int_velocityY = (int *) velocityY; /* if image sizes can't be divided by block sizes then right blocks will */ /* have not full width - BorderWidth */ /* and bottom blocks will */ /* have not full height - BorderHeight */ int BorderWidth; int BorderHeight; int CurrentWidth; int CurrentHeight; int NumberBlocksX; int NumberBlocksY; int Y1 = 0; int X1 = 0; int DownStep = blockSize.height * imgStep; uchar *blockA = 0; uchar *blockB = 0; uchar *blockZ = 0; int blSize = blockSize.width * blockSize.height; int bufferSize = cvAlign(blSize + 9,16); int cmpSize = cvAlign(blSize,4); int patch_ofs = blSize & -8; int64 patch_mask = (((int64) 1) << (blSize - patch_ofs * 8)) - 1; velStep /= sizeof(velocityX[0]); if( patch_ofs == blSize ) patch_mask = (int64) - 1; /****************************************************************************************\ * Checking bad arguments * \****************************************************************************************/ if( imgA == NULL ) return CV_NULLPTR_ERR; if( imgB == NULL ) return CV_NULLPTR_ERR; /****************************************************************************************\ * Allocate buffers * \****************************************************************************************/ blockA = (uchar *) cvAlloc( bufferSize * 3 ); if( !blockA ) return CV_OUTOFMEM_ERR; blockB = blockA + bufferSize; blockZ = blockB + bufferSize; memset( blockZ, 0, bufferSize ); ss = (CvPoint *) cvAlloc( (2 * maxRange.width + 1) * (2 * maxRange.height + 1) * sizeof( CvPoint )); if( !ss ) { cvFree( &blockA ); return CV_OUTOFMEM_ERR; } /****************************************************************************************\ * Calculate scanning scheme * \****************************************************************************************/ { int X_shift_count = maxRange.width / shiftSize.width; int Y_shift_count = maxRange.height / shiftSize.height; int min_count = MIN( X_shift_count, Y_shift_count ); /* cycle by neighborhood rings */ /* scanning scheme is . 9 10 11 12 . 8 1 2 13 . 7 * 3 14 . 6 5 4 15 20 19 18 17 16 */ for( i = 0; i < min_count; i++ ) { /* four cycles along sides */ int y = -(i + 1) * shiftSize.height; int x = -(i + 1) * shiftSize.width; /* upper side */ for( j = -i; j <= i + 1; j++, ss_count++ ) { x += shiftSize.width; ss[ss_count].x = x; ss[ss_count].y = y; } /* right side */ for( j = -i; j <= i + 1; j++, ss_count++ ) { y += shiftSize.height; ss[ss_count].x = x; ss[ss_count].y = y; } /* bottom side */ for( j = -i; j <= i + 1; j++, ss_count++ ) { x -= shiftSize.width; ss[ss_count].x = x; ss[ss_count].y = y; } /* left side */ for( j = -i; j <= i + 1; j++, ss_count++ ) { y -= shiftSize.height; ss[ss_count].x = x; ss[ss_count].y = y; } } /* the rest part */ if( X_shift_count < Y_shift_count ) { int xleft = -min_count * shiftSize.width; /* cycle by neighbor rings */ for( i = min_count; i < Y_shift_count; i++ ) { /* two cycles by x */ int y = -(i + 1) * shiftSize.height; int x = xleft; /* upper side */ for( j = -X_shift_count; j <= X_shift_count; j++, ss_count++ ) { ss[ss_count].x = x; ss[ss_count].y = y; x += shiftSize.width; } x = xleft; y = -y; /* bottom side */ for( j = -X_shift_count; j <= X_shift_count; j++, ss_count++ ) { ss[ss_count].x = x; ss[ss_count].y = y; x += shiftSize.width; } } } else if( X_shift_count > Y_shift_count ) { int yupper = -min_count * shiftSize.height; /* cycle by neighbor rings */ for( i = min_count; i < X_shift_count; i++ ) { /* two cycles by y */ int x = -(i + 1) * shiftSize.width; int y = yupper; /* left side */ for( j = -Y_shift_count; j <= Y_shift_count; j++, ss_count++ ) { ss[ss_count].x = x; ss[ss_count].y = y; y += shiftSize.height; } y = yupper; x = -x; /* right side */ for( j = -Y_shift_count; j <= Y_shift_count; j++, ss_count++ ) { ss[ss_count].x = x; ss[ss_count].y = y; y += shiftSize.height; } } } } /****************************************************************************************\ * Calculate some neeeded variables * \****************************************************************************************/ /* Calculate number of full blocks */ NumberBlocksX = (int) imgSize.width / blockSize.width; NumberBlocksY = (int) imgSize.height / blockSize.height; /* add 1 if not full border blocks exist */ BorderWidth = imgSize.width % blockSize.width; if( BorderWidth ) NumberBlocksX++; else BorderWidth = blockSize.width; BorderHeight = imgSize.height % blockSize.height; if( BorderHeight ) NumberBlocksY++; else BorderHeight = blockSize.height; /****************************************************************************************\ * Round input velocities integer searching area center position * \****************************************************************************************/ if( usePrev ) { float *velxf = velocityX, *velyf = velocityY; int* velx = (int*)velocityX, *vely = (int*)velocityY; for( i = 0; i < NumberBlocksY; i++, velxf += velStep, velyf += velStep, velx += velStep, vely += velStep ) { for( j = 0; j < NumberBlocksX; j++ ) { int vx = cvRound( velxf[j] ), vy = cvRound( velyf[j] ); velx[j] = vx; vely[j] = vy; } } } /****************************************************************************************\ * Main loop * \****************************************************************************************/ Y1 = 0; for( i = 0; i < NumberBlocksY; i++ ) { /* calculate height of current block */ CurrentHeight = (i == NumberBlocksY - 1) ? BorderHeight : blockSize.height; X1 = 0; for( j = 0; j < NumberBlocksX; j++ ) { int accept_level; int escape_level; int blDist; int VelocityX = 0; int VelocityY = 0; int offX = 0, offY = 0; int CountDirection = 1; int main_flag = i < NumberBlocksY - 1 && j < NumberBlocksX - 1; CvSize CurSize; /* calculate width of current block */ CurrentWidth = (j == NumberBlocksX - 1) ? BorderWidth : blockSize.width; /* compute initial offset */ if( usePrev ) { offX = int_velocityX[j]; offY = int_velocityY[j]; } CurSize.width = CurrentWidth; CurSize.height = CurrentHeight; if( main_flag ) { icvCopyBM_8u_C1R( imgA + X1, imgStep, blockA, CurSize.width, CurSize ); icvCopyBM_8u_C1R( imgB + (Y1 + offY)*imgStep + (X1 + offX), imgStep, blockB, CurSize.width, CurSize ); *((int64 *) (blockA + patch_ofs)) &= patch_mask; *((int64 *) (blockB + patch_ofs)) &= patch_mask; } else { memset( blockA, 0, bufferSize ); memset( blockB, 0, bufferSize ); icvCopyBM_8u_C1R( imgA + X1, imgStep, blockA, blockSize.width, CurSize ); icvCopyBM_8u_C1R( imgB + (Y1 + offY) * imgStep + (X1 + offX), imgStep, blockB, blockSize.width, CurSize ); } if( !main_flag ) { int tmp = CurSize.width * CurSize.height; accept_level = tmp * SMALL_DIFF; escape_level = tmp * BIG_DIFF; } else { accept_level = stand_accept_level; escape_level = stand_escape_level; } blDist = icvCmpBlocksL1_8u_C1( blockA, blockB, cmpSize ); if( blDist > accept_level ) { int k; int VelX = 0; int VelY = 0; /* walk around basic block */ /* cycle for neighborhood */ for( k = 0; k < ss_count; k++ ) { int tmpDist; int Y2 = Y1 + offY + ss[k].y; int X2 = X1 + offX + ss[k].x; /* if we break upper border */ if( Y2 < 0 ) { continue; } /* if we break bottom border */ if( Y2 + CurrentHeight >= imgSize.height ) { continue; } /* if we break left border */ if( X2 < 0 ) { continue; } /* if we break right border */ if( X2 + CurrentWidth >= imgSize.width ) { continue; } if( main_flag ) { icvCopyBM_8u_C1R( imgB + Y2 * imgStep + X2, imgStep, blockB, CurSize.width, CurSize ); *((int64 *) (blockB + patch_ofs)) &= patch_mask; } else { memset( blockB, 0, bufferSize ); icvCopyBM_8u_C1R( imgB + Y1 * imgStep + X1, imgStep, blockB, blockSize.width, CurSize ); } tmpDist = icvCmpBlocksL1_8u_C1( blockA, blockB, cmpSize ); if( tmpDist < accept_level ) { VelX = ss[k].x; VelY = ss[k].y; break; /*for */ } else if( tmpDist < blDist ) { blDist = tmpDist; VelX = ss[k].x; VelY = ss[k].y; CountDirection = 1; } else if( tmpDist == blDist ) { VelX += ss[k].x; VelY += ss[k].y; CountDirection++; } } if( blDist > escape_level ) { VelX = VelY = 0; CountDirection = 1; } if( CountDirection > 1 ) { int temp = CountDirection == 2 ? 1 << 15 : ((1 << 16) / CountDirection); VelocityX = VelX * temp; VelocityY = VelY * temp; } else { VelocityX = VelX << 16; VelocityY = VelY << 16; } } /*if */ int_velocityX[j] = VelocityX + (offX << 16); int_velocityY[j] = VelocityY + (offY << 16); X1 += blockSize.width; } /*for */ int_velocityX += velStep; int_velocityY += velStep; imgA += DownStep; Y1 += blockSize.height; } /*for */ /****************************************************************************************\ * Converting fixed point velocities to floating point * \****************************************************************************************/ { float *velxf = velocityX, *velyf = velocityY; int* velx = (int*)velocityX, *vely = (int*)velocityY; for( i = 0; i < NumberBlocksY; i++, velxf += velStep, velyf += velStep, velx += velStep, vely += velStep ) { for( j = 0; j < NumberBlocksX; j++ ) { float vx = (float)velx[j]*back, vy = (float)vely[j]*back; velxf[j] = vx; velyf[j] = vy; } } } cvFree( &ss ); cvFree( &blockA ); return CV_OK; } /*cvCalcOpticalFlowBM_8u */ /*F/////////////////////////////////////////////////////////////////////////////////////// // Name: cvCalcOpticalFlowBM // Purpose: Optical flow implementation // Context: // Parameters: // srcA, srcB - source image // velx, vely - destination image // Returns: // // Notes: //F*/ CV_IMPL void cvCalcOpticalFlowBM( const void* srcarrA, const void* srcarrB, CvSize blockSize, CvSize shiftSize, CvSize maxRange, int usePrevious, void* velarrx, void* velarry ) { CV_FUNCNAME( "cvCalcOpticalFlowBM" ); __BEGIN__; CvMat stubA, *srcA = (CvMat*)srcarrA; CvMat stubB, *srcB = (CvMat*)srcarrB; CvMat stubx, *velx = (CvMat*)velarrx; CvMat stuby, *vely = (CvMat*)velarry; CV_CALL( srcA = cvGetMat( srcA, &stubA )); CV_CALL( srcB = cvGetMat( srcB, &stubB )); CV_CALL( velx = cvGetMat( velx, &stubx )); CV_CALL( vely = cvGetMat( vely, &stuby )); if( !CV_ARE_TYPES_EQ( srcA, srcB )) CV_ERROR( CV_StsUnmatchedFormats, "Source images have different formats" ); if( !CV_ARE_TYPES_EQ( velx, vely )) CV_ERROR( CV_StsUnmatchedFormats, "Destination images have different formats" ); if( !CV_ARE_SIZES_EQ( srcA, srcB ) || !CV_ARE_SIZES_EQ( velx, vely ) || (unsigned)(velx->width*blockSize.width - srcA->width) >= (unsigned)blockSize.width || (unsigned)(velx->height*blockSize.height - srcA->height) >= (unsigned)blockSize.height ) CV_ERROR( CV_StsUnmatchedSizes, "" ); if( CV_MAT_TYPE( srcA->type ) != CV_8UC1 || CV_MAT_TYPE( velx->type ) != CV_32FC1 ) CV_ERROR( CV_StsUnsupportedFormat, "Source images must have 8uC1 type and " "destination images must have 32fC1 type" ); if( srcA->step != srcB->step || velx->step != vely->step ) CV_ERROR( CV_BadStep, "two source or two destination images have different steps" ); IPPI_CALL( icvCalcOpticalFlowBM_8u32fR( (uchar*)srcA->data.ptr, (uchar*)srcB->data.ptr, srcA->step, cvGetMatSize( srcA ), blockSize, shiftSize, maxRange, usePrevious, velx->data.fl, vely->data.fl, velx->step )); __END__; } /* End of file. */