feat(ui): sd1 outpaint graph

This commit is contained in:
psychedelicious 2024-06-24 21:07:47 +10:00
parent c3acc15e8b
commit 221f32eca7
5 changed files with 70 additions and 51 deletions

View File

@ -16,7 +16,7 @@ export const addImageToImage = async (
scaledSize: Dimensions, scaledSize: Dimensions,
bbox: CanvasV2State['bbox'], bbox: CanvasV2State['bbox'],
strength: ParameterStrength strength: ParameterStrength
) => { ): Promise<Invocation<'img_resize' | 'l2i'>> => {
denoise.denoising_start = 1 - strength; denoise.denoising_start = 1 - strength;
const cropBbox = pick(bbox, ['x', 'y', 'width', 'height']); const cropBbox = pick(bbox, ['x', 'y', 'width', 'height']);
@ -46,11 +46,12 @@ export const addImageToImage = async (
g.addEdge(l2i, 'image', resizeImageToOriginalSize, 'image'); g.addEdge(l2i, 'image', resizeImageToOriginalSize, 'image');
// This is the new output node // This is the new output node
imageOutput = resizeImageToOriginalSize; return resizeImageToOriginalSize;
} else { } else {
// No need to resize, just denoise // No need to resize, just denoise
const i2l = g.addNode({ id: 'i2l', type: 'i2l', image: { image_name: initialImage.image_name } }); const i2l = g.addNode({ id: 'i2l', type: 'i2l', image: { image_name: initialImage.image_name } });
g.addEdge(vaeSource, 'vae', i2l, 'vae'); g.addEdge(vaeSource, 'vae', i2l, 'vae');
g.addEdge(i2l, 'latents', denoise, 'latents'); g.addEdge(i2l, 'latents', denoise, 'latents');
return l2i;
} }
}; };

View File

@ -12,14 +12,13 @@ export const addInpaint = async (
denoise: Invocation<'denoise_latents'>, denoise: Invocation<'denoise_latents'>,
vaeSource: Invocation<'main_model_loader' | 'seamless' | 'vae_loader'>, vaeSource: Invocation<'main_model_loader' | 'seamless' | 'vae_loader'>,
modelLoader: Invocation<'main_model_loader'>, modelLoader: Invocation<'main_model_loader'>,
imageOutput: Invocation<'canvas_paste_back' | 'img_nsfw' | 'img_resize' | 'img_watermark' | 'l2i'>,
originalSize: Dimensions, originalSize: Dimensions,
scaledSize: Dimensions, scaledSize: Dimensions,
bbox: CanvasV2State['bbox'], bbox: CanvasV2State['bbox'],
compositing: CanvasV2State['compositing'], compositing: CanvasV2State['compositing'],
strength: ParameterStrength, strength: ParameterStrength,
vaePrecision: ParameterPrecision vaePrecision: ParameterPrecision
) => { ): Promise<Invocation<'canvas_paste_back'>> => {
denoise.denoising_start = 1 - strength; denoise.denoising_start = 1 - strength;
const cropBbox = pick(bbox, ['x', 'y', 'width', 'height']); const cropBbox = pick(bbox, ['x', 'y', 'width', 'height']);
@ -98,7 +97,7 @@ export const addInpaint = async (
g.addEdge(resizeImageToOriginalSize, 'image', canvasPasteBack, 'target_image'); g.addEdge(resizeImageToOriginalSize, 'image', canvasPasteBack, 'target_image');
g.addEdge(resizeMaskToOriginalSize, 'image', canvasPasteBack, 'mask'); g.addEdge(resizeMaskToOriginalSize, 'image', canvasPasteBack, 'mask');
imageOutput = canvasPasteBack; return canvasPasteBack;
} else { } else {
// No scale before processing, much simpler // No scale before processing, much simpler
const i2l = g.addNode({ id: 'i2l', type: 'i2l', image: { image_name: initialImage.image_name } }); const i2l = g.addNode({ id: 'i2l', type: 'i2l', image: { image_name: initialImage.image_name } });
@ -132,6 +131,6 @@ export const addInpaint = async (
g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask'); g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask');
g.addEdge(l2i, 'image', canvasPasteBack, 'target_image'); g.addEdge(l2i, 'image', canvasPasteBack, 'target_image');
imageOutput = canvasPasteBack; return canvasPasteBack;
} }
}; };

View File

@ -13,14 +13,13 @@ export const addOutpaint = async (
denoise: Invocation<'denoise_latents'>, denoise: Invocation<'denoise_latents'>,
vaeSource: Invocation<'main_model_loader' | 'seamless' | 'vae_loader'>, vaeSource: Invocation<'main_model_loader' | 'seamless' | 'vae_loader'>,
modelLoader: Invocation<'main_model_loader'>, modelLoader: Invocation<'main_model_loader'>,
imageOutput: Invocation<'canvas_paste_back' | 'img_nsfw' | 'img_resize' | 'img_watermark' | 'l2i'>,
originalSize: Dimensions, originalSize: Dimensions,
scaledSize: Dimensions, scaledSize: Dimensions,
bbox: CanvasV2State['bbox'], bbox: CanvasV2State['bbox'],
compositing: CanvasV2State['compositing'], compositing: CanvasV2State['compositing'],
strength: ParameterStrength, strength: ParameterStrength,
vaePrecision: ParameterPrecision vaePrecision: ParameterPrecision
) => { ): Promise<Invocation<'canvas_paste_back'>> => {
denoise.denoising_start = 1 - strength; denoise.denoising_start = 1 - strength;
const cropBbox = pick(bbox, ['x', 'y', 'width', 'height']); const cropBbox = pick(bbox, ['x', 'y', 'width', 'height']);
@ -36,24 +35,65 @@ export const addOutpaint = async (
if (!isEqual(scaledSize, originalSize)) { if (!isEqual(scaledSize, originalSize)) {
// Scale before processing requires some resizing // Scale before processing requires some resizing
const i2l = g.addNode({ id: 'i2l', type: 'i2l' });
// Combine the inpaint mask and the initial image's alpha channel into a single mask
const maskAlphaToMask = g.addNode({
id: 'alpha_to_mask',
type: 'tomask',
image: { image_name: maskImage.image_name },
invert: true,
});
const initialImageAlphaToMask = g.addNode({
id: 'image_alpha_to_mask',
type: 'tomask',
image: { image_name: initialImage.image_name },
});
const maskCombine = g.addNode({
id: 'mask_combine',
type: 'mask_combine',
});
g.addEdge(maskAlphaToMask, 'image', maskCombine, 'mask1');
g.addEdge(initialImageAlphaToMask, 'image', maskCombine, 'mask2');
// Resize the combined and initial image to the scaled size
const resizeMaskToScaledSize = g.addNode({
id: 'resize_mask_to_scaled_size',
type: 'img_resize',
...scaledSize,
});
g.addEdge(maskCombine, 'image', resizeMaskToScaledSize, 'image');
// Resize the initial image to the scaled size and infill
const resizeImageToScaledSize = g.addNode({ const resizeImageToScaledSize = g.addNode({
id: 'resize_image_to_scaled_size', id: 'resize_image_to_scaled_size',
type: 'img_resize', type: 'img_resize',
image: { image_name: initialImage.image_name }, image: { image_name: initialImage.image_name },
...scaledSize, ...scaledSize,
}); });
const alphaToMask = g.addNode({ g.addEdge(resizeImageToScaledSize, 'image', infill, 'image');
id: 'alpha_to_mask',
type: 'tomask', // Create the gradient denoising mask from the combined mask
image: { image_name: maskImage.image_name }, const createGradientMask = g.addNode({
invert: true, id: 'create_gradient_mask',
}); type: 'create_gradient_mask',
const resizeMaskToScaledSize = g.addNode({ coherence_mode: compositing.canvasCoherenceMode,
id: 'resize_mask_to_scaled_size', minimum_denoise: compositing.canvasCoherenceMinDenoise,
type: 'img_resize', edge_radius: compositing.canvasCoherenceEdgeSize,
...scaledSize, fp32: vaePrecision === 'fp32',
}); });
g.addEdge(infill, 'image', createGradientMask, 'image');
g.addEdge(maskCombine, 'image', createGradientMask, 'mask');
g.addEdge(vaeSource, 'vae', createGradientMask, 'vae');
g.addEdge(modelLoader, 'unet', createGradientMask, 'unet');
g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask');
// Decode infilled image and connect to denoise
const i2l = g.addNode({ id: 'i2l', type: 'i2l' });
g.addEdge(infill, 'image', i2l, 'image');
g.addEdge(vaeSource, 'vae', i2l, 'vae');
g.addEdge(i2l, 'latents', denoise, 'latents');
// Resize the output image back to the original size
const resizeImageToOriginalSize = g.addNode({ const resizeImageToOriginalSize = g.addNode({
id: 'resize_image_to_original_size', id: 'resize_image_to_original_size',
type: 'img_resize', type: 'img_resize',
@ -64,14 +104,6 @@ export const addOutpaint = async (
type: 'img_resize', type: 'img_resize',
...originalSize, ...originalSize,
}); });
const createGradientMask = g.addNode({
id: 'create_gradient_mask',
type: 'create_gradient_mask',
coherence_mode: compositing.canvasCoherenceMode,
minimum_denoise: compositing.canvasCoherenceMinDenoise,
edge_radius: compositing.canvasCoherenceEdgeSize,
fp32: vaePrecision === 'fp32',
});
const canvasPasteBack = g.addNode({ const canvasPasteBack = g.addNode({
id: 'canvas_paste_back', id: 'canvas_paste_back',
type: 'canvas_paste_back', type: 'canvas_paste_back',
@ -80,17 +112,6 @@ export const addOutpaint = async (
}); });
// Resize initial image and mask to scaled size, feed into to gradient mask // Resize initial image and mask to scaled size, feed into to gradient mask
g.addEdge(alphaToMask, 'image', resizeMaskToScaledSize, 'image');
g.addEdge(resizeImageToScaledSize, 'image', i2l, 'image');
g.addEdge(i2l, 'latents', denoise, 'latents');
g.addEdge(vaeSource, 'vae', i2l, 'vae');
g.addEdge(vaeSource, 'vae', createGradientMask, 'vae');
g.addEdge(modelLoader, 'unet', createGradientMask, 'unet');
g.addEdge(resizeImageToScaledSize, 'image', createGradientMask, 'image');
g.addEdge(resizeMaskToScaledSize, 'image', createGradientMask, 'mask');
g.addEdge(createGradientMask, 'denoise_mask', denoise, 'denoise_mask');
// After denoising, resize the image and mask back to original size // After denoising, resize the image and mask back to original size
g.addEdge(l2i, 'image', resizeImageToOriginalSize, 'image'); g.addEdge(l2i, 'image', resizeImageToOriginalSize, 'image');
@ -100,7 +121,7 @@ export const addOutpaint = async (
g.addEdge(resizeImageToOriginalSize, 'image', canvasPasteBack, 'target_image'); g.addEdge(resizeImageToOriginalSize, 'image', canvasPasteBack, 'target_image');
g.addEdge(resizeMaskToOriginalSize, 'image', canvasPasteBack, 'mask'); g.addEdge(resizeMaskToOriginalSize, 'image', canvasPasteBack, 'mask');
imageOutput = canvasPasteBack; return canvasPasteBack;
} else { } else {
infill.image = { image_name: initialImage.image_name }; infill.image = { image_name: initialImage.image_name };
// No scale before processing, much simpler // No scale before processing, much simpler
@ -147,6 +168,6 @@ export const addOutpaint = async (
g.addEdge(infill, 'image', canvasPasteBack, 'source_image'); g.addEdge(infill, 'image', canvasPasteBack, 'source_image');
g.addEdge(l2i, 'image', canvasPasteBack, 'target_image'); g.addEdge(l2i, 'image', canvasPasteBack, 'target_image');
imageOutput = canvasPasteBack; return canvasPasteBack;
} }
}; };

View File

@ -6,10 +6,9 @@ import type { Invocation } from 'services/api/types';
export const addTextToImage = ( export const addTextToImage = (
g: Graph, g: Graph,
l2i: Invocation<'l2i'>, l2i: Invocation<'l2i'>,
imageOutput: Invocation<'canvas_paste_back' | 'img_nsfw' | 'img_resize' | 'img_watermark' | 'l2i'>,
originalSize: Dimensions, originalSize: Dimensions,
scaledSize: Dimensions scaledSize: Dimensions
) => { ): Invocation<'img_resize' | 'l2i'> => {
if (!isEqual(scaledSize, originalSize)) { if (!isEqual(scaledSize, originalSize)) {
// We need to resize the output image back to the original size // We need to resize the output image back to the original size
const resizeImageToOriginalSize = g.addNode({ const resizeImageToOriginalSize = g.addNode({
@ -19,7 +18,8 @@ export const addTextToImage = (
}); });
g.addEdge(l2i, 'image', resizeImageToOriginalSize, 'image'); g.addEdge(l2i, 'image', resizeImageToOriginalSize, 'image');
// This is the new output node return resizeImageToOriginalSize;
imageOutput = resizeImageToOriginalSize; } else {
return l2i;
} }
}; };

View File

@ -109,7 +109,6 @@ export const buildSD1Graph = async (state: RootState, manager: KonvaNodeManager)
type: 'l2i', type: 'l2i',
id: LATENTS_TO_IMAGE, id: LATENTS_TO_IMAGE,
fp32: vaePrecision === 'fp32', fp32: vaePrecision === 'fp32',
board: getBoardField(state),
}); });
const vaeLoader = const vaeLoader =
vae?.base === model.base vae?.base === model.base
@ -162,9 +161,9 @@ export const buildSD1Graph = async (state: RootState, manager: KonvaNodeManager)
g.addEdge(vaeSource, 'vae', l2i, 'vae'); g.addEdge(vaeSource, 'vae', l2i, 'vae');
if (generationMode === 'txt2img') { if (generationMode === 'txt2img') {
addTextToImage(g, l2i, imageOutput, originalSize, scaledSize); imageOutput = addTextToImage(g, l2i, originalSize, scaledSize);
} else if (generationMode === 'img2img') { } else if (generationMode === 'img2img') {
addImageToImage( imageOutput = await addImageToImage(
g, g,
manager, manager,
l2i, l2i,
@ -178,14 +177,13 @@ export const buildSD1Graph = async (state: RootState, manager: KonvaNodeManager)
); );
} else if (generationMode === 'inpaint') { } else if (generationMode === 'inpaint') {
const { compositing } = state.canvasV2; const { compositing } = state.canvasV2;
addInpaint( imageOutput = await addInpaint(
g, g,
manager, manager,
l2i, l2i,
denoise, denoise,
vaeSource, vaeSource,
modelLoader, modelLoader,
imageOutput,
originalSize, originalSize,
scaledSize, scaledSize,
bbox, bbox,
@ -195,14 +193,13 @@ export const buildSD1Graph = async (state: RootState, manager: KonvaNodeManager)
); );
} else if (generationMode === 'outpaint') { } else if (generationMode === 'outpaint') {
const { compositing } = state.canvasV2; const { compositing } = state.canvasV2;
addOutpaint( imageOutput = await addOutpaint(
g, g,
manager, manager,
l2i, l2i,
denoise, denoise,
vaeSource, vaeSource,
modelLoader, modelLoader,
imageOutput,
originalSize, originalSize,
scaledSize, scaledSize,
bbox, bbox,
@ -244,6 +241,7 @@ export const buildSD1Graph = async (state: RootState, manager: KonvaNodeManager)
// This is the terminal node and must always save to gallery. // This is the terminal node and must always save to gallery.
imageOutput.is_intermediate = false; imageOutput.is_intermediate = false;
imageOutput.use_cache = false; imageOutput.use_cache = false;
imageOutput.board = getBoardField(state);
g.setMetadataReceivingNode(imageOutput); g.setMetadataReceivingNode(imageOutput);
return g.getGraph(); return g.getGraph();