Skip to content

Commit d30f8d8

Browse files
Revert "In Data Loading, Clip to Layer BoundingBox (#8551)" (#8572)
This reverts commit 7275b8b.
1 parent 0acb96f commit d30f8d8

File tree

6 files changed

+10
-61
lines changed

6 files changed

+10
-61
lines changed

CHANGELOG.unreleased.md

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
1919
- Adjusted the names of custom model inference jobs and train model jobs to match the worker's naming. [#8524](https://github.com/scalableminds/webknossos/pull/8524)
2020
- Updated screenshot tests to use `vitest` framework instead of `ava`. [#8553](https://github.com/scalableminds/webknossos/pull/8553)
2121
- The mapping dropdown for segmentation is wider now so that mapping names are fully readable. [#8570](https://github.com/scalableminds/webknossos/pull/8570)
22-
- When loading data from a data layer that has data stored beyond the bounding box specified in the datasource-properties.json, data outside of the bounding box is now zeroed. (the layer is “clipped”). [#8551](https://github.com/scalableminds/webknossos/pull/8551)
2322

2423
### Fixed
2524
- Fixed a bug in the trees tab where the color change of a tree would affect the tree on which the context menu was previously opened. [#8562](https://github.com/scalableminds/webknossos/pull/8562)

util/src/main/scala/com/scalableminds/util/geometry/BoundingBox.scala

+1-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import play.api.libs.json.{JsObject, Json}
66

77
case class BoundingBox(topLeft: Vec3Int, width: Int, height: Int, depth: Int) {
88

9-
lazy val bottomRight: Vec3Int = topLeft.move(width, height, depth)
9+
val bottomRight: Vec3Int = topLeft.move(width, height, depth)
1010

1111
def intersects(other: BoundingBox): Boolean =
1212
math.max(topLeft.x, other.topLeft.x) < math.min(bottomRight.x, other.bottomRight.x) &&
@@ -45,9 +45,6 @@ case class BoundingBox(topLeft: Vec3Int, width: Int, height: Int, depth: Int) {
4545
BoundingBox(Vec3Int(x, y, z), w, h, d)
4646
}
4747

48-
def isFullyContainedIn(other: BoundingBox): Boolean =
49-
this.intersection(other).contains(this)
50-
5148
def isEmpty: Boolean =
5249
width <= 0 || height <= 0 || depth <= 0
5350

@@ -64,9 +61,6 @@ case class BoundingBox(topLeft: Vec3Int, width: Int, height: Int, depth: Int) {
6461
// Since floorDiv is used for topLeft, ceilDiv is used for the size to avoid voxels being lost at the border
6562
BoundingBox(topLeft / that, ceilDiv(width, that.x), ceilDiv(height, that.y), ceilDiv(depth, that.z))
6663

67-
def move(delta: Vec3Int): BoundingBox =
68-
this.copy(topLeft = this.topLeft + delta)
69-
7064
def toSql: List[Int] =
7165
List(topLeft.x, topLeft.y, topLeft.z, width, height, depth)
7266

util/src/main/scala/com/scalableminds/util/geometry/Vec3Int.scala

+2-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ case class Vec3Int(x: Int, y: Int, z: Int) {
2323
def /(that: Vec3Int): Vec3Int =
2424
Vec3Int(x / that.x, y / that.y, z / that.z)
2525

26-
def unary_- : Vec3Int =
27-
Vec3Int(-x, -y, -z)
28-
2926
def scale(s: Float): Vec3Int =
3027
Vec3Int((x * s).toInt, (y * s).toInt, (z * s).toInt)
3128

@@ -56,6 +53,8 @@ case class Vec3Int(x: Int, y: Int, z: Int) {
5653
def move(other: Vec3Int): Vec3Int =
5754
move(other.x, other.y, other.z)
5855

56+
def negate: Vec3Int = Vec3Int(-x, -y, -z)
57+
5958
def to(bottomRight: Vec3Int): Seq[Vec3Int] =
6059
range(bottomRight, _ to _)
6160

webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/requests/Cuboid.scala

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.scalableminds.webknossos.datastore.models.requests
22

3-
import com.scalableminds.util.geometry.{BoundingBox, Vec3Int}
3+
import com.scalableminds.util.geometry.Vec3Int
44
import com.scalableminds.webknossos.datastore.models.{BucketPosition, VoxelPosition}
55

66
/**
@@ -51,10 +51,4 @@ case class Cuboid(topLeft: VoxelPosition, width: Int, height: Int, depth: Int) {
5151
height * mag.y,
5252
depth * mag.z
5353
)
54-
55-
def toBoundingBoxInMag: BoundingBox =
56-
BoundingBox(Vec3Int(topLeft.voxelXInMag, topLeft.voxelYInMag, topLeft.voxelZInMag), width, height, depth)
57-
58-
def toMag1BoundingBox: BoundingBox =
59-
toMag1.toBoundingBoxInMag
6054
}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/requests/DataServiceRequests.scala

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.scalableminds.webknossos.datastore.models.requests
22

3-
import com.scalableminds.util.geometry.Vec3Int
43
import com.scalableminds.webknossos.datastore.models.{AdditionalCoordinate, BucketPosition}
54
import com.scalableminds.webknossos.datastore.models.datasource.{DataLayer, DataSource, SegmentationLayer}
65

@@ -22,7 +21,6 @@ case class DataServiceDataRequest(
2221
settings: DataServiceRequestSettings
2322
) {
2423
def isSingleBucket: Boolean = cuboid.isSingleBucket(DataLayer.bucketLength)
25-
def mag: Vec3Int = cuboid.mag
2624
}
2725

2826
case class DataReadInstruction(

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

+6-41
Original file line numberDiff line numberDiff line change
@@ -108,52 +108,16 @@ class BinaryDataService(val dataBaseDir: Path,
108108
conversionFunc(inputArray))
109109
else Fox.successful(inputArray)
110110

111-
/*
112-
* Everything outside of the layer bounding box is set to black (zero) so data outside of the specified
113-
* bounding box is not exposed to the user
114-
*/
115-
private def clipToLayerBoundingBox(request: DataServiceDataRequest)(inputArray: Array[Byte]): Box[Array[Byte]] = {
116-
val bytesPerElement = request.dataLayer.bytesPerElement
117-
val requestBboxInMag = request.cuboid.toBoundingBoxInMag
118-
val layerBboxInMag = request.dataLayer.boundingBox / request.mag // Note that this div is implemented to round to the bigger bbox so we don’t lose voxels inside.
119-
val intersectionOpt = requestBboxInMag.intersection(layerBboxInMag).map(_.move(-requestBboxInMag.topLeft))
120-
val outputArray = Array.fill[Byte](inputArray.length)(0)
121-
intersectionOpt.foreach { intersection =>
122-
for {
123-
z <- intersection.topLeft.z until intersection.bottomRight.z
124-
y <- intersection.topLeft.y until intersection.bottomRight.y
125-
// We can bulk copy a row of voxels and do not need to iterate in the x dimension
126-
} {
127-
val offset =
128-
(intersection.topLeft.x +
129-
y * requestBboxInMag.width +
130-
z * requestBboxInMag.width * requestBboxInMag.height) * bytesPerElement
131-
System.arraycopy(inputArray,
132-
offset,
133-
outputArray,
134-
offset,
135-
(intersection.bottomRight.x - intersection.topLeft.x) * bytesPerElement)
136-
}
137-
}
138-
Full(outputArray)
139-
}
140-
141111
private def convertAccordingToRequest(request: DataServiceDataRequest, inputArray: Array[Byte]): Fox[Array[Byte]] =
142112
for {
143-
clippedData <- convertIfNecessary(
144-
!request.cuboid.toMag1BoundingBox.isFullyContainedIn(request.dataLayer.boundingBox),
145-
inputArray,
146-
data => clipToLayerBoundingBox(request)(data).toFox,
147-
request
148-
)
149113
mappedDataFox <- agglomerateServiceOpt.map { agglomerateService =>
150114
convertIfNecessary(
151115
request.settings.appliedAgglomerate.isDefined && request.dataLayer.category == Category.segmentation && request.cuboid.mag.maxDim <= MaxMagForAgglomerateMapping,
152-
clippedData,
116+
inputArray,
153117
data => agglomerateService.applyAgglomerate(request)(data).toFox,
154118
request
155119
)
156-
}.toFox.fillEmpty(Fox.successful(clippedData)) ?~> "Failed to apply agglomerate mapping"
120+
}.toFox.fillEmpty(Fox.successful(inputArray)) ?~> "Failed to apply agglomerate mapping"
157121
mappedData <- mappedDataFox
158122
resultData <- convertIfNecessary(request.settings.halfByte, mappedData, convertToHalfByte, request)
159123
} yield resultData
@@ -204,6 +168,7 @@ class BinaryDataService(val dataBaseDir: Path,
204168
private def cutOutCuboid(request: DataServiceDataRequest, rs: List[(BucketPosition, Array[Byte])]): Array[Byte] = {
205169
val bytesPerElement = request.dataLayer.bytesPerElement
206170
val cuboid = request.cuboid
171+
val subsamplingStrides = Vec3Int.ones
207172

208173
val resultShape = Vec3Int(cuboid.width, cuboid.height, cuboid.depth)
209174
val result = new Array[Byte](cuboid.volume * bytesPerElement)
@@ -229,9 +194,9 @@ class BinaryDataService(val dataBaseDir: Path,
229194
y % bucketLength * bucketLength +
230195
z % bucketLength * bucketLength * bucketLength) * bytesPerElement
231196

232-
val rx = xMin - cuboid.topLeft.voxelXInMag
233-
val ry = y - cuboid.topLeft.voxelYInMag
234-
val rz = z - cuboid.topLeft.voxelZInMag
197+
val rx = (xMin - cuboid.topLeft.voxelXInMag) / subsamplingStrides.x
198+
val ry = (y - cuboid.topLeft.voxelYInMag) / subsamplingStrides.y
199+
val rz = (z - cuboid.topLeft.voxelZInMag) / subsamplingStrides.z
235200

236201
val resultOffset = (rx + ry * resultShape.x + rz * resultShape.x * resultShape.y) * bytesPerElement
237202
System.arraycopy(data, dataOffset, result, resultOffset, (xMax - xMin) * bytesPerElement)

0 commit comments

Comments
 (0)