/*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" typedef struct { float xx; float xy; float yy; float xt; float yt; } icvDerProduct; #define CONV( A, B, C) ((float)( A + (B<<1) + C )) /*F/////////////////////////////////////////////////////////////////////////////////////// // Name: icvCalcOpticalFlowLK_8u32fR ( Lucas & Kanade method ) // Purpose: calculate Optical flow for 2 images using Lucas & Kanade algorithm // Context: // Parameters: // imgA, // pointer to first frame ROI // imgB, // pointer to second frame ROI // imgStep, // width of single row of source images in bytes // imgSize, // size of the source image ROI // winSize, // size of the averaging window used for grouping // velocityX, // pointer to horizontal and // velocityY, // vertical components of optical flow ROI // velStep // width of single row of velocity frames in bytes // // Returns: CV_OK - all ok // CV_OUTOFMEM_ERR - insufficient memory for function work // CV_NULLPTR_ERR - if one of input pointers is NULL // CV_BADSIZE_ERR - wrong input sizes interrelation // // Notes: 1.Optical flow to be computed for every pixel in ROI // 2.For calculating spatial derivatives we use 3x3 Sobel operator. // 3.We use the following border mode. // The last row or column is replicated for the border // ( IPL_BORDER_REPLICATE in IPL ). // // //F*/ static CvStatus CV_STDCALL icvCalcOpticalFlowLK_8u32fR( uchar * imgA, uchar * imgB, int imgStep, CvSize imgSize, CvSize winSize, float *velocityX, float *velocityY, int velStep ) { /* Loops indexes */ int i, j, k; /* Gaussian separable kernels */ float GaussX[16]; float GaussY[16]; float *KerX; float *KerY; /* Buffers for Sobel calculations */ float *MemX[2]; float *MemY[2]; float ConvX, ConvY; float GradX, GradY, GradT; int winWidth = winSize.width; int winHeight = winSize.height; int imageWidth = imgSize.width; int imageHeight = imgSize.height; int HorRadius = (winWidth - 1) >> 1; int VerRadius = (winHeight - 1) >> 1; int PixelLine; int ConvLine; int BufferAddress; int BufferHeight = 0; int BufferWidth; int BufferSize; /* buffers derivatives product */ icvDerProduct *II; /* buffers for gaussian horisontal convolution */ icvDerProduct *WII; /* variables for storing number of first pixel of image line */ int Line1; int Line2; int Line3; /* we must have 2*2 linear system coeffs | A1B2 B1 | {u} {C1} {0} | | { } + { } = { } | A2 A1B2 | {v} {C2} {0} */ float A1B2, A2, B1, C1, C2; int pixNumber; /* auxiliary */ int NoMem = 0; velStep /= sizeof(velocityX[0]); /* Checking bad arguments */ if( imgA == NULL ) return CV_NULLPTR_ERR; if( imgB == NULL ) return CV_NULLPTR_ERR; if( imageHeight < winHeight ) return CV_BADSIZE_ERR; if( imageWidth < winWidth ) return CV_BADSIZE_ERR; if( winHeight >= 16 ) return CV_BADSIZE_ERR; if( winWidth >= 16 ) return CV_BADSIZE_ERR; if( !(winHeight & 1) ) return CV_BADSIZE_ERR; if( !(winWidth & 1) ) return CV_BADSIZE_ERR; BufferHeight = winHeight; BufferWidth = imageWidth; /****************************************************************************************/ /* Computing Gaussian coeffs */ /****************************************************************************************/ GaussX[0] = 1; GaussY[0] = 1; for( i = 1; i < winWidth; i++ ) { GaussX[i] = 1; for( j = i - 1; j > 0; j-- ) { GaussX[j] += GaussX[j - 1]; } } for( i = 1; i < winHeight; i++ ) { GaussY[i] = 1; for( j = i - 1; j > 0; j-- ) { GaussY[j] += GaussY[j - 1]; } } KerX = &GaussX[HorRadius]; KerY = &GaussY[VerRadius]; /****************************************************************************************/ /* Allocating memory for all buffers */ /****************************************************************************************/ for( k = 0; k < 2; k++ ) { MemX[k] = (float *) cvAlloc( (imgSize.height) * sizeof( float )); if( MemX[k] == NULL ) NoMem = 1; MemY[k] = (float *) cvAlloc( (imgSize.width) * sizeof( float )); if( MemY[k] == NULL ) NoMem = 1; } BufferSize = BufferHeight * BufferWidth; II = (icvDerProduct *) cvAlloc( BufferSize * sizeof( icvDerProduct )); WII = (icvDerProduct *) cvAlloc( BufferSize * sizeof( icvDerProduct )); if( (II == NULL) || (WII == NULL) ) NoMem = 1; if( NoMem ) { for( k = 0; k < 2; k++ ) { if( MemX[k] ) cvFree( &MemX[k] ); if( MemY[k] ) cvFree( &MemY[k] ); } if( II ) cvFree( &II ); if( WII ) cvFree( &WII ); return CV_OUTOFMEM_ERR; } /****************************************************************************************/ /* Calculate first line of memX and memY */ /****************************************************************************************/ MemY[0][0] = MemY[1][0] = CONV( imgA[0], imgA[0], imgA[1] ); MemX[0][0] = MemX[1][0] = CONV( imgA[0], imgA[0], imgA[imgStep] ); for( j = 1; j < imageWidth - 1; j++ ) { MemY[0][j] = MemY[1][j] = CONV( imgA[j - 1], imgA[j], imgA[j + 1] ); } pixNumber = imgStep; for( i = 1; i < imageHeight - 1; i++ ) { MemX[0][i] = MemX[1][i] = CONV( imgA[pixNumber - imgStep], imgA[pixNumber], imgA[pixNumber + imgStep] ); pixNumber += imgStep; } MemY[0][imageWidth - 1] = MemY[1][imageWidth - 1] = CONV( imgA[imageWidth - 2], imgA[imageWidth - 1], imgA[imageWidth - 1] ); MemX[0][imageHeight - 1] = MemX[1][imageHeight - 1] = CONV( imgA[pixNumber - imgStep], imgA[pixNumber], imgA[pixNumber] ); /****************************************************************************************/ /* begin scan image, calc derivatives and solve system */ /****************************************************************************************/ PixelLine = -VerRadius; ConvLine = 0; BufferAddress = -BufferWidth; while( PixelLine < imageHeight ) { if( ConvLine < imageHeight ) { /*Here we calculate derivatives for line of image */ int address; i = ConvLine; int L1 = i - 1; int L2 = i; int L3 = i + 1; int memYline = L3 & 1; if( L1 < 0 ) L1 = 0; if( L3 >= imageHeight ) L3 = imageHeight - 1; BufferAddress += BufferWidth; BufferAddress -= ((BufferAddress >= BufferSize) ? 0xffffffff : 0) & BufferSize; address = BufferAddress; Line1 = L1 * imgStep; Line2 = L2 * imgStep; Line3 = L3 * imgStep; /* Process first pixel */ ConvX = CONV( imgA[Line1 + 1], imgA[Line2 + 1], imgA[Line3 + 1] ); ConvY = CONV( imgA[Line3], imgA[Line3], imgA[Line3 + 1] ); GradY = ConvY - MemY[memYline][0]; GradX = ConvX - MemX[1][L2]; MemY[memYline][0] = ConvY; MemX[1][L2] = ConvX; GradT = (float) (imgB[Line2] - imgA[Line2]); II[address].xx = GradX * GradX; II[address].xy = GradX * GradY; II[address].yy = GradY * GradY; II[address].xt = GradX * GradT; II[address].yt = GradY * GradT; address++; /* Process middle of line */ for( j = 1; j < imageWidth - 1; j++ ) { ConvX = CONV( imgA[Line1 + j + 1], imgA[Line2 + j + 1], imgA[Line3 + j + 1] ); ConvY = CONV( imgA[Line3 + j - 1], imgA[Line3 + j], imgA[Line3 + j + 1] ); GradY = ConvY - MemY[memYline][j]; GradX = ConvX - MemX[(j - 1) & 1][L2]; MemY[memYline][j] = ConvY; MemX[(j - 1) & 1][L2] = ConvX; GradT = (float) (imgB[Line2 + j] - imgA[Line2 + j]); II[address].xx = GradX * GradX; II[address].xy = GradX * GradY; II[address].yy = GradY * GradY; II[address].xt = GradX * GradT; II[address].yt = GradY * GradT; address++; } /* Process last pixel of line */ ConvX = CONV( imgA[Line1 + imageWidth - 1], imgA[Line2 + imageWidth - 1], imgA[Line3 + imageWidth - 1] ); ConvY = CONV( imgA[Line3 + imageWidth - 2], imgA[Line3 + imageWidth - 1], imgA[Line3 + imageWidth - 1] ); GradY = ConvY - MemY[memYline][imageWidth - 1]; GradX = ConvX - MemX[(imageWidth - 2) & 1][L2]; MemY[memYline][imageWidth - 1] = ConvY; GradT = (float) (imgB[Line2 + imageWidth - 1] - imgA[Line2 + imageWidth - 1]); II[address].xx = GradX * GradX; II[address].xy = GradX * GradY; II[address].yy = GradY * GradY; II[address].xt = GradX * GradT; II[address].yt = GradY * GradT; address++; /* End of derivatives for line */ /****************************************************************************************/ /* ---------Calculating horizontal convolution of processed line----------------------- */ /****************************************************************************************/ address -= BufferWidth; /* process first HorRadius pixels */ for( j = 0; j < HorRadius; j++ ) { int jj; WII[address].xx = 0; WII[address].xy = 0; WII[address].yy = 0; WII[address].xt = 0; WII[address].yt = 0; for( jj = -j; jj <= HorRadius; jj++ ) { float Ker = KerX[jj]; WII[address].xx += II[address + jj].xx * Ker; WII[address].xy += II[address + jj].xy * Ker; WII[address].yy += II[address + jj].yy * Ker; WII[address].xt += II[address + jj].xt * Ker; WII[address].yt += II[address + jj].yt * Ker; } address++; } /* process inner part of line */ for( j = HorRadius; j < imageWidth - HorRadius; j++ ) { int jj; float Ker0 = KerX[0]; WII[address].xx = 0; WII[address].xy = 0; WII[address].yy = 0; WII[address].xt = 0; WII[address].yt = 0; for( jj = 1; jj <= HorRadius; jj++ ) { float Ker = KerX[jj]; WII[address].xx += (II[address - jj].xx + II[address + jj].xx) * Ker; WII[address].xy += (II[address - jj].xy + II[address + jj].xy) * Ker; WII[address].yy += (II[address - jj].yy + II[address + jj].yy) * Ker; WII[address].xt += (II[address - jj].xt + II[address + jj].xt) * Ker; WII[address].yt += (II[address - jj].yt + II[address + jj].yt) * Ker; } WII[address].xx += II[address].xx * Ker0; WII[address].xy += II[address].xy * Ker0; WII[address].yy += II[address].yy * Ker0; WII[address].xt += II[address].xt * Ker0; WII[address].yt += II[address].yt * Ker0; address++; } /* process right side */ for( j = imageWidth - HorRadius; j < imageWidth; j++ ) { int jj; WII[address].xx = 0; WII[address].xy = 0; WII[address].yy = 0; WII[address].xt = 0; WII[address].yt = 0; for( jj = -HorRadius; jj < imageWidth - j; jj++ ) { float Ker = KerX[jj]; WII[address].xx += II[address + jj].xx * Ker; WII[address].xy += II[address + jj].xy * Ker; WII[address].yy += II[address + jj].yy * Ker; WII[address].xt += II[address + jj].xt * Ker; WII[address].yt += II[address + jj].yt * Ker; } address++; } } /****************************************************************************************/ /* Calculating velocity line */ /****************************************************************************************/ if( PixelLine >= 0 ) { int USpace; int BSpace; int address; if( PixelLine < VerRadius ) USpace = PixelLine; else USpace = VerRadius; if( PixelLine >= imageHeight - VerRadius ) BSpace = imageHeight - PixelLine - 1; else BSpace = VerRadius; address = ((PixelLine - USpace) % BufferHeight) * BufferWidth; for( j = 0; j < imageWidth; j++ ) { int addr = address; A1B2 = 0; A2 = 0; B1 = 0; C1 = 0; C2 = 0; for( i = -USpace; i <= BSpace; i++ ) { A2 += WII[addr + j].xx * KerY[i]; A1B2 += WII[addr + j].xy * KerY[i]; B1 += WII[addr + j].yy * KerY[i]; C2 += WII[addr + j].xt * KerY[i]; C1 += WII[addr + j].yt * KerY[i]; addr += BufferWidth; addr -= ((addr >= BufferSize) ? 0xffffffff : 0) & BufferSize; } /****************************************************************************************\ * Solve Linear System * \****************************************************************************************/ { float delta = (A1B2 * A1B2 - A2 * B1); if( delta ) { /* system is not singular - solving by Kramer method */ float deltaX; float deltaY; float Idelta = 8 / delta; deltaX = -(C1 * A1B2 - C2 * B1); deltaY = -(A1B2 * C2 - A2 * C1); velocityX[j] = deltaX * Idelta; velocityY[j] = deltaY * Idelta; } else { /* singular system - find optical flow in gradient direction */ float Norm = (A1B2 + A2) * (A1B2 + A2) + (B1 + A1B2) * (B1 + A1B2); if( Norm ) { float IGradNorm = 8 / Norm; float temp = -(C1 + C2) * IGradNorm; velocityX[j] = (A1B2 + A2) * temp; velocityY[j] = (B1 + A1B2) * temp; } else { velocityX[j] = 0; velocityY[j] = 0; } } } /****************************************************************************************\ * End of Solving Linear System * \****************************************************************************************/ } /*for */ velocityX += velStep; velocityY += velStep; } /*for */ PixelLine++; ConvLine++; } /* Free memory */ for( k = 0; k < 2; k++ ) { cvFree( &MemX[k] ); cvFree( &MemY[k] ); } cvFree( &II ); cvFree( &WII ); return CV_OK; } /*icvCalcOpticalFlowLK_8u32fR*/ /*F/////////////////////////////////////////////////////////////////////////////////////// // Name: cvCalcOpticalFlowLK // Purpose: Optical flow implementation // Context: // Parameters: // srcA, srcB - source image // velx, vely - destination image // Returns: // // Notes: //F*/ CV_IMPL void cvCalcOpticalFlowLK( const void* srcarrA, const void* srcarrB, CvSize winSize, void* velarrx, void* velarry ) { CV_FUNCNAME( "cvCalcOpticalFlowLK" ); __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 ) || !CV_ARE_SIZES_EQ( srcA, velx )) 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, "source and destination images have different step" ); IPPI_CALL( icvCalcOpticalFlowLK_8u32fR( (uchar*)srcA->data.ptr, (uchar*)srcB->data.ptr, srcA->step, cvGetMatSize( srcA ), winSize, velx->data.fl, vely->data.fl, velx->step )); __END__; } /* End of file. */