+ Start an AI background job to automatically detect and segment all nuclei in this
+ dataset. This AI will create a copy of this dataset containing all the detected nuclei
+ as a new segmentation layer.
+
+ {
+ if (!selectedBoundingBox) {
+ return Promise.resolve();
+ }
+
+ const bbox = computeArrayFromBoundingBox(selectedBoundingBox.boundingBox);
+ return startNeuronInferralJob(
+ dataset.owningOrganization,
+ dataset.name,
+ colorLayer.name,
+ bbox,
+ newDatasetName,
+ );
+ }}
+ description={
+ <>
+
+ Start an AI background job that automatically detects and segments all neurons in this
+ dataset. The AI will create a copy of this dataset containing the new neuron
+ segmentation.
+
+
+
+ Note that this feature is still experimental and processing can take a long time.
+ Thus, we suggest to use a small bounding box and not the full dataset extent. The
+ neuron detection currently only works with EM data. The best resolution for the
+ process will be chosen automatically.
+
+
+ >
+ }
+ />
+ );
+}
+
+type MaterializeVolumeAnnotationModalProps = Props & {
+ selectedVolumeLayer?: APIDataLayer;
+};
+
+export function MaterializeVolumeAnnotationModal({
+ selectedVolumeLayer,
+ handleClose,
+}: MaterializeVolumeAnnotationModalProps) {
+ const dataset = useSelector((state: OxalisState) => state.dataset);
+ const tracing = useSelector((state: OxalisState) => state.tracing);
+ const activeSegmentationTracingLayer = useSelector(getActiveSegmentationTracingLayer);
+ const fixedSelectedLayer = selectedVolumeLayer || activeSegmentationTracingLayer;
+ const readableVolumeLayerName =
+ fixedSelectedLayer && getReadableNameOfVolumeLayer(fixedSelectedLayer, tracing);
+ const hasFallbackLayer =
+ fixedSelectedLayer && "tracingId" in fixedSelectedLayer
+ ? fixedSelectedLayer.fallbackLayer != null
+ : false;
+ const isMergerModeEnabled = useSelector(
+ (state: OxalisState) => state.temporaryConfiguration.isMergerModeEnabled,
+ );
+ let description = (
+
+ Start a job that takes the current state of this volume annotation and materializes it into a
+ new dataset.
+ {hasFallbackLayer
+ ? ` All annotations done on the "${readableVolumeLayerName}" volume layer will be merged with the data of the fallback layer. `
+ : null}
+ {isMergerModeEnabled
+ ? " Since the merger mode is currently active, the segments connected via skeleton nodes will be merged within the new output dataset. "
+ : " "}
+ Please enter the name of the output dataset and the output segmentation layer.
+
+ );
+ if (tracing.volumes.length === 0) {
+ description = (
+
+ Start a job that takes the current state of this merger mode tracing and materializes it
+ into a new dataset. Since the merger mode is currently active, the segments connected via
+ skeleton nodes will be merged within the new output dataset. Please enter the name of the
+ output dataset and the output segmentation layer.
+
+ );
+ }
+
+ return (
+ {
+ if (outputSegmentationLayerName == null) {
+ return Promise.resolve();
+ }
+ const volumeLayerName = getReadableNameOfVolumeLayer(segmentationLayer, tracing);
+ const baseSegmentationName = getBaseSegmentationName(segmentationLayer);
+ return startMaterializingVolumeAnnotationJob(
+ dataset.owningOrganization,
+ dataset.name,
+ baseSegmentationName,
+ volumeLayerName,
+ newDatasetName,
+ outputSegmentationLayerName,
+ tracing.annotationId,
+ tracing.annotationType,
+ isMergerModeEnabled,
+ );
+ }}
+ description={description}
+ />
+ );
+}
diff --git a/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx b/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx
index 1ab2c7728d..2ccbe5374d 100644
--- a/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx
+++ b/frontend/javascripts/oxalis/view/action-bar/toolbar_view.tsx
@@ -48,7 +48,7 @@ import { updateUserSettingAction } from "oxalis/model/actions/settings_actions";
import { usePrevious, useKeyPress } from "libs/react_hooks";
import { userSettings } from "types/schemas/user_settings.schema";
import ButtonComponent from "oxalis/view/components/button_component";
-import { MaterializeVolumeAnnotationModal } from "oxalis/view/right-border-tabs/starting_job_modals";
+import { MaterializeVolumeAnnotationModal } from "oxalis/view/action-bar/starting_job_modals";
import {
ToolsWithOverwriteCapabilities,
AnnotationToolEnum,
diff --git a/frontend/javascripts/oxalis/view/action-bar/tracing_actions_view.tsx b/frontend/javascripts/oxalis/view/action-bar/tracing_actions_view.tsx
index c453db37e7..a06fa5cefa 100644
--- a/frontend/javascripts/oxalis/view/action-bar/tracing_actions_view.tsx
+++ b/frontend/javascripts/oxalis/view/action-bar/tracing_actions_view.tsx
@@ -1,4 +1,4 @@
-import { Button, Dropdown, Modal, Tooltip } from "antd";
+import { Button, Dropdown, Modal, Radio, Tooltip } from "antd";
import {
HistoryOutlined,
CheckCircleOutlined,
@@ -72,10 +72,7 @@ import UrlManager from "oxalis/controller/url_manager";
import { withAuthentication } from "admin/auth/authentication_modal";
import { PrivateLinksModal } from "./private_links_view";
import { ItemType, SubMenuType } from "antd/lib/menu/hooks/useItems";
-import {
- NeuronSegmentationModal,
- NucleiSegmentationModal,
-} from "../right-border-tabs/starting_job_modals";
+import { NeuronSegmentationModal, NucleiSegmentationModal } from "./starting_job_modals";
const AsyncButtonWithAuthentication = withAuthentication(
AsyncButton,
@@ -261,38 +258,51 @@ export function getLayoutMenu(props: LayoutMenuProps): SubMenuType {
export function getAISegmentationMenu(
isAINucleiSegmentationModalOpen: boolean,
isAINeuronSegmentationModalOpen: boolean,
-): [SubMenuType, React.ReactNode] {
- const AISegmentationMenu = {
- key: "ai-segmentation-menu",
- icon: ,
- label: "AI Segmentation",
- children: [
- // {
- // key: "ai-nuclei-segmentation",
- // label: "AI Nuclei Segmentation",
- // onClick: () => Store.dispatch(setAINucleiSegmentationModalVisibilityAction(true)),
- // },
- {
- key: "ai-neuron-segmentation",
- label: "AI Neuron Segmentation",
- onClick: () => Store.dispatch(setAINeuronSegmentationModalVisibilityAction(true)),
- },
- ],
- };
-
- const AISegmentationModals = isAINucleiSegmentationModalOpen ? (
- Store.dispatch(setAINucleiSegmentationModalVisibilityAction(false))}
- />
- ) : isAINeuronSegmentationModalOpen ? (
- Store.dispatch(setAINeuronSegmentationModalVisibilityAction(false))}
- />
+): React.ReactNode {
+ return isAINeuronSegmentationModalOpen ? (
+
+
+ Automated analysis with WEBKNOSSOS
+ >
+ }
+ onCancel={() => Store.dispatch(setAINeuronSegmentationModalVisibilityAction(false))}
+ >
+ Choose a processing job for your dataset:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* insert modal here */}
+
) : null;
-
- return [AISegmentationMenu, AISegmentationModals];
}
class TracingActionsView extends React.PureComponent {
@@ -675,11 +685,11 @@ class TracingActionsView extends React.PureComponent {
}
if (features().jobsEnabled) {
- const [AISegmentationMenu, AISegmentationModals] = getAISegmentationMenu(
+ debugger;
+ const AISegmentationModals = getAISegmentationMenu(
this.props.isAINucleiSegmentationModalOpen,
this.props.isAINeuronSegmentationModalOpen,
);
- menuItems.push(AISegmentationMenu);
modals.push(AISegmentationModals);
}
diff --git a/frontend/javascripts/oxalis/view/action-bar/view_dataset_actions_view.tsx b/frontend/javascripts/oxalis/view/action-bar/view_dataset_actions_view.tsx
index ac842e37e5..576f892511 100644
--- a/frontend/javascripts/oxalis/view/action-bar/view_dataset_actions_view.tsx
+++ b/frontend/javascripts/oxalis/view/action-bar/view_dataset_actions_view.tsx
@@ -36,6 +36,7 @@ export default function ViewDatasetActionsView(props: Props) {
const isAINucleiSegmentationModalOpen = useSelector(
(state: OxalisState) => state.uiInformation.showAINucleiSegmentationModal,
);
+ debugger;
const isAINeuronSegmentationModalOpen = useSelector(
(state: OxalisState) => state.uiInformation.showAINeuronSegmentationModal,
);
@@ -43,7 +44,7 @@ export default function ViewDatasetActionsView(props: Props) {
(state: OxalisState) => state.uiInformation.showPythonClientModal,
);
- const [AISegmentationMenu, AISegmentationModals] = getAISegmentationMenu(
+ const AISegmentationModals = getAISegmentationMenu(
isAINucleiSegmentationModalOpen,
isAINeuronSegmentationModalOpen,
);
@@ -79,7 +80,6 @@ export default function ViewDatasetActionsView(props: Props) {
icon: ,
label: "Download",
},
- isAISegmentationEnabled ? AISegmentationMenu : null,
props.layoutMenu,
],
};
diff --git a/frontend/javascripts/oxalis/view/action_bar_view.tsx b/frontend/javascripts/oxalis/view/action_bar_view.tsx
index 26c5a218b5..3070e12ffa 100644
--- a/frontend/javascripts/oxalis/view/action_bar_view.tsx
+++ b/frontend/javascripts/oxalis/view/action_bar_view.tsx
@@ -1,4 +1,4 @@
-import { Alert, Popover } from "antd";
+import { Alert, Popover, Tooltip } from "antd";
import { connect, useDispatch, useSelector } from "react-redux";
import * as React from "react";
import type { APIDataset, APIUser } from "types/api_flow_types";
@@ -36,6 +36,8 @@ import { setAdditionalCoordinatesAction } from "oxalis/model/actions/flycam_acti
import { NumberSliderSetting } from "./components/setting_input_views";
import { ArbitraryVectorInput } from "libs/vector_input";
import { type AdditionalCoordinate } from "types/api_flow_types";
+import ButtonComponent from "./components/button_component";
+import { setAINeuronSegmentationModalVisibilityAction } from "oxalis/model/actions/ui_actions";
const VersionRestoreWarning = (
{
location.href = `${location.origin}/annotations/${annotation.typ}/${annotation.id}${location.hash}`;
};
+ renderStartAIJobButton(): React.ReactNode {
+ return (
+ Store.dispatch(setAINeuronSegmentationModalVisibilityAction(true))}
+ style={{ marginLeft: 12 }}
+ >
+
+ AI Analysis
+
+
+ );
+ }
+
renderStartTracingButton(): React.ReactNode {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '(props: AsyncButtonProps) => Ele... Remove this comment to see the full error message
const ButtonWithAuthentication = withAuthentication(AsyncButton);
@@ -249,6 +265,7 @@ class ActionBarView extends React.PureComponent {
{isArbitrarySupported && !is2d ? : null}
+ {isViewMode ? null : this.renderStartAIJobButton()}
{!isReadOnly && constants.MODES_PLANE.indexOf(viewMode) > -1 ? : null}
{isViewMode ? this.renderStartTracingButton() : 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 58bae03a21..050ec7f4ab 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
@@ -110,7 +110,7 @@ import {
settings,
settingsTooltips,
} from "messages";
-import { MaterializeVolumeAnnotationModal } from "oxalis/view/right-border-tabs/starting_job_modals";
+import { MaterializeVolumeAnnotationModal } from "oxalis/view/action-bar/starting_job_modals";
import AddVolumeLayerModal, { validateReadableLayerName } from "./modals/add_volume_layer_modal";
import DownsampleVolumeModal from "./modals/downsample_volume_modal";
import Histogram, { isHistogramSupported } from "./histogram_view";
diff --git a/public/images/mito_detection.png b/public/images/mito_detection.png
new file mode 100644
index 0000000000..b06d27b08b
Binary files /dev/null and b/public/images/mito_detection.png differ
diff --git a/public/images/neuron_segmentation.png b/public/images/neuron_segmentation.png
new file mode 100644
index 0000000000..d72603ea56
Binary files /dev/null and b/public/images/neuron_segmentation.png differ
diff --git a/public/images/nuclei_inferral.png b/public/images/nuclei_inferral.png
new file mode 100644
index 0000000000..458ab615c9
Binary files /dev/null and b/public/images/nuclei_inferral.png differ