From 76a01c046024be4bb7a8f5388a9849faef6e3b28 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Tue, 15 Oct 2024 17:02:26 -0400 Subject: [PATCH 01/14] fix: SamCacheLoader should be robust to child job failures --- .../saalfeldlab/paintera/cache/AsyncCacheWithLoader.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/AsyncCacheWithLoader.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/AsyncCacheWithLoader.kt index bbb9e973e..0ea38a062 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/AsyncCacheWithLoader.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/AsyncCacheWithLoader.kt @@ -14,8 +14,8 @@ open class AsyncCacheWithLoader( private val loader: suspend (K) -> V ) : Cache { - private val loaderContext = Dispatchers.IO + Job() - private val loaderQueueContext = Dispatchers.IO + Job() + private val loaderScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + private val loaderQueueScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) val cacheSizeProperty = SimpleIntegerProperty() var cacheSize by cacheSizeProperty.nonnull() @@ -25,7 +25,7 @@ open class AsyncCacheWithLoader( private set fun cancelUnsubmittedLoadRequests() { - loaderQueueContext.cancelChildren() + loaderScope.coroutineContext.cancelChildren() } override fun getIfPresent(key: K): V? { @@ -44,7 +44,7 @@ open class AsyncCacheWithLoader( fun request(key: K, clear : Boolean = false): Deferred = runBlocking { cache.get(key) { if (clear) cancelUnsubmittedLoadRequests() - async(loaderContext) { loader(key) } + loaderScope.async { loader(key) } }.also { if (it.isCancelled) cache.invalidate(key) } } @@ -57,7 +57,7 @@ open class AsyncCacheWithLoader( } return runBlocking { - async(loaderQueueContext) { + loaderQueueScope.async { if (!isActive) invalidate(key) else request(key) } From f0e5cee9d1496e57d3e76c3d46334f70b99355dd Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 18 Oct 2024 09:39:36 -0400 Subject: [PATCH 02/14] perf: remove scale levels that are too small --- .../saalfeldlab/paintera/ui/dialogs/create/CreateDataset.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/ui/dialogs/create/CreateDataset.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/ui/dialogs/create/CreateDataset.kt index 8514b4ff9..9db566e16 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/ui/dialogs/create/CreateDataset.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/ui/dialogs/create/CreateDataset.kt @@ -394,6 +394,11 @@ class CreateDataset(private val currentSource: Source<*>?, vararg allSources: So levels.add(level) } provideAbsoluteValues(levels, resolution, dimensions) + levels.removeIf { level -> + val isFirst = level == levels[0] + val tooSmall = level.dimensions.asLongArray().zip(blockSize.asLongArray()).all { (dim, block) -> dim <= block } + !isFirst && tooSmall + } mipmapLevels.clear() mipmapLevels += levels } From 0779c110db3affe4dc5c58d222582953ea1be2ed Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 18 Oct 2024 09:44:11 -0400 Subject: [PATCH 03/14] fix: don't constantly request new navigation based embeddings --- .../saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt index e2fd0726a..90fb031e1 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt @@ -86,9 +86,12 @@ object SamEmbeddingLoaderCache : AsyncCacheWithLoader Date: Fri, 18 Oct 2024 09:44:50 -0400 Subject: [PATCH 04/14] fix: use non-volatile source for SAM render --- .../saalfeldlab/bdv/fx/viewer/SourceUtils.kt | 35 +++++++++++++++++++ .../paintera/cache/SamEmbeddingLoaderCache.kt | 10 ++++-- .../control/modes/ShapeInterpolationMode.kt | 2 ++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt diff --git a/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt new file mode 100644 index 000000000..b6c21cae9 --- /dev/null +++ b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt @@ -0,0 +1,35 @@ +package org.janelia.saalfeldlab.bdv.fx.viewer + +import bdv.viewer.Interpolation +import bdv.viewer.Source +import bdv.viewer.SourceAndConverter +import net.imglib2.converter.Converter +import net.imglib2.realtransform.AffineTransform3D +import net.imglib2.type.numeric.ARGBType +import org.janelia.saalfeldlab.paintera.data.DataSource + +internal fun getDataSourceAndConverter(sourceAndConverter: SourceAndConverter<*>): SourceAndConverter<*> { + val data = sourceAndConverter.spimSource as? DataSource ?: return sourceAndConverter + val dataSource = object : Source { + override fun isPresent(t: Int) = data.isPresent(t) + + override fun getSource(t: Int, level: Int) = data.getDataSource(t, level) + + override fun getInterpolatedSource(t: Int, level: Int, method: Interpolation?) = data.getInterpolatedDataSource(t, level, method) + + override fun getSourceTransform(t: Int, level: Int, transform: AffineTransform3D?) { + data.getSourceTransform(t, level, transform) + } + + override fun getType() = data.dataType + + override fun getName() = sourceAndConverter.spimSource.name + + override fun getVoxelDimensions() = data.voxelDimensions + + override fun getNumMipmapLevels() = data.numMipmapLevels + } + + return SourceAndConverter(dataSource, sourceAndConverter.converter as Converter) + +} \ No newline at end of file diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt index 90fb031e1..e8f65205a 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt @@ -25,6 +25,7 @@ import org.apache.http.entity.mime.MultipartEntityBuilder import org.apache.http.impl.client.HttpClientBuilder import org.apache.http.util.EntityUtils import org.janelia.saalfeldlab.bdv.fx.viewer.ViewerPanelFX +import org.janelia.saalfeldlab.bdv.fx.viewer.getDataSourceAndConverter import org.janelia.saalfeldlab.fx.extensions.LazyForeignValue import org.janelia.saalfeldlab.fx.ortho.OrthogonalViews.ViewerAndTransforms import org.janelia.saalfeldlab.paintera.PainteraBaseView @@ -343,7 +344,10 @@ object SamEmbeddingLoaderCache : AsyncCacheWithLoader? = null): RenderUnitState { val activeSourceToSkip = paintera.currentSource?.sourceAndConverter?.spimSource - val sacs = state.sources.filterNot { it.spimSource == activeSourceToSkip }.toList() + val sacs = state.sources + .filterNot { it.spimSource == activeSourceToSkip } + .map { sac -> getDataSourceAndConverter (sac) } // to ensure non-volatile + .toList() return RenderUnitState( globalToViewerTransform?.copy() ?: AffineTransform3D().also { state.getViewerTransform(it) }, state.timepoint, @@ -354,8 +358,8 @@ object SamEmbeddingLoaderCache : AsyncCacheWithLoader>(val controller: ShapeInterpolat val activeSource = activeSourceStateProperty.value!!.sourceAndConverter!!.spimSource val sources = mask.viewer.state.sources .filter { it.spimSource !== activeSource } + .map { sac -> getDataSourceAndConverter (sac) } // to ensure non-volatile .toList() val renderState = RenderUnitState(mask.initialGlobalToViewerTransform.copy(), mask.info.time, sources, width.toLong(), height.toLong()) From 2edbf67711da1ccc25bbe1233b94c5b0c40a2c9a Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 18 Oct 2024 16:03:25 -0400 Subject: [PATCH 05/14] fix: support rendering of non-volatile LMT --- .../CompositeProjectorPreMultiply.java | 8 +++++- .../stream/HighlightingStreamConverter.java | 4 +++ ...htingStreamConverterLabelMultisetType.java | 16 +++-------- ...eamConverterVolatileLabelMultisetType.java | 28 +++++++++++++++++++ .../saalfeldlab/bdv/fx/viewer/SourceUtils.kt | 15 +++++++++- 5 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterVolatileLabelMultisetType.java diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java b/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java index 5ee059235..0e8811761 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java @@ -10,6 +10,7 @@ import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; import net.imglib2.type.numeric.ARGBType; +import org.janelia.saalfeldlab.bdv.fx.viewer.CompositeSourceSupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,7 +60,12 @@ public VolatileProjector createProjector( final ArrayList> activeComposites = new ArrayList<>(); for (final var activeSource : sources) { - activeComposites.add(composites.get(activeSource.getSpimSource())); + final Source compositeSource; + if (activeSource instanceof CompositeSourceSupplier) + compositeSource = ((CompositeSourceSupplier)activeSource).getCompositeSource(); + else + compositeSource = activeSource.getSpimSource(); + activeComposites.add(composites.get(compositeSource)); } projector.setComposites(activeComposites); diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverter.java b/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverter.java index 3933a783f..c2ae4f0e9 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverter.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverter.java @@ -14,6 +14,7 @@ import javafx.scene.paint.Color; import net.imglib2.Volatile; import net.imglib2.converter.Converter; +import net.imglib2.type.label.LabelMultisetType; import net.imglib2.type.label.VolatileLabelMultisetType; import net.imglib2.type.numeric.ARGBType; import net.imglib2.type.numeric.IntegerType; @@ -125,6 +126,9 @@ public static HighlightingStreamConverter forType( LOG.debug("Getting {} for type {}", HighlightingStreamConverter.class.getSimpleName(), t); if (t instanceof VolatileLabelMultisetType) { + return (HighlightingStreamConverter)new HighlightingStreamConverterVolatileLabelMultisetType(stream); + } + if (t instanceof LabelMultisetType) { return (HighlightingStreamConverter)new HighlightingStreamConverterLabelMultisetType(stream); } if (t instanceof Volatile && ((Volatile)t).get() instanceof IntegerType) { diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterLabelMultisetType.java b/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterLabelMultisetType.java index dcc669585..c868749c1 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterLabelMultisetType.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterLabelMultisetType.java @@ -1,10 +1,9 @@ package org.janelia.saalfeldlab.paintera.stream; -import net.imglib2.type.label.Label; -import net.imglib2.type.label.VolatileLabelMultisetType; +import net.imglib2.type.label.LabelMultisetType; import net.imglib2.type.numeric.ARGBType; -public class HighlightingStreamConverterLabelMultisetType extends HighlightingStreamConverter { +public class HighlightingStreamConverterLabelMultisetType extends HighlightingStreamConverter { final static private double ONE_OVER_255 = 1.0 / 255.0; @@ -13,15 +12,9 @@ public HighlightingStreamConverterLabelMultisetType(final AbstractHighlightingAR super(stream); } - @Override - public void convert(final VolatileLabelMultisetType input, final ARGBType output) { + @Override public void convert(LabelMultisetType input, ARGBType output) { - final boolean isValid = input.isValid(); - if (!isValid) { - return; - } - // entry - final var entries = input.get().entrySet(); + final var entries = input.entrySet(); final int numEntries = entries.size(); if (numEntries == 0) { final long emptyValue = 0; @@ -51,5 +44,4 @@ public void convert(final VolatileLabelMultisetType input, final ARGBType output output.set(((aInt << 8 | rInt) << 8 | gInt) << 8 | bInt); } } - } diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterVolatileLabelMultisetType.java b/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterVolatileLabelMultisetType.java new file mode 100644 index 000000000..4b63cd133 --- /dev/null +++ b/src/main/java/org/janelia/saalfeldlab/paintera/stream/HighlightingStreamConverterVolatileLabelMultisetType.java @@ -0,0 +1,28 @@ +package org.janelia.saalfeldlab.paintera.stream; + +import net.imglib2.type.label.VolatileLabelMultisetType; +import net.imglib2.type.numeric.ARGBType; + +public class HighlightingStreamConverterVolatileLabelMultisetType extends HighlightingStreamConverter { + + private final HighlightingStreamConverterLabelMultisetType nonVolatileConverter; + public HighlightingStreamConverterVolatileLabelMultisetType(final AbstractHighlightingARGBStream stream) { + + super(stream); + nonVolatileConverter = new HighlightingStreamConverterLabelMultisetType(stream); + } + + public HighlightingStreamConverterLabelMultisetType getNonVolatileConverter() { + return nonVolatileConverter; + } + + @Override + public void convert(final VolatileLabelMultisetType input, final ARGBType output) { + + final boolean isValid = input.isValid(); + if (!isValid) { + return; + } + nonVolatileConverter.convert(input.get(), output); + } +} diff --git a/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt index b6c21cae9..02651364d 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt @@ -7,6 +7,11 @@ import net.imglib2.converter.Converter import net.imglib2.realtransform.AffineTransform3D import net.imglib2.type.numeric.ARGBType import org.janelia.saalfeldlab.paintera.data.DataSource +import org.janelia.saalfeldlab.paintera.stream.HighlightingStreamConverterVolatileLabelMultisetType + +interface CompositeSourceSupplier { + fun getCompositeSource() : Source<*> +} internal fun getDataSourceAndConverter(sourceAndConverter: SourceAndConverter<*>): SourceAndConverter<*> { val data = sourceAndConverter.spimSource as? DataSource ?: return sourceAndConverter @@ -30,6 +35,14 @@ internal fun getDataSourceAndConverter(sourceAndConverter: SourceAndCo override fun getNumMipmapLevels() = data.numMipmapLevels } - return SourceAndConverter(dataSource, sourceAndConverter.converter as Converter) + val converter = sourceAndConverter.converter.let { + (it as? HighlightingStreamConverterVolatileLabelMultisetType)?.nonVolatileConverter ?: it + } as Converter + + return object : SourceAndConverter(dataSource, converter), CompositeSourceSupplier { + override fun getCompositeSource(): Source<*> { + return sourceAndConverter.spimSource + } + } } \ No newline at end of file From 6c8fe970c9f73dc6aadddfb684fe8d4f877b9034 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 18 Oct 2024 16:03:51 -0400 Subject: [PATCH 06/14] feat: change mesh settings defaults --- .../saalfeldlab/paintera/meshes/ManagedMeshSettings.java | 2 +- .../java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java | 2 +- .../org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/ManagedMeshSettings.java b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/ManagedMeshSettings.java index ae67da06c..ab8f110ef 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/ManagedMeshSettings.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/ManagedMeshSettings.java @@ -29,7 +29,7 @@ public class ManagedMeshSettings { private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public static final boolean DEFAULT_IS_MESH_LIST_ENABLED = false; + public static final boolean DEFAULT_IS_MESH_LIST_ENABLED = true; public static final boolean DEFAULT_ARE_MESHES_ENABLED = true; diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java index e8bff0374..7c88dc18c 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java @@ -24,7 +24,7 @@ public class Smooth { public static final double DEFAULT_LAMBDA = 1.0; - public static final int DEFAULT_ITERATIONS = 1; + public static final int DEFAULT_ITERATIONS = 5; private static boolean isBoundary( final ArrayList vertexTriangleLUT, diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt index c92135785..99d22e70f 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt @@ -61,7 +61,7 @@ class MeshSettings @JvmOverloads constructor( val maxLevelOfDetail = 10 @JvmStatic - val levelOfDetail = (minLevelOfDetail + maxLevelOfDetail) / 2 + val levelOfDetail = 8 } companion object { @@ -72,7 +72,7 @@ class MeshSettings @JvmOverloads constructor( fun getDefaultCoarsestScaleLevel(numScaleLevels: Int) = numScaleLevels - 1 @JvmStatic - fun getDefaultFinestScaleLevel(numScaleLevels: Int) = numScaleLevels / 2 + fun getDefaultFinestScaleLevel(numScaleLevels: Int) = 0 } } From eef2705d27648053c9e15a8aa299520842f006d1 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 18 Oct 2024 16:04:22 -0400 Subject: [PATCH 07/14] fix: cancel selectAll from prior attempts --- .../state/LabelSourceStateIdSelectorHandler.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/state/LabelSourceStateIdSelectorHandler.java b/src/main/java/org/janelia/saalfeldlab/paintera/state/LabelSourceStateIdSelectorHandler.java index 682675009..a3179ea80 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/state/LabelSourceStateIdSelectorHandler.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/state/LabelSourceStateIdSelectorHandler.java @@ -30,6 +30,7 @@ import java.lang.invoke.MethodHandles; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.LongPredicate; @@ -118,8 +119,10 @@ public List makeActionSets(KeyTracker keyTracker, Supplier selectAllTask == null); keyAction.onAction(keyEvent -> { + if (selectAllTask != null) { + selectAllTask.cancel(new CancellationException("Cancelled by User")); + } selectAllTask = Tasks.createTask(() -> { Paintera.getPaintera().getBaseView().getNode().getScene().setCursor(Cursor.WAIT); selector.selectAll(); @@ -133,13 +136,19 @@ public List makeActionSets(KeyTracker keyTracker, Supplier selectAllTask == null); keyAction.verify(event -> getActiveViewer.get() != null); keyAction.onAction(keyEvent -> { + if (selectAllTask != null) { + selectAllTask.cancel(new CancellationException("Cancelled by User")); + } + final ViewerPanelFX viewer = getActiveViewer.get(); selectAllTask = Tasks.createTask(() -> { Paintera.getPaintera().getBaseView().getNode().getScene().setCursor(Cursor.WAIT); - selector.selectAllInCurrentView(getActiveViewer.get()); + selector.selectAllInCurrentView(viewer); }).onEnd((result, error) -> { + if (error != null) { + LOG.error("Error selecting all labels in view", error); + } selectAllTask = null; Paintera.getPaintera().getBaseView().getNode().getScene().setCursor(Cursor.DEFAULT); }); From c9ba5d649f7089bfce17b33606df5750ddd4ff36 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Sat, 19 Oct 2024 15:12:41 -0400 Subject: [PATCH 08/14] feat: reuse clients, accept cookies --- .../paintera/cache/SamEmbeddingLoaderCache.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt index e8f65205a..d0a17862f 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt @@ -17,11 +17,13 @@ import net.imglib2.parallel.TaskExecutors import net.imglib2.realtransform.AffineTransform3D import org.apache.commons.lang.builder.HashCodeBuilder import org.apache.http.HttpException +import org.apache.http.client.HttpClient import org.apache.http.client.config.RequestConfig import org.apache.http.client.methods.HttpGet import org.apache.http.client.methods.HttpPost import org.apache.http.entity.ContentType import org.apache.http.entity.mime.MultipartEntityBuilder +import org.apache.http.impl.client.BasicCookieStore import org.apache.http.impl.client.HttpClientBuilder import org.apache.http.util.EntityUtils import org.janelia.saalfeldlab.bdv.fx.viewer.ViewerPanelFX @@ -238,6 +240,12 @@ object SamEmbeddingLoaderCache : AsyncCacheWithLoader Date: Sat, 19 Oct 2024 15:13:55 -0400 Subject: [PATCH 09/14] fix: wrap nonvolatile for volatile sam image fixes issues with the initial sam image rending --- .../paintera/control/tools/paint/SamTool.kt | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt index 70350a767..10cfa24b7 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt @@ -1010,38 +1010,13 @@ open class SamTool(activeSourceStateProperty: SimpleObjectProperty - val setCompositeVal = { - var checkOriginal = false - val overlayVal = overlay.get() - if (overlayVal == predictionLabel) { - composite.get().set(predictionLabel) - composite.isValid = true - } else checkOriginal = true - if (checkOriginal) { - if (original.isValid) { - composite.set(original) - composite.isValid = true - } else composite.isValid = false - composite.isValid = true - } - } - if (maskPriority == MaskPriority.PREDICTION || !original.isValid) { - setCompositeVal() - } else { - val originalVal = original.get().get() - if (originalVal != Label.INVALID) { - composite.set(originalVal) - composite.isValid = true - } else setCompositeVal() - } - }.interval(maskAlignedSelectedComponents) + val compositeMaskAsVolatile = compositeMask.convertRAI(VolatileUnsignedLongType(0L)) { source, output -> + output.set(source.get()) + output.isValid = true + } paintMask.updateBackingImages( - compositeMask to compositeVolatileMask, + compositeMask to compositeMaskAsVolatile, writableSourceImages = originalBackingImage to originalVolatileBackingImage ) From e122a7f04bfd86f9a808bad4929f44998d398cb8 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Sat, 19 Oct 2024 15:14:42 -0400 Subject: [PATCH 10/14] fix: better fix for non-volatile data source composite --- .../CompositeProjectorPreMultiply.java | 8 +------- .../saalfeldlab/bdv/fx/viewer/SourceUtils.kt | 18 +++++++----------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java b/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java index 0e8811761..5ee059235 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/composition/CompositeProjectorPreMultiply.java @@ -10,7 +10,6 @@ import net.imglib2.RandomAccessible; import net.imglib2.RandomAccessibleInterval; import net.imglib2.type.numeric.ARGBType; -import org.janelia.saalfeldlab.bdv.fx.viewer.CompositeSourceSupplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,12 +59,7 @@ public VolatileProjector createProjector( final ArrayList> activeComposites = new ArrayList<>(); for (final var activeSource : sources) { - final Source compositeSource; - if (activeSource instanceof CompositeSourceSupplier) - compositeSource = ((CompositeSourceSupplier)activeSource).getCompositeSource(); - else - compositeSource = activeSource.getSpimSource(); - activeComposites.add(composites.get(compositeSource)); + activeComposites.add(composites.get(activeSource.getSpimSource())); } projector.setComposites(activeComposites); diff --git a/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt index 02651364d..d89c1d502 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt @@ -9,12 +9,8 @@ import net.imglib2.type.numeric.ARGBType import org.janelia.saalfeldlab.paintera.data.DataSource import org.janelia.saalfeldlab.paintera.stream.HighlightingStreamConverterVolatileLabelMultisetType -interface CompositeSourceSupplier { - fun getCompositeSource() : Source<*> -} - internal fun getDataSourceAndConverter(sourceAndConverter: SourceAndConverter<*>): SourceAndConverter<*> { - val data = sourceAndConverter.spimSource as? DataSource ?: return sourceAndConverter + val data = sourceAndConverter.spimSource as? DataSource ?: return sourceAndConverter val dataSource = object : Source { override fun isPresent(t: Int) = data.isPresent(t) @@ -28,21 +24,21 @@ internal fun getDataSourceAndConverter(sourceAndConverter: SourceAndCo override fun getType() = data.dataType - override fun getName() = sourceAndConverter.spimSource.name + override fun getName() = data.name override fun getVoxelDimensions() = data.voxelDimensions override fun getNumMipmapLevels() = data.numMipmapLevels + + override fun hashCode() = data.hashCode() + + override fun equals(other: Any?) = data == other } val converter = sourceAndConverter.converter.let { (it as? HighlightingStreamConverterVolatileLabelMultisetType)?.nonVolatileConverter ?: it } as Converter - return object : SourceAndConverter(dataSource, converter), CompositeSourceSupplier { - override fun getCompositeSource(): Source<*> { - return sourceAndConverter.spimSource - } - } + return SourceAndConverter(dataSource, converter) } \ No newline at end of file From 416f500d4a12e7366108df4959ce7415d0558d34 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Sat, 19 Oct 2024 15:15:18 -0400 Subject: [PATCH 11/14] perf: shape interpolation improvements --- .../control/ShapeInterpolationController.kt | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt index 14eede3c7..188590343 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt @@ -102,7 +102,7 @@ class ShapeInterpolationController>( val currentSliceMaskInterval get() = sliceAtCurrentDepth?.maskBoundingBox - val numSlices: Int get() = slicesAndInterpolants.slices.size + val numSlices: Int get() = slicesAndInterpolants.count { it.isSlice } var activeSelectionAlpha = (AbstractHighlightingARGBStream.DEFAULT_ACTIVE_FRAGMENT_ALPHA ushr 24) / 255.0 @@ -302,9 +302,6 @@ class ShapeInterpolationController>( if (replaceExistingInterpolants) { slicesAndInterpolants.removeAllInterpolants() } - if (interpolator != null) { - interpolator!!.cancel() - } isBusy = true interpolator = CoroutineScope(Dispatchers.Default).launch { @@ -582,8 +579,6 @@ class ShapeInterpolationController>( private fun interruptInterpolation() { interpolator?.let { it.cancel() - /* Ensure it's done */ - runBlocking { it.join() } } } @@ -955,8 +950,8 @@ class ShapeInterpolationController>( val sliceDepth: Double get() = sliceAndDepth!!.first - fun getInterpolant(): InterpolantInfo? { - return interpolant + fun getInterpolant(): InterpolantInfo { + return interpolant!! } override fun equals(other: Any?): Boolean { @@ -1091,18 +1086,25 @@ class ShapeInterpolationController>( } val slices: List - get() = synchronized(this) { - stream() - .filter { it.isSlice } - .map { it.getSlice() } - .collect(Collectors.toList()) + get() = mutableListOf().let { + val iterator = iterator() + while (iterator.hasNext()) { + val element = iterator.next() + if (element.isSlice) + it.add(element.getSlice()) + } + it.toList() } val interpolants: List - get() = synchronized(this) { - stream() - .filter { it.isInterpolant } - .map { it.getInterpolant()!! } - .collect(Collectors.toList()) + get() = mutableListOf().let { + removeIf { false } + val iterator = iterator() + while (iterator.hasNext()) { + val element = iterator.next() + if (element.isInterpolant) + it.add(element.getInterpolant()) + } + it.toList() } fun clearInterpolantsAroundSlice(z: Double) { From d8beaad02e51487dd040d130591c76878a820a08 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Sat, 19 Oct 2024 23:59:02 -0400 Subject: [PATCH 12/14] fix: ensure unwrapped/wrapped SourceAndConverter for non-volatile data sources play nicely with the sam cache loader --- .../saalfeldlab/bdv/fx/viewer/SourceUtils.kt | 56 +++++++++++++------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt index d89c1d502..64cae7e35 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/bdv/fx/viewer/SourceUtils.kt @@ -9,36 +9,58 @@ import net.imglib2.type.numeric.ARGBType import org.janelia.saalfeldlab.paintera.data.DataSource import org.janelia.saalfeldlab.paintera.stream.HighlightingStreamConverterVolatileLabelMultisetType -internal fun getDataSourceAndConverter(sourceAndConverter: SourceAndConverter<*>): SourceAndConverter<*> { - val data = sourceAndConverter.spimSource as? DataSource ?: return sourceAndConverter - val dataSource = object : Source { - override fun isPresent(t: Int) = data.isPresent(t) +//FIXME Caleb: These are both private because imho this is a bit of a hack. +// We want to Paintera's DataSource implemenet Source over the volatile type, +// and the corrsponding SourceAndConverter has a convert also over the volatile type +// This makes it not possible get a non-volatile SourceAndConverter over the DataSource. +// So what we do here is manually unwrap, but we still want it to play nicely with the +// volatile renderer, so we need it to "equal" it's volatile version. +// I think SourceAndConverter has a `volatileConverter` that we should instead be using +// when we want volatile (most the time) and support a proper non-volatile version without +// wrapping/unwrapping this way. +private class UnwrappedDataSource(val dataSource : DataSource) : Source { + override fun isPresent(t: Int) = dataSource.isPresent(t) + + override fun getSource(t: Int, level: Int) = dataSource.getDataSource(t, level) + + override fun getInterpolatedSource(t: Int, level: Int, method: Interpolation?) = dataSource.getInterpolatedDataSource(t, level, method) + + override fun getSourceTransform(t: Int, level: Int, transform: AffineTransform3D?) { + dataSource.getSourceTransform(t, level, transform) + } - override fun getSource(t: Int, level: Int) = data.getDataSource(t, level) + override fun getType() = dataSource.dataType - override fun getInterpolatedSource(t: Int, level: Int, method: Interpolation?) = data.getInterpolatedDataSource(t, level, method) + override fun getName() = dataSource.name - override fun getSourceTransform(t: Int, level: Int, transform: AffineTransform3D?) { - data.getSourceTransform(t, level, transform) - } + override fun getVoxelDimensions() = dataSource.voxelDimensions - override fun getType() = data.dataType + override fun getNumMipmapLevels() = dataSource.numMipmapLevels - override fun getName() = data.name + override fun hashCode() = dataSource.hashCode() - override fun getVoxelDimensions() = data.voxelDimensions + override fun equals(other: Any?) = dataSource == ((other as? UnwrappedDataSource<*>)?.dataSource ?: other) +} - override fun getNumMipmapLevels() = data.numMipmapLevels - override fun hashCode() = data.hashCode() +private class WrappedSourceAndConverter(val sourceAndConverter : SourceAndConverter<*>, source : Source, converter : Converter) : SourceAndConverter(source, converter) { - override fun equals(other: Any?) = data == other + override fun equals(other: Any?): Boolean { + return sourceAndConverter == ((other as? WrappedSourceAndConverter<*>)?.sourceAndConverter ?: other) } + override fun hashCode(): Int { + return sourceAndConverter.hashCode() + } +} + +internal fun getDataSourceAndConverter(sourceAndConverter: SourceAndConverter<*>): SourceAndConverter<*> { + val data = sourceAndConverter.spimSource as? DataSource ?: return sourceAndConverter + val unwrappedDataSource = UnwrappedDataSource(data) + val converter = sourceAndConverter.converter.let { (it as? HighlightingStreamConverterVolatileLabelMultisetType)?.nonVolatileConverter ?: it } as Converter - return SourceAndConverter(dataSource, converter) - + return WrappedSourceAndConverter(sourceAndConverter, unwrappedDataSource, converter) } \ No newline at end of file From 693bb3edf92a1f79aaf6fd9d8129218045a91a03 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Sun, 20 Oct 2024 12:21:52 -0400 Subject: [PATCH 13/14] feat: change brush size with keys (minus,down and equals,plus,up) --- .../control/tools/paint/PaintBrushTool.kt | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/PaintBrushTool.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/PaintBrushTool.kt index 95267288c..6a5568860 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/PaintBrushTool.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/PaintBrushTool.kt @@ -251,15 +251,44 @@ open class PaintBrushTool(activeSourceStateProperty: SimpleObjectProperty + KEY_PRESSED(key) { + keysExclusive = false + name = "plus_brush_size" + verifyEventNotNull() + onAction { + paint2D.increaseBrushRadius() + if (it?.isShiftDown == true) { + paint2D.increaseBrushRadius() + paint2D.increaseBrushRadius() + } + } + } + } + + arrayOf(KeyCode.MINUS, KeyCode.DOWN).map { key -> + KEY_PRESSED(key) { + keysExclusive = false + name = "minus_brush_size" + verifyEventNotNull() + onAction { + paint2D.decreaseBrushRadius() + if (it?.isShiftDown == true) { + paint2D.decreaseBrushRadius() + paint2D.decreaseBrushRadius() + } + } + } + } }, painteraActionSet(CHANGE_BRUSH_DEPTH, PaintActionType.SetBrushDepth) { ScrollEvent.SCROLL(KeyCode.SHIFT) { From f9d49571b956ecbdfa18c28c44acb22f43f94bd7 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Sun, 20 Oct 2024 17:31:21 -0400 Subject: [PATCH 14/14] fix: re-enable navigation based sam embedding request --- .../saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt index d0a17862f..b07d6ca8c 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/cache/SamEmbeddingLoaderCache.kt @@ -89,7 +89,6 @@ object SamEmbeddingLoaderCache : AsyncCacheWithLoader