UNPKG

jsfeat

Version:

JavaScript Computer Vision library

1,323 lines (1,168 loc) 226 kB
/** * @author Eugene Zatepyakin / http://inspirit.ru/ */ // namespace ? var jsfeat = jsfeat || { REVISION: 'ALPHA' }; /** * @author Eugene Zatepyakin / http://inspirit.ru/ */ (function(global) { "use strict"; // // CONSTANTS var EPSILON = 0.0000001192092896; var FLT_MIN = 1E-37; // implementation from CCV project // currently working only with u8,s32,f32 var U8_t = 0x0100, S32_t = 0x0200, F32_t = 0x0400, S64_t = 0x0800, F64_t = 0x1000; var C1_t = 0x01, C2_t = 0x02, C3_t = 0x03, C4_t = 0x04; var _data_type_size = new Int32Array([ -1, 1, 4, -1, 4, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1, 8 ]); var get_data_type = (function () { return function(type) { return (type & 0xFF00); } })(); var get_channel = (function () { return function(type) { return (type & 0xFF); } })(); var get_data_type_size = (function () { return function(type) { return _data_type_size[(type & 0xFF00) >> 8]; } })(); // color conversion var COLOR_RGBA2GRAY = 0; var COLOR_RGB2GRAY = 1; var COLOR_BGRA2GRAY = 2; var COLOR_BGR2GRAY = 3; // box blur option var BOX_BLUR_NOSCALE = 0x01; // svd options var SVD_U_T = 0x01; var SVD_V_T = 0x02; var data_t = (function () { function data_t(size_in_bytes, buffer) { // we need align size to multiple of 8 this.size = ((size_in_bytes + 7) | 0) & -8; if (typeof buffer === "undefined") { this.buffer = new ArrayBuffer(this.size); } else { this.buffer = buffer; this.size = buffer.length; } this.u8 = new Uint8Array(this.buffer); this.i32 = new Int32Array(this.buffer); this.f32 = new Float32Array(this.buffer); this.f64 = new Float64Array(this.buffer); } return data_t; })(); var matrix_t = (function () { // columns, rows, data_type function matrix_t(c, r, data_type, data_buffer) { this.type = get_data_type(data_type)|0; this.channel = get_channel(data_type)|0; this.cols = c|0; this.rows = r|0; if (typeof data_buffer === "undefined") { this.allocate(); } else { this.buffer = data_buffer; // data user asked for this.data = this.type&U8_t ? this.buffer.u8 : (this.type&S32_t ? this.buffer.i32 : (this.type&F32_t ? this.buffer.f32 : this.buffer.f64)); } } matrix_t.prototype.allocate = function() { // clear references delete this.data; delete this.buffer; // this.buffer = new data_t((this.cols * get_data_type_size(this.type) * this.channel) * this.rows); this.data = this.type&U8_t ? this.buffer.u8 : (this.type&S32_t ? this.buffer.i32 : (this.type&F32_t ? this.buffer.f32 : this.buffer.f64)); } matrix_t.prototype.copy_to = function(other) { var od = other.data, td = this.data; var i = 0, n = (this.cols*this.rows*this.channel)|0; for(; i < n-4; i+=4) { od[i] = td[i]; od[i+1] = td[i+1]; od[i+2] = td[i+2]; od[i+3] = td[i+3]; } for(; i < n; ++i) { od[i] = td[i]; } } matrix_t.prototype.resize = function(c, r, ch) { if (typeof ch === "undefined") { ch = this.channel; } // relocate buffer only if new size doesnt fit var new_size = (c * get_data_type_size(this.type) * ch) * r; if(new_size > this.buffer.size) { this.cols = c; this.rows = r; this.channel = ch; this.allocate(); } else { this.cols = c; this.rows = r; this.channel = ch; } } return matrix_t; })(); var pyramid_t = (function () { function pyramid_t(levels) { this.levels = levels|0; this.data = new Array(levels); this.pyrdown = jsfeat.imgproc.pyrdown; } pyramid_t.prototype.allocate = function(start_w, start_h, data_type) { var i = this.levels; while(--i >= 0) { this.data[i] = new matrix_t(start_w >> i, start_h >> i, data_type); } } pyramid_t.prototype.build = function(input, skip_first_level) { if (typeof skip_first_level === "undefined") { skip_first_level = true; } // just copy data to first level var i = 2, a = input, b = this.data[0]; if(!skip_first_level) { var j=input.cols*input.rows; while(--j >= 0) { b.data[j] = input.data[j]; } } b = this.data[1]; this.pyrdown(a, b); for(; i < this.levels; ++i) { a = b; b = this.data[i]; this.pyrdown(a, b); } } return pyramid_t; })(); var keypoint_t = (function () { function keypoint_t(x,y,score,level,angle) { if (typeof x === "undefined") { x=0; } if (typeof y === "undefined") { y=0; } if (typeof score === "undefined") { score=0; } if (typeof level === "undefined") { level=0; } if (typeof angle === "undefined") { angle=-1.0; } this.x = x; this.y = y; this.score = score; this.level = level; this.angle = angle; } return keypoint_t; })(); // data types global.U8_t = U8_t; global.S32_t = S32_t; global.F32_t = F32_t; global.S64_t = S64_t; global.F64_t = F64_t; // data channels global.C1_t = C1_t; global.C2_t = C2_t; global.C3_t = C3_t; global.C4_t = C4_t; // popular formats global.U8C1_t = U8_t | C1_t; global.U8C3_t = U8_t | C3_t; global.U8C4_t = U8_t | C4_t; global.F32C1_t = F32_t | C1_t; global.F32C2_t = F32_t | C2_t; global.S32C1_t = S32_t | C1_t; global.S32C2_t = S32_t | C2_t; // constants global.EPSILON = EPSILON; global.FLT_MIN = FLT_MIN; // color convert global.COLOR_RGBA2GRAY = COLOR_RGBA2GRAY; global.COLOR_RGB2GRAY = COLOR_RGB2GRAY; global.COLOR_BGRA2GRAY = COLOR_BGRA2GRAY; global.COLOR_BGR2GRAY = COLOR_BGR2GRAY; // options global.BOX_BLUR_NOSCALE = BOX_BLUR_NOSCALE; global.SVD_U_T = SVD_U_T; global.SVD_V_T = SVD_V_T; global.get_data_type = get_data_type; global.get_channel = get_channel; global.get_data_type_size = get_data_type_size; global.data_t = data_t; global.matrix_t = matrix_t; global.pyramid_t = pyramid_t; 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 math = (function() { var qsort_stack = new Int32Array(48*2); return { get_gaussian_kernel: function(size, sigma, kernel, data_type) { var i=0,x=0.0,t=0.0,sigma_x=0.0,scale_2x=0.0; var sum = 0.0; var kern_node = jsfeat.cache.get_buffer(size<<2); var _kernel = kern_node.f32;//new Float32Array(size); if((size&1) == 1 && size <= 7 && sigma <= 0) { switch(size>>1) { case 0: _kernel[0] = 1.0; sum = 1.0; break; case 1: _kernel[0] = 0.25, _kernel[1] = 0.5, _kernel[2] = 0.25; sum = 0.25+0.5+0.25; break; case 2: _kernel[0] = 0.0625, _kernel[1] = 0.25, _kernel[2] = 0.375, _kernel[3] = 0.25, _kernel[4] = 0.0625; sum = 0.0625+0.25+0.375+0.25+0.0625; break; case 3: _kernel[0] = 0.03125, _kernel[1] = 0.109375, _kernel[2] = 0.21875, _kernel[3] = 0.28125, _kernel[4] = 0.21875, _kernel[5] = 0.109375, _kernel[6] = 0.03125; sum = 0.03125+0.109375+0.21875+0.28125+0.21875+0.109375+0.03125; break; } } else { sigma_x = sigma > 0 ? sigma : ((size-1)*0.5 - 1.0)*0.3 + 0.8; scale_2x = -0.5/(sigma_x*sigma_x); for( ; i < size; ++i ) { x = i - (size-1)*0.5; t = Math.exp(scale_2x*x*x); _kernel[i] = t; sum += t; } } if(data_type & jsfeat.U8_t) { // int based kernel sum = 256.0/sum; for (i = 0; i < size; ++i) { kernel[i] = (_kernel[i] * sum + 0.5)|0; } } else { // classic kernel sum = 1.0/sum; for (i = 0; i < size; ++i) { kernel[i] = _kernel[i] * sum; } } jsfeat.cache.put_buffer(kern_node); }, // model is 3x3 matrix_t perspective_4point_transform: function(model, src_x0, src_y0, dst_x0, dst_y0, src_x1, src_y1, dst_x1, dst_y1, src_x2, src_y2, dst_x2, dst_y2, src_x3, src_y3, dst_x3, dst_y3) { var t1 = src_x0; var t2 = src_x2; var t4 = src_y1; var t5 = t1 * t2 * t4; var t6 = src_y3; var t7 = t1 * t6; var t8 = t2 * t7; var t9 = src_y2; var t10 = t1 * t9; var t11 = src_x1; var t14 = src_y0; var t15 = src_x3; var t16 = t14 * t15; var t18 = t16 * t11; var t20 = t15 * t11 * t9; var t21 = t15 * t4; var t24 = t15 * t9; var t25 = t2 * t4; var t26 = t6 * t2; var t27 = t6 * t11; var t28 = t9 * t11; var t30 = 1.0 / (t21-t24 - t25 + t26 - t27 + t28); var t32 = t1 * t15; var t35 = t14 * t11; var t41 = t4 * t1; var t42 = t6 * t41; var t43 = t14 * t2; var t46 = t16 * t9; var t48 = t14 * t9 * t11; var t51 = t4 * t6 * t2; var t55 = t6 * t14; var Hr0 = -(t8-t5 + t10 * t11 - t11 * t7 - t16 * t2 + t18 - t20 + t21 * t2) * t30; var Hr1 = (t5 - t8 - t32 * t4 + t32 * t9 + t18 - t2 * t35 + t27 * t2 - t20) * t30; var Hr2 = t1; var Hr3 = (-t9 * t7 + t42 + t43 * t4 - t16 * t4 + t46 - t48 + t27 * t9 - t51) * t30; var Hr4 = (-t42 + t41 * t9 - t55 * t2 + t46 - t48 + t55 * t11 + t51 - t21 * t9) * t30; var Hr5 = t14; var Hr6 = (-t10 + t41 + t43 - t35 + t24 - t21 - t26 + t27) * t30; var Hr7 = (-t7 + t10 + t16 - t43 + t27 - t28 - t21 + t25) * t30; t1 = dst_x0; t2 = dst_x2; t4 = dst_y1; t5 = t1 * t2 * t4; t6 = dst_y3; t7 = t1 * t6; t8 = t2 * t7; t9 = dst_y2; t10 = t1 * t9; t11 = dst_x1; t14 = dst_y0; t15 = dst_x3; t16 = t14 * t15; t18 = t16 * t11; t20 = t15 * t11 * t9; t21 = t15 * t4; t24 = t15 * t9; t25 = t2 * t4; t26 = t6 * t2; t27 = t6 * t11; t28 = t9 * t11; t30 = 1.0 / (t21-t24 - t25 + t26 - t27 + t28); t32 = t1 * t15; t35 = t14 * t11; t41 = t4 * t1; t42 = t6 * t41; t43 = t14 * t2; t46 = t16 * t9; t48 = t14 * t9 * t11; t51 = t4 * t6 * t2; t55 = t6 * t14; var Hl0 = -(t8-t5 + t10 * t11 - t11 * t7 - t16 * t2 + t18 - t20 + t21 * t2) * t30; var Hl1 = (t5 - t8 - t32 * t4 + t32 * t9 + t18 - t2 * t35 + t27 * t2 - t20) * t30; var Hl2 = t1; var Hl3 = (-t9 * t7 + t42 + t43 * t4 - t16 * t4 + t46 - t48 + t27 * t9 - t51) * t30; var Hl4 = (-t42 + t41 * t9 - t55 * t2 + t46 - t48 + t55 * t11 + t51 - t21 * t9) * t30; var Hl5 = t14; var Hl6 = (-t10 + t41 + t43 - t35 + t24 - t21 - t26 + t27) * t30; var Hl7 = (-t7 + t10 + t16 - t43 + t27 - t28 - t21 + t25) * t30; // the following code computes R = Hl * inverse Hr t2 = Hr4-Hr7*Hr5; t4 = Hr0*Hr4; t5 = Hr0*Hr5; t7 = Hr3*Hr1; t8 = Hr2*Hr3; t10 = Hr1*Hr6; var t12 = Hr2*Hr6; t15 = 1.0 / (t4-t5*Hr7-t7+t8*Hr7+t10*Hr5-t12*Hr4); t18 = -Hr3+Hr5*Hr6; var t23 = -Hr3*Hr7+Hr4*Hr6; t28 = -Hr1+Hr2*Hr7; var t31 = Hr0-t12; t35 = Hr0*Hr7-t10; t41 = -Hr1*Hr5+Hr2*Hr4; var t44 = t5-t8; var t47 = t4-t7; t48 = t2*t15; var t49 = t28*t15; var t50 = t41*t15; var mat = model.data; mat[0] = Hl0*t48+Hl1*(t18*t15)-Hl2*(t23*t15); mat[1] = Hl0*t49+Hl1*(t31*t15)-Hl2*(t35*t15); mat[2] = -Hl0*t50-Hl1*(t44*t15)+Hl2*(t47*t15); mat[3] = Hl3*t48+Hl4*(t18*t15)-Hl5*(t23*t15); mat[4] = Hl3*t49+Hl4*(t31*t15)-Hl5*(t35*t15); mat[5] = -Hl3*t50-Hl4*(t44*t15)+Hl5*(t47*t15); mat[6] = Hl6*t48+Hl7*(t18*t15)-t23*t15; mat[7] = Hl6*t49+Hl7*(t31*t15)-t35*t15; mat[8] = -Hl6*t50-Hl7*(t44*t15)+t47*t15; }, // The current implementation was derived from *BSD system qsort(): // Copyright (c) 1992, 1993 // The Regents of the University of California. All rights reserved. qsort: function(array, low, high, cmp) { var isort_thresh = 7; var t,ta,tb,tc; var sp = 0,left=0,right=0,i=0,n=0,m=0,ptr=0,ptr2=0,d=0; var left0=0,left1=0,right0=0,right1=0,pivot=0,a=0,b=0,c=0,swap_cnt=0; var stack = qsort_stack; if( (high-low+1) <= 1 ) return; stack[0] = low; stack[1] = high; while( sp >= 0 ) { left = stack[sp<<1]; right = stack[(sp<<1)+1]; sp--; for(;;) { n = (right - left) + 1; if( n <= isort_thresh ) { //insert_sort: for( ptr = left + 1; ptr <= right; ptr++ ) { for( ptr2 = ptr; ptr2 > left && cmp(array[ptr2],array[ptr2-1]); ptr2--) { t = array[ptr2]; array[ptr2] = array[ptr2-1]; array[ptr2-1] = t; } } break; } else { swap_cnt = 0; left0 = left; right0 = right; pivot = left + (n>>1); if( n > 40 ) { d = n >> 3; a = left, b = left + d, c = left + (d<<1); ta = array[a],tb = array[b],tc = array[c]; left = cmp(ta, tb) ? (cmp(tb, tc) ? b : (cmp(ta, tc) ? c : a)) : (cmp(tc, tb) ? b : (cmp(ta, tc) ? a : c)); a = pivot - d, b = pivot, c = pivot + d; ta = array[a],tb = array[b],tc = array[c]; pivot = cmp(ta, tb) ? (cmp(tb, tc) ? b : (cmp(ta, tc) ? c : a)) : (cmp(tc, tb) ? b : (cmp(ta, tc) ? a : c)); a = right - (d<<1), b = right - d, c = right; ta = array[a],tb = array[b],tc = array[c]; right = cmp(ta, tb) ? (cmp(tb, tc) ? b : (cmp(ta, tc) ? c : a)) : (cmp(tc, tb) ? b : (cmp(ta, tc) ? a : c)); } a = left, b = pivot, c = right; ta = array[a],tb = array[b],tc = array[c]; pivot = cmp(ta, tb) ? (cmp(tb, tc) ? b : (cmp(ta, tc) ? c : a)) : (cmp(tc, tb) ? b : (cmp(ta, tc) ? a : c)); if( pivot != left0 ) { t = array[pivot]; array[pivot] = array[left0]; array[left0] = t; pivot = left0; } left = left1 = left0 + 1; right = right1 = right0; ta = array[pivot]; for(;;) { while( left <= right && !cmp(ta, array[left]) ) { if( !cmp(array[left], ta) ) { if( left > left1 ) { t = array[left1]; array[left1] = array[left]; array[left] = t; } swap_cnt = 1; left1++; } left++; } while( left <= right && !cmp(array[right], ta) ) { if( !cmp(ta, array[right]) ) { if( right < right1 ) { t = array[right1]; array[right1] = array[right]; array[right] = t; } swap_cnt = 1; right1--; } right--; } if( left > right ) break; t = array[left]; array[left] = array[right]; array[right] = t; swap_cnt = 1; left++; right--; } if( swap_cnt == 0 ) { left = left0, right = right0; //goto insert_sort; for( ptr = left + 1; ptr <= right; ptr++ ) { for( ptr2 = ptr; ptr2 > left && cmp(array[ptr2],array[ptr2-1]); ptr2--) { t = array[ptr2]; array[ptr2] = array[ptr2-1]; array[ptr2-1] = t; } } break; } n = Math.min( (left1 - left0), (left - left1) ); m = (left-n)|0; for( i = 0; i < n; ++i,++m ) { t = array[left0+i]; array[left0+i] = array[m]; array[m] = t; } n = Math.min( (right0 - right1), (right1 - right) ); m = (right0-n+1)|0; for( i = 0; i < n; ++i,++m ) { t = array[left+i]; array[left+i] = array[m]; array[m] = t; } n = (left - left1); m = (right1 - right); if( n > 1 ) { if( m > 1 ) { if( n > m ) { ++sp; stack[sp<<1] = left0; stack[(sp<<1)+1] = left0 + n - 1; left = right0 - m + 1, right = right0; } else { ++sp; stack[sp<<1] = right0 - m + 1; stack[(sp<<1)+1] = right0; left = left0, right = left0 + n - 1; } } else { left = left0, right = left0 + n - 1; } } else if( m > 1 ) left = right0 - m + 1, right = right0; else break; } } } }, median: function(array, low, high) { var w; var middle=0,ll=0,hh=0,median=(low+high)>>1; for (;;) { if (high <= low) return array[median]; if (high == (low + 1)) { if (array[low] > array[high]) { w = array[low]; array[low] = array[high]; array[high] = w; } return array[median]; } middle = ((low + high) >> 1); if (array[middle] > array[high]) { w = array[middle]; array[middle] = array[high]; array[high] = w; } if (array[low] > array[high]) { w = array[low]; array[low] = array[high]; array[high] = w; } if (array[middle] > array[low]) { w = array[middle]; array[middle] = array[low]; array[low] = w; } ll = (low + 1); w = array[middle]; array[middle] = array[ll]; array[ll] = w; hh = high; for (;;) { do ++ll; while (array[low] > array[ll]); do --hh; while (array[hh] > array[low]); if (hh < ll) break; w = array[ll]; array[ll] = array[hh]; array[hh] = w; } w = array[low]; array[low] = array[hh]; array[hh] = w; if (hh <= median) low = ll; else if (hh >= median) high = (hh - 1); } return 0; } }; })(); global.math = math; })(jsfeat); /** * @author Eugene Zatepyakin / http://inspirit.ru/ * */ (function(global) { "use strict"; // var matmath = (function() { return { identity: function(M, value) { if (typeof value === "undefined") { value=1; } var src=M.data; var rows=M.rows, cols=M.cols, cols_1=(cols+1)|0; var len = rows * cols; var k = len; while(--len >= 0) src[len] = 0.0; len = k; k = 0; while(k < len) { src[k] = value; k = k + cols_1; } }, transpose: function(At, A) { var i=0,j=0,nrows=A.rows,ncols=A.cols; var Ai=0,Ati=0,pAt=0; var ad=A.data,atd=At.data; for (; i < nrows; Ati += 1, Ai += ncols, i++) { pAt = Ati; for (j = 0; j < ncols; pAt += nrows, j++) atd[pAt] = ad[Ai+j]; } }, // C = A * B multiply: function(C, A, B) { var i=0,j=0,k=0; var Ap=0,pA=0,pB=0,p_B=0,Cp=0; var ncols=A.cols,nrows=A.rows,mcols=B.cols; var ad=A.data,bd=B.data,cd=C.data; var sum=0.0; for (; i < nrows; Ap += ncols, i++) { for (p_B = 0, j = 0; j < mcols; Cp++, p_B++, j++) { pB = p_B; pA = Ap; sum = 0.0; for (k = 0; k < ncols; pA++, pB += mcols, k++) { sum += ad[pA] * bd[pB]; } cd[Cp] = sum; } } }, // C = A * B' multiply_ABt: function(C, A, B) { var i=0,j=0,k=0; var Ap=0,pA=0,pB=0,Cp=0; var ncols=A.cols,nrows=A.rows,mrows=B.rows; var ad=A.data,bd=B.data,cd=C.data; var sum=0.0; for (; i < nrows; Ap += ncols, i++) { for (pB = 0, j = 0; j < mrows; Cp++, j++) { pA = Ap; sum = 0.0; for (k = 0; k < ncols; pA++, pB++, k++) { sum += ad[pA] * bd[pB]; } cd[Cp] = sum; } } }, // C = A' * B multiply_AtB: function(C, A, B) { var i=0,j=0,k=0; var Ap=0,pA=0,pB=0,p_B=0,Cp=0; var ncols=A.cols,nrows=A.rows,mcols=B.cols; var ad=A.data,bd=B.data,cd=C.data; var sum=0.0; for (; i < ncols; Ap++, i++) { for (p_B = 0, j = 0; j < mcols; Cp++, p_B++, j++) { pB = p_B; pA = Ap; sum = 0.0; for (k = 0; k < nrows; pA += ncols, pB += mcols, k++) { sum += ad[pA] * bd[pB]; } cd[Cp] = sum; } } }, // C = A * A' multiply_AAt: function(C, A) { var i=0,j=0,k=0; var pCdiag=0,p_A=0,pA=0,pB=0,pC=0,pCt=0; var ncols=A.cols,nrows=A.rows; var ad=A.data,cd=C.data; var sum=0.0; for (; i < nrows; pCdiag += nrows + 1, p_A = pA, i++) { pC = pCdiag; pCt = pCdiag; pB = p_A; for (j = i; j < nrows; pC++, pCt += nrows, j++) { pA = p_A; sum = 0.0; for (k = 0; k < ncols; k++) { sum += ad[pA++] * ad[pB++]; } cd[pC] = sum cd[pCt] = sum; } } }, // C = A' * A multiply_AtA: function(C, A) { var i=0,j=0,k=0; var p_A=0,pA=0,pB=0,p_C=0,pC=0,p_CC=0; var ncols=A.cols,nrows=A.rows; var ad=A.data,cd=C.data; var sum=0.0; for (; i < ncols; p_C += ncols, i++) { p_A = i; p_CC = p_C + i; pC = p_CC; for (j = i; j < ncols; pC++, p_CC += ncols, j++) { pA = p_A; pB = j; sum = 0.0; for (k = 0; k < nrows; pA += ncols, pB += ncols, k++) { sum += ad[pA] * ad[pB]; } cd[pC] = sum cd[p_CC] = sum; } } }, // various small matrix operations identity_3x3: function(M, value) { if (typeof value === "undefined") { value=1; } var dt=M.data; dt[0] = dt[4] = dt[8] = value; dt[1] = dt[2] = dt[3] = 0; dt[5] = dt[6] = dt[7] = 0; }, invert_3x3: function(from, to) { var A = from.data, invA = to.data; var t1 = A[4]; var t2 = A[8]; var t4 = A[5]; var t5 = A[7]; var t8 = A[0]; var t9 = t8*t1; var t11 = t8*t4; var t13 = A[3]; var t14 = A[1]; var t15 = t13*t14; var t17 = A[2]; var t18 = t13*t17; var t20 = A[6]; var t21 = t20*t14; var t23 = t20*t17; var t26 = 1.0/(t9*t2-t11*t5-t15*t2+t18*t5+t21*t4-t23*t1); invA[0] = (t1*t2-t4*t5)*t26; invA[1] = -(t14*t2-t17*t5)*t26; invA[2] = -(-t14*t4+t17*t1)*t26; invA[3] = -(t13*t2-t4*t20)*t26; invA[4] = (t8*t2-t23)*t26; invA[5] = -(t11-t18)*t26; invA[6] = -(-t13*t5+t1*t20)*t26; invA[7] = -(t8*t5-t21)*t26; invA[8] = (t9-t15)*t26; }, // C = A * B multiply_3x3: function(C, A, B) { var Cd=C.data, Ad=A.data, Bd=B.data; var m1_0 = Ad[0], m1_1 = Ad[1], m1_2 = Ad[2]; var m1_3 = Ad[3], m1_4 = Ad[4], m1_5 = Ad[5]; var m1_6 = Ad[6], m1_7 = Ad[7], m1_8 = Ad[8]; var m2_0 = Bd[0], m2_1 = Bd[1], m2_2 = Bd[2]; var m2_3 = Bd[3], m2_4 = Bd[4], m2_5 = Bd[5]; var m2_6 = Bd[6], m2_7 = Bd[7], m2_8 = Bd[8]; Cd[0] = m1_0 * m2_0 + m1_1 * m2_3 + m1_2 * m2_6; Cd[1] = m1_0 * m2_1 + m1_1 * m2_4 + m1_2 * m2_7; Cd[2] = m1_0 * m2_2 + m1_1 * m2_5 + m1_2 * m2_8; Cd[3] = m1_3 * m2_0 + m1_4 * m2_3 + m1_5 * m2_6; Cd[4] = m1_3 * m2_1 + m1_4 * m2_4 + m1_5 * m2_7; Cd[5] = m1_3 * m2_2 + m1_4 * m2_5 + m1_5 * m2_8; Cd[6] = m1_6 * m2_0 + m1_7 * m2_3 + m1_8 * m2_6; Cd[7] = m1_6 * m2_1 + m1_7 * m2_4 + m1_8 * m2_7; Cd[8] = m1_6 * m2_2 + m1_7 * m2_5 + m1_8 * m2_8; }, mat3x3_determinant: function(M) { var md=M.data; return md[0] * md[4] * md[8] - md[0] * md[5] * md[7] - md[3] * md[1] * md[8] + md[3] * md[2] * md[7] + md[6] * md[1] * md[5] - md[6] * md[2] * md[4]; }, determinant_3x3: function(M11, M12, M13, M21, M22, M23, M31, M32, M33) { return M11 * M22 * M33 - M11 * M23 * M32 - M21 * M12 * M33 + M21 * M13 * M32 + M31 * M12 * M23 - M31 * M13 * M22; } }; })(); global.matmath = matmath; })(jsfeat); /** * @author Eugene Zatepyakin / http://inspirit.ru/ * */ (function(global) { "use strict"; // var linalg = (function() { var swap = function(A, i0, i1, t) { t = A[i0]; A[i0] = A[i1]; A[i1] = t; } var hypot = function(a, b) { a = Math.abs(a); b = Math.abs(b); if( a > b ) { b /= a; return a*Math.sqrt(1.0 + b*b); } if( b > 0 ) { a /= b; return b*Math.sqrt(1.0 + a*a); } return 0.0; } var JacobiImpl = function(A, astep, W, V, vstep, n) { var eps = jsfeat.EPSILON; var i=0,j=0,k=0,m=0,l=0,idx=0,_in=0,_in2=0; var iters=0,max_iter=n*n*30; var mv=0.0,val=0.0,p=0.0,y=0.0,t=0.0,s=0.0,c=0.0,a0=0.0,b0=0.0; var indR_buff = jsfeat.cache.get_buffer(n<<2); var indC_buff = jsfeat.cache.get_buffer(n<<2); var indR = indR_buff.i32; var indC = indC_buff.i32; if(V) { for(; i < n; i++) { k = i*vstep; for(j = 0; j < n; j++) { V[k + j] = 0.0; } V[k + i] = 1.0; } } for(k = 0; k < n; k++) { W[k] = A[(astep + 1)*k]; if(k < n - 1) { for(m = k+1, mv = Math.abs(A[astep*k + m]), i = k+2; i < n; i++) { val = Math.abs(A[astep*k+i]); if(mv < val) mv = val, m = i; } indR[k] = m; } if(k > 0) { for(m = 0, mv = Math.abs(A[k]), i = 1; i < k; i++) { val = Math.abs(A[astep*i+k]); if(mv < val) mv = val, m = i; } indC[k] = m; } } if(n > 1) for( ; iters < max_iter; iters++) { // find index (k,l) of pivot p for(k = 0, mv = Math.abs(A[indR[0]]), i = 1; i < n-1; i++) { val = Math.abs(A[astep*i + indR[i]]); if( mv < val ) mv = val, k = i; } l = indR[k]; for(i = 1; i < n; i++) { val = Math.abs(A[astep*indC[i] + i]); if( mv < val ) mv = val, k = indC[i], l = i; } p = A[astep*k + l]; if(Math.abs(p) <= eps) break; y = (W[l] - W[k])*0.5; t = Math.abs(y) + hypot(p, y); s = hypot(p, t); c = t/s; s = p/s; t = (p/t)*p; if(y < 0) s = -s, t = -t; A[astep*k + l] = 0; W[k] -= t; W[l] += t; // rotate rows and columns k and l for (i = 0; i < k; i++) { _in = (astep * i + k); _in2 = (astep * i + l); a0 = A[_in]; b0 = A[_in2]; A[_in] = a0 * c - b0 * s; A[_in2] = a0 * s + b0 * c; } for (i = (k + 1); i < l; i++) { _in = (astep * k + i); _in2 = (astep * i + l); a0 = A[_in]; b0 = A[_in2]; A[_in] = a0 * c - b0 * s; A[_in2] = a0 * s + b0 * c; } i = l + 1; _in = (astep * k + i); _in2 = (astep * l + i); for (; i < n; i++, _in++, _in2++) { a0 = A[_in]; b0 = A[_in2]; A[_in] = a0 * c - b0 * s; A[_in2] = a0 * s + b0 * c; } // rotate eigenvectors if (V) { _in = vstep * k; _in2 = vstep * l; for (i = 0; i < n; i++, _in++, _in2++) { a0 = V[_in]; b0 = V[_in2]; V[_in] = a0 * c - b0 * s; V[_in2] = a0 * s + b0 * c; } } for(j = 0; j < 2; j++) { idx = j == 0 ? k : l; if(idx < n - 1) { for(m = idx+1, mv = Math.abs(A[astep*idx + m]), i = idx+2; i < n; i++) { val = Math.abs(A[astep*idx+i]); if( mv < val ) mv = val, m = i; } indR[idx] = m; } if(idx > 0) { for(m = 0, mv = Math.abs(A[idx]), i = 1; i < idx; i++) { val = Math.abs(A[astep*i+idx]); if( mv < val ) mv = val, m = i; } indC[idx] = m; } } } // sort eigenvalues & eigenvectors for(k = 0; k < n-1; k++) { m = k; for(i = k+1; i < n; i++) { if(W[m] < W[i]) m = i; } if(k != m) { swap(W, m, k, mv); if(V) { for(i = 0; i < n; i++) { swap(V, vstep*m + i, vstep*k + i, mv); } } } } jsfeat.cache.put_buffer(indR_buff); jsfeat.cache.put_buffer(indC_buff); } var JacobiSVDImpl = function(At, astep, _W, Vt, vstep, m, n, n1) { var eps = jsfeat.EPSILON * 2.0; var minval = jsfeat.FLT_MIN; var i=0,j=0,k=0,iter=0,max_iter=Math.max(m, 30); var Ai=0,Aj=0,Vi=0,Vj=0,changed=0; var c=0.0, s=0.0, t=0.0; var t0=0.0,t1=0.0,sd=0.0,beta=0.0,gamma=0.0,delta=0.0,a=0.0,p=0.0,b=0.0; var seed = 0x1234; var val=0.0,val0=0.0,asum=0.0; var W_buff = jsfeat.cache.get_buffer(n<<3); var W = W_buff.f64; for(; i < n; i++) { for(k = 0, sd = 0; k < m; k++) { t = At[i*astep + k]; sd += t*t; } W[i] = sd; if(Vt) { for(k = 0; k < n; k++) { Vt[i*vstep + k] = 0; } Vt[i*vstep + i] = 1; } } for(; iter < max_iter; iter++) { changed = 0; for(i = 0; i < n-1; i++) { for(j = i+1; j < n; j++) { Ai = (i*astep)|0, Aj = (j*astep)|0; a = W[i], p = 0, b = W[j]; k = 2; p += At[Ai]*At[Aj]; p += At[Ai+1]*At[Aj+1]; for(; k < m; k++) p += At[Ai+k]*At[Aj+k]; if(Math.abs(p) <= eps*Math.sqrt(a*b)) continue; p *= 2.0; beta = a - b, gamma = hypot(p, beta); if( beta < 0 ) { delta = (gamma - beta)*0.5; s = Math.sqrt(delta/gamma); c = (p/(gamma*s*2.0)); } else { c = Math.sqrt((gamma + beta)/(gamma*2.0)); s = (p/(gamma*c*2.0)); } a=0.0, b=0.0; k = 2; // unroll t0 = c*At[Ai] + s*At[Aj]; t1 = -s*At[Ai] + c*At[Aj]; At[Ai] = t0; At[Aj] = t1; a += t0*t0; b += t1*t1; t0 = c*At[Ai+1] + s*At[Aj+1]; t1 = -s*At[Ai+1] + c*At[Aj+1]; At[Ai+1] = t0; At[Aj+1] = t1; a += t0*t0; b += t1*t1; for( ; k < m; k++ ) { t0 = c*At[Ai+k] + s*At[Aj+k]; t1 = -s*At[Ai+k] + c*At[Aj+k]; At[Ai+k] = t0; At[Aj+k] = t1; a += t0*t0; b += t1*t1; } W[i] = a; W[j] = b; changed = 1; if(Vt) { Vi = (i*vstep)|0, Vj = (j*vstep)|0; k = 2; t0 = c*Vt[Vi] + s*Vt[Vj]; t1 = -s*Vt[Vi] + c*Vt[Vj]; Vt[Vi] = t0; Vt[Vj] = t1; t0 = c*Vt[Vi+1] + s*Vt[Vj+1]; t1 = -s*Vt[Vi+1] + c*Vt[Vj+1]; Vt[Vi+1] = t0; Vt[Vj+1] = t1; for(; k < n; k++) { t0 = c*Vt[Vi+k] + s*Vt[Vj+k]; t1 = -s*Vt[Vi+k] + c*Vt[Vj+k]; Vt[Vi+k] = t0; Vt[Vj+k] = t1; } } } } if(changed == 0) break; } for(i = 0; i < n; i++) { for(k = 0, sd = 0; k < m; k++) { t = At[i*astep + k]; sd += t*t; } W[i] = Math.sqrt(sd); } for(i = 0; i < n-1; i++) { j = i; for(k = i+1; k < n; k++) { if(W[j] < W[k]) j = k; } if(i != j) { swap(W, i, j, sd); if(Vt) { for(k = 0; k < m; k++) { swap(At, i*astep + k, j*astep + k, t); } for(k = 0; k < n; k++) { swap(Vt, i*vstep + k, j*vstep + k, t); } } } } for(i = 0; i < n; i++) { _W[i] = W[i]; } if(!Vt) { jsfeat.cache.put_buffer(W_buff); return; } for(i = 0; i < n1; i++) { sd = i < n ? W[i] : 0; while(sd <= minval) { // if we got a zero singular value, then in order to get the corresponding left singular vector // we generate a random vector, project it to the previously computed left singular vectors, // subtract the projection and normalize the difference. val0 = (1.0/m); for(k = 0; k < m; k++) { seed = (seed * 214013 + 2531011); val = (((seed >> 16) & 0x7fff) & 256) != 0 ? val0 : -val0; At[i*astep + k] = val; } for(iter = 0; iter < 2; iter++) { for(j = 0; j < i; j++) { sd = 0; for(k = 0; k < m; k++) { sd += At[i*astep + k]*At[j*astep + k]; } asum = 0.0; for(k = 0; k < m; k++) { t = (At[i*astep + k] - sd*At[j*astep + k]); At[i*astep + k] = t; asum += Math.abs(t); } asum = asum ? 1.0/asum : 0; for(k = 0; k < m; k++) { At[i*astep + k] *= asum; } } }