@srsergio/taptapp-ar 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/dist/compiler/aframe.d.ts +1 -0
- package/dist/compiler/aframe.js +275 -0
- package/dist/compiler/compiler-base.d.ts +12 -0
- package/dist/compiler/compiler-base.js +165 -0
- package/dist/compiler/compiler.d.ts +9 -0
- package/dist/compiler/compiler.js +24 -0
- package/dist/compiler/compiler.worker.d.ts +1 -0
- package/dist/compiler/compiler.worker.js +28 -0
- package/dist/compiler/controller.d.ts +101 -0
- package/dist/compiler/controller.js +400 -0
- package/dist/compiler/controller.worker.d.ts +1 -0
- package/dist/compiler/controller.worker.js +61 -0
- package/dist/compiler/detector/crop-detector.d.ts +65 -0
- package/dist/compiler/detector/crop-detector.js +59 -0
- package/dist/compiler/detector/detector.d.ts +98 -0
- package/dist/compiler/detector/detector.js +1049 -0
- package/dist/compiler/detector/freak.d.ts +1 -0
- package/dist/compiler/detector/freak.js +89 -0
- package/dist/compiler/detector/kernels/cpu/binomialFilter.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/binomialFilter.js +51 -0
- package/dist/compiler/detector/kernels/cpu/buildExtremas.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/buildExtremas.js +89 -0
- package/dist/compiler/detector/kernels/cpu/computeExtremaAngles.d.ts +7 -0
- package/dist/compiler/detector/kernels/cpu/computeExtremaAngles.js +79 -0
- package/dist/compiler/detector/kernels/cpu/computeExtremaFreak.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/computeExtremaFreak.js +68 -0
- package/dist/compiler/detector/kernels/cpu/computeFreakDescriptors.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/computeFreakDescriptors.js +57 -0
- package/dist/compiler/detector/kernels/cpu/computeLocalization.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/computeLocalization.js +54 -0
- package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.js +118 -0
- package/dist/compiler/detector/kernels/cpu/downsampleBilinear.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/downsampleBilinear.js +29 -0
- package/dist/compiler/detector/kernels/cpu/extremaReduction.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/extremaReduction.js +50 -0
- package/dist/compiler/detector/kernels/cpu/fakeShader.d.ts +20 -0
- package/dist/compiler/detector/kernels/cpu/fakeShader.js +80 -0
- package/dist/compiler/detector/kernels/cpu/index.d.ts +1 -0
- package/dist/compiler/detector/kernels/cpu/index.js +25 -0
- package/dist/compiler/detector/kernels/cpu/prune.d.ts +1 -0
- package/dist/compiler/detector/kernels/cpu/prune.js +103 -0
- package/dist/compiler/detector/kernels/cpu/smoothHistograms.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/smoothHistograms.js +47 -0
- package/dist/compiler/detector/kernels/cpu/upsampleBilinear.d.ts +6 -0
- package/dist/compiler/detector/kernels/cpu/upsampleBilinear.js +43 -0
- package/dist/compiler/detector/kernels/index.d.ts +1 -0
- package/dist/compiler/detector/kernels/index.js +2 -0
- package/dist/compiler/detector/kernels/webgl/binomialFilter.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/binomialFilter.js +67 -0
- package/dist/compiler/detector/kernels/webgl/buildExtremas.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/buildExtremas.js +101 -0
- package/dist/compiler/detector/kernels/webgl/computeExtremaAngles.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/computeExtremaAngles.js +78 -0
- package/dist/compiler/detector/kernels/webgl/computeExtremaFreak.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/computeExtremaFreak.js +86 -0
- package/dist/compiler/detector/kernels/webgl/computeFreakDescriptors.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/computeFreakDescriptors.js +52 -0
- package/dist/compiler/detector/kernels/webgl/computeLocalization.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/computeLocalization.js +58 -0
- package/dist/compiler/detector/kernels/webgl/computeOrientationHistograms.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/computeOrientationHistograms.js +116 -0
- package/dist/compiler/detector/kernels/webgl/downsampleBilinear.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/downsampleBilinear.js +46 -0
- package/dist/compiler/detector/kernels/webgl/extremaReduction.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/extremaReduction.js +48 -0
- package/dist/compiler/detector/kernels/webgl/index.d.ts +1 -0
- package/dist/compiler/detector/kernels/webgl/index.js +25 -0
- package/dist/compiler/detector/kernels/webgl/smoothHistograms.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/smoothHistograms.js +49 -0
- package/dist/compiler/detector/kernels/webgl/upsampleBilinear.d.ts +6 -0
- package/dist/compiler/detector/kernels/webgl/upsampleBilinear.js +56 -0
- package/dist/compiler/estimation/esimate-experiment.d.ts +5 -0
- package/dist/compiler/estimation/esimate-experiment.js +267 -0
- package/dist/compiler/estimation/estimate.d.ts +5 -0
- package/dist/compiler/estimation/estimate.js +51 -0
- package/dist/compiler/estimation/estimator.d.ts +13 -0
- package/dist/compiler/estimation/estimator.js +30 -0
- package/dist/compiler/estimation/refine-estimate-experiment.d.ts +6 -0
- package/dist/compiler/estimation/refine-estimate-experiment.js +429 -0
- package/dist/compiler/estimation/refine-estimate.d.ts +6 -0
- package/dist/compiler/estimation/refine-estimate.js +299 -0
- package/dist/compiler/estimation/utils.d.ts +10 -0
- package/dist/compiler/estimation/utils.js +80 -0
- package/dist/compiler/image-list.d.ts +13 -0
- package/dist/compiler/image-list.js +52 -0
- package/dist/compiler/index.d.ts +3 -0
- package/dist/compiler/index.js +10 -0
- package/dist/compiler/input-loader.d.ts +23 -0
- package/dist/compiler/input-loader.js +88 -0
- package/dist/compiler/matching/hamming-distance.d.ts +1 -0
- package/dist/compiler/matching/hamming-distance.js +20 -0
- package/dist/compiler/matching/hierarchical-clustering.d.ts +7 -0
- package/dist/compiler/matching/hierarchical-clustering.js +109 -0
- package/dist/compiler/matching/hough.d.ts +1 -0
- package/dist/compiler/matching/hough.js +169 -0
- package/dist/compiler/matching/matcher.d.ts +28 -0
- package/dist/compiler/matching/matcher.js +48 -0
- package/dist/compiler/matching/matching.d.ts +41 -0
- package/dist/compiler/matching/matching.js +197 -0
- package/dist/compiler/matching/ransacHomography.d.ts +1 -0
- package/dist/compiler/matching/ransacHomography.js +136 -0
- package/dist/compiler/offline-compiler.d.ts +10 -0
- package/dist/compiler/offline-compiler.js +450 -0
- package/dist/compiler/tensorflow-setup.d.ts +7 -0
- package/dist/compiler/tensorflow-setup.js +73 -0
- package/dist/compiler/three.d.ts +66 -0
- package/dist/compiler/three.js +310 -0
- package/dist/compiler/tracker/extract-utils.d.ts +1 -0
- package/dist/compiler/tracker/extract-utils.js +29 -0
- package/dist/compiler/tracker/extract.d.ts +4 -0
- package/dist/compiler/tracker/extract.js +349 -0
- package/dist/compiler/tracker/tracker.d.ts +38 -0
- package/dist/compiler/tracker/tracker.js +327 -0
- package/dist/compiler/utils/cumsum.d.ts +5 -0
- package/dist/compiler/utils/cumsum.js +39 -0
- package/dist/compiler/utils/geometry.d.ts +8 -0
- package/dist/compiler/utils/geometry.js +101 -0
- package/dist/compiler/utils/homography.d.ts +1 -0
- package/dist/compiler/utils/homography.js +138 -0
- package/dist/compiler/utils/images.d.ts +24 -0
- package/dist/compiler/utils/images.js +99 -0
- package/dist/compiler/utils/randomizer.d.ts +5 -0
- package/dist/compiler/utils/randomizer.js +25 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/react/AREditor.d.ts +5 -0
- package/dist/react/AREditor.js +159 -0
- package/dist/react/ProgressDialog.d.ts +13 -0
- package/dist/react/ProgressDialog.js +57 -0
- package/dist/react/types.d.ts +22 -0
- package/dist/react/types.js +14 -0
- package/package.json +53 -0
- package/src/astro/ARScene.astro +59 -0
- package/src/astro/ARVideoTrigger.astro +73 -0
- package/src/astro/overlays/ErrorOverlay.astro +40 -0
- package/src/astro/overlays/LoadingOverlay.astro +28 -0
- package/src/astro/overlays/ScanningOverlay.astro +119 -0
- package/src/astro/scripts/ARScripts.astro +118 -0
- package/src/astro/styles/ARStyles.astro +147 -0
- package/src/compiler/aframe.js +343 -0
- package/src/compiler/compiler-base.js +195 -0
- package/src/compiler/compiler.js +25 -0
- package/src/compiler/compiler.worker.js +30 -0
- package/src/compiler/controller.js +473 -0
- package/src/compiler/controller.worker.js +77 -0
- package/src/compiler/detector/crop-detector.js +68 -0
- package/src/compiler/detector/detector.js +1130 -0
- package/src/compiler/detector/freak.js +91 -0
- package/src/compiler/detector/kernels/cpu/binomialFilter.js +59 -0
- package/src/compiler/detector/kernels/cpu/buildExtremas.js +108 -0
- package/src/compiler/detector/kernels/cpu/computeExtremaAngles.js +91 -0
- package/src/compiler/detector/kernels/cpu/computeExtremaFreak.js +92 -0
- package/src/compiler/detector/kernels/cpu/computeFreakDescriptors.js +68 -0
- package/src/compiler/detector/kernels/cpu/computeLocalization.js +71 -0
- package/src/compiler/detector/kernels/cpu/computeOrientationHistograms.js +141 -0
- package/src/compiler/detector/kernels/cpu/downsampleBilinear.js +33 -0
- package/src/compiler/detector/kernels/cpu/extremaReduction.js +53 -0
- package/src/compiler/detector/kernels/cpu/fakeShader.js +88 -0
- package/src/compiler/detector/kernels/cpu/index.js +26 -0
- package/src/compiler/detector/kernels/cpu/prune.js +114 -0
- package/src/compiler/detector/kernels/cpu/smoothHistograms.js +57 -0
- package/src/compiler/detector/kernels/cpu/upsampleBilinear.js +51 -0
- package/src/compiler/detector/kernels/index.js +2 -0
- package/src/compiler/detector/kernels/webgl/binomialFilter.js +72 -0
- package/src/compiler/detector/kernels/webgl/buildExtremas.js +109 -0
- package/src/compiler/detector/kernels/webgl/computeExtremaAngles.js +82 -0
- package/src/compiler/detector/kernels/webgl/computeExtremaFreak.js +105 -0
- package/src/compiler/detector/kernels/webgl/computeFreakDescriptors.js +56 -0
- package/src/compiler/detector/kernels/webgl/computeLocalization.js +70 -0
- package/src/compiler/detector/kernels/webgl/computeOrientationHistograms.js +129 -0
- package/src/compiler/detector/kernels/webgl/downsampleBilinear.js +50 -0
- package/src/compiler/detector/kernels/webgl/extremaReduction.js +50 -0
- package/src/compiler/detector/kernels/webgl/index.js +26 -0
- package/src/compiler/detector/kernels/webgl/smoothHistograms.js +53 -0
- package/src/compiler/detector/kernels/webgl/upsampleBilinear.js +62 -0
- package/src/compiler/estimation/esimate-experiment.js +316 -0
- package/src/compiler/estimation/estimate.js +67 -0
- package/src/compiler/estimation/estimator.js +34 -0
- package/src/compiler/estimation/refine-estimate-experiment.js +512 -0
- package/src/compiler/estimation/refine-estimate.js +365 -0
- package/src/compiler/estimation/utils.js +97 -0
- package/src/compiler/image-list.js +62 -0
- package/src/compiler/index.js +13 -0
- package/src/compiler/input-loader.js +107 -0
- package/src/compiler/matching/hamming-distance.js +23 -0
- package/src/compiler/matching/hierarchical-clustering.js +131 -0
- package/src/compiler/matching/hough.js +206 -0
- package/src/compiler/matching/matcher.js +59 -0
- package/src/compiler/matching/matching.js +237 -0
- package/src/compiler/matching/ransacHomography.js +192 -0
- package/src/compiler/offline-compiler.js +553 -0
- package/src/compiler/tensorflow-setup.js +88 -0
- package/src/compiler/three.js +368 -0
- package/src/compiler/tracker/extract-utils.js +34 -0
- package/src/compiler/tracker/extract.js +419 -0
- package/src/compiler/tracker/tracker.js +397 -0
- package/src/compiler/utils/cumsum.js +40 -0
- package/src/compiler/utils/geometry.js +114 -0
- package/src/compiler/utils/homography.js +150 -0
- package/src/compiler/utils/images.js +111 -0
- package/src/compiler/utils/randomizer.js +29 -0
- package/src/index.ts +8 -0
- package/src/react/AREditor.tsx +394 -0
- package/src/react/ProgressDialog.tsx +185 -0
- package/src/react/types.ts +35 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import * as tf from "@tensorflow/tfjs";
|
|
2
|
+
import { buildModelViewProjectionTransform, computeScreenCoordiate } from "../estimation/utils.js";
|
|
3
|
+
|
|
4
|
+
const AR2_DEFAULT_TS = 6;
|
|
5
|
+
const AR2_DEFAULT_TS_GAP = 1;
|
|
6
|
+
const AR2_SEARCH_SIZE = 10;
|
|
7
|
+
const AR2_SEARCH_GAP = 1;
|
|
8
|
+
const AR2_SIM_THRESH = 0.8;
|
|
9
|
+
|
|
10
|
+
const TRACKING_KEYFRAME = 1; // 0: 256px, 1: 128px
|
|
11
|
+
|
|
12
|
+
// For some mobile device, only 16bit floating point texture is supported
|
|
13
|
+
// ref: https://www.tensorflow.org/js/guide/platform_environment#precision
|
|
14
|
+
// Empirical results shows that modelViewProjectTransform can go up beyond that, resulting in error
|
|
15
|
+
// We get around this by dividing the transform matrix by 1000, and then multiply back inside webgl program
|
|
16
|
+
const PRECISION_ADJUST = 1000;
|
|
17
|
+
|
|
18
|
+
class Tracker {
|
|
19
|
+
constructor(
|
|
20
|
+
markerDimensions,
|
|
21
|
+
trackingDataList,
|
|
22
|
+
projectionTransform,
|
|
23
|
+
inputWidth,
|
|
24
|
+
inputHeight,
|
|
25
|
+
debugMode = false,
|
|
26
|
+
) {
|
|
27
|
+
this.markerDimensions = markerDimensions;
|
|
28
|
+
this.trackingDataList = trackingDataList;
|
|
29
|
+
this.projectionTransform = projectionTransform;
|
|
30
|
+
this.debugMode = debugMode;
|
|
31
|
+
|
|
32
|
+
this.trackingKeyframeList = [];
|
|
33
|
+
for (let i = 0; i < trackingDataList.length; i++) {
|
|
34
|
+
this.trackingKeyframeList.push(trackingDataList[i][TRACKING_KEYFRAME]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// prebuild feature and marker pixel tensors
|
|
38
|
+
let maxCount = 0;
|
|
39
|
+
for (let i = 0; i < this.trackingKeyframeList.length; i++) {
|
|
40
|
+
maxCount = Math.max(maxCount, this.trackingKeyframeList[i].points.length);
|
|
41
|
+
}
|
|
42
|
+
this.featurePointsListT = [];
|
|
43
|
+
this.imagePixelsListT = [];
|
|
44
|
+
this.imagePropertiesListT = [];
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < this.trackingKeyframeList.length; i++) {
|
|
47
|
+
const { featurePoints, imagePixels, imageProperties } = this._prebuild(
|
|
48
|
+
this.trackingKeyframeList[i],
|
|
49
|
+
maxCount,
|
|
50
|
+
);
|
|
51
|
+
this.featurePointsListT[i] = featurePoints;
|
|
52
|
+
this.imagePixelsListT[i] = imagePixels;
|
|
53
|
+
this.imagePropertiesListT[i] = imageProperties;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.kernelCaches = {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
dummyRun(inputT) {
|
|
60
|
+
let transform = [
|
|
61
|
+
[1, 1, 1, 1],
|
|
62
|
+
[1, 1, 1, 1],
|
|
63
|
+
[1, 1, 1, 1],
|
|
64
|
+
];
|
|
65
|
+
for (let targetIndex = 0; targetIndex < this.featurePointsListT.length; targetIndex++) {
|
|
66
|
+
this.track(inputT, transform, targetIndex);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
track(inputImageT, lastModelViewTransform, targetIndex) {
|
|
71
|
+
let debugExtra = {};
|
|
72
|
+
|
|
73
|
+
const modelViewProjectionTransform = buildModelViewProjectionTransform(
|
|
74
|
+
this.projectionTransform,
|
|
75
|
+
lastModelViewTransform,
|
|
76
|
+
);
|
|
77
|
+
const modelViewProjectionTransformT = this._buildAdjustedModelViewTransform(
|
|
78
|
+
modelViewProjectionTransform,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const markerWidth = this.markerDimensions[targetIndex][0];
|
|
82
|
+
const markerHeight = this.markerDimensions[targetIndex][1];
|
|
83
|
+
const keyframeWidth = this.trackingKeyframeList[targetIndex].width;
|
|
84
|
+
const keyframeHeight = this.trackingKeyframeList[targetIndex].height;
|
|
85
|
+
|
|
86
|
+
const featurePointsT = this.featurePointsListT[targetIndex];
|
|
87
|
+
const imagePixelsT = this.imagePixelsListT[targetIndex];
|
|
88
|
+
const imagePropertiesT = this.imagePropertiesListT[targetIndex];
|
|
89
|
+
|
|
90
|
+
const projectedImageT = this._computeProjection(
|
|
91
|
+
modelViewProjectionTransformT,
|
|
92
|
+
inputImageT,
|
|
93
|
+
targetIndex,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const { matchingPointsT, simT } = this._computeMatching(
|
|
97
|
+
featurePointsT,
|
|
98
|
+
imagePixelsT,
|
|
99
|
+
imagePropertiesT,
|
|
100
|
+
projectedImageT,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const matchingPoints = matchingPointsT.arraySync();
|
|
104
|
+
const sim = simT.arraySync();
|
|
105
|
+
|
|
106
|
+
const trackingFrame = this.trackingKeyframeList[targetIndex];
|
|
107
|
+
const worldCoords = [];
|
|
108
|
+
const screenCoords = [];
|
|
109
|
+
const goodTrack = [];
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < matchingPoints.length; i++) {
|
|
112
|
+
if (sim[i] > AR2_SIM_THRESH && i < trackingFrame.points.length) {
|
|
113
|
+
goodTrack.push(i);
|
|
114
|
+
const point = computeScreenCoordiate(
|
|
115
|
+
modelViewProjectionTransform,
|
|
116
|
+
matchingPoints[i][0],
|
|
117
|
+
matchingPoints[i][1],
|
|
118
|
+
);
|
|
119
|
+
screenCoords.push(point);
|
|
120
|
+
worldCoords.push({
|
|
121
|
+
x: trackingFrame.points[i].x / trackingFrame.scale,
|
|
122
|
+
y: trackingFrame.points[i].y / trackingFrame.scale,
|
|
123
|
+
z: 0,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (this.debugMode) {
|
|
129
|
+
debugExtra = {
|
|
130
|
+
projectedImage: projectedImageT.arraySync(),
|
|
131
|
+
matchingPoints: matchingPointsT.arraySync(),
|
|
132
|
+
goodTrack,
|
|
133
|
+
trackedPoints: screenCoords,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// tensors cleanup
|
|
138
|
+
modelViewProjectionTransformT.dispose();
|
|
139
|
+
projectedImageT.dispose();
|
|
140
|
+
matchingPointsT.dispose();
|
|
141
|
+
simT.dispose();
|
|
142
|
+
|
|
143
|
+
return { worldCoords, screenCoords, debugExtra };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_computeMatching(featurePointsT, imagePixelsT, imagePropertiesT, projectedImageT) {
|
|
147
|
+
const templateOneSize = AR2_DEFAULT_TS;
|
|
148
|
+
const templateSize = templateOneSize * 2 + 1;
|
|
149
|
+
const templateGap = AR2_DEFAULT_TS_GAP;
|
|
150
|
+
const searchOneSize = AR2_SEARCH_SIZE * templateGap;
|
|
151
|
+
const searchGap = AR2_SEARCH_GAP;
|
|
152
|
+
const searchSize = searchOneSize * 2 + 1;
|
|
153
|
+
const targetHeight = projectedImageT.shape[0];
|
|
154
|
+
const targetWidth = projectedImageT.shape[1];
|
|
155
|
+
const featureCount = featurePointsT.shape[0];
|
|
156
|
+
|
|
157
|
+
if (!this.kernelCaches.computeMatching) {
|
|
158
|
+
const kernel1 = {
|
|
159
|
+
variableNames: ["features", "markerPixels", "markerProperties", "targetPixels"],
|
|
160
|
+
outputShape: [featureCount, searchSize * searchSize],
|
|
161
|
+
userCode: `
|
|
162
|
+
void main() {
|
|
163
|
+
ivec2 coords = getOutputCoords();
|
|
164
|
+
|
|
165
|
+
int featureIndex = coords[0];
|
|
166
|
+
int searchOffsetIndex = coords[1];
|
|
167
|
+
|
|
168
|
+
int markerWidth = int(getMarkerProperties(0));
|
|
169
|
+
int markerHeight = int(getMarkerProperties(1));
|
|
170
|
+
float markerScale = getMarkerProperties(2);
|
|
171
|
+
|
|
172
|
+
int searchOffsetX = imod(searchOffsetIndex, ${searchSize}) * ${searchGap};
|
|
173
|
+
int searchOffsetY = searchOffsetIndex / ${searchSize} * ${searchGap};
|
|
174
|
+
|
|
175
|
+
int sCenterX = int(getFeatures(featureIndex, 0) * markerScale);
|
|
176
|
+
int sCenterY = int(getFeatures(featureIndex, 1) * markerScale);
|
|
177
|
+
|
|
178
|
+
int sx = sCenterX + searchOffsetX - ${searchOneSize};
|
|
179
|
+
int sy = sCenterY + searchOffsetY - ${searchOneSize};
|
|
180
|
+
|
|
181
|
+
if (sx < ${templateOneSize} || sx >= (${targetWidth} - ${templateOneSize}) || sy < ${templateOneSize} || sy >= (${targetHeight} - ${templateOneSize})) {
|
|
182
|
+
setOutput(-2.);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
float sumPoint = 0.;
|
|
186
|
+
float sumPointSquare = 0.;
|
|
187
|
+
float sumTemplate = 0.;
|
|
188
|
+
float sumTemplateSquare = 0.;
|
|
189
|
+
float sumPointTemplate = 0.;
|
|
190
|
+
|
|
191
|
+
for (int templateOffsetY = 0; templateOffsetY < ${templateSize}; templateOffsetY++) {
|
|
192
|
+
for (int templateOffsetX = 0; templateOffsetX < ${templateSize}; templateOffsetX++) {
|
|
193
|
+
int fx2 = sCenterX + templateOffsetX - ${templateOneSize};
|
|
194
|
+
int fy2 = sCenterY + templateOffsetY - ${templateOneSize};
|
|
195
|
+
|
|
196
|
+
int sx2 = sx + templateOffsetX - ${templateOneSize};
|
|
197
|
+
int sy2 = sy + templateOffsetY - ${templateOneSize};
|
|
198
|
+
|
|
199
|
+
int markerPixelIndex = fy2 * markerWidth + fx2;
|
|
200
|
+
float markerPixel = getMarkerPixels(markerPixelIndex);
|
|
201
|
+
float targetPixel = getTargetPixels(sy2, sx2);
|
|
202
|
+
|
|
203
|
+
sumTemplate += markerPixel;
|
|
204
|
+
sumTemplateSquare += markerPixel * markerPixel;
|
|
205
|
+
sumPoint += targetPixel;
|
|
206
|
+
sumPointSquare += targetPixel * targetPixel;
|
|
207
|
+
sumPointTemplate += targetPixel * markerPixel;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Normalized cross-correlation
|
|
212
|
+
// !important divide first avoid overflow (e.g. sumPoint / count * sumPoint)
|
|
213
|
+
float count = float(${templateSize} * ${templateSize});
|
|
214
|
+
float pointVariance = sqrt(sumPointSquare - sumPoint / count * sumPoint);
|
|
215
|
+
float templateVariance = sqrt(sumTemplateSquare - sumTemplate / count * sumTemplate);
|
|
216
|
+
|
|
217
|
+
if (pointVariance < 0.0000001) {
|
|
218
|
+
setOutput(-3.);
|
|
219
|
+
} else if (templateVariance < 0.0000001) {
|
|
220
|
+
//setOutput(sumTemplate);
|
|
221
|
+
setOutput(-4.);
|
|
222
|
+
} else {
|
|
223
|
+
sumPointTemplate -= sumPoint / count * sumTemplate;
|
|
224
|
+
float sim = sumPointTemplate / pointVariance / templateVariance;
|
|
225
|
+
setOutput(sim);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
`,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const kernel2 = {
|
|
233
|
+
variableNames: ["featurePoints", "markerProperties", "maxIndex"],
|
|
234
|
+
outputShape: [featureCount, 2], // [x, y]
|
|
235
|
+
userCode: `
|
|
236
|
+
void main() {
|
|
237
|
+
ivec2 coords = getOutputCoords();
|
|
238
|
+
|
|
239
|
+
float markerScale = getMarkerProperties(2);
|
|
240
|
+
|
|
241
|
+
int featureIndex = coords[0];
|
|
242
|
+
|
|
243
|
+
int maxIndex = int(getMaxIndex(featureIndex));
|
|
244
|
+
int searchLocationIndex = maxIndex / ${searchSize * searchSize};
|
|
245
|
+
int searchOffsetIndex = imod(maxIndex, ${searchSize * searchSize});
|
|
246
|
+
|
|
247
|
+
if (coords[1] == 0) {
|
|
248
|
+
int searchOffsetX = imod(searchOffsetIndex, ${searchSize}) * ${searchGap};
|
|
249
|
+
setOutput(getFeaturePoints(featureIndex, 0) + float(searchOffsetX - ${searchOneSize}) / markerScale);
|
|
250
|
+
}
|
|
251
|
+
else if (coords[1] == 1) {
|
|
252
|
+
int searchOffsetY = searchOffsetIndex / ${searchSize} * ${searchGap};
|
|
253
|
+
setOutput(getFeaturePoints(featureIndex, 1) + float(searchOffsetY - ${searchOneSize}) / markerScale);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
`,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const kernel3 = {
|
|
260
|
+
variableNames: ["sims", "maxIndex"],
|
|
261
|
+
outputShape: [featureCount],
|
|
262
|
+
userCode: `
|
|
263
|
+
void main() {
|
|
264
|
+
int featureIndex = getOutputCoords();
|
|
265
|
+
int maxIndex = int(getMaxIndex(featureIndex));
|
|
266
|
+
setOutput(getSims(featureIndex, maxIndex));
|
|
267
|
+
}
|
|
268
|
+
`,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
this.kernelCaches.computeMatching = [kernel1, kernel2, kernel3];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return tf.tidy(() => {
|
|
275
|
+
const programs = this.kernelCaches.computeMatching;
|
|
276
|
+
const allSims = this._compileAndRun(programs[0], [
|
|
277
|
+
featurePointsT,
|
|
278
|
+
imagePixelsT,
|
|
279
|
+
imagePropertiesT,
|
|
280
|
+
projectedImageT,
|
|
281
|
+
]);
|
|
282
|
+
const maxIndex = allSims.argMax(1);
|
|
283
|
+
const matchingPointsT = this._compileAndRun(programs[1], [
|
|
284
|
+
featurePointsT,
|
|
285
|
+
imagePropertiesT,
|
|
286
|
+
maxIndex,
|
|
287
|
+
]);
|
|
288
|
+
const simT = this._compileAndRun(programs[2], [allSims, maxIndex]);
|
|
289
|
+
return { matchingPointsT, simT };
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
_computeProjection(modelViewProjectionTransformT, inputImageT, targetIndex) {
|
|
294
|
+
const markerWidth = this.trackingKeyframeList[targetIndex].width;
|
|
295
|
+
const markerHeight = this.trackingKeyframeList[targetIndex].height;
|
|
296
|
+
const markerScale = this.trackingKeyframeList[targetIndex].scale;
|
|
297
|
+
const kernelKey = markerWidth + "-" + markerHeight + "-" + markerScale;
|
|
298
|
+
|
|
299
|
+
if (!this.kernelCaches.computeProjection) {
|
|
300
|
+
this.kernelCaches.computeProjection = {};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (!this.kernelCaches.computeProjection[kernelKey]) {
|
|
304
|
+
const kernel = {
|
|
305
|
+
variableNames: ["M", "pixel"],
|
|
306
|
+
outputShape: [markerHeight, markerWidth],
|
|
307
|
+
userCode: `
|
|
308
|
+
void main() {
|
|
309
|
+
ivec2 coords = getOutputCoords();
|
|
310
|
+
|
|
311
|
+
float m00 = getM(0, 0) * ${PRECISION_ADJUST}.;
|
|
312
|
+
float m01 = getM(0, 1) * ${PRECISION_ADJUST}.;
|
|
313
|
+
float m03 = getM(0, 3) * ${PRECISION_ADJUST}.;
|
|
314
|
+
float m10 = getM(1, 0) * ${PRECISION_ADJUST}.;
|
|
315
|
+
float m11 = getM(1, 1) * ${PRECISION_ADJUST}.;
|
|
316
|
+
float m13 = getM(1, 3) * ${PRECISION_ADJUST}.;
|
|
317
|
+
float m20 = getM(2, 0) * ${PRECISION_ADJUST}.;
|
|
318
|
+
float m21 = getM(2, 1) * ${PRECISION_ADJUST}.;
|
|
319
|
+
float m23 = getM(2, 3) * ${PRECISION_ADJUST}.;
|
|
320
|
+
|
|
321
|
+
float y = float(coords[0]) / float(${markerScale});
|
|
322
|
+
float x = float(coords[1]) / float(${markerScale});
|
|
323
|
+
float uz = (x * m20) + (y * m21) + m23;
|
|
324
|
+
float oneOverUz = 1. / uz;
|
|
325
|
+
|
|
326
|
+
float ux = (x * m00) + (y * m01) + m03;
|
|
327
|
+
float uy = (x * m10) + (y * m11) + m13;
|
|
328
|
+
|
|
329
|
+
ux = floor(ux * oneOverUz + 0.5);
|
|
330
|
+
uy = floor(uy * oneOverUz + 0.5);
|
|
331
|
+
setOutput(getPixel(int(uy), int(ux)));
|
|
332
|
+
}
|
|
333
|
+
`,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
this.kernelCaches.computeProjection[kernelKey] = kernel;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return tf.tidy(() => {
|
|
340
|
+
const program = this.kernelCaches.computeProjection[kernelKey];
|
|
341
|
+
const result = this._compileAndRun(program, [modelViewProjectionTransformT, inputImageT]);
|
|
342
|
+
return result;
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
_buildAdjustedModelViewTransform(modelViewProjectionTransform) {
|
|
347
|
+
return tf.tidy(() => {
|
|
348
|
+
let modelViewProjectionTransformAdjusted = [];
|
|
349
|
+
for (let i = 0; i < modelViewProjectionTransform.length; i++) {
|
|
350
|
+
modelViewProjectionTransformAdjusted.push([]);
|
|
351
|
+
for (let j = 0; j < modelViewProjectionTransform[i].length; j++) {
|
|
352
|
+
modelViewProjectionTransformAdjusted[i].push(
|
|
353
|
+
modelViewProjectionTransform[i][j] / PRECISION_ADJUST,
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const t = tf.tensor(modelViewProjectionTransformAdjusted, [3, 4]);
|
|
358
|
+
return t;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
_prebuild(trackingFrame, maxCount) {
|
|
363
|
+
return tf.tidy(() => {
|
|
364
|
+
const scale = trackingFrame.scale;
|
|
365
|
+
|
|
366
|
+
const p = [];
|
|
367
|
+
for (let k = 0; k < maxCount; k++) {
|
|
368
|
+
if (k < trackingFrame.points.length) {
|
|
369
|
+
p.push([trackingFrame.points[k].x / scale, trackingFrame.points[k].y / scale]);
|
|
370
|
+
} else {
|
|
371
|
+
p.push([-1, -1]);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const imagePixels = tf.tensor(trackingFrame.data, [
|
|
375
|
+
trackingFrame.width * trackingFrame.height,
|
|
376
|
+
]);
|
|
377
|
+
const imageProperties = tf.tensor(
|
|
378
|
+
[trackingFrame.width, trackingFrame.height, trackingFrame.scale],
|
|
379
|
+
[3],
|
|
380
|
+
);
|
|
381
|
+
const featurePoints = tf.tensor(p, [p.length, 2], "float32");
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
featurePoints,
|
|
385
|
+
imagePixels,
|
|
386
|
+
imageProperties,
|
|
387
|
+
};
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
_compileAndRun(program, inputs) {
|
|
392
|
+
const outInfo = tf.backend().compileAndRun(program, inputs);
|
|
393
|
+
return tf.engine().makeTensorFromDataId(outInfo.dataId, outInfo.shape, outInfo.dtype);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export { Tracker };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// fast 2D submatrix sum using cumulative sum algorithm
|
|
2
|
+
class Cumsum {
|
|
3
|
+
constructor(data, width, height) {
|
|
4
|
+
this.cumsum = [];
|
|
5
|
+
for (let j = 0; j < height; j++) {
|
|
6
|
+
this.cumsum.push([]);
|
|
7
|
+
for (let i = 0; i < width; i++) {
|
|
8
|
+
this.cumsum[j].push(0);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
this.cumsum[0][0] = data[0];
|
|
13
|
+
for (let i = 1; i < width; i++) {
|
|
14
|
+
this.cumsum[0][i] = this.cumsum[0][i - 1] + data[i];
|
|
15
|
+
}
|
|
16
|
+
for (let j = 1; j < height; j++) {
|
|
17
|
+
this.cumsum[j][0] = this.cumsum[j - 1][0] + data[j * width];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (let j = 1; j < height; j++) {
|
|
21
|
+
for (let i = 1; i < width; i++) {
|
|
22
|
+
this.cumsum[j][i] =
|
|
23
|
+
data[j * width + i] +
|
|
24
|
+
this.cumsum[j - 1][i] +
|
|
25
|
+
this.cumsum[j][i - 1] -
|
|
26
|
+
this.cumsum[j - 1][i - 1];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
query(x1, y1, x2, y2) {
|
|
32
|
+
let ret = this.cumsum[y2][x2];
|
|
33
|
+
if (y1 > 0) ret -= this.cumsum[y1 - 1][x2];
|
|
34
|
+
if (x1 > 0) ret -= this.cumsum[y2][x1 - 1];
|
|
35
|
+
if (x1 > 0 && y1 > 0) ret += this.cumsum[y1 - 1][x1 - 1];
|
|
36
|
+
return ret;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { Cumsum };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// check which side point C on the line from A to B
|
|
2
|
+
const linePointSide = (A, B, C) => {
|
|
3
|
+
return (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]);
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
// srcPoints, dstPoints: array of four elements [x, y]
|
|
7
|
+
const checkFourPointsConsistent = (x1, x2, x3, x4, x1p, x2p, x3p, x4p) => {
|
|
8
|
+
if (linePointSide(x1, x2, x3) > 0 !== linePointSide(x1p, x2p, x3p) > 0) return false;
|
|
9
|
+
if (linePointSide(x2, x3, x4) > 0 !== linePointSide(x2p, x3p, x4p) > 0) return false;
|
|
10
|
+
if (linePointSide(x3, x4, x1) > 0 !== linePointSide(x3p, x4p, x1p) > 0) return false;
|
|
11
|
+
if (linePointSide(x4, x1, x2) > 0 !== linePointSide(x4p, x1p, x2p) > 0) return false;
|
|
12
|
+
return true;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const checkThreePointsConsistent = (x1, x2, x3, x1p, x2p, x3p) => {
|
|
16
|
+
if (linePointSide(x1, x2, x3) > 0 !== linePointSide(x1p, x2p, x3p) > 0) return false;
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const determinant = (A) => {
|
|
21
|
+
const C1 = A[4] * A[8] - A[5] * A[7];
|
|
22
|
+
const C2 = A[3] * A[8] - A[5] * A[6];
|
|
23
|
+
const C3 = A[3] * A[7] - A[4] * A[6];
|
|
24
|
+
return A[0] * C1 - A[1] * C2 + A[2] * C3;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const matrixInverse33 = (A, threshold) => {
|
|
28
|
+
const det = determinant(A);
|
|
29
|
+
if (Math.abs(det) <= threshold) return null;
|
|
30
|
+
const oneOver = 1.0 / det;
|
|
31
|
+
|
|
32
|
+
const B = [
|
|
33
|
+
(A[4] * A[8] - A[5] * A[7]) * oneOver,
|
|
34
|
+
(A[2] * A[7] - A[1] * A[8]) * oneOver,
|
|
35
|
+
(A[1] * A[5] - A[2] * A[4]) * oneOver,
|
|
36
|
+
(A[5] * A[6] - A[3] * A[8]) * oneOver,
|
|
37
|
+
(A[0] * A[8] - A[2] * A[6]) * oneOver,
|
|
38
|
+
(A[2] * A[3] - A[0] * A[5]) * oneOver,
|
|
39
|
+
(A[3] * A[7] - A[4] * A[6]) * oneOver,
|
|
40
|
+
(A[1] * A[6] - A[0] * A[7]) * oneOver,
|
|
41
|
+
(A[0] * A[4] - A[1] * A[3]) * oneOver,
|
|
42
|
+
];
|
|
43
|
+
return B;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const matrixMul33 = (A, B) => {
|
|
47
|
+
const C = [];
|
|
48
|
+
C[0] = A[0] * B[0] + A[1] * B[3] + A[2] * B[6];
|
|
49
|
+
C[1] = A[0] * B[1] + A[1] * B[4] + A[2] * B[7];
|
|
50
|
+
C[2] = A[0] * B[2] + A[1] * B[5] + A[2] * B[8];
|
|
51
|
+
C[3] = A[3] * B[0] + A[4] * B[3] + A[5] * B[6];
|
|
52
|
+
C[4] = A[3] * B[1] + A[4] * B[4] + A[5] * B[7];
|
|
53
|
+
C[5] = A[3] * B[2] + A[4] * B[5] + A[5] * B[8];
|
|
54
|
+
C[6] = A[6] * B[0] + A[7] * B[3] + A[8] * B[6];
|
|
55
|
+
C[7] = A[6] * B[1] + A[7] * B[4] + A[8] * B[7];
|
|
56
|
+
C[8] = A[6] * B[2] + A[7] * B[5] + A[8] * B[8];
|
|
57
|
+
return C;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const multiplyPointHomographyInhomogenous = (x, H) => {
|
|
61
|
+
const w = H[6] * x[0] + H[7] * x[1] + H[8];
|
|
62
|
+
const xp = [];
|
|
63
|
+
xp[0] = (H[0] * x[0] + H[1] * x[1] + H[2]) / w;
|
|
64
|
+
xp[1] = (H[3] * x[0] + H[4] * x[1] + H[5]) / w;
|
|
65
|
+
return xp;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const smallestTriangleArea = (x1, x2, x3, x4) => {
|
|
69
|
+
const v12 = _vector(x2, x1);
|
|
70
|
+
const v13 = _vector(x3, x1);
|
|
71
|
+
const v14 = _vector(x4, x1);
|
|
72
|
+
const v32 = _vector(x2, x3);
|
|
73
|
+
const v34 = _vector(x4, x3);
|
|
74
|
+
const a1 = _areaOfTriangle(v12, v13);
|
|
75
|
+
const a2 = _areaOfTriangle(v13, v14);
|
|
76
|
+
const a3 = _areaOfTriangle(v12, v14);
|
|
77
|
+
const a4 = _areaOfTriangle(v32, v34);
|
|
78
|
+
return Math.min(Math.min(Math.min(a1, a2), a3), a4);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// check if four points form a convex quadrilaternal.
|
|
82
|
+
// all four combinations should have same sign
|
|
83
|
+
const quadrilateralConvex = (x1, x2, x3, x4) => {
|
|
84
|
+
const first = linePointSide(x1, x2, x3) <= 0;
|
|
85
|
+
if (linePointSide(x2, x3, x4) <= 0 !== first) return false;
|
|
86
|
+
if (linePointSide(x3, x4, x1) <= 0 !== first) return false;
|
|
87
|
+
if (linePointSide(x4, x1, x2) <= 0 !== first) return false;
|
|
88
|
+
|
|
89
|
+
//if (linePointSide(x1, x2, x3) <= 0) return false;
|
|
90
|
+
//if (linePointSide(x2, x3, x4) <= 0) return false;
|
|
91
|
+
//if (linePointSide(x3, x4, x1) <= 0) return false;
|
|
92
|
+
//if (linePointSide(x4, x1, x2) <= 0) return false;
|
|
93
|
+
return true;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const _vector = (a, b) => {
|
|
97
|
+
return [a[0] - b[0], a[1] - b[1]];
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const _areaOfTriangle = (u, v) => {
|
|
101
|
+
const a = u[0] * v[1] - u[1] * v[0];
|
|
102
|
+
return Math.abs(a) * 0.5;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
matrixInverse33,
|
|
107
|
+
matrixMul33,
|
|
108
|
+
quadrilateralConvex,
|
|
109
|
+
smallestTriangleArea,
|
|
110
|
+
multiplyPointHomographyInhomogenous,
|
|
111
|
+
checkThreePointsConsistent,
|
|
112
|
+
checkFourPointsConsistent,
|
|
113
|
+
determinant,
|
|
114
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Matrix, inverse } from "ml-matrix";
|
|
2
|
+
|
|
3
|
+
const solveHomography = (srcPoints, dstPoints) => {
|
|
4
|
+
const { normPoints: normSrcPoints, param: srcParam } = _normalizePoints(srcPoints);
|
|
5
|
+
const { normPoints: normDstPoints, param: dstParam } = _normalizePoints(dstPoints);
|
|
6
|
+
|
|
7
|
+
const num = normDstPoints.length;
|
|
8
|
+
const AData = [];
|
|
9
|
+
const BData = [];
|
|
10
|
+
for (let j = 0; j < num; j++) {
|
|
11
|
+
const row1 = [
|
|
12
|
+
normSrcPoints[j][0],
|
|
13
|
+
normSrcPoints[j][1],
|
|
14
|
+
1,
|
|
15
|
+
0,
|
|
16
|
+
0,
|
|
17
|
+
0,
|
|
18
|
+
-(normSrcPoints[j][0] * normDstPoints[j][0]),
|
|
19
|
+
-(normSrcPoints[j][1] * normDstPoints[j][0]),
|
|
20
|
+
];
|
|
21
|
+
const row2 = [
|
|
22
|
+
0,
|
|
23
|
+
0,
|
|
24
|
+
0,
|
|
25
|
+
normSrcPoints[j][0],
|
|
26
|
+
normSrcPoints[j][1],
|
|
27
|
+
1,
|
|
28
|
+
-(normSrcPoints[j][0] * normDstPoints[j][1]),
|
|
29
|
+
-(normSrcPoints[j][1] * normDstPoints[j][1]),
|
|
30
|
+
];
|
|
31
|
+
AData.push(row1);
|
|
32
|
+
AData.push(row2);
|
|
33
|
+
|
|
34
|
+
BData.push([normDstPoints[j][0]]);
|
|
35
|
+
BData.push([normDstPoints[j][1]]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const A = new Matrix(AData);
|
|
40
|
+
const B = new Matrix(BData);
|
|
41
|
+
const AT = A.transpose();
|
|
42
|
+
const ATA = AT.mmul(A);
|
|
43
|
+
const ATB = AT.mmul(B);
|
|
44
|
+
const ATAInv = inverse(ATA);
|
|
45
|
+
const C = ATAInv.mmul(ATB).to1DArray();
|
|
46
|
+
const H = _denormalizeHomography(C, srcParam, dstParam);
|
|
47
|
+
return H;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// centroid at origin and avg distance from origin is sqrt(2)
|
|
54
|
+
const _normalizePoints = (coords) => {
|
|
55
|
+
//return {normalizedCoords: coords, param: {meanX: 0, meanY: 0, s: 1}}; // skip normalization
|
|
56
|
+
|
|
57
|
+
let sumX = 0;
|
|
58
|
+
let sumY = 0;
|
|
59
|
+
for (let i = 0; i < coords.length; i++) {
|
|
60
|
+
sumX += coords[i][0];
|
|
61
|
+
sumY += coords[i][1];
|
|
62
|
+
}
|
|
63
|
+
let meanX = sumX / coords.length;
|
|
64
|
+
let meanY = sumY / coords.length;
|
|
65
|
+
|
|
66
|
+
let sumDiff = 0;
|
|
67
|
+
for (let i = 0; i < coords.length; i++) {
|
|
68
|
+
const diffX = coords[i][0] - meanX;
|
|
69
|
+
const diffY = coords[i][1] - meanY;
|
|
70
|
+
sumDiff += Math.sqrt(diffX * diffX + diffY * diffY);
|
|
71
|
+
}
|
|
72
|
+
let s = (Math.sqrt(2) * coords.length) / sumDiff;
|
|
73
|
+
|
|
74
|
+
const normPoints = [];
|
|
75
|
+
for (let i = 0; i < coords.length; i++) {
|
|
76
|
+
normPoints.push([(coords[i][0] - meanX) * s, (coords[i][1] - meanY) * s]);
|
|
77
|
+
}
|
|
78
|
+
return { normPoints, param: { meanX, meanY, s } };
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Denormalize homography
|
|
82
|
+
// where T is the normalization matrix, i.e.
|
|
83
|
+
//
|
|
84
|
+
// [1 0 -meanX]
|
|
85
|
+
// T = [0 1 -meanY]
|
|
86
|
+
// [0 0 1/s]
|
|
87
|
+
//
|
|
88
|
+
// [1 0 s*meanX]
|
|
89
|
+
// inv(T) = [0 1 s*meanY]
|
|
90
|
+
// [0 0 s]
|
|
91
|
+
//
|
|
92
|
+
// H = inv(Tdst) * Hn * Tsrc
|
|
93
|
+
//
|
|
94
|
+
// @param {
|
|
95
|
+
// nH: normH,
|
|
96
|
+
// srcParam: param of src transform,
|
|
97
|
+
// dstParam: param of dst transform
|
|
98
|
+
// }
|
|
99
|
+
const _denormalizeHomography = (nH, srcParam, dstParam) => {
|
|
100
|
+
/*
|
|
101
|
+
Matrix version
|
|
102
|
+
const normH = new Matrix([
|
|
103
|
+
[nH[0], nH[1], nH[2]],
|
|
104
|
+
[nH[3], nH[4], nH[5]],
|
|
105
|
+
[nH[6], nH[7], 1],
|
|
106
|
+
]);
|
|
107
|
+
const Tsrc = new Matrix([
|
|
108
|
+
[1, 0, -srcParam.meanX],
|
|
109
|
+
[0, 1, -srcParam.meanY],
|
|
110
|
+
[0, 0, 1/srcParam.s],
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const invTdst = new Matrix([
|
|
114
|
+
[1, 0, dstParam.s * dstParam.meanX],
|
|
115
|
+
[0, 1, dstParam.s * dstParam.meanY],
|
|
116
|
+
[0, 0, dstParam.s],
|
|
117
|
+
]);
|
|
118
|
+
const H = invTdst.mmul(normH).mmul(Tsrc);
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
// plain implementation of the above using Matrix
|
|
122
|
+
const sMeanX = dstParam.s * dstParam.meanX;
|
|
123
|
+
const sMeanY = dstParam.s * dstParam.meanY;
|
|
124
|
+
|
|
125
|
+
const H = [
|
|
126
|
+
nH[0] + sMeanX * nH[6],
|
|
127
|
+
nH[1] + sMeanX * nH[7],
|
|
128
|
+
(nH[0] + sMeanX * nH[6]) * -srcParam.meanX +
|
|
129
|
+
(nH[1] + sMeanX * nH[7]) * -srcParam.meanY +
|
|
130
|
+
(nH[2] + sMeanX) / srcParam.s,
|
|
131
|
+
nH[3] + sMeanY * nH[6],
|
|
132
|
+
nH[4] + sMeanY * nH[7],
|
|
133
|
+
(nH[3] + sMeanY * nH[6]) * -srcParam.meanX +
|
|
134
|
+
(nH[4] + sMeanY * nH[7]) * -srcParam.meanY +
|
|
135
|
+
(nH[5] + sMeanY) / srcParam.s,
|
|
136
|
+
dstParam.s * nH[6],
|
|
137
|
+
dstParam.s * nH[7],
|
|
138
|
+
dstParam.s * nH[6] * -srcParam.meanX +
|
|
139
|
+
dstParam.s * nH[7] * -srcParam.meanY +
|
|
140
|
+
dstParam.s / srcParam.s,
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
// make H[8] === 1;
|
|
144
|
+
for (let i = 0; i < 9; i++) {
|
|
145
|
+
H[i] = H[i] / H[8];
|
|
146
|
+
}
|
|
147
|
+
return H;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export { solveHomography };
|