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

Add (minimal) UI for auto brush and allow it for white-listed datasets #4053

Merged
merged 8 commits into from May 6, 2019
1 change: 1 addition & 0 deletions conf/application.conf
Expand Up @@ -93,6 +93,7 @@ features {
# if it is false, / will be the login page, if not logged in, and the dashboard otherwise
enableFrontpage = false
isDemoInstance = false
autoBrushReadyDatasets = ["dsA"]
}

# Actor settings
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/admin/api_flow_types.js
Expand Up @@ -496,6 +496,7 @@ export type APIFeatureToggles = {
+hideNavbarLogin: boolean,
+addMissingDatasetButtonEnabled: boolean,
+enableFrontpage: boolean,
autoBrushReadyDatasets: Array<string>,
};

// Tracing related datatypes
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/libs/toast.js
Expand Up @@ -90,7 +90,7 @@ const Toast = {
notification[type](toastConfig);
},

info(message: string, config: ToastConfig = {}): void {
info(message: string | React$Element<any>, config: ToastConfig = {}): void {
return this.message("info", message, config);
},

Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/messages.js
Expand Up @@ -36,6 +36,7 @@ export const settings = {
but it will take more time until the best quality is shown).`,
mergerMode: "Enable Merger Mode",
gpuMemoryFactor: "Quality",
autoBrush: "Automatic Brush (Beta)",
};

export default {
Expand Down
Expand Up @@ -39,6 +39,8 @@ import * as Utils from "libs/utils";

// TODO: Build proper UI for this
window.isAutomaticBrushEnabled = false;
const isAutomaticBrushEnabled = () =>
window.isAutomaticBrushEnabled || Store.getState().temporaryConfiguration.isAutoBrushEnabled;

// eslint-disable-next-line no-unused-vars
const simulateTracing = async (): Promise<void> => {
Expand Down Expand Up @@ -95,7 +97,7 @@ export function getPlaneMouseControls(_planeId: OrthoView): * {

if (!event.shiftKey && (tool === VolumeToolEnum.TRACE || tool === VolumeToolEnum.BRUSH)) {
if (event.ctrlKey) {
if (window.isAutomaticBrushEnabled) {
if (isAutomaticBrushEnabled()) {
return;
}
Store.dispatch(setContourTracingMode(ContourModeEnum.DRAW));
Expand Down Expand Up @@ -169,7 +171,7 @@ export function getPlaneMouseControls(_planeId: OrthoView): * {
Store.dispatch(setActiveCellAction(cellId));
}
} else if (event.ctrlKey) {
if (window.isAutomaticBrushEnabled) {
if (isAutomaticBrushEnabled()) {
Store.dispatch(inferSegmentationInViewportAction(calculateGlobalPos(pos)));
}
}
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/default_state.js
Expand Up @@ -87,6 +87,7 @@ const defaultState: OxalisState = {
mappingSize: 0,
},
isMergerModeEnabled: false,
isAutoBrushEnabled: false,
gpuSetup: {
smallestCommonBucketCapacity:
Constants.GPU_FACTOR_MULTIPLIER * Constants.DEFAULT_GPU_MEMORY_FACTOR,
Expand Down
23 changes: 19 additions & 4 deletions frontend/javascripts/oxalis/model/sagas/automatic_brush_saga.js
@@ -1,7 +1,7 @@
// @flow
import * as tf from "@tensorflow/tfjs";
import memoizeOne from "memoize-one";

import React from "react";
import { type Saga, call, fork, select, take, _cancel } from "oxalis/model/sagas/effect-generators";
import floodfill from "libs/floodfill";
import { FlycamActions } from "oxalis/model/actions/flycam_actions";
Expand All @@ -18,6 +18,7 @@ import Model from "oxalis/model";
import Toast from "libs/toast";
import { enforceVolumeTracing } from "oxalis/model/accessors/volumetracing_accessor";
import Dimensions from "oxalis/model/dimensions";
import Shortcut from "libs/shortcut_component";
import api from "oxalis/api/internal_api";
import {
getPosition,
Expand Down Expand Up @@ -138,9 +139,21 @@ export default function* inferSegmentInViewport(
sticky: true,
key: "automatic-brush",
};
const toastDescription = "Automatic brush is active. Close the toast to stop using it.";
const getEscapableToast = text => (
<div>
{text}
<Shortcut
keys="esc"
onTrigger={() => {
aborted = true;
Toast.close(toastConfig.key);
}}
/>
</div>
);

Toast.info(toastDescription, toastConfig);
const brushIsActiveText = "Automatic brush is active. Close the toast to stop using it.";
Toast.info(getEscapableToast(brushIsActiveText), toastConfig);

const colorLayers = yield* call([Model, Model.getColorLayers]);
const colorLayerName = colorLayers[0].name;
Expand Down Expand Up @@ -209,7 +222,9 @@ export default function* inferSegmentInViewport(
console.time("predict");
const thirdDimension = Dimensions.thirdDimensionForPlane(activeViewport);
Toast.info(
`${toastDescription}\nLabeling ${["x", "y", "z"][thirdDimension]}=${z}.`,
getEscapableToast(
`${brushIsActiveText}\nLabeling ${["x", "y", "z"][thirdDimension]}=${z}.`,
),
toastConfig,
);
predictions.set(
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/store.js
Expand Up @@ -286,6 +286,7 @@ export type TemporaryConfiguration = {
+mappingSize: number,
},
+isMergerModeEnabled: boolean,
+isAutoBrushEnabled: boolean,
+gpuSetup: {
// These rendering-related variables are set up
// during startup and cannot change (with the current
Expand Down
43 changes: 42 additions & 1 deletion frontend/javascripts/oxalis/view/settings/user_settings_view.js
Expand Up @@ -3,6 +3,7 @@
* @flow
*/

import features from "features";
import { Collapse } from "antd";
import type { Dispatch } from "redux";
import { connect } from "react-redux";
Expand Down Expand Up @@ -37,14 +38,18 @@ import {
import { setUserBoundingBoxAction } from "oxalis/model/actions/annotation_actions";
import { setZoomStepAction } from "oxalis/model/actions/flycam_actions";
import { settings as settingsLabels } from "messages";
import { updateUserSettingAction } from "oxalis/model/actions/settings_actions";
import {
updateTemporarySettingAction,
updateUserSettingAction,
} from "oxalis/model/actions/settings_actions";
import { userSettings } from "libs/user_settings.schema";
import Constants, {
type ControlMode,
ControlModeEnum,
type ViewMode,
type Vector6,
} from "oxalis/constants";
import Toast from "libs/toast";
import * as Utils from "libs/utils";

import MergerModeModalView from "./merger_mode_modal_view";
Expand All @@ -64,7 +69,9 @@ type UserSettingsViewProps = {
onChangeRadius: (value: number) => void,
onChangeZoomStep: (value: number) => void,
onChangeEnableMergerMode: (active: boolean) => void,
onChangeEnableAutoBrush: (active: boolean) => void,
isMergerModeEnabled: boolean,
isAutoBrushEnabled: boolean,
viewMode: ViewMode,
controlMode: ControlMode,
dataset: APIDataset,
Expand Down Expand Up @@ -109,6 +116,15 @@ class UserSettingsView extends PureComponent<UserSettingsViewProps, State> {
}
};

handleAutoBrushChange = async (active: boolean) => {
this.props.onChangeEnableAutoBrush(active);
if (active) {
Toast.info(
"You enabled the experimental automatic brush feature. Activate the brush tool and use CTRL+Click to use it.",
);
}
};

getViewportOptions = () => {
switch (this.props.viewMode) {
case Constants.MODE_PLANE_TRACING:
Expand Down Expand Up @@ -321,6 +337,7 @@ class UserSettingsView extends PureComponent<UserSettingsViewProps, State> {
value={this.props.userConfiguration.brushSize}
onChange={this.onChangeUser.brushSize}
/>
{this.maybeGetAutoBrushUi()}
<NumberInputSetting
label="Active Cell ID"
value={volumeTracing.activeCellId}
Expand All @@ -332,6 +349,26 @@ class UserSettingsView extends PureComponent<UserSettingsViewProps, State> {
return panels;
};

maybeGetAutoBrushUi() {
const { autoBrushReadyDatasets } = features();
if (
autoBrushReadyDatasets == null ||
!autoBrushReadyDatasets.includes(this.props.dataset.name)
) {
return null;
}

return (
<SwitchSetting
label={settingsLabels.autoBrush}
value={this.props.isAutoBrushEnabled}
onChange={value => {
this.handleAutoBrushChange(value);
}}
/>
);
}

render() {
const { isMergerModeModalVisible, isMergerModeModalClosable } = this.state;
const moveValueSetting = Constants.MODES_ARBITRARY.includes(this.props.viewMode) ? (
Expand Down Expand Up @@ -409,6 +446,7 @@ const mapStateToProps = (state: OxalisState) => ({
viewMode: state.temporaryConfiguration.viewMode,
controlMode: state.temporaryConfiguration.controlMode,
isMergerModeEnabled: state.temporaryConfiguration.isMergerModeEnabled,
isAutoBrushEnabled: state.temporaryConfiguration.isAutoBrushEnabled,
dataset: state.dataset,
});

Expand Down Expand Up @@ -437,6 +475,9 @@ const mapDispatchToProps = (dispatch: Dispatch<*>) => ({
onChangeEnableMergerMode(active: boolean) {
dispatch(setMergerModeEnabledAction(active));
},
onChangeEnableAutoBrush(active: boolean) {
dispatch(updateTemporarySettingAction("isAutoBrushEnabled", active));
},
});

export default connect<UserSettingsViewProps, {||}, _, _, _, _>(
Expand Down