diff --git a/scripts/miscProcessing.js b/scripts/miscProcessing.js index 8665369..9f62fc6 100644 --- a/scripts/miscProcessing.js +++ b/scripts/miscProcessing.js @@ -417,10 +417,12 @@ onmessage = async (evt) => { } catch (e) { console.log("Error saving thumbnail to Box", e) } + break case "getTMAAnnotations": const { folderToGetFrom, annotations, format } = data await retrieveTMAAnnotations(op, folderToGetFrom, annotations, format) + break } } diff --git a/scripts/modelPrediction.js b/scripts/modelPrediction.js index 31f1875..5f728f9 100644 --- a/scripts/modelPrediction.js +++ b/scripts/modelPrediction.js @@ -470,6 +470,7 @@ onmessage = async (evt) => { const datasetConfig = data.body.datasetConfig clearWSIDataFromIndexedDB() + const predsToInsert = [] const { previousPredictions, ...otherChanges } = await getPredsFromBox(imageId, annotationId, modelId, datasetConfig, wsiPredsFiles) const positiveLabel = data.body.positiveLabel || datasetConfig.annotations.find(annot => annot.annotationId === annotationId).labels[0] previousPredictions @@ -481,8 +482,9 @@ onmessage = async (evt) => { predictedLabel, predictionScore } - insertWSIDataToIndexedDB(predictionForIDB, annotationId) + predsToInsert.push(predictionForIDB) }) + insertWSIDataToIndexedDB(predsToInsert, annotationId) postMessage({ op, diff --git a/scripts/modelWorkerUtils.js b/scripts/modelWorkerUtils.js index e068272..0762810 100644 --- a/scripts/modelWorkerUtils.js +++ b/scripts/modelWorkerUtils.js @@ -330,10 +330,26 @@ const getPredsFromBox = async (imageId, annotationId, modelId, datasetConfig, ws } const insertWSIDataToIndexedDB = (data, annotationId) => new Promise (async resolve => { - if (indexedDBConfig['wsi'].objectStoreOpts.keyPath.every(key => data[key] >= 0)) { + let dataValidated = false + if (Array.isArray(data)) { + // Check if all relevant keys of all rows are non-negative. + dataValidated = data.every(row => indexedDBConfig['wsi'].objectStoreOpts.keyPath.every(key => row[key] >= 0)) + } else if (typeof(data) === 'object') { + dataValidated = indexedDBConfig['wsi'].objectStoreOpts.keyPath.every(key => data[key] >= 0) + } else { + console.error("Data to be inserted into IDB out of range of possible values", data) + } + if (dataValidated) { wsiPredsDB = wsiPredsDB || await fetchIndexedDBInstance('wsi') - const objectStore = wsiPredsDB.transaction(`${indexedDBConfig['wsi'].objectStoreNamePrefix}_${annotationId}`, "readwrite").objectStore(`${indexedDBConfig['wsi'].objectStoreNamePrefix}_${annotationId}`) - objectStore.put(data).onsuccess = ({target}) => resolve(target.result) + const transaction = wsiPredsDB.transaction(`${indexedDBConfig['wsi'].objectStoreNamePrefix}_${annotationId}`, "readwrite") + if (Array.isArray(data)) { + data.forEach(row => { + transaction.objectStore(`${indexedDBConfig['wsi'].objectStoreNamePrefix}_${annotationId}`).put(row) + }) + transaction.oncomplete = ({target}) => resolve(target.result) + } else { + transaction.objectStore(`${indexedDBConfig['wsi'].objectStoreNamePrefix}_${annotationId}`).put(row).onsuccess = ({target}) => resolve(target.result) + } } }) diff --git a/scripts/path.js b/scripts/path.js index b23e184..9bd9a7e 100644 --- a/scripts/path.js +++ b/scripts/path.js @@ -84,6 +84,8 @@ const loadHashParams = async () => { if (hashParams.image && hashParams.image !== path.tmaImage.getAttribute("entry_id")) { loadImageFromBox(hashParams.image) + } else if (hashParams.wsiCenterX && hashParams.wsiCenterY && hashParams.wsiZoom) { + wsi.handlePanAndZoom(hashParams.wsiCenterX, hashParams.wsiCenterY, hashParams.wsiZoom) } if (hashParams.folder) { @@ -96,9 +98,6 @@ const loadHashParams = async () => { path.selectFolder(boxRootFolderId) } - if (hashParams.wsiCenterX && hashParams.wsiCenterY && hashParams.wsiZoom) { - wsi.handlePanAndZoom(hashParams.wsiCenterX, hashParams.wsiCenterY, hashParams.wsiZoom) - } if (!hashParams.sort) { window.location.hash += "&sort=name" @@ -512,7 +511,6 @@ const loadImageFromBox = async (id, url) => { } window.localStorage.currentThumbnailsFolder = parent.id - thumbnails.showThumbnailPicker(window.localStorage.currentThumbnailsOffset, DEFAULT_THUMBNAILS_LIST_LENGTH) path.isWSI = utils.isWSI(name) path.tmaImage.setAttribute("alt", name) @@ -552,10 +550,10 @@ const loadImageFromBox = async (id, url) => { } }, { resolution: 0, url: "" }) - url = await box.getRepresentation(maxResolutionRep.url) - if (url) { - await loadImgFromBoxFile(null, url) - } + box.getRepresentation(maxResolutionRep.url).then(repURL => { + url = repURL + loadImgFromBoxFile(null, url) + }) if (!path.datasetConfig.jpegRepresentationsFolderId || path.datasetConfig.jpegRepresentationsFolderId === -1) { const jpegRepresentationsFolderEntry = await box.createFolder("jpegRepresentations", path.datasetConfig.datasetConfigFolderId) @@ -567,7 +565,7 @@ const loadImageFromBox = async (id, url) => { if (typeof OffscreenCanvas === "function") { const op = "tiffConvert" - path.miscProcessWorker.postMessage({ + path.miscProcessingWorker.postMessage({ op, 'data': { 'imageId': id, @@ -577,7 +575,7 @@ const loadImageFromBox = async (id, url) => { } }) - path.miscProcessWorker.onmessage = (evt) => { + path.miscProcessingWorker.onmessage = (evt) => { if (evt.data.op === op) { const { originalImageId, metadataWithRepresentation: newMetadata, representationFileId } = evt.data if (originalImageId === hashParams.image) { @@ -588,7 +586,7 @@ const loadImageFromBox = async (id, url) => { } } - path.miscProcessWorker.onerror = (err) => { + path.miscProcessingWorker.onerror = (err) => { console.log("Error converting TIFF from worker", err) } } @@ -596,16 +594,17 @@ const loadImageFromBox = async (id, url) => { } else { // Just use the representation created before. const { representationFileId } = JSON.parse(fileMetadata["jpegRepresentation"]) console.log("Using the JPEG representation created already", new Date()) - await loadImgFromBoxFile(representationFileId) + loadImgFromBoxFile(representationFileId) } } else { - await loadImgFromBoxFile(id) + loadImgFromBoxFile(id) } } } addImageHeader(filePathInBox, id, name) + thumbnails.showThumbnailPicker(window.localStorage.currentThumbnailsOffset, DEFAULT_THUMBNAILS_LIST_LENGTH) if (!hashParams.folder) { path.selectFolder(parent.id) diff --git a/scripts/wsi.js b/scripts/wsi.js index f39c5ef..f9af13e 100644 --- a/scripts/wsi.js +++ b/scripts/wsi.js @@ -100,7 +100,6 @@ wsi.loadImage = async (id, name, fileMetadata={}) => { } } - if (!path.wsiViewer.canvas) { path.tmaCanvas.parentElement.style.backgroundColor = "black" path.wsiViewer = OpenSeadragon({ @@ -352,7 +351,7 @@ wsi.loadImage = async (id, name, fileMetadata={}) => { } if (!wsiPredsFileId) { wsi.getPreviousPredsFromBox(fileMetadata) - document.addEventListener("previousPredsReady", (e) => { + path.wsiViewer.addEventListener("previousPredsReady", (e) => { e.preventDefault() wsiPredsFileId = e.detail.wsiPredsFileId wsi.startPrediction(annotationId, imageId, name, width, height, predictionBounds, wsiPredsFileId) @@ -797,11 +796,12 @@ wsi.loadImage = async (id, name, fileMetadata={}) => { fileMetadata = await box.updateMetadata(id, "/wsiInfo", JSON.stringify(imageInfoForMetadata)) window.localStorage.fileMetadata = JSON.stringify(fileMetadata) } - wsi.getPreviousPredsFromBox(fileMetadata) } - + path.wsiViewer.world.getItemAt(0).addOnceHandler('fully-loaded-change', fullyLoadedChangeHandler) + wsi.getPreviousPredsFromBox(fileMetadata) + // Handle cases where the image loads but an error (typically "SOI not found") causes the fully-loaded-change event to not fire. let failedTiles = 0 path.wsiViewer.addHandler('tile-load-failed', (e) => failedTiles += 1) @@ -1254,7 +1254,7 @@ wsi.handleMessage = (data, op) => { 'wsiPredsFileId': JSON.parse(data.newFileMetadata.wsiPredsFiles).find(file => file.annotationId === data.annotationId && file.modelId === data.modelId).fileId } }) - document.dispatchEvent(previousPredsReadyEvent) + path.wsiViewer.dispatchEvent(previousPredsReadyEvent) } wsi.overlayPreviousPredictions() annotations.populateWSIAnnotations(true, true) @@ -1416,11 +1416,13 @@ wsi.getFromIndexedDB = (objectStore, queryOpts={}) => new Promise((resolve, reje reject("Malformed query") } const queryResult = [] - let offset = typeof(queryOpts.offset) === "number" && queryOpts.offset >= 0 ? queryOpts.offset : 0 + queryOpts.offset = typeof(queryOpts.offset) === "number" && queryOpts.offset >= 0 ? queryOpts.offset : 0 queryOpts.limit = typeof(queryOpts.limit) === "number" && queryOpts.limit > 0 ? queryOpts.limit : 25 // let numRecords = 0 // objectStoreTransaction.count(queryOpts.query).onsuccess = (e) => { - // numRecords = e.target.result + // numRecords = e.target.result + // console.log(numRecords) + // } let cursorSource = objectStoreTransaction if (queryOpts.index) { @@ -1428,6 +1430,7 @@ wsi.getFromIndexedDB = (objectStore, queryOpts={}) => new Promise((resolve, reje } let pagesSkippedFlag = queryOpts.pageNum && queryOpts.pageNum > 0 + const cursorRequest = cursorSource.openCursor(queryOpts.query, queryOpts.direction) cursorRequest.onsuccess = (e) => { const cursor = e.target.result @@ -1528,6 +1531,7 @@ wsi.overlayPreviousPredictions = async (labelsToDisplay=wsi.defaultSelectedLabel }) handleLabelSelectionChanged() + for (const objectStore of Object.values(wsi.predsDB.objectStoreNames)) { const annotation = path.datasetConfig.annotations.find(annot => annot.annotationId === parseInt(objectStore.split(`${indexedDBConfig['wsi'].objectStoreNamePrefix}_`)[1])) if (annotation && annotation.labels.some(annotationLabel => labelsToDisplay.find(displayLabel => displayLabel.label === annotationLabel.label))) { @@ -1593,10 +1597,11 @@ wsi.overlayPreviousPredictions = async (labelsToDisplay=wsi.defaultSelectedLabel } else { offset += limit } - + } } } + for (const i in allOverlays) { if (interruptFlag) { break