|
@@ -23,7 +23,7 @@
|
23
|
23
|
import Speedy from 'speedy-vision';
|
24
|
24
|
import { SpeedyMedia } from 'speedy-vision/types/core/speedy-media';
|
25
|
25
|
import { SpeedyPromise } from 'speedy-vision/types/core/speedy-promise';
|
26
|
|
-import { ReferenceImage } from './reference-image';
|
|
26
|
+import { ReferenceImage, ReferenceImageWithMedia } from './reference-image';
|
27
|
27
|
import { Utils } from '../../utils/utils';
|
28
|
28
|
import { IllegalArgumentError, IllegalOperationError } from '../../utils/errors';
|
29
|
29
|
|
|
@@ -33,28 +33,13 @@ const DEFAULT_CAPACITY = 100; // this number should exceed normal usage
|
33
|
33
|
// further testing is needed to verify the appropriateness of this number;
|
34
|
34
|
// it depends on the images, on the keypoint descriptors, and even on the target devices
|
35
|
35
|
|
36
|
|
-/** Generate a unique name for a reference image */
|
37
|
|
-const generateUniqueName = () => 'target-' + Math.random().toString(16).substr(2);
|
38
|
|
-
|
39
|
|
-/**
|
40
|
|
- * An entry of a Reference Image Database
|
41
|
|
- */
|
42
|
|
-interface ReferenceImageDatabaseEntry
|
43
|
|
-{
|
44
|
|
- /** reference image */
|
45
|
|
- readonly referenceImage: ReferenceImage;
|
46
|
|
-
|
47
|
|
- /** previously loaded media */
|
48
|
|
- readonly media: SpeedyMedia;
|
49
|
|
-}
|
50
|
|
-
|
51
|
36
|
/**
|
52
|
37
|
* A collection of Reference Images
|
53
|
38
|
*/
|
54
|
39
|
export class ReferenceImageDatabase implements Iterable<ReferenceImage>
|
55
|
40
|
{
|
56
|
41
|
/** Entries */
|
57
|
|
- private _entries: Map<string, ReferenceImageDatabaseEntry>;
|
|
42
|
+ private _entries: Map<string, ReferenceImageWithMedia>;
|
58
|
43
|
|
59
|
44
|
/** Maximum number of entries */
|
60
|
45
|
private _capacity: number;
|
|
@@ -62,8 +47,6 @@ export class ReferenceImageDatabase implements Iterable<ReferenceImage>
|
62
|
47
|
/** Is the database locked? */
|
63
|
48
|
private _locked: boolean;
|
64
|
49
|
|
65
|
|
- /** Are we busy loading an image? */
|
66
|
|
- private _busy: boolean;
|
67
|
50
|
|
68
|
51
|
|
69
|
52
|
|
|
@@ -75,7 +58,6 @@ export class ReferenceImageDatabase implements Iterable<ReferenceImage>
|
75
|
58
|
this._capacity = DEFAULT_CAPACITY;
|
76
|
59
|
this._entries = new Map();
|
77
|
60
|
this._locked = false;
|
78
|
|
- this._busy = false;
|
79
|
61
|
}
|
80
|
62
|
|
81
|
63
|
/**
|
|
@@ -111,11 +93,9 @@ export class ReferenceImageDatabase implements Iterable<ReferenceImage>
|
111
|
93
|
/**
|
112
|
94
|
* Iterates over the collection
|
113
|
95
|
*/
|
114
|
|
- *[Symbol.iterator](): Iterator<ReferenceImage>
|
|
96
|
+ [Symbol.iterator](): Iterator<ReferenceImageWithMedia>
|
115
|
97
|
{
|
116
|
|
- const arr = Array.from(this._entries.values());
|
117
|
|
- const ref = arr.map(entry => entry.referenceImage);
|
118
|
|
- yield* ref;
|
|
98
|
+ return this._entries.values();
|
119
|
99
|
}
|
120
|
100
|
|
121
|
101
|
/**
|
|
@@ -128,56 +108,40 @@ export class ReferenceImageDatabase implements Iterable<ReferenceImage>
|
128
|
108
|
*/
|
129
|
109
|
add(referenceImages: ReferenceImage[]): SpeedyPromise<void>
|
130
|
110
|
{
|
131
|
|
- const n = referenceImages.length;
|
132
|
|
-
|
133
|
|
- // handle no input
|
134
|
|
- if(n == 0)
|
135
|
|
- return Speedy.Promise.resolve();
|
136
|
|
-
|
137
|
|
- // handle multiple images as input
|
138
|
|
- if(n > 1) {
|
139
|
|
- Utils.log(`Loading ${n} reference image${n != 1 ? 's' : ''}...`);
|
140
|
|
- const preloadMedias = referenceImages.map(referenceImage => Speedy.load(referenceImage.image));
|
141
|
|
- return Speedy.Promise.all(preloadMedias).then(() => {
|
142
|
|
- const promises = referenceImages.map(referenceImage => this.add([ referenceImage ]));
|
143
|
|
- return Utils.runInSequence(promises);
|
|
111
|
+ return this._preloadMany(referenceImages).then(referenceImagesWithMedia => {
|
|
112
|
+ referenceImagesWithMedia.forEach(referenceImageWithMedia => {
|
|
113
|
+ this._addOne(referenceImageWithMedia);
|
144
|
114
|
});
|
145
|
|
- }
|
|
115
|
+ });
|
|
116
|
+ }
|
146
|
117
|
|
147
|
|
- // handle a single image as input
|
148
|
|
- const referenceImage = referenceImages[0];
|
|
118
|
+ /**
|
|
119
|
+ * Add a single preloaded reference image to the database
|
|
120
|
+ * @param referenceImage
|
|
121
|
+ */
|
|
122
|
+ _addOne(referenceImage: ReferenceImageWithMedia): void
|
|
123
|
+ {
|
|
124
|
+ const name = referenceImage.name;
|
149
|
125
|
|
150
|
126
|
// locked database?
|
151
|
127
|
if(this._locked)
|
152
|
|
- throw new IllegalOperationError(`Can't add reference image "${referenceImage.name}" to the database: it's locked`);
|
153
|
|
-
|
154
|
|
- // busy loading another image?
|
155
|
|
- if(this._busy)
|
156
|
|
- return Utils.wait(4).then(() => this.add(referenceImages)); // try again later
|
|
128
|
+ throw new IllegalOperationError(`Can't add reference image "${name}" to the database: it's locked`);
|
157
|
129
|
|
158
|
130
|
// reached full capacity?
|
159
|
131
|
if(this.count >= this.capacity)
|
160
|
|
- throw new IllegalOperationError(`Can't add reference image "${referenceImage.name}" to the database: the capacity of ${this.capacity} images has been exceeded.`);
|
|
132
|
+ throw new IllegalOperationError(`Can't add reference image "${name}" to the database: the capacity of ${this.capacity} images has been exceeded.`);
|
161
|
133
|
|
162
|
134
|
// check if the image is valid
|
163
|
135
|
if(!(referenceImage.image instanceof HTMLImageElement) && !(referenceImage.image instanceof HTMLCanvasElement) && !(referenceImage.image instanceof ImageBitmap))
|
164
|
|
- throw new IllegalArgumentError(`Can't add reference image "${referenceImage.name}" to the database: invalid image`);
|
|
136
|
+ throw new IllegalArgumentError(`Can't add reference image "${name}" to the database: invalid image`);
|
165
|
137
|
|
166
|
138
|
// check for duplicate names
|
167
|
|
- if(this._entries.has(referenceImage.name))
|
168
|
|
- throw new IllegalArgumentError(`Can't add reference image "${referenceImage.name}" to the database: found duplicated name`);
|
|
139
|
+ if(this._entries.has(name))
|
|
140
|
+ throw new IllegalArgumentError(`Can't add reference image "${name}" to the database: found duplicated name`);
|
169
|
141
|
|
170
|
142
|
// add the reference image to the database
|
171
|
|
- Utils.log(`Adding reference image "${referenceImage.name}" to the database...`);
|
172
|
|
- this._busy = true;
|
173
|
|
- return Speedy.load(referenceImage.image).then(media => {
|
174
|
|
- const name = referenceImage.name || generateUniqueName();
|
175
|
|
- this._busy = false;
|
176
|
|
- this._entries.set(name, {
|
177
|
|
- referenceImage: Object.freeze(Object.assign({ }, referenceImage, { name })),
|
178
|
|
- media: media
|
179
|
|
- });
|
180
|
|
- });
|
|
143
|
+ Utils.log(`Adding reference image "${name}" to the database...`);
|
|
144
|
+ this._entries.set(name, referenceImage);
|
181
|
145
|
}
|
182
|
146
|
|
183
|
147
|
/**
|
|
@@ -186,25 +150,51 @@ export class ReferenceImageDatabase implements Iterable<ReferenceImage>
|
186
|
150
|
*/
|
187
|
151
|
_lock(): void
|
188
|
152
|
{
|
189
|
|
- if(this._busy)
|
190
|
|
- throw new IllegalOperationError(`Can't lock the reference image database: we're busy loading an image`);
|
191
|
|
-
|
192
|
153
|
this._locked = true;
|
193
|
154
|
}
|
194
|
155
|
|
195
|
156
|
/**
|
196
|
|
- * Get the media object associated to a reference image
|
197
|
|
- * @param name reference image name
|
198
|
|
- * @returns media
|
|
157
|
+ * Get reference image by name
|
|
158
|
+ * @param name
|
|
159
|
+ * @returns the reference image with the given name, or null if there isn't any
|
199
|
160
|
* @internal
|
200
|
161
|
*/
|
201
|
|
- _findMedia(name: string): SpeedyMedia
|
|
162
|
+ _find(name: string): ReferenceImageWithMedia | null
|
|
163
|
+ {
|
|
164
|
+ return this._entries.get(name) || null;
|
|
165
|
+ }
|
|
166
|
+
|
|
167
|
+ /**
|
|
168
|
+ * Load a reference image
|
|
169
|
+ * @param referenceImage
|
|
170
|
+ * @returns a promise that resolves to a corresponding ReferenceImageWithMedia
|
|
171
|
+ */
|
|
172
|
+ private _preloadOne(referenceImage: ReferenceImage): SpeedyPromise<ReferenceImageWithMedia>
|
202
|
173
|
{
|
203
|
|
- const entry = this._entries.get(name);
|
|
174
|
+ if(referenceImage.name !== undefined)
|
|
175
|
+ Utils.log(`Loading reference image \"${referenceImage.name}\"...`);
|
|
176
|
+ else
|
|
177
|
+ Utils.log(`Loading reference image...`);
|
|
178
|
+
|
|
179
|
+ if(!referenceImage.image)
|
|
180
|
+ return Speedy.Promise.reject(new IllegalArgumentError('The reference image was not provided!'));
|
204
|
181
|
|
205
|
|
- if(!entry)
|
206
|
|
- throw new IllegalArgumentError(`Can't find reference image "${name}"`);
|
|
182
|
+ return Speedy.load(referenceImage.image).then(media => {
|
|
183
|
+ return new ReferenceImageWithMedia(referenceImage, media);
|
|
184
|
+ });
|
|
185
|
+ }
|
|
186
|
+
|
|
187
|
+ /**
|
|
188
|
+ * Load multiple reference images
|
|
189
|
+ * @param referenceImages
|
|
190
|
+ * @returns a promise that resolves to corresponding ReferenceImageWithMedia objects
|
|
191
|
+ */
|
|
192
|
+ private _preloadMany(referenceImages: ReferenceImage[]): SpeedyPromise<ReferenceImageWithMedia[]>
|
|
193
|
+ {
|
|
194
|
+ const n = referenceImages.length;
|
|
195
|
+ Utils.log(`Loading ${n} reference image${n != 1 ? 's' : ''}...`);
|
207
|
196
|
|
208
|
|
- return entry.media;
|
|
197
|
+ const promises = referenceImages.map(referenceImage => this._preloadOne(referenceImage));
|
|
198
|
+ return Speedy.Promise.all<ReferenceImageWithMedia>(promises);
|
209
|
199
|
}
|
210
|
200
|
}
|