Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Added the option for "Selective Segment Visibility" for segmentation layers. Select this option in the left sidebar to only show segments that are currently active or hovered. [#8281](https://github.com/scalableminds/webknossos/pull/8281)
- A segment can be activated with doubleclick now. [#8281](https://github.com/scalableminds/webknossos/pull/8281)
- It is now possible to select the magnification of the layers on which an AI model will be trained. [#8266](https://github.com/scalableminds/webknossos/pull/8266)
- Added support for translations in OME NGFF zarr datasets (translation within coordinateTransformations on datasets). [#8311](https://github.com/scalableminds/webknossos/pull/8311)

### Changed
- Renamed "resolution" to "magnification" in more places within the codebase, including local variables. [#8168](https://github.com/scalableminds/webknossos/pull/8168)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ object NgffMetadata {
mag =>
NgffDataset(
path = mag.toMagLiteral(allowScalar = true),
List(NgffCoordinateTransformation(
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList)))
List(
NgffCoordinateTransformation(
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList),
translation = Some(List[Double](0.0, 0.0, 0.0))
))
))
val lengthUnitStr = dataSourceVoxelSize.unit.toString
val axes = List(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ object NgffMetadataV0_5 {
mag =>
NgffDataset(
path = mag.toMagLiteral(allowScalar = true),
List(NgffCoordinateTransformation(
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList)))
List(
NgffCoordinateTransformation(
scale = Some(List[Double](1.0) ++ (dataSourceVoxelSize.factor * Vec3Double(mag)).toList),
translation = Some(List[Double](0.0, 0.0, 0.0))))
))
val lengthUnitStr = dataSourceVoxelSize.unit.toString
val axes = List(NgffAxis(name = "c", `type` = "channel")) ++ additionalAxes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import com.scalableminds.webknossos.datastore.models.{LengthUnit, VoxelSize}
import net.liftweb.common.{Box, Failure, Full}
import play.api.libs.json.{Json, OFormat}

case class NgffCoordinateTransformation(`type`: String = "scale", scale: Option[List[Double]])
case class NgffCoordinateTransformation(`type`: String = "scale",
scale: Option[List[Double]],
translation: Option[List[Double]])

object NgffCoordinateTransformation {
implicit val jsonFormat: OFormat[NgffCoordinateTransformation] = Json.format[NgffCoordinateTransformation]
Expand All @@ -17,17 +19,19 @@ object NgffDataset {
implicit val jsonFormat: OFormat[NgffDataset] = Json.format[NgffDataset]
}

case class NgffAxis(name: String, `type`: String, unit: Option[String] = None) {
case class NgffAxis(name: String, `type`: String, unit: Option[String] = None, units: Option[String] = None) {

def lengthUnit: Box[models.LengthUnit.Value] =
def lengthUnit: Box[models.LengthUnit.Value] = {
val u = units.orElse(unit)
if (`type` != "space")
Failure(f"Could not convert NGFF unit $name of type ${`type`} to LengthUnit")
else {
unit match {
u match {
case None | Some("") => Full(VoxelSize.DEFAULT_UNIT)
case Some(someUnit) => LengthUnit.fromString(someUnit)
}
}
}
}

object NgffAxis {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ trait ExploreLayerUtils extends FoxImplicits {
preferredVoxelSize: Option[VoxelSize],
voxelSize: VoxelSize): List[DataLayerWithMagLocators] =
layers.map(l => {
val coordinateTransformations = coordinateTransformationForVoxelSize(voxelSize, preferredVoxelSize)
l.mapped(coordinateTransformations = coordinateTransformations)
val generatedCoordinateTransformation = coordinateTransformationForVoxelSize(voxelSize, preferredVoxelSize)
l.mapped(coordinateTransformations = generatedCoordinateTransformation.orElse(l.coordinateTransformations))
})

private def isPowerOfTwo(x: Int): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,12 @@ import com.scalableminds.util.image.Color
import com.scalableminds.util.tools.TextUtils.normalizeStrong
import com.scalableminds.util.tools.{Fox, FoxImplicits, TextUtils}
import com.scalableminds.webknossos.datastore.datareaders.AxisOrder
import com.scalableminds.webknossos.datastore.datareaders.zarr.{
NgffAxis,
NgffChannelWindow,
NgffCoordinateTransformation,
NgffDataset,
NgffLabelsGroup,
NgffMultiscalesItem,
NgffMultiscalesItemV0_5,
NgffOmeroMetadata
}
import com.scalableminds.webknossos.datastore.datareaders.zarr.{NgffAxis, NgffChannelWindow, NgffCoordinateTransformation, NgffDataset, NgffLabelsGroup, NgffMultiscalesItem, NgffMultiscalesItemV0_5, NgffOmeroMetadata}
import com.scalableminds.webknossos.datastore.datavault.VaultPath
import com.scalableminds.webknossos.datastore.models.{LengthUnit, VoxelSize}
import com.scalableminds.webknossos.datastore.models.LengthUnit.LengthUnit
import com.scalableminds.webknossos.datastore.models.datasource.LayerViewConfiguration.LayerViewConfiguration
import com.scalableminds.webknossos.datastore.models.datasource.{
AdditionalAxis,
DataLayerWithMagLocators,
ElementClass,
LayerViewConfiguration
}
import com.scalableminds.webknossos.datastore.models.datasource.{AdditionalAxis, CoordinateTransformation, CoordinateTransformationType, DataLayerWithMagLocators, ElementClass, LayerViewConfiguration}
import net.liftweb.common.Box
import play.api.libs.json.{JsArray, JsBoolean, JsNumber, Json}

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

protected def getTranslation(multiscale: NgffMultiscalesItem): Option[List[CoordinateTransformation]] = {
val is2d = !multiscale.axes.exists(_.name == "z")
val baseTranslation = if (is2d) List(1.0, 1.0) else List(1.0, 1.0, 1.0)
if (!multiscale.datasets.head.coordinateTransformations.exists(_.`type` == "translation")) {
None
} else {
var translation = multiscale.datasets.head.coordinateTransformations.foldLeft(baseTranslation)((acc, ct) => {
ct.`type` match {
case "translation" =>
ct.translation match {
case Some(translationList) =>
acc.zipWithIndex.map { case (v, i) => v * translationList(translationList.length - 1 - i) }
case _ => acc
}
case _ =>
ct.scale match {
case Some(scaleList) => acc.zipWithIndex.map { case (v, i) => v / scaleList(scaleList.length - 1 - i) }
case _ => acc
}
}
})
if (is2d) {
translation = translation :+ 0.0
}
val xTranslation = translation(0)
val yTranslation = translation(1)
val zTranslation = translation(2)
val coordinateTransformation = CoordinateTransformation(
`type` = CoordinateTransformationType.affine,
matrix = Some(
List(List(1, 0, 0, xTranslation), List(0, 1, 0, yTranslation), List(0, 0, 1, zTranslation), List(0, 0, 0, 1)))
)
Some(List(coordinateTransformation))
}
}

protected def layersForLabel(remotePath: VaultPath,
labelPath: String,
credentialId: Option[String]): Fox[List[(DataLayerWithMagLocators, VoxelSize)]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
for {
zattrsPath <- Fox.successful(remotePath / NgffMetadata.FILENAME_DOT_ZATTRS)
ngffHeader <- zattrsPath.parseAsJson[NgffMetadata] ?~> s"Failed to read OME NGFF header at $zattrsPath"
labelLayers <- exploreLabelLayers(remotePath, credentialId).orElse(
Fox.successful(List[(DataLayerWithMagLocators, VoxelSize)]()))
//labelLayers <- exploreLabelLayers(remotePath, credentialId).orElse(
// Fox.successful(List[(DataLayerWithMagLocators, VoxelSize)]()))

layerLists: List[List[(DataLayerWithMagLocators, VoxelSize)]] <- Fox.serialCombined(ngffHeader.multiscales)(
multiscale => {
Expand All @@ -34,7 +34,7 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
} yield layers
})
layers: List[(DataLayerWithMagLocators, VoxelSize)] = layerLists.flatten
} yield layers ++ labelLayers
} yield layers //++ labelLayers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a reminder to re-add this before merging


protected def createLayer(remotePath: VaultPath,
credentialId: Option[String],
Expand All @@ -58,6 +58,7 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
channelIndex)
boundingBox = boundingBoxFromMags(magsWithAttributes)
additionalAxes <- getAdditionalAxes(multiscale, remotePath)
translationOpt = getTranslation(multiscale)
layer: ZarrLayer = if (looksLikeSegmentationLayer(datasetName, elementClass) || isSegmentation) {
ZarrSegmentationLayer(
channelName,
Expand All @@ -67,6 +68,7 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
largestSegmentId = None,
additionalAxes = Some(additionalAxes),
defaultViewConfiguration = Some(viewConfig),
coordinateTransformations = translationOpt,
dataFormat = DataFormat.zarr
)
} else
Expand All @@ -78,6 +80,7 @@ class NgffV0_4Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
magsWithAttributes.map(_.mag),
additionalAxes = Some(additionalAxes),
defaultViewConfiguration = Some(viewConfig),
coordinateTransformations = translationOpt,
dataFormat = DataFormat.zarr
)
} yield layer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class NgffV0_5Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp

boundingBox = boundingBoxFromMags(magsWithAttributes)
additionalAxes <- getAdditionalAxes(multiscale, remotePath)
translationOpt = getTranslation(multiscale)
layer: Zarr3Layer = if (looksLikeSegmentationLayer(datasetName, elementClass) || isSegmentation) {
Zarr3SegmentationLayer(
channelName,
Expand All @@ -70,6 +71,7 @@ class NgffV0_5Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
largestSegmentId = None,
additionalAxes = Some(additionalAxes),
defaultViewConfiguration = Some(viewConfig),
coordinateTransformations = translationOpt,
)
} else
Zarr3DataLayer(
Expand All @@ -80,6 +82,7 @@ class NgffV0_5Explorer(implicit val ec: ExecutionContext) extends RemoteLayerExp
magsWithAttributes.map(_.mag),
additionalAxes = Some(additionalAxes),
defaultViewConfiguration = Some(viewConfig),
coordinateTransformations = translationOpt,
)
} yield layer

Expand Down