-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Send Canvas Image & Mask To ControlNet (#4374)
## What type of PR is this? (check all applicable) - [x] Feature ## Have you discussed this change with the InvokeAI team? - [x] Yes ## Description Send stuff directly from canvas to ControlNet ## Usage - Two new buttons available on canvas Controlnet to import image and mask. - Click them.
- Loading branch information
Showing
7 changed files
with
245 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
...tend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { logger } from 'app/logging/logger'; | ||
import { canvasImageToControlNet } from 'features/canvas/store/actions'; | ||
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob'; | ||
import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice'; | ||
import { addToast } from 'features/system/store/systemSlice'; | ||
import { imagesApi } from 'services/api/endpoints/images'; | ||
import { startAppListening } from '..'; | ||
|
||
export const addCanvasImageToControlNetListener = () => { | ||
startAppListening({ | ||
actionCreator: canvasImageToControlNet, | ||
effect: async (action, { dispatch, getState }) => { | ||
const log = logger('canvas'); | ||
const state = getState(); | ||
|
||
const blob = await getBaseLayerBlob(state); | ||
|
||
if (!blob) { | ||
log.error('Problem getting base layer blob'); | ||
dispatch( | ||
addToast({ | ||
title: 'Problem Saving Canvas', | ||
description: 'Unable to export base layer', | ||
status: 'error', | ||
}) | ||
); | ||
return; | ||
} | ||
|
||
const { autoAddBoardId } = state.gallery; | ||
|
||
const imageDTO = await dispatch( | ||
imagesApi.endpoints.uploadImage.initiate({ | ||
file: new File([blob], 'savedCanvas.png', { | ||
type: 'image/png', | ||
}), | ||
image_category: 'mask', | ||
is_intermediate: false, | ||
board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId, | ||
crop_visible: true, | ||
postUploadAction: { | ||
type: 'TOAST', | ||
toastOptions: { title: 'Canvas Sent to ControlNet & Assets' }, | ||
}, | ||
}) | ||
).unwrap(); | ||
|
||
const { image_name } = imageDTO; | ||
|
||
dispatch( | ||
controlNetImageChanged({ | ||
controlNetId: action.payload.controlNet.controlNetId, | ||
controlImage: image_name, | ||
}) | ||
); | ||
}, | ||
}); | ||
}; |
70 changes: 70 additions & 0 deletions
70
...ntend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskToControlNet.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { logger } from 'app/logging/logger'; | ||
import { canvasMaskToControlNet } from 'features/canvas/store/actions'; | ||
import { getCanvasData } from 'features/canvas/util/getCanvasData'; | ||
import { controlNetImageChanged } from 'features/controlNet/store/controlNetSlice'; | ||
import { addToast } from 'features/system/store/systemSlice'; | ||
import { imagesApi } from 'services/api/endpoints/images'; | ||
import { startAppListening } from '..'; | ||
|
||
export const addCanvasMaskToControlNetListener = () => { | ||
startAppListening({ | ||
actionCreator: canvasMaskToControlNet, | ||
effect: async (action, { dispatch, getState }) => { | ||
const log = logger('canvas'); | ||
const state = getState(); | ||
|
||
const canvasBlobsAndImageData = await getCanvasData( | ||
state.canvas.layerState, | ||
state.canvas.boundingBoxCoordinates, | ||
state.canvas.boundingBoxDimensions, | ||
state.canvas.isMaskEnabled, | ||
state.canvas.shouldPreserveMaskedArea | ||
); | ||
|
||
if (!canvasBlobsAndImageData) { | ||
return; | ||
} | ||
|
||
const { maskBlob } = canvasBlobsAndImageData; | ||
|
||
if (!maskBlob) { | ||
log.error('Problem getting mask layer blob'); | ||
dispatch( | ||
addToast({ | ||
title: 'Problem Importing Mask', | ||
description: 'Unable to export mask', | ||
status: 'error', | ||
}) | ||
); | ||
return; | ||
} | ||
|
||
const { autoAddBoardId } = state.gallery; | ||
|
||
const imageDTO = await dispatch( | ||
imagesApi.endpoints.uploadImage.initiate({ | ||
file: new File([maskBlob], 'canvasMaskImage.png', { | ||
type: 'image/png', | ||
}), | ||
image_category: 'mask', | ||
is_intermediate: false, | ||
board_id: autoAddBoardId === 'none' ? undefined : autoAddBoardId, | ||
crop_visible: true, | ||
postUploadAction: { | ||
type: 'TOAST', | ||
toastOptions: { title: 'Mask Sent to ControlNet & Assets' }, | ||
}, | ||
}) | ||
).unwrap(); | ||
|
||
const { image_name } = imageDTO; | ||
|
||
dispatch( | ||
controlNetImageChanged({ | ||
controlNetId: action.payload.controlNet.controlNetId, | ||
controlImage: image_name, | ||
}) | ||
); | ||
}, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
.../frontend/web/src/features/controlNet/components/imports/ControlNetCanvasImageImports.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Flex } from '@chakra-ui/react'; | ||
import { useAppDispatch } from 'app/store/storeHooks'; | ||
import IAIIconButton from 'common/components/IAIIconButton'; | ||
import { | ||
canvasImageToControlNet, | ||
canvasMaskToControlNet, | ||
} from 'features/canvas/store/actions'; | ||
import { ControlNetConfig } from 'features/controlNet/store/controlNetSlice'; | ||
import { memo, useCallback } from 'react'; | ||
import { FaImage, FaMask } from 'react-icons/fa'; | ||
|
||
type ControlNetCanvasImageImportsProps = { | ||
controlNet: ControlNetConfig; | ||
}; | ||
|
||
const ControlNetCanvasImageImports = ( | ||
props: ControlNetCanvasImageImportsProps | ||
) => { | ||
const { controlNet } = props; | ||
const dispatch = useAppDispatch(); | ||
|
||
const handleImportImageFromCanvas = useCallback(() => { | ||
dispatch(canvasImageToControlNet({ controlNet })); | ||
}, [controlNet, dispatch]); | ||
|
||
const handleImportMaskFromCanvas = useCallback(() => { | ||
dispatch(canvasMaskToControlNet({ controlNet })); | ||
}, [controlNet, dispatch]); | ||
|
||
return ( | ||
<Flex | ||
sx={{ | ||
gap: 2, | ||
}} | ||
> | ||
<IAIIconButton | ||
size="sm" | ||
icon={<FaImage />} | ||
tooltip="Import Image From Canvas" | ||
aria-label="Import Image From Canvas" | ||
onClick={handleImportImageFromCanvas} | ||
/> | ||
<IAIIconButton | ||
size="sm" | ||
icon={<FaMask />} | ||
tooltip="Import Mask From Canvas" | ||
aria-label="Import Mask From Canvas" | ||
onClick={handleImportMaskFromCanvas} | ||
/> | ||
</Flex> | ||
); | ||
}; | ||
|
||
export default memo(ControlNetCanvasImageImports); |