feat: Add Paste / Mask Blur / Color Correction to Inpainting

Seam options are now removed. They are replaced by two options --Mask Blur and Mask Blur Method .. which control the softness of the mask that is being painted.
This commit is contained in:
blessedcoolant 2023-08-12 03:28:19 +12:00
parent 69a9dc7b36
commit 1affb7f647
11 changed files with 147 additions and 155 deletions

View File

@ -503,6 +503,9 @@
"hiresStrength": "High Res Strength",
"imageFit": "Fit Initial Image To Output Size",
"codeformerFidelity": "Fidelity",
"maskAdjustmentsHeader": "Mask Adjustments",
"maskBlur": "Mask Blur",
"maskBlurMethod": "Mask Blur Method",
"seamSize": "Seam Size",
"seamBlur": "Seam Blur",
"seamStrength": "Seam Strength",

View File

@ -13,12 +13,15 @@ import { addVAEToGraph } from './addVAEToGraph';
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
import {
CLIP_SKIP,
COLOR_CORRECT,
INPAINT,
INPAINT_FINAL_IMAGE,
INPAINT_GRAPH,
INPAINT_IMAGE,
ITERATE,
LATENTS_TO_IMAGE,
MAIN_MODEL_LOADER,
MASK_BLUR,
NEGATIVE_CONDITIONING,
NOISE,
POSITIVE_CONDITIONING,
@ -50,10 +53,8 @@ export const buildCanvasInpaintGraph = (
vaePrecision,
shouldUseNoiseSettings,
shouldUseCpuNoise,
seamSize,
seamBlur,
seamSteps,
seamStrength,
maskBlur,
maskBlurMethod,
tileSize,
infillMethod,
clipSkip,
@ -90,7 +91,6 @@ export const buildCanvasInpaintGraph = (
scheduler: scheduler,
denoising_start: 1 - strength,
denoising_end: 1,
mask: canvasMaskImage,
},
[INPAINT_IMAGE]: {
type: 'i2l',
@ -137,7 +137,27 @@ export const buildCanvasInpaintGraph = (
is_intermediate: true,
skipped_layers: clipSkip,
},
[COLOR_CORRECT]: {
type: 'color_correct',
id: COLOR_CORRECT,
is_intermediate: true,
reference: canvasInitImage,
mask: canvasMaskImage,
},
[MASK_BLUR]: {
type: 'img_blur',
id: MASK_BLUR,
is_intermediate: true,
image: canvasMaskImage,
radius: maskBlur,
blur_type: maskBlurMethod,
},
[INPAINT_FINAL_IMAGE]: {
type: 'img_paste',
id: INPAINT_FINAL_IMAGE,
is_intermediate: true,
base_image: canvasInitImage,
},
[RANGE_OF_SIZE]: {
type: 'range_of_size',
id: RANGE_OF_SIZE,
@ -234,6 +254,16 @@ export const buildCanvasInpaintGraph = (
field: 'latents',
},
},
{
source: {
node_id: MASK_BLUR,
field: 'image',
},
destination: {
node_id: INPAINT,
field: 'mask',
},
},
{
source: {
node_id: RANGE_OF_SIZE,
@ -264,6 +294,36 @@ export const buildCanvasInpaintGraph = (
field: 'latents',
},
},
{
source: {
node_id: LATENTS_TO_IMAGE,
field: 'image',
},
destination: {
node_id: COLOR_CORRECT,
field: 'image',
},
},
{
source: {
node_id: MASK_BLUR,
field: 'image',
},
destination: {
node_id: INPAINT_FINAL_IMAGE,
field: 'mask',
},
},
{
source: {
node_id: COLOR_CORRECT,
field: 'image',
},
destination: {
node_id: INPAINT_FINAL_IMAGE,
field: 'image',
},
},
],
};

View File

@ -0,0 +1,21 @@
import { Flex } from '@chakra-ui/react';
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamMaskBlur from './ParamMaskBlur';
import ParamMaskBlurMethod from './ParamMaskBlurMethod';
const ParamMaskAdjustmentCollapse = () => {
const { t } = useTranslation();
return (
<IAICollapse label={t('parameters.maskAdjustmentsHeader')}>
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
<ParamMaskBlur />
<ParamMaskBlurMethod />
</Flex>
</IAICollapse>
);
};
export default memo(ParamMaskAdjustmentCollapse);

View File

@ -1,31 +1,31 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamBlur } from 'features/parameters/store/generationSlice';
import { setMaskBlur } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
export default function ParamSeamBlur() {
export default function ParamMaskBlur() {
const dispatch = useAppDispatch();
const seamBlur = useAppSelector(
(state: RootState) => state.generation.seamBlur
const maskBlur = useAppSelector(
(state: RootState) => state.generation.maskBlur
);
const { t } = useTranslation();
return (
<IAISlider
label={t('parameters.seamBlur')}
label={t('parameters.maskBlur')}
min={0}
max={64}
sliderNumberInputProps={{ max: 512 }}
value={seamBlur}
value={maskBlur}
onChange={(v) => {
dispatch(setSeamBlur(v));
dispatch(setMaskBlur(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => {
dispatch(setSeamBlur(16));
dispatch(setMaskBlur(16));
}}
/>
);

View File

@ -0,0 +1,36 @@
import { SelectItem } from '@mantine/core';
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAIMantineSelect from 'common/components/IAIMantineSelect';
import { setMaskBlurMethod } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
export type MaskBlurMethods = 'box' | 'gaussian';
const maskBlurMethods: SelectItem[] = [
{ label: 'Box Blur', value: 'box' },
{ label: 'Gaussian Blur', value: 'gaussian' },
];
export default function ParamMaskBlurMethod() {
const maskBlurMethod = useAppSelector(
(state: RootState) => state.generation.maskBlurMethod
);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleMaskBlurMethodChange = (v: string | null) => {
if (!v) return;
dispatch(setMaskBlurMethod(v as MaskBlurMethods));
};
return (
<IAIMantineSelect
value={maskBlurMethod}
onChange={handleMaskBlurMethodChange}
label={t('parameters.maskBlurMethod')}
data={maskBlurMethods}
/>
);
}

View File

@ -1,22 +0,0 @@
import IAICollapse from 'common/components/IAICollapse';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ParamSeamBlur from './ParamSeamBlur';
import ParamSeamSize from './ParamSeamSize';
import ParamSeamSteps from './ParamSeamSteps';
import ParamSeamStrength from './ParamSeamStrength';
const ParamSeamCorrectionCollapse = () => {
const { t } = useTranslation();
return (
<IAICollapse label={t('parameters.seamCorrectionHeader')}>
<ParamSeamSize />
<ParamSeamBlur />
<ParamSeamStrength />
<ParamSeamSteps />
</IAICollapse>
);
};
export default memo(ParamSeamCorrectionCollapse);

View File

@ -1,31 +0,0 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamSize } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
export default function ParamSeamSize() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const seamSize = useAppSelector(
(state: RootState) => state.generation.seamSize
);
return (
<IAISlider
label={t('parameters.seamSize')}
min={1}
max={256}
sliderNumberInputProps={{ max: 512 }}
value={seamSize}
onChange={(v) => {
dispatch(setSeamSize(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => dispatch(setSeamSize(96))}
/>
);
}

View File

@ -1,32 +0,0 @@
import type { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamSteps } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
export default function ParamSeamSteps() {
const { t } = useTranslation();
const seamSteps = useAppSelector(
(state: RootState) => state.generation.seamSteps
);
const dispatch = useAppDispatch();
return (
<IAISlider
label={t('parameters.seamSteps')}
min={1}
max={100}
sliderNumberInputProps={{ max: 999 }}
value={seamSteps}
onChange={(v) => {
dispatch(setSeamSteps(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => {
dispatch(setSeamSteps(30));
}}
/>
);
}

View File

@ -1,32 +0,0 @@
import { RootState } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import IAISlider from 'common/components/IAISlider';
import { setSeamStrength } from 'features/parameters/store/generationSlice';
import { useTranslation } from 'react-i18next';
export default function ParamSeamStrength() {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const seamStrength = useAppSelector(
(state: RootState) => state.generation.seamStrength
);
return (
<IAISlider
label={t('parameters.seamStrength')}
min={0.01}
max={0.99}
step={0.01}
value={seamStrength}
onChange={(v) => {
dispatch(setSeamStrength(v));
}}
withInput
withSliderMarks
withReset
handleReset={() => {
dispatch(setSeamStrength(0.7));
}}
/>
);
}

View File

@ -4,6 +4,7 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { configChanged } from 'features/system/store/configSlice';
import { clamp } from 'lodash-es';
import { ImageDTO } from 'services/api/types';
import { MaskBlurMethods } from '../components/Parameters/Canvas/MaskAdjustment/ParamMaskBlurMethod';
import { clipSkipMap } from '../types/constants';
import {
CfgScaleParam,
@ -33,10 +34,8 @@ export interface GenerationState {
positivePrompt: PositivePromptParam;
negativePrompt: NegativePromptParam;
scheduler: SchedulerParam;
seamBlur: number;
seamSize: number;
seamSteps: number;
seamStrength: number;
maskBlur: number;
maskBlurMethod: MaskBlurMethods;
seed: SeedParam;
seedWeights: string;
shouldFitToWidthHeight: boolean;
@ -72,10 +71,8 @@ export const initialGenerationState: GenerationState = {
positivePrompt: '',
negativePrompt: '',
scheduler: 'euler',
seamBlur: 16,
seamSize: 96,
seamSteps: 30,
seamStrength: 0.7,
maskBlur: 16,
maskBlurMethod: 'box',
seed: 0,
seedWeights: '',
shouldFitToWidthHeight: true,
@ -196,17 +193,11 @@ export const generationSlice = createSlice({
clearInitialImage: (state) => {
state.initialImage = undefined;
},
setSeamSize: (state, action: PayloadAction<number>) => {
state.seamSize = action.payload;
setMaskBlur: (state, action: PayloadAction<number>) => {
state.maskBlur = action.payload;
},
setSeamBlur: (state, action: PayloadAction<number>) => {
state.seamBlur = action.payload;
},
setSeamStrength: (state, action: PayloadAction<number>) => {
state.seamStrength = action.payload;
},
setSeamSteps: (state, action: PayloadAction<number>) => {
state.seamSteps = action.payload;
setMaskBlurMethod: (state, action: PayloadAction<MaskBlurMethods>) => {
state.maskBlurMethod = action.payload;
},
setTileSize: (state, action: PayloadAction<number>) => {
state.tileSize = action.payload;
@ -312,10 +303,8 @@ export const {
setPositivePrompt,
setNegativePrompt,
setScheduler,
setSeamBlur,
setSeamSize,
setSeamSteps,
setSeamStrength,
setMaskBlur,
setMaskBlurMethod,
setSeed,
setSeedWeights,
setShouldFitToWidthHeight,

View File

@ -2,10 +2,10 @@ import ParamDynamicPromptsCollapse from 'features/dynamicPrompts/components/Para
import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse';
import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse';
import ParamInfillAndScalingCollapse from 'features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamInfillAndScalingCollapse';
import ParamSeamCorrectionCollapse from 'features/parameters/components/Parameters/Canvas/SeamCorrection/ParamSeamCorrectionCollapse';
import ParamControlNetCollapse from 'features/parameters/components/Parameters/ControlNet/ParamControlNetCollapse';
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
// import ParamVariationCollapse from 'features/parameters/components/Parameters/Variations/ParamVariationCollapse';
import ParamMaskAdjustmentCollapse from 'features/parameters/components/Parameters/Canvas/MaskAdjustment/ParamMaskAdjustmentCollapse';
import ParamPromptArea from 'features/parameters/components/Parameters/Prompt/ParamPromptArea';
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
import UnifiedCanvasCoreParameters from './UnifiedCanvasCoreParameters';
@ -21,7 +21,7 @@ const UnifiedCanvasParameters = () => {
<ParamDynamicPromptsCollapse />
{/* <ParamVariationCollapse /> */}
<ParamSymmetryCollapse />
<ParamSeamCorrectionCollapse />
<ParamMaskAdjustmentCollapse />
<ParamInfillAndScalingCollapse />
<ParamAdvancedCollapse />
</>