/*! * MARTINS.js Free Edition version 0.1.0 * GPU-accelerated Augmented Reality for the web * Copyright 2022 Alexandre Martins (https://github.com/alemart) * https://github.com/alemart/martins-js * * @license AGPL-3.0-only * Date: 2022-04-21T17:21:12.069Z */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["Martins"] = factory(); else root["Martins"] = factory(); })(self, function() { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ 528: /***/ ((module) => { /*! * Speedy Vision version 0.9.0-wip * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins (https://github.com/alemart) * https://github.com/alemart/speedy-vision * * @license Apache-2.0 * Date: 2022-04-19T18:11:01.441Z */ (function webpackUniversalModuleDefinition(root, factory) { if(true) module.exports = factory(); else {} })(self, function() { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ "./src/core/pipeline/factories/filter-factory.js": /*!*******************************************************!*\ !*** ./src/core/pipeline/factories/filter-factory.js ***! \*******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_1027__) => { "use strict"; __nested_webpack_require_1027__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_1027__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineFilterFactory": () => (/* binding */ SpeedyPipelineFilterFactory) /* harmony export */ }); /* harmony import */ var _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_1027__(/*! ../../speedy-namespace */ "./src/core/speedy-namespace.js"); /* harmony import */ var _nodes_filters_greyscale__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_1027__(/*! ../nodes/filters/greyscale */ "./src/core/pipeline/nodes/filters/greyscale.js"); /* harmony import */ var _nodes_filters_gaussian_blur__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_1027__(/*! ../nodes/filters/gaussian-blur */ "./src/core/pipeline/nodes/filters/gaussian-blur.js"); /* harmony import */ var _nodes_filters_simple_blur__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_1027__(/*! ../nodes/filters/simple-blur */ "./src/core/pipeline/nodes/filters/simple-blur.js"); /* harmony import */ var _nodes_filters_median_blur__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_1027__(/*! ../nodes/filters/median-blur */ "./src/core/pipeline/nodes/filters/median-blur.js"); /* harmony import */ var _nodes_filters_convolution__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_1027__(/*! ../nodes/filters/convolution */ "./src/core/pipeline/nodes/filters/convolution.js"); /* harmony import */ var _nodes_filters_nightvision__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_1027__(/*! ../nodes/filters/nightvision */ "./src/core/pipeline/nodes/filters/nightvision.js"); /* harmony import */ var _nodes_filters_normalize__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_1027__(/*! ../nodes/filters/normalize */ "./src/core/pipeline/nodes/filters/normalize.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * filter-factory.js * Image filters */ /** * Image filters */ class SpeedyPipelineFilterFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Convert image to greyscale * @param {string} [name] * @returns {SpeedyPipelineNodeGreyscale} */ static Greyscale(name = undefined) { return new _nodes_filters_greyscale__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeGreyscale(name); } /** * Gaussian Blur * @param {string} [name] * @returns {SpeedyPipelineNodeGaussianBlur} */ static GaussianBlur(name = undefined) { return new _nodes_filters_gaussian_blur__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineNodeGaussianBlur(name); } /** * Simple Blur (Box Filter) * @param {string} [name] * @returns {SpeedyPipelineNodeSimpleBlur} */ static SimpleBlur(name = undefined) { return new _nodes_filters_simple_blur__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineNodeSimpleBlur(name); } /** * Median Blur * @param {string} [name] * @returns {SpeedyPipelineNodeMedianBlur} */ static MedianBlur(name = undefined) { return new _nodes_filters_median_blur__WEBPACK_IMPORTED_MODULE_4__.SpeedyPipelineNodeMedianBlur(name); } /** * Image Convolution * @param {string} [name] * @returns {SpeedyPipelineNodeConvolution} */ static Convolution(name = undefined) { return new _nodes_filters_convolution__WEBPACK_IMPORTED_MODULE_5__.SpeedyPipelineNodeConvolution(name); } /** * Nightvision * @param {string} [name] * @returns {SpeedyPipelineNodeNightvision} */ static Nightvision(name = undefined) { return new _nodes_filters_nightvision__WEBPACK_IMPORTED_MODULE_6__.SpeedyPipelineNodeNightvision(name); } /** * Normalize image * @param {string} [name] * @returns {SpeedyPipelineNodeNormalize} */ static Normalize(name = undefined) { return new _nodes_filters_normalize__WEBPACK_IMPORTED_MODULE_7__.SpeedyPipelineNodeNormalize(name); } } /***/ }), /***/ "./src/core/pipeline/factories/image-factory.js": /*!******************************************************!*\ !*** ./src/core/pipeline/factories/image-factory.js ***! \******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_5965__) => { "use strict"; __nested_webpack_require_5965__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_5965__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineImagePortalFactory": () => (/* binding */ SpeedyPipelineImagePortalFactory), /* harmony export */ "SpeedyPipelineImageFactory": () => (/* binding */ SpeedyPipelineImageFactory) /* harmony export */ }); /* harmony import */ var _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_5965__(/*! ../../speedy-namespace */ "./src/core/speedy-namespace.js"); /* harmony import */ var _nodes_images_source__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_5965__(/*! ../nodes/images/source */ "./src/core/pipeline/nodes/images/source.js"); /* harmony import */ var _nodes_images_sink__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_5965__(/*! ../nodes/images/sink */ "./src/core/pipeline/nodes/images/sink.js"); /* harmony import */ var _nodes_images_multiplexer__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_5965__(/*! ../nodes/images/multiplexer */ "./src/core/pipeline/nodes/images/multiplexer.js"); /* harmony import */ var _nodes_images_buffer__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_5965__(/*! ../nodes/images/buffer */ "./src/core/pipeline/nodes/images/buffer.js"); /* harmony import */ var _nodes_images_pyramid__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_5965__(/*! ../nodes/images/pyramid */ "./src/core/pipeline/nodes/images/pyramid.js"); /* harmony import */ var _nodes_images_mixer__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_5965__(/*! ../nodes/images/mixer */ "./src/core/pipeline/nodes/images/mixer.js"); /* harmony import */ var _nodes_images_portal__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_5965__(/*! ../nodes/images/portal */ "./src/core/pipeline/nodes/images/portal.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * image-factory.js * Image-related nodes */ /** * Portal nodes */ class SpeedyPipelineImagePortalFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Create an image portal source * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImagePortalSource} */ static Source(name = undefined) { return new _nodes_images_portal__WEBPACK_IMPORTED_MODULE_7__.SpeedyPipelineNodeImagePortalSource(name); } /** * Create an image portal sink * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImagePortalSink} */ static Sink(name = undefined) { return new _nodes_images_portal__WEBPACK_IMPORTED_MODULE_7__.SpeedyPipelineNodeImagePortalSink(name); } } /** * Image nodes */ class SpeedyPipelineImageFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Create an image source * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImageSource} */ static Source(name = undefined) { return new _nodes_images_source__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeImageSource(name); } /** * Create an image sink * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImageSink} */ static Sink(name = undefined) { return new _nodes_images_sink__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineNodeImageSink(name); } /** * Create an image multiplexer * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImageMultiplexer} */ static Multiplexer(name = undefined) { return new _nodes_images_multiplexer__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineNodeImageMultiplexer(name); } /** * Create an image buffer * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImageBuffer} */ static Buffer(name = undefined) { return new _nodes_images_buffer__WEBPACK_IMPORTED_MODULE_4__.SpeedyPipelineNodeImageBuffer(name); } /** * Image Pyramid * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImagePyramid} */ static Pyramid(name = undefined) { return new _nodes_images_pyramid__WEBPACK_IMPORTED_MODULE_5__.SpeedyPipelineNodeImagePyramid(name); } /** * Image Mixer (blending) * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeImageMixer} */ static Mixer(name = undefined) { return new _nodes_images_mixer__WEBPACK_IMPORTED_MODULE_6__.SpeedyPipelineNodeImageMixer(name); } /** * Image Portals * @returns {typeof SpeedyPipelineImagePortalFactory} */ static get Portal() { return SpeedyPipelineImagePortalFactory; } } /***/ }), /***/ "./src/core/pipeline/factories/keypoint-factory.js": /*!*********************************************************!*\ !*** ./src/core/pipeline/factories/keypoint-factory.js ***! \*********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_11666__) => { "use strict"; __nested_webpack_require_11666__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_11666__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineKeypointPortalFactory": () => (/* binding */ SpeedyPipelineKeypointPortalFactory), /* harmony export */ "SpeedyPipelineKeypointFactory": () => (/* binding */ SpeedyPipelineKeypointFactory) /* harmony export */ }); /* harmony import */ var _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_11666__(/*! ../../speedy-namespace */ "./src/core/speedy-namespace.js"); /* harmony import */ var _nodes_keypoints_source__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/source */ "./src/core/pipeline/nodes/keypoints/source.js"); /* harmony import */ var _nodes_keypoints_sink__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/sink */ "./src/core/pipeline/nodes/keypoints/sink.js"); /* harmony import */ var _nodes_keypoints_clipper__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/clipper */ "./src/core/pipeline/nodes/keypoints/clipper.js"); /* harmony import */ var _nodes_keypoints_border_clipper__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/border-clipper */ "./src/core/pipeline/nodes/keypoints/border-clipper.js"); /* harmony import */ var _nodes_keypoints_buffer__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/buffer */ "./src/core/pipeline/nodes/keypoints/buffer.js"); /* harmony import */ var _nodes_keypoints_mixer__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/mixer */ "./src/core/pipeline/nodes/keypoints/mixer.js"); /* harmony import */ var _nodes_keypoints_shuffler__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/shuffler */ "./src/core/pipeline/nodes/keypoints/shuffler.js"); /* harmony import */ var _nodes_keypoints_multiplexer__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/multiplexer */ "./src/core/pipeline/nodes/keypoints/multiplexer.js"); /* harmony import */ var _nodes_keypoints_transformer__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/transformer */ "./src/core/pipeline/nodes/keypoints/transformer.js"); /* harmony import */ var _nodes_keypoints_subpixel__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/subpixel */ "./src/core/pipeline/nodes/keypoints/subpixel.js"); /* harmony import */ var _nodes_keypoints_detectors_fast__WEBPACK_IMPORTED_MODULE_11__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/detectors/fast */ "./src/core/pipeline/nodes/keypoints/detectors/fast.js"); /* harmony import */ var _nodes_keypoints_detectors_harris__WEBPACK_IMPORTED_MODULE_12__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/detectors/harris */ "./src/core/pipeline/nodes/keypoints/detectors/harris.js"); /* harmony import */ var _nodes_keypoints_descriptors_orb__WEBPACK_IMPORTED_MODULE_13__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/descriptors/orb */ "./src/core/pipeline/nodes/keypoints/descriptors/orb.js"); /* harmony import */ var _nodes_keypoints_trackers_lk__WEBPACK_IMPORTED_MODULE_14__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/trackers/lk */ "./src/core/pipeline/nodes/keypoints/trackers/lk.js"); /* harmony import */ var _nodes_keypoints_matchers_lsh_static_tables__WEBPACK_IMPORTED_MODULE_15__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/matchers/lsh-static-tables */ "./src/core/pipeline/nodes/keypoints/matchers/lsh-static-tables.js"); /* harmony import */ var _nodes_keypoints_matchers_lsh_knn__WEBPACK_IMPORTED_MODULE_16__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/matchers/lsh-knn */ "./src/core/pipeline/nodes/keypoints/matchers/lsh-knn.js"); /* harmony import */ var _nodes_keypoints_matchers_bf_knn__WEBPACK_IMPORTED_MODULE_17__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/matchers/bf-knn */ "./src/core/pipeline/nodes/keypoints/matchers/bf-knn.js"); /* harmony import */ var _nodes_keypoints_distance_filter__WEBPACK_IMPORTED_MODULE_18__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/distance-filter */ "./src/core/pipeline/nodes/keypoints/distance-filter.js"); /* harmony import */ var _nodes_keypoints_hamming_distance_filter__WEBPACK_IMPORTED_MODULE_19__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/hamming-distance-filter */ "./src/core/pipeline/nodes/keypoints/hamming-distance-filter.js"); /* harmony import */ var _nodes_keypoints_portal__WEBPACK_IMPORTED_MODULE_20__ = __nested_webpack_require_11666__(/*! ../nodes/keypoints/portal */ "./src/core/pipeline/nodes/keypoints/portal.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * keypoint-factory.js * Keypoint-related nodes */ /** * Keypoint detectors */ class SpeedyPipelineKeypointDetectorFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * FAST corner detector * @param {string} [name] * @returns {SpeedyPipelineNodeFASTKeypointDetector} */ static FAST(name = undefined) { return new _nodes_keypoints_detectors_fast__WEBPACK_IMPORTED_MODULE_11__.SpeedyPipelineNodeFASTKeypointDetector(name); } /** * Harris corner detector * @param {string} [name] * @returns {SpeedyPipelineNodeHarrisKeypointDetector} */ static Harris(name = undefined) { return new _nodes_keypoints_detectors_harris__WEBPACK_IMPORTED_MODULE_12__.SpeedyPipelineNodeHarrisKeypointDetector(name); } } /** * Keypoint descriptors */ class SpeedyPipelineKeypointDescriptorFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * ORB descriptors * @param {string} [name] * @returns {SpeedyPipelineNodeORBKeypointDescriptor} */ static ORB(name = undefined) { return new _nodes_keypoints_descriptors_orb__WEBPACK_IMPORTED_MODULE_13__.SpeedyPipelineNodeORBKeypointDescriptor(name); } } /** * Keypoint trackers */ class SpeedyPipelineKeypointTrackerFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * LK optical-flow * @param {string} [name] * @returns {SpeedyPipelineNodeLKKeypointTracker} */ static LK(name = undefined) { return new _nodes_keypoints_trackers_lk__WEBPACK_IMPORTED_MODULE_14__.SpeedyPipelineNodeLKKeypointTracker(name); } } /** * Keypoint matchers */ class SpeedyPipelineKeypointMatcherFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Static LSH tables * @param {string} [name] * @returns {SpeedyPipelineNodeStaticLSHTables} */ static StaticLSHTables(name = undefined) { return new _nodes_keypoints_matchers_lsh_static_tables__WEBPACK_IMPORTED_MODULE_15__.SpeedyPipelineNodeStaticLSHTables(name); } /** * LSH-based K-approximate nearest neighbors * @param {string} [name] * @returns {SpeedyPipelineNodeLSHKNNMatcher} */ static LSHKNN(name = undefined) { return new _nodes_keypoints_matchers_lsh_knn__WEBPACK_IMPORTED_MODULE_16__.SpeedyPipelineNodeLSHKNNMatcher(name); } /** * Brute-force K-nearest neighbors keypoint matcher * @param {string} [name] * @returns {SpeedyPipelineNodeBruteForceKNNKeypointMatcher} */ static BFKNN(name = undefined) { return new _nodes_keypoints_matchers_bf_knn__WEBPACK_IMPORTED_MODULE_17__.SpeedyPipelineNodeBruteForceKNNKeypointMatcher(name); } } /** * Portal nodes */ class SpeedyPipelineKeypointPortalFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Create an image portal source * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeKeypointPortalSource} */ static Source(name = undefined) { return new _nodes_keypoints_portal__WEBPACK_IMPORTED_MODULE_20__.SpeedyPipelineNodeKeypointPortalSource(name); } /** * Create an image portal sink * @param {string} [name] name of the node * @returns {SpeedyPipelineNodeKeypointPortalSink} */ static Sink(name = undefined) { return new _nodes_keypoints_portal__WEBPACK_IMPORTED_MODULE_20__.SpeedyPipelineNodeKeypointPortalSink(name); } } /** * Keypoint-related nodes */ class SpeedyPipelineKeypointFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Keypoint detectors * @returns {typeof SpeedyPipelineKeypointDetectorFactory} */ static get Detector() { return SpeedyPipelineKeypointDetectorFactory; } /** * Keypoint descriptors * @returns {typeof SpeedyPipelineKeypointDescriptorFactory} */ static get Descriptor() { return SpeedyPipelineKeypointDescriptorFactory; } /** * Keypoint trackers * @returns {typeof SpeedyPipelineKeypointTrackerFactory} */ static get Tracker() { return SpeedyPipelineKeypointTrackerFactory; } /** * Keypoint matchers * @returns {typeof SpeedyPipelineKeypointMatcherFactory} */ static get Matcher() { return SpeedyPipelineKeypointMatcherFactory; } /** * Keypoint Portals * @returns {typeof SpeedyPipelineKeypointPortalFactory} */ static get Portal() { return SpeedyPipelineKeypointPortalFactory; } /** * Create a keypoint source * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointSource} */ static Source(name = undefined) { return new _nodes_keypoints_source__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointSource(name); } /** * Create a keypoint sink * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointSink} */ static Sink(name = undefined) { return new _nodes_keypoints_sink__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineNodeKeypointSink(name); } /** * Create a sink of tracked keypoints * @param {string} [name] * @returns {SpeedyPipelineNodeTrackedKeypointSink} */ static SinkOfTrackedKeypoints(name = undefined) { return new _nodes_keypoints_sink__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineNodeTrackedKeypointSink(name); } /** * Create a sink of matched keypoints * @param {string} [name] * @returns {SpeedyPipelineNodeMatchedKeypointSink} */ static SinkOfMatchedKeypoints(name = undefined) { return new _nodes_keypoints_sink__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineNodeMatchedKeypointSink(name); } /** * Keypoint clipper * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointClipper} */ static Clipper(name = undefined) { return new _nodes_keypoints_clipper__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineNodeKeypointClipper(name); } /** * Border Clipper * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointBorderClipper} */ static BorderClipper(name = undefined) { return new _nodes_keypoints_border_clipper__WEBPACK_IMPORTED_MODULE_4__.SpeedyPipelineNodeKeypointBorderClipper(name); } /** * Create a keypoint buffer * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointBuffer} */ static Buffer(name = undefined) { return new _nodes_keypoints_buffer__WEBPACK_IMPORTED_MODULE_5__.SpeedyPipelineNodeKeypointBuffer(name); } /** * Create a keypoint mixer * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointMixer} */ static Mixer(name = undefined) { return new _nodes_keypoints_mixer__WEBPACK_IMPORTED_MODULE_6__.SpeedyPipelineNodeKeypointMixer(name); } /** * Create a keypoint shuffler * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointShuffler} */ static Shuffler(name = undefined) { return new _nodes_keypoints_shuffler__WEBPACK_IMPORTED_MODULE_7__.SpeedyPipelineNodeKeypointShuffler(name); } /** * Create a keypoint multiplexer * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointMultiplexer} */ static Multiplexer(name = undefined) { return new _nodes_keypoints_multiplexer__WEBPACK_IMPORTED_MODULE_8__.SpeedyPipelineNodeKeypointMultiplexer(name); } /** * Create a keypoint transformer * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointTransformer} */ static Transformer(name = undefined) { return new _nodes_keypoints_transformer__WEBPACK_IMPORTED_MODULE_9__.SpeedyPipelineNodeKeypointTransformer(name); } /** * Create a subpixel refiner of keypoint locations * @param {string} [name] * @returns {SpeedyPipelineNodeKeypointSubpixelRefiner} */ static SubpixelRefiner(name = undefined) { return new _nodes_keypoints_subpixel__WEBPACK_IMPORTED_MODULE_10__.SpeedyPipelineNodeKeypointSubpixelRefiner(name); } /** * Distance filter * @param {string} [name] * @returns {SpeedyPipelineNodeDistanceFilter} */ static DistanceFilter(name = undefined) { return new _nodes_keypoints_distance_filter__WEBPACK_IMPORTED_MODULE_18__.SpeedyPipelineNodeKeypointDistanceFilter(name); } /** * Hamming distance filter * @param {string} [name] * @returns {SpeedyPipelineNodeHammingDistanceFilter} */ static HammingDistanceFilter(name = undefined) { return new _nodes_keypoints_hamming_distance_filter__WEBPACK_IMPORTED_MODULE_19__.SpeedyPipelineNodeKeypointHammingDistanceFilter(name); } } /***/ }), /***/ "./src/core/pipeline/factories/transform-factory.js": /*!**********************************************************!*\ !*** ./src/core/pipeline/factories/transform-factory.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_26355__) => { "use strict"; __nested_webpack_require_26355__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_26355__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineTransformFactory": () => (/* binding */ SpeedyPipelineTransformFactory) /* harmony export */ }); /* harmony import */ var _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_26355__(/*! ../../speedy-namespace */ "./src/core/speedy-namespace.js"); /* harmony import */ var _nodes_transforms_perspective_warp__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_26355__(/*! ../nodes/transforms/perspective-warp */ "./src/core/pipeline/nodes/transforms/perspective-warp.js"); /* harmony import */ var _nodes_transforms_resize__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_26355__(/*! ../nodes/transforms/resize */ "./src/core/pipeline/nodes/transforms/resize.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * transform-factory.js * Image transforms */ /** * Image transforms */ class SpeedyPipelineTransformFactory extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Resize image * @param {string} [name] * @returns {SpeedyPipelineNodeResize} */ static Resize(name = undefined) { return new _nodes_transforms_resize__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineNodeResize(name); } /** * Warp an image using a perspective transformation * @param {string} [name] * @returns {SpeedyPipelineNodePerspectiveWarp} */ static PerspectiveWarp(name = undefined) { return new _nodes_transforms_perspective_warp__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodePerspectiveWarp(name); } } /***/ }), /***/ "./src/core/pipeline/factories/vector2-factory.js": /*!********************************************************!*\ !*** ./src/core/pipeline/factories/vector2-factory.js ***! \********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_29001__) => { "use strict"; __nested_webpack_require_29001__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_29001__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineVector2Factory": () => (/* binding */ SpeedyPipelineVector2Factory) /* harmony export */ }); /* harmony import */ var _speedy_vector__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_29001__(/*! ../../speedy-vector */ "./src/core/speedy-vector.js"); /* harmony import */ var _nodes_vector2_sink__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_29001__(/*! ../nodes/vector2/sink */ "./src/core/pipeline/nodes/vector2/sink.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * vector2-factory.js * 2D vectors */ /** * 2D vectors */ class SpeedyPipelineVector2Factory extends Function { /** * Constructor */ constructor() { // This factory can be invoked as a function super('...args', 'return this._create(...args)'); return this.bind(this); } /** * @private * * Create a 2D vector * @param {number} x x-coordinate * @param {number} y y-coordinate * @returns {SpeedyVector2} */ _create(x, y) { return new _speedy_vector__WEBPACK_IMPORTED_MODULE_0__.SpeedyVector2(x, y); } /** * Create a Vector2 sink * @param {string} [name] * @returns {SpeedyPipelineNodeVector2Sink} */ Sink(name = undefined) { return new _nodes_vector2_sink__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeVector2Sink(name); } } /***/ }), /***/ "./src/core/pipeline/nodes/filters/convolution.js": /*!********************************************************!*\ !*** ./src/core/pipeline/nodes/filters/convolution.js ***! \********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_31499__) => { "use strict"; __nested_webpack_require_31499__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_31499__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeConvolution": () => (/* binding */ SpeedyPipelineNodeConvolution) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_31499__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_31499__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_31499__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_31499__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_31499__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_31499__(/*! ../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_31499__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_31499__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_31499__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_31499__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _speedy_matrix__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_31499__(/*! ../../../speedy-matrix */ "./src/core/speedy-matrix.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * convolution.js * Image convolution */ // 2D convolution programs const CONVOLUTION = { 3: 'convolution3', 5: 'convolution5', 7: 'convolution7', }; /** * Image convolution */ class SpeedyPipelineNodeConvolution extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {SpeedyMatrix} convolution kernel (square matrix) */ this._kernel = _speedy_matrix__WEBPACK_IMPORTED_MODULE_10__.SpeedyMatrix.Create(3, 3, [0, 0, 0, 0, 1, 0, 0, 0, 0]); // identity transform } /** * Convolution kernel * @returns {SpeedyMatrix} */ get kernel() { return this._kernel; } /** * Convolution kernel * @param {SpeedyMatrix} kernel */ set kernel(kernel) { if(kernel.rows != kernel.columns) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.NotSupportedError(`Use a square kernel`); else if(!(kernel.rows == 3 || kernel.rows == 5 || kernel.rows == 7)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.NotSupportedError(`Invalid kernel size. Supported sizes: 3x3, 5x5, 7x7`); this._kernel = kernel; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const outputTexture = this._tex[0]; const ksize = this._kernel.rows; const conv = CONVOLUTION[ksize]; const kernel = this._kernel.read(); (gpu.programs.filters[conv] .outputs(width, height, outputTexture) )(image, kernel); this.output().swrite(outputTexture, format); } } /***/ }), /***/ "./src/core/pipeline/nodes/filters/gaussian-blur.js": /*!**********************************************************!*\ !*** ./src/core/pipeline/nodes/filters/gaussian-blur.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_36861__) => { "use strict"; __nested_webpack_require_36861__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_36861__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeGaussianBlur": () => (/* binding */ SpeedyPipelineNodeGaussianBlur) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_36861__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_36861__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_36861__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_36861__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_36861__(/*! ../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _speedy_vector__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_36861__(/*! ../../../speedy-vector */ "./src/core/speedy-vector.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_36861__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_36861__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_36861__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * gaussian-blur.js * Gaussian Blur */ /** * Default kernels for different sizes: 3x3, 5x5, 7x7... (use sigma_x = sigma_y) * Heuristics: in order to pick a sigma, we set radius = 2 * sigma. Since * ksize = 1 + 2 * radius, it follows that sigma = (ksize - 1) / 4. When * ksize is 3, we set sigma = 1. Therefore, sigma = max(1, (ksize - 1) / 4). */ const DEFAULT_KERNEL = Object.freeze({ 3: [ 0.27901008925473514, 0.44197982149052983, 0.27901008925473514 ], // 1D convolution (sigma = 1) 5: [ 0.06135959781344021, 0.2447701955296099, 0.3877404133138998, 0.2447701955296099, 0.06135959781344021 ], // 1D convolution (separable kernel) 7: [ 0.03873542500847274, 0.11308485700794121, 0.2150068609928349, 0.26634571398150225, 0.2150068609928349, 0.11308485700794121, 0.03873542500847274 ], 9: [ 0.028532262603370988, 0.067234535494912, 0.12400932997922749, 0.17904386461741617, 0.20236001461014655, 0.17904386461741617, 0.12400932997922749, 0.067234535494912, 0.028532262603370988 ], 11:[ 0.022656882730580346, 0.04610857898527292, 0.08012661469398517, 0.11890414969751599, 0.15067709325491124, 0.16305336127546846, 0.15067709325491124, 0.11890414969751599, 0.08012661469398517, 0.04610857898527292, 0.022656882730580346 ], 13:[ 0.018815730430644363, 0.03447396964662016, 0.05657737457255748, 0.08317258170844948, 0.10952340502389682, 0.12918787500405662, 0.13649812722755, 0.12918787500405662, 0.10952340502389682, 0.08317258170844948, 0.05657737457255748, 0.03447396964662016, 0.018815730430644363 ], 15:[ 0.016100340991695383, 0.027272329212157102, 0.042598338587449644, 0.06135478775568558, 0.08148767614129326, 0.09979838342934616, 0.11270444144735056, 0.11736740487004466, 0.11270444144735056, 0.09979838342934616, 0.08148767614129326, 0.06135478775568558, 0.042598338587449644, 0.027272329212157102, 0.016100340991695383 ], //3: [ 0.25, 0.5, 0.25 ], //5: [ 0.05, 0.25, 0.4, 0.25, 0.05 ], }); /** Zero vector. When we set sigma_x = sigma_y = 0, we use the default rule to compute the actual sigma */ const DEFAULT_SIGMA = new _speedy_vector__WEBPACK_IMPORTED_MODULE_5__.SpeedyVector2(0,0); /** convolution programs (x-axis) */ const CONVOLUTION_X = Object.freeze({ 3: 'convolution3x', 5: 'convolution5x', 7: 'convolution7x', 9: 'convolution9x', 11: 'convolution11x', 13: 'convolution13x', 15: 'convolution15x', }); /** convolution programs (y-axis) */ const CONVOLUTION_Y = Object.freeze({ 3: 'convolution3y', 5: 'convolution5y', 7: 'convolution7y', 9: 'convolution9y', 11: 'convolution11y', 13: 'convolution13y', 15: 'convolution15y', }); /** * @typedef {object} SeparableConvolutionKernel * @property {number[]} x * @property {number[]} y */ /** * Gaussian Blur */ class SpeedyPipelineNodeGaussianBlur extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {SpeedySize} size of the kernel */ this._kernelSize = new _speedy_size__WEBPACK_IMPORTED_MODULE_4__.SpeedySize(5,5); /** @type {SpeedyVector2} sigma of the Gaussian kernel (0 means: use default settings) */ this._sigma = DEFAULT_SIGMA; /** @type {SeparableConvolutionKernel} convolution kernel */ this._kernel = { x: DEFAULT_KERNEL[this._kernelSize.width], y: DEFAULT_KERNEL[this._kernelSize.height] }; } /** * Size of the kernel * @returns {SpeedySize} */ get kernelSize() { return this._kernelSize; } /** * Size of the kernel * @param {SpeedySize} kernelSize */ set kernelSize(kernelSize) { _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.assert(kernelSize instanceof _speedy_size__WEBPACK_IMPORTED_MODULE_4__.SpeedySize); const kw = kernelSize.width, kh = kernelSize.height; if(kw < 3 || kh < 3 || kw > 15 || kh > 15 || kw % 2 == 0 || kh % 2 == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.NotSupportedError(`Unsupported kernel size: ${kw}x${kh}`); this._kernelSize = kernelSize; this._updateKernel(); } /** * Sigma of the Gaussian kernel * @returns {SpeedyVector2} */ get sigma() { return this._sigma; } /** * Sigma of the Gaussian kernel * @param {SpeedyVector2} sigma */ set sigma(sigma) { _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.assert(sigma instanceof _speedy_vector__WEBPACK_IMPORTED_MODULE_5__.SpeedyVector2, `Sigma must be a SpeedyVector2`); _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.assert(sigma.x >= 0 && sigma.y >= 0); this._sigma = sigma; this._updateKernel(); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const kernX = this._kernel.x; const kernY = this._kernel.y; const convX = CONVOLUTION_X[this._kernelSize.width]; const convY = CONVOLUTION_Y[this._kernelSize.height]; const tex = this._tex[0]; const outputTexture = this._tex[1]; (gpu.programs.filters[convX] .outputs(width, height, tex) )(image, kernX); (gpu.programs.filters[convY] .outputs(width, height, outputTexture) )(tex, kernY); this.output().swrite(outputTexture, format); } /** * Update the internal kernel to match * sigma and kernelSize */ _updateKernel() { if(this._sigma.x == DEFAULT_SIGMA.x) this._kernel.x = DEFAULT_KERNEL[this._kernelSize.width]; else this._kernel.x = _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.gaussianKernel(this._sigma.x, this._kernelSize.width, true); if(this._sigma.y == DEFAULT_SIGMA.y) this._kernel.y = DEFAULT_KERNEL[this._kernelSize.height]; else this._kernel.y = _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.gaussianKernel(this._sigma.y, this._kernelSize.height, true); } } /***/ }), /***/ "./src/core/pipeline/nodes/filters/greyscale.js": /*!******************************************************!*\ !*** ./src/core/pipeline/nodes/filters/greyscale.js ***! \******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_46305__) => { "use strict"; __nested_webpack_require_46305__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_46305__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeGreyscale": () => (/* binding */ SpeedyPipelineNodeGreyscale) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_46305__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_46305__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_46305__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_46305__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_46305__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_46305__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_46305__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_46305__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * greyscale.js * Convert an image to greyscale */ /** * Convert an image to greyscale */ class SpeedyPipelineNodeGreyscale extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const outputTexture = this._tex[0]; const filters = gpu.programs.filters; filters.rgb2grey.outputs(width, height, outputTexture); filters.rgb2grey(image); this.output().swrite(outputTexture, _utils_types__WEBPACK_IMPORTED_MODULE_6__.ImageFormat.GREY); } } /***/ }), /***/ "./src/core/pipeline/nodes/filters/median-blur.js": /*!********************************************************!*\ !*** ./src/core/pipeline/nodes/filters/median-blur.js ***! \********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_50196__) => { "use strict"; __nested_webpack_require_50196__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_50196__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeMedianBlur": () => (/* binding */ SpeedyPipelineNodeMedianBlur) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_50196__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_50196__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_50196__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_50196__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_50196__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_50196__(/*! ../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_50196__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_50196__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_50196__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_50196__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * median-blur.js * Median Blur */ // Median programs const MEDIAN = { 3: 'median3', 5: 'median5', 7: 'median7', }; /** * Median Blur */ class SpeedyPipelineNodeMedianBlur extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_7__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {SpeedySize} size of the kernel (assumed to be square) */ this._kernelSize = new _speedy_size__WEBPACK_IMPORTED_MODULE_5__.SpeedySize(5,5); } /** * Size of the kernel * @returns {SpeedySize} */ get kernelSize() { return this._kernelSize; } /** * Size of the kernel * @param {SpeedySize} kernelSize */ set kernelSize(kernelSize) { _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.assert(kernelSize instanceof _speedy_size__WEBPACK_IMPORTED_MODULE_5__.SpeedySize); const ksize = kernelSize.width; if(!(ksize == 3 || ksize == 5 || ksize == 7)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.NotSupportedError(`Supported kernel sizes: 3x3, 5x5, 7x7`); else if(kernelSize.width != kernelSize.height) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.NotSupportedError(`Use a square kernel`); this._kernelSize = kernelSize; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const ksize = this._kernelSize.width; const med = MEDIAN[ksize]; const outputTexture = this._tex[0]; (gpu.programs.filters[med] .outputs(width, height, outputTexture) )(image); this.output().swrite(outputTexture, format); } } /***/ }), /***/ "./src/core/pipeline/nodes/filters/nightvision.js": /*!********************************************************!*\ !*** ./src/core/pipeline/nodes/filters/nightvision.js ***! \********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_55621__) => { "use strict"; __nested_webpack_require_55621__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_55621__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeNightvision": () => (/* binding */ SpeedyPipelineNodeNightvision) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_55621__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_55621__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_55621__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_55621__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_55621__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_55621__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_55621__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_55621__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_55621__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * nightvision.js * Nightvision filter */ /** * @typedef {"high"|"medium"|"low"} NightvisionQualityLevel */ /** * Nightvision filter: "see in the dark" */ class SpeedyPipelineNodeNightvision extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 3, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_7__.ImageFormat.RGBA || msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_7__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {number} a value typically in [0,1]: larger number => higher contrast */ this._gain = 0.5; /** @type {number} a value typically in [0,1]: controls brightness */ this._offset = 0.5; /** @type {number} gain decay, a value in [0,1] */ this._decay = 0.0; /** @type {NightvisionQualityLevel} quality level */ this._quality = 'medium'; } /** * Gain, a value typically in [0,1]: larger number => higher contrast * @returns {number} */ get gain() { return this._gain; } /** * Gain, a value typically in [0,1]: larger number => higher contrast * @param {number} gain */ set gain(gain) { this._gain = +gain; } /** * Offset, a value typically in [0,1] that controls the brightness * @returns {number} */ get offset() { return this._offset; } /** * Offset, a value typically in [0,1] that controls the brightness * @param {number} offset */ set offset(offset) { this._offset = +offset; } /** * Gain decay, a value in [0,1] that controls how the gain decays from the center of the image * @returns {number} */ get decay() { return this._decay; } /** * Gain decay, a value in [0,1] that controls how the gain decays from the center of the image * @param {number} decay */ set decay(decay) { this._decay = Math.max(0.0, Math.min(+decay, 1.0)); } /** * Quality level of the filter * @returns {NightvisionQualityLevel} */ get quality() { return this._quality; } /** * Quality level of the filter * @param {NightvisionQualityLevel} quality */ set quality(quality) { if(quality === 'high' || quality === 'medium' || quality === 'low') this._quality = quality; else throw new _utils_errors__WEBPACK_IMPORTED_MODULE_6__.IllegalArgumentError(`Invalid quality level for the Nightvision filter: "${quality}"`); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const gain = this._gain; const offset = this._offset; const decay = this._decay; const quality = this._quality; const filters = gpu.programs.filters; const tmp = this._tex[0]; const illuminationMap = this._tex[1]; const outputTexture = this._tex[2]; // compute illumination map if(quality == 'medium') { filters.illuminationMapX.outputs(width, height, tmp); filters.illuminationMapY.outputs(width, height, illuminationMap); filters.illuminationMapX(image); filters.illuminationMapY(tmp); } else if(quality == 'high') { filters.illuminationMapHiX.outputs(width, height, tmp); filters.illuminationMapHiY.outputs(width, height, illuminationMap); filters.illuminationMapHiX(image); filters.illuminationMapHiY(tmp); } else if(quality == 'low') { filters.illuminationMapLoX.outputs(width, height, tmp); filters.illuminationMapLoY.outputs(width, height, illuminationMap); filters.illuminationMapLoX(image); filters.illuminationMapLoY(tmp); } // run nightvision if(format === _utils_types__WEBPACK_IMPORTED_MODULE_7__.ImageFormat.GREY) { filters.nightvisionGreyscale.outputs(width, height, outputTexture); filters.nightvisionGreyscale(image, illuminationMap, gain, offset, decay); } else if(format === _utils_types__WEBPACK_IMPORTED_MODULE_7__.ImageFormat.RGBA) { filters.nightvision.outputs(width, height, outputTexture); filters.nightvision(image, illuminationMap, gain, offset, decay); } // done! this.output().swrite(outputTexture, format); } } /***/ }), /***/ "./src/core/pipeline/nodes/filters/normalize.js": /*!******************************************************!*\ !*** ./src/core/pipeline/nodes/filters/normalize.js ***! \******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_63648__) => { "use strict"; __nested_webpack_require_63648__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_63648__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeNormalize": () => (/* binding */ SpeedyPipelineNodeNormalize) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_63648__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_63648__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_63648__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_63648__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_63648__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_63648__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_63648__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_63648__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * normalize.js * Normalize image to a range */ /** * Normalize image to a range */ class SpeedyPipelineNodeNormalize extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 4, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_6__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {number} a value in [0,255] */ this._minValue = 0; /** @type {number} a value in [0,255] */ this._maxValue = 255; } /** * Minimum intensity in the output image, a value in [0,255] * @returns {number} */ get minValue() { return this._minValue; } /** * Minimum intensity in the output image, a value in [0,255] * @param {number} minValue */ set minValue(minValue) { this._minValue = Math.max(0, Math.min(+minValue, 255)); } /** * Maximum intensity in the output image, a value in [0,255] * @returns {number} */ get maxValue() { return this._maxValue; } /** * Maximum intensity in the output image, a value in [0,255] * @param {number} maxValue */ set maxValue(maxValue) { this._maxValue = Math.max(0, Math.min(+maxValue, 255)); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const outputTexture = this._tex[3]; let minValue = this._minValue; let maxValue = this._maxValue; if(minValue > maxValue) minValue = maxValue = (minValue + maxValue) / 2; const minmax = this._scanMinMax(gpu, image, _utils_types__WEBPACK_IMPORTED_MODULE_6__.PixelComponent.GREEN); gpu.programs.filters.normalizeGreyscale.outputs(width, height, outputTexture); gpu.programs.filters.normalizeGreyscale(minmax, minValue, maxValue); this.output().swrite(outputTexture, format); } /** * Scan a single component in all pixels of the image and find the min & max intensities * @param {SpeedyGPU} gpu * @param {SpeedyTexture} image input image * @param {PixelComponent} pixelComponent a single PixelComponent flag * @returns {SpeedyDrawableTexture} RGBA = (max, min, max - min, original_pixel) */ _scanMinMax(gpu, image, pixelComponent) { const tex = this._tex; const program = gpu.programs.utils; const width = image.width, height = image.height; const numIterations = Math.ceil(Math.log2(Math.max(width, height))) | 0; _utils_utils__WEBPACK_IMPORTED_MODULE_5__.Utils.assert(_utils_types__WEBPACK_IMPORTED_MODULE_6__.ColorComponentId[pixelComponent] !== undefined); program.copyComponents.outputs(width, height, tex[2]); program.scanMinMax2D.outputs(width, height, tex[0], tex[1]); let texture = program.copyComponents(image, image, _utils_types__WEBPACK_IMPORTED_MODULE_6__.PixelComponent.ALL, _utils_types__WEBPACK_IMPORTED_MODULE_6__.ColorComponentId[pixelComponent]); for(let i = 0; i < numIterations; i++) texture = program.scanMinMax2D(texture, i); return texture; } } /***/ }), /***/ "./src/core/pipeline/nodes/filters/simple-blur.js": /*!********************************************************!*\ !*** ./src/core/pipeline/nodes/filters/simple-blur.js ***! \********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_70155__) => { "use strict"; __nested_webpack_require_70155__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_70155__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeSimpleBlur": () => (/* binding */ SpeedyPipelineNodeSimpleBlur) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_70155__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_70155__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_70155__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_70155__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_70155__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_70155__(/*! ../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_70155__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_70155__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_70155__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_70155__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * simple-blur.js * Simple Blur (Box Filter) */ /** 1D convolution filters */ const BOX_FILTER = Object.freeze({ 3: (new Array(3)).fill(1/3), 5: (new Array(5)).fill(1/5), 7: (new Array(7)).fill(1/7), 9: (new Array(9)).fill(1/9), 11: (new Array(11)).fill(1/11), 13: (new Array(13)).fill(1/13), 15: (new Array(15)).fill(1/15), }); /** convolution programs (x-axis) */ const CONVOLUTION_X = Object.freeze({ 3: 'convolution3x', 5: 'convolution5x', 7: 'convolution7x', 9: 'convolution9x', 11: 'convolution11x', 13: 'convolution13x', 15: 'convolution15x', }); /** convolution programs (y-axis) */ const CONVOLUTION_Y = Object.freeze({ 3: 'convolution3y', 5: 'convolution5y', 7: 'convolution7y', 9: 'convolution9y', 11: 'convolution11y', 13: 'convolution13y', 15: 'convolution15y', }); /** * @typedef {object} SeparableConvolutionKernel * @property {number[]} x * @property {number[]} y */ /** * Simple Blur (Box Filter) */ class SpeedyPipelineNodeSimpleBlur extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {SpeedySize} size of the kernel */ this._kernelSize = new _speedy_size__WEBPACK_IMPORTED_MODULE_5__.SpeedySize(5,5); /** @type {SeparableConvolutionKernel} convolution kernel */ this._kernel = { x: BOX_FILTER[this._kernelSize.width], y: BOX_FILTER[this._kernelSize.height] }; } /** * Size of the kernel * @returns {SpeedySize} */ get kernelSize() { return this._kernelSize; } /** * Size of the kernel * @param {SpeedySize} kernelSize */ set kernelSize(kernelSize) { _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.assert(kernelSize instanceof _speedy_size__WEBPACK_IMPORTED_MODULE_5__.SpeedySize); const kw = kernelSize.width, kh = kernelSize.height; if(kw < 3 || kh < 3 || kw > 15 || kh > 15 || kw % 2 == 0 || kh % 2 == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.NotSupportedError(`Unsupported kernel size: ${kw}x${kh}`); this._kernelSize = kernelSize; this._kernel.x = BOX_FILTER[this._kernelSize.width]; this._kernel.y = BOX_FILTER[this._kernelSize.height]; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const kernX = this._kernel.x; const kernY = this._kernel.y; const convX = CONVOLUTION_X[this._kernelSize.width]; const convY = CONVOLUTION_Y[this._kernelSize.height]; const tex = this._tex[0]; const outputTexture = this._tex[1]; (gpu.programs.filters[convX] .outputs(width, height, tex) )(image, kernX); (gpu.programs.filters[convY] .outputs(width, height, outputTexture) )(tex, kernY); this.output().swrite(outputTexture, format); } } /***/ }), /***/ "./src/core/pipeline/nodes/images/buffer.js": /*!**************************************************!*\ !*** ./src/core/pipeline/nodes/images/buffer.js ***! \**************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_76678__) => { "use strict"; __nested_webpack_require_76678__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_76678__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeImageBuffer": () => (/* binding */ SpeedyPipelineNodeImageBuffer) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_76678__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_76678__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_76678__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_76678__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_76678__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_76678__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_76678__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_76678__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_76678__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * buffer.js * Image Buffer */ /** * Image Buffer: a node with memory. * At time t, it outputs the image received at time t-1 */ class SpeedyPipelineNodeImageBuffer extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image) ]); /** @type {number} current page: 0 or 1 */ this._pageIndex = 0; /** @type {boolean} first run? */ this._initialized = false; /** @type {ImageFormat} previous image format */ this._previousFormat = _utils_types__WEBPACK_IMPORTED_MODULE_3__.ImageFormat.RGBA; /** @type {boolean} frozen buffer? */ this._frozen = false; } /** * A frozen buffer discards the input, effectively increasing the buffering time * @returns {boolean} */ get frozen() { return this._frozen; } /** * A frozen buffer discards the input, effectively increasing the buffering time * @param {boolean} value */ set frozen(value) { this._frozen = Boolean(value); } /** * Releases this node * @param {SpeedyGPU} gpu */ release(gpu) { this._initialized = false; super.release(gpu); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const previousFormat = this._previousFormat; const page = this._tex; const previousInputTexture = page[1 - this._pageIndex]; const outputTexture = page[this._pageIndex]; // can't store pyramids if(image.hasMipmaps()) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.NotSupportedError(`${this.fullName} can't bufferize a pyramid`); // bufferize if(!this._frozen || !this._initialized) { // store input this._previousFormat = format; previousInputTexture.resize(image.width, image.height); image.copyTo(previousInputTexture); // page flipping this._pageIndex = 1 - this._pageIndex; } // first run? if(!this._initialized) { this._initialized = true; this.output().swrite(previousInputTexture, format); return; } // done! this.output().swrite(outputTexture, previousFormat); } } /***/ }), /***/ "./src/core/pipeline/nodes/images/mixer.js": /*!*************************************************!*\ !*** ./src/core/pipeline/nodes/images/mixer.js ***! \*************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_82338__) => { "use strict"; __nested_webpack_require_82338__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_82338__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeImageMixer": () => (/* binding */ SpeedyPipelineNodeImageMixer) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_82338__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_82338__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_82338__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_82338__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_82338__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_82338__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_82338__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_82338__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_82338__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * mixer.js * Image Mixer */ /** * Image Mixer */ class SpeedyPipelineNodeImageMixer extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('in0').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('in1').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {number} alpha coefficient (applied to image0) */ this._alpha = 0.5; /** @type {number} beta coefficient (applied to image1) */ this._beta = 0.5; /** @type {number} gamma coefficient (brightness control) */ this._gamma = 0.0; } /** * Alpha coefficient (applied to image0) * @returns {number} */ get alpha() { return this._alpha; } /** * Alpha coefficient (applied to image0) * @param {number} value */ set alpha(value) { this._alpha = +value; } /** * Beta coefficient (applied to image1) * @returns {number} */ get beta() { return this._beta; } /** * Beta coefficient (applied to image1) * @param {number} value */ set beta(value) { this._beta = +value; } /** * Gamma coefficient (brightness control) * @returns {number} */ get gamma() { return this._gamma; } /** * Gamma coefficient (brightness control) * @param {number} value */ set gamma(value) { this._gamma = +value; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const in0 = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input('in0').read() ); const in1 = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input('in1').read() ); const image0 = in0.image, image1 = in1.image; const format0 = in0.format, format1 = in1.format; const width = Math.max(image0.width, image1.width); const height = Math.max(image0.height, image1.height); const alpha = this._alpha, beta = this._beta, gamma = this._gamma; const outputTexture = this._tex[0]; if(format0 != format1) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.NotSupportedError(`Can't mix images of different formats`); gpu.programs.transforms.additiveMix.outputs(width, height, outputTexture); gpu.programs.transforms.additiveMix(image0, image1, alpha, beta, gamma); this.output().swrite(outputTexture, format0); } } /***/ }), /***/ "./src/core/pipeline/nodes/images/multiplexer.js": /*!*******************************************************!*\ !*** ./src/core/pipeline/nodes/images/multiplexer.js ***! \*******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_88155__) => { "use strict"; __nested_webpack_require_88155__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_88155__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeImageMultiplexer": () => (/* binding */ SpeedyPipelineNodeImageMultiplexer) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_88155__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_88155__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_88155__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_88155__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_88155__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_media__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_88155__(/*! ../../../speedy-media */ "./src/core/speedy-media.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_88155__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_88155__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_88155__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * multiplexer.js * Image multiplexer */ /** @type {string[]} the names of the input ports indexed by their number */ const INPUT_PORT = [ 'in0', 'in1' ]; /** * Image multiplexer */ class SpeedyPipelineNodeImageMultiplexer extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 0, [ ...(INPUT_PORT.map(portName => (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)(portName).expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image))), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {number} which port should be linked to the output? */ this._port = 0; } /** * The number of the port that should be linked to the output * @returns {number} */ get port() { return this._port; } /** * The number of the port that should be linked to the output * @param {number} port */ set port(port) { if(port < 0 || port >= INPUT_PORT.length) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Invalid port: ${port}`); this._port = port | 0; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const message = this.input(INPUT_PORT[this._port]).read(); this.output().write(message); } } /***/ }), /***/ "./src/core/pipeline/nodes/images/portal.js": /*!**************************************************!*\ !*** ./src/core/pipeline/nodes/images/portal.js ***! \**************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_92576__) => { "use strict"; __nested_webpack_require_92576__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_92576__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeImagePortalSink": () => (/* binding */ SpeedyPipelineNodeImagePortalSink), /* harmony export */ "SpeedyPipelineNodeImagePortalSource": () => (/* binding */ SpeedyPipelineNodeImagePortalSource) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_92576__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_92576__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_92576__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_92576__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_92576__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_92576__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_92576__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_92576__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_92576__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * portal.js * Image Portals */ /** * A sink of an Image Portal * This is not a pipeline sink - it doesn't export any data! */ class SpeedyPipelineNodeImagePortalSink extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {ImageFormat} stored image format */ this._format = _utils_types__WEBPACK_IMPORTED_MODULE_3__.ImageFormat.RGBA; /** @type {boolean} is this node initialized? */ this._initialized = false; } /** * Stored image * @returns {SpeedyTexture} */ get image() { if(!this._initialized) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Portal error: ${this.fullName} holds no data`); return this._tex[0]; } /** * Stored image format * @returns {ImageFormat} */ get format() { if(!this._initialized) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Portal error: ${this.fullName} holds no data`); return this._format; } /** * Initializes this node * @param {SpeedyGPU} gpu */ init(gpu) { super.init(gpu); this._tex[0].resize(1, 1).clear(); // initial texture this._format = _utils_types__WEBPACK_IMPORTED_MODULE_3__.ImageFormat.RGBA; this._initialized = true; } /** * Releases this node * @param {SpeedyGPU} gpu */ release(gpu) { this._initialized = false; super.release(gpu); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const tex = this._tex[0]; // can't store pyramids if(image.hasMipmaps()) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.NotSupportedError(`${this.fullName} can't store a pyramid`); // copy input this._format = format; tex.resize(image.width, image.height); image.copyTo(tex); } } /** * A source of an Image Portal */ class SpeedyPipelineNodeImagePortalSource extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSourceNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 0, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {SpeedyPipelineNodeImagePortalSink|null} portal sink */ this._source = null; } /** * Data source * @returns {SpeedyPipelineNodeImagePortalSink|null} */ get source() { return this._source; } /** * Data source * @param {SpeedyPipelineNodeImagePortalSink|null} node */ set source(node) { if(node !== null && !(node instanceof SpeedyPipelineNodeImagePortalSink)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Incompatible source for ${this.fullName}`); this._source = node; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { if(this._source == null) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`${this.fullName} has no source`); this.output().swrite(this._source.image, this._source.format); } } /***/ }), /***/ "./src/core/pipeline/nodes/images/pyramid.js": /*!***************************************************!*\ !*** ./src/core/pipeline/nodes/images/pyramid.js ***! \***************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_99417__) => { "use strict"; __nested_webpack_require_99417__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_99417__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeImagePyramid": () => (/* binding */ SpeedyPipelineNodeImagePyramid) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_99417__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_99417__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_99417__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_99417__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_99417__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_99417__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_99417__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_99417__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_99417__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * pyramid.js * Generate pyramid */ // Constants const MAX_LEVELS = _utils_globals__WEBPACK_IMPORTED_MODULE_6__.PYRAMID_MAX_LEVELS; //14; // supposing image size <= 8K = 2^13 (downto 1) const MAX_TEXTURES = 2 * MAX_LEVELS; //MAX_LEVELS; /** * Generate pyramid */ class SpeedyPipelineNodeImagePyramid extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, MAX_TEXTURES + 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const outputTexture = this._tex[0]; const pyramids = gpu.programs.pyramids; let width = image.width, height = image.height; // number of mipmap levels according to the OpenGL ES 3.0 spec (sec 3.8.10.4) const mipLevels = 1 + Math.floor(Math.log2(Math.max(width, height))); // get work textures const mip = new Array(MAX_TEXTURES + 1); for(let i = MAX_TEXTURES; i >= 1; i--) mip[i-1] = this._tex[i]; // get a copy of the input image mip[0].resize(width, height); image.copyTo(mip[0]); // generate gaussian pyramid const numLevels = Math.min(mipLevels, MAX_LEVELS); for(let level = 1; level < numLevels; level++) { // use max(1, floor(size / 2^lod)), in accordance to // the OpenGL ES 3.0 spec sec 3.8.10.4 (Mipmapping) const halfWidth = Math.max(1, width >>> 1); const halfHeight = Math.max(1, height >>> 1); // reduce operation const tmp = (level - 1) + MAX_LEVELS; (pyramids.smoothX.outputs(width, height, mip[tmp]))(mip[level-1]); (pyramids.smoothY.outputs(width, height, mip[level-1]))(mip[tmp]); (pyramids.downsample2.outputs(halfWidth, halfHeight, mip[level]))(mip[level-1]); /* (pyramids.reduce.outputs(width, height, mip[tmp]))(mip[level-1]); (pyramids.downsample2.outputs(halfWidth, halfHeight, mip[level]))(mip[tmp]); */ // flush gpu.gl.flush(); // next level width = halfWidth; height = halfHeight; /* // debug: view pyramid const view = mip[level-1]; const canvas = gpu.renderToCanvas(view); if(!window._ww) document.body.appendChild(canvas); window._ww = 1; */ } // copy to output & set mipmap outputTexture.resize(image.width, image.height); outputTexture.clear(); image.copyTo(outputTexture); outputTexture.generateMipmaps(mip.slice(0, numLevels)); // done! this.output().swrite(outputTexture, format); } } /***/ }), /***/ "./src/core/pipeline/nodes/images/sink.js": /*!************************************************!*\ !*** ./src/core/pipeline/nodes/images/sink.js ***! \************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_105489__) => { "use strict"; __nested_webpack_require_105489__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_105489__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeImageSink": () => (/* binding */ SpeedyPipelineNodeImageSink) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_105489__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_105489__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_105489__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_105489__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_105489__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_media__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_105489__(/*! ../../../speedy-media */ "./src/core/speedy-media.js"); /* harmony import */ var _speedy_media_source__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_105489__(/*! ../../../speedy-media-source */ "./src/core/speedy-media-source.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_105489__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_105489__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_105489__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * image-output.js * Gets an image out of a pipeline */ /** * Gets an image out of a pipeline */ class SpeedyPipelineNodeImageSink extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSinkNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = 'image') { super(name, 0, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image) ]); /** @type {ImageBitmap} output bitmap */ this._bitmap = null; /** @type {ImageFormat} output format */ this._format = _utils_types__WEBPACK_IMPORTED_MODULE_8__.ImageFormat.RGBA; } /** * Export data from this node to the user * @returns {SpeedyPromise} */ export() { _utils_utils__WEBPACK_IMPORTED_MODULE_7__.Utils.assert(this._bitmap != null); return _speedy_media__WEBPACK_IMPORTED_MODULE_5__.SpeedyMedia.load(this._bitmap, { format: this._format }, false); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); return new _speedy_promise__WEBPACK_IMPORTED_MODULE_9__.SpeedyPromise(resolve => { const canvas = gpu.renderToCanvas(image); createImageBitmap(canvas, 0, canvas.height - image.height, image.width, image.height).then(bitmap => { this._bitmap = bitmap; this._format = format; resolve(); }); }); } } /***/ }), /***/ "./src/core/pipeline/nodes/images/source.js": /*!**************************************************!*\ !*** ./src/core/pipeline/nodes/images/source.js ***! \**************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_110114__) => { "use strict"; __nested_webpack_require_110114__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_110114__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeImageSource": () => (/* binding */ SpeedyPipelineNodeImageSource) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_110114__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_110114__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_110114__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_110114__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_110114__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_media__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_110114__(/*! ../../../speedy-media */ "./src/core/speedy-media.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_110114__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_110114__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_110114__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_110114__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * image-input.js * Gets an image into a pipeline */ // Constants const UPLOAD_BUFFER_SIZE = 2; // how many textures we allocate for uploading data /** * Gets an image into a pipeline */ class SpeedyPipelineNodeImageSource extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSourceNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, UPLOAD_BUFFER_SIZE, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image) ]); /** @type {SpeedyMedia|null} source media */ this._media = null; /** @type {number} texture index */ this._textureIndex = 0; } /** * Source media * @returns {SpeedyMedia|null} */ get media() { return this._media; } /** * Source media * @param {SpeedyMedia|null} media */ set media(media) { if(media !== null && !(media instanceof _speedy_media__WEBPACK_IMPORTED_MODULE_5__.SpeedyMedia)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalArgumentError(`Not a SpeedyMedia: ${media}`); this._media = media; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { if(this._media == null) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalOperationError(`Did you forget to set the media of ${this.fullName}?`); // use round-robin to mitigate WebGL's implicit synchronization // and maybe minimize texture upload times this._textureIndex = (this._textureIndex + 1) % this._tex.length; // upload texture const outputTexture = this._tex[this._textureIndex]; gpu.upload(this._media._source, outputTexture); this.output().swrite(outputTexture, this._media._format); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/border-clipper.js": /*!*************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/border-clipper.js ***! \*************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_115060__) => { "use strict"; __nested_webpack_require_115060__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_115060__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointBorderClipper": () => (/* binding */ SpeedyPipelineNodeKeypointBorderClipper) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_115060__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_115060__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_115060__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_115060__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_115060__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_115060__(/*! ../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _speedy_vector__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_115060__(/*! ../../../speedy-vector */ "./src/core/speedy-vector.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_115060__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_115060__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_115060__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_115060__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_11__ = __nested_webpack_require_115060__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * border-clipper.js * Keypoint Border Clipper */ /** * The Border Clipper removes all keypoints within a border of the edges of an image */ class SpeedyPipelineNodeKeypointBorderClipper extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 5, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {SpeedySize} image size, in pixels */ this._imageSize = new _speedy_size__WEBPACK_IMPORTED_MODULE_5__.SpeedySize(0,0); /** @type {SpeedyVector2} border size, in pixels */ this._borderSize = new _speedy_vector__WEBPACK_IMPORTED_MODULE_6__.SpeedyVector2(0,0); } /** * Image size, in pixels * @returns {SpeedySize} */ get imageSize() { return this._imageSize; } /** * Image size, in pixels * @param {SpeedySize} imageSize */ set imageSize(imageSize) { this._imageSize = imageSize; } /** * Border size, in pixels * @returns {SpeedyVector2} */ get borderSize() { return this._borderSize; } /** * Border size, in pixels * @param {SpeedyVector2} borderSize */ set borderSize(borderSize) { this._borderSize = borderSize; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const keypoints = gpu.programs.keypoints; const imageSize = this._imageSize; const borderSize = this._borderSize; const imageWidth = imageSize.width, imageHeight = imageSize.height; const borderLeft = borderSize.x, borderRight = borderSize.x; const borderTop = borderSize.y, borderBottom = borderSize.y; const tex = this._tex; // validate if(imageWidth == 0 || imageHeight == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_9__.IllegalOperationError(`BorderClipper: did you forget to set the image size?`); // find the capacity of the keypoint stream const capacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); const mixEncoderLength = Math.max(1, Math.ceil(Math.sqrt(capacity))); // prepare programs keypoints.clipBorder.outputs(encoderLength, encoderLength, tex[0]); keypoints.mixKeypointsInit.outputs(mixEncoderLength, mixEncoderLength, tex[1]); keypoints.mixKeypointsSort.outputs(mixEncoderLength, mixEncoderLength, tex[2], tex[3]); keypoints.mixKeypointsApply.outputs(encoderLength, encoderLength, tex[4]); // clip keypoints let clippedKeypoints = keypoints.clipBorder( imageWidth, imageHeight, borderTop, borderRight, borderBottom, borderLeft, encodedKeypoints, descriptorSize, extraSize, encoderLength ); // sort keypoints let sortedKeypoints = keypoints.mixKeypointsInit( clippedKeypoints, descriptorSize, extraSize, encoderLength, capacity ); for(let b = 1; b < capacity; b *= 2) sortedKeypoints = keypoints.mixKeypointsSort(sortedKeypoints, b); clippedKeypoints = keypoints.mixKeypointsApply( sortedKeypoints, clippedKeypoints, descriptorSize, extraSize, encoderLength ); /* // debug: view keypoints keypoints.mixKeypointsView.outputs(mixEncoderLength, mixEncoderLength, tex[1]); this._visualize(gpu, keypoints.mixKeypointsView(sortedKeypoints)); */ // done! this.output().swrite(clippedKeypoints, descriptorSize, extraSize, encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/buffer.js": /*!*****************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/buffer.js ***! \*****************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_122606__) => { "use strict"; __nested_webpack_require_122606__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_122606__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointBuffer": () => (/* binding */ SpeedyPipelineNodeKeypointBuffer) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_122606__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_122606__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_122606__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_122606__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_122606__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_122606__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_122606__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_122606__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * buffer.js * Keypoint Buffer */ /** * Keypoint Buffer: a node with memory. * At time t, it outputs the keypoints received at time t-1 */ class SpeedyPipelineNodeKeypointBuffer extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {number} current page: 0 or 1 */ this._pageIndex = 0; /** @type {boolean} first run? */ this._initialized = false; /** @type {number} previous descriptor size, in bytes */ this._previousDescriptorSize = 0; /** @type {number} previous extra size, in bytes */ this._previousExtraSize = 0; /** @type {number} previous encoder length */ this._previousEncoderLength = 0; /** @type {boolean} frozen buffer? */ this._frozen = false; } /** * A frozen buffer discards the input, effectively increasing the buffering time * @returns {boolean} */ get frozen() { return this._frozen; } /** * A frozen buffer discards the input, effectively increasing the buffering time * @param {boolean} value */ set frozen(value) { this._frozen = Boolean(value); } /** * Releases this node * @param {SpeedyGPU} gpu */ release(gpu) { this._initialized = false; super.release(gpu); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const previousDescriptorSize = this._previousDescriptorSize; const previousExtraSize = this._previousExtraSize; const previousEncoderLength = this._previousEncoderLength; const page = this._tex; const previousInputTexture = page[1 - this._pageIndex]; const outputTexture = page[this._pageIndex]; // bufferize if(!this._frozen || !this._initialized) { // store input this._previousDescriptorSize = descriptorSize; this._previousExtraSize = extraSize; this._previousEncoderLength = encoderLength; previousInputTexture.resize(encoderLength, encoderLength); encodedKeypoints.copyTo(previousInputTexture); // page flipping this._pageIndex = 1 - this._pageIndex; } // first run? if(!this._initialized) { this._initialized = true; this.output().swrite(previousInputTexture, descriptorSize, extraSize, encoderLength); return; } // done! this.output().swrite(outputTexture, previousDescriptorSize, previousExtraSize, previousEncoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/clipper.js": /*!******************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/clipper.js ***! \******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_128575__) => { "use strict"; __nested_webpack_require_128575__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_128575__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointClipper": () => (/* binding */ SpeedyPipelineNodeKeypointClipper) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_128575__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_128575__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_128575__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_128575__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_128575__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_128575__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_128575__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_128575__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_128575__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * clipper.js * Keypoint clipper */ // Constants const LOG2_STRIDE = 5; const MAX_SIZE = _utils_globals__WEBPACK_IMPORTED_MODULE_7__.MAX_ENCODER_CAPACITY; /** * Keypoint clipper: filters the best keypoints from a stream */ class SpeedyPipelineNodeKeypointClipper extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 4, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {number} the maximum number of keypoints in the output */ this._size = MAX_SIZE; } /** * The maximum number of keypoints in the output * @returns {number} */ get size() { return this._size; } /** * The maximum number of keypoints in the output * @param {number} size */ set size(size) { this._size = Math.max(0, Math.min(size | 0, MAX_SIZE)); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const keypoints = gpu.programs.keypoints; const clipValue = this._size; const tex = this._tex; const outputTexture = this._tex[3]; // find the minimum power of 2 pot such that pot >= capacity const capacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); //const pot = 1 << (Math.ceil(Math.log2(capacity)) | 0); // find the dimensions of the sorting shaders const stride = 1 << LOG2_STRIDE; // must be a power of 2 //const height = Math.max(1, pot >>> LOG2_STRIDE); // this is also a power of 2 const height = Math.ceil(capacity / stride); // more economical, maybe not a power of 2 const numberOfPixels = stride * height; // find the dimensions of the output texture const newCapacity = Math.min(capacity, clipValue); const newEncoderLength = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderLength(newCapacity, descriptorSize, extraSize); // generate permutation of keypoints keypoints.sortCreatePermutation.outputs(stride, height, tex[0]); let permutation = keypoints.sortCreatePermutation(encodedKeypoints, descriptorSize, extraSize, encoderLength); // sort permutation const numPasses = Math.ceil(Math.log2(numberOfPixels)); keypoints.sortMergePermutation.outputs(stride, height, tex[1], tex[2]); for(let i = 1; i <= numPasses; i++) { const blockSize = 1 << i; // 2, 4, 8... const dblLog2BlockSize = i << 1; // 2 * log2(blockSize) permutation = keypoints.sortMergePermutation(permutation, blockSize, dblLog2BlockSize); } // apply permutation keypoints.sortApplyPermutation.outputs(newEncoderLength, newEncoderLength, outputTexture); keypoints.sortApplyPermutation(permutation, newCapacity, encodedKeypoints, descriptorSize, extraSize); /* // debug (read the contents of the permutation) const pixels = this._inspect(gpu, permutation), debug = []; for(let i = 0; i < pixels.length; i += 4) { let id = pixels[i] | (pixels[i+1] << 8); let score = pixels[i+2] / 255.0; let valid = pixels[i+3] / 255.0; debug.push([ id, valid, score, ].join(', ')); } console.log(debug); */ // done! this.output().swrite(outputTexture, descriptorSize, extraSize, newEncoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/descriptors/descriptor.js": /*!*********************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/descriptors/descriptor.js ***! \*********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_135597__) => { "use strict"; __nested_webpack_require_135597__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_135597__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointDescriptor": () => (/* binding */ SpeedyPipelineNodeKeypointDescriptor) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_135597__(/*! ../../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_135597__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_135597__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_135597__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_135597__(/*! ../detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_135597__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * descriptor.js * Abstract keypoint descriptor */ /** * Abstract keypoint descriptor * @abstract */ class SpeedyPipelineNodeKeypointDescriptor extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node * @param {number} [texCount] number of work textures * @param {SpeedyPipelinePortBuilder[]} [portBuilders] port builders */ constructor(name = undefined, texCount = 0, portBuilders = undefined) { super(name, texCount + 1, portBuilders); } /** * * Allocate space for keypoint descriptors * @param {SpeedyGPU} gpu * @param {number} inputDescriptorSize should be 0 * @param {number} inputExtraSize must be non-negative * @param {number} outputDescriptorSize in bytes, must be a multiple of 4 * @param {number} outputExtraSize must be inputExtraSize * @param {SpeedyTexture} inputEncodedKeypoints input with no descriptors * @returns {SpeedyDrawableTexture} encodedKeypoints */ _allocateDescriptors(gpu, inputDescriptorSize, inputExtraSize, outputDescriptorSize, outputExtraSize, inputEncodedKeypoints) { _utils_utils__WEBPACK_IMPORTED_MODULE_5__.Utils.assert(inputDescriptorSize >= 0 && inputExtraSize >= 0); _utils_utils__WEBPACK_IMPORTED_MODULE_5__.Utils.assert(outputDescriptorSize >= 0 && outputDescriptorSize % 4 === 0 && outputExtraSize === inputExtraSize); const inputEncoderLength = inputEncodedKeypoints.width; const inputEncoderCapacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_4__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(inputDescriptorSize, inputExtraSize, inputEncoderLength); const outputEncoderCapacity = inputEncoderCapacity; const outputEncoderLength = _detectors_detector__WEBPACK_IMPORTED_MODULE_4__.SpeedyPipelineNodeKeypointDetector.encoderLength(outputEncoderCapacity, outputDescriptorSize, outputExtraSize); const tex = this._tex[this._tex.length - 1]; return (gpu.programs.keypoints.allocateDescriptors .outputs(outputEncoderLength, outputEncoderLength, tex) )(inputEncodedKeypoints, inputDescriptorSize, inputExtraSize, inputEncoderLength, outputDescriptorSize, outputExtraSize, outputEncoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/descriptors/orb.js": /*!**************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/descriptors/orb.js ***! \**************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_140270__) => { "use strict"; __nested_webpack_require_140270__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_140270__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeORBKeypointDescriptor": () => (/* binding */ SpeedyPipelineNodeORBKeypointDescriptor) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_140270__(/*! ../../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_140270__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_140270__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_140270__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_140270__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_140270__(/*! ../../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_140270__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_140270__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_140270__(/*! ../detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _descriptor__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_140270__(/*! ./descriptor */ "./src/core/pipeline/nodes/keypoints/descriptors/descriptor.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * orb.js * ORB descriptors */ // Constants const DESCRIPTOR_SIZE = 32; // 256 bits /** * ORB descriptors */ class SpeedyPipelineNodeORBKeypointDescriptor extends _descriptor__WEBPACK_IMPORTED_MODULE_9__.SpeedyPipelineNodeKeypointDescriptor { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 3, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('image').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_5__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('keypoints').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), ]); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('keypoints').read() ); const image = ( /** @type {SpeedyPipelineMessageWithImage} */ ( this.input('image').read() ) ).image; const tex = this._tex; const outputTexture = this._tex[2]; // compute orientation const capacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_8__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); const orientationEncoderLength = Math.max(1, Math.ceil(Math.sqrt(capacity))); // 1 pixel per keypoint const encodedOrientations = (gpu.programs.keypoints.orbOrientation .outputs(orientationEncoderLength, orientationEncoderLength, tex[0]) )(image, encodedKeypoints, descriptorSize, extraSize, encoderLength); const orientedKeypoints = (gpu.programs.keypoints.transferOrientation .outputs(encoderLength, encoderLength, tex[1]) )(encodedOrientations, encodedKeypoints, descriptorSize, extraSize, encoderLength); // allocate space const encodedKps = this._allocateDescriptors(gpu, descriptorSize, extraSize, DESCRIPTOR_SIZE, extraSize, orientedKeypoints); const newEncoderLength = encodedKps.width; // compute descriptors (it's a good idea to blur the image) const describedKeypoints = (gpu.programs.keypoints.orbDescriptor .outputs(newEncoderLength, newEncoderLength, outputTexture) )(image, encodedKps, extraSize, newEncoderLength); // done! this.output().swrite(describedKeypoints, DESCRIPTOR_SIZE, extraSize, newEncoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/detectors/detector.js": /*!*****************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/detectors/detector.js ***! \*****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_146307__) => { "use strict"; __nested_webpack_require_146307__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_146307__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointDetector": () => (/* binding */ SpeedyPipelineNodeKeypointDetector), /* harmony export */ "SpeedyPipelineNodeMultiscaleKeypointDetector": () => (/* binding */ SpeedyPipelineNodeMultiscaleKeypointDetector) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_146307__(/*! ../../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_146307__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_146307__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_146307__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_146307__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_146307__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_146307__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_146307__(/*! ../../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * detector.js * Abstract keypoint detectors */ // Constants const MAX_CAPACITY = _utils_globals__WEBPACK_IMPORTED_MODULE_7__.MAX_ENCODER_CAPACITY; // maximum capacity of the encoder (up to this many keypoints can be stored) const DEFAULT_CAPACITY = _utils_globals__WEBPACK_IMPORTED_MODULE_7__.DEFAULT_ENCODER_CAPACITY; // default capacity of the encoder const DEFAULT_SCALE_FACTOR = 1.4142135623730951; // sqrt(2) const NUMBER_OF_RGBA16_TEXTURES = 2; // legacy constants const NUMBER_OF_INTERNAL_TEXTURES = 0; //5; // number of internal textures used to encode the keypoints const ENCODER_PASSES = 4; // number of passes of the keypoint encoder: directly impacts performance const LONG_SKIP_OFFSET_PASSES = 2; // number of passes of the long skip offsets shader /** * Abstract keypoint detector * @abstract */ class SpeedyPipelineNodeKeypointDetector extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node * @param {number} [texCount] number of work textures * @param {SpeedyPipelinePortBuilder[]} [portBuilders] port builders */ constructor(name = undefined, texCount = 0, portBuilders = undefined) { super(name, texCount + NUMBER_OF_INTERNAL_TEXTURES, portBuilders); /** @type {number} encoder capacity */ this._capacity = DEFAULT_CAPACITY; // must not be greater than MAX_ENCODER_CAPACITY /** @type {GLint} auxiliary storage */ this._oldWrapS = 0; /** @type {SpeedyDrawableTexture[]} textures with 8-bytes per pixel */ this._tex16 = new Array(NUMBER_OF_RGBA16_TEXTURES).fill(null); } /** * Initialize this node * @param {SpeedyGPU} gpu */ init(gpu) { // initialize super.init(gpu); // encodeKeypointSkipOffsets() relies on this this._oldWrapS = this._setupSpecialTexture(gpu.gl.TEXTURE_WRAP_S, gpu.gl.REPEAT); // allocate RGBA16 textures this._allocateTex16(gpu); gpu.subscribe(this._allocateTex16, this, gpu); } /** * Release this node * @param {SpeedyGPU} gpu */ release(gpu) { // deallocate RGBA16 textures gpu.unsubscribe(this._allocateTex16, this); this._deallocateTex16(gpu); // we need to restore the texture parameter because textures come from a pool! this._setupSpecialTexture(gpu.gl.TEXTURE_WRAP_S, this._oldWrapS); // release super.release(gpu); } /** * Set a parameter of the special texture * @param {GLenum} pname * @param {GLint} param new value * @returns {GLint} old value of param */ _setupSpecialTexture(pname, param) { if(NUMBER_OF_INTERNAL_TEXTURES == 0) return; // legacy code const texture = this._tex[this._tex.length - 1]; const gl = texture.gl; gl.bindTexture(gl.TEXTURE_2D, texture.glTexture); const oldval = gl.getTexParameter(gl.TEXTURE_2D, pname); gl.texParameteri(gl.TEXTURE_2D, pname, param); gl.bindTexture(gl.TEXTURE_2D, null); return oldval; } /** * We can encode up to this many keypoints. If you find a * tight bound for this, download times will be faster. * @returns {number} */ get capacity() { return this._capacity; } /** * We can encode up to this many keypoints. If you find a * tight bound for this, download times will be faster. * @param {number} capacity */ set capacity(capacity) { this._capacity = Math.min(Math.max(0, capacity | 0), MAX_CAPACITY); } /** * Create a tiny texture with encoded keypoints out of * an encoded corners texture * @param {SpeedyGPU} gpu * @param {SpeedyTexture} corners input * @param {SpeedyDrawableTexture} encodedKeypoints output * @param {number} [descriptorSize] in bytes * @param {number} [extraSize] in bytes * @returns {SpeedyDrawableTexture} encodedKeypoints */ _encodeKeypoints(gpu, corners, encodedKeypoints, descriptorSize = 0, extraSize = 0) { const encoderCapacity = this._capacity; const encoderLength = SpeedyPipelineNodeKeypointDetector.encoderLength(encoderCapacity, descriptorSize, extraSize); const width = 1 << (Math.ceil(Math.log2(corners.width * corners.height)) >>> 1); // power of two const height = Math.ceil(corners.width * corners.height / width); // probabilistic approach in Parallel Ale Sort 2D //const width = corners.width, height = corners.height; // independent texture reads approach in Parallel Ale Sort 2D const maxSize = Math.max(width, height); const keypoints = gpu.programs.keypoints; // prepare programs keypoints.initLookupTable.outputs(width, height, this._tex16[1]); keypoints.sortLookupTable.outputs(width, height, this._tex16[0], this._tex16[1]); keypoints.encodeKeypoints.outputs(encoderLength, encoderLength, encodedKeypoints); // compute lookup table let lookupTable = keypoints.initLookupTable(corners); for(let b = 1; b < maxSize; b *= 2) lookupTable = keypoints.sortLookupTable(lookupTable, b, width, height); /* // debug: view texture const lookupView = (keypoints.viewLookupTable.outputs( width, height, this._tex[0] ))(lookupTable); const canvas = gpu.renderToCanvas(lookupView); if(!this._ww) document.body.appendChild(canvas); this._ww = 1; */ // encode keypoints return keypoints.encodeKeypoints(corners, lookupTable, width, descriptorSize, extraSize, encoderLength, encoderCapacity); } _encodeKeypointsOLD(gpu, corners, encodedKeypoints, descriptorSize = 0, extraSize = 0) { const capacity = this._capacity; const encoderLength = SpeedyPipelineNodeKeypointDetector.encoderLength(capacity, descriptorSize, extraSize); const width = corners.width, height = corners.height; const imageSize = [ width, height ]; const tex = this._tex.slice(this._tex.length - NUMBER_OF_INTERNAL_TEXTURES); // array of internal textures const keypoints = gpu.programs.keypoints; const specialTexture = tex.pop(); // gl.TEXTURE_WRAP_S is set to gl.REPEAT // prepare programs keypoints.encodeKeypointSkipOffsets.outputs(width, height, tex[0]); keypoints.encodeKeypointLongSkipOffsets.outputs(width, height, tex[1], tex[0]); keypoints.encodeKeypointPositions.outputs(encoderLength, encoderLength, tex[2], tex[3]); keypoints.encodeKeypointProperties.outputs(encoderLength, encoderLength, encodedKeypoints); // copy the input corners to a special texture // that is needed by encodeKeypointSkipOffsets() corners = (gpu.programs.utils.copy .outputs(width, height, specialTexture) )(corners); // encode skip offsets let offsets = keypoints.encodeKeypointSkipOffsets(corners, imageSize); for(let i = 0; i < LONG_SKIP_OFFSET_PASSES; i++) { // to boost performance // the maximum skip offset of pass p=1,2,3... is 7 * (1+m)^p, // where m = MAX_ITERATIONS of encodeKeypointLongSkipOffsets() offsets = keypoints.encodeKeypointLongSkipOffsets(offsets, imageSize); // **bottleneck** } /* // debug: view corners let cornerview = offsets; const canvas = gpu.renderToCanvas(cornerview); if(!window._ww) document.body.appendChild(canvas); window._ww = 1; */ // encode keypoint positions let encodedKps = tex[3].clear(); for(let j = 0; j < ENCODER_PASSES; j++) encodedKps = keypoints.encodeKeypointPositions(offsets, imageSize, j, ENCODER_PASSES, capacity, encodedKps, descriptorSize, extraSize, encoderLength); // encode keypoint properties return keypoints.encodeKeypointProperties(corners, encodedKps, descriptorSize, extraSize, encoderLength); } /** * Create a tiny texture with zero encoded keypoints * @param {SpeedyGPU} gpu * @param {SpeedyDrawableTexture} encodedKeypoints output texture * @param {number} [descriptorSize] in bytes * @param {number} [extraSize] in bytes * @returns {SpeedyDrawableTexture} encodedKeypoints */ _encodeZeroKeypoints(gpu, encodedKeypoints, descriptorSize = 0, extraSize = 0) { const capacity = 0; const encoderLength = SpeedyPipelineNodeKeypointDetector.encoderLength(capacity, descriptorSize, extraSize); const keypoints = gpu.programs.keypoints; keypoints.encodeNullKeypoints.outputs(encoderLength, encoderLength, encodedKeypoints); return keypoints.encodeNullKeypoints(); } /** * Allocate RGBA16 textures * @param {SpeedyGPU} gpu */ _allocateTex16(gpu) { const gl = gpu.gl; // RGBA16UI is color renderable according to the OpenGL ES 3 spec for(let i = 0; i < this._tex16.length; i++) this._tex16[i] = new _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__.SpeedyDrawableTexture(gl, 1, 1, gl.RGBA_INTEGER, gl.RGBA16UI, gl.UNSIGNED_SHORT, gl.NEAREST, gl.CLAMP_TO_EDGE); } /** * Deallocate RGBA16 textures * @param {SpeedyGPU} gpu */ _deallocateTex16(gpu) { for(let i = 0; i < this._tex16.length; i++) this._tex16[i] = this._tex16[i].release(); } /** * Compute the length of the keypoint encoder, given its capacity * @param {number} encoderCapacity how many keypoints can we fit? * @param {number} descriptorSize in bytes * @param {number} extraSize in bytes */ static encoderLength(encoderCapacity, descriptorSize, extraSize) { const pixelsPerKeypoint = Math.ceil((_utils_globals__WEBPACK_IMPORTED_MODULE_7__.MIN_KEYPOINT_SIZE + descriptorSize + extraSize) / 4); const numberOfPixels = encoderCapacity * pixelsPerKeypoint; return Math.max(_utils_globals__WEBPACK_IMPORTED_MODULE_7__.MIN_ENCODER_LENGTH, Math.ceil(Math.sqrt(numberOfPixels))); } /** * The maximum number of keypoints we can store using * a particular configuration of a keypoint encoder * @param {number} descriptorSize in bytes * @param {number} extraSize in bytes * @param {number} encoderLength */ static encoderCapacity(descriptorSize, extraSize, encoderLength) { const pixelsPerKeypoint = Math.ceil((_utils_globals__WEBPACK_IMPORTED_MODULE_7__.MIN_KEYPOINT_SIZE + descriptorSize + extraSize) / 4); const numberOfPixels = encoderLength * encoderLength; return Math.floor(numberOfPixels / pixelsPerKeypoint); } } /** * Abstract scale-space keypoint detector * @abstract */ class SpeedyPipelineNodeMultiscaleKeypointDetector extends SpeedyPipelineNodeKeypointDetector { /** * Constructor * @param {string} [name] name of the node * @param {number} [texCount] number of work textures * @param {SpeedyPipelinePortBuilder[]} [portBuilders] port builders */ constructor(name = undefined, texCount = undefined, portBuilders = undefined) { super(name, texCount, portBuilders); /** @type {number} number of pyramid levels */ this._levels = 1; /** @type {number} scale factor between two pyramid levels */ this._scaleFactor = DEFAULT_SCALE_FACTOR; } /** * Number of pyramid levels * @returns {number} */ get levels() { return this._levels; } /** * Number of pyramid levels * @param {number} levels */ set levels(levels) { this._levels = Math.max(1, levels | 0); } /** * Scale factor between two pyramid levels * @returns {number} */ get scaleFactor() { return this._scaleFactor; } /** * Scale factor between two pyramid levels * @param {number} scaleFactor should be greater than 1 */ set scaleFactor(scaleFactor) { this._scaleFactor = Math.max(1.0, Math.min(+scaleFactor, 2.0)); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/detectors/fast.js": /*!*************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/detectors/fast.js ***! \*************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_161364__) => { "use strict"; __nested_webpack_require_161364__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_161364__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeFASTKeypointDetector": () => (/* binding */ SpeedyPipelineNodeFASTKeypointDetector) /* harmony export */ }); /* harmony import */ var _detector__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_161364__(/*! ./detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_161364__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_161364__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_161364__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_161364__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_161364__(/*! ../../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_161364__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_161364__(/*! ../../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_161364__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_161364__(/*! ../../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * fast.js * FAST corner detector */ // Constants const DEFAULT_THRESHOLD = 20; /** * FAST corner detector */ class SpeedyPipelineNodeFASTKeypointDetector extends _detector__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNodeMultiscaleKeypointDetector { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 5, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_5__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), ]); /** @type {number} FAST threshold in [0,255] */ this._threshold = DEFAULT_THRESHOLD; } /** * FAST threshold in [0,255] * @returns {number} */ get threshold() { return this._threshold; } /** * FAST threshold in [0,255] * @param {number} threshold */ set threshold(threshold) { this._threshold = Math.max(0, Math.min(threshold | 0, 255)); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const tex = this._tex; const capacity = this._capacity; const threshold = this._threshold; const lodStep = Math.log2(this.scaleFactor); const levels = this.levels; // validate pyramid if(!(levels == 1 || image.hasMipmaps())) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Expected a pyramid in ${this.fullName}`); // skip if the capacity is zero if(capacity == 0) { const encodedKeypoints = this._encodeZeroKeypoints(gpu, tex[4]); const encoderLength = encodedKeypoints.width; this.output().swrite(encodedKeypoints, 0, 0, encoderLength); return; } // FAST gpu.programs.keypoints.fast9_16.outputs(width, height, tex[0], tex[1]); gpu.programs.keypoints.nonmaxSpace.outputs(width, height, tex[2]); let corners = tex[1].clear(); let numPasses = Math.max(1, Math.min(levels, (_utils_globals__WEBPACK_IMPORTED_MODULE_9__.PYRAMID_MAX_LEVELS / lodStep) | 0)); for(let lod = lodStep * (numPasses - 1); numPasses-- > 0; lod -= lodStep) { corners = gpu.programs.keypoints.fast9_16(corners, image, lod, threshold); //corners = gpu.programs.keypoints.nonmaxSpace(corners); // see below* } // Same-scale non-maximum suppression // *nicer results inside the loop; faster outside // Hard to notice a difference when using FAST corners = gpu.programs.keypoints.nonmaxSpace(corners); // Multi-scale non-maximum suppression // (doesn't seem to remove many keypoints) if(levels > 1) { corners = (gpu.programs.keypoints.nonmaxScaleSimple .outputs(width, height, tex[1]) )(corners, image, lodStep); } // encode keypoints let encodedKeypoints = this._encodeKeypoints(gpu, corners, tex[3]); const encoderLength = encodedKeypoints.width; // scale refinement if(levels > 1) { encodedKeypoints = (gpu.programs.keypoints.refineScaleFAST916 .outputs(encoderLength, encoderLength, tex[4]) )(image, lodStep, encodedKeypoints, 0, 0, encoderLength, threshold); } // done! this.output().swrite(encodedKeypoints, 0, 0, encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/detectors/harris.js": /*!***************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/detectors/harris.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_168397__) => { "use strict"; __nested_webpack_require_168397__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_168397__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeHarrisKeypointDetector": () => (/* binding */ SpeedyPipelineNodeHarrisKeypointDetector) /* harmony export */ }); /* harmony import */ var _detector__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_168397__(/*! ./detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_168397__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_168397__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_168397__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_168397__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_168397__(/*! ../../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_168397__(/*! ../../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_168397__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_168397__(/*! ../../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_168397__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_168397__(/*! ../../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * harris.js * Harris corner detector */ /** Window size helper */ const HARRIS = Object.freeze({ 1: 'harris1', 3: 'harris3', 5: 'harris5', 7: 'harris7', }); /** * Harris corner detector */ class SpeedyPipelineNodeHarrisKeypointDetector extends _detector__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNodeMultiscaleKeypointDetector { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 6, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_5__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), ]); /** @type {SpeedySize} neighborhood size */ this._windowSize = new _speedy_size__WEBPACK_IMPORTED_MODULE_6__.SpeedySize(3, 3); /** @type {number} min corner quality in [0,1] */ this._quality = 0.1; } /** * Minimum corner quality in [0,1] - this is a fraction of * the largest min. eigenvalue of the autocorrelation matrix * over the entire image * @returns {number} */ get quality() { return this._quality; } /** * Minimum corner quality in [0,1] * @param {number} quality */ set quality(quality) { this._quality = Math.max(0.0, Math.min(+quality, 1.0)); } /** * Neighborhood size * @returns {SpeedySize} */ get windowSize() { return this._windowSize; } /** * Neighborhood size * @param {SpeedySize} windowSize */ set windowSize(windowSize) { const d = windowSize.width; if(!((d == windowSize.height) && (d == 1 || d == 3 || d == 5 || d == 7))) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalArgumentError(`Invalid window: ${windowSize}. Acceptable sizes: 1x1, 3x3, 5x5, 7x7`); this._windowSize = windowSize; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const capacity = this._capacity; const quality = this._quality; const windowSize = this._windowSize.width; const levels = this.levels; const lodStep = Math.log2(this.scaleFactor); const intFactor = levels > 1 ? this.scaleFactor : 1; const harris = gpu.programs.keypoints[HARRIS[windowSize]]; const tex = this._tex; // validate pyramid if(!(levels == 1 || image.hasMipmaps())) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalOperationError(`Expected a pyramid in ${this.fullName}`); // skip if the capacity is zero if(capacity == 0) { const encodedKeypoints = this._encodeZeroKeypoints(gpu, tex[5]); const encoderLength = encodedKeypoints.width; this.output().swrite(encodedKeypoints, 0, 0, encoderLength); return; } // compute corner response map harris.outputs(width, height, tex[0], tex[1]); gpu.programs.utils.sobelDerivatives.outputs(width, height, tex[2]); gpu.programs.keypoints.nonmaxSpace.outputs(width, height, tex[3]); let corners = tex[1].clear(); let numPasses = Math.max(1, Math.min(levels, (_utils_globals__WEBPACK_IMPORTED_MODULE_10__.PYRAMID_MAX_LEVELS / lodStep) | 0)); for(let lod = lodStep * (numPasses - 1); numPasses-- > 0; lod -= lodStep) { const gaussian = _utils_utils__WEBPACK_IMPORTED_MODULE_7__.Utils.gaussianKernel(intFactor * (1 + lod), windowSize); const derivatives = gpu.programs.utils.sobelDerivatives(image, lod); corners = harris(corners, image, derivatives, lod, lodStep, gaussian); corners = gpu.programs.keypoints.nonmaxSpace(corners); // see below* } // Same-scale non-maximum suppression // *performs better inside the loop //corners = gpu.programs.keypoints.nonmaxSpace(corners); // Multi-scale non-maximum suppression // (doesn't seem to remove many keypoints) if(levels > 1) { const laplacian = (gpu.programs.keypoints.laplacian .outputs(width, height, tex[0]) )(corners, image, lodStep, 0); corners = (gpu.programs.keypoints.nonmaxScale .outputs(width, height, tex[2]) )(corners, image, laplacian, lodStep); } // find the maximum corner response over the entire image gpu.programs.keypoints.harrisScoreFindMax.outputs(width, height, tex[0], tex[1]); numPasses = Math.ceil(Math.log2(Math.max(width, height))); let maxScore = corners; for(let j = 0; j < numPasses; j++) maxScore = gpu.programs.keypoints.harrisScoreFindMax(maxScore, j); // discard corners below a quality level corners = (gpu.programs.keypoints.harrisScoreCutoff .outputs(width, height, maxScore == tex[0] ? tex[1] : tex[0]) )(corners, maxScore, quality); // encode keypoints let encodedKeypoints = this._encodeKeypoints(gpu, corners, tex[4]); const encoderLength = encodedKeypoints.width; // scale refinement if(levels > 1) { encodedKeypoints = (gpu.programs.keypoints.refineScaleLoG .outputs(encoderLength, encoderLength, tex[5]) )(image, lodStep, encodedKeypoints, 0, 0, encoderLength); } // done! this.output().swrite(encodedKeypoints, 0, 0, encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/distance-filter.js": /*!**************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/distance-filter.js ***! \**************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_177632__) => { "use strict"; __nested_webpack_require_177632__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_177632__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointDistanceFilter": () => (/* binding */ SpeedyPipelineNodeKeypointDistanceFilter) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_177632__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_177632__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_177632__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_177632__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_177632__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_177632__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_177632__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _speedy_matrix__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_177632__(/*! ../../../speedy-matrix */ "./src/core/speedy-matrix.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_177632__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_177632__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * distance-filter.js * Given a set of pairs of keypoints, discard all pairs whose distance is * above a user-defined threshold. Useful for bidirectional optical-flow. */ /** * Given a set of pairs of keypoints, discard all pairs whose distance is * above a user-defined threshold. Useful for bidirectional optical-flow. * * The pairs of keypoints are provided as two separate sets, "in" and * "reference". Keypoints that are kept will have their data extracted * from the "in" set. */ class SpeedyPipelineNodeKeypointDistanceFilter extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('in').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('reference').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {number} maximum accepted distance */ this._threshold = _utils_globals__WEBPACK_IMPORTED_MODULE_9__.MAX_TEXTURE_LENGTH + 1; } /** * Maximum accepted distance * @returns {number} */ get threshold() { return this._threshold; } /** * Maximum accepted distance * @param {number} value */ set threshold(value) { this._threshold = Math.max(0, +value); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const set0 = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('in').read() ); const set1 = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('reference').read() ); const threshold = this._threshold; // validate shapes if(set0.descriptorSize != set1.descriptorSize || set0.extraSize != set1.extraSize) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalOperationError(`The distance filter requires two compatible shapes of keypoint streams`); // calculate the shape of the output const outputTexture = this._tex[0]; const encoderLength = Math.max(set0.encoderLength, set1.encoderLength); const descriptorSize = set0.descriptorSize; const extraSize = set0.extraSize; // apply the distance filter (gpu.programs.keypoints.distanceFilter .outputs(encoderLength, encoderLength, outputTexture) )(set0.encodedKeypoints, set0.encoderLength, set1.encodedKeypoints, set1.encoderLength, descriptorSize, extraSize, encoderLength, threshold); // done! this.output().swrite(outputTexture, descriptorSize, extraSize, encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/hamming-distance-filter.js": /*!**********************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/hamming-distance-filter.js ***! \**********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_183722__) => { "use strict"; __nested_webpack_require_183722__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_183722__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointHammingDistanceFilter": () => (/* binding */ SpeedyPipelineNodeKeypointHammingDistanceFilter) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_183722__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_183722__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_183722__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_183722__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_183722__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_183722__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_183722__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _speedy_matrix__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_183722__(/*! ../../../speedy-matrix */ "./src/core/speedy-matrix.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_183722__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_183722__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * hamming-distance-filter.js * Given a set of pairs of keypoints, discard all pairs whose hamming * distance (of descriptor) is above a user-defined threshold */ /** @type {Object} Program names */ const PROGRAM_NAME = { 32: 'hammingDistanceFilter32', 64: 'hammingDistanceFilter64', }; /** * Given a set of pairs of keypoints, discard all pairs whose hamming * distance (of descriptor) is above a user-defined threshold * * The pairs of keypoints are provided as two separate sets, "in" and * "reference". Keypoints that are kept will have their data extracted * from the "in" set. */ class SpeedyPipelineNodeKeypointHammingDistanceFilter extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('in').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints).satisfying( ( /** @type {SpeedyPipelineMessageWithKeypoints} */ msg ) => msg.descriptorSize > 0 ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('reference').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints).satisfying( ( /** @type {SpeedyPipelineMessageWithKeypoints} */ msg ) => msg.descriptorSize > 0 ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {number} distance threshold, an integer */ this._threshold = _utils_globals__WEBPACK_IMPORTED_MODULE_9__.MAX_DESCRIPTOR_SIZE * 8; // convert from bytes to bits } /** * Distance threshold, an integer * @returns {number} */ get threshold() { return this._threshold; } /** * Distance threshold, an integer * @param {number} value */ set threshold(value) { this._threshold = Math.max(0, value | 0); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const set0 = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('in').read() ); const set1 = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('reference').read() ); const threshold = this._threshold; // validate shapes if(set0.descriptorSize != set1.descriptorSize || set0.extraSize != set1.extraSize) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalOperationError(`The Hamming distance filter requires two compatible shapes of keypoint streams`); // validate descriptor size if(!Object.prototype.hasOwnProperty.call(PROGRAM_NAME, set0.descriptorSize)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.NotSupportedError(`Hamming distance filter - invalid descriptor size: ${set0.descriptorSize}`); // calculate the shape of the output const outputTexture = this._tex[0]; const encoderLength = Math.max(set0.encoderLength, set1.encoderLength); const descriptorSize = set0.descriptorSize; const extraSize = set0.extraSize; // apply the distance filter const program = PROGRAM_NAME[set0.descriptorSize]; (gpu.programs.keypoints[program] .outputs(encoderLength, encoderLength, outputTexture) )(set0.encodedKeypoints, set0.encoderLength, set1.encodedKeypoints, set1.encoderLength, descriptorSize, extraSize, encoderLength, threshold); // done! this.output().swrite(outputTexture, descriptorSize, extraSize, encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/matchers/bf-knn.js": /*!**************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/matchers/bf-knn.js ***! \**************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_190610__) => { "use strict"; __nested_webpack_require_190610__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_190610__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeBruteForceKNNKeypointMatcher": () => (/* binding */ SpeedyPipelineNodeBruteForceKNNKeypointMatcher) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_190610__(/*! ../../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_190610__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_190610__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_190610__(/*! ../detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_190610__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_190610__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_190610__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_190610__(/*! ../../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_190610__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * bf-knn.js * Brute Force KNN Keypoint Matcher */ /** @type {Object} program name indexed by descriptor size */ const PROGRAM_NAME = { 32: 'bfMatcher32', 64: 'bfMatcher64', }; /** * Brute Force KNN Keypoint Matcher. Make sure to use a Keypoint Clipper before * invoking this (use a database of 50 keypoints or so - your mileage may vary) */ class SpeedyPipelineNodeBruteForceKNNKeypointMatcher extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 6, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('keypoints').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints).satisfying( ( /** @type {SpeedyPipelineMessageWithKeypoints} */ msg ) => msg.descriptorSize > 0 ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('database').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints).satisfying( ( /** @type {SpeedyPipelineMessageWithKeypoints} */ msg ) => msg.descriptorSize > 0 ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.KeypointMatches), ]); /** @type {number} number of matches per keypoint (the "k" of knn) */ this._matchesPerKeypoint = 1; } /** * Number of matches per keypoint * @returns {number} */ get k() { return this._matchesPerKeypoint; } /** * Number of matches per keypoint * @param {number} value */ set k(value) { this._matchesPerKeypoint = Math.max(1, value | 0); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('keypoints').read() ); const database = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('database').read() ); const candidatesA = this._tex[0]; const candidatesB = this._tex[1]; const candidatesC = this._tex[2]; const encodedFiltersA = this._tex[3]; const encodedMatchesA = this._tex[4]; const encodedMatchesB = this._tex[5]; const matchesPerKeypoint = this._matchesPerKeypoint; const keypoints = gpu.programs.keypoints; // validate parameters if(descriptorSize !== database.descriptorSize) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Incompatible descriptors in ${this.fullName}`); else if(!Object.prototype.hasOwnProperty.call(PROGRAM_NAME, descriptorSize)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.NotSupportedError(`Unsupported descriptor size (${descriptorSize}) in ${this.fullName}`); // prepare the brute force matching const bfMatcher = keypoints[PROGRAM_NAME[descriptorSize]]; const capacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); const dbCapacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(database.descriptorSize, database.extraSize, database.encoderLength); const numberOfKeypointsPerPass = bfMatcher.definedConstant('NUMBER_OF_KEYPOINTS_PER_PASS'); const numberOfPasses = Math.ceil(dbCapacity / numberOfKeypointsPerPass); const partialMatcherLength = Math.max(1, Math.ceil(Math.sqrt(capacity))); const matcherLength = Math.max(1, Math.ceil(Math.sqrt(capacity * matchesPerKeypoint))); keypoints.bfMatcherTransfer.outputs(matcherLength, matcherLength, encodedMatchesA, encodedMatchesB); keypoints.bfMatcherInitCandidates.outputs(partialMatcherLength, partialMatcherLength, candidatesC); keypoints.bfMatcherInitFilters.outputs(partialMatcherLength, partialMatcherLength, encodedFiltersA); bfMatcher.outputs(partialMatcherLength, partialMatcherLength, candidatesA, candidatesB); // match keypoints let encodedMatches = encodedMatchesB.clear(); // will hold all best matches let encodedFilters = keypoints.bfMatcherInitFilters(); for(let k = 0; k < matchesPerKeypoint; k++) { let encodedPartialMatches = keypoints.bfMatcherInitCandidates(); // hold the (k+1)-th best matches // find the (k+1)-th best match for(let passId = 0; passId < numberOfPasses; passId++) { encodedPartialMatches = bfMatcher( encodedPartialMatches, encodedFilters, partialMatcherLength, database.encodedKeypoints, database.descriptorSize, database.extraSize, database.encoderLength, encodedKeypoints, descriptorSize, extraSize, encoderLength, passId ); gpu.gl.flush(); } //gpu.gl.flush(); // copy the (k+1)-th best match to the filter if(matchesPerKeypoint > 1) encodedPartialMatches.copyTo(encodedFilters); // aggregate matches encodedMatches = keypoints.bfMatcherTransfer( encodedMatches, encodedPartialMatches, matchesPerKeypoint, k ); } // done! this.output().swrite(encodedMatches, matchesPerKeypoint); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/matchers/lsh-knn.js": /*!***************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/matchers/lsh-knn.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_199268__) => { "use strict"; __nested_webpack_require_199268__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_199268__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeLSHKNNMatcher": () => (/* binding */ SpeedyPipelineNodeLSHKNNMatcher) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_199268__(/*! ../../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_199268__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_199268__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_199268__(/*! ../detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_199268__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_199268__(/*! ../../../../../gpu/speedy-lsh */ "./src/gpu/speedy-lsh.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_199268__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_199268__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_199268__(/*! ../../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_199268__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * lsh-knn.js * K approximate nearest neighbors matcher */ /** @typedef {'fastest' | 'default' | 'demanding'} LSHKNNQualityLevel quality of the approximate matching */ /** @type {number} how many neighbors to search for, by default */ const DEFAULT_K = 1; /** @type {LSHKNNQualityLevel} default quality level */ const DEFAULT_QUALITY = 'default'; /** @type {{ [key in LSHKNNQualityLevel]: number }} maps quality level to bit swaps */ const NUMBER_OF_BIT_SWAPS = { 'fastest': 0, 'default': 1, 'demanding': 2, }; /** @type {object} program names indexed as LSH_KNN[descriptorSize][hashSize][level] */ const LSH_KNN = (fd => _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_5__.LSH_ACCEPTABLE_DESCRIPTOR_SIZES.reduce((o,d) => ((o[d] = fd(d)), o), {}))( d => ((fh => _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_5__.LSH_ACCEPTABLE_HASH_SIZES.reduce((o,h) => ((o[h] = fh(h)), o), {}))( h => ((fl => [0,1,2].reduce((o,l) => ((o[l] = fl(l)), o), {}))( l => `lshKnn${d}h${h}lv${l}` )) )) ); /** * K approximate nearest neighbors matcher */ class SpeedyPipelineNodeLSHKNNMatcher extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 6, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('keypoints').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints).satisfying( ( /** @type {SpeedyPipelineMessageWithKeypoints} */ msg ) => msg.descriptorSize > 0 ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)('lsh').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.LSHTables), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.KeypointMatches), ]); /** @type {number} how many neighbors do you want? */ this._k = DEFAULT_K; /** @type {LSHKNNQualityLevel} quality of the matching */ this._quality = DEFAULT_QUALITY; } /** * How many neighbors do you want? * @returns {number} */ get k() { return this._k; } /** * How many neighbors do you want? * @param {number} k number of neighbors */ set k(k) { this._k = Math.max(1, k | 0); } /** * Quality of the matching * @returns {LSHKNNQualityLevel} */ get quality() { return this._quality; } /** * Quality of the matching * @param {LSHKNNQualityLevel} quality */ set quality(quality) { if(!Object.prototype.hasOwnProperty.call(NUMBER_OF_BIT_SWAPS, quality)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalArgumentError(`Invalid quality level: "${quality}"`); this._quality = quality; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('keypoints').read() ); /** @type {SpeedyLSH} */ const lsh = this.input('lsh').read().lsh; const keypoints = gpu.programs.keypoints; const tables = lsh.tables; const descriptorDB = lsh.descriptorDB; const tablesStride = tables.width; const descriptorDBStride = descriptorDB.width; const tableCount = lsh.tableCount; const hashSize = lsh.hashSize; const bucketCapacity = lsh.bucketCapacity; const bucketsPerTable = lsh.bucketsPerTable; const sequences = lsh.sequences; const candidatesA = this._tex[0]; const candidatesB = this._tex[1]; const candidatesC = this._tex[2]; const filters = this._tex[3]; const transferA = this._tex[4]; const transferB = this._tex[5]; const level = NUMBER_OF_BIT_SWAPS[this._quality]; const matchesPerKeypoint = this._k; // validate parameters if(descriptorSize !== lsh.descriptorSize) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalArgumentError(`Can't match different types of descriptors in ${this.fullName}`); _utils_utils__WEBPACK_IMPORTED_MODULE_7__.Utils.assert(LSH_KNN[descriptorSize] != undefined); _utils_utils__WEBPACK_IMPORTED_MODULE_7__.Utils.assert(LSH_KNN[descriptorSize][hashSize] != undefined); _utils_utils__WEBPACK_IMPORTED_MODULE_7__.Utils.assert(LSH_KNN[descriptorSize][hashSize][level] != undefined); // configure the output texture const capacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); const matcherLength = Math.max(1, Math.ceil(Math.sqrt(capacity * matchesPerKeypoint))); let encodedMatches = transferB; keypoints.lshKnnTransfer.outputs(matcherLength, matcherLength, transferA, transferB); // prepare the LSH matching const kthMatcherLength = Math.max(1, Math.ceil(Math.sqrt(capacity))); keypoints.lshKnnInitCandidates.outputs(kthMatcherLength, kthMatcherLength, candidatesA); keypoints.lshKnnInitFilters.outputs(kthMatcherLength, kthMatcherLength, filters); const lshKnn = keypoints[LSH_KNN[descriptorSize][hashSize][level]]; lshKnn.outputs(kthMatcherLength, kthMatcherLength, candidatesB, candidatesC); lshKnn.setUBO('LSHSequences', sequences); // match keypoints encodedMatches.clear(); keypoints.lshKnnInitFilters(); for(let i = 0; i < matchesPerKeypoint; i++) { // find the (i+1)-th best match let candidates = keypoints.lshKnnInitCandidates(); for(let tableIndex = 0; tableIndex < tableCount; tableIndex++) { candidates = lshKnn(candidates, filters, kthMatcherLength, tables, descriptorDB, tableIndex, bucketCapacity, bucketsPerTable, tablesStride, descriptorDBStride, encodedKeypoints, descriptorSize, extraSize, encoderLength); gpu.gl.flush(); } candidates.copyTo(filters); // transfer matches to an encoded matches texture encodedMatches = keypoints.lshKnnTransfer(encodedMatches, candidates, matchesPerKeypoint, i); } // done this.output().swrite(encodedMatches, matchesPerKeypoint); /* // debug let data = this._inspect32(filters), debug = []; for(let i = 0; i < data.length; i++) { const bits = MATCH_INDEX_BITS; const mask = (1 << bits) - 1; const u32 = data[i]; const index = u32 & mask, distance = u32 >>> bits; //debug.push('|'+[ u32 ].toString()); debug.push('|'+[ index, distance ].toString()); } console.log(debug.join(',')); */ } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/matchers/lsh-static-tables.js": /*!*************************************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/matchers/lsh-static-tables.js ***! \*************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_209466__) => { "use strict"; __nested_webpack_require_209466__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_209466__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeStaticLSHTables": () => (/* binding */ SpeedyPipelineNodeStaticLSHTables) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_209466__(/*! ../../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_209466__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_209466__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _speedy_keypoint__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_209466__(/*! ../../../../speedy-keypoint */ "./src/core/speedy-keypoint.js"); /* harmony import */ var _speedy_keypoint_descriptor__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_209466__(/*! ../../../../speedy-keypoint-descriptor */ "./src/core/speedy-keypoint-descriptor.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_209466__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_209466__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_209466__(/*! ../../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_209466__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_209466__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_209466__(/*! ../../../../../gpu/speedy-lsh */ "./src/gpu/speedy-lsh.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * lsh-static-tables.js * Static LSH tables */ /** * Static LSH tables */ class SpeedyPipelineNodeStaticLSHTables extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSourceNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.LSHTables) ]); /** @type {SpeedyKeypoint[]} "training" keypoints */ this._keypoints = []; /** @type {SpeedyKeypoint[]} internal copy of the "training" keypoints */ this._keypointsCopy = []; /** @type {number} number of tables in the LSH data structure */ this._numberOfTables = _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__.LSH_DEFAULT_NUMBER_OF_TABLES; /** @type {number} number of bits of a hash */ this._hashSize = _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__.LSH_DEFAULT_HASH_SIZE; /** @type {SpeedyLSH|null} LSH data structure */ this._lsh = null; } /** * "Training" keypoints * @returns {SpeedyKeypoint[]} */ get keypoints() { return this._keypoints; } /** * "Training" keypoints * @param {SpeedyKeypoint[]} keypoints */ set keypoints(keypoints) { if(!Array.isArray(keypoints) || keypoints.find(keypoint => !(keypoint instanceof _speedy_keypoint__WEBPACK_IMPORTED_MODULE_3__.SpeedyKeypoint))) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Static LSH tables: an invalid set of keypoints has been provided`); if(this._keypoints !== keypoints) { this._keypoints = keypoints; // update internal pointer this._keypointsCopy = keypoints.slice(0); // clone the array, so it won't be modified externally this._lsh = null; // (re)train the model } } /** * Number of tables in the LSH data structure * @returns {number} */ get numberOfTables() { return this._numberOfTables; } /** * Number of tables in the LSH data structure * @param {number} n */ set numberOfTables(n) { if(!_gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__.LSH_ACCEPTABLE_NUMBER_OF_TABLES.includes(n)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Invalid number of tables: ${n}. Acceptable values: ${_gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__.LSH_ACCEPTABLE_NUMBER_OF_TABLES.join(', ')}`); if(n !== this._numberOfTables) { this._numberOfTables = n | 0; this._lsh = null; // need to retrain the model } } /** * Number of bits of a hash * @returns {number} */ get hashSize() { return this._hashSize; } /** * Number of bits of a hash * @param {number} h */ set hashSize(h) { if(!_gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__.LSH_ACCEPTABLE_HASH_SIZES.includes(h)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Invalid hash size: ${h}. Acceptable values: ${_gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__.LSH_ACCEPTABLE_HASH_SIZES.join(', ')}`); if(h !== this._hashSize) { this._hashSize = h | 0; this._lsh = null; // need to retrain the model } } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { // Need to train the model? if(this._lsh == null) { // internal work textures are only available after initialization, // i.e., after calling this._init() this._lsh = this._train(); } // Pass it forward this.output().swrite(this._lsh); } /** * Train the model * @returns {SpeedyLSH} */ _train() { const keypoints = this._keypointsCopy; const numberOfTables = this._numberOfTables; const hashSize = this._hashSize; if(keypoints.find(keypoint => keypoint.descriptor == null)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Static LSH tables: can't train the model with no keypoint descriptors!`); const descriptors = keypoints.map(keypoint => keypoint.descriptor.data); const lshTables = this._tex[0]; const descriptorDB = this._tex[1]; return new _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_10__.SpeedyLSH(lshTables, descriptorDB, descriptors, numberOfTables, hashSize); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/mixer.js": /*!****************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/mixer.js ***! \****************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_217333__) => { "use strict"; __nested_webpack_require_217333__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_217333__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointMixer": () => (/* binding */ SpeedyPipelineNodeKeypointMixer) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_217333__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_217333__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_217333__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_217333__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_217333__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_217333__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_217333__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_217333__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_217333__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_217333__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * mixer.js * Keypoint Mixer */ /** * Keypoint Mixer: merges two sets of keypoints */ class SpeedyPipelineNodeKeypointMixer extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 5, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('in0').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('in1').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints) ]); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const kps0 = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('in0').read() ); const kps1 = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('in1').read() ); const descriptorSize = kps0.descriptorSize; const extraSize = kps0.extraSize; const keypoints = gpu.programs.keypoints; const tex = this._tex; // ensure that the format of kps0 equals the format of kps1 if(!(kps0.descriptorSize === kps1.descriptorSize && kps0.extraSize === kps0.extraSize)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Can't merge two sets of keypoints that have different formats`); // find the capacity of kps0 + kps1 const cap0 = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(kps0.descriptorSize, kps0.extraSize, kps0.encoderLength); const cap1 = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(kps1.descriptorSize, kps1.extraSize, kps1.encoderLength); const capacity = cap0 + cap1; // find the dimensions of the output texture const encoderLength = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderLength(capacity, descriptorSize, extraSize); const mixEncoderLength = Math.max(1, Math.ceil(Math.sqrt(capacity))); // prepare programs keypoints.mixKeypointsPreInit.outputs(encoderLength, encoderLength, tex[0]); keypoints.mixKeypointsInit.outputs(mixEncoderLength, mixEncoderLength, tex[1]); keypoints.mixKeypointsSort.outputs(mixEncoderLength, mixEncoderLength, tex[2], tex[3]); keypoints.mixKeypointsApply.outputs(encoderLength, encoderLength, tex[4]); // mix keypoints let mixedKeypoints = keypoints.mixKeypointsPreInit( kps0.encodedKeypoints, kps1.encodedKeypoints, kps0.encoderLength, kps1.encoderLength, cap0, cap1, descriptorSize, extraSize, encoderLength ); let sortedKeypoints = keypoints.mixKeypointsInit( mixedKeypoints, descriptorSize, extraSize, encoderLength, capacity ); for(let b = 1; b < capacity; b *= 2) sortedKeypoints = keypoints.mixKeypointsSort(sortedKeypoints, b); mixedKeypoints = keypoints.mixKeypointsApply( sortedKeypoints, mixedKeypoints, descriptorSize, extraSize, encoderLength ); /* // debug: view keypoints keypoints.mixKeypointsView.outputs(mixEncoderLength, mixEncoderLength, tex[1]); this._visualize(gpu, keypoints.mixKeypointsView(sortedKeypoints)); */ this.output().swrite(mixedKeypoints, descriptorSize, extraSize, encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/multiplexer.js": /*!**********************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/multiplexer.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_224170__) => { "use strict"; __nested_webpack_require_224170__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_224170__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointMultiplexer": () => (/* binding */ SpeedyPipelineNodeKeypointMultiplexer) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_224170__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_224170__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_224170__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_224170__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_224170__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_media__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_224170__(/*! ../../../speedy-media */ "./src/core/speedy-media.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_224170__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_224170__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_224170__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * multiplexer.js * Keypoint multiplexer */ /** @type {string[]} the names of the input ports indexed by their number */ const INPUT_PORT = [ 'in0', 'in1' ]; /** * Keypoint multiplexer */ class SpeedyPipelineNodeKeypointMultiplexer extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 0, [ ...(INPUT_PORT.map(portName => (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)(portName).expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints))), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), ]); /** @type {number} which port should be linked to the output? */ this._port = 0; } /** * The number of the port that should be linked to the output * @returns {number} */ get port() { return this._port; } /** * The number of the port that should be linked to the output * @param {number} port */ set port(port) { if(port < 0 || port >= INPUT_PORT.length) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Invalid port: ${port}`); this._port = port | 0; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const message = this.input(INPUT_PORT[this._port]).read(); this.output().write(message); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/portal.js": /*!*****************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/portal.js ***! \*****************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_228626__) => { "use strict"; __nested_webpack_require_228626__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_228626__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointPortalSink": () => (/* binding */ SpeedyPipelineNodeKeypointPortalSink), /* harmony export */ "SpeedyPipelineNodeKeypointPortalSource": () => (/* binding */ SpeedyPipelineNodeKeypointPortalSource) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_228626__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_228626__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_228626__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_228626__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_228626__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_228626__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_228626__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_228626__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_228626__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * portal.js * Keypoint Portals */ /** * A sink of a Keypoint Portal * This is not a pipeline sink - it doesn't export any data! */ class SpeedyPipelineNodeKeypointPortalSink extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), ]); /** @type {number} descriptor size, in bytes */ this._descriptorSize = 0; /** @type {number} extra size, in bytes */ this._extraSize = 0; /** @type {number} extra size */ this._encoderLength = 0; /** @type {boolean} is this node initialized? */ this._initialized = false; } /** * Encoded keypoints * @returns {SpeedyTexture} */ get encodedKeypoints() { if(!this._initialized) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Portal error: ${this.fullName} holds no data`); return this._tex[0]; } /** * Descriptor size, in bytes * @returns {number} */ get descriptorSize() { if(!this._initialized) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Portal error: ${this.fullName} holds no data`); return this._descriptorSize; } /** * Extra size, in bytes * @returns {number} */ get extraSize() { if(!this._initialized) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Portal error: ${this.fullName} holds no data`); return this._extraSize; } /** * Encoder length * @returns {number} */ get encoderLength() { if(!this._initialized) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`Portal error: ${this.fullName} holds no data`); return this._encoderLength; } /** * Initializes this node * @param {SpeedyGPU} gpu */ init(gpu) { super.init(gpu); const encoderLength = _detectors_detector__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineNodeKeypointDetector.encoderLength(0, 0, 0); this._tex[0].resize(encoderLength, encoderLength).clearToColor(1,1,1,1); // initial texture this._descriptorSize = this._extraSize = 0; this._encoderLength = encoderLength; this._initialized = true; } /** * Releases this node * @param {SpeedyGPU} gpu */ release(gpu) { this._initialized = false; super.release(gpu); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const tex = this._tex[0]; // copy input tex.resize(encodedKeypoints.width, encodedKeypoints.height); encodedKeypoints.copyTo(tex); this._descriptorSize = descriptorSize; this._extraSize = extraSize; this._encoderLength = encoderLength; } } /** * A source of a Keypoint Portal */ class SpeedyPipelineNodeKeypointPortalSource extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSourceNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 0, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), ]); /** @type {SpeedyPipelineNodeKeypointPortalSink|null} portal sink */ this._source = null; } /** * Data source * @returns {SpeedyPipelineNodeKeypointPortalSink|null} */ get source() { return this._source; } /** * Data source * @param {SpeedyPipelineNodeKeypointPortalSink|null} node */ set source(node) { if(node !== null && !(node instanceof SpeedyPipelineNodeKeypointPortalSink)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Incompatible source for ${this.fullName}`); this._source = node; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { if(this._source == null) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalOperationError(`${this.fullName} has no source`); this.output().swrite(this._source.encodedKeypoints, this._source.descriptorSize, this._source.extraSize, this._source.encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/shuffler.js": /*!*******************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/shuffler.js ***! \*******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_236567__) => { "use strict"; __nested_webpack_require_236567__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_236567__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointShuffler": () => (/* binding */ SpeedyPipelineNodeKeypointShuffler) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_236567__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_236567__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_236567__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_236567__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_236567__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_236567__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_236567__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * shuffler.js * Keypoint Shuffler */ /** * The Keypoint Shuffler shuffles a list of keypoints */ class SpeedyPipelineNodeKeypointShuffler extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 6, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {number} maximum number of keypoints */ this._maxKeypoints = Number.NaN; } /** * Maximum number of keypoints (optional) * @returns {number} */ get maxKeypoints() { return this._maxKeypoints; } /** * Maximum number of keypoints (optional) * @param {number} value */ set maxKeypoints(value) { if(!Number.isNaN(value)) this._maxKeypoints = Math.max(0, value | 0); else this._maxKeypoints = Number.NaN; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { let { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const capacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); const maxKeypoints = this._maxKeypoints; // shuffle the keypoints (including nulls) const permutationMaxLength = gpu.programs.keypoints.shuffle.definedConstant('PERMUTATION_MAXLEN'); const permutationLength = Math.min(permutationMaxLength, capacity); const permutation = this._generatePermutation(permutationLength, permutationMaxLength); encodedKeypoints = (gpu.programs.keypoints.shuffle .setUBO('Permutation', permutation) .outputs(encoderLength, encoderLength, this._tex[0]) )(encodedKeypoints, descriptorSize, extraSize, encoderLength); // sort the keypoints gpu.programs.keypoints.mixKeypointsInit.outputs(encoderLength, encoderLength, this._tex[1]); gpu.programs.keypoints.mixKeypointsSort.outputs(encoderLength, encoderLength, this._tex[2], this._tex[3]); gpu.programs.keypoints.mixKeypointsApply.outputs(encoderLength, encoderLength, this._tex[4]); let sortedKeypoints = gpu.programs.keypoints.mixKeypointsInit( encodedKeypoints, descriptorSize, extraSize, encoderLength, capacity ); for(let b = 1; b < capacity; b *= 2) sortedKeypoints = gpu.programs.keypoints.mixKeypointsSort(sortedKeypoints, b); encodedKeypoints = gpu.programs.keypoints.mixKeypointsApply( sortedKeypoints, encodedKeypoints, descriptorSize, extraSize, encoderLength ); // clip the output? if(!Number.isNaN(maxKeypoints) && maxKeypoints < capacity) { const newEncoderLength = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderLength(maxKeypoints, descriptorSize, extraSize); encodedKeypoints = (gpu.programs.keypoints.clip .outputs(newEncoderLength, newEncoderLength, this._tex[5]) )(encodedKeypoints, descriptorSize, extraSize, encoderLength, maxKeypoints); encoderLength = newEncoderLength; } // done! this.output().swrite(encodedKeypoints, descriptorSize, extraSize, encoderLength); } /** * Generate a permutation p of { 0, 1, ..., n-1 } such that p(p(x)) = x for all x * @param {number} n positive integer * @param {number} [bufsize] size of the output array * @returns {Int32Array} permutation */ _generatePermutation(n, bufsize = n) { const array = new Int32Array(bufsize); const p = array.subarray(0, n).fill(-1); const q = _utils_utils__WEBPACK_IMPORTED_MODULE_5__.Utils.shuffle(_utils_utils__WEBPACK_IMPORTED_MODULE_5__.Utils.range(n)); for(let i = 0, j = 0; i < n; i++) { if(p[i] < 0) { do { p[i] = q[j++]; } while(p[i] < i); p[p[i]] = i; } } return array; // padded with zeros } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/sink.js": /*!***************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/sink.js ***! \***************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_243606__) => { "use strict"; __nested_webpack_require_243606__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_243606__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointSink": () => (/* binding */ SpeedyPipelineNodeKeypointSink), /* harmony export */ "SpeedyPipelineNodeTrackedKeypointSink": () => (/* binding */ SpeedyPipelineNodeTrackedKeypointSink), /* harmony export */ "SpeedyPipelineNodeMatchedKeypointSink": () => (/* binding */ SpeedyPipelineNodeMatchedKeypointSink) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_243606__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_243606__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_243606__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_243606__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_243606__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture_reader__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_243606__(/*! ../../../../gpu/speedy-texture-reader */ "./src/gpu/speedy-texture-reader.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_243606__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _speedy_media__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_243606__(/*! ../../../speedy-media */ "./src/core/speedy-media.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_243606__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_243606__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_243606__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_11__ = __nested_webpack_require_243606__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _speedy_keypoint__WEBPACK_IMPORTED_MODULE_12__ = __nested_webpack_require_243606__(/*! ../../../speedy-keypoint */ "./src/core/speedy-keypoint.js"); /* harmony import */ var _speedy_keypoint_descriptor__WEBPACK_IMPORTED_MODULE_13__ = __nested_webpack_require_243606__(/*! ../../../speedy-keypoint-descriptor */ "./src/core/speedy-keypoint-descriptor.js"); /* harmony import */ var _speedy_keypoint_match__WEBPACK_IMPORTED_MODULE_14__ = __nested_webpack_require_243606__(/*! ../../../speedy-keypoint-match */ "./src/core/speedy-keypoint-match.js"); /* harmony import */ var _speedy_vector__WEBPACK_IMPORTED_MODULE_15__ = __nested_webpack_require_243606__(/*! ../../../speedy-vector */ "./src/core/speedy-vector.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_16__ = __nested_webpack_require_243606__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * sink.js * Gets keypoints out of the pipeline */ /** next power of 2 */ const nextPot = x => x > 1 ? 1 << Math.ceil(Math.log2(x)) : 1; /** empty array of bytes */ const ZERO_BYTES = new Uint8Array([]); /** * Gets keypoints out of the pipeline * @template {SpeedyKeypoint} T * @abstract */ class SpeedyPipelineNodeAbstractKeypointSink extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSinkNode { /** * Constructor * @param {string} [name] name of the node * @param {number} [texCount] * @param {SpeedyPipelinePortBuilder[]} [portBuilders] */ constructor(name = 'keypoints', texCount = 0, portBuilders = []) { super(name, texCount + 2, portBuilders); /** @type {Array} keypoints (output) */ this._keypoints = []; /** @type {SpeedyTextureReader} texture reader */ this._textureReader = new _gpu_speedy_texture_reader__WEBPACK_IMPORTED_MODULE_5__.SpeedyTextureReader(); /** @type {number} page flipping index */ this._page = 0; /** @type {boolean} accelerate GPU-CPU transfers */ this._turbo = false; /** @type {boolean} should discarded keypoints be exported as null or dropped altogether? */ this._includeDiscarded = false; } /** * Accelerate GPU-CPU transfers * @returns {boolean} */ get turbo() { return this._turbo; } /** * Accelerate GPU-CPU transfers * @param {boolean} value */ set turbo(value) { this._turbo = Boolean(value); } /** * Should discarded keypoints be exported as null or dropped altogether? * @returns {boolean} */ get includeDiscarded() { return this._includeDiscarded; } /** * Should discarded keypoints be exported as null or dropped altogether? * @param {boolean} value */ set includeDiscarded(value) { this._includeDiscarded = Boolean(value); } /** * Initializes this node * @param {SpeedyGPU} gpu */ init(gpu) { super.init(gpu); this._textureReader.init(gpu); } /** * Releases this node * @param {SpeedyGPU} gpu */ release(gpu) { this._textureReader.release(gpu); super.release(gpu); } /** * Export data from this node to the user * @returns {SpeedyPromise>} */ export() { return _speedy_promise__WEBPACK_IMPORTED_MODULE_11__.SpeedyPromise.resolve(this._keypoints); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); return this._download(gpu, encodedKeypoints, descriptorSize, extraSize, encoderLength); } /** * Download and decode keypoints from the GPU * @param {SpeedyGPU} gpu * @param {SpeedyDrawableTexture} encodedKeypoints * @param {number} descriptorSize * @param {number} extraSize * @param {number} encoderLength * @returns {SpeedyPromise} */ _download(gpu, encodedKeypoints, descriptorSize, extraSize, encoderLength) { const useBufferedDownloads = this._turbo; /* I have found experimentally that, in Firefox, readPixelsAsync() performs MUCH better if the width of the target texture is a power of two. I have no idea why this is the case, nor if it's related to some interaction with the GL drivers, somehow. This seems to make no difference on Chrome, however. In any case, let's convert the input texture to POT. */ const encoderWidth = nextPot(encoderLength); //const encoderHeight = nextPot(Math.ceil(encoderLength * encoderLength / encoderWidth)); const encoderHeight = Math.ceil(encoderLength * encoderLength / encoderWidth); //const encoderWidth=encoderLength,encoderHeight=encoderLength; // copy the set of keypoints to an internal texture const copiedTexture = this._tex[(this._tex.length - 1) - this._page]; (gpu.programs.utils.copyKeypoints .outputs(encoderWidth, encoderHeight, copiedTexture) )(encodedKeypoints); // flip page this._page = 1 - this._page; // download the internal texture return this._textureReader.readPixelsAsync(copiedTexture, 0, 0, copiedTexture.width, copiedTexture.height, useBufferedDownloads).then(pixels => { // decode the keypoints and store them in this._keypoints this._keypoints = this._decode(pixels, descriptorSize, extraSize, encoderWidth, encoderHeight); }); } /** * Decode a sequence of keypoints, given a flattened image of encoded pixels * @param {Uint8Array} pixels pixels in the [r,g,b,a,...] format * @param {number} descriptorSize in bytes * @param {number} extraSize in bytes * @param {number} encoderWidth * @param {number} encoderHeight * @returns {Array} keypoints */ _decode(pixels, descriptorSize, extraSize, encoderWidth, encoderHeight) { const bytesPerKeypoint = _utils_globals__WEBPACK_IMPORTED_MODULE_16__.MIN_KEYPOINT_SIZE + descriptorSize + extraSize; const m = _utils_globals__WEBPACK_IMPORTED_MODULE_16__.LOG2_PYRAMID_MAX_SCALE, h = _utils_globals__WEBPACK_IMPORTED_MODULE_16__.PYRAMID_MAX_LEVELS; const piOver255 = Math.PI / 255.0; const keypoints = /** @type {Array} */ ( [] ); const includeDiscarded = this._includeDiscarded; let descriptorBytes = ZERO_BYTES, extraBytes = ZERO_BYTES; let x, y, z, w, lod, rotation, score; let keypoint; // validate if(descriptorSize % 4 != 0 || extraSize % 4 != 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_10__.IllegalArgumentError(`Invalid descriptorSize (${descriptorSize}) / extraSize (${extraSize})`); // how many bytes should we read? const e2 = encoderWidth * encoderHeight * 4; const size = pixels.byteLength; if(size != e2) _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.warning(`Expected ${e2} bytes when decoding a set of keypoints, found ${size}`); // copy the data (we use shared buffers when receiving pixels[]) if(descriptorSize + extraSize > 0) pixels = new Uint8Array(pixels); // for each encoded keypoint for(let i = 0; i < size; i += bytesPerKeypoint) { // extract encoded header x = (pixels[i+1] << 8) | pixels[i]; y = (pixels[i+3] << 8) | pixels[i+2]; z = (pixels[i+5] << 8) | pixels[i+4]; w = (pixels[i+7] << 8) | pixels[i+6]; // the keypoint is "null": we have reached the end of the list if(x == 0xFFFF && y == 0xFFFF) break; // the header is zero: discard the keypoint if(x + y + z + w == 0) { if(includeDiscarded) keypoints.push(null); continue; } // extract extra & descriptor bytes if(extraSize > 0) { extraBytes = pixels.subarray(8 + i, 8 + i + extraSize); if(extraBytes.byteLength < extraSize) { _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.warning(`KeypointSink: expected ${extraSize} extra bytes when decoding the ${i/bytesPerKeypoint}-th keypoint, found ${extraBytes.byteLength} instead`); continue; // something is off here; discard } } if(descriptorSize > 0) { descriptorBytes = pixels.subarray(8 + i + extraSize, 8 + i + extraSize + descriptorSize); if(descriptorBytes.byteLength < descriptorSize) { _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.warning(`KeypointSink: expected ${descriptorSize} descriptor bytes when decoding the ${i/bytesPerKeypoint}-th keypoint, found ${descriptorBytes.byteLength} instead`); continue; // something is off here; discard } } // decode position: convert from fixed-point x /= _utils_globals__WEBPACK_IMPORTED_MODULE_16__.FIX_RESOLUTION; y /= _utils_globals__WEBPACK_IMPORTED_MODULE_16__.FIX_RESOLUTION; // decode level-of-detail lod = (pixels[i+4] < 255) ? -m + ((m + h) * pixels[i+4]) / 255.0 : 0.0; // decode orientation rotation = (2 * pixels[i+5] - 255) * piOver255; // decode score score = _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.decodeFloat16(w); // create keypoint keypoint = this._createKeypoint(x, y, lod, rotation, score, descriptorBytes, extraBytes); // register keypoint keypoints.push(keypoint); } // done! return keypoints; } /** * Instantiate a new keypoint * @param {number} x * @param {number} y * @param {number} lod * @param {number} rotation * @param {number} score * @param {Uint8Array} descriptorBytes * @param {Uint8Array} extraBytes * @returns {T} */ _createKeypoint(x, y, lod, rotation, score, descriptorBytes, extraBytes) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_10__.AbstractMethodError(); } /** * Allocate extra soace * @param {SpeedyGPU} gpu * @param {SpeedyDrawableTexture} output output texture * @param {SpeedyTexture} inputEncodedKeypoints input with no extra space * @param {number} inputDescriptorSize in bytes, must be positive * @param {number} inputExtraSize must be 0 * @param {number} outputDescriptorSize must be inputDescriptorSize * @param {number} outputExtraSize in bytes, must be positive and a multiple of 4 * @returns {SpeedyDrawableTexture} encodedKeypoints with extra space */ _allocateExtra(gpu, output, inputEncodedKeypoints, inputDescriptorSize, inputExtraSize, outputDescriptorSize, outputExtraSize) { _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.assert(inputExtraSize === 0); _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.assert(outputDescriptorSize === inputDescriptorSize && outputExtraSize > 0 && outputExtraSize % 4 === 0); const inputEncoderLength = inputEncodedKeypoints.width; const inputEncoderCapacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(inputDescriptorSize, inputExtraSize, inputEncoderLength); const outputEncoderCapacity = inputEncoderCapacity; const outputEncoderLength = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderLength(outputEncoderCapacity, outputDescriptorSize, outputExtraSize); return (gpu.programs.keypoints.allocateExtra .outputs(outputEncoderLength, outputEncoderLength, output) )(inputEncodedKeypoints, inputDescriptorSize, inputExtraSize, inputEncoderLength, outputDescriptorSize, outputExtraSize, outputEncoderLength); } } /** * Gets standard keypoints out of the pipeline * @extends {SpeedyPipelineNodeAbstractKeypointSink} */ class SpeedyPipelineNodeKeypointSink extends SpeedyPipelineNodeAbstractKeypointSink { /** * Constructor * @param {string} [name] name of the node */ constructor(name = 'keypoints') { super(name, 0, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints) ]); } /** * Instantiate a new keypoint * @param {number} x * @param {number} y * @param {number} lod * @param {number} rotation * @param {number} score * @param {Uint8Array} descriptorBytes * @param {Uint8Array} extraBytes * @returns {SpeedyKeypoint} */ _createKeypoint(x, y, lod, rotation, score, descriptorBytes, extraBytes) { const descriptorSize = descriptorBytes.byteLength; // read descriptor, if any const descriptor = descriptorSize > 0 ? new _speedy_keypoint_descriptor__WEBPACK_IMPORTED_MODULE_13__.SpeedyKeypointDescriptor(descriptorBytes) : null; // create keypoint return new _speedy_keypoint__WEBPACK_IMPORTED_MODULE_12__.SpeedyKeypoint(x, y, lod, rotation, score, descriptor); } } /** * Gets tracked keypoints out of the pipeline * @extends {SpeedyPipelineNodeAbstractKeypointSink} */ class SpeedyPipelineNodeTrackedKeypointSink extends SpeedyPipelineNodeAbstractKeypointSink { /** * Constructor * @param {string} [name] name of the node */ constructor(name = 'keypoints') { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints).satisfying( ( /** @type {SpeedyPipelineMessageWithKeypoints} */ msg ) => msg.extraSize == 0 ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('flow').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Vector2) ]); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const { vectors } = /** @type {SpeedyPipelineMessageWith2DVectors} */ ( this.input('flow').read() ); // allocate extra space const newDescriptorSize = descriptorSize; const newExtraSize = 4; // 1 pixel per flow vector per keypoint const encodedKeypointsWithExtraSpace = this._allocateExtra(gpu, this._tex[0], encodedKeypoints, descriptorSize, extraSize, newDescriptorSize, newExtraSize); // attach flow vectors const newEncoderLength = encodedKeypointsWithExtraSpace.width; const newEncodedKeypoints = (gpu.programs.keypoints.transferToExtra .outputs(newEncoderLength, newEncoderLength, this._tex[1]) )(vectors, vectors.width, encodedKeypointsWithExtraSpace, newDescriptorSize, newExtraSize, newEncoderLength); // done! return this._download(gpu, newEncodedKeypoints, newDescriptorSize, newExtraSize, newEncoderLength); } /** * Instantiate a new keypoint * @param {number} x * @param {number} y * @param {number} lod * @param {number} rotation * @param {number} score * @param {Uint8Array} descriptorBytes * @param {Uint8Array} extraBytes * @returns {SpeedyTrackedKeypoint} */ _createKeypoint(x, y, lod, rotation, score, descriptorBytes, extraBytes) { const descriptorSize = descriptorBytes.byteLength; const extraSize = extraBytes.byteLength; // read descriptor, if any const descriptor = descriptorSize > 0 ? new _speedy_keypoint_descriptor__WEBPACK_IMPORTED_MODULE_13__.SpeedyKeypointDescriptor(descriptorBytes) : null; // read flow vector const fx = _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.decodeFloat16((extraBytes[1] << 8) | extraBytes[0]); const fy = _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.decodeFloat16((extraBytes[3] << 8) | extraBytes[2]); const flow = new _speedy_vector__WEBPACK_IMPORTED_MODULE_15__.SpeedyVector2(fx, fy); // create keypoint return new _speedy_keypoint__WEBPACK_IMPORTED_MODULE_12__.SpeedyTrackedKeypoint(x, y, lod, rotation, score, descriptor, flow); } } /** * Gets matched keypoints out of the pipeline * @extends SpeedyPipelineNodeAbstractKeypointSink */ class SpeedyPipelineNodeMatchedKeypointSink extends SpeedyPipelineNodeAbstractKeypointSink { /** * Constructor * @param {string} [name] name of the node */ constructor(name = 'keypoints') { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints).satisfying( ( /** @type {SpeedyPipelineMessageWithKeypoints} */ msg ) => msg.extraSize == 0 ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('matches').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.KeypointMatches) ]); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const { encodedMatches, matchesPerKeypoint } = /** @type {SpeedyPipelineMessageWithKeypointMatches} */ ( this.input('matches').read() ); // allocate space for the matches const newDescriptorSize = descriptorSize; const newExtraSize = matchesPerKeypoint * 4; // 4 bytes per pixel const encodedKeypointsWithExtraSpace = this._allocateExtra(gpu, this._tex[0], encodedKeypoints, descriptorSize, extraSize, newDescriptorSize, newExtraSize); // transfer matches to a new texture const newEncoderLength = encodedKeypointsWithExtraSpace.width; const newEncodedKeypoints = (gpu.programs.keypoints.transferToExtra .outputs(newEncoderLength, newEncoderLength, this._tex[1]) )(encodedMatches, encodedMatches.width, encodedKeypointsWithExtraSpace, newDescriptorSize, newExtraSize, newEncoderLength); // done! return this._download(gpu, newEncodedKeypoints, newDescriptorSize, newExtraSize, newEncoderLength); } /** * Instantiate a new keypoint * @param {number} x * @param {number} y * @param {number} lod * @param {number} rotation * @param {number} score * @param {Uint8Array} descriptorBytes * @param {Uint8Array} extraBytes * @returns {SpeedyMatchedKeypoint} */ _createKeypoint(x, y, lod, rotation, score, descriptorBytes, extraBytes) { const descriptorSize = descriptorBytes.byteLength; const extraSize = extraBytes.byteLength; // read descriptor, if any const descriptor = descriptorSize > 0 ? new _speedy_keypoint_descriptor__WEBPACK_IMPORTED_MODULE_13__.SpeedyKeypointDescriptor(descriptorBytes) : null; // decode matches const matchesPerKeypoint = extraSize / 4; const matches = /** @type {SpeedyKeypointMatch[]} */ ( new Array(matchesPerKeypoint) ); for(let matchIndex = 0; matchIndex < matchesPerKeypoint; matchIndex++) { const base = matchIndex * 4; const u32 = extraBytes[base] | (extraBytes[base+1] << 8) | (extraBytes[base+2] << 16) | (extraBytes[base+3] << 24); const match = new _speedy_keypoint_match__WEBPACK_IMPORTED_MODULE_14__.SpeedyKeypointMatch(u32 & _utils_globals__WEBPACK_IMPORTED_MODULE_16__.MATCH_INDEX_MASK, u32 >>> _utils_globals__WEBPACK_IMPORTED_MODULE_16__.MATCH_INDEX_BITS); matches[matchIndex] = match; } // done! return new _speedy_keypoint__WEBPACK_IMPORTED_MODULE_12__.SpeedyMatchedKeypoint(x, y, lod, rotation, score, descriptor, matches); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/source.js": /*!*****************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/source.js ***! \*****************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_267803__) => { "use strict"; __nested_webpack_require_267803__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_267803__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointSource": () => (/* binding */ SpeedyPipelineNodeKeypointSource) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_267803__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_267803__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_267803__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_267803__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_267803__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_267803__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_267803__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_267803__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_267803__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _speedy_keypoint__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_267803__(/*! ../../../speedy-keypoint */ "./src/core/speedy-keypoint.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_267803__(/*! ../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * source.js * Gets keypoints into the pipeline */ // Constants const UBO_MAX_BYTES = 16384; // UBOs can hold at least 16KB of data: gl.MAX_UNIFORM_BLOCK_SIZE >= 16384 according to the GL ES 3 reference const BUFFER_SIZE = 1024; // how many keypoints we can upload in one pass of the shader (as defined in the shader program) const SIZEOF_VEC4 = Float32Array.BYTES_PER_ELEMENT * 4; // 16 bytes /** * Gets keypoints into the pipeline */ class SpeedyPipelineNodeKeypointSource extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSourceNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {SpeedyKeypoint[]} keypoints to be uploaded to the GPU */ this._keypoints = []; /** @type {Float32Array} upload buffer (UBO) */ this._buffer = SpeedyPipelineNodeKeypointSource._createUploadBuffer(BUFFER_SIZE); /** @type {number} maximum number of keypoints */ this._capacity = _utils_globals__WEBPACK_IMPORTED_MODULE_10__.DEFAULT_ENCODER_CAPACITY; } /** * Keypoints to be uploaded * @returns {SpeedyKeypoint[]} */ get keypoints() { return this._keypoints; } /** * Keypoints to be uploaded * @param {SpeedyKeypoint[]} keypoints */ set keypoints(keypoints) { if(!Array.isArray(keypoints)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_7__.IllegalArgumentError(`Not an array of keypoints`); this._keypoints = keypoints; } /** * The maximum number of keypoints we'll accept. * This should be a tight bound for better performance. * @returns {number} */ get capacity() { return this._capacity; } /** * The maximum number of keypoints we'll accept. * This should be a tight bound for better performance. * @param {number} capacity */ set capacity(capacity) { this._capacity = Math.min(Math.max(0, capacity | 0), _utils_globals__WEBPACK_IMPORTED_MODULE_10__.MAX_ENCODER_CAPACITY); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { // Orientation, descriptors and extra bytes will be lost const descriptorSize = 0, extraSize = 0; const keypoints = this._keypoints; const maxKeypoints = this._capacity; const numKeypoints = Math.min(keypoints.length, maxKeypoints); const numPasses = Math.max(1, Math.ceil(numKeypoints / BUFFER_SIZE)); const buffer = this._buffer; const uploadKeypoints = gpu.programs.keypoints.uploadKeypoints; const encoderLength = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderLength(maxKeypoints, descriptorSize, extraSize); // we're using maxKeypoints to avoid constant texture resize (slow on Firefox) uploadKeypoints.outputs(encoderLength, encoderLength, this._tex[0], this._tex[1]); let startIndex = 0, encodedKeypoints = uploadKeypoints.clear(); for(let i = 0; i < numPasses; i++) { const n = Math.min(BUFFER_SIZE, numKeypoints - startIndex); const endIndex = startIndex + n; uploadKeypoints.setUBO('KeypointBuffer', SpeedyPipelineNodeKeypointSource._fillUploadBuffer(buffer, keypoints, startIndex, endIndex)); encodedKeypoints = uploadKeypoints(encodedKeypoints, startIndex, endIndex, descriptorSize, extraSize, encoderLength); startIndex = endIndex; } this.output().swrite(encodedKeypoints, descriptorSize, extraSize, encoderLength); } /** * Create an upload buffer * @param {number} bufferSize number of keypoints * @returns {Float32Array} */ static _createUploadBuffer(bufferSize) { const internalBuffer = new ArrayBuffer(SIZEOF_VEC4 * bufferSize); _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.assert(internalBuffer.byteLength <= UBO_MAX_BYTES); return new Float32Array(internalBuffer); } /** * Fill upload buffer with keypoint data * @param {Float32Array} buffer * @param {SpeedyKeypoint[]} keypoints * @param {number} start index, inclusive * @param {number} end index, exclusive * @returns {Float32Array} buffer */ static _fillUploadBuffer(buffer, keypoints, start, end) { const n = end - start; for(let i = 0; i < n; i++) { const keypoint = keypoints[start + i]; const hasPos = keypoint.position !== undefined; const j = i * 4; // Format data as follows: // vec4(xpos, ypos, lod, score) buffer[j] = +(hasPos ? keypoint.position.x : keypoint.x) || 0; buffer[j+1] = +(hasPos ? keypoint.position.y : keypoint.y) || 0; buffer[j+2] = +(keypoint.lod) || 0; buffer[j+3] = +(keypoint.score) || 0; } // done! return buffer; } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/subpixel.js": /*!*******************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/subpixel.js ***! \*******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_276177__) => { "use strict"; __nested_webpack_require_276177__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_276177__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointSubpixelRefiner": () => (/* binding */ SpeedyPipelineNodeKeypointSubpixelRefiner) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_276177__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_276177__(/*! ./detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_276177__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_276177__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_276177__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_276177__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_276177__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_276177__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_276177__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_276177__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * subpixel.js * Subpixel refinement of keypoint location */ /** @typedef {"quadratic1d"|"taylor2d"|"bicubic-upsample"|"bilinear-upsample"} SubpixelRefinementMethod */ /** @const {Object} method name to program name */ const METHOD2PROGRAM = Object.freeze({ 'quadratic1d': 'subpixelQuadratic1d', 'taylor2d': 'subpixelTaylor2d', 'bicubic-upsample': 'subpixelBicubic', 'bilinear-upsample': 'subpixelBilinear', }); /** * Subpixel refinement of keypoint location */ class SpeedyPipelineNodeKeypointSubpixelRefiner extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('image').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_6__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('keypoints').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)('displacements').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Vector2), ]); /** @type {SubpixelRefinementMethod} subpixel refinement method */ this._method = 'quadratic1d'; /** @type {number} max iterations for the upsampling methods */ this._maxIterations = 6; /** @type {number} convergence threshold for the upsampling methods */ this._epsilon = 0.1; } /** * Subpixel refinement method * @returns {SubpixelRefinementMethod} */ get method() { return this._method; } /** * Subpixel refinement method * @param {SubpixelRefinementMethod} name */ set method(name) { if(!Object.prototype.hasOwnProperty.call(METHOD2PROGRAM, name)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalArgumentError(`Invalid method: "${name}"`); this._method = name; } /** * Max. iterations for the upsampling methods * @returns {number} */ get maxIterations() { return this._maxIterations; } /** * Max. iterations for the upsampling methods * @param {number} value */ set maxIterations(value) { this._maxIterations = Math.max(0, +value); } /** * Convergence threshold for the upsampling methods * @returns {number} */ get epsilon() { return this._epsilon; } /** * Convergence threshold for the upsampling methods * @param {number} value */ set epsilon(value) { this._epsilon = Math.max(0, +value); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('keypoints').read() ); const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input('image').read() ); const tex = this._tex; const program = METHOD2PROGRAM[this._method]; const maxIterations = this._maxIterations; const epsilon = this._epsilon; // note: if you detected the keypoints using a pyramid, // you need to pass that pyramid as input! // we'll compute the offsets for each keypoint const capacity = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); const offsetEncoderLength = Math.max(1, Math.ceil(Math.sqrt(capacity))); // 1 pixel per refinement offset const offsets = (gpu.programs.keypoints[program] .outputs(offsetEncoderLength, offsetEncoderLength, tex[0]) )(image, encodedKeypoints, descriptorSize, extraSize, encoderLength, maxIterations, epsilon); // apply the offsets to the keypoints const refinedKeypoints = (gpu.programs.keypoints.transferFlow .outputs(encoderLength, encoderLength, tex[1]) )(offsets, encodedKeypoints, descriptorSize, extraSize, encoderLength); // done! this.output().swrite(refinedKeypoints, descriptorSize, extraSize, encoderLength); this.output('displacements').swrite(offsets); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/trackers/lk.js": /*!**********************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/trackers/lk.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_284074__) => { "use strict"; __nested_webpack_require_284074__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_284074__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeLKKeypointTracker": () => (/* binding */ SpeedyPipelineNodeLKKeypointTracker) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_284074__(/*! ../../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _detectors_detector__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_284074__(/*! ../detectors/detector */ "./src/core/pipeline/nodes/keypoints/detectors/detector.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_284074__(/*! ../../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_284074__(/*! ../../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_284074__(/*! ../../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_284074__(/*! ../../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_284074__(/*! ../../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_284074__(/*! ../../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_284074__(/*! ../../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_284074__(/*! ../../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_284074__(/*! ../../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_11__ = __nested_webpack_require_284074__(/*! ../../../../../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * lk.js * LK optical-flow */ // Constants const DEFAULT_WINDOW_SIZE = new _speedy_size__WEBPACK_IMPORTED_MODULE_7__.SpeedySize(11, 11); // nice on mobile? const DEFAULT_DEPTH = Math.min(3, _utils_globals__WEBPACK_IMPORTED_MODULE_11__.PYRAMID_MAX_LEVELS); const DEFAULT_NUMBER_OF_ITERATIONS = 30; const DEFAULT_DISCARD_THRESHOLD = 0.0001; const DEFAULT_EPSILON = 0.01; const LK_PROGRAM = { 3: 'lk3', 5: 'lk5', 7: 'lk7', 9: 'lk9', 11: 'lk11', 13: 'lk13', 15: 'lk15', 17: 'lk17', 19: 'lk19', 21: 'lk21', }; /** * LK optical-flow */ class SpeedyPipelineNodeLKKeypointTracker extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 3, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('previousImage').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_6__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('nextImage').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Image).satisfying( ( /** @type {SpeedyPipelineMessageWithImage} */ msg ) => msg.format === _utils_types__WEBPACK_IMPORTED_MODULE_6__.ImageFormat.GREY ), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.InputPort)('previousKeypoints').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_3__.OutputPort)('flow').expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelineMessageType.Vector2), ]); /** @type {SpeedySize} window size */ this._windowSize = DEFAULT_WINDOW_SIZE; /** @type {number} number of pyramid levels to use */ this._levels = DEFAULT_DEPTH; /** @type {number} minimum acceptable corner response */ this._discardThreshold = DEFAULT_DISCARD_THRESHOLD; /** @type {number} number of iterations per pyramid level (termination criteria) */ this._numberOfIterations = DEFAULT_NUMBER_OF_ITERATIONS; /** @type {number} minimum increment per iteration (termination criteria) */ this._epsilon = DEFAULT_EPSILON; } /** * Window size (use odd numbers) * @returns {SpeedySize} */ get windowSize() { return this._windowSize; } /** * Window size (use odd numbers) * @param {SpeedySize} windowSize must be a square window */ set windowSize(windowSize) { if(windowSize.width != windowSize.height) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_9__.NotSupportedError(`LK: window ${this._windowSize.toString()} is not square!`); } else if(!Object.prototype.hasOwnProperty.call(LK_PROGRAM, windowSize.width)) { const SUPPORTED_WINDOWS = Object.keys(LK_PROGRAM).sort((a,b) => a-b).map(k => k+'x'+k).join(', '); throw new _utils_errors__WEBPACK_IMPORTED_MODULE_9__.NotSupportedError(`LK: window of size ${this._windowSize.toString()} is not supported! Supported sizes: ${SUPPORTED_WINDOWS}`); } this._windowSize = windowSize; } /** * Number of pyramid levels to use * @returns {number} */ get levels() { return this._levels; } /** * Number of pyramid levels to use * @param {number} levels */ set levels(levels) { _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.assert(levels >= 1 && levels <= _utils_globals__WEBPACK_IMPORTED_MODULE_11__.PYRAMID_MAX_LEVELS); this._levels = levels | 0; } /** * Get the discard threshold, used to discard "bad" keypoints * @returns {number} */ get discardThreshold() { return this._discardThreshold; } /** * Set the discard threshold, used to discard "bad" keypoints * @param {number} value typically 10^(-4) - increase to discard more */ set discardThreshold(value) { _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.assert(value >= 0); this._discardThreshold = +value; } /** * Get the maximum number of iterations of the pyramidal LK algorithm * @returns {number} */ get numberOfIterations() { return this._numberOfIterations; } /** * Set the maximum number of iterations of the pyramidal LK algorithm * @param {number} value */ set numberOfIterations(value) { _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.assert(value >= 1); this._numberOfIterations = value | 0; } /** * Get the accuracy threshold, used to stop LK iterations * @returns {number} */ get epsilon() { return this._epsilon; } /** * Get the accuracy threshold, used to stop LK iterations * @param {number} value typically 0.01 */ set epsilon(value) { _utils_utils__WEBPACK_IMPORTED_MODULE_8__.Utils.assert(value >= 0); this._epsilon = +value; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input('previousKeypoints').read() ); const previousImage = ( /** @type {SpeedyPipelineMessageWithImage} */ ( this.input('previousImage').read() )).image; const nextImage = ( /** @type {SpeedyPipelineMessageWithImage} */ ( this.input('nextImage').read() )).image; const previousKeypoints = encodedKeypoints; const levels = this._levels; const windowSize = this._windowSize; const wsize = windowSize.width; // square window const numberOfIterations = this._numberOfIterations; const discardThreshold = this._discardThreshold; const epsilon = this._epsilon; const keypoints = gpu.programs.keypoints; const tex = this._tex; // do we need a pyramid? if(!(levels == 1 || (previousImage.hasMipmaps() && nextImage.hasMipmaps()))) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_9__.IllegalOperationError(`LK: a pyramid is required if levels > 1`); else if(previousImage.width !== nextImage.width || previousImage.height !== nextImage.height) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_9__.IllegalOperationError(`LK: can't use input images of different size`); // select the appropriate program const lk = keypoints[LK_PROGRAM[wsize]]; // find the dimensions of the flow texture (1 pixel per flow vector) const numKeypoints = _detectors_detector__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineNodeKeypointDetector.encoderCapacity(descriptorSize, extraSize, encoderLength); const lkEncoderLength = Math.max(1, Math.ceil(Math.sqrt(numKeypoints))); lk.outputs(lkEncoderLength, lkEncoderLength, tex[0], tex[1]); // compute optical-flow let flow = lk.clear(); for(let lod = levels - 1; lod >= 0; lod--) flow = lk(flow, previousKeypoints, nextImage, previousImage, lod, levels, numberOfIterations, discardThreshold, epsilon, descriptorSize, extraSize, encoderLength); // transfer optical-flow to nextKeypoints keypoints.transferFlow.outputs(encoderLength, encoderLength, tex[2]); const nextKeypoints = keypoints.transferFlow(flow, previousKeypoints, descriptorSize, extraSize, encoderLength); // done! this.output().swrite(nextKeypoints, descriptorSize, extraSize, encoderLength); this.output('flow').swrite(flow); } } /***/ }), /***/ "./src/core/pipeline/nodes/keypoints/transformer.js": /*!**********************************************************!*\ !*** ./src/core/pipeline/nodes/keypoints/transformer.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_295590__) => { "use strict"; __nested_webpack_require_295590__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_295590__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeKeypointTransformer": () => (/* binding */ SpeedyPipelineNodeKeypointTransformer) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_295590__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_295590__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_295590__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_295590__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_295590__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_295590__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_295590__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _speedy_matrix__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_295590__(/*! ../../../speedy-matrix */ "./src/core/speedy-matrix.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_295590__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * transformer.js * Apply a transformation matrix to a set of keypoints */ /** * Apply a transformation matrix to a set of keypoints */ class SpeedyPipelineNodeKeypointTransformer extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Keypoints) ]); /** @type {SpeedyMatrix} transformation matrix */ this._transform = _speedy_matrix__WEBPACK_IMPORTED_MODULE_7__.SpeedyMatrix.Create(3, 3, [1, 0, 0, 0, 1, 0, 0, 0, 1]); // identity matrix } /** * Transformation matrix * @returns {SpeedyMatrix} */ get transform() { return this._transform; } /** * Transformation matrix. Must be 3x3 * @param {SpeedyMatrix} transform */ set transform(transform) { if(!(transform.rows == 3 && transform.columns == 3)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalArgumentError(`Not a 3x3 transformation matrix: ${transform}`); this._transform = transform; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { encodedKeypoints, descriptorSize, extraSize, encoderLength } = /** @type {SpeedyPipelineMessageWithKeypoints} */ ( this.input().read() ); const outputTexture = this._tex[0]; const homography = this._transform.read(); // apply homography (gpu.programs.keypoints.applyHomography .outputs(encodedKeypoints.width, encodedKeypoints.height, outputTexture) )(homography, encodedKeypoints, descriptorSize, extraSize, encoderLength); // done! this.output().swrite(outputTexture, descriptorSize, extraSize, encoderLength); } } /***/ }), /***/ "./src/core/pipeline/nodes/transforms/perspective-warp.js": /*!****************************************************************!*\ !*** ./src/core/pipeline/nodes/transforms/perspective-warp.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_300615__) => { "use strict"; __nested_webpack_require_300615__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_300615__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodePerspectiveWarp": () => (/* binding */ SpeedyPipelineNodePerspectiveWarp) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_300615__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_300615__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_300615__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_300615__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_300615__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_300615__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_300615__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_300615__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_300615__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _speedy_matrix__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_300615__(/*! ../../../speedy-matrix */ "./src/core/speedy-matrix.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * perspective-warp.js * Warp an image using a perspective transformation */ // Used when an invalid matrix is provided const SINGULAR_MATRIX = [0,0,0,0,0,0,0,0,1]; /** * Warp an image using a perspective transformation */ class SpeedyPipelineNodePerspectiveWarp extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {SpeedyMatrix} perspective transformation */ this._transform = _speedy_matrix__WEBPACK_IMPORTED_MODULE_9__.SpeedyMatrix.Create(3, 3, [1, 0, 0, 0, 1, 0, 0, 0, 1]); // identity matrix } /** * Perspective transform, a 3x3 homography matrix * @returns {SpeedyMatrix} */ get transform() { return this._transform; } /** * Perspective transform, a 3x3 homography matrix * @param {SpeedyMatrix} transform */ set transform(transform) { if(!(transform.rows == 3 && transform.columns == 3)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_8__.IllegalArgumentError(`Not a 3x3 transformation matrix: ${transform}`); this._transform = transform; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const outputTexture = this._tex[0]; const homography = this._transform.read(); const inverseHomography = this._inverse3(homography); const isValidHomography = !Number.isNaN(inverseHomography[0]); gpu.programs.transforms.warpPerspective.outputs(width, height, outputTexture); gpu.programs.transforms.warpPerspective(image, isValidHomography ? inverseHomography : SINGULAR_MATRIX); this.output().swrite(outputTexture, format); } /** * Compute the inverse of a 3x3 matrix IN-PLACE (do it fast!) * @param {number[]} mat 3x3 matrix in column-major format * @param {number} [eps] epsilon * @returns {number[]} 3x3 inverse matrix in column-major format */ _inverse3(mat, eps = 1e-6) { // read the entries of the matrix const a11 = mat[0]; const a21 = mat[1]; const a31 = mat[2]; const a12 = mat[3]; const a22 = mat[4]; const a32 = mat[5]; const a13 = mat[6]; const a23 = mat[7]; const a33 = mat[8]; // compute cofactors const b1 = a33 * a22 - a32 * a23; // b11 const b2 = a33 * a12 - a32 * a13; // b21 const b3 = a23 * a12 - a22 * a13; // b31 // compute the determinant const det = a11 * b1 - a21 * b2 + a31 * b3; // set up the inverse if(!(Math.abs(det) < eps)) { const d = 1.0 / det; mat[0] = b1 * d; mat[1] = -(a33 * a21 - a31 * a23) * d; mat[2] = (a32 * a21 - a31 * a22) * d; mat[3] = -b2 * d; mat[4] = (a33 * a11 - a31 * a13) * d; mat[5] = -(a32 * a11 - a31 * a12) * d; mat[6] = b3 * d; mat[7] = -(a23 * a11 - a21 * a13) * d; mat[8] = (a22 * a11 - a21 * a12) * d; } else mat.fill(Number.NaN, 0, 9); // done! return mat; } } /***/ }), /***/ "./src/core/pipeline/nodes/transforms/resize.js": /*!******************************************************!*\ !*** ./src/core/pipeline/nodes/transforms/resize.js ***! \******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_307350__) => { "use strict"; __nested_webpack_require_307350__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_307350__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeResize": () => (/* binding */ SpeedyPipelineNodeResize) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_307350__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_307350__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_307350__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_307350__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_307350__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_307350__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_307350__(/*! ../../../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_307350__(/*! ../../../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _speedy_size__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_307350__(/*! ../../../speedy-size */ "./src/core/speedy-size.js"); /* harmony import */ var _speedy_vector__WEBPACK_IMPORTED_MODULE_9__ = __nested_webpack_require_307350__(/*! ../../../speedy-vector */ "./src/core/speedy-vector.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_10__ = __nested_webpack_require_307350__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * resize.js * Resize image */ /** @typedef {"bilinear"|"nearest"} SpeedyPipelineNodeResizeMethod */ /** * Resize image */ class SpeedyPipelineNodeResize extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = undefined) { super(name, 1, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.OutputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Image), ]); /** @type {SpeedySize} size of the output image, in pixels */ this._size = new _speedy_size__WEBPACK_IMPORTED_MODULE_8__.SpeedySize(0, 0); /** @type {SpeedyVector2} size of the output relative to the size of the input */ this._scale = new _speedy_vector__WEBPACK_IMPORTED_MODULE_9__.SpeedyVector2(1, 1); /** @type {SpeedyPipelineNodeResizeMethod} interpolation method */ this._method = 'bilinear'; } /** * Size of the output image, in pixels (use 0 to use scale) * @returns {SpeedySize} */ get size() { return this._size; } /** * Size of the output image, in pixels (use 0 to use scale) * @param {SpeedySize} size */ set size(size) { this._size = size; } /** * Size of the output image relative to the size of the input image * @returns {SpeedyVector2} */ get scale() { return this._scale; } /** * Size of the output image relative to the size of the input image * @param {SpeedyVector2} scale */ set scale(scale) { this._scale = scale; } /** * Interpolation method * @returns {SpeedyPipelineNodeResizeMethod} */ get method() { return this._method; } /** * Interpolation method * @param {SpeedyPipelineNodeResizeMethod} method */ set method(method) { if(method !== 'nearest' && method !== 'bilinear') throw new _utils_errors__WEBPACK_IMPORTED_MODULE_6__.IllegalArgumentError(`Invalid method method: "${method}"`); this._method = method; } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { image, format } = /** @type {SpeedyPipelineMessageWithImage} */ ( this.input().read() ); const width = image.width, height = image.height; const outputTexture = this._tex[0]; const method = this._method; const newWidth = this._size.width || Math.max(1, this._scale.x * width); const newHeight = this._size.height || Math.max(1, this._scale.y * height); if(method == 'bilinear') { (gpu.programs.transforms.resizeBilinear .outputs(newWidth, newHeight, outputTexture) )(image); } else if(method == 'nearest') { (gpu.programs.transforms.resizeNearest .outputs(newWidth, newHeight, outputTexture) )(image); } this.output().swrite(outputTexture, format); } } /***/ }), /***/ "./src/core/pipeline/nodes/vector2/sink.js": /*!*************************************************!*\ !*** ./src/core/pipeline/nodes/vector2/sink.js ***! \*************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_313709__) => { "use strict"; __nested_webpack_require_313709__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_313709__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNodeVector2Sink": () => (/* binding */ SpeedyPipelineNodeVector2Sink) /* harmony export */ }); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_313709__(/*! ../../pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_313709__(/*! ../../pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_313709__(/*! ../../pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_313709__(/*! ../../../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture_reader__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_313709__(/*! ../../../../gpu/speedy-texture-reader */ "./src/gpu/speedy-texture-reader.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_313709__(/*! ../../../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_313709__(/*! ../../../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_313709__(/*! ../../../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _speedy_vector__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_313709__(/*! ../../../speedy-vector */ "./src/core/speedy-vector.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * sink.js * Gets keypoints out of the pipeline */ // next power of 2 const nextPot = x => x > 1 ? 1 << Math.ceil(Math.log2(x)) : 1; /** * Gets 2D vectors out of the pipeline */ class SpeedyPipelineNodeVector2Sink extends _pipeline_node__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineSinkNode { /** * Constructor * @param {string} [name] name of the node */ constructor(name = 'vec2') { super(name, 2, [ (0,_pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_2__.InputPort)().expects(_pipeline_message__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineMessageType.Vector2) ]); /** @type {SpeedyVector2[]} 2D vectors (output) */ this._vectors = []; /** @type {SpeedyTextureReader} texture reader */ this._textureReader = new _gpu_speedy_texture_reader__WEBPACK_IMPORTED_MODULE_4__.SpeedyTextureReader(); /** @type {number} page flipping index */ this._page = 0; /** @type {boolean} accelerate GPU-CPU transfers */ this._turbo = false; } /** * Accelerate GPU-CPU transfers * @returns {boolean} */ get turbo() { return this._turbo; } /** * Accelerate GPU-CPU transfers * @param {boolean} value */ set turbo(value) { this._turbo = Boolean(value); } /** * Initializes this node * @param {SpeedyGPU} gpu */ init(gpu) { super.init(gpu); this._textureReader.init(gpu); } /** * Releases this node * @param {SpeedyGPU} gpu */ release(gpu) { this._textureReader.release(gpu); super.release(gpu); } /** * Export data from this node to the user * @returns {SpeedyPromise} */ export() { return _speedy_promise__WEBPACK_IMPORTED_MODULE_7__.SpeedyPromise.resolve(this._vectors); } /** * Run the specific task of this node * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { const { vectors } = /** @type {SpeedyPipelineMessageWith2DVectors} */ ( this.input().read() ); const useBufferedDownloads = this._turbo; const encoderLength = vectors.width; /* I have found experimentally that, in Firefox, readPixelsAsync() performs MUCH better if the width of the target texture is a power of two. I have no idea why this is the case, nor if it's related to some interaction with the GL drivers, somehow. This seems to make no difference on Chrome, however. In any case, let's convert the input texture to POT. */ const encoderWidth = nextPot(encoderLength); const encoderHeight = nextPot(Math.ceil(encoderLength * encoderLength / encoderWidth)); //const encoderHeight = (Math.ceil(encoderLength * encoderLength / encoderWidth)); // copy the set of vectors to an internal texture const copiedTexture = this._tex[this._page]; (gpu.programs.utils.copy2DVectors .outputs(encoderWidth, encoderHeight, copiedTexture) )(vectors); // flip page this._page = 1 - this._page; // download the internal texture return this._textureReader.readPixelsAsync(copiedTexture, 0, 0, copiedTexture.width, copiedTexture.height, useBufferedDownloads).then(pixels => { this._vectors = SpeedyPipelineNodeVector2Sink._decode(pixels, encoderWidth, encoderHeight); }); } /** * Decode a sequence of vectors, given a flattened image of encoded pixels * @param {Uint8Array} pixels pixels in the [r,g,b,a,...] format * @param {number} encoderWidth * @param {number} encoderHeight * @returns {SpeedyVector2[]} vectors */ static _decode(pixels, encoderWidth, encoderHeight) { const bytesPerVector = 4; // 1 pixel per vector const vectors = []; let hi = 0, lo = 0; let x = 0, y = 0; // how many bytes should we read? const e2 = encoderWidth * encoderHeight * bytesPerVector; const size = Math.min(pixels.length, e2); // for each encoded vector for(let i = 0; i < size; i += bytesPerVector) { // extract 16-bit words lo = (pixels[i+1] << 8) | pixels[i]; hi = (pixels[i+3] << 8) | pixels[i+2]; // the vector is "null": we have reached the end of the list if(lo == 0xFFFF && hi == 0xFFFF) break; // the vector must be discarded if(lo == 0xFF00 && hi == 0xFF00) continue; // decode floats x = _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.decodeFloat16(lo); y = _utils_utils__WEBPACK_IMPORTED_MODULE_6__.Utils.decodeFloat16(hi); // register vector vectors.push(new _speedy_vector__WEBPACK_IMPORTED_MODULE_8__.SpeedyVector2(x, y)); } // done! return vectors; } } /***/ }), /***/ "./src/core/pipeline/pipeline-message.js": /*!***********************************************!*\ !*** ./src/core/pipeline/pipeline-message.js ***! \***********************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_321498__) => { "use strict"; __nested_webpack_require_321498__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_321498__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineMessageType": () => (/* binding */ SpeedyPipelineMessageType), /* harmony export */ "SpeedyPipelineMessage": () => (/* binding */ SpeedyPipelineMessage), /* harmony export */ "SpeedyPipelineMessageWithNothing": () => (/* binding */ SpeedyPipelineMessageWithNothing), /* harmony export */ "SpeedyPipelineMessageWithImage": () => (/* binding */ SpeedyPipelineMessageWithImage), /* harmony export */ "SpeedyPipelineMessageWithKeypoints": () => (/* binding */ SpeedyPipelineMessageWithKeypoints), /* harmony export */ "SpeedyPipelineMessageWith2DVectors": () => (/* binding */ SpeedyPipelineMessageWith2DVectors), /* harmony export */ "SpeedyPipelineMessageWithLSHTables": () => (/* binding */ SpeedyPipelineMessageWithLSHTables), /* harmony export */ "SpeedyPipelineMessageWithKeypointMatches": () => (/* binding */ SpeedyPipelineMessageWithKeypointMatches) /* harmony export */ }); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_321498__(/*! ../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_321498__(/*! ../../utils/types */ "./src/utils/types.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_321498__(/*! ../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_321498__(/*! ../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _gpu_speedy_lsh__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_321498__(/*! ../../gpu/speedy-lsh */ "./src/gpu/speedy-lsh.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * pipeline-message.js * A message that is shared between nodes of a pipeline */ /** * Types of messages * @enum {Symbol} */ const SpeedyPipelineMessageType = Object.freeze({ Nothing: Symbol('Nothing'), Image: Symbol('Image'), Keypoints: Symbol('Keypoints'), Vector2: Symbol('Vector2'), LSHTables: Symbol('LSHTables'), KeypointMatches: Symbol('KeypointMatches'), }); /** * A message that is shared between nodes of a pipeline * @abstract */ class SpeedyPipelineMessage { /** * Constructor * @param {SpeedyPipelineMessageType} type message type */ constructor(type) { /** @type {SpeedyPipelineMessageType} message type */ this._type = type; } /** * Message type * @returns {SpeedyPipelineMessageType} */ get type() { return this._type; } /** * Checks if the type of this message is equal to parameter type * @param {SpeedyPipelineMessageType} type * @returns {boolean} */ hasType(type) { return this._type === type; } /** * Is this an empty message? * @returns {boolean} */ isEmpty() { return this.hasType(SpeedyPipelineMessageType.Nothing); } /** * Convert to string * @returns {string} */ toString() { const type = Object.keys(SpeedyPipelineMessageType).find( type => SpeedyPipelineMessageType[type] === this.type ); return `message of type ${type}`; } /** * Set parameters * @abstract * @param {...any} args * @returns {SpeedyPipelineMessage} this message */ set(...args) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.AbstractMethodError(); } /** * Create a message of the specified type * @param {SpeedyPipelineMessageType} type * @returns {SpeedyPipelineMessage} */ static create(type) { return createMessage(type); } } /** * An empty message carrying nothing */ class SpeedyPipelineMessageWithNothing extends SpeedyPipelineMessage { /** * Constructor */ constructor() { super(SpeedyPipelineMessageType.Nothing); } /** * Set parameters * @returns {SpeedyPipelineMessage} this message */ set() { return this; } } /** * A message transporting an image */ class SpeedyPipelineMessageWithImage extends SpeedyPipelineMessage { /** * Constructor */ constructor() { super(SpeedyPipelineMessageType.Image); /** @type {SpeedyDrawableTexture} the image we carry */ this._image = null; /** @type {ImageFormat} image format */ this._format = _utils_types__WEBPACK_IMPORTED_MODULE_1__.ImageFormat.RGBA; } /** * Set parameters * @param {SpeedyDrawableTexture} image the image we carry * @param {ImageFormat} [format] image format * @returns {SpeedyPipelineMessage} this message */ set(image, format = _utils_types__WEBPACK_IMPORTED_MODULE_1__.ImageFormat.RGBA) { // set parameters this._image = image; this._format = format; // done! return this; } /** * The image we carry * @returns {SpeedyDrawableTexture} */ get image() { return this._image; } /** * Image format * @returns {ImageFormat} */ get format() { return this._format; } } /** * A message transporting keypoints */ class SpeedyPipelineMessageWithKeypoints extends SpeedyPipelineMessage { /** * Constructor */ constructor() { super(SpeedyPipelineMessageType.Keypoints); /** @type {SpeedyDrawableTexture} encoded keypoints */ this._encodedKeypoints = null; /** @type {number} descriptor size in bytes */ this._descriptorSize = 0; /** @type {number} extra size in bytes */ this._extraSize = 0; /** @type {number} encoder length */ this._encoderLength = 1; } /** * Set parameters * @param {SpeedyDrawableTexture} encodedKeypoints encoded keypoints * @param {number} descriptorSize in bytes * @param {number} extraSize in bytes * @param {number} encoderLength positive integer * @returns {SpeedyPipelineMessage} this message */ set(encodedKeypoints, descriptorSize, extraSize, encoderLength) { // set parameters this._encodedKeypoints = encodedKeypoints; this._descriptorSize = descriptorSize | 0; this._extraSize = extraSize | 0; this._encoderLength = encoderLength | 0; // validate _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._descriptorSize >= 0 && this._extraSize >= 0); _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._encoderLength === this._encodedKeypoints.width, 'Invalid encoderLength'); _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._encodedKeypoints.width === this._encodedKeypoints.height, 'Invalid encodedKeypoints texture'); // done! return this; } /** * Encoded keypoints * @returns {SpeedyDrawableTexture} */ get encodedKeypoints() { return this._encodedKeypoints; } /** * Descriptor size, in bytes * @returns {number} */ get descriptorSize() { return this._descriptorSize; } /** * Extra size, in bytes * @returns {number} */ get extraSize() { return this._extraSize; } /** * Encoder length * @returns {number} */ get encoderLength() { return this._encoderLength; } } /* * A message transporting a set of 2D vectors */ class SpeedyPipelineMessageWith2DVectors extends SpeedyPipelineMessage { /** * Constructor */ constructor() { super(SpeedyPipelineMessageType.Vector2); /** @type {SpeedyDrawableTexture} the set of vectors */ this._vectors = null; } /** * Set parameters * @param {SpeedyDrawableTexture} vectors the set of vectors * @returns {SpeedyPipelineMessage} this message */ set(vectors) { // set parameters this._vectors = vectors; // done! return this; } /** * The set of vectors * @returns {SpeedyDrawableTexture} */ get vectors() { return this._vectors; } } /** * A message transporting LSH tables */ class SpeedyPipelineMessageWithLSHTables extends SpeedyPipelineMessage { /** * Constructor */ constructor() { super(SpeedyPipelineMessageType.LSHTables); /** @type {SpeedyLSH} LSH data structure */ this._lsh = null; } /** * Set parameters * @param {SpeedyLSH} lsh * @returns {SpeedyPipelineMessage} this message */ set(lsh) { // set parameters this._lsh = lsh; // done! return this; } /** * LSH data structure * @returns {SpeedyLSH} */ get lsh() { return this._lsh; } } /* * A message transporting a set of keypoint matches */ class SpeedyPipelineMessageWithKeypointMatches extends SpeedyPipelineMessage { /** * Constructor */ constructor() { super(SpeedyPipelineMessageType.KeypointMatches); /** @type {SpeedyDrawableTexture} keypoint matches (note: 1 pixel encodes 1 match) */ this._encodedMatches = null; /** @type {number} number of matches per keypoint */ this._matchesPerKeypoint = 1; } /** * Set parameters * @param {SpeedyDrawableTexture} encodedMatches * @param {number} matchesPerKeypoint * @returns {SpeedyPipelineMessage} this message */ set(encodedMatches, matchesPerKeypoint) { // set parameters this._encodedMatches = encodedMatches; this._matchesPerKeypoint = matchesPerKeypoint | 0; // validate _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._matchesPerKeypoint > 0); // done! return this; } /** * The matches * @returns {SpeedyDrawableTexture} */ get encodedMatches() { return this._encodedMatches; } /** * Number of matches per keypoint * @returns {number} */ get matchesPerKeypoint() { return this._matchesPerKeypoint; } } // // Utilities // /** Map message type to message class */ const MESSAGE_CLASS = Object.freeze({ [SpeedyPipelineMessageType.Nothing]: SpeedyPipelineMessageWithNothing, [SpeedyPipelineMessageType.Image]: SpeedyPipelineMessageWithImage, [SpeedyPipelineMessageType.Keypoints]: SpeedyPipelineMessageWithKeypoints, [SpeedyPipelineMessageType.Vector2]: SpeedyPipelineMessageWith2DVectors, [SpeedyPipelineMessageType.LSHTables]: SpeedyPipelineMessageWithLSHTables, [SpeedyPipelineMessageType.KeypointMatches]: SpeedyPipelineMessageWithKeypointMatches, }); /** * Create a message of the specified type * @param {SpeedyPipelineMessageType} type * @returns {SpeedyPipelineMessage} */ function createMessage(type) { //return Reflect.construct(MESSAGE_CLASS[type], []); return new MESSAGE_CLASS[ // error TS2538: Type 'Symbol' cannot be used as an index type. // heck, what the hack... /** @type {any} */ ( type ) ]; } /***/ }), /***/ "./src/core/pipeline/pipeline-node.js": /*!********************************************!*\ !*** ./src/core/pipeline/pipeline-node.js ***! \********************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_333809__) => { "use strict"; __nested_webpack_require_333809__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_333809__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelineNode": () => (/* binding */ SpeedyPipelineNode), /* harmony export */ "SpeedyPipelineSourceNode": () => (/* binding */ SpeedyPipelineSourceNode), /* harmony export */ "SpeedyPipelineSinkNode": () => (/* binding */ SpeedyPipelineSinkNode) /* harmony export */ }); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_333809__(/*! ../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_333809__(/*! ../../utils/globals */ "./src/utils/globals.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_333809__(/*! ../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_333809__(/*! ../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _pipeline_port__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_333809__(/*! ./pipeline-port */ "./src/core/pipeline/pipeline-port.js"); /* harmony import */ var _pipeline_portbuilder__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_333809__(/*! ./pipeline-portbuilder */ "./src/core/pipeline/pipeline-portbuilder.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_333809__(/*! ../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _gpu_speedy_texture__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_333809__(/*! ../../gpu/speedy-texture */ "./src/gpu/speedy-texture.js"); /* harmony import */ var _gpu_speedy_texture_reader__WEBPACK_IMPORTED_MODULE_8__ = __nested_webpack_require_333809__(/*! ../../gpu/speedy-texture-reader */ "./src/gpu/speedy-texture-reader.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * pipeline-node.js * Node of a pipeline */ /** @typedef {Object} InputPortDictionary */ /** @typedef {Object} OutputPortDictionary */ /** Generate a random name for a node */ const generateRandomName = () => Math.random().toString(16).substr(2); /** Create an empty input port dictionary */ const createInputPortDictionary = () => /** @type {InputPortDictionary} */ ( Object.create(null) ); /** Create an empty output port dictionary */ const createOutputPortDictionary = () => /** @type {OutputPortDictionary} */ ( Object.create(null) ); /** * Map an array of input ports to an InputPortDictionary whose keys are their names * @param {SpeedyPipelineInputPort[]} ports * @returns {InputPortDictionary} */ function InputPortDictionary(ports) { return ports.reduce((dict, port) => ((dict[port.name] = port), dict), createInputPortDictionary()); } /** * Map an array of output ports to an OutputPortDictionary whose keys are their names * @param {SpeedyPipelineOutputPort[]} ports * @returns {OutputPortDictionary} */ function OutputPortDictionary(ports) { return ports.reduce((dict, port) => ((dict[port.name] = port), dict), createOutputPortDictionary()); } /** A flag used for debugging purposes */ let _texView = false; /** * Node of a pipeline * @abstract */ class SpeedyPipelineNode { /** * Constructor * @param {string} [name] the name of this node * @param {number} [texCount] number of work textures * @param {SpeedyPipelinePortBuilder[]} [portBuilders] port builders */ constructor(name = generateRandomName(), texCount = 0, portBuilders = []) { /** @type {string} the name of this node */ this._name = String(name); /** @type {SpeedyDrawableTexture[]} work texture(s) */ this._tex = (new Array(texCount)).fill(null); // build the ports const ports = portBuilders.map(builder => builder.build(this)); const inputPorts = /** @type {SpeedyPipelineInputPort[]} */ ( ports.filter(port => port.isInputPort()) ); const outputPorts = /** @type {SpeedyPipelineOutputPort[]} */ ( ports.filter(port => port.isOutputPort()) ); /** @type {InputPortDictionary} input ports */ this._inputPorts = InputPortDictionary(inputPorts); /** @type {OutputPortDictionary} output ports */ this._outputPorts = OutputPortDictionary(outputPorts); // validate if(this._name.length == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_3__.IllegalArgumentError(`Invalid name "${this._name}" for node ${this.fullName}`); else if(portBuilders.length == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_3__.IllegalArgumentError(`No ports have been found in node ${this.fullName}`); } /** * The name of this node * @returns {string} */ get name() { return this._name; } /** * Name and type of this node * @returns {string} */ get fullName() { return `${this.constructor.name}[${this.name}]`; } /** * Find input port by name * @param {string} [portName] * @returns {SpeedyPipelineInputPort} */ input(portName = _pipeline_port__WEBPACK_IMPORTED_MODULE_4__.SpeedyPipelineInputPort.DEFAULT_NAME) { if(portName in this._inputPorts) return this._inputPorts[portName]; throw new _utils_errors__WEBPACK_IMPORTED_MODULE_3__.IllegalArgumentError(`Can't find input port ${portName} in node ${this.fullName}`); } /** * Find output port by name * @param {string} [portName] * @returns {SpeedyPipelineOutputPort} */ output(portName = _pipeline_port__WEBPACK_IMPORTED_MODULE_4__.SpeedyPipelineOutputPort.DEFAULT_NAME) { if(portName in this._outputPorts) return this._outputPorts[portName]; throw new _utils_errors__WEBPACK_IMPORTED_MODULE_3__.IllegalArgumentError(`Can't find output port ${portName} in node ${this.fullName}`); } /** * Get data from the input ports and execute * the task that this node is supposed to! * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ execute(gpu) { let portName; // clear output ports for(portName in this._outputPorts) this._outputPorts[portName].clearMessage(); // let the input ports receive what is due for(portName in this._inputPorts) this._inputPorts[portName].pullMessage(this.fullName); // run the task const runTask = this._run(gpu); if(typeof runTask === 'undefined') { for(portName in this._outputPorts) // ensure that no output ports are empty _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._outputPorts[portName].hasMessage(), `Did you forget to write data to the output port ${portName} of ${this.fullName}?`); return undefined; } else return runTask.then(() => { for(portName in this._outputPorts) // ensure that no output ports are empty _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._outputPorts[portName].hasMessage(), `Did you forget to write data to the output port ${portName} of ${this.fullName}?`); }); } /** * Run the specific task of this node * @abstract * @param {SpeedyGPU} gpu * @returns {void|SpeedyPromise} */ _run(gpu) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_3__.AbstractMethodError(); } /** * Initializes this node * @param {SpeedyGPU} gpu */ init(gpu) { gpu.subscribe(this._allocateWorkTextures, this, gpu); this._allocateWorkTextures(gpu); } /** * Releases this node * @param {SpeedyGPU} gpu */ release(gpu) { this._deallocateWorkTextures(gpu); gpu.unsubscribe(this._allocateWorkTextures, this); } /** * Clear all ports */ clearPorts() { let portName; for(portName in this._inputPorts) this._inputPorts[portName].clearMessage(); for(portName in this._outputPorts) this._outputPorts[portName].clearMessage(); } /** * Find all nodes that feed input to this node * @returns {SpeedyPipelineNode[]} */ inputNodes() { const nodes = []; for(const portName in this._inputPorts) { const port = this._inputPorts[portName]; if(port.incomingLink != null) nodes.push(port.incomingLink.node); } return nodes; } /** * Is this a source of the pipeline? * @returns {boolean} */ isSource() { return false; } /** * Is this a sink of the pipeline? * @returns {boolean} */ isSink() { return false; // note: a portal sink has no output ports, but it isn't a sink of the pipeline! //return Object.keys(this._outputPorts).length == 0; } /** * Allocate work texture(s) * @param {SpeedyGPU} gpu */ _allocateWorkTextures(gpu) { for(let j = 0; j < this._tex.length; j++) this._tex[j] = gpu.texturePool.allocate(); } /** * Deallocate work texture(s) * @param {SpeedyGPU} gpu */ _deallocateWorkTextures(gpu) { for(let j = this._tex.length - 1; j >= 0; j--) this._tex[j] = gpu.texturePool.free(this._tex[j]); } /** * Inspect the pixels of a texture for debugging purposes * @param {SpeedyGPU} gpu * @param {SpeedyDrawableTexture} texture * @returns {Uint8Array} */ _inspect(gpu, texture) { const textureReader = new _gpu_speedy_texture_reader__WEBPACK_IMPORTED_MODULE_8__.SpeedyTextureReader(); textureReader.init(gpu); const pixels = textureReader.readPixelsSync(texture); textureReader.release(gpu); return new Uint8Array(pixels); // copy the array } /** * Inspect the pixels of a texture as unsigned 32-bit integers * @param {SpeedyGPU} gpu * @param {SpeedyDrawableTexture} texture * @returns {Uint32Array} */ _inspect32(gpu, texture) { _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(_utils_globals__WEBPACK_IMPORTED_MODULE_1__.LITTLE_ENDIAN); // make sure we use little-endian return new Uint32Array(this._inspect(gpu, texture).buffer); } /** * Visually inspect a texture for debugging purposes * @param {SpeedyGPU} gpu * @param {SpeedyDrawableTexture} texture */ _visualize(gpu, texture) { const canvas = gpu.renderToCanvas(texture); if(!_texView) { document.body.appendChild(canvas); _texView = true; } } } /** * Source node (a node with no input ports) * @abstract */ class SpeedyPipelineSourceNode extends SpeedyPipelineNode { /** * Constructor * @param {string} [name] the name of this node * @param {number} [texCount] number of work textures * @param {SpeedyPipelinePortBuilder[]} [portBuilders] port builders */ constructor(name = undefined, texCount = undefined, portBuilders = undefined) { super(name, texCount, portBuilders); _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(Object.keys(this._inputPorts).length == 0); } /** * Is this a source of the pipeline? * @returns {boolean} */ isSource() { return true; } } /** * Sink node (a node with no output ports) * @abstract */ class SpeedyPipelineSinkNode extends SpeedyPipelineNode { /** * Constructor * @param {string} [name] the name of this node * @param {number} [texCount] number of work textures * @param {SpeedyPipelinePortBuilder[]} [portBuilders] port builders */ constructor(name = undefined, texCount = undefined, portBuilders = undefined) { super(name, texCount, portBuilders); _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(Object.keys(this._outputPorts).length == 0); } /** * Export data from this node to the user * @abstract * @returns {SpeedyPromise} */ export() { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_3__.AbstractMethodError(); } /** * Is this a sink of the pipeline? * @returns {boolean} */ isSink() { return true; } } /***/ }), /***/ "./src/core/pipeline/pipeline-port.js": /*!********************************************!*\ !*** ./src/core/pipeline/pipeline-port.js ***! \********************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_347268__) => { "use strict"; __nested_webpack_require_347268__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_347268__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelinePort": () => (/* binding */ SpeedyPipelinePort), /* harmony export */ "SpeedyPipelineOutputPort": () => (/* binding */ SpeedyPipelineOutputPort), /* harmony export */ "SpeedyPipelineInputPort": () => (/* binding */ SpeedyPipelineInputPort) /* harmony export */ }); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_347268__(/*! ../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_347268__(/*! ../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _pipeline_portspec__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_347268__(/*! ./pipeline-portspec */ "./src/core/pipeline/pipeline-portspec.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_347268__(/*! ./pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_347268__(/*! ./pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * pipeline-port.js * Port of a node of a pipeline */ // Constants const DEFAULT_INPUT_PORT_NAME = 'in'; const DEFAULT_OUTPUT_PORT_NAME = 'out'; const ACCEPTABLE_PORT_NAME = /^[a-z][a-zA-Z0-9]*$/; const EMPTY_MESSAGE = new _pipeline_message__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineMessageWithNothing(); /** * Port of a node of a pipeline * @abstract */ class SpeedyPipelinePort { /** * Constructor * @param {string} name the name of this port * @param {SpeedyPipelinePortSpec} spec port specification * @param {SpeedyPipelineNode} node the node to which this port belongs */ constructor(name, spec, node) { /** @type {string} the name of this port */ this._name = String(name); /** @type {SpeedyPipelinePortSpec} the specification of this port */ this._spec = spec; /** @type {SpeedyPipelineNode} the node to which this port belongs */ this._node = node; /** @type {SpeedyPipelineMessage} the message located in this port */ this._message = EMPTY_MESSAGE; // check if we've got an acceptable port name _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(ACCEPTABLE_PORT_NAME.test(this._name), `Port name "${this._name}" is not acceptable`); } /** * The name of this port * @returns {string} */ get name() { return this._name; } /** * The node to which this port belongs * @returns {SpeedyPipelineNode} */ get node() { return this._node; } /** * Connect this port to another * @abstract * @param {SpeedyPipelinePort} port */ connectTo(port) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.AbstractMethodError(); } /** * Is this an input port? * @abstract * @returns {boolean} */ isInputPort() { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.AbstractMethodError(); } /** * Is this an output port? * @returns {boolean} */ isOutputPort() { return !this.isInputPort(); } /** * Clear the message stored in this port */ clearMessage() { this._message = EMPTY_MESSAGE; } /** * Is there a valid message located in this port? * @returns {boolean} */ hasMessage() { return !this._message.isEmpty(); } /** * Read the message that is in this port * @returns {SpeedyPipelineMessage} */ read() { if(this._message.isEmpty()) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.IllegalOperationError(`Can't read from port ${this.name}: nothing to read`); return this._message; } /** * Write a message to this port * @param {SpeedyPipelineMessage} message */ write(message) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.NotSupportedError(`Can't write ${message} to port ${this.name}: unsupported operation`); } /** * Default port name * @abstract * @returns {string} */ static get DEFAULT_NAME() { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.AbstractMethodError(); } } /** * Output port */ class SpeedyPipelineOutputPort extends SpeedyPipelinePort { /** * Constructor * @param {string} name the name of this port * @param {SpeedyPipelinePortSpec} spec port specification * @param {SpeedyPipelineNode} node the node to which this port belongs */ constructor(name, spec, node) { super(name, spec, node); /** @type {SpeedyPipelineMessage} cached message */ this._cachedMessage = null; } /** * Connect this port to another * @param {SpeedyPipelineInputPort} port */ connectTo(port) { if(!port.isInputPort()) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.IllegalArgumentError(`Can't connect output port ${this.name} to port ${port.name}: expected an input port`); port.connectTo(this); } /** * Is this an input port? * @returns {boolean} */ isInputPort() { return false; } /** * Write a message to this port * @param {SpeedyPipelineMessage} message */ write(message) { if(!this._spec.accepts(message)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.IllegalArgumentError(`Can't write ${message} to port ${this.name}. ${this._spec}`); this._message = message; } /** * Write a message to this port using a cached message object * @param {...any} args to be passed to SpeedyPipelineMessage.set() */ swrite(...args) { if(this._cachedMessage == null) this._cachedMessage = _pipeline_message__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineMessage.create(this._spec.expectedMessageType); this.write(this._cachedMessage.set(...args)); } /** * Default port name * @returns {string} */ static get DEFAULT_NAME() { return DEFAULT_OUTPUT_PORT_NAME; } } /** * Input port */ class SpeedyPipelineInputPort extends SpeedyPipelinePort { /** * Constructor * @param {string} name the name of this port * @param {SpeedyPipelinePortSpec} spec port specification * @param {SpeedyPipelineNode} node the node to which this port belongs */ constructor(name, spec, node) { super(name, spec, node); /** @type {SpeedyPipelineOutputPort|null} incoming link */ this._incomingLink = null; } /** * Incoming link * @returns {SpeedyPipelineOutputPort|null} */ get incomingLink() { return this._incomingLink; } /** * Connect this port to another * @param {SpeedyPipelineOutputPort} port */ connectTo(port) { if(!port.isOutputPort()) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.IllegalArgumentError(`Can't connect input port ${this.name} of "${this.node.fullName}" to input port ${port.name} of "${port.node.fullName}": expected an output port`); else if(!this._spec.isCompatibleWith(port._spec)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.IllegalArgumentError(`Can't connect port ${this.name} of "${this.node.fullName}" to port ${port.name} of "${port.node.fullName}": incompatible types`); this._incomingLink = port; } /** * Unlink this port */ disconnect() { this._incomingLink = null; } /** * Is this an input port? * @returns {boolean} */ isInputPort() { return true; } /** * Receive a message using the incoming link * @param {string} [nodeName] * @returns {SpeedyPipelineMessage} */ pullMessage(nodeName = '') { const name = nodeName.length > 0 ? `${this.name} of ${nodeName}` : this.name; if(this._incomingLink == null) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.IllegalOperationError(`No incoming link for input port ${name}`); const message = this._incomingLink.read(); if(!this._spec.accepts(message)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.IllegalArgumentError(`Can't receive ${message} at port ${name}: ${this._spec}`); return (this._message = message); } /** * Default port name * @returns {string} */ static get DEFAULT_NAME() { return DEFAULT_INPUT_PORT_NAME; } } /***/ }), /***/ "./src/core/pipeline/pipeline-portbuilder.js": /*!***************************************************!*\ !*** ./src/core/pipeline/pipeline-portbuilder.js ***! \***************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_357107__) => { "use strict"; __nested_webpack_require_357107__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_357107__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelinePortBuilder": () => (/* binding */ SpeedyPipelinePortBuilder), /* harmony export */ "InputPort": () => (/* binding */ InputPort), /* harmony export */ "OutputPort": () => (/* binding */ OutputPort) /* harmony export */ }); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_357107__(/*! ../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _pipeline_port__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_357107__(/*! ./pipeline-port */ "./src/core/pipeline/pipeline-port.js"); /* harmony import */ var _pipeline_portspec__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_357107__(/*! ./pipeline-portspec */ "./src/core/pipeline/pipeline-portspec.js"); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_357107__(/*! ./pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_357107__(/*! ./pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * pipeline-portbuilder.js * Builder of a port of a node of a pipeline */ /** * @typedef {import('./pipeline-portspec').SpeedyPipelineMessageConstraint} SpeedyPipelineMessageConstraint */ /** * Builder of a port of a node of a pipeline */ class SpeedyPipelinePortBuilder { /** * Constructor * @param {typeof SpeedyPipelinePort} portClass input or output? * @param {string} portName */ constructor(portClass, portName) { /** @type {typeof SpeedyPipelinePort} input or output? */ this._class = portClass; /** @type {string} port name */ this._name = String(portName); /** @type {SpeedyPipelineMessageType} accepted message type */ this._type = _pipeline_message__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineMessageType.Nothing; /** @type {SpeedyPipelineMessageConstraint} message validation function */ this._messageConstraint = undefined; } /** * Declare that the new port expects a certain type of message * @param {SpeedyPipelineMessageType} type expected type * @returns {SpeedyPipelinePortBuilder} this builder */ expects(type) { _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._type == _pipeline_message__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineMessageType.Nothing); _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(type != _pipeline_message__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineMessageType.Nothing); this._type = type; return this; } /** * Declare that the new port expects messages satisfying a constraint * @param {SpeedyPipelineMessageConstraint} constraint * @returns {SpeedyPipelinePortBuilder} this builder */ satisfying(constraint) { _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._type != _pipeline_message__WEBPACK_IMPORTED_MODULE_3__.SpeedyPipelineMessageType.Nothing, 'You must first declare what type of message this port expects'); _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._messageConstraint === undefined); _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(typeof constraint === 'function'); this._messageConstraint = constraint; return this; } /** * Build a port * @param {SpeedyPipelineNode} node the node to which the new port will belong * @returns {SpeedyPipelinePort} */ build(node) { const spec = new _pipeline_portspec__WEBPACK_IMPORTED_MODULE_2__.SpeedyPipelinePortSpec(this._type, this._messageConstraint); return Reflect.construct(this._class, [this._name, spec, node]); } } /** * Creates a builder for an input port * @param {string} [portName] * @returns {SpeedyPipelinePortBuilder} */ function InputPort(portName = _pipeline_port__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineInputPort.DEFAULT_NAME) { return new SpeedyPipelinePortBuilder(_pipeline_port__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineInputPort, portName); } /** * Creates a builder for an output port * @param {string} [portName] * @returns {SpeedyPipelinePortBuilder} */ function OutputPort(portName = _pipeline_port__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineOutputPort.DEFAULT_NAME) { return new SpeedyPipelinePortBuilder(_pipeline_port__WEBPACK_IMPORTED_MODULE_1__.SpeedyPipelineOutputPort, portName); } /***/ }), /***/ "./src/core/pipeline/pipeline-portspec.js": /*!************************************************!*\ !*** ./src/core/pipeline/pipeline-portspec.js ***! \************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_362679__) => { "use strict"; __nested_webpack_require_362679__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_362679__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipelinePortSpec": () => (/* binding */ SpeedyPipelinePortSpec) /* harmony export */ }); /* harmony import */ var _pipeline_message__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_362679__(/*! ./pipeline-message */ "./src/core/pipeline/pipeline-message.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_362679__(/*! ../../utils/utils */ "./src/utils/utils.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * pipeline-portspec.js * Specification (requirements) of a port of a node of a pipeline */ /** * A message constraint is a message validation predicate * @typedef {function(SpeedyPipelineMessage): boolean} SpeedyPipelineMessageConstraint */ /** * A validation predicate that validates all messages * @type {SpeedyPipelineMessageConstraint} */ const none = message => true; /** * Specification (requirements) of a port of a node of a pipeline */ class SpeedyPipelinePortSpec { /** * Constructor * @param {SpeedyPipelineMessageType} expectedMessageType expected message type * @param {SpeedyPipelineMessageConstraint} [messageConstraint] message validation function */ constructor(expectedMessageType, messageConstraint = none) { /** @type {SpeedyPipelineMessageType} expected message type */ this._expectedMessageType = expectedMessageType; /** @type {SpeedyPipelineMessageConstraint} message validation function */ this._isValidMessage = (typeof messageConstraint === 'function') ? messageConstraint : none; // expect a valid type _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(this._expectedMessageType != _pipeline_message__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineMessageType.Nothing); } /** * Checks if two specs have the same expected type * @param {SpeedyPipelinePortSpec} spec * @returns {boolean} */ isCompatibleWith(spec) { return this._expectedMessageType == spec._expectedMessageType; } /** * Is the given message accepted by a port that abides by this specification? * @param {SpeedyPipelineMessage} message * @returns {boolean} */ accepts(message) { return message.hasType(this._expectedMessageType) && this._isValidMessage(message); } /** * Convert to string * @returns {string} */ toString() { const type = Object.keys(_pipeline_message__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineMessageType).find( type => _pipeline_message__WEBPACK_IMPORTED_MODULE_0__.SpeedyPipelineMessageType[type] === this._expectedMessageType ); return `Port expects ${type} satisfying ${this._isValidMessage}`; } /** * Expected message type * @returns {SpeedyPipelineMessageType} */ get expectedMessageType() { return this._expectedMessageType; } } /***/ }), /***/ "./src/core/pipeline/pipeline.js": /*!***************************************!*\ !*** ./src/core/pipeline/pipeline.js ***! \***************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_366642__) => { "use strict"; __nested_webpack_require_366642__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_366642__.d(__webpack_exports__, { /* harmony export */ "SpeedyPipeline": () => (/* binding */ SpeedyPipeline) /* harmony export */ }); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_366642__(/*! ../../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_366642__(/*! ../speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_366642__(/*! ../../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _pipeline_node__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_366642__(/*! ./pipeline-node */ "./src/core/pipeline/pipeline-node.js"); /* harmony import */ var _pipeline_port__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_366642__(/*! ./pipeline-port */ "./src/core/pipeline/pipeline-port.js"); /* harmony import */ var _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_366642__(/*! ../../gpu/speedy-gpu */ "./src/gpu/speedy-gpu.js"); /* harmony import */ var _speedy_media__WEBPACK_IMPORTED_MODULE_6__ = __nested_webpack_require_366642__(/*! ../speedy-media */ "./src/core/speedy-media.js"); /* harmony import */ var _speedy_keypoint__WEBPACK_IMPORTED_MODULE_7__ = __nested_webpack_require_366642__(/*! ../speedy-keypoint */ "./src/core/speedy-keypoint.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * pipeline.js * A pipeline is a network of nodes in which data flows to a sink */ /** * A dictionary indexed by the names of the sink nodes * @typedef {Object} SpeedyPipelineOutput */ /** @type {SpeedyGPU} shared GPU programs & textures */ let gpu = null; /** @type {number} gpu reference count */ let referenceCount = 0; /** * A pipeline is a network of nodes in which data flows to a sink */ class SpeedyPipeline { /** * Constructor */ constructor() { /** @type {SpeedyPipelineNode[]} the collection of all nodes that belong to this pipeline */ this._nodes = []; /** @type {SpeedyPipelineNode[]} a sequence of nodes: from the source(s) to the sink */ this._sequence = []; /** @type {boolean} are we running the pipeline at this moment? */ this._busy = false; } /** * Find a node by its name * @template T extends SpeedyPipelineNode * @param {string} name * @returns {T|null} */ node(name) { for(let i = 0, n = this._nodes.length; i < n; i++) { if(this._nodes[i].name === name) return this._nodes[i]; } return null; } /** * Initialize the pipeline * @param {...SpeedyPipelineNode} nodes * @returns {SpeedyPipeline} this pipeline */ init(...nodes) { // validate if(this._nodes.length > 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalOperationError(`The pipeline has already been initialized`); else if(nodes.length == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalArgumentError(`Can't initialize the pipeline. Please specify its nodes`); // create a GPU instance and increase the reference count if(0 == referenceCount++) { _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(!gpu, 'Duplicate SpeedyGPU instance'); gpu = new _gpu_speedy_gpu__WEBPACK_IMPORTED_MODULE_5__.SpeedyGPU(); } // add nodes to the network for(let i = 0; i < nodes.length; i++) { const node = nodes[i]; if(!this._nodes.includes(node)) this._nodes.push(node); } // generate the sequence of nodes this._sequence = SpeedyPipeline._tsort(this._nodes); SpeedyPipeline._validateSequence(this._sequence); // initialize nodes for(let i = 0; i < this._sequence.length; i++) this._sequence[i].init(gpu); // done! return this; } /** * Release the resources associated with this pipeline * @returns {null} */ release() { if(this._nodes.length == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalOperationError(`The pipeline has already been released or has never been initialized`); // release nodes for(let i = this._sequence.length - 1; i >= 0; i--) this._sequence[i].release(gpu); this._sequence.length = 0; this._nodes.length = 0; // decrease reference count and release GPU if necessary if(0 == --referenceCount) gpu = gpu.release(); // done! return null; } /** * Run the pipeline * @returns {SpeedyPromise} results are indexed by the names of the sink nodes */ run() { _utils_utils__WEBPACK_IMPORTED_MODULE_0__.Utils.assert(this._sequence.length > 0, `The pipeline has not been initialized or has been released`); // is the pipeline busy? if(this._busy) { // if so, we need to wait 'til it finishes return new _speedy_promise__WEBPACK_IMPORTED_MODULE_1__.SpeedyPromise((resolve, reject) => { setTimeout(() => this.run().then(resolve, reject), 0); }); } else { // the pipeline is now busy and won't accept concurrent tasks // (we allocate textures using a single pool) this._busy = true; } // find the sinks const sinks = /** @type {SpeedyPipelineSinkNode[]} */ ( this._sequence.filter(node => node.isSink()) ); // create output template const template = SpeedyPipeline._createOutputTemplate(sinks); // run the pipeline return SpeedyPipeline._runSequence(this._sequence).then(() => // export results _speedy_promise__WEBPACK_IMPORTED_MODULE_1__.SpeedyPromise.all(sinks.map(sink => sink.export().turbocharge())).then(results => // aggregate results by the names of the sinks results.reduce((obj, val, idx) => ((obj[sinks[idx].name] = val), obj), template) ) ).finally(() => { // clear all ports for(let i = this._sequence.length - 1; i >= 0; i--) this._sequence[i].clearPorts(); // the pipeline is no longer busy this._busy = false; }).turbocharge(); } /** * @internal * * GPU instance * @returns {SpeedyGPU} */ get _gpu() { return gpu; } /** * Execute the tasks of a sequence of nodes * @param {SpeedyPipelineNode[]} sequence sequence of nodes * @param {number} [i] in [0,n) * @param {number} [n] number of nodes * @returns {SpeedyPromise} */ static _runSequence(sequence, i = 0, n = sequence.length) { for(; i < n; i++) { const runTask = sequence[i].execute(gpu); // this call greatly improves performance when downloading pixel data using PBOs gpu.gl.flush(); if(typeof runTask !== 'undefined') return runTask.then(() => SpeedyPipeline._runSequence(sequence, i+1, n)); } return _speedy_promise__WEBPACK_IMPORTED_MODULE_1__.SpeedyPromise.resolve(); } /** * Topological sorting * @param {SpeedyPipelineNode[]} nodes * @returns {SpeedyPipelineNode[]} */ static _tsort(nodes) { /** @typedef {[SpeedyPipelineNode, boolean]} StackNode */ const outlinks = SpeedyPipeline._outlinks(nodes); const stack = nodes.map(node => /** @type {StackNode} */ ([ node, false ]) ); const trash = new Set(); const sorted = new Array(nodes.length); let j = sorted.length; while(stack.length > 0) { const [ node, done ] = stack.pop(); if(!done) { if(!trash.has(node)) { const outnodes = outlinks.get(node); trash.add(node); stack.push([ node, true ]); stack.push(...(outnodes.map(node => /** @type {StackNode} */ ([ node, false ]) ))); if(outnodes.some(node => trash.has(node) && !sorted.includes(node))) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalOperationError(`Pipeline networks cannot have cycles!`); } } else sorted[--j] = node; } return sorted; } /** * Figure out the outgoing links of all nodes * @param {SpeedyPipelineNode[]} nodes * @returns {Map} */ static _outlinks(nodes) { const outlinks = new Map(); for(let k = 0; k < nodes.length; k++) outlinks.set(nodes[k], []); for(let i = 0; i < nodes.length; i++) { const to = nodes[i]; const inputs = to.inputNodes(); for(let j = 0; j < inputs.length; j++) { const from = inputs[j]; const links = outlinks.get(from); if(!links) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalOperationError(`Can't initialize the pipeline. Missing node: ${from.fullName}. Did you forget to add it to the initialization list?`); if(!links.includes(to)) links.push(to); } } return outlinks; } /** * Generate the output template by aggregating the names of the sinks * @param {SpeedyPipelineNode[]} [sinks] * @returns {SpeedyPipelineOutput} */ static _createOutputTemplate(sinks = []) { const template = Object.create(null); for(let i = sinks.length - 1; i >= 0; i--) template[sinks[i].name] = null; return template; } /** * Validate a sequence of nodes * @param {SpeedyPipelineNode[]} sequence */ static _validateSequence(sequence) { if(sequence.length == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalOperationError(`Pipeline doesn't have nodes`); else if(!sequence[0].isSource()) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalOperationError(`Pipeline doesn't have a source`); else if(!sequence.find(node => node.isSink())) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalOperationError(`Pipeline doesn't have a sink`); } } /***/ }), /***/ "./src/core/settings.js": /*!******************************!*\ !*** ./src/core/settings.js ***! \******************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_378214__) => { "use strict"; __nested_webpack_require_378214__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_378214__.d(__webpack_exports__, { /* harmony export */ "Settings": () => (/* binding */ Settings) /* harmony export */ }); /* harmony import */ var _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_378214__(/*! ./speedy-namespace */ "./src/core/speedy-namespace.js"); /* harmony import */ var _gpu_speedy_gl__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_378214__(/*! ../gpu/speedy-gl */ "./src/gpu/speedy-gl.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_378214__(/*! ../utils/errors */ "./src/utils/errors.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * settings.js * Global settings */ /** @typedef {import('../gpu/speedy-gl').PowerPreference} PowerPreference */ /** @typedef {"raf" | "asap"} GPUPollingMode */ /** @type {GPUPollingMode} Default GPU polling mode */ const DEFAULT_GPU_POLLING_MODE = 'raf'; /** @type {GPUPollingMode} GPU polling mode */ let gpuPollingMode = DEFAULT_GPU_POLLING_MODE; /** * Global settings */ class Settings extends _speedy_namespace__WEBPACK_IMPORTED_MODULE_0__.SpeedyNamespace { /** * Power preference of the WebGL context * @returns {PowerPreference} */ static get powerPreference() { return _gpu_speedy_gl__WEBPACK_IMPORTED_MODULE_1__.SpeedyGL.powerPreference; } /** * Power preference of the WebGL context * @param {PowerPreference} value */ static set powerPreference(value) { _gpu_speedy_gl__WEBPACK_IMPORTED_MODULE_1__.SpeedyGL.powerPreference = value; } /** * GPU polling mode * @returns {GPUPollingMode} */ static get gpuPollingMode() { return gpuPollingMode; } /** * GPU polling mode * @param {GPUPollingMode} value */ static set gpuPollingMode(value) { if(value !== 'raf' && value !== 'asap') throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.IllegalArgumentError(`Invalid GPU polling mode: "${value}"`); gpuPollingMode = value; } } /***/ }), /***/ "./src/core/speedy-keypoint-descriptor.js": /*!************************************************!*\ !*** ./src/core/speedy-keypoint-descriptor.js ***! \************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_381293__) => { "use strict"; __nested_webpack_require_381293__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_381293__.d(__webpack_exports__, { /* harmony export */ "SpeedyKeypointDescriptor": () => (/* binding */ SpeedyKeypointDescriptor) /* harmony export */ }); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-keypoint-descriptor.js * Keypoint descriptor */ /** * Represents a keypoint descriptor */ class SpeedyKeypointDescriptor { /** * Constructor * @param {Uint8Array} data descriptor bytes */ constructor(data) { this._data = data; return Object.freeze(this); } /** * Descriptor data * @returns {Uint8Array} */ get data() { return this._data; } /** * The size of the descriptor, in bytes * @returns {number} */ get size() { return this._data.byteLength; } /** * A string representation of the keypoint descriptor * @returns {string} */ toString() { return `SpeedyKeypointDescriptor(${this._data.join(',')})`; } } /***/ }), /***/ "./src/core/speedy-keypoint-match.js": /*!*******************************************!*\ !*** ./src/core/speedy-keypoint-match.js ***! \*******************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_383313__) => { "use strict"; __nested_webpack_require_383313__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_383313__.d(__webpack_exports__, { /* harmony export */ "SpeedyKeypointMatch": () => (/* binding */ SpeedyKeypointMatch) /* harmony export */ }); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_383313__(/*! ../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-match.js * A match between two keypoint descriptors */ // Constants const MATCH_NOT_FOUND = -1; /** * A match between two keypoint descriptors */ class SpeedyKeypointMatch { /** * Constructor * @param {number} index index of the stored keypoint, a non-negative integer * @param {number} distance a measure of the quality of the match, a non-negative number */ constructor(index, distance) { const isValid = distance < _utils_globals__WEBPACK_IMPORTED_MODULE_0__.MATCH_MAX_DISTANCE; /** @type {number} index of the stored keypoint */ this._index = isValid ? (index | 0) : MATCH_NOT_FOUND; /** @type {number} a measure of the quality of the match */ this._distance = isValid ? +distance : Number.POSITIVE_INFINITY; // done! return Object.freeze(this); } /** * The index of the stored keypoint * @returns {number} */ get index() { return this._index; } /** * A measure of the quality of the match (lower values indicate better matches) * @returns {number} */ get distance() { return this._distance; } /** * A string representation of the keypoint match * @returns {string} */ toString() { return `SpeedyKeypointMatch(${this.index},${this.distance})`; } } /***/ }), /***/ "./src/core/speedy-keypoint.js": /*!*************************************!*\ !*** ./src/core/speedy-keypoint.js ***! \*************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_386036__) => { "use strict"; __nested_webpack_require_386036__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_386036__.d(__webpack_exports__, { /* harmony export */ "SpeedyKeypoint": () => (/* binding */ SpeedyKeypoint), /* harmony export */ "SpeedyTrackedKeypoint": () => (/* binding */ SpeedyTrackedKeypoint), /* harmony export */ "SpeedyMatchedKeypoint": () => (/* binding */ SpeedyMatchedKeypoint) /* harmony export */ }); /* harmony import */ var _speedy_keypoint_descriptor__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_386036__(/*! ./speedy-keypoint-descriptor */ "./src/core/speedy-keypoint-descriptor.js"); /* harmony import */ var _speedy_keypoint_match__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_386036__(/*! ./speedy-keypoint-match */ "./src/core/speedy-keypoint-match.js"); /* harmony import */ var _speedy_point__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_386036__(/*! ./speedy-point */ "./src/core/speedy-point.js"); /* harmony import */ var _speedy_vector__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_386036__(/*! ./speedy-vector */ "./src/core/speedy-vector.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-keypoint.js * Keypoint class */ /** * Represents a keypoint */ class SpeedyKeypoint { /** * Constructor * @param {number} x X position * @param {number} y Y position * @param {number} [lod] Level-of-detail * @param {number} [rotation] Rotation in radians * @param {number} [score] Cornerness measure * @param {SpeedyKeypointDescriptor|null} [descriptor] Keypoint descriptor, if any */ constructor(x, y, lod = 0.0, rotation = 0.0, score = 0.0, descriptor = null) { /** @type {SpeedyPoint2} keypoint position */ this._position = new _speedy_point__WEBPACK_IMPORTED_MODULE_2__.SpeedyPoint2(+x, +y); /** @type {number} level of detail */ this._lod = +lod; /** @type {number} rotation in radians */ this._rotation = +rotation; /** @type {number} a cornerness measure */ this._score = +score; /** @type {SpeedyKeypointDescriptor|null} keypoint descriptor, if any */ this._descriptor = descriptor; } /** * Converts this keypoint to a descriptive string * @returns {string} */ toString() { return `SpeedyKeypoint(${this.x},${this.y})`; } /** * The position of this keypoint * @returns {SpeedyPoint2} */ get position() { return this._position; } /** * The x-position of this keypoint * @returns {number} */ get x() { return this._position.x; } /** * The x-position of this keypoint * @param {number} value */ set x(value) { this._position.x = +value; } /** * The y-position of this keypoint * @returns {number} */ get y() { return this._position.y; } /** * The y-position of this keypoint * @param {number} value */ set y(value) { this._position.y = +value; } /** * The pyramid level-of-detail from which this keypoint was extracted * @returns {number} */ get lod() { return this._lod; } /** * Scale: 2^lod * @returns {number} */ get scale() { return Math.pow(2, this._lod); } /** * The orientation of the keypoint, in radians * @returns {number} Angle in radians */ get rotation() { return this._rotation; } /** * Score: a cornerness measure * @returns {number} Score */ get score() { return this._score; } /** * Keypoint descriptor * @return {SpeedyKeypointDescriptor|null} */ get descriptor() { return this._descriptor; } } /** * Represents a tracked keypoint */ class SpeedyTrackedKeypoint extends SpeedyKeypoint { /** * Constructor * @param {number} x X position * @param {number} y Y position * @param {number} [lod] Level-of-detail * @param {number} [rotation] Rotation in radians * @param {number} [score] Cornerness measure * @param {SpeedyKeypointDescriptor|null} [descriptor] Keypoint descriptor, if any * @param {SpeedyVector2} [flow] flow vector */ constructor(x, y, lod = 0.0, rotation = 0.0, score = 0.0, descriptor = null, flow = new _speedy_vector__WEBPACK_IMPORTED_MODULE_3__.SpeedyVector2(0,0)) { super(x, y, lod, rotation, score, descriptor); /** @type {SpeedyVector2} flow vector */ this._flow = flow; } /** * Flow vector * @returns {SpeedyVector2} */ get flow() { return this._flow; } } /** * Represents a matched keypoint */ class SpeedyMatchedKeypoint extends SpeedyKeypoint { /** * Constructor * @param {number} x X position * @param {number} y Y position * @param {number} [lod] Level-of-detail * @param {number} [rotation] Rotation in radians * @param {number} [score] Cornerness measure * @param {SpeedyKeypointDescriptor|null} [descriptor] Keypoint descriptor, if any * @param {SpeedyKeypointMatch[]} [matches] Keypoint matches, if any */ constructor(x, y, lod = 0.0, rotation = 0.0, score = 0.0, descriptor = null, matches = []) { super(x, y, lod, rotation, score, descriptor); /** @type {SpeedyKeypointMatch[]} keypoint matches */ this._matches = matches; } /** * Keypoint matches * @returns {SpeedyKeypointMatch[]} */ get matches() { return this._matches; } } /***/ }), /***/ "./src/core/speedy-matrix-expr.js": /*!****************************************!*\ !*** ./src/core/speedy-matrix-expr.js ***! \****************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_392606__) => { "use strict"; __nested_webpack_require_392606__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_392606__.d(__webpack_exports__, { /* harmony export */ "SpeedyMatrixExpr": () => (/* binding */ SpeedyMatrixExpr) /* harmony export */ }); /* harmony import */ var _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_392606__(/*! ./speedy-matrix-wasm */ "./src/core/speedy-matrix-wasm.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_392606__(/*! ../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_392606__(/*! ../utils/errors */ "./src/utils/errors.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-matrix-expr.js * Symbolic matrix expressions */ /** @typedef {import('./speedy-matrix').SpeedyMatrixDtype} SpeedyMatrixDtype */ /** @typedef {import('./speedy-matrix').SpeedyMatrixBufferType} SpeedyMatrixBufferType */ /** @typedef {import('./speedy-matrix').SpeedyMatrixBufferTypeConstructor} SpeedyMatrixBufferTypeConstructor */ /** @typedef {import('./speedy-matrix-wasm').SpeedyMatrixWASMMemory} SpeedyMatrixWASMMemory */ /** @typedef {Object} Dtype2BufferType */ /** @const {Dtype2BufferType} */ const DTYPE_TO_BUFFER_TYPE = Object.freeze({ 'float32': Float32Array }); /** * @abstract Matrix expression * It's an opaque object representing an algebraic * expression. It has no data attached to it. */ class SpeedyMatrixExpr { /** * Constructor * @param {number} rows * @param {number} columns * @param {SpeedyMatrixDtype} dtype */ constructor(rows, columns, dtype) { _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(rows > 0 && columns > 0); _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(dtype === SpeedyMatrixExpr.DEFAULT_DTYPE); // we only support float32 for now /** @type {number} number of rows */ this._rows = rows | 0; /** @type {number} number of columns */ this._columns = columns | 0; /** @type {SpeedyMatrixDtype} data type */ this._dtype = dtype; } /** * Number of rows * @returns {number} */ get rows() { return this._rows; } /** * Number of columns * @returns {number} */ get columns() { return this._columns; } /** * Data type * @returns {SpeedyMatrixDtype} */ get dtype() { return this._dtype; } /** * Default data type * @returns {SpeedyMatrixDtype} */ static get DEFAULT_DTYPE() { return 'float32'; } /** * Buffer types * @returns {Dtype2BufferType} */ static get BUFFER_TYPE() { return DTYPE_TO_BUFFER_TYPE; } /** * Matrix addition * @param {SpeedyMatrixExpr} expr * @returns {SpeedyMatrixExpr} */ plus(expr) { return new SpeedyMatrixAddExpr(this, expr); } /** * Matrix subtraction * @param {SpeedyMatrixExpr} expr * @returns {SpeedyMatrixExpr} */ minus(expr) { return new SpeedyMatrixSubtractExpr(this, expr); } /** * Matrix multiplication * @param {SpeedyMatrixExpr|number} expr * @returns {SpeedyMatrixExpr} */ times(expr) { if(typeof expr === 'number') return new SpeedyMatrixScaleExpr(this, expr); else return new SpeedyMatrixMultiplyExpr(this, expr); } /** * Matrix transposition * @returns {SpeedyMatrixExpr} */ transpose() { return new SpeedyMatrixTransposeExpr(this); } /** * Matrix inversion * @returns {SpeedyMatrixExpr} */ inverse() { return new SpeedyMatrixInvertExpr(this); } /** * Component-wise multiplication * @param {SpeedyMatrixExpr} expr * @returns {SpeedyMatrixExpr} */ compMult(expr) { return new SpeedyMatrixCompMultExpr(this, expr); } /** * Left division: A \ b, which is equivalent to (pseudo-)inverse(A) * b * @param {SpeedyMatrixExpr} expr * @returns {SpeedyMatrixExpr} */ ldiv(expr) { return new SpeedyMatrixLdivExpr(this, expr); } /** * Returns a human-readable string representation of the matrix expression * @returns {string} */ toString() { return `SpeedyMatrixExpr(rows=${this.rows}, columns=${this.columns})`; } /** * Evaluate this expression * @abstract * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @returns {SpeedyMatrix} */ _evaluate(wasm, memory) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.AbstractMethodError(); } } const { SpeedyMatrix } = __nested_webpack_require_392606__(/*! ./speedy-matrix */ "./src/core/speedy-matrix.js"); /** * @abstract operation storing a temporary matrix */ class SpeedyMatrixTempExpr extends SpeedyMatrixExpr { /** * Constructor * @param {number} rows * @param {number} columns * @param {SpeedyMatrixDtype} dtype */ constructor(rows, columns, dtype) { super(rows, columns, dtype); /** @type {SpeedyMatrix} holds the results of a computation */ this._tempMatrix = SpeedyMatrix.Zeros(this.rows, this.columns, this.dtype); } } /** * @abstract unary operation */ class SpeedyMatrixUnaryOperationExpr extends SpeedyMatrixTempExpr { /** * Constructor * @param {number} rows rows of the output matrix * @param {number} columns columns of the output matrix * @param {SpeedyMatrixExpr} operand */ constructor(rows, columns, operand) { super(rows, columns, operand.dtype); /** @type {SpeedyMatrixExpr} operand */ this._operand = operand; } /** * Evaluate this expression * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @returns {SpeedyMatrix} */ _evaluate(wasm, memory) { const operand = this._operand._evaluate(wasm, memory); const result = this._tempMatrix; // allocate matrices const resultptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.allocateMat32(wasm, memory, result); const operandptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.allocateMat32(wasm, memory, operand); // copy operand to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.copyToMat32(wasm, memory, operandptr, operand); // run the WASM routine this._compute(wasm, memory, resultptr, operandptr); // copy result from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, resultptr, result); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, operandptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, resultptr); // done! return result; } /** * Compute the result of this operation * @abstract * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} operandptr pointer to Mat32 */ _compute(wasm, memory, resultptr, operandptr) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.AbstractMethodError(); } } /** * @abstract binary operation */ class SpeedyMatrixBinaryOperationExpr extends SpeedyMatrixTempExpr { /** * Constructor * @param {number} rows rows of the output matrix * @param {number} columns columns of the output matrix * @param {SpeedyMatrixExpr} left left operand * @param {SpeedyMatrixExpr} right right operand */ constructor(rows, columns, left, right) { _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(left.dtype === right.dtype); super(rows, columns, left.dtype); /** @type {SpeedyMatrixExpr} left operand */ this._left = left; /** @type {SpeedyMatrixExpr} right operand */ this._right = right; } /** * Evaluate this expression * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @returns {SpeedyMatrix} */ _evaluate(wasm, memory) { const left = this._left._evaluate(wasm, memory); const right = this._right._evaluate(wasm, memory); const result = this._tempMatrix; // allocate matrices const resultptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.allocateMat32(wasm, memory, result); const leftptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.allocateMat32(wasm, memory, left); const rightptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.allocateMat32(wasm, memory, right); // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.copyToMat32(wasm, memory, leftptr, left); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.copyToMat32(wasm, memory, rightptr, right); // run the WASM routine this._compute(wasm, memory, resultptr, leftptr, rightptr); // copy output matrix from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, resultptr, result); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, rightptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, leftptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, resultptr); // done! return result; } /** * Compute the result of this operation * @abstract * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} leftptr pointer to Mat32 * @param {number} rightptr pointer to Mat32 */ _compute(wasm, memory, resultptr, leftptr, rightptr) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_2__.AbstractMethodError(); } } /** * Transpose matrix */ class SpeedyMatrixTransposeExpr extends SpeedyMatrixUnaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} operand */ constructor(operand) { super(operand.columns, operand.rows, operand); } /** * Compute result = operand^T * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} operandptr pointer to Mat32 */ _compute(wasm, memory, resultptr, operandptr) { wasm.exports.Mat32_transpose(resultptr, operandptr); } } /** * Invert square matrix */ class SpeedyMatrixInvertExpr extends SpeedyMatrixUnaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} operand */ constructor(operand) { _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(operand.rows === operand.columns); super(operand.rows, operand.columns, operand); /** @type {number} size of the matrix */ this._size = operand.rows; } /** * Compute result = operand ^ (-1) * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} operandptr pointer to Mat32 */ _compute(wasm, memory, resultptr, operandptr) { switch(this._size) { case 0: break; case 1: wasm.exports.Mat32_inverse1(resultptr, operandptr); break; case 2: wasm.exports.Mat32_inverse2(resultptr, operandptr); break; case 3: wasm.exports.Mat32_inverse3(resultptr, operandptr); break; default: wasm.exports.Mat32_qr_inverse(resultptr, operandptr); break; } } } /** * Multiply matrix by a scalar value */ class SpeedyMatrixScaleExpr extends SpeedyMatrixUnaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} operand * @param {number} scalar */ constructor(operand, scalar) { super(operand.rows, operand.columns, operand); /** @type {number} scalar value */ this._scalar = +scalar; } /** * Compute result = scalar * operand * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} operandptr pointer to Mat32 */ _compute(wasm, memory, resultptr, operandptr) { wasm.exports.Mat32_scale(resultptr, operandptr, this._scalar); } } /** * Matrix addition */ class SpeedyMatrixAddExpr extends SpeedyMatrixBinaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} left left operand * @param {SpeedyMatrixExpr} right right operand */ constructor(left, right) { _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(left.rows === right.rows && left.columns === right.columns); super(left.rows, left.columns, left, right); } /** * Compute result = left + right * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} leftptr pointer to Mat32 * @param {number} rightptr pointer to Mat32 */ _compute(wasm, memory, resultptr, leftptr, rightptr) { wasm.exports.Mat32_add(resultptr, leftptr, rightptr); } } /** * Matrix subtraction */ class SpeedyMatrixSubtractExpr extends SpeedyMatrixBinaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} left left operand * @param {SpeedyMatrixExpr} right right operand */ constructor(left, right) { _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(left.rows === right.rows && left.columns === right.columns); super(left.rows, left.columns, left, right); } /** * Compute result = left - right * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} leftptr pointer to Mat32 * @param {number} rightptr pointer to Mat32 */ _compute(wasm, memory, resultptr, leftptr, rightptr) { wasm.exports.Mat32_subtract(resultptr, leftptr, rightptr); } } /** * Matrix multiplication */ class SpeedyMatrixMultiplyExpr extends SpeedyMatrixBinaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} left left operand * @param {SpeedyMatrixExpr} right right operand */ constructor(left, right) { _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(left.columns === right.rows); super(left.rows, right.columns, left, right); } /** * Compute result = left * right * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} leftptr pointer to Mat32 * @param {number} rightptr pointer to Mat32 */ _compute(wasm, memory, resultptr, leftptr, rightptr) { wasm.exports.Mat32_multiply(resultptr, leftptr, rightptr); } } /** * Component-wise multiplication */ class SpeedyMatrixCompMultExpr extends SpeedyMatrixBinaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} left left operand * @param {SpeedyMatrixExpr} right right operand */ constructor(left, right) { _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(left.rows === right.rows && left.columns === right.columns); super(right.rows, right.columns, left, right); } /** * Compute result = left right * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} leftptr pointer to Mat32 * @param {number} rightptr pointer to Mat32 */ _compute(wasm, memory, resultptr, leftptr, rightptr) { wasm.exports.Mat32_compmult(resultptr, leftptr, rightptr); } } /** * Left-division. A \ b is equivalent to (pseudo-)inverse(A) * b */ class SpeedyMatrixLdivExpr extends SpeedyMatrixBinaryOperationExpr { /** * Constructor * @param {SpeedyMatrixExpr} left left operand * @param {SpeedyMatrixExpr} right right operand */ constructor(left, right) { const m = left.rows, n = left.columns; // TODO right doesn't need to be a column vector _utils_utils__WEBPACK_IMPORTED_MODULE_1__.Utils.assert(m >= n && right.rows === m && right.columns === 1); super(n, 1, left, right); } /** * Compute result = left \ right * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} resultptr pointer to Mat32 * @param {number} leftptr pointer to Mat32 * @param {number} rightptr pointer to Mat32 */ _compute(wasm, memory, resultptr, leftptr, rightptr) { wasm.exports.Mat32_qr_ols(resultptr, leftptr, rightptr, 2); } } /***/ }), /***/ "./src/core/speedy-matrix-factory.js": /*!*******************************************!*\ !*** ./src/core/speedy-matrix-factory.js ***! \*******************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_411390__) => { "use strict"; __nested_webpack_require_411390__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_411390__.d(__webpack_exports__, { /* harmony export */ "SpeedyMatrixFactory": () => (/* binding */ SpeedyMatrixFactory) /* harmony export */ }); /* harmony import */ var _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_411390__(/*! ./speedy-matrix-expr */ "./src/core/speedy-matrix-expr.js"); /* harmony import */ var _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_411390__(/*! ./speedy-matrix-wasm */ "./src/core/speedy-matrix-wasm.js"); /* harmony import */ var _speedy_matrix__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_411390__(/*! ./speedy-matrix */ "./src/core/speedy-matrix.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_411390__(/*! ./speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_4__ = __nested_webpack_require_411390__(/*! ../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_5__ = __nested_webpack_require_411390__(/*! ../utils/errors */ "./src/utils/errors.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-matrix-factory.js * A factory of matrices */ /** * Matrix routines */ class SpeedyMatrixFactory extends Function { /** * Constructor */ constructor() { // This factory can be invoked as a function super('...args', 'return args.length > 1 ? this._create(...args) : this._from(args[0])'); return this.bind(this); } /** * @private * * Create a new matrix filled with the specified size and entries * @param {number} rows * @param {number} [columns] * @param {number[]} [entries] in column-major format * @returns {SpeedyMatrix} */ _create(rows, columns = rows, entries = []) { return _speedy_matrix__WEBPACK_IMPORTED_MODULE_2__.SpeedyMatrix.Create(rows, columns, entries); } /** * @private * * Evaluate an expression synchronously and store the result in a new matrix * @param {SpeedyMatrixExpr} expr matrix expression * @returns {SpeedyMatrix} */ _from(expr) { return _speedy_matrix__WEBPACK_IMPORTED_MODULE_2__.SpeedyMatrix.From(expr); } /** * Create a new matrix filled with zeros with the specified size * @param {number} rows * @param {number} [columns] * @returns {SpeedyMatrix} */ Zeros(rows, columns = rows) { return _speedy_matrix__WEBPACK_IMPORTED_MODULE_2__.SpeedyMatrix.Zeros(rows, columns); } /** * Create a new matrix filled with ones with the specified size * @param {number} rows * @param {number} [columns] * @returns {SpeedyMatrix} */ Ones(rows, columns = rows) { return _speedy_matrix__WEBPACK_IMPORTED_MODULE_2__.SpeedyMatrix.Ones(rows, columns); } /** * Create an identity matrix with the specified size * @param {number} rows * @param {number} [columns] * @returns {SpeedyMatrix} */ Eye(rows, columns = rows) { return _speedy_matrix__WEBPACK_IMPORTED_MODULE_2__.SpeedyMatrix.Eye(rows, columns); } /** * Returns a promise that resolves immediately if the WebAssembly routines * are ready to be used, or as soon as they do become ready * @returns {SpeedyPromise} */ ready() { return _speedy_matrix__WEBPACK_IMPORTED_MODULE_2__.SpeedyMatrix.ready(); } /** * QR decomposition * @param {SpeedyMatrix} Q is m x n (reduced) or m x m (full), output * @param {SpeedyMatrix} R is n x n (reduced) or m x n (full), output * @param {SpeedyMatrix} mat is m x n, input * @param {object} [options] * @param {'reduced'|'full'} [options.mode] * @returns {SpeedyPromise<[SpeedyMatrix,SpeedyMatrix]>} resolves to [Q,R] */ qr(Q, R, mat, { mode = 'reduced' } = {}) { const A = mat, m = mat.rows, n = mat.columns; // validate shapes & mode if(mode == 'reduced') { if(Q.rows != m || Q.columns != n || R.rows != n || R.columns != n) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shape for reduced QR`); } else if(mode == 'full') { if(Q.rows != m || Q.columns != m || R.rows != m || R.columns != n) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shape for full QR`); } else throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid mode for QR: "${mode}"`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const Qptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, Q); const Rptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, R); const Aptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, A); // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, Aptr, A); // run the WASM routine if(mode == 'reduced') wasm.exports.Mat32_qr_reduced(Qptr, Rptr, Aptr); else wasm.exports.Mat32_qr_full(Qptr, Rptr, Aptr); // copy output matrices from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, Qptr, Q); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, Rptr, R); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, Aptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, Rptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, Qptr); // done! return [Q, R]; }); } /** * Solve a possibly overdetermined system of linear * equations Ax = b for x using ordinary least squares * @param {SpeedyMatrix} solution n x 1, output * @param {SpeedyMatrix} A m x n, m >= n, input * @param {SpeedyMatrix} b m x 1, output * @param {object} [options] * @param {'qr'} [options.method] method of resolution * @returns {SpeedyPromise} resolves to solution */ ols(solution, A, b, { method = 'qr' } = {}) { const m = A.rows, n = A.columns; const x = solution; // validate shapes if(m < n || n == 0) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Can't solve an underdetermined system of equations`); else if(b.rows != m || b.columns != 1 || x.rows != n || x.columns != 1) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shapes`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const Aptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, A); const bptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, b); const xptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, x); // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, Aptr, A); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, bptr, b); // run the WASM routine switch(method) { case 'qr': wasm.exports.Mat32_qr_ols(xptr, Aptr, bptr, 2); break; default: throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid method: "${method}"`); } // copy output matrix from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, xptr, x); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, xptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, bptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, Aptr); // done! return solution; }); } /** * Solve a system of linear equations Ax = b for x * @param {SpeedyMatrix} solution m x 1, output * @param {SpeedyMatrix} A m x m, input * @param {SpeedyMatrix} b m x 1, output * @param {object} [options] * @param {'qr'} [options.method] method of resolution * @returns {SpeedyPromise} resolves to solution */ solve(solution, A, b, { method = 'qr' } = {}) { const m = A.rows, n = A.columns; const x = solution; // validate shapes if(m != n) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Can't solve an over or underdetermined system of equations`); else if(b.rows != m || b.columns != 1 || x.rows != m || x.columns != 1) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shapes`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // select method switch(method) { case 'qr': return this.ols(x, A, b, { method }); /*case 'lu': break;*/ default: throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid method: "${method}"`); } }); } /** * Compute a perspective transformation using 4 correspondences of points * @param {SpeedyMatrix} homography 3x3 output - homography matrix * @param {SpeedyMatrix} src 2x4 input points - source coordinates * @param {SpeedyMatrix} dest 2x4 input points - destination coordinates * @returns {SpeedyPromise} resolves to homography */ perspective(homography, src, dest) { // validate shapes if(src.rows != 2 || src.columns != 4 || dest.rows != 2 || dest.columns != 4) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`You need two 2x4 input matrices to compute a perspective transformation`); else if(homography.rows != 3 || homography.columns != 3) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`The output of perspective() is a 3x3 homography`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const homptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, homography); const srcptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, src); const destptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, dest); // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, srcptr, src); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, destptr, dest); // run the WASM routine wasm.exports.Mat32_homography_ndlt4(homptr, srcptr, destptr); // copy output matrix from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, homptr, homography); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, destptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, srcptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, homptr); // done! return homography; }); } /** * Compute a perspective transformation using n >= 4 correspondences of points * @param {SpeedyMatrix} homography 3x3 output - homography matrix * @param {SpeedyMatrix} src 2 x n input points - source coordinates * @param {SpeedyMatrix} dest 2 x n input points - destination coordinates * @param {object} [options] * @param {'default'|'pransac'} [options.method] method of computation * @param {SpeedyMatrix|null} [options.mask] (pransac) 1 x n output: i-th entry will be 1 if the i-th input point is an inlier, or 0 otherwise * @param {number} [options.reprojectionError] (pransac) given in pixels, used to separate inliers from outliers of a particular model (e.g., 1 pixel) * @param {number} [options.numberOfHypotheses] (pransac) number of hypotheses to be generated up-front (e.g., 512) * @param {number} [options.bundleSize] (pransac) how many points should we check before reducing the number of viable hypotheses (e.g., 128) * @returns {SpeedyPromise} resolves to homography */ findHomography(homography, src, dest, { method = 'default', mask = null, reprojectionError = 3, numberOfHypotheses = 512, bundleSize = 128, } = {}) { // validate shapes if(src.rows != 2 || src.columns < 4 || dest.rows != 2 || dest.columns != src.columns) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`You need two 2 x n (n >= 4) input matrices to compute a homography`); else if(homography.rows != 3 || homography.columns != 3) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`The output of findHomography() is a 3x3 homography`); else if(mask != null && (mask.rows != 1 || mask.columns != src.columns)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shape of the inliers mask`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const homptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, homography); const srcptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, src); const destptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, dest); const maskptr = mask != null ? _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, mask) : 0; // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, srcptr, src); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, destptr, dest); // run the WASM routine switch(method) { case 'pransac': _utils_utils__WEBPACK_IMPORTED_MODULE_4__.Utils.assert(reprojectionError >= 0 && numberOfHypotheses > 0 && bundleSize > 0); wasm.exports.Mat32_pransac_homography(homptr, maskptr, srcptr, destptr, numberOfHypotheses, bundleSize, reprojectionError); break; case 'default': case 'dlt': // obsolete wasm.exports.Mat32_homography_ndlt(homptr, srcptr, destptr); break; default: throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Illegal method for findHomography(): "${method}"`); } // copy output matrices from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, homptr, homography); if(mask != null) _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, maskptr, mask); // deallocate matrices if(mask != null) _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, maskptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, destptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, srcptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, homptr); // done! return homography; }); } /** * Apply a perspective transformation to a set of 2D points * @param {SpeedyMatrix} dest 2 x n output matrix * @param {SpeedyMatrix} src 2 x n input matrix (a set of points) * @param {SpeedyMatrix} transform 3x3 homography matrix * @returns {SpeedyPromise} resolves to dest */ applyPerspectiveTransform(dest, src, transform) { // validate shapes if(src.rows != 2 || dest.rows != 2 || src.columns != dest.columns) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shapes`); else if(transform.rows != 3 || transform.columns != 3) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`The perspective transformation must be a 3x3 matrix`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const matptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, transform); const srcptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, src); const destptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, dest); // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, srcptr, src); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, matptr, transform); // run the WASM routine wasm.exports.Mat32_transform_perspective(destptr, srcptr, matptr); // copy output matrix from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, destptr, dest); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, destptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, srcptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, matptr); // done! return dest; }); } /** * Compute an affine transform using 3 correspondences of points * @param {SpeedyMatrix} transform 2x3 output - affine transform * @param {SpeedyMatrix} src 2x3 input points - source coordinates * @param {SpeedyMatrix} dest 2x3 input points - destination coordinates * @returns {SpeedyPromise} resolves to homography */ affine(transform, src, dest) { // validate shapes if(src.rows != 2 || src.columns != 3 || dest.rows != 2 || dest.columns != 3) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`You need two 2x3 input matrices to compute an affine transform`); else if(transform.rows != 2 || transform.columns != 3) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`The output of affine() is a 2x3 matrix`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const matptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, transform); const srcptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, src); const destptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, dest); // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, srcptr, src); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, destptr, dest); // run the WASM routine wasm.exports.Mat32_affine_direct3(matptr, srcptr, destptr); // copy output matrix from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, matptr, transform); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, destptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, srcptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, matptr); // done! return transform; }); } /** * Compute an affine transformation using n >= 3 correspondences of points * @param {SpeedyMatrix} transform 2x3 output - affine transform * @param {SpeedyMatrix} src 2 x n input points - source coordinates * @param {SpeedyMatrix} dest 2 x n input points - destination coordinates * @param {object} [options] * @param {'default'|'pransac'} [options.method] method of computation * @param {SpeedyMatrix|null} [options.mask] (pransac) 1 x n output: i-th entry will be 1 if the i-th input point is an inlier, or 0 otherwise * @param {number} [options.reprojectionError] (pransac) given in pixels, used to separate inliers from outliers of a particular model (e.g., 1 pixel) * @param {number} [options.numberOfHypotheses] (pransac) number of hypotheses to be generated up-front (e.g., 512) * @param {number} [options.bundleSize] (pransac) how many points should we check before reducing the number of viable hypotheses (e.g., 128) * @returns {SpeedyPromise} resolves to an affine transform */ findAffineTransform(transform, src, dest, { method = 'default', mask = null, reprojectionError = 3, numberOfHypotheses = 512, bundleSize = 128, } = {}) { // validate shapes if(src.rows != 2 || src.columns < 3 || dest.rows != 2 || dest.columns != src.columns) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`You need two 2 x n (n >= 3) input matrices to compute an affine transform`); else if(transform.rows != 2 || transform.columns != 3) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`The output of findAffineTransform() is a 2x3 matrix`); else if(mask != null && (mask.rows != 1 || mask.columns != src.columns)) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shape of the inliers mask`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const matptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, transform); const srcptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, src); const destptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, dest); const maskptr = mask != null ? _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, mask) : 0; // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, srcptr, src); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, destptr, dest); // run the WASM routine switch(method) { case 'pransac': _utils_utils__WEBPACK_IMPORTED_MODULE_4__.Utils.assert(reprojectionError >= 0 && numberOfHypotheses > 0 && bundleSize > 0); wasm.exports.Mat32_pransac_affine(matptr, maskptr, srcptr, destptr, numberOfHypotheses, bundleSize, reprojectionError); break; case 'default': wasm.exports.Mat32_affine_direct(matptr, srcptr, destptr); break; default: throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Illegal method for findAffineTransform(): "${method}"`); } // copy output matrices from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, matptr, transform); if(mask != null) _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, maskptr, mask); // deallocate matrices if(mask != null) _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, maskptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, destptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, srcptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, matptr); // done! return transform; }); } /** * Apply an affine transformation to a set of 2D points * @param {SpeedyMatrix} dest 2 x n output matrix * @param {SpeedyMatrix} src 2 x n input matrix (a set of points) * @param {SpeedyMatrix} transform 2x3 affine transform * @returns {SpeedyPromise} resolves to dest */ applyAffineTransform(dest, src, transform) { // validate shapes if(src.rows != 2 || dest.rows != 2 || src.columns != dest.columns) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`Invalid shapes`); else if(transform.rows != 2 || transform.columns != 3) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_5__.IllegalArgumentError(`The affine transformation must be a 2x3 matrix`); return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(({wasm, memory}) => { // allocate matrices const matptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, transform); const srcptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, src); const destptr = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.allocateMat32(wasm, memory, dest); // copy input matrices to WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, srcptr, src); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyToMat32(wasm, memory, matptr, transform); // run the WASM routine wasm.exports.Mat32_transform_affine(destptr, srcptr, matptr); // copy output matrix from WASM memory _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.copyFromMat32(wasm, memory, destptr, dest); // deallocate matrices _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, destptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, srcptr); _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.deallocateMat32(wasm, memory, matptr); // done! return dest; }); } } /***/ }), /***/ "./src/core/speedy-matrix-wasm.js": /*!****************************************!*\ !*** ./src/core/speedy-matrix-wasm.js ***! \****************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_441690__) => { "use strict"; __nested_webpack_require_441690__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_441690__.d(__webpack_exports__, { /* harmony export */ "SpeedyMatrixWASM": () => (/* binding */ SpeedyMatrixWASM) /* harmony export */ }); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_441690__(/*! ./speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_441690__(/*! ../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_441690__(/*! ../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _utils_globals__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_441690__(/*! ../utils/globals */ "./src/utils/globals.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-matrix-wasm.js * WebAssembly bridge */ /** @typedef {import('./speedy-matrix').SpeedyMatrix} SpeedyMatrix */ /** * @typedef {object} SpeedyMatrixWASMMemory a union-like helper for accessing a WebAssembly.Memory object * @property {object} as * @property {WebAssembly.Memory} as.object * @property {Uint8Array} as.uint8 * @property {Int32Array} as.int32 * @property {Uint32Array} as.uint32 * @property {Float32Array} as.float32 * @property {Float64Array} as.float64 */ /** * @typedef {object} SpeedyMatrixWASMHandle * @property {WebAssembly.Instance} wasm * @property {SpeedyMatrixWASMMemory} memory * @property {WebAssembly.Module} module */ /** @type {Uint8Array} WebAssembly binary */ const WASM_BINARY = __nested_webpack_require_441690__(/*! ./wasm/speedy-matrix.wasm.txt */ "./src/core/wasm/speedy-matrix.wasm.txt"); /** @type {WebAssembly.Instance|null} WebAssembly Instance, to be loaded asynchronously */ let _instance = null; /** @type {WebAssembly.Module|null} WebAssembly Module, to be loaded asynchronously */ let _module = null; /** @type {SpeedyMatrixWASMMemory} Augmented WebAssembly Memory object */ const _memory = (mem => ({ as: { object: mem, uint8: new Uint8Array(mem.buffer), int32: new Int32Array(mem.buffer), uint32: new Uint32Array(mem.buffer), float32: new Float32Array(mem.buffer), float64: new Float64Array(mem.buffer), }, }))(new WebAssembly.Memory({ initial: 16, // 1 MB maximum: 256 })); /** * WebAssembly utilities */ class SpeedyMatrixWASM { /** * Gets you the WASM instance, augmented memory & module * @returns {SpeedyPromise} */ static ready() { return new _speedy_promise__WEBPACK_IMPORTED_MODULE_0__.SpeedyPromise((resolve, reject) => { SpeedyMatrixWASM._ready(resolve, reject); }); } /** * Synchronously gets you the WASM instance, augmented memory & module * @returns {SpeedyMatrixWASMHandle} */ static get handle() { if(!_instance || !_module) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.WebAssemblyError(`Can't get WASM handle: routines not yet loaded`); return { wasm: _instance, memory: _memory, module: _module, }; } /** * Gets you the WASM imports bound to a memory object * @param {SpeedyMatrixWASMMemory} memory * @returns {Object} */ static imports(memory) { const obj = new SpeedyMatrixWASMImports(memory); return Object.getOwnPropertyNames(SpeedyMatrixWASMImports.prototype) .filter(property => typeof obj[property] === 'function' && property !== 'constructor') .reduce( (imports, methodName) => ((imports[methodName] = obj[methodName]), imports), Object.create(null) ); } /** * Allocate a Mat32 in WebAssembly memory without copying any data * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {SpeedyMatrix} matrix * @returns {number} pointer to the new Mat32 */ static allocateMat32(wasm, memory, matrix) { const dataptr = wasm.exports.malloc(matrix.data.byteLength); const matptr = wasm.exports.Mat32_create(matrix.rows, matrix.columns, matrix.step0, matrix.step1, matrix._data.length, dataptr); return matptr; } /** * Deallocate a Mat32 in WebAssembly * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} matptr pointer to the allocated Mat32 * @returns {number} NULL */ static deallocateMat32(wasm, memory, matptr) { const dataptr = wasm.exports.Mat32_data(matptr); wasm.exports.free(matptr); wasm.exports.free(dataptr); return 0; } /** * Copy the data of a matrix to a WebAssembly Mat32 * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} matptr pointer to a Mat32 * @param {SpeedyMatrix} matrix * @returns {number} matptr */ static copyToMat32(wasm, memory, matptr, matrix) { // We assume the following: // 1. the host uses little-endian byte ordering (just like WebAssembly) // 2. the allocated pointers are 4-byte aligned (the bump allocator guarantees this) // 3. the data type is float32 _utils_utils__WEBPACK_IMPORTED_MODULE_2__.Utils.assert( //matrix.dtype === 'float32' && matrix.data.byteLength === wasm.exports.Mat32_dataSize(matptr) ); const dataptr = wasm.exports.Mat32_data(matptr); memory.as.float32.set(matrix.data, dataptr / Float32Array.BYTES_PER_ELEMENT); return matptr; } /** * Copy the data of a WebAssembly Mat32 to a matrix * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @param {number} matptr pointer to a Mat32 * @param {SpeedyMatrix} matrix * @returns {number} matptr */ static copyFromMat32(wasm, memory, matptr, matrix) { // We assume the following: // 1. the host uses little-endian byte ordering (just like WebAssembly) // 2. the allocated pointers are 4-byte aligned (the bump allocator guarantees this) // 3. the data type is float32 _utils_utils__WEBPACK_IMPORTED_MODULE_2__.Utils.assert( //matrix.dtype === 'float32' && matrix.data.byteLength === wasm.exports.Mat32_dataSize(matptr) ); const base = wasm.exports.Mat32_data(matptr) / Float32Array.BYTES_PER_ELEMENT; for(let offset = matrix.data.length - 1; offset >= 0; offset--) matrix.data[offset] = memory.as.float32[base + offset]; return matptr; } /** * Polls the WebAssembly instance until it's ready * @param {function(SpeedyMatrixWASMHandle): void} resolve * @param {function(Error): void} reject * @param {number} [counter] */ static _ready(resolve, reject, counter = 1000) { if(_instance !== null && _module !== null) resolve({ wasm: _instance, memory: _memory, module: _module }); else if(counter <= 0) reject(new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.TimeoutError(`Can't load WASM routines`)); else setTimeout(SpeedyMatrixWASM._ready, 0, resolve, reject, counter - 1); } } /** * Methods called from WASM */ class SpeedyMatrixWASMImports { /** * Constructor * @param {SpeedyMatrixWASMMemory} memory will be bound to this object */ constructor(memory) { // find all methods of this object const methodNames = Object.getOwnPropertyNames(this.constructor.prototype) .filter(property => typeof this[property] === 'function') .filter(property => property !== 'constructor'); // bind all methods to this object methodNames.forEach(methodName => { this[methodName] = this[methodName].bind(this); }); /** @type {SpeedyMatrixWASMMemory} WASM memory */ this.memory = memory; /** @type {CStringUtils} utilities related to C strings */ this.cstring = new CStringUtils(memory); // done! return Object.freeze(this); } /** * Prints a message * @param {number} ptr pointer to char */ print(ptr) { _utils_utils__WEBPACK_IMPORTED_MODULE_2__.Utils.log(this.cstring.get(ptr)); } /** * Throws an error * @param {number} ptr pointer to char */ fatal(ptr) { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.WebAssemblyError(this.cstring.get(ptr)); } /** * Fills a memory segment with a byte * @param {number} value byte * @param {number} start memory address, inclusive * @param {number} end memory address greater than start, exclusive */ bytefill(value, start, end) { this.memory.as.uint8.fill(value, start, end); } /** * Copy a memory segment to another segment * @param {number} target memory address, where we'll start writing * @param {number} start memory address, where we'll start copying (inclusive) * @param {number} end memory address, where we'll end the copy (exclusive) */ copyWithin(target, start, end) { this.memory.as.uint8.copyWithin(target, start, end); } } /** * Utilities related to C strings */ class CStringUtils { /** * Constructor * @param {SpeedyMatrixWASMMemory} memory */ constructor(memory) { /** @type {TextDecoder} */ this._decoder = new TextDecoder('utf-8'); /** @type {SpeedyMatrixWASMMemory} */ this._memory = memory; } /** * Convert a C string to a JavaScript string * @param {number} ptr pointer to char * @returns {string} */ get(ptr) { const byte = this._memory.as.uint8; const size = this._memory.as.uint8.byteLength; let p = ptr; while(p < size && 0 !== byte[p]) ++p; return this._decoder.decode(byte.subarray(ptr, p)); } } /** * WebAssembly loader * @param {SpeedyMatrixWASMMemory} memory */ (function loadWASM(memory) { const base64decode = data => Uint8Array.from(atob(data), v => v.charCodeAt(0)); // Endianness check if(!_utils_globals__WEBPACK_IMPORTED_MODULE_3__.LITTLE_ENDIAN) throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.NotSupportedError(`Can't run WebAssembly code: not in a little-endian machine!`); // Load the WASM binary _speedy_promise__WEBPACK_IMPORTED_MODULE_0__.SpeedyPromise.resolve(WASM_BINARY) .then(data => base64decode(data)) .then(bytes => WebAssembly.instantiate(bytes, { env: { memory: memory.as.object, ...SpeedyMatrixWASM.imports(memory), } })) .then(wasm => { _instance = wasm.instance; _module = wasm.module; wasm.instance.exports.srand((Date.now() * 0.001) & 0xffffffff); // srand(time(NULL)) _utils_utils__WEBPACK_IMPORTED_MODULE_2__.Utils.log(`The WebAssembly routines have been loaded!`); }) .catch(err => { throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__.WebAssemblyError(`Can't load the WebAssembly routines: ${err}`, err); }); })(_memory); /***/ }), /***/ "./src/core/speedy-matrix.js": /*!***********************************!*\ !*** ./src/core/speedy-matrix.js ***! \***********************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_454013__) => { "use strict"; __nested_webpack_require_454013__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_454013__.d(__webpack_exports__, { /* harmony export */ "SpeedyMatrix": () => (/* binding */ SpeedyMatrix) /* harmony export */ }); /* harmony import */ var _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_454013__(/*! ./speedy-matrix-expr */ "./src/core/speedy-matrix-expr.js"); /* harmony import */ var _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_454013__(/*! ./speedy-matrix-wasm */ "./src/core/speedy-matrix-wasm.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_454013__(/*! ./speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_454013__(/*! ../utils/utils */ "./src/utils/utils.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-matrix.js * Matrix class */ /** @typedef {"float32"} SpeedyMatrixDtype Matrix data type */ /** @typedef {Float32Array} SpeedyMatrixBufferType Buffer type */ /** @typedef {Float32ArrayConstructor} SpeedyMatrixBufferTypeConstructor Buffer class */ /** @typedef {import('./speedy-matrix-wasm').SpeedyMatrixWASMMemory} SpeedyMatrixWASMMemory */ /** @typedef {import('./speedy-matrix-wasm').SpeedyMatrixWASMHandle} SpeedyMatrixWASMHandle */ /** * Matrix class */ class SpeedyMatrix extends _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr { /** * @private * * Low-level constructor * @param {number} rows number of rows * @param {number} columns number of columns * @param {number} step0 step size between two consecutive elements (e.g., 1) * @param {number} step1 step size between two consecutive columns (e.g., rows) * @param {SpeedyMatrixBufferType} data entries in column-major format */ constructor(rows, columns, step0, step1, data) { super(rows, columns, _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.DEFAULT_DTYPE); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(data.constructor === _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE[this.dtype]); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(step0 > 0 && step1 >= step0); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert( data.length + rows * columns === 0 || // empty matrix and empty buffer, or data.length === 1 + step0 * (rows - 1) + step1 * (columns - 1) // correctly sized buffer ); /** @type {number} step size between two consecutive elements */ this._step0 = step0 | 0; /** @type {number} step size between two consecutive columns */ this._step1 = step1 | 0; /** @type {SpeedyMatrixBufferType} buffer containing the entries of the matrix in column-major order */ this._data = data; } /** * Create a new matrix with the specified size and entries * @param {number} rows number of rows * @param {number} columns number of columns * @param {number[]} entries in column-major format * @param {SpeedyMatrixDtype} [dtype] data type * @returns {SpeedyMatrix} */ static Create(rows, columns, entries, dtype = _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.DEFAULT_DTYPE) { _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(rows * columns > 0, `Can't create a matrix without a shape`); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(rows * columns === entries.length, `Can't create matrix: expected ${rows * columns} entries, but found ${entries.length}`); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(Object.prototype.hasOwnProperty.call(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE, dtype), `Invalid dtype: "${dtype}"`); return new SpeedyMatrix(rows, columns, 1, rows, Reflect.construct(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE[dtype], [entries])); } /** * Create a new matrix filled with zeros with the specified size * @param {number} rows number of rows * @param {number} [columns] number of columns * @param {SpeedyMatrixDtype} [dtype] data type * @returns {SpeedyMatrix} */ static Zeros(rows, columns = rows, dtype = _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.DEFAULT_DTYPE) { _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(rows * columns > 0, `Can't create a matrix without a shape`); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(Object.prototype.hasOwnProperty.call(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE, dtype), `Invalid dtype: "${dtype}"`); return new SpeedyMatrix(rows, columns, 1, rows, Reflect.construct(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE[dtype], [rows * columns])); } /** * Create a new matrix filled with ones with the specified size * @param {number} rows number of rows * @param {number} [columns] number of columns * @param {SpeedyMatrixDtype} [dtype] data type * @returns {SpeedyMatrix} */ static Ones(rows, columns = rows, dtype = _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.DEFAULT_DTYPE) { _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(rows * columns > 0, `Can't create a matrix without a shape`); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(Object.prototype.hasOwnProperty.call(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE, dtype), `Invalid dtype: "${dtype}"`); return new SpeedyMatrix(rows, columns, 1, rows, Reflect.construct(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE[dtype], [rows * columns]).fill(1)); } /** * Create a new identity matrix with the specified size * @param {number} rows number of rows * @param {number} [columns] number of columns * @param {SpeedyMatrixDtype} [dtype] data type * @returns {SpeedyMatrix} */ static Eye(rows, columns = rows, dtype = _speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.DEFAULT_DTYPE) { _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(rows * columns > 0, `Can't create a matrix without a shape`); _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert(Object.prototype.hasOwnProperty.call(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE, dtype), `Invalid dtype: "${dtype}"`); const data = Reflect.construct(_speedy_matrix_expr__WEBPACK_IMPORTED_MODULE_0__.SpeedyMatrixExpr.BUFFER_TYPE[dtype], [rows * columns]); for(let j = Math.min(rows, columns) - 1; j >= 0; j--) data[j * rows + j] = 1; return new SpeedyMatrix(rows, columns, 1, rows, data); } /** * Evaluate an expression synchronously and store the result in a new matrix * @param {SpeedyMatrixExpr} expr matrix expression * @returns {SpeedyMatrix} */ static From(expr) { return SpeedyMatrix.Zeros(expr.rows, expr.columns, expr.dtype).setToSync(expr); } /** * Returns a promise that resolves immediately if the WebAssembly routines * are ready to be used, or as soon as they do become ready * @returns {SpeedyPromise} */ static ready() { return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(_ => void(0)); } /** * Get the underlying buffer * @returns {SpeedyMatrixBufferType} */ get data() { return this._data; } /** * Row-step * @returns {number} defaults to 1 */ get step0() { return this._step0; } /** * Column-step * @returns {number} defaults to this.rows */ get step1() { return this._step1; } /** * Extract a block from this matrix. Use a shared underlying buffer * @param {number} firstRow * @param {number} lastRow * @param {number} firstColumn * @param {number} lastColumn * @returns {SpeedyMatrix} */ block(firstRow, lastRow, firstColumn, lastColumn) { _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert( firstRow <= lastRow && firstColumn <= lastColumn, `Invalid indices: [${firstRow}:${lastRow},${firstColumn}:${lastColumn}]` ); // ensure that the indices are within bounds firstRow = Math.max(firstRow, 0); lastRow = Math.min(lastRow, this._rows - 1); firstColumn = Math.max(firstColumn, 0); lastColumn = Math.min(lastColumn, this._columns - 1); // compute the dimensions of the new submatrix const rows = lastRow - firstRow + 1; const columns = lastColumn - firstColumn + 1; // obtain the relevant portion of the data const step0 = this._step0, step1 = this._step1; const begin = firstRow * step0 + firstColumn * step1; // inclusive const end = 1 + lastRow * step0 + lastColumn * step1; // exclusive // create new matrix return new SpeedyMatrix(rows, columns, step0, step1, this._data.subarray(begin, end)); } /** * Extract a row from this matrix * @param {number} index 0-based * @returns {SpeedyMatrix} */ row(index) { return this.block(index, index, 0, this._columns - 1); } /** * Extract a column from this matrix * @param {number} index 0-based * @returns {SpeedyMatrix} */ column(index) { return this.block(0, this._rows - 1, index, index); } /** * Extract the main diagonal from this matrix * @returns {SpeedyMatrix} as a column-vector */ diagonal() { const diagsize = Math.min(this._rows, this._columns); // compute the dimensions of the new submatrix const rows = diagsize; // make it a column vector const columns = 1; // obtain the relevant portion of the data const diagstep = this._step0 + this._step1; // jump a row and a column const begin = 0; // inclusive const end = 1 + (diagsize - 1) * diagstep; // exclusive // create new matrix return new SpeedyMatrix(rows, columns, diagstep, diagstep, this._data.subarray(begin, end)); } /** * Read a single entry of this matrix * @param {number} row 0-based index * @param {number} column 0-based index * @returns {number} */ at(row, column) { if(row >= 0 && row < this._rows && column >= 0 && column < this._columns) return this._data[this._step0 * row + this._step1 * column]; else return Number.NaN; } /** * Read the entries of the matrix in column-major format * @returns {number[]} */ read() { const entries = new Array(this._rows * this._columns); const step0 = this._step0, step1 = this._step1; let i = 0; for(let column = 0; column < this._columns; column++) { for(let row = 0; row < this._rows; row++) entries[i++] = this._data[row * step0 + column * step1]; } return entries; } /** * Returns a human-readable string representation of the matrix * @returns {string} */ toString() { const DECIMALS = 5; const rows = this.rows, columns = this.columns; const entries = this.read(); const mat = /** @type {number[][]} */ ( new Array(rows) ); for(let i = 0; i < rows; i++) { mat[i] = new Array(columns); for(let j = 0; j < columns; j++) mat[i][j] = entries[j * rows + i]; } const fix = x => x.toFixed(DECIMALS); const fmt = mat.map(row => ' ' + row.map(fix).join(', ')).join(',\n'); const str = `SpeedyMatrix(rows=${rows}, columns=${columns}, data=[\n${fmt}\n])`; return str; } /** * Set the contents of this matrix to the result of an expression * @param {SpeedyMatrixExpr} expr matrix expression * @returns {SpeedyPromise} resolves to this */ setTo(expr) { return _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.ready().then(_ => { // TODO: add support for WebWorkers return this.setToSync(expr); }); } /** * Synchronously set the contents of this matrix to the result of an expression * @param {SpeedyMatrixExpr} expr matrix expression * @returns {SpeedyMatrix} this */ setToSync(expr) { const { wasm, memory } = _speedy_matrix_wasm__WEBPACK_IMPORTED_MODULE_1__.SpeedyMatrixWASM.handle; // evaluate the expression const result = expr._evaluate(wasm, memory); /* // shallow copy the results to this matrix // limitation: can't handle blocks properly // (a tree-like structure could be useful) this._rows = result.rows; this._columns = result.columns; //this._dtype = result.dtype; this._data = result.data; this._step0 = result.step0; this._step1 = result.step1; */ // validate shape _utils_utils__WEBPACK_IMPORTED_MODULE_3__.Utils.assert( this._rows === result._rows && this._columns === result._columns && this.dtype === result.dtype, `Can't set the values of a ${this.rows} x ${this.columns} ${this.dtype} matrix to those of a ${result.rows} x ${result.columns} ${result.dtype} matrix` ); // deep copy const step0 = this._step0, step1 = this._step1, rstep0 = result._step0, rstep1 = result._step1; if(step0 === rstep0 && step1 === rstep1 && this._data.length === result._data.length) { // fast copy this._data.set(result._data); } else { // copy each element for(let column = this._columns - 1; column >= 0; column--) { for(let row = this._rows - 1; row >= 0; row--) this._data[row * step0 + column * step1] = result._data[row * rstep0 + column * rstep1]; } } // done! return this; } /** * Fill this matrix with a scalar value * @param {number} value * @returns {SpeedyPromise} resolves to this */ fill(value) { this.fillSync(value); return _speedy_promise__WEBPACK_IMPORTED_MODULE_2__.SpeedyPromise.resolve(this); } /** * Synchronously fill this matrix with a scalar value * @param {number} value * @returns {SpeedyMatrix} this */ fillSync(value) { value = +value; if(this._rows * this._columns === this._data.length) { this._data.fill(value); return this; } for(let column = 0; column < this._columns; column++) { for(let row = 0; row < this._rows; row++) { this._data[row * this._step0 + column * this._step1] = value; } } return this; } /** * Evaluate this expression * @param {WebAssembly.Instance} wasm * @param {SpeedyMatrixWASMMemory} memory * @returns {SpeedyMatrix} */ _evaluate(wasm, memory) { return this; } } /***/ }), /***/ "./src/core/speedy-media-source.js": /*!*****************************************!*\ !*** ./src/core/speedy-media-source.js ***! \*****************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __nested_webpack_require_470481__) => { "use strict"; __nested_webpack_require_470481__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_470481__.d(__webpack_exports__, { /* harmony export */ "SpeedyMediaSource": () => (/* binding */ SpeedyMediaSource) /* harmony export */ }); /* harmony import */ var _utils_utils__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_470481__(/*! ../utils/utils */ "./src/utils/utils.js"); /* harmony import */ var _speedy_promise__WEBPACK_IMPORTED_MODULE_1__ = __nested_webpack_require_470481__(/*! ./speedy-promise */ "./src/core/speedy-promise.js"); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_2__ = __nested_webpack_require_470481__(/*! ../utils/errors */ "./src/utils/errors.js"); /* harmony import */ var _utils_types__WEBPACK_IMPORTED_MODULE_3__ = __nested_webpack_require_470481__(/*! ../utils/types */ "./src/utils/types.js"); /* * speedy-vision.js * GPU-accelerated Computer Vision for JavaScript * Copyright 2020-2022 Alexandre Martins * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * speedy-media-source.js * Wrappers around ,