From 97ea16a9d9fed21b9737fc727ae6788f1fe2a28d Mon Sep 17 00:00:00 2001 From: Anders Sildnes Date: Tue, 25 Feb 2025 11:02:47 -0600 Subject: [PATCH 1/4] Prevent users from annotating the background --- .../views/vue/components/ActiveLearningSlideViewer.vue | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/histomics_label/web_client/views/vue/components/ActiveLearningSlideViewer.vue b/histomics_label/web_client/views/vue/components/ActiveLearningSlideViewer.vue index 6fb70eb..631d721 100644 --- a/histomics_label/web_client/views/vue/components/ActiveLearningSlideViewer.vue +++ b/histomics_label/web_client/views/vue/components/ActiveLearningSlideViewer.vue @@ -444,6 +444,14 @@ export default Vue.extend({ if (newLabel === previousLabel) { return; } + + const bboxes = overlayElement.get('user').bbox + const isEmtpyBackground = index == 0 && bboxes.length >= 4 && bboxes.slice(0, 4).every((val, idx) => val === [0, 0, 1, 1][idx]); + // Don't label the empty background superpixel + if (isEmtpyBackground) { + return; + } + const offset = boundaries ? 1 : 0; data[index] = data[index + offset] = newLabel; store.labelsOverlayLayer.indexModified(index, index + offset).draw(); From c3af082b3fa9e5d3cdc7420d07f9e57618b6f9ba Mon Sep 17 00:00:00 2001 From: Anders Sildnes Date: Tue, 25 Feb 2025 11:19:45 -0600 Subject: [PATCH 2/4] Support for overlapping pixelmaps In our AML case, some bounding boxes for cells overlap. Thus, masks don't look good since a good chunk of the bounding box for a cells may be masked away. This commit adds an option to remove the mask altogether. --- histomics_label/web_client/views/body/ActiveLearningView.js | 3 +++ .../components/ActiveLearning/ActiveLearningFilmStripCard.vue | 3 ++- .../ActiveLearningReview/ActiveLearningReviewCard.vue | 3 ++- histomics_label/web_client/views/vue/components/store.js | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/histomics_label/web_client/views/body/ActiveLearningView.js b/histomics_label/web_client/views/body/ActiveLearningView.js index 0c38750..d357c75 100644 --- a/histomics_label/web_client/views/body/ActiveLearningView.js +++ b/histomics_label/web_client/views/body/ActiveLearningView.js @@ -522,6 +522,9 @@ const ActiveLearningView = View.extend({ return; } const annotation = this.annotationsByImageId[imageId].predictions.get('annotation'); + if (annotation && annotation.attributes && annotation.attributes.overlappingSuperpixels) { + store.overlappingSuperpixels = annotation.attributes.overlappingSuperpixels; + } const labels = this.annotationsByImageId[imageId].labels.get('annotation'); const labelValues = labels.elements[0].values; const userData = annotation.elements[0].user; diff --git a/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStripCard.vue b/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStripCard.vue index e000f20..d5b9777 100644 --- a/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStripCard.vue +++ b/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStripCard.vue @@ -77,6 +77,7 @@ export default Vue.extend({ const thumbnailWidth = Math.floor(125 * regionWidth / scaleFactor); const thumbnailHeight = Math.floor(125 * regionHeight / scaleFactor); const params = `?left=${bbox[0] / scale}&top=${bbox[1] / scale}&right=${bbox[2] / scale}&bottom=${bbox[3] / scale}&width=${thumbnailWidth}&height=${thumbnailHeight}&encoding=PNG`; + const mask = store.overlappingSuperpixels ? [0,0,0,0] : [255,255,255,255]; const functionJson = JSON.stringify({ function: { name: 'large_image.tilesource.stylefuncs.maskPixelValues', @@ -84,7 +85,7 @@ export default Vue.extend({ parameters: { values: pixelVals, positive: [0, 0, 0, 0], - negative: [255, 255, 255, 255] + negative: mask, } }, bands: [] diff --git a/histomics_label/web_client/views/vue/components/ActiveLearningReview/ActiveLearningReviewCard.vue b/histomics_label/web_client/views/vue/components/ActiveLearningReview/ActiveLearningReviewCard.vue index 0633e93..56a3b0d 100644 --- a/histomics_label/web_client/views/vue/components/ActiveLearningReview/ActiveLearningReviewCard.vue +++ b/histomics_label/web_client/views/vue/components/ActiveLearningReview/ActiveLearningReviewCard.vue @@ -38,6 +38,7 @@ export default Vue.extend({ const scale = this.superpixel.scale; const thumbnailSize = this.previewSize * 100; const params = `?left=${bbox[0] / scale}&top=${bbox[1] / scale}&right=${bbox[2] / scale}&bottom=${bbox[3] / scale}&width=${thumbnailSize}&height=${thumbnailSize}&encoding=PNG`; + const mask = store.overlappingSuperpixels ? [0,0,0,0] : [255,255,255,255]; const functionJson = JSON.stringify({ function: { name: 'large_image.tilesource.stylefuncs.maskPixelValues', @@ -45,7 +46,7 @@ export default Vue.extend({ parameters: { values: pixelVals, positive: [0, 0, 0, 0], - negative: [255, 255, 255, 255] + negative: mask, } }, bands: [] diff --git a/histomics_label/web_client/views/vue/components/store.js b/histomics_label/web_client/views/vue/components/store.js index e9f9614..81e6573 100644 --- a/histomics_label/web_client/views/vue/components/store.js +++ b/histomics_label/web_client/views/vue/components/store.js @@ -23,6 +23,7 @@ const store = Vue.observable({ zoom: 1, center: { x: 1, y: 1 }, sortedSuperpixelIndices: [], + overlappingSuperpixels: false, reviewSuperpixel: null, currentUser: null, epoch: -1, From bd1cf0536c3488ae3f23a0b9dea18bcfdb24958c Mon Sep 17 00:00:00 2001 From: Anders Sildnes Date: Tue, 4 Mar 2025 15:51:00 -0600 Subject: [PATCH 3/4] Issue 185: log errors to console Even though an error is raised, it never makes it to the console. See issue on github for more details --- histomics_label/web_client/views/body/ActiveLearningView.js | 1 + 1 file changed, 1 insertion(+) diff --git a/histomics_label/web_client/views/body/ActiveLearningView.js b/histomics_label/web_client/views/body/ActiveLearningView.js index d357c75..f11c67f 100644 --- a/histomics_label/web_client/views/body/ActiveLearningView.js +++ b/histomics_label/web_client/views/body/ActiveLearningView.js @@ -569,6 +569,7 @@ const ActiveLearningView = View.extend({ const version = imageAndJob[0].split(':')[1]; const jobInfo = ((dockerImages[image] || {})[version] || {})[imageAndJob[1]]; if (!jobInfo) { + console.error('Unable to find specified superpixel classification image.'); throw new Error('Unable to find specified superpixel classification image.'); } return this.getJobCertaintyAndFeatureChoices(jobInfo.xmlspec); From eeac499b59285924ec671bec6260e133d413d59d Mon Sep 17 00:00:00 2001 From: Anders Sildnes Date: Tue, 4 Mar 2025 15:55:33 -0600 Subject: [PATCH 4/4] Fix potential error when having few superpixels If there are fewer superpixels than what you can show on a single page, the following code would yield -1 --- .../vue/components/ActiveLearning/ActiveLearningFilmStrip.vue | 2 +- histomics_label/web_client/views/vue/components/store.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue b/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue index f2951fd..ffa436c 100644 --- a/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue +++ b/histomics_label/web_client/views/vue/components/ActiveLearning/ActiveLearningFilmStrip.vue @@ -100,7 +100,7 @@ export default { store.pageSize = Math.floor(width / (cardWidth + padding)); // update page store.page = Math.floor(currentIndex / store.pageSize); - store.maxPage = Math.ceil((store.sortedSuperpixelIndices.length) / store.pageSize) - 1; + store.maxPage = Math.max(Math.ceil((store.sortedSuperpixelIndices.length) / store.pageSize) - 1, 0); // update selected index store.selectedIndex = currentIndex - (store.pageSize * store.page); updateSelectedPage(); diff --git a/histomics_label/web_client/views/vue/components/store.js b/histomics_label/web_client/views/vue/components/store.js index 81e6573..18c822c 100644 --- a/histomics_label/web_client/views/vue/components/store.js +++ b/histomics_label/web_client/views/vue/components/store.js @@ -88,7 +88,7 @@ const updateSelectedPage = () => { const endIndex = Math.min(startIndex + store.pageSize, store.sortedSuperpixelIndices.length); store.superpixelsToDisplay = store.sortedSuperpixelIndices.slice(startIndex, endIndex); store.currentImageId = store.superpixelsToDisplay[store.selectedIndex].imageId; - store.maxPage = Math.ceil(store.sortedSuperpixelIndices.length / store.pageSize) - 1; + store.maxPage = Math.max(Math.ceil(store.sortedSuperpixelIndices.length / store.pageSize) - 1, 0); }; /**