Skip to content

Commit

Permalink
fix statistics bug and rename resolution methods to coarsest and fine…
Browse files Browse the repository at this point in the history
…st (#7355)

* fix statistics bug and rename resolution methods to coarsest and finest

* convert bounding box to mag1

* complete changelog
  • Loading branch information
knollengewaechs authored Sep 26, 2023
1 parent 086980f commit 43d88dd
Show file tree
Hide file tree
Showing 14 changed files with 90 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Changed

### Fixed
- Fixed that segment statistics were requested in the wrong resolution and without properly considering the dataset scale. [#7355](https://github.com/scalableminds/webknossos/pull/7355)

### Removed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ export function RestrictResolutionSlider({
resolutionIndices,
setResolutionIndices,
}: RestrictResolutionSliderProps) {
let highestResolutionIndex = resolutionInfo.getHighestResolutionIndex();
let lowestResolutionIndex = resolutionInfo.getLowestResolutionIndex();
let highestResolutionIndex = resolutionInfo.getCoarsestResolutionIndex();
let lowestResolutionIndex = resolutionInfo.getFinestResolutionIndex();

if (selectedSegmentationLayer != null) {
const datasetFallbackLayerResolutionInfo = getResolutionInfo(
selectedSegmentationLayer.resolutions,
);
highestResolutionIndex = datasetFallbackLayerResolutionInfo.getHighestResolutionIndex();
lowestResolutionIndex = datasetFallbackLayerResolutionInfo.getLowestResolutionIndex();
highestResolutionIndex = datasetFallbackLayerResolutionInfo.getCoarsestResolutionIndex();
lowestResolutionIndex = datasetFallbackLayerResolutionInfo.getFinestResolutionIndex();
}

const highResolutionIndex = Math.min(highestResolutionIndex, resolutionIndices[1]);
Expand Down Expand Up @@ -197,8 +197,8 @@ function CreateExplorativeModal({ datasetId, onClose }: Props) {
selectedSegmentationLayer == null
? getSomeResolutionInfoForDataset(dataset)
: getResolutionInfo(selectedSegmentationLayer.resolutions);
const highestResolutionIndex = resolutionInfo.getHighestResolutionIndex();
const lowestResolutionIndex = resolutionInfo.getLowestResolutionIndex();
const highestResolutionIndex = resolutionInfo.getCoarsestResolutionIndex();
const lowestResolutionIndex = resolutionInfo.getFinestResolutionIndex();

const highResolutionIndex = Math.min(highestResolutionIndex, userDefinedResolutionIndices[1]);
const lowResolutionIndex = Math.max(lowestResolutionIndex, userDefinedResolutionIndices[0]);
Expand Down
6 changes: 3 additions & 3 deletions frontend/javascripts/oxalis/api/api_latest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1632,7 +1632,7 @@ class DataApi {
} else {
const layer = getLayerByName(Store.getState().dataset, layerName);
const resolutionInfo = getResolutionInfo(layer.resolutions);
zoomStep = resolutionInfo.getLowestResolutionIndex();
zoomStep = resolutionInfo.getFinestResolutionIndex();
}

const cube = this.model.getCubeByLayerName(layerName);
Expand Down Expand Up @@ -1694,7 +1694,7 @@ class DataApi {
if (_zoomStep != null) {
zoomStep = _zoomStep;
} else {
zoomStep = resolutionInfo.getLowestResolutionIndex();
zoomStep = resolutionInfo.getFinestResolutionIndex();
}

const resolutions = resolutionInfo.getDenseResolutions();
Expand Down Expand Up @@ -1896,7 +1896,7 @@ class DataApi {
): string {
const { dataset } = Store.getState();
const resolutionInfo = getResolutionInfo(getLayerByName(dataset, layerName, true).resolutions);
resolution = resolution || resolutionInfo.getLowestResolution();
resolution = resolution || resolutionInfo.getFinestResolution();

const magString = resolution.join("-");
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export function isVolumeAnnotationDisallowedForZoom(tool: AnnotationTool, state:
}

const volumeResolutions = getResolutionInfoOfActiveSegmentationTracingLayer(state);
const lowestExistingResolutionIndex = volumeResolutions.getLowestResolutionIndex();
const lowestExistingResolutionIndex = volumeResolutions.getFinestResolutionIndex();
// The current resolution is too high for the tool
// because too many voxels could be annotated at the same time.
const isZoomStepTooHigh =
Expand All @@ -221,7 +221,7 @@ export function getMaximumBrushSize(state: OxalisState) {
return MAX_BRUSH_SIZE_FOR_MAG1;
}

const lowestExistingResolutionIndex = volumeResolutions.getLowestResolutionIndex();
const lowestExistingResolutionIndex = volumeResolutions.getFinestResolutionIndex();
// For each leading magnification which does not exist,
// we double the maximum brush size.
return MAX_BRUSH_SIZE_FOR_MAG1 * 2 ** lowestExistingResolutionIndex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export default class LayerRenderingManager {
const { dataset, datasetConfiguration } = state;
const layer = getLayerByName(dataset, this.name);
const resolutionInfo = getResolutionInfo(layer.resolutions);
const maximumResolutionIndex = resolutionInfo.getHighestResolutionIndex();
const maximumResolutionIndex = resolutionInfo.getCoarsestResolutionIndex();

if (logZoomStep > maximumResolutionIndex) {
// Don't render anything if the zoomStep is too high
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy {
return [];
}

const maxZoomStep = resolutionInfo.getHighestResolutionIndex();
const maxZoomStep = resolutionInfo.getCoarsestResolutionIndex();
const zoomStepDiff = currentZoomStep - zoomStep;
const queueItemsForCurrentZoomStep = this.prefetchImpl(
cube,
Expand Down
20 changes: 10 additions & 10 deletions frontend/javascripts/oxalis/model/helpers/resolution_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,30 +128,30 @@ export class ResolutionInfo {
return this.resolutionMap.get(powerOfTwo);
}

getHighestResolutionPowerOf2(): number {
getCoarsestResolutionPowerOf2(): number {
return maxValue(Array.from(this.resolutionMap.keys()));
}

getLowestResolutionPowerOf2(): number {
getFinestResolutionPowerOf2(): number {
return minValue(Array.from(this.resolutionMap.keys()));
}

getHighestResolutionIndex(): number {
return Math.log2(this.getHighestResolutionPowerOf2());
getCoarsestResolutionIndex(): number {
return Math.log2(this.getCoarsestResolutionPowerOf2());
}

getLowestResolutionIndex(): number {
return Math.log2(this.getLowestResolutionPowerOf2());
getFinestResolutionIndex(): number {
return Math.log2(this.getFinestResolutionPowerOf2());
}

getHighestResolution(): Vector3 {
getCoarsestResolution(): Vector3 {
// @ts-ignore
return this.getResolutionByPowerOf2(this.getHighestResolutionPowerOf2());
return this.getResolutionByPowerOf2(this.getCoarsestResolutionPowerOf2());
}

getLowestResolution(): Vector3 {
getFinestResolution(): Vector3 {
// @ts-ignore
return this.getResolutionByPowerOf2(this.getLowestResolutionPowerOf2());
return this.getResolutionByPowerOf2(this.getFinestResolutionPowerOf2());
}

getAllIndices(): Array<number> {
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/oxalis/model/sagas/dataset_saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function* watchZ1Downsampling(): Saga<void> {
break;
}
const resolutionInfo = getResolutionInfo(dataLayer.resolutions);
const bestExistingIndex = resolutionInfo.getLowestResolutionIndex();
const bestExistingIndex = resolutionInfo.getFinestResolutionIndex();
const currentIndex = resolutionInfo.getIndexByResolution(currentRes);
if (currentIndex <= bestExistingIndex) {
// There's no better mag to render the current layer in.
Expand Down
6 changes: 3 additions & 3 deletions frontend/javascripts/oxalis/model/sagas/proofread_saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,8 @@ function* prepareSplitOrMerge(

const resolutionInfo = getResolutionInfo(volumeTracingLayer.resolutions);
// The mag the agglomerate skeleton corresponds to should be the finest available mag of the volume tracing layer
const agglomerateFileMag = resolutionInfo.getLowestResolution();
const agglomerateFileZoomstep = resolutionInfo.getLowestResolutionIndex();
const agglomerateFileMag = resolutionInfo.getFinestResolution();
const agglomerateFileZoomstep = resolutionInfo.getFinestResolutionIndex();

const getDataValue = (position: Vector3) => {
const { additionalCoordinates } = Store.getState().flycam;
Expand Down Expand Up @@ -765,7 +765,7 @@ function* createGetUnmappedDataValueFn(
const layerName = volumeTracingLayer.tracingId;

const resolutionInfo = getResolutionInfo(volumeTracingLayer.resolutions);
const mag = resolutionInfo.getLowestResolution();
const mag = resolutionInfo.getFinestResolution();

const fallbackLayerName = volumeTracingLayer.fallbackLayer;
if (fallbackLayerName == null) return null;
Expand Down
15 changes: 14 additions & 1 deletion frontend/javascripts/oxalis/model/sagas/volume/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import DataCube from "oxalis/model/bucket_data_handling/data_cube";
import { Model } from "oxalis/singletons";
import VolumeLayer, { VoxelBuffer2D } from "oxalis/model/volumetracing/volumelayer";
import { enforceActiveVolumeTracing } from "oxalis/model/accessors/volumetracing_accessor";
import { VolumeTracing } from "oxalis/store";
import { BoundingBoxObject, VolumeTracing } from "oxalis/store";
import { getFlooredPosition } from "oxalis/model/accessors/flycam_accessor";
import { zoomedPositionToZoomedAddress } from "oxalis/model/helpers/position_converter";
import { ResolutionInfo } from "oxalis/model/helpers/resolution_info";
Expand Down Expand Up @@ -58,6 +58,19 @@ export function* getBoundingBoxForViewport(
return new BoundingBox(currentViewportBounding).intersectedWith(datasetBoundingBox);
}

export function getBoundingBoxInMag1(boudingBox: BoundingBoxObject, magOfBB: Vector3) {
return {
topLeft: [
boudingBox.topLeft[0] * magOfBB[0],
boudingBox.topLeft[1] * magOfBB[1],
boudingBox.topLeft[2] * magOfBB[2],
] as Vector3,
width: boudingBox.width * magOfBB[0],
height: boudingBox.height * magOfBB[1],
depth: boudingBox.depth * magOfBB[2],
};
}

export function applyLabeledVoxelMapToAllMissingResolutions(
inputLabeledVoxelMap: LabeledVoxelsMap,
labeledZoomStep: number,
Expand Down
7 changes: 7 additions & 0 deletions frontend/javascripts/oxalis/model/scaleinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ export function getBaseVoxel(dataSetScale: Vector3): number {
// base voxel should be a cube with highest resolution
return Math.min(...dataSetScale);
}

export function voxelToNm3(dataSetScale: Vector3, mag: Vector3, volumeInVx: number): number {
return (
mag[0] * mag[1] * mag[2] * dataSetScale[0] * dataSetScale[1] * dataSetScale[2] * volumeInVx
);
}

export function getBaseVoxelFactors(dataSetScale: Vector3): Vector3 {
// base voxel should be a cube with highest resolution
const baseVoxel = getBaseVoxel(dataSetScale);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ function _DownloadModalView({
const [selectedBoundingBoxId, setSelectedBoundingBoxId] = useState(
initialBoundingBoxId ?? userBoundingBoxes[0].id,
);
const [rawMag, setMag] = useState<Vector3>(selectedLayerResolutionInfo.getLowestResolution());
const [rawMag, setMag] = useState<Vector3>(selectedLayerResolutionInfo.getFinestResolution());
const mag = selectedLayerResolutionInfo.getClosestExistingResolution(rawMag);
const [exportFormat, setExportFormat] = useState<ExportFormat>(ExportFormat.OME_TIFF);

Expand Down
23 changes: 16 additions & 7 deletions frontend/javascripts/oxalis/view/context_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ import { getSegmentBoundingBoxes, getSegmentVolumes } from "admin/admin_rest_api
import { useFetch } from "libs/react_helpers";
import { AsyncIconButton } from "components/async_clickables";
import { type AdditionalCoordinate } from "types/api_flow_types";
import { voxelToNm3 } from "oxalis/model/scaleinfo";
import { getBoundingBoxInMag1 } from "oxalis/model/sagas/volume/helpers";

type ContextMenuContextValue = React.MutableRefObject<HTMLElement | null> | null;
export const ContextMenuContext = createContext<ContextMenuContextValue>(null);
Expand Down Expand Up @@ -1118,23 +1120,30 @@ function ContextMenuInner(propsWithInputRef: Props) {
} else {
const tracingId = volumeTracing.tracingId;
const tracingStoreUrl = Store.getState().tracing.tracingStore.url;
const mag = getResolutionInfo(visibleSegmentationLayer.resolutions);
const magInfo = getResolutionInfo(visibleSegmentationLayer.resolutions);
const layersFinestResolution = magInfo.getFinestResolution();
const dataSetScale = Store.getState().dataset.dataSource.scale;
const [segmentSize] = await getSegmentVolumes(
tracingStoreUrl,
tracingId,
mag.getLowestResolution(),
layersFinestResolution,
[segmentIdAtPosition],
);
const [boundingBox] = await getSegmentBoundingBoxes(
const [boundingBoxInRequestedMag] = await getSegmentBoundingBoxes(
tracingStoreUrl,
tracingId,
mag.getLowestResolution(),
layersFinestResolution,
[segmentIdAtPosition],
);
const boundingBoxTopLeftString = `(${boundingBox.topLeft[0]}, ${boundingBox.topLeft[1]}, ${boundingBox.topLeft[2]})`;
const boundingBoxSizeString = `(${boundingBox.width}, ${boundingBox.height}, ${boundingBox.depth})`;
const boundingBoxInMag1 = getBoundingBoxInMag1(
boundingBoxInRequestedMag,
layersFinestResolution,
);
const boundingBoxTopLeftString = `(${boundingBoxInMag1.topLeft[0]}, ${boundingBoxInMag1.topLeft[1]}, ${boundingBoxInMag1.topLeft[2]})`;
const boundingBoxSizeString = `(${boundingBoxInMag1.width}, ${boundingBoxInMag1.height}, ${boundingBoxInMag1.depth})`;
const volumeInNm3 = voxelToNm3(dataSetScale, layersFinestResolution, segmentSize);
return [
formatNumberToVolume(segmentSize),
formatNumberToVolume(volumeInNm3),
`${boundingBoxTopLeftString}, ${boundingBoxSizeString}`,
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import React from "react";
import { SegmentHierarchyNode, SegmentHierarchyGroup } from "./segments_view_helper";
import { Store, api } from "oxalis/singletons";
import { APISegmentationLayer } from "types/api_flow_types";
import { getBaseVoxel } from "oxalis/model/scaleinfo";
import { voxelToNm3 } from "oxalis/model/scaleinfo";
import { getBoundingBoxInMag1 } from "oxalis/model/sagas/volume/helpers";

const SEGMENT_STATISTICS_CSV_HEADER =
"segmendId,segmentName,groupId,groupName,volumeInVoxel,volumeInNm3,boundingBoxTopLeftPositionX,boundingBoxTopLeftPositionY,boundingBoxTopLeftPositionZ,boundingBoxSizeX,boundingBoxSizeY,boundingBoxSizeZ";
Expand Down Expand Up @@ -84,22 +85,23 @@ export function SegmentStatisticsModal({
parentGroup,
groupTree,
}: Props) {
const mag = getResolutionInfo(visibleSegmentationLayer.resolutions);
const scaleFactor = getBaseVoxel(Store.getState().dataset.dataSource.scale);
const magInfo = getResolutionInfo(visibleSegmentationLayer.resolutions);
const layersFinestResolution = magInfo.getFinestResolution();
const dataSetScale = Store.getState().dataset.dataSource.scale;
const dataSource = useFetch(
async () => {
await api.tracing.save();
const segmentStatisticsObjects = await Promise.all([
getSegmentVolumes(
tracingStoreUrl,
tracingId,
mag.getHighestResolution(),
layersFinestResolution,
segments.map((segment) => segment.id),
),
getSegmentBoundingBoxes(
tracingStoreUrl,
tracingId,
mag.getHighestResolution(),
layersFinestResolution,
segments.map((segment) => segment.id),
),
]).then((response) => {
Expand All @@ -110,10 +112,18 @@ export function SegmentStatisticsModal({
// segments in request and their statistics in the response are in the same order
const currentSegment = segments[i];
const currentBoundingBox = boundingBoxes[i];
const boundingBoxInMag1 = getBoundingBoxInMag1(
currentBoundingBox,
layersFinestResolution,
);
const currentSegmentSizeInVx = segmentSizes[i];
const volumeInNm3 = currentSegmentSizeInVx * scaleFactor;
const volumeInNm3 = voxelToNm3(
dataSetScale,
layersFinestResolution,
currentSegmentSizeInVx,
);
const currentGroupId = getGroupIdForSegment(currentSegment);
const segmentStatObject = {
const segmentStateObject = {
key: currentSegment.id,
segmentId: currentSegment.id,
segmentName:
Expand All @@ -123,16 +133,16 @@ export function SegmentStatisticsModal({
volumeInVoxel: currentSegmentSizeInVx,
volumeInNm3,
formattedSize: formatNumberToVolume(volumeInNm3),
boundingBoxTopLeft: currentBoundingBox.topLeft,
boundingBoxTopLeftAsString: `(${currentBoundingBox.topLeft.join(", ")})`,
boundingBoxTopLeft: boundingBoxInMag1.topLeft,
boundingBoxTopLeftAsString: `(${boundingBoxInMag1.topLeft.join(", ")})`,
boundingBoxPosition: [
currentBoundingBox.width,
currentBoundingBox.height,
currentBoundingBox.depth,
boundingBoxInMag1.width,
boundingBoxInMag1.height,
boundingBoxInMag1.depth,
] as Vector3,
boundingBoxPositionAsString: `(${currentBoundingBox.width}, ${currentBoundingBox.height}, ${currentBoundingBox.depth})`,
boundingBoxPositionAsString: `(${boundingBoxInMag1.width}, ${boundingBoxInMag1.height}, ${boundingBoxInMag1.depth})`,
};
statisticsObjects.push(segmentStatObject);
statisticsObjects.push(segmentStateObject);
}
return statisticsObjects;
});
Expand Down

0 comments on commit 43d88dd

Please sign in to comment.