diff --git a/package-lock.json b/package-lock.json index ed9475d5..de0f8b3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1023,6 +1023,20 @@ "to-fast-properties": "^2.0.0" } }, + "@d4c/numjs": { + "version": "0.17.34", + "resolved": "https://registry.npmjs.org/@d4c/numjs/-/numjs-0.17.34.tgz", + "integrity": "sha512-wPA61nUOFR1S7a40m3m9Ko+eHyRaKKE4Cha7/JR53jVL9VN6ljDUZX8x4G1k6DW3TCbtu0emn3c8yJvlyckpoA==", + "requires": { + "@types/ndarray": "^1.0.11", + "cwise": "^1.0.10", + "ndarray": "^1.0.19", + "ndarray-fft": "^1.0.3", + "ndarray-gemm": "^1.0.0", + "ndarray-ops": "^1.2.2", + "typedarray-pool": "^1.2.0" + } + }, "@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -1088,6 +1102,11 @@ "glob-to-regexp": "^0.3.0" } }, + "@msgpack/msgpack": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", + "integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==" + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -1122,6 +1141,11 @@ "fastq": "^1.6.0" } }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, "@soda/friendly-errors-webpack-plugin": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz", @@ -1214,6 +1238,58 @@ "integrity": "sha512-zeOomWIE52M9JpYXlsR3iOf7TXTTmNQHnSbqjMsQZ5phzfAenHzL/1+vQ0ZoJfagocK11LNf8vnn2JG0ufRMUQ==", "dev": true }, + "@tensorflow/tfjs-backend-cpu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.13.0.tgz", + "integrity": "sha512-k44G+2WZShxI2ejvQdsSQcicFMNWaccsf6bkI0R7dol9t9uj73yg7JkiT0U0uuJE6XwXymJgDe+KJVprg3fAgA==", + "requires": { + "@types/seedrandom": "^2.4.28", + "seedrandom": "^3.0.5" + } + }, + "@tensorflow/tfjs-core": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.13.0.tgz", + "integrity": "sha512-vvz/kHakvv5Tppp2GDTUBA2/XkNmEkManbdsFEXfwVc5+rVMPEMsRFOjsKTy/TpDRd/4wsJBA99L4F7iG2tr/Q==", + "requires": { + "@types/long": "^4.0.1", + "@types/offscreencanvas": "~2019.7.0", + "@types/seedrandom": "^2.4.28", + "@webgpu/types": "0.1.30", + "long": "4.0.0", + "node-fetch": "~2.6.1", + "seedrandom": "^3.0.5" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1243,12 +1319,22 @@ "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", "dev": true }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, + "@types/ndarray": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz", + "integrity": "sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==" + }, "@types/node": { "version": "14.0.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.10.tgz", @@ -1261,12 +1347,22 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/offscreencanvas": { + "version": "2019.7.3", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==" + }, "@types/q": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", "dev": true }, + "@types/seedrandom": { + "version": "2.4.34", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.34.tgz", + "integrity": "sha512-ytDiArvrn/3Xk6/vtylys5tlY6eo7Ane0hvcx++TKo6RxQXuVfW0AF/oeWqAj9dN29SyhtawuXstgmPlwNcv/A==" + }, "@vue/babel-helper-vue-jsx-merge-props": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz", @@ -2206,6 +2302,11 @@ "@xtuc/long": "4.2.2" } }, + "@webgpu/types": { + "version": "0.1.30", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.30.tgz", + "integrity": "sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==" + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -2308,12 +2409,38 @@ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "alphanum-sort": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "dev": true }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "optional": true + }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -2805,6 +2932,11 @@ "file-uri-to-path": "1.0.0" } }, + "bit-twiddle": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz", + "integrity": "sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==" + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3104,8 +3236,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-indexof": { "version": "1.1.1", @@ -3358,6 +3489,15 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, "chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", @@ -3957,7 +4097,6 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -4364,6 +4503,14 @@ "sha.js": "^2.4.8" } }, + "cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "requires": { + "node-fetch": "^2.6.12" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -4622,6 +4769,79 @@ } } }, + "cwise": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/cwise/-/cwise-1.0.10.tgz", + "integrity": "sha512-4OQ6FXVTRO2bk/OkIEt0rNqDk63aOv3Siny6ZD2/WN9CH7k8X6XyQdcip4zKg1WG+L8GP5t2zicXbDb+H7Y77Q==", + "requires": { + "cwise-compiler": "^1.1.1", + "cwise-parser": "^1.0.0", + "static-module": "^1.0.0", + "uglify-js": "^2.6.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w==", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "requires": { + "uniq": "^1.0.0" + } + }, + "cwise-parser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz", + "integrity": "sha512-nAe238ctwjt9l5exq9CQkHS1Tj6YRGAQxqfL4VaN1B2oqG1Ss0VVqIrBG/vyOgN301PI22wL6ZIhe/zA+BO56Q==", + "requires": { + "esprima": "^1.0.3", + "uniq": "^1.0.0" + }, + "dependencies": { + "esprima": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", + "integrity": "sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==" + } + } + }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", @@ -4666,8 +4886,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decimal.js": { "version": "10.3.1", @@ -5147,12 +5366,48 @@ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "dev": true }, + "dup": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz", + "integrity": "sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA==" + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", + "requires": { + "readable-stream": "~1.1.9" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + } + } + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -5278,6 +5533,38 @@ "once": "^1.4.0" } }, + "engine.io-client": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz", + "integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==" + } + } + }, + "engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==" + }, "enhanced-resolve": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", @@ -5945,6 +6232,27 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, + "falafel": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.5.tgz", + "integrity": "sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==", + "requires": { + "acorn": "^7.1.1", + "isarray": "^2.0.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + } + } + }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", @@ -6333,8 +6641,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -6500,7 +6807,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -6935,6 +7241,15 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "imjoy-rpc": { + "version": "0.5.46", + "resolved": "https://registry.npmjs.org/imjoy-rpc/-/imjoy-rpc-0.5.46.tgz", + "integrity": "sha512-g8jBxUgnab64plgaYvZuPRwb9p4z3w/D7FCPDdjPt9Nu3EzoK+E4JXgppmy6YntbE3OJFD82eCddIDsr2RsvSQ==", + "requires": { + "@msgpack/msgpack": "^2.7.1", + "socket.io-client": "^4.6.2" + } + }, "immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -7167,6 +7482,11 @@ "loose-envify": "^1.0.0" } }, + "iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -7236,8 +7556,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.2.0", @@ -7790,6 +8109,11 @@ "launch-editor": "^2.2.1" } }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==" + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -7921,8 +8245,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -8006,6 +8329,16 @@ "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==", "dev": true }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8875,8 +9208,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { "version": "6.2.3", @@ -8943,6 +9275,40 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ndarray": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz", + "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==", + "requires": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "ndarray-fft": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ndarray-fft/-/ndarray-fft-1.0.3.tgz", + "integrity": "sha512-p7OPcNAHP616TdoQdmroW666To530jY1q32Gy1DvK3fkaAQ4BuGu715UDDPIARkVQGhHC2qhbjwrhYG2eUQPCw==", + "requires": { + "bit-twiddle": "^1.0.2", + "cwise": "^1.0.4", + "ndarray": "^1.0.15", + "ndarray-ops": "^1.2.2", + "typedarray-pool": "^1.0.0" + } + }, + "ndarray-gemm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ndarray-gemm/-/ndarray-gemm-1.0.0.tgz", + "integrity": "sha512-LSAzu9dFrQHGImnO/14EtKuRsxQwyehtYg56mxajTB2XnJ4eVx90Dq+xP2x9lyH4PLPtVnZMhGrvnHiIxtGysw==" + }, + "ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "requires": { + "cwise-compiler": "^1.0.0" + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -8980,6 +9346,35 @@ "semver": "^5.7.0" } }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", @@ -9091,6 +9486,14 @@ "path-key": "^2.0.0" } }, + "npyjs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/npyjs/-/npyjs-0.5.0.tgz", + "integrity": "sha512-jPycEiEapKnc5BiIezw1W5RmK0fwFGW5bTFx/JcZbJ2/EUuYGxf1Sx3cguHsKUFmAF2wuXACKMsullOplsm0xQ==", + "requires": { + "cross-fetch": "^3.1.5" + } + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -10583,6 +10986,65 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, + "quote-stream": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-0.0.0.tgz", + "integrity": "sha512-m4VtvjAMx00wgAS6eOy50ZDat1EBQeFKBIrtF/oxUt0MenEI33y7runJcRiOihc+JBBIt2aFFJhILIh4e9shJA==", + "requires": { + "minimist": "0.0.8", + "through2": "~0.4.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==", + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~2.1.1" + } + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==", + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, "ramda": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", @@ -10847,8 +11309,7 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "request": { "version": "2.88.2", @@ -10999,6 +11460,14 @@ "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", + "requires": { + "align-text": "^0.1.1" + } + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -11099,6 +11568,11 @@ "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=" }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -11305,6 +11779,11 @@ "safe-buffer": "^5.0.1" } }, + "shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -11507,6 +11986,46 @@ } } }, + "socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + } + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + } + } + }, "sockjs": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", @@ -11575,8 +12094,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { "version": "0.5.3", @@ -11745,6 +12263,36 @@ "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", "dev": true }, + "static-eval": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-0.2.4.tgz", + "integrity": "sha512-6dWWPfa/0+1zULdQi7ssT5EQZHsGK8LygBzhE/HdafNCo4e/Ibt7vLPfxBw9VcdVV+t0ARtN4ZAJKtApVc0A5Q==", + "requires": { + "escodegen": "~0.0.24" + }, + "dependencies": { + "escodegen": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.28.tgz", + "integrity": "sha512-6ioQhg16lFs5c7XJlJFXIDxBjO4yRvXC9yK6dLNNGuhI3a/fJukHanPF6qtpjGDgAFzI8Wuq3PSIarWmaOq/5A==", + "requires": { + "esprima": "~1.0.2", + "estraverse": "~1.3.0", + "source-map": ">= 0.1.2" + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==" + }, + "estraverse": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.3.2.tgz", + "integrity": "sha512-OkbCPVUu8D9tbsLcUR+CKFRBbhZlogmkbWaP3BPERlkqzWL5Q6IdTz6eUk+b5cid2MTaCqJb2nNRGoJ8TpfPrg==" + } + } + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -11766,6 +12314,109 @@ } } }, + "static-module": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/static-module/-/static-module-1.5.0.tgz", + "integrity": "sha512-XTj7pQOHT33l77lK/Pu8UXqzI44C6LYAqwAc9hLTTESHRqJAFudBpReuopFPpoRr5CtOoSmGfFQC6FPlbDnyCw==", + "requires": { + "concat-stream": "~1.6.0", + "duplexer2": "~0.0.2", + "escodegen": "~1.3.2", + "falafel": "^2.1.0", + "has": "^1.0.0", + "object-inspect": "~0.4.0", + "quote-stream": "~0.0.0", + "readable-stream": "~1.0.27-1", + "shallow-copy": "~0.0.1", + "static-eval": "~0.2.0", + "through2": "~0.4.1" + }, + "dependencies": { + "escodegen": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", + "integrity": "sha512-z9FWgKc48wjMlpzF5ymKS1AF8OIgnKLp9VyN7KbdtyrP/9lndwUFqCtMm+TAJmJf7KJFFYc4cFJfVTTGkKEwsA==", + "requires": { + "esprima": "~1.1.1", + "estraverse": "~1.5.0", + "esutils": "~1.0.0", + "source-map": "~0.1.33" + } + }, + "esprima": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz", + "integrity": "sha512-qxxB994/7NtERxgXdFgLHIs9M6bhLXc6qtUmWZ3L8+gTQ9qaoyki2887P2IqAYsoENyr8SUbTutStDniOHSDHg==" + }, + "estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==" + }, + "esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "object-inspect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-0.4.0.tgz", + "integrity": "sha512-8WvkvUZiKAjjsy/63rJjA7jw9uyF0CLVLjBKEfnPHE3Jxvs1LgwqL2OmJN+LliIX1vrzKW+AAu02Cc+xv27ncQ==" + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==", + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~2.1.1" + } + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==", + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -12389,8 +13040,16 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-pool": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.2.0.tgz", + "integrity": "sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ==", + "requires": { + "bit-twiddle": "^1.0.0", + "dup": "^1.0.0" + } }, "uglify-js": { "version": "3.4.10", @@ -12416,6 +13075,12 @@ } } }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==", + "optional": true + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -12459,8 +13124,7 @@ "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" }, "uniqs": { "version": "2.0.0", @@ -13468,12 +14132,22 @@ } } }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==" + }, "workbox-background-sync": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz", @@ -13726,6 +14400,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 8dc6b8c5..5eb54621 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "format": "prettier --write \"{src,tests}/**/**\"" }, "dependencies": { + "@d4c/numjs": "^0.17.34", "@mdi/font": "^5.3.45", + "@tensorflow/tfjs-backend-cpu": "^4.13.0", + "@tensorflow/tfjs-core": "^4.13.0", "axios": "^0.21.2", "buefy": "^0.8.0", "core-js": "^3.6.5", @@ -20,11 +23,14 @@ "file-saver": "^2.0.5", "github-markdown-css": "^4.0.0", "highlight.js": "^10.1.1", + "imjoy-rpc": "^0.5.46", "js-yaml": "^4.1.0", "json-stringify-safe": "^5.0.1", "jszip": "^3.7.0", + "lodash": "^4.17.21", "marked": "^2.1.3", "mathjs": "^7.5.1", + "npyjs": "^0.5.0", "ol": "^6.3.1", "register-service-worker": "^1.7.1", "sortablejs": "^1.10.2", diff --git a/public/plugins/bioengine-test-run.imjoy.html b/public/plugins/bioengine-test-run.imjoy.html deleted file mode 100644 index d111a270..00000000 --- a/public/plugins/bioengine-test-run.imjoy.html +++ /dev/null @@ -1,666 +0,0 @@ - -The panel for upload the image file. - - - -{ - "name": "BioEngine Model Runner", - "type": "window", - "tags": [], - "ui": "", - "version": "0.1.0", - "cover": "", - "description": "[TODO: describe this plugin with one sentence.]", - "icon": "extension", - "inputs": null, - "outputs": null, - "api_version": "0.1.8", - "env": "", - "permissions": [], - "requirements": [ - "https://cdn.tailwindcss.com", - "https://cdn.jsdelivr.net/npm/imjoy-rpc@0.5.6/dist/hypha-rpc-websocket.min.js" - ], - "dependencies": [] -} - - - - - -
-

Load an image file:

-

Example formats: PNG, JPG or TIFF

-

TODO: display expected tensor info

-
- No file selected. - - -
- - -

or you may load the image from an URL.

-

A test image is provided for this model. Click the Load button to run the model on the provided test image or provide the url to your own test image.

-

The URL to OME-Zarr are supported.

-
- - -
-
-

- -
- -

Input axes (optional, for example yxzc):

-

-

More documentation about expected axes in the documentation page.

- - -
-
- -

-
-
- - -
-

- -
- -
- - - -
-
-
- - - -{ - "name": "mi-core", - "type": "web-python", - "tags": [], - "flags": [], - "ui": "", - "version": "0.1.0", - "cover": "", - "description": "Connect to the bioengine server, and execute operations.", - "icon": "extension", - "inputs": null, - "outputs": null, - "api_version": "0.1.8", - "env": "", - "permissions": [], - "requirements": ["imageio", "xarray"], - "dependencies": [] -} - - - - diff --git a/src/components/ResourceItemInfo.vue b/src/components/ResourceItemInfo.vue index 93cd86b6..ecb04c06 100644 --- a/src/components/ResourceItemInfo.vue +++ b/src/components/ResourceItemInfo.vue @@ -103,12 +103,14 @@ >
- + + +
+ This model is not available for testing. +

@@ -158,6 +160,7 @@ import TestSummary from "@/components/TestSummary.vue"; import CommentBox from "@/components/CommentBox.vue"; import { randId, concatAndResolveUrl } from "../utils"; import ResourceItemCard from "./ResourceItemCard"; +import TestRunForm from "./TestRun.vue"; async function fetchTestSummary(url) { const response = await fetch(url); @@ -204,13 +207,15 @@ export default { attachments: Attachments, "app-icons": AppIcons, "comment-box": CommentBox, - "resource-item-card": ResourceItemCard + "resource-item-card": ResourceItemCard, + "test-run-form": TestRunForm }, data() { return { maxDescriptionLetters: 100, maxDocsLetters: 500, - showSource: false + showSource: false, + modelAvailable: false }; }, @@ -236,9 +241,7 @@ export default { this.$forceUpdate(); }); } - this.gettestRunDocs(this.resourceItem).then(() => { - this.$forceUpdate(); - }); + this.getManifestList(); }, computed: { runButtonContext: function() { @@ -353,69 +356,12 @@ export default { } } }, - async gettestRunDocs(resourceItem) { - const response = await fetch( - "https://raw.githubusercontent.com/bioimage-io/bioengine-model-runner/gh-pages/manifest.bioengine.json" - ); - const manifest = await response.json(); - if (!manifest.collection.find(c => c.id === resourceItem.id)) { - resourceItem.testRunDocs = `## Model testing is not available for this model\n\nSee [conversion log](https://github.com/bioimage-io/bioengine-model-runner/blob/gh-pages/manifest.bioengine.yaml) for more details.`; - return; - } - const url = - window.location.origin + "/plugins/bioengine-test-run.imjoy.html"; - const docs = ` -## Quick model testing with your own data -By clicking the \`Test the model\` button, you can test the model with your own data. - - -\`\`\`js -api.createWindow({ - src: "${url}", - window_id: "test-run-form", - data: { - id: "${resourceItem.id}", - input_window_id: "image_input_window", - output_window_id: "image_output_window" - }} - ) -\`\`\` - - -
-
-
-
-
- `; - resourceItem.testRunDocs = docs; + async getManifestList() { + const manifestUrl = + "https://raw.githubusercontent.com/bioimage-io/bioengine-model-runner/gh-pages/manifest.bioengine.json"; + const list = await fetch(manifestUrl).then(r => r.json()); + const aviableModels = list.collection.map(m => m.id); + this.modelAvailable = aviableModels.includes(this.resourceItem.id); } } }; @@ -460,4 +406,7 @@ api.createWindow({ display: inline-block; margin-right: 5px; } +.not-available { + color: red; +} diff --git a/src/components/TestRun.vue b/src/components/TestRun.vue new file mode 100644 index 00000000..bd617a2f --- /dev/null +++ b/src/components/TestRun.vue @@ -0,0 +1,671 @@ + + + + + diff --git a/src/imgProcess.js b/src/imgProcess.js new file mode 100644 index 00000000..79858d1c --- /dev/null +++ b/src/imgProcess.js @@ -0,0 +1,614 @@ +/** + * Functions for image processing. + * Used in the test run form. + */ + +import "@tensorflow/tfjs-backend-cpu"; +import * as tf from "@tensorflow/tfjs-core"; +import lodash from "lodash"; + +export function inferImgAxes(shape, order = "bcz") { + /** + * Infer the axes of an image. + * + * @param {Array} shape Shape of the image. + * @returns {string} Axes string. + */ + if (shape.length === 2) { + return "yx"; + } else if (shape.length <= 5) { + let minDimIdx = shape.indexOf(Math.min(...shape)); + let lowDimShape = shape.slice(); // Clone the shape array + lowDimShape.splice(minDimIdx, 1); // Remove the smallest dimension + let lowDimAxes = inferImgAxes(lowDimShape, order.slice(1)); + const insert = order[0]; + return insertCharAtPosition(lowDimAxes, insert, minDimIdx); + } else { + throw new Error(`Image shape [${shape.join(", ")}] is not supported.`); + } +} + +export function inferImgAxesViaSpec(shape, specAxes, fromIJ = false) { + let imgAxes; + if (fromIJ) { + if (shape.length === 2) { + imgAxes = "yx"; + } else if (shape.length === 3) { + imgAxes = "yxc"; + } else if (shape.length === 4) { + if (specAxes.includes("z")) { + imgAxes = "zyxc"; + } else { + imgAxes = "cyxb"; + } + } else { + throw new Error(`Image shape [${shape.join(", ")}] is not supported.`); + } + } else { + let order = "bcz"; + if (!specAxes.includes("c")) { + order = "bz"; + } else if (!specAxes.includes("z")) { + order = "bc"; + } else if (!specAxes.includes("b")) { + order = "cz"; + } + imgAxes = inferImgAxes(shape, order); + } + return imgAxes; +} + +export function insertCharAtPosition(originalString, charToInsert, position) { + return ( + originalString.substring(0, position) + + charToInsert + + originalString.substring(position) + ); +} + +function getConstructor(tpstr) { + /** +Int8Array int8 int8 +Int16Array int16 int16 +Int32Array int32 int32 +Uint8Array uint8 uint8 +Uint16Array uint16 uint16 +Uint32Array uint32 uint32 +Float32Array float32 float32 +Float64Array float64 float64 + */ + let Constructor; + if (tpstr == "uint8") { + Constructor = Uint8Array; + } else if (tpstr == "int8") { + Constructor = Int8Array; + } else if (tpstr == "uint16") { + Constructor = Uint16Array; + } else if (tpstr == "int16") { + Constructor = Int16Array; + } else if (tpstr == "uint32") { + Constructor = Uint32Array; + } else if (tpstr == "int32") { + Constructor = Int32Array; + } else if (tpstr == "float32") { + Constructor = Float32Array; + } else if (tpstr == "float64") { + Constructor = Float64Array; + } else if (tpstr == "bool") { + Constructor = Uint8Array; + } else { + throw new Error("Unsupported dtype: " + tpstr); + } + return Constructor; +} + +function reverseEndianness(arrayBuffer, bytesPerElement) { + let uint8Array = new Uint8Array(arrayBuffer); + for (let i = 0; i < uint8Array.length; i += bytesPerElement) { + for (let j = i, k = i + bytesPerElement - 1; j < k; j++, k--) { + [uint8Array[j], uint8Array[k]] = [uint8Array[k], uint8Array[j]]; + } + } + return arrayBuffer; +} + +export function ImjoyToTfJs(arr) { + let buffer = new ArrayBuffer(arr._rvalue.length); + let bufferView = new Uint8Array(buffer); + bufferView.set(arr._rvalue); + const Constructor = getConstructor(arr._rdtype); + let tarr = new Constructor(buffer); + if (arr._rdtype === "bool") { + // convert 1 to 255 + for (let i = 0; i < tarr.length; i++) { + if (tarr[i] === 1) { + tarr[i] = 255; + } + } + } + const tensor = tf.tensor(Array.from(tarr), arr._rshape); + tensor._rdtype = arr._rdtype; + return tensor; +} + +export function toImJoyArr(tensor, reverseEnd = false) { + const data = tensor.dataSync(); + const Constructor = getConstructor(tensor._rdtype); + let casted = new Constructor(data.length); + for (let i = 0; i < data.length; i++) { + casted[i] = data[i]; + } + if (reverseEnd) { + casted = new Constructor( + reverseEndianness(casted.buffer, Constructor.BYTES_PER_ELEMENT) + ); + } + const value = new Uint8Array(casted.buffer); + const ijarr = { + _rtype: "ndarray", + _rdtype: tensor._rdtype, + _rshape: tensor.shape, + _rvalue: value + }; + return ijarr; +} + +export function pick(tensor, idxes) { + const sliceBegin = []; + for (let i = 0; i < tensor.shape.length; i++) { + if (idxes[i] === null) { + sliceBegin.push(0); + } else { + sliceBegin.push(idxes[i]); + } + } + const sliceSize = []; + for (let i = 0; i < tensor.shape.length; i++) { + if (idxes[i] === null) { + sliceSize.push(tensor.shape[i]); + } else { + sliceSize.push(1); + } + } + const subTensor = tf.slice(tensor, sliceBegin, sliceSize); + const newShape = []; + for (let i = 0; i < tensor.shape.length; i++) { + if (idxes[i] === null) { + newShape.push(tensor.shape[i]); + } + } + return tf.reshape(subTensor, newShape); +} + +export function mapAxes(inputArray, inputAxes, outputAxes) { + if (inputAxes.length !== inputArray.shape.length) { + throw new Error( + `Number of axes ${inputAxes.length} and dimension of input ${inputArray.shape.length} don't match` + ); + } + + const pickIdxes = []; + inputAxes.split("").forEach((axName, axIdx) => { + const axLen = inputArray.shape[axIdx]; + if (!outputAxes.includes(axName)) { + let pickIdx = 0; + if ("zyx".includes(axName)) { + pickIdx = Math.floor(axLen / 2); + } + pickIdxes.push(pickIdx); + } else { + pickIdxes.push(null); + } + }); + let axes = inputAxes.split("").filter((name, idx) => pickIdxes[idx] === null); + + let newArray = pick(inputArray, pickIdxes); + + outputAxes.split("").forEach(axName => { + if (!inputAxes.includes(axName)) { + newArray = tf.reshape(newArray, newArray.shape.concat([1])); + axes.push(axName); + } + }); + + const transposeIdxes = []; + for (let i = 0; i < outputAxes.length; i++) { + const axName = outputAxes[i]; + const axIdx = axes.indexOf(axName); + transposeIdxes.push(axIdx); + } + + newArray = tf.transpose(newArray, transposeIdxes); + newArray._rdtype = inputArray._rdtype; + + return newArray; +} + +export const splitBy = (tensor, by, specAxes) => { + const byIdx = specAxes.indexOf(by); + const byLen = tensor.shape[byIdx]; + const splited = []; + for (let i = 0; i < byLen; i++) { + const pickIdx = []; + for (let j = 0; j < tensor.shape.length; j++) { + if (j === byIdx) { + pickIdx.push(i); + } else { + pickIdx.push(null); + } + } + const subArr = pick(tensor, pickIdx); + splited.push(subArr); + } + return splited; +}; + +export function splitForShow(tensor, specAxes) { + if (!specAxes.includes("x") || !specAxes.includes("y")) { + throw new Error("Unsupported axes: " + specAxes); + } + const hasC = specAxes.includes("c"); + const lenC = tensor.shape[specAxes.indexOf("c")]; + const hasZ = specAxes.includes("z"); + const lenZ = tensor.shape[specAxes.indexOf("z")]; + let newImgs = []; + if (specAxes.length === 2) { + newImgs.push(tensor); + } else if (specAxes.length === 3) { + if (hasC) { + if (lenC === 3) { + if (tensor._rdtype === "uint8") { + newImgs.push(mapAxes(tensor, specAxes, "yxc")); + } else { + newImgs.push(mapAxes(tensor, specAxes, "cyx")); + } + } else if (lenC === 1) { + newImgs.push(mapAxes(tensor, specAxes, "yx")); + } else { + newImgs.push(mapAxes(tensor, specAxes, "cyx")); + } + } else if (hasZ) { + newImgs.push(mapAxes(tensor, specAxes, "zyx")); + } else { + // b, y, x + newImgs = splitBy(tensor, "b", specAxes); + } + } else if (specAxes.length === 4) { + if (hasC && hasZ) { + if (lenC == 3) { + newImgs.push(mapAxes(tensor, specAxes, "zyxc")); + } else if (lenC == 1) { + newImgs.push(mapAxes(tensor, specAxes, "zyx")); + } else if (lenZ == 1) { + newImgs.push(mapAxes(tensor, specAxes, "cyx")); + } else { + // split by c + splitBy(tensor, "c", specAxes).map(arrs => { + const subAxes = specAxes.replace("c", ""); + newImgs = newImgs.concat(splitForShow(arrs, subAxes)); + }); + } + } else { + // b,c,y,x or b,z,y,x + // split by b + splitBy(tensor, "b", specAxes).map(arrs => { + const subAxes = specAxes.replace("b", ""); + newImgs = newImgs.concat(splitForShow(arrs, subAxes)); + }); + } + } else if (specAxes.length === 5) { + // b,c,z,y,x + // split by b + splitBy(tensor, "b", specAxes).map(arrs => { + const subAxes = specAxes.replace("b", ""); + newImgs = newImgs.concat(splitForShow(arrs, subAxes)); + }); + } else { + throw new Error("Unsupported axes: " + specAxes); + } + return newImgs; +} + +export function processForShow(tensor, specAxes) { + /** + Process the image for showing. + ImageJ.JS only supports: + [height, width] + [height, width, 1] + [height, width, 3] (will show as RGB image) + [z-stack, height, width] + [z-stack, height, width, 1] + [z-stack, height, width, 3] (will show as a stack of RGB image) + */ + const isImg2Img = specAxes.includes("x") && specAxes.includes("y"); + let splitedArrs; + if (isImg2Img) { + splitedArrs = splitForShow(tensor, specAxes); + } else { + if (specAxes.length > 2 && specAxes.includes("b")) { + splitedArrs = splitBy(tensor, "b", specAxes); + } else { + splitedArrs = [tensor]; + } + } + return splitedArrs.map(arr => { + arr._rdtype = tensor._rdtype; + return toImJoyArr(arr); + }); +} + +function getNpyDtype(buffer) { + const headerLength = new DataView(buffer.slice(8, 10)).getUint8(0); + const hcontents = new TextDecoder("utf-8").decode( + new Uint8Array(buffer.slice(10, 10 + headerLength)) + ); + const header = JSON.parse( + hcontents + .toLowerCase() // True -> true + .replace(/'/g, '"') + .replace("(", "[") + .replace(/,*\),*/g, "]") + ); + return header.descr; +} + +export async function getNpyEndianness(url) { + const resp = await fetch(url, { + headers: { + Range: "bytes=0-999" + } + }); + if (!resp.ok) { + console.error(resp); + return null; + } + const arrayBuffer = await resp.arrayBuffer(); + const npyDtype = getNpyDtype(arrayBuffer); + return npyDtype[0]; +} + +export class ImgPadder { + constructor(inputSpec, outputSpec, padValue = 0) { + this.inputSpec = inputSpec; + this.outputSpec = outputSpec; + this.padValue = padValue; + } + + getPaddedShape(shape) { + const specShape = this.inputSpec.shape; + let paddedShape = []; + if (specShape instanceof Array) { + // Explicit shape + paddedShape = specShape; + } else { + // Implicit shape + // infer from the min and step + const min = specShape.min; + const step = specShape.step; + for (let d = 0; d < shape.length; d++) { + if (step[d] === 0) { + paddedShape.push(shape[d]); + } else { + const pad = Math.max( + 0, + Math.ceil((shape[d] - min[d]) / step[d]) * step[d] + ); + paddedShape.push(pad + min[d]); + } + } + } + return paddedShape; + } + + pad(tensor, position = "center") { + const paddedShape = this.getPaddedShape(tensor.shape); + const pad = []; + for (let d = 0; d < tensor.shape.length; d++) { + if (paddedShape[d] < tensor.shape[d]) { + throw new Error( + `Invalid shape: ${tensor.shape} for ${this.inputSpec.shape}` + ); + } + const diff = paddedShape[d] - tensor.shape[d]; + if (position === "center") { + pad.push([Math.floor(diff / 2), Math.ceil(diff / 2)]); + } else if (position === "begin") { + pad.push([0, diff]); + } else if (position === "end") { + pad.push([diff, 0]); + } else { + throw new Error(`Invalid position: ${position}`); + } + } + const res = tf.pad(tensor, pad, this.padValue); + res._rdtype = tensor._rdtype; + return [res, pad]; + } + + crop(tensor, pad, halo = undefined) { + let res; + const isImg2Img = + this.outputSpec.axes.includes("x") && this.outputSpec.axes.includes("y"); + if (isImg2Img) { + // img-to-img model + if (halo) { + res = tf.slice( + tensor, + pad.map((p, i) => p[0] + halo[i]), + tensor.shape.map((s, i) => s - pad[i][0] - pad[i][1] - halo[i] * 2) + ); + } else { + res = tf.slice( + tensor, + pad.map(p => p[0]), + tensor.shape.map((s, i) => s - pad[i][0] - pad[i][1]) + ); + } + } else { + // other model, e.g. classification + // no crop + res = tensor; + } + res._rdtype = tensor._rdtype; + return res; + } +} + +export class ImgTile { + constructor(starts, ends, indexes) { + this.starts = starts; + this.ends = ends; + this.indexes = indexes; + this.shape = ends.map((e, i) => e - this.starts[i]); + this.data = null; + } + + slice(tensor) { + this.data = tf.slice(tensor, this.starts, this.shape); + this.data._rdtype = tensor._rdtype; + } + + merge(another, axis) { + const newStarts = this.starts.slice(); + const newEnds = this.ends.slice(); + newEnds[axis] = another.ends[axis]; + const overlap = this.ends[axis] - another.starts[axis]; + if (overlap < 0) { + throw new Error("Cannot merge tiles with negative overlap."); + } + let newData; + if (this.data === null || another.data === null) { + newData = null; + } else { + if (overlap === 0) { + newData = tf.concat([this.data, another.data], axis); + } else { + const size1 = this.data.shape.slice(); + size1[axis] -= Math.ceil(overlap / 2); + const starts1 = size1.map(() => 0); + const firstPart = tf.slice(this.data, starts1, size1); + const size2 = another.data.shape.slice(); + size2[axis] -= Math.floor(overlap / 2); + const starts2 = size2.map(() => 0); + starts2[axis] += Math.floor(overlap / 2); + const secondPart = tf.slice(another.data, starts2, size2); + newData = tf.concat([firstPart, secondPart], axis); + } + newData._rdtype = this.data._rdtype; + } + const newTile = new ImgTile(newStarts, newEnds, this.indexes); + newTile.data = newData; + return newTile; + } + + mergeMean(another) { + const newStarts = this.starts.slice(); + const newEnds = this.ends.slice(); + const newData = tf.add(this.data, another.data).div(2); + newData._rdtype = this.data._rdtype; + const newTile = new ImgTile(newStarts, newEnds, this.indexes); + newTile.data = newData; + return newTile; + } +} + +const cartesian = (...a) => + a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat()))); + +export class ImgTiler { + constructor(imgShape, tileShape, overlap = undefined) { + this.imgShape = imgShape; + this.tileShape = tileShape; + if (overlap === undefined) { + overlap = tileShape.map(() => 0); + } + this.overlap = overlap; + } + + getNTiles() { + const overlap = this.overlap; + const tileShape = this.tileShape; + const imgShape = this.imgShape; + const nTiles = tileShape.map((s, i) => { + const n = Math.ceil(imgShape[i] / (s - overlap[i])); + return n; + }); + return nTiles; + } + + getTiles() { + const overlap = this.overlap; + const tileShape = this.tileShape; + const imgShape = this.imgShape; + const nTiles = this.getNTiles(); + const tileIndexes = cartesian( + ...nTiles.map(n => Array.from(Array(n).keys())) + ); + const starts = tileIndexes.map(idx => { + return idx.map((i, j) => { + return i * (tileShape[j] - overlap[j]); + }); + }); + const ends = starts.map(s => { + return s.map((v, i) => { + return Math.min(v + tileShape[i], imgShape[i]); + }); + }); + const tiles = starts.map((s, i) => { + return new ImgTile(s, ends[i], tileIndexes[i]); + }); + return tiles; + } +} + +export class TileMerger { + constructor(imgShape) { + this.imgShape = imgShape; + } + + mergeTiles(tiles) { + for (let d = 0; d < this.imgShape.length; d++) { + const newTiles = []; + const key = t => { + const res = []; + t.indexes.map((idx, j) => { + if (j !== d) { + res.push(idx); + } + }); + return res.join("-"); + }; + const groups = lodash.groupBy(tiles, key); + for (let k in groups) { + const v = groups[k]; + v.sort((a, b) => a.indexes[d] - b.indexes[d]); + if (v.length > 1) { + let merged = v[0]; + for (let i = 1; i < v.length; i++) { + merged = merged.merge(v[i], d); + } + newTiles.push(merged); + } else { + newTiles.push(v[0]); + } + } + tiles = newTiles; + } + const res = tiles[0]; + return res; + } +} + +export class MeanTileMerger extends TileMerger { + constructor(imgShape) { + super(imgShape); + } + + mergeTiles(tiles) { + const merged = tiles[0]; + for (let i = 1; i < tiles.length; i++) { + merged.mergeMean(tiles[i]); + } + return merged; + } +}