├── .gitignore ├── LICENSE ├── README.md ├── demo.py ├── examples ├── car1.jpg ├── car2.jpg ├── car2Warped.jpg └── outFlow.png ├── pyflow.pyx ├── setup.py └── src ├── Coarse2FineFlowWrapper.cpp ├── Coarse2FineFlowWrapper.h ├── GaussianPyramid.cpp ├── GaussianPyramid.h ├── Image.h ├── ImageIO.h ├── ImageProcessing.h ├── Matrix.h ├── NoiseModel.h ├── OpticalFlow.cpp ├── OpticalFlow.h ├── Stochastic.cpp ├── Stochastic.h ├── Vector.h └── project.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.cpp 3 | **/*.npy 4 | *.so 5 | examples/car2Warped_new.jpg 6 | examples/outFlow_new.png 7 | *.pyc 8 | __init__.py -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is a python wrapper around the Ce Liu's optical flow code downloaded 2 | from here: https://people.csail.mit.edu/celiu/OpticalFlow/. The following 3 | license applies for python wrapper only. 4 | 5 | -------------------------------------------------------------------------------- 6 | MIT License 7 | 8 | Copyright (c) 2017 Deepak Pathak 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | -------------------------------------------------------------------------------- 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Python Dense Optical Flow 2 | 3 | **Python** wrapper for Ce Liu's [C++ implementation](https://people.csail.mit.edu/celiu/OpticalFlow/) of Coarse2Fine Optical Flow. This is **super fast and accurate** optical flow method based on Coarse2Fine warping method from Thomas Brox. This python wrapper has minimal dependencies, and it also eliminates the need for C++ OpenCV library. For real time performance, one can additionally resize the images to a smaller size. 4 | 5 | Run the following steps to download, install and demo the library: 6 | ```Shell 7 | git clone https://github.com/pathak22/pyflow.git 8 | cd pyflow/ 9 | python setup.py build_ext -i 10 | python demo.py # -viz option to visualize output 11 | ``` 12 | 13 | This wrapper code was developed as part of our [CVPR 2017 paper on Unsupervised Learning using unlabeled videos](http://cs.berkeley.edu/~pathak/unsupervised_video/). Github repository for our CVPR 17 paper is [here](https://github.com/pathak22/unsupervised-video). 14 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | # Author: Deepak Pathak (c) 2016 2 | 3 | from __future__ import absolute_import 4 | from __future__ import division 5 | from __future__ import print_function 6 | # from __future__ import unicode_literals 7 | import numpy as np 8 | from PIL import Image 9 | import time 10 | import argparse 11 | import pyflow 12 | 13 | parser = argparse.ArgumentParser( 14 | description='Demo for python wrapper of Coarse2Fine Optical Flow') 15 | parser.add_argument( 16 | '-viz', dest='viz', action='store_true', 17 | help='Visualize (i.e. save) output of flow.') 18 | args = parser.parse_args() 19 | 20 | im1 = np.array(Image.open('examples/car1.jpg')) 21 | im2 = np.array(Image.open('examples/car2.jpg')) 22 | im1 = im1.astype(float) / 255. 23 | im2 = im2.astype(float) / 255. 24 | 25 | # Flow Options: 26 | alpha = 0.012 27 | ratio = 0.75 28 | minWidth = 20 29 | nOuterFPIterations = 7 30 | nInnerFPIterations = 1 31 | nSORIterations = 30 32 | colType = 0 # 0 or default:RGB, 1:GRAY (but pass gray image with shape (h,w,1)) 33 | 34 | s = time.time() 35 | u, v, im2W = pyflow.coarse2fine_flow( 36 | im1, im2, alpha, ratio, minWidth, nOuterFPIterations, nInnerFPIterations, 37 | nSORIterations, colType) 38 | e = time.time() 39 | print('Time Taken: %.2f seconds for image of size (%d, %d, %d)' % ( 40 | e - s, im1.shape[0], im1.shape[1], im1.shape[2])) 41 | flow = np.concatenate((u[..., None], v[..., None]), axis=2) 42 | np.save('examples/outFlow.npy', flow) 43 | 44 | if args.viz: 45 | import cv2 46 | hsv = np.zeros(im1.shape, dtype=np.uint8) 47 | hsv[:, :, 0] = 255 48 | hsv[:, :, 1] = 255 49 | mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1]) 50 | hsv[..., 0] = ang * 180 / np.pi / 2 51 | hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX) 52 | rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) 53 | cv2.imwrite('examples/outFlow_new.png', rgb) 54 | cv2.imwrite('examples/car2Warped_new.jpg', im2W[:, :, ::-1] * 255) 55 | -------------------------------------------------------------------------------- /examples/car1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathak22/pyflow/8f8ab9e90845f1b819b3833f995f481cb5d92166/examples/car1.jpg -------------------------------------------------------------------------------- /examples/car2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathak22/pyflow/8f8ab9e90845f1b819b3833f995f481cb5d92166/examples/car2.jpg -------------------------------------------------------------------------------- /examples/car2Warped.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathak22/pyflow/8f8ab9e90845f1b819b3833f995f481cb5d92166/examples/car2Warped.jpg -------------------------------------------------------------------------------- /examples/outFlow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pathak22/pyflow/8f8ab9e90845f1b819b3833f995f481cb5d92166/examples/outFlow.png -------------------------------------------------------------------------------- /pyflow.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | # distutils: sources = src/Coarse2FineFlowWrapper.cpp 3 | from __future__ import absolute_import 4 | from __future__ import division 5 | from __future__ import print_function 6 | # from __future__ import unicode_literals 7 | import numpy as np 8 | cimport numpy as np 9 | # Author: Deepak Pathak (c) 2016 10 | 11 | cdef extern from "src/Coarse2FineFlowWrapper.h": 12 | void Coarse2FineFlowWrapper(double * vx, double * vy, double * warpI2, 13 | const double * Im1, const double * Im2, 14 | double alpha, double ratio, int minWidth, 15 | int nOuterFPIterations, int nInnerFPIterations, 16 | int nSORIterations, int colType, 17 | int h, int w, int c); 18 | 19 | def coarse2fine_flow(np.ndarray[double, ndim=3, mode="c"] Im1 not None, 20 | np.ndarray[double, ndim=3, mode="c"] Im2 not None, 21 | double alpha=1, double ratio=0.5, int minWidth=40, 22 | int nOuterFPIterations=3, int nInnerFPIterations=1, 23 | int nSORIterations=20, int colType=0): 24 | """ 25 | Input Format: 26 | double * vx, double * vy, double * warpI2, 27 | const double * Im1 (range [0,1]), const double * Im2 (range [0,1]), 28 | double alpha (1), double ratio (0.5), int minWidth (40), 29 | int nOuterFPIterations (3), int nInnerFPIterations (1), 30 | int nSORIterations (20), 31 | int colType (0 or default:RGB, 1:GRAY) 32 | Images Format: (h,w,c): float64: [0,1] 33 | """ 34 | cdef int h = Im1.shape[0] 35 | cdef int w = Im1.shape[1] 36 | cdef int c = Im1.shape[2] 37 | cdef np.ndarray[double, ndim=2, mode="c"] vx = \ 38 | np.ascontiguousarray(np.zeros((h, w), dtype=np.float64)) 39 | cdef np.ndarray[double, ndim=2, mode="c"] vy = \ 40 | np.ascontiguousarray(np.zeros((h, w), dtype=np.float64)) 41 | cdef np.ndarray[double, ndim=3, mode="c"] warpI2 = \ 42 | np.ascontiguousarray(np.zeros((h, w, c), dtype=np.float64)) 43 | Im1 = np.ascontiguousarray(Im1) 44 | Im2 = np.ascontiguousarray(Im2) 45 | 46 | Coarse2FineFlowWrapper(&vx[0, 0], &vy[0, 0], &warpI2[0, 0, 0], 47 | &Im1[0, 0, 0], &Im2[0, 0, 0], 48 | alpha, ratio, minWidth, nOuterFPIterations, 49 | nInnerFPIterations, nSORIterations, colType, 50 | h, w, c) 51 | return vx, vy, warpI2 52 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Author: Deepak Pathak (c) 2016 2 | 3 | from __future__ import absolute_import 4 | from __future__ import division 5 | from __future__ import print_function 6 | # from __future__ import unicode_literals 7 | from distutils.core import setup 8 | from Cython.Build import cythonize 9 | from distutils.extension import Extension 10 | from glob import glob 11 | 12 | import numpy 13 | 14 | sourcefiles = ['pyflow.pyx', ] 15 | sourcefiles.extend(glob("src/*.cpp")) 16 | extensions = [Extension("pyflow", sourcefiles, include_dirs=[numpy.get_include()])] 17 | setup( 18 | name="pyflow", 19 | version="1.0", 20 | description="Python wrapper for the Coarse2Fine Optical Flow code.", 21 | author="Deepak Pathak", 22 | ext_modules=cythonize(extensions), 23 | include_dirs=[numpy.get_include()] 24 | ) 25 | -------------------------------------------------------------------------------- /src/Coarse2FineFlowWrapper.cpp: -------------------------------------------------------------------------------- 1 | // This is a wrapper for Ce Liu's Coarse2Fine optical flow implementation. 2 | // It converts the contiguous image array to the format needed by the optical 3 | // flow code. Handling conversion in the wrapper makes the cythonization 4 | // simpler. 5 | // Author: Deepak Pathak (c) 2016 6 | 7 | #include "Coarse2FineFlowWrapper.h" 8 | #include "Image.h" 9 | #include "OpticalFlow.h" 10 | using namespace std; 11 | 12 | void Coarse2FineFlowWrapper(double * vx, double * vy, double * warpI2, 13 | const double * Im1, const double * Im2, 14 | double alpha, double ratio, int minWidth, 15 | int nOuterFPIterations, int nInnerFPIterations, 16 | int nSORIterations, int colType, 17 | int h, int w, int c) { 18 | DImage ImFormatted1, ImFormatted2; 19 | DImage vxFormatted, vyFormatted, warpI2Formatted; 20 | 21 | // format input in the format needed by backend 22 | ImFormatted1.allocate(w, h, c); 23 | ImFormatted2.allocate(w, h, c); 24 | memcpy(ImFormatted1.pData, Im1, h * w * c * sizeof(double)); 25 | memcpy(ImFormatted2.pData, Im2, h * w * c * sizeof(double)); 26 | ImFormatted1.setColorType(colType); 27 | ImFormatted2.setColorType(colType); 28 | 29 | // call optical flow backend 30 | OpticalFlow::Coarse2FineFlow(vxFormatted, vyFormatted, warpI2Formatted, 31 | ImFormatted1, ImFormatted2, 32 | alpha, ratio, minWidth, 33 | nOuterFPIterations, nInnerFPIterations, 34 | nSORIterations); 35 | 36 | // copy formatted output to a contiguous memory to be returned 37 | memcpy(vx, vxFormatted.pData, h * w * sizeof(double)); 38 | memcpy(vy, vyFormatted.pData, h * w * sizeof(double)); 39 | memcpy(warpI2, warpI2Formatted.pData, h * w * c * sizeof(double)); 40 | 41 | // clear c memory 42 | ImFormatted1.clear(); 43 | ImFormatted2.clear(); 44 | vxFormatted.clear(); 45 | vyFormatted.clear(); 46 | warpI2Formatted.clear(); 47 | 48 | return; 49 | } 50 | -------------------------------------------------------------------------------- /src/Coarse2FineFlowWrapper.h: -------------------------------------------------------------------------------- 1 | // This is a wrapper for Ce Liu's Coarse2Fine optical flow implementation. 2 | // It converts the contiguous image array to the format needed by the optical 3 | // flow code. Handling conversion in the wrapper makes the cythonization 4 | // simpler. 5 | // Author: Deepak Pathak (c) 2016 6 | 7 | // override-include-guard 8 | extern void Coarse2FineFlowWrapper(double * vx, double * vy, double * warpI2, 9 | const double * Im1, const double * Im2, 10 | double alpha, double ratio, int minWidth, 11 | int nOuterFPIterations, int nInnerFPIterations, 12 | int nSORIterations, int colType, 13 | int h, int w, int c); 14 | -------------------------------------------------------------------------------- /src/GaussianPyramid.cpp: -------------------------------------------------------------------------------- 1 | // Author: Ce Liu (c) Dec, 2009; celiu@mit.edu 2 | // Modified By: Deepak Pathak (c) 2016; pathak@berkeley.edu 3 | 4 | #include "GaussianPyramid.h" 5 | #include "math.h" 6 | 7 | GaussianPyramid::GaussianPyramid(void) 8 | { 9 | ImPyramid=NULL; 10 | } 11 | 12 | GaussianPyramid::~GaussianPyramid(void) 13 | { 14 | if(ImPyramid!=NULL) 15 | delete []ImPyramid; 16 | } 17 | 18 | //--------------------------------------------------------------------------------------- 19 | // function to construct the pyramid 20 | // this is the slow way 21 | //--------------------------------------------------------------------------------------- 22 | /*void GaussianPyramid::ConstructPyramid(const DImage &image, double ratio, int minWidth) 23 | { 24 | // the ratio cannot be arbitrary numbers 25 | if(ratio>0.98 || ratio<0.4) 26 | ratio=0.75; 27 | // first decide how many levels 28 | nLevels=log((double)minWidth/image.width())/log(ratio); 29 | if(ImPyramid!=NULL) 30 | delete []ImPyramid; 31 | ImPyramid=new DImage[nLevels]; 32 | ImPyramid[0].copyData(image); 33 | double baseSigma=(1/ratio-1); 34 | for(int i=1;i0.98 || ratio<0.4) 51 | ratio=0.75; 52 | // first decide how many levels 53 | nLevels=log((double)minWidth/image.width())/log(ratio); 54 | if(ImPyramid!=NULL) 55 | delete []ImPyramid; 56 | ImPyramid=new DImage[nLevels]; 57 | ImPyramid[0].copyData(image); 58 | double baseSigma=(1/ratio-1); 59 | int n=log(0.25)/log(ratio); 60 | double nSigma=baseSigma*n; 61 | for(int i=1;i0.98 || ratio<0.4) 83 | ratio=0.75; 84 | nLevels = _nLevels; 85 | if(ImPyramid!=NULL) 86 | delete []ImPyramid; 87 | ImPyramid=new DImage[nLevels]; 88 | ImPyramid[0].copyData(image); 89 | double baseSigma=(1/ratio-1); 90 | int n=log(0.25)/log(ratio); 91 | double nSigma=baseSigma*n; 92 | for(int i=1;i 21 | static bool loadImage(const char* filename,T*& pImagePlane,int& width,int& height, int& nchannels); 22 | template 23 | static bool saveImage(const char* filename,const T* pImagePlane,int width,int height, int nchannels,ImageType imtype = standard); 24 | 25 | }; 26 | 27 | // template 28 | // bool ImageIO::loadImage(const char *filename, T *&pImagePlane, int &width, int &height, int &nchannels) 29 | // { 30 | // cv::Mat im = cv::imread(filename); 31 | // if(im.data == NULL) // if allocation fails 32 | // return false; 33 | // if(im.type()!= CV_8UC1 && im.type()!=CV_8UC3 && im.type()!=CV_8UC4) // we only support three types of image information for now 34 | // return false; 35 | // width = im.size().width; 36 | // height = im.size().height; 37 | // nchannels = im.channels(); 38 | // pImagePlane = new T[width*height*nchannels]; 39 | // 40 | // if(typeid(T) == typeid(unsigned char)) 41 | // { 42 | // for(int i = 0;i 68 | // bool ImageIO::saveImage(const char* filename,const T* pImagePlane,int width,int height, int nchannels,ImageType imtype) 69 | // { 70 | // cv::Mat im; 71 | // switch(nchannels){ 72 | // case 1: 73 | // im.create(height,width,CV_8UC1); 74 | // break; 75 | // case 3: 76 | // im.create(height,width,CV_8UC3); 77 | // break; 78 | // default: 79 | // return -1; 80 | // } 81 | // // check whether the type is float point 82 | // bool IsFloat=false; 83 | // if(typeid(T)==typeid(double) || typeid(T)==typeid(float) || typeid(T)==typeid(long double)) 84 | // IsFloat=true; 85 | // 86 | // T Max,Min; 87 | // int nElements = width*height*nchannels; 88 | // switch(imtype){ 89 | // case standard: 90 | // break; 91 | // case derivative: 92 | // // find the max of the absolute value 93 | // Max = pImagePlane[0]; 94 | // if(!IsFloat) 95 | // for(int i = 0;i 152 | #include 153 | #include 154 | #include "math.h" 155 | //----------------------------------------------------------------------------------------- 156 | // this class is a wrapper to use QImage to load image into image planes 157 | //----------------------------------------------------------------------------------------- 158 | 159 | class ImageIO 160 | { 161 | public: 162 | enum ImageType{standard, derivative, normalized}; 163 | ImageIO(void); 164 | ~ImageIO(void); 165 | public: 166 | template 167 | static void loadImage(const QImage& image,T*& pImagePlane,int& width,int& height,int& nchannels); 168 | template 169 | static bool loadImage(const QString& filename,T*& pImagePlane,int& width,int& height,int& nchannels); 170 | 171 | template 172 | static unsigned char convertPixel(const T& value,bool IsFloat,ImageType type,T& _Max,T& _Min); 173 | 174 | template 175 | static bool writeImage(const QString& filename, const T*& pImagePlane,int width,int height,int nchannels,ImageType type=standard,int quality=-1); 176 | 177 | template 178 | static bool writeImage(const QString& filename,const T* pImagePlane,int width,int height,int nchannels,T min, T max,int quality=-1); 179 | 180 | }; 181 | 182 | template 183 | void ImageIO::loadImage(const QImage& image, T*& pImagePlane,int& width,int& height,int& nchannels) 184 | { 185 | // get the image information 186 | width=image.width(); 187 | height=image.height(); 188 | nchannels=3; 189 | pImagePlane=new T[width*height*nchannels]; 190 | 191 | // check whether the type is float point 192 | bool IsFloat=false; 193 | if(typeid(T)==typeid(double) || typeid(T)==typeid(float) || typeid(T)==typeid(long double)) 194 | IsFloat=true; 195 | 196 | const unsigned char* plinebuffer; 197 | for(int i=0;i 219 | bool ImageIO::loadImage(const QString&filename, T*& pImagePlane,int& width,int& height,int& nchannels) 220 | { 221 | QImage image; 222 | if(image.load(filename)==false) 223 | return false; 224 | if(image.format()!=QImage::Format_RGB32) 225 | { 226 | QImage temp=image.convertToFormat(QImage::Format_RGB32); 227 | image=temp; 228 | } 229 | loadImage(image,pImagePlane,width,height,nchannels); 230 | return true; 231 | } 232 | 233 | template 234 | bool ImageIO::writeImage(const QString& filename, const T*& pImagePlane,int width,int height,int nchannels,ImageType type,int quality) 235 | { 236 | int nPixels=width*height,nElements; 237 | nElements=nPixels*nchannels; 238 | unsigned char* pTempBuffer; 239 | pTempBuffer=new unsigned char[nPixels*4]; 240 | memset(pTempBuffer,0,nPixels*4); 241 | 242 | // check whether the type is float point 243 | bool IsFloat=false; 244 | if(typeid(T)==typeid(double) || typeid(T)==typeid(float) || typeid(T)==typeid(long double)) 245 | IsFloat=true; 246 | 247 | T _Max=0,_Min=0; 248 | switch(type){ 249 | case standard: 250 | break; 251 | case derivative: 252 | _Max=0; 253 | for(int i=0;i=3) 274 | { 275 | pTempBuffer[i*4]=convertPixel(pImagePlane[i*nchannels],IsFloat,type,_Max,_Min); 276 | pTempBuffer[i*4+1]=convertPixel(pImagePlane[i*nchannels+1],IsFloat,type,_Max,_Min); 277 | pTempBuffer[i*4+2]=convertPixel(pImagePlane[i*nchannels+2],IsFloat,type,_Max,_Min); 278 | } 279 | else 280 | for (int j=0;j<3;j++) 281 | pTempBuffer[i*4+j]=convertPixel(pImagePlane[i*nchannels],IsFloat,type,_Max,_Min); 282 | pTempBuffer[i*4+3]=255; 283 | } 284 | QImage *pQImage=new QImage(pTempBuffer,width,height,QImage::Format_RGB32); 285 | bool result= pQImage->save(filename,0,quality); 286 | delete pQImage; 287 | delete pTempBuffer; 288 | return result; 289 | } 290 | 291 | template 292 | bool ImageIO::writeImage(const QString& filename, const T* pImagePlane,int width,int height,int nchannels,T min,T max,int quality) 293 | { 294 | int nPixels=width*height,nElements; 295 | nElements=nPixels*nchannels; 296 | unsigned char* pTempBuffer; 297 | pTempBuffer=new unsigned char[nPixels*4]; 298 | memset(pTempBuffer,0,nPixels*4); 299 | 300 | // check whether the type is float point 301 | bool IsFloat=false; 302 | if(typeid(T)==typeid(double) || typeid(T)==typeid(float) || typeid(T)==typeid(long double)) 303 | IsFloat=true; 304 | 305 | T _Max=max,_Min=min; 306 | 307 | for(int i=0;i=3) 310 | { 311 | pTempBuffer[i*4]=convertPixel(pImagePlane[i*nchannels],IsFloat,normalized,_Max,_Min); 312 | pTempBuffer[i*4+1]=convertPixel(pImagePlane[i*nchannels+1],IsFloat,normalized,_Max,_Min); 313 | pTempBuffer[i*4+2]=convertPixel(pImagePlane[i*nchannels+2],IsFloat,normalized,_Max,_Min); 314 | } 315 | else 316 | for (int j=0;j<3;j++) 317 | pTempBuffer[i*4+j]=convertPixel(pImagePlane[i*nchannels],IsFloat,normalized,_Max,_Min); 318 | pTempBuffer[i*4+3]=255; 319 | } 320 | QImage *pQImage=new QImage(pTempBuffer,width,height,QImage::Format_RGB32); 321 | bool result= pQImage->save(filename,0,quality); 322 | delete pQImage; 323 | delete pTempBuffer; 324 | return result; 325 | } 326 | 327 | template 328 | unsigned char ImageIO::convertPixel(const T& value,bool IsFloat,ImageType type,T& _Max,T& _Min) 329 | { 330 | switch(type){ 331 | case standard: 332 | if(IsFloat) 333 | return __max(__min(value*255,255),0); 334 | else 335 | return __max(__min(value,255),0); 336 | break; 337 | case derivative: 338 | return (double)((double)value/_Max+1)/2*255; 339 | break; 340 | case normalized: 341 | return (double)(value-_Min)/(_Max-_Min)*255; 342 | break; 343 | } 344 | return 0; 345 | } 346 | //*/ 347 | #endif 348 | -------------------------------------------------------------------------------- /src/ImageProcessing.h: -------------------------------------------------------------------------------- 1 | // Author: Ce Liu (c) Dec, 2009; celiu@mit.edu 2 | // Modified By: Deepak Pathak (c) 2016; pathak@berkeley.edu 3 | 4 | #ifndef _ImageProcessing_h 5 | #define _ImageProcessing_h 6 | 7 | #include "math.h" 8 | #include "stdio.h" 9 | #include "stdlib.h" 10 | #include 11 | 12 | //---------------------------------------------------------------------------------- 13 | // class to handle basic image processing functions 14 | // this is a collection of template functions. These template functions are 15 | // used in other image classes such as BiImage, IntImage and FImage 16 | //---------------------------------------------------------------------------------- 17 | 18 | class ImageProcessing 19 | { 20 | public: 21 | ImageProcessing(void); 22 | ~ImageProcessing(void); 23 | public: 24 | // basic functions 25 | template 26 | static inline T EnforceRange(const T& x,const int& MaxValue) {return __min(__max(x,0),MaxValue-1);}; 27 | 28 | //--------------------------------------------------------------------------------- 29 | // function to interpolate the image plane 30 | //--------------------------------------------------------------------------------- 31 | template 32 | static inline void BilinearInterpolate(const T1* pImage,int width,int height,int nChannels,double x,double y,T2* result); 33 | 34 | template 35 | static inline T1 BilinearInterpolate(const T1* pImage,int width,int height,double x,double y); 36 | 37 | // the transpose of bilinear interpolation 38 | template 39 | static inline void BilinearInterpolate_transpose(const T1* pImage,int width,int height,int nChannels,double x,double y,T2* result); 40 | 41 | template 42 | static inline T1 BilinearInterpolate_transpose(const T1* pImage,int width,int height,double x,double y); 43 | 44 | template 45 | static void ResizeImage(const T1* pSrcImage,T2* pDstImage,int SrcWidth,int SrcHeight,int nChannels,double Ratio); 46 | 47 | template 48 | static void ResizeImage(const T1* pSrcImage,T2* pDstImage,int SrcWidth,int SrcHeight,int nChannels,int DstWidth,int DstHeight); 49 | 50 | //--------------------------------------------------------------------------------- 51 | // functions for 1D filtering 52 | //--------------------------------------------------------------------------------- 53 | template 54 | static void hfiltering(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize); 55 | 56 | template 57 | static void vfiltering(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize); 58 | 59 | template 60 | static void hfiltering_transpose(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize); 61 | 62 | template 63 | static void vfiltering_transpose(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize); 64 | 65 | //--------------------------------------------------------------------------------- 66 | // functions for 2D filtering 67 | //--------------------------------------------------------------------------------- 68 | template 69 | static void filtering(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter2D,int fsize); 70 | 71 | template 72 | static void filtering_transpose(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter2D,int fsize); 73 | 74 | template 75 | static void Laplacian(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels); 76 | 77 | //--------------------------------------------------------------------------------- 78 | // functions for sample a patch from the image 79 | //--------------------------------------------------------------------------------- 80 | template 81 | static void getPatch(const T1* pSrcImgae,T2* pPatch,int width,int height,int nChannels,double x,double y,int wsize); 82 | 83 | //--------------------------------------------------------------------------------- 84 | // function to warp image 85 | //--------------------------------------------------------------------------------- 86 | template 87 | static void warpImage(T1* pWarpIm2,const T1* pIm1,const T1* pIm2,const T2* pVx,const T2* pVy,int width,int height,int nChannels); 88 | 89 | template 90 | static void warpImageFlow(T1* pWarpIm2,const T1* pIm1,const T1* pIm2,const T2* pFlow,int width,int height,int nChannels); 91 | 92 | template 93 | static void warpImage(T1* pWarpIm2,const T1* pIm2,const T2* pVx,const T2* pVy,int width,int height,int nChannels); 94 | 95 | template 96 | static void warpImage_transpose(T1* pWarpIm2,const T1* pIm2,const T2* pVx,const T2* pVy,int width,int height,int nChannels); 97 | 98 | template 99 | static void warpImage(T1* pWarpIm2,const T1* pIm2,const T2*flow,int width,int height,int nChannels); 100 | 101 | template 102 | static void warpImage_transpose(T1* pWarpIm2,const T1* pIm2,const T2* flow,int width,int height,int nChannels); 103 | 104 | template 105 | static void warpImage(T1 *pWarpIm2, T3* pMask,const T1 *pIm1, const T1 *pIm2, const T2 *pVx, const T2 *pVy, int width, int height, int nChannels); 106 | 107 | 108 | //--------------------------------------------------------------------------------- 109 | // function to crop an image 110 | //--------------------------------------------------------------------------------- 111 | template 112 | static void cropImage(const T1* pSrcImage,int SrcWidth,int SrcHeight,int nChannels,T2* pDstImage,int Left,int Top,int DstWidth,int DstHeight); 113 | //--------------------------------------------------------------------------------- 114 | 115 | //--------------------------------------------------------------------------------- 116 | // function to generate a 2D Gaussian 117 | //--------------------------------------------------------------------------------- 118 | template 119 | static void generate2DGaussian(T*& pImage,int wsize,double sigma=-1); 120 | 121 | template 122 | static void generate1DGaussian(T*& pImage,int wsize,double sigma=-1); 123 | 124 | }; 125 | 126 | //-------------------------------------------------------------------------------------------------- 127 | // function to interplate multi-channel image plane for (x,y) 128 | // -------------------------------------------------------------------------------------------------- 129 | template 130 | inline void ImageProcessing::BilinearInterpolate(const T1* pImage,int width,int height,int nChannels,double x,double y,T2* result) 131 | { 132 | int xx,yy,m,n,u,v,l,offset; 133 | xx=x; 134 | yy=y; 135 | double dx,dy,s; 136 | dx=__max(__min(x-xx,1),0); 137 | dy=__max(__min(y-yy,1),0); 138 | 139 | for(m=0;m<=1;m++) 140 | for(n=0;n<=1;n++) 141 | { 142 | u=EnforceRange(xx+m,width); 143 | v=EnforceRange(yy+n,height); 144 | offset=(v*width+u)*nChannels; 145 | s=fabs(1-m-dx)*fabs(1-n-dy); 146 | for(l=0;l 152 | inline T1 ImageProcessing::BilinearInterpolate(const T1* pImage,int width,int height,double x,double y) 153 | { 154 | int xx,yy,m,n,u,v,l,offset; 155 | xx=x; 156 | yy=y; 157 | double dx,dy,s; 158 | dx=__max(__min(x-xx,1),0); 159 | dy=__max(__min(y-yy,1),0); 160 | 161 | T1 result=0; 162 | for(m=0;m<=1;m++) 163 | for(n=0;n<=1;n++) 164 | { 165 | u=EnforceRange(xx+m,width); 166 | v=EnforceRange(yy+n,height); 167 | offset=v*width+u; 168 | s=fabs(1-m-dx)*fabs(1-n-dy); 169 | result+=pImage[offset]*s; 170 | } 171 | return result; 172 | } 173 | 174 | 175 | //-------------------------------------------------------------------------------------------------- 176 | // function to interplate multi-channel image plane for (x,y) 177 | // -------------------------------------------------------------------------------------------------- 178 | template 179 | inline void ImageProcessing::BilinearInterpolate_transpose(const T1* pInput,int width,int height,int nChannels,double x,double y,T2* pDstImage) 180 | { 181 | int xx,yy,m,n,u,v,l,offset; 182 | xx=x; 183 | yy=y; 184 | double dx,dy,s; 185 | dx=__max(__min(x-xx,1),0); 186 | dy=__max(__min(y-yy,1),0); 187 | 188 | for(m=0;m<=1;m++) 189 | for(n=0;n<=1;n++) 190 | { 191 | u=EnforceRange(xx+m,width); 192 | v=EnforceRange(yy+n,height); 193 | offset=(v*width+u)*nChannels; 194 | s=fabs(1-m-dx)*fabs(1-n-dy); 195 | for(l=0;l 205 | void ImageProcessing::ResizeImage(const T1* pSrcImage,T2* pDstImage,int SrcWidth,int SrcHeight,int nChannels,double Ratio) 206 | { 207 | int DstWidth,DstHeight; 208 | DstWidth=(double)SrcWidth*Ratio; 209 | DstHeight=(double)SrcHeight*Ratio; 210 | memset(pDstImage,0,sizeof(T2)*DstWidth*DstHeight*nChannels); 211 | 212 | double x,y; 213 | 214 | for(int i=0;i 226 | void ImageProcessing::ResizeImage(const T1 *pSrcImage, T2 *pDstImage, int SrcWidth, int SrcHeight, int nChannels, int DstWidth, int DstHeight) 227 | { 228 | double xRatio=(double)DstWidth/SrcWidth; 229 | double yRatio=(double)DstHeight/SrcHeight; 230 | memset(pDstImage,sizeof(T2)*DstWidth*DstHeight*nChannels,0); 231 | 232 | double x,y; 233 | 234 | for(int i=0;i 249 | void ImageProcessing::hfiltering(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize) 250 | { 251 | memset(pDstImage,0,sizeof(T2)*width*height*nChannels); 252 | T2* pBuffer; 253 | double w; 254 | int i,j,l,k,offset,jj; 255 | for(i=0;i 274 | void ImageProcessing::hfiltering_transpose(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize) 275 | { 276 | memset(pDstImage,0,sizeof(T2)*width*height*nChannels); 277 | const T1* pBuffer; 278 | double w; 279 | int i,j,l,k,offset,jj; 280 | for(i=0;i 299 | void ImageProcessing::Laplacian(const T1 *pSrcImage, T2 *pDstImage, int width, int height, int nChannels) 300 | { 301 | int LineWidth=width*nChannels; 302 | int nElements=width*height*nChannels; 303 | // first treat the corners 304 | for(int k=0;k 339 | void ImageProcessing::vfiltering(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize) 340 | { 341 | memset(pDstImage,0,sizeof(T2)*width*height*nChannels); 342 | T2* pBuffer; 343 | double w; 344 | int i,j,l,k,offset,ii; 345 | for(i=0;i 363 | void ImageProcessing::vfiltering_transpose(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter1D,int fsize) 364 | { 365 | memset(pDstImage,0,sizeof(T2)*width*height*nChannels); 366 | const T1* pBuffer; 367 | double w; 368 | int i,j,l,k,offset,ii; 369 | for(i=0;i 390 | void ImageProcessing::filtering(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter2D,int fsize) 391 | { 392 | double w; 393 | int i,j,u,v,k,ii,jj,wsize,offset; 394 | wsize=fsize*2+1; 395 | double* pBuffer=new double[nChannels]; 396 | for(i=0;i 422 | void ImageProcessing::filtering_transpose(const T1* pSrcImage,T2* pDstImage,int width,int height,int nChannels,const double* pfilter2D,int fsize) 423 | { 424 | double w; 425 | int i,j,u,v,k,ii,jj,wsize,offset; 426 | wsize=fsize*2+1; 427 | memset(pDstImage,0,sizeof(T2)*width*height*nChannels); 428 | for(i=0;i 450 | void ImageProcessing::getPatch(const T1* pSrcImage,T2* pPatch,int width,int height,int nChannels,double x0,double y0,int wsize) 451 | { 452 | // suppose pPatch has been allocated and cleared before calling the function 453 | int wlength=wsize*2+1; 454 | double x,y; 455 | for(int i=-wsize;i<=wsize;i++) 456 | for(int j=-wsize;j<=wsize;j++) 457 | { 458 | y=y0+i; 459 | x=x0+j; 460 | if(x<0 || x>width-1 || y<0 || y>height-1) 461 | continue; 462 | BilinearInterpolate(pSrcImage,width,height,nChannels,x,y,pPatch+((i+wsize)*wlength+j+wsize)*nChannels); 463 | } 464 | } 465 | 466 | //------------------------------------------------------------------------------------------------------------ 467 | // function to warp an image with respect to flow field 468 | // pWarpIm2 has to be allocated before hands 469 | //------------------------------------------------------------------------------------------------------------ 470 | template 471 | void ImageProcessing::warpImage(T1 *pWarpIm2, const T1 *pIm1, const T1 *pIm2, const T2 *pVx, const T2 *pVy, int width, int height, int nChannels) 472 | { 473 | memset(pWarpIm2,0,sizeof(T1)*width*height*nChannels); 474 | for(int i=0;iwidth-1 || y<0 || y>height-1) 483 | { 484 | for(int k=0;k 493 | void ImageProcessing::warpImageFlow(T1 *pWarpIm2, const T1 *pIm1, const T1 *pIm2, const T2 *pFlow, int width, int height, int nChannels) 494 | { 495 | memset(pWarpIm2,0,sizeof(T1)*width*height*nChannels); 496 | for(int i=0;iwidth-1 || y<0 || y>height-1) 505 | { 506 | for(int k=0;k 515 | void ImageProcessing::warpImage(T1 *pWarpIm2,const T1 *pIm2, const T2 *pVx, const T2 *pVy, int width, int height, int nChannels) 516 | { 517 | memset(pWarpIm2,0,sizeof(T1)*width*height*nChannels); 518 | for(int i=0;iwidth-1 || y<0 || y>height-1) 527 | continue; 528 | BilinearInterpolate(pIm2,width,height,nChannels,x,y,pWarpIm2+offset); 529 | } 530 | } 531 | 532 | template 533 | void ImageProcessing::warpImage_transpose(T1 *pWarpIm2,const T1 *pIm2, const T2 *pVx, const T2 *pVy, int width, int height, int nChannels) 534 | { 535 | memset(pWarpIm2,0,sizeof(T1)*width*height*nChannels); 536 | for(int i=0;iwidth-1 || y<0 || y>height-1) 545 | continue; 546 | //BilinearInterpolate(pIm2,width,height,nChannels,x,y,pWarpIm2+offset); 547 | BilinearInterpolate_transpose(pIm2+offset,width,height,nChannels,x,y,pWarpIm2); 548 | } 549 | } 550 | 551 | ////////////////////////////////////////////////////////////////////////////////////// 552 | // different format 553 | ////////////////////////////////////////////////////////////////////////////////////// 554 | template 555 | void ImageProcessing::warpImage(T1 *pWarpIm2,const T1 *pIm2, const T2 *flow, int width, int height, int nChannels) 556 | { 557 | memset(pWarpIm2,0,sizeof(T1)*width*height*nChannels); 558 | for(int i=0;iwidth-1 || y<0 || y>height-1) 567 | continue; 568 | BilinearInterpolate(pIm2,width,height,nChannels,x,y,pWarpIm2+offset); 569 | } 570 | } 571 | 572 | template 573 | void ImageProcessing::warpImage_transpose(T1 *pWarpIm2,const T1 *pIm2, const T2 *flow, int width, int height, int nChannels) 574 | { 575 | memset(pWarpIm2,0,sizeof(T1)*width*height*nChannels); 576 | for(int i=0;iwidth-1 || y<0 || y>height-1) 585 | continue; 586 | //BilinearInterpolate(pIm2,width,height,nChannels,x,y,pWarpIm2+offset); 587 | BilinearInterpolate_transpose(pIm2+offset,width,height,nChannels,x,y,pWarpIm2); 588 | } 589 | } 590 | 591 | 592 | template 593 | void ImageProcessing::warpImage(T1 *pWarpIm2, T3* pMask,const T1 *pIm1, const T1 *pIm2, const T2 *pVx, const T2 *pVy, int width, int height, int nChannels) 594 | { 595 | memset(pWarpIm2,0,sizeof(T1)*width*height*nChannels); 596 | for(int i=0;iwidth-1 || y<0 || y>height-1) 605 | { 606 | for(int k=0;k 623 | void ImageProcessing::cropImage(const T1 *pSrcImage, int SrcWidth, int SrcHeight, int nChannels, T2 *pDstImage, int Left, int Top, int DstWidth, int DstHeight) 624 | { 625 | if(typeid(T1)==typeid(T2)) 626 | { 627 | for(int i=0;i 647 | void ImageProcessing::generate2DGaussian(T*& pImage, int wsize, double sigma) 648 | { 649 | if(sigma==-1) 650 | sigma=wsize/2; 651 | double alpha=1/(2*sigma*sigma); 652 | int winlength=wsize*2+1; 653 | if(pImage==NULL) 654 | pImage=new T[winlength*winlength]; 655 | double total = 0; 656 | for(int i=-wsize;i<=wsize;i++) 657 | for(int j=-wsize;j<=wsize;j++) 658 | { 659 | pImage[(i+wsize)*winlength+j+wsize]=exp(-(double)(i*i+j*j)*alpha); 660 | total += pImage[(i+wsize)*winlength+j+wsize]; 661 | } 662 | for(int i = 0;i 671 | void ImageProcessing::generate1DGaussian(T*& pImage, int wsize, double sigma) 672 | { 673 | if(sigma==-1) 674 | sigma=wsize/2; 675 | double alpha=1/(2*sigma*sigma); 676 | int winlength=wsize*2+1; 677 | if(pImage==NULL) 678 | pImage=new T[winlength]; 679 | double total = 0; 680 | for(int i=-wsize;i<=wsize;i++) 681 | { 682 | pImage[i+wsize]=exp(-(double)(i*i)*alpha); 683 | total += pImage[i+wsize]; 684 | } 685 | for(int i = 0;i 11 | #endif 12 | #include 13 | 14 | using namespace std; 15 | 16 | template 17 | class Matrix 18 | { 19 | private: 20 | int nRow,nCol; 21 | double* pData; 22 | static bool IsDispInfo; 23 | public: 24 | Matrix(void); 25 | Matrix(int _nrow,int _ncol,double* data=NULL); 26 | Matrix(const Matrix& matrix); 27 | ~Matrix(void); 28 | void releaseData(); 29 | void copyData(const Matrix& matrix); 30 | void allocate(const Matrix& matrix); 31 | void allocate(int _nrow,int _ncol); 32 | void reset(); 33 | bool dimMatch(const Matrix& matrix) const; 34 | bool dimcheck(const Matrix& matrix) const; 35 | void loadData(int _nrow,int _ncol,T* data); 36 | static void enableDispInfo(bool dispInfo=false){IsDispInfo=dispInfo;}; 37 | // display the matrix 38 | void printMatrix(); 39 | void identity(int ndim); 40 | 41 | // function to access the member variables 42 | inline int nrow() const{return nRow;}; 43 | inline int ncol() const{return nCol;}; 44 | inline double* data() {return pData;}; 45 | inline const double* data() const {return (const double*)pData;}; 46 | inline double operator [](int index) const{return pData[index];}; 47 | inline double& operator[](int index) {return pData[index];}; 48 | inline double data(int row,int col)const {return pData[row*nCol+col];}; 49 | inline double& data(int row,int col) {return pData[row*nCol+col];}; 50 | bool matchDimension(int _nrow,int _ncol) const {if(nRow==_nrow && nCol==_ncol) return true; else return false;}; 51 | bool matchDimension(const Matrix& matrix) const {return matchDimension(matrix.nrow(),matrix.ncol());}; 52 | 53 | // functions to check dimensions 54 | bool checkDimRight(const Vector& vector) const; 55 | bool checkDimRight(const Matrix& matrix) const; 56 | bool checkDimLeft(const Vector& vector) const; 57 | bool checkDimLeft(const Matrix& matrix) const; 58 | 59 | // functions for matrix computation 60 | void Multiply(Vector& result,const Vector& vect) const; 61 | void Multiply(Matrix& result,const Matrix& matrix) const; 62 | 63 | void transpose(Matrix& result) const; 64 | void fromVector(const Vector& vect); 65 | double norm2() const; 66 | double sum() const 67 | { 68 | double total = 0; 69 | for(int i = 0;i& matrix); 75 | 76 | Matrix& operator+=(double val); 77 | Matrix& operator-=(double val); 78 | Matrix& operator*=(double val); 79 | Matrix& operator/=(double val); 80 | 81 | Matrix& operator+=(const Matrix& matrix); 82 | Matrix& operator-=(const Matrix& matrix); 83 | Matrix& operator*=(const Matrix& matrix); 84 | Matrix& operator/=(const Matrix& matrix); 85 | 86 | friend Vector operator*(const Matrix& matrix,const Vector& vect); 87 | friend Matrix operator*(const Matrix& matrix1,const Matrix& matrix2); 88 | 89 | 90 | // solve linear systems 91 | void SolveLinearSystem(Vector& result,const Vector& b) const; 92 | void ConjugateGradient(Vector& result,const Vector& b) const; 93 | 94 | #ifdef _QT 95 | bool writeMatrix(QFile& file) const; 96 | bool readMatrix(QFile& file); 97 | #endif 98 | #ifdef _MATLAB 99 | void readMatrix(const mxArray* prhs); 100 | void writeMatrix(mxArray*& prhs) const; 101 | #endif 102 | }; 103 | 104 | template 105 | bool Matrix::IsDispInfo=false; 106 | 107 | template 108 | Matrix::Matrix(void) 109 | { 110 | nRow=nCol=0; 111 | pData=NULL; 112 | } 113 | 114 | template 115 | Matrix::Matrix(int nrow,int ncol,double* data) 116 | { 117 | nRow=nrow; 118 | nCol=ncol; 119 | pData=new T[nRow*nCol]; 120 | if(data==NULL) 121 | memset(pData,0,sizeof(T)*nRow*nCol); 122 | else 123 | memcpy(pData,data,sizeof(T)*nRow*nCol); 124 | } 125 | 126 | template 127 | Matrix::Matrix(const Matrix& matrix) 128 | { 129 | nRow=nCol=0; 130 | pData=NULL; 131 | copyData(matrix); 132 | } 133 | 134 | template 135 | Matrix::~Matrix(void) 136 | { 137 | releaseData(); 138 | } 139 | 140 | template 141 | void Matrix::releaseData() 142 | { 143 | if(pData!=NULL) 144 | delete pData; 145 | pData=NULL; 146 | nRow=nCol=0; 147 | } 148 | 149 | template 150 | void Matrix::copyData(const Matrix &matrix) 151 | { 152 | if(!dimMatch(matrix)) 153 | allocate(matrix); 154 | memcpy(pData,matrix.pData,sizeof(T)*nRow*nCol); 155 | } 156 | 157 | template 158 | bool Matrix::dimMatch(const Matrix& matrix) const 159 | { 160 | if(nCol==matrix.nCol && nRow==matrix.nRow) 161 | return true; 162 | else 163 | return false; 164 | } 165 | 166 | template 167 | bool Matrix::dimcheck(const Matrix& matrix) const 168 | { 169 | if(!dimMatch(matrix)) 170 | { 171 | cout<<"The dimensions of the matrices don't match!"< 178 | void Matrix::reset() 179 | { 180 | if(pData!=NULL) 181 | memset(pData,0,sizeof(T)*nRow*nCol); 182 | } 183 | 184 | template 185 | void Matrix::allocate(int nrow,int ncol) 186 | { 187 | releaseData(); 188 | nRow=nrow; 189 | nCol=ncol; 190 | if(nRow*nCol>0) 191 | { 192 | pData=new T[nRow*nCol]; 193 | memset(pData,0,sizeof(T)*nRow*nCol); 194 | } 195 | } 196 | 197 | template 198 | void Matrix::allocate(const Matrix& matrix) 199 | { 200 | allocate(matrix.nRow,matrix.nCol); 201 | } 202 | 203 | template 204 | void Matrix::loadData(int _nrow, int _ncol, T *data) 205 | { 206 | if(!matchDimension(_nrow,_ncol)) 207 | allocate(_nrow,_ncol); 208 | memcpy(pData,data,sizeof(T)*nRow*nCol); 209 | } 210 | 211 | template 212 | void Matrix::printMatrix() 213 | { 214 | for(int i=0;i 223 | void Matrix::identity(int ndim) 224 | { 225 | allocate(ndim,ndim); 226 | reset(); 227 | for(int i=0;i 235 | bool Matrix::checkDimRight(const Vector& vect) const 236 | { 237 | if(nCol==vect.dim()) 238 | return true; 239 | else 240 | { 241 | cout<<"The matrix and vector don't match in multiplication!"< 247 | bool Matrix::checkDimRight(const Matrix &matrix) const 248 | { 249 | if(nCol==matrix.nrow()) 250 | return true; 251 | else 252 | { 253 | cout<<"The matrix and matrix don't match in multiplication!"< 259 | bool Matrix::checkDimLeft(const Vector& vect) const 260 | { 261 | if(nRow==vect.dim()) 262 | return true; 263 | else 264 | { 265 | cout<<"The vector and matrix don't match in multiplication!"< 271 | bool Matrix::checkDimLeft(const Matrix &matrix) const 272 | { 273 | if(nRow==matrix.ncol()) 274 | return true; 275 | else 276 | { 277 | cout<<"The matrix and matrix don't match in multiplication!"< 286 | void Matrix::Multiply(Vector &result, const Vector&vect) const 287 | { 288 | checkDimRight(vect); 289 | if(result.dim()!=nRow) 290 | result.allocate(nRow); 291 | for(int i=0;i 301 | void Matrix::Multiply(Matrix &result, const Matrix &matrix) const 302 | { 303 | checkDimRight(matrix); 304 | if(!result.matchDimension(nRow,matrix.nCol)) 305 | result.allocate(nRow,matrix.nCol); 306 | for(int i=0;i 317 | void Matrix::transpose(Matrix &result) const 318 | { 319 | if(!result.matchDimension(nCol,nRow)) 320 | result.allocate(nCol,nRow); 321 | for(int i=0;i 327 | void Matrix::fromVector(const Vector&vect) 328 | { 329 | if(!matchDimension(vect.dim(),1)) 330 | allocate(vect.dim(),1); 331 | memcpy(pData,vect.data(),sizeof(double)*vect.dim()); 332 | } 333 | 334 | template 335 | double Matrix::norm2() const 336 | { 337 | if(pData==NULL) 338 | return 0; 339 | double temp=0; 340 | for(int i=0;i 349 | Matrix& Matrix::operator=(const Matrix& matrix) 350 | { 351 | copyData(matrix); 352 | return *this; 353 | } 354 | 355 | template 356 | Matrix& Matrix::operator +=(double val) 357 | { 358 | for(int i=0;i 364 | Matrix& Matrix::operator -=(double val) 365 | { 366 | for(int i=0;i 372 | Matrix& Matrix::operator *=(double val) 373 | { 374 | for(int i=0;i 380 | Matrix& Matrix::operator /=(double val) 381 | { 382 | for(int i=0;i 388 | Matrix& Matrix::operator +=(const Matrix &matrix) 389 | { 390 | dimcheck(matrix); 391 | for(int i=0;i 397 | Matrix& Matrix::operator -=(const Matrix &matrix) 398 | { 399 | dimcheck(matrix); 400 | for(int i=0;i 406 | Matrix& Matrix::operator *=(const Matrix &matrix) 407 | { 408 | dimcheck(matrix); 409 | for(int i=0;i 415 | Matrix& Matrix::operator /=(const Matrix &matrix) 416 | { 417 | dimcheck(matrix); 418 | for(int i=0;i 424 | Vector operator*(const Matrix& matrix,const Vector& vect) 425 | { 426 | Vector result; 427 | matrix.Multiply(result,vect); 428 | return result; 429 | } 430 | 431 | template 432 | Matrix operator*(const Matrix& matrix1,const Matrix& matrix2) 433 | { 434 | Matrix result; 435 | matrix1.Multiply(result,matrix2); 436 | return result; 437 | } 438 | 439 | //-------------------------------------------------------------------------------------------------- 440 | // function for conjugate gradient method 441 | //-------------------------------------------------------------------------------------------------- 442 | template 443 | void Matrix::ConjugateGradient(Vector &result, const Vector&b) const 444 | { 445 | if(nCol!=nRow) 446 | { 447 | cout<<"Error: when solving Ax=b, A is not square!"< r(b),p,q; 455 | result.reset(); 456 | 457 | int nIterations=nRow*5; 458 | Vector rou(nIterations); 459 | for(int k=0;k 482 | void Matrix::SolveLinearSystem(Vector &result, const Vector&b) const 483 | { 484 | if(nCol==nRow) 485 | { 486 | ConjugateGradient(result,b); 487 | return; 488 | } 489 | if(nRow AT,ATA; 495 | transpose(AT); 496 | AT.Multiply(ATA,*this); 497 | Vector ATb; 498 | AT.Multiply(ATb,b); 499 | ATA.ConjugateGradient(result,ATb); 500 | } 501 | 502 | #ifdef _QT 503 | 504 | template 505 | bool Matrix::writeMatrix(QFile &file) const 506 | { 507 | file.write((char *)&nRow,sizeof(int)); 508 | file.write((char *)&nCol,sizeof(int)); 509 | if(file.write((char *)pData,sizeof(double)*nRow*nCol)!=sizeof(double)*nRow*nCol) 510 | return false; 511 | return true; 512 | } 513 | 514 | template 515 | bool Matrix::readMatrix(QFile &file) 516 | { 517 | releaseData(); 518 | file.read((char *)&nRow,sizeof(int)); 519 | file.read((char *)&nCol,sizeof(int)); 520 | if(nRow*nCol>0) 521 | { 522 | allocate(nRow,nCol); 523 | if(file.read((char *)pData,sizeof(double)*nRow*nCol)!=sizeof(double)*nRow*nCol) 524 | return false; 525 | } 526 | return true; 527 | } 528 | #endif 529 | 530 | #ifdef _MATLAB 531 | 532 | template 533 | void Matrix::readMatrix(const mxArray* prhs) 534 | { 535 | if(pData!=NULL) 536 | delete pData; 537 | int nElements = mxGetNumberOfDimensions(prhs); 538 | if(nElements>2) 539 | mexErrMsgTxt("A matrix is expected to be loaded!"); 540 | const int* dims = mxGetDimensions(prhs); 541 | allocate(dims[0],dims[1]); 542 | double* data = (double*)mxGetData(prhs); 543 | for(int i =0; i 549 | void Matrix::writeMatrix(mxArray*& plhs) const 550 | { 551 | int dims[2]; 552 | dims[0]=nRow;dims[1]=nCol; 553 | plhs=mxCreateNumericArray(2, dims,mxDOUBLE_CLASS, mxREAL); 554 | double* data = (double *)mxGetData(plhs); 555 | for(int i =0; i 9 | 10 | #ifndef PI 11 | #define PI 3.1415926535897932384626433832 12 | #endif 13 | 14 | using namespace std; 15 | 16 | class GaussianMixture 17 | { 18 | public: 19 | int nChannels; 20 | double* alpha; 21 | double* sigma; 22 | double* beta; 23 | double* sigma_square; 24 | double* beta_square; 25 | public: 26 | GaussianMixture() 27 | { 28 | nChannels = 0; 29 | alpha = sigma = beta = sigma_square = beta_square = NULL; 30 | } 31 | GaussianMixture(int _nChannels) 32 | { 33 | nChannels = _nChannels; 34 | allocate(); 35 | for(int i = 0;i scale; 190 | //public: 191 | // Laplacian() 192 | // { 193 | // } 194 | // Laplacian(int _nChannels) 195 | // { 196 | // nChannels = _nChannels; 197 | // scale.allocate(nChannels); 198 | // } 199 | // Laplacian(const Laplacian 200 | // 201 | //}; 202 | -------------------------------------------------------------------------------- /src/OpticalFlow.cpp: -------------------------------------------------------------------------------- 1 | // Author: Ce Liu (c) Dec, 2009; celiu@mit.edu 2 | // Modified By: Deepak Pathak (c) 2016; pathak@berkeley.edu 3 | 4 | #include "OpticalFlow.h" 5 | #include "ImageProcessing.h" 6 | #include "GaussianPyramid.h" 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | #ifndef _MATLAB 13 | bool OpticalFlow::IsDisplay=true; 14 | #else 15 | bool OpticalFlow::IsDisplay=false; 16 | #endif 17 | 18 | //OpticalFlow::InterpolationMethod OpticalFlow::interpolation = OpticalFlow::Bicubic; 19 | OpticalFlow::InterpolationMethod OpticalFlow::interpolation = OpticalFlow::Bilinear; 20 | OpticalFlow::NoiseModel OpticalFlow::noiseModel = OpticalFlow::Lap; 21 | GaussianMixture OpticalFlow::GMPara; 22 | Vector OpticalFlow::LapPara; 23 | 24 | 25 | OpticalFlow::OpticalFlow(void) 26 | { 27 | } 28 | 29 | OpticalFlow::~OpticalFlow(void) 30 | { 31 | } 32 | 33 | //-------------------------------------------------------------------------------------------------------- 34 | // function to compute dx, dy and dt for motion estimation 35 | //-------------------------------------------------------------------------------------------------------- 36 | void OpticalFlow::getDxs(DImage &imdx, DImage &imdy, DImage &imdt, const DImage &im1, const DImage &im2) 37 | { 38 | //double gfilter[5]={0.01,0.09,0.8,0.09,0.01}; 39 | double gfilter[5]={0.02,0.11,0.74,0.11,0.02}; 40 | //double gfilter[5]={0,0,1,0,0}; 41 | if(1) 42 | { 43 | //DImage foo,Im; 44 | //Im.Add(im1,im2); 45 | //Im.Multiplywith(0.5); 46 | ////foo.imfilter_hv(Im,gfilter,2,gfilter,2); 47 | //Im.dx(imdx,true); 48 | //Im.dy(imdy,true); 49 | //imdt.Subtract(im2,im1); 50 | DImage Im1,Im2,Im; 51 | 52 | im1.imfilter_hv(Im1,gfilter,2,gfilter,2); 53 | im2.imfilter_hv(Im2,gfilter,2,gfilter,2); 54 | Im.copyData(Im1); 55 | Im.Multiplywith(0.4); 56 | Im.Add(Im2,0.6); 57 | //Im.Multiplywith(0.5); 58 | //Im1.copyData(im1); 59 | //Im2.copyData(im2); 60 | 61 | Im.dx(imdx,true); 62 | Im.dy(imdy,true); 63 | imdt.Subtract(Im2,Im1); 64 | } 65 | else 66 | { 67 | // Im1 and Im2 are the smoothed version of im1 and im2 68 | DImage Im1,Im2; 69 | 70 | im1.imfilter_hv(Im1,gfilter,2,gfilter,2); 71 | im2.imfilter_hv(Im2,gfilter,2,gfilter,2); 72 | 73 | //Im1.copyData(im1); 74 | //Im2.copyData(im2); 75 | 76 | Im2.dx(imdx,true); 77 | Im2.dy(imdy,true); 78 | imdt.Subtract(Im2,Im1); 79 | } 80 | 81 | 82 | imdx.setDerivative(); 83 | imdy.setDerivative(); 84 | imdt.setDerivative(); 85 | } 86 | 87 | //-------------------------------------------------------------------------------------------------------- 88 | // function to do sanity check: imdx*du+imdy*dy+imdt=0 89 | //-------------------------------------------------------------------------------------------------------- 90 | void OpticalFlow::SanityCheck(const DImage &imdx, const DImage &imdy, const DImage &imdt, double du, double dv) 91 | { 92 | if(imdx.matchDimension(imdy)==false || imdx.matchDimension(imdt)==false) 93 | { 94 | cout<<"The dimensions of the derivatives don't match!"<imWidth-1-interval || yimHeight-1-interval) 156 | continue; 157 | pMask[offset]=1; 158 | } 159 | } 160 | 161 | void OpticalFlow::genInImageMask(DImage &mask, const DImage &flow,int interval) 162 | { 163 | int imWidth,imHeight; 164 | imWidth=flow.width(); 165 | imHeight=flow.height(); 166 | if(mask.matchDimension(flow.width(),flow.height(),1)==false) 167 | mask.allocate(imWidth,imHeight); 168 | else 169 | mask.reset(); 170 | 171 | const _FlowPrecision *pFlow; 172 | _FlowPrecision *pMask; 173 | pFlow = flow.data();; 174 | pMask=mask.data(); 175 | double x,y; 176 | for(int i=0;iimWidth-1-interval || yimHeight-1-interval) 183 | continue; 184 | pMask[offset]=1; 185 | } 186 | } 187 | 188 | //-------------------------------------------------------------------------------------------------------- 189 | // function to compute optical flow field using two fixed point iterations 190 | // Input arguments: 191 | // Im1, Im2: frame 1 and frame 2 192 | // warpIm2: the warped frame 2 according to the current flow field u and v 193 | // u,v: the current flow field, NOTICE that they are also output arguments 194 | // 195 | //-------------------------------------------------------------------------------------------------------- 196 | void OpticalFlow::SmoothFlowSOR(const DImage &Im1, const DImage &Im2, DImage &warpIm2, DImage &u, DImage &v, 197 | double alpha, int nOuterFPIterations, int nInnerFPIterations, int nSORIterations) 198 | { 199 | DImage mask,imdx,imdy,imdt; 200 | int imWidth,imHeight,nChannels,nPixels; 201 | imWidth=Im1.width(); 202 | imHeight=Im1.height(); 203 | nChannels=Im1.nchannels(); 204 | nPixels=imWidth*imHeight; 205 | 206 | DImage du(imWidth,imHeight),dv(imWidth,imHeight); 207 | DImage uu(imWidth,imHeight),vv(imWidth,imHeight); 208 | DImage ux(imWidth,imHeight),uy(imWidth,imHeight); 209 | DImage vx(imWidth,imHeight),vy(imWidth,imHeight); 210 | DImage Phi_1st(imWidth,imHeight); 211 | DImage Psi_1st(imWidth,imHeight,nChannels); 212 | 213 | DImage imdxy,imdx2,imdy2,imdtdx,imdtdy; 214 | DImage ImDxy,ImDx2,ImDy2,ImDtDx,ImDtDy; 215 | DImage foo1,foo2; 216 | 217 | double prob1,prob2,prob11,prob22; 218 | 219 | double varepsilon_phi=pow(0.001,2); 220 | double varepsilon_psi=pow(0.001,2); 221 | 222 | //-------------------------------------------------------------------------- 223 | // the outer fixed point iteration 224 | //-------------------------------------------------------------------------- 225 | for(int count=0;count1) 350 | { 351 | ImDxy.collapse(imdxy); 352 | ImDx2.collapse(imdx2); 353 | ImDy2.collapse(imdy2); 354 | ImDtDx.collapse(imdtdx); 355 | ImDtDy.collapse(imdtdy); 356 | } 357 | else 358 | { 359 | imdxy.copyData(ImDxy); 360 | imdx2.copyData(ImDx2); 361 | imdy2.copyData(ImDy2); 362 | imdtdx.copyData(ImDtDx); 363 | imdtdy.copyData(ImDtDy); 364 | } 365 | // laplacian filtering of the current flow field 366 | Laplacian(foo1,u,Phi_1st); 367 | Laplacian(foo2,v,Phi_1st); 368 | 369 | for(int i=0;i0) 393 | { 394 | _weight = phiData[offset-1]; 395 | sigma1 += _weight*du.data()[offset-1]; 396 | sigma2 += _weight*dv.data()[offset-1]; 397 | coeff += _weight; 398 | } 399 | if(j0) 407 | { 408 | _weight = phiData[offset-imWidth]; 409 | sigma1 += _weight*du.data()[offset-imWidth]; 410 | sigma2 += _weight*dv.data()[offset-imWidth]; 411 | coeff += _weight; 412 | } 413 | if(i1) 627 | { 628 | ImDxy.collapse(imdxy); 629 | ImDx2.collapse(imdx2); 630 | ImDy2.collapse(imdy2); 631 | ImDtDx.collapse(imdtdx); 632 | ImDtDy.collapse(imdtdy); 633 | } 634 | else 635 | { 636 | imdxy.copyData(ImDxy); 637 | imdx2.copyData(ImDx2); 638 | imdy2.copyData(ImDy2); 639 | imdtdx.copyData(ImDtDx); 640 | imdtdy.copyData(ImDtDy); 641 | } 642 | 643 | // filtering 644 | //imdx2.smoothing(A11,3); 645 | //imdxy.smoothing(A12,3); 646 | //imdy2.smoothing(A22,3); 647 | A11.copyData(imdx2); 648 | A12.copyData(imdxy); 649 | A22.copyData(imdy2); 650 | 651 | // add epsilon to A11 and A22 652 | A11.Add(alpha*0.5); 653 | A22.Add(alpha*0.5); 654 | 655 | // form b 656 | //imdtdx.smoothing(b1,3); 657 | //imdtdy.smoothing(b2,3); 658 | b1.copyData(imdtdx); 659 | b2.copyData(imdtdy); 660 | 661 | // laplacian filtering of the current flow field 662 | Laplacian(foo1,u,Phi_1st); 663 | Laplacian(foo2,v,Phi_1st); 664 | _FlowPrecision *b1Data,*b2Data; 665 | const _FlowPrecision *foo1Data,*foo2Data; 666 | b1Data=b1.data(); 667 | b2Data=b2.data(); 668 | foo1Data=foo1.data(); 669 | foo2Data=foo2.data(); 670 | 671 | for(int i=0;i& para) 820 | { 821 | int nChannels = Im1.nchannels(); 822 | if(para.dim()!=nChannels) 823 | para.allocate(nChannels); 824 | else 825 | para.reset(); 826 | double temp; 827 | Vector total(nChannels); 828 | for(int k = 0;k0 && temp<1000000) 837 | { 838 | para[k] += temp; 839 | total[k]++; 840 | } 841 | } 842 | for(int k = 0;k0) 887 | outputData[offset]+=fooData[offset-1]; 888 | } 889 | foo.reset(); 890 | // vertical filtering 891 | for(int i=0;i0) 904 | outputData[offset]+=fooData[offset-width]; 905 | } 906 | } 907 | 908 | void OpticalFlow::testLaplacian(int dim) 909 | { 910 | // generate the random weight 911 | DImage weight(dim,dim); 912 | for(int i=0;i=0) 933 | printf(" "); 934 | printf(" %1.0f ",sysMatrix.data()[i*dim*dim+j]); 935 | } 936 | printf("\n"); 937 | } 938 | } 939 | 940 | //-------------------------------------------------------------------------------------- 941 | // function to perfomr coarse to fine optical flow estimation 942 | //-------------------------------------------------------------------------------------- 943 | void OpticalFlow::Coarse2FineFlow(DImage &vx, DImage &vy, DImage &warpI2,const DImage &Im1, const DImage &Im2, double alpha, double ratio, int minWidth, 944 | int nOuterFPIterations, int nInnerFPIterations, int nCGIterations) 945 | { 946 | // first build the pyramid of the two images 947 | GaussianPyramid GPyramid1; 948 | GaussianPyramid GPyramid2; 949 | if(IsDisplay) 950 | cout<<"Constructing pyramid..."; 951 | GPyramid1.ConstructPyramid(Im1,ratio,minWidth); 952 | GPyramid2.ConstructPyramid(Im2,ratio,minWidth); 953 | if(IsDisplay) 954 | cout<<"done!"<=0;k--) 973 | { 974 | if(IsDisplay) 975 | cout<<"Pyramid level "<=0;k--) 1054 | { 1055 | if(IsDisplay) 1056 | cout<<"Pyramid level "< foo; 1138 | if(foo.loadImage(filename) == false) 1139 | return false; 1140 | if(!flow.matchDimension(foo)) 1141 | flow.allocate(foo); 1142 | for(int i = 0;i foo; 1153 | if(foo.loadImage(myfile) == false) 1154 | return false; 1155 | if(!flow.matchDimension(foo)) 1156 | flow.allocate(foo); 1157 | for(int i = 0;i foo; 1168 | foo.allocate(flow); 1169 | for(int i =0;i foo; 1180 | foo.allocate(flow); 1181 | for(int i =0;i foo; 1197 | foo.allocate(flow.width(),flow.height()); 1198 | double Max = flow.max(); 1199 | double Min = flow.min(); 1200 | for(int i = 0;i 10 | 11 | typedef double _FlowPrecision; 12 | 13 | class OpticalFlow 14 | { 15 | public: 16 | static bool IsDisplay; 17 | public: 18 | enum InterpolationMethod {Bilinear,Bicubic}; 19 | static InterpolationMethod interpolation; 20 | enum NoiseModel {GMixture,Lap}; 21 | OpticalFlow(void); 22 | ~OpticalFlow(void); 23 | static GaussianMixture GMPara; 24 | static Vector LapPara; 25 | static NoiseModel noiseModel; 26 | public: 27 | static void getDxs(DImage& imdx,DImage& imdy,DImage& imdt,const DImage& im1,const DImage& im2); 28 | static void SanityCheck(const DImage& imdx,const DImage& imdy,const DImage& imdt,double du,double dv); 29 | static void warpFL(DImage& warpIm2,const DImage& Im1,const DImage& Im2,const DImage& vx,const DImage& vy); 30 | static void warpFL(DImage& warpIm2,const DImage& Im1,const DImage& Im2,const DImage& flow); 31 | 32 | 33 | static void genConstFlow(DImage& flow,double value,int width,int height); 34 | static void genInImageMask(DImage& mask,const DImage& vx,const DImage& vy,int interval = 0); 35 | static void genInImageMask(DImage& mask,const DImage& flow,int interval =0 ); 36 | static void SmoothFlowPDE(const DImage& Im1,const DImage& Im2, DImage& warpIm2,DImage& vx,DImage& vy, 37 | double alpha,int nOuterFPIterations,int nInnerFPIterations,int nCGIterations); 38 | 39 | static void SmoothFlowSOR(const DImage& Im1,const DImage& Im2, DImage& warpIm2, DImage& vx, DImage& vy, 40 | double alpha,int nOuterFPIterations,int nInnerFPIterations,int nSORIterations); 41 | 42 | static void estGaussianMixture(const DImage& Im1,const DImage& Im2,GaussianMixture& para,double prior = 0.9); 43 | static void estLaplacianNoise(const DImage& Im1,const DImage& Im2,Vector& para); 44 | static void Laplacian(DImage& output,const DImage& input,const DImage& weight); 45 | static void testLaplacian(int dim=3); 46 | 47 | // function of coarse to fine optical flow 48 | static void Coarse2FineFlow(DImage& vx,DImage& vy,DImage &warpI2,const DImage& Im1,const DImage& Im2,double alpha,double ratio,int minWidth, 49 | int nOuterFPIterations,int nInnerFPIterations,int nCGIterations); 50 | 51 | static void Coarse2FineFlowLevel(DImage& vx,DImage& vy,DImage &warpI2,const DImage& Im1,const DImage& Im2,double alpha,double ratio,int nLevels, 52 | int nOuterFPIterations,int nInnerFPIterations,int nCGIterations); 53 | 54 | // function to convert image to features 55 | static void im2feature(DImage& imfeature,const DImage& im); 56 | 57 | // function to load optical flow 58 | static bool LoadOpticalFlow(const char* filename,DImage& flow); 59 | 60 | static bool LoadOpticalFlow(ifstream& myfile,DImage& flow); 61 | 62 | static bool SaveOpticalFlow(const DImage& flow, const char* filename); 63 | 64 | static bool SaveOpticalFlow(const DImage& flow,ofstream& myfile); 65 | 66 | static bool showFlow(const DImage& vx,const char* filename); 67 | 68 | // function to assemble and dissemble flows 69 | static void AssembleFlow(const DImage& vx,const DImage& vy,DImage& flow) 70 | { 71 | if(!flow.matchDimension(vx.width(),vx.height(),2)) 72 | flow.allocate(vx.width(),vx.height(),2); 73 | for(int i = 0;iR-1) 40 | Index=R-1; 41 | return Index; 42 | } 43 | 44 | double CStochastic::GaussianSampling() 45 | { 46 | int i; 47 | double result=0; 48 | for (i=0;i<12;i++) 49 | result+=UniformSampling(); 50 | result-=6; 51 | return result; 52 | } 53 | 54 | 55 | double CStochastic::GetMean(double* signal,int length) 56 | { 57 | double mean=0; 58 | int i; 59 | for(i=0;i=RandNumber) 74 | return i; 75 | } 76 | return NumSamples-1; 77 | } 78 | 79 | void CStochastic::Generate1DGaussian(double* pGaussian,int size,double sigma) 80 | { 81 | int i; 82 | if(sigma==0) 83 | sigma=size/2; 84 | for(i=-size;i<=size;i++) 85 | pGaussian[i+size]=exp(-(double)i*i/(2*sigma)); 86 | } 87 | 88 | void CStochastic::Generate2DGaussian(double* pGaussian,int WinSize,double sigma) 89 | { 90 | int i,j,WinLength=WinSize*2+1; 91 | double Sigma; 92 | if(sigma==0) 93 | Sigma=WinSize; 94 | else 95 | Sigma=sigma; 96 | Sigma*=Sigma; 97 | for (i=-WinSize;i<=WinSize;i++) 98 | for(j=-WinSize;j<=WinSize;j++) 99 | pGaussian[(i+WinSize)*WinLength+j+WinSize]=exp(-(double)(i*i+j*j)/(2*Sigma)); 100 | Normalize(WinLength*WinLength,pGaussian); 101 | } 102 | 103 | double CStochastic::entropy(double* pDensity,int n) 104 | { 105 | double result=0; 106 | int i; 107 | for(i=0;i=0)?x:-x 16 | #endif 17 | 18 | #ifndef PI 19 | #define PI 3.1415927 20 | #endif 21 | 22 | enum SortType{SortAscending,SortDescending}; 23 | 24 | class CStochastic 25 | { 26 | public: 27 | CStochastic(void); 28 | ~CStochastic(void); 29 | static void ConvertInt2String(int x,char* string,int BitNumber=3); 30 | static double UniformSampling(); 31 | static int UniformSampling(int R); 32 | static double GaussianSampling(); 33 | template static void GetMeanVar(T* signal,int length,double* mean,double* var); 34 | static int Sampling(double* Density,int NumSamples); 35 | static double GetMean(double *signal,int length); 36 | static void Generate1DGaussian(double* pGaussian,int size,double sigma=0); 37 | static void Generate2DGaussian(double* pGaussian,int size,double sigma=0); 38 | static double entropy(double* pDensity,int n); 39 | 40 | template static T sum(int NumData,T* pData); 41 | template static void Normalize(int NumData,T* pData); 42 | template static T mean(int NumData, T* pData); 43 | template static void sort(int number, T* pData,int *pIndex,SortType m_SortType=SortDescending); 44 | template static T Min(int NumData, T* pData); 45 | template static T Min(int NumData, T* pData1,T* pData2); 46 | template static T Max(int NumData ,T* pData); 47 | template static int FindMax(int NumData,T* pData); 48 | template static void ComputeVectorMean(int Dim,int NumData,T1* pData,T2* pMean,double* pWeight=NULL); 49 | template static void ComputeMeanCovariance(int Dim,int NumData,T1* pData,T2* pMean,T2* pCovarance,double* pWeight=NULL); 50 | template static double VectorSquareDistance(int Dim,T1* pVector1,T2* pVector2); 51 | template static void KMeanClustering(int Dim,int NumData,int NumClusters,T1* pData,int *pPartition,double** pClusterMean=NULL,int MaxIterationNum=10,int MinClusterSampleNumber=2); 52 | template static double norm(T* X,int Dim); 53 | template static int FindClosestPoint(T1* pPointSet,int NumPoints,int nDim,T2* QueryPoint); 54 | template static void GaussianFiltering(T1* pSrcArray,T2* pDstArray,int NumPoints,int nChannels,int size,double sigma); 55 | }; 56 | 57 | template 58 | void CStochastic::GetMeanVar(T* signal,int length,double* mean,double* var) 59 | { 60 | double m_mean=0,m_var=0; 61 | 62 | int i; 63 | for (i=0;i 74 | T CStochastic::sum(int NumData, T* pData) 75 | { 76 | T sum=0; 77 | int i; 78 | for(i=0;i 84 | void CStochastic::Normalize(int NumData,T* pData) 85 | { 86 | int i; 87 | T Sum; 88 | Sum=sum(NumData,pData); 89 | for(i=0;i 94 | T CStochastic::mean(int NumData,T* pData) 95 | { 96 | return sum(NumData,pData)/NumData; 97 | } 98 | 99 | //////////////////////////////////////////////////////////// 100 | // sort data in descending order 101 | template 102 | void CStochastic::sort(int Number,T* pData,int *pIndex,SortType m_SortType) 103 | { 104 | int i,j,offset_extreme,*flag; 105 | double extreme; 106 | flag=new int[Number]; 107 | memset(flag,0,sizeof(int)*Number); 108 | for(i=0;ipData[j])) 120 | { 121 | extreme=pData[j]; 122 | offset_extreme=j; 123 | } 124 | } 125 | pIndex[i]=offset_extreme; 126 | flag[offset_extreme]=1; 127 | } 128 | delete flag; 129 | } 130 | 131 | template 132 | T CStochastic::Min(int NumData,T* pData) 133 | { 134 | int i; 135 | T result=pData[0]; 136 | for(i=1;i 142 | T CStochastic::Min(int NumData,T* pData1,T* pData2) 143 | { 144 | int i; 145 | T result=pData1[0]+pData2[0]; 146 | for(i=1;i 152 | T CStochastic::Max(int NumData,T* pData) 153 | { 154 | int i; 155 | T result=pData[0]; 156 | for(i=1;i 162 | int CStochastic::FindMax(int NumData,T* pData) 163 | { 164 | int i,index; 165 | T result=pData[0]; 166 | index=0; 167 | for(i=1;iresult) 169 | { 170 | index=i; 171 | result=pData[i]; 172 | } 173 | return index; 174 | } 175 | 176 | 177 | template 178 | void CStochastic::ComputeMeanCovariance(int Dim,int NumData,T1* pData,T2* pMean,T2* pCovariance,double* pWeight) 179 | { 180 | int i,j,k; 181 | memset(pMean,0,sizeof(T2)*Dim); 182 | memset(pCovariance,0,sizeof(T2)*Dim*Dim); 183 | 184 | bool IsWeightLoaded=false; 185 | double Sum; 186 | if(pWeight!=NULL) 187 | IsWeightLoaded=true; 188 | 189 | // compute mean first 190 | Sum=0; 191 | if(IsWeightLoaded) 192 | for(i=0;i 242 | void CStochastic::ComputeVectorMean(int Dim,int NumData,T1* pData,T2* pMean,double* pWeight) 243 | { 244 | int i,j; 245 | memset(pMean,0,sizeof(T2)*Dim); 246 | bool IsWeightLoaded; 247 | double Sum; 248 | if(pWeight==NULL) 249 | IsWeightLoaded=false; 250 | else 251 | IsWeightLoaded=true; 252 | 253 | Sum=0; 254 | if(IsWeightLoaded) 255 | for(i=0;i 275 | double CStochastic::VectorSquareDistance(int Dim,T1* pVector1,T2* pVector2) 276 | { 277 | double result=0,temp; 278 | int i; 279 | for(i=0;i 288 | void CStochastic::KMeanClustering(int Dim,int NumData,int NumClusters,T1* pData,int *pPartition,double** pClusterMean,int MaxIterationNum, int MinClusterSampleNumber) 289 | { 290 | int i,j,k,l,Index,ClusterSampleNumber; 291 | double MinDistance,Distance; 292 | double** pCenters; 293 | pCenters=new double*[NumClusters]; 294 | for(i=0;i 355 | double CStochastic::norm(T* X,int Dim) 356 | { 357 | double result=0; 358 | int i; 359 | for(i=0;i 366 | int CStochastic::FindClosestPoint(T1* pPointSet,int NumPoints,int nDim,T2* QueryPoint) 367 | { 368 | int i,j,Index=0,offset; 369 | T1 MinDistance,Distance,x; 370 | MinDistance=0; 371 | for(j=0;j 392 | void CStochastic::GaussianFiltering(T1* pSrcArray,T2* pDstArray,int NumPoints,int nChannels,int size,double sigma) 393 | { 394 | int i,j,u,l; 395 | double *pGaussian,temp; 396 | pGaussian=new double[2*size+1]; 397 | Generate1DGaussian(pGaussian,size,sigma); 398 | for(i=0;i 9 | 10 | using namespace std; 11 | 12 | template 13 | class Vector 14 | { 15 | protected: 16 | int nDim; 17 | T* pData; 18 | public: 19 | Vector(void); 20 | Vector(int ndim,const T *data=NULL); 21 | Vector(const Vector& vect); 22 | ~Vector(void); 23 | void releaseData(); 24 | void allocate(int ndim); 25 | void allocate(const Vector& vect){allocate(vect.nDim);}; 26 | void copyData(const Vector& vect); 27 | void dimcheck(const Vector& vect) const; 28 | void reset(); 29 | double norm2() const; 30 | 31 | T sum() const; 32 | 33 | void printVector(); 34 | 35 | // access the members 36 | const T* data() const{return (const T*)pData;}; 37 | T* data() {return pData;}; 38 | int dim() const {return nDim;}; 39 | inline bool matchDimension(int _ndim) const {if(nDim==_ndim) return true;else return false;}; 40 | inline bool matchDimension(const Vector& vect) const {return matchDimension(vect.nDim);}; 41 | 42 | // operators 43 | inline T operator[](int index) const {return pData[index];}; 44 | inline T& operator[](int index){return *(pData+index);}; 45 | Vector& operator=(const Vector& vect); 46 | 47 | //const Vector& operator/(double val) const 48 | //{ 49 | // Vector result(nDim); 50 | // for(int i =0;i& operator+=(const Vector& vect); 56 | Vector& operator*=(const Vector& vect); 57 | Vector& operator-=(const Vector& vect); 58 | Vector& operator/=(const Vector& vect); 59 | 60 | Vector& operator+=(double val); 61 | Vector& operator*=(double val); 62 | Vector& operator-=(double val); 63 | Vector& operator/=(double val); 64 | 65 | //friend const Vector operator+(const Vector& vect1,const Vector& vect2); 66 | //friend const Vector operator*(const Vector& vect1,const Vector& vect2); 67 | //friend const Vector operator-(const Vector& vect1,const Vector& vect2); 68 | //friend const Vector operator/(const Vector& vect1,const Vector& vect2); 69 | 70 | //friend const Vector operator+(const Vector& vect1,double val); 71 | //friend const Vector operator*(const Vector& vect1,double val); 72 | //friend const Vector operator-(const Vector& vect1,double val); 73 | //friend Vector operator/(const Vector& vect,double val); 74 | 75 | friend double innerproduct(const Vector& vect1,const Vector& vect2) 76 | { 77 | double result = 0; 78 | for(int i = 0;i >& vect); 84 | 85 | //friend const Vector concatenate(const vector>& vect){Vector result; result.concatenate(vect); return result;}; 86 | bool write(ofstream& myfile) 87 | { 88 | myfile.write((char *)&nDim,sizeof(int)); 89 | myfile.write((char *)pData,sizeof(T)*nDim); 90 | return true; 91 | } 92 | bool read(ifstream& myfile) 93 | { 94 | myfile.read((char *)&nDim,sizeof(int)); 95 | allocate(nDim); 96 | myfile.read((char *)pData,sizeof(T)*nDim); 97 | return true; 98 | } 99 | T mean(int N=-1) const 100 | { 101 | if(N==-1) 102 | N = nDim; 103 | T result = 0; 104 | for(int i = 0;i 115 | //double innerproduct(const Vector& vect1,const Vector& vect2) 116 | //{ 117 | // double result = 0; 118 | // for(int i = 0;i 124 | Vector::Vector(void) 125 | { 126 | nDim=0; 127 | pData=NULL; 128 | } 129 | 130 | template 131 | Vector::Vector(int ndim, const T *data) 132 | { 133 | nDim=ndim; 134 | pData=new T[nDim]; 135 | if(data!=NULL) 136 | memcpy(pData,data,sizeof(T)*nDim); 137 | else 138 | memset(pData,0,sizeof(T)*nDim); 139 | } 140 | 141 | template 142 | Vector::Vector(const Vector& vect) 143 | { 144 | nDim=0; 145 | pData=NULL; 146 | copyData(vect); 147 | } 148 | 149 | template 150 | Vector::~Vector(void) 151 | { 152 | releaseData(); 153 | } 154 | 155 | template 156 | void Vector::releaseData() 157 | { 158 | if(pData!=NULL) 159 | delete[] pData; 160 | pData=NULL; 161 | nDim=0; 162 | } 163 | 164 | template 165 | void Vector::allocate(int ndim) 166 | { 167 | releaseData(); 168 | nDim=ndim; 169 | if(nDim>0) 170 | { 171 | pData=new T[nDim]; 172 | reset(); 173 | } 174 | } 175 | 176 | 177 | template 178 | void Vector::copyData(const Vector &vect) 179 | { 180 | if(nDim!=vect.nDim) 181 | { 182 | releaseData(); 183 | nDim=vect.nDim; 184 | pData=new T[nDim]; 185 | } 186 | memcpy(pData,vect.pData,sizeof(T)*nDim); 187 | } 188 | 189 | template 190 | void Vector::dimcheck(const Vector &vect) const 191 | { 192 | if(nDim!=vect.nDim) 193 | cout<<"The dimensions of the vectors don't match!"< 197 | void Vector::reset() 198 | { 199 | if(pData!=NULL) 200 | memset(pData,0,sizeof(T)*nDim); 201 | } 202 | 203 | 204 | template 205 | T Vector::sum() const 206 | { 207 | T total = 0; 208 | for(int i=0;i 214 | double Vector::norm2() const 215 | { 216 | double temp=0; 217 | for(int i=0;i 223 | void Vector::printVector() 224 | { 225 | for(int i=0;i 235 | Vector& Vector::operator =(const Vector &vect) 236 | { 237 | copyData(vect); 238 | return *this; 239 | } 240 | 241 | template 242 | Vector& Vector::operator +=(const Vector &vect) 243 | { 244 | dimcheck(vect); 245 | for(int i=0;i 251 | Vector& Vector::operator *=(const Vector &vect) 252 | { 253 | dimcheck(vect); 254 | for(int i=0;i 260 | Vector& Vector::operator -=(const Vector &vect) 261 | { 262 | dimcheck(vect); 263 | for(int i=0;i 269 | Vector& Vector::operator /=(const Vector &vect) 270 | { 271 | dimcheck(vect); 272 | for(int i=0;i 278 | Vector& Vector::operator +=(double val) 279 | { 280 | for(int i=0;i 286 | Vector& Vector::operator *=(double val) 287 | { 288 | for(int i=0;i 294 | Vector& Vector::operator -=(double val) 295 | { 296 | for(int i=0;i 302 | Vector& Vector::operator /=(double val) 303 | { 304 | for(int i=0;i 311 | const Vector operator+(const Vector& vect1,const Vector& vect2) 312 | { 313 | vect1.dimcheck(vect2); 314 | Vector result(vect1); 315 | result+=vect2; 316 | return result; 317 | } 318 | 319 | template 320 | const Vector operator-(const Vector& vect1,const Vector& vect2) 321 | { 322 | vect1.dimcheck(vect2); 323 | Vector result(vect1); 324 | result-=vect2; 325 | return result; 326 | } 327 | 328 | template 329 | const Vector operator*(const Vector& vect1,const Vector& vect2) 330 | { 331 | vect1.dimcheck(vect2); 332 | Vector result(vect1); 333 | result*=vect2; 334 | return result; 335 | } 336 | 337 | template 338 | const Vector operator/(const Vector& vect1,const Vector& vect2) 339 | { 340 | vect1.dimcheck(vect2); 341 | Vector result(vect1); 342 | result/=vect2; 343 | return result; 344 | } 345 | 346 | template 347 | Vector operator+(const Vector& vect,double val) 348 | { 349 | Vector result(vect); 350 | result+=val; 351 | return result; 352 | } 353 | 354 | template 355 | Vector operator-(const Vector& vect,double val) 356 | { 357 | Vector result(vect); 358 | result-=val; 359 | return result; 360 | } 361 | 362 | template 363 | Vector operator*(const Vector& vect,double val) 364 | { 365 | Vector result(vect); 366 | result*=val; 367 | return result; 368 | } 369 | 370 | template 371 | Vector operator/(const Vector& vect,double val) 372 | { 373 | Vector result(vect); 374 | result/=val; 375 | return result; 376 | } 377 | 378 | 379 | template 380 | double innerproduct(const Vector& vect1,const Vector& vect2) 381 | { 382 | vect1.dimcheck(vect2); 383 | double result=0; 384 | for(int i=0;i 390 | void Vector::concatenate(const vector< Vector >& vect) 391 | { 392 | releaseData(); 393 | nDim = 0; 394 | for(int i = 0;i0) 423 | { 424 | allocate(nDim); 425 | if(file.read((char *)pData,sizeof(double)*nDim)!=sizeof(double)*nDim) 426 | return false; 427 | } 428 | return true; 429 | } 430 | 431 | #endif 432 | 433 | 434 | #ifdef _MATLAB 435 | 436 | template 437 | void Vector::readVector(const mxArray* prhs) 438 | { 439 | if(pData!=NULL) 440 | delete[] pData; 441 | int nElements = mxGetNumberOfDimensions(prhs); 442 | if(nElements>2) 443 | mexErrMsgTxt("A vector is expected to be loaded!"); 444 | const int* dims = mxGetDimensions(prhs); 445 | nDim = dims[0]*dims[1]; 446 | pData = new T[nDim]; 447 | double* ptr = (double*)mxGetData(prhs); 448 | for(int i =0;i 453 | void Vector::writeVector(mxArray*& plhs) const 454 | { 455 | int dims[2]; 456 | dims[0]=nDim;dims[1]=1; 457 | plhs=mxCreateNumericArray(2, dims,mxDOUBLE_CLASS, mxREAL); 458 | double *ptr = (double*)mxGetData(plhs); 459 | for(int i =0;i 7 | 8 | // if the files are compiled in linux or mac os then uncomment the following line, otherwise comment it if you compile using visual studio in windows 9 | #define _LINUX_MAC 10 | // #define _OPENCV 11 | 12 | template 13 | void _Release1DBuffer(T* pBuffer) 14 | { 15 | if(pBuffer!=NULL) 16 | delete []pBuffer; 17 | pBuffer=NULL; 18 | } 19 | 20 | template 21 | void _Rlease2DBuffer(T** pBuffer,size_t nElements) 22 | { 23 | for(size_t i=0;i 41 | T1 __min(T1 a, T2 b) 42 | { 43 | return (a>b)?b:a; 44 | } 45 | 46 | template 47 | T1 __max(T1 a, T2 b) 48 | { 49 | return (a