Skip to content

Commit 58620ec

Browse files
committed
Support ngff coordinateTransformations translation property
1 parent 5bd3ec4 commit 58620ec

File tree

7 files changed

+58
-23
lines changed

7 files changed

+58
-23
lines changed

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr/NgffMetadata.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@ object NgffMetadata {
3636
mag =>
3737
NgffDataset(
3838
path = mag.toMagLiteral(allowScalar = true),
39-
List(NgffCoordinateTransformation(
40-
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList)))
39+
List(
40+
NgffCoordinateTransformation(
41+
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList),
42+
translation = Some(List[Double](0.0, 0.0, 0.0))
43+
))
4144
))
4245
val lengthUnitStr = dataSourceVoxelSize.unit.toString
4346
val axes = List(

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr/NgffMetadataV0_5.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ object NgffMetadataV0_5 {
4444
mag =>
4545
NgffDataset(
4646
path = mag.toMagLiteral(allowScalar = true),
47-
List(NgffCoordinateTransformation(
48-
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList)))
47+
List(
48+
NgffCoordinateTransformation(
49+
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList),
50+
translation = Some(List[Double](0.0, 0.0, 0.0))))
4951
))
5052
val lengthUnitStr = dataSourceVoxelSize.unit.toString
5153
val axes = List(NgffAxis(name = "c", `type` = "channel")) ++ additionalAxes

webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr/SharedNgffMetadataAttributes.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import com.scalableminds.webknossos.datastore.models.{LengthUnit, VoxelSize}
55
import net.liftweb.common.{Box, Failure, Full}
66
import play.api.libs.json.{Json, OFormat}
77

8-
case class NgffCoordinateTransformation(`type`: String = "scale", scale: Option[List[Double]])
8+
case class NgffCoordinateTransformation(`type`: String = "scale",
9+
scale: Option[List[Double]],
10+
translation: Option[List[Double]])
911

1012
object NgffCoordinateTransformation {
1113
implicit val jsonFormat: OFormat[NgffCoordinateTransformation] = Json.format[NgffCoordinateTransformation]

webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/ExploreLayerUtils.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ trait ExploreLayerUtils extends FoxImplicits {
5050
preferredVoxelSize: Option[VoxelSize],
5151
voxelSize: VoxelSize): List[DataLayerWithMagLocators] =
5252
layers.map(l => {
53-
val coordinateTransformations = coordinateTransformationForVoxelSize(voxelSize, preferredVoxelSize)
54-
l.mapped(coordinateTransformations = coordinateTransformations)
53+
val generatedCoordinateTransformation = coordinateTransformationForVoxelSize(voxelSize, preferredVoxelSize)
54+
l.mapped(coordinateTransformations = generatedCoordinateTransformation.orElse(l.coordinateTransformations))
5555
})
5656

5757
private def isPowerOfTwo(x: Int): Boolean =

webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/NgffExplorationUtils.scala

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,12 @@ import com.scalableminds.util.image.Color
55
import com.scalableminds.util.tools.TextUtils.normalizeStrong
66
import com.scalableminds.util.tools.{Fox, FoxImplicits, TextUtils}
77
import com.scalableminds.webknossos.datastore.datareaders.AxisOrder
8-
import com.scalableminds.webknossos.datastore.datareaders.zarr.{
9-
NgffAxis,
10-
NgffChannelWindow,
11-
NgffCoordinateTransformation,
12-
NgffDataset,
13-
NgffLabelsGroup,
14-
NgffMultiscalesItem,
15-
NgffMultiscalesItemV0_5,
16-
NgffOmeroMetadata
17-
}
8+
import com.scalableminds.webknossos.datastore.datareaders.zarr.{NgffAxis, NgffChannelWindow, NgffCoordinateTransformation, NgffDataset, NgffLabelsGroup, NgffMultiscalesItem, NgffMultiscalesItemV0_5, NgffOmeroMetadata}
189
import com.scalableminds.webknossos.datastore.datavault.VaultPath
1910
import com.scalableminds.webknossos.datastore.models.{LengthUnit, VoxelSize}
2011
import com.scalableminds.webknossos.datastore.models.LengthUnit.LengthUnit
2112
import com.scalableminds.webknossos.datastore.models.datasource.LayerViewConfiguration.LayerViewConfiguration
22-
import com.scalableminds.webknossos.datastore.models.datasource.{
23-
AdditionalAxis,
24-
DataLayerWithMagLocators,
25-
ElementClass,
26-
LayerViewConfiguration
27-
}
13+
import com.scalableminds.webknossos.datastore.models.datasource.{AdditionalAxis, CoordinateTransformation, CoordinateTransformationType, DataLayerWithMagLocators, ElementClass, LayerViewConfiguration}
2814
import net.liftweb.common.Box
2915
import play.api.libs.json.{JsArray, JsBoolean, JsNumber, Json}
3016

@@ -259,6 +245,42 @@ trait NgffExplorationUtils extends FoxImplicits {
259245
layerAndVoxelSizeTuples = layers.map((_, VoxelSize(voxelSizeFactor, unifiedAxisUnit)))
260246
} yield layerAndVoxelSizeTuples
261247

248+
protected def getTranslation(multiscale: NgffMultiscalesItem): Option[List[CoordinateTransformation]] = {
249+
val is2d = !multiscale.axes.exists(_.name == "z")
250+
val baseTranslation = if (is2d) List(1.0, 1.0) else List(1.0, 1.0, 1.0)
251+
if (!multiscale.datasets.head.coordinateTransformations.exists(_.`type` == "translation")) {
252+
None
253+
} else {
254+
var translation = multiscale.datasets.head.coordinateTransformations.foldLeft(baseTranslation)((acc, ct) => {
255+
ct.`type` match {
256+
case "translation" =>
257+
ct.translation match {
258+
case Some(translationList) =>
259+
acc.zipWithIndex.map { case (v, i) => v * translationList(translationList.length - 1 - i) }
260+
case _ => acc
261+
}
262+
case _ =>
263+
ct.scale match {
264+
case Some(scaleList) => acc.zipWithIndex.map { case (v, i) => v / scaleList(scaleList.length - 1 - i) }
265+
case _ => acc
266+
}
267+
}
268+
})
269+
if (is2d) {
270+
translation = translation :+ 0.0
271+
}
272+
val xTranslation = translation(0)
273+
val yTranslation = translation(1)
274+
val zTranslation = translation(2)
275+
val coordinateTransformation = CoordinateTransformation(
276+
`type` = CoordinateTransformationType.affine,
277+
matrix = Some(
278+
List(List(1, 0, 0, xTranslation), List(0, 1, 0, yTranslation), List(0, 0, 1, zTranslation), List(0, 0, 0, 1)))
279+
)
280+
Some(List(coordinateTransformation))
281+
}
282+
}
283+
262284
protected def layersForLabel(remotePath: VaultPath,
263285
labelPath: String,
264286
credentialId: Option[String]): Fox[List[(DataLayerWithMagLocators, VoxelSize)]]

webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/NgffV0_4Explorer.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
5858
channelIndex)
5959
boundingBox = boundingBoxFromMags(magsWithAttributes)
6060
additionalAxes <- getAdditionalAxes(multiscale, remotePath)
61+
translationOpt = getTranslation(multiscale)
6162
layer: ZarrLayer = if (looksLikeSegmentationLayer(datasetName, elementClass) || isSegmentation) {
6263
ZarrSegmentationLayer(
6364
channelName,
@@ -67,6 +68,7 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
6768
largestSegmentId = None,
6869
additionalAxes = Some(additionalAxes),
6970
defaultViewConfiguration = Some(viewConfig),
71+
coordinateTransformations = translationOpt,
7072
dataFormat = DataFormat.zarr
7173
)
7274
} else
@@ -78,6 +80,7 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
7880
magsWithAttributes.map(_.mag),
7981
additionalAxes = Some(additionalAxes),
8082
defaultViewConfiguration = Some(viewConfig),
83+
coordinateTransformations = translationOpt,
8184
dataFormat = DataFormat.zarr
8285
)
8386
} yield layer

webknossos-datastore/app/com/scalableminds/webknossos/datastore/explore/NgffV0_5Explorer.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class NgffV0_5Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
6161

6262
boundingBox = boundingBoxFromMags(magsWithAttributes)
6363
additionalAxes <- getAdditionalAxes(multiscale, remotePath)
64+
translationOpt = getTranslation(multiscale)
6465
layer: Zarr3Layer = if (looksLikeSegmentationLayer(datasetName, elementClass) || isSegmentation) {
6566
Zarr3SegmentationLayer(
6667
channelName,
@@ -70,6 +71,7 @@ class NgffV0_5Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
7071
largestSegmentId = None,
7172
additionalAxes = Some(additionalAxes),
7273
defaultViewConfiguration = Some(viewConfig),
74+
coordinateTransformations = translationOpt,
7375
)
7476
} else
7577
Zarr3DataLayer(
@@ -80,6 +82,7 @@ class NgffV0_5Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
8082
magsWithAttributes.map(_.mag),
8183
additionalAxes = Some(additionalAxes),
8284
defaultViewConfiguration = Some(viewConfig),
85+
coordinateTransformations = translationOpt,
8386
)
8487
} yield layer
8588

0 commit comments

Comments
 (0)