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

Auto-Select via SAM #7051

Merged
merged 83 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
dd5fd1a
WIP: Auto-Select via SAM
fm3 May 4, 2023
f069f52
load data from datastore
fm3 May 4, 2023
6604240
send element class to sam server
fm3 May 4, 2023
3df1b35
mag handling
fm3 May 5, 2023
6d985ef
rough sam integration into frontend
philippotto May 5, 2023
1ab28af
Merge branch 'sam' of github.com:scalableminds/webknossos into sam
philippotto May 5, 2023
86b7466
use embedding route from backend instead of fetching precomputed embe…
philippotto May 5, 2023
1992858
small unrelated bug fix for canceling poll under certain race condition
philippotto May 8, 2023
7d82f92
switch for dynamic vs hardcoded embedding
philippotto May 8, 2023
46bf263
better error handling and disable fetching of hardcoded embedding
philippotto May 8, 2023
3077639
only use user-requested bbox when applying results for better perform…
philippotto May 8, 2023
14c40d9
request embedding for actual user position
philippotto May 8, 2023
d3c4fe9
implement caching and reuse of embedding
philippotto May 8, 2023
3f28c99
temporarily disable most CI checks
philippotto May 8, 2023
d87c3c3
restore application.conf setting for slick and swagger; add another c…
philippotto May 8, 2023
4f1d606
make it work in arbitrary mags
philippotto May 8, 2023
c5d8d7a
integrate mag into embedding cache
philippotto May 8, 2023
e88bc82
show busy indicator when loading embedding
philippotto May 8, 2023
ff85c21
remove the entire heuristic-based quickselect code
philippotto May 8, 2023
26ef93e
refactor and clean up so that ML and heuristic based quick select are…
philippotto May 8, 2023
289af62
allow to select AI or not for quick select in UI
philippotto May 8, 2023
8d53d74
fix inverted style
philippotto May 8, 2023
fe9b8f2
only allow quick-select with SAM on wkorg
philippotto May 9, 2023
bacc9fc
make sam select compatible with other viewports
philippotto May 9, 2023
205d0dc
refactor embedding request
philippotto May 9, 2023
c9760c5
fix linting
philippotto May 9, 2023
13bafd4
Revert "temporarily disable most CI checks"
philippotto May 9, 2023
e3cce00
prefetch embedding as soon as user presses mouse down
philippotto May 9, 2023
81545e9
use segmentAnythingEnabled instead of isWkorgInstance
philippotto May 9, 2023
36ae23c
update snapshots
philippotto May 9, 2023
798d198
also prefetch ORT session
philippotto May 9, 2023
2918785
use camel case in infer code
philippotto May 9, 2023
5151943
optimize extraction of mask
philippotto May 9, 2023
e17e4e2
rename some vars
philippotto May 9, 2023
c266b98
adapt analytics event
philippotto May 9, 2023
346a46a
update changelog
philippotto May 9, 2023
2ec15cd
Merge branch 'master' into sam
philippotto May 9, 2023
988d212
update docs
philippotto May 9, 2023
158f627
fix some deprecations
philippotto May 9, 2023
8f6b70a
Merge branch 'sam' of github.com:scalableminds/webknossos into sam
philippotto May 9, 2023
1f8adf7
don't cache failed embeddings; only support uint8 in ai mode
philippotto May 9, 2023
cb33595
Merge branch 'master' into sam
fm3 May 10, 2023
36c6ed9
re-add assertion, disable in conf by default
fm3 May 10, 2023
8d74ea5
update snapshot (segmentAnything disabled by default)
fm3 May 10, 2023
4400736
don't clamp min coord of bounding boxes to 0
philippotto May 10, 2023
099c3cc
remove unnecessary V3.max calls
philippotto May 10, 2023
e75a637
pr feedback
philippotto May 10, 2023
805c62d
catch error better if wasm cannot be loaded
philippotto May 10, 2023
5b2cc11
avoid redundant error toast
philippotto May 10, 2023
70eaf6f
further simplication for bbox
philippotto May 10, 2023
fa0bd00
add assertion for bbox < 1024**2
philippotto May 10, 2023
2b6bbcf
slice cache when adding to it instead of when accessing it
philippotto May 10, 2023
a469fcb
remove time measurement code
philippotto May 10, 2023
4706963
align user bbox to mag before using it to ai-select
philippotto May 10, 2023
df37965
more bbox alignment (except for geometry)
philippotto May 11, 2023
b940e28
fix onnxCoord interpretation for yz viewport
philippotto May 11, 2023
0568f5c
fix mag-alignment logic by rewriting the alignWithMag function to pro…
philippotto May 11, 2023
ec78745
fix usage of wrong cache entries because of zero-volume bounding boxe…
philippotto May 11, 2023
cf3ebed
fix that QuickSelectGeometry would be invisible when the third dimens…
philippotto May 11, 2023
5170fe5
extrude bounding boxes by correctly mag-adapted depth
philippotto May 11, 2023
32d79ce
add comments to inference code
philippotto May 11, 2023
bdde1a7
prefetch session as soon as quick select tool is activated
philippotto May 12, 2023
f30c90b
allow to cancel quick select with escape while drawing rectangle
philippotto May 12, 2023
e8606a1
fix invisible geometry
philippotto May 12, 2023
84046a3
clean up console.logs
philippotto May 12, 2023
d9ec7ad
fix linting
philippotto May 12, 2023
a1ef7ee
Merge branch 'master' into sam
normanrz May 15, 2023
1eb5585
include intensity min/max and element class in byte array sent to sam…
fm3 May 16, 2023
04b6479
Merge branch 'master' of github.com:scalableminds/webknossos into sam
philippotto May 16, 2023
6b25b11
assert min/max intensity is supplied if element class is float or double
fm3 May 16, 2023
deb3461
fix frontend typecheck
fm3 May 16, 2023
910495c
send intensity range to embedding endpoint if layer is not uint8
philippotto May 16, 2023
6c1c50f
Merge branch 'sam' of github.com:scalableminds/webknossos into sam
philippotto May 16, 2023
a81efb1
backend error messages
fm3 May 16, 2023
fd85947
use 4 bytes each for min/max of intensity range, not 8
fm3 May 16, 2023
b19c0cf
only show center marker when using old quick-select mode; fix scale o…
philippotto May 16, 2023
9990703
Merge branch 'sam' of github.com:scalableminds/webknossos into sam
philippotto May 16, 2023
d28e105
add comment
philippotto May 16, 2023
40aec28
send metadata with correct endianness
fm3 May 16, 2023
1ae7278
Merge branch 'sam' of github.com:scalableminds/webknossos into sam
fm3 May 16, 2023
1967637
take layer name into account when caching embedding
philippotto May 16, 2023
a28d084
Merge branch 'sam' of github.com:scalableminds/webknossos into sam
philippotto May 16, 2023
0c137d1
Merge branch 'master' into sam
fm3 May 17, 2023
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 frontend/javascripts/admin/admin_rest_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2369,6 +2369,7 @@ export async function getSamEmbedding(
`/api/datasets/${dataset.owningOrganization}/${dataset.name}/layers/${layerName}/segmentAnythingEmbedding`,
{
data: { mag, boundingBox: embeddingBoxMag1.asServerBoundingBox() },
showErrorToast: false,
},
);
return new Float32Array(buffer);
Expand Down
9 changes: 9 additions & 0 deletions frontend/javascripts/libs/mjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,15 @@ const V3 = {
isEqual(a: Vector3, b: Vector3) {
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
},

alignWithMag(a: Vector3, mag: Vector3, ceil: boolean = false) {
const roundFn = ceil ? Math.ceil : Math.floor;
return [
roundFn(a[0] / mag[0]) * mag[0],
roundFn(a[1] / mag[1]) * mag[1],
roundFn(a[2] / mag[2]) * mag[2],
] as Vector3;
},
};

const V4 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
} from "oxalis/model/actions/proofread_actions";
import { calculateGlobalPos } from "oxalis/model/accessors/view_mode_accessor";
import { V3 } from "libs/mjs";
import { setQuickSelectStateAction } from "oxalis/model/actions/ui_actions";

export type ActionDescriptor = {
leftClick?: string;
Expand Down Expand Up @@ -662,6 +663,8 @@ export class QuickSelectTool {
Store.dispatch(confirmQuickSelectAction());
quickSelectGeometry.detachTextureMask();

Store.dispatch(setQuickSelectStateAction("drawing"));

const state = Store.getState();
quickSelectGeometry.rotateToViewport();

Expand All @@ -683,7 +686,10 @@ export class QuickSelectTool {
isDragging = false;
// Identity equality is enough, since we want to catch the case
// in which the user didn't move the mouse at all
if (startPos === currentPos) {
if (
startPos === currentPos ||
Store.getState().uiInformation.quickSelectState === "inactive"
) {
// clear rectangle because user didn't drag
return;
}
Expand All @@ -699,7 +705,11 @@ export class QuickSelectTool {
_id: string | null | undefined,
event: MouseEvent,
) => {
if (!isDragging || startPos == null) {
if (
!isDragging ||
startPos == null ||
Store.getState().uiInformation.quickSelectState === "inactive"
) {
return;
}
const newCurrentPos = V3.floor(calculateGlobalPos(Store.getState(), pos));
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/oxalis/default_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ const defaultState: OxalisState = {
busyBlockingInfo: {
isBusy: false,
},
isQuickSelectActive: false,
quickSelectState: "inactive",
areQuickSelectSettingsOpen: false,
},
localSegmentationData: {},
Expand Down
28 changes: 24 additions & 4 deletions frontend/javascripts/oxalis/geometries/helper_geometries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,22 @@ export class QuickSelectGeometry {
}

setCoordinates(startPosition: Vector3, endPosition: Vector3) {
const centerPosition = V3.scale(V3.add(startPosition, endPosition), 0.5);
const extentXYZ = V3.abs(V3.sub(endPosition, startPosition));
const { activeViewport } = Store.getState().viewModeData.plane;
// Add a depth to the endPosition so that the extent of the geometry
// will have a depth of 1. Note that the extent is only used to scale
// the geometry. Since it is a plane, a depth scale factor of > 1 won't
// extrude it.
const endPositionWithDepth = V3.add(
endPosition,
Dimensions.transDim([0, 0, 1], activeViewport),
);

const centerPosition = V3.scale(V3.add(startPosition, endPosition), 0.5);
const extentXYZ = V3.abs(V3.sub(endPositionWithDepth, startPosition));
const extentUVW = Dimensions.transDim(extentXYZ, activeViewport);
// Set the depth-component of the rectangle to 1
extentUVW[2] = 1;

// Note that the third dimension's value will be adapted again
// in adaptVisibilityForRendering.
this.rectangle.position.set(...centerPosition);
this.rectangle.scale.set(...extentUVW);
this.rectangle.geometry.computeBoundingSphere();
Expand All @@ -165,6 +174,17 @@ export class QuickSelectGeometry {
this.meshGroup.visible =
Math.trunc(flycamPosition[thirdDim]) ===
Math.trunc(this.rectangle.position.toArray()[thirdDim]);

if (this.meshGroup.visible) {
// If the group is visible, adapt the position's third dimension to
// be exactly at the third dimension of the flycam. Otherwise,
// the geometry might be invisible when the current position is
// fractional.
const pos = this.rectangle.position.toArray();
pos[thirdDim] = flycamPosition[thirdDim];
this.rectangle.position.set(...pos);
this.centerMarker.position.set(...pos);
}
}

getMeshGroup() {
Expand Down
15 changes: 8 additions & 7 deletions frontend/javascripts/oxalis/model/actions/ui_actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AnnotationTool } from "oxalis/constants";
import type { BorderOpenStatus, Theme } from "oxalis/store";
import type { OxalisState, BorderOpenStatus, Theme } from "oxalis/store";

type SetDropzoneModalVisibilityAction = ReturnType<typeof setDropzoneModalVisibilityAction>;
type SetVersionRestoreVisibilityAction = ReturnType<typeof setVersionRestoreVisibilityAction>;
Expand All @@ -17,7 +17,7 @@ type SetBusyBlockingInfoAction = ReturnType<typeof setBusyBlockingInfoAction>;
type SetPythonClientModalVisibilityAction = ReturnType<typeof setPythonClientModalVisibilityAction>;
export type EnterAction = ReturnType<typeof enterAction>;
export type EscapeAction = ReturnType<typeof escapeAction>;
export type SetIsQuickSelectActiveAction = ReturnType<typeof setIsQuickSelectActiveAction>;
export type SetQuickSelectStateAction = ReturnType<typeof setQuickSelectStateAction>;
type ShowQuickSelectSettingsAction = ReturnType<typeof showQuickSelectSettingsAction>;

export type UiAction =
Expand All @@ -37,7 +37,7 @@ export type UiAction =
| SetBusyBlockingInfoAction
| EnterAction
| EscapeAction
| SetIsQuickSelectActiveAction
| SetQuickSelectStateAction
| ShowQuickSelectSettingsAction;

export const setDropzoneModalVisibilityAction = (visible: boolean) =>
Expand Down Expand Up @@ -121,12 +121,13 @@ export const escapeAction = () =>
({
type: "ESCAPE",
} as const);
export const setIsQuickSelectActiveAction = (isActive: boolean) =>
export const setQuickSelectStateAction = (
state: OxalisState["uiInformation"]["quickSelectState"],
) =>
({
type: "SET_IS_QUICK_SELECT_ACTIVE",
isActive,
type: "SET_QUICK_SELECT_STATE",
state,
} as const);

export const showQuickSelectSettingsAction = (isOpen: boolean) =>
({
type: "SET_ARE_QUICK_SELECT_SETTINGS_OPEN",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class BoundingBox {
constructor(boundingBox: BoundingBoxType | null | undefined, maxRestriction?: Vector3) {
this.boundingBox = boundingBox;
// Min is including
this.min = [0, 0, 0];
this.min = [-Infinity, -Infinity, -Infinity];
// Max is excluding
this.max = maxRestriction != null ? _.clone(maxRestriction) : [Infinity, Infinity, Infinity];

Expand All @@ -29,8 +29,8 @@ class BoundingBox {

getBoxForZoomStep = _.memoize((resolution: Vector3): BoundingBoxType => {
// No `map` for performance reasons
const min = [0, 0, 0];
const max = [0, 0, 0];
const min = [0, 0, 0] as Vector3;
const max = [0, 0, 0] as Vector3;

for (let i = 0; i < 3; i++) {
const divisor = constants.BUCKET_WIDTH * resolution[i];
Expand All @@ -39,9 +39,7 @@ class BoundingBox {
}

return {
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number[]' is not assignable to type 'Vector3... Remove this comment to see the full error message
min,
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number[]' is not assignable to type 'Vector3... Remove this comment to see the full error message
max,
};
});
Expand Down Expand Up @@ -163,23 +161,21 @@ class BoundingBox {
});
}

alignWithMag(mag: Vector3, ceil: boolean = false): BoundingBox {
alignWithMag(mag: Vector3, strategy: "shrink" | "grow" | "ceil" | "floor"): BoundingBox {
/*
* Rounds the bounding box, so that both min and max are divisible by mag.
* :argument ceil: If true, the bounding box is enlarged when necessary. If false, it's shrinked when necessary.
* The strategy parameter controls how the coordinates are rounded:
* - shrink: ceils `min` and floors `max`
* - grow: floors `min` and ceils `max`
* - ceil: ceils `min` and `max`
* - floor: floors `min` and `max`
*/
const align = (point: Vector3, round_fn: (vec: Vector3) => Vector3) =>
V3.scale3(round_fn(V3.divide3(point, mag)), mag);

if (ceil) {
const min = align(this.min, V3.floor);
const max = align(this.max, V3.ceil);
return new BoundingBox({ min, max });
} else {
const min = align(this.min, V3.ceil);
const max = align(this.max, V3.floor);
return new BoundingBox({ min, max });
}
const min = align(this.min, strategy === "ceil" || strategy === "shrink" ? V3.ceil : V3.floor);
const max = align(this.max, strategy === "floor" || strategy === "shrink" ? V3.floor : V3.ceil);
return new BoundingBox({ min, max });
}

/*
Expand Down
4 changes: 2 additions & 2 deletions frontend/javascripts/oxalis/model/reducers/ui_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ function UiReducer(state: OxalisState, action: Action): OxalisState {
});
}

case "SET_IS_QUICK_SELECT_ACTIVE": {
case "SET_QUICK_SELECT_STATE": {
return updateKey(state, "uiInformation", {
isQuickSelectActive: action.isActive,
quickSelectState: action.state,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,17 +519,14 @@ export function* finalizeQuickSelect(
size[secondDim],
);

console.time("fill voxel buffer");
for (let u = 0; u < size[firstDim]; u++) {
for (let v = 0; v < size[secondDim]; v++) {
if (mask.get(u, v, 0) > 0) {
voxelBuffer2D.setValue(u, v, 1);
}
}
}
console.timeEnd("fill voxel buffer");

console.time("label with voxel buffer");
yield* call(
labelWithVoxelBuffer2D,
voxelBuffer2D,
Expand All @@ -538,7 +535,6 @@ export function* finalizeQuickSelect(
labeledZoomStep,
activeViewport,
);
console.timeEnd("label with voxel buffer");
yield* put(finishAnnotationStrokeAction(volumeTracing.tracingId));
yield* put(registerLabelPointAction(boundingBoxMag1.getCenter()));
yield* put(
Expand Down
Loading