update Tue 07/14/2026 11:38:16.92
This commit is contained in:
commit
b2b555ef16
38 files changed
+7348
No files matched your search
Vendored
+468
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
Copyright (c) 2020 Damiano Falcioni
|
||||
Copyright (c) 2011 Juan Mellado
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
References:
|
||||
- "ArUco: a minimal library for Augmented Reality applications based on OpenCv"
|
||||
http://www.uco.es/investiga/grupos/ava/node/26
|
||||
- "js-aruco: a port to JavaScript of the ArUco library"
|
||||
https://github.com/jcmellado/js-aruco
|
||||
*/
|
||||
|
||||
var AR = {};
|
||||
var CV = this.CV || require('./cv').CV;
|
||||
this.AR = AR;
|
||||
|
||||
AR.DICTIONARIES = {
|
||||
ARUCO: {
|
||||
nBits: 25,
|
||||
tau: 3,
|
||||
codeList: [0x1084210,0x1084217,0x1084209,0x108420e,0x10842f0,0x10842f7,0x10842e9,0x10842ee,0x1084130,0x1084137,0x1084129,0x108412e,0x10841d0,0x10841d7,0x10841c9,0x10841ce,0x1085e10,0x1085e17,0x1085e09,0x1085e0e,0x1085ef0,0x1085ef7,0x1085ee9,0x1085eee,0x1085d30,0x1085d37,0x1085d29,0x1085d2e,0x1085dd0,0x1085dd7,0x1085dc9,0x1085dce,0x1082610,0x1082617,0x1082609,0x108260e,0x10826f0,0x10826f7,0x10826e9,0x10826ee,0x1082530,0x1082537,0x1082529,0x108252e,0x10825d0,0x10825d7,0x10825c9,0x10825ce,0x1083a10,0x1083a17,0x1083a09,0x1083a0e,0x1083af0,0x1083af7,0x1083ae9,0x1083aee,0x1083930,0x1083937,0x1083929,0x108392e,0x10839d0,0x10839d7,0x10839c9,0x10839ce,0x10bc210,0x10bc217,0x10bc209,0x10bc20e,0x10bc2f0,0x10bc2f7,0x10bc2e9,0x10bc2ee,0x10bc130,0x10bc137,0x10bc129,0x10bc12e,0x10bc1d0,0x10bc1d7,0x10bc1c9,0x10bc1ce,0x10bde10,0x10bde17,0x10bde09,0x10bde0e,0x10bdef0,0x10bdef7,0x10bdee9,0x10bdeee,0x10bdd30,0x10bdd37,0x10bdd29,0x10bdd2e,0x10bddd0,0x10bddd7,0x10bddc9,0x10bddce,0x10ba610,0x10ba617,0x10ba609,0x10ba60e,0x10ba6f0,0x10ba6f7,0x10ba6e9,0x10ba6ee,0x10ba530,0x10ba537,0x10ba529,0x10ba52e,0x10ba5d0,0x10ba5d7,0x10ba5c9,0x10ba5ce,0x10bba10,0x10bba17,0x10bba09,0x10bba0e,0x10bbaf0,0x10bbaf7,0x10bbae9,0x10bbaee,0x10bb930,0x10bb937,0x10bb929,0x10bb92e,0x10bb9d0,0x10bb9d7,0x10bb9c9,0x10bb9ce,0x104c210,0x104c217,0x104c209,0x104c20e,0x104c2f0,0x104c2f7,0x104c2e9,0x104c2ee,0x104c130,0x104c137,0x104c129,0x104c12e,0x104c1d0,0x104c1d7,0x104c1c9,0x104c1ce,0x104de10,0x104de17,0x104de09,0x104de0e,0x104def0,0x104def7,0x104dee9,0x104deee,0x104dd30,0x104dd37,0x104dd29,0x104dd2e,0x104ddd0,0x104ddd7,0x104ddc9,0x104ddce,0x104a610,0x104a617,0x104a609,0x104a60e,0x104a6f0,0x104a6f7,0x104a6e9,0x104a6ee,0x104a530,0x104a537,0x104a529,0x104a52e,0x104a5d0,0x104a5d7,0x104a5c9,0x104a5ce,0x104ba10,0x104ba17,0x104ba09,0x104ba0e,0x104baf0,0x104baf7,0x104bae9,0x104baee,0x104b930,0x104b937,0x104b929,0x104b92e,0x104b9d0,0x104b9d7,0x104b9c9,0x104b9ce,0x1074210,0x1074217,0x1074209,0x107420e,0x10742f0,0x10742f7,0x10742e9,0x10742ee,0x1074130,0x1074137,0x1074129,0x107412e,0x10741d0,0x10741d7,0x10741c9,0x10741ce,0x1075e10,0x1075e17,0x1075e09,0x1075e0e,0x1075ef0,0x1075ef7,0x1075ee9,0x1075eee,0x1075d30,0x1075d37,0x1075d29,0x1075d2e,0x1075dd0,0x1075dd7,0x1075dc9,0x1075dce,0x1072610,0x1072617,0x1072609,0x107260e,0x10726f0,0x10726f7,0x10726e9,0x10726ee,0x1072530,0x1072537,0x1072529,0x107252e,0x10725d0,0x10725d7,0x10725c9,0x10725ce,0x1073a10,0x1073a17,0x1073a09,0x1073a0e,0x1073af0,0x1073af7,0x1073ae9,0x1073aee,0x1073930,0x1073937,0x1073929,0x107392e,0x10739d0,0x10739d7,0x10739c9,0x10739ce,0x1784210,0x1784217,0x1784209,0x178420e,0x17842f0,0x17842f7,0x17842e9,0x17842ee,0x1784130,0x1784137,0x1784129,0x178412e,0x17841d0,0x17841d7,0x17841c9,0x17841ce,0x1785e10,0x1785e17,0x1785e09,0x1785e0e,0x1785ef0,0x1785ef7,0x1785ee9,0x1785eee,0x1785d30,0x1785d37,0x1785d29,0x1785d2e,0x1785dd0,0x1785dd7,0x1785dc9,0x1785dce,0x1782610,0x1782617,0x1782609,0x178260e,0x17826f0,0x17826f7,0x17826e9,0x17826ee,0x1782530,0x1782537,0x1782529,0x178252e,0x17825d0,0x17825d7,0x17825c9,0x17825ce,0x1783a10,0x1783a17,0x1783a09,0x1783a0e,0x1783af0,0x1783af7,0x1783ae9,0x1783aee,0x1783930,0x1783937,0x1783929,0x178392e,0x17839d0,0x17839d7,0x17839c9,0x17839ce,0x17bc210,0x17bc217,0x17bc209,0x17bc20e,0x17bc2f0,0x17bc2f7,0x17bc2e9,0x17bc2ee,0x17bc130,0x17bc137,0x17bc129,0x17bc12e,0x17bc1d0,0x17bc1d7,0x17bc1c9,0x17bc1ce,0x17bde10,0x17bde17,0x17bde09,0x17bde0e,0x17bdef0,0x17bdef7,0x17bdee9,0x17bdeee,0x17bdd30,0x17bdd37,0x17bdd29,0x17bdd2e,0x17bddd0,0x17bddd7,0x17bddc9,0x17bddce,0x17ba610,0x17ba617,0x17ba609,0x17ba60e,0x17ba6f0,0x17ba6f7,0x17ba6e9,0x17ba6ee,0x17ba530,0x17ba537,0x17ba529,0x17ba52e,0x17ba5d0,0x17ba5d7,0x17ba5c9,0x17ba5ce,0x17bba10,0x17bba17,0x17bba09,0x17bba0e,0x17bbaf0,0x17bbaf7,0x17bbae9,0x17bbaee,0x17bb930,0x17bb937,0x17bb929,0x17bb92e,0x17bb9d0,0x17bb9d7,0x17bb9c9,0x17bb9ce,0x174c210,0x174c217,0x174c209,0x174c20e,0x174c2f0,0x174c2f7,0x174c2e9,0x174c2ee,0x174c130,0x174c137,0x174c129,0x174c12e,0x174c1d0,0x174c1d7,0x174c1c9,0x174c1ce,0x174de10,0x174de17,0x174de09,0x174de0e,0x174def0,0x174def7,0x174dee9,0x174deee,0x174dd30,0x174dd37,0x174dd29,0x174dd2e,0x174ddd0,0x174ddd7,0x174ddc9,0x174ddce,0x174a610,0x174a617,0x174a609,0x174a60e,0x174a6f0,0x174a6f7,0x174a6e9,0x174a6ee,0x174a530,0x174a537,0x174a529,0x174a52e,0x174a5d0,0x174a5d7,0x174a5c9,0x174a5ce,0x174ba10,0x174ba17,0x174ba09,0x174ba0e,0x174baf0,0x174baf7,0x174bae9,0x174baee,0x174b930,0x174b937,0x174b929,0x174b92e,0x174b9d0,0x174b9d7,0x174b9c9,0x174b9ce,0x1774210,0x1774217,0x1774209,0x177420e,0x17742f0,0x17742f7,0x17742e9,0x17742ee,0x1774130,0x1774137,0x1774129,0x177412e,0x17741d0,0x17741d7,0x17741c9,0x17741ce,0x1775e10,0x1775e17,0x1775e09,0x1775e0e,0x1775ef0,0x1775ef7,0x1775ee9,0x1775eee,0x1775d30,0x1775d37,0x1775d29,0x1775d2e,0x1775dd0,0x1775dd7,0x1775dc9,0x1775dce,0x1772610,0x1772617,0x1772609,0x177260e,0x17726f0,0x17726f7,0x17726e9,0x17726ee,0x1772530,0x1772537,0x1772529,0x177252e,0x17725d0,0x17725d7,0x17725c9,0x17725ce,0x1773a10,0x1773a17,0x17Line truncated
|
||||
},
|
||||
ARUCO_MIP_36h12: {
|
||||
nBits: 36,
|
||||
tau: 12,
|
||||
codeList: [0xd2b63a09d,0x6001134e5,0x1206fbe72,0xff8ad6cb4,0x85da9bc49,0xb461afe9c,0x6db51fe13,0x5248c541f,0x8f34503,0x8ea462ece,0xeac2be76d,0x1af615c44,0xb48a49f27,0x2e4e1283b,0x78b1f2fa8,0x27d34f57e,0x89222fff1,0x4c1669406,0xbf49b3511,0xdc191cd5d,0x11d7c3f85,0x16a130e35,0xe29f27eff,0x428d8ae0c,0x90d548477,0x2319cbc93,0xc3b0c3dfc,0x424bccc9,0x2a081d630,0x762743d96,0xd0645bf19,0xf38d7fd60,0xc6cbf9a10,0x3c1be7c65,0x276f75e63,0x4490a3f63,0xda60acd52,0x3cc68df59,0xab46f9dae,0x88d533d78,0xb6d62ec21,0xb3c02b646,0x22e56d408,0xac5f5770a,0xaaa993f66,0x4caa07c8d,0x5c9b4f7b0,0xaa9ef0e05,0x705c5750,0xac81f545e,0x735b91e74,0x8cc35cee4,0xe44694d04,0xb5e121de0,0x261017d0f,0xf1d439eb5,0xa1a33ac96,0x174c62c02,0x1ee27f716,0x8b1c5ece9,0x6a05b0c6a,0xd0568dfc,0x192d25e5f,0x1adbeccc8,0xcfec87f00,0xd0b9dde7a,0x88dcef81e,0x445681cb9,0xdbb2ffc83,0xa48d96df1,0xb72cc2e7d,0xc295b53f,0xf49832704,0x9968edc29,0x9e4e1af85,0x8683e2d1b,0x810b45c04,0x6ac44bfe2,0x645346615,0x3990bd598,0x1c9ed0f6a,0xc26729d65,0x83993f795,0x3ac05ac5d,0x357adff3b,0xd5c05565,0x2f547ef44,0x86c115041,0x640fd9e5f,0xce08bbcf7,0x109bb343e,0xc21435c92,0x35b4dfce4,0x459752cf2,0xec915b82c,0x51881eed0,0x2dda7dc97,0x2e0142144,0x42e890f99,0x9a8856527,0x8e80d9d80,0x891cbcf34,0x25dd82410,0x239551d34,0x8fe8f0c70,0x94106a970,0x82609b40c,0xfc9caf36,0x688181d11,0x718613c08,0xf1ab7629,0xa357bfc18,0x4c03b7a46,0x204dedce6,0xad6300d37,0x84cc4cd09,0x42160e5c4,0x87d2adfa8,0x7850e7749,0x4e750fc7c,0xbf2e5dfda,0xd88324da5,0x234b52f80,0x378204514,0xabdf2ad53,0x365e78ef9,0x49caa6ca2,0x3c39ddf3,0xc68c5385d,0x5bfcbbf67,0x623241e21,0xabc90d5cc,0x388c6fe85,0xda0e2d62d,0x10855dfe9,0x4d46efd6b,0x76ea12d61,0x9db377d3d,0xeed0efa71,0xe6ec3ae2f,0x441faee83,0xba19c8ff5,0x313035eab,0x6ce8f7625,0x880dab58d,0x8d3409e0d,0x2be92ee21,0xd60302c6c,0x469ffc724,0x87eebeed3,0x42587ef7a,0x7a8cc4e52,0x76a437650,0x999e41ef4,0x7d0969e42,0xc02baf46b,0x9259f3e47,0x2116a1dc0,0x9f2de4d84,0xeffac29,0x7b371ff8c,0x668339da9,0xd010aee3f,0x1cd00b4c0,0x95070fc3b,0xf84c9a770,0x38f863d76,0x3646ff045,0xce1b96412,0x7a5d45da8,0x14e00ef6c,0x5e95abfd8,0xb2e9cb729,0x36c47dd7,0xb8ee97c6b,0xe9e8f657,0xd4ad2ef1a,0x8811c7f32,0x47bde7c31,0x3adadfb64,0x6e5b28574,0x33e67cd91,0x2ab9fdd2d,0x8afa67f2b,0xe6a28fc5e,0x72049cdbd,0xae65dac12,0x1251a4526,0x1089ab841,0xe2f096ee0,0xb0caee573,0xfd6677e86,0x444b3f518,0xbe8b3a56a,0x680a75cfc,0xac02baea8,0x97d815e1c,0x1d4386e08,0x1a14f5b0e,0xe658a8d81,0xa3868efa7,0x3668a9673,0xe8fc53d85,0x2e2b7edd5,0x8b2470f13,0xf69795f32,0x4589ffc8e,0x2e2080c9c,0x64265f7d,0x3d714dd10,0x1692c6ef1,0x3e67f2f49,0x5041dad63,0x1a1503415,0x64c18c742,0xa72eec35,0x1f0f9dc60,0xa9559bc67,0xf32911d0d,0x21c0d4ffc,0xe01cef5b0,0x4e23a3520,0xaa4f04e49,0xe1c4fcc43,0x208e8f6e8,0x8486774a5,0x9e98c7558,0x2c59fb7dc,0x9446a4613,0x8292dcc2e,0x4d61631,0xd05527809,0xa0163852d,0x8f657f639,0xcca6c3e37,0xcb136bc7a,0xfc5a83e53,0x9aa44fc30,0xbdec1bd3c,0xe020b9f7c,0x4b8f35fb0,0xb8165f637,0x33dc88d69,0x10a2f7e4d,0xc8cb5ff53,0xde259ff6b,0x46d070dd4,0x32d3b9741,0x7075f1c04,0x4d58dbea0]
|
||||
}
|
||||
};
|
||||
|
||||
AR.Dictionary = function (dicName) {
|
||||
this.codes = {};
|
||||
this.codeList = [];
|
||||
this.tau = 0;
|
||||
this._initialize(dicName);
|
||||
};
|
||||
|
||||
AR.Dictionary.prototype._initialize = function (dicName) {
|
||||
this.codes = {};
|
||||
this.codeList = [];
|
||||
this.tau = 0;
|
||||
this.nBits = 0;
|
||||
this.markSize = 0;
|
||||
this.dicName = dicName;
|
||||
var dictionary = AR.DICTIONARIES[dicName];
|
||||
if (!dictionary)
|
||||
throw 'The dictionary "' + dicName + '" is not recognized.';
|
||||
|
||||
this.nBits = dictionary.nBits;
|
||||
this.markSize = Math.sqrt(dictionary.nBits) + 2;
|
||||
for (var i = 0; i < dictionary.codeList.length; i++) {
|
||||
var code = null;
|
||||
if (typeof dictionary.codeList[i] === 'number')
|
||||
code = this._hex2bin(dictionary.codeList[i], dictionary.nBits);
|
||||
if (typeof dictionary.codeList[i] === 'string')
|
||||
code = this._hex2bin(parseInt(dictionary.codeList[i], 16), dictionary.nBits);
|
||||
if (Array.isArray(dictionary.codeList[i]))
|
||||
code = this._bytes2bin(dictionary.codeList[i], dictionary.nBits);
|
||||
if (code === null)
|
||||
throw 'Invalid code ' + i + ' in dictionary ' + dicName + ': ' + JSON.stringify(dictionary.codeList[i]);
|
||||
if (code.length != dictionary.nBits)
|
||||
throw 'The code ' + i + ' in dictionary ' + dicName + ' is not ' + dictionary.nBits + ' bits long but ' + code.length + ': ' + code;
|
||||
this.codeList.push(code);
|
||||
this.codes[code] = {
|
||||
id: i
|
||||
};
|
||||
}
|
||||
this.tau = dictionary.tau || this._calculateTau();
|
||||
};
|
||||
|
||||
AR.Dictionary.prototype.find = function (bits) {
|
||||
var val = '',
|
||||
i, j;
|
||||
for (i = 0; i < bits.length; i++) {
|
||||
var bitRow = bits[i];
|
||||
for (j = 0; j < bitRow.length; j++) {
|
||||
val += bitRow[j];
|
||||
}
|
||||
}
|
||||
var minFound = this.codes[val];
|
||||
if (minFound)
|
||||
return {
|
||||
id: minFound.id,
|
||||
distance: 0
|
||||
};
|
||||
|
||||
for (i = 0; i < this.codeList.length; i++) {
|
||||
var code = this.codeList[i];
|
||||
var distance = this._hammingDistance(val, code);
|
||||
if (this._hammingDistance(val, code) < this.tau) {
|
||||
if (!minFound || minFound.distance > distance) {
|
||||
minFound = {
|
||||
id: this.codes[code].id,
|
||||
distance: distance
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return minFound;
|
||||
};
|
||||
|
||||
AR.Dictionary.prototype._hex2bin = function (hex, nBits) {
|
||||
return hex.toString(2).padStart(nBits, '0');
|
||||
};
|
||||
|
||||
AR.Dictionary.prototype._bytes2bin = function (byteList, nBits) {
|
||||
var bits = '', byte;
|
||||
for (byte of byteList) {
|
||||
bits += byte.toString(2).padStart(bits.length + 8 > nBits?nBits - bits.length:8, '0');
|
||||
}
|
||||
return bits;
|
||||
};
|
||||
|
||||
AR.Dictionary.prototype._hammingDistance = function (str1, str2) {
|
||||
if (str1.length != str2.length)
|
||||
throw 'Hamming distance calculation require inputs of the same length';
|
||||
var distance = 0,
|
||||
i;
|
||||
for (i = 0; i < str1.length; i++)
|
||||
if (str1[i] !== str2[i])
|
||||
distance += 1;
|
||||
return distance;
|
||||
};
|
||||
|
||||
AR.Dictionary.prototype._calculateTau = function () {
|
||||
var tau = Number.MAX_VALUE;
|
||||
for(var i=0;i<this.codeList.length;i++)
|
||||
for(var j=i+1;j<this.codeList.length;j++) {
|
||||
var distance = this._hammingDistance(this.codeList[i], this.codeList[j]);
|
||||
tau = distance < tau ? distance : tau;
|
||||
}
|
||||
return tau;
|
||||
};
|
||||
|
||||
AR.Dictionary.prototype.generateSVG = function (id) {
|
||||
var code = this.codeList[id];
|
||||
if (code == null)
|
||||
throw 'The id "' + id + '" is not valid for the dictionary "' + this.dicName + '". ID must be between 0 and ' + (this.codeList.length-1) + ' included.';
|
||||
var size = this.markSize - 2;
|
||||
var svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 '+ (size+4) + ' ' + (size+4) + '">';
|
||||
svg += '<rect x="0" y="0" width="' + (size+4) + '" height="' + (size+4) + '" fill="white"/>';
|
||||
svg += '<rect x="1" y="1" width="' + (size+2) + '" height="' + (size+2) + '" fill="black"/>';
|
||||
for(var y=0;y<size;y++) {
|
||||
for(var x=0;x<size;x++) {
|
||||
if (code[y*size+x]=='1')
|
||||
svg += '<rect x="' + (x+2) + '" y="' + (y+2) + '" width="1" height="1" fill="white"/>';
|
||||
}
|
||||
}
|
||||
svg += '</svg>';
|
||||
return svg;
|
||||
};
|
||||
|
||||
AR.Marker = function (id, corners, hammingDistance) {
|
||||
this.id = id;
|
||||
this.corners = corners;
|
||||
this.hammingDistance = hammingDistance;
|
||||
};
|
||||
|
||||
AR.Detector = function (config) {
|
||||
config = config || {};
|
||||
this.grey = new CV.Image();
|
||||
this.thres = new CV.Image();
|
||||
this.homography = new CV.Image();
|
||||
this.binary = [];
|
||||
this.contours = [];
|
||||
this.polys = [];
|
||||
this.candidates = [];
|
||||
config.dictionaryName = config.dictionaryName || 'ARUCO_MIP_36h12';
|
||||
this.dictionary = new AR.Dictionary(config.dictionaryName);
|
||||
this.dictionary.tau = config.maxHammingDistance != null ? config.maxHammingDistance : this.dictionary.tau;
|
||||
};
|
||||
|
||||
AR.Detector.prototype.detectImage = function (width, height, data) {
|
||||
return this.detect({
|
||||
width: width,
|
||||
height: height,
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
AR.Detector.prototype.detectStreamInit = function (width, height, callback) {
|
||||
this.streamConfig = {};
|
||||
this.streamConfig.width = width;
|
||||
this.streamConfig.height = height;
|
||||
this.streamConfig.imageSize = width * height * 4; //provided image must be a sequence of rgba bytes (4 bytes represent a pixel)
|
||||
this.streamConfig.index = 0;
|
||||
this.streamConfig.imageData = new Uint8ClampedArray(this.streamConfig.imageSize);
|
||||
this.streamConfig.callback = callback || function (image, markerList) {};
|
||||
};
|
||||
|
||||
//accept data chunks of different sizes
|
||||
AR.Detector.prototype.detectStream = function (data) {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
this.streamConfig.imageData[this.streamConfig.index] = data[i];
|
||||
this.streamConfig.index = (this.streamConfig.index + 1) % this.streamConfig.imageSize;
|
||||
if (this.streamConfig.index == 0) {
|
||||
var image = {
|
||||
width: this.streamConfig.width,
|
||||
height: this.streamConfig.height,
|
||||
data: this.streamConfig.imageData
|
||||
};
|
||||
var markerList = this.detect(image);
|
||||
this.streamConfig.callback(image, markerList);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AR.Detector.prototype.detectMJPEGStreamInit = function (width, height, callback, decoderFn) {
|
||||
this.mjpeg = {
|
||||
decoderFn: decoderFn,
|
||||
chunks: [],
|
||||
SOI: [0xff, 0xd8],
|
||||
EOI: [0xff, 0xd9]
|
||||
};
|
||||
this.detectStreamInit(width, height, callback);
|
||||
};
|
||||
|
||||
AR.Detector.prototype.detectMJPEGStream = function (chunk) {
|
||||
var eoiPos = chunk.findIndex(function (element, index, array) {
|
||||
return this.mjpeg.EOI[0] == element && array.length > index + 1 && this.mjpeg.EOI[1] == array[index + 1];
|
||||
});
|
||||
var soiPos = chunk.findIndex(function (element, index, array) {
|
||||
return this.mjpeg.SOI[0] == element && array.length > index + 1 && this.mjpeg.SOI[1] == array[index + 1];
|
||||
});
|
||||
|
||||
if (eoiPos === -1) {
|
||||
this.mjpeg.chunks.push(chunk);
|
||||
} else {
|
||||
var part1 = chunk.slice(0, eoiPos + 2);
|
||||
if (part1.length) {
|
||||
this.mjpeg.chunks.push(part1);
|
||||
}
|
||||
if (this.mjpeg.chunks.length) {
|
||||
var jpegImage = this.mjpeg.chunks.flat();
|
||||
var rgba = this.mjpeg.decoderFn(jpegImage);
|
||||
this.detectStream(rgba);
|
||||
}
|
||||
this.mjpeg.chunks = [];
|
||||
}
|
||||
if (soiPos > -1) {
|
||||
this.mjpeg.chunks = [];
|
||||
this.mjpeg.chunks.push(chunk.slice(soiPos));
|
||||
}
|
||||
};
|
||||
|
||||
AR.Detector.prototype.detect = function (image) {
|
||||
CV.grayscale(image, this.grey);
|
||||
CV.adaptiveThreshold(this.grey, this.thres, 2, 7);
|
||||
|
||||
this.contours = CV.findContours(this.thres, this.binary);
|
||||
//Scale Fix: https://stackoverflow.com/questions/35936397/marker-detection-on-paper-sheet-using-javascript
|
||||
//this.candidates = this.findCandidates(this.contours, image.width * 0.20, 0.05, 10);
|
||||
this.candidates = this.findCandidates(this.contours, image.width * 0.01, 0.05, 10);
|
||||
this.candidates = this.clockwiseCorners(this.candidates);
|
||||
this.candidates = this.notTooNear(this.candidates, 10);
|
||||
|
||||
return this.findMarkers(this.grey, this.candidates, 49);
|
||||
};
|
||||
|
||||
AR.Detector.prototype.findCandidates = function (contours, minSize, epsilon, minLength) {
|
||||
var candidates = [],
|
||||
len = contours.length,
|
||||
contour, poly, i;
|
||||
|
||||
this.polys = [];
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
contour = contours[i];
|
||||
|
||||
if (contour.length >= minSize) {
|
||||
poly = CV.approxPolyDP(contour, contour.length * epsilon);
|
||||
|
||||
this.polys.push(poly);
|
||||
|
||||
if ((4 === poly.length) && (CV.isContourConvex(poly))) {
|
||||
|
||||
if (CV.minEdgeLength(poly) >= minLength) {
|
||||
candidates.push(poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
};
|
||||
|
||||
AR.Detector.prototype.clockwiseCorners = function (candidates) {
|
||||
var len = candidates.length,
|
||||
dx1, dx2, dy1, dy2, swap, i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
dx1 = candidates[i][1].x - candidates[i][0].x;
|
||||
dy1 = candidates[i][1].y - candidates[i][0].y;
|
||||
dx2 = candidates[i][2].x - candidates[i][0].x;
|
||||
dy2 = candidates[i][2].y - candidates[i][0].y;
|
||||
|
||||
if ((dx1 * dy2 - dy1 * dx2) < 0) {
|
||||
swap = candidates[i][1];
|
||||
candidates[i][1] = candidates[i][3];
|
||||
candidates[i][3] = swap;
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
};
|
||||
|
||||
AR.Detector.prototype.notTooNear = function (candidates, minDist) {
|
||||
var notTooNear = [],
|
||||
len = candidates.length,
|
||||
dist, dx, dy, i, j, k;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
|
||||
for (j = i + 1; j < len; ++j) {
|
||||
dist = 0;
|
||||
|
||||
for (k = 0; k < 4; ++k) {
|
||||
dx = candidates[i][k].x - candidates[j][k].x;
|
||||
dy = candidates[i][k].y - candidates[j][k].y;
|
||||
|
||||
dist += dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
if ((dist / 4) < (minDist * minDist)) {
|
||||
|
||||
if (CV.perimeter(candidates[i]) < CV.perimeter(candidates[j])) {
|
||||
candidates[i].tooNear = true;
|
||||
} else {
|
||||
candidates[j].tooNear = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (!candidates[i].tooNear) {
|
||||
notTooNear.push(candidates[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return notTooNear;
|
||||
};
|
||||
|
||||
AR.Detector.prototype.findMarkers = function (imageSrc, candidates, warpSize) {
|
||||
var markers = [],
|
||||
len = candidates.length,
|
||||
candidate, marker, i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
candidate = candidates[i];
|
||||
|
||||
CV.warp(imageSrc, this.homography, candidate, warpSize);
|
||||
|
||||
CV.threshold(this.homography, this.homography, CV.otsu(this.homography));
|
||||
|
||||
marker = this.getMarker(this.homography, candidate);
|
||||
if (marker) {
|
||||
markers.push(marker);
|
||||
}
|
||||
}
|
||||
|
||||
return markers;
|
||||
};
|
||||
|
||||
AR.Detector.prototype.getMarker = function (imageSrc, candidate) {
|
||||
var markSize = this.dictionary.markSize;
|
||||
var width = (imageSrc.width / markSize) >>> 0,
|
||||
minZero = (width * width) >> 1,
|
||||
bits = [],
|
||||
rotations = [],
|
||||
square, inc, i, j;
|
||||
|
||||
for (i = 0; i < markSize; ++i) {
|
||||
inc = (0 === i || (markSize - 1) === i) ? 1 : (markSize - 1);
|
||||
|
||||
for (j = 0; j < markSize; j += inc) {
|
||||
square = {
|
||||
x: j * width,
|
||||
y: i * width,
|
||||
width: width,
|
||||
height: width
|
||||
};
|
||||
if (CV.countNonZero(imageSrc, square) > minZero) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < markSize - 2; ++i) {
|
||||
bits[i] = [];
|
||||
|
||||
for (j = 0; j < markSize - 2; ++j) {
|
||||
square = {
|
||||
x: (j + 1) * width,
|
||||
y: (i + 1) * width,
|
||||
width: width,
|
||||
height: width
|
||||
};
|
||||
|
||||
bits[i][j] = CV.countNonZero(imageSrc, square) > minZero ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
rotations[0] = bits;
|
||||
|
||||
var foundMin = null;
|
||||
var rot = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
var found = this.dictionary.find(rotations[i]);
|
||||
if (found && (foundMin === null || found.distance < foundMin.distance)) {
|
||||
foundMin = found;
|
||||
rot = i;
|
||||
if (foundMin.distance === 0)
|
||||
break;
|
||||
}
|
||||
rotations[i + 1] = this.rotate(rotations[i]);
|
||||
}
|
||||
|
||||
if (foundMin)
|
||||
return new AR.Marker(foundMin.id, this.rotate2(candidate, 4 - rot), foundMin.distance);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
AR.Detector.prototype.rotate = function (src) {
|
||||
var dst = [],
|
||||
len = src.length,
|
||||
i, j;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
dst[i] = [];
|
||||
for (j = 0; j < src[i].length; ++j) {
|
||||
dst[i][j] = src[src[i].length - j - 1][i];
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
};
|
||||
|
||||
AR.Detector.prototype.rotate2 = function (src, rotation) {
|
||||
var dst = [],
|
||||
len = src.length,
|
||||
i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
dst[i] = src[(rotation + i) % len];
|
||||
}
|
||||
|
||||
return dst;
|
||||
};
|
||||
Vendored
+737
@@ -0,0 +1,737 @@
|
||||
/*
|
||||
Copyright (c) 2011 Juan Mellado
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
References:
|
||||
- "OpenCV: Open Computer Vision Library"
|
||||
http://sourceforge.net/projects/opencvlibrary/
|
||||
- "Stack Blur: Fast But Goodlooking"
|
||||
http://incubator.quasimondo.com/processing/fast_blur_deluxe.php
|
||||
*/
|
||||
|
||||
var CV = CV || {};
|
||||
this.CV = CV;
|
||||
|
||||
CV.Image = function(width, height, data){
|
||||
this.width = width || 0;
|
||||
this.height = height || 0;
|
||||
this.data = data || [];
|
||||
};
|
||||
|
||||
CV.grayscale = function(imageSrc, imageDst){
|
||||
var src = imageSrc.data, dst = imageDst.data, len = src.length,
|
||||
i = 0, j = 0;
|
||||
|
||||
for (; i < len; i += 4){
|
||||
dst[j ++] =
|
||||
(src[i] * 0.299 + src[i + 1] * 0.587 + src[i + 2] * 0.114 + 0.5) & 0xff;
|
||||
}
|
||||
|
||||
imageDst.width = imageSrc.width;
|
||||
imageDst.height = imageSrc.height;
|
||||
|
||||
return imageDst;
|
||||
};
|
||||
|
||||
CV.threshold = function(imageSrc, imageDst, threshold){
|
||||
var src = imageSrc.data, dst = imageDst.data,
|
||||
len = src.length, tab = [], i;
|
||||
|
||||
for (i = 0; i < 256; ++ i){
|
||||
tab[i] = i <= threshold? 0: 255;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++ i){
|
||||
dst[i] = tab[ src[i] ];
|
||||
}
|
||||
|
||||
imageDst.width = imageSrc.width;
|
||||
imageDst.height = imageSrc.height;
|
||||
|
||||
return imageDst;
|
||||
};
|
||||
|
||||
CV.adaptiveThreshold = function(imageSrc, imageDst, kernelSize, threshold){
|
||||
var src = imageSrc.data, dst = imageDst.data, len = src.length, tab = [], i;
|
||||
|
||||
CV.stackBoxBlur(imageSrc, imageDst, kernelSize);
|
||||
|
||||
for (i = 0; i < 768; ++ i){
|
||||
tab[i] = (i - 255 <= -threshold)? 255: 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++ i){
|
||||
dst[i] = tab[ src[i] - dst[i] + 255 ];
|
||||
}
|
||||
|
||||
imageDst.width = imageSrc.width;
|
||||
imageDst.height = imageSrc.height;
|
||||
|
||||
return imageDst;
|
||||
};
|
||||
|
||||
CV.otsu = function(imageSrc){
|
||||
var src = imageSrc.data, len = src.length, hist = [],
|
||||
threshold = 0, sum = 0, sumB = 0, wB = 0, wF = 0, max = 0,
|
||||
mu, between, i;
|
||||
|
||||
for (i = 0; i < 256; ++ i){
|
||||
hist[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++ i){
|
||||
hist[ src[i] ] ++;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; ++ i){
|
||||
sum += hist[i] * i;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; ++ i){
|
||||
wB += hist[i];
|
||||
if (0 !== wB){
|
||||
|
||||
wF = len - wB;
|
||||
if (0 === wF){
|
||||
break;
|
||||
}
|
||||
|
||||
sumB += hist[i] * i;
|
||||
|
||||
mu = (sumB / wB) - ( (sum - sumB) / wF );
|
||||
|
||||
between = wB * wF * mu * mu;
|
||||
|
||||
if (between > max){
|
||||
max = between;
|
||||
threshold = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return threshold;
|
||||
};
|
||||
|
||||
CV.stackBoxBlurMult =
|
||||
[1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265];
|
||||
|
||||
CV.stackBoxBlurShift =
|
||||
[0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13];
|
||||
|
||||
CV.BlurStack = function(){
|
||||
this.color = 0;
|
||||
this.next = null;
|
||||
};
|
||||
|
||||
CV.stackBoxBlur = function(imageSrc, imageDst, kernelSize){
|
||||
var src = imageSrc.data, dst = imageDst.data,
|
||||
height = imageSrc.height, width = imageSrc.width,
|
||||
heightMinus1 = height - 1, widthMinus1 = width - 1,
|
||||
size = kernelSize + kernelSize + 1, radius = kernelSize + 1,
|
||||
mult = CV.stackBoxBlurMult[kernelSize],
|
||||
shift = CV.stackBoxBlurShift[kernelSize],
|
||||
stack, stackStart, color, sum, pos, start, p, x, y, i;
|
||||
|
||||
stack = stackStart = new CV.BlurStack();
|
||||
for (i = 1; i < size; ++ i){
|
||||
stack = stack.next = new CV.BlurStack();
|
||||
}
|
||||
stack.next = stackStart;
|
||||
|
||||
pos = 0;
|
||||
|
||||
for (y = 0; y < height; ++ y){
|
||||
start = pos;
|
||||
|
||||
color = src[pos];
|
||||
sum = radius * color;
|
||||
|
||||
stack = stackStart;
|
||||
for (i = 0; i < radius; ++ i){
|
||||
stack.color = color;
|
||||
stack = stack.next;
|
||||
}
|
||||
for (i = 1; i < radius; ++ i){
|
||||
stack.color = src[pos + i];
|
||||
sum += stack.color;
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
stack = stackStart;
|
||||
for (x = 0; x < width; ++ x){
|
||||
dst[pos ++] = (sum * mult) >>> shift;
|
||||
|
||||
p = x + radius;
|
||||
p = start + (p < widthMinus1? p: widthMinus1);
|
||||
sum -= stack.color - src[p];
|
||||
|
||||
stack.color = src[p];
|
||||
stack = stack.next;
|
||||
}
|
||||
}
|
||||
|
||||
for (x = 0; x < width; ++ x){
|
||||
pos = x;
|
||||
start = pos + width;
|
||||
|
||||
color = dst[pos];
|
||||
sum = radius * color;
|
||||
|
||||
stack = stackStart;
|
||||
for (i = 0; i < radius; ++ i){
|
||||
stack.color = color;
|
||||
stack = stack.next;
|
||||
}
|
||||
for (i = 1; i < radius; ++ i){
|
||||
stack.color = dst[start];
|
||||
sum += stack.color;
|
||||
stack = stack.next;
|
||||
|
||||
start += width;
|
||||
}
|
||||
|
||||
stack = stackStart;
|
||||
for (y = 0; y < height; ++ y){
|
||||
dst[pos] = (sum * mult) >>> shift;
|
||||
|
||||
p = y + radius;
|
||||
p = x + ( (p < heightMinus1? p: heightMinus1) * width );
|
||||
sum -= stack.color - dst[p];
|
||||
|
||||
stack.color = dst[p];
|
||||
stack = stack.next;
|
||||
|
||||
pos += width;
|
||||
}
|
||||
}
|
||||
|
||||
return imageDst;
|
||||
};
|
||||
|
||||
CV.gaussianBlur = function(imageSrc, imageDst, imageMean, kernelSize){
|
||||
var kernel = CV.gaussianKernel(kernelSize);
|
||||
|
||||
imageDst.width = imageSrc.width;
|
||||
imageDst.height = imageSrc.height;
|
||||
|
||||
imageMean.width = imageSrc.width;
|
||||
imageMean.height = imageSrc.height;
|
||||
|
||||
CV.gaussianBlurFilter(imageSrc, imageMean, kernel, true);
|
||||
CV.gaussianBlurFilter(imageMean, imageDst, kernel, false);
|
||||
|
||||
return imageDst;
|
||||
};
|
||||
|
||||
CV.gaussianBlurFilter = function(imageSrc, imageDst, kernel, horizontal){
|
||||
var src = imageSrc.data, dst = imageDst.data,
|
||||
height = imageSrc.height, width = imageSrc.width,
|
||||
pos = 0, limit = kernel.length >> 1,
|
||||
cur, value, i, j, k;
|
||||
|
||||
for (i = 0; i < height; ++ i){
|
||||
|
||||
for (j = 0; j < width; ++ j){
|
||||
value = 0.0;
|
||||
|
||||
for (k = -limit; k <= limit; ++ k){
|
||||
|
||||
if (horizontal){
|
||||
cur = pos + k;
|
||||
if (j + k < 0){
|
||||
cur = pos;
|
||||
}
|
||||
else if (j + k >= width){
|
||||
cur = pos;
|
||||
}
|
||||
}else{
|
||||
cur = pos + (k * width);
|
||||
if (i + k < 0){
|
||||
cur = pos;
|
||||
}
|
||||
else if (i + k >= height){
|
||||
cur = pos;
|
||||
}
|
||||
}
|
||||
|
||||
value += kernel[limit + k] * src[cur];
|
||||
}
|
||||
|
||||
dst[pos ++] = horizontal? value: (value + 0.5) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return imageDst;
|
||||
};
|
||||
|
||||
CV.gaussianKernel = function(kernelSize){
|
||||
var tab =
|
||||
[ [1],
|
||||
[0.25, 0.5, 0.25],
|
||||
[0.0625, 0.25, 0.375, 0.25, 0.0625],
|
||||
[0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125] ],
|
||||
kernel = [], center, sigma, scale2X, sum, x, i;
|
||||
|
||||
if ( (kernelSize <= 7) && (kernelSize % 2 === 1) ){
|
||||
kernel = tab[kernelSize >> 1];
|
||||
}else{
|
||||
center = (kernelSize - 1.0) * 0.5;
|
||||
sigma = 0.8 + (0.3 * (center - 1.0) );
|
||||
scale2X = -0.5 / (sigma * sigma);
|
||||
sum = 0.0;
|
||||
for (i = 0; i < kernelSize; ++ i){
|
||||
x = i - center;
|
||||
sum += kernel[i] = Math.exp(scale2X * x * x);
|
||||
}
|
||||
sum = 1 / sum;
|
||||
for (i = 0; i < kernelSize; ++ i){
|
||||
kernel[i] *= sum;
|
||||
}
|
||||
}
|
||||
|
||||
return kernel;
|
||||
};
|
||||
|
||||
CV.findContours = function(imageSrc, binary){
|
||||
var width = imageSrc.width, height = imageSrc.height, contours = [],
|
||||
src, deltas, pos, pix, nbd, outer, hole, i, j;
|
||||
|
||||
src = CV.binaryBorder(imageSrc, binary);
|
||||
|
||||
deltas = CV.neighborhoodDeltas(width + 2);
|
||||
|
||||
pos = width + 3;
|
||||
nbd = 1;
|
||||
|
||||
for (i = 0; i < height; ++ i, pos += 2){
|
||||
|
||||
for (j = 0; j < width; ++ j, ++ pos){
|
||||
pix = src[pos];
|
||||
|
||||
if (0 !== pix){
|
||||
outer = hole = false;
|
||||
|
||||
if (1 === pix && 0 === src[pos - 1]){
|
||||
outer = true;
|
||||
}
|
||||
else if (pix >= 1 && 0 === src[pos + 1]){
|
||||
hole = true;
|
||||
}
|
||||
|
||||
if (outer || hole){
|
||||
++ nbd;
|
||||
|
||||
contours.push( CV.borderFollowing(src, pos, nbd, {x: j, y: i}, hole, deltas) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return contours;
|
||||
};
|
||||
|
||||
CV.borderFollowing = function(src, pos, nbd, point, hole, deltas){
|
||||
var contour = [], pos1, pos3, pos4, s, s_end, s_prev;
|
||||
|
||||
contour.hole = hole;
|
||||
|
||||
s = s_end = hole? 0: 4;
|
||||
do{
|
||||
s = (s - 1) & 7;
|
||||
pos1 = pos + deltas[s];
|
||||
if (src[pos1] !== 0){
|
||||
break;
|
||||
}
|
||||
}while(s !== s_end);
|
||||
|
||||
if (s === s_end){
|
||||
src[pos] = -nbd;
|
||||
contour.push( {x: point.x, y: point.y} );
|
||||
|
||||
}else{
|
||||
pos3 = pos;
|
||||
s_prev = s ^ 4;
|
||||
|
||||
while(true){
|
||||
s_end = s;
|
||||
|
||||
do{
|
||||
pos4 = pos3 + deltas[++ s];
|
||||
}while(src[pos4] === 0);
|
||||
|
||||
s &= 7;
|
||||
|
||||
if ( ( (s - 1) >>> 0) < (s_end >>> 0) ){
|
||||
src[pos3] = -nbd;
|
||||
}
|
||||
else if (src[pos3] === 1){
|
||||
src[pos3] = nbd;
|
||||
}
|
||||
|
||||
contour.push( {x: point.x, y: point.y} );
|
||||
|
||||
s_prev = s;
|
||||
|
||||
point.x += CV.neighborhood[s][0];
|
||||
point.y += CV.neighborhood[s][1];
|
||||
|
||||
if ( (pos4 === pos) && (pos3 === pos1) ){
|
||||
break;
|
||||
}
|
||||
|
||||
pos3 = pos4;
|
||||
s = (s + 4) & 7;
|
||||
}
|
||||
}
|
||||
|
||||
return contour;
|
||||
};
|
||||
|
||||
CV.neighborhood =
|
||||
[ [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1], [0, 1], [1, 1] ];
|
||||
|
||||
CV.neighborhoodDeltas = function(width){
|
||||
var deltas = [], len = CV.neighborhood.length, i = 0;
|
||||
|
||||
for (; i < len; ++ i){
|
||||
deltas[i] = CV.neighborhood[i][0] + (CV.neighborhood[i][1] * width);
|
||||
}
|
||||
|
||||
return deltas.concat(deltas);
|
||||
};
|
||||
|
||||
CV.approxPolyDP = function(contour, epsilon){
|
||||
var slice = {start_index: 0, end_index: 0},
|
||||
right_slice = {start_index: 0, end_index: 0},
|
||||
poly = [], stack = [], len = contour.length,
|
||||
pt, start_pt, end_pt, dist, max_dist, le_eps,
|
||||
dx, dy, i, j, k;
|
||||
|
||||
epsilon *= epsilon;
|
||||
|
||||
k = 0;
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
max_dist = 0;
|
||||
|
||||
k = (k + right_slice.start_index) % len;
|
||||
start_pt = contour[k];
|
||||
if (++ k === len) {k = 0;}
|
||||
|
||||
for (j = 1; j < len; ++ j){
|
||||
pt = contour[k];
|
||||
if (++ k === len) {k = 0;}
|
||||
|
||||
dx = pt.x - start_pt.x;
|
||||
dy = pt.y - start_pt.y;
|
||||
dist = dx * dx + dy * dy;
|
||||
|
||||
if (dist > max_dist){
|
||||
max_dist = dist;
|
||||
right_slice.start_index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (max_dist <= epsilon){
|
||||
poly.push( {x: start_pt.x, y: start_pt.y} );
|
||||
|
||||
}else{
|
||||
slice.start_index = k;
|
||||
slice.end_index = (right_slice.start_index += slice.start_index);
|
||||
|
||||
right_slice.start_index -= right_slice.start_index >= len? len: 0;
|
||||
right_slice.end_index = slice.start_index;
|
||||
if (right_slice.end_index < right_slice.start_index){
|
||||
right_slice.end_index += len;
|
||||
}
|
||||
|
||||
stack.push( {start_index: right_slice.start_index, end_index: right_slice.end_index} );
|
||||
stack.push( {start_index: slice.start_index, end_index: slice.end_index} );
|
||||
}
|
||||
|
||||
while(stack.length !== 0){
|
||||
slice = stack.pop();
|
||||
|
||||
end_pt = contour[slice.end_index % len];
|
||||
start_pt = contour[k = slice.start_index % len];
|
||||
if (++ k === len) {k = 0;}
|
||||
|
||||
if (slice.end_index <= slice.start_index + 1){
|
||||
le_eps = true;
|
||||
|
||||
}else{
|
||||
max_dist = 0;
|
||||
|
||||
dx = end_pt.x - start_pt.x;
|
||||
dy = end_pt.y - start_pt.y;
|
||||
|
||||
for (i = slice.start_index + 1; i < slice.end_index; ++ i){
|
||||
pt = contour[k];
|
||||
if (++ k === len) {k = 0;}
|
||||
|
||||
dist = Math.abs( (pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
|
||||
|
||||
if (dist > max_dist){
|
||||
max_dist = dist;
|
||||
right_slice.start_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
le_eps = max_dist * max_dist <= epsilon * (dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
if (le_eps){
|
||||
poly.push( {x: start_pt.x, y: start_pt.y} );
|
||||
|
||||
}else{
|
||||
right_slice.end_index = slice.end_index;
|
||||
slice.end_index = right_slice.start_index;
|
||||
|
||||
stack.push( {start_index: right_slice.start_index, end_index: right_slice.end_index} );
|
||||
stack.push( {start_index: slice.start_index, end_index: slice.end_index} );
|
||||
}
|
||||
}
|
||||
|
||||
return poly;
|
||||
};
|
||||
|
||||
CV.warp = function(imageSrc, imageDst, contour, warpSize){
|
||||
var src = imageSrc.data, dst = imageDst.data,
|
||||
width = imageSrc.width, height = imageSrc.height,
|
||||
pos = 0,
|
||||
sx1, sx2, dx1, dx2, sy1, sy2, dy1, dy2, p1, p2, p3, p4,
|
||||
m, r, s, t, u, v, w, x, y, i, j;
|
||||
|
||||
m = CV.getPerspectiveTransform(contour, warpSize - 1);
|
||||
|
||||
r = m[8];
|
||||
s = m[2];
|
||||
t = m[5];
|
||||
|
||||
for (i = 0; i < warpSize; ++ i){
|
||||
r += m[7];
|
||||
s += m[1];
|
||||
t += m[4];
|
||||
|
||||
u = r;
|
||||
v = s;
|
||||
w = t;
|
||||
|
||||
for (j = 0; j < warpSize; ++ j){
|
||||
u += m[6];
|
||||
v += m[0];
|
||||
w += m[3];
|
||||
|
||||
x = v / u;
|
||||
y = w / u;
|
||||
|
||||
sx1 = x >>> 0;
|
||||
sx2 = (sx1 === width - 1)? sx1: sx1 + 1;
|
||||
dx1 = x - sx1;
|
||||
dx2 = 1.0 - dx1;
|
||||
|
||||
sy1 = y >>> 0;
|
||||
sy2 = (sy1 === height - 1)? sy1: sy1 + 1;
|
||||
dy1 = y - sy1;
|
||||
dy2 = 1.0 - dy1;
|
||||
|
||||
p1 = p2 = sy1 * width;
|
||||
p3 = p4 = sy2 * width;
|
||||
|
||||
dst[pos ++] =
|
||||
(dy2 * (dx2 * src[p1 + sx1] + dx1 * src[p2 + sx2]) +
|
||||
dy1 * (dx2 * src[p3 + sx1] + dx1 * src[p4 + sx2]) ) & 0xff;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
imageDst.width = warpSize;
|
||||
imageDst.height = warpSize;
|
||||
|
||||
return imageDst;
|
||||
};
|
||||
|
||||
CV.getPerspectiveTransform = function(src, size){
|
||||
var rq = CV.square2quad(src);
|
||||
|
||||
rq[0] /= size;
|
||||
rq[1] /= size;
|
||||
rq[3] /= size;
|
||||
rq[4] /= size;
|
||||
rq[6] /= size;
|
||||
rq[7] /= size;
|
||||
|
||||
return rq;
|
||||
};
|
||||
|
||||
CV.square2quad = function(src){
|
||||
var sq = [], px, py, dx1, dx2, dy1, dy2, den;
|
||||
|
||||
px = src[0].x - src[1].x + src[2].x - src[3].x;
|
||||
py = src[0].y - src[1].y + src[2].y - src[3].y;
|
||||
|
||||
if (0 === px && 0 === py){
|
||||
sq[0] = src[1].x - src[0].x;
|
||||
sq[1] = src[2].x - src[1].x;
|
||||
sq[2] = src[0].x;
|
||||
sq[3] = src[1].y - src[0].y;
|
||||
sq[4] = src[2].y - src[1].y;
|
||||
sq[5] = src[0].y;
|
||||
sq[6] = 0;
|
||||
sq[7] = 0;
|
||||
sq[8] = 1;
|
||||
|
||||
}else{
|
||||
dx1 = src[1].x - src[2].x;
|
||||
dx2 = src[3].x - src[2].x;
|
||||
dy1 = src[1].y - src[2].y;
|
||||
dy2 = src[3].y - src[2].y;
|
||||
den = dx1 * dy2 - dx2 * dy1;
|
||||
|
||||
sq[6] = (px * dy2 - dx2 * py) / den;
|
||||
sq[7] = (dx1 * py - px * dy1) / den;
|
||||
sq[8] = 1;
|
||||
sq[0] = src[1].x - src[0].x + sq[6] * src[1].x;
|
||||
sq[1] = src[3].x - src[0].x + sq[7] * src[3].x;
|
||||
sq[2] = src[0].x;
|
||||
sq[3] = src[1].y - src[0].y + sq[6] * src[1].y;
|
||||
sq[4] = src[3].y - src[0].y + sq[7] * src[3].y;
|
||||
sq[5] = src[0].y;
|
||||
}
|
||||
|
||||
return sq;
|
||||
};
|
||||
|
||||
CV.isContourConvex = function(contour){
|
||||
var orientation = 0, convex = true,
|
||||
len = contour.length, i = 0, j = 0,
|
||||
cur_pt, prev_pt, dxdy0, dydx0, dx0, dy0, dx, dy;
|
||||
|
||||
prev_pt = contour[len - 1];
|
||||
cur_pt = contour[0];
|
||||
|
||||
dx0 = cur_pt.x - prev_pt.x;
|
||||
dy0 = cur_pt.y - prev_pt.y;
|
||||
|
||||
for (; i < len; ++ i){
|
||||
if (++ j === len) {j = 0;}
|
||||
|
||||
prev_pt = cur_pt;
|
||||
cur_pt = contour[j];
|
||||
|
||||
dx = cur_pt.x - prev_pt.x;
|
||||
dy = cur_pt.y - prev_pt.y;
|
||||
dxdy0 = dx * dy0;
|
||||
dydx0 = dy * dx0;
|
||||
|
||||
orientation |= dydx0 > dxdy0? 1: (dydx0 < dxdy0? 2: 3);
|
||||
|
||||
if (3 === orientation){
|
||||
convex = false;
|
||||
break;
|
||||
}
|
||||
|
||||
dx0 = dx;
|
||||
dy0 = dy;
|
||||
}
|
||||
|
||||
return convex;
|
||||
};
|
||||
|
||||
CV.perimeter = function(poly){
|
||||
var len = poly.length, i = 0, j = len - 1,
|
||||
p = 0.0, dx, dy;
|
||||
|
||||
for (; i < len; j = i ++){
|
||||
dx = poly[i].x - poly[j].x;
|
||||
dy = poly[i].y - poly[j].y;
|
||||
|
||||
p += Math.sqrt(dx * dx + dy * dy) ;
|
||||
}
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
CV.minEdgeLength = function(poly){
|
||||
var len = poly.length, i = 0, j = len - 1,
|
||||
min = Infinity, d, dx, dy;
|
||||
|
||||
for (; i < len; j = i ++){
|
||||
dx = poly[i].x - poly[j].x;
|
||||
dy = poly[i].y - poly[j].y;
|
||||
|
||||
d = dx * dx + dy * dy;
|
||||
|
||||
if (d < min){
|
||||
min = d;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.sqrt(min);
|
||||
};
|
||||
|
||||
CV.countNonZero = function(imageSrc, square){
|
||||
var src = imageSrc.data, height = square.height, width = square.width,
|
||||
pos = square.x + (square.y * imageSrc.width),
|
||||
span = imageSrc.width - width,
|
||||
nz = 0, i, j;
|
||||
|
||||
for (i = 0; i < height; ++ i){
|
||||
|
||||
for (j = 0; j < width; ++ j){
|
||||
|
||||
if ( 0 !== src[pos ++] ){
|
||||
++ nz;
|
||||
}
|
||||
}
|
||||
|
||||
pos += span;
|
||||
}
|
||||
|
||||
return nz;
|
||||
};
|
||||
|
||||
CV.binaryBorder = function(imageSrc, dst){
|
||||
var src = imageSrc.data, height = imageSrc.height, width = imageSrc.width,
|
||||
posSrc = 0, posDst = 0, i, j;
|
||||
|
||||
for (j = -2; j < width; ++ j){
|
||||
dst[posDst ++] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < height; ++ i){
|
||||
dst[posDst ++] = 0;
|
||||
|
||||
for (j = 0; j < width; ++ j){
|
||||
dst[posDst ++] = (0 === src[posSrc ++]? 0: 1);
|
||||
}
|
||||
|
||||
dst[posDst ++] = 0;
|
||||
}
|
||||
|
||||
for (j = -2; j < width; ++ j){
|
||||
dst[posDst ++] = 0;
|
||||
}
|
||||
|
||||
return dst;
|
||||
};
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
By downloading, copying, installing or using the software you agree to this
|
||||
license. If you do not agree to this license, do not download, install,
|
||||
copy or use the software.
|
||||
License Agreement
|
||||
For Open Source Computer Vision Library
|
||||
(3-clause BSD License)
|
||||
Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
Third party copyrights are property of their respective owners.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall copyright holders or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused
|
||||
and on any theory of liability, whether in contract, strict liability,
|
||||
or tort (including negligence or otherwise) arising in any way out of
|
||||
the use of this software, even if advised of the possibility of such damage.
|
||||
*/
|
||||
|
||||
//Dictionary extracted from https://github.com/opencv/opencv_contrib/blob/4.x/modules/aruco/src/predefined_dictionaries.hpp
|
||||
|
||||
var AR = this.AR || require('../aruco').AR;
|
||||
AR.DICTIONARIES['ARUCO_4X4_1000'] = {
|
||||
nBits: 16,
|
||||
tau: null,
|
||||
codeList: [[181,50],[15,154],[51,45],[153,70],[84,158],[121,205],[158,46],[196,242],[254,218],[207,86],[249,145],[17,167],[14,183],[42,15],[36,177],[38,62],[70,101],[102,0],[108,94],[118,175],[134,139],[176,43],[204,213],[221,130],[254,71],[148,113],[172,228],[165,84],[33,35],[52,111],[68,21],[87,178],[158,207],[240,203],[8,174],[9,41],[24,117],[4,255],[13,246],[28,90],[23,24],[42,40],[50,140],[56,178],[36,232],[46,235],[45,63],[75,100],[80,46],[80,19],[81,148],[85,104],[93,65],[95,151],[104,1],[104,103],[97,36],[97,233],[107,18],[111,229],[103,223],[126,27],[128,160],[131,68],[139,162],[147,122],[132,108],[133,42],[133,156],[156,137],[159,161],[187,124],[188,4],[182,91],[191,200],[183,171],[202,31],[201,98],[217,88],[211,213],[204,152],[199,160],[197,55],[233,93],[249,37],[251,187],[238,42],[247,77],[53,117],[138,173],[118,23],[10,207],[6,75],[45,193],[73,216],[67,244],[79,54],[79,211],[105,228],[112,199],[122,110],[180,234],[237,79],[252,231],[254,166],[0,37],[0,67],[10,136],[10,134],[2,111],[0,28],[0,151],[8,55],[10,49],[9,198],[11,1],[9,251],[11,88],[16,130],[24,45],[16,120],[16,115],[18,116],[18,177],[26,249],[19,6],[12,14],[12,241],[4,51],[12,159],[14,242],[14,253],[7,76],[15,164],[7,47],[5,181],[15,145],[7,219],[30,228],[20,57],[29,128],[21,200],[31,139],[21,186],[29,177],[32,128],[40,233],[34,162],[40,83],[42,240],[34,247],[41,64],[33,70],[41,185],[43,156],[43,178],[56,202],[56,46],[48,7],[56,231],[58,73],[58,101],[50,93],[59,136],[57,29],[59,211],[38,71],[39,128],[47,170],[45,20],[37,222],[37,83],[47,119],[52,72],[60,168],[60,65],[52,13],[52,251],[54,154],[61,224],[53,106],[61,9],[61,237],[63,196],[63,108],[55,206],[61,92],[61,118],[55,176],[63,23],[63,255],[72,229],[66,104],[74,45],[65,96],[73,81],[65,221],[75,223],[88,79],[90,72],[88,22],[80,93],[90,250],[90,181],[81,35],[91,138],[89,25],[81,53],[76,105],[70,193],[78,11],[68,95],[78,89],[77,131],[77,125],[71,216],[71,115],[92,133],[94,68],[86,43],[92,187],[85,195],[95,110],[95,235],[93,18],[85,94],[98,112],[98,21],[97,194],[107,32],[99,69],[107,92],[107,91],[120,12],[122,207],[120,127],[121,128],[113,229],[113,116],[121,182],[113,211],[123,51],[100,106],[102,168],[110,167],[110,145],[101,34],[109,203],[103,141],[109,49],[126,128],[126,226],[126,141],[116,210],[124,50],[126,53],[117,171],[119,5],[127,43],[125,218],[127,146],[128,117],[128,243],[129,166],[137,237],[129,252],[152,166],[154,32],[145,67],[153,249],[145,147],[155,212],[132,9],[132,107],[134,196],[142,100],[134,26],[133,78],[141,203],[133,103],[133,175],[133,215],[135,179],[156,225],[156,242],[148,23],[149,0],[149,162],[157,35],[159,98],[157,82],[149,218],[160,197],[170,205],[162,216],[162,87],[169,61],[169,87],[171,82],[163,54],[163,89],[176,244],[184,18],[176,191],[178,157],[187,237],[185,114],[185,150],[164,195],[172,210],[174,177],[165,130],[175,101],[165,123],[175,250],[180,100],[188,98],[180,129],[182,160],[190,238],[190,13],[188,217],[190,248],[181,40],[183,9],[183,210],[192,234],[192,25],[192,253],[200,211],[202,90],[193,77],[201,180],[193,87],[195,152],[195,29],[216,128],[216,239],[218,43],[208,30],[209,5],[211,173],[219,167],[196,201],[204,120],[205,69],[197,11],[207,207],[220,172],[212,2],[220,99],[212,39],[212,245],[214,120],[222,184],[221,230],[213,93],[221,189],[223,29],[226,202],[234,107],[224,180],[226,56],[226,212],[227,34],[225,216],[240,3],[242,204],[248,246],[241,73],[243,234],[241,156],[249,245],[241,59],[236,141],[238,201],[230,15],[228,247],[231,96],[239,232],[237,178],[229,21],[239,209],[244,134],[252,1],[246,195],[244,124],[252,147],[245,66],[253,152],[245,61],[2,189],[0,225],[2,226],[2,174],[8,120],[0,116],[8,158],[8,209],[8,125],[10,50],[10,222],[2,81],[1,162],[3,128],[11,131],[11,75],[11,39],[11,239],[9,182],[9,89],[9,147],[11,248],[3,217],[3,241],[16,196],[24,171],[26,160],[26,4],[26,108],[26,174],[18,137],[16,23],[26,243],[25,64],[17,2],[17,43],[17,207],[27,34],[19,46],[17,21],[19,187],[12,32],[12,201],[12,220],[12,54],[6,20],[6,114],[13,97],[5,13],[13,143],[15,224],[15,73],[7,133],[5,144],[13,51],[15,150],[15,118],[20,96],[28,141],[20,218],[28,115],[30,148],[30,186],[22,217],[30,61],[22,251],[29,233],[29,254],[31,159],[40,139],[32,175],[34,14],[34,169],[42,141],[42,163],[42,239],[40,144],[40,59],[42,88],[34,51],[33,160],[33,2],[33,165],[33,199],[43,3],[35,103],[41,48],[41,210],[43,25],[43,155],[43,151],[56,40],[56,165],[58,134],[50,1],[56,159],[50,210],[58,153],[58,213],[57,232],[59,193],[51,67],[59,231],[49,154],[51,144],[59,158],[36,196],[44,74],[44,173],[44,207],[44,103],[38,234],[46,229],[44,112],[46,18],[46,209],[46,57],[37,100],[37,231],[47,204],[45,188],[45,113],[37,213],[37,155],[39,16],[47,124],[39,242],[39,58],[47,182],[39,211],[47,179],[39,31],[60,75],[54,192],[54,238],[62,233],[52,184],[60,20],[60,82],[52,114],[52,126],[52,191],[62,113],[62,83],[61,140],[53,162],[53,46],[53,45],[55,172],[53,112],[55,250],[63,241],[63,219],[72,196],[72,233],[74,194],[74,65],[66,235],[72,19],[74,216],[66,253],[74,23],[73,99],[67,110],[65,58],[73,177],[65,61],[Line truncated
|
||||
};
|
||||
Vendored
+495
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
Copyright (c) 2012 Juan Mellado
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
References:
|
||||
- "Iterative Pose Estimation using Coplanar Feature Points"
|
||||
Denis Oberkampf, Daniel F. DeMenthon, Larry S. Davis
|
||||
http://www.cfar.umd.edu/~daniel/daniel_papersfordownload/CoplanarPts.pdf
|
||||
*/
|
||||
|
||||
var POS = POS || {};
|
||||
this.POS = POS;
|
||||
|
||||
var SVD = this.SVD || require('./svd').SVD;
|
||||
|
||||
POS.Posit = function(modelSize, focalLength){
|
||||
this.objectPoints = this.buildModel(modelSize);
|
||||
this.focalLength = focalLength;
|
||||
|
||||
this.objectVectors = [];
|
||||
this.objectNormal = [];
|
||||
this.objectMatrix = [[],[],[]];
|
||||
|
||||
this.init();
|
||||
};
|
||||
|
||||
POS.Posit.prototype.buildModel = function(modelSize){
|
||||
var half = modelSize / 2.0;
|
||||
|
||||
return [
|
||||
[-half, half, 0.0],
|
||||
[ half, half, 0.0],
|
||||
[ half, -half, 0.0],
|
||||
[-half, -half, 0.0] ];
|
||||
};
|
||||
|
||||
POS.Posit.prototype.init = function(){
|
||||
var np = this.objectPoints.length,
|
||||
vectors = [], n = [], len = 0.0, row = 2, i;
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
this.objectVectors[i] = [this.objectPoints[i][0] - this.objectPoints[0][0],
|
||||
this.objectPoints[i][1] - this.objectPoints[0][1],
|
||||
this.objectPoints[i][2] - this.objectPoints[0][2]];
|
||||
|
||||
vectors[i] = [this.objectVectors[i][0],
|
||||
this.objectVectors[i][1],
|
||||
this.objectVectors[i][2]];
|
||||
}
|
||||
|
||||
while(0.0 === len){
|
||||
n[0] = this.objectVectors[1][1] * this.objectVectors[row][2] -
|
||||
this.objectVectors[1][2] * this.objectVectors[row][1];
|
||||
n[1] = this.objectVectors[1][2] * this.objectVectors[row][0] -
|
||||
this.objectVectors[1][0] * this.objectVectors[row][2];
|
||||
n[2] = this.objectVectors[1][0] * this.objectVectors[row][1] -
|
||||
this.objectVectors[1][1] * this.objectVectors[row][0];
|
||||
|
||||
len = Math.sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]);
|
||||
|
||||
++ row;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
this.objectNormal[i] = n[i] / len;
|
||||
}
|
||||
|
||||
POS.pseudoInverse(vectors, np, this.objectMatrix);
|
||||
};
|
||||
|
||||
POS.Posit.prototype.pose = function(imagePoints){
|
||||
var posRotation1 = [[],[],[]], posRotation2 = [[],[],[]], posTranslation = [],
|
||||
rotation1 = [[],[],[]], rotation2 = [[],[],[]], translation1 = [], translation2 = [],
|
||||
error1, error2, valid1, valid2, i, j;
|
||||
|
||||
this.pos(imagePoints, posRotation1, posRotation2, posTranslation);
|
||||
|
||||
valid1 = this.isValid(posRotation1, posTranslation);
|
||||
if (valid1){
|
||||
error1 = this.iterate(imagePoints, posRotation1, posTranslation, rotation1, translation1);
|
||||
}else{
|
||||
error1 = {euclidean: -1.0, pixels: -1, maximum: -1.0};
|
||||
}
|
||||
|
||||
valid2 = this.isValid(posRotation2, posTranslation);
|
||||
if (valid2){
|
||||
error2 = this.iterate(imagePoints, posRotation2, posTranslation, rotation2, translation2);
|
||||
}else{
|
||||
error2 = {euclidean: -1.0, pixels: -1, maximum: -1.0};
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < 3; ++ j){
|
||||
if (valid1){
|
||||
translation1[i] -= rotation1[i][j] * this.objectPoints[0][j];
|
||||
}
|
||||
if (valid2){
|
||||
translation2[i] -= rotation2[i][j] * this.objectPoints[0][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error1.euclidean < error2.euclidean?
|
||||
new POS.Pose(error1.pixels, rotation1, translation1, error2.pixels, rotation2, translation2):
|
||||
new POS.Pose(error2.pixels, rotation2, translation2, error1.pixels, rotation1, translation1);
|
||||
};
|
||||
|
||||
POS.Posit.prototype.pos = function(imagePoints, rotation1, rotation2, translation){
|
||||
var np = this.objectPoints.length, imageVectors = [],
|
||||
i0 = [], j0 = [], ivec = [], jvec = [], row1 = [], row2 = [], row3 = [],
|
||||
i0i0, j0j0, i0j0, delta, q, lambda, mu, scale, i, j;
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
imageVectors[i] = [imagePoints[i].x - imagePoints[0].x,
|
||||
imagePoints[i].y - imagePoints[0].y];
|
||||
}
|
||||
|
||||
//i0 and j0
|
||||
for (i = 0; i < 3; ++ i){
|
||||
i0[i] = 0.0;
|
||||
j0[i] = 0.0;
|
||||
for (j = 0; j < np; ++ j){
|
||||
i0[i] += this.objectMatrix[i][j] * imageVectors[j][0];
|
||||
j0[i] += this.objectMatrix[i][j] * imageVectors[j][1];
|
||||
}
|
||||
}
|
||||
|
||||
i0i0 = i0[0] * i0[0] + i0[1] * i0[1] + i0[2] * i0[2];
|
||||
j0j0 = j0[0] * j0[0] + j0[1] * j0[1] + j0[2] * j0[2];
|
||||
i0j0 = i0[0] * j0[0] + i0[1] * j0[1] + i0[2] * j0[2];
|
||||
|
||||
//Lambda and mu
|
||||
delta = (j0j0 - i0i0) * (j0j0 - i0i0) + 4.0 * (i0j0 * i0j0);
|
||||
|
||||
if (j0j0 - i0i0 >= 0.0){
|
||||
q = (j0j0 - i0i0 + Math.sqrt(delta) ) / 2.0;
|
||||
}else{
|
||||
q = (j0j0 - i0i0 - Math.sqrt(delta) ) / 2.0;
|
||||
}
|
||||
|
||||
if (q >= 0.0){
|
||||
lambda = Math.sqrt(q);
|
||||
if (0.0 === lambda){
|
||||
mu = 0.0;
|
||||
}else{
|
||||
mu = -i0j0 / lambda;
|
||||
}
|
||||
}else{
|
||||
lambda = Math.sqrt( -(i0j0 * i0j0) / q);
|
||||
if (0.0 === lambda){
|
||||
mu = Math.sqrt(i0i0 - j0j0);
|
||||
}else{
|
||||
mu = -i0j0 / lambda;
|
||||
}
|
||||
}
|
||||
|
||||
//First rotation
|
||||
for (i = 0; i < 3; ++ i){
|
||||
ivec[i] = i0[i] + lambda * this.objectNormal[i];
|
||||
jvec[i] = j0[i] + mu * this.objectNormal[i];
|
||||
}
|
||||
|
||||
scale = Math.sqrt(ivec[0] * ivec[0] + ivec[1] * ivec[1] + ivec[2] * ivec[2]);
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
row1[i] = ivec[i] / scale;
|
||||
row2[i] = jvec[i] / scale;
|
||||
}
|
||||
|
||||
row3[0] = row1[1] * row2[2] - row1[2] * row2[1];
|
||||
row3[1] = row1[2] * row2[0] - row1[0] * row2[2];
|
||||
row3[2] = row1[0] * row2[1] - row1[1] * row2[0];
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
rotation1[0][i] = row1[i];
|
||||
rotation1[1][i] = row2[i];
|
||||
rotation1[2][i] = row3[i];
|
||||
}
|
||||
|
||||
//Second rotation
|
||||
for (i = 0; i < 3; ++ i){
|
||||
ivec[i] = i0[i] - lambda * this.objectNormal[i];
|
||||
jvec[i] = j0[i] - mu * this.objectNormal[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
row1[i] = ivec[i] / scale;
|
||||
row2[i] = jvec[i] / scale;
|
||||
}
|
||||
|
||||
row3[0] = row1[1] * row2[2] - row1[2] * row2[1];
|
||||
row3[1] = row1[2] * row2[0] - row1[0] * row2[2];
|
||||
row3[2] = row1[0] * row2[1] - row1[1] * row2[0];
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
rotation2[0][i] = row1[i];
|
||||
rotation2[1][i] = row2[i];
|
||||
rotation2[2][i] = row3[i];
|
||||
}
|
||||
|
||||
//Translation
|
||||
translation[0] = imagePoints[0].x / scale;
|
||||
translation[1] = imagePoints[0].y / scale;
|
||||
translation[2] = this.focalLength / scale;
|
||||
};
|
||||
|
||||
POS.Posit.prototype.isValid = function(rotation, translation){
|
||||
var np = this.objectPoints.length, zmin = Infinity, i = 0, zi;
|
||||
|
||||
for (; i < np; ++ i){
|
||||
zi = translation[2] +
|
||||
(rotation[2][0] * this.objectVectors[i][0] +
|
||||
rotation[2][1] * this.objectVectors[i][1] +
|
||||
rotation[2][2] * this.objectVectors[i][2]);
|
||||
if (zi < zmin){
|
||||
zmin = zi;
|
||||
}
|
||||
}
|
||||
|
||||
return zmin >= 0.0;
|
||||
};
|
||||
|
||||
POS.Posit.prototype.iterate = function(imagePoints, posRotation, posTranslation, rotation, translation){
|
||||
var np = this.objectPoints.length,
|
||||
oldSopImagePoints = [], sopImagePoints = [],
|
||||
rotation1 = [[],[],[]], rotation2 = [[],[],[]],
|
||||
translation1 = [], translation2 = [],
|
||||
converged = false, iteration = 0,
|
||||
oldImageDifference, imageDifference, factor,
|
||||
error, error1, error2, delta, i, j;
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
oldSopImagePoints[i] = {x: imagePoints[i].x,
|
||||
y: imagePoints[i].y};
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < 3; ++ j){
|
||||
rotation[i][j] = posRotation[i][j];
|
||||
}
|
||||
translation[i] = posTranslation[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
factor = 0.0;
|
||||
for (j = 0; j < 3; ++ j){
|
||||
factor += this.objectVectors[i][j] * rotation[2][j] / translation[2];
|
||||
}
|
||||
sopImagePoints[i] = {x: (1.0 + factor) * imagePoints[i].x,
|
||||
y: (1.0 + factor) * imagePoints[i].y};
|
||||
}
|
||||
|
||||
imageDifference = 0.0;
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
imageDifference += Math.abs(sopImagePoints[i].x - oldSopImagePoints[i].x);
|
||||
imageDifference += Math.abs(sopImagePoints[i].y - oldSopImagePoints[i].y);
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
translation1[i] = translation[i] -
|
||||
(rotation[i][0] * this.objectPoints[0][0] +
|
||||
rotation[i][1] * this.objectPoints[0][1] +
|
||||
rotation[i][2] * this.objectPoints[0][2]);
|
||||
}
|
||||
|
||||
error = error1 = this.error(imagePoints, rotation, translation1);
|
||||
|
||||
//Convergence
|
||||
converged = (0.0 === error1.pixels) || (imageDifference < 0.01);
|
||||
|
||||
while( iteration ++ < 100 && !converged ){
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
oldSopImagePoints[i].x = sopImagePoints[i].x;
|
||||
oldSopImagePoints[i].y = sopImagePoints[i].y;
|
||||
}
|
||||
|
||||
this.pos(sopImagePoints, rotation1, rotation2, translation);
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
translation1[i] = translation[i] -
|
||||
(rotation1[i][0] * this.objectPoints[0][0] +
|
||||
rotation1[i][1] * this.objectPoints[0][1] +
|
||||
rotation1[i][2] * this.objectPoints[0][2]);
|
||||
|
||||
translation2[i] = translation[i] -
|
||||
(rotation2[i][0] * this.objectPoints[0][0] +
|
||||
rotation2[i][1] * this.objectPoints[0][1] +
|
||||
rotation2[i][2] * this.objectPoints[0][2]);
|
||||
}
|
||||
|
||||
error1 = this.error(imagePoints, rotation1, translation1);
|
||||
error2 = this.error(imagePoints, rotation2, translation2);
|
||||
|
||||
if ( (error1.euclidean >= 0.0) && (error2.euclidean >= 0.0) ){
|
||||
if (error2.euclidean < error1.euclidean){
|
||||
error = error2;
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < 3; ++ j){
|
||||
rotation[i][j] = rotation2[i][j];
|
||||
}
|
||||
}
|
||||
}else{
|
||||
error = error1;
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < 3; ++ j){
|
||||
rotation[i][j] = rotation1[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (error1.euclidean < 0.0) && (error2.euclidean >= 0.0) ){
|
||||
error = error2;
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < 3; ++ j){
|
||||
rotation[i][j] = rotation2[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (error2.euclidean < 0.0) && (error1.euclidean >= 0.0) ){
|
||||
error = error1;
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < 3; ++ j){
|
||||
rotation[i][j] = rotation1[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
factor = 0.0;
|
||||
for (j = 0; j < 3; ++ j){
|
||||
factor += this.objectVectors[i][j] * rotation[2][j] / translation[2];
|
||||
}
|
||||
sopImagePoints[i].x = (1.0 + factor) * imagePoints[i].x;
|
||||
sopImagePoints[i].y = (1.0 + factor) * imagePoints[i].y;
|
||||
}
|
||||
|
||||
oldImageDifference = imageDifference;
|
||||
imageDifference = 0.0;
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
imageDifference += Math.abs(sopImagePoints[i].x - oldSopImagePoints[i].x);
|
||||
imageDifference += Math.abs(sopImagePoints[i].y - oldSopImagePoints[i].y);
|
||||
}
|
||||
|
||||
delta = Math.abs(imageDifference - oldImageDifference);
|
||||
|
||||
converged = (0.0 === error.pixels) || (delta < 0.01);
|
||||
}
|
||||
|
||||
return error;
|
||||
};
|
||||
|
||||
POS.Posit.prototype.error = function(imagePoints, rotation, translation){
|
||||
var np = this.objectPoints.length,
|
||||
move = [], projection = [], errorvec = [],
|
||||
euclidean = 0.0, pixels = 0.0, maximum = 0.0,
|
||||
i, j, k;
|
||||
|
||||
if ( !this.isValid(rotation, translation) ){
|
||||
return {euclidean: -1.0, pixels: -1, maximum: -1.0};
|
||||
}
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
move[i] = [];
|
||||
for (j = 0; j < 3; ++ j){
|
||||
move[i][j] = translation[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
for (j = 0; j < 3; ++ j){
|
||||
for (k = 0; k < 3; ++ k){
|
||||
move[i][j] += rotation[j][k] * this.objectPoints[i][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
projection[i] = [];
|
||||
for (j = 0; j < 2; ++ j){
|
||||
projection[i][j] = this.focalLength * move[i][j] / move[i][2];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
errorvec[i] = [projection[i][0] - imagePoints[i].x,
|
||||
projection[i][1] - imagePoints[i].y];
|
||||
}
|
||||
|
||||
for (i = 0; i < np; ++ i){
|
||||
euclidean += Math.sqrt(errorvec[i][0] * errorvec[i][0] +
|
||||
errorvec[i][1] * errorvec[i][1]);
|
||||
|
||||
pixels += Math.abs( Math.round(projection[i][0]) - Math.round(imagePoints[i].x) ) +
|
||||
Math.abs( Math.round(projection[i][1]) - Math.round(imagePoints[i].y) );
|
||||
|
||||
if (Math.abs(errorvec[i][0]) > maximum){
|
||||
maximum = Math.abs(errorvec[i][0]);
|
||||
}
|
||||
if (Math.abs(errorvec[i][1]) > maximum){
|
||||
maximum = Math.abs(errorvec[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
return {euclidean: euclidean / np, pixels: pixels, maximum: maximum};
|
||||
};
|
||||
|
||||
POS.pseudoInverse = function(a, n, b){
|
||||
var w = [], v = [[],[],[]], s = [[],[],[]],
|
||||
wmax = 0.0, cn = 0,
|
||||
i, j, k;
|
||||
|
||||
SVD.svdcmp(a, n, 3, w, v);
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
if (w[i] > wmax){
|
||||
wmax = w[i];
|
||||
}
|
||||
}
|
||||
|
||||
wmax *= 0.01;
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
if (w[i] < wmax){
|
||||
w[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 3; ++ j){
|
||||
if (0.0 === w[j]){
|
||||
++ cn;
|
||||
for (k = j; k < 2; ++ k){
|
||||
for (i = 0; i < n; ++ i){
|
||||
a[i][k] = a[i][k + 1];
|
||||
}
|
||||
for (i = 0; i < 3; ++ i){
|
||||
v[i][k] = v[i][k + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 2; ++ j){
|
||||
if (0.0 === w[j]){
|
||||
w[j] = w[j + 1];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < 3 - cn; ++ j){
|
||||
s[i][j] = v[i][j] / w[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++ i){
|
||||
for (j = 0; j < n; ++ j){
|
||||
b[i][j] = 0.0;
|
||||
for (k = 0; k < 3 - cn; ++ k){
|
||||
b[i][j] += s[i][k] * a[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
POS.Pose = function(error1, rotation1, translation1, error2, rotation2, translation2){
|
||||
this.bestError = error1;
|
||||
this.bestRotation = rotation1;
|
||||
this.bestTranslation = translation1;
|
||||
this.alternativeError = error2;
|
||||
this.alternativeRotation = rotation2;
|
||||
this.alternativeTranslation = translation2;
|
||||
};
|
||||
Vendored
+284
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
Copyright (c) 2012 Juan Mellado
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
References:
|
||||
- "Numerical Recipes in C - Second Edition"
|
||||
http://www.nr.com/
|
||||
*/
|
||||
|
||||
var SVD = SVD || {};
|
||||
this.SVD = SVD;
|
||||
|
||||
SVD.svdcmp = function(a, m, n, w, v){
|
||||
var flag, i, its, j, jj, k, l, nm,
|
||||
anorm = 0.0, c, f, g = 0.0, h, s, scale = 0.0, x, y, z, rv1 = [];
|
||||
|
||||
//Householder reduction to bidiagonal form
|
||||
for (i = 0; i < n; ++ i){
|
||||
l = i + 1;
|
||||
rv1[i] = scale * g;
|
||||
g = s = scale = 0.0;
|
||||
if (i < m){
|
||||
for (k = i; k < m; ++ k){
|
||||
scale += Math.abs( a[k][i] );
|
||||
}
|
||||
if (0.0 !== scale){
|
||||
for (k = i; k < m; ++ k){
|
||||
a[k][i] /= scale;
|
||||
s += a[k][i] * a[k][i];
|
||||
}
|
||||
f = a[i][i];
|
||||
g = -SVD.sign( Math.sqrt(s), f );
|
||||
h = f * g - s;
|
||||
a[i][i] = f - g;
|
||||
for (j = l; j < n; ++ j){
|
||||
for (s = 0.0, k = i; k < m; ++ k){
|
||||
s += a[k][i] * a[k][j];
|
||||
}
|
||||
f = s / h;
|
||||
for (k = i; k < m; ++ k){
|
||||
a[k][j] += f * a[k][i];
|
||||
}
|
||||
}
|
||||
for (k = i; k < m; ++ k){
|
||||
a[k][i] *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
w[i] = scale * g;
|
||||
g = s = scale = 0.0;
|
||||
if ( (i < m) && (i !== n - 1) ){
|
||||
for (k = l; k < n; ++ k){
|
||||
scale += Math.abs( a[i][k] );
|
||||
}
|
||||
if (0.0 !== scale){
|
||||
for (k = l; k < n; ++ k){
|
||||
a[i][k] /= scale;
|
||||
s += a[i][k] * a[i][k];
|
||||
}
|
||||
f = a[i][l];
|
||||
g = -SVD.sign( Math.sqrt(s), f );
|
||||
h = f * g - s;
|
||||
a[i][l] = f - g;
|
||||
for (k = l; k < n; ++ k){
|
||||
rv1[k] = a[i][k] / h;
|
||||
}
|
||||
for (j = l; j < m; ++ j){
|
||||
for (s = 0.0, k = l; k < n; ++ k){
|
||||
s += a[j][k] * a[i][k];
|
||||
}
|
||||
for (k = l; k < n; ++ k){
|
||||
a[j][k] += s * rv1[k];
|
||||
}
|
||||
}
|
||||
for (k = l; k < n; ++ k){
|
||||
a[i][k] *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
anorm = Math.max(anorm, ( Math.abs( w[i] ) + Math.abs( rv1[i] ) ) );
|
||||
}
|
||||
|
||||
//Acumulation of right-hand transformation
|
||||
for (i = n - 1; i >= 0; -- i){
|
||||
if (i < n - 1){
|
||||
if (0.0 !== g){
|
||||
for (j = l; j < n; ++ j){
|
||||
v[j][i] = ( a[i][j] / a[i][l] ) / g;
|
||||
}
|
||||
for (j = l; j < n; ++ j){
|
||||
for (s = 0.0, k = l; k < n; ++ k){
|
||||
s += a[i][k] * v[k][j];
|
||||
}
|
||||
for (k = l; k < n; ++ k){
|
||||
v[k][j] += s * v[k][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (j = l; j < n; ++ j){
|
||||
v[i][j] = v[j][i] = 0.0;
|
||||
}
|
||||
}
|
||||
v[i][i] = 1.0;
|
||||
g = rv1[i];
|
||||
l = i;
|
||||
}
|
||||
|
||||
//Acumulation of left-hand transformation
|
||||
for (i = Math.min(n, m) - 1; i >= 0; -- i){
|
||||
l = i + 1;
|
||||
g = w[i];
|
||||
for (j = l; j < n; ++ j){
|
||||
a[i][j] = 0.0;
|
||||
}
|
||||
if (0.0 !== g){
|
||||
g = 1.0 / g;
|
||||
for (j = l; j < n; ++ j){
|
||||
for (s = 0.0, k = l; k < m; ++ k){
|
||||
s += a[k][i] * a[k][j];
|
||||
}
|
||||
f = (s / a[i][i]) * g;
|
||||
for (k = i; k < m; ++ k){
|
||||
a[k][j] += f * a[k][i];
|
||||
}
|
||||
}
|
||||
for (j = i; j < m; ++ j){
|
||||
a[j][i] *= g;
|
||||
}
|
||||
}else{
|
||||
for (j = i; j < m; ++ j){
|
||||
a[j][i] = 0.0;
|
||||
}
|
||||
}
|
||||
++ a[i][i];
|
||||
}
|
||||
|
||||
//Diagonalization of the bidiagonal form
|
||||
for (k = n - 1; k >= 0; -- k){
|
||||
for (its = 1; its <= 30; ++ its){
|
||||
flag = true;
|
||||
for (l = k; l >= 0; -- l){
|
||||
nm = l - 1;
|
||||
if ( Math.abs( rv1[l] ) + anorm === anorm ){
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
if ( Math.abs( w[nm] ) + anorm === anorm ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag){
|
||||
c = 0.0;
|
||||
s = 1.0;
|
||||
for (i = l; i <= k; ++ i){
|
||||
f = s * rv1[i];
|
||||
if ( Math.abs(f) + anorm === anorm ){
|
||||
break;
|
||||
}
|
||||
g = w[i];
|
||||
h = SVD.pythag(f, g);
|
||||
w[i] = h;
|
||||
h = 1.0 / h;
|
||||
c = g * h;
|
||||
s = -f * h;
|
||||
for (j = 1; j <= m; ++ j){
|
||||
y = a[j][nm];
|
||||
z = a[j][i];
|
||||
a[j][nm] = y * c + z * s;
|
||||
a[j][i] = z * c - y * s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Convergence
|
||||
z = w[k];
|
||||
if (l === k){
|
||||
if (z < 0.0){
|
||||
w[k] = -z;
|
||||
for (j = 0; j < n; ++ j){
|
||||
v[j][k] = -v[j][k];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (30 === its){
|
||||
return false;
|
||||
}
|
||||
|
||||
//Shift from bottom 2-by-2 minor
|
||||
x = w[l];
|
||||
nm = k - 1;
|
||||
y = w[nm];
|
||||
g = rv1[nm];
|
||||
h = rv1[k];
|
||||
f = ( (y - z) * (y + z) + (g - h) * (g + h) ) / (2.0 * h * y);
|
||||
g = SVD.pythag( f, 1.0 );
|
||||
f = ( (x - z) * (x + z) + h * ( (y / (f + SVD.sign(g, f) ) ) - h) ) / x;
|
||||
|
||||
//Next QR transformation
|
||||
c = s = 1.0;
|
||||
for (j = l; j <= nm; ++ j){
|
||||
i = j + 1;
|
||||
g = rv1[i];
|
||||
y = w[i];
|
||||
h = s * g;
|
||||
g = c * g;
|
||||
z = SVD.pythag(f, h);
|
||||
rv1[j] = z;
|
||||
c = f / z;
|
||||
s = h / z;
|
||||
f = x * c + g * s;
|
||||
g = g * c - x * s;
|
||||
h = y * s;
|
||||
y *= c;
|
||||
for (jj = 0; jj < n; ++ jj){
|
||||
x = v[jj][j];
|
||||
z = v[jj][i];
|
||||
v[jj][j] = x * c + z * s;
|
||||
v[jj][i] = z * c - x * s;
|
||||
}
|
||||
z = SVD.pythag(f, h);
|
||||
w[j] = z;
|
||||
if (0.0 !== z){
|
||||
z = 1.0 / z;
|
||||
c = f * z;
|
||||
s = h * z;
|
||||
}
|
||||
f = c * g + s * y;
|
||||
x = c * y - s * g;
|
||||
for (jj = 0; jj < m; ++ jj){
|
||||
y = a[jj][j];
|
||||
z = a[jj][i];
|
||||
a[jj][j] = y * c + z * s;
|
||||
a[jj][i] = z * c - y * s;
|
||||
}
|
||||
}
|
||||
rv1[l] = 0.0;
|
||||
rv1[k] = f;
|
||||
w[k] = x;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
SVD.pythag = function(a, b){
|
||||
var at = Math.abs(a), bt = Math.abs(b), ct;
|
||||
|
||||
if (at > bt){
|
||||
ct = bt / at;
|
||||
return at * Math.sqrt(1.0 + ct * ct);
|
||||
}
|
||||
|
||||
if (0.0 === bt){
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
ct = at / bt;
|
||||
return bt * Math.sqrt(1.0 + ct * ct);
|
||||
};
|
||||
|
||||
SVD.sign = function(a, b){
|
||||
return b >= 0.0? Math.abs(a): -Math.abs(a);
|
||||
};
|
||||
Reference in new issue
Block a user