Skip to content

Commit 2da75a5

Browse files
authored
Remove VoxelType Enum, use ArrayDataType instead for WKW (#8559)
* WIP: unify backend dtype classes * Fix VoxelType off-by-one * fix merge artifact in migrations guide
1 parent c17b29a commit 2da75a5

File tree

8 files changed

+64
-104
lines changed

8 files changed

+64
-104
lines changed

MIGRATIONS.unreleased.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md).
99
[Commits](https://github.com/scalableminds/webknossos/compare/25.05.0...HEAD)
1010

1111
### Postgres Evolutions:
12-
<<<<<<< HEAD
1312

1413
- [131-more-indices-on-users.sql](conf/evolutions/131-more-indices-on-users.sql)
1514
- [132-remove-stored-meshes.sql](conf/evolutions/132-remove-stored-meshes.sql)

webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/VoxelType.scala

Lines changed: 0 additions & 69 deletions
This file was deleted.

webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWHeader.scala

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.scalableminds.webknossos.datastore.datareaders.ArrayDataType.ArrayDat
77
import com.scalableminds.webknossos.datastore.datareaders.ArrayOrder.ArrayOrder
88
import com.scalableminds.webknossos.datastore.datareaders.DimensionSeparator.DimensionSeparator
99
import com.scalableminds.webknossos.datastore.datareaders.{
10+
ArrayDataType,
1011
ArrayOrder,
1112
Compressor,
1213
DatasetHeader,
@@ -32,8 +33,8 @@ case class WKWHeader(
3233
numChunksPerShardDimension: Int,
3334
numVoxelsPerChunkDimension: Int,
3435
blockType: ChunkType.Value,
35-
voxelType: VoxelType.Value,
36-
numBytesPerVoxel: Int, // this encodes the channel count together with voxelType
36+
dataType: ArrayDataType,
37+
numChannels: Int,
3738
jumpTable: Array[Long]
3839
) extends DatasetHeader
3940
with WKWDataFormatHelper {
@@ -44,11 +45,11 @@ case class WKWHeader(
4445

4546
def numChunksPerShard: Int = numChunksPerShardDimension * numChunksPerShardDimension * numChunksPerShardDimension
4647

48+
private def numBytesPerVoxel = numChannels * ArrayDataType.bytesPerElement(dataType)
49+
4750
def numBytesPerChunk: Int =
4851
numVoxelsPerChunkDimension * numVoxelsPerChunkDimension * numVoxelsPerChunkDimension * numBytesPerVoxel
4952

50-
def numChannels: Int = numBytesPerVoxel / VoxelType.bytesPerVoxelPerChannel(voxelType)
51-
5253
def blockLengths: Iterator[Int] =
5354
if (isCompressed) {
5455
jumpTable.sliding(2).map(a => (a(1) - a(0)).toInt)
@@ -64,7 +65,7 @@ case class WKWHeader(
6465
val sideLengths = (numChunksPerShardDimensionLog2 << 4) + numVoxelsPerChunkDimensionLog2
6566
output.writeByte(sideLengths)
6667
output.writeByte(blockType.id)
67-
output.writeByte(voxelType.id)
68+
output.writeByte(ArrayDataType.toWKWId(dataType))
6869
output.writeByte(numBytesPerVoxel)
6970
if (isHeaderFile) {
7071
output.writeLong(0L)
@@ -96,7 +97,7 @@ case class WKWHeader(
9697

9798
override def order: ArrayOrder = ArrayOrder.F
9899

99-
override def resolvedDataType: ArrayDataType = VoxelType.toArrayDataType(voxelType)
100+
override def resolvedDataType: ArrayDataType = dataType
100101

101102
override def compressorImpl: Compressor = blockType match {
102103
case ChunkType.Raw => nullCompressor
@@ -146,7 +147,8 @@ object WKWHeader {
146147
numVoxelsPerChunkDimension,
147148
"[0, 1024)")
148149
blockType <- tryo(ChunkType(blockTypeId)) ?~! error("Specified blockType is not supported")
149-
voxelType <- tryo(VoxelType(voxelTypeId)) ?~! error("Specified voxelType is not supported")
150+
voxelType <- tryo(ArrayDataType.fromWKWTypeId(voxelTypeId)) ?~! error("Specified voxelType is not supported")
151+
numChannels = numBytesPerVoxel / ArrayDataType.bytesPerElement(voxelType)
150152
} yield {
151153
val jumpTable = if (ChunkType.isCompressed(blockType) && readJumpTable) {
152154
val numChunksPerShard = numChunksPerShardDimension * numChunksPerShardDimension * numChunksPerShardDimension
@@ -159,7 +161,7 @@ object WKWHeader {
159161
numVoxelsPerChunkDimension,
160162
blockType,
161163
voxelType,
162-
numBytesPerVoxel,
164+
numChannels,
163165
jumpTable)
164166
}
165167
}
@@ -170,15 +172,15 @@ object WKWHeader {
170172
def apply(numChunksPerShardDimension: Int,
171173
numVoxelsPerChunkDimension: Int,
172174
blockType: ChunkType.Value,
173-
voxelType: VoxelType.Value,
175+
dataType: ArrayDataType,
174176
numChannels: Int): WKWHeader =
175177
new WKWHeader(
176178
currentVersion,
177179
numChunksPerShardDimension,
178180
numVoxelsPerChunkDimension,
179181
blockType,
180-
voxelType,
181-
VoxelType.bytesPerVoxelPerChannel(voxelType) * numChannels,
182+
dataType,
183+
numChannels,
182184
Array(0)
183185
)
184186
}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/ArrayDataType.scala

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ object ArrayDataType extends ExtendedEnumeration {
66
type ArrayDataType = Value
77
val f8, f4, i8, u8, i4, u4, i2, u2, i1, u1 = Value
88

9-
def bytesPerElementFor(dataType: ArrayDataType): Int =
9+
def bytesPerElement(dataType: ArrayDataType): Int =
1010
dataType match {
1111
case ArrayDataType.f8 => 8
1212
case ArrayDataType.f4 => 4
@@ -47,4 +47,32 @@ object ArrayDataType extends ExtendedEnumeration {
4747
case ArrayDataType.i1 => Byte.MinValue
4848
case ArrayDataType.u1 => 0
4949
}
50+
51+
def toWKWId(dataType: ArrayDataType): Int =
52+
dataType match {
53+
case ArrayDataType.u1 => 1
54+
case ArrayDataType.u2 => 2
55+
case ArrayDataType.u4 => 3
56+
case ArrayDataType.u8 => 4
57+
case ArrayDataType.f4 => 5
58+
case ArrayDataType.f8 => 6
59+
case ArrayDataType.i1 => 7
60+
case ArrayDataType.i2 => 8
61+
case ArrayDataType.i4 => 9
62+
case ArrayDataType.i8 => 10
63+
}
64+
65+
def fromWKWTypeId(wkwVoxelTypeId: Int): ArrayDataType.Value =
66+
wkwVoxelTypeId match {
67+
case 1 => ArrayDataType.u1
68+
case 2 => ArrayDataType.u2
69+
case 3 => ArrayDataType.u4
70+
case 4 => ArrayDataType.u8
71+
case 5 => ArrayDataType.f4
72+
case 6 => ArrayDataType.f8
73+
case 7 => ArrayDataType.i1
74+
case 8 => ArrayDataType.i2
75+
case 9 => ArrayDataType.i4
76+
case 10 => ArrayDataType.i8
77+
}
5078
}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/BytesConverter.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.scalableminds.webknossos.datastore.datareaders
22

3-
import com.scalableminds.webknossos.datastore.datareaders.ArrayDataType.{ArrayDataType, bytesPerElementFor}
3+
import com.scalableminds.webknossos.datastore.datareaders.ArrayDataType.ArrayDataType
44
import net.liftweb.common.Box
55
import net.liftweb.common.Box.tryo
66
import ucar.ma2.{Array => MultiArray}
@@ -10,7 +10,7 @@ import java.nio.{ByteBuffer, ByteOrder}
1010
object BytesConverter {
1111
def toByteArray(multiArray: MultiArray, dataType: ArrayDataType, byteOrder: ByteOrder): Box[Array[Byte]] = tryo {
1212
val array = multiArray.getStorage
13-
val bytesPerElement = bytesPerElementFor(dataType)
13+
val bytesPerElement = ArrayDataType.bytesPerElement(dataType)
1414
// If the multiArray dtype size is 1, use the array directly.
1515
// This may be happen due to the skipTyping shortcut even for non-uint8-datasets
1616
if (multiArray.getDataType.getSize == 1) {

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetHeader.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.scalableminds.webknossos.datastore.datareaders
33
import com.scalableminds.util.geometry.{BoundingBox, Vec3Int}
44
import com.scalableminds.webknossos.datastore.datareaders.ArrayOrder.ArrayOrder
55
import com.scalableminds.webknossos.datastore.datareaders.DimensionSeparator.DimensionSeparator
6-
import ArrayDataType.{ArrayDataType, bytesPerElementFor}
6+
import ArrayDataType.ArrayDataType
77
import com.scalableminds.webknossos.datastore.models.datasource.ElementClass
88

99
import java.nio.ByteOrder
@@ -28,7 +28,7 @@ trait DatasetHeader {
2828

2929
lazy val byteOrder: ByteOrder = ByteOrder.BIG_ENDIAN
3030

31-
lazy val bytesPerElement: Int = bytesPerElementFor(resolvedDataType)
31+
lazy val bytesPerElement: Int = ArrayDataType.bytesPerElement(resolvedDataType)
3232

3333
lazy val bytesPerChunk: Int = chunkShape.toList.product * bytesPerElement
3434

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ object DataFormat extends ExtendedEnumeration {
3232

3333
object Category extends ExtendedEnumeration {
3434
val color, segmentation = Value
35-
36-
def guessFromElementClass(elementClass: ElementClass.Value): Category.Value =
37-
elementClass match {
38-
case ElementClass.uint16 => segmentation
39-
case ElementClass.uint32 => segmentation
40-
case ElementClass.uint64 => segmentation
41-
case _ => color
42-
}
4335
}
4436

4537
object ElementClass extends ExtendedEnumeration {
@@ -186,6 +178,20 @@ object ElementClass extends ExtendedEnumeration {
186178
case ArrayDataType.i8 => Some(ElementClass.int64)
187179
case _ => None
188180
}
181+
182+
def toArrayDataTypeAndChannel(elementClass: ElementClass.Value): (ArrayDataType, Int) = elementClass match {
183+
case ElementClass.uint8 => (ArrayDataType.u1, 1)
184+
case ElementClass.uint16 => (ArrayDataType.u2, 1)
185+
case ElementClass.uint24 => (ArrayDataType.u1, 3)
186+
case ElementClass.uint32 => (ArrayDataType.u4, 1)
187+
case ElementClass.uint64 => (ArrayDataType.u8, 1)
188+
case ElementClass.float => (ArrayDataType.f4, 1)
189+
case ElementClass.double => (ArrayDataType.f8, 1)
190+
case ElementClass.int8 => (ArrayDataType.i1, 1)
191+
case ElementClass.int16 => (ArrayDataType.i2, 1)
192+
case ElementClass.int32 => (ArrayDataType.i4, 1)
193+
case ElementClass.int64 => (ArrayDataType.i8, 1)
194+
}
189195
}
190196

191197
object LayerViewConfiguration {

webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/WKWBucketStreamSink.scala

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,9 @@ package com.scalableminds.webknossos.tracingstore.tracings.volume
22

33
import com.scalableminds.util.geometry.Vec3Int
44
import com.scalableminds.util.io.{NamedFunctionStream, NamedStream}
5-
import com.scalableminds.webknossos.datastore.dataformats.wkw.{
6-
ChunkType,
7-
VoxelType,
8-
WKWDataFormatHelper,
9-
WKWFile,
10-
WKWHeader
11-
}
5+
import com.scalableminds.webknossos.datastore.dataformats.wkw.{ChunkType, WKWDataFormatHelper, WKWFile, WKWHeader}
126
import com.scalableminds.webknossos.datastore.models.BucketPosition
13-
import com.scalableminds.webknossos.datastore.models.datasource.DataLayer
7+
import com.scalableminds.webknossos.datastore.models.datasource.{DataLayer, ElementClass}
148
import com.scalableminds.util.tools.{ByteUtils, Fox, FoxImplicits}
159

1610
import java.io.DataOutputStream
@@ -24,8 +18,8 @@ class WKWBucketStreamSink(val layer: DataLayer, tracingHasFallbackLayer: Boolean
2418

2519
def apply(bucketStream: Iterator[(BucketPosition, Array[Byte])], mags: Seq[Vec3Int])(
2620
implicit ec: ExecutionContext): Iterator[NamedStream] = {
27-
val (voxelType, numChannels) = VoxelType.fromElementClass(layer.elementClass)
28-
val header = WKWHeader(1, DataLayer.bucketLength, ChunkType.LZ4, voxelType, numChannels)
21+
val (dataType, numChannels) = ElementClass.toArrayDataTypeAndChannel(layer.elementClass)
22+
val header = WKWHeader(1, DataLayer.bucketLength, ChunkType.LZ4, dataType, numChannels)
2923
bucketStream.flatMap {
3024
case (bucket, data) =>
3125
val skipBucket = if (tracingHasFallbackLayer) isRevertedElement(data) else isAllZero(data)

0 commit comments

Comments
 (0)