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

Unify segementation and color layers #4232

Merged
merged 8 commits into from
Aug 21, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md).
### Added
- Indicating active nml downloads with a loading icon. [#4228](https://github.com/scalableminds/webknossos/pull/4228)
- Added possibility for users to see their own time statistics. [#4220](https://github.com/scalableminds/webknossos/pull/4220)
- The segmentation layer can now be turned invisible and also supports the find data feature. [#4232](https://github.com/scalableminds/webknossos/pull/4232)

### Changed
- Each of the columns of the dataset table and explorative annotations table in the dashboard now have an individual fixed width, so the tables become scrollable on smaller screens. [#4207](https://github.com/scalableminds/webknossos/pull/4207)
- When uploading a zipped annotation (such as volume / hybrid / collection), the zip name is used for the resulting explorative annotation, rather than the name of the contained NML file. [#4222](https://github.com/scalableminds/webknossos/pull/4222)
- Color and segmentation layer are not longer treated separately in the dataset settings in tracing/view mode. [#4232](https://github.com/scalableminds/webknossos/pull/4232)

### Fixed
- Data for disabled or invisible layers will no longer be downloaded, saving bandwidth and speeding up webKnossos in general. [#4202](https://github.com/scalableminds/webknossos/pull/4202)
Expand Down
1 change: 0 additions & 1 deletion frontend/javascripts/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export const settings = {
fourBit: "4 Bit",
interpolation: "Interpolation",
quality: "Quality",
segmentationOpacity: "Segmentation Opacity",
highlightHoveredCellId: "Highlight Hovered Cells",
zoom: "Zoom",
renderMissingDataBlack: "Render Missing Data Black",
Expand Down
11 changes: 11 additions & 0 deletions frontend/javascripts/oxalis/controller/scene_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@ class SceneController {
}
}

setSegmentationVisibility(isVisible: boolean): void {
for (const plane of _.values(this.planes)) {
plane.setSegmentationVisibility(isVisible);
}
}

setIsMappingEnabled(isMappingEnabled: boolean): void {
for (const plane of _.values(this.planes)) {
plane.setIsMappingEnabled(isMappingEnabled);
Expand Down Expand Up @@ -457,6 +463,11 @@ class SceneController {
segmentationOpacity => this.setSegmentationAlpha(segmentationOpacity),
);

listenToStoreProperty(
storeState => storeState.datasetConfiguration.isSegmentationDisabled,
isSegmentationDisabled => this.setSegmentationVisibility(!isSegmentationDisabled),
);

listenToStoreProperty(
storeState => storeState.datasetConfiguration.interpolation,
interpolation => this.setInterpolation(interpolation),
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 @@ -39,6 +39,7 @@ const defaultState: OxalisState = {
interpolation: false,
layers: {},
quality: 0,
isSegmentationDisabled: false,
loadingStrategy: "PROGRESSIVE_QUALITY",
segmentationOpacity: 20,
highlightHoveredCellId: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,12 @@ class PlaneMaterialFactory {
this.uniforms.alpha.value = alpha / 100;
};

this.material.setSegmentationVisibility = isVisible => {
this.uniforms.alpha.value = isVisible
? Store.getState().datasetConfiguration.segmentationOpacity / 100
: 0;
};

this.material.setUseBilinearFiltering = isEnabled => {
this.uniforms.useBilinearFiltering.value = isEnabled;
};
Expand Down
4 changes: 4 additions & 0 deletions frontend/javascripts/oxalis/geometries/plane.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ class Plane {
this.plane.material.setSegmentationAlpha(alpha);
}

setSegmentationVisibility(isVisible: boolean): void {
this.plane.material.setSegmentationVisibility(isVisible);
}

getMeshes = () => [this.plane, this.TDViewBorders, this.crosshair[0], this.crosshair[1]];

setLinearInterpolationEnabled = (enabled: boolean) => {
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ export type DatasetConfiguration = {|
},
+quality: 0 | 1 | 2,
+segmentationOpacity: number,
+isSegmentationDisabled: boolean,
+highlightHoveredCellId: boolean,
+renderIsosurfaces: boolean,
+position?: Vector3,
Expand Down
226 changes: 133 additions & 93 deletions frontend/javascripts/oxalis/view/settings/dataset_settings_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @flow
*/

import { Col, Collapse, Divider, Icon, Row, Select, Switch, Tag, Tooltip } from "antd";
import { Col, Collapse, Icon, Row, Select, Switch, Tag, Tooltip } from "antd";
import type { Dispatch } from "redux";
import { connect } from "react-redux";
import * as React from "react";
Expand Down Expand Up @@ -126,79 +126,147 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps, State> {
});
};

getColorSettings = (
[layerName, layer]: [string, DatasetLayerConfiguration],
layerIndex: number,
isLastLayer: boolean,
) => {
const elementClass = getElementClass(this.props.dataset, layerName);
const { alpha, color, intensityRange, isDisabled } = layer;
getEnableDisableLayerSwitch = (
isDisabled: boolean,
onChange: (boolean, SyntheticMouseEvent<>) => void,
) => (
<Tooltip title={isDisabled ? "Enable" : "Disable"} placement="top">
{/* This div is necessary for the tooltip to be displayed */}
<div style={{ display: "inline-block", marginRight: 8 }}>
<Switch size="small" onChange={onChange} checked={!isDisabled} />
</div>
</Tooltip>
);

getHistogram = (layerName: string, layer: DatasetLayerConfiguration) => {
const { intensityRange } = layer;
let histograms = [
{ numberOfElements: 256, elementCounts: new Array(256).fill(0), min: 0, max: 255 },
];
if (this.state.histograms && this.state.histograms[layerName]) {
histograms = this.state.histograms[layerName];
}
return (
<Histogram
data={histograms}
min={intensityRange[0]}
max={intensityRange[1]}
layerName={layerName}
/>
);
};

getLayerSettingsHeader = (
isDisabled: boolean,
isColorLayer: boolean,
layerName: ?string,
elementClass: string,
) => {
let onChange = () => {};
if (isColorLayer && layerName != null) {
const layerNameFixed: string = layerName;
onChange = (value, event) => {
if (!event.ctrlKey) {
this.props.onChangeLayer(layerNameFixed, "isDisabled", !value);
return;
}
// If ctrl is pressed, toggle between "all layers visible" and
// "only selected layer visible".
// Phillip fragen wie das gehandhabt werden soll
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
if (this.isLayerExclusivelyVisible(layerNameFixed)) {
this.setVisibilityForAllLayers(true);
} else {
this.setVisibilityForAllLayers(false);
this.props.onChangeLayer(layerNameFixed, "isDisabled", false);
}
};
} else if (!isColorLayer) {
onChange = value => {
console.log("received value from segemntation visibility switch", !value);
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
this.props.onChange("isSegmentationDisabled", !value);
};
}
return (
<Row>
<Col span={24}>
{/* TODO Maybe use the new antd icons instead of the switch when upgrading antd. */
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
this.getEnableDisableLayerSwitch(isDisabled, onChange)}
<span style={{ fontWeight: 700 }}>{layerName}</span>
<Tag style={{ cursor: "default", marginLeft: 8 }}>{elementClass} Layer</Tag>
{layerName != null ? this.getFindDataButton(layerName, isDisabled) : null}
</Col>
</Row>
);
};

getLayerSettings = (
layerName: ?string,
layer: ?DatasetLayerConfiguration,
isColorLayer: boolean = true,
) => {
// Ensure that a color layer needs a layer and a layer name
if (isColorLayer && (!layerName || !layer)) {
return null;
}
if (!isColorLayer && !layerName) {
layerName = "segmentation";
}
let elementClass = "uint8";
if (layerName != null) {
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
elementClass = getElementClass(this.props.dataset, layerName);
}
const isDisabled =
isColorLayer && layer != null
? layer.isDisabled
: this.props.datasetConfiguration.isSegmentationDisabled;

return (
<div key={layerName}>
<Row>
<Col span={24}>
{/* TODO Maybe use the new antd icons instead of the switch when upgrading antd. */}
<Tooltip title={isDisabled ? "Enable" : "Disable"} placement="top">
{/* This div is necessary for the tooltip to be displayed */}
<div style={{ display: "inline-block", marginRight: 8 }}>
<Switch
size="small"
onChange={(val, event) => {
if (!event.ctrlKey) {
this.props.onChangeLayer(layerName, "isDisabled", !val);
return;
}
// If ctrl is pressed, toggle between "all layers visible" and
// "only selected layer visible".
if (this.isLayerExclusivelyVisible(layerName)) {
this.setVisibilityForAllLayers(true);
} else {
this.setVisibilityForAllLayers(false);
this.props.onChangeLayer(layerName, "isDisabled", false);
}
}}
checked={!isDisabled}
/>
</div>
</Tooltip>
<span style={{ fontWeight: 700 }}>{layerName}</span>
<Tag style={{ cursor: "default", marginLeft: 8 }}>{elementClass} Layer</Tag>
{this.getFindDataButton(layerName, isDisabled)}
</Col>
</Row>
{this.getLayerSettingsHeader(isDisabled, isColorLayer, layerName, elementClass)}
{isDisabled ? null : (
<React.Fragment>
{isHistogramSupported(elementClass) ? (
<Histogram
data={histograms}
min={intensityRange[0]}
max={intensityRange[1]}
layerName={layerName}
/>
) : null}
{isHistogramSupported(elementClass) && layerName != null && layer != null
? this.getHistogram(layerName, layer)
: null}
<NumberSliderSetting
label="Opacity"
min={0}
max={100}
value={alpha}
onChange={_.partial(this.props.onChangeLayer, layerName, "alpha")}
/>
<ColorSetting
label="Color"
value={Utils.rgbToHex(color)}
onChange={_.partial(this.props.onChangeLayer, layerName, "color")}
className="ant-btn"
value={
isColorLayer && layer != null
? layer.alpha
: this.props.datasetConfiguration.segmentationOpacity
}
onChange={
isColorLayer && layerName != null
? _.partial(this.props.onChangeLayer, layerName, "alpha")
: _.partial(this.props.onChange, "segmentationOpacity")
}
/>
{isColorLayer && layer != null ? (
<ColorSetting
label="Color"
value={Utils.rgbToHex(layer.color)}
onChange={_.partial(this.props.onChangeLayer, layerName, "color")}
className="ant-btn"
/>
) : (
<SwitchSetting
label={settings.highlightHoveredCellId}
value={this.props.datasetConfiguration.highlightHoveredCellId}
onChange={_.partial(this.props.onChange, "highlightHoveredCellId")}
/>
)}
{!isColorLayer &&
(this.props.controlMode === ControlModeEnum.VIEW || window.allowIsosurfaces) ? (
<SwitchSetting
label="Render Isosurfaces (Beta)"
value={this.props.datasetConfiguration.renderIsosurfaces}
onChange={_.partial(this.props.onChange, "renderIsosurfaces")}
/>
) : null}
</React.Fragment>
)}
{!isLastLayer && <Divider />}
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
</div>
);
};
Expand Down Expand Up @@ -240,66 +308,38 @@ class DatasetSettings extends React.PureComponent<DatasetSettingsProps, State> {
getSegmentationPanel() {
const segmentation = Model.getSegmentationLayer();
const segmentationLayerName = segmentation != null ? segmentation.name : null;
return (
<Panel header="Segmentation" key="2">
{segmentationLayerName ? (
<div style={{ overflow: "auto" }}>
{this.getFindDataButton(segmentationLayerName, false)}
</div>
) : null}
<NumberSliderSetting
label={settings.segmentationOpacity}
min={0}
max={100}
value={this.props.datasetConfiguration.segmentationOpacity}
onChange={_.partial(this.props.onChange, "segmentationOpacity")}
/>
<SwitchSetting
label={settings.highlightHoveredCellId}
value={this.props.datasetConfiguration.highlightHoveredCellId}
onChange={_.partial(this.props.onChange, "highlightHoveredCellId")}
/>

{this.props.controlMode === ControlModeEnum.VIEW || window.allowIsosurfaces ? (
<SwitchSetting
label="Render Isosurfaces (Beta)"
value={this.props.datasetConfiguration.renderIsosurfaces}
onChange={_.partial(this.props.onChange, "renderIsosurfaces")}
/>
) : null}
</Panel>
);
return this.getLayerSettings(segmentationLayerName, null, false);
MichaelBuessemeyer marked this conversation as resolved.
Show resolved Hide resolved
}

renderPanelHeader = (hasInvisibleLayers: boolean) =>
hasInvisibleLayers ? (
<span>
Color Layers
Layers
<Tooltip title="Not all layers are currently visible.">
<Icon type="exclamation-circle-o" style={{ marginLeft: 16, color: "coral" }} />
</Tooltip>
</span>
) : (
"Color Layers"
"Layers"
);

render() {
const { layers } = this.props.datasetConfiguration;
const colorSettings = Object.entries(layers).map((entry, index) =>
const colorSettings = Object.entries(layers).map(entry => {
const [layerName, layer] = entry;
// $FlowFixMe Object.entries returns mixed for Flow
this.getColorSettings(entry, index, index === _.size(layers) - 1),
);
return this.getLayerSettings(layerName, layer, true);
});
const hasInvisibleLayers =
Object.keys(layers).find(
layerName => layers[layerName].isDisabled || layers[layerName].alpha === 0,
) != null;

) != null || this.props.datasetConfiguration.isSegmentationDisabled;
return (
<Collapse bordered={false} defaultActiveKey={["1", "2", "3", "4"]}>
<Panel header={this.renderPanelHeader(hasInvisibleLayers)} key="1">
{colorSettings}
{this.props.hasSegmentation ? this.getSegmentationPanel() : null}
</Panel>
{this.props.hasSegmentation ? this.getSegmentationPanel() : null}
<Panel header="Data Rendering" key="3">
<DropdownSetting
label={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const datasetConfigOverrides: { [key: string]: DatasetConfiguration } = {
},
quality: 0,
segmentationOpacity: 0,
isSegmentationDisabled: false,
highlightHoveredCellId: true,
renderIsosurfaces: false,
renderMissingDataBlack: false,
Expand Down