Download this file

143 lines (117 with data), 5.8 kB

/*-----------------------------------------------------------------------

--------------------------------------------------------------------------*/

#include <mex.h>
#include <mat.h>
#include <matrix.h>

#define cimg_plugin "cimgmatlab.h"

#include "CImg.h"
#include <iostream>
#include <string>

using namespace cimg_library;
using namespace std;

// The lines below are necessary when using a non-standard compiler as visualcpp6.
#ifdef cimg_use_visualcpp6
#define std
#endif
#ifdef min
#undef min
#undef max
#endif

#ifndef cimg_imagepath
#define cimg_imagepath "img/"
#endif

//global constants
const float smooth = 0.1f;//"Flow Smoothness"
const float precision = 0.09f;//"Convergence precision"
const unsigned int nb=110;//"Number of warped frames"
const unsigned int nbscale = 0 ;//"Number of scales (0=auto)");

const bool normalize = true; //"Histogram normalization of the images"
const bool morph = true;//"Morphing mode"
const bool imode  = true;//"Complete interpolation (or last frame is missing)"
const bool dispflag = true;//"Visualization"
//Functions
CImg<> optmonoflow(const CImg<>& I1, const CImg<>& I2, const CImg<>& u0,
                   const float smooth, const float precision, CImgDisplay& disp);
CImg<> optflow(const CImg<>& xsrc, const CImg<>& xdest,
               const float smooth, const float precision, const unsigned int pnb_scale, CImgDisplay& disp);
                
//mex function
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
  if (nrhs < 2) mexErrMsgTxt("No enough input arguments.");
  if (nrhs > 2) mexErrMsgTxt("Too many input arguments.");
  if (nrhs == 2){
                CImg<> src_blur(prhs[0],false), dest_blur(prhs[1],false);//2 Input Volumes
                //Input images preprocessing
                 src_blur = normalize?src_blur.get_blur(0.5f).equalize(256):src_blur.get_blur(0.5f),
                 dest_blur  = normalize?dest_blur.get_blur(0.5f).equalize(256):dest_blur.get_blur(0.5f);
                 CImgDisplay disp;
                 const CImg<> u = optflow(src_blur,dest_blur,smooth,precision,nbscale,disp);
                CImgDisplay disp2(src_blur);
                 
                 plhs[0] = u.toMatlab();
                      
                }
  return;
     
}
/////////////////////////////////////////////////////////////////////////////////

CImg<> optflow(const CImg<>& xsrc, const CImg<>& xdest,
               const float smooth, const float precision, const unsigned int pnb_scale, CImgDisplay& disp) {
  const CImg<>
    src  = xsrc.get_pointwise_norm(1).resize(xdest.dimx(),xdest.dimy(),xdest.dimz()).normalize(0,1),
    dest = xdest.get_pointwise_norm(1).resize(xdest.dimx(),xdest.dimy(),xdest.dimz()).normalize(0,1);
    CImg<> u =  CImg<>(src.dimx(),src.dimy(),src.dimz(),3,1).fill(0);
       
  //multi-scalling 
    const unsigned int nb_scale = pnb_scale>0?pnb_scale:(unsigned int)(2*log((double)(max(max(src.dimx(),src.dimy()),src.dimz()))));
  //const unsigned int nb_scale = pnb_scale>0?pnb_scale:(unsigned int)(2*std::log((double)(cimg::max(src.dimx(),src.dimy()))));
  for (int scale=nb_scale-1; scale>=0; scale--) {
       const CImg<> I1 = src.get_resize((int)(src.dimx()/std::pow(1.5,scale)), (int)(src.dimy()/std::pow(1.5,scale)) ,(int)(ceil(src.dimz()/std::pow(1.5,scale))));
       const CImg<> I2 = dest.get_resize((int)(src.dimx()/std::pow(1.5,scale)), (int)(src.dimy()/std::pow(1.5,scale)) ,(int)(ceil(src.dimz()/std::pow(1.5,scale))));
       u*=1.5;
       u = optmonoflow(I1,I2,u,smooth,(float)(precision/std::pow(2.25,1+scale)),disp);//apply optical flow algorithm for different scales
    
  }
 return u;
}

// optmonoflow() : estimate optical flow for one scale ( semi-implicite PDE scheme ) between I2->I1
//---------------
CImg<> optmonoflow(const CImg<>& I1, const CImg<>& I2, const CImg<>& u0,
                   const float smooth, const float precision, CImgDisplay& disp) {

 CImg<float> u = u0.get_resize(I1.dimx(),I1.dimy(),I1.dimz(),3);
 CImg<float> dI(I2.dimx(),I2.dimy(),I2.dimz(),3);
  dI.fill(0);
 //CImg_3x3x3(I,float);
  float dt=2,E=1e20f;

  // compute first derivatives of I2
    cimg_for3XYZ(dI,x,y,z){
              dI(x,y,z,0) = 0.5*(I2(_n1x,y,z)-I2(_p1x,y,z));//derivatives of I2
              dI(x,y,z,1) = 0.5*(I2(x,_n1y,z)-I2(x,_p1y,z));
              dI(x,y,z,2) = 0.5*(I2(x,y,_n1z)-I2(x,y,_p1z));
                          
     }
   
// Main PDE iteration
for (unsigned int iter=0; iter<10000; iter++) {
    std::fprintf(stderr,"\r- Iteration %d - E = %g",iter,E); std::fflush(stderr);
    const float Eold = E;
    E = 0;
    cimg_for3XYZ(u,x,y,z) { //3x3 neighborhood  
      const float
        X = x + u(x,y,z,0),
        Y = y + u(x,y,z,1),
        Z = z + u(x,y,z,2),
        deltaI = (float)(I2.linear_atXYZ(X,Y,Z) - I1(x,y,z));// partial derivative over time-dI/dt
      float tmpf = 0;
      cimg_forV(u,k) {
        const float
          ux  = 0.5f*(u(_n1x,y,z,k)-u(_p1x,y,z,k)),//derivatives of velocity(displacemenet)
          uy  = 0.5f*(u(x,_n1y,z,k)-u(x,_p1y,z,k)),
          uz  = 0.5f*(u(x,y,_n1z,k)-u(x,y,_p1z,k)); 
          u(x,y,z,k) = (float)( u(x,y,z,k) +
                            dt*(
                                -deltaI*dI.linear_atXYZ(X,Y,Z,k) +
                                smooth* ( u(_n1x,y,z,k) + u(_p1x,y,z,k) + u(x,_n1y,z,k) + u(x,_p1y,z,k)+  u(x,y,_n1z,k) + u(x,y,_p1z,k)-6*u(x,y,z,k))
                                )/(1+4*smooth*dt)
                              );
      tmpf += ux*ux + uy*uy + uz*uz;
     
      }
      E += deltaI*deltaI + smooth * tmpf;
    }
    if (cimg::abs(Eold-E)<precision) break;
    if (Eold<E) dt*=0.5;
 
}
  return  I1;
}