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 flask import Flask, redirect, send_from_directory, flash, request, url_for, jsonify
from flask_socketio import SocketIO
from PIL import Image
from PIL import Image, ImageOps
from PIL.Image import Image as ImageType
from uuid import uuid4
from threading import Event
@ -698,11 +698,11 @@ class InvokeAIWebServer:
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]
init_img_url = generation_parameters["init_img"]
init_img_url = generation_parameters["init_img"]
init_mask_url = generation_parameters["init_mask"]
init_img_path = self.get_image_path_from_url(init_img_url)
@ -723,6 +723,9 @@ class InvokeAIWebServer:
generation_parameters["init_mask"]
).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
transparency where inpainting should occur. This is the kind of

View File

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

View File

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

View File

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

View File

@ -546,25 +546,38 @@ export const canvasSlice = createSlice({
});
},
addLine: (state, action: PayloadAction<number[]>) => {
const { tool, layer, brushColor, brushSize, eraserSize } =
state[state.currentCanvas];
const currentCanvas = state[state.currentCanvas];
const { tool, layer, brushColor, brushSize, eraserSize } = currentCanvas;
if (tool === 'move') return;
state[state.currentCanvas].pastObjects.push(
state[state.currentCanvas].objects
);
let newTool: CanvasDrawingTool;
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',
layer,
tool,
strokeWidth: tool === 'brush' ? brushSize / 2 : eraserSize / 2,
tool: newTool,
strokeWidth: newStrokeWidth,
points: action.payload,
...(layer === 'base' && tool === 'brush' && { color: brushColor }),
...newColor,
});
state[state.currentCanvas].futureObjects = [];
currentCanvas.futureObjects = [];
},
addPointToCurrentLine: (state, action: PayloadAction<number[]>) => {
const lastLine =
@ -579,14 +592,18 @@ export const canvasSlice = createSlice({
const newObjects = state[state.currentCanvas].pastObjects.pop();
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;
},
redo: (state) => {
if (state[state.currentCanvas].futureObjects.length === 0) return;
const newObjects = state[state.currentCanvas].futureObjects.shift();
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;
},
setShouldShowGrid: (state, action: PayloadAction<boolean>) => {