jsfeat
Version:
JavaScript Computer Vision library
1,323 lines (1,168 loc) • 226 kB
JavaScript
/**
* @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;
}
}
}