Skip to content

Commit e96aa6d

Browse files
committed
feat: add slice suport when going to-paintera
1 parent 7e5621f commit e96aa6d

4 files changed

Lines changed: 24 additions & 11 deletions

File tree

pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545

4646
<label-utilities-spark.version>1.2.0-SNAPSHOT</label-utilities-spark.version>
4747
<n5-spark.version>4.1.0-SNAPSHOT</n5-spark.version>
48+
<n5-universe.version>3.1.0-SNAPSHOT</n5-universe.version>
49+
<n5.version>4.0.2-SNAPSHOT</n5.version>
4850
<spark.version>3.5.4</spark.version>
4951
<spark-core_2.12.version>${spark.version}</spark-core_2.12.version>
5052
<jackson.version>2.15.4</jackson.version>

src/main/kotlin/org/janelia/saalfeldlab/conversion/to/scalar/ToScalar.kt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,23 @@ class ToScalar : Callable<Int> {
174174
}
175175
}
176176

177-
val chunksPerShard = this.chunksPerShard?.let { cps ->
178-
when (cps.size) {
179-
1 -> IntArray(3) { cps[0] }
180-
3 -> cps.clone()
181-
else -> throw InvalidBlockSize(cps, "chunks-per-shard has to be specified with one or three entries but got ${cps.joinToString(", ", "[", "]")}")
177+
val chunksPerShard = this.chunksPerShard?.let { chunksPerShard ->
178+
val argString = chunksPerShard.joinToString(", ", "[", "]")
179+
val chunks = when (chunksPerShard.size) {
180+
1 -> IntArray(3) { chunksPerShard[0] }
181+
3 -> chunksPerShard.clone()
182+
else -> throw InvalidBlockSize(chunksPerShard, "chunks-per-shard has to be specified with one or three entries but got $argString")
182183
}
184+
185+
val zeroChunksAnyDim = chunks.any { it < 1 }
186+
if (zeroChunksAnyDim)
187+
throw InvalidBlockSize(chunksPerShard, "chunks-per-shard entries have to be positive but got $argString")
188+
189+
val oneChunkPerShard = chunks.all { it == 1 }
190+
if (oneChunkPerShard)
191+
throw InvalidBlockSize(chunksPerShard, "chunks-per-shard has to be greater than 1 in at least one dimension but got $argString")
192+
193+
chunks
183194
}
184195

185196
val xyzUnit = this.xyzUnit.let { units ->

src/test/kotlin/org/janelia/saalfeldlab/conversion/NDSlicingToPainteraTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class NDSlicingToPainteraTest {
4343
"-d", "labels",
4444
"--type=label",
4545
"--target-dataset=seg",
46-
"--slice-positions", "x,y,z,1", // axis0→x, axis1→y, axis2→z, channel axis fixed at 1
46+
"--slice-positions", "x,y,z,1", // axis0→x, axis1→y, axis2→z, channel axis sliced at 1
4747
"--dataset-resolution", "1,1,1"
4848
)
4949
)
@@ -53,11 +53,11 @@ class NDSlicingToPainteraTest {
5353
val s0 = reader.getDatasetAttributes("seg/data/s0")
5454
assertEquals(listOf(6L, 7L, 8L), s0.dimensions.toList(), "s0 is the 3D sliced shape (x,y,z)")
5555

56-
/* the indices are derived from s0, so they must carry the sliced shape too */
56+
/* the indices are derived from s0, so they have the sliced shape too */
5757
assertTrue(reader.datasetExists("seg/unique-labels/s0"), "unique-labels index exists")
5858
assertEquals(listOf(6L, 7L, 8L), reader.getDatasetAttributes("seg/unique-labels/s0").dimensions.toList(), "unique-labels has sliced shape")
5959

60-
/* the LMT round-trips the c=1 slice voxel-for-voxel */
60+
/* the LMT matches the c=1 slice voxel-for-voxel */
6161
val lmt = N5LabelMultisets.openLabelMultiset(reader, "seg/data/s0")
6262
val ra = lmt.randomAccess()
6363
for (x in 0 until 6) for (y in 0 until 7) for (z in 0 until 8) {

src/test/kotlin/org/janelia/saalfeldlab/conversion/ToScalarZarr3OutputTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class ToScalarZarr3OutputTest {
160160
.count { it.isFile && it.name != "zarr.json" }
161161
assertEquals(1, shardFiles)
162162

163-
/* the 3 skipped shards read back as the fill value (0); the one voxel round-trips */
163+
/* the 3 skipped shards read back as the fill value (0); the one voxel reads back unchanged */
164164
val reader = createReader(outputPath)
165165
LoopBuilder.setImages(sparseImg, N5Utils.open<UnsignedLongType>(reader, outputDataset))
166166
.forEachPixel(BiConsumer { e: UnsignedLongType, a: UnsignedLongType -> assertTrue(e.valueEquals(a)) })
@@ -196,7 +196,7 @@ class ToScalarZarr3OutputTest {
196196
@Test
197197
fun `to-scalar sharded zarr3 keeps within-shard chunk sparsity`() {
198198
/* one whole shard of 2x2x2 chunks (block 3, chunks-per-shard 2 -> shard 6); chunk (1,1,1) is all fill (0)
199-
* while the other seven carry data, so the shard is written but that chunk must be omitted, not materialized */
199+
* while the other seven hold data, so the shard is written but that chunk must be omitted, not written out */
200200
val chunkDims = longArrayOf(6, 6, 6)
201201
val img = ArrayImgs.unsignedLongs(LongArray((6 * 6 * 6)) { 7L }, *chunkDims)
202202
val ra = img.randomAccess()
@@ -230,7 +230,7 @@ class ToScalarZarr3OutputTest {
230230
assertNull(reader.readChunk<LongArray>(outputDataset, attrs, *longArrayOf(1, 1, 1)), "all-fill inner chunk should be omitted")
231231
assertNotNull(reader.readChunk<LongArray>(outputDataset, attrs, *longArrayOf(0, 0, 0)), "data inner chunk should be present")
232232

233-
/* and it still round-trips: the omitted chunk reads back as fill 0 */
233+
/* the omitted chunk still reads back as fill 0 */
234234
LoopBuilder.setImages(img, N5Utils.open<UnsignedLongType>(reader, outputDataset))
235235
.forEachPixel(BiConsumer { e: UnsignedLongType, a: UnsignedLongType -> assertTrue(e.valueEquals(a)) })
236236
}

0 commit comments

Comments
 (0)