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

isosurfaces for volume tracings #4567

Merged
merged 34 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8badcdd
[WIP] isosurfaces for volume tracings
Apr 23, 2020
9bdaf8f
Merge branch 'master' of github.com:scalableminds/webknossos into iso…
Apr 24, 2020
b1ceca2
update changelog
Apr 24, 2020
b442c03
Merge branch 'master' of github.com:scalableminds/webknossos into iso…
Apr 24, 2020
894eb94
make datasource optional
Apr 24, 2020
dc50d98
merge master
May 13, 2020
ee28593
Merge branch 'master' of github.com:scalableminds/webknossos into iso…
May 19, 2020
e0767ad
add frontend part for isosurfaces for volumes
May 19, 2020
6d98b8c
merge master
May 19, 2020
a1470c2
send scale to correctly display isosurfaces
Jun 2, 2020
f3a6600
Add button to reload Isosurfaces of modified cells
MichaelBuessemeyer Jul 29, 2020
16f9a29
save before reloading & reload isosurface of all cubes
MichaelBuessemeyer Jul 30, 2020
7a6e801
Merge branch 'master' of github.com:scalableminds/webknossos into iso…
MichaelBuessemeyer Jul 30, 2020
0877cf8
Merge branch 'master' of github.com:scalableminds/webknossos into iso…
Aug 11, 2020
19cec69
fix isosurface dependency injection
Aug 11, 2020
7eb494d
merge master
Aug 12, 2020
5594a18
add loading animation to isosurface refreshing
MichaelBuessemeyer Aug 17, 2020
b8bdba2
Merge branch 'isosurface-for-volume-tracing' of github.com:scalablemi…
MichaelBuessemeyer Aug 17, 2020
5efc153
Merge branch 'master' of github.com:scalableminds/webknossos into iso…
MichaelBuessemeyer Aug 17, 2020
d1df350
remove isosurfaces from scene, when after fetching the first part
MichaelBuessemeyer Aug 17, 2020
87ddb09
fetch isosurface of active cell id in if volume tracing exists
MichaelBuessemeyer Aug 17, 2020
ad680ec
fix flow definition
MichaelBuessemeyer Aug 17, 2020
33110aa
Merge branch 'master' into isosurface-for-volume-tracing
MichaelBuessemeyer Aug 18, 2020
f4eb119
apply some feedback
MichaelBuessemeyer Sep 1, 2020
763413e
Merge branch 'master' of github.com:scalableminds/webknossos into iso…
MichaelBuessemeyer Sep 1, 2020
1e02ec0
apply feedback & load isosurface at flycam position on refresh
MichaelBuessemeyer Sep 1, 2020
25ca0b2
fetch from datastore if fallback layer exists
MichaelBuessemeyer Sep 1, 2020
bf8a1bc
only refetch isosurfaces if there is no fallback layer
MichaelBuessemeyer Sep 1, 2020
cbb9333
adjust refesh button tooltip
MichaelBuessemeyer Sep 1, 2020
ff62171
fix linting
MichaelBuessemeyer Sep 1, 2020
491a971
implement pr feedback
Sep 10, 2020
8013a4f
pr feedback 2
Sep 10, 2020
44f9dbf
merge master
fm3 Sep 14, 2020
c9729db
Merge branch 'master' into isosurface-for-volume-tracing
youri-k Sep 17, 2020
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 @@ -14,6 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Added the possibility to move nodes in skeleton tracings. This can be done either by pressing CTRL + arrow key or by dragging while holding CTRL. [#4743](https://github.com/scalableminds/webknossos/pull/4743)
- Added the possibility to delete datasets on disk from webKnossos. Use with care. [#4696](https://github.com/scalableminds/webknossos/pull/4696)
- Added error toasts for failing bucket requests. [#4740](https://github.com/scalableminds/webknossos/pull/4740)
- Isosurface generation now also supports volume tracings without fallback layer. [#4567](https://github.com/scalableminds/webknossos/pull/4567)
- Improved the distinguishability of segments by improving the color generation and also by rendering patterns within the segments. The pattern opacity can be adapted in the layer settings (next to the opacity of the segmentation layer). [#4730](https://github.com/scalableminds/webknossos/pull/4730)
- Added a list of all projects containing tasks of a specific task type. It's accessible from the task types list view. [#4420](https://github.com/scalableminds/webknossos/pull/4745)

Expand Down
11 changes: 5 additions & 6 deletions frontend/javascripts/admin/admin_rest_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1251,20 +1251,18 @@ type IsosurfaceRequest = {
segmentId: number,
voxelDimensions: Vector3,
cubeSize: Vector3,
scale: Vector3,
};

export function computeIsosurface(
datastoreUrl: string,
datasetId: APIDatasetId,
requestUrl: string,
layer: DataLayer,
isosurfaceRequest: IsosurfaceRequest,
): Promise<{ buffer: ArrayBuffer, neighbors: Array<number> }> {
const { position, zoomStep, segmentId, voxelDimensions, cubeSize } = isosurfaceRequest;
const { position, zoomStep, segmentId, voxelDimensions, cubeSize, scale } = isosurfaceRequest;
return doWithToken(async token => {
const { buffer, headers } = await Request.sendJSONReceiveArraybufferWithHeaders(
`${datastoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${
layer.fallbackLayer != null ? layer.fallbackLayer : layer.name
}/isosurface?token=${token}`,
`${requestUrl}/isosurface?token=${token}`,
{
data: {
// The back-end needs a small padding at the border of the
Expand All @@ -1280,6 +1278,7 @@ export function computeIsosurface(
mappingType: layer.activeMappingType,
// "size" of each voxel (i.e., only every nth voxel is considered in each dimension)
voxelDimensions,
scale,
},
},
);
Expand Down
18 changes: 18 additions & 0 deletions frontend/javascripts/libs/ThreeDMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ export default class ThreeDMap<T> {
.set(z, value);
}

entries(): Array<[T, Vector3]> {
const entries: Array<[T, Vector3]> = [];
this.map.forEach((atX, x) => {
if (!atX) {
return;
}
atX.forEach((atY, y) => {
if (!atY) {
return;
}
atY.forEach((value, z) => {
entries.push([value, [x, y, z]]);
});
});
});
return entries;
}

// This could be extended so the key is a Vector1 | Vector2
// if needed in the future
delete(key: number): boolean {
Expand Down
18 changes: 18 additions & 0 deletions frontend/javascripts/oxalis/api/api_latest.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
setTreeGroupsAction,
} from "oxalis/model/actions/skeletontracing_actions";
import { setPositionAction, setRotationAction } from "oxalis/model/actions/flycam_actions";
import { refreshIsosurfacesAction } from "oxalis/model/actions/annotation_actions";
import {
updateUserSettingAction,
updateDatasetSettingAction,
Expand Down Expand Up @@ -102,6 +103,7 @@ import messages from "messages";
import window, { location } from "libs/window";
import { type ElementClass } from "admin/api_flow_types";
import UserLocalStorage from "libs/user_local_storage";
import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers";

type OutdatedDatasetConfigurationKeys = "segmentationOpacity" | "isSegmentationDisabled";

Expand Down Expand Up @@ -821,6 +823,22 @@ class DataApi {
return Store.getState().temporaryConfiguration.activeMapping.isMappingEnabled;
}

refreshIsosurfaces(): Promise<void> {
Store.dispatch(refreshIsosurfacesAction());
const isRefreshingComplete = new Promise(resolve => {
const unsubscribe = listenToStoreProperty(
state => state.uiInformation.refreshingIsosurfaces,
refreshingIsosurfaces => {
if (!refreshingIsosurfaces) {
unsubscribe();
resolve();
}
},
);
});
return isRefreshingComplete;
}
philippotto marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the bounding box for a given layer name.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// @flow
import { type OrthoView, type Point2 } from "oxalis/constants";
import Model from "oxalis/model";
import { changeActiveIsosurfaceCellAction } from "oxalis/model/actions/segmentation_actions";
import { calculateGlobalPos } from "oxalis/controller/viewmodes/plane_controller";
import { getRequestLogZoomStep } from "oxalis/model/accessors/flycam_accessor";
import Store from "oxalis/store";

function isosurfaceLeftClick(pos: Point2, plane: OrthoView, event: MouseEvent) {
if (!event.shiftKey) {
return;
}
let cellId = 0;
const position = calculateGlobalPos(pos);
const volumeTracingMaybe = Store.getState().tracing.volume;
if (volumeTracingMaybe) {
cellId = volumeTracingMaybe.activeCellId;
} else {
const segmentation = Model.getSegmentationLayer();
if (!segmentation) {
return;
}
cellId = segmentation.cube.getMappedDataValue(
position,
getRequestLogZoomStep(Store.getState()),
);
}
if (cellId > 0) {
Store.dispatch(changeActiveIsosurfaceCellAction(cellId, position));
}
}

export default isosurfaceLeftClick;
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import Store from "oxalis/store";
import api from "oxalis/api/internal_api";
import getSceneController from "oxalis/controller/scene_controller_provider";
import { renderToTexture } from "oxalis/view/rendering_utils";
import isosurfaceLeftClick from "oxalis/controller/combinations/segmentation_plane_controller";
import { getBaseVoxelFactors } from "oxalis/model/scaleinfo";
import Dimensions from "oxalis/model/dimensions";

Expand Down Expand Up @@ -85,7 +86,7 @@ export function getPlaneMouseControls(planeView: PlaneView) {
}
},
leftClick: (pos: Point2, plane: OrthoView, event: MouseEvent, isTouch: boolean) =>
onClick(planeView, pos, event.shiftKey, event.altKey, event.ctrlKey, plane, isTouch),
onClick(planeView, pos, event.shiftKey, event.altKey, event.ctrlKey, plane, isTouch, event),
rightClick: (pos: Point2, plane: OrthoView, event: MouseEvent) => {
const { volume } = Store.getState().tracing;
if (!volume || volume.activeTool !== VolumeToolEnum.BRUSH) {
Expand Down Expand Up @@ -183,8 +184,9 @@ function onClick(
ctrlPressed: boolean,
plane: OrthoView,
isTouch: boolean,
event?: MouseEvent,
): void {
if (!shiftPressed && !isTouch) {
if (!shiftPressed && !isTouch && !(ctrlPressed && event != null)) {
// do nothing
return;
}
Expand Down Expand Up @@ -230,6 +232,8 @@ function onClick(
} else {
Store.dispatch(setActiveNodeAction(nodeId));
}
} else if (shiftPressed && event != null) {
isosurfaceLeftClick(position, plane, event);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { movePlaneFlycamOrthoAction, setPositionAction } from "oxalis/model/acti
import Model from "oxalis/model";
import Store from "oxalis/store";
import * as Utils from "libs/utils";
import isosurfaceLeftClick from "oxalis/controller/combinations/segmentation_plane_controller";

// TODO: Build proper UI for this
window.isAutomaticBrushEnabled = false;
Expand Down Expand Up @@ -168,6 +169,7 @@ export function getPlaneMouseControls(_planeId: OrthoView): * {
);
if (cellId > 0) {
Store.dispatch(setActiveCellAction(cellId));
isosurfaceLeftClick(pos, plane, event);
}
} else if (event.ctrlKey) {
if (isAutomaticBrushEnabled()) {
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/controller/scene_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class SceneController {
.to({ opacity: 0.95 }, 500)
.onUpdate(function onUpdate() {
meshMaterial.opacity = this.opacity;
app.vent.trigger("rerender");
})
.start();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import * as React from "react";
import _ from "lodash";

import { InputKeyboard, InputKeyboardNoLoop, InputMouse, type ModifierKeys } from "libs/input";
import { changeActiveIsosurfaceCellAction } from "oxalis/model/actions/segmentation_actions";
import { document } from "libs/window";
import { getBaseVoxel, getBaseVoxelFactors } from "oxalis/model/scaleinfo";
import { getViewportScale, getInputCatcherRect } from "oxalis/model/accessors/view_mode_accessor";
Expand Down Expand Up @@ -51,6 +50,7 @@ import getSceneController from "oxalis/controller/scene_controller_provider";
import * as skeletonController from "oxalis/controller/combinations/skeletontracing_plane_controller";
import * as volumeController from "oxalis/controller/combinations/volumetracing_plane_controller";
import { downloadScreenshot } from "oxalis/view/rendering_utils";
import isosurfaceLeftClick from "oxalis/controller/combinations/segmentation_plane_controller";

const MAX_BRUSH_CHANGE_VALUE = 5;
const BRUSH_CHANGING_CONSTANT = 0.02;
Expand All @@ -69,24 +69,6 @@ function ensureNonConflictingHandlers(skeletonControls: Object, volumeControls:
}
}

const isosurfaceLeftClick = (pos: Point2, plane: OrthoView, event: MouseEvent) => {
if (!event.shiftKey) {
return;
}
const segmentation = Model.getSegmentationLayer();
if (!segmentation) {
return;
}
const position = calculateGlobalPos(pos);
const cellId = segmentation.cube.getMappedDataValue(
position,
getRequestLogZoomStep(Store.getState()),
);
if (cellId > 0) {
Store.dispatch(changeActiveIsosurfaceCellAction(cellId, position));
}
};

type StateProps = {|
tracing: Tracing,
|};
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/default_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ const defaultState: OxalisState = {
isImportingMesh: false,
isInAnnotationView: false,
hasOrganizations: false,
refreshingIsosurfaces: false,
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
},
};

Expand Down
18 changes: 18 additions & 0 deletions frontend/javascripts/oxalis/model/actions/annotation_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ export type TriggerIsosurfaceDownloadAction = {
type: "TRIGGER_ISOSURFACE_DOWNLOAD",
};

export type RefreshIsosurfacesAction = {
type: "REFRESH_ISOSURFACES",
};

export type FinishedRefreshingIsosurfacesAction = {
type: "FINISHED_REFRESHING_ISOSURFACES",
};

export type ImportIsosurfaceFromStlAction = {
type: "IMPORT_ISOSURFACE_FROM_STL",
buffer: ArrayBuffer,
Expand All @@ -99,6 +107,8 @@ export type AnnotationActionTypes =
| CreateMeshFromBufferAction
| UpdateLocalMeshMetaDataAction
| TriggerIsosurfaceDownloadAction
| RefreshIsosurfacesAction
| FinishedRefreshingIsosurfacesAction
| ImportIsosurfaceFromStlAction
| RemoveIsosurfaceAction;

Expand Down Expand Up @@ -192,6 +202,14 @@ export const triggerIsosurfaceDownloadAction = (): TriggerIsosurfaceDownloadActi
type: "TRIGGER_ISOSURFACE_DOWNLOAD",
});

export const refreshIsosurfacesAction = (): RefreshIsosurfacesAction => ({
type: "REFRESH_ISOSURFACES",
});

export const finishedRefreshingIsosurfacesAction = (): FinishedRefreshingIsosurfacesAction => ({
type: "FINISHED_REFRESHING_ISOSURFACES",
});

export const importIsosurfaceFromStlAction = (
buffer: ArrayBuffer,
): ImportIsosurfaceFromStlAction => ({
Expand Down
8 changes: 8 additions & 0 deletions frontend/javascripts/oxalis/model/reducers/ui_reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ function UiReducer(state: OxalisState, action: Action): OxalisState {
return updateKey(state, "uiInformation", { hasOrganizations: action.value });
}

case "REFRESH_ISOSURFACES": {
return updateKey(state, "uiInformation", { refreshingIsosurfaces: true });
}

case "FINISHED_REFRESHING_ISOSURFACES": {
return updateKey(state, "uiInformation", { refreshingIsosurfaces: false });
}

default:
return state;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type Saga as _Saga,
channel,
effects
} from 'redux-saga';
} from "redux-saga";
import {
all as typedAll,
race as typedRace,
Expand All @@ -19,7 +19,7 @@ import {
takeLeading as typedTakeLeading,
throttle as typedThrottle,
cancel as typedCancel,
} from 'redux-saga/effects';
} from "redux-saga/effects";

import type { Action } from "oxalis/model/actions/actions";
import type { OxalisState } from "oxalis/store";
Expand All @@ -37,6 +37,7 @@ declare type Fn3<T1, T2, T3, R> = (t1: T1, t2: T2, t3: T3) => Promise<R> | Gener
declare type Fn4<T1, T2, T3, T4, R> = (t1: T1, t2: T2, t3: T3, t4: T4) => Promise<R> | Generator<*,R,*> | R;
declare type Fn5<T1, T2, T3, T4, T5, R> = (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => Promise<R> | Generator<*,R,*> | R;
declare type Fn6<T1, T2, T3, T4, T5, T6, R> = (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => Promise<R> | Generator<*,R,*> | R;
declare type Fn7<T1, T2, T3, T4, T5, T6, T7, R> = (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7) => Promise<R> | Generator<*,R,*> | R;

/* ------------------ SELECT Stuff ------------------ */

Expand Down Expand Up @@ -69,6 +70,7 @@ declare type ContextCallFn =
& (<T1, T2, T3, T4, R, C: Context, Fn: Fn4<T1, T2, T3, T4, R>>(cfn: [C, Fn], t1: T1, t2: T2, t3: T3, t4: T4, ...rest: Array<void>) => Generator<*, R, *>)
& (<T1, T2, T3, T4, T5, R, C: Context, Fn: Fn5<T1, T2, T3, T4, T5, R>>(cfn: [C, Fn], t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, ...rest: Array<void>) => Generator<*, R, *>)
& (<T1, T2, T3, T4, T5, T6, R, C: Context, Fn: Fn6<T1, T2, T3, T4, T5, T6, R>>(cfn: [C, Fn], t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, ...rest: Array<void>) => Generator<*, R, *>)
& (<T1, T2, T3, T4, T5, T6, T7, R, C: Context, Fn: Fn7<T1, T2, T3, T4, T5, T6, T7, R>>(cfn: [C, Fn], t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, ...rest: Array<void>) => Generator<*, R, *>)
// & (<T, R, C: Context, Fn: FnSpread<T, R>>(cfn: [C, Fn], t1: T, t2: T, t3: T, t4: T, t5: T, t6: T, ...args: Array<T>) => Generator<*, R, *>);

declare type CallFn =
Expand All @@ -79,7 +81,8 @@ declare type CallFn =
& (<T1, T2, T3, R, Fn: Fn3<T1, T2, T3, R>>(fn: Fn, t1: T1, t2: T2, t3: T3) => Generator<*, R, *>)
& (<T1, T2, T3, T4, R, Fn: Fn4<T1, T2, T3, T4, R>>(fn: Fn, t1: T1, t2: T2, t3: T3, t4: T4) => Generator<*, R, *>)
& (<T1, T2, T3, T4, T5, R, Fn: Fn5<T1, T2, T3, T4, T5, R>>(fn: Fn, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => Generator<*, R, *>)
& (<T1, T2, T3, T4, T5, T6, R, Fn: Fn6<T1, T2, T3, T4, T5, T6, R>>(fn: Fn, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => Generator<*, R, *>);
& (<T1, T2, T3, T4, T5, T6, R, Fn: Fn6<T1, T2, T3, T4, T5, T6, R>>(fn: Fn, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => Generator<*, R, *>)
& (<T1, T2, T3, T4, T5, T6, T7, R, Fn: Fn7<T1, T2, T3, T4, T5, T6, T7, R>>(fn: Fn, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7) => Generator<*, R, *>);
// & (<T, R, Fn: FnSpread<T, R>>(fn: Fn, ...args: Array<T>) => Generator<*, R, *>);

/* ------------------ CPS Stuff ------------------ */
Expand Down
Loading