Skip to content

Commit

Permalink
Communicate truncated 64 bit segmentation (#4598)
Browse files Browse the repository at this point in the history
* add warning to user if 64-segmentation is active
* not working as intended as the segmentation is 32 bit for the frontend

* add optional originalElementClass field, which is filled for uint64 segmentation layer

* use original element class to determine whether the segmentation is uint64

* add changelog entry

* remove original element class and always send real element class to frontend

* fix typo

* add check so volumes can not be created for uint64 fallback layer

* added manually saving of the originalElementClass in the frontend

* update error message

* Update frontend/javascripts/messages.js

Co-authored-by: Daniel <[email protected]>

* make comments more concise and remove unnecessary changes

* Change "Show debug information" in toast to "Show more information"

Co-authored-by: Michael Büßemeyer <[email protected]>
Co-authored-by: MichaelBuessemeyer <[email protected]>
Co-authored-by: Daniel <[email protected]>
  • Loading branch information
4 people authored May 27, 2020
1 parent b0e90f2 commit 9bb14a6
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 37 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.released.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Fixed a rendering error which could make some layers disappear in certain circumstances. [#4556](https://github.com/scalableminds/webknossos/pull/4556)
- Fixed a rendering error which caused negative float data to be rendered white. [#4556](https://github.com/scalableminds/webknossos/pull/4571)
- Fixed the histogram creation if some sampled positions don't contain data. [#4584](https://github.com/scalableminds/webknossos/pull/4584)
- Fixed a rendering exception which could occur in rare circumstandes. [#4588](https://github.com/scalableminds/webknossos/pull/4588)
- Fixed a rendering exception which could occur in rare circumstances. [#4588](https://github.com/scalableminds/webknossos/pull/4588)

## [20.04.0](https://github.com/scalableminds/webknossos/releases/tag/20.04.0) - 2020-03-23
[Commits](https://github.com/scalableminds/webknossos/compare/20.03.0...20.04.0)
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released

### Added

-
- Added a warning to the segmentation tab when viewing `uint64` bit segmentation data. [#4598](https://github.com/scalableminds/webknossos/pull/4598)

### Changed

-

### Fixed
Expand Down
50 changes: 33 additions & 17 deletions app/models/annotation/AnnotationService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.scalableminds.util.tools.{BoxImplicits, Fox, FoxImplicits, TextUtils}
import com.scalableminds.webknossos.tracingstore.SkeletonTracing._
import com.scalableminds.webknossos.tracingstore.VolumeTracing.{VolumeTracing, VolumeTracingOpt, VolumeTracings}
import com.scalableminds.webknossos.datastore.models.datasource.{
ElementClass,
DataSourceLike => DataSource,
SegmentationLayerLike => SegmentationLayer
}
Expand Down Expand Up @@ -102,18 +103,11 @@ class AnnotationService @Inject()(annotationInformationProvider: AnnotationInfor

private def createVolumeTracing(
dataSource: DataSource,
withFallback: Boolean,
fallbackLayer: Option[SegmentationLayer],
boundingBox: Option[BoundingBox] = None,
startPosition: Option[Point3D] = None,
startRotation: Option[Vector3D] = None
): VolumeTracing = {
val fallbackLayer: Option[SegmentationLayer] = if (withFallback) {
dataSource.dataLayers.flatMap {
case layer: SegmentationLayer => Some(layer)
case _ => None
}.headOption
} else None

): VolumeTracing =
VolumeTracing(
None,
boundingBoxToProto(boundingBox.getOrElse(dataSource.boundingBox)),
Expand All @@ -127,14 +121,21 @@ class AnnotationService @Inject()(annotationInformationProvider: AnnotationInfor
0,
VolumeTracingDefaults.zoomLevel
)
}

def createTracings(
dataSet: DataSet,
dataSource: DataSource,
tracingType: TracingType.Value,
withFallback: Boolean,
oldTracingId: Option[String] = None)(implicit ctx: DBAccessContext): Fox[(Option[String], Option[String])] =
oldTracingId: Option[String] = None)(implicit ctx: DBAccessContext): Fox[(Option[String], Option[String])] = {
def getFallbackLayer(): Option[SegmentationLayer] =
if (withFallback) {
dataSource.dataLayers.flatMap {
case layer: SegmentationLayer => Some(layer)
case _ => None
}.headOption
} else None

tracingType match {
case TracingType.skeleton =>
for {
Expand All @@ -148,23 +149,29 @@ class AnnotationService @Inject()(annotationInformationProvider: AnnotationInfor
case TracingType.volume =>
for {
client <- tracingStoreService.clientFor(dataSet)
volumeTracingId <- client.saveVolumeTracing(createVolumeTracing(dataSource, withFallback))
fallbackLayer = getFallbackLayer()
_ <- bool2Fox(fallbackLayer.forall(_.elementClass != ElementClass.uint64)) ?~> "annotation.volume.uint64"
volumeTracingId <- client.saveVolumeTracing(createVolumeTracing(dataSource, fallbackLayer))
} yield (None, Some(volumeTracingId))
case TracingType.hybrid =>
for {
client <- tracingStoreService.clientFor(dataSet)
fallbackLayer = getFallbackLayer()
_ <- bool2Fox(fallbackLayer.forall(_.elementClass != ElementClass.uint64)) ?~> "annotation.volume.uint64"
skeletonTracingId <- client.saveSkeletonTracing(
SkeletonTracingDefaults.createInstance.copy(dataSetName = dataSet.name, editPosition = dataSource.center))
volumeTracingId <- client.saveVolumeTracing(createVolumeTracing(dataSource, withFallback))
volumeTracingId <- client.saveVolumeTracing(createVolumeTracing(dataSource, fallbackLayer))
} yield (Some(skeletonTracingId), Some(volumeTracingId))
}
}

def createExplorationalFor(user: User, _dataSet: ObjectId, tracingType: TracingType.Value, withFallback: Boolean)(
implicit ctx: DBAccessContext): Fox[Annotation] =
implicit ctx: DBAccessContext,
m: MessagesProvider): Fox[Annotation] =
for {
dataSet <- dataSetDAO.findOne(_dataSet)
dataSet <- dataSetDAO.findOne(_dataSet) ?~> "dataSet.noAccessById"
dataSource <- dataSetService.dataSourceFor(dataSet)
usableDataSource <- dataSource.toUsable ?~> "DataSet is not imported."
usableDataSource <- dataSource.toUsable ?~> Messages("dataSet.notImported", dataSource.id.name)
tracingIds <- createTracings(dataSet, usableDataSource, tracingType, withFallback)
teamId <- selectSuitableTeam(user, dataSet)
annotation = Annotation(
Expand Down Expand Up @@ -338,9 +345,18 @@ class AnnotationService @Inject()(annotationInformationProvider: AnnotationInfor
dataSet <- dataSetDAO.findOneByNameAndOrganization(dataSetName, organizationId) ?~> Messages("dataset.notFound",
dataSetName)
dataSource <- dataSetService.dataSourceFor(dataSet).flatMap(_.toUsable)

fallbackLayer = if (volumeShowFallbackLayer) {
dataSource.dataLayers.flatMap {
case layer: SegmentationLayer => Some(layer)
case _ => None
}.headOption
} else None
_ <- bool2Fox(fallbackLayer.forall(_.elementClass != ElementClass.uint64)) ?~> "annotation.volume.uint64"

volumeTracing = createVolumeTracing(
dataSource,
withFallback = volumeShowFallbackLayer,
fallbackLayer = fallbackLayer,
boundingBox = boundingBox.flatMap { box =>
if (box.isEmpty) None else Some(box)
},
Expand Down
15 changes: 1 addition & 14 deletions app/models/binary/DataSetService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,12 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO,
lastUsedByUser <- lastUsedTimeFor(dataSet._id, requestingUserOpt)
dataStoreJs <- dataStoreService.publicWrites(dataStore)
dataSource <- dataSourceFor(dataSet, Some(organization), skipResolutions)
dataSourceWith64BitSupport = dataSource.toUsable.map(replaceUint64Layers).getOrElse(dataSource)
publicationOpt <- Fox.runOptional(dataSet._publication)(publicationDAO.findOne(_))
publicationJson <- Fox.runOptional(publicationOpt)(publicationService.publicWrites)
} yield {
Json.obj(
"name" -> dataSet.name,
"dataSource" -> dataSourceWith64BitSupport,
"dataSource" -> dataSource,
"dataStore" -> dataStoreJs,
"owningOrganization" -> organization.name,
"allowedTeams" -> teamsJs,
Expand All @@ -346,16 +345,4 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO,
"isForeign" -> dataStore.isForeign
)
}

private def replaceUint64Layers(dataSource: GenericDataSource[DataLayer]) = {
val newLayers = dataSource.dataLayers.map {
case l: WKWSegmentationLayer if l.elementClass == ElementClass.uint64 =>
l.copy(elementClass = ElementClass.uint32)
case l: AbstractSegmentationLayer if l.elementClass == ElementClass.uint64 =>
l.copy(elementClass = ElementClass.uint32)
case l => l
}

dataSource.copy(dataLayers = newLayers)
}
}
4 changes: 3 additions & 1 deletion conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ dataSet.notFound=Dataset {0} does not exist or could not be accessed
dataSet.notFoundConsiderLogin=Dataset {0} does not exist or could not be accessed. You may need to log in.
dataSet.notFoundForAnnotation=The Dataset for this annotation does not exist or could not be accessed.
dataSet.noAccess=Could not access DataSet {0}. Does your team have access?
dataSet.noAccessById=Could not access the corresponding DataSet. This is likely because you are not a member of a team that has access to it.
dataSet.notImported=Dataset {0} is not imported
dataSet.name.invalid=A dataset name can only contain letters, digits and underscores
dataSet.import.impossible.name=Import impossible. Dataset name can only consist of a-Z, 0-9, "-" and "_".
Expand Down Expand Up @@ -183,7 +184,8 @@ role.added=Added role to {0}
tracing=Annotation
tracing.notFound=Tracing couldn’t be found

annotation.create.failed=Failed to create annotation. This is likely because you are not a member of a team that has access to the dataset.
annotation.create.failed=Failed to create annotation.
annotation.volume.uint64=Creating volume tracings on a uint64 fallback layer is not allowed.
annotation.notFound=Annotation couldn’t be found
annotation.notFound.considerLoggingIn=Annotation couldn’t be found. If the annotation is not public, you need to log in to see it.
annotation.invalid=Invalid annotation
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/admin/api_flow_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type APISegmentationLayer = {|
...APIDataLayerBase,
+category: "segmentation",
+largestSegmentId: number,
+originalElementClass?: ElementClass,
+mappings?: Array<string>,
+agglomerates?: Array<string>,
+fallbackLayer?: ?string,
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/libs/toast.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const Toast = {
style={{ background: "transparent", marginLeft: -16 }}
>
<Panel
header="Show debug information"
header="Show more information"
style={{ background: "transparent", border: 0, fontSize: 10 }}
>
{errorChain}
Expand Down
2 changes: 2 additions & 0 deletions frontend/javascripts/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ instead. Only enable this option if you understand its effect. All layers will n
"You didn't add a node after jumping to this branchpoint, do you really want to jump again?",
"tracing.segmentation_zoom_warning":
"Segmentation data and volume annotation is only fully supported at a smaller zoom level.",
"tracing.uint64_segmentation_warning":
"This is an unsigned 64-bit segmentation. The displayed ids are truncated to 32-bit. Thus, they might not match the ids on the server.",
"tracing.segmentation_zoom_warning_agglomerate":
"Segmentation data which is mapped using an agglomerate file cannot be rendered in this magnification. Please zoom in further.",
"tracing.no_access": "You are not allowed to access this annotation.",
Expand Down
18 changes: 18 additions & 0 deletions frontend/javascripts/oxalis/model_initialization.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,24 @@ function initializeDataset(
dataSet: dataset.dataSource.id.name,
});

// Add the originalElementClass property to the segmentation layer if it exists.
// Also set the elementClass to uint32 because uint64 segmentation data is truncated to uint32 by the backend.
const updatedDataLayers = dataset.dataSource.dataLayers.map(dataLayer => {
const { elementClass } = dataLayer;
if (dataLayer.category === "segmentation") {
const adjustedElementClass = elementClass === "uint64" ? "uint32" : elementClass;
return {
...dataLayer,
originalElementClass: elementClass,
elementClass: adjustedElementClass,
};
} else {
return dataLayer;
}
});
// $FlowFixMe assigning the adjusted dataset layers, although this property is not writable.
dataset.dataSource.dataLayers = updatedDataLayers;

serverTracingAsVolumeTracingMaybe(tracing).map(volumeTracing => {
const newDataLayers = setupLayerForVolumeTracing(dataset, volumeTracing);
// $FlowFixMe We mutate the dataset here to avoid that an outdated version is used somewhere else
Expand Down
20 changes: 18 additions & 2 deletions frontend/javascripts/oxalis/view/right-menu/mapping_info_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,28 @@ class MappingInfoView extends React.Component<Props, State> {
title,
dataIndex,
});
const showSegmentation64bitWarning =
segmentationLayer && segmentationLayer.originalElementClass === "uint64";
const maybeWithTooltipWarningTitle = title =>
showSegmentation64bitWarning ? (
<React.Fragment>
{title}{" "}
<Tooltip title={message["tracing.uint64_segmentation_warning"]}>
<Icon type="warning" style={{ color: "rgb(255, 155, 85)" }} />
</Tooltip>
</React.Fragment>
) : (
title
);
const idColumns =
hasMapping && this.props.isMappingEnabled
? // Show an unmapped and mapped id column if there's a mapping
[columnHelper("Unmapped", "unmapped"), columnHelper("Mapped", "mapped")]
[
columnHelper(maybeWithTooltipWarningTitle("Unmapped"), "unmapped"),
columnHelper(maybeWithTooltipWarningTitle("Mapped"), "mapped"),
]
: // Otherwise, only show an ID column
[columnHelper("ID", "unmapped")];
[columnHelper(maybeWithTooltipWarningTitle("ID"), "unmapped")];
const columns = [
columnHelper("", "name"),
...idColumns,
Expand Down

0 comments on commit 9bb14a6

Please sign in to comment.