jsfeat
Version:
JavaScript Computer Vision library
247 lines (199 loc) • 10.7 kB
JavaScript
/**
* @author Eugene Zatepyakin / http://inspirit.ru/
*
* this code is a rewrite from OpenCV's Lucas-Kanade optical flow implementation
*/
(function(global) {
"use strict";
//
var optical_flow_lk = (function() {
// short link to shar deriv
var scharr_deriv = jsfeat.imgproc.scharr_derivatives;
return {
track: function(prev_pyr, curr_pyr, prev_xy, curr_xy, count, win_size, max_iter, status, eps, min_eigen_threshold) {
if (typeof max_iter === "undefined") { max_iter = 30; }
if (typeof status === "undefined") { status = new Uint8Array(count); }
if (typeof eps === "undefined") { eps = 0.01; }
if (typeof min_eigen_threshold === "undefined") { min_eigen_threshold = 0.0001; }
var half_win = (win_size-1)*0.5;
var win_area = (win_size*win_size)|0;
var win_area2 = win_area << 1;
var prev_imgs = prev_pyr.data, next_imgs = curr_pyr.data;
var img_prev=prev_imgs[0].data,img_next=next_imgs[0].data;
var w0 = prev_imgs[0].cols, h0 = prev_imgs[0].rows,lw=0,lh=0;
var iwin_node = jsfeat.cache.get_buffer(win_area<<2);
var deriv_iwin_node = jsfeat.cache.get_buffer(win_area2<<2);
var deriv_lev_node = jsfeat.cache.get_buffer((h0*(w0<<1))<<2);
var deriv_m = new jsfeat.matrix_t(w0, h0, jsfeat.S32C2_t, deriv_lev_node.data);
var iwin_buf = iwin_node.i32;
var deriv_iwin = deriv_iwin_node.i32;
var deriv_lev = deriv_lev_node.i32;
var dstep=0,src=0,dsrc=0,iptr=0,diptr=0,jptr=0;
var lev_sc=0.0,prev_x=0.0,prev_y=0.0,next_x=0.0,next_y=0.0;
var prev_delta_x=0.0,prev_delta_y=0.0,delta_x=0.0,delta_y=0.0;
var iprev_x=0,iprev_y=0,inext_x=0,inext_y=0;
var i=0,j=0,x=0,y=0,level=0,ptid=0,iter=0;
var brd_tl=0,brd_r=0,brd_b=0;
var a=0.0,b=0.0,b1=0.0,b2=0.0;
// fixed point math
var W_BITS14 = 14;
var W_BITS4 = 14;
var W_BITS1m5 = W_BITS4 - 5;
var W_BITS1m51 = (1 << ((W_BITS1m5) - 1));
var W_BITS14_ = (1 << W_BITS14);
var W_BITS41 = (1 << ((W_BITS4) - 1));
var FLT_SCALE = 1.0/(1 << 20);
var iw00=0,iw01=0,iw10=0,iw11=0,ival=0,ixval=0,iyval=0;
var A11=0.0,A12=0.0,A22=0.0,D=0.0,min_eig=0.0;
var FLT_EPSILON = 0.00000011920929;
eps *= eps;
// reset status
for(; i < count; ++i) {
status[i] = 1;
}
var max_level = (prev_pyr.levels - 1)|0;
level = max_level;
for(; level >= 0; --level) {
lev_sc = (1.0/(1 << level));
lw = w0 >> level;
lh = h0 >> level;
dstep = lw << 1;
img_prev = prev_imgs[level].data;
img_next = next_imgs[level].data;
brd_r = (lw - win_size)|0;
brd_b = (lh - win_size)|0;
// calculate level derivatives
scharr_deriv(prev_imgs[level], deriv_m);
// iterate through points
for(ptid = 0; ptid < count; ++ptid) {
i = ptid << 1;
j = i + 1;
prev_x = prev_xy[i]*lev_sc;
prev_y = prev_xy[j]*lev_sc;
if( level == max_level ) {
next_x = prev_x;
next_y = prev_y;
} else {
next_x = curr_xy[i]*2.0;
next_y = curr_xy[j]*2.0;
}
curr_xy[i] = next_x;
curr_xy[j] = next_y;
prev_x -= half_win;
prev_y -= half_win;
iprev_x = prev_x|0;
iprev_y = prev_y|0;
// border check
x = (iprev_x <= brd_tl)|(iprev_x >= brd_r)|(iprev_y <= brd_tl)|(iprev_y >= brd_b);
if( x != 0 ) {
if( level == 0 ) {
status[ptid] = 0;
}
continue;
}
a = prev_x - iprev_x;
b = prev_y - iprev_y;
iw00 = (((1.0 - a)*(1.0 - b)*W_BITS14_) + 0.5)|0;
iw01 = ((a*(1.0 - b)*W_BITS14_) + 0.5)|0;
iw10 = (((1.0 - a)*b*W_BITS14_) + 0.5)|0;
iw11 = (W_BITS14_ - iw00 - iw01 - iw10);
A11 = 0.0, A12 = 0.0, A22 = 0.0;
// extract the patch from the first image, compute covariation matrix of derivatives
for( y = 0; y < win_size; ++y ) {
src = ( (y + iprev_y)*lw + iprev_x )|0;
dsrc = src << 1;
iptr = (y*win_size)|0;
diptr = iptr << 1;
for(x = 0 ; x < win_size; ++x, ++src, ++iptr, dsrc += 2) {
ival = ( (img_prev[src])*iw00 + (img_prev[src+1])*iw01 +
(img_prev[src+lw])*iw10 + (img_prev[src+lw+1])*iw11 );
ival = (((ival) + W_BITS1m51) >> (W_BITS1m5));
ixval = ( deriv_lev[dsrc]*iw00 + deriv_lev[dsrc+2]*iw01 +
deriv_lev[dsrc+dstep]*iw10 + deriv_lev[dsrc+dstep+2]*iw11 );
ixval = (((ixval) + W_BITS41) >> (W_BITS4));
iyval = ( deriv_lev[dsrc+1]*iw00 + deriv_lev[dsrc+3]*iw01 + deriv_lev[dsrc+dstep+1]*iw10 +
deriv_lev[dsrc+dstep+3]*iw11 );
iyval = (((iyval) + W_BITS41) >> (W_BITS4));
iwin_buf[iptr] = ival;
deriv_iwin[diptr++] = ixval;
deriv_iwin[diptr++] = iyval;
A11 += ixval*ixval;
A12 += ixval*iyval;
A22 += iyval*iyval;
}
}
A11 *= FLT_SCALE; A12 *= FLT_SCALE; A22 *= FLT_SCALE;
D = A11*A22 - A12*A12;
min_eig = (A22 + A11 - Math.sqrt((A11-A22)*(A11-A22) + 4.0*A12*A12)) / win_area2;
if( min_eig < min_eigen_threshold || D < FLT_EPSILON )
{
if( level == 0 ) {
status[ptid] = 0;
}
continue;
}
D = 1.0/D;
next_x -= half_win;
next_y -= half_win;
prev_delta_x = 0.0;
prev_delta_y = 0.0;
for( iter = 0; iter < max_iter; ++iter ) {
inext_x = next_x|0;
inext_y = next_y|0;
x = (inext_x <= brd_tl)|(inext_x >= brd_r)|(inext_y <= brd_tl)|(inext_y >= brd_b);
if( x != 0 ) {
if( level == 0 ) {
status[ptid] = 0;
}
break;
}
a = next_x - inext_x;
b = next_y - inext_y;
iw00 = (((1.0 - a)*(1.0 - b)*W_BITS14_) + 0.5)|0;
iw01 = ((a*(1.0 - b)*W_BITS14_) + 0.5)|0;
iw10 = (((1.0 - a)*b*W_BITS14_) + 0.5)|0;
iw11 = (W_BITS14_ - iw00 - iw01 - iw10);
b1 = 0.0, b2 = 0.0;
for( y = 0; y < win_size; ++y ) {
jptr = ( (y + inext_y)*lw + inext_x )|0;
iptr = (y*win_size)|0;
diptr = iptr << 1;
for( x = 0 ; x < win_size; ++x, ++jptr, ++iptr ) {
ival = ( (img_next[jptr])*iw00 + (img_next[jptr+1])*iw01 +
(img_next[jptr+lw])*iw10 + (img_next[jptr+lw+1])*iw11 );
ival = (((ival) + W_BITS1m51) >> (W_BITS1m5));
ival = (ival - iwin_buf[iptr]);
b1 += ival * deriv_iwin[diptr++];
b2 += ival * deriv_iwin[diptr++];
}
}
b1 *= FLT_SCALE;
b2 *= FLT_SCALE;
delta_x = ((A12*b2 - A22*b1) * D);
delta_y = ((A12*b1 - A11*b2) * D);
next_x += delta_x;
next_y += delta_y;
curr_xy[i] = next_x + half_win;
curr_xy[j] = next_y + half_win;
if( delta_x*delta_x + delta_y*delta_y <= eps ) {
break;
}
if( iter > 0 && Math.abs(delta_x + prev_delta_x) < 0.01 &&
Math.abs(delta_y + prev_delta_y) < 0.01 ) {
curr_xy[i] -= delta_x*0.5;
curr_xy[j] -= delta_y*0.5;
break;
}
prev_delta_x = delta_x;
prev_delta_y = delta_y;
}
} // points loop
} // levels loop
jsfeat.cache.put_buffer(iwin_node);
jsfeat.cache.put_buffer(deriv_iwin_node);
jsfeat.cache.put_buffer(deriv_lev_node);
}
};
})();
global.optical_flow_lk = optical_flow_lk;
})(jsfeat);