Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ad-hoc meshing with agglomerates and cumsum.json #7449

Merged
merged 4 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Fixed styling issues with the maintenance banner so that it no longer overlaps other menus, tabs, and buttons. [#7421](https://github.com/scalableminds/webknossos/pull/7421)
- Exploring HTTP uris of unknown hosts no longer causes an exception error message to be displayed. [#7422](https://github.com/scalableminds/webknossos/pull/7422)
- Fixed the initialization of the dark theme if it was active during page load. [#7446](https://github.com/scalableminds/webknossos/pull/7446)
- Fixed a rare bug in ad-hoc meshing for voxelytics-created segmentations with agglomerate mappings. [#7449](https://github.com/scalableminds/webknossos/pull/7449)

### Removed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ case class VoxelPosition(
def move(dx: Int, dy: Int, dz: Int): VoxelPosition =
VoxelPosition(mag1X + dx, mag1Y + dy, mag1Z + dz, mag)

def toMag1: VoxelPosition = this.copy(mag = Vec3Int.ones) // other properties are already in mag1 and do not change.

override def toString = s"($mag1X, $mag1Y, $mag1Z) / $mag"

override def equals(obj: scala.Any): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,11 @@ case class Cuboid(topLeft: VoxelPosition, width: Int, height: Int, depth: Int) {
}

def mag: Vec3Int = topLeft.mag

def toMag1: Cuboid = Cuboid(
topLeft.toMag1,
width * mag.x,
height * mag.y,
depth * mag.z
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class AdHocMeshService(binaryDataService: BinaryDataService,

for {
data <- binaryDataService.handleDataRequest(dataRequest)
agglomerateMappedData <- applyAgglomerate(data).toFox
agglomerateMappedData <- applyAgglomerate(data) ?~> "failed to apply agglomerate for ad-hoc meshing"
typedData = convertData(agglomerateMappedData)
mappedData <- applyMapping(typedData)
mappedSegmentId <- applyMapping(Array(typedSegmentId)).map(_.head)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import java.util

import ch.systemsx.cisd.hdf5.{HDF5DataSet, IHDF5Reader}
import com.scalableminds.util.cache.LRUConcurrentCache
import com.scalableminds.util.geometry.Vec3Int
import com.scalableminds.webknossos.datastore.dataformats.SafeCachable
import com.scalableminds.webknossos.datastore.models.VoxelPosition
import com.scalableminds.webknossos.datastore.models.requests.{Cuboid, DataServiceDataRequest}
import com.typesafe.scalalogging.LazyLogging

Expand Down Expand Up @@ -117,52 +115,42 @@ class BoundingBoxCache(
val boundingBoxFinder: BoundingBoxFinder, // saves the bb top left positions
val maxReaderRange: Long) // config value for maximum amount of elements that are allowed to be read as once
extends LazyLogging {
private def getGlobalCuboid(cuboid: Cuboid): Cuboid = {
val res = cuboid.mag
val tl = cuboid.topLeft
Cuboid(
VoxelPosition(tl.voxelXInMag * res.x, tl.voxelYInMag * res.y, tl.voxelZInMag * res.z, Vec3Int(1, 1, 1)),
cuboid.width * res.x,
cuboid.height * res.y,
cuboid.depth * res.z
)
}

// get the segment ID range for one cuboid
// get the segment id range for one cuboid
private def getReaderRange(request: DataServiceDataRequest): (Long, Long) = {
// convert cuboid to global coordinates (in res 1)
val globalCuboid = getGlobalCuboid(request.cuboid)
val requestedCuboidMag1 = request.cuboid.toMag1

// get min bounds
val initialBoundingBox = boundingBoxFinder.findInitialBoundingBox(globalCuboid)
val initialBoundingBoxTopleft: (Long, Long, Long) = boundingBoxFinder.findInitialBoundingBox(requestedCuboidMag1)

// get max bounds
val requestedCuboid = globalCuboid.bottomRight
val dataLayerBox = request.dataLayer.boundingBox.bottomRight
val requestedCuboidBottomRight = requestedCuboidMag1.bottomRight
val dataLayerBoxBottomRight = request.dataLayer.boundingBox.bottomRight

// use the values of first bb to initialize the range and dimensions
val initialValues = cache(initialBoundingBox)
val initialValues = cache(initialBoundingBoxTopleft)
var range = initialValues.idRange
var currDimensions = initialValues.dimensions

var x = initialBoundingBox._1
var y = initialBoundingBox._2
var z = initialBoundingBox._3
var x = initialBoundingBoxTopleft._1
var y = initialBoundingBoxTopleft._2
var z = initialBoundingBoxTopleft._3

// step through each bb, but save starting coordinates to reset iteration once the outer bound is reached
while (x < requestedCuboid.voxelXInMag && x < dataLayerBox.x) {
while (x < requestedCuboidBottomRight.voxelXInMag && x < dataLayerBoxBottomRight.x) {
val nextBBinX = (x + currDimensions._1, y, z)
while (y < requestedCuboid.voxelYInMag && y < dataLayerBox.y) {
currDimensions = (currDimensions._1, initialValues.dimensions._2, currDimensions._3) // reset currDimensions y to start next loop at beginning
while (y < requestedCuboidBottomRight.voxelYInMag && y < dataLayerBoxBottomRight.y) {
val nextBBinY = (x, y + currDimensions._2, z)
while (z < requestedCuboid.voxelZInMag && z < dataLayerBox.z) {
currDimensions = (currDimensions._1, currDimensions._2, initialValues.dimensions._3) // reset currDimensions z to start next loop at beginning
while (z < requestedCuboidBottomRight.voxelZInMag && z < dataLayerBoxBottomRight.z) {
// get cached values for current bb and update the reader range by extending if necessary
cache.get((x, y, z)).foreach { value =>
range = (Math.min(range._1, value.idRange._1), Math.max(range._2, value.idRange._2))
currDimensions = value.dimensions
}
z = z + currDimensions._3
}
x = nextBBinY._1
y = nextBBinY._2
z = nextBBinY._3
}
Expand All @@ -177,7 +165,7 @@ class BoundingBoxCache(
readHDF: (IHDF5Reader, Long, Long) => Array[Long]): Array[Long] = {
val readerRange = getReaderRange(request)
if (readerRange._2 - readerRange._1 < maxReaderRange) {
val agglomerateIds = readHDF(reader, readerRange._1.toLong, (readerRange._2 - readerRange._1).toLong + 1)
val agglomerateIds = readHDF(reader, readerRange._1, (readerRange._2 - readerRange._1) + 1)
input.map(i => if (i == 0L) 0L else agglomerateIds((i - readerRange._1).toInt))
} else {
// if reader range does not fit in main memory, read agglomerate ids in chunks
Expand All @@ -186,7 +174,7 @@ class BoundingBoxCache(
val isTransformed = Array.fill(input.length)(false)
while (offset <= readerRange._2) {
val agglomerateIds: Array[Long] =
readHDF(reader, offset.toLong, spire.math.min(maxReaderRange, readerRange._2 - offset).toLong + 1)
readHDF(reader, offset, spire.math.min(maxReaderRange, readerRange._2 - offset) + 1)
for (i <- input.indices) {
val inputElement = input(i)
if (!isTransformed(i) && inputElement >= offset && inputElement < offset + maxReaderRange) {
Expand Down