Implements invert mask

This commit is contained in:
psychedelicious 2022-11-12 07:36:19 +11:00 committed by blessedcoolant
parent 7f0fb47cf3
commit 7075a17091
5 changed files with 66 additions and 23 deletions

View File

@ -12,7 +12,7 @@ import os
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from flask import Flask, redirect, send_from_directory, flash, request, url_for, jsonify from flask import Flask, redirect, send_from_directory, flash, request, url_for, jsonify
from flask_socketio import SocketIO from flask_socketio import SocketIO
from PIL import Image from PIL import Image, ImageOps
from PIL.Image import Image as ImageType from PIL.Image import Image as ImageType
from uuid import uuid4 from uuid import uuid4
from threading import Event from threading import Event
@ -698,11 +698,11 @@ class InvokeAIWebServer:
So we need to convert each into a PIL Image. So we need to convert each into a PIL Image.
""" """
truncated_outpaint_image_b64 = generation_parameters["init_img"][:64]
truncated_outpaint_mask_b64 = generation_parameters["init_mask"][:64] truncated_outpaint_mask_b64 = generation_parameters["init_mask"][:64]
init_img_url = generation_parameters["init_img"] init_img_url = generation_parameters["init_img"]
init_mask_url = generation_parameters["init_mask"]
init_img_url = generation_parameters["init_img"]
init_img_path = self.get_image_path_from_url(init_img_url) init_img_path = self.get_image_path_from_url(init_img_url)
@ -723,6 +723,9 @@ class InvokeAIWebServer:
generation_parameters["init_mask"] generation_parameters["init_mask"]
).convert("L") ).convert("L")
if generation_parameters.invert_mask:
mask_image = ImageOps.invert(mask_image)
""" """
Apply the mask to the init image, creating a "mask" image with Apply the mask to the init image, creating a "mask" image with
transparency where inpainting should occur. This is the kind of transparency where inpainting should occur. This is the kind of

View File

@ -118,6 +118,7 @@ export const frontendToBackendParameters = (
shouldUseInpaintReplace, shouldUseInpaintReplace,
stageScale, stageScale,
isMaskEnabled, isMaskEnabled,
shouldInvertMask,
} = canvasState[canvasState.currentCanvas]; } = canvasState[canvasState.currentCanvas];
const boundingBox = { const boundingBox = {
@ -137,6 +138,10 @@ export const frontendToBackendParameters = (
generationParameters.init_img = imageToProcessUrl; generationParameters.init_img = imageToProcessUrl;
generationParameters.strength = img2imgStrength; generationParameters.strength = img2imgStrength;
if (shouldInvertMask) {
generationParameters.invert_mask = true;
}
if (shouldUseInpaintReplace) { if (shouldUseInpaintReplace) {
generationParameters.inpaint_replace = inpaintReplace; generationParameters.inpaint_replace = inpaintReplace;
} }

View File

@ -6,6 +6,7 @@ import {
setIsMaskEnabled, setIsMaskEnabled,
setLayer, setLayer,
setMaskColor, setMaskColor,
setShouldInvertMask,
} from './canvasSlice'; } from './canvasSlice';
import { useAppDispatch, useAppSelector } from 'app/store'; import { useAppDispatch, useAppSelector } from 'app/store';
import _ from 'lodash'; import _ from 'lodash';
@ -19,12 +20,13 @@ import IAIButton from 'common/components/IAIButton';
export const selector = createSelector( export const selector = createSelector(
[currentCanvasSelector], [currentCanvasSelector],
(currentCanvas) => { (currentCanvas) => {
const { maskColor, layer, isMaskEnabled } = currentCanvas; const { maskColor, layer, isMaskEnabled, shouldInvertMask } = currentCanvas;
return { return {
layer, layer,
maskColor, maskColor,
isMaskEnabled, isMaskEnabled,
shouldInvertMask,
}; };
}, },
{ {
@ -35,7 +37,8 @@ export const selector = createSelector(
); );
const IAICanvasMaskButtonPopover = () => { const IAICanvasMaskButtonPopover = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { layer, maskColor, isMaskEnabled } = useAppSelector(selector); const { layer, maskColor, isMaskEnabled, shouldInvertMask } =
useAppSelector(selector);
return ( return (
<IAIPopover <IAIPopover
@ -57,7 +60,11 @@ const IAICanvasMaskButtonPopover = () => {
isChecked={isMaskEnabled} isChecked={isMaskEnabled}
onChange={(e) => dispatch(setIsMaskEnabled(e.target.checked))} onChange={(e) => dispatch(setIsMaskEnabled(e.target.checked))}
/> />
<IAICheckbox label="Invert Mask" /> <IAICheckbox
label="Invert Mask"
isChecked={shouldInvertMask}
onChange={(e) => dispatch(setShouldInvertMask(e.target.checked))}
/>
<IAIColorPicker <IAIColorPicker
color={maskColor} color={maskColor}
onChange={(newColor) => dispatch(setMaskColor(newColor))} onChange={(newColor) => dispatch(setMaskColor(newColor))}

View File

@ -15,14 +15,20 @@ import Konva from 'konva';
export const canvasMaskCompositerSelector = createSelector( export const canvasMaskCompositerSelector = createSelector(
currentCanvasSelector, currentCanvasSelector,
(currentCanvas) => { (currentCanvas) => {
const { maskColor, stageCoordinates, stageDimensions, stageScale } = const {
currentCanvas as InpaintingCanvasState | OutpaintingCanvasState; maskColor,
stageCoordinates,
stageDimensions,
stageScale,
shouldInvertMask,
} = currentCanvas as InpaintingCanvasState | OutpaintingCanvasState;
return { return {
stageCoordinates, stageCoordinates,
stageDimensions, stageDimensions,
stageScale, stageScale,
maskColorString: rgbaColorToString(maskColor), maskColorString: rgbaColorToString(maskColor),
shouldInvertMask,
}; };
} }
); );
@ -114,8 +120,13 @@ const getColoredSVG = (color: string) => {
const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => { const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
const { ...rest } = props; const { ...rest } = props;
const { maskColorString, stageCoordinates, stageDimensions, stageScale } = const {
useAppSelector(canvasMaskCompositerSelector); maskColorString,
stageCoordinates,
stageDimensions,
stageScale,
shouldInvertMask,
} = useAppSelector(canvasMaskCompositerSelector);
const [fillPatternImage, setFillPatternImage] = const [fillPatternImage, setFillPatternImage] =
useState<HTMLImageElement | null>(null); useState<HTMLImageElement | null>(null);
@ -162,7 +173,7 @@ const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
fillPatternRepeat={'repeat'} fillPatternRepeat={'repeat'}
fillPatternScale={{ x: 1 / stageScale, y: 1 / stageScale }} fillPatternScale={{ x: 1 / stageScale, y: 1 / stageScale }}
listening={true} listening={true}
globalCompositeOperation={'source-in'} globalCompositeOperation={shouldInvertMask ? 'source-out' : 'source-in'}
{...rest} {...rest}
/> />
); );

View File

@ -546,25 +546,38 @@ export const canvasSlice = createSlice({
}); });
}, },
addLine: (state, action: PayloadAction<number[]>) => { addLine: (state, action: PayloadAction<number[]>) => {
const { tool, layer, brushColor, brushSize, eraserSize } = const currentCanvas = state[state.currentCanvas];
state[state.currentCanvas];
const { tool, layer, brushColor, brushSize, eraserSize } = currentCanvas;
if (tool === 'move') return; if (tool === 'move') return;
state[state.currentCanvas].pastObjects.push( let newTool: CanvasDrawingTool;
state[state.currentCanvas].objects
);
state[state.currentCanvas].objects.push({ if (layer === 'mask' && currentCanvas.shouldInvertMask) {
newTool = tool === 'eraser' ? 'brush' : 'eraser';
} else {
newTool = tool;
}
const newStrokeWidth = tool === 'brush' ? brushSize / 2 : eraserSize / 2;
// set & then spread this to only conditionally add the "color" key
const newColor =
layer === 'base' && tool === 'brush' ? { color: brushColor } : {};
currentCanvas.pastObjects.push(currentCanvas.objects);
currentCanvas.objects.push({
kind: 'line', kind: 'line',
layer, layer,
tool, tool: newTool,
strokeWidth: tool === 'brush' ? brushSize / 2 : eraserSize / 2, strokeWidth: newStrokeWidth,
points: action.payload, points: action.payload,
...(layer === 'base' && tool === 'brush' && { color: brushColor }), ...newColor,
}); });
state[state.currentCanvas].futureObjects = []; currentCanvas.futureObjects = [];
}, },
addPointToCurrentLine: (state, action: PayloadAction<number[]>) => { addPointToCurrentLine: (state, action: PayloadAction<number[]>) => {
const lastLine = const lastLine =
@ -579,14 +592,18 @@ export const canvasSlice = createSlice({
const newObjects = state[state.currentCanvas].pastObjects.pop(); const newObjects = state[state.currentCanvas].pastObjects.pop();
if (!newObjects) return; if (!newObjects) return;
state[state.currentCanvas].futureObjects.unshift(state[state.currentCanvas].objects); state[state.currentCanvas].futureObjects.unshift(
state[state.currentCanvas].objects
);
state[state.currentCanvas].objects = newObjects; state[state.currentCanvas].objects = newObjects;
}, },
redo: (state) => { redo: (state) => {
if (state[state.currentCanvas].futureObjects.length === 0) return; if (state[state.currentCanvas].futureObjects.length === 0) return;
const newObjects = state[state.currentCanvas].futureObjects.shift(); const newObjects = state[state.currentCanvas].futureObjects.shift();
if (!newObjects) return; if (!newObjects) return;
state[state.currentCanvas].pastObjects.push(state[state.currentCanvas].objects); state[state.currentCanvas].pastObjects.push(
state[state.currentCanvas].objects
);
state[state.currentCanvas].objects = newObjects; state[state.currentCanvas].objects = newObjects;
}, },
setShouldShowGrid: (state, action: PayloadAction<boolean>) => { setShouldShowGrid: (state, action: PayloadAction<boolean>) => {