Skip to content

Commit 4c6b2b2

Browse files
Read Zarr Segment Index Files (#8711)
- Introduces attachment lookup for segment index files - enables reading zarr segment index files - Fixes bug where segment index file positions were wrongly interpreted as being target-mag when really they are mag1 - Adds cache clear (including closing hdf5 file handles) for segment index files - Note to reviewer: git didn’t catch that a lot of code was moved (from the old segmentIndexFileService to the new one, and partially to the hdf5 one). ### Steps to test: - For a dataset with segment index file (try both hdf5 and zarr, talk to me for sample data), right-click a segment, context menu should show plausible segment volume + bbox (not zero) - Layer reload in the sidebar should log that cached segment index file handles were cleared. ### TODOs: - [x] attachment lookup - [x] make sure hdf5 still works - [x] implement for zarr - [x] clear caches ### Issues: - contributes to #8567 - contributes to #8618 ------ - [x] Added changelog entry (create a `$PR_NUMBER.md` file in `unreleased_changes` or use `./tools/create-changelog-entry.py`) - [x] Removed dev-only changes like prints and application.conf edits - [x] Considered [common edge cases](../blob/master/.github/common_edge_cases.md) - [x] Needs datastore update after deployment --------- Co-authored-by: MichaelBuessemeyer <39529669+MichaelBuessemeyer@users.noreply.github.com>
1 parent 56e8306 commit 4c6b2b2

31 files changed

+693
-410
lines changed

conf/messages

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ mesh.file.readVersion.failed=Failed to read format version from file “{0}”
270270
mesh.file.readMappingName.failed=Failed to read mapping name from mesh file “{0}”
271271
mesh.meshFileName.required=Trying to load mesh from mesh file, but mesh file name was not supplied.
272272

273+
segmentIndexFile.notFound=Could not find requested segment index file
274+
273275
task.create.noTasks=Zero tasks were requested
274276
task.create.failed=Failed to create Task
275277
task.create.limitExceeded=Cannot create more than 1000 tasks in one request.

unreleased_changes/8711.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### Added
2+
- Segment index files can now also be read from the new zarr3-based format, and from remote object storage.
3+
4+
### Fixed
5+
- Fixed a bug where segment index files for segmentation layers with no mag1 would be read incorrectly.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/DataStoreModule.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,24 @@ import org.apache.pekko.actor.ActorSystem
44
import com.google.inject.AbstractModule
55
import com.google.inject.name.Names
66
import com.scalableminds.webknossos.datastore.services._
7+
import com.scalableminds.webknossos.datastore.services.mapping.{
8+
AgglomerateService,
9+
Hdf5AgglomerateService,
10+
MappingService,
11+
ZarrAgglomerateService
12+
}
713
import com.scalableminds.webknossos.datastore.services.mesh.{
814
AdHocMeshServiceHolder,
915
Hdf5MeshFileService,
1016
MeshFileService,
1117
NeuroglancerPrecomputedMeshFileService,
1218
ZarrMeshFileService
1319
}
20+
import com.scalableminds.webknossos.datastore.services.segmentindex.{
21+
Hdf5SegmentIndexFileService,
22+
SegmentIndexFileService,
23+
ZarrSegmentIndexFileService
24+
}
1425
import com.scalableminds.webknossos.datastore.services.uploading.UploadService
1526
import com.scalableminds.webknossos.datastore.storage.{DataVaultService, RemoteSourceDescriptorService}
1627

@@ -38,6 +49,9 @@ class DataStoreModule extends AbstractModule {
3849
bind(classOf[AgglomerateService]).asEagerSingleton()
3950
bind(classOf[ZarrAgglomerateService]).asEagerSingleton()
4051
bind(classOf[Hdf5AgglomerateService]).asEagerSingleton()
52+
bind(classOf[SegmentIndexFileService]).asEagerSingleton()
53+
bind(classOf[ZarrSegmentIndexFileService]).asEagerSingleton()
54+
bind(classOf[Hdf5SegmentIndexFileService]).asEagerSingleton()
4155
bind(classOf[NeuroglancerPrecomputedMeshFileService]).asEagerSingleton()
4256
bind(classOf[RemoteSourceDescriptorService]).asEagerSingleton()
4357
bind(classOf[ChunkCacheService]).asEagerSingleton()

webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/BinaryDataController.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.scalableminds.webknossos.datastore.services._
2121
import com.scalableminds.webknossos.datastore.services.mesh.{AdHocMeshRequest, AdHocMeshService, AdHocMeshServiceHolder}
2222
import com.scalableminds.webknossos.datastore.slacknotification.DSSlackNotificationService
2323
import com.scalableminds.util.tools.Box.tryo
24+
import com.scalableminds.webknossos.datastore.services.mapping.MappingService
2425
import play.api.i18n.Messages
2526
import play.api.libs.json.Json
2627
import play.api.mvc.{AnyContent, _}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/DataSourceController.scala

Lines changed: 45 additions & 34 deletions
Large diffs are not rendered by default.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/SegmentStatistics.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ trait SegmentStatistics extends ProtoGeometryImplicits with FoxImplicits {
3535
implicit ec: ExecutionContext): Fox[Long] =
3636
for {
3737
bucketPositionsProtos: Set[Vec3IntProto] <- getBucketPositions(segmentId, mag)
38-
bucketPositionsInMag = bucketPositionsProtos.map(vec3IntFromProto)
39-
(bucketBoxes, elementClass) <- getDataForBucketPositions(bucketPositionsInMag.toSeq, mag, additionalCoordinates)
38+
bucketPositionsInRequestedMag = bucketPositionsProtos.map(vec3IntFromProto)
39+
(bucketBoxes, elementClass) <- getDataForBucketPositions(bucketPositionsInRequestedMag.toSeq,
40+
mag,
41+
additionalCoordinates)
4042
counts <- Fox.serialCombined(bucketBoxes.toList) {
4143
case Full(bucketBytes) =>
4244
tryo(

webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DatasetLayerAttachments.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,16 @@ object LayerAttachmentType extends ExtendedEnumeration {
3838
case class LayerAttachment(name: String,
3939
path: URI,
4040
dataFormat: LayerAttachmentDataformat.LayerAttachmentDataformat,
41-
credentialId: Option[String] = None)
41+
credentialId: Option[String] = None) {
42+
// Warning: throws! Use inside of tryo
43+
def localPath: Path = {
44+
if (path.getScheme.nonEmpty && path.getScheme != "file") {
45+
throw new Exception(
46+
"Trying to open non-local hdf5 file. Hdf5 files are only supported on the datastore-local file system.")
47+
}
48+
Path.of(path)
49+
}
50+
}
4251

4352
object LayerAttachment {
4453
implicit val jsonFormat: Format[LayerAttachment] = Json.format[LayerAttachment]

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/Hdf5HashedArrayUtils.scala renamed to webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/ArrayArtifactHashing.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import org.apache.commons.codec.digest.MurmurHash3
44

55
import java.nio.ByteBuffer
66

7-
trait Hdf5HashedArrayUtils {
8-
9-
val hdf5FileExtension = "hdf5"
7+
trait ArrayArtifactHashing {
108

119
def getHashFunction(name: String): Long => Long = name match {
1210
case "identity" => identity
1311
case "murmurhash3_x64_128" =>
1412
x: Long =>
1513
Math.abs(MurmurHash3.hash128x64(ByteBuffer.allocate(8).putLong(x).array())(1))
1614
}
15+
1716
}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/BinaryDataService.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.typesafe.scalalogging.LazyLogging
1414
import com.scalableminds.util.tools.{Box, Empty, Full}
1515
import ucar.ma2.{Array => MultiArray}
1616
import com.scalableminds.util.tools.Box.tryo
17+
import com.scalableminds.webknossos.datastore.services.mapping.AgglomerateService
1718

1819
import java.nio.file.Path
1920
import scala.concurrent.ExecutionContext

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/BinaryDataServiceHolder.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.scalableminds.webknossos.datastore.services
22

33
import java.nio.file.Paths
44
import com.scalableminds.webknossos.datastore.DataStoreConfig
5+
import com.scalableminds.webknossos.datastore.services.mapping.AgglomerateService
56
import com.scalableminds.webknossos.datastore.storage.RemoteSourceDescriptorService
67

78
import javax.inject.Inject

0 commit comments

Comments
 (0)