tfjs-evolution 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +6 -1
- package/esm2022/lib/components/display-panel/display-panel.component.mjs +32 -5
- package/esm2022/lib/models/custom-mobilenet.mjs +182 -0
- package/esm2022/lib/models/teachable-evolution.mjs +424 -0
- package/esm2022/lib/utils/class.mjs +2 -0
- package/esm2022/lib/utils/tf.mjs +29 -0
- package/esm2022/lib/utils/util.mjs +165 -0
- package/fesm2022/tfjs-evolution.mjs +824 -4
- package/fesm2022/tfjs-evolution.mjs.map +1 -1
- package/lib/components/display-panel/display-panel.component.d.ts +4 -0
- package/lib/models/custom-mobilenet.d.ts +63 -0
- package/lib/models/teachable-evolution.d.ts +101 -0
- package/lib/utils/class.d.ts +5 -0
- package/lib/utils/tf.d.ts +9 -0
- package/lib/utils/util.d.ts +43 -0
- package/package.json +1 -1
package/README.md
CHANGED
@@ -52,7 +52,7 @@ Make the call for loading images. You could most likely make the call at the con
|
|
52
52
|
```typescript
|
53
53
|
ngAfterViewInit(): void {
|
54
54
|
const options={
|
55
|
-
base: "./assets/dataset",//base where are
|
55
|
+
base: "./assets/dataset",//base where are your iamges
|
56
56
|
file_name:"image",//how your images are named
|
57
57
|
file_extension:"jpeg" //extension used
|
58
58
|
};
|
@@ -72,6 +72,11 @@ Finally, do not forget to add the HTML code
|
|
72
72
|
|
73
73
|
You should see in our HTML file, in Angular, the images
|
74
74
|
|
75
|
+
|
76
|
+
# Updates
|
77
|
+
|
78
|
+
I have finished! Hope to publish a paper soon!
|
79
|
+
|
75
80
|
# Further help
|
76
81
|
|
77
82
|
Feel free to get in touch: jorgeguerrabrazil@gmail.com
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Component } from '@angular/core';
|
2
2
|
import { CommonModule } from '@angular/common';
|
3
|
+
import { TeachableMobileNet } from '../../models/teachable-evolution';
|
3
4
|
import * as i0 from "@angular/core";
|
4
5
|
import * as i1 from "@angular/common";
|
5
6
|
export class DisplayPanelComponent {
|
@@ -8,6 +9,7 @@ export class DisplayPanelComponent {
|
|
8
9
|
}
|
9
10
|
loadImages(number_of_species, classes_names, options) {
|
10
11
|
this.classes_names = classes_names;
|
12
|
+
this.number_of_samples_per_class = number_of_species;
|
11
13
|
this.add_species(number_of_species, options);
|
12
14
|
}
|
13
15
|
add_species(number_of_species, options) {
|
@@ -17,16 +19,41 @@ export class DisplayPanelComponent {
|
|
17
19
|
}
|
18
20
|
add_images(name, number_of_species, options) {
|
19
21
|
const class_add = [];
|
20
|
-
for (let i =
|
21
|
-
class_add.push(`${options.base}/${name}/${options.file_name} ${i}.${options.file_extension}`);
|
22
|
+
for (let i = 0; i < number_of_species; i++) {
|
23
|
+
class_add.push(`${options.base}/${name}/${options.file_name} ${i + 1}.${options.file_extension}`);
|
22
24
|
}
|
23
25
|
this.classes.push({ name: name, images: class_add });
|
24
26
|
}
|
27
|
+
async addexamples() {
|
28
|
+
//This is needed to make sure it gives time for the images to upload
|
29
|
+
//The images upload very fast, what makes this method execute before the images are on HTML
|
30
|
+
//It can be removed if somehow this method is just called after the images are available.
|
31
|
+
// console.log("Loading examples as tensors....")
|
32
|
+
await this.delay(0);
|
33
|
+
for (let i = 0; i < this.classes_names.length; i++) {
|
34
|
+
await this.add_example(this.classes_names[i], this.number_of_samples_per_class);
|
35
|
+
}
|
36
|
+
}
|
37
|
+
async add_example(name, number_of_species) {
|
38
|
+
const class_add = [];
|
39
|
+
// console.log(name)
|
40
|
+
for (let i = 0; i < number_of_species; i++) {
|
41
|
+
//Collecting the images from HTML
|
42
|
+
const aux = document.getElementById(`class-${name}-${i}`);
|
43
|
+
//Adding the example
|
44
|
+
const index = this.classes_names.findIndex((elem) => elem === name);
|
45
|
+
await TeachableMobileNet.addExample(index, name, aux);
|
46
|
+
}
|
47
|
+
// this.classes.push({name: name, images: class_add})
|
48
|
+
}
|
49
|
+
delay(ms) {
|
50
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
51
|
+
}
|
25
52
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.4", ngImport: i0, type: DisplayPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
26
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.4", type: DisplayPanelComponent, isStandalone: true, selector: "neuroevolution-display-panel", ngImport: i0, template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"
|
53
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.4", type: DisplayPanelComponent, isStandalone: true, selector: "neuroevolution-display-panel", ngImport: i0, template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"224\" height=\"224\" [id]=\"'class-' + class.name + '-' + i\" crossorigin=\"anonymous\" >\r\n</div>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] }); }
|
27
54
|
}
|
28
55
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.4", ngImport: i0, type: DisplayPanelComponent, decorators: [{
|
29
56
|
type: Component,
|
30
|
-
args: [{ selector: 'neuroevolution-display-panel', standalone: true, imports: [CommonModule], template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"
|
57
|
+
args: [{ selector: 'neuroevolution-display-panel', standalone: true, imports: [CommonModule], template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"224\" height=\"224\" [id]=\"'class-' + class.name + '-' + i\" crossorigin=\"anonymous\" >\r\n</div>\r\n" }]
|
31
58
|
}] });
|
32
|
-
//# sourceMappingURL=data:application/json;base64,
|
59
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzcGxheS1wYW5lbC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90ZmpzLWV2b2x1dGlvbi9zcmMvbGliL2NvbXBvbmVudHMvZGlzcGxheS1wYW5lbC9kaXNwbGF5LXBhbmVsLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RmanMtZXZvbHV0aW9uL3NyYy9saWIvY29tcG9uZW50cy9kaXNwbGF5LXBhbmVsL2Rpc3BsYXktcGFuZWwuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUV6QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7OztBQVN0RSxNQUFNLE9BQU8scUJBQXFCO0lBUGxDO1FBU0UsWUFBTyxHQUFVLEVBQUUsQ0FBQztLQXlFckI7SUFuRUQsVUFBVSxDQUFDLGlCQUF5QixFQUFFLGFBQXVCLEVBQUUsT0FBZTtRQUUxRSxJQUFJLENBQUMsYUFBYSxHQUFDLGFBQWEsQ0FBQztRQUNqQyxJQUFJLENBQUMsMkJBQTJCLEdBQUMsaUJBQWlCLENBQUM7UUFFbkQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUUvQyxDQUFDO0lBR0gsV0FBVyxDQUFDLGlCQUF5QixFQUFFLE9BQWU7UUFFbEQsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ2xELElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsRUFBRSxPQUFPLENBQUMsQ0FBQztTQUVwRTtJQUVILENBQUM7SUFFRCxVQUFVLENBQUMsSUFBWSxFQUFFLGlCQUF5QixFQUFFLE9BQVk7UUFFOUQsTUFBTSxTQUFTLEdBQU8sRUFBRSxDQUFDO1FBRXpCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxpQkFBaUIsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUMxQyxTQUFTLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyxDQUFDLElBQUksSUFBSSxJQUFJLElBQUksT0FBTyxDQUFDLFNBQVMsSUFBSSxDQUFDLEdBQUMsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1NBQ25HO1FBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUMsQ0FBQyxDQUFBO0lBRWxELENBQUM7SUFFRixLQUFLLENBQUMsV0FBVztRQUVoQixvRUFBb0U7UUFDcEUsMkZBQTJGO1FBQzNGLHlGQUF5RjtRQUN6RixpREFBaUQ7UUFDakQsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRXBCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNsRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsMkJBQTJCLENBQUMsQ0FBQztTQUNqRjtJQUNELENBQUM7SUFFRCxLQUFLLENBQUMsV0FBVyxDQUFDLElBQVksRUFBRSxpQkFBeUI7UUFFdkQsTUFBTSxTQUFTLEdBQU8sRUFBRSxDQUFDO1FBQ3pCLG9CQUFvQjtRQUNwQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFFMUMsaUNBQWlDO1lBQ2pDLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxFQUFFLENBQXFCLENBQUM7WUFFOUUsb0JBQW9CO1lBQ3BCLE1BQU0sS0FBSyxHQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBSSxFQUFDLEVBQUUsQ0FBQSxJQUFJLEtBQUcsSUFBSSxDQUFDLENBQUE7WUFFOUQsTUFBTSxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztTQUV6RDtRQUVELHVEQUF1RDtJQUV2RCxDQUFDO0lBRUQsS0FBSyxDQUFDLEVBQVU7UUFDZCxPQUFPLElBQUksT0FBTyxDQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbkUsQ0FBQzs4R0ExRVkscUJBQXFCO2tHQUFyQixxQkFBcUIsd0ZDWmxDLDBRQUlBLHlERElZLFlBQVk7OzJGQUlYLHFCQUFxQjtrQkFQakMsU0FBUzsrQkFDRSw4QkFBOEIsY0FDNUIsSUFBSSxXQUNQLENBQUMsWUFBWSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50fSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgR3JvdXAgfSBmcm9tICcuLi8uLi9pbnRlcmZhY2VzL2dyb3VwJztcclxuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcclxuaW1wb3J0IHsgVGVhY2hhYmxlTW9iaWxlTmV0IH0gZnJvbSAnLi4vLi4vbW9kZWxzL3RlYWNoYWJsZS1ldm9sdXRpb24nO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICduZXVyb2V2b2x1dGlvbi1kaXNwbGF5LXBhbmVsJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGVdLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9kaXNwbGF5LXBhbmVsLmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybDogJy4vZGlzcGxheS1wYW5lbC5jb21wb25lbnQuY3NzJ1xyXG59KVxyXG5leHBvcnQgY2xhc3MgRGlzcGxheVBhbmVsQ29tcG9uZW50IHtcclxuXHJcbiAgY2xhc3NlczogR3JvdXBbXT1bXTtcclxuXHJcbiAgY2xhc3Nlc19uYW1lcyE6IHN0cmluZ1tdO1xyXG5cclxuICBudW1iZXJfb2Zfc2FtcGxlc19wZXJfY2xhc3MhOiBudW1iZXI7XHJcblxyXG5sb2FkSW1hZ2VzKG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIsIGNsYXNzZXNfbmFtZXM6IHN0cmluZ1tdLCBvcHRpb25zOiBvYmplY3Qpe1xyXG4gICAgXHJcbiAgICB0aGlzLmNsYXNzZXNfbmFtZXM9Y2xhc3Nlc19uYW1lcztcclxuICAgIHRoaXMubnVtYmVyX29mX3NhbXBsZXNfcGVyX2NsYXNzPW51bWJlcl9vZl9zcGVjaWVzO1xyXG5cclxuICAgIHRoaXMuYWRkX3NwZWNpZXMobnVtYmVyX29mX3NwZWNpZXMsIG9wdGlvbnMpOyAgICBcclxuXHJcbiAgfVxyXG5cclxuXHJcbmFkZF9zcGVjaWVzKG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIsIG9wdGlvbnM6IG9iamVjdCl7XHJcblxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNsYXNzZXNfbmFtZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdGhpcy5hZGRfaW1hZ2VzKHRoaXMuY2xhc3Nlc19uYW1lc1tpXSwgbnVtYmVyX29mX3NwZWNpZXMsIG9wdGlvbnMpO1xyXG4gICAgICBcclxuICAgIH1cclxuXHJcbiAgfVxyXG5cclxuICBhZGRfaW1hZ2VzKG5hbWU6IHN0cmluZywgbnVtYmVyX29mX3NwZWNpZXM6IG51bWJlciwgb3B0aW9uczogYW55KXsgICBcclxuXHJcbiAgICBjb25zdCBjbGFzc19hZGQ6IGFueT0gW107XHJcblxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBudW1iZXJfb2Zfc3BlY2llczsgaSsrKSB7ICAgICAgXHJcbiAgICAgIGNsYXNzX2FkZC5wdXNoKGAke29wdGlvbnMuYmFzZX0vJHtuYW1lfS8ke29wdGlvbnMuZmlsZV9uYW1lfSAke2krMX0uJHtvcHRpb25zLmZpbGVfZXh0ZW5zaW9ufWApO1xyXG4gIH1cclxuXHJcbiAgdGhpcy5jbGFzc2VzLnB1c2goe25hbWU6IG5hbWUsIGltYWdlczogY2xhc3NfYWRkfSkgIFxyXG5cclxuICB9XHJcblxyXG4gYXN5bmMgYWRkZXhhbXBsZXMoKXtcclxuXHJcbiAgLy9UaGlzIGlzIG5lZWRlZCB0byBtYWtlIHN1cmUgaXQgZ2l2ZXMgdGltZSBmb3IgdGhlIGltYWdlcyB0byB1cGxvYWRcclxuICAvL1RoZSBpbWFnZXMgdXBsb2FkIHZlcnkgZmFzdCwgd2hhdCBtYWtlcyB0aGlzIG1ldGhvZCBleGVjdXRlIGJlZm9yZSB0aGUgaW1hZ2VzIGFyZSBvbiBIVE1MXHJcbiAgLy9JdCBjYW4gYmUgcmVtb3ZlZCBpZiBzb21laG93IHRoaXMgbWV0aG9kIGlzIGp1c3QgY2FsbGVkIGFmdGVyIHRoZSBpbWFnZXMgYXJlIGF2YWlsYWJsZS5cclxuICAvLyBjb25zb2xlLmxvZyhcIkxvYWRpbmcgZXhhbXBsZXMgYXMgdGVuc29ycy4uLi5cIilcclxuICBhd2FpdCB0aGlzLmRlbGF5KDApO1xyXG5cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuY2xhc3Nlc19uYW1lcy5sZW5ndGg7IGkrKykge1xyXG4gICAgYXdhaXQgdGhpcy5hZGRfZXhhbXBsZSh0aGlzLmNsYXNzZXNfbmFtZXNbaV0sIHRoaXMubnVtYmVyX29mX3NhbXBsZXNfcGVyX2NsYXNzKTtcclxuICB9XHJcbiAgfVxyXG5cclxuICBhc3luYyBhZGRfZXhhbXBsZShuYW1lOiBzdHJpbmcsIG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIpeyAgIFxyXG5cclxuICAgIGNvbnN0IGNsYXNzX2FkZDogYW55PSBbXTtcclxuICAgIC8vIGNvbnNvbGUubG9nKG5hbWUpXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG51bWJlcl9vZl9zcGVjaWVzOyBpKyspIHsgXHJcbiAgICAgIFxyXG4gICAgICAvL0NvbGxlY3RpbmcgdGhlIGltYWdlcyBmcm9tIEhUTUxcclxuICAgICAgY29uc3QgYXV4ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoYGNsYXNzLSR7bmFtZX0tJHtpfWApIGFzIEhUTUxJbWFnZUVsZW1lbnQ7ICAgICAgXHJcblxyXG4gICAgICAvL0FkZGluZyB0aGUgZXhhbXBsZVxyXG4gICAgICBjb25zdCBpbmRleD0gdGhpcy5jbGFzc2VzX25hbWVzLmZpbmRJbmRleCgoZWxlbSk9PmVsZW09PT1uYW1lKVxyXG5cclxuICAgICAgYXdhaXQgVGVhY2hhYmxlTW9iaWxlTmV0LmFkZEV4YW1wbGUoaW5kZXgsIG5hbWUsIGF1eCk7ICAgIFxyXG4gICAgICBcclxuICB9XHJcblxyXG4gIC8vIHRoaXMuY2xhc3Nlcy5wdXNoKHtuYW1lOiBuYW1lLCBpbWFnZXM6IGNsYXNzX2FkZH0pICBcclxuXHJcbiAgfVxyXG5cclxuICBkZWxheShtczogbnVtYmVyKSB7XHJcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcclxufVxyXG59XHJcbiIsIjxkaXYgKm5nRm9yPVwibGV0IGNsYXNzIG9mIGNsYXNzZXM7IGluZGV4IGFzIGlcIj5cclxuICA8aDE+e3tjbGFzcy5uYW1lfX08L2gxPlxyXG4gIDxpbWcgICpuZ0Zvcj1cImxldCBpdGVtIG9mIGNsYXNzLmltYWdlczsgaW5kZXggYXMgaVwiIFtzcmNdPVwiaXRlbVwiIHdpZHRoPVwiMjI0XCIgaGVpZ2h0PVwiMjI0XCIgW2lkXT1cIidjbGFzcy0nICsgY2xhc3MubmFtZSArICctJyArIGlcIiBjcm9zc29yaWdpbj1cImFub255bW91c1wiID5cclxuPC9kaXY+XHJcbiJdfQ==
|
@@ -0,0 +1,182 @@
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
2
|
+
export const IMAGE_SIZE = 224;
|
3
|
+
const DEFAULT_MOBILENET_VERSION = 2;
|
4
|
+
const DEFAULT_TRAINING_LAYER_V1 = 'conv_pw_13_relu';
|
5
|
+
const DEFAULT_TRAINING_LAYER_V2 = "out_relu";
|
6
|
+
const DEFAULT_ALPHA_V1_v2 = 0.35;
|
7
|
+
const DEFAULT_ALPHA_V1 = 0.25; //256
|
8
|
+
const DEFAULT_ALPHA_V2 = 0.5; //512
|
9
|
+
const DEFAULT_ALPHA_V3 = 0.75; //768 features
|
10
|
+
const DEFAULT_ALPHA_V4 = 1; //1024 features
|
11
|
+
const DEFAULT_ALPHA = 1; //1024 features
|
12
|
+
// v2: 0.35, 0.50, 0.75 or 1.00.
|
13
|
+
const isAlphaValid = (version, alpha) => {
|
14
|
+
if (version === 1) {
|
15
|
+
if (alpha !== 0.25 && alpha !== 0.5 && alpha !== 0.75 && alpha !== 1) {
|
16
|
+
console.warn("Invalid alpha. Options are: 0.25, 0.50, 0.75 or 1.00.");
|
17
|
+
console.log("Loading model with alpha: ", DEFAULT_ALPHA_V1.toFixed(2));
|
18
|
+
return DEFAULT_ALPHA_V1;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
if (alpha !== 0.35 && alpha !== 0.5 && alpha !== 0.75 && alpha !== 1) {
|
23
|
+
console.warn("Invalid alpha. Options are: 0.35, 0.50, 0.75 or 1.00.");
|
24
|
+
console.log("Loading model with alpha: ", DEFAULT_ALPHA_V2.toFixed(2));
|
25
|
+
return DEFAULT_ALPHA_V2;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
return alpha;
|
29
|
+
};
|
30
|
+
const parseModelOptions = (options) => {
|
31
|
+
options = options || {};
|
32
|
+
if (options.checkpointUrl && options.trainingLayer) {
|
33
|
+
if (options.alpha || options.version) {
|
34
|
+
console.warn("Checkpoint URL passed to modelOptions, alpha options are ignored");
|
35
|
+
}
|
36
|
+
return [options.checkpointUrl, options.trainingLayer];
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
options.version = options.version || DEFAULT_MOBILENET_VERSION;
|
40
|
+
if (options.version === 1) {
|
41
|
+
options.alpha = options.alpha || DEFAULT_ALPHA_V4;
|
42
|
+
options.alpha = isAlphaValid(options.version, options.alpha);
|
43
|
+
console.log(`Loading mobilenet ${options.version} and alpha ${options.alpha}`);
|
44
|
+
// exception is alpha o f 1 can only be 1.0
|
45
|
+
let alphaString = options.alpha.toFixed(2);
|
46
|
+
if (alphaString === "1.00") {
|
47
|
+
alphaString = "1.0";
|
48
|
+
}
|
49
|
+
console.log("Using the model: ");
|
50
|
+
return [
|
51
|
+
// tslint:disable-next-line:max-line-length
|
52
|
+
//They are loading MobileNet_v1
|
53
|
+
`https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_${alphaString}_${IMAGE_SIZE}/model.json`,
|
54
|
+
DEFAULT_TRAINING_LAYER_V1
|
55
|
+
];
|
56
|
+
}
|
57
|
+
else if (options.version === 2) {
|
58
|
+
options.alpha = options.alpha || DEFAULT_ALPHA_V4;
|
59
|
+
options.alpha = isAlphaValid(options.version, options.alpha);
|
60
|
+
console.log(`Loading mobilenet ${options.version} and alpha ${options.alpha}`);
|
61
|
+
console.log(`Loading mobilenet ${options.version} and alpha ${options.alpha}`);
|
62
|
+
return [
|
63
|
+
// tslint:disable-next-line:max-line-length
|
64
|
+
`https://storage.googleapis.com/teachable-machine-models/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_${options.alpha.toFixed(2)}_${IMAGE_SIZE}_no_top/model.json`,
|
65
|
+
DEFAULT_TRAINING_LAYER_V2
|
66
|
+
];
|
67
|
+
}
|
68
|
+
else {
|
69
|
+
throw new Error(`MobileNet V${options.version} doesn't exist`);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
};
|
73
|
+
/**
|
74
|
+
* load the base mobilenet model
|
75
|
+
* @param modelOptions options determining what model to load
|
76
|
+
*/
|
77
|
+
export async function loadTruncatedMobileNet(modelOptions) {
|
78
|
+
const [checkpointUrl, trainingLayer] = parseModelOptions(modelOptions);
|
79
|
+
const mobilenet = await tf.loadLayersModel(checkpointUrl);
|
80
|
+
if (modelOptions && modelOptions.version === 1) {
|
81
|
+
const layer = mobilenet.getLayer(trainingLayer);
|
82
|
+
const truncatedModel = tf.model({ inputs: mobilenet.inputs, outputs: layer.output });
|
83
|
+
console.log("Feature model loaded, memory: ", tf.memory().numBytes);
|
84
|
+
const model = tf.sequential();
|
85
|
+
model.add(truncatedModel);
|
86
|
+
model.add(tf.layers.flatten());
|
87
|
+
return model;
|
88
|
+
}
|
89
|
+
else {
|
90
|
+
const layer = mobilenet.getLayer(trainingLayer);
|
91
|
+
const truncatedModel = tf.model({ inputs: mobilenet.inputs, outputs: layer.output });
|
92
|
+
console.log("Feature model loaded, memory: ", tf.memory().numBytes);
|
93
|
+
const model = tf.sequential();
|
94
|
+
model.add(truncatedModel);
|
95
|
+
model.add(tf.layers.globalAveragePooling2d({})); // go from shape [7, 7, 1280] to [1280]
|
96
|
+
return model;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
export class CustomMobileNet {
|
100
|
+
static getinputShape() {
|
101
|
+
/**truncatedModel is the base model, the model used to apply transfer learning */
|
102
|
+
const inputShape = this.truncatedModel.outputs[0].shape.slice(1); // [ 7 x 7 x 1280] (not sure about those dimensions)
|
103
|
+
// console.log("Input Shape(complete): ", this.truncatedModel.outputs[0].shape);
|
104
|
+
// console.log("Input Shape: ", inputShape);
|
105
|
+
const inputSize = tf.util.sizeFromShape(inputShape);
|
106
|
+
// console.log("Input Size: ", inputSize);
|
107
|
+
return inputSize;
|
108
|
+
}
|
109
|
+
static get EXPECTED_IMAGE_SIZE() {
|
110
|
+
return IMAGE_SIZE;
|
111
|
+
}
|
112
|
+
getMetadata() {
|
113
|
+
return this._metadata;
|
114
|
+
}
|
115
|
+
constructor() {
|
116
|
+
// this._metadata = fillMetadata(metadata);
|
117
|
+
//Loading the truncated model
|
118
|
+
// loadTruncatedMobileNet();
|
119
|
+
// this.loadFeatureModel();
|
120
|
+
}
|
121
|
+
static async loadFeatureModel() {
|
122
|
+
this.truncatedModel = await loadTruncatedMobileNet();
|
123
|
+
}
|
124
|
+
/**
|
125
|
+
* get the total number of classes existing within model
|
126
|
+
*/
|
127
|
+
// getTotalClasses() {
|
128
|
+
// const output = this.model.output as SymbolicTensor;
|
129
|
+
// const totalClasses = output.shape[1];
|
130
|
+
// return totalClasses;
|
131
|
+
// }
|
132
|
+
/**
|
133
|
+
* get the model labels
|
134
|
+
*/
|
135
|
+
getClassLabels() {
|
136
|
+
return this._metadata.labels;
|
137
|
+
}
|
138
|
+
/**
|
139
|
+
* Given an image element, makes a prediction through mobilenet returning the
|
140
|
+
* probabilities of the top K classes.
|
141
|
+
* @param image the image to classify
|
142
|
+
* @param maxPredictions the maximum number of classification predictions
|
143
|
+
*/
|
144
|
+
// async predictTopK(image: ClassifierInputSource, maxPredictions = 10, flipped = false) {
|
145
|
+
// const croppedImage = cropTo(image, this._metadata.imageSize, flipped);
|
146
|
+
// const logits = tf.tidy(() => {
|
147
|
+
// const captured = capture(croppedImage, this._metadata.grayscale);
|
148
|
+
// return this.model.predict(captured);
|
149
|
+
// });
|
150
|
+
// // Convert logits to probabilities and class names.
|
151
|
+
// const classes = await getTopKClasses(this._metadata.labels, logits as tf.Tensor<tf.Rank>, maxPredictions);
|
152
|
+
// dispose(logits);
|
153
|
+
// return classes;
|
154
|
+
// }
|
155
|
+
/**
|
156
|
+
* Given an image element, makes a prediction through mobilenet returning the
|
157
|
+
* probabilities for ALL classes.
|
158
|
+
* @param image the image to classify
|
159
|
+
* @param flipped whether to flip the image on X
|
160
|
+
*/
|
161
|
+
// async predict(image: ClassifierInputSource, flipped = false) {
|
162
|
+
// const croppedImage = cropTo(image, this._metadata.imageSize, flipped);
|
163
|
+
// const logits = tf.tidy(() => {
|
164
|
+
// const captured = capture(croppedImage, this._metadata.grayscale);
|
165
|
+
// return this.model.predict(captured);
|
166
|
+
// });
|
167
|
+
// const values = await (logits as tf.Tensor<tf.Rank>).data();
|
168
|
+
// const classes = [];
|
169
|
+
// for (let i = 0; i < values.length; i++) {
|
170
|
+
// classes.push({
|
171
|
+
// className: this._metadata.labels[i],
|
172
|
+
// probability: values[i]
|
173
|
+
// });
|
174
|
+
// }
|
175
|
+
// dispose(logits);
|
176
|
+
// return classes;
|
177
|
+
// }
|
178
|
+
dispose() {
|
179
|
+
this.truncatedModel.dispose();
|
180
|
+
}
|
181
|
+
} // end of CustomMobileNet
|
182
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tLW1vYmlsZW5ldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RmanMtZXZvbHV0aW9uL3NyYy9saWIvbW9kZWxzL2N1c3RvbS1tb2JpbGVuZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUt2QyxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDO0FBNEI5QixNQUFNLHlCQUF5QixHQUFHLENBQUMsQ0FBQztBQUNwQyxNQUFNLHlCQUF5QixHQUFHLGlCQUFpQixDQUFDO0FBQ3BELE1BQU0seUJBQXlCLEdBQUcsVUFBVSxDQUFDO0FBQzdDLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDO0FBQ2pDLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLENBQUEsS0FBSztBQUNuQyxNQUFNLGdCQUFnQixHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUs7QUFDbkMsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsQ0FBQSxjQUFjO0FBQzVDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDLENBQUEsZUFBZTtBQUMxQyxNQUFNLGFBQWEsR0FBRyxDQUFDLENBQUMsQ0FBQSxlQUFlO0FBRXZDLGdDQUFnQztBQUVoQyxNQUFNLFlBQVksR0FBRyxDQUFDLE9BQWUsRUFBRSxLQUFhLEVBQUUsRUFBRTtJQUNwRCxJQUFJLE9BQU8sS0FBSyxDQUFDLEVBQUU7UUFDZixJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxLQUFLLEdBQUcsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxDQUFDLEVBQUU7WUFDbEUsT0FBTyxDQUFDLElBQUksQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1lBQ3RFLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLEVBQUUsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENB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QUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQztJQUNqQyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCwwRkFBMEY7SUFDMUYsNkVBQTZFO0lBRTdFLHFDQUFxQztJQUNyQyw0RUFBNEU7SUFDNUUsK0NBQStDO0lBQy9DLFVBQVU7SUFFViwwREFBMEQ7SUFDMUQsaUhBQWlIO0lBQ2pILHVCQUF1QjtJQUV2QixzQkFBc0I7SUFDdEIsSUFBSTtJQUVKOzs7OztPQUtHO0lBQ0gsaUVBQWlFO0lBQ2pFLDZFQUE2RTtJQUU3RSxxQ0FBcUM7SUFDckMsNEVBQTRFO0lBQzVFLCtDQUErQztJQUMvQyxVQUFVO0lBRVYsa0VBQWtFO0lBRWxFLDBCQUEwQjtJQUMxQixnREFBZ0Q7SUFDaEQseUJBQXlCO0lBQ3pCLG1EQUFtRDtJQUNuRCxxQ0FBcUM7SUFDckMsY0FBYztJQUNkLFFBQVE7SUFFUix1QkFBdUI7SUFFdkIsc0JBQXNCO0lBQ3RCLElBQUk7SUFFRyxPQUFPO1FBQ1YsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0NBQ0osQ0FBQSx5QkFBeUIiLCJzb3VyY2VzQ29udGVudCI6WyJcclxuaW1wb3J0ICogYXMgdGYgZnJvbSAnQHRlbnNvcmZsb3cvdGZqcyc7XHJcblxyXG5pbXBvcnQgeyBTeW1ib2xpY1RlbnNvciB9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMnO1xyXG5cclxuXHJcbmV4cG9ydCBjb25zdCBJTUFHRV9TSVpFID0gMjI0O1xyXG5cclxuXHJcbi8qKlxyXG4gKiB0aGUgbWV0YWRhdGEgdG8gZGVzY3JpYmUgdGhlIG1vZGVsJ3MgY3JlYXRpb24sXHJcbiAqIGluY2x1ZGVzIHRoZSBsYWJlbHMgYXNzb2NpYXRlZCB3aXRoIHRoZSBjbGFzc2VzXHJcbiAqIGFuZCB2ZXJzaW9uaW5nIGluZm9ybWF0aW9uIGZyb20gdHJhaW5pbmcuXHJcbiAqL1xyXG5leHBvcnQgaW50ZXJmYWNlIE1ldGFkYXRhIHtcclxuICAgIHRmanNWZXJzaW9uOiBzdHJpbmc7XHJcbiAgICB0bVZlcnNpb24/OiBzdHJpbmc7XHJcbiAgICBwYWNrYWdlVmVyc2lvbjogc3RyaW5nO1xyXG4gICAgcGFja2FnZU5hbWU6IHN0cmluZztcclxuICAgIG1vZGVsTmFtZT86IHN0cmluZztcclxuICAgIHRpbWVTdGFtcD86IHN0cmluZztcclxuICAgIGxhYmVsczogc3RyaW5nW107XHJcbiAgICB1c2VyTWV0YWRhdGE/OiB7fTtcclxuICAgIGdyYXlzY2FsZT86IGJvb2xlYW47XHJcbiAgICBpbWFnZVNpemU/OiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgTW9kZWxPcHRpb25zIHtcclxuICAgIHZlcnNpb24/OiBudW1iZXI7XHJcbiAgICBjaGVja3BvaW50VXJsPzogc3RyaW5nO1xyXG4gICAgYWxwaGE/OiBudW1iZXI7XHJcbiAgICB0cmFpbmluZ0xheWVyPzogc3RyaW5nO1xyXG59XHJcblxyXG5jb25zdCBERUZBVUxUX01PQklMRU5FVF9WRVJTSU9OID0gMjtcclxuY29uc3QgREVGQVVMVF9UUkFJTklOR19MQVlFUl9WMSA9ICdjb252X3B3XzEzX3JlbHUnO1xyXG5jb25zdCBERUZBVUxUX1RSQUlOSU5HX0xBWUVSX1YyID0gXCJvdXRfcmVsdVwiOyBcclxuY29uc3QgREVGQVVMVF9BTFBIQV9WMV92MiA9IDAuMzU7XHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjEgPSAwLjI1Oy8vMjU2XHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjIgPSAwLjU7IC8vNTEyXHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjMgPSAwLjc1Oy8vNzY4IGZlYXR1cmVzXHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjQgPSAxOy8vMTAyNCBmZWF0dXJlc1xyXG5jb25zdCBERUZBVUxUX0FMUEhBID0gMTsvLzEwMjQgZmVhdHVyZXNcclxuXHJcbi8vIHYyOiAwLjM1LCAwLjUwLCAwLjc1IG9yIDEuMDAuXHJcblxyXG5jb25zdCBpc0FscGhhVmFsaWQgPSAodmVyc2lvbjogbnVtYmVyLCBhbHBoYTogbnVtYmVyKSA9PiB7XHJcbiAgICBpZiAodmVyc2lvbiA9PT0gMSkge1xyXG4gICAgICAgIGlmIChhbHBoYSAhPT0gMC4yNSAmJiBhbHBoYSAhPT0gMC41ICYmIGFscGhhICE9PSAwLjc1ICYmIGFscGhhICE9PSAxKSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkludmFsaWQgYWxwaGEuIE9wdGlvbnMgYXJlOiAwLjI1LCAwLjUwLCAwLjc1IG9yIDEuMDAuXCIpO1xyXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIkxvYWRpbmcgbW9kZWwgd2l0aCBhbHBoYTogXCIsIERFRkFVTFRfQUxQSEFfVjEudG9GaXhlZCgyKSk7IFxyXG4gICAgICAgICAgICByZXR1cm4gREVGQVVMVF9BTFBIQV9WMTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBlbHNlIHtcclxuICAgICAgICBpZiAoYWxwaGEgIT09IDAuMzUgJiYgYWxwaGEgIT09IDAuNSAmJiBhbHBoYSAhPT0gMC43NSAmJiBhbHBoYSAhPT0gMSkge1xyXG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJJbnZhbGlkIGFscGhhLiBPcHRpb25zIGFyZTogMC4zNSwgMC41MCwgMC43NSBvciAxLjAwLlwiKTtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coXCJMb2FkaW5nIG1vZGVsIHdpdGggYWxwaGE6IFwiLCBERUZBVUxUX0FMUEhBX1YyLnRvRml4ZWQoMikpOyBcclxuICAgICAgICAgICAgcmV0dXJuIERFRkFVTFRfQUxQSEFfVjI7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBhbHBoYTtcclxufTtcclxuXHJcblxyXG5jb25zdCBwYXJzZU1vZGVsT3B0aW9ucyA9IChvcHRpb25zPzogTW9kZWxPcHRpb25zKSA9PiB7XHJcbiAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fVxyXG5cclxuICAgIGlmIChvcHRpb25zLmNoZWNrcG9pbnRVcmwgJiYgb3B0aW9ucy50cmFpbmluZ0xheWVyKSB7XHJcbiAgICAgICAgaWYgKG9wdGlvbnMuYWxwaGEgfHwgb3B0aW9ucy52ZXJzaW9uKXtcclxuICAgICAgICAgICAgY29uc29sZS53YXJuKFwiQ2hlY2twb2ludCBVUkwgcGFzc2VkIHRvIG1vZGVsT3B0aW9ucywgYWxwaGEgb3B0aW9ucyBhcmUgaWdub3JlZFwiKTtcclxuICAgICAgICB9ICAgICAgICBcclxuICAgICAgICByZXR1cm4gW29wdGlvbnMuY2hlY2twb2ludFVybCwgb3B0aW9ucy50cmFpbmluZ0xheWVyXTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgb3B0aW9ucy52ZXJzaW9uID0gb3B0aW9ucy52ZXJzaW9uIHx8IERFRkFVTFRfTU9CSUxFTkVUX1ZFUlNJT047XHJcbiAgICAgICAgXHJcbiAgICAgICAgaWYob3B0aW9ucy52ZXJzaW9uID09PSAxKXtcclxuICAgICAgICAgICAgb3B0aW9ucy5hbHBoYSA9IG9wdGlvbnMuYWxwaGEgfHwgREVGQVVMVF9BTFBIQV9WNDsgIFxyXG4gICAgICAgICAgICBvcHRpb25zLmFscGhhID0gaXNBbHBoYVZhbGlkKG9wdGlvbnMudmVyc2lvbiwgb3B0aW9ucy5hbHBoYSk7XHJcblxyXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgTG9hZGluZyBtb2JpbGVuZXQgJHtvcHRpb25zLnZlcnNpb259IGFuZCBhbHBoYSAke29wdGlvbnMuYWxwaGF9YCk7XHJcbiAgICAgICAgICAgIC8vIGV4Y2VwdGlvbiBpcyBhbHBoYSBvIGYgMSBjYW4gb25seSBiZSAxLjBcclxuICAgICAgICAgICAgbGV0IGFscGhhU3RyaW5nID0gb3B0aW9ucy5hbHBoYS50b0ZpeGVkKDIpO1xyXG4gICAgICAgICAgICBpZiAoYWxwaGFTdHJpbmcgPT09IFwiMS4wMFwiKSB7IGFscGhhU3RyaW5nID0gXCIxLjBcIjsgfVxyXG5cclxuICAgICAgICAgICAgY29uc29sZS5sb2coXCJVc2luZyB0aGUgbW9kZWw6IFwiLCAgKVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIFtcclxuICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTptYXgtbGluZS1sZW5ndGhcclxuICAgICAgICAgICAgICAgIC8vVGhleSBhcmUgbG9hZGluZyBNb2JpbGVOZXRfdjEgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgYGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS90ZmpzLW1vZGVscy90ZmpzL21vYmlsZW5ldF92MV8ke2FscGhhU3RyaW5nfV8ke0lNQUdFX1NJWkV9L21vZGVsLmpzb25gLFxyXG4gICAgICAgICAgICAgICAgREVGQVVMVF9UUkFJTklOR19MQVlFUl9WMVxyXG4gICAgICAgICAgICBdO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIGlmIChvcHRpb25zLnZlcnNpb24gPT09IDIpe1xyXG4gICAgICAgICAgICBvcHRpb25zLmFscGhhID0gb3B0aW9ucy5hbHBoYSB8fCBERUZBVUxUX0FMUEhBX1Y0OyAgXHJcbiAgICAgICAgICAgIG9wdGlvbnMuYWxwaGEgPSBpc0FscGhhVmFsaWQob3B0aW9ucy52ZXJzaW9uLCBvcHRpb25zLmFscGhhKTtcclxuXHJcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBMb2FkaW5nIG1vYmlsZW5ldCAke29wdGlvbnMudmVyc2lvbn0gYW5kIGFscGhhICR7b3B0aW9ucy5hbHBoYX1gKTtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coYExvYWRpbmcgbW9iaWxlbmV0ICR7b3B0aW9ucy52ZXJzaW9ufSBhbmQgYWxwaGEgJHtvcHRpb25zLmFscGhhfWApO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIFtcclxuICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTptYXgtbGluZS1sZW5ndGggICAgICAgIFxyXG4gICAgICAgICAgICAgICAgYGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS90ZWFjaGFibGUtbWFjaGluZS1tb2RlbHMvbW9iaWxlbmV0X3YyX3dlaWdodHNfdGZfZGltX29yZGVyaW5nX3RmX2tlcm5lbHNfJHtvcHRpb25zLmFscGhhLnRvRml4ZWQoMil9XyR7SU1BR0VfU0laRX1fbm9fdG9wL21vZGVsLmpzb25gLFxyXG4gICAgICAgICAgICAgICAgREVGQVVMVF9UUkFJTklOR19MQVlFUl9WMlxyXG4gICAgICAgICAgICBdO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTW9iaWxlTmV0IFYke29wdGlvbnMudmVyc2lvbn0gZG9lc24ndCBleGlzdGApO1xyXG4gICAgICAgIH0gICBcclxuICAgIH1cclxufTtcclxuXHJcblxyXG4vKipcclxuICogbG9hZCB0aGUgYmFzZSBtb2JpbGVuZXQgbW9kZWxcclxuICogQHBhcmFtIG1vZGVsT3B0aW9ucyBvcHRpb25zIGRldGVybWluaW5nIHdoYXQgbW9kZWwgdG8gbG9hZFxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRUcnVuY2F0ZWRNb2JpbGVOZXQobW9kZWxPcHRpb25zPzogTW9kZWxPcHRpb25zKSB7XHJcbiAgICBjb25zdCBbY2hlY2twb2ludFVybCwgdHJhaW5pbmdMYXllcl0gPSBwYXJzZU1vZGVsT3B0aW9ucyhtb2RlbE9wdGlvbnMpO1xyXG4gICAgXHJcbiAgICBcclxuICAgIGNvbnN0IG1vYmlsZW5ldCA9IGF3YWl0IHRmLmxvYWRMYXllcnNNb2RlbChjaGVja3BvaW50VXJsKTtcclxuXHJcbiAgICBpZiAobW9kZWxPcHRpb25zICYmIG1vZGVsT3B0aW9ucy52ZXJzaW9uID09PSAxKXtcclxuICAgICAgICBjb25zdCBsYXllciA9IG1vYmlsZW5ldC5nZXRMYXllcih0cmFpbmluZ0xheWVyKTtcclxuICAgICAgICBcclxuICAgICAgICBjb25zdCB0cnVuY2F0ZWRNb2RlbCA9IHRmLm1vZGVsKHsgaW5wdXRzOiBtb2JpbGVuZXQuaW5wdXRzLCBvdXRwdXRzOiBsYXllci5vdXRwdXQgfSk7XHJcbiAgICAgICAgY29uc29sZS5sb2coXCJGZWF0dXJlIG1vZGVsIGxvYWRlZCwgbWVtb3J5OiBcIiwgdGYubWVtb3J5KCkubnVtQnl0ZXMpO1xyXG5cclxuICAgICAgICBcclxuICAgICAgICBjb25zdCBtb2RlbCA9IHRmLnNlcXVlbnRpYWwoKTtcclxuICAgICAgICBtb2RlbC5hZGQodHJ1bmNhdGVkTW9kZWwpO1xyXG4gICAgICAgIG1vZGVsLmFkZCh0Zi5sYXllcnMuZmxhdHRlbigpKTsgICAgICAgIFxyXG4gICAgICAgIFxyXG4gICAgICAgIFxyXG4gICAgICAgIHJldHVybiBtb2RlbDtcclxuICAgIH1cclxuICAgIGVsc2Uge1xyXG4gICAgICAgIGNvbnN0IGxheWVyID0gbW9iaWxlbmV0LmdldExheWVyKHRyYWluaW5nTGF5ZXIpO1xyXG4gICAgICAgIGNvbnN0IHRydW5jYXRlZE1vZGVsID0gdGYubW9kZWwoeyBpbnB1dHM6IG1vYmlsZW5ldC5pbnB1dHMsIG91dHB1dHM6IGxheWVyLm91dHB1dCB9KTtcclxuICAgICAgICBjb25zb2xlLmxvZyhcIkZlYXR1cmUgbW9kZWwgbG9hZGVkLCBtZW1vcnk6IFwiLCB0Zi5tZW1vcnkoKS5udW1CeXRlcyk7XHJcbiAgICAgICAgY29uc3QgbW9kZWwgPSB0Zi5zZXF1ZW50aWFsKCk7XHJcbiAgICAgICAgbW9kZWwuYWRkKHRydW5jYXRlZE1vZGVsKTtcclxuICAgICAgICBtb2RlbC5hZGQodGYubGF5ZXJzLmdsb2JhbEF2ZXJhZ2VQb29saW5nMmQoe30pKTsgLy8gZ28gZnJvbSBzaGFwZSBbNywgNywgMTI4MF0gdG8gWzEyODBdXHJcbiAgICAgICAgcmV0dXJuIG1vZGVsO1xyXG4gICAgfVxyXG59XHJcblxyXG5cclxuZXhwb3J0IGNsYXNzIEN1c3RvbU1vYmlsZU5ldCB7XHJcbiAgICAvKipcclxuICAgICAqIHRoZSB0cnVuY2F0ZWQgbW9iaWxlbmV0IG1vZGVsIHdlIHdpbGwgdHJhaW4gb24gdG9wIG9mXHJcbiAgICAgKi9cclxuICAgIHByb3RlY3RlZCB0cnVuY2F0ZWRNb2RlbCE6IHRmLkxheWVyc01vZGVsO1xyXG4gICAgc3RhdGljIHRydW5jYXRlZE1vZGVsOiB0Zi5TZXF1ZW50aWFsO1xyXG5cclxuc3RhdGljIGdldGlucHV0U2hhcGUoKXtcclxuICAgLyoqdHJ1bmNhdGVkTW9kZWwgaXMgdGhlIGJhc2UgbW9kZWwsIHRoZSBtb2RlbCB1c2VkIHRvIGFwcGx5IHRyYW5zZmVyIGxlYXJuaW5nICovXHJcbiAgY29uc3QgaW5wdXRTaGFwZTogYW55ID0gdGhpcy50cnVuY2F0ZWRNb2RlbC5vdXRwdXRzWzBdLnNoYXBlLnNsaWNlKDEpOyAvLyBbIDcgeCA3IHggMTI4MF0gKG5vdCBzdXJlIGFib3V0IHRob3NlIGRpbWVuc2lvbnMpXHJcbi8vICAgY29uc29sZS5sb2coXCJJbnB1dCBTaGFwZShjb21wbGV0ZSk6IFwiLCB0aGlzLnRydW5jYXRlZE1vZGVsLm91dHB1dHNbMF0uc2hhcGUpO1xyXG4vLyAgIGNvbnNvbGUubG9nKFwiSW5wdXQgU2hhcGU6IFwiLCBpbnB1dFNoYXBlKTtcclxuXHJcbiAgY29uc3QgaW5wdXRTaXplID0gdGYudXRpbC5zaXplRnJvbVNoYXBlKGlucHV0U2hhcGUpO1xyXG5cclxuLy8gICBjb25zb2xlLmxvZyhcIklucHV0IFNpemU6IFwiLCBpbnB1dFNpemUpO1xyXG5cclxuICByZXR1cm4gaW5wdXRTaXplO1xyXG59XHJcblxyXG4gICAgc3RhdGljIGdldCBFWFBFQ1RFRF9JTUFHRV9TSVpFKCkge1xyXG4gICAgICAgIHJldHVybiBJTUFHRV9TSVpFO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBfbWV0YWRhdGEhOiBNZXRhZGF0YTtcclxuXHJcbiAgICBwdWJsaWMgZ2V0TWV0YWRhdGEoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX21ldGFkYXRhO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIC8vIHRoaXMuX21ldGFkYXRhID0gZmlsbE1ldGFkYXRhKG1ldGFkYXRhKTtcclxuICAgICAgICAvL0xvYWRpbmcgdGhlIHRydW5jYXRlZCBtb2RlbFxyXG4gICAgICAgIC8vIGxvYWRUcnVuY2F0ZWRNb2JpbGVOZXQoKTtcclxuICAgICAgICAvLyB0aGlzLmxvYWRGZWF0dXJlTW9kZWwoKTtcclxuXHJcbiAgICB9XHJcblxyXG4gICBzdGF0aWMgIGFzeW5jIGxvYWRGZWF0dXJlTW9kZWwoKXtcclxuXHJcbiAgICAgICAgdGhpcy50cnVuY2F0ZWRNb2RlbCA9IGF3YWl0IGxvYWRUcnVuY2F0ZWRNb2JpbGVOZXQoKTtcclxuXHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBnZXQgdGhlIHRvdGFsIG51bWJlciBvZiBjbGFzc2VzIGV4aXN0aW5nIHdpdGhpbiBtb2RlbFxyXG4gICAgICovXHJcbiAgICAvLyBnZXRUb3RhbENsYXNzZXMoKSB7XHJcbiAgICAvLyAgICAgY29uc3Qgb3V0cHV0ID0gdGhpcy5tb2RlbC5vdXRwdXQgYXMgU3ltYm9saWNUZW5zb3I7XHJcbiAgICAvLyAgICAgY29uc3QgdG90YWxDbGFzc2VzID0gb3V0cHV0LnNoYXBlWzFdO1xyXG4gICAgLy8gICAgIHJldHVybiB0b3RhbENsYXNzZXM7XHJcbiAgICAvLyB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBnZXQgdGhlIG1vZGVsIGxhYmVsc1xyXG4gICAgICovXHJcbiAgICBnZXRDbGFzc0xhYmVscygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGEubGFiZWxzO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogR2l2ZW4gYW4gaW1hZ2UgZWxlbWVudCwgbWFrZXMgYSBwcmVkaWN0aW9uIHRocm91Z2ggbW9iaWxlbmV0IHJldHVybmluZyB0aGVcclxuICAgICAqIHByb2JhYmlsaXRpZXMgb2YgdGhlIHRvcCBLIGNsYXNzZXMuXHJcbiAgICAgKiBAcGFyYW0gaW1hZ2UgdGhlIGltYWdlIHRvIGNsYXNzaWZ5XHJcbiAgICAgKiBAcGFyYW0gbWF4UHJlZGljdGlvbnMgdGhlIG1heGltdW0gbnVtYmVyIG9mIGNsYXNzaWZpY2F0aW9uIHByZWRpY3Rpb25zXHJcbiAgICAgKi9cclxuICAgIC8vIGFzeW5jIHByZWRpY3RUb3BLKGltYWdlOiBDbGFzc2lmaWVySW5wdXRTb3VyY2UsIG1heFByZWRpY3Rpb25zID0gMTAsIGZsaXBwZWQgPSBmYWxzZSkge1xyXG4gICAgLy8gICAgIGNvbnN0IGNyb3BwZWRJbWFnZSA9IGNyb3BUbyhpbWFnZSwgdGhpcy5fbWV0YWRhdGEuaW1hZ2VTaXplLCBmbGlwcGVkKTtcclxuXHJcbiAgICAvLyAgICAgY29uc3QgbG9naXRzID0gdGYudGlkeSgoKSA9PiB7XHJcbiAgICAvLyAgICAgICAgIGNvbnN0IGNhcHR1cmVkID0gY2FwdHVyZShjcm9wcGVkSW1hZ2UsIHRoaXMuX21ldGFkYXRhLmdyYXlzY2FsZSk7XHJcbiAgICAvLyAgICAgICAgIHJldHVybiB0aGlzLm1vZGVsLnByZWRpY3QoY2FwdHVyZWQpO1xyXG4gICAgLy8gICAgIH0pO1xyXG5cclxuICAgIC8vICAgICAvLyBDb252ZXJ0IGxvZ2l0cyB0byBwcm9iYWJpbGl0aWVzIGFuZCBjbGFzcyBuYW1lcy5cclxuICAgIC8vICAgICBjb25zdCBjbGFzc2VzID0gYXdhaXQgZ2V0VG9wS0NsYXNzZXModGhpcy5fbWV0YWRhdGEubGFiZWxzLCBsb2dpdHMgYXMgdGYuVGVuc29yPHRmLlJhbms+LCBtYXhQcmVkaWN0aW9ucyk7XHJcbiAgICAvLyAgICAgZGlzcG9zZShsb2dpdHMpO1xyXG5cclxuICAgIC8vICAgICByZXR1cm4gY2xhc3NlcztcclxuICAgIC8vIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEdpdmVuIGFuIGltYWdlIGVsZW1lbnQsIG1ha2VzIGEgcHJlZGljdGlvbiB0aHJvdWdoIG1vYmlsZW5ldCByZXR1cm5pbmcgdGhlXHJcbiAgICAgKiBwcm9iYWJpbGl0aWVzIGZvciBBTEwgY2xhc3Nlcy5cclxuICAgICAqIEBwYXJhbSBpbWFnZSB0aGUgaW1hZ2UgdG8gY2xhc3NpZnlcclxuICAgICAqIEBwYXJhbSBmbGlwcGVkIHdoZXRoZXIgdG8gZmxpcCB0aGUgaW1hZ2Ugb24gWFxyXG4gICAgICovXHJcbiAgICAvLyBhc3luYyBwcmVkaWN0KGltYWdlOiBDbGFzc2lmaWVySW5wdXRTb3VyY2UsIGZsaXBwZWQgPSBmYWxzZSkge1xyXG4gICAgLy8gICAgIGNvbnN0IGNyb3BwZWRJbWFnZSA9IGNyb3BUbyhpbWFnZSwgdGhpcy5fbWV0YWRhdGEuaW1hZ2VTaXplLCBmbGlwcGVkKTtcclxuXHJcbiAgICAvLyAgICAgY29uc3QgbG9naXRzID0gdGYudGlkeSgoKSA9PiB7XHJcbiAgICAvLyAgICAgICAgIGNvbnN0IGNhcHR1cmVkID0gY2FwdHVyZShjcm9wcGVkSW1hZ2UsIHRoaXMuX21ldGFkYXRhLmdyYXlzY2FsZSk7XHJcbiAgICAvLyAgICAgICAgIHJldHVybiB0aGlzLm1vZGVsLnByZWRpY3QoY2FwdHVyZWQpO1xyXG4gICAgLy8gICAgIH0pO1xyXG5cclxuICAgIC8vICAgICBjb25zdCB2YWx1ZXMgPSBhd2FpdCAobG9naXRzIGFzIHRmLlRlbnNvcjx0Zi5SYW5rPikuZGF0YSgpO1xyXG5cclxuICAgIC8vICAgICBjb25zdCBjbGFzc2VzID0gW107XHJcbiAgICAvLyAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyBpKyspIHtcclxuICAgIC8vICAgICAgICAgY2xhc3Nlcy5wdXNoKHtcclxuICAgIC8vICAgICAgICAgICAgIGNsYXNzTmFtZTogdGhpcy5fbWV0YWRhdGEubGFiZWxzW2ldLFxyXG4gICAgLy8gICAgICAgICAgICAgcHJvYmFiaWxpdHk6IHZhbHVlc1tpXVxyXG4gICAgLy8gICAgICAgICB9KTtcclxuICAgIC8vICAgICB9XHJcblxyXG4gICAgLy8gICAgIGRpc3Bvc2UobG9naXRzKTtcclxuXHJcbiAgICAvLyAgICAgcmV0dXJuIGNsYXNzZXM7XHJcbiAgICAvLyB9XHJcblxyXG4gICAgcHVibGljIGRpc3Bvc2UoKSB7XHJcbiAgICAgICAgdGhpcy50cnVuY2F0ZWRNb2RlbC5kaXNwb3NlKCk7XHJcbiAgICB9XHJcbn0vLyBlbmQgb2YgQ3VzdG9tTW9iaWxlTmV0XHJcbiJdfQ==
|