optica-core 0.2.1 → 0.2.2
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/package.json
CHANGED
package/src/data/models/image.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Minimal image identity shared by every app (stack, rotation, spectral, ...)
|
|
2
2
|
export type ImageName = {
|
|
3
3
|
name: string,
|
|
4
|
-
label: string
|
|
4
|
+
label: string,
|
|
5
|
+
// sent by the backend when the thumbnail of this image exists
|
|
6
|
+
hasThumbnail?: boolean
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
// What an annotation keeps of its image: the name to find it back, the label and the
|
|
@@ -11,7 +11,6 @@ export type AcquisitionObject<TImage extends ProjectImage = ProjectImage> = {
|
|
|
11
11
|
// images on their own: the stacked results, the depth maps...
|
|
12
12
|
standaloneImages?: TImage[]
|
|
13
13
|
size: Size
|
|
14
|
-
thumbnails: boolean
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
// Images taken together, with the viewer state and the annotations of the acquisition.
|
|
@@ -22,7 +21,6 @@ export class Acquisition<TImage extends ProjectImage = ProjectImage, TAnnotation
|
|
|
22
21
|
images: TImage[]
|
|
23
22
|
standaloneImages: TImage[]
|
|
24
23
|
size: Size
|
|
25
|
-
thumbnails: boolean
|
|
26
24
|
camera: Camera
|
|
27
25
|
// shared by the images of the group and the standalone ones
|
|
28
26
|
annotations: TAnnotations
|
|
@@ -37,7 +35,6 @@ export class Acquisition<TImage extends ProjectImage = ProjectImage, TAnnotation
|
|
|
37
35
|
this.images = object.images
|
|
38
36
|
this.standaloneImages = object.standaloneImages ?? []
|
|
39
37
|
this.size = object.size
|
|
40
|
-
this.thumbnails = object.thumbnails
|
|
41
38
|
this.camera = createCamera()
|
|
42
39
|
this.annotations = this.createAnnotations()
|
|
43
40
|
this.index = 0
|
|
@@ -121,7 +118,6 @@ export class Acquisition<TImage extends ProjectImage = ProjectImage, TAnnotation
|
|
|
121
118
|
images: this.images,
|
|
122
119
|
standaloneImages: this.standaloneImages,
|
|
123
120
|
size: this.size,
|
|
124
|
-
thumbnails: this.thumbnails,
|
|
125
121
|
}
|
|
126
122
|
}
|
|
127
123
|
}
|
|
@@ -28,11 +28,12 @@ export abstract class DataRepository<TData> implements Repository<TData> {
|
|
|
28
28
|
return this.provider.getThumbnail(objectPath, imageName)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// Fill the image and thumbnail urls of images sent by /images
|
|
32
|
-
|
|
31
|
+
// Fill the image and thumbnail urls of images sent by /images,
|
|
32
|
+
// the thumbnail url only for the images the backend marks with `hasThumbnail`
|
|
33
|
+
protected resolveUrls<T extends ImageName>(objectPath: string, images: T[]): (T & ProjectImage)[] {
|
|
33
34
|
return images.map((image) => Object.assign(image, {
|
|
34
35
|
image: this.getFullImage(objectPath, image.name),
|
|
35
|
-
thumbnail:
|
|
36
|
+
thumbnail: image.hasThumbnail ? this.getThumbnail(objectPath, image.name) : undefined,
|
|
36
37
|
}))
|
|
37
38
|
}
|
|
38
39
|
}
|
|
@@ -34,8 +34,8 @@ export class ProjectRepository<TImage extends ProjectImage = ProjectImage, TAcqu
|
|
|
34
34
|
async getImages(objectPath: string): Promise<Project<TImage, TAcquisition>> {
|
|
35
35
|
const res = await this.provider.getImages(objectPath)
|
|
36
36
|
const acquisitions = this.parse(res.data).map((object) => {
|
|
37
|
-
object.images = this.resolveUrls(objectPath, object.images
|
|
38
|
-
object.standaloneImages = this.resolveUrls(objectPath, object.standaloneImages ?? []
|
|
37
|
+
object.images = this.resolveUrls(objectPath, object.images)
|
|
38
|
+
object.standaloneImages = this.resolveUrls(objectPath, object.standaloneImages ?? [])
|
|
39
39
|
return this.createAcquisition(object)
|
|
40
40
|
})
|
|
41
41
|
return new Project<TImage, TAcquisition>(objectPath, acquisitions)
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
const size = { width: 100, height: 100 }
|
|
10
10
|
const image = (name: string): ProjectImage => ({ name, label: name, image: "" })
|
|
11
11
|
const landmark = (id: string, img = image("i0")) => new Landmark(id, id, { marker: { x: 1, y: 2 }, image: img }, { x: 10, y: 20 }, Color("#ff0000"))
|
|
12
|
-
const acquisition = (id: string, names: string[] = ["i0"]) => new Acquisition({ id, images: names.map(image), size
|
|
12
|
+
const acquisition = (id: string, names: string[] = ["i0"]) => new Acquisition({ id, images: names.map(image), size })
|
|
13
13
|
|
|
14
14
|
describe("Annotations", () => {
|
|
15
15
|
it("creates a selected distance and returns the reactive copy through a store", () => {
|
|
@@ -88,7 +88,7 @@ class TaggedAcquisition extends Acquisition<ProjectImage> {
|
|
|
88
88
|
|
|
89
89
|
describe("Project annotations", () => {
|
|
90
90
|
it("lets an app use its own Annotations", () => {
|
|
91
|
-
const a = new TaggedAcquisition({ id: "a", images: [image("i0")], size
|
|
91
|
+
const a = new TaggedAcquisition({ id: "a", images: [image("i0")], size })
|
|
92
92
|
expect(a.annotations.tagged).toBe(true)
|
|
93
93
|
a.annotations.restore({ distances: [{ label: "d", color: "#00ff00", landmarks: [], value: 0 }] })
|
|
94
94
|
expect(a.annotations.distances[0]).toBeInstanceOf(Distance)
|
package/tests/project.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ const size = { width: 2000, height: 1000 }
|
|
|
6
6
|
const img = (name: string, extra = {}): ProjectImage => ({ name, label: name, image: "", ...extra })
|
|
7
7
|
|
|
8
8
|
function acquisition(images: string[], standalone: string[] = []): Acquisition {
|
|
9
|
-
return new Acquisition({ id: "a", images: images.map((n) => img(n)), standaloneImages: standalone.map((n) => img(n)), size
|
|
9
|
+
return new Acquisition({ id: "a", images: images.map((n) => img(n)), standaloneImages: standalone.map((n) => img(n)), size })
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
describe("Acquisition", () => {
|
|
@@ -42,7 +42,7 @@ describe("Acquisition", () => {
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
it("uses the size of the image when it has one", () => {
|
|
45
|
-
const a = new Acquisition({ id: "a", images: [], standaloneImages: [img("d0", { size: { width: 5, height: 5 } }), img("d1")], size
|
|
45
|
+
const a = new Acquisition({ id: "a", images: [], standaloneImages: [img("d0", { size: { width: 5, height: 5 } }), img("d1")], size })
|
|
46
46
|
expect(a.sizeOf()).toEqual({ width: 5, height: 5 })
|
|
47
47
|
expect(a.sizeOf(a.getImage("d1"))).toEqual(size)
|
|
48
48
|
})
|
|
@@ -50,8 +50,8 @@ describe("Acquisition", () => {
|
|
|
50
50
|
|
|
51
51
|
describe("Project", () => {
|
|
52
52
|
it("selects acquisitions and finds images", () => {
|
|
53
|
-
const vis = new Acquisition({ id: "VIS", images: [img("vis0")], size
|
|
54
|
-
const uv = new Acquisition({ id: "UV", images: [img("uv0")], standaloneImages: [img("uvRGB")], size
|
|
53
|
+
const vis = new Acquisition({ id: "VIS", images: [img("vis0")], size })
|
|
54
|
+
const uv = new Acquisition({ id: "UV", images: [img("uv0")], standaloneImages: [img("uvRGB")], size })
|
|
55
55
|
const p = new Project("A1", [vis, uv])
|
|
56
56
|
expect(p.selectedImage?.name).toBe("vis0")
|
|
57
57
|
p.selectAcquisition("UV")
|
|
@@ -75,21 +75,32 @@ function provider(data: unknown): DataProvider {
|
|
|
75
75
|
|
|
76
76
|
describe("ProjectRepository", () => {
|
|
77
77
|
it("reads the standard payload and resolves the urls", async () => {
|
|
78
|
-
const payload = { acquisitions: [{ id: "stack", images: [{ name: "s0", label: "s0" }], standaloneImages: [{ name: "r", label: "Result" }], size
|
|
78
|
+
const payload = { acquisitions: [{ id: "stack", images: [{ name: "s0", label: "s0", hasThumbnail: true }], standaloneImages: [{ name: "r", label: "Result" }], size }] }
|
|
79
79
|
const project = await new ProjectRepository(provider(payload)).getImages("A1")
|
|
80
80
|
const a = project.acquisition!
|
|
81
81
|
expect(a.images[0]).toMatchObject({ image: "S/A1/s0/full-image", thumbnail: "S/A1/s0/thumbnail" })
|
|
82
82
|
expect(a.standaloneImages[0]).toMatchObject({ label: "Result", image: "S/A1/r/full-image" })
|
|
83
83
|
})
|
|
84
84
|
|
|
85
|
+
it("resolves the thumbnail of the images marked with hasThumbnail only", async () => {
|
|
86
|
+
const payload = { acquisitions: [{
|
|
87
|
+
id: "stack", size,
|
|
88
|
+
images: [{ name: "s0", label: "s0", hasThumbnail: true }, { name: "s1", label: "s1", hasThumbnail: false }, { name: "s2", label: "s2" }],
|
|
89
|
+
standaloneImages: [{ name: "r", label: "Result", hasThumbnail: true }],
|
|
90
|
+
}] }
|
|
91
|
+
const a = (await new ProjectRepository(provider(payload)).getImages("A1")).acquisition!
|
|
92
|
+
expect(a.images.map((image) => image.thumbnail)).toEqual(["S/A1/s0/thumbnail", undefined, undefined])
|
|
93
|
+
expect(a.standaloneImages[0].thumbnail).toBe("S/A1/r/thumbnail")
|
|
94
|
+
})
|
|
95
|
+
|
|
85
96
|
it("adapts a legacy payload with a parser (spectral: one acquisition per group)", async () => {
|
|
86
97
|
type SpectralImage = ProjectImage & { wavelength: { value: number } }
|
|
87
98
|
const legacy = {
|
|
88
|
-
VIS: { spectralImages: [{ name: "v450", label: "v450", wavelength: { value: 450 } }], individualImages: { RGB: { name: "vRGB", label: "RGB" } }, size
|
|
89
|
-
UV: { spectralImages: [], individualImages: { RGB: { name: "uRGB", label: "RGB" } }, size
|
|
99
|
+
VIS: { spectralImages: [{ name: "v450", label: "v450", wavelength: { value: 450 } }], individualImages: { RGB: { name: "vRGB", label: "RGB" } }, size },
|
|
100
|
+
UV: { spectralImages: [], individualImages: { RGB: { name: "uRGB", label: "RGB" } }, size },
|
|
90
101
|
}
|
|
91
102
|
const parse = (data: any): AcquisitionObject<SpectralImage>[] => Object.entries<any>(data).map(([id, group]) => ({
|
|
92
|
-
id, images: group.spectralImages, standaloneImages: Object.values(group.individualImages), size: group.size,
|
|
103
|
+
id, images: group.spectralImages, standaloneImages: Object.values(group.individualImages), size: group.size,
|
|
93
104
|
}))
|
|
94
105
|
const project = await new ProjectRepository<SpectralImage>(provider(legacy), parse).getImages("A1")
|
|
95
106
|
expect(project.acquisitions.map((a) => a.id)).toEqual(["VIS", "UV"])
|
|
@@ -99,9 +110,9 @@ describe("ProjectRepository", () => {
|
|
|
99
110
|
})
|
|
100
111
|
|
|
101
112
|
it("adapts depth: no group, standalone images with their own size", async () => {
|
|
102
|
-
const legacy = { images: [{ id: "d0", label: "D0", size: { width: 10, height: 20 } }]
|
|
113
|
+
const legacy = { images: [{ id: "d0", label: "D0", size: { width: 10, height: 20 } }] }
|
|
103
114
|
const parse = (data: any): AcquisitionObject[] => [{
|
|
104
|
-
id: "depth", images: [], size: data.images[0].size,
|
|
115
|
+
id: "depth", images: [], size: data.images[0].size,
|
|
105
116
|
standaloneImages: data.images.map((i: any) => ({ name: i.id, label: i.label, size: i.size })),
|
|
106
117
|
}]
|
|
107
118
|
const project = await new ProjectRepository(provider(legacy), parse).getImages("A1")
|