@cornerstonejs/ai 3.7.1 → 3.7.3
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.
|
@@ -80,6 +80,7 @@ export default class ONNXSegmentationController {
|
|
|
80
80
|
protected annotationsNeedUpdating: boolean;
|
|
81
81
|
protected maskImageData: any;
|
|
82
82
|
protected promptAnnotationTypes: string[];
|
|
83
|
+
protected _cachedPromptAnnotations: any;
|
|
83
84
|
protected islandFillOptions: {
|
|
84
85
|
maxInternalRemove: number;
|
|
85
86
|
fillInternalEdge: boolean;
|
|
@@ -98,11 +99,13 @@ export default class ONNXSegmentationController {
|
|
|
98
99
|
initViewport(viewport: any): void;
|
|
99
100
|
acceptPreview(element: any): void;
|
|
100
101
|
rejectPreview(element: any): void;
|
|
102
|
+
restoreCachedPromptAnnotations(viewport: Types.IViewport): any[];
|
|
103
|
+
removePromptAnnotationsWithCache(viewport: Types.IViewport): void;
|
|
101
104
|
interpolateScroll(viewport?: any, dir?: number): Promise<void>;
|
|
102
105
|
protected log(logger: Loggers, ...args: any[]): void;
|
|
103
106
|
protected getPromptAnnotations: (viewport?: any) => cornerstoneTools.Types.Annotations;
|
|
104
107
|
protected viewportRenderedListener: (_event: any) => void;
|
|
105
|
-
protected annotationModifiedListener:
|
|
108
|
+
protected annotationModifiedListener: Function;
|
|
106
109
|
disconnectViewport(viewport: any): void;
|
|
107
110
|
protected load(): Promise<void>;
|
|
108
111
|
clear(viewport: any): void;
|
|
@@ -115,8 +118,8 @@ export default class ONNXSegmentationController {
|
|
|
115
118
|
tryLoad(options?: {
|
|
116
119
|
resetImage: boolean;
|
|
117
120
|
}): void;
|
|
118
|
-
mapAnnotationPoint(worldPoint: any): number[];
|
|
119
|
-
updateAnnotations(): void;
|
|
121
|
+
mapAnnotationPoint(worldPoint: any, canvasPosition: any): number[];
|
|
122
|
+
updateAnnotations(useSession?: any): void;
|
|
120
123
|
restoreImageEncoding(session: any, imageId: any): Promise<any>;
|
|
121
124
|
loadStorageImageEncoding(session: any, imageId: any, index?: any): Promise<any>;
|
|
122
125
|
storeImageEncoding(session: any, imageId: any, data: any): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { utilities, eventTarget, Enums } from '@cornerstonejs/core';
|
|
1
|
+
import { utilities, eventTarget, Enums, triggerEvent, } from '@cornerstonejs/core';
|
|
2
2
|
import * as cornerstoneTools from '@cornerstonejs/tools';
|
|
3
3
|
import { segmentation as cstSegmentation, LabelmapBaseTool, } from '@cornerstonejs/tools';
|
|
4
4
|
const { strategies } = cstSegmentation;
|
|
@@ -13,6 +13,11 @@ const { segmentation } = cornerstoneTools;
|
|
|
13
13
|
const { filterAnnotationsForDisplay } = cornerstoneTools.utilities.planar;
|
|
14
14
|
const { IslandRemoval } = cornerstoneTools.utilities;
|
|
15
15
|
const { triggerSegmentationDataModified } = segmentation.triggerSegmentationEvents;
|
|
16
|
+
const ONNX_EVENTS = {
|
|
17
|
+
MODEL_LOADING_STARTED: 'ONNX_MODEL_LOADING_STARTED',
|
|
18
|
+
MODEL_LOADING_COMPLETED: 'ONNX_MODEL_LOADING_COMPLETED',
|
|
19
|
+
MODEL_COMPONENT_LOADED: 'ONNX_MODEL_COMPONENT_LOADED',
|
|
20
|
+
};
|
|
16
21
|
function cloneTensor(t) {
|
|
17
22
|
return new ort.Tensor(t.type, Float32Array.from(t.data), t.dims);
|
|
18
23
|
}
|
|
@@ -176,14 +181,14 @@ export default class ONNXSegmentationController {
|
|
|
176
181
|
ctxMask.clearRect(0, 0, canvasMask.width, canvasMask.height);
|
|
177
182
|
this.tryLoad({ resetImage: true });
|
|
178
183
|
};
|
|
179
|
-
this.annotationModifiedListener = (_event) => {
|
|
184
|
+
this.annotationModifiedListener = cornerstoneTools.utilities.debounce((_event) => {
|
|
180
185
|
const currentAnnotations = this.getPromptAnnotations();
|
|
181
186
|
if (!currentAnnotations.length) {
|
|
182
187
|
return;
|
|
183
188
|
}
|
|
184
189
|
this.annotationsNeedUpdating = true;
|
|
185
190
|
this.tryLoad();
|
|
186
|
-
};
|
|
191
|
+
}, 300);
|
|
187
192
|
if (options.listeners) {
|
|
188
193
|
this.listeners = [...options.listeners];
|
|
189
194
|
}
|
|
@@ -225,11 +230,6 @@ export default class ONNXSegmentationController {
|
|
|
225
230
|
activeStrategy: 'FILL_INSIDE_CIRCLE',
|
|
226
231
|
preview: {
|
|
227
232
|
enabled: true,
|
|
228
|
-
previewColors: {
|
|
229
|
-
0: [255, 255, 255, 128],
|
|
230
|
-
1: [0, 255, 255, 192],
|
|
231
|
-
2: [255, 0, 255, 255],
|
|
232
|
-
},
|
|
233
233
|
},
|
|
234
234
|
},
|
|
235
235
|
});
|
|
@@ -257,15 +257,56 @@ export default class ONNXSegmentationController {
|
|
|
257
257
|
rejectPreview(element) {
|
|
258
258
|
this.tool.rejectPreview(element);
|
|
259
259
|
}
|
|
260
|
+
restoreCachedPromptAnnotations(viewport) {
|
|
261
|
+
if (!this._cachedPromptAnnotations) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
const annotations = [];
|
|
265
|
+
const { include, exclude } = this._cachedPromptAnnotations;
|
|
266
|
+
if (include) {
|
|
267
|
+
const newInclude = cornerstoneTools.utilities.moveAnnotationToViewPlane(include, viewport);
|
|
268
|
+
annotations.push(newInclude);
|
|
269
|
+
}
|
|
270
|
+
if (exclude) {
|
|
271
|
+
const newExclude = cornerstoneTools.utilities.moveAnnotationToViewPlane(exclude, viewport);
|
|
272
|
+
annotations.push(newExclude);
|
|
273
|
+
}
|
|
274
|
+
return annotations;
|
|
275
|
+
}
|
|
276
|
+
removePromptAnnotationsWithCache(viewport) {
|
|
277
|
+
const toolNames = [
|
|
278
|
+
ONNXSegmentationController.MarkerInclude,
|
|
279
|
+
ONNXSegmentationController.MarkerExclude,
|
|
280
|
+
ONNXSegmentationController.BoxPrompt,
|
|
281
|
+
];
|
|
282
|
+
let cachedInclude = null;
|
|
283
|
+
let cachedExclude = null;
|
|
284
|
+
const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations();
|
|
285
|
+
for (const annotation of allAnnotations) {
|
|
286
|
+
const toolName = annotation.metadata.toolName;
|
|
287
|
+
if (toolNames.includes(toolName)) {
|
|
288
|
+
if (toolName === ONNXSegmentationController.MarkerInclude &&
|
|
289
|
+
!cachedInclude) {
|
|
290
|
+
cachedInclude = annotation;
|
|
291
|
+
}
|
|
292
|
+
else if (toolName === ONNXSegmentationController.MarkerExclude &&
|
|
293
|
+
!cachedExclude) {
|
|
294
|
+
cachedExclude = annotation;
|
|
295
|
+
}
|
|
296
|
+
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
this._cachedPromptAnnotations = {
|
|
300
|
+
include: cachedInclude,
|
|
301
|
+
exclude: cachedExclude,
|
|
302
|
+
};
|
|
303
|
+
viewport.render();
|
|
304
|
+
}
|
|
260
305
|
async interpolateScroll(viewport = this.viewport, dir = 1) {
|
|
261
306
|
const { element } = viewport;
|
|
262
307
|
this.tool.acceptPreview(element);
|
|
263
308
|
const promptAnnotations = this.getPromptAnnotations(viewport);
|
|
264
|
-
if (!promptAnnotations.length) {
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
309
|
const currentSliceIndex = viewport.getCurrentImageIdIndex();
|
|
268
|
-
const { focalPoint } = viewport.getCamera();
|
|
269
310
|
const viewRef = viewport.getViewReference({
|
|
270
311
|
sliceIndex: currentSliceIndex + dir,
|
|
271
312
|
});
|
|
@@ -275,22 +316,21 @@ export default class ONNXSegmentationController {
|
|
|
275
316
|
}
|
|
276
317
|
viewport.scroll(dir);
|
|
277
318
|
await new Promise((resolve) => window.setTimeout(resolve, 250));
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
319
|
+
let annotations = [];
|
|
320
|
+
if (!promptAnnotations.length) {
|
|
321
|
+
annotations = this.restoreCachedPromptAnnotations(viewport);
|
|
281
322
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
newAnnotation.cachedStats = {};
|
|
290
|
-
for (const handle of newAnnotation.data.handles.points) {
|
|
291
|
-
vec3.add(handle, handle, newDelta);
|
|
323
|
+
else {
|
|
324
|
+
for (const annotation of promptAnnotations) {
|
|
325
|
+
const newAnnotation = structuredClone(annotation);
|
|
326
|
+
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID);
|
|
327
|
+
Object.assign(newAnnotation.metadata, viewRef);
|
|
328
|
+
cornerstoneTools.utilities.moveAnnotationToViewPlane(newAnnotation, viewport);
|
|
329
|
+
annotations.push(newAnnotation);
|
|
292
330
|
}
|
|
293
|
-
|
|
331
|
+
}
|
|
332
|
+
for (const annotation of annotations) {
|
|
333
|
+
annotationState.addAnnotation(annotation, viewport.element);
|
|
294
334
|
}
|
|
295
335
|
viewport.render();
|
|
296
336
|
}
|
|
@@ -486,16 +526,30 @@ export default class ONNXSegmentationController {
|
|
|
486
526
|
}
|
|
487
527
|
this.handleImage(desiredImage, session);
|
|
488
528
|
}
|
|
489
|
-
mapAnnotationPoint(worldPoint) {
|
|
529
|
+
mapAnnotationPoint(worldPoint, canvasPosition) {
|
|
490
530
|
const { viewport } = this;
|
|
491
531
|
const canvasPoint = viewport.worldToCanvas(worldPoint);
|
|
492
532
|
const { width, height } = viewport.canvas;
|
|
493
533
|
const { width: destWidth, height: destHeight } = this.canvas;
|
|
494
534
|
const x = Math.trunc((canvasPoint[0] * destWidth * devicePixelRatio) / width);
|
|
495
535
|
const y = Math.trunc((canvasPoint[1] * destHeight * devicePixelRatio) / height);
|
|
496
|
-
|
|
536
|
+
const { bottomLeft, topRight, origin } = canvasPosition;
|
|
537
|
+
const yVector = vec3.sub([0, 0, 0], origin, bottomLeft);
|
|
538
|
+
const xVector = vec3.sub([0, 0, 0], origin, topRight);
|
|
539
|
+
const xLen = vec3.length(xVector);
|
|
540
|
+
const yLen = vec3.length(yVector);
|
|
541
|
+
vec3.scale(xVector, xVector, 1 / xLen);
|
|
542
|
+
vec3.scale(yVector, yVector, 1 / yLen);
|
|
543
|
+
const xDot = vec3.dot(worldPoint, xVector);
|
|
544
|
+
const yDot = vec3.dot(worldPoint, yVector);
|
|
545
|
+
const centerX = vec3.dot(origin, xVector);
|
|
546
|
+
const centerY = vec3.dot(origin, yVector);
|
|
547
|
+
const newX = Math.round(((centerX - xDot) * destWidth) / xLen);
|
|
548
|
+
const newY = Math.round(((centerY - yDot) * destHeight) / yLen);
|
|
549
|
+
console.log('Old/new X,Y', x, y, newX, newY, x - newX, y - newY);
|
|
550
|
+
return [newX, newY];
|
|
497
551
|
}
|
|
498
|
-
updateAnnotations() {
|
|
552
|
+
updateAnnotations(useSession = this.currentImage) {
|
|
499
553
|
if (this.isGpuInUse ||
|
|
500
554
|
!this.annotationsNeedUpdating ||
|
|
501
555
|
!this.currentImage) {
|
|
@@ -506,16 +560,16 @@ export default class ONNXSegmentationController {
|
|
|
506
560
|
this.points = [];
|
|
507
561
|
this.labels = [];
|
|
508
562
|
this.worldPoints = [];
|
|
509
|
-
if (!promptAnnotations?.length) {
|
|
563
|
+
if (!promptAnnotations?.length || !useSession?.canvasPosition) {
|
|
510
564
|
return;
|
|
511
565
|
}
|
|
512
566
|
for (const annotation of promptAnnotations) {
|
|
513
567
|
const handle = annotation.data.handles.points[0];
|
|
514
|
-
const point = this.mapAnnotationPoint(handle);
|
|
568
|
+
const point = this.mapAnnotationPoint(handle, useSession.canvasPosition);
|
|
515
569
|
this.points.push(...point);
|
|
516
570
|
if (annotation.metadata.toolName === ONNXSegmentationController.BoxPrompt) {
|
|
517
571
|
this.labels.push(2, 3);
|
|
518
|
-
this.points.push(...this.mapAnnotationPoint(annotation.data.handles.points[3]));
|
|
572
|
+
this.points.push(...this.mapAnnotationPoint(annotation.data.handles.points[3], useSession.canvasPosition));
|
|
519
573
|
}
|
|
520
574
|
else {
|
|
521
575
|
const label = annotation.metadata.toolName === this.excludeTool ? 0 : 1;
|
|
@@ -698,11 +752,23 @@ export default class ONNXSegmentationController {
|
|
|
698
752
|
const cache = await caches.open('onnx');
|
|
699
753
|
let cachedResponse = await cache.match(url);
|
|
700
754
|
if (cachedResponse == undefined) {
|
|
755
|
+
triggerEvent(eventTarget, ONNX_EVENTS.MODEL_COMPONENT_LOADED, {
|
|
756
|
+
name,
|
|
757
|
+
url,
|
|
758
|
+
status: 'loading',
|
|
759
|
+
source: 'network',
|
|
760
|
+
});
|
|
701
761
|
await cache.add(url);
|
|
702
762
|
cachedResponse = await cache.match(url);
|
|
703
763
|
this.log(Loggers.Log, `${name} (network)`);
|
|
704
764
|
}
|
|
705
765
|
else {
|
|
766
|
+
triggerEvent(eventTarget, ONNX_EVENTS.MODEL_COMPONENT_LOADED, {
|
|
767
|
+
name,
|
|
768
|
+
url,
|
|
769
|
+
status: 'loaded',
|
|
770
|
+
source: 'cache',
|
|
771
|
+
});
|
|
706
772
|
this.log(Loggers.Log, `${name} (cached)`);
|
|
707
773
|
}
|
|
708
774
|
const data = await cachedResponse.arrayBuffer();
|
|
@@ -710,6 +776,12 @@ export default class ONNXSegmentationController {
|
|
|
710
776
|
}
|
|
711
777
|
catch (error) {
|
|
712
778
|
this.log(Loggers.Log, `${name} (network)`);
|
|
779
|
+
triggerEvent(eventTarget, ONNX_EVENTS.MODEL_COMPONENT_LOADED, {
|
|
780
|
+
name,
|
|
781
|
+
url,
|
|
782
|
+
status: 'error',
|
|
783
|
+
error,
|
|
784
|
+
});
|
|
713
785
|
return await fetch(url).then((response) => response.arrayBuffer());
|
|
714
786
|
}
|
|
715
787
|
}
|
|
@@ -724,6 +796,11 @@ export default class ONNXSegmentationController {
|
|
|
724
796
|
}
|
|
725
797
|
urls.push(model.url);
|
|
726
798
|
}
|
|
799
|
+
triggerEvent(eventTarget, ONNX_EVENTS.MODEL_LOADING_STARTED, {
|
|
800
|
+
modelConfig: this.config.model,
|
|
801
|
+
totalSize: missing,
|
|
802
|
+
urls,
|
|
803
|
+
});
|
|
727
804
|
if (missing > 0) {
|
|
728
805
|
this.log(Loggers.Log, `downloading ${missing} MB from network ... it might take a while`);
|
|
729
806
|
}
|
|
@@ -754,7 +831,13 @@ export default class ONNXSegmentationController {
|
|
|
754
831
|
imageSession[model.key] = await ort.InferenceSession.create(model_bytes, sessionOptions);
|
|
755
832
|
}
|
|
756
833
|
const stop = performance.now();
|
|
757
|
-
|
|
834
|
+
const loadTime = stop - start;
|
|
835
|
+
triggerEvent(eventTarget, ONNX_EVENTS.MODEL_LOADING_COMPLETED, {
|
|
836
|
+
modelConfig: this.config.model,
|
|
837
|
+
loadTimeMs: loadTime,
|
|
838
|
+
urls,
|
|
839
|
+
});
|
|
840
|
+
this.log(Loggers.Log, `ready, ${loadTime.toFixed(1)}ms`, urls.join(', '));
|
|
758
841
|
}
|
|
759
842
|
async getDirectoryForImageId(session, imageId) {
|
|
760
843
|
if (imageId.indexOf('/studies/') === -1 ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/ai",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.3",
|
|
4
4
|
"description": "AI and ML Interfaces for Cornerstone3D",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"onnxruntime-web": "1.17.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@cornerstonejs/core": "^3.7.
|
|
60
|
-
"@cornerstonejs/tools": "^3.7.
|
|
59
|
+
"@cornerstonejs/core": "^3.7.3",
|
|
60
|
+
"@cornerstonejs/tools": "^3.7.3"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "319a7741f4757201093d6b234a92dc6278d26f66"
|
|
63
63
|
}
|