From 9cdad95f488a21011d532814082119403ce594f5 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Fri, 2 Jun 2023 17:26:05 +1000
Subject: [PATCH] feat(ui): add rest of controlnet processors
---
.../middleware/listenerMiddleware/index.ts | 2 +
.../listeners/controlNetImageProcessed.ts | 23 +-
.../controlNetProcessorParamsChanged.ts | 27 ++
.../controlNet/components/ControlNet.tsx | 105 +++---
.../ControlNetProcessorCollapse.tsx | 76 -----
.../components/ProcessorComponent.tsx | 131 +++++++
.../hooks/useProcessorNodeChanged.ts | 20 ++
.../ParamControlNetProcessorSelect.tsx | 46 +++
.../components/processors/CannyProcessor.tsx | 86 ++---
.../processors/ContentShuffleProcessor.tsx | 98 ++++++
.../components/processors/HedProcessor.tsx | 55 ++-
.../processors/LineartAnimeProcessor.tsx | 40 ++-
.../processors/LineartProcessor.tsx | 54 ++-
.../processors/MediapipeFaceProcessor.tsx | 57 ++++
.../processors/MidasDepthProcessor.tsx | 55 +++
.../processors/MlsdImageProcessor.tsx | 85 +++++
.../processors/NormalBaeProcessor.tsx | 53 +++
.../processors/OpenposeProcessor.tsx | 66 ++++
.../components/processors/PidiProcessor.tsx | 74 ++++
.../processors/ZoeDepthProcessor.tsx | 14 +
.../common/ControlNetProcessorButtons.tsx | 18 +-
.../src/features/controlNet/store/actions.ts | 2 -
.../features/controlNet/store/constants.ts | 166 +++++++++
.../controlNet/store/controlNetSlice.ts | 67 ++--
.../src/features/controlNet/store/types.ts | 323 +++++++++++++++++-
.../graphBuilders/buildTextToImageGraph.ts | 6 +-
26 files changed, 1458 insertions(+), 291 deletions(-)
create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetProcessorParamsChanged.ts
delete mode 100644 invokeai/frontend/web/src/features/controlNet/components/ControlNetProcessorCollapse.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/ProcessorComponent.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/hooks/useProcessorNodeChanged.ts
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetProcessorSelect.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/ContentShuffleProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/MediapipeFaceProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/MidasDepthProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/MlsdImageProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/NormalBaeProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/OpenposeProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/PidiProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/components/processors/ZoeDepthProcessor.tsx
create mode 100644 invokeai/frontend/web/src/features/controlNet/store/constants.ts
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
index 7089707217..9d938755f0 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
@@ -71,6 +71,7 @@ import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSa
import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener';
import { addImageCategoriesChangedListener } from './listeners/imageCategoriesChanged';
import { addControlNetImageProcessedListener } from './listeners/controlNetImageProcessed';
+import { addControlNetProcessorParamsChangedListener } from './listeners/controlNetProcessorParamsChanged';
export const listenerMiddleware = createListenerMiddleware();
@@ -177,3 +178,4 @@ addImageCategoriesChangedListener();
// ControlNet
addControlNetImageProcessedListener();
+addControlNetProcessorParamsChangedListener();
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts
index 901cb99bef..00cc2d2474 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts
@@ -8,6 +8,7 @@ import { sessionReadyToInvoke } from 'features/system/store/actions';
import { socketInvocationComplete } from 'services/events/actions';
import { isImageOutput } from 'services/types/guards';
import { controlNetProcessedImageChanged } from 'features/controlNet/store/controlNetSlice';
+import { pick } from 'lodash-es';
const moduleLog = log.child({ namespace: 'controlNet' });
@@ -15,11 +16,27 @@ export const addControlNetImageProcessedListener = () => {
startAppListening({
actionCreator: controlNetImageProcessed,
effect: async (action, { dispatch, getState, take }) => {
- const { controlNetId, processorNode } = action.payload;
+ const { controlNetId } = action.payload;
+ const controlNet = getState().controlNet.controlNets[controlNetId];
- // ControlNet one-off procressing graph is just he processor node, no edges
+ if (!controlNet.controlImage) {
+ moduleLog.error('Unable to process ControlNet image');
+ return;
+ }
+
+ // ControlNet one-off procressing graph is just the processor node, no edges.
+ // Also we need to grab the image.
const graph: Graph = {
- nodes: { [processorNode.id]: processorNode },
+ nodes: {
+ [controlNet.processorNode.id]: {
+ ...controlNet.processorNode,
+ is_intermediate: true,
+ image: pick(controlNet.controlImage, [
+ 'image_name',
+ 'image_origin',
+ ]),
+ },
+ },
};
// Create a session to run the graph & wait til it's ready to invoke
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetProcessorParamsChanged.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetProcessorParamsChanged.ts
new file mode 100644
index 0000000000..315b793e53
--- /dev/null
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetProcessorParamsChanged.ts
@@ -0,0 +1,27 @@
+import { startAppListening } from '..';
+import { log } from 'app/logging/useLogger';
+import { controlNetImageProcessed } from 'features/controlNet/store/actions';
+import {
+ controlNetProcessorParamsChanged,
+ controlNetProcessorTypeChanged,
+} from 'features/controlNet/store/controlNetSlice';
+
+const moduleLog = log.child({ namespace: 'controlNet' });
+
+export const addControlNetProcessorParamsChangedListener = () => {
+ startAppListening({
+ predicate: (action) =>
+ controlNetProcessorParamsChanged.match(action) ||
+ controlNetProcessorTypeChanged.match(action),
+ effect: async (action, { dispatch, cancelActiveListeners, delay }) => {
+ const { controlNetId } = action.payload;
+ // Cancel any in-progress instances of this listener
+ cancelActiveListeners();
+
+ // Delay before starting actual work
+ await delay(1000);
+
+ dispatch(controlNetImageProcessed({ controlNetId }));
+ },
+ });
+};
diff --git a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx
index 0626b08fd9..b9b8e77fcc 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/ControlNet.tsx
@@ -1,51 +1,33 @@
-import { memo, useCallback, useState } from 'react';
-import { ControlNetProcessorNode } from '../store/types';
+import { memo, useCallback } from 'react';
+import { RequiredControlNetProcessorNode } from '../store/types';
import { ImageDTO } from 'services/api';
import CannyProcessor from './processors/CannyProcessor';
import {
- CONTROLNET_PROCESSORS,
ControlNet,
- ControlNetModel,
- ControlNetProcessor,
- controlNetBeginStepPctChanged,
- controlNetEndStepPctChanged,
controlNetImageChanged,
- controlNetModelChanged,
controlNetProcessedImageChanged,
- controlNetProcessorChanged,
controlNetRemoved,
- controlNetToggled,
- controlNetWeightChanged,
- isControlNetImageProcessedToggled,
} from '../store/controlNetSlice';
import { useAppDispatch } from 'app/store/storeHooks';
-import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
-import IAISlider from 'common/components/IAISlider';
-import ParamControlNetIsEnabled from './parameters/ParamControlNetIsEnabled';
import ParamControlNetModel from './parameters/ParamControlNetModel';
import ParamControlNetWeight from './parameters/ParamControlNetWeight';
import ParamControlNetBeginStepPct from './parameters/ParamControlNetBeginStepPct';
import ParamControlNetEndStepPct from './parameters/ParamControlNetEndStepPct';
import {
- Box,
Flex,
- HStack,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
- VStack,
- useDisclosure,
} from '@chakra-ui/react';
import IAISelectableImage from './parameters/IAISelectableImage';
import IAIButton from 'common/components/IAIButton';
-import IAIIconButton from 'common/components/IAIIconButton';
-import IAISwitch from 'common/components/IAISwitch';
-import ParamControlNetIsPreprocessed from './parameters/ParamControlNetIsPreprocessed';
-import IAICollapse from 'common/components/IAICollapse';
-import ControlNetProcessorCollapse from './ControlNetProcessorCollapse';
-import IAICustomSelect from 'common/components/IAICustomSelect';
+import { controlNetImageProcessed } from '../store/actions';
+import { FaUndo } from 'react-icons/fa';
+import HedProcessor from './processors/HedProcessor';
+import ParamControlNetProcessorSelect from './parameters/ParamControlNetProcessorSelect';
+import ProcessorComponent from './ProcessorComponent';
type ControlNetProps = {
controlNet: ControlNet;
@@ -62,22 +44,10 @@ const ControlNet = (props: ControlNetProps) => {
controlImage,
isControlImageProcessed,
processedControlImage,
- processor,
+ processorNode,
} = props.controlNet;
const dispatch = useAppDispatch();
- const handleProcessorTypeChanged = useCallback(
- (processor: string | null | undefined) => {
- dispatch(
- controlNetProcessorChanged({
- controlNetId,
- processor: processor as ControlNetProcessor,
- })
- );
- },
- [controlNetId, dispatch]
- );
-
const handleControlImageChanged = useCallback(
(controlImage: ImageDTO) => {
dispatch(controlNetImageChanged({ controlNetId, controlImage }));
@@ -85,6 +55,23 @@ const ControlNet = (props: ControlNetProps) => {
[controlNetId, dispatch]
);
+ const handleProcess = useCallback(() => {
+ dispatch(
+ controlNetImageProcessed({
+ controlNetId,
+ })
+ );
+ }, [controlNetId, dispatch]);
+
+ const handleReset = useCallback(() => {
+ dispatch(
+ controlNetProcessedImageChanged({
+ controlNetId,
+ processedControlImage: null,
+ })
+ );
+ }, [controlNetId, dispatch]);
+
const handleControlImageReset = useCallback(() => {
dispatch(controlNetImageChanged({ controlNetId, controlImage: null }));
}, [controlNetId, dispatch]);
@@ -137,18 +124,29 @@ const ControlNet = (props: ControlNetProps) => {
/>
-
+
+ Preprocess
+
+ }
+ onClick={handleReset}
+ isDisabled={Boolean(!processedControlImage)}
+ >
+ Reset Processing
+
@@ -158,18 +156,3 @@ const ControlNet = (props: ControlNetProps) => {
};
export default memo(ControlNet);
-
-export type ControlNetProcessorProps = {
- controlNetId: string;
- controlImage: ImageDTO | null;
- processedControlImage: ImageDTO | null;
- type: ControlNetProcessor;
-};
-
-const ProcessorComponent = (props: ControlNetProcessorProps) => {
- const { type } = props;
- if (type === 'canny') {
- return ;
- }
- return null;
-};
diff --git a/invokeai/frontend/web/src/features/controlNet/components/ControlNetProcessorCollapse.tsx b/invokeai/frontend/web/src/features/controlNet/components/ControlNetProcessorCollapse.tsx
deleted file mode 100644
index 0ce675f4ed..0000000000
--- a/invokeai/frontend/web/src/features/controlNet/components/ControlNetProcessorCollapse.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-// import { Collapse, Flex, useDisclosure } from '@chakra-ui/react';
-// import { memo, useState } from 'react';
-// import CannyProcessor from './processors/CannyProcessor';
-// import { ImageDTO } from 'services/api';
-// import IAICustomSelect from 'common/components/IAICustomSelect';
-// import {
-// CONTROLNET_PROCESSORS,
-// ControlNetProcessor,
-// } from '../store/controlNetSlice';
-// import IAISwitch from 'common/components/IAISwitch';
-
-// export type ControlNetProcessorProps = {
-// controlNetId: string;
-// controlImage: ImageDTO | null;
-// processedControlImage: ImageDTO | null;
-// type: ControlNetProcessor;
-// };
-
-// const ProcessorComponent = (props: ControlNetProcessorProps) => {
-// const { type } = props;
-// if (type === 'canny') {
-// return ;
-// }
-// return null;
-// };
-
-// type ControlNetProcessorCollapseProps = {
-// isOpen: boolean;
-// controlNetId: string;
-// controlImage: ImageDTO | null;
-// processedControlImage: ImageDTO | null;
-// };
-// const ControlNetProcessorCollapse = (
-// props: ControlNetProcessorCollapseProps
-// ) => {
-// const { isOpen, controlImage, controlNetId, processedControlImage } = props;
-
-// const [processorType, setProcessorType] =
-// useState('canny');
-
-// const handleProcessorTypeChanged = (type: string | null | undefined) => {
-// setProcessorType(type as ControlNetProcessor);
-// };
-
-// return (
-//
-//
-// {controlImage && (
-//
-// )}
-//
-// );
-// };
-
-// export default memo(ControlNetProcessorCollapse);
-
-export default {};
diff --git a/invokeai/frontend/web/src/features/controlNet/components/ProcessorComponent.tsx b/invokeai/frontend/web/src/features/controlNet/components/ProcessorComponent.tsx
new file mode 100644
index 0000000000..246aea70c7
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/ProcessorComponent.tsx
@@ -0,0 +1,131 @@
+import { memo } from 'react';
+import { RequiredControlNetProcessorNode } from '../store/types';
+import CannyProcessor from './processors/CannyProcessor';
+import HedProcessor from './processors/HedProcessor';
+import LineartProcessor from './processors/LineartProcessor';
+import LineartAnimeProcessor from './processors/LineartAnimeProcessor';
+import ContentShuffleProcessor from './processors/ContentShuffleProcessor';
+import MediapipeFaceProcessor from './processors/MediapipeFaceProcessor';
+import MidasDepthProcessor from './processors/MidasDepthProcessor';
+import MlsdImageProcessor from './processors/MlsdImageProcessor';
+import NormalBaeProcessor from './processors/NormalBaeProcessor';
+import OpenposeProcessor from './processors/OpenposeProcessor';
+import PidiProcessor from './processors/PidiProcessor';
+import ZoeDepthProcessor from './processors/ZoeDepthProcessor';
+
+export type ControlNetProcessorProps = {
+ controlNetId: string;
+ processorNode: RequiredControlNetProcessorNode;
+};
+
+const ProcessorComponent = (props: ControlNetProcessorProps) => {
+ const { controlNetId, processorNode } = props;
+ if (processorNode.type === 'canny_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'hed_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'lineart_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'content_shuffle_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'lineart_anime_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'mediapipe_face_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'midas_depth_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'mlsd_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'normalbae_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'openpose_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'pidi_image_processor') {
+ return (
+
+ );
+ }
+
+ if (processorNode.type === 'zoe_depth_image_processor') {
+ return (
+
+ );
+ }
+
+ return null;
+};
+
+export default memo(ProcessorComponent);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/hooks/useProcessorNodeChanged.ts b/invokeai/frontend/web/src/features/controlNet/components/hooks/useProcessorNodeChanged.ts
new file mode 100644
index 0000000000..79a502cb0e
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/hooks/useProcessorNodeChanged.ts
@@ -0,0 +1,20 @@
+import { useAppDispatch } from 'app/store/storeHooks';
+import { controlNetProcessorParamsChanged } from 'features/controlNet/store/controlNetSlice';
+import { ControlNetProcessorNode } from 'features/controlNet/store/types';
+import { useCallback } from 'react';
+
+export const useProcessorNodeChanged = () => {
+ const dispatch = useAppDispatch();
+ const handleProcessorNodeChanged = useCallback(
+ (controlNetId: string, changes: Partial) => {
+ dispatch(
+ controlNetProcessorParamsChanged({
+ controlNetId,
+ changes,
+ })
+ );
+ },
+ [dispatch]
+ );
+ return handleProcessorNodeChanged;
+};
diff --git a/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetProcessorSelect.tsx b/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetProcessorSelect.tsx
new file mode 100644
index 0000000000..9d21727a3c
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/parameters/ParamControlNetProcessorSelect.tsx
@@ -0,0 +1,46 @@
+import IAICustomSelect from 'common/components/IAICustomSelect';
+import { memo, useCallback } from 'react';
+import {
+ ControlNetProcessorNode,
+ ControlNetProcessorType,
+} from '../../store/types';
+import { controlNetProcessorTypeChanged } from '../../store/controlNetSlice';
+import { useAppDispatch } from 'app/store/storeHooks';
+import { CONTROLNET_PROCESSORS } from '../../store/constants';
+
+type ParamControlNetProcessorSelectProps = {
+ controlNetId: string;
+ processorNode: ControlNetProcessorNode;
+};
+
+const CONTROLNET_PROCESSOR_TYPES = Object.keys(
+ CONTROLNET_PROCESSORS
+) as ControlNetProcessorType[];
+
+const ParamControlNetProcessorSelect = (
+ props: ParamControlNetProcessorSelectProps
+) => {
+ const { controlNetId, processorNode } = props;
+ const dispatch = useAppDispatch();
+ const handleProcessorTypeChanged = useCallback(
+ (v: string | null | undefined) => {
+ dispatch(
+ controlNetProcessorTypeChanged({
+ controlNetId,
+ processorType: v as ControlNetProcessorType,
+ })
+ );
+ },
+ [controlNetId, dispatch]
+ );
+ return (
+
+ );
+};
+
+export default memo(ParamControlNetProcessorSelect);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/CannyProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/CannyProcessor.tsx
index a30c003e86..872be06177 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/processors/CannyProcessor.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/CannyProcessor.tsx
@@ -1,75 +1,61 @@
import { Flex } from '@chakra-ui/react';
import IAISlider from 'common/components/IAISlider';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { controlNetImageProcessed } from 'features/controlNet/store/actions';
-import { controlNetProcessedImageChanged } from 'features/controlNet/store/controlNetSlice';
-import ControlNetProcessorButtons from './common/ControlNetProcessorButtons';
-import { memo, useCallback, useState } from 'react';
-import { ControlNetProcessorProps } from '../ControlNet';
+import { memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredCannyImageProcessorInvocation } from 'features/controlNet/store/types';
-export const CANNY_PROCESSOR = 'canny_image_processor';
+type CannyProcessorProps = {
+ controlNetId: string;
+ processorNode: RequiredCannyImageProcessorInvocation;
+};
-const CannyProcessor = (props: ControlNetProcessorProps) => {
- const { controlNetId, controlImage, processedControlImage, type } = props;
- const dispatch = useAppDispatch();
- const [lowThreshold, setLowThreshold] = useState(100);
- const [highThreshold, setHighThreshold] = useState(200);
+const CannyProcessor = (props: CannyProcessorProps) => {
+ const { controlNetId, processorNode } = props;
+ const { low_threshold, high_threshold } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
- const handleProcess = useCallback(() => {
- if (!controlImage) {
- return;
- }
+ const handleLowThresholdChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { low_threshold: v });
+ },
+ [controlNetId, processorChanged]
+ );
- dispatch(
- controlNetImageProcessed({
- controlNetId,
- processorNode: {
- id: CANNY_PROCESSOR,
- type: 'canny_image_processor',
- image: {
- image_name: controlImage.image_name,
- image_origin: controlImage.image_origin,
- },
- low_threshold: lowThreshold,
- high_threshold: highThreshold,
- },
- })
- );
- }, [controlNetId, dispatch, highThreshold, controlImage, lowThreshold]);
+ const handleLowThresholdReset = useCallback(() => {
+ processorChanged(controlNetId, { low_threshold: 100 });
+ }, [controlNetId, processorChanged]);
- const handleReset = useCallback(() => {
- dispatch(
- controlNetProcessedImageChanged({
- controlNetId,
- processedControlImage: null,
- })
- );
- }, [controlNetId, dispatch]);
+ const handleHighThresholdChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { high_threshold: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleHighThresholdReset = useCallback(() => {
+ processorChanged(controlNetId, { high_threshold: 200 });
+ }, [controlNetId, processorChanged]);
return (
-
);
};
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/ContentShuffleProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/ContentShuffleProcessor.tsx
new file mode 100644
index 0000000000..480275cd1c
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/ContentShuffleProcessor.tsx
@@ -0,0 +1,98 @@
+import { Flex } from '@chakra-ui/react';
+import IAISlider from 'common/components/IAISlider';
+import { memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredContentShuffleImageProcessorInvocation } from 'features/controlNet/store/types';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredContentShuffleImageProcessorInvocation;
+};
+
+const ContentShuffleProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { image_resolution, detect_resolution, w, h, f } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleWChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { w: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleHChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { h: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleFChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { f: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export default memo(ContentShuffleProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/HedProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/HedProcessor.tsx
index 891f6d0adc..22c5c487cd 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/processors/HedProcessor.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/HedProcessor.tsx
@@ -1,39 +1,66 @@
import { Flex } from '@chakra-ui/react';
import IAISlider from 'common/components/IAISlider';
import IAISwitch from 'common/components/IAISwitch';
-import { ChangeEvent, memo, useState } from 'react';
+import { ChangeEvent, memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredHedImageProcessorInvocation } from 'features/controlNet/store/types';
-const HedPreprocessor = () => {
- const [detectResolution, setDetectResolution] = useState(512);
- const [imageResolution, setImageResolution] = useState(512);
- const [isScribbleEnabled, setIsScribbleEnabled] = useState(false);
+type HedProcessorProps = {
+ controlNetId: string;
+ processorNode: RequiredHedImageProcessorInvocation;
+};
- const handleChangeScribble = (e: ChangeEvent) => {
- setIsScribbleEnabled(e.target.checked);
- };
+const HedPreprocessor = (props: HedProcessorProps) => {
+ const {
+ controlNetId,
+ processorNode: { detect_resolution, image_resolution, scribble },
+ } = props;
+
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleScribbleChanged = useCallback(
+ (e: ChangeEvent) => {
+ processorChanged(controlNetId, { scribble: e.target.checked });
+ },
+ [controlNetId, processorChanged]
+ );
return (
);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/LineartAnimeProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/LineartAnimeProcessor.tsx
index 6d4f61d8af..87a21f95f0 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/processors/LineartAnimeProcessor.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/LineartAnimeProcessor.tsx
@@ -1,25 +1,47 @@
import { Flex } from '@chakra-ui/react';
import IAISlider from 'common/components/IAISlider';
-import { memo, useState } from 'react';
+import { memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredLineartAnimeImageProcessorInvocation } from 'features/controlNet/store/types';
-const LineartPreprocessor = () => {
- const [detectResolution, setDetectResolution] = useState(512);
- const [imageResolution, setImageResolution] = useState(512);
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredLineartAnimeImageProcessorInvocation;
+};
+
+const LineartAnimeProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { image_resolution, detect_resolution } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
return (
{
);
};
-export default memo(LineartPreprocessor);
+export default memo(LineartAnimeProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/LineartProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/LineartProcessor.tsx
index 763d6f2b37..503fd73fa8 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/processors/LineartProcessor.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/LineartProcessor.tsx
@@ -1,42 +1,66 @@
import { Flex } from '@chakra-ui/react';
import IAISlider from 'common/components/IAISlider';
+import { ChangeEvent, memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredLineartImageProcessorInvocation } from 'features/controlNet/store/types';
import IAISwitch from 'common/components/IAISwitch';
-import { ChangeEvent, memo, useState } from 'react';
-const LineartPreprocessor = () => {
- const [detectResolution, setDetectResolution] = useState(512);
- const [imageResolution, setImageResolution] = useState(512);
- const [isCoarseEnabled, setIsCoarseEnabled] = useState(false);
+type LineartProcessorProps = {
+ controlNetId: string;
+ processorNode: RequiredLineartImageProcessorInvocation;
+};
- const handleChangeScribble = (e: ChangeEvent) => {
- setIsCoarseEnabled(e.target.checked);
- };
+const LineartProcessor = (props: LineartProcessorProps) => {
+ const { controlNetId, processorNode } = props;
+ const { image_resolution, detect_resolution, coarse } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleCoarseChanged = useCallback(
+ (e: ChangeEvent) => {
+ processorChanged(controlNetId, { coarse: e.target.checked });
+ },
+ [controlNetId, processorChanged]
+ );
return (
);
};
-export default memo(LineartPreprocessor);
+export default memo(LineartProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/MediapipeFaceProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/MediapipeFaceProcessor.tsx
new file mode 100644
index 0000000000..f76f00c3b7
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/MediapipeFaceProcessor.tsx
@@ -0,0 +1,57 @@
+import { Flex } from '@chakra-ui/react';
+import IAISlider from 'common/components/IAISlider';
+import { memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import {
+ RequiredContentShuffleImageProcessorInvocation,
+ RequiredMediapipeFaceProcessorInvocation,
+} from 'features/controlNet/store/types';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredMediapipeFaceProcessorInvocation;
+};
+
+const MediapipeFaceProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { max_faces, min_confidence } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleMaxFacesChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { max_faces: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleMinConfidenceChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { min_confidence: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ return (
+
+
+
+
+ );
+};
+
+export default memo(MediapipeFaceProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/MidasDepthProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/MidasDepthProcessor.tsx
new file mode 100644
index 0000000000..9a205f28b5
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/MidasDepthProcessor.tsx
@@ -0,0 +1,55 @@
+import { Flex } from '@chakra-ui/react';
+import IAISlider from 'common/components/IAISlider';
+import { memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredMidasDepthImageProcessorInvocation } from 'features/controlNet/store/types';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredMidasDepthImageProcessorInvocation;
+};
+
+const MidasDepthProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { a_mult, bg_th } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleAMultChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { a_mult: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleBgThChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { bg_th: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ return (
+
+
+
+
+ );
+};
+
+export default memo(MidasDepthProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/MlsdImageProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/MlsdImageProcessor.tsx
new file mode 100644
index 0000000000..e33b2102d1
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/MlsdImageProcessor.tsx
@@ -0,0 +1,85 @@
+import { Flex } from '@chakra-ui/react';
+import IAISlider from 'common/components/IAISlider';
+import { memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredMlsdImageProcessorInvocation } from 'features/controlNet/store/types';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredMlsdImageProcessorInvocation;
+};
+
+const MlsdImageProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { image_resolution, detect_resolution, thr_d, thr_v } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleThrDChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { thr_d: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleThrVChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { thr_v: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default memo(MlsdImageProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/NormalBaeProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/NormalBaeProcessor.tsx
new file mode 100644
index 0000000000..61836e7668
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/NormalBaeProcessor.tsx
@@ -0,0 +1,53 @@
+import { Flex } from '@chakra-ui/react';
+import IAISlider from 'common/components/IAISlider';
+import { memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredNormalbaeImageProcessorInvocation } from 'features/controlNet/store/types';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredNormalbaeImageProcessorInvocation;
+};
+
+const NormalBaeProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { image_resolution, detect_resolution } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ return (
+
+
+
+
+ );
+};
+
+export default memo(NormalBaeProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/OpenposeProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/OpenposeProcessor.tsx
new file mode 100644
index 0000000000..63556d4da4
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/OpenposeProcessor.tsx
@@ -0,0 +1,66 @@
+import { Flex } from '@chakra-ui/react';
+import IAISlider from 'common/components/IAISlider';
+import { ChangeEvent, memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredOpenposeImageProcessorInvocation } from 'features/controlNet/store/types';
+import IAISwitch from 'common/components/IAISwitch';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredOpenposeImageProcessorInvocation;
+};
+
+const OpenposeProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { image_resolution, detect_resolution, hand_and_face } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleHandAndFaceChanged = useCallback(
+ (e: ChangeEvent) => {
+ processorChanged(controlNetId, { hand_and_face: e.target.checked });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default memo(OpenposeProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/PidiProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/PidiProcessor.tsx
new file mode 100644
index 0000000000..711d4930ab
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/PidiProcessor.tsx
@@ -0,0 +1,74 @@
+import { Flex } from '@chakra-ui/react';
+import IAISlider from 'common/components/IAISlider';
+import { ChangeEvent, memo, useCallback } from 'react';
+import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
+import { RequiredPidiImageProcessorInvocation } from 'features/controlNet/store/types';
+import IAISwitch from 'common/components/IAISwitch';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredPidiImageProcessorInvocation;
+};
+
+const PidiProcessor = (props: Props) => {
+ const { controlNetId, processorNode } = props;
+ const { image_resolution, detect_resolution, scribble, safe } = processorNode;
+ const processorChanged = useProcessorNodeChanged();
+
+ const handleDetectResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { detect_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleImageResolutionChanged = useCallback(
+ (v: number) => {
+ processorChanged(controlNetId, { image_resolution: v });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleScribbleChanged = useCallback(
+ (e: ChangeEvent) => {
+ processorChanged(controlNetId, { scribble: e.target.checked });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ const handleSafeChanged = useCallback(
+ (e: ChangeEvent) => {
+ processorChanged(controlNetId, { safe: e.target.checked });
+ },
+ [controlNetId, processorChanged]
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default memo(PidiProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/ZoeDepthProcessor.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/ZoeDepthProcessor.tsx
new file mode 100644
index 0000000000..20a1ec4493
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/ZoeDepthProcessor.tsx
@@ -0,0 +1,14 @@
+import { memo } from 'react';
+import { RequiredZoeDepthImageProcessorInvocation } from 'features/controlNet/store/types';
+
+type Props = {
+ controlNetId: string;
+ processorNode: RequiredZoeDepthImageProcessorInvocation;
+};
+
+const ZoeDepthProcessor = (props: Props) => {
+ // Has no parameters?
+ return null;
+};
+
+export default memo(ZoeDepthProcessor);
diff --git a/invokeai/frontend/web/src/features/controlNet/components/processors/common/ControlNetProcessorButtons.tsx b/invokeai/frontend/web/src/features/controlNet/components/processors/common/ControlNetProcessorButtons.tsx
index afa94d6ada..a051990f67 100644
--- a/invokeai/frontend/web/src/features/controlNet/components/processors/common/ControlNetProcessorButtons.tsx
+++ b/invokeai/frontend/web/src/features/controlNet/components/processors/common/ControlNetProcessorButtons.tsx
@@ -21,23 +21,7 @@ const ControlNetProcessorButtons = (props: ControlNetProcessorButtonsProps) => {
alignItems: 'center',
justifyContent: 'stretch',
}}
- >
-
- Preprocess
-
- }
- onClick={handleReset}
- isDisabled={isResetDisabled}
- >
- Reset Processing
-
-
+ >
);
};
diff --git a/invokeai/frontend/web/src/features/controlNet/store/actions.ts b/invokeai/frontend/web/src/features/controlNet/store/actions.ts
index 9b6c11f22d..3d9f56a36b 100644
--- a/invokeai/frontend/web/src/features/controlNet/store/actions.ts
+++ b/invokeai/frontend/web/src/features/controlNet/store/actions.ts
@@ -1,7 +1,5 @@
import { createAction } from '@reduxjs/toolkit';
-import { ControlNetProcessorNode } from './types';
export const controlNetImageProcessed = createAction<{
controlNetId: string;
- processorNode: ControlNetProcessorNode;
}>('controlNet/imageProcessed');
diff --git a/invokeai/frontend/web/src/features/controlNet/store/constants.ts b/invokeai/frontend/web/src/features/controlNet/store/constants.ts
new file mode 100644
index 0000000000..d7a76b6a5e
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlNet/store/constants.ts
@@ -0,0 +1,166 @@
+import {
+ ControlNetProcessorType,
+ RequiredControlNetProcessorNode,
+} from './types';
+
+type ControlNetProcessorsDict = Record<
+ ControlNetProcessorType,
+ {
+ type: ControlNetProcessorType;
+ label: string;
+ description: string;
+ default: RequiredControlNetProcessorNode;
+ }
+>;
+
+/**
+ * A dict of ControlNet processors, including:
+ * - type
+ * - label
+ * - description
+ * - default values
+ *
+ * TODO: Generate from the OpenAPI schema
+ */
+export const CONTROLNET_PROCESSORS: ControlNetProcessorsDict = {
+ canny_image_processor: {
+ type: 'canny_image_processor',
+ label: 'Canny',
+ description: '',
+ default: {
+ id: 'canny_image_processor',
+ type: 'canny_image_processor',
+ low_threshold: 100,
+ high_threshold: 200,
+ },
+ },
+ content_shuffle_image_processor: {
+ type: 'content_shuffle_image_processor',
+ label: 'Content Shuffle',
+ description: '',
+ default: {
+ id: 'content_shuffle_image_processor',
+ type: 'content_shuffle_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ h: 512,
+ w: 512,
+ f: 256,
+ },
+ },
+ hed_image_processor: {
+ type: 'hed_image_processor',
+ label: 'HED',
+ description: '',
+ default: {
+ id: 'hed_image_processor',
+ type: 'hed_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ scribble: false,
+ },
+ },
+ lineart_anime_image_processor: {
+ type: 'lineart_anime_image_processor',
+ label: 'Lineart Anime',
+ description: '',
+ default: {
+ id: 'lineart_anime_image_processor',
+ type: 'lineart_anime_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ },
+ },
+ lineart_image_processor: {
+ type: 'lineart_image_processor',
+ label: 'Lineart',
+ description: '',
+ default: {
+ id: 'lineart_image_processor',
+ type: 'lineart_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ coarse: false,
+ },
+ },
+ mediapipe_face_processor: {
+ type: 'mediapipe_face_processor',
+ label: 'Mediapipe Face',
+ description: '',
+ default: {
+ id: 'mediapipe_face_processor',
+ type: 'mediapipe_face_processor',
+ max_faces: 1,
+ min_confidence: 0.5,
+ },
+ },
+ midas_depth_image_processor: {
+ type: 'midas_depth_image_processor',
+ label: 'Depth (Midas)',
+ description: '',
+ default: {
+ id: 'midas_depth_image_processor',
+ type: 'midas_depth_image_processor',
+ a_mult: 2,
+ bg_th: 0.1,
+ },
+ },
+ mlsd_image_processor: {
+ type: 'mlsd_image_processor',
+ label: 'MLSD',
+ description: '',
+ default: {
+ id: 'mlsd_image_processor',
+ type: 'mlsd_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ thr_d: 0.1,
+ thr_v: 0.1,
+ },
+ },
+ normalbae_image_processor: {
+ type: 'normalbae_image_processor',
+ label: 'NormalBae',
+ description: '',
+ default: {
+ id: 'normalbae_image_processor',
+ type: 'normalbae_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ },
+ },
+ openpose_image_processor: {
+ type: 'openpose_image_processor',
+ label: 'Openpose',
+ description: '',
+ default: {
+ id: 'openpose_image_processor',
+ type: 'openpose_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ hand_and_face: false,
+ },
+ },
+ pidi_image_processor: {
+ type: 'pidi_image_processor',
+ label: 'PIDI',
+ description: '',
+ default: {
+ id: 'pidi_image_processor',
+ type: 'pidi_image_processor',
+ detect_resolution: 512,
+ image_resolution: 512,
+ scribble: false,
+ safe: false,
+ },
+ },
+ zoe_depth_image_processor: {
+ type: 'zoe_depth_image_processor',
+ label: 'Depth (Zoe)',
+ description: '',
+ default: {
+ id: 'zoe_depth_image_processor',
+ type: 'zoe_depth_image_processor',
+ },
+ },
+};
diff --git a/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts b/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts
index a87b591bad..674e130bdc 100644
--- a/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts
+++ b/invokeai/frontend/web/src/features/controlNet/store/controlNetSlice.ts
@@ -1,11 +1,12 @@
-import {
- $CombinedState,
- PayloadAction,
- createSelector,
-} from '@reduxjs/toolkit';
+import { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
import { RootState } from 'app/store/store';
import { ImageDTO } from 'services/api';
+import {
+ ControlNetProcessorType,
+ RequiredControlNetProcessorNode,
+} from './types';
+import { CONTROLNET_PROCESSORS } from './constants';
export const CONTROLNET_MODELS = [
'lllyasviel/sd-controlnet-canny',
@@ -18,23 +19,6 @@ export const CONTROLNET_MODELS = [
'lllyasviel/sd-controlnet-mlsd',
];
-export const CONTROLNET_PROCESSORS = [
- 'canny',
- 'contentShuffle',
- 'hed',
- 'lineart',
- 'lineartAnime',
- 'mediapipeFace',
- 'midasDepth',
- 'mlsd',
- 'normalBae',
- 'openpose',
- 'pidi',
- 'zoeDepth',
-];
-
-export type ControlNetProcessor = (typeof CONTROLNET_PROCESSORS)[number];
-
export type ControlNetModel = (typeof CONTROLNET_MODELS)[number];
export const initialControlNet: Omit = {
@@ -46,7 +30,7 @@ export const initialControlNet: Omit = {
controlImage: null,
isControlImageProcessed: false,
processedControlImage: null,
- processor: 'canny',
+ processorNode: CONTROLNET_PROCESSORS.canny_image_processor.default,
};
export type ControlNet = {
@@ -59,17 +43,19 @@ export type ControlNet = {
controlImage: ImageDTO | null;
isControlImageProcessed: boolean;
processedControlImage: ImageDTO | null;
- processor: ControlNetProcessor;
+ processorNode: RequiredControlNetProcessorNode;
};
export type ControlNetState = {
controlNets: Record;
isEnabled: boolean;
+ shouldAutoProcess: boolean;
};
export const initialControlNetState: ControlNetState = {
controlNets: {},
isEnabled: false,
+ shouldAutoProcess: true,
};
export const controlNetSlice = createSlice({
@@ -169,15 +155,36 @@ export const controlNetSlice = createSlice({
const { controlNetId, endStepPct } = action.payload;
state.controlNets[controlNetId].endStepPct = endStepPct;
},
- controlNetProcessorChanged: (
+ controlNetProcessorParamsChanged: (
state,
action: PayloadAction<{
controlNetId: string;
- processor: ControlNetProcessor;
+ changes: Omit<
+ Partial,
+ 'id' | 'type' | 'is_intermediate'
+ >;
}>
) => {
- const { controlNetId, processor } = action.payload;
- state.controlNets[controlNetId].processor = processor;
+ const { controlNetId, changes } = action.payload;
+ const processorNode = state.controlNets[controlNetId].processorNode;
+ state.controlNets[controlNetId].processorNode = {
+ ...processorNode,
+ ...changes,
+ };
+ },
+ controlNetProcessorTypeChanged: (
+ state,
+ action: PayloadAction<{
+ controlNetId: string;
+ processorType: ControlNetProcessorType;
+ }>
+ ) => {
+ const { controlNetId, processorType } = action.payload;
+ state.controlNets[controlNetId].processorNode =
+ CONTROLNET_PROCESSORS[processorType].default;
+ },
+ shouldAutoProcessToggled: (state) => {
+ state.shouldAutoProcess = !state.shouldAutoProcess;
},
},
});
@@ -195,7 +202,9 @@ export const {
controlNetWeightChanged,
controlNetBeginStepPctChanged,
controlNetEndStepPctChanged,
- controlNetProcessorChanged,
+ controlNetProcessorParamsChanged,
+ controlNetProcessorTypeChanged,
+ shouldAutoProcessToggled,
} = controlNetSlice.actions;
export default controlNetSlice.reducer;
diff --git a/invokeai/frontend/web/src/features/controlNet/store/types.ts b/invokeai/frontend/web/src/features/controlNet/store/types.ts
index ca3af7b406..808a50010b 100644
--- a/invokeai/frontend/web/src/features/controlNet/store/types.ts
+++ b/invokeai/frontend/web/src/features/controlNet/store/types.ts
@@ -1,7 +1,8 @@
+import { isObject } from 'lodash-es';
import {
CannyImageProcessorInvocation,
ContentShuffleImageProcessorInvocation,
- HedImageprocessorInvocation,
+ HedImageProcessorInvocation,
LineartAnimeImageProcessorInvocation,
LineartImageProcessorInvocation,
MediapipeFaceProcessorInvocation,
@@ -12,17 +13,317 @@ import {
PidiImageProcessorInvocation,
ZoeDepthImageProcessorInvocation,
} from 'services/api';
+import { O } from 'ts-toolbelt';
+/**
+ * Any ControlNet processor node
+ */
export type ControlNetProcessorNode =
| CannyImageProcessorInvocation
- | HedImageprocessorInvocation
- | LineartImageProcessorInvocation
- | LineartAnimeImageProcessorInvocation
- | OpenposeImageProcessorInvocation
- | MidasDepthImageProcessorInvocation
- | NormalbaeImageProcessorInvocation
- | MlsdImageProcessorInvocation
- | PidiImageProcessorInvocation
| ContentShuffleImageProcessorInvocation
- | ZoeDepthImageProcessorInvocation
- | MediapipeFaceProcessorInvocation;
+ | HedImageProcessorInvocation
+ | LineartAnimeImageProcessorInvocation
+ | LineartImageProcessorInvocation
+ | MediapipeFaceProcessorInvocation
+ | MidasDepthImageProcessorInvocation
+ | MlsdImageProcessorInvocation
+ | NormalbaeImageProcessorInvocation
+ | OpenposeImageProcessorInvocation
+ | PidiImageProcessorInvocation
+ | ZoeDepthImageProcessorInvocation;
+
+/**
+ * Any ControlNet processor type
+ */
+export type ControlNetProcessorType = NonNullable<
+ ControlNetProcessorNode['type']
+>;
+
+/**
+ * The Canny processor node, with parameters flagged as required
+ */
+export type RequiredCannyImageProcessorInvocation = O.Required<
+ CannyImageProcessorInvocation,
+ 'type' | 'low_threshold' | 'high_threshold'
+>;
+
+/**
+ * The ContentShuffle processor node, with parameters flagged as required
+ */
+export type RequiredContentShuffleImageProcessorInvocation = O.Required<
+ ContentShuffleImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution' | 'w' | 'h' | 'f'
+>;
+
+/**
+ * The HED processor node, with parameters flagged as required
+ */
+export type RequiredHedImageProcessorInvocation = O.Required<
+ HedImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution' | 'scribble'
+>;
+
+/**
+ * The Lineart Anime processor node, with parameters flagged as required
+ */
+export type RequiredLineartAnimeImageProcessorInvocation = O.Required<
+ LineartAnimeImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution'
+>;
+
+/**
+ * The Lineart processor node, with parameters flagged as required
+ */
+export type RequiredLineartImageProcessorInvocation = O.Required<
+ LineartImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution' | 'coarse'
+>;
+
+/**
+ * The MediapipeFace processor node, with parameters flagged as required
+ */
+export type RequiredMediapipeFaceProcessorInvocation = O.Required<
+ MediapipeFaceProcessorInvocation,
+ 'type' | 'max_faces' | 'min_confidence'
+>;
+
+/**
+ * The MidasDepth processor node, with parameters flagged as required
+ */
+export type RequiredMidasDepthImageProcessorInvocation = O.Required<
+ MidasDepthImageProcessorInvocation,
+ 'type' | 'a_mult' | 'bg_th'
+>;
+
+/**
+ * The MLSD processor node, with parameters flagged as required
+ */
+export type RequiredMlsdImageProcessorInvocation = O.Required<
+ MlsdImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution' | 'thr_v' | 'thr_d'
+>;
+
+/**
+ * The NormalBae processor node, with parameters flagged as required
+ */
+export type RequiredNormalbaeImageProcessorInvocation = O.Required<
+ NormalbaeImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution'
+>;
+
+/**
+ * The Openpose processor node, with parameters flagged as required
+ */
+export type RequiredOpenposeImageProcessorInvocation = O.Required<
+ OpenposeImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution' | 'hand_and_face'
+>;
+
+/**
+ * The Pidi processor node, with parameters flagged as required
+ */
+export type RequiredPidiImageProcessorInvocation = O.Required<
+ PidiImageProcessorInvocation,
+ 'type' | 'detect_resolution' | 'image_resolution' | 'safe' | 'scribble'
+>;
+
+/**
+ * The ZoeDepth processor node, with parameters flagged as required
+ */
+export type RequiredZoeDepthImageProcessorInvocation = O.Required<
+ ZoeDepthImageProcessorInvocation,
+ 'type'
+>;
+
+/**
+ * Any ControlNet Processor node, with its parameters flagged as required
+ */
+export type RequiredControlNetProcessorNode =
+ | RequiredCannyImageProcessorInvocation
+ | RequiredContentShuffleImageProcessorInvocation
+ | RequiredHedImageProcessorInvocation
+ | RequiredLineartAnimeImageProcessorInvocation
+ | RequiredLineartImageProcessorInvocation
+ | RequiredMediapipeFaceProcessorInvocation
+ | RequiredMidasDepthImageProcessorInvocation
+ | RequiredMlsdImageProcessorInvocation
+ | RequiredNormalbaeImageProcessorInvocation
+ | RequiredOpenposeImageProcessorInvocation
+ | RequiredPidiImageProcessorInvocation
+ | RequiredZoeDepthImageProcessorInvocation;
+
+/**
+ * Type guard for CannyImageProcessorInvocation
+ */
+export const isCannyImageProcessorInvocation = (
+ obj: unknown
+): obj is CannyImageProcessorInvocation => {
+ if (isObject(obj) && 'type' in obj && obj.type === 'canny_image_processor') {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for ContentShuffleImageProcessorInvocation
+ */
+export const isContentShuffleImageProcessorInvocation = (
+ obj: unknown
+): obj is ContentShuffleImageProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'content_shuffle_image_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for HedImageprocessorInvocation
+ */
+export const isHedImageprocessorInvocation = (
+ obj: unknown
+): obj is HedImageProcessorInvocation => {
+ if (isObject(obj) && 'type' in obj && obj.type === 'hed_image_processor') {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for LineartAnimeImageProcessorInvocation
+ */
+export const isLineartAnimeImageProcessorInvocation = (
+ obj: unknown
+): obj is LineartAnimeImageProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'lineart_anime_image_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for LineartImageProcessorInvocation
+ */
+export const isLineartImageProcessorInvocation = (
+ obj: unknown
+): obj is LineartImageProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'lineart_image_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for MediapipeFaceProcessorInvocation
+ */
+export const isMediapipeFaceProcessorInvocation = (
+ obj: unknown
+): obj is MediapipeFaceProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'mediapipe_face_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for MidasDepthImageProcessorInvocation
+ */
+export const isMidasDepthImageProcessorInvocation = (
+ obj: unknown
+): obj is MidasDepthImageProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'midas_depth_image_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for MlsdImageProcessorInvocation
+ */
+export const isMlsdImageProcessorInvocation = (
+ obj: unknown
+): obj is MlsdImageProcessorInvocation => {
+ if (isObject(obj) && 'type' in obj && obj.type === 'mlsd_image_processor') {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for NormalbaeImageProcessorInvocation
+ */
+export const isNormalbaeImageProcessorInvocation = (
+ obj: unknown
+): obj is NormalbaeImageProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'normalbae_image_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for OpenposeImageProcessorInvocation
+ */
+export const isOpenposeImageProcessorInvocation = (
+ obj: unknown
+): obj is OpenposeImageProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'openpose_image_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for PidiImageProcessorInvocation
+ */
+export const isPidiImageProcessorInvocation = (
+ obj: unknown
+): obj is PidiImageProcessorInvocation => {
+ if (isObject(obj) && 'type' in obj && obj.type === 'pidi_image_processor') {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Type guard for ZoeDepthImageProcessorInvocation
+ */
+export const isZoeDepthImageProcessorInvocation = (
+ obj: unknown
+): obj is ZoeDepthImageProcessorInvocation => {
+ if (
+ isObject(obj) &&
+ 'type' in obj &&
+ obj.type === 'zoe_depth_image_processor'
+ ) {
+ return true;
+ }
+ return false;
+};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts
index d52310abdd..9975d446a3 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildTextToImageGraph.ts
@@ -12,9 +12,7 @@ import {
TextToLatentsInvocation,
} from 'services/api';
import { NonNullableGraph } from 'features/nodes/types/types';
-import { forEach, map, size } from 'lodash-es';
-import { ControlNetProcessorNode } from 'features/controlNet/store/types';
-import { ControlNetModel } from 'features/controlNet/store/controlNetSlice';
+import { forEach, size } from 'lodash-es';
const POSITIVE_CONDITIONING = 'positive_conditioning';
const NEGATIVE_CONDITIONING = 'negative_conditioning';
@@ -344,7 +342,7 @@ export const buildTextToImageGraph = (state: RootState): Graph => {
beginStepPct,
endStepPct,
model,
- processor,
+ processorNode,
weight,
} = controlNet;