From 64d23766f07ca3788f7db0ce0e02d2f84d3fc141 Mon Sep 17 00:00:00 2001 From: Arthur Hilbert Date: Wed, 13 Jul 2022 10:11:15 +0200 Subject: [PATCH 01/14] remove old hybrid annotation workflow --- .../dataset_info_tab_view.tsx | 58 +------------------ 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/frontend/javascripts/oxalis/view/right-border-tabs/dataset_info_tab_view.tsx b/frontend/javascripts/oxalis/view/right-border-tabs/dataset_info_tab_view.tsx index 9dc048ab6e7..92768fe7f10 100644 --- a/frontend/javascripts/oxalis/view/right-border-tabs/dataset_info_tab_view.tsx +++ b/frontend/javascripts/oxalis/view/right-border-tabs/dataset_info_tab_view.tsx @@ -1,33 +1,27 @@ import type { Dispatch } from "redux"; import { Tooltip, Button, Dropdown, Menu } from "antd"; -import { SettingOutlined, InfoCircleOutlined, StarOutlined } from "@ant-design/icons"; +import { SettingOutlined, StarOutlined } from "@ant-design/icons"; import { connect } from "react-redux"; // @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module 'reac... Remove this comment to see the full error message import Markdown from "react-remarkable"; import React from "react"; import { Link } from "react-router-dom"; import type { APIDataset, APIUser } from "types/api_flow_types"; -import { APIAnnotationTypeEnum } from "types/api_flow_types"; import type { Vector3 } from "oxalis/constants"; import { ControlModeEnum } from "oxalis/constants"; -import { convertToHybridTracing } from "admin/admin_rest_api"; import { formatScale } from "libs/format_utils"; import { getBaseVoxel } from "oxalis/model/scaleinfo"; import { getDatasetExtentAsString, getResolutions, - getVisibleSegmentationLayer, } from "oxalis/model/accessors/dataset_accessor"; import { getCurrentResolution } from "oxalis/model/accessors/flycam_accessor"; import { getStats } from "oxalis/model/accessors/skeletontracing_accessor"; -import { location } from "libs/window"; import { setAnnotationNameAction, setAnnotationDescriptionAction, } from "oxalis/model/actions/annotation_actions"; -import ButtonComponent from "oxalis/view/components/button_component"; import EditableTextLabel from "oxalis/view/components/editable_text_label"; -import Model from "oxalis/model"; import features from "features"; import type { OxalisState, Task, Tracing } from "oxalis/store"; import Store from "oxalis/store"; @@ -476,55 +470,6 @@ class DatasetInfoTabView extends React.PureComponent { ); } - handleConvertToHybrid = async () => { - await Model.ensureSavedState(); - const maybeSegmentationLayer = getVisibleSegmentationLayer(Store.getState()); - await convertToHybridTracing( - this.props.tracing.annotationId, - maybeSegmentationLayer != null ? maybeSegmentationLayer.name : null, - ); - location.reload(); - }; - - getTracingType(isDatasetViewMode: boolean) { - if (isDatasetViewMode) return null; - const isSkeleton = this.props.tracing.skeleton != null; - const isVolume = this.props.tracing.volumes.length > 0; - const isHybrid = isSkeleton && isVolume; - const { allowUpdate } = this.props.tracing.restrictions; - const isExplorational = - this.props.tracing.annotationType === APIAnnotationTypeEnum.Explorational; - - if (isHybrid) { - return ( -

- Annotation Type:{" "} - - Hybrid - -

- ); - } else { - return ( -

- Annotation Type: {isVolume ? "Volume" : "Skeleton"} - {allowUpdate && isExplorational ? ( - - Convert to Hybrid - - ) : null} -

- ); - } - } - maybePrintOwner() { const { activeUser } = this.props; const { owner } = this.props.tracing; @@ -614,7 +559,6 @@ class DatasetInfoTabView extends React.PureComponent {
{this.getTracingName(isDatasetViewMode)} - {this.getTracingType(isDatasetViewMode)} {this.getDatasetName(isDatasetViewMode)} {this.maybePrintOwner()}
From 4ae8a0e3eecd225dc72386f3d2680b787ccfcffe Mon Sep 17 00:00:00 2001 From: Arthur Hilbert Date: Wed, 13 Jul 2022 10:12:31 +0200 Subject: [PATCH 02/14] add Add Skeleton button to Layers tab --- .../left-border-tabs/layer_settings_tab.tsx | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx b/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx index ec8e0cdd76b..7b937e0ea39 100644 --- a/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx +++ b/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx @@ -33,6 +33,7 @@ import { findDataPositionForLayer, clearCache, findDataPositionForVolumeTracing, + convertToHybridTracing, } from "admin/admin_rest_api"; import { getDefaultIntensityRangeOfLayer, @@ -42,6 +43,7 @@ import { getLayerByName, getResolutionInfo, getResolutions, + getVisibleSegmentationLayer, } from "oxalis/model/accessors/dataset_accessor"; import { getMaxZoomValueForResolution } from "oxalis/model/accessors/flycam_accessor"; import { @@ -897,6 +899,16 @@ class DatasetSettings extends React.PureComponent { }); }; + handleConvertToHybrid = async () => { + await Model.ensureSavedState(); + const maybeSegmentationLayer = getVisibleSegmentationLayer(Store.getState()); + await convertToHybridTracing( + this.props.tracing.annotationId, + maybeSegmentationLayer != null ? maybeSegmentationLayer.name : null, + ); + location.reload(); + }; + render() { const { layers } = this.props.datasetConfiguration; @@ -914,6 +926,10 @@ class DatasetSettings extends React.PureComponent { (el) => !el.isColorLayer, ).map((el) => this.getLayerSettings(el.layerName, el.layer, el.isColorLayer)); + const isSkeleton = this.props.tracing.skeleton != null; + const isVolume = this.props.tracing.volumes.length > 0; + const isHybrid = isSkeleton && isVolume; + return (
{layerSettings} @@ -924,7 +940,12 @@ class DatasetSettings extends React.PureComponent { <> - @@ -932,6 +953,21 @@ class DatasetSettings extends React.PureComponent { ) : null} + {this.props.tracing.restrictions.allowUpdate && !isHybrid ? ( + + + + ) : null} + {this.state.volumeTracingToDownsample != null ? ( Date: Wed, 13 Jul 2022 10:15:40 +0200 Subject: [PATCH 03/14] pretty and check for explorational --- .../view/left-border-tabs/layer_settings_tab.tsx | 11 +++++++++-- .../view/right-border-tabs/dataset_info_tab_view.tsx | 5 +---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx b/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx index 7b937e0ea39..5abf198decc 100644 --- a/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx +++ b/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx @@ -14,7 +14,12 @@ import React from "react"; import _ from "lodash"; import classnames from "classnames"; -import type { APIDataLayer, APIDataset, EditableLayerProperties } from "types/api_flow_types"; +import { + APIAnnotationTypeEnum, + APIDataLayer, + APIDataset, + EditableLayerProperties, +} from "types/api_flow_types"; import { ValueOf } from "types/globals"; import { AsyncIconButton } from "components/async_clickables"; import { @@ -929,6 +934,8 @@ class DatasetSettings extends React.PureComponent { const isSkeleton = this.props.tracing.skeleton != null; const isVolume = this.props.tracing.volumes.length > 0; const isHybrid = isSkeleton && isVolume; + const isExplorational = + this.props.tracing.annotationType === APIAnnotationTypeEnum.Explorational; return (
@@ -953,7 +960,7 @@ class DatasetSettings extends React.PureComponent { ) : null} - {this.props.tracing.restrictions.allowUpdate && !isHybrid ? ( + {!isHybrid && this.props.tracing.restrictions.allowUpdate && isExplorational ? (
diff --git a/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx b/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx index 52b20474e18..f1b4ba968b9 100644 --- a/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx +++ b/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx @@ -85,13 +85,13 @@ export default function AddVolumeLayerModal({ onCancel, tracing, preselectedLayerIndex, - showLayerSelectionDisabled, + disableLayerSelection, }: { dataset: APIDataset; onCancel: () => void; tracing: Tracing; - preselectedLayerIndex: number | null | undefined; - showLayerSelectionDisabled: boolean | null | undefined; + preselectedLayerIndex: number | undefined; + disableLayerSelection: boolean | undefined; }) { const [selectedSegmentationLayerIndex, setSelectedSegmentationLayerIndex] = useState< number | null | undefined @@ -194,9 +194,7 @@ export default function AddVolumeLayerModal({ segmentationLayers={availableSegmentationLayers} selectedSegmentationLayerIndex={selectedSegmentationLayerIndex} setSelectedSegmentationLayerIndex={setSelectedSegmentationLayerIndex} - showLayerSelectionDisabled={ - showLayerSelectionDisabled != null ? showLayerSelectionDisabled : false - } + disableLayerSelection={disableLayerSelection ?? false} /> ) : null} Date: Thu, 21 Jul 2022 11:04:41 +0200 Subject: [PATCH 13/14] code review changes 2/2 passing layer names instead of indices --- .../create_explorative_modal.tsx | 29 ++++++++++++------- .../left-border-tabs/layer_settings_tab.tsx | 18 ++++-------- .../modals/add_volume_layer_modal.tsx | 19 ++++++------ 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/frontend/javascripts/dashboard/advanced_dataset/create_explorative_modal.tsx b/frontend/javascripts/dashboard/advanced_dataset/create_explorative_modal.tsx index cab39b22cd7..4f6d8d53cfa 100644 --- a/frontend/javascripts/dashboard/advanced_dataset/create_explorative_modal.tsx +++ b/frontend/javascripts/dashboard/advanced_dataset/create_explorative_modal.tsx @@ -9,6 +9,7 @@ import { getSegmentationLayers, getResolutionInfo, ResolutionInfo, + getSegmentationLayerByName, } from "oxalis/model/accessors/dataset_accessor"; import { getDataset } from "admin/admin_rest_api"; import { useFetch } from "libs/react_helpers"; @@ -25,16 +26,22 @@ type RestrictResolutionSliderProps = { export function NewVolumeLayerSelection({ segmentationLayers, dataset, - selectedSegmentationLayerIndex, - setSelectedSegmentationLayerIndex, + selectedSegmentationLayerName, + setSelectedSegmentationLayerName, disableLayerSelection, }: { segmentationLayers: Array; dataset: APIDataset; - selectedSegmentationLayerIndex: number | null | undefined; - setSelectedSegmentationLayerIndex: (arg0: number | null | undefined) => void; + selectedSegmentationLayerName: string | null | undefined; + setSelectedSegmentationLayerName: (arg0: string | null | undefined) => void; disableLayerSelection: boolean | undefined; }) { + const selectedSegmentationLayerIndex = + selectedSegmentationLayerName != null + ? segmentationLayers.indexOf( + getSegmentationLayerByName(dataset, selectedSegmentationLayerName), + ) + : -1; return (
{ const index = parseInt(e.target.value); - setSelectedSegmentationLayerIndex(index !== -1 ? index : null); + setSelectedSegmentationLayerName(index !== -1 ? segmentationLayers[index].name : null); }} - value={selectedSegmentationLayerIndex != null ? selectedSegmentationLayerIndex : -1} + value={selectedSegmentationLayerIndex} disabled={disableLayerSelection ?? false} > @@ -157,7 +164,7 @@ function CreateExplorativeModal({ datasetId, onClose }: Props) { const dataset = useFetch(() => getDataset(datasetId), null, [datasetId]); const [annotationType, setAnnotationType] = useState("hybrid"); const [userDefinedResolutionIndices, setUserDefinedResolutionIndices] = useState([0, 10000]); - const [selectedSegmentationLayerIndex, setSelectedSegmentationLayerIndex] = useState(null); + const [selectedSegmentationLayerName, setSelectedSegmentationLayerName] = useState(null); let modalContent = ; if (dataset !== null) { @@ -165,8 +172,8 @@ function CreateExplorativeModal({ datasetId, onClose }: Props) { const selectedSegmentationLayer = annotationType !== "skeleton" && segmentationLayers.length > 0 && - selectedSegmentationLayerIndex != null - ? segmentationLayers[selectedSegmentationLayerIndex] + selectedSegmentationLayerName != null + ? getSegmentationLayerByName(dataset, selectedSegmentationLayerName) : null; const fallbackLayerGetParameter = selectedSegmentationLayer != null @@ -213,9 +220,9 @@ function CreateExplorativeModal({ datasetId, onClose }: Props) { >' is not assig... Remove this comment to see the full error message - setSelectedSegmentationLayerIndex={setSelectedSegmentationLayerIndex} + setSelectedSegmentationLayerName={setSelectedSegmentationLayerName} /> ) : null} diff --git a/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx b/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx index 8f9872290b8..ea9d6c66408 100644 --- a/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx +++ b/frontend/javascripts/oxalis/view/left-border-tabs/layer_settings_tab.tsx @@ -51,7 +51,6 @@ import { getLayerByName, getResolutionInfo, getResolutions, - getSegmentationLayers, } from "oxalis/model/accessors/dataset_accessor"; import { getMaxZoomValueForResolution } from "oxalis/model/accessors/flycam_accessor"; import { @@ -127,7 +126,7 @@ type State = { // is shown for that VolumeTracing volumeTracingToDownsample: VolumeTracing | null | undefined; isAddVolumeLayerModalVisible: boolean; - preselectedSegmentationLayerIndex: number | undefined; + preselectedSegmentationLayerName: string | undefined; segmentationLayerWasPreselected: boolean | undefined; layerToMergeWithFallback: APIDataLayer | null | undefined; }; @@ -137,7 +136,7 @@ class DatasetSettings extends React.PureComponent { state: State = { volumeTracingToDownsample: null, isAddVolumeLayerModalVisible: false, - preselectedSegmentationLayerIndex: undefined, + preselectedSegmentationLayerName: undefined, segmentationLayerWasPreselected: false, layerToMergeWithFallback: null, }; @@ -483,12 +482,10 @@ class DatasetSettings extends React.PureComponent { icon={} hoveredIcon={} onClick={() => { - const segmentationLayers = getSegmentationLayers(dataset); - const segmentationLayerIndex = segmentationLayers.indexOf(layer); this.setState({ isAddVolumeLayerModalVisible: true, segmentationLayerWasPreselected: true, - preselectedSegmentationLayerIndex: segmentationLayerIndex, + preselectedSegmentationLayerName: layer.name, }); }} /> @@ -932,16 +929,13 @@ class DatasetSettings extends React.PureComponent { this.setState({ isAddVolumeLayerModalVisible: false, segmentationLayerWasPreselected: false, - preselectedSegmentationLayerIndex: undefined, + preselectedSegmentationLayerName: undefined, }); }; addSkeletonAnnotationLayer = async () => { await Model.ensureSavedState(); - await convertToHybridTracing( - this.props.tracing.annotationId, - null, - ); + await convertToHybridTracing(this.props.tracing.annotationId, null); location.reload(); }; @@ -1025,7 +1019,7 @@ class DatasetSettings extends React.PureComponent { dataset={this.props.dataset} onCancel={this.hideAddVolumeLayerModal} tracing={this.props.tracing} - preselectedLayerIndex={this.state.preselectedSegmentationLayerIndex} + preselectedLayerName={this.state.preselectedSegmentationLayerName} disableLayerSelection={this.state.segmentationLayerWasPreselected} /> ) : null} diff --git a/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx b/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx index f1b4ba968b9..bef1ea439d8 100644 --- a/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx +++ b/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx @@ -12,6 +12,7 @@ import type { Tracing } from "oxalis/store"; import { addAnnotationLayer } from "admin/admin_rest_api"; import { getDatasetResolutionInfo, + getLayerByName, getSegmentationLayers, } from "oxalis/model/accessors/dataset_accessor"; import { @@ -84,18 +85,18 @@ export default function AddVolumeLayerModal({ dataset, onCancel, tracing, - preselectedLayerIndex, + preselectedLayerName, disableLayerSelection, }: { dataset: APIDataset; onCancel: () => void; tracing: Tracing; - preselectedLayerIndex: number | undefined; + preselectedLayerName: string | undefined; disableLayerSelection: boolean | undefined; }) { - const [selectedSegmentationLayerIndex, setSelectedSegmentationLayerIndex] = useState< - number | null | undefined - >(preselectedLayerIndex); + const [selectedSegmentationLayerName, setSelectedSegmentationLayerName] = useState< + string | null | undefined + >(preselectedLayerName); const allReadableLayerNames = useMemo( () => getAllReadableLayerNames(dataset, tracing), [dataset, tracing], @@ -141,7 +142,7 @@ export default function AddVolumeLayerModal({ ...datasetResolutionInfo.getResolutionByIndexOrThrow(resolutionIndices[1]), ); - if (selectedSegmentationLayerIndex == null) { + if (selectedSegmentationLayerName == null) { await addAnnotationLayer(tracing.annotationId, tracing.annotationType, { typ: "Volume", name: newLayerName, @@ -152,7 +153,7 @@ export default function AddVolumeLayerModal({ }, }); } else { - selectedSegmentationLayer = availableSegmentationLayers[selectedSegmentationLayerIndex]; + selectedSegmentationLayer = getLayerByName(dataset, selectedSegmentationLayerName); const fallbackLayerName = selectedSegmentationLayer.name; await addAnnotationLayer(tracing.annotationId, tracing.annotationType, { typ: "Volume", @@ -192,8 +193,8 @@ export default function AddVolumeLayerModal({ ) : null} From bd32c2222e89705bdbfed95566e236b33b9986b3 Mon Sep 17 00:00:00 2001 From: Arthur Hilbert Date: Thu, 21 Jul 2022 13:56:04 +0200 Subject: [PATCH 14/14] updated (unreleased) changelog --- CHANGELOG.unreleased.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index a3d183380e4..dc8f0afe843 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -11,11 +11,14 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released [Commits](https://github.com/scalableminds/webknossos/compare/22.08.0...HEAD) ### Added +- Segmentation layers which were not previously editable now show an (un)lock icon button which shortcuts to the Add Volume Layer modal with the layer being preselected. [#6330](https://github.com/scalableminds/webknossos/pull/6330) ### Changed +- The Layers tab now displays an Add Skeleton Annotation Layer button with which volume-only annotations can be converted to hybrid annotations. [#6330](https://github.com/scalableminds/webknossos/pull/6330) ### Fixed ### Removed +- Annotation Type was removed from the info tab. [#6330](https://github.com/scalableminds/webknossos/pull/6330) ### Breaking Changes