diff --git a/build/jsfeat.js b/build/jsfeat.js index f9cc93b..6d2e280 100644 --- a/build/jsfeat.js +++ b/build/jsfeat.js @@ -236,85 +236,85 @@ var jsfeat = jsfeat || { REVISION: 'ALPHA' }; global.keypoint_t = keypoint_t; })(jsfeat); -/** - * @author Eugene Zatepyakin / http://inspirit.ru/ - */ - -(function(global) { - "use strict"; - // - - var cache = (function() { - - // very primitive array cache, still need testing if it helps - // of course V8 has its own powerful cache sys but i'm not sure - // it caches several multichannel 640x480 buffer creations each frame - - var _pool_node_t = (function () { - function _pool_node_t(size_in_bytes) { - this.next = null; - this.data = new jsfeat.data_t(size_in_bytes); - this.size = this.data.size; - this.buffer = this.data.buffer; - this.u8 = this.data.u8; - this.i32 = this.data.i32; - this.f32 = this.data.f32; - this.f64 = this.data.f64; - } - _pool_node_t.prototype.resize = function(size_in_bytes) { - delete this.data; - this.data = new jsfeat.data_t(size_in_bytes); - this.size = this.data.size; - this.buffer = this.data.buffer; - this.u8 = this.data.u8; - this.i32 = this.data.i32; - this.f32 = this.data.f32; - this.f64 = this.data.f64; - } - return _pool_node_t; - })(); - - var _pool_head, _pool_tail; - var _pool_size = 0; - - return { - - allocate: function(capacity, data_size) { - _pool_head = _pool_tail = new _pool_node_t(data_size); - for (var i = 0; i < capacity; ++i) { - var node = new _pool_node_t(data_size); - _pool_tail = _pool_tail.next = node; - - _pool_size++; - } - }, - - get_buffer: function(size_in_bytes) { - // assume we have enough free nodes - var node = _pool_head; - _pool_head = _pool_head.next; - _pool_size--; - - if(size_in_bytes > node.size) { - node.resize(size_in_bytes); - } - - return node; - }, - - put_buffer: function(node) { - _pool_tail = _pool_tail.next = node; - _pool_size++; - } - }; - })(); - - global.cache = cache; - // for now we dont need more than 30 buffers - // if having cache sys really helps we can add auto extending sys - cache.allocate(30, 640*4); - -})(jsfeat); +/** + * @author Eugene Zatepyakin / http://inspirit.ru/ + */ + +(function(global) { + "use strict"; + // + + var cache = (function() { + + // very primitive array cache, still need testing if it helps + // of course V8 has its own powerful cache sys but i'm not sure + // it caches several multichannel 640x480 buffer creations each frame + + var _pool_node_t = (function () { + function _pool_node_t(size_in_bytes) { + this.next = null; + this.data = new jsfeat.data_t(size_in_bytes); + this.size = this.data.size; + this.buffer = this.data.buffer; + this.u8 = this.data.u8; + this.i32 = this.data.i32; + this.f32 = this.data.f32; + this.f64 = this.data.f64; + } + _pool_node_t.prototype.resize = function(size_in_bytes) { + delete this.data; + this.data = new jsfeat.data_t(size_in_bytes); + this.size = this.data.size; + this.buffer = this.data.buffer; + this.u8 = this.data.u8; + this.i32 = this.data.i32; + this.f32 = this.data.f32; + this.f64 = this.data.f64; + } + return _pool_node_t; + })(); + + var _pool_head, _pool_tail; + var _pool_size = 0; + + return { + + allocate: function(capacity, data_size) { + _pool_head = _pool_tail = new _pool_node_t(data_size); + for (var i = 0; i < capacity; ++i) { + var node = new _pool_node_t(data_size); + _pool_tail = _pool_tail.next = node; + + _pool_size++; + } + }, + + get_buffer: function(size_in_bytes) { + // assume we have enough free nodes + var node = _pool_head; + _pool_head = _pool_head.next; + _pool_size--; + + if(size_in_bytes > node.size) { + node.resize(size_in_bytes); + } + + return node; + }, + + put_buffer: function(node) { + _pool_tail = _pool_tail.next = node; + _pool_size++; + } + }; + })(); + + global.cache = cache; + // for now we dont need more than 30 buffers + // if having cache sys really helps we can add auto extending sys + cache.allocate(30, 640*4); + +})(jsfeat); /** * @author Eugene Zatepyakin / http://inspirit.ru/ */ @@ -961,7 +961,7 @@ var jsfeat = jsfeat || { REVISION: 'ALPHA' }; global.matmath = matmath; -})(jsfeat); +})(jsfeat); /** * @author Eugene Zatepyakin / http://inspirit.ru/ * @@ -1649,1921 +1649,1921 @@ var jsfeat = jsfeat || { REVISION: 'ALPHA' }; global.linalg = linalg; -})(jsfeat); -/** - * @author Eugene Zatepyakin / http://inspirit.ru/ - * - */ - -(function(global) { - "use strict"; - // - - var motion_model = (function() { - - var sqr = function(x) { - return x*x; - } - - // does isotropic normalization - var iso_normalize_points = function(from, to, T0, T1, count) { - var i=0; - var cx0=0.0, cy0=0.0, d0=0.0, s0=0.0; - var cx1=0.0, cy1=0.0, d1=0.0, s1=0.0; - var dx=0.0,dy=0.0; - - for (; i < count; ++i) { - cx0 += from[i].x; - cy0 += from[i].y; - cx1 += to[i].x; - cy1 += to[i].y; - } - - cx0 /= count; cy0 /= count; - cx1 /= count; cy1 /= count; - - for (i = 0; i < count; ++i) { - dx = from[i].x - cx0; - dy = from[i].y - cy0; - d0 += Math.sqrt(dx*dx + dy*dy); - dx = to[i].x - cx1; - dy = to[i].y - cy1; - d1 += Math.sqrt(dx*dx + dy*dy); - } - - d0 /= count; d1 /= count; - - s0 = Math.SQRT2 / d0; s1 = Math.SQRT2 / d1; - - T0[0] = T0[4] = s0; - T0[2] = -cx0*s0; - T0[5] = -cy0*s0; - T0[1] = T0[3] = T0[6] = T0[7] = 0.0; - T0[8] = 1.0; - - T1[0] = T1[4] = s1; - T1[2] = -cx1*s1; - T1[5] = -cy1*s1; - T1[1] = T1[3] = T1[6] = T1[7] = 0.0; - T1[8] = 1.0; - } - - var have_collinear_points = function(points, count) { - var j=0,k=0,i=(count-1)|0; - var dx1=0.0,dy1=0.0,dx2=0.0,dy2=0.0; - - // check that the i-th selected point does not belong - // to a line connecting some previously selected points - for(; j < i; ++j) { - dx1 = points[j].x - points[i].x; - dy1 = points[j].y - points[i].y; - for(k = 0; k < j; ++k) { - dx2 = points[k].x - points[i].x; - dy2 = points[k].y - points[i].y; - if( Math.abs(dx2*dy1 - dy2*dx1) <= jsfeat.EPSILON*(Math.abs(dx1) + Math.abs(dy1) + Math.abs(dx2) + Math.abs(dy2))) - return true; - } - } - return false; - } - - var T0 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); - var T1 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); - var AtA = new jsfeat.matrix_t(6, 6, jsfeat.F32_t|jsfeat.C1_t); - var AtB = new jsfeat.matrix_t(6, 1, jsfeat.F32_t|jsfeat.C1_t); - - var affine2d = (function () { - - function affine2d() { - // empty constructor - } - - affine2d.prototype.run = function(from, to, model, count) { - var i=0,j=0; - var dt=model.type|jsfeat.C1_t; - var md=model.data, t0d=T0.data, t1d=T1.data; - var pt0,pt1,px=0.0,py=0.0; - - iso_normalize_points(from, to, t0d, t1d, count); - - var a_buff = jsfeat.cache.get_buffer((2*count*6)<<3); - var b_buff = jsfeat.cache.get_buffer((2*count)<<3); - - var a_mt = new jsfeat.matrix_t(6, 2*count, dt, a_buff.data); - var b_mt = new jsfeat.matrix_t(1, 2*count, dt, b_buff.data); - var ad=a_mt.data, bd=b_mt.data; - - for (; i < count; ++i) { - pt0 = from[i]; - pt1 = to[i]; - - px = t0d[0]*pt0.x + t0d[1]*pt0.y + t0d[2]; - py = t0d[3]*pt0.x + t0d[4]*pt0.y + t0d[5]; - - j = i*2*6; - ad[j]=px, ad[j+1]=py, ad[j+2]=1.0, ad[j+3]=0.0, ad[j+4]=0.0, ad[j+5]=0.0; - - j += 6; - ad[j]=0.0, ad[j+1]=0.0, ad[j+2]=0.0, ad[j+3]=px, ad[j+4]=py, ad[j+5]=1.0; - - bd[i<<1] = t1d[0]*pt1.x + t1d[1]*pt1.y + t1d[2]; - bd[(i<<1)+1] = t1d[3]*pt1.x + t1d[4]*pt1.y + t1d[5]; - } - - jsfeat.matmath.multiply_AtA(AtA, a_mt); - jsfeat.matmath.multiply_AtB(AtB, a_mt, b_mt); - - jsfeat.linalg.lu_solve(AtA, AtB); - - md[0] = AtB.data[0], md[1]=AtB.data[1], md[2]=AtB.data[2]; - md[3] = AtB.data[3], md[4]=AtB.data[4], md[5]=AtB.data[5]; - md[6] = 0.0, md[7] = 0.0, md[8] = 1.0; // fill last row - - // denormalize - jsfeat.matmath.invert_3x3(T1, T1); - jsfeat.matmath.multiply_3x3(model, T1, model); - jsfeat.matmath.multiply_3x3(model, model, T0); - - // free buffer - jsfeat.cache.put_buffer(a_buff); - jsfeat.cache.put_buffer(b_buff); - - return 1; - } - - affine2d.prototype.error = function(from, to, model, err, count) { - var i=0; - var pt0,pt1; - var m=model.data; - - for (; i < count; ++i) { - pt0 = from[i]; - pt1 = to[i]; - - err[i] = sqr(pt1.x - m[0]*pt0.x - m[1]*pt0.y - m[2]) + - sqr(pt1.y - m[3]*pt0.x - m[4]*pt0.y - m[5]); - } - } - - affine2d.prototype.check_subset = function(from, to, count) { - return true; // all good - } - - return affine2d; - })(); - - var mLtL = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); - var Evec = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); - - var homography2d = (function () { - - function homography2d() { - // empty constructor - //this.T0 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); - //this.T1 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); - //this.mLtL = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); - //this.Evec = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); - } - - homography2d.prototype.run = function(from, to, model, count) { - var i=0,j=0; - var md=model.data, t0d=T0.data, t1d=T1.data; - var LtL=mLtL.data, evd=Evec.data; - var x=0.0,y=0.0,X=0.0,Y=0.0; - - // norm - var smx=0.0, smy=0.0, cmx=0.0, cmy=0.0, sMx=0.0, sMy=0.0, cMx=0.0, cMy=0.0; - - for(; i < count; ++i) { - cmx += to[i].x; - cmy += to[i].y; - cMx += from[i].x; - cMy += from[i].y; - } - - cmx /= count; cmy /= count; - cMx /= count; cMy /= count; - - for(i = 0; i < count; ++i) - { - smx += Math.abs(to[i].x - cmx); - smy += Math.abs(to[i].y - cmy); - sMx += Math.abs(from[i].x - cMx); - sMy += Math.abs(from[i].y - cMy); - } - - if( Math.abs(smx) < jsfeat.EPSILON - || Math.abs(smy) < jsfeat.EPSILON - || Math.abs(sMx) < jsfeat.EPSILON - || Math.abs(sMy) < jsfeat.EPSILON ) return 0; - - smx = count/smx; smy = count/smy; - sMx = count/sMx; sMy = count/sMy; - - t0d[0] = sMx; t0d[1] = 0; t0d[2] = -cMx*sMx; - t0d[3] = 0; t0d[4] = sMy; t0d[5] = -cMy*sMy; - t0d[6] = 0; t0d[7] = 0; t0d[8] = 1; - - t1d[0] = 1.0/smx; t1d[1] = 0; t1d[2] = cmx; - t1d[3] = 0; t1d[4] = 1.0/smy; t1d[5] = cmy; - t1d[6] = 0; t1d[7] = 0; t1d[8] = 1; - // - - // construct system - i = 81; - while(--i >= 0) { - LtL[i] = 0.0; - } - for(i = 0; i < count; ++i) { - x = (to[i].x - cmx) * smx; - y = (to[i].y - cmy) * smy; - X = (from[i].x - cMx) * sMx; - Y = (from[i].y - cMy) * sMy; - - LtL[0] += X*X; - LtL[1] += X*Y; - LtL[2] += X; - - LtL[6] += X*-x*X; - LtL[7] += X*-x*Y; - LtL[8] += X*-x; - LtL[10] += Y*Y; - LtL[11] += Y; - - LtL[15] += Y*-x*X; - LtL[16] += Y*-x*Y; - LtL[17] += Y*-x; - LtL[20] += 1.0; - - LtL[24] += -x*X; - LtL[25] += -x*Y; - LtL[26] += -x; - LtL[30] += X*X; - LtL[31] += X*Y; - LtL[32] += X; - LtL[33] += X*-y*X; - LtL[34] += X*-y*Y; - LtL[35] += X*-y; - LtL[40] += Y*Y; - LtL[41] += Y; - LtL[42] += Y*-y*X; - LtL[43] += Y*-y*Y; - LtL[44] += Y*-y; - LtL[50] += 1.0; - LtL[51] += -y*X; - LtL[52] += -y*Y; - LtL[53] += -y; - LtL[60] += -x*X*-x*X + -y*X*-y*X; - LtL[61] += -x*X*-x*Y + -y*X*-y*Y; - LtL[62] += -x*X*-x + -y*X*-y; - LtL[70] += -x*Y*-x*Y + -y*Y*-y*Y; - LtL[71] += -x*Y*-x + -y*Y*-y; - LtL[80] += -x*-x + -y*-y; - } - // - - // symmetry - for(i = 0; i < 9; ++i) { - for(j = 0; j < i; ++j) - LtL[i*9+j] = LtL[j*9+i]; - } - - jsfeat.linalg.eigenVV(mLtL, Evec); - - md[0]=evd[72], md[1]=evd[73], md[2]=evd[74]; - md[3]=evd[75], md[4]=evd[76], md[5]=evd[77]; - md[6]=evd[78], md[7]=evd[79], md[8]=evd[80]; - - // denormalize - jsfeat.matmath.multiply_3x3(model, T1, model); - jsfeat.matmath.multiply_3x3(model, model, T0); - - // set bottom right to 1.0 - x = 1.0/md[8]; - md[0] *= x; md[1] *= x; md[2] *= x; - md[3] *= x; md[4] *= x; md[5] *= x; - md[6] *= x; md[7] *= x; md[8] = 1.0; - - return 1; - } - - homography2d.prototype.error = function(from, to, model, err, count) { - var i=0; - var pt0,pt1,ww=0.0,dx=0.0,dy=0.0; - var m=model.data; - - for (; i < count; ++i) { - pt0 = from[i]; - pt1 = to[i]; - - ww = 1.0/(m[6]*pt0.x + m[7]*pt0.y + 1.0); - dx = (m[0]*pt0.x + m[1]*pt0.y + m[2])*ww - pt1.x; - dy = (m[3]*pt0.x + m[4]*pt0.y + m[5])*ww - pt1.y; - err[i] = (dx*dx + dy*dy); - } - } - - homography2d.prototype.check_subset = function(from, to, count) { - // seems to reject good subsets actually - //if( have_collinear_points(from, count) || have_collinear_points(to, count) ) { - //return false; - //} - if( count == 4 ) { - var negative = 0; - - var fp0=from[0],fp1=from[1],fp2=from[2],fp3=from[3]; - var tp0=to[0],tp1=to[1],tp2=to[2],tp3=to[3]; - - // set1 - var A11=fp0.x, A12=fp0.y, A13=1.0; - var A21=fp1.x, A22=fp1.y, A23=1.0; - var A31=fp2.x, A32=fp2.y, A33=1.0; - - var B11=tp0.x, B12=tp0.y, B13=1.0; - var B21=tp1.x, B22=tp1.y, B23=1.0; - var B31=tp2.x, B32=tp2.y, B33=1.0; - - var detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); - var detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); - - if(detA*detB < 0) negative++; - - // set2 - A11=fp1.x, A12=fp1.y; - A21=fp2.x, A22=fp2.y; - A31=fp3.x, A32=fp3.y; - - B11=tp1.x, B12=tp1.y; - B21=tp2.x, B22=tp2.y; - B31=tp3.x, B32=tp3.y; - - detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); - detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); - - if(detA*detB < 0) negative++; - - // set3 - A11=fp0.x, A12=fp0.y; - A21=fp2.x, A22=fp2.y; - A31=fp3.x, A32=fp3.y; - - B11=tp0.x, B12=tp0.y; - B21=tp2.x, B22=tp2.y; - B31=tp3.x, B32=tp3.y; - - detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); - detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); - - if(detA*detB < 0) negative++; - - // set4 - A11=fp0.x, A12=fp0.y; - A21=fp1.x, A22=fp1.y; - A31=fp3.x, A32=fp3.y; - - B11=tp0.x, B12=tp0.y; - B21=tp1.x, B22=tp1.y; - B31=tp3.x, B32=tp3.y; - - detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); - detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); - - if(detA*detB < 0) negative++; - - if(negative != 0 && negative != 4) { - return false; - } - } - return true; // all good - } - - return homography2d; - })(); - - return { - - affine2d:affine2d, - homography2d:homography2d - - }; - - })(); - - var ransac_params_t = (function () { - function ransac_params_t(size, thresh, eps, prob) { - if (typeof size === "undefined") { size=0; } - if (typeof thresh === "undefined") { thresh=0.5; } - if (typeof eps === "undefined") { eps=0.5; } - if (typeof prob === "undefined") { prob=0.99; } - - this.size = size; - this.thresh = thresh; - this.eps = eps; - this.prob = prob; - }; - ransac_params_t.prototype.update_iters = function(_eps, max_iters) { - var num = Math.log(1 - this.prob); - var denom = Math.log(1 - Math.pow(1 - _eps, this.size)); - return (denom >= 0 || -num >= max_iters*(-denom) ? max_iters : Math.round(num/denom))|0; - }; - return ransac_params_t; - })(); - - var motion_estimator = (function() { - - var get_subset = function(kernel, from, to, need_cnt, max_cnt, from_sub, to_sub) { - var max_try = 1000; - var indices = []; - var i=0, j=0, ssiter=0, idx_i=0, ok=false; - for(; ssiter < max_try; ++ssiter) { - i = 0; - for (; i < need_cnt && ssiter < max_try;) { - ok = false; - idx_i = 0; - while (!ok) { - ok = true; - idx_i = indices[i] = Math.floor(Math.random() * max_cnt)|0; - for (j = 0; j < i; ++j) { - if (idx_i == indices[j]) - { ok = false; break; } - } - } - from_sub[i] = from[idx_i]; - to_sub[i] = to[idx_i]; - if( !kernel.check_subset( from_sub, to_sub, i+1 ) ) { - ssiter++; - continue; - } - ++i; - } - break; - } - - return (i == need_cnt && ssiter < max_try); - } - - var find_inliers = function(kernel, model, from, to, count, thresh, err, mask) { - var numinliers = 0, i=0, f=0; - var t = thresh*thresh; - - kernel.error(from, to, model, err, count); - - for(; i < count; ++i) { - f = err[i] <= t; - mask[i] = f; - numinliers += f; - } - return numinliers; - } - - return { - - ransac: function(params, kernel, from, to, count, model, mask, max_iters) { - if (typeof max_iters === "undefined") { max_iters=1000; } - - if(count < params.size) return false; - - var model_points = params.size; - var niters = max_iters, iter=0; - var result = false; - - var subset0 = []; - var subset1 = []; - var found = false; - - var mc=model.cols,mr=model.rows; - var dt = model.type | jsfeat.C1_t; - - var m_buff = jsfeat.cache.get_buffer((mc*mr)<<3); - var ms_buff = jsfeat.cache.get_buffer(count); - var err_buff = jsfeat.cache.get_buffer(count<<2); - var M = new jsfeat.matrix_t(mc, mr, dt, m_buff.data); - var curr_mask = new jsfeat.matrix_t(count, 1, jsfeat.U8C1_t, ms_buff.data); - - var inliers_max = -1, numinliers=0; - var nmodels = 0; - - var err = err_buff.f32; - - // special case - if(count == model_points) { - if(kernel.run(from, to, M, count) <= 0) { - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - return false; - } - - M.copy_to(model); - if(mask) { - while(--count >= 0) { - mask.data[count] = 1; - } - } - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - return true; - } - - for (; iter < niters; ++iter) { - // generate subset - found = get_subset(kernel, from, to, model_points, count, subset0, subset1); - if(!found) { - if(iter == 0) { - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - return false; - } - break; - } - - nmodels = kernel.run( subset0, subset1, M, model_points ); - if(nmodels <= 0) - continue; - - // TODO handle multimodel output - - numinliers = find_inliers(kernel, M, from, to, count, params.thresh, err, curr_mask.data); - - if( numinliers > Math.max(inliers_max, model_points-1) ) { - M.copy_to(model); - inliers_max = numinliers; - if(mask) curr_mask.copy_to(mask); - niters = params.update_iters((count - numinliers)/count, niters); - result = true; - } - } - - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - - return result; - }, - - lmeds: function(params, kernel, from, to, count, model, mask, max_iters) { - if (typeof max_iters === "undefined") { max_iters=1000; } - - if(count < params.size) return false; - - var model_points = params.size; - var niters = max_iters, iter=0; - var result = false; - - var subset0 = []; - var subset1 = []; - var found = false; - - var mc=model.cols,mr=model.rows; - var dt = model.type | jsfeat.C1_t; - - var m_buff = jsfeat.cache.get_buffer((mc*mr)<<3); - var ms_buff = jsfeat.cache.get_buffer(count); - var err_buff = jsfeat.cache.get_buffer(count<<2); - var M = new jsfeat.matrix_t(mc, mr, dt, m_buff.data); - var curr_mask = new jsfeat.matrix_t(count, 1, jsfeat.U8_t|jsfeat.C1_t, ms_buff.data); - - var numinliers=0; - var nmodels = 0; - - var err = err_buff.f32; - var min_median = 1000000000.0, sigma=0.0, median=0.0; - - params.eps = 0.45; - niters = params.update_iters(params.eps, niters); - - // special case - if(count == model_points) { - if(kernel.run(from, to, M, count) <= 0) { - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - return false; - } - - M.copy_to(model); - if(mask) { - while(--count >= 0) { - mask.data[count] = 1; - } - } - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - return true; - } - - for (; iter < niters; ++iter) { - // generate subset - found = get_subset(kernel, from, to, model_points, count, subset0, subset1); - if(!found) { - if(iter == 0) { - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - return false; - } - break; - } - - nmodels = kernel.run( subset0, subset1, M, model_points ); - if(nmodels <= 0) - continue; - - // TODO handle multimodel output - - kernel.error(from, to, M, err, count); - median = jsfeat.math.median(err, 0, count-1); - - if(median < min_median) { - min_median = median; - M.copy_to(model); - result = true; - } - } - - if(result) { - sigma = 2.5*1.4826*(1 + 5.0/(count - model_points))*Math.sqrt(min_median); - sigma = Math.max(sigma, 0.001); - - numinliers = find_inliers(kernel, model, from, to, count, sigma, err, curr_mask.data); - if(mask) curr_mask.copy_to(mask); - - result = numinliers >= model_points; - } - - jsfeat.cache.put_buffer(m_buff); - jsfeat.cache.put_buffer(ms_buff); - jsfeat.cache.put_buffer(err_buff); - - return result; - } - - }; - - })(); - - global.ransac_params_t = ransac_params_t; - global.motion_model = motion_model; - global.motion_estimator = motion_estimator; - -})(jsfeat); -/** - * @author Eugene Zatepyakin / http://inspirit.ru/ - */ - -(function(global) { - "use strict"; - // - - var imgproc = (function() { - - var _resample_u8 = function(src, dst, nw, nh) { - var xofs_count=0; - var ch=src.channel,w=src.cols,h=src.rows; - var src_d=src.data,dst_d=dst.data; - var scale_x = w / nw, scale_y = h / nh; - var inv_scale_256 = (scale_x * scale_y * 0x10000)|0; - var dx=0,dy=0,sx=0,sy=0,sx1=0,sx2=0,i=0,k=0,fsx1=0.0,fsx2=0.0; - var a=0,b=0,dxn=0,alpha=0,beta=0,beta1=0; - - var buf_node = jsfeat.cache.get_buffer((nw*ch)<<2); - var sum_node = jsfeat.cache.get_buffer((nw*ch)<<2); - var xofs_node = jsfeat.cache.get_buffer((w*2*3)<<2); - - var buf = buf_node.i32; - var sum = sum_node.i32; - var xofs = xofs_node.i32; - - for (; dx < nw; dx++) { - fsx1 = dx * scale_x, fsx2 = fsx1 + scale_x; - sx1 = (fsx1 + 1.0 - 1e-6)|0, sx2 = fsx2|0; - sx1 = Math.min(sx1, w - 1); - sx2 = Math.min(sx2, w - 1); - - if(sx1 > fsx1) { - xofs[k++] = (dx * ch)|0; - xofs[k++] = ((sx1 - 1)*ch)|0; - xofs[k++] = ((sx1 - fsx1) * 0x100)|0; - xofs_count++; - } - for(sx = sx1; sx < sx2; sx++){ - xofs_count++; - xofs[k++] = (dx * ch)|0; - xofs[k++] = (sx * ch)|0; - xofs[k++] = 256; - } - if(fsx2 - sx2 > 1e-3) { - xofs_count++; - xofs[k++] = (dx * ch)|0; - xofs[k++] = (sx2 * ch)|0; - xofs[k++] = ((fsx2 - sx2) * 256)|0; - } - } - - for (dx = 0; dx < nw * ch; dx++) { - buf[dx] = sum[dx] = 0; - } - dy = 0; - for (sy = 0; sy < h; sy++) { - a = w * sy; - for (k = 0; k < xofs_count; k++) { - dxn = xofs[k*3]; - sx1 = xofs[k*3+1]; - alpha = xofs[k*3+2]; - for (i = 0; i < ch; i++) { - buf[dxn + i] += src_d[a+sx1+i] * alpha; - } - } - if ((dy + 1) * scale_y <= sy + 1 || sy == h - 1) { - beta = (Math.max(sy + 1 - (dy + 1) * scale_y, 0.0) * 256)|0; - beta1 = 256 - beta; - b = nw * dy; - if (beta <= 0) { - for (dx = 0; dx < nw * ch; dx++) { - dst_d[b+dx] = Math.min(Math.max((sum[dx] + buf[dx] * 256) / inv_scale_256, 0), 255); - sum[dx] = buf[dx] = 0; - } - } else { - for (dx = 0; dx < nw * ch; dx++) { - dst_d[b+dx] = Math.min(Math.max((sum[dx] + buf[dx] * beta1) / inv_scale_256, 0), 255); - sum[dx] = buf[dx] * beta; - buf[dx] = 0; - } - } - dy++; - } else { - for(dx = 0; dx < nw * ch; dx++) { - sum[dx] += buf[dx] * 256; - buf[dx] = 0; - } - } - } - - jsfeat.cache.put_buffer(sum_node); - jsfeat.cache.put_buffer(buf_node); - jsfeat.cache.put_buffer(xofs_node); - } - - var _resample = function(src, dst, nw, nh) { - var xofs_count=0; - var ch=src.channel,w=src.cols,h=src.rows; - var src_d=src.data,dst_d=dst.data; - var scale_x = w / nw, scale_y = h / nh; - var scale = 1.0 / (scale_x * scale_y); - var dx=0,dy=0,sx=0,sy=0,sx1=0,sx2=0,i=0,k=0,fsx1=0.0,fsx2=0.0; - var a=0,b=0,dxn=0,alpha=0.0,beta=0.0,beta1=0.0; - - var buf_node = jsfeat.cache.get_buffer((nw*ch)<<2); - var sum_node = jsfeat.cache.get_buffer((nw*ch)<<2); - var xofs_node = jsfeat.cache.get_buffer((w*2*3)<<2); - - var buf = buf_node.f32; - var sum = sum_node.f32; - var xofs = xofs_node.f32; - - for (; dx < nw; dx++) { - fsx1 = dx * scale_x, fsx2 = fsx1 + scale_x; - sx1 = (fsx1 + 1.0 - 1e-6)|0, sx2 = fsx2|0; - sx1 = Math.min(sx1, w - 1); - sx2 = Math.min(sx2, w - 1); - - if(sx1 > fsx1) { - xofs_count++; - xofs[k++] = ((sx1 - 1)*ch)|0; - xofs[k++] = (dx * ch)|0; - xofs[k++] = (sx1 - fsx1) * scale; - } - for(sx = sx1; sx < sx2; sx++){ - xofs_count++; - xofs[k++] = (sx * ch)|0; - xofs[k++] = (dx * ch)|0; - xofs[k++] = scale; - } - if(fsx2 - sx2 > 1e-3) { - xofs_count++; - xofs[k++] = (sx2 * ch)|0; - xofs[k++] = (dx * ch)|0; - xofs[k++] = (fsx2 - sx2) * scale; - } - } - - for (dx = 0; dx < nw * ch; dx++) { - buf[dx] = sum[dx] = 0; - } - dy = 0; - for (sy = 0; sy < h; sy++) { - a = w * sy; - for (k = 0; k < xofs_count; k++) { - sx1 = xofs[k*3]|0; - dxn = xofs[k*3+1]|0; - alpha = xofs[k*3+2]; - for (i = 0; i < ch; i++) { - buf[dxn + i] += src_d[a+sx1+i] * alpha; - } - } - if ((dy + 1) * scale_y <= sy + 1 || sy == h - 1) { - beta = Math.max(sy + 1 - (dy + 1) * scale_y, 0.0); - beta1 = 1.0 - beta; - b = nw * dy; - if (Math.abs(beta) < 1e-3) { - for (dx = 0; dx < nw * ch; dx++) { - dst_d[b+dx] = sum[dx] + buf[dx]; - sum[dx] = buf[dx] = 0; - } - } else { - for (dx = 0; dx < nw * ch; dx++) { - dst_d[b+dx] = sum[dx] + buf[dx] * beta1; - sum[dx] = buf[dx] * beta; - buf[dx] = 0; - } - } - dy++; - } else { - for(dx = 0; dx < nw * ch; dx++) { - sum[dx] += buf[dx]; - buf[dx] = 0; - } - } - } - jsfeat.cache.put_buffer(sum_node); - jsfeat.cache.put_buffer(buf_node); - jsfeat.cache.put_buffer(xofs_node); - } - - var _convol_u8 = function(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel) { - var i=0,j=0,k=0,sp=0,dp=0,sum=0,sum1=0,sum2=0,sum3=0,f0=filter[0],fk=0; - var w2=w<<1,w3=w*3,w4=w<<2; - // hor pass - for (; i < h; ++i) { - sum = src_d[sp]; - for (j = 0; j < half_kernel; ++j) { - buf[j] = sum; - } - for (j = 0; j <= w-2; j+=2) { - buf[j + half_kernel] = src_d[sp+j]; - buf[j + half_kernel+1] = src_d[sp+j+1]; - } - for (; j < w; ++j) { - buf[j + half_kernel] = src_d[sp+j]; - } - sum = src_d[sp+w-1]; - for (j = w; j < half_kernel + w; ++j) { - buf[j + half_kernel] = sum; - } - for (j = 0; j <= w-4; j+=4) { - sum = buf[j] * f0, - sum1 = buf[j+1] * f0, - sum2 = buf[j+2] * f0, - sum3 = buf[j+3] * f0; - for (k = 1; k < kernel_size; ++k) { - fk = filter[k]; - sum += buf[k + j] * fk; - sum1 += buf[k + j+1] * fk; - sum2 += buf[k + j+2] * fk; - sum3 += buf[k + j+3] * fk; - } - dst_d[dp+j] = Math.min(sum >> 8, 255); - dst_d[dp+j+1] = Math.min(sum1 >> 8, 255); - dst_d[dp+j+2] = Math.min(sum2 >> 8, 255); - dst_d[dp+j+3] = Math.min(sum3 >> 8, 255); - } - for (; j < w; ++j) { - sum = buf[j] * f0; - for (k = 1; k < kernel_size; ++k) { - sum += buf[k + j] * filter[k]; - } - dst_d[dp+j] = Math.min(sum >> 8, 255); - } - sp += w; - dp += w; - } - - // vert pass - for (i = 0; i < w; ++i) { - sum = dst_d[i]; - for (j = 0; j < half_kernel; ++j) { - buf[j] = sum; - } - k = i; - for (j = 0; j <= h-2; j+=2, k+=w2) { - buf[j+half_kernel] = dst_d[k]; - buf[j+half_kernel+1] = dst_d[k+w]; - } - for (; j < h; ++j, k+=w) { - buf[j+half_kernel] = dst_d[k]; - } - sum = dst_d[(h-1)*w + i]; - for (j = h; j < half_kernel + h; ++j) { - buf[j + half_kernel] = sum; - } - dp = i; - for (j = 0; j <= h-4; j+=4, dp+=w4) { - sum = buf[j] * f0, - sum1 = buf[j+1] * f0, - sum2 = buf[j+2] * f0, - sum3 = buf[j+3] * f0; - for (k = 1; k < kernel_size; ++k) { - fk = filter[k]; - sum += buf[k + j] * fk; - sum1 += buf[k + j+1] * fk; - sum2 += buf[k + j+2] * fk; - sum3 += buf[k + j+3] * fk; - } - dst_d[dp] = Math.min(sum >> 8, 255); - dst_d[dp+w] = Math.min(sum1 >> 8, 255); - dst_d[dp+w2] = Math.min(sum2 >> 8, 255); - dst_d[dp+w3] = Math.min(sum3 >> 8, 255); - } - for (; j < h; ++j, dp+=w) { - sum = buf[j] * f0; - for (k = 1; k < kernel_size; ++k) { - sum += buf[k + j] * filter[k]; - } - dst_d[dp] = Math.min(sum >> 8, 255); - } - } - } - - var _convol = function(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel) { - var i=0,j=0,k=0,sp=0,dp=0,sum=0.0,sum1=0.0,sum2=0.0,sum3=0.0,f0=filter[0],fk=0.0; - var w2=w<<1,w3=w*3,w4=w<<2; - // hor pass - for (; i < h; ++i) { - sum = src_d[sp]; - for (j = 0; j < half_kernel; ++j) { - buf[j] = sum; - } - for (j = 0; j <= w-2; j+=2) { - buf[j + half_kernel] = src_d[sp+j]; - buf[j + half_kernel+1] = src_d[sp+j+1]; - } - for (; j < w; ++j) { - buf[j + half_kernel] = src_d[sp+j]; - } - sum = src_d[sp+w-1]; - for (j = w; j < half_kernel + w; ++j) { - buf[j + half_kernel] = sum; - } - for (j = 0; j <= w-4; j+=4) { - sum = buf[j] * f0, - sum1 = buf[j+1] * f0, - sum2 = buf[j+2] * f0, - sum3 = buf[j+3] * f0; - for (k = 1; k < kernel_size; ++k) { - fk = filter[k]; - sum += buf[k + j] * fk; - sum1 += buf[k + j+1] * fk; - sum2 += buf[k + j+2] * fk; - sum3 += buf[k + j+3] * fk; - } - dst_d[dp+j] = sum; - dst_d[dp+j+1] = sum1; - dst_d[dp+j+2] = sum2; - dst_d[dp+j+3] = sum3; - } - for (; j < w; ++j) { - sum = buf[j] * f0; - for (k = 1; k < kernel_size; ++k) { - sum += buf[k + j] * filter[k]; - } - dst_d[dp+j] = sum; - } - sp += w; - dp += w; - } - - // vert pass - for (i = 0; i < w; ++i) { - sum = dst_d[i]; - for (j = 0; j < half_kernel; ++j) { - buf[j] = sum; - } - k = i; - for (j = 0; j <= h-2; j+=2, k+=w2) { - buf[j+half_kernel] = dst_d[k]; - buf[j+half_kernel+1] = dst_d[k+w]; - } - for (; j < h; ++j, k+=w) { - buf[j+half_kernel] = dst_d[k]; - } - sum = dst_d[(h-1)*w + i]; - for (j = h; j < half_kernel + h; ++j) { - buf[j + half_kernel] = sum; - } - dp = i; - for (j = 0; j <= h-4; j+=4, dp+=w4) { - sum = buf[j] * f0, - sum1 = buf[j+1] * f0, - sum2 = buf[j+2] * f0, - sum3 = buf[j+3] * f0; - for (k = 1; k < kernel_size; ++k) { - fk = filter[k]; - sum += buf[k + j] * fk; - sum1 += buf[k + j+1] * fk; - sum2 += buf[k + j+2] * fk; - sum3 += buf[k + j+3] * fk; - } - dst_d[dp] = sum; - dst_d[dp+w] = sum1; - dst_d[dp+w2] = sum2; - dst_d[dp+w3] = sum3; - } - for (; j < h; ++j, dp+=w) { - sum = buf[j] * f0; - for (k = 1; k < kernel_size; ++k) { - sum += buf[k + j] * filter[k]; - } - dst_d[dp] = sum; - } - } - } - - return { - // TODO: add support for RGB/BGR order - // for raw arrays - grayscale: function(src, w, h, dst, code) { - // this is default image data representation in browser - if (typeof code === "undefined") { code = jsfeat.COLOR_RGBA2GRAY; } - var x=0, y=0, i=0, j=0, ir=0,jr=0; - var coeff_r = 4899, coeff_g = 9617, coeff_b = 1868, cn = 4; - - if(code == jsfeat.COLOR_BGRA2GRAY || code == jsfeat.COLOR_BGR2GRAY) { - coeff_r = 1868; - coeff_b = 4899; - } - if(code == jsfeat.COLOR_RGB2GRAY || code == jsfeat.COLOR_BGR2GRAY) { - cn = 3; - } - var cn2 = cn<<1, cn3 = (cn*3)|0; - - dst.resize(w, h, 1); - var dst_u8 = dst.data; - - for(y = 0; y < h; ++y, j+=w, i+=w*cn) { - for(x = 0, ir = i, jr = j; x <= w-4; x+=4, ir+=cn<<2, jr+=4) { - dst_u8[jr] = (src[ir] * coeff_r + src[ir+1] * coeff_g + src[ir+2] * coeff_b + 8192) >> 14; - dst_u8[jr + 1] = (src[ir+cn] * coeff_r + src[ir+cn+1] * coeff_g + src[ir+cn+2] * coeff_b + 8192) >> 14; - dst_u8[jr + 2] = (src[ir+cn2] * coeff_r + src[ir+cn2+1] * coeff_g + src[ir+cn2+2] * coeff_b + 8192) >> 14; - dst_u8[jr + 3] = (src[ir+cn3] * coeff_r + src[ir+cn3+1] * coeff_g + src[ir+cn3+2] * coeff_b + 8192) >> 14; - } - for (; x < w; ++x, ++jr, ir+=cn) { - dst_u8[jr] = (src[ir] * coeff_r + src[ir+1] * coeff_g + src[ir+2] * coeff_b + 8192) >> 14; - } - } - }, - // derived from CCV library - resample: function(src, dst, nw, nh) { - var h=src.rows,w=src.cols; - if (h > nh && w > nw) { - dst.resize(nw, nh, src.channel); - // using the fast alternative (fix point scale, 0x100 to avoid overflow) - if (src.type&jsfeat.U8_t && dst.type&jsfeat.U8_t && h * w / (nh * nw) < 0x100) { - _resample_u8(src, dst, nw, nh); - } else { - _resample(src, dst, nw, nh); - } - } - }, - - box_blur_gray: function(src, dst, radius, options) { - if (typeof options === "undefined") { options = 0; } - var w=src.cols, h=src.rows, h2=h<<1, w2=w<<1; - var i=0,x=0,y=0,end=0; - var windowSize = ((radius << 1) + 1)|0; - var radiusPlusOne = (radius + 1)|0, radiusPlus2 = (radiusPlusOne+1)|0; - var scale = options&jsfeat.BOX_BLUR_NOSCALE ? 1 : (1.0 / (windowSize*windowSize)); - - var tmp_buff = jsfeat.cache.get_buffer((w*h)<<2); - - var sum=0, dstIndex=0, srcIndex = 0, nextPixelIndex=0, previousPixelIndex=0; - var data_i32 = tmp_buff.i32; // to prevent overflow - var data_u8 = src.data; - var hold=0; - - dst.resize(w, h, src.channel); - - // first pass - // no need to scale - //data_u8 = src.data; - //data_i32 = tmp; - for (y = 0; y < h; ++y) { - dstIndex = y; - sum = radiusPlusOne * data_u8[srcIndex]; - - for(i = (srcIndex+1)|0, end=(srcIndex+radius)|0; i <= end; ++i) { - sum += data_u8[i]; - } - - nextPixelIndex = (srcIndex + radiusPlusOne)|0; - previousPixelIndex = srcIndex; - hold = data_u8[previousPixelIndex]; - for(x = 0; x < radius; ++x, dstIndex += h) { - data_i32[dstIndex] = sum; - sum += data_u8[nextPixelIndex]- hold; - nextPixelIndex ++; - } - for(; x < w-radiusPlus2; x+=2, dstIndex += h2) { - data_i32[dstIndex] = sum; - sum += data_u8[nextPixelIndex]- data_u8[previousPixelIndex]; - - data_i32[dstIndex+h] = sum; - sum += data_u8[nextPixelIndex+1]- data_u8[previousPixelIndex+1]; - - nextPixelIndex +=2; - previousPixelIndex +=2; - } - for(; x < w-radiusPlusOne; ++x, dstIndex += h) { - data_i32[dstIndex] = sum; - sum += data_u8[nextPixelIndex]- data_u8[previousPixelIndex]; - - nextPixelIndex ++; - previousPixelIndex ++; - } - - hold = data_u8[nextPixelIndex-1]; - for(; x < w; ++x, dstIndex += h) { - data_i32[dstIndex] = sum; - - sum += hold- data_u8[previousPixelIndex]; - previousPixelIndex ++; - } - - srcIndex += w; - } - // - // second pass - srcIndex = 0; - //data_i32 = tmp; // this is a transpose - data_u8 = dst.data; - - // dont scale result - if(scale == 1) { - for (y = 0; y < w; ++y) { - dstIndex = y; - sum = radiusPlusOne * data_i32[srcIndex]; - - for(i = (srcIndex+1)|0, end=(srcIndex+radius)|0; i <= end; ++i) { - sum += data_i32[i]; - } - - nextPixelIndex = srcIndex + radiusPlusOne; - previousPixelIndex = srcIndex; - hold = data_i32[previousPixelIndex]; - - for(x = 0; x < radius; ++x, dstIndex += w) { - data_u8[dstIndex] = sum; - sum += data_i32[nextPixelIndex]- hold; - nextPixelIndex ++; - } - for(; x < h-radiusPlus2; x+=2, dstIndex += w2) { - data_u8[dstIndex] = sum; - sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; - - data_u8[dstIndex+w] = sum; - sum += data_i32[nextPixelIndex+1]- data_i32[previousPixelIndex+1]; - - nextPixelIndex +=2; - previousPixelIndex +=2; - } - for(; x < h-radiusPlusOne; ++x, dstIndex += w) { - data_u8[dstIndex] = sum; - - sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; - nextPixelIndex ++; - previousPixelIndex ++; - } - hold = data_i32[nextPixelIndex-1]; - for(; x < h; ++x, dstIndex += w) { - data_u8[dstIndex] = sum; - - sum += hold- data_i32[previousPixelIndex]; - previousPixelIndex ++; - } - - srcIndex += h; - } - } else { - for (y = 0; y < w; ++y) { - dstIndex = y; - sum = radiusPlusOne * data_i32[srcIndex]; - - for(i = (srcIndex+1)|0, end=(srcIndex+radius)|0; i <= end; ++i) { - sum += data_i32[i]; - } - - nextPixelIndex = srcIndex + radiusPlusOne; - previousPixelIndex = srcIndex; - hold = data_i32[previousPixelIndex]; - - for(x = 0; x < radius; ++x, dstIndex += w) { - data_u8[dstIndex] = sum*scale; - sum += data_i32[nextPixelIndex]- hold; - nextPixelIndex ++; - } - for(; x < h-radiusPlus2; x+=2, dstIndex += w2) { - data_u8[dstIndex] = sum*scale; - sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; - - data_u8[dstIndex+w] = sum*scale; - sum += data_i32[nextPixelIndex+1]- data_i32[previousPixelIndex+1]; - - nextPixelIndex +=2; - previousPixelIndex +=2; - } - for(; x < h-radiusPlusOne; ++x, dstIndex += w) { - data_u8[dstIndex] = sum*scale; - - sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; - nextPixelIndex ++; - previousPixelIndex ++; - } - hold = data_i32[nextPixelIndex-1]; - for(; x < h; ++x, dstIndex += w) { - data_u8[dstIndex] = sum*scale; - - sum += hold- data_i32[previousPixelIndex]; - previousPixelIndex ++; - } - - srcIndex += h; - } - } - - jsfeat.cache.put_buffer(tmp_buff); - }, - - gaussian_blur: function(src, dst, kernel_size, sigma) { - if (typeof sigma === "undefined") { sigma = 0.0; } - if (typeof kernel_size === "undefined") { kernel_size = 0; } - kernel_size = kernel_size == 0 ? (Math.max(1, (4.0 * sigma + 1.0 - 1e-8)) * 2 + 1)|0 : kernel_size; - var half_kernel = kernel_size >> 1; - var w = src.cols, h = src.rows; - var data_type = src.type, is_u8 = data_type&jsfeat.U8_t; - - dst.resize(w, h, src.channel); - - var src_d = src.data, dst_d = dst.data; - var buf,filter,buf_sz=(kernel_size + Math.max(h, w))|0; - - var buf_node = jsfeat.cache.get_buffer(buf_sz<<2); - var filt_node = jsfeat.cache.get_buffer(kernel_size<<2); - - if(is_u8) { - buf = buf_node.i32; - filter = filt_node.i32; - } else if(data_type&jsfeat.S32_t) { - buf = buf_node.i32; - filter = filt_node.f32; - } else { - buf = buf_node.f32; - filter = filt_node.f32; - } - - jsfeat.math.get_gaussian_kernel(kernel_size, sigma, filter, data_type); - - if(is_u8) { - _convol_u8(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel); - } else { - _convol(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel); - } - - jsfeat.cache.put_buffer(buf_node); - jsfeat.cache.put_buffer(filt_node); - }, - hough_transform: function( img, rho_res, theta_res, threshold ) { - var image = img.data; - - var width = img.cols; - var height = img.rows; - var step = width; - - min_theta = 0.0; - max_theta = Math.PI; - - numangle = Math.round((max_theta - min_theta) / theta_res); - numrho = Math.round(((width + height) * 2 + 1) / rho_res); - irho = 1.0 / rho_res; - - var accum = new Int32Array((numangle+2) * (numrho+2)); //typed arrays are initialized to 0 - var tabSin = new Float32Array(numangle); - var tabCos = new Float32Array(numangle); - - var n=0; - var ang = min_theta; - for(; n < numangle; n++ ) { - tabSin[n] = Math.sin(ang) * irho; - tabCos[n] = Math.cos(ang) * irho; - ang += theta_res - } - - // stage 1. fill accumulator - for( var i = 0; i < height; i++ ) { - for( var j = 0; j < width; j++ ) { - if( image[i * step + j] != 0 ) { - //console.log(r, (n+1) * (numrho+2) + r+1, tabCos[n], tabSin[n]); - for(var n = 0; n < numangle; n++ ) { - var r = Math.round( j * tabCos[n] + i * tabSin[n] ); - r += (numrho - 1) / 2; - accum[(n+1) * (numrho+2) + r+1] += 1; - } - } - } - } - - // stage 2. find local maximums - //TODO: Consider making a vector class that uses typed arrays - _sort_buf = new Array(); - for(var r = 0; r < numrho; r++ ) { - for(var n = 0; n < numangle; n++ ) { - var base = (n+1) * (numrho+2) + r+1; - if( accum[base] > threshold && - accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] && - accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] ) { - _sort_buf.push(base); - } - } - } - - // stage 3. sort the detected lines by accumulator value - _sort_buf.sort(function(l1, l2) { - return accum[l1] > accum[l2] || (accum[l1] == accum[l2] && l1 < l2); - }); - - // stage 4. store the first min(total,linesMax) lines to the output buffer - linesMax = Math.min(numangle*numrho, _sort_buf.length); - scale = 1.0 / (numrho+2); - lines = new Array(); - for( var i = 0; i < linesMax; i++ ) { - var idx = _sort_buf[i]; - var n = Math.floor(idx*scale) - 1; - var r = idx - (n+1)*(numrho+2) - 1; - var lrho = (r - (numrho - 1)*0.5) * rho_res; - var langle = n * theta_res; - lines.push([lrho, langle]); - } - return lines; - }, - // assume we always need it for u8 image - pyrdown: function(src, dst, sx, sy) { - // this is needed for bbf - if (typeof sx === "undefined") { sx = 0; } - if (typeof sy === "undefined") { sy = 0; } - - var w = src.cols, h = src.rows; - var w2 = w >> 1, h2 = h >> 1; - var _w2 = w2 - (sx << 1), _h2 = h2 - (sy << 1); - var x=0,y=0,sptr=sx+sy*w,sline=0,dptr=0,dline=0; - - dst.resize(w2, h2, src.channel); - - var src_d = src.data, dst_d = dst.data; - - for(y = 0; y < _h2; ++y) { - sline = sptr; - dline = dptr; - for(x = 0; x <= _w2-2; x+=2, dline+=2, sline += 4) { - dst_d[dline] = (src_d[sline] + src_d[sline+1] + - src_d[sline+w] + src_d[sline+w+1] + 2) >> 2; - dst_d[dline+1] = (src_d[sline+2] + src_d[sline+3] + - src_d[sline+w+2] + src_d[sline+w+3] + 2) >> 2; - } - for(; x < _w2; ++x, ++dline, sline += 2) { - dst_d[dline] = (src_d[sline] + src_d[sline+1] + - src_d[sline+w] + src_d[sline+w+1] + 2) >> 2; - } - sptr += w << 1; - dptr += w2; - } - }, - - // dst: [gx,gy,...] - scharr_derivatives: function(src, dst) { - var w = src.cols, h = src.rows; - var dstep = w<<1,x=0,y=0,x1=0,a,b,c,d,e,f; - var srow0=0,srow1=0,srow2=0,drow=0; - var trow0,trow1; - - dst.resize(w, h, 2); // 2 channel output gx, gy - - var img = src.data, gxgy=dst.data; - - var buf0_node = jsfeat.cache.get_buffer((w+2)<<2); - var buf1_node = jsfeat.cache.get_buffer((w+2)<<2); - - if(src.type&jsfeat.U8_t || src.type&jsfeat.S32_t) { - trow0 = buf0_node.i32; - trow1 = buf1_node.i32; - } else { - trow0 = buf0_node.f32; - trow1 = buf1_node.f32; - } - - for(; y < h; ++y, srow1+=w) { - srow0 = ((y > 0 ? y-1 : 1)*w)|0; - srow2 = ((y < h-1 ? y+1 : h-2)*w)|0; - drow = (y*dstep)|0; - // do vertical convolution - for(x = 0, x1 = 1; x <= w-2; x+=2, x1+=2) { - a = img[srow0+x], b = img[srow2+x]; - trow0[x1] = ( (a + b)*3 + (img[srow1+x])*10 ); - trow1[x1] = ( b - a ); - // - a = img[srow0+x+1], b = img[srow2+x+1]; - trow0[x1+1] = ( (a + b)*3 + (img[srow1+x+1])*10 ); - trow1[x1+1] = ( b - a ); - } - for(; x < w; ++x, ++x1) { - a = img[srow0+x], b = img[srow2+x]; - trow0[x1] = ( (a + b)*3 + (img[srow1+x])*10 ); - trow1[x1] = ( b - a ); - } - // make border - x = (w + 1)|0; - trow0[0] = trow0[1]; trow0[x] = trow0[w]; - trow1[0] = trow1[1]; trow1[x] = trow1[w]; - // do horizontal convolution, interleave the results and store them - for(x = 0; x <= w-4; x+=4) { - a = trow1[x+2], b = trow1[x+1], c = trow1[x+3], d = trow1[x+4], - e = trow0[x+2], f = trow0[x+3]; - gxgy[drow++] = ( e - trow0[x] ); - gxgy[drow++] = ( (a + trow1[x])*3 + b*10 ); - gxgy[drow++] = ( f - trow0[x+1] ); - gxgy[drow++] = ( (c + b)*3 + a*10 ); - - gxgy[drow++] = ( (trow0[x+4] - e) ); - gxgy[drow++] = ( ((d + a)*3 + c*10) ); - gxgy[drow++] = ( (trow0[x+5] - f) ); - gxgy[drow++] = ( ((trow1[x+5] + c)*3 + d*10) ); - } - for(; x < w; ++x) { - gxgy[drow++] = ( (trow0[x+2] - trow0[x]) ); - gxgy[drow++] = ( ((trow1[x+2] + trow1[x])*3 + trow1[x+1]*10) ); - } - } - jsfeat.cache.put_buffer(buf0_node); - jsfeat.cache.put_buffer(buf1_node); - }, - - // compute gradient using Sobel kernel [1 2 1] * [-1 0 1]^T - // dst: [gx,gy,...] - sobel_derivatives: function(src, dst) { - var w = src.cols, h = src.rows; - var dstep = w<<1,x=0,y=0,x1=0,a,b,c,d,e,f; - var srow0=0,srow1=0,srow2=0,drow=0; - var trow0,trow1; - - dst.resize(w, h, 2); // 2 channel output gx, gy - - var img = src.data, gxgy=dst.data; - - var buf0_node = jsfeat.cache.get_buffer((w+2)<<2); - var buf1_node = jsfeat.cache.get_buffer((w+2)<<2); - - if(src.type&jsfeat.U8_t || src.type&jsfeat.S32_t) { - trow0 = buf0_node.i32; - trow1 = buf1_node.i32; - } else { - trow0 = buf0_node.f32; - trow1 = buf1_node.f32; - } - - for(; y < h; ++y, srow1+=w) { - srow0 = ((y > 0 ? y-1 : 1)*w)|0; - srow2 = ((y < h-1 ? y+1 : h-2)*w)|0; - drow = (y*dstep)|0; - // do vertical convolution - for(x = 0, x1 = 1; x <= w-2; x+=2, x1+=2) { - a = img[srow0+x], b = img[srow2+x]; - trow0[x1] = ( (a + b) + (img[srow1+x]*2) ); - trow1[x1] = ( b - a ); - // - a = img[srow0+x+1], b = img[srow2+x+1]; - trow0[x1+1] = ( (a + b) + (img[srow1+x+1]*2) ); - trow1[x1+1] = ( b - a ); - } - for(; x < w; ++x, ++x1) { - a = img[srow0+x], b = img[srow2+x]; - trow0[x1] = ( (a + b) + (img[srow1+x]*2) ); - trow1[x1] = ( b - a ); - } - // make border - x = (w + 1)|0; - trow0[0] = trow0[1]; trow0[x] = trow0[w]; - trow1[0] = trow1[1]; trow1[x] = trow1[w]; - // do horizontal convolution, interleave the results and store them - for(x = 0; x <= w-4; x+=4) { - a = trow1[x+2], b = trow1[x+1], c = trow1[x+3], d = trow1[x+4], - e = trow0[x+2], f = trow0[x+3]; - gxgy[drow++] = ( e - trow0[x] ); - gxgy[drow++] = ( a + trow1[x] + b*2 ); - gxgy[drow++] = ( f - trow0[x+1] ); - gxgy[drow++] = ( c + b + a*2 ); - - gxgy[drow++] = ( trow0[x+4] - e ); - gxgy[drow++] = ( d + a + c*2 ); - gxgy[drow++] = ( trow0[x+5] - f ); - gxgy[drow++] = ( trow1[x+5] + c + d*2 ); - } - for(; x < w; ++x) { - gxgy[drow++] = ( trow0[x+2] - trow0[x] ); - gxgy[drow++] = ( trow1[x+2] + trow1[x] + trow1[x+1]*2 ); - } - } - jsfeat.cache.put_buffer(buf0_node); - jsfeat.cache.put_buffer(buf1_node); - }, - - // please note: - // dst_(type) size should be cols = src.cols+1, rows = src.rows+1 - compute_integral_image: function(src, dst_sum, dst_sqsum, dst_tilted) { - var w0=src.cols|0,h0=src.rows|0,src_d=src.data; - var w1=(w0+1)|0; - var s=0,s2=0,p=0,pup=0,i=0,j=0,v=0,k=0; - - if(dst_sum && dst_sqsum) { - // fill first row with zeros - for(; i < w1; ++i) { - dst_sum[i] = 0, dst_sqsum[i] = 0; - } - p = (w1+1)|0, pup = 1; - for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { - s = s2 = 0; - for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { - v = src_d[k]; - s += v, s2 += v*v; - dst_sum[p] = dst_sum[pup] + s; - dst_sqsum[p] = dst_sqsum[pup] + s2; - - v = src_d[k+1]; - s += v, s2 += v*v; - dst_sum[p+1] = dst_sum[pup+1] + s; - dst_sqsum[p+1] = dst_sqsum[pup+1] + s2; - } - for(; j < w0; ++j, ++k, ++p, ++pup) { - v = src_d[k]; - s += v, s2 += v*v; - dst_sum[p] = dst_sum[pup] + s; - dst_sqsum[p] = dst_sqsum[pup] + s2; - } - } - } else if(dst_sum) { - // fill first row with zeros - for(; i < w1; ++i) { - dst_sum[i] = 0; - } - p = (w1+1)|0, pup = 1; - for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { - s = 0; - for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { - s += src_d[k]; - dst_sum[p] = dst_sum[pup] + s; - s += src_d[k+1]; - dst_sum[p+1] = dst_sum[pup+1] + s; - } - for(; j < w0; ++j, ++k, ++p, ++pup) { - s += src_d[k]; - dst_sum[p] = dst_sum[pup] + s; - } - } - } else if(dst_sqsum) { - // fill first row with zeros - for(; i < w1; ++i) { - dst_sqsum[i] = 0; - } - p = (w1+1)|0, pup = 1; - for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { - s2 = 0; - for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { - v = src_d[k]; - s2 += v*v; - dst_sqsum[p] = dst_sqsum[pup] + s2; - v = src_d[k+1]; - s2 += v*v; - dst_sqsum[p+1] = dst_sqsum[pup+1] + s2; - } - for(; j < w0; ++j, ++k, ++p, ++pup) { - v = src_d[k]; - s2 += v*v; - dst_sqsum[p] = dst_sqsum[pup] + s2; - } - } - } - - if(dst_tilted) { - // fill first row with zeros - for(i = 0; i < w1; ++i) { - dst_tilted[i] = 0; - } - // diagonal - p = (w1+1)|0, pup = 0; - for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { - for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { - dst_tilted[p] = src_d[k] + dst_tilted[pup]; - dst_tilted[p+1] = src_d[k+1] + dst_tilted[pup+1]; - } - for(; j < w0; ++j, ++k, ++p, ++pup) { - dst_tilted[p] = src_d[k] + dst_tilted[pup]; - } - } - // diagonal - p = (w1+w0)|0, pup = w0; - for(i = 0; i < h0; ++i, p+=w1, pup+=w1) { - dst_tilted[p] += dst_tilted[pup]; - } - - for(j = w0-1; j > 0; --j) { - p = j+h0*w1, pup=p-w1; - for(i = h0; i > 0; --i, p-=w1, pup-=w1) { - dst_tilted[p] += dst_tilted[pup] + dst_tilted[pup+1]; - } - } - } - }, - equalize_histogram: function(src, dst) { - var w=src.cols,h=src.rows,src_d=src.data; - - dst.resize(w, h, src.channel); - - var dst_d=dst.data,size=w*h; - var i=0,prev=0,hist0,norm; - - var hist0_node = jsfeat.cache.get_buffer(256<<2); - hist0 = hist0_node.i32; - for(; i < 256; ++i) hist0[i] = 0; - for (i = 0; i < size; ++i) { - ++hist0[src_d[i]]; - } - - prev = hist0[0]; - for (i = 1; i < 256; ++i) { - prev = hist0[i] += prev; - } - - norm = 255 / size; - for (i = 0; i < size; ++i) { - dst_d[i] = (hist0[src_d[i]] * norm + 0.5)|0; - } - jsfeat.cache.put_buffer(hist0_node); - }, - - canny: function(src, dst, low_thresh, high_thresh) { - var w=src.cols,h=src.rows,src_d=src.data; - - dst.resize(w, h, src.channel); - - var dst_d=dst.data; - var i=0,j=0,grad=0,w2=w<<1,_grad=0,suppress=0,f=0,x=0,y=0,s=0; - var tg22x=0,tg67x=0; - - // cache buffers - var dxdy_node = jsfeat.cache.get_buffer((h * w2)<<2); - var buf_node = jsfeat.cache.get_buffer((3 * (w + 2))<<2); - var map_node = jsfeat.cache.get_buffer(((h+2) * (w + 2))<<2); - var stack_node = jsfeat.cache.get_buffer((h * w)<<2); - - - var buf = buf_node.i32; - var map = map_node.i32; - var stack = stack_node.i32; - var dxdy = dxdy_node.i32; - var dxdy_m = new jsfeat.matrix_t(w, h, jsfeat.S32C2_t, dxdy_node.data); - var row0=1,row1=(w+2+1)|0,row2=(2*(w+2)+1)|0,map_w=(w+2)|0,map_i=(map_w+1)|0,stack_i=0; - - this.sobel_derivatives(src, dxdy_m); - - if(low_thresh > high_thresh) { - i = low_thresh; - low_thresh = high_thresh; - high_thresh = i; - } - - i = (3 * (w + 2))|0; - while(--i>=0) { - buf[i] = 0; - } - - i = ((h+2) * (w + 2))|0; - while(--i>=0) { - map[i] = 0; - } - - for (; j < w; ++j, grad+=2) { - //buf[row1+j] = Math.abs(dxdy[grad]) + Math.abs(dxdy[grad+1]); - x = dxdy[grad], y = dxdy[grad+1]; - //buf[row1+j] = x*x + y*y; - buf[row1+j] = ((x ^ (x >> 31)) - (x >> 31)) + ((y ^ (y >> 31)) - (y >> 31)); - } - - for(i=1; i <= h; ++i, grad+=w2) { - if(i == h) { - j = row2+w; - while(--j>=row2) { - buf[j] = 0; - } - } else { - for (j = 0; j < w; j++) { - //buf[row2+j] = Math.abs(dxdy[grad+(j<<1)]) + Math.abs(dxdy[grad+(j<<1)+1]); - x = dxdy[grad+(j<<1)], y = dxdy[grad+(j<<1)+1]; - //buf[row2+j] = x*x + y*y; - buf[row2+j] = ((x ^ (x >> 31)) - (x >> 31)) + ((y ^ (y >> 31)) - (y >> 31)); - } - } - _grad = (grad - w2)|0; - map[map_i-1] = 0; - suppress = 0; - for(j = 0; j < w; ++j, _grad+=2) { - f = buf[row1+j]; - if (f > low_thresh) { - x = dxdy[_grad]; - y = dxdy[_grad+1]; - s = x ^ y; - // seems ot be faster than Math.abs - x = ((x ^ (x >> 31)) - (x >> 31))|0; - y = ((y ^ (y >> 31)) - (y >> 31))|0; - //x * tan(22.5) x * tan(67.5) == 2 * x + x * tan(22.5) - tg22x = x * 13573; - tg67x = tg22x + ((x + x) << 15); - y <<= 15; - if (y < tg22x) { - if (f > buf[row1+j-1] && f >= buf[row1+j+1]) { - if (f > high_thresh && !suppress && map[map_i+j-map_w] != 2) { - map[map_i+j] = 2; - suppress = 1; - stack[stack_i++] = map_i + j; - } else { - map[map_i+j] = 1; - } - continue; - } - } else if (y > tg67x) { - if (f > buf[row0+j] && f >= buf[row2+j]) { - if (f > high_thresh && !suppress && map[map_i+j-map_w] != 2) { - map[map_i+j] = 2; - suppress = 1; - stack[stack_i++] = map_i + j; - } else { - map[map_i+j] = 1; - } - continue; - } - } else { - s = s < 0 ? -1 : 1; - if (f > buf[row0+j-s] && f > buf[row2+j+s]) { - if (f > high_thresh && !suppress && map[map_i+j-map_w] != 2) { - map[map_i+j] = 2; - suppress = 1; - stack[stack_i++] = map_i + j; - } else { - map[map_i+j] = 1; - } - continue; - } - } - } - map[map_i+j] = 0; - suppress = 0; - } - map[map_i+w] = 0; - map_i += map_w; - j = row0; - row0 = row1; - row1 = row2; - row2 = j; - } - - j = map_i - map_w - 1; - for(i = 0; i < map_w; ++i, ++j) { - map[j] = 0; - } - // path following - while(stack_i > 0) { - map_i = stack[--stack_i]; - map_i -= map_w+1; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - map_i += 1; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - map_i += 1; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - map_i += map_w; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - map_i -= 2; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - map_i += map_w; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - map_i += 1; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - map_i += 1; - if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; - } - - map_i = map_w + 1; - row0 = 0; - for(i = 0; i < h; ++i, map_i+=map_w) { - for(j = 0; j < w; ++j) { - dst_d[row0++] = (map[map_i+j] == 2) * 0xff; - } - } - - // free buffers - jsfeat.cache.put_buffer(dxdy_node); - jsfeat.cache.put_buffer(buf_node); - jsfeat.cache.put_buffer(map_node); - jsfeat.cache.put_buffer(stack_node); - }, - // transform is 3x3 matrix_t - warp_perspective: function(src, dst, transform, fill_value) { - if (typeof fill_value === "undefined") { fill_value = 0; } - var src_width=src.cols|0, src_height=src.rows|0, dst_width=dst.cols|0, dst_height=dst.rows|0; - var src_d=src.data, dst_d=dst.data; - var x=0,y=0,off=0,ixs=0,iys=0,xs=0.0,ys=0.0,xs0=0.0,ys0=0.0,ws=0.0,sc=0.0,a=0.0,b=0.0,p0=0.0,p1=0.0; - var td=transform.data; - var m00=td[0],m01=td[1],m02=td[2], - m10=td[3],m11=td[4],m12=td[5], - m20=td[6],m21=td[7],m22=td[8]; - - for(var dptr = 0; y < dst_height; ++y) { - xs0 = m01 * y + m02, - ys0 = m11 * y + m12, - ws = m21 * y + m22; - for(x = 0; x < dst_width; ++x, ++dptr, xs0+=m00, ys0+=m10, ws+=m20) { - sc = 1.0 / ws; - xs = xs0 * sc, ys = ys0 * sc; - ixs = xs | 0, iys = ys | 0; - - if(xs > 0 && ys > 0 && ixs < (src_width - 1) && iys < (src_height - 1)) { - a = Math.max(xs - ixs, 0.0); - b = Math.max(ys - iys, 0.0); - off = (src_width*iys + ixs)|0; - - p0 = src_d[off] + a * (src_d[off+1] - src_d[off]); - p1 = src_d[off+src_width] + a * (src_d[off+src_width+1] - src_d[off+src_width]); - - dst_d[dptr] = p0 + b * (p1 - p0); - } - else dst_d[dptr] = fill_value; - } - } - }, - // transform is 3x3 or 2x3 matrix_t only first 6 values referenced - warp_affine: function(src, dst, transform, fill_value) { - if (typeof fill_value === "undefined") { fill_value = 0; } - var src_width=src.cols, src_height=src.rows, dst_width=dst.cols, dst_height=dst.rows; - var src_d=src.data, dst_d=dst.data; - var x=0,y=0,off=0,ixs=0,iys=0,xs=0.0,ys=0.0,a=0.0,b=0.0,p0=0.0,p1=0.0; - var td=transform.data; - var m00=td[0],m01=td[1],m02=td[2], - m10=td[3],m11=td[4],m12=td[5]; - - for(var dptr = 0; y < dst_height; ++y) { - xs = m01 * y + m02; - ys = m11 * y + m12; - for(x = 0; x < dst_width; ++x, ++dptr, xs+=m00, ys+=m10) { - ixs = xs | 0; iys = ys | 0; - - if(ixs >= 0 && iys >= 0 && ixs < (src_width - 1) && iys < (src_height - 1)) { - a = xs - ixs; - b = ys - iys; - off = src_width*iys + ixs; - - p0 = src_d[off] + a * (src_d[off+1] - src_d[off]); - p1 = src_d[off+src_width] + a * (src_d[off+src_width+1] - src_d[off+src_width]); - - dst_d[dptr] = p0 + b * (p1 - p0); - } - else dst_d[dptr] = fill_value; - } - } - }, - - // Basic RGB Skin detection filter - // from http://popscan.blogspot.fr/2012/08/skin-detection-in-digital-images.html - skindetector: function(src,dst) { - var r,g,b,j; - var i = src.width*src.height; - while(i--){ - j = i*4; - r = src.data[j]; - g = src.data[j+1]; - b = src.data[j+2]; - if((r>95)&&(g>40)&&(b>20) - &&(r>g)&&(r>b) - &&(r-Math.min(g,b)>15) - &&(Math.abs(r-g)>15)){ - dst[i] = 255; - } else { - dst[i] = 0; - } - } - } - }; - })(); - - global.imgproc = imgproc; - -})(jsfeat); +})(jsfeat); +/** + * @author Eugene Zatepyakin / http://inspirit.ru/ + * + */ + +(function(global) { + "use strict"; + // + + var motion_model = (function() { + + var sqr = function(x) { + return x*x; + } + + // does isotropic normalization + var iso_normalize_points = function(from, to, T0, T1, count) { + var i=0; + var cx0=0.0, cy0=0.0, d0=0.0, s0=0.0; + var cx1=0.0, cy1=0.0, d1=0.0, s1=0.0; + var dx=0.0,dy=0.0; + + for (; i < count; ++i) { + cx0 += from[i].x; + cy0 += from[i].y; + cx1 += to[i].x; + cy1 += to[i].y; + } + + cx0 /= count; cy0 /= count; + cx1 /= count; cy1 /= count; + + for (i = 0; i < count; ++i) { + dx = from[i].x - cx0; + dy = from[i].y - cy0; + d0 += Math.sqrt(dx*dx + dy*dy); + dx = to[i].x - cx1; + dy = to[i].y - cy1; + d1 += Math.sqrt(dx*dx + dy*dy); + } + + d0 /= count; d1 /= count; + + s0 = Math.SQRT2 / d0; s1 = Math.SQRT2 / d1; + + T0[0] = T0[4] = s0; + T0[2] = -cx0*s0; + T0[5] = -cy0*s0; + T0[1] = T0[3] = T0[6] = T0[7] = 0.0; + T0[8] = 1.0; + + T1[0] = T1[4] = s1; + T1[2] = -cx1*s1; + T1[5] = -cy1*s1; + T1[1] = T1[3] = T1[6] = T1[7] = 0.0; + T1[8] = 1.0; + } + + var have_collinear_points = function(points, count) { + var j=0,k=0,i=(count-1)|0; + var dx1=0.0,dy1=0.0,dx2=0.0,dy2=0.0; + + // check that the i-th selected point does not belong + // to a line connecting some previously selected points + for(; j < i; ++j) { + dx1 = points[j].x - points[i].x; + dy1 = points[j].y - points[i].y; + for(k = 0; k < j; ++k) { + dx2 = points[k].x - points[i].x; + dy2 = points[k].y - points[i].y; + if( Math.abs(dx2*dy1 - dy2*dx1) <= jsfeat.EPSILON*(Math.abs(dx1) + Math.abs(dy1) + Math.abs(dx2) + Math.abs(dy2))) + return true; + } + } + return false; + } + + var T0 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); + var T1 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); + var AtA = new jsfeat.matrix_t(6, 6, jsfeat.F32_t|jsfeat.C1_t); + var AtB = new jsfeat.matrix_t(6, 1, jsfeat.F32_t|jsfeat.C1_t); + + var affine2d = (function () { + + function affine2d() { + // empty constructor + } + + affine2d.prototype.run = function(from, to, model, count) { + var i=0,j=0; + var dt=model.type|jsfeat.C1_t; + var md=model.data, t0d=T0.data, t1d=T1.data; + var pt0,pt1,px=0.0,py=0.0; + + iso_normalize_points(from, to, t0d, t1d, count); + + var a_buff = jsfeat.cache.get_buffer((2*count*6)<<3); + var b_buff = jsfeat.cache.get_buffer((2*count)<<3); + + var a_mt = new jsfeat.matrix_t(6, 2*count, dt, a_buff.data); + var b_mt = new jsfeat.matrix_t(1, 2*count, dt, b_buff.data); + var ad=a_mt.data, bd=b_mt.data; + + for (; i < count; ++i) { + pt0 = from[i]; + pt1 = to[i]; + + px = t0d[0]*pt0.x + t0d[1]*pt0.y + t0d[2]; + py = t0d[3]*pt0.x + t0d[4]*pt0.y + t0d[5]; + + j = i*2*6; + ad[j]=px, ad[j+1]=py, ad[j+2]=1.0, ad[j+3]=0.0, ad[j+4]=0.0, ad[j+5]=0.0; + + j += 6; + ad[j]=0.0, ad[j+1]=0.0, ad[j+2]=0.0, ad[j+3]=px, ad[j+4]=py, ad[j+5]=1.0; + + bd[i<<1] = t1d[0]*pt1.x + t1d[1]*pt1.y + t1d[2]; + bd[(i<<1)+1] = t1d[3]*pt1.x + t1d[4]*pt1.y + t1d[5]; + } + + jsfeat.matmath.multiply_AtA(AtA, a_mt); + jsfeat.matmath.multiply_AtB(AtB, a_mt, b_mt); + + jsfeat.linalg.lu_solve(AtA, AtB); + + md[0] = AtB.data[0], md[1]=AtB.data[1], md[2]=AtB.data[2]; + md[3] = AtB.data[3], md[4]=AtB.data[4], md[5]=AtB.data[5]; + md[6] = 0.0, md[7] = 0.0, md[8] = 1.0; // fill last row + + // denormalize + jsfeat.matmath.invert_3x3(T1, T1); + jsfeat.matmath.multiply_3x3(model, T1, model); + jsfeat.matmath.multiply_3x3(model, model, T0); + + // free buffer + jsfeat.cache.put_buffer(a_buff); + jsfeat.cache.put_buffer(b_buff); + + return 1; + } + + affine2d.prototype.error = function(from, to, model, err, count) { + var i=0; + var pt0,pt1; + var m=model.data; + + for (; i < count; ++i) { + pt0 = from[i]; + pt1 = to[i]; + + err[i] = sqr(pt1.x - m[0]*pt0.x - m[1]*pt0.y - m[2]) + + sqr(pt1.y - m[3]*pt0.x - m[4]*pt0.y - m[5]); + } + } + + affine2d.prototype.check_subset = function(from, to, count) { + return true; // all good + } + + return affine2d; + })(); + + var mLtL = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); + var Evec = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); + + var homography2d = (function () { + + function homography2d() { + // empty constructor + //this.T0 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); + //this.T1 = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); + //this.mLtL = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); + //this.Evec = new jsfeat.matrix_t(9, 9, jsfeat.F32_t|jsfeat.C1_t); + } + + homography2d.prototype.run = function(from, to, model, count) { + var i=0,j=0; + var md=model.data, t0d=T0.data, t1d=T1.data; + var LtL=mLtL.data, evd=Evec.data; + var x=0.0,y=0.0,X=0.0,Y=0.0; + + // norm + var smx=0.0, smy=0.0, cmx=0.0, cmy=0.0, sMx=0.0, sMy=0.0, cMx=0.0, cMy=0.0; + + for(; i < count; ++i) { + cmx += to[i].x; + cmy += to[i].y; + cMx += from[i].x; + cMy += from[i].y; + } + + cmx /= count; cmy /= count; + cMx /= count; cMy /= count; + + for(i = 0; i < count; ++i) + { + smx += Math.abs(to[i].x - cmx); + smy += Math.abs(to[i].y - cmy); + sMx += Math.abs(from[i].x - cMx); + sMy += Math.abs(from[i].y - cMy); + } + + if( Math.abs(smx) < jsfeat.EPSILON + || Math.abs(smy) < jsfeat.EPSILON + || Math.abs(sMx) < jsfeat.EPSILON + || Math.abs(sMy) < jsfeat.EPSILON ) return 0; + + smx = count/smx; smy = count/smy; + sMx = count/sMx; sMy = count/sMy; + + t0d[0] = sMx; t0d[1] = 0; t0d[2] = -cMx*sMx; + t0d[3] = 0; t0d[4] = sMy; t0d[5] = -cMy*sMy; + t0d[6] = 0; t0d[7] = 0; t0d[8] = 1; + + t1d[0] = 1.0/smx; t1d[1] = 0; t1d[2] = cmx; + t1d[3] = 0; t1d[4] = 1.0/smy; t1d[5] = cmy; + t1d[6] = 0; t1d[7] = 0; t1d[8] = 1; + // + + // construct system + i = 81; + while(--i >= 0) { + LtL[i] = 0.0; + } + for(i = 0; i < count; ++i) { + x = (to[i].x - cmx) * smx; + y = (to[i].y - cmy) * smy; + X = (from[i].x - cMx) * sMx; + Y = (from[i].y - cMy) * sMy; + + LtL[0] += X*X; + LtL[1] += X*Y; + LtL[2] += X; + + LtL[6] += X*-x*X; + LtL[7] += X*-x*Y; + LtL[8] += X*-x; + LtL[10] += Y*Y; + LtL[11] += Y; + + LtL[15] += Y*-x*X; + LtL[16] += Y*-x*Y; + LtL[17] += Y*-x; + LtL[20] += 1.0; + + LtL[24] += -x*X; + LtL[25] += -x*Y; + LtL[26] += -x; + LtL[30] += X*X; + LtL[31] += X*Y; + LtL[32] += X; + LtL[33] += X*-y*X; + LtL[34] += X*-y*Y; + LtL[35] += X*-y; + LtL[40] += Y*Y; + LtL[41] += Y; + LtL[42] += Y*-y*X; + LtL[43] += Y*-y*Y; + LtL[44] += Y*-y; + LtL[50] += 1.0; + LtL[51] += -y*X; + LtL[52] += -y*Y; + LtL[53] += -y; + LtL[60] += -x*X*-x*X + -y*X*-y*X; + LtL[61] += -x*X*-x*Y + -y*X*-y*Y; + LtL[62] += -x*X*-x + -y*X*-y; + LtL[70] += -x*Y*-x*Y + -y*Y*-y*Y; + LtL[71] += -x*Y*-x + -y*Y*-y; + LtL[80] += -x*-x + -y*-y; + } + // + + // symmetry + for(i = 0; i < 9; ++i) { + for(j = 0; j < i; ++j) + LtL[i*9+j] = LtL[j*9+i]; + } + + jsfeat.linalg.eigenVV(mLtL, Evec); + + md[0]=evd[72], md[1]=evd[73], md[2]=evd[74]; + md[3]=evd[75], md[4]=evd[76], md[5]=evd[77]; + md[6]=evd[78], md[7]=evd[79], md[8]=evd[80]; + + // denormalize + jsfeat.matmath.multiply_3x3(model, T1, model); + jsfeat.matmath.multiply_3x3(model, model, T0); + + // set bottom right to 1.0 + x = 1.0/md[8]; + md[0] *= x; md[1] *= x; md[2] *= x; + md[3] *= x; md[4] *= x; md[5] *= x; + md[6] *= x; md[7] *= x; md[8] = 1.0; + + return 1; + } + + homography2d.prototype.error = function(from, to, model, err, count) { + var i=0; + var pt0,pt1,ww=0.0,dx=0.0,dy=0.0; + var m=model.data; + + for (; i < count; ++i) { + pt0 = from[i]; + pt1 = to[i]; + + ww = 1.0/(m[6]*pt0.x + m[7]*pt0.y + 1.0); + dx = (m[0]*pt0.x + m[1]*pt0.y + m[2])*ww - pt1.x; + dy = (m[3]*pt0.x + m[4]*pt0.y + m[5])*ww - pt1.y; + err[i] = (dx*dx + dy*dy); + } + } + + homography2d.prototype.check_subset = function(from, to, count) { + // seems to reject good subsets actually + //if( have_collinear_points(from, count) || have_collinear_points(to, count) ) { + //return false; + //} + if( count == 4 ) { + var negative = 0; + + var fp0=from[0],fp1=from[1],fp2=from[2],fp3=from[3]; + var tp0=to[0],tp1=to[1],tp2=to[2],tp3=to[3]; + + // set1 + var A11=fp0.x, A12=fp0.y, A13=1.0; + var A21=fp1.x, A22=fp1.y, A23=1.0; + var A31=fp2.x, A32=fp2.y, A33=1.0; + + var B11=tp0.x, B12=tp0.y, B13=1.0; + var B21=tp1.x, B22=tp1.y, B23=1.0; + var B31=tp2.x, B32=tp2.y, B33=1.0; + + var detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); + var detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); + + if(detA*detB < 0) negative++; + + // set2 + A11=fp1.x, A12=fp1.y; + A21=fp2.x, A22=fp2.y; + A31=fp3.x, A32=fp3.y; + + B11=tp1.x, B12=tp1.y; + B21=tp2.x, B22=tp2.y; + B31=tp3.x, B32=tp3.y; + + detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); + detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); + + if(detA*detB < 0) negative++; + + // set3 + A11=fp0.x, A12=fp0.y; + A21=fp2.x, A22=fp2.y; + A31=fp3.x, A32=fp3.y; + + B11=tp0.x, B12=tp0.y; + B21=tp2.x, B22=tp2.y; + B31=tp3.x, B32=tp3.y; + + detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); + detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); + + if(detA*detB < 0) negative++; + + // set4 + A11=fp0.x, A12=fp0.y; + A21=fp1.x, A22=fp1.y; + A31=fp3.x, A32=fp3.y; + + B11=tp0.x, B12=tp0.y; + B21=tp1.x, B22=tp1.y; + B31=tp3.x, B32=tp3.y; + + detA = jsfeat.matmath.determinant_3x3(A11,A12,A13, A21,A22,A23, A31,A32,A33); + detB = jsfeat.matmath.determinant_3x3(B11,B12,B13, B21,B22,B23, B31,B32,B33); + + if(detA*detB < 0) negative++; + + if(negative != 0 && negative != 4) { + return false; + } + } + return true; // all good + } + + return homography2d; + })(); + + return { + + affine2d:affine2d, + homography2d:homography2d + + }; + + })(); + + var ransac_params_t = (function () { + function ransac_params_t(size, thresh, eps, prob) { + if (typeof size === "undefined") { size=0; } + if (typeof thresh === "undefined") { thresh=0.5; } + if (typeof eps === "undefined") { eps=0.5; } + if (typeof prob === "undefined") { prob=0.99; } + + this.size = size; + this.thresh = thresh; + this.eps = eps; + this.prob = prob; + }; + ransac_params_t.prototype.update_iters = function(_eps, max_iters) { + var num = Math.log(1 - this.prob); + var denom = Math.log(1 - Math.pow(1 - _eps, this.size)); + return (denom >= 0 || -num >= max_iters*(-denom) ? max_iters : Math.round(num/denom))|0; + }; + return ransac_params_t; + })(); + + var motion_estimator = (function() { + + var get_subset = function(kernel, from, to, need_cnt, max_cnt, from_sub, to_sub) { + var max_try = 1000; + var indices = []; + var i=0, j=0, ssiter=0, idx_i=0, ok=false; + for(; ssiter < max_try; ++ssiter) { + i = 0; + for (; i < need_cnt && ssiter < max_try;) { + ok = false; + idx_i = 0; + while (!ok) { + ok = true; + idx_i = indices[i] = Math.floor(Math.random() * max_cnt)|0; + for (j = 0; j < i; ++j) { + if (idx_i == indices[j]) + { ok = false; break; } + } + } + from_sub[i] = from[idx_i]; + to_sub[i] = to[idx_i]; + if( !kernel.check_subset( from_sub, to_sub, i+1 ) ) { + ssiter++; + continue; + } + ++i; + } + break; + } + + return (i == need_cnt && ssiter < max_try); + } + + var find_inliers = function(kernel, model, from, to, count, thresh, err, mask) { + var numinliers = 0, i=0, f=0; + var t = thresh*thresh; + + kernel.error(from, to, model, err, count); + + for(; i < count; ++i) { + f = err[i] <= t; + mask[i] = f; + numinliers += f; + } + return numinliers; + } + + return { + + ransac: function(params, kernel, from, to, count, model, mask, max_iters) { + if (typeof max_iters === "undefined") { max_iters=1000; } + + if(count < params.size) return false; + + var model_points = params.size; + var niters = max_iters, iter=0; + var result = false; + + var subset0 = []; + var subset1 = []; + var found = false; + + var mc=model.cols,mr=model.rows; + var dt = model.type | jsfeat.C1_t; + + var m_buff = jsfeat.cache.get_buffer((mc*mr)<<3); + var ms_buff = jsfeat.cache.get_buffer(count); + var err_buff = jsfeat.cache.get_buffer(count<<2); + var M = new jsfeat.matrix_t(mc, mr, dt, m_buff.data); + var curr_mask = new jsfeat.matrix_t(count, 1, jsfeat.U8C1_t, ms_buff.data); + + var inliers_max = -1, numinliers=0; + var nmodels = 0; + + var err = err_buff.f32; + + // special case + if(count == model_points) { + if(kernel.run(from, to, M, count) <= 0) { + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + return false; + } + + M.copy_to(model); + if(mask) { + while(--count >= 0) { + mask.data[count] = 1; + } + } + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + return true; + } + + for (; iter < niters; ++iter) { + // generate subset + found = get_subset(kernel, from, to, model_points, count, subset0, subset1); + if(!found) { + if(iter == 0) { + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + return false; + } + break; + } + + nmodels = kernel.run( subset0, subset1, M, model_points ); + if(nmodels <= 0) + continue; + + // TODO handle multimodel output + + numinliers = find_inliers(kernel, M, from, to, count, params.thresh, err, curr_mask.data); + + if( numinliers > Math.max(inliers_max, model_points-1) ) { + M.copy_to(model); + inliers_max = numinliers; + if(mask) curr_mask.copy_to(mask); + niters = params.update_iters((count - numinliers)/count, niters); + result = true; + } + } + + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + + return result; + }, + + lmeds: function(params, kernel, from, to, count, model, mask, max_iters) { + if (typeof max_iters === "undefined") { max_iters=1000; } + + if(count < params.size) return false; + + var model_points = params.size; + var niters = max_iters, iter=0; + var result = false; + + var subset0 = []; + var subset1 = []; + var found = false; + + var mc=model.cols,mr=model.rows; + var dt = model.type | jsfeat.C1_t; + + var m_buff = jsfeat.cache.get_buffer((mc*mr)<<3); + var ms_buff = jsfeat.cache.get_buffer(count); + var err_buff = jsfeat.cache.get_buffer(count<<2); + var M = new jsfeat.matrix_t(mc, mr, dt, m_buff.data); + var curr_mask = new jsfeat.matrix_t(count, 1, jsfeat.U8_t|jsfeat.C1_t, ms_buff.data); + + var numinliers=0; + var nmodels = 0; + + var err = err_buff.f32; + var min_median = 1000000000.0, sigma=0.0, median=0.0; + + params.eps = 0.45; + niters = params.update_iters(params.eps, niters); + + // special case + if(count == model_points) { + if(kernel.run(from, to, M, count) <= 0) { + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + return false; + } + + M.copy_to(model); + if(mask) { + while(--count >= 0) { + mask.data[count] = 1; + } + } + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + return true; + } + + for (; iter < niters; ++iter) { + // generate subset + found = get_subset(kernel, from, to, model_points, count, subset0, subset1); + if(!found) { + if(iter == 0) { + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + return false; + } + break; + } + + nmodels = kernel.run( subset0, subset1, M, model_points ); + if(nmodels <= 0) + continue; + + // TODO handle multimodel output + + kernel.error(from, to, M, err, count); + median = jsfeat.math.median(err, 0, count-1); + + if(median < min_median) { + min_median = median; + M.copy_to(model); + result = true; + } + } + + if(result) { + sigma = 2.5*1.4826*(1 + 5.0/(count - model_points))*Math.sqrt(min_median); + sigma = Math.max(sigma, 0.001); + + numinliers = find_inliers(kernel, model, from, to, count, sigma, err, curr_mask.data); + if(mask) curr_mask.copy_to(mask); + + result = numinliers >= model_points; + } + + jsfeat.cache.put_buffer(m_buff); + jsfeat.cache.put_buffer(ms_buff); + jsfeat.cache.put_buffer(err_buff); + + return result; + } + + }; + + })(); + + global.ransac_params_t = ransac_params_t; + global.motion_model = motion_model; + global.motion_estimator = motion_estimator; + +})(jsfeat); +/** + * @author Eugene Zatepyakin / http://inspirit.ru/ + */ + +(function(global) { + "use strict"; + // + + var imgproc = (function() { + + var _resample_u8 = function(src, dst, nw, nh) { + var xofs_count=0; + var ch=src.channel,w=src.cols,h=src.rows; + var src_d=src.data,dst_d=dst.data; + var scale_x = w / nw, scale_y = h / nh; + var inv_scale_256 = (scale_x * scale_y * 0x10000)|0; + var dx=0,dy=0,sx=0,sy=0,sx1=0,sx2=0,i=0,k=0,fsx1=0.0,fsx2=0.0; + var a=0,b=0,dxn=0,alpha=0,beta=0,beta1=0; + + var buf_node = jsfeat.cache.get_buffer((nw*ch)<<2); + var sum_node = jsfeat.cache.get_buffer((nw*ch)<<2); + var xofs_node = jsfeat.cache.get_buffer((w*2*3)<<2); + + var buf = buf_node.i32; + var sum = sum_node.i32; + var xofs = xofs_node.i32; + + for (; dx < nw; dx++) { + fsx1 = dx * scale_x, fsx2 = fsx1 + scale_x; + sx1 = (fsx1 + 1.0 - 1e-6)|0, sx2 = fsx2|0; + sx1 = Math.min(sx1, w - 1); + sx2 = Math.min(sx2, w - 1); + + if(sx1 > fsx1) { + xofs[k++] = (dx * ch)|0; + xofs[k++] = ((sx1 - 1)*ch)|0; + xofs[k++] = ((sx1 - fsx1) * 0x100)|0; + xofs_count++; + } + for(sx = sx1; sx < sx2; sx++){ + xofs_count++; + xofs[k++] = (dx * ch)|0; + xofs[k++] = (sx * ch)|0; + xofs[k++] = 256; + } + if(fsx2 - sx2 > 1e-3) { + xofs_count++; + xofs[k++] = (dx * ch)|0; + xofs[k++] = (sx2 * ch)|0; + xofs[k++] = ((fsx2 - sx2) * 256)|0; + } + } + + for (dx = 0; dx < nw * ch; dx++) { + buf[dx] = sum[dx] = 0; + } + dy = 0; + for (sy = 0; sy < h; sy++) { + a = w * sy; + for (k = 0; k < xofs_count; k++) { + dxn = xofs[k*3]; + sx1 = xofs[k*3+1]; + alpha = xofs[k*3+2]; + for (i = 0; i < ch; i++) { + buf[dxn + i] += src_d[a+sx1+i] * alpha; + } + } + if ((dy + 1) * scale_y <= sy + 1 || sy == h - 1) { + beta = (Math.max(sy + 1 - (dy + 1) * scale_y, 0.0) * 256)|0; + beta1 = 256 - beta; + b = nw * dy; + if (beta <= 0) { + for (dx = 0; dx < nw * ch; dx++) { + dst_d[b+dx] = Math.min(Math.max((sum[dx] + buf[dx] * 256) / inv_scale_256, 0), 255); + sum[dx] = buf[dx] = 0; + } + } else { + for (dx = 0; dx < nw * ch; dx++) { + dst_d[b+dx] = Math.min(Math.max((sum[dx] + buf[dx] * beta1) / inv_scale_256, 0), 255); + sum[dx] = buf[dx] * beta; + buf[dx] = 0; + } + } + dy++; + } else { + for(dx = 0; dx < nw * ch; dx++) { + sum[dx] += buf[dx] * 256; + buf[dx] = 0; + } + } + } + + jsfeat.cache.put_buffer(sum_node); + jsfeat.cache.put_buffer(buf_node); + jsfeat.cache.put_buffer(xofs_node); + } + + var _resample = function(src, dst, nw, nh) { + var xofs_count=0; + var ch=src.channel,w=src.cols,h=src.rows; + var src_d=src.data,dst_d=dst.data; + var scale_x = w / nw, scale_y = h / nh; + var scale = 1.0 / (scale_x * scale_y); + var dx=0,dy=0,sx=0,sy=0,sx1=0,sx2=0,i=0,k=0,fsx1=0.0,fsx2=0.0; + var a=0,b=0,dxn=0,alpha=0.0,beta=0.0,beta1=0.0; + + var buf_node = jsfeat.cache.get_buffer((nw*ch)<<2); + var sum_node = jsfeat.cache.get_buffer((nw*ch)<<2); + var xofs_node = jsfeat.cache.get_buffer((w*2*3)<<2); + + var buf = buf_node.f32; + var sum = sum_node.f32; + var xofs = xofs_node.f32; + + for (; dx < nw; dx++) { + fsx1 = dx * scale_x, fsx2 = fsx1 + scale_x; + sx1 = (fsx1 + 1.0 - 1e-6)|0, sx2 = fsx2|0; + sx1 = Math.min(sx1, w - 1); + sx2 = Math.min(sx2, w - 1); + + if(sx1 > fsx1) { + xofs_count++; + xofs[k++] = ((sx1 - 1)*ch)|0; + xofs[k++] = (dx * ch)|0; + xofs[k++] = (sx1 - fsx1) * scale; + } + for(sx = sx1; sx < sx2; sx++){ + xofs_count++; + xofs[k++] = (sx * ch)|0; + xofs[k++] = (dx * ch)|0; + xofs[k++] = scale; + } + if(fsx2 - sx2 > 1e-3) { + xofs_count++; + xofs[k++] = (sx2 * ch)|0; + xofs[k++] = (dx * ch)|0; + xofs[k++] = (fsx2 - sx2) * scale; + } + } + + for (dx = 0; dx < nw * ch; dx++) { + buf[dx] = sum[dx] = 0; + } + dy = 0; + for (sy = 0; sy < h; sy++) { + a = w * sy; + for (k = 0; k < xofs_count; k++) { + sx1 = xofs[k*3]|0; + dxn = xofs[k*3+1]|0; + alpha = xofs[k*3+2]; + for (i = 0; i < ch; i++) { + buf[dxn + i] += src_d[a+sx1+i] * alpha; + } + } + if ((dy + 1) * scale_y <= sy + 1 || sy == h - 1) { + beta = Math.max(sy + 1 - (dy + 1) * scale_y, 0.0); + beta1 = 1.0 - beta; + b = nw * dy; + if (Math.abs(beta) < 1e-3) { + for (dx = 0; dx < nw * ch; dx++) { + dst_d[b+dx] = sum[dx] + buf[dx]; + sum[dx] = buf[dx] = 0; + } + } else { + for (dx = 0; dx < nw * ch; dx++) { + dst_d[b+dx] = sum[dx] + buf[dx] * beta1; + sum[dx] = buf[dx] * beta; + buf[dx] = 0; + } + } + dy++; + } else { + for(dx = 0; dx < nw * ch; dx++) { + sum[dx] += buf[dx]; + buf[dx] = 0; + } + } + } + jsfeat.cache.put_buffer(sum_node); + jsfeat.cache.put_buffer(buf_node); + jsfeat.cache.put_buffer(xofs_node); + } + + var _convol_u8 = function(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel) { + var i=0,j=0,k=0,sp=0,dp=0,sum=0,sum1=0,sum2=0,sum3=0,f0=filter[0],fk=0; + var w2=w<<1,w3=w*3,w4=w<<2; + // hor pass + for (; i < h; ++i) { + sum = src_d[sp]; + for (j = 0; j < half_kernel; ++j) { + buf[j] = sum; + } + for (j = 0; j <= w-2; j+=2) { + buf[j + half_kernel] = src_d[sp+j]; + buf[j + half_kernel+1] = src_d[sp+j+1]; + } + for (; j < w; ++j) { + buf[j + half_kernel] = src_d[sp+j]; + } + sum = src_d[sp+w-1]; + for (j = w; j < half_kernel + w; ++j) { + buf[j + half_kernel] = sum; + } + for (j = 0; j <= w-4; j+=4) { + sum = buf[j] * f0, + sum1 = buf[j+1] * f0, + sum2 = buf[j+2] * f0, + sum3 = buf[j+3] * f0; + for (k = 1; k < kernel_size; ++k) { + fk = filter[k]; + sum += buf[k + j] * fk; + sum1 += buf[k + j+1] * fk; + sum2 += buf[k + j+2] * fk; + sum3 += buf[k + j+3] * fk; + } + dst_d[dp+j] = Math.min(sum >> 8, 255); + dst_d[dp+j+1] = Math.min(sum1 >> 8, 255); + dst_d[dp+j+2] = Math.min(sum2 >> 8, 255); + dst_d[dp+j+3] = Math.min(sum3 >> 8, 255); + } + for (; j < w; ++j) { + sum = buf[j] * f0; + for (k = 1; k < kernel_size; ++k) { + sum += buf[k + j] * filter[k]; + } + dst_d[dp+j] = Math.min(sum >> 8, 255); + } + sp += w; + dp += w; + } + + // vert pass + for (i = 0; i < w; ++i) { + sum = dst_d[i]; + for (j = 0; j < half_kernel; ++j) { + buf[j] = sum; + } + k = i; + for (j = 0; j <= h-2; j+=2, k+=w2) { + buf[j+half_kernel] = dst_d[k]; + buf[j+half_kernel+1] = dst_d[k+w]; + } + for (; j < h; ++j, k+=w) { + buf[j+half_kernel] = dst_d[k]; + } + sum = dst_d[(h-1)*w + i]; + for (j = h; j < half_kernel + h; ++j) { + buf[j + half_kernel] = sum; + } + dp = i; + for (j = 0; j <= h-4; j+=4, dp+=w4) { + sum = buf[j] * f0, + sum1 = buf[j+1] * f0, + sum2 = buf[j+2] * f0, + sum3 = buf[j+3] * f0; + for (k = 1; k < kernel_size; ++k) { + fk = filter[k]; + sum += buf[k + j] * fk; + sum1 += buf[k + j+1] * fk; + sum2 += buf[k + j+2] * fk; + sum3 += buf[k + j+3] * fk; + } + dst_d[dp] = Math.min(sum >> 8, 255); + dst_d[dp+w] = Math.min(sum1 >> 8, 255); + dst_d[dp+w2] = Math.min(sum2 >> 8, 255); + dst_d[dp+w3] = Math.min(sum3 >> 8, 255); + } + for (; j < h; ++j, dp+=w) { + sum = buf[j] * f0; + for (k = 1; k < kernel_size; ++k) { + sum += buf[k + j] * filter[k]; + } + dst_d[dp] = Math.min(sum >> 8, 255); + } + } + } + + var _convol = function(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel) { + var i=0,j=0,k=0,sp=0,dp=0,sum=0.0,sum1=0.0,sum2=0.0,sum3=0.0,f0=filter[0],fk=0.0; + var w2=w<<1,w3=w*3,w4=w<<2; + // hor pass + for (; i < h; ++i) { + sum = src_d[sp]; + for (j = 0; j < half_kernel; ++j) { + buf[j] = sum; + } + for (j = 0; j <= w-2; j+=2) { + buf[j + half_kernel] = src_d[sp+j]; + buf[j + half_kernel+1] = src_d[sp+j+1]; + } + for (; j < w; ++j) { + buf[j + half_kernel] = src_d[sp+j]; + } + sum = src_d[sp+w-1]; + for (j = w; j < half_kernel + w; ++j) { + buf[j + half_kernel] = sum; + } + for (j = 0; j <= w-4; j+=4) { + sum = buf[j] * f0, + sum1 = buf[j+1] * f0, + sum2 = buf[j+2] * f0, + sum3 = buf[j+3] * f0; + for (k = 1; k < kernel_size; ++k) { + fk = filter[k]; + sum += buf[k + j] * fk; + sum1 += buf[k + j+1] * fk; + sum2 += buf[k + j+2] * fk; + sum3 += buf[k + j+3] * fk; + } + dst_d[dp+j] = sum; + dst_d[dp+j+1] = sum1; + dst_d[dp+j+2] = sum2; + dst_d[dp+j+3] = sum3; + } + for (; j < w; ++j) { + sum = buf[j] * f0; + for (k = 1; k < kernel_size; ++k) { + sum += buf[k + j] * filter[k]; + } + dst_d[dp+j] = sum; + } + sp += w; + dp += w; + } + + // vert pass + for (i = 0; i < w; ++i) { + sum = dst_d[i]; + for (j = 0; j < half_kernel; ++j) { + buf[j] = sum; + } + k = i; + for (j = 0; j <= h-2; j+=2, k+=w2) { + buf[j+half_kernel] = dst_d[k]; + buf[j+half_kernel+1] = dst_d[k+w]; + } + for (; j < h; ++j, k+=w) { + buf[j+half_kernel] = dst_d[k]; + } + sum = dst_d[(h-1)*w + i]; + for (j = h; j < half_kernel + h; ++j) { + buf[j + half_kernel] = sum; + } + dp = i; + for (j = 0; j <= h-4; j+=4, dp+=w4) { + sum = buf[j] * f0, + sum1 = buf[j+1] * f0, + sum2 = buf[j+2] * f0, + sum3 = buf[j+3] * f0; + for (k = 1; k < kernel_size; ++k) { + fk = filter[k]; + sum += buf[k + j] * fk; + sum1 += buf[k + j+1] * fk; + sum2 += buf[k + j+2] * fk; + sum3 += buf[k + j+3] * fk; + } + dst_d[dp] = sum; + dst_d[dp+w] = sum1; + dst_d[dp+w2] = sum2; + dst_d[dp+w3] = sum3; + } + for (; j < h; ++j, dp+=w) { + sum = buf[j] * f0; + for (k = 1; k < kernel_size; ++k) { + sum += buf[k + j] * filter[k]; + } + dst_d[dp] = sum; + } + } + } + + return { + // TODO: add support for RGB/BGR order + // for raw arrays + grayscale: function(src, w, h, dst, code) { + // this is default image data representation in browser + if (typeof code === "undefined") { code = jsfeat.COLOR_RGBA2GRAY; } + var x=0, y=0, i=0, j=0, ir=0,jr=0; + var coeff_r = 4899, coeff_g = 9617, coeff_b = 1868, cn = 4; + + if(code == jsfeat.COLOR_BGRA2GRAY || code == jsfeat.COLOR_BGR2GRAY) { + coeff_r = 1868; + coeff_b = 4899; + } + if(code == jsfeat.COLOR_RGB2GRAY || code == jsfeat.COLOR_BGR2GRAY) { + cn = 3; + } + var cn2 = cn<<1, cn3 = (cn*3)|0; + + dst.resize(w, h, 1); + var dst_u8 = dst.data; + + for(y = 0; y < h; ++y, j+=w, i+=w*cn) { + for(x = 0, ir = i, jr = j; x <= w-4; x+=4, ir+=cn<<2, jr+=4) { + dst_u8[jr] = (src[ir] * coeff_r + src[ir+1] * coeff_g + src[ir+2] * coeff_b + 8192) >> 14; + dst_u8[jr + 1] = (src[ir+cn] * coeff_r + src[ir+cn+1] * coeff_g + src[ir+cn+2] * coeff_b + 8192) >> 14; + dst_u8[jr + 2] = (src[ir+cn2] * coeff_r + src[ir+cn2+1] * coeff_g + src[ir+cn2+2] * coeff_b + 8192) >> 14; + dst_u8[jr + 3] = (src[ir+cn3] * coeff_r + src[ir+cn3+1] * coeff_g + src[ir+cn3+2] * coeff_b + 8192) >> 14; + } + for (; x < w; ++x, ++jr, ir+=cn) { + dst_u8[jr] = (src[ir] * coeff_r + src[ir+1] * coeff_g + src[ir+2] * coeff_b + 8192) >> 14; + } + } + }, + // derived from CCV library + resample: function(src, dst, nw, nh) { + var h=src.rows,w=src.cols; + if (h > nh && w > nw) { + dst.resize(nw, nh, src.channel); + // using the fast alternative (fix point scale, 0x100 to avoid overflow) + if (src.type&jsfeat.U8_t && dst.type&jsfeat.U8_t && h * w / (nh * nw) < 0x100) { + _resample_u8(src, dst, nw, nh); + } else { + _resample(src, dst, nw, nh); + } + } + }, + + box_blur_gray: function(src, dst, radius, options) { + if (typeof options === "undefined") { options = 0; } + var w=src.cols, h=src.rows, h2=h<<1, w2=w<<1; + var i=0,x=0,y=0,end=0; + var windowSize = ((radius << 1) + 1)|0; + var radiusPlusOne = (radius + 1)|0, radiusPlus2 = (radiusPlusOne+1)|0; + var scale = options&jsfeat.BOX_BLUR_NOSCALE ? 1 : (1.0 / (windowSize*windowSize)); + + var tmp_buff = jsfeat.cache.get_buffer((w*h)<<2); + + var sum=0, dstIndex=0, srcIndex = 0, nextPixelIndex=0, previousPixelIndex=0; + var data_i32 = tmp_buff.i32; // to prevent overflow + var data_u8 = src.data; + var hold=0; + + dst.resize(w, h, src.channel); + + // first pass + // no need to scale + //data_u8 = src.data; + //data_i32 = tmp; + for (y = 0; y < h; ++y) { + dstIndex = y; + sum = radiusPlusOne * data_u8[srcIndex]; + + for(i = (srcIndex+1)|0, end=(srcIndex+radius)|0; i <= end; ++i) { + sum += data_u8[i]; + } + + nextPixelIndex = (srcIndex + radiusPlusOne)|0; + previousPixelIndex = srcIndex; + hold = data_u8[previousPixelIndex]; + for(x = 0; x < radius; ++x, dstIndex += h) { + data_i32[dstIndex] = sum; + sum += data_u8[nextPixelIndex]- hold; + nextPixelIndex ++; + } + for(; x < w-radiusPlus2; x+=2, dstIndex += h2) { + data_i32[dstIndex] = sum; + sum += data_u8[nextPixelIndex]- data_u8[previousPixelIndex]; + + data_i32[dstIndex+h] = sum; + sum += data_u8[nextPixelIndex+1]- data_u8[previousPixelIndex+1]; + + nextPixelIndex +=2; + previousPixelIndex +=2; + } + for(; x < w-radiusPlusOne; ++x, dstIndex += h) { + data_i32[dstIndex] = sum; + sum += data_u8[nextPixelIndex]- data_u8[previousPixelIndex]; + + nextPixelIndex ++; + previousPixelIndex ++; + } + + hold = data_u8[nextPixelIndex-1]; + for(; x < w; ++x, dstIndex += h) { + data_i32[dstIndex] = sum; + + sum += hold- data_u8[previousPixelIndex]; + previousPixelIndex ++; + } + + srcIndex += w; + } + // + // second pass + srcIndex = 0; + //data_i32 = tmp; // this is a transpose + data_u8 = dst.data; + + // dont scale result + if(scale == 1) { + for (y = 0; y < w; ++y) { + dstIndex = y; + sum = radiusPlusOne * data_i32[srcIndex]; + + for(i = (srcIndex+1)|0, end=(srcIndex+radius)|0; i <= end; ++i) { + sum += data_i32[i]; + } + + nextPixelIndex = srcIndex + radiusPlusOne; + previousPixelIndex = srcIndex; + hold = data_i32[previousPixelIndex]; + + for(x = 0; x < radius; ++x, dstIndex += w) { + data_u8[dstIndex] = sum; + sum += data_i32[nextPixelIndex]- hold; + nextPixelIndex ++; + } + for(; x < h-radiusPlus2; x+=2, dstIndex += w2) { + data_u8[dstIndex] = sum; + sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; + + data_u8[dstIndex+w] = sum; + sum += data_i32[nextPixelIndex+1]- data_i32[previousPixelIndex+1]; + + nextPixelIndex +=2; + previousPixelIndex +=2; + } + for(; x < h-radiusPlusOne; ++x, dstIndex += w) { + data_u8[dstIndex] = sum; + + sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; + nextPixelIndex ++; + previousPixelIndex ++; + } + hold = data_i32[nextPixelIndex-1]; + for(; x < h; ++x, dstIndex += w) { + data_u8[dstIndex] = sum; + + sum += hold- data_i32[previousPixelIndex]; + previousPixelIndex ++; + } + + srcIndex += h; + } + } else { + for (y = 0; y < w; ++y) { + dstIndex = y; + sum = radiusPlusOne * data_i32[srcIndex]; + + for(i = (srcIndex+1)|0, end=(srcIndex+radius)|0; i <= end; ++i) { + sum += data_i32[i]; + } + + nextPixelIndex = srcIndex + radiusPlusOne; + previousPixelIndex = srcIndex; + hold = data_i32[previousPixelIndex]; + + for(x = 0; x < radius; ++x, dstIndex += w) { + data_u8[dstIndex] = sum*scale; + sum += data_i32[nextPixelIndex]- hold; + nextPixelIndex ++; + } + for(; x < h-radiusPlus2; x+=2, dstIndex += w2) { + data_u8[dstIndex] = sum*scale; + sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; + + data_u8[dstIndex+w] = sum*scale; + sum += data_i32[nextPixelIndex+1]- data_i32[previousPixelIndex+1]; + + nextPixelIndex +=2; + previousPixelIndex +=2; + } + for(; x < h-radiusPlusOne; ++x, dstIndex += w) { + data_u8[dstIndex] = sum*scale; + + sum += data_i32[nextPixelIndex]- data_i32[previousPixelIndex]; + nextPixelIndex ++; + previousPixelIndex ++; + } + hold = data_i32[nextPixelIndex-1]; + for(; x < h; ++x, dstIndex += w) { + data_u8[dstIndex] = sum*scale; + + sum += hold- data_i32[previousPixelIndex]; + previousPixelIndex ++; + } + + srcIndex += h; + } + } + + jsfeat.cache.put_buffer(tmp_buff); + }, + + gaussian_blur: function(src, dst, kernel_size, sigma) { + if (typeof sigma === "undefined") { sigma = 0.0; } + if (typeof kernel_size === "undefined") { kernel_size = 0; } + kernel_size = kernel_size == 0 ? (Math.max(1, (4.0 * sigma + 1.0 - 1e-8)) * 2 + 1)|0 : kernel_size; + var half_kernel = kernel_size >> 1; + var w = src.cols, h = src.rows; + var data_type = src.type, is_u8 = data_type&jsfeat.U8_t; + + dst.resize(w, h, src.channel); + + var src_d = src.data, dst_d = dst.data; + var buf,filter,buf_sz=(kernel_size + Math.max(h, w))|0; + + var buf_node = jsfeat.cache.get_buffer(buf_sz<<2); + var filt_node = jsfeat.cache.get_buffer(kernel_size<<2); + + if(is_u8) { + buf = buf_node.i32; + filter = filt_node.i32; + } else if(data_type&jsfeat.S32_t) { + buf = buf_node.i32; + filter = filt_node.f32; + } else { + buf = buf_node.f32; + filter = filt_node.f32; + } + + jsfeat.math.get_gaussian_kernel(kernel_size, sigma, filter, data_type); + + if(is_u8) { + _convol_u8(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel); + } else { + _convol(buf, src_d, dst_d, w, h, filter, kernel_size, half_kernel); + } + + jsfeat.cache.put_buffer(buf_node); + jsfeat.cache.put_buffer(filt_node); + }, + hough_transform: function( img, rho_res, theta_res, threshold ) { + var image = img.data; + + var width = img.cols; + var height = img.rows; + var step = width; + + min_theta = 0.0; + max_theta = Math.PI; + + numangle = Math.round((max_theta - min_theta) / theta_res); + numrho = Math.round(((width + height) * 2 + 1) / rho_res); + irho = 1.0 / rho_res; + + var accum = new Int32Array((numangle+2) * (numrho+2)); //typed arrays are initialized to 0 + var tabSin = new Float32Array(numangle); + var tabCos = new Float32Array(numangle); + + var n=0; + var ang = min_theta; + for(; n < numangle; n++ ) { + tabSin[n] = Math.sin(ang) * irho; + tabCos[n] = Math.cos(ang) * irho; + ang += theta_res + } + + // stage 1. fill accumulator + for( var i = 0; i < height; i++ ) { + for( var j = 0; j < width; j++ ) { + if( image[i * step + j] != 0 ) { + //console.log(r, (n+1) * (numrho+2) + r+1, tabCos[n], tabSin[n]); + for(var n = 0; n < numangle; n++ ) { + var r = Math.round( j * tabCos[n] + i * tabSin[n] ); + r += (numrho - 1) / 2; + accum[(n+1) * (numrho+2) + r+1] += 1; + } + } + } + } + + // stage 2. find local maximums + //TODO: Consider making a vector class that uses typed arrays + _sort_buf = new Array(); + for(var r = 0; r < numrho; r++ ) { + for(var n = 0; n < numangle; n++ ) { + var base = (n+1) * (numrho+2) + r+1; + if( accum[base] > threshold && + accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] && + accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] ) { + _sort_buf.push(base); + } + } + } + + // stage 3. sort the detected lines by accumulator value + _sort_buf.sort(function(l1, l2) { + return accum[l1] > accum[l2] || (accum[l1] == accum[l2] && l1 < l2); + }); + + // stage 4. store the first min(total,linesMax) lines to the output buffer + linesMax = Math.min(numangle*numrho, _sort_buf.length); + scale = 1.0 / (numrho+2); + lines = new Array(); + for( var i = 0; i < linesMax; i++ ) { + var idx = _sort_buf[i]; + var n = Math.floor(idx*scale) - 1; + var r = idx - (n+1)*(numrho+2) - 1; + var lrho = (r - (numrho - 1)*0.5) * rho_res; + var langle = n * theta_res; + lines.push([lrho, langle]); + } + return lines; + }, + // assume we always need it for u8 image + pyrdown: function(src, dst, sx, sy) { + // this is needed for bbf + if (typeof sx === "undefined") { sx = 0; } + if (typeof sy === "undefined") { sy = 0; } + + var w = src.cols, h = src.rows; + var w2 = w >> 1, h2 = h >> 1; + var _w2 = w2 - (sx << 1), _h2 = h2 - (sy << 1); + var x=0,y=0,sptr=sx+sy*w,sline=0,dptr=0,dline=0; + + dst.resize(w2, h2, src.channel); + + var src_d = src.data, dst_d = dst.data; + + for(y = 0; y < _h2; ++y) { + sline = sptr; + dline = dptr; + for(x = 0; x <= _w2-2; x+=2, dline+=2, sline += 4) { + dst_d[dline] = (src_d[sline] + src_d[sline+1] + + src_d[sline+w] + src_d[sline+w+1] + 2) >> 2; + dst_d[dline+1] = (src_d[sline+2] + src_d[sline+3] + + src_d[sline+w+2] + src_d[sline+w+3] + 2) >> 2; + } + for(; x < _w2; ++x, ++dline, sline += 2) { + dst_d[dline] = (src_d[sline] + src_d[sline+1] + + src_d[sline+w] + src_d[sline+w+1] + 2) >> 2; + } + sptr += w << 1; + dptr += w2; + } + }, + + // dst: [gx,gy,...] + scharr_derivatives: function(src, dst) { + var w = src.cols, h = src.rows; + var dstep = w<<1,x=0,y=0,x1=0,a,b,c,d,e,f; + var srow0=0,srow1=0,srow2=0,drow=0; + var trow0,trow1; + + dst.resize(w, h, 2); // 2 channel output gx, gy + + var img = src.data, gxgy=dst.data; + + var buf0_node = jsfeat.cache.get_buffer((w+2)<<2); + var buf1_node = jsfeat.cache.get_buffer((w+2)<<2); + + if(src.type&jsfeat.U8_t || src.type&jsfeat.S32_t) { + trow0 = buf0_node.i32; + trow1 = buf1_node.i32; + } else { + trow0 = buf0_node.f32; + trow1 = buf1_node.f32; + } + + for(; y < h; ++y, srow1+=w) { + srow0 = ((y > 0 ? y-1 : 1)*w)|0; + srow2 = ((y < h-1 ? y+1 : h-2)*w)|0; + drow = (y*dstep)|0; + // do vertical convolution + for(x = 0, x1 = 1; x <= w-2; x+=2, x1+=2) { + a = img[srow0+x], b = img[srow2+x]; + trow0[x1] = ( (a + b)*3 + (img[srow1+x])*10 ); + trow1[x1] = ( b - a ); + // + a = img[srow0+x+1], b = img[srow2+x+1]; + trow0[x1+1] = ( (a + b)*3 + (img[srow1+x+1])*10 ); + trow1[x1+1] = ( b - a ); + } + for(; x < w; ++x, ++x1) { + a = img[srow0+x], b = img[srow2+x]; + trow0[x1] = ( (a + b)*3 + (img[srow1+x])*10 ); + trow1[x1] = ( b - a ); + } + // make border + x = (w + 1)|0; + trow0[0] = trow0[1]; trow0[x] = trow0[w]; + trow1[0] = trow1[1]; trow1[x] = trow1[w]; + // do horizontal convolution, interleave the results and store them + for(x = 0; x <= w-4; x+=4) { + a = trow1[x+2], b = trow1[x+1], c = trow1[x+3], d = trow1[x+4], + e = trow0[x+2], f = trow0[x+3]; + gxgy[drow++] = ( e - trow0[x] ); + gxgy[drow++] = ( (a + trow1[x])*3 + b*10 ); + gxgy[drow++] = ( f - trow0[x+1] ); + gxgy[drow++] = ( (c + b)*3 + a*10 ); + + gxgy[drow++] = ( (trow0[x+4] - e) ); + gxgy[drow++] = ( ((d + a)*3 + c*10) ); + gxgy[drow++] = ( (trow0[x+5] - f) ); + gxgy[drow++] = ( ((trow1[x+5] + c)*3 + d*10) ); + } + for(; x < w; ++x) { + gxgy[drow++] = ( (trow0[x+2] - trow0[x]) ); + gxgy[drow++] = ( ((trow1[x+2] + trow1[x])*3 + trow1[x+1]*10) ); + } + } + jsfeat.cache.put_buffer(buf0_node); + jsfeat.cache.put_buffer(buf1_node); + }, + + // compute gradient using Sobel kernel [1 2 1] * [-1 0 1]^T + // dst: [gx,gy,...] + sobel_derivatives: function(src, dst) { + var w = src.cols, h = src.rows; + var dstep = w<<1,x=0,y=0,x1=0,a,b,c,d,e,f; + var srow0=0,srow1=0,srow2=0,drow=0; + var trow0,trow1; + + dst.resize(w, h, 2); // 2 channel output gx, gy + + var img = src.data, gxgy=dst.data; + + var buf0_node = jsfeat.cache.get_buffer((w+2)<<2); + var buf1_node = jsfeat.cache.get_buffer((w+2)<<2); + + if(src.type&jsfeat.U8_t || src.type&jsfeat.S32_t) { + trow0 = buf0_node.i32; + trow1 = buf1_node.i32; + } else { + trow0 = buf0_node.f32; + trow1 = buf1_node.f32; + } + + for(; y < h; ++y, srow1+=w) { + srow0 = ((y > 0 ? y-1 : 1)*w)|0; + srow2 = ((y < h-1 ? y+1 : h-2)*w)|0; + drow = (y*dstep)|0; + // do vertical convolution + for(x = 0, x1 = 1; x <= w-2; x+=2, x1+=2) { + a = img[srow0+x], b = img[srow2+x]; + trow0[x1] = ( (a + b) + (img[srow1+x]*2) ); + trow1[x1] = ( b - a ); + // + a = img[srow0+x+1], b = img[srow2+x+1]; + trow0[x1+1] = ( (a + b) + (img[srow1+x+1]*2) ); + trow1[x1+1] = ( b - a ); + } + for(; x < w; ++x, ++x1) { + a = img[srow0+x], b = img[srow2+x]; + trow0[x1] = ( (a + b) + (img[srow1+x]*2) ); + trow1[x1] = ( b - a ); + } + // make border + x = (w + 1)|0; + trow0[0] = trow0[1]; trow0[x] = trow0[w]; + trow1[0] = trow1[1]; trow1[x] = trow1[w]; + // do horizontal convolution, interleave the results and store them + for(x = 0; x <= w-4; x+=4) { + a = trow1[x+2], b = trow1[x+1], c = trow1[x+3], d = trow1[x+4], + e = trow0[x+2], f = trow0[x+3]; + gxgy[drow++] = ( e - trow0[x] ); + gxgy[drow++] = ( a + trow1[x] + b*2 ); + gxgy[drow++] = ( f - trow0[x+1] ); + gxgy[drow++] = ( c + b + a*2 ); + + gxgy[drow++] = ( trow0[x+4] - e ); + gxgy[drow++] = ( d + a + c*2 ); + gxgy[drow++] = ( trow0[x+5] - f ); + gxgy[drow++] = ( trow1[x+5] + c + d*2 ); + } + for(; x < w; ++x) { + gxgy[drow++] = ( trow0[x+2] - trow0[x] ); + gxgy[drow++] = ( trow1[x+2] + trow1[x] + trow1[x+1]*2 ); + } + } + jsfeat.cache.put_buffer(buf0_node); + jsfeat.cache.put_buffer(buf1_node); + }, + + // please note: + // dst_(type) size should be cols = src.cols+1, rows = src.rows+1 + compute_integral_image: function(src, dst_sum, dst_sqsum, dst_tilted) { + var w0=src.cols|0,h0=src.rows|0,src_d=src.data; + var w1=(w0+1)|0; + var s=0,s2=0,p=0,pup=0,i=0,j=0,v=0,k=0; + + if(dst_sum && dst_sqsum) { + // fill first row with zeros + for(; i < w1; ++i) { + dst_sum[i] = 0, dst_sqsum[i] = 0; + } + p = (w1+1)|0, pup = 1; + for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { + s = s2 = 0; + for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { + v = src_d[k]; + s += v, s2 += v*v; + dst_sum[p] = dst_sum[pup] + s; + dst_sqsum[p] = dst_sqsum[pup] + s2; + + v = src_d[k+1]; + s += v, s2 += v*v; + dst_sum[p+1] = dst_sum[pup+1] + s; + dst_sqsum[p+1] = dst_sqsum[pup+1] + s2; + } + for(; j < w0; ++j, ++k, ++p, ++pup) { + v = src_d[k]; + s += v, s2 += v*v; + dst_sum[p] = dst_sum[pup] + s; + dst_sqsum[p] = dst_sqsum[pup] + s2; + } + } + } else if(dst_sum) { + // fill first row with zeros + for(; i < w1; ++i) { + dst_sum[i] = 0; + } + p = (w1+1)|0, pup = 1; + for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { + s = 0; + for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { + s += src_d[k]; + dst_sum[p] = dst_sum[pup] + s; + s += src_d[k+1]; + dst_sum[p+1] = dst_sum[pup+1] + s; + } + for(; j < w0; ++j, ++k, ++p, ++pup) { + s += src_d[k]; + dst_sum[p] = dst_sum[pup] + s; + } + } + } else if(dst_sqsum) { + // fill first row with zeros + for(; i < w1; ++i) { + dst_sqsum[i] = 0; + } + p = (w1+1)|0, pup = 1; + for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { + s2 = 0; + for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { + v = src_d[k]; + s2 += v*v; + dst_sqsum[p] = dst_sqsum[pup] + s2; + v = src_d[k+1]; + s2 += v*v; + dst_sqsum[p+1] = dst_sqsum[pup+1] + s2; + } + for(; j < w0; ++j, ++k, ++p, ++pup) { + v = src_d[k]; + s2 += v*v; + dst_sqsum[p] = dst_sqsum[pup] + s2; + } + } + } + + if(dst_tilted) { + // fill first row with zeros + for(i = 0; i < w1; ++i) { + dst_tilted[i] = 0; + } + // diagonal + p = (w1+1)|0, pup = 0; + for(i = 0, k = 0; i < h0; ++i, ++p, ++pup) { + for(j = 0; j <= w0-2; j+=2, k+=2, p+=2, pup+=2) { + dst_tilted[p] = src_d[k] + dst_tilted[pup]; + dst_tilted[p+1] = src_d[k+1] + dst_tilted[pup+1]; + } + for(; j < w0; ++j, ++k, ++p, ++pup) { + dst_tilted[p] = src_d[k] + dst_tilted[pup]; + } + } + // diagonal + p = (w1+w0)|0, pup = w0; + for(i = 0; i < h0; ++i, p+=w1, pup+=w1) { + dst_tilted[p] += dst_tilted[pup]; + } + + for(j = w0-1; j > 0; --j) { + p = j+h0*w1, pup=p-w1; + for(i = h0; i > 0; --i, p-=w1, pup-=w1) { + dst_tilted[p] += dst_tilted[pup] + dst_tilted[pup+1]; + } + } + } + }, + equalize_histogram: function(src, dst) { + var w=src.cols,h=src.rows,src_d=src.data; + + dst.resize(w, h, src.channel); + + var dst_d=dst.data,size=w*h; + var i=0,prev=0,hist0,norm; + + var hist0_node = jsfeat.cache.get_buffer(256<<2); + hist0 = hist0_node.i32; + for(; i < 256; ++i) hist0[i] = 0; + for (i = 0; i < size; ++i) { + ++hist0[src_d[i]]; + } + + prev = hist0[0]; + for (i = 1; i < 256; ++i) { + prev = hist0[i] += prev; + } + + norm = 255 / size; + for (i = 0; i < size; ++i) { + dst_d[i] = (hist0[src_d[i]] * norm + 0.5)|0; + } + jsfeat.cache.put_buffer(hist0_node); + }, + + canny: function(src, dst, low_thresh, high_thresh) { + var w=src.cols,h=src.rows,src_d=src.data; + + dst.resize(w, h, src.channel); + + var dst_d=dst.data; + var i=0,j=0,grad=0,w2=w<<1,_grad=0,suppress=0,f=0,x=0,y=0,s=0; + var tg22x=0,tg67x=0; + + // cache buffers + var dxdy_node = jsfeat.cache.get_buffer((h * w2)<<2); + var buf_node = jsfeat.cache.get_buffer((3 * (w + 2))<<2); + var map_node = jsfeat.cache.get_buffer(((h+2) * (w + 2))<<2); + var stack_node = jsfeat.cache.get_buffer((h * w)<<2); + + + var buf = buf_node.i32; + var map = map_node.i32; + var stack = stack_node.i32; + var dxdy = dxdy_node.i32; + var dxdy_m = new jsfeat.matrix_t(w, h, jsfeat.S32C2_t, dxdy_node.data); + var row0=1,row1=(w+2+1)|0,row2=(2*(w+2)+1)|0,map_w=(w+2)|0,map_i=(map_w+1)|0,stack_i=0; + + this.sobel_derivatives(src, dxdy_m); + + if(low_thresh > high_thresh) { + i = low_thresh; + low_thresh = high_thresh; + high_thresh = i; + } + + i = (3 * (w + 2))|0; + while(--i>=0) { + buf[i] = 0; + } + + i = ((h+2) * (w + 2))|0; + while(--i>=0) { + map[i] = 0; + } + + for (; j < w; ++j, grad+=2) { + //buf[row1+j] = Math.abs(dxdy[grad]) + Math.abs(dxdy[grad+1]); + x = dxdy[grad], y = dxdy[grad+1]; + //buf[row1+j] = x*x + y*y; + buf[row1+j] = ((x ^ (x >> 31)) - (x >> 31)) + ((y ^ (y >> 31)) - (y >> 31)); + } + + for(i=1; i <= h; ++i, grad+=w2) { + if(i == h) { + j = row2+w; + while(--j>=row2) { + buf[j] = 0; + } + } else { + for (j = 0; j < w; j++) { + //buf[row2+j] = Math.abs(dxdy[grad+(j<<1)]) + Math.abs(dxdy[grad+(j<<1)+1]); + x = dxdy[grad+(j<<1)], y = dxdy[grad+(j<<1)+1]; + //buf[row2+j] = x*x + y*y; + buf[row2+j] = ((x ^ (x >> 31)) - (x >> 31)) + ((y ^ (y >> 31)) - (y >> 31)); + } + } + _grad = (grad - w2)|0; + map[map_i-1] = 0; + suppress = 0; + for(j = 0; j < w; ++j, _grad+=2) { + f = buf[row1+j]; + if (f > low_thresh) { + x = dxdy[_grad]; + y = dxdy[_grad+1]; + s = x ^ y; + // seems ot be faster than Math.abs + x = ((x ^ (x >> 31)) - (x >> 31))|0; + y = ((y ^ (y >> 31)) - (y >> 31))|0; + //x * tan(22.5) x * tan(67.5) == 2 * x + x * tan(22.5) + tg22x = x * 13573; + tg67x = tg22x + ((x + x) << 15); + y <<= 15; + if (y < tg22x) { + if (f > buf[row1+j-1] && f >= buf[row1+j+1]) { + if (f > high_thresh && !suppress && map[map_i+j-map_w] != 2) { + map[map_i+j] = 2; + suppress = 1; + stack[stack_i++] = map_i + j; + } else { + map[map_i+j] = 1; + } + continue; + } + } else if (y > tg67x) { + if (f > buf[row0+j] && f >= buf[row2+j]) { + if (f > high_thresh && !suppress && map[map_i+j-map_w] != 2) { + map[map_i+j] = 2; + suppress = 1; + stack[stack_i++] = map_i + j; + } else { + map[map_i+j] = 1; + } + continue; + } + } else { + s = s < 0 ? -1 : 1; + if (f > buf[row0+j-s] && f > buf[row2+j+s]) { + if (f > high_thresh && !suppress && map[map_i+j-map_w] != 2) { + map[map_i+j] = 2; + suppress = 1; + stack[stack_i++] = map_i + j; + } else { + map[map_i+j] = 1; + } + continue; + } + } + } + map[map_i+j] = 0; + suppress = 0; + } + map[map_i+w] = 0; + map_i += map_w; + j = row0; + row0 = row1; + row1 = row2; + row2 = j; + } + + j = map_i - map_w - 1; + for(i = 0; i < map_w; ++i, ++j) { + map[j] = 0; + } + // path following + while(stack_i > 0) { + map_i = stack[--stack_i]; + map_i -= map_w+1; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + map_i += 1; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + map_i += 1; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + map_i += map_w; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + map_i -= 2; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + map_i += map_w; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + map_i += 1; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + map_i += 1; + if(map[map_i] == 1) map[map_i] = 2, stack[stack_i++] = map_i; + } + + map_i = map_w + 1; + row0 = 0; + for(i = 0; i < h; ++i, map_i+=map_w) { + for(j = 0; j < w; ++j) { + dst_d[row0++] = (map[map_i+j] == 2) * 0xff; + } + } + + // free buffers + jsfeat.cache.put_buffer(dxdy_node); + jsfeat.cache.put_buffer(buf_node); + jsfeat.cache.put_buffer(map_node); + jsfeat.cache.put_buffer(stack_node); + }, + // transform is 3x3 matrix_t + warp_perspective: function(src, dst, transform, fill_value) { + if (typeof fill_value === "undefined") { fill_value = 0; } + var src_width=src.cols|0, src_height=src.rows|0, dst_width=dst.cols|0, dst_height=dst.rows|0; + var src_d=src.data, dst_d=dst.data; + var x=0,y=0,off=0,ixs=0,iys=0,xs=0.0,ys=0.0,xs0=0.0,ys0=0.0,ws=0.0,sc=0.0,a=0.0,b=0.0,p0=0.0,p1=0.0; + var td=transform.data; + var m00=td[0],m01=td[1],m02=td[2], + m10=td[3],m11=td[4],m12=td[5], + m20=td[6],m21=td[7],m22=td[8]; + + for(var dptr = 0; y < dst_height; ++y) { + xs0 = m01 * y + m02, + ys0 = m11 * y + m12, + ws = m21 * y + m22; + for(x = 0; x < dst_width; ++x, ++dptr, xs0+=m00, ys0+=m10, ws+=m20) { + sc = 1.0 / ws; + xs = xs0 * sc, ys = ys0 * sc; + ixs = xs | 0, iys = ys | 0; + + if(xs > 0 && ys > 0 && ixs < (src_width - 1) && iys < (src_height - 1)) { + a = Math.max(xs - ixs, 0.0); + b = Math.max(ys - iys, 0.0); + off = (src_width*iys + ixs)|0; + + p0 = src_d[off] + a * (src_d[off+1] - src_d[off]); + p1 = src_d[off+src_width] + a * (src_d[off+src_width+1] - src_d[off+src_width]); + + dst_d[dptr] = p0 + b * (p1 - p0); + } + else dst_d[dptr] = fill_value; + } + } + }, + // transform is 3x3 or 2x3 matrix_t only first 6 values referenced + warp_affine: function(src, dst, transform, fill_value) { + if (typeof fill_value === "undefined") { fill_value = 0; } + var src_width=src.cols, src_height=src.rows, dst_width=dst.cols, dst_height=dst.rows; + var src_d=src.data, dst_d=dst.data; + var x=0,y=0,off=0,ixs=0,iys=0,xs=0.0,ys=0.0,a=0.0,b=0.0,p0=0.0,p1=0.0; + var td=transform.data; + var m00=td[0],m01=td[1],m02=td[2], + m10=td[3],m11=td[4],m12=td[5]; + + for(var dptr = 0; y < dst_height; ++y) { + xs = m01 * y + m02; + ys = m11 * y + m12; + for(x = 0; x < dst_width; ++x, ++dptr, xs+=m00, ys+=m10) { + ixs = xs | 0; iys = ys | 0; + + if(ixs >= 0 && iys >= 0 && ixs < (src_width - 1) && iys < (src_height - 1)) { + a = xs - ixs; + b = ys - iys; + off = src_width*iys + ixs; + + p0 = src_d[off] + a * (src_d[off+1] - src_d[off]); + p1 = src_d[off+src_width] + a * (src_d[off+src_width+1] - src_d[off+src_width]); + + dst_d[dptr] = p0 + b * (p1 - p0); + } + else dst_d[dptr] = fill_value; + } + } + }, + + // Basic RGB Skin detection filter + // from http://popscan.blogspot.fr/2012/08/skin-detection-in-digital-images.html + skindetector: function(src,dst) { + var r,g,b,j; + var i = src.width*src.height; + while(i--){ + j = i*4; + r = src.data[j]; + g = src.data[j+1]; + b = src.data[j+2]; + if((r>95)&&(g>40)&&(b>20) + &&(r>g)&&(r>b) + &&(r-Math.min(g,b)>15) + &&(Math.abs(r-g)>15)){ + dst[i] = 255; + } else { + dst[i] = 0; + } + } + } + }; + })(); + + global.imgproc = imgproc; + +})(jsfeat); /** * @author Eugene Zatepyakin / http://inspirit.ru/ * @@ -3818,106 +3818,106 @@ The references are: fast_corners.set_threshold(20); // set default })(jsfeat); -/** - * @author Eugene Zatepyakin / http://inspirit.ru/ - * - * Copyright 2007 Computer Vision Lab, - * Ecole Polytechnique Federale de Lausanne (EPFL), Switzerland. - * @author Vincent Lepetit (http://cvlab.epfl.ch/~lepetit) - */ - -(function(global) { - "use strict"; - // - - var yape06 = (function() { - - var compute_laplacian = function(src, dst, w, h, Dxx, Dyy, sx,sy, ex,ey) { - var y=0,x=0,yrow=(sy*w+sx)|0,row=yrow; - - for(y = sy; y < ey; ++y, yrow+=w, row = yrow) { - for(x = sx; x < ex; ++x, ++row) { - dst[row] = -4 * src[row] + src[row+Dxx] + src[row-Dxx] + src[row+Dyy] + src[row-Dyy]; - } - } - } - - var hessian_min_eigen_value = function(src, off, tr, Dxx, Dyy, Dxy, Dyx) { - var Ixx = -2 * src[off] + src[off + Dxx] + src[off - Dxx]; - var Iyy = -2 * src[off] + src[off + Dyy] + src[off - Dyy]; - var Ixy = src[off + Dxy] + src[off - Dxy] - src[off + Dyx] - src[off - Dyx]; - var sqrt_delta = ( Math.sqrt(((Ixx - Iyy) * (Ixx - Iyy) + 4 * Ixy * Ixy) ) )|0; - - return Math.min(Math.abs(tr - sqrt_delta), Math.abs(-(tr + sqrt_delta))); - } - - return { - - laplacian_threshold: 30, - min_eigen_value_threshold: 25, - - detect: function(src, points, border) { - if (typeof border === "undefined") { border = 5; } - var x=0,y=0; - var w=src.cols, h=src.rows, srd_d=src.data; - var Dxx = 5, Dyy = (5 * w)|0; - var Dxy = (3 + 3 * w)|0, Dyx = (3 - 3 * w)|0; - var lap_buf = jsfeat.cache.get_buffer((w*h)<<2); - var laplacian = lap_buf.i32; - var lv=0, row=0,rowx=0,min_eigen_value=0,pt; - var number_of_points = 0; - var lap_thresh = this.laplacian_threshold; - var eigen_thresh = this.min_eigen_value_threshold; - - var sx = Math.max(5, border)|0; - var sy = Math.max(3, border)|0; - var ex = Math.min(w-5, w-border)|0; - var ey = Math.min(h-3, h-border)|0; - - x = w*h; - while(--x>=0) {laplacian[x]=0;} - compute_laplacian(srd_d, laplacian, w, h, Dxx, Dyy, sx,sy, ex,ey); - - row = (sy*w+sx)|0; - for(y = sy; y < ey; ++y, row += w) { - for(x = sx, rowx=row; x < ex; ++x, ++rowx) { - - lv = laplacian[rowx]; - if ((lv < -lap_thresh && - lv < laplacian[rowx - 1] && lv < laplacian[rowx + 1] && - lv < laplacian[rowx - w] && lv < laplacian[rowx + w] && - lv < laplacian[rowx - w - 1] && lv < laplacian[rowx + w - 1] && - lv < laplacian[rowx - w + 1] && lv < laplacian[rowx + w + 1]) - || - (lv > lap_thresh && - lv > laplacian[rowx - 1] && lv > laplacian[rowx + 1] && - lv > laplacian[rowx - w] && lv > laplacian[rowx + w] && - lv > laplacian[rowx - w - 1] && lv > laplacian[rowx + w - 1] && - lv > laplacian[rowx - w + 1] && lv > laplacian[rowx + w + 1]) - ) { - - min_eigen_value = hessian_min_eigen_value(srd_d, rowx, lv, Dxx, Dyy, Dxy, Dyx); - if (min_eigen_value > eigen_thresh) { - pt = points[number_of_points]; - pt.x = x, pt.y = y, pt.score = min_eigen_value; - ++number_of_points; - ++x, ++rowx; // skip next pixel since this is maxima in 3x3 - } - } - } - } - - jsfeat.cache.put_buffer(lap_buf); - - return number_of_points; - } - - }; - })(); - - global.yape06 = yape06; - -})(jsfeat); +/** + * @author Eugene Zatepyakin / http://inspirit.ru/ + * + * Copyright 2007 Computer Vision Lab, + * Ecole Polytechnique Federale de Lausanne (EPFL), Switzerland. + * @author Vincent Lepetit (http://cvlab.epfl.ch/~lepetit) + */ + +(function(global) { + "use strict"; + // + + var yape06 = (function() { + + var compute_laplacian = function(src, dst, w, h, Dxx, Dyy, sx,sy, ex,ey) { + var y=0,x=0,yrow=(sy*w+sx)|0,row=yrow; + + for(y = sy; y < ey; ++y, yrow+=w, row = yrow) { + for(x = sx; x < ex; ++x, ++row) { + dst[row] = -4 * src[row] + src[row+Dxx] + src[row-Dxx] + src[row+Dyy] + src[row-Dyy]; + } + } + } + + var hessian_min_eigen_value = function(src, off, tr, Dxx, Dyy, Dxy, Dyx) { + var Ixx = -2 * src[off] + src[off + Dxx] + src[off - Dxx]; + var Iyy = -2 * src[off] + src[off + Dyy] + src[off - Dyy]; + var Ixy = src[off + Dxy] + src[off - Dxy] - src[off + Dyx] - src[off - Dyx]; + var sqrt_delta = ( Math.sqrt(((Ixx - Iyy) * (Ixx - Iyy) + 4 * Ixy * Ixy) ) )|0; + + return Math.min(Math.abs(tr - sqrt_delta), Math.abs(-(tr + sqrt_delta))); + } + + return { + + laplacian_threshold: 30, + min_eigen_value_threshold: 25, + + detect: function(src, points, border) { + if (typeof border === "undefined") { border = 5; } + var x=0,y=0; + var w=src.cols, h=src.rows, srd_d=src.data; + var Dxx = 5, Dyy = (5 * w)|0; + var Dxy = (3 + 3 * w)|0, Dyx = (3 - 3 * w)|0; + var lap_buf = jsfeat.cache.get_buffer((w*h)<<2); + var laplacian = lap_buf.i32; + var lv=0, row=0,rowx=0,min_eigen_value=0,pt; + var number_of_points = 0; + var lap_thresh = this.laplacian_threshold; + var eigen_thresh = this.min_eigen_value_threshold; + + var sx = Math.max(5, border)|0; + var sy = Math.max(3, border)|0; + var ex = Math.min(w-5, w-border)|0; + var ey = Math.min(h-3, h-border)|0; + + x = w*h; + while(--x>=0) {laplacian[x]=0;} + compute_laplacian(srd_d, laplacian, w, h, Dxx, Dyy, sx,sy, ex,ey); + + row = (sy*w+sx)|0; + for(y = sy; y < ey; ++y, row += w) { + for(x = sx, rowx=row; x < ex; ++x, ++rowx) { + + lv = laplacian[rowx]; + if ((lv < -lap_thresh && + lv < laplacian[rowx - 1] && lv < laplacian[rowx + 1] && + lv < laplacian[rowx - w] && lv < laplacian[rowx + w] && + lv < laplacian[rowx - w - 1] && lv < laplacian[rowx + w - 1] && + lv < laplacian[rowx - w + 1] && lv < laplacian[rowx + w + 1]) + || + (lv > lap_thresh && + lv > laplacian[rowx - 1] && lv > laplacian[rowx + 1] && + lv > laplacian[rowx - w] && lv > laplacian[rowx + w] && + lv > laplacian[rowx - w - 1] && lv > laplacian[rowx + w - 1] && + lv > laplacian[rowx - w + 1] && lv > laplacian[rowx + w + 1]) + ) { + + min_eigen_value = hessian_min_eigen_value(srd_d, rowx, lv, Dxx, Dyy, Dxy, Dyx); + if (min_eigen_value > eigen_thresh) { + pt = points[number_of_points]; + pt.x = x, pt.y = y, pt.score = min_eigen_value; + ++number_of_points; + ++x, ++rowx; // skip next pixel since this is maxima in 3x3 + } + } + } + } + + jsfeat.cache.put_buffer(lap_buf); + + return number_of_points; + } + + }; + })(); + + global.yape06 = yape06; + +})(jsfeat); /** * @author Eugene Zatepyakin / http://inspirit.ru/ * @@ -4327,617 +4327,617 @@ The references are: global.yape = yape; -})(jsfeat); -/** - * @author Eugene Zatepyakin / http://inspirit.ru/ - * - * Original implementation derived from OpenCV, - * @authors Ethan Rublee, Vincent Rabaud, Gary Bradski - */ - -(function(global) { - "use strict"; - // - - var orb = (function() { - - var bit_pattern_31_ = new Int32Array([ - 8,-3, 9,5/*mean (0), correlation (0)*/, - 4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/, - -11,9, -8,2/*mean (3.37382e-05), correlation (0.0617409)*/, - 7,-12, 12,-13/*mean (5.62303e-05), correlation (0.0636977)*/, - 2,-13, 2,12/*mean (0.000134953), correlation (0.085099)*/, - 1,-7, 1,6/*mean (0.000528565), correlation (0.0857175)*/, - -2,-10, -2,-4/*mean (0.0188821), correlation (0.0985774)*/, - -13,-13, -11,-8/*mean (0.0363135), correlation (0.0899616)*/, - -13,-3, -12,-9/*mean (0.121806), correlation (0.099849)*/, - 10,4, 11,9/*mean (0.122065), correlation (0.093285)*/, - -13,-8, -8,-9/*mean (0.162787), correlation (0.0942748)*/, - -11,7, -9,12/*mean (0.21561), correlation (0.0974438)*/, - 7,7, 12,6/*mean (0.160583), correlation (0.130064)*/, - -4,-5, -3,0/*mean (0.228171), correlation (0.132998)*/, - -13,2, -12,-3/*mean (0.00997526), correlation (0.145926)*/, - -9,0, -7,5/*mean (0.198234), correlation (0.143636)*/, - 12,-6, 12,-1/*mean (0.0676226), correlation (0.16689)*/, - -3,6, -2,12/*mean (0.166847), correlation (0.171682)*/, - -6,-13, -4,-8/*mean (0.101215), correlation (0.179716)*/, - 11,-13, 12,-8/*mean (0.200641), correlation (0.192279)*/, - 4,7, 5,1/*mean (0.205106), correlation (0.186848)*/, - 5,-3, 10,-3/*mean (0.234908), correlation (0.192319)*/, - 3,-7, 6,12/*mean (0.0709964), correlation (0.210872)*/, - -8,-7, -6,-2/*mean (0.0939834), correlation (0.212589)*/, - -2,11, -1,-10/*mean (0.127778), correlation (0.20866)*/, - -13,12, -8,10/*mean (0.14783), correlation (0.206356)*/, - -7,3, -5,-3/*mean (0.182141), correlation (0.198942)*/, - -4,2, -3,7/*mean (0.188237), correlation (0.21384)*/, - -10,-12, -6,11/*mean (0.14865), correlation (0.23571)*/, - 5,-12, 6,-7/*mean (0.222312), correlation (0.23324)*/, - 5,-6, 7,-1/*mean (0.229082), correlation (0.23389)*/, - 1,0, 4,-5/*mean (0.241577), correlation (0.215286)*/, - 9,11, 11,-13/*mean (0.00338507), correlation (0.251373)*/, - 4,7, 4,12/*mean (0.131005), correlation (0.257622)*/, - 2,-1, 4,4/*mean (0.152755), correlation (0.255205)*/, - -4,-12, -2,7/*mean (0.182771), correlation (0.244867)*/, - -8,-5, -7,-10/*mean (0.186898), correlation (0.23901)*/, - 4,11, 9,12/*mean (0.226226), correlation (0.258255)*/, - 0,-8, 1,-13/*mean (0.0897886), correlation (0.274827)*/, - -13,-2, -8,2/*mean (0.148774), correlation (0.28065)*/, - -3,-2, -2,3/*mean (0.153048), correlation (0.283063)*/, - -6,9, -4,-9/*mean (0.169523), correlation (0.278248)*/, - 8,12, 10,7/*mean (0.225337), correlation (0.282851)*/, - 0,9, 1,3/*mean (0.226687), correlation (0.278734)*/, - 7,-5, 11,-10/*mean (0.00693882), correlation (0.305161)*/, - -13,-6, -11,0/*mean (0.0227283), correlation (0.300181)*/, - 10,7, 12,1/*mean (0.125517), correlation (0.31089)*/, - -6,-3, -6,12/*mean (0.131748), correlation (0.312779)*/, - 10,-9, 12,-4/*mean (0.144827), correlation (0.292797)*/, - -13,8, -8,-12/*mean (0.149202), correlation (0.308918)*/, - -13,0, -8,-4/*mean (0.160909), correlation (0.310013)*/, - 3,3, 7,8/*mean (0.177755), correlation (0.309394)*/, - 5,7, 10,-7/*mean (0.212337), correlation (0.310315)*/, - -1,7, 1,-12/*mean (0.214429), correlation (0.311933)*/, - 3,-10, 5,6/*mean (0.235807), correlation (0.313104)*/, - 2,-4, 3,-10/*mean (0.00494827), correlation (0.344948)*/, - -13,0, -13,5/*mean (0.0549145), correlation (0.344675)*/, - -13,-7, -12,12/*mean (0.103385), correlation (0.342715)*/, - -13,3, -11,8/*mean (0.134222), correlation (0.322922)*/, - -7,12, -4,7/*mean (0.153284), correlation (0.337061)*/, - 6,-10, 12,8/*mean (0.154881), correlation (0.329257)*/, - -9,-1, -7,-6/*mean (0.200967), correlation (0.33312)*/, - -2,-5, 0,12/*mean (0.201518), correlation (0.340635)*/, - -12,5, -7,5/*mean (0.207805), correlation (0.335631)*/, - 3,-10, 8,-13/*mean (0.224438), correlation (0.34504)*/, - -7,-7, -4,5/*mean (0.239361), correlation (0.338053)*/, - -3,-2, -1,-7/*mean (0.240744), correlation (0.344322)*/, - 2,9, 5,-11/*mean (0.242949), correlation (0.34145)*/, - -11,-13, -5,-13/*mean (0.244028), correlation (0.336861)*/, - -1,6, 0,-1/*mean (0.247571), correlation (0.343684)*/, - 5,-3, 5,2/*mean (0.000697256), correlation (0.357265)*/, - -4,-13, -4,12/*mean (0.00213675), correlation (0.373827)*/, - -9,-6, -9,6/*mean (0.0126856), correlation (0.373938)*/, - -12,-10, -8,-4/*mean (0.0152497), correlation (0.364237)*/, - 10,2, 12,-3/*mean (0.0299933), correlation (0.345292)*/, - 7,12, 12,12/*mean (0.0307242), correlation (0.366299)*/, - -7,-13, -6,5/*mean (0.0534975), correlation (0.368357)*/, - -4,9, -3,4/*mean (0.099865), correlation (0.372276)*/, - 7,-1, 12,2/*mean (0.117083), correlation (0.364529)*/, - -7,6, -5,1/*mean (0.126125), correlation (0.369606)*/, - -13,11, -12,5/*mean (0.130364), correlation (0.358502)*/, - -3,7, -2,-6/*mean (0.131691), correlation (0.375531)*/, - 7,-8, 12,-7/*mean (0.160166), correlation (0.379508)*/, - -13,-7, -11,-12/*mean (0.167848), correlation (0.353343)*/, - 1,-3, 12,12/*mean (0.183378), correlation (0.371916)*/, - 2,-6, 3,0/*mean (0.228711), correlation (0.371761)*/, - -4,3, -2,-13/*mean (0.247211), correlation (0.364063)*/, - -1,-13, 1,9/*mean (0.249325), correlation (0.378139)*/, - 7,1, 8,-6/*mean (0.000652272), correlation (0.411682)*/, - 1,-1, 3,12/*mean (0.00248538), correlation (0.392988)*/, - 9,1, 12,6/*mean (0.0206815), correlation (0.386106)*/, - -1,-9, -1,3/*mean (0.0364485), correlation (0.410752)*/, - -13,-13, -10,5/*mean (0.0376068), correlation (0.398374)*/, - 7,7, 10,12/*mean (0.0424202), correlation (0.405663)*/, - 12,-5, 12,9/*mean (0.0942645), correlation (0.410422)*/, - 6,3, 7,11/*mean (0.1074), correlation (0.413224)*/, - 5,-13, 6,10/*mean (0.109256), correlation (0.408646)*/, - 2,-12, 2,3/*mean (0.131691), correlation (0.416076)*/, - 3,8, 4,-6/*mean (0.165081), correlation (0.417569)*/, - 2,6, 12,-13/*mean (0.171874), correlation (0.408471)*/, - 9,-12, 10,3/*mean (0.175146), correlation (0.41296)*/, - -8,4, -7,9/*mean (0.183682), correlation (0.402956)*/, - -11,12, -4,-6/*mean (0.184672), correlation (0.416125)*/, - 1,12, 2,-8/*mean (0.191487), correlation (0.386696)*/, - 6,-9, 7,-4/*mean (0.192668), correlation (0.394771)*/, - 2,3, 3,-2/*mean (0.200157), correlation (0.408303)*/, - 6,3, 11,0/*mean (0.204588), correlation (0.411762)*/, - 3,-3, 8,-8/*mean (0.205904), correlation (0.416294)*/, - 7,8, 9,3/*mean (0.213237), correlation (0.409306)*/, - -11,-5, -6,-4/*mean (0.243444), correlation (0.395069)*/, - -10,11, -5,10/*mean (0.247672), correlation (0.413392)*/, - -5,-8, -3,12/*mean (0.24774), correlation (0.411416)*/, - -10,5, -9,0/*mean (0.00213675), correlation (0.454003)*/, - 8,-1, 12,-6/*mean (0.0293635), correlation (0.455368)*/, - 4,-6, 6,-11/*mean (0.0404971), correlation (0.457393)*/, - -10,12, -8,7/*mean (0.0481107), correlation (0.448364)*/, - 4,-2, 6,7/*mean (0.050641), correlation (0.455019)*/, - -2,0, -2,12/*mean (0.0525978), correlation (0.44338)*/, - -5,-8, -5,2/*mean (0.0629667), correlation (0.457096)*/, - 7,-6, 10,12/*mean (0.0653846), correlation (0.445623)*/, - -9,-13, -8,-8/*mean (0.0858749), correlation (0.449789)*/, - -5,-13, -5,-2/*mean (0.122402), correlation (0.450201)*/, - 8,-8, 9,-13/*mean (0.125416), correlation (0.453224)*/, - -9,-11, -9,0/*mean (0.130128), correlation (0.458724)*/, - 1,-8, 1,-2/*mean (0.132467), correlation (0.440133)*/, - 7,-4, 9,1/*mean (0.132692), correlation (0.454)*/, - -2,1, -1,-4/*mean (0.135695), correlation (0.455739)*/, - 11,-6, 12,-11/*mean (0.142904), correlation (0.446114)*/, - -12,-9, -6,4/*mean (0.146165), correlation (0.451473)*/, - 3,7, 7,12/*mean (0.147627), correlation (0.456643)*/, - 5,5, 10,8/*mean (0.152901), correlation (0.455036)*/, - 0,-4, 2,8/*mean (0.167083), correlation (0.459315)*/, - -9,12, -5,-13/*mean (0.173234), correlation (0.454706)*/, - 0,7, 2,12/*mean (0.18312), correlation (0.433855)*/, - -1,2, 1,7/*mean (0.185504), correlation (0.443838)*/, - 5,11, 7,-9/*mean (0.185706), correlation (0.451123)*/, - 3,5, 6,-8/*mean (0.188968), correlation (0.455808)*/, - -13,-4, -8,9/*mean (0.191667), correlation (0.459128)*/, - -5,9, -3,-3/*mean (0.193196), correlation (0.458364)*/, - -4,-7, -3,-12/*mean (0.196536), correlation (0.455782)*/, - 6,5, 8,0/*mean (0.1972), correlation (0.450481)*/, - -7,6, -6,12/*mean (0.199438), correlation (0.458156)*/, - -13,6, -5,-2/*mean (0.211224), correlation (0.449548)*/, - 1,-10, 3,10/*mean (0.211718), correlation (0.440606)*/, - 4,1, 8,-4/*mean (0.213034), correlation (0.443177)*/, - -2,-2, 2,-13/*mean (0.234334), correlation (0.455304)*/, - 2,-12, 12,12/*mean (0.235684), correlation (0.443436)*/, - -2,-13, 0,-6/*mean (0.237674), correlation (0.452525)*/, - 4,1, 9,3/*mean (0.23962), correlation (0.444824)*/, - -6,-10, -3,-5/*mean (0.248459), correlation (0.439621)*/, - -3,-13, -1,1/*mean (0.249505), correlation (0.456666)*/, - 7,5, 12,-11/*mean (0.00119208), correlation (0.495466)*/, - 4,-2, 5,-7/*mean (0.00372245), correlation (0.484214)*/, - -13,9, -9,-5/*mean (0.00741116), correlation (0.499854)*/, - 7,1, 8,6/*mean (0.0208952), correlation (0.499773)*/, - 7,-8, 7,6/*mean (0.0220085), correlation (0.501609)*/, - -7,-4, -7,1/*mean (0.0233806), correlation (0.496568)*/, - -8,11, -7,-8/*mean (0.0236505), correlation (0.489719)*/, - -13,6, -12,-8/*mean (0.0268781), correlation (0.503487)*/, - 2,4, 3,9/*mean (0.0323324), correlation (0.501938)*/, - 10,-5, 12,3/*mean (0.0399235), correlation (0.494029)*/, - -6,-5, -6,7/*mean (0.0420153), correlation (0.486579)*/, - 8,-3, 9,-8/*mean (0.0548021), correlation (0.484237)*/, - 2,-12, 2,8/*mean (0.0616622), correlation (0.496642)*/, - -11,-2, -10,3/*mean (0.0627755), correlation (0.498563)*/, - -12,-13, -7,-9/*mean (0.0829622), correlation (0.495491)*/, - -11,0, -10,-5/*mean (0.0843342), correlation (0.487146)*/, - 5,-3, 11,8/*mean (0.0929937), correlation (0.502315)*/, - -2,-13, -1,12/*mean (0.113327), correlation (0.48941)*/, - -1,-8, 0,9/*mean (0.132119), correlation (0.467268)*/, - -13,-11, -12,-5/*mean (0.136269), correlation (0.498771)*/, - -10,-2, -10,11/*mean (0.142173), correlation (0.498714)*/, - -3,9, -2,-13/*mean (0.144141), correlation (0.491973)*/, - 2,-3, 3,2/*mean (0.14892), correlation (0.500782)*/, - -9,-13, -4,0/*mean (0.150371), correlation (0.498211)*/, - -4,6, -3,-10/*mean (0.152159), correlation (0.495547)*/, - -4,12, -2,-7/*mean (0.156152), correlation (0.496925)*/, - -6,-11, -4,9/*mean (0.15749), correlation (0.499222)*/, - 6,-3, 6,11/*mean (0.159211), correlation (0.503821)*/, - -13,11, -5,5/*mean (0.162427), correlation (0.501907)*/, - 11,11, 12,6/*mean (0.16652), correlation (0.497632)*/, - 7,-5, 12,-2/*mean (0.169141), correlation (0.484474)*/, - -1,12, 0,7/*mean (0.169456), correlation (0.495339)*/, - -4,-8, -3,-2/*mean (0.171457), correlation (0.487251)*/, - -7,1, -6,7/*mean (0.175), correlation (0.500024)*/, - -13,-12, -8,-13/*mean (0.175866), correlation (0.497523)*/, - -7,-2, -6,-8/*mean (0.178273), correlation (0.501854)*/, - -8,5, -6,-9/*mean (0.181107), correlation (0.494888)*/, - -5,-1, -4,5/*mean (0.190227), correlation (0.482557)*/, - -13,7, -8,10/*mean (0.196739), correlation (0.496503)*/, - 1,5, 5,-13/*mean (0.19973), correlation (0.499759)*/, - 1,0, 10,-13/*mean (0.204465), correlation (0.49873)*/, - 9,12, 10,-1/*mean (0.209334), correlation (0.49063)*/, - 5,-8, 10,-9/*mean (0.211134), correlation (0.503011)*/, - -1,11, 1,-13/*mean (0.212), correlation (0.499414)*/, - -9,-3, -6,2/*mean (0.212168), correlation (0.480739)*/, - -1,-10, 1,12/*mean (0.212731), correlation (0.502523)*/, - -13,1, -8,-10/*mean (0.21327), correlation (0.489786)*/, - 8,-11, 10,-6/*mean (0.214159), correlation (0.488246)*/, - 2,-13, 3,-6/*mean (0.216993), correlation (0.50287)*/, - 7,-13, 12,-9/*mean (0.223639), correlation (0.470502)*/, - -10,-10, -5,-7/*mean (0.224089), correlation (0.500852)*/, - -10,-8, -8,-13/*mean (0.228666), correlation (0.502629)*/, - 4,-6, 8,5/*mean (0.22906), correlation (0.498305)*/, - 3,12, 8,-13/*mean (0.233378), correlation (0.503825)*/, - -4,2, -3,-3/*mean (0.234323), correlation (0.476692)*/, - 5,-13, 10,-12/*mean (0.236392), correlation (0.475462)*/, - 4,-13, 5,-1/*mean (0.236842), correlation (0.504132)*/, - -9,9, -4,3/*mean (0.236977), correlation (0.497739)*/, - 0,3, 3,-9/*mean (0.24314), correlation (0.499398)*/, - -12,1, -6,1/*mean (0.243297), correlation (0.489447)*/, - 3,2, 4,-8/*mean (0.00155196), correlation (0.553496)*/, - -10,-10, -10,9/*mean (0.00239541), correlation (0.54297)*/, - 8,-13, 12,12/*mean (0.0034413), correlation (0.544361)*/, - -8,-12, -6,-5/*mean (0.003565), correlation (0.551225)*/, - 2,2, 3,7/*mean (0.00835583), correlation (0.55285)*/, - 10,6, 11,-8/*mean (0.00885065), correlation (0.540913)*/, - 6,8, 8,-12/*mean (0.0101552), correlation (0.551085)*/, - -7,10, -6,5/*mean (0.0102227), correlation (0.533635)*/, - -3,-9, -3,9/*mean (0.0110211), correlation (0.543121)*/, - -1,-13, -1,5/*mean (0.0113473), correlation (0.550173)*/, - -3,-7, -3,4/*mean (0.0140913), correlation (0.554774)*/, - -8,-2, -8,3/*mean (0.017049), correlation (0.55461)*/, - 4,2, 12,12/*mean (0.01778), correlation (0.546921)*/, - 2,-5, 3,11/*mean (0.0224022), correlation (0.549667)*/, - 6,-9, 11,-13/*mean (0.029161), correlation (0.546295)*/, - 3,-1, 7,12/*mean (0.0303081), correlation (0.548599)*/, - 11,-1, 12,4/*mean (0.0355151), correlation (0.523943)*/, - -3,0, -3,6/*mean (0.0417904), correlation (0.543395)*/, - 4,-11, 4,12/*mean (0.0487292), correlation (0.542818)*/, - 2,-4, 2,1/*mean (0.0575124), correlation (0.554888)*/, - -10,-6, -8,1/*mean (0.0594242), correlation (0.544026)*/, - -13,7, -11,1/*mean (0.0597391), correlation (0.550524)*/, - -13,12, -11,-13/*mean (0.0608974), correlation (0.55383)*/, - 6,0, 11,-13/*mean (0.065126), correlation (0.552006)*/, - 0,-1, 1,4/*mean (0.074224), correlation (0.546372)*/, - -13,3, -9,-2/*mean (0.0808592), correlation (0.554875)*/, - -9,8, -6,-3/*mean (0.0883378), correlation (0.551178)*/, - -13,-6, -8,-2/*mean (0.0901035), correlation (0.548446)*/, - 5,-9, 8,10/*mean (0.0949843), correlation (0.554694)*/, - 2,7, 3,-9/*mean (0.0994152), correlation (0.550979)*/, - -1,-6, -1,-1/*mean (0.10045), correlation (0.552714)*/, - 9,5, 11,-2/*mean (0.100686), correlation (0.552594)*/, - 11,-3, 12,-8/*mean (0.101091), correlation (0.532394)*/, - 3,0, 3,5/*mean (0.101147), correlation (0.525576)*/, - -1,4, 0,10/*mean (0.105263), correlation (0.531498)*/, - 3,-6, 4,5/*mean (0.110785), correlation (0.540491)*/, - -13,0, -10,5/*mean (0.112798), correlation (0.536582)*/, - 5,8, 12,11/*mean (0.114181), correlation (0.555793)*/, - 8,9, 9,-6/*mean (0.117431), correlation (0.553763)*/, - 7,-4, 8,-12/*mean (0.118522), correlation (0.553452)*/, - -10,4, -10,9/*mean (0.12094), correlation (0.554785)*/, - 7,3, 12,4/*mean (0.122582), correlation (0.555825)*/, - 9,-7, 10,-2/*mean (0.124978), correlation (0.549846)*/, - 7,0, 12,-2/*mean (0.127002), correlation (0.537452)*/, - -1,-6, 0,-11/*mean (0.127148), correlation (0.547401)*/ - ]); - - var H = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); - var patch_img = new jsfeat.matrix_t(32, 32, jsfeat.U8_t|jsfeat.C1_t); - - var rectify_patch = function(src, dst, angle, px, py, psize) { - var cosine = Math.cos(angle); - var sine = Math.sin(angle); - - H.data[0] = cosine, H.data[1] = -sine, H.data[2] = (-cosine + sine ) * psize*0.5 + px, - H.data[3] = sine, H.data[4] = cosine, H.data[5] = (-sine - cosine) * psize*0.5 + py; - - jsfeat.imgproc.warp_affine(src, dst, H, 128); - } - - return { - - describe: function(src, corners, count, descriptors) { - var DESCR_SIZE = 32; // bytes; - var i=0,b=0,px=0.0,py=0.0,angle=0.0; - var t0=0, t1=0, val=0; - var img = src.data, w = src.cols, h = src.rows; - var patch_d = patch_img.data; - var patch_off = 16*32 + 16; // center of patch - var patt=0; - - if(!(descriptors.type&jsfeat.U8_t)) { - // relocate to U8 type - descriptors.type = jsfeat.U8_t; - descriptors.cols = DESCR_SIZE; - descriptors.rows = count; - descriptors.channel = 1; - descriptors.allocate(); - } else { - descriptors.resize(DESCR_SIZE, count, 1); - } - - var descr_d = descriptors.data; - var descr_off = 0; - - for(i = 0; i < count; ++i) { - px = corners[i].x; - py = corners[i].y; - angle = corners[i].angle; - - rectify_patch(src, patch_img, angle, px, py, 32); - - // describe the patch - patt = 0; - for (b = 0; b < DESCR_SIZE; ++b) { - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val = (t0 < t1)|0; - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val |= (t0 < t1) << 1; - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val |= (t0 < t1) << 2; - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val |= (t0 < t1) << 3; - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val |= (t0 < t1) << 4; - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val |= (t0 < t1) << 5; - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val |= (t0 < t1) << 6; - - t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 - val |= (t0 < t1) << 7; - - descr_d[descr_off+b] = val; - } - descr_off += DESCR_SIZE; - } - } - }; - })(); - - global.orb = orb; - -})(jsfeat); -/** - * @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); +})(jsfeat); +/** + * @author Eugene Zatepyakin / http://inspirit.ru/ + * + * Original implementation derived from OpenCV, + * @authors Ethan Rublee, Vincent Rabaud, Gary Bradski + */ + +(function(global) { + "use strict"; + // + + var orb = (function() { + + var bit_pattern_31_ = new Int32Array([ + 8,-3, 9,5/*mean (0), correlation (0)*/, + 4,2, 7,-12/*mean (1.12461e-05), correlation (0.0437584)*/, + -11,9, -8,2/*mean (3.37382e-05), correlation (0.0617409)*/, + 7,-12, 12,-13/*mean (5.62303e-05), correlation (0.0636977)*/, + 2,-13, 2,12/*mean (0.000134953), correlation (0.085099)*/, + 1,-7, 1,6/*mean (0.000528565), correlation (0.0857175)*/, + -2,-10, -2,-4/*mean (0.0188821), correlation (0.0985774)*/, + -13,-13, -11,-8/*mean (0.0363135), correlation (0.0899616)*/, + -13,-3, -12,-9/*mean (0.121806), correlation (0.099849)*/, + 10,4, 11,9/*mean (0.122065), correlation (0.093285)*/, + -13,-8, -8,-9/*mean (0.162787), correlation (0.0942748)*/, + -11,7, -9,12/*mean (0.21561), correlation (0.0974438)*/, + 7,7, 12,6/*mean (0.160583), correlation (0.130064)*/, + -4,-5, -3,0/*mean (0.228171), correlation (0.132998)*/, + -13,2, -12,-3/*mean (0.00997526), correlation (0.145926)*/, + -9,0, -7,5/*mean (0.198234), correlation (0.143636)*/, + 12,-6, 12,-1/*mean (0.0676226), correlation (0.16689)*/, + -3,6, -2,12/*mean (0.166847), correlation (0.171682)*/, + -6,-13, -4,-8/*mean (0.101215), correlation (0.179716)*/, + 11,-13, 12,-8/*mean (0.200641), correlation (0.192279)*/, + 4,7, 5,1/*mean (0.205106), correlation (0.186848)*/, + 5,-3, 10,-3/*mean (0.234908), correlation (0.192319)*/, + 3,-7, 6,12/*mean (0.0709964), correlation (0.210872)*/, + -8,-7, -6,-2/*mean (0.0939834), correlation (0.212589)*/, + -2,11, -1,-10/*mean (0.127778), correlation (0.20866)*/, + -13,12, -8,10/*mean (0.14783), correlation (0.206356)*/, + -7,3, -5,-3/*mean (0.182141), correlation (0.198942)*/, + -4,2, -3,7/*mean (0.188237), correlation (0.21384)*/, + -10,-12, -6,11/*mean (0.14865), correlation (0.23571)*/, + 5,-12, 6,-7/*mean (0.222312), correlation (0.23324)*/, + 5,-6, 7,-1/*mean (0.229082), correlation (0.23389)*/, + 1,0, 4,-5/*mean (0.241577), correlation (0.215286)*/, + 9,11, 11,-13/*mean (0.00338507), correlation (0.251373)*/, + 4,7, 4,12/*mean (0.131005), correlation (0.257622)*/, + 2,-1, 4,4/*mean (0.152755), correlation (0.255205)*/, + -4,-12, -2,7/*mean (0.182771), correlation (0.244867)*/, + -8,-5, -7,-10/*mean (0.186898), correlation (0.23901)*/, + 4,11, 9,12/*mean (0.226226), correlation (0.258255)*/, + 0,-8, 1,-13/*mean (0.0897886), correlation (0.274827)*/, + -13,-2, -8,2/*mean (0.148774), correlation (0.28065)*/, + -3,-2, -2,3/*mean (0.153048), correlation (0.283063)*/, + -6,9, -4,-9/*mean (0.169523), correlation (0.278248)*/, + 8,12, 10,7/*mean (0.225337), correlation (0.282851)*/, + 0,9, 1,3/*mean (0.226687), correlation (0.278734)*/, + 7,-5, 11,-10/*mean (0.00693882), correlation (0.305161)*/, + -13,-6, -11,0/*mean (0.0227283), correlation (0.300181)*/, + 10,7, 12,1/*mean (0.125517), correlation (0.31089)*/, + -6,-3, -6,12/*mean (0.131748), correlation (0.312779)*/, + 10,-9, 12,-4/*mean (0.144827), correlation (0.292797)*/, + -13,8, -8,-12/*mean (0.149202), correlation (0.308918)*/, + -13,0, -8,-4/*mean (0.160909), correlation (0.310013)*/, + 3,3, 7,8/*mean (0.177755), correlation (0.309394)*/, + 5,7, 10,-7/*mean (0.212337), correlation (0.310315)*/, + -1,7, 1,-12/*mean (0.214429), correlation (0.311933)*/, + 3,-10, 5,6/*mean (0.235807), correlation (0.313104)*/, + 2,-4, 3,-10/*mean (0.00494827), correlation (0.344948)*/, + -13,0, -13,5/*mean (0.0549145), correlation (0.344675)*/, + -13,-7, -12,12/*mean (0.103385), correlation (0.342715)*/, + -13,3, -11,8/*mean (0.134222), correlation (0.322922)*/, + -7,12, -4,7/*mean (0.153284), correlation (0.337061)*/, + 6,-10, 12,8/*mean (0.154881), correlation (0.329257)*/, + -9,-1, -7,-6/*mean (0.200967), correlation (0.33312)*/, + -2,-5, 0,12/*mean (0.201518), correlation (0.340635)*/, + -12,5, -7,5/*mean (0.207805), correlation (0.335631)*/, + 3,-10, 8,-13/*mean (0.224438), correlation (0.34504)*/, + -7,-7, -4,5/*mean (0.239361), correlation (0.338053)*/, + -3,-2, -1,-7/*mean (0.240744), correlation (0.344322)*/, + 2,9, 5,-11/*mean (0.242949), correlation (0.34145)*/, + -11,-13, -5,-13/*mean (0.244028), correlation (0.336861)*/, + -1,6, 0,-1/*mean (0.247571), correlation (0.343684)*/, + 5,-3, 5,2/*mean (0.000697256), correlation (0.357265)*/, + -4,-13, -4,12/*mean (0.00213675), correlation (0.373827)*/, + -9,-6, -9,6/*mean (0.0126856), correlation (0.373938)*/, + -12,-10, -8,-4/*mean (0.0152497), correlation (0.364237)*/, + 10,2, 12,-3/*mean (0.0299933), correlation (0.345292)*/, + 7,12, 12,12/*mean (0.0307242), correlation (0.366299)*/, + -7,-13, -6,5/*mean (0.0534975), correlation (0.368357)*/, + -4,9, -3,4/*mean (0.099865), correlation (0.372276)*/, + 7,-1, 12,2/*mean (0.117083), correlation (0.364529)*/, + -7,6, -5,1/*mean (0.126125), correlation (0.369606)*/, + -13,11, -12,5/*mean (0.130364), correlation (0.358502)*/, + -3,7, -2,-6/*mean (0.131691), correlation (0.375531)*/, + 7,-8, 12,-7/*mean (0.160166), correlation (0.379508)*/, + -13,-7, -11,-12/*mean (0.167848), correlation (0.353343)*/, + 1,-3, 12,12/*mean (0.183378), correlation (0.371916)*/, + 2,-6, 3,0/*mean (0.228711), correlation (0.371761)*/, + -4,3, -2,-13/*mean (0.247211), correlation (0.364063)*/, + -1,-13, 1,9/*mean (0.249325), correlation (0.378139)*/, + 7,1, 8,-6/*mean (0.000652272), correlation (0.411682)*/, + 1,-1, 3,12/*mean (0.00248538), correlation (0.392988)*/, + 9,1, 12,6/*mean (0.0206815), correlation (0.386106)*/, + -1,-9, -1,3/*mean (0.0364485), correlation (0.410752)*/, + -13,-13, -10,5/*mean (0.0376068), correlation (0.398374)*/, + 7,7, 10,12/*mean (0.0424202), correlation (0.405663)*/, + 12,-5, 12,9/*mean (0.0942645), correlation (0.410422)*/, + 6,3, 7,11/*mean (0.1074), correlation (0.413224)*/, + 5,-13, 6,10/*mean (0.109256), correlation (0.408646)*/, + 2,-12, 2,3/*mean (0.131691), correlation (0.416076)*/, + 3,8, 4,-6/*mean (0.165081), correlation (0.417569)*/, + 2,6, 12,-13/*mean (0.171874), correlation (0.408471)*/, + 9,-12, 10,3/*mean (0.175146), correlation (0.41296)*/, + -8,4, -7,9/*mean (0.183682), correlation (0.402956)*/, + -11,12, -4,-6/*mean (0.184672), correlation (0.416125)*/, + 1,12, 2,-8/*mean (0.191487), correlation (0.386696)*/, + 6,-9, 7,-4/*mean (0.192668), correlation (0.394771)*/, + 2,3, 3,-2/*mean (0.200157), correlation (0.408303)*/, + 6,3, 11,0/*mean (0.204588), correlation (0.411762)*/, + 3,-3, 8,-8/*mean (0.205904), correlation (0.416294)*/, + 7,8, 9,3/*mean (0.213237), correlation (0.409306)*/, + -11,-5, -6,-4/*mean (0.243444), correlation (0.395069)*/, + -10,11, -5,10/*mean (0.247672), correlation (0.413392)*/, + -5,-8, -3,12/*mean (0.24774), correlation (0.411416)*/, + -10,5, -9,0/*mean (0.00213675), correlation (0.454003)*/, + 8,-1, 12,-6/*mean (0.0293635), correlation (0.455368)*/, + 4,-6, 6,-11/*mean (0.0404971), correlation (0.457393)*/, + -10,12, -8,7/*mean (0.0481107), correlation (0.448364)*/, + 4,-2, 6,7/*mean (0.050641), correlation (0.455019)*/, + -2,0, -2,12/*mean (0.0525978), correlation (0.44338)*/, + -5,-8, -5,2/*mean (0.0629667), correlation (0.457096)*/, + 7,-6, 10,12/*mean (0.0653846), correlation (0.445623)*/, + -9,-13, -8,-8/*mean (0.0858749), correlation (0.449789)*/, + -5,-13, -5,-2/*mean (0.122402), correlation (0.450201)*/, + 8,-8, 9,-13/*mean (0.125416), correlation (0.453224)*/, + -9,-11, -9,0/*mean (0.130128), correlation (0.458724)*/, + 1,-8, 1,-2/*mean (0.132467), correlation (0.440133)*/, + 7,-4, 9,1/*mean (0.132692), correlation (0.454)*/, + -2,1, -1,-4/*mean (0.135695), correlation (0.455739)*/, + 11,-6, 12,-11/*mean (0.142904), correlation (0.446114)*/, + -12,-9, -6,4/*mean (0.146165), correlation (0.451473)*/, + 3,7, 7,12/*mean (0.147627), correlation (0.456643)*/, + 5,5, 10,8/*mean (0.152901), correlation (0.455036)*/, + 0,-4, 2,8/*mean (0.167083), correlation (0.459315)*/, + -9,12, -5,-13/*mean (0.173234), correlation (0.454706)*/, + 0,7, 2,12/*mean (0.18312), correlation (0.433855)*/, + -1,2, 1,7/*mean (0.185504), correlation (0.443838)*/, + 5,11, 7,-9/*mean (0.185706), correlation (0.451123)*/, + 3,5, 6,-8/*mean (0.188968), correlation (0.455808)*/, + -13,-4, -8,9/*mean (0.191667), correlation (0.459128)*/, + -5,9, -3,-3/*mean (0.193196), correlation (0.458364)*/, + -4,-7, -3,-12/*mean (0.196536), correlation (0.455782)*/, + 6,5, 8,0/*mean (0.1972), correlation (0.450481)*/, + -7,6, -6,12/*mean (0.199438), correlation (0.458156)*/, + -13,6, -5,-2/*mean (0.211224), correlation (0.449548)*/, + 1,-10, 3,10/*mean (0.211718), correlation (0.440606)*/, + 4,1, 8,-4/*mean (0.213034), correlation (0.443177)*/, + -2,-2, 2,-13/*mean (0.234334), correlation (0.455304)*/, + 2,-12, 12,12/*mean (0.235684), correlation (0.443436)*/, + -2,-13, 0,-6/*mean (0.237674), correlation (0.452525)*/, + 4,1, 9,3/*mean (0.23962), correlation (0.444824)*/, + -6,-10, -3,-5/*mean (0.248459), correlation (0.439621)*/, + -3,-13, -1,1/*mean (0.249505), correlation (0.456666)*/, + 7,5, 12,-11/*mean (0.00119208), correlation (0.495466)*/, + 4,-2, 5,-7/*mean (0.00372245), correlation (0.484214)*/, + -13,9, -9,-5/*mean (0.00741116), correlation (0.499854)*/, + 7,1, 8,6/*mean (0.0208952), correlation (0.499773)*/, + 7,-8, 7,6/*mean (0.0220085), correlation (0.501609)*/, + -7,-4, -7,1/*mean (0.0233806), correlation (0.496568)*/, + -8,11, -7,-8/*mean (0.0236505), correlation (0.489719)*/, + -13,6, -12,-8/*mean (0.0268781), correlation (0.503487)*/, + 2,4, 3,9/*mean (0.0323324), correlation (0.501938)*/, + 10,-5, 12,3/*mean (0.0399235), correlation (0.494029)*/, + -6,-5, -6,7/*mean (0.0420153), correlation (0.486579)*/, + 8,-3, 9,-8/*mean (0.0548021), correlation (0.484237)*/, + 2,-12, 2,8/*mean (0.0616622), correlation (0.496642)*/, + -11,-2, -10,3/*mean (0.0627755), correlation (0.498563)*/, + -12,-13, -7,-9/*mean (0.0829622), correlation (0.495491)*/, + -11,0, -10,-5/*mean (0.0843342), correlation (0.487146)*/, + 5,-3, 11,8/*mean (0.0929937), correlation (0.502315)*/, + -2,-13, -1,12/*mean (0.113327), correlation (0.48941)*/, + -1,-8, 0,9/*mean (0.132119), correlation (0.467268)*/, + -13,-11, -12,-5/*mean (0.136269), correlation (0.498771)*/, + -10,-2, -10,11/*mean (0.142173), correlation (0.498714)*/, + -3,9, -2,-13/*mean (0.144141), correlation (0.491973)*/, + 2,-3, 3,2/*mean (0.14892), correlation (0.500782)*/, + -9,-13, -4,0/*mean (0.150371), correlation (0.498211)*/, + -4,6, -3,-10/*mean (0.152159), correlation (0.495547)*/, + -4,12, -2,-7/*mean (0.156152), correlation (0.496925)*/, + -6,-11, -4,9/*mean (0.15749), correlation (0.499222)*/, + 6,-3, 6,11/*mean (0.159211), correlation (0.503821)*/, + -13,11, -5,5/*mean (0.162427), correlation (0.501907)*/, + 11,11, 12,6/*mean (0.16652), correlation (0.497632)*/, + 7,-5, 12,-2/*mean (0.169141), correlation (0.484474)*/, + -1,12, 0,7/*mean (0.169456), correlation (0.495339)*/, + -4,-8, -3,-2/*mean (0.171457), correlation (0.487251)*/, + -7,1, -6,7/*mean (0.175), correlation (0.500024)*/, + -13,-12, -8,-13/*mean (0.175866), correlation (0.497523)*/, + -7,-2, -6,-8/*mean (0.178273), correlation (0.501854)*/, + -8,5, -6,-9/*mean (0.181107), correlation (0.494888)*/, + -5,-1, -4,5/*mean (0.190227), correlation (0.482557)*/, + -13,7, -8,10/*mean (0.196739), correlation (0.496503)*/, + 1,5, 5,-13/*mean (0.19973), correlation (0.499759)*/, + 1,0, 10,-13/*mean (0.204465), correlation (0.49873)*/, + 9,12, 10,-1/*mean (0.209334), correlation (0.49063)*/, + 5,-8, 10,-9/*mean (0.211134), correlation (0.503011)*/, + -1,11, 1,-13/*mean (0.212), correlation (0.499414)*/, + -9,-3, -6,2/*mean (0.212168), correlation (0.480739)*/, + -1,-10, 1,12/*mean (0.212731), correlation (0.502523)*/, + -13,1, -8,-10/*mean (0.21327), correlation (0.489786)*/, + 8,-11, 10,-6/*mean (0.214159), correlation (0.488246)*/, + 2,-13, 3,-6/*mean (0.216993), correlation (0.50287)*/, + 7,-13, 12,-9/*mean (0.223639), correlation (0.470502)*/, + -10,-10, -5,-7/*mean (0.224089), correlation (0.500852)*/, + -10,-8, -8,-13/*mean (0.228666), correlation (0.502629)*/, + 4,-6, 8,5/*mean (0.22906), correlation (0.498305)*/, + 3,12, 8,-13/*mean (0.233378), correlation (0.503825)*/, + -4,2, -3,-3/*mean (0.234323), correlation (0.476692)*/, + 5,-13, 10,-12/*mean (0.236392), correlation (0.475462)*/, + 4,-13, 5,-1/*mean (0.236842), correlation (0.504132)*/, + -9,9, -4,3/*mean (0.236977), correlation (0.497739)*/, + 0,3, 3,-9/*mean (0.24314), correlation (0.499398)*/, + -12,1, -6,1/*mean (0.243297), correlation (0.489447)*/, + 3,2, 4,-8/*mean (0.00155196), correlation (0.553496)*/, + -10,-10, -10,9/*mean (0.00239541), correlation (0.54297)*/, + 8,-13, 12,12/*mean (0.0034413), correlation (0.544361)*/, + -8,-12, -6,-5/*mean (0.003565), correlation (0.551225)*/, + 2,2, 3,7/*mean (0.00835583), correlation (0.55285)*/, + 10,6, 11,-8/*mean (0.00885065), correlation (0.540913)*/, + 6,8, 8,-12/*mean (0.0101552), correlation (0.551085)*/, + -7,10, -6,5/*mean (0.0102227), correlation (0.533635)*/, + -3,-9, -3,9/*mean (0.0110211), correlation (0.543121)*/, + -1,-13, -1,5/*mean (0.0113473), correlation (0.550173)*/, + -3,-7, -3,4/*mean (0.0140913), correlation (0.554774)*/, + -8,-2, -8,3/*mean (0.017049), correlation (0.55461)*/, + 4,2, 12,12/*mean (0.01778), correlation (0.546921)*/, + 2,-5, 3,11/*mean (0.0224022), correlation (0.549667)*/, + 6,-9, 11,-13/*mean (0.029161), correlation (0.546295)*/, + 3,-1, 7,12/*mean (0.0303081), correlation (0.548599)*/, + 11,-1, 12,4/*mean (0.0355151), correlation (0.523943)*/, + -3,0, -3,6/*mean (0.0417904), correlation (0.543395)*/, + 4,-11, 4,12/*mean (0.0487292), correlation (0.542818)*/, + 2,-4, 2,1/*mean (0.0575124), correlation (0.554888)*/, + -10,-6, -8,1/*mean (0.0594242), correlation (0.544026)*/, + -13,7, -11,1/*mean (0.0597391), correlation (0.550524)*/, + -13,12, -11,-13/*mean (0.0608974), correlation (0.55383)*/, + 6,0, 11,-13/*mean (0.065126), correlation (0.552006)*/, + 0,-1, 1,4/*mean (0.074224), correlation (0.546372)*/, + -13,3, -9,-2/*mean (0.0808592), correlation (0.554875)*/, + -9,8, -6,-3/*mean (0.0883378), correlation (0.551178)*/, + -13,-6, -8,-2/*mean (0.0901035), correlation (0.548446)*/, + 5,-9, 8,10/*mean (0.0949843), correlation (0.554694)*/, + 2,7, 3,-9/*mean (0.0994152), correlation (0.550979)*/, + -1,-6, -1,-1/*mean (0.10045), correlation (0.552714)*/, + 9,5, 11,-2/*mean (0.100686), correlation (0.552594)*/, + 11,-3, 12,-8/*mean (0.101091), correlation (0.532394)*/, + 3,0, 3,5/*mean (0.101147), correlation (0.525576)*/, + -1,4, 0,10/*mean (0.105263), correlation (0.531498)*/, + 3,-6, 4,5/*mean (0.110785), correlation (0.540491)*/, + -13,0, -10,5/*mean (0.112798), correlation (0.536582)*/, + 5,8, 12,11/*mean (0.114181), correlation (0.555793)*/, + 8,9, 9,-6/*mean (0.117431), correlation (0.553763)*/, + 7,-4, 8,-12/*mean (0.118522), correlation (0.553452)*/, + -10,4, -10,9/*mean (0.12094), correlation (0.554785)*/, + 7,3, 12,4/*mean (0.122582), correlation (0.555825)*/, + 9,-7, 10,-2/*mean (0.124978), correlation (0.549846)*/, + 7,0, 12,-2/*mean (0.127002), correlation (0.537452)*/, + -1,-6, 0,-11/*mean (0.127148), correlation (0.547401)*/ + ]); + + var H = new jsfeat.matrix_t(3, 3, jsfeat.F32_t|jsfeat.C1_t); + var patch_img = new jsfeat.matrix_t(32, 32, jsfeat.U8_t|jsfeat.C1_t); + + var rectify_patch = function(src, dst, angle, px, py, psize) { + var cosine = Math.cos(angle); + var sine = Math.sin(angle); + + H.data[0] = cosine, H.data[1] = -sine, H.data[2] = (-cosine + sine ) * psize*0.5 + px, + H.data[3] = sine, H.data[4] = cosine, H.data[5] = (-sine - cosine) * psize*0.5 + py; + + jsfeat.imgproc.warp_affine(src, dst, H, 128); + } + + return { + + describe: function(src, corners, count, descriptors) { + var DESCR_SIZE = 32; // bytes; + var i=0,b=0,px=0.0,py=0.0,angle=0.0; + var t0=0, t1=0, val=0; + var img = src.data, w = src.cols, h = src.rows; + var patch_d = patch_img.data; + var patch_off = 16*32 + 16; // center of patch + var patt=0; + + if(!(descriptors.type&jsfeat.U8_t)) { + // relocate to U8 type + descriptors.type = jsfeat.U8_t; + descriptors.cols = DESCR_SIZE; + descriptors.rows = count; + descriptors.channel = 1; + descriptors.allocate(); + } else { + descriptors.resize(DESCR_SIZE, count, 1); + } + + var descr_d = descriptors.data; + var descr_off = 0; + + for(i = 0; i < count; ++i) { + px = corners[i].x; + py = corners[i].y; + angle = corners[i].angle; + + rectify_patch(src, patch_img, angle, px, py, 32); + + // describe the patch + patt = 0; + for (b = 0; b < DESCR_SIZE; ++b) { + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val = (t0 < t1)|0; + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val |= (t0 < t1) << 1; + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val |= (t0 < t1) << 2; + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val |= (t0 < t1) << 3; + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val |= (t0 < t1) << 4; + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val |= (t0 < t1) << 5; + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val |= (t0 < t1) << 6; + + t0 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + t1 = patch_d[patch_off + bit_pattern_31_[patt+1] * 32 + bit_pattern_31_[patt]]; patt += 2 + val |= (t0 < t1) << 7; + + descr_d[descr_off+b] = val; + } + descr_off += DESCR_SIZE; + } + } + }; + })(); + + global.orb = orb; + +})(jsfeat); +/** + * @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); /** * @author Eugene Zatepyakin / http://inspirit.ru/ * @@ -4965,7 +4965,8 @@ The references are: edges_density: 0.07, - detect_single_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale, classifier) { + detect_single_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale, classifier,flipClassifer) { + if(typeof flipClassifer =='undefined') flipClassifer=false; var win_w = (classifier.size[0] * scale)|0, win_h = (classifier.size[1] * scale)|0, step_x = (0.5 * scale + 1.5)|0, @@ -4985,7 +4986,6 @@ The references are: for(y = 0; y < ey; y += step_y) { ii_a = y * w1; for(x = 0; x < ex; x += step_x, ii_a += step_x) { - mean = int_sum[ii_a] - int_sum[ii_a+ii_b] - int_sum[ii_a+ii_c] @@ -5028,29 +5028,57 @@ The references are: if(tree.tilted === 1) { for(k=0; k < fn; ++k) { feature = features[k]; - fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; - fw = ~~(feature[2] * scale); - fh = ~~(feature[3] * scale); - fi_b = fw * w1; - fi_c = fh * w1; - - tree_sum += (int_tilted[fi_a] - - int_tilted[fi_a + fw + fi_b] - - int_tilted[fi_a - fh + fi_c] - + int_tilted[fi_a + fw - fh + fi_b + fi_c]) * feature[4]; + if(flipClassifer){ + fi_a = ~~((x+win_w)-(feature[0] * scale)-(feature[2] * scale)) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_b = fw * w1; + fi_c = fh * w1; + + tree_sum += (int_tilted[fi_a] + - int_tilted[fi_a + fw + fi_b] + - int_tilted[fi_a - fh + fi_c] + + int_tilted[fi_a + fw - fh + fi_b + fi_c]) * feature[4]; + } + else{ + fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_b = fw * w1; + fi_c = fh * w1; + + tree_sum += (int_tilted[fi_a] + - int_tilted[fi_a + fw + fi_b] + - int_tilted[fi_a - fh + fi_c] + + int_tilted[fi_a + fw - fh + fi_b + fi_c]) * feature[4]; + } } } else { for(k=0; k < fn; ++k) { feature = features[k]; - fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; - fw = ~~(feature[2] * scale); - fh = ~~(feature[3] * scale); - fi_c = fh * w1; - - tree_sum += (int_sum[fi_a] - - int_sum[fi_a+fw] - - int_sum[fi_a+fi_c] - + int_sum[fi_a+fi_c+fw]) * feature[4]; + if(flipClassifer){ + fi_a = ~~((x+win_w)-(feature[0] * scale)-(feature[2] * scale)) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_c = fh * w1; + + tree_sum += (int_sum[fi_a] + -int_sum[fi_a+fw] + - int_sum[fi_a+fi_c] + + int_sum[fi_a+fi_c+fw]) * feature[4]; + } + else{ + fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_c = fh * w1; + + tree_sum += (int_sum[fi_a] + - int_sum[fi_a+fw] + - int_sum[fi_a+fi_c] + + int_sum[fi_a+fi_c+fw]) * feature[4]; + } + } } stage_sum += (tree_sum * inv_area < tree.threshold * std) ? tree.left_val : tree.right_val; @@ -5075,14 +5103,15 @@ The references are: return rects; }, - detect_multi_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, classifier, scale_factor, scale_min) { + detect_multi_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, classifier, scale_factor, scale_min, flipClassifier) { + if(typeof flipClassifier==="undefined") { flipClassifier=false;} if (typeof scale_factor === "undefined") { scale_factor = 1.2; } if (typeof scale_min === "undefined") { scale_min = 1.0; } var win_w = classifier.size[0]; var win_h = classifier.size[1]; var rects = []; while (scale_min * win_w < width && scale_min * win_h < height) { - rects = rects.concat(this.detect_single_scale(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale_min, classifier)); + rects = rects.concat(this.detect_single_scale(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale_min, classifier, flipClassifier)); scale_min *= scale_factor; } return rects; @@ -5622,18 +5651,18 @@ The references are: global.bbf = bbf; })(jsfeat); -/** - * @author Eugene Zatepyakin / http://inspirit.ru/ - */ - -(function(lib) { - "use strict"; - - if (typeof module === "undefined" || typeof module.exports === "undefined") { - // in a browser, define its namespaces in global - window.jsfeat = lib; - } else { - // in commonjs, or when AMD wrapping has been applied, define its namespaces as exports - module.exports = lib; - } -})(jsfeat); +/** + * @author Eugene Zatepyakin / http://inspirit.ru/ + */ + +(function(lib) { + "use strict"; + + if (typeof module === "undefined" || typeof module.exports === "undefined") { + // in a browser, define its namespaces in global + window.jsfeat = lib; + } else { + // in commonjs, or when AMD wrapping has been applied, define its namespaces as exports + module.exports = lib; + } +})(jsfeat); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..951978e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "jsfeat", + "version": "0.0.8", + "lockfileVersion": 1 +} diff --git a/package.json b/package.json index 6bc3310..0733c07 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name" : "jsfeat", - "version" : "0.0.8", - "description" : "JavaScript Computer Vision library", - "author" : "Eugene Zatepyakin (http://www.inspirit.ru/)", - "files" : "build/jsfeat.js", - "main" : "build/jsfeat" + "name": "jsfeat", + "version": "0.0.8", + "description": "JavaScript Computer Vision library", + "author": "Eugene Zatepyakin (http://www.inspirit.ru/)", + "files": "build/jsfeat.js", + "main": "build/jsfeat" } diff --git a/src/jsfeat_haar.js b/src/jsfeat_haar.js index 7d29544..91abc03 100755 --- a/src/jsfeat_haar.js +++ b/src/jsfeat_haar.js @@ -25,7 +25,8 @@ edges_density: 0.07, - detect_single_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale, classifier) { + detect_single_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale, classifier,flipClassifer) { + if(typeof flipClassifer =='undefined') flipClassifer=false; var win_w = (classifier.size[0] * scale)|0, win_h = (classifier.size[1] * scale)|0, step_x = (0.5 * scale + 1.5)|0, @@ -88,29 +89,57 @@ if(tree.tilted === 1) { for(k=0; k < fn; ++k) { feature = features[k]; - fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; - fw = ~~(feature[2] * scale); - fh = ~~(feature[3] * scale); - fi_b = fw * w1; - fi_c = fh * w1; - - tree_sum += (int_tilted[fi_a] - - int_tilted[fi_a + fw + fi_b] - - int_tilted[fi_a - fh + fi_c] - + int_tilted[fi_a + fw - fh + fi_b + fi_c]) * feature[4]; + if(flipClassifer){ + fi_a = ~~((x+win_w)-(feature[0] * scale)-(feature[2] * scale)) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_b = fw * w1; + fi_c = fh * w1; + + tree_sum += (int_tilted[fi_a] + - int_tilted[fi_a + fw + fi_b] + - int_tilted[fi_a - fh + fi_c] + + int_tilted[fi_a + fw - fh + fi_b + fi_c]) * feature[4]; + } + else{ + fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_b = fw * w1; + fi_c = fh * w1; + + tree_sum += (int_tilted[fi_a] + - int_tilted[fi_a + fw + fi_b] + - int_tilted[fi_a - fh + fi_c] + + int_tilted[fi_a + fw - fh + fi_b + fi_c]) * feature[4]; + } } } else { for(k=0; k < fn; ++k) { feature = features[k]; - fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; - fw = ~~(feature[2] * scale); - fh = ~~(feature[3] * scale); - fi_c = fh * w1; - - tree_sum += (int_sum[fi_a] - - int_sum[fi_a+fw] - - int_sum[fi_a+fi_c] - + int_sum[fi_a+fi_c+fw]) * feature[4]; + if(flipClassifer){ + fi_a = ~~((x+win_w)-(feature[0] * scale)-(feature[2] * scale)) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_c = fh * w1; + + tree_sum += (int_sum[fi_a] + -int_sum[fi_a+fw] + - int_sum[fi_a+fi_c] + + int_sum[fi_a+fi_c+fw]) * feature[4]; + } + else{ + fi_a = ~~(x + feature[0] * scale) + ~~(y + feature[1] * scale) * w1; + fw = ~~(feature[2] * scale); + fh = ~~(feature[3] * scale); + fi_c = fh * w1; + + tree_sum += (int_sum[fi_a] + - int_sum[fi_a+fw] + - int_sum[fi_a+fi_c] + + int_sum[fi_a+fi_c+fw]) * feature[4]; + } + } } stage_sum += (tree_sum * inv_area < tree.threshold * std) ? tree.left_val : tree.right_val; @@ -135,14 +164,15 @@ return rects; }, - detect_multi_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, classifier, scale_factor, scale_min) { + detect_multi_scale: function(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, classifier, scale_factor, scale_min, flipClassifier) { + if(typeof flipClassifier==="undefined") { flipClassifier=false;} if (typeof scale_factor === "undefined") { scale_factor = 1.2; } if (typeof scale_min === "undefined") { scale_min = 1.0; } var win_w = classifier.size[0]; var win_h = classifier.size[1]; var rects = []; while (scale_min * win_w < width && scale_min * win_h < height) { - rects = rects.concat(this.detect_single_scale(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale_min, classifier)); + rects = rects.concat(this.detect_single_scale(int_sum, int_sqsum, int_tilted, int_canny_sum, width, height, scale_min, classifier,flipClassifier)); scale_min *= scale_factor; } return rects;