mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
tidy(ui): remove old canvas graphs
This commit is contained in:
parent
89de04775e
commit
c5172d4c5a
@ -1,150 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { selectValidControlNets } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import type { ControlAdapterProcessorType, ControlNetConfig } from 'features/controlAdapters/store/types';
|
||||
import type { ImageField } from 'features/nodes/types/common';
|
||||
import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import { CONTROL_NET_COLLECT } from 'features/nodes/util/graph/constants';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import type { Invocation, NonNullableGraph, S } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const addControlNetToLinearGraph = async (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
baseNodeId: string
|
||||
): Promise<void> => {
|
||||
const controlNetMetadata: S['CoreMetadataInvocation']['controlnets'] = [];
|
||||
const controlNets = selectValidControlNets(state.controlAdapters).filter(
|
||||
({ model, processedControlImage, processorType, controlImage, isEnabled }) => {
|
||||
const hasModel = Boolean(model);
|
||||
const doesBaseMatch = model?.base === state.canvasV2.params.model?.base;
|
||||
const hasControlImage = (processedControlImage && processorType !== 'none') || controlImage;
|
||||
|
||||
return isEnabled && hasModel && doesBaseMatch && hasControlImage;
|
||||
}
|
||||
);
|
||||
|
||||
// The generation tab has special handling - its control adapters are set up in the Control Layers graph helper.
|
||||
const activeTabName = activeTabNameSelector(state);
|
||||
assert(activeTabName !== 'generation', 'Tried to use addControlNetToLinearGraph on generation tab');
|
||||
|
||||
if (controlNets.length) {
|
||||
// Even though denoise_latents' control input is SINGLE_OR_COLLECTION, keep it simple and always use a collect
|
||||
const controlNetIterateNode: Invocation<'collect'> = {
|
||||
id: CONTROL_NET_COLLECT,
|
||||
type: 'collect',
|
||||
is_intermediate: true,
|
||||
};
|
||||
graph.nodes[CONTROL_NET_COLLECT] = controlNetIterateNode;
|
||||
graph.edges.push({
|
||||
source: { node_id: CONTROL_NET_COLLECT, field: 'collection' },
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'control',
|
||||
},
|
||||
});
|
||||
|
||||
for (const controlNet of controlNets) {
|
||||
if (!controlNet.model) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
id,
|
||||
controlImage,
|
||||
processedControlImage,
|
||||
beginStepPct,
|
||||
endStepPct,
|
||||
controlMode,
|
||||
resizeMode,
|
||||
model,
|
||||
processorType,
|
||||
weight,
|
||||
} = controlNet;
|
||||
|
||||
const controlNetNode: Invocation<'controlnet'> = {
|
||||
id: `control_net_${id}`,
|
||||
type: 'controlnet',
|
||||
is_intermediate: true,
|
||||
begin_step_percent: beginStepPct,
|
||||
end_step_percent: endStepPct,
|
||||
control_mode: controlMode,
|
||||
resize_mode: resizeMode,
|
||||
control_model: model,
|
||||
control_weight: weight,
|
||||
image: buildControlImage(controlImage, processedControlImage, processorType),
|
||||
};
|
||||
|
||||
graph.nodes[controlNetNode.id] = controlNetNode;
|
||||
|
||||
controlNetMetadata.push(buildControlNetMetadata(controlNet));
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: controlNetNode.id, field: 'control' },
|
||||
destination: {
|
||||
node_id: CONTROL_NET_COLLECT,
|
||||
field: 'item',
|
||||
},
|
||||
});
|
||||
}
|
||||
upsertMetadata(graph, { controlnets: controlNetMetadata });
|
||||
}
|
||||
};
|
||||
|
||||
const buildControlImage = (
|
||||
controlImage: string | null,
|
||||
processedControlImage: string | null,
|
||||
processorType: ControlAdapterProcessorType
|
||||
): ImageField => {
|
||||
let image: ImageField | null = null;
|
||||
if (processedControlImage && processorType !== 'none') {
|
||||
// We've already processed the image in the app, so we can just use the processed image
|
||||
image = {
|
||||
image_name: processedControlImage,
|
||||
};
|
||||
} else if (controlImage) {
|
||||
// The control image is preprocessed
|
||||
image = {
|
||||
image_name: controlImage,
|
||||
};
|
||||
}
|
||||
assert(image, 'ControlNet image is required');
|
||||
return image;
|
||||
};
|
||||
|
||||
const buildControlNetMetadata = (controlNet: ControlNetConfig): S['ControlNetMetadataField'] => {
|
||||
const {
|
||||
controlImage,
|
||||
processedControlImage,
|
||||
beginStepPct,
|
||||
endStepPct,
|
||||
controlMode,
|
||||
resizeMode,
|
||||
model,
|
||||
processorType,
|
||||
weight,
|
||||
} = controlNet;
|
||||
|
||||
assert(model, 'ControlNet model is required');
|
||||
|
||||
const processed_image =
|
||||
processedControlImage && processorType !== 'none'
|
||||
? {
|
||||
image_name: processedControlImage,
|
||||
}
|
||||
: null;
|
||||
|
||||
assert(controlImage, 'ControlNet image is required');
|
||||
|
||||
return {
|
||||
control_model: model,
|
||||
control_weight: weight,
|
||||
control_mode: controlMode,
|
||||
begin_step_percent: beginStepPct,
|
||||
end_step_percent: endStepPct,
|
||||
resize_mode: resizeMode,
|
||||
image: {
|
||||
image_name: controlImage,
|
||||
},
|
||||
processed_image,
|
||||
};
|
||||
};
|
@ -1,109 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { selectValidIPAdapters } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import type { IPAdapterConfig } from 'features/controlAdapters/store/types';
|
||||
import type { ImageField } from 'features/nodes/types/common';
|
||||
import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import { IP_ADAPTER_COLLECT } from 'features/nodes/util/graph/constants';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import type { Invocation, NonNullableGraph, S } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const addIPAdapterToLinearGraph = async (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
baseNodeId: string
|
||||
): Promise<void> => {
|
||||
// The generation tab has special handling - its control adapters are set up in the Control Layers graph helper.
|
||||
const activeTabName = activeTabNameSelector(state);
|
||||
assert(activeTabName !== 'generation', 'Tried to use addT2IAdaptersToLinearGraph on generation tab');
|
||||
|
||||
const ipAdapters = selectValidIPAdapters(state.controlAdapters).filter(({ model, controlImage, isEnabled }) => {
|
||||
const hasModel = Boolean(model);
|
||||
const doesBaseMatch = model?.base === state.canvasV2.params.model?.base;
|
||||
const hasControlImage = controlImage;
|
||||
return isEnabled && hasModel && doesBaseMatch && hasControlImage;
|
||||
});
|
||||
|
||||
if (ipAdapters.length) {
|
||||
// Even though denoise_latents' ip adapter input is SINGLE_OR_COLLECTION, keep it simple and always use a collect
|
||||
const ipAdapterCollectNode: Invocation<'collect'> = {
|
||||
id: IP_ADAPTER_COLLECT,
|
||||
type: 'collect',
|
||||
is_intermediate: true,
|
||||
};
|
||||
graph.nodes[IP_ADAPTER_COLLECT] = ipAdapterCollectNode;
|
||||
graph.edges.push({
|
||||
source: { node_id: IP_ADAPTER_COLLECT, field: 'collection' },
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'ip_adapter',
|
||||
},
|
||||
});
|
||||
|
||||
const ipAdapterMetdata: S['CoreMetadataInvocation']['ipAdapters'] = [];
|
||||
|
||||
for (const ipAdapter of ipAdapters) {
|
||||
if (!ipAdapter.model) {
|
||||
return;
|
||||
}
|
||||
const { id, weight, model, clipVisionModel, method, beginStepPct, endStepPct, controlImage } = ipAdapter;
|
||||
|
||||
assert(controlImage, 'IP Adapter image is required');
|
||||
|
||||
const ipAdapterNode: Invocation<'ip_adapter'> = {
|
||||
id: `ip_adapter_${id}`,
|
||||
type: 'ip_adapter',
|
||||
is_intermediate: true,
|
||||
weight: weight,
|
||||
method: method,
|
||||
ip_adapter_model: model,
|
||||
clip_vision_model: clipVisionModel,
|
||||
begin_step_percent: beginStepPct,
|
||||
end_step_percent: endStepPct,
|
||||
image: {
|
||||
image_name: controlImage,
|
||||
},
|
||||
};
|
||||
|
||||
graph.nodes[ipAdapterNode.id] = ipAdapterNode;
|
||||
|
||||
ipAdapterMetdata.push(buildIPAdapterMetadata(ipAdapter));
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: ipAdapterNode.id, field: 'ip_adapter' },
|
||||
destination: {
|
||||
node_id: ipAdapterCollectNode.id,
|
||||
field: 'item',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
upsertMetadata(graph, { ipAdapters: ipAdapterMetdata });
|
||||
}
|
||||
};
|
||||
|
||||
const buildIPAdapterMetadata = (ipAdapter: IPAdapterConfig): S['IPAdapterMetadataField'] => {
|
||||
const { controlImage, beginStepPct, endStepPct, model, clipVisionModel, method, weight } = ipAdapter;
|
||||
|
||||
assert(model, 'IP Adapter model is required');
|
||||
|
||||
let image: ImageField | null = null;
|
||||
|
||||
if (controlImage) {
|
||||
image = {
|
||||
image_name: controlImage,
|
||||
};
|
||||
}
|
||||
|
||||
assert(image, 'IP Adapter image is required');
|
||||
|
||||
return {
|
||||
ip_adapter_model: model,
|
||||
clip_vision_model: clipVisionModel,
|
||||
weight,
|
||||
method,
|
||||
begin_step_percent: beginStepPct,
|
||||
end_step_percent: endStepPct,
|
||||
image,
|
||||
};
|
||||
};
|
@ -1,158 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CLIP_SKIP,
|
||||
LORA_LOADER,
|
||||
MAIN_MODEL_LOADER,
|
||||
NEGATIVE_CONDITIONING,
|
||||
POSITIVE_CONDITIONING,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import { filter, size } from 'lodash-es';
|
||||
import type { Invocation, NonNullableGraph, S } from 'services/api/types';
|
||||
|
||||
export const addLoRAsToGraph = async (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
baseNodeId: string,
|
||||
modelLoaderNodeId: string = MAIN_MODEL_LOADER
|
||||
): Promise<void> => {
|
||||
/**
|
||||
* LoRA nodes get the UNet and CLIP models from the main model loader and apply the LoRA to them.
|
||||
* They then output the UNet and CLIP models references on to either the next LoRA in the chain,
|
||||
* or to the inference/conditioning nodes.
|
||||
*
|
||||
* So we need to inject a LoRA chain into the graph.
|
||||
*/
|
||||
|
||||
// TODO(MM2): check base model
|
||||
const enabledLoRAs = filter(state.lora.loras, (l) => l.isEnabled ?? false);
|
||||
const loraCount = size(enabledLoRAs);
|
||||
|
||||
if (loraCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove modelLoaderNodeId unet connection to feed it to LoRAs
|
||||
graph.edges = graph.edges.filter(
|
||||
(e) => !(e.source.node_id === modelLoaderNodeId && ['unet'].includes(e.source.field))
|
||||
);
|
||||
// Remove CLIP_SKIP connections to conditionings to feed it through LoRAs
|
||||
graph.edges = graph.edges.filter((e) => !(e.source.node_id === CLIP_SKIP && ['clip'].includes(e.source.field)));
|
||||
|
||||
// we need to remember the last lora so we can chain from it
|
||||
let lastLoraNodeId = '';
|
||||
let currentLoraIndex = 0;
|
||||
const loraMetadata: S['CoreMetadataInvocation']['loras'] = [];
|
||||
|
||||
enabledLoRAs.forEach(async (lora) => {
|
||||
const { weight } = lora;
|
||||
const { key } = lora.model;
|
||||
const currentLoraNodeId = `${LORA_LOADER}_${key}`;
|
||||
const parsedModel = zModelIdentifierField.parse(lora.model);
|
||||
|
||||
const loraLoaderNode: Invocation<'lora_loader'> = {
|
||||
type: 'lora_loader',
|
||||
id: currentLoraNodeId,
|
||||
is_intermediate: true,
|
||||
lora: parsedModel,
|
||||
weight,
|
||||
};
|
||||
|
||||
loraMetadata.push({
|
||||
model: parsedModel,
|
||||
weight,
|
||||
});
|
||||
|
||||
// add to graph
|
||||
graph.nodes[currentLoraNodeId] = loraLoaderNode;
|
||||
if (currentLoraIndex === 0) {
|
||||
// first lora = start the lora chain, attach directly to model loader
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// we are in the middle of the lora chain, instead connect to the previous lora
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: lastLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
});
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: lastLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (currentLoraIndex === loraCount - 1) {
|
||||
// final lora, end the lora chain - we need to connect up to inference and conditioning nodes
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// increment the lora for the next one in the chain
|
||||
lastLoraNodeId = currentLoraNodeId;
|
||||
currentLoraIndex += 1;
|
||||
});
|
||||
|
||||
upsertMetadata(graph, { loras: loraMetadata });
|
||||
};
|
@ -1,39 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { LATENTS_TO_IMAGE, NSFW_CHECKER } from 'features/nodes/util/graph/constants';
|
||||
import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { Invocation, NonNullableGraph } from 'services/api/types';
|
||||
|
||||
export const addNSFWCheckerToGraph = (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
nodeIdToAddTo = LATENTS_TO_IMAGE
|
||||
): void => {
|
||||
const nodeToAddTo = graph.nodes[nodeIdToAddTo] as Invocation<'l2i'> | undefined;
|
||||
|
||||
if (!nodeToAddTo) {
|
||||
// something has gone terribly awry
|
||||
return;
|
||||
}
|
||||
|
||||
nodeToAddTo.is_intermediate = true;
|
||||
nodeToAddTo.use_cache = true;
|
||||
|
||||
const nsfwCheckerNode: Invocation<'img_nsfw'> = {
|
||||
id: NSFW_CHECKER,
|
||||
type: 'img_nsfw',
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
};
|
||||
|
||||
graph.nodes[NSFW_CHECKER] = nsfwCheckerNode;
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: nodeIdToAddTo,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: NSFW_CHECKER,
|
||||
field: 'image',
|
||||
},
|
||||
});
|
||||
};
|
@ -1,208 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
LORA_LOADER,
|
||||
NEGATIVE_CONDITIONING,
|
||||
POSITIVE_CONDITIONING,
|
||||
SDXL_MODEL_LOADER,
|
||||
SDXL_REFINER_INPAINT_CREATE_MASK,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import { filter, size } from 'lodash-es';
|
||||
import type { Invocation, NonNullableGraph, S } from 'services/api/types';
|
||||
|
||||
export const addSDXLLoRAsToGraph = async (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
baseNodeId: string,
|
||||
modelLoaderNodeId: string = SDXL_MODEL_LOADER
|
||||
): Promise<void> => {
|
||||
/**
|
||||
* LoRA nodes get the UNet and CLIP models from the main model loader and apply the LoRA to them.
|
||||
* They then output the UNet and CLIP models references on to either the next LoRA in the chain,
|
||||
* or to the inference/conditioning nodes.
|
||||
*
|
||||
* So we need to inject a LoRA chain into the graph.
|
||||
*/
|
||||
|
||||
// TODO(MM2): check base model
|
||||
const enabledLoRAs = filter(state.lora.loras, (l) => l.isEnabled ?? false);
|
||||
const loraCount = size(enabledLoRAs);
|
||||
|
||||
if (loraCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loraMetadata: S['CoreMetadataInvocation']['loras'] = [];
|
||||
|
||||
// Handle Seamless Plugs
|
||||
const unetLoaderId = modelLoaderNodeId;
|
||||
let clipLoaderId = modelLoaderNodeId;
|
||||
if ([SEAMLESS, SDXL_REFINER_INPAINT_CREATE_MASK].includes(modelLoaderNodeId)) {
|
||||
clipLoaderId = SDXL_MODEL_LOADER;
|
||||
}
|
||||
|
||||
// Remove modelLoaderNodeId unet/clip/clip2 connections to feed it to LoRAs
|
||||
graph.edges = graph.edges.filter(
|
||||
(e) =>
|
||||
!(e.source.node_id === unetLoaderId && ['unet'].includes(e.source.field)) &&
|
||||
!(e.source.node_id === clipLoaderId && ['clip'].includes(e.source.field)) &&
|
||||
!(e.source.node_id === clipLoaderId && ['clip2'].includes(e.source.field))
|
||||
);
|
||||
|
||||
// we need to remember the last lora so we can chain from it
|
||||
let lastLoraNodeId = '';
|
||||
let currentLoraIndex = 0;
|
||||
|
||||
enabledLoRAs.forEach(async (lora) => {
|
||||
const { weight } = lora;
|
||||
const currentLoraNodeId = `${LORA_LOADER}_${lora.model.key}`;
|
||||
const parsedModel = zModelIdentifierField.parse(lora.model);
|
||||
|
||||
const loraLoaderNode: Invocation<'sdxl_lora_loader'> = {
|
||||
type: 'sdxl_lora_loader',
|
||||
id: currentLoraNodeId,
|
||||
is_intermediate: true,
|
||||
lora: parsedModel,
|
||||
weight,
|
||||
};
|
||||
|
||||
loraMetadata.push({ model: parsedModel, weight });
|
||||
|
||||
// add to graph
|
||||
graph.nodes[currentLoraNodeId] = loraLoaderNode;
|
||||
if (currentLoraIndex === 0) {
|
||||
// first lora = start the lora chain, attach directly to model loader
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: unetLoaderId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: clipLoaderId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: clipLoaderId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// we are in the middle of the lora chain, instead connect to the previous lora
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: lastLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
});
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: lastLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: lastLoraNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (currentLoraIndex === loraCount - 1) {
|
||||
// final lora, end the lora chain - we need to connect up to inference and conditioning nodes
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: currentLoraNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// increment the lora for the next one in the chain
|
||||
lastLoraNodeId = currentLoraNodeId;
|
||||
currentLoraIndex += 1;
|
||||
});
|
||||
|
||||
upsertMetadata(graph, { loras: loraMetadata });
|
||||
};
|
@ -1,239 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { getModelMetadataField, upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_OUTPUT,
|
||||
INPAINT_CREATE_MASK,
|
||||
LATENTS_TO_IMAGE,
|
||||
SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
SDXL_CANVAS_INPAINT_GRAPH,
|
||||
SDXL_CANVAS_OUTPAINT_GRAPH,
|
||||
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
SDXL_MODEL_LOADER,
|
||||
SDXL_REFINER_DENOISE_LATENTS,
|
||||
SDXL_REFINER_MODEL_LOADER,
|
||||
SDXL_REFINER_NEGATIVE_CONDITIONING,
|
||||
SDXL_REFINER_POSITIVE_CONDITIONING,
|
||||
SDXL_REFINER_SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import { getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { NonNullableGraph } from 'services/api/types';
|
||||
import { isRefinerMainModelModelConfig } from 'services/api/types';
|
||||
|
||||
export const addSDXLRefinerToGraph = async (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
baseNodeId: string,
|
||||
modelLoaderNodeId?: string
|
||||
): Promise<void> => {
|
||||
const {
|
||||
refinerModel,
|
||||
refinerPositiveAestheticScore,
|
||||
refinerNegativeAestheticScore,
|
||||
refinerSteps,
|
||||
refinerScheduler,
|
||||
refinerCFGScale,
|
||||
refinerStart,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
if (!refinerModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { seamlessXAxis, seamlessYAxis } = state.canvasV2.params;
|
||||
const { boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
const modelConfig = await fetchModelConfigWithTypeGuard(refinerModel.key, isRefinerMainModelModelConfig);
|
||||
|
||||
upsertMetadata(graph, {
|
||||
refiner_model: getModelMetadataField(modelConfig),
|
||||
refiner_positive_aesthetic_score: refinerPositiveAestheticScore,
|
||||
refiner_negative_aesthetic_score: refinerNegativeAestheticScore,
|
||||
refiner_cfg_scale: refinerCFGScale,
|
||||
refiner_scheduler: refinerScheduler,
|
||||
refiner_start: refinerStart,
|
||||
refiner_steps: refinerSteps,
|
||||
});
|
||||
|
||||
const modelLoaderId = modelLoaderNodeId ? modelLoaderNodeId : SDXL_MODEL_LOADER;
|
||||
|
||||
// Construct Style Prompt
|
||||
const { positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
// Unplug SDXL Latents Generation To Latents To Image
|
||||
graph.edges = graph.edges.filter((e) => !(e.source.node_id === baseNodeId && ['latents'].includes(e.source.field)));
|
||||
|
||||
graph.edges = graph.edges.filter((e) => !(e.source.node_id === modelLoaderId && ['vae'].includes(e.source.field)));
|
||||
|
||||
graph.nodes[SDXL_REFINER_MODEL_LOADER] = {
|
||||
type: 'sdxl_refiner_model_loader',
|
||||
id: SDXL_REFINER_MODEL_LOADER,
|
||||
model: refinerModel,
|
||||
};
|
||||
graph.nodes[SDXL_REFINER_POSITIVE_CONDITIONING] = {
|
||||
type: 'sdxl_refiner_compel_prompt',
|
||||
id: SDXL_REFINER_POSITIVE_CONDITIONING,
|
||||
style: positiveStylePrompt,
|
||||
aesthetic_score: refinerPositiveAestheticScore,
|
||||
};
|
||||
graph.nodes[SDXL_REFINER_NEGATIVE_CONDITIONING] = {
|
||||
type: 'sdxl_refiner_compel_prompt',
|
||||
id: SDXL_REFINER_NEGATIVE_CONDITIONING,
|
||||
style: negativeStylePrompt,
|
||||
aesthetic_score: refinerNegativeAestheticScore,
|
||||
};
|
||||
graph.nodes[SDXL_REFINER_DENOISE_LATENTS] = {
|
||||
type: 'denoise_latents',
|
||||
id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
cfg_scale: refinerCFGScale,
|
||||
steps: refinerSteps,
|
||||
scheduler: refinerScheduler,
|
||||
denoising_start: refinerStart,
|
||||
denoising_end: 1,
|
||||
};
|
||||
|
||||
// Add Seamless To Refiner
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
graph.nodes[SDXL_REFINER_SEAMLESS] = {
|
||||
id: SDXL_REFINER_SEAMLESS,
|
||||
type: 'seamless',
|
||||
seamless_x: seamlessXAxis,
|
||||
seamless_y: seamlessYAxis,
|
||||
};
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_REFINER_MODEL_LOADER,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_SEAMLESS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_REFINER_MODEL_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_SEAMLESS,
|
||||
field: 'vae',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_REFINER_SEAMLESS,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: SDXL_REFINER_MODEL_LOADER,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_REFINER_MODEL_LOADER,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_POSITIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_REFINER_MODEL_LOADER,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_NEGATIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_REFINER_POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_REFINER_NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: baseNodeId,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (graph.id === SDXL_CANVAS_INPAINT_GRAPH || graph.id === SDXL_CANVAS_OUTPAINT_GRAPH) {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (graph.id === SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH || graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH) {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: isUsingScaledDimensions ? LATENTS_TO_IMAGE : CANVAS_OUTPUT,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: SDXL_REFINER_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
@ -1,102 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
DENOISE_LATENTS,
|
||||
SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
SDXL_CANVAS_INPAINT_GRAPH,
|
||||
SDXL_CANVAS_OUTPAINT_GRAPH,
|
||||
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
SDXL_CONTROL_LAYERS_GRAPH,
|
||||
SDXL_DENOISE_LATENTS,
|
||||
SEAMLESS,
|
||||
VAE_LOADER,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import type { NonNullableGraph } from 'services/api/types';
|
||||
|
||||
export const addSeamlessToLinearGraph = (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
modelLoaderNodeId: string
|
||||
): void => {
|
||||
// Remove Existing UNet Connections
|
||||
const { seamlessXAxis, seamlessYAxis, vae } = state.canvasV2.params;
|
||||
const isAutoVae = !vae;
|
||||
|
||||
graph.nodes[SEAMLESS] = {
|
||||
id: SEAMLESS,
|
||||
type: 'seamless',
|
||||
seamless_x: seamlessXAxis,
|
||||
seamless_y: seamlessYAxis,
|
||||
};
|
||||
|
||||
if (!isAutoVae) {
|
||||
graph.nodes[VAE_LOADER] = {
|
||||
type: 'vae_loader',
|
||||
id: VAE_LOADER,
|
||||
is_intermediate: true,
|
||||
vae_model: vae,
|
||||
};
|
||||
}
|
||||
|
||||
if (seamlessXAxis) {
|
||||
upsertMetadata(graph, {
|
||||
seamless_x: seamlessXAxis,
|
||||
});
|
||||
}
|
||||
if (seamlessYAxis) {
|
||||
upsertMetadata(graph, {
|
||||
seamless_y: seamlessYAxis,
|
||||
});
|
||||
}
|
||||
|
||||
let denoisingNodeId = DENOISE_LATENTS;
|
||||
|
||||
if (
|
||||
graph.id === SDXL_CONTROL_LAYERS_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_INPAINT_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_OUTPAINT_GRAPH
|
||||
) {
|
||||
denoisingNodeId = SDXL_DENOISE_LATENTS;
|
||||
}
|
||||
|
||||
graph.edges = graph.edges.filter(
|
||||
(e) =>
|
||||
!(e.source.node_id === modelLoaderNodeId && ['unet'].includes(e.source.field)) &&
|
||||
!(e.source.node_id === modelLoaderNodeId && ['vae'].includes(e.source.field))
|
||||
);
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SEAMLESS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: isAutoVae ? modelLoaderNodeId : VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: SEAMLESS,
|
||||
field: 'vae',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SEAMLESS,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: denoisingNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
@ -1,140 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { selectValidT2IAdapters } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import type { ControlAdapterProcessorType, T2IAdapterConfig } from 'features/controlAdapters/store/types';
|
||||
import type { ImageField } from 'features/nodes/types/common';
|
||||
import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import { T2I_ADAPTER_COLLECT } from 'features/nodes/util/graph/constants';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import type { Invocation, NonNullableGraph, S } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const addT2IAdaptersToLinearGraph = async (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
baseNodeId: string
|
||||
): Promise<void> => {
|
||||
// The generation tab has special handling - its control adapters are set up in the Control Layers graph helper.
|
||||
const activeTabName = activeTabNameSelector(state);
|
||||
assert(activeTabName !== 'generation', 'Tried to use addT2IAdaptersToLinearGraph on generation tab');
|
||||
|
||||
const t2iAdapters = selectValidT2IAdapters(state.controlAdapters).filter(
|
||||
({ model, processedControlImage, processorType, controlImage, isEnabled }) => {
|
||||
const hasModel = Boolean(model);
|
||||
const doesBaseMatch = model?.base === state.canvasV2.params.model?.base;
|
||||
const hasControlImage = (processedControlImage && processorType !== 'none') || controlImage;
|
||||
|
||||
return isEnabled && hasModel && doesBaseMatch && hasControlImage;
|
||||
}
|
||||
);
|
||||
|
||||
if (t2iAdapters.length) {
|
||||
// Even though denoise_latents' t2i adapter input is SINGLE_OR_COLLECTION, keep it simple and always use a collect
|
||||
const t2iAdapterCollectNode: Invocation<'collect'> = {
|
||||
id: T2I_ADAPTER_COLLECT,
|
||||
type: 'collect',
|
||||
is_intermediate: true,
|
||||
};
|
||||
graph.nodes[T2I_ADAPTER_COLLECT] = t2iAdapterCollectNode;
|
||||
graph.edges.push({
|
||||
source: { node_id: T2I_ADAPTER_COLLECT, field: 'collection' },
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 't2i_adapter',
|
||||
},
|
||||
});
|
||||
|
||||
const t2iAdapterMetadata: S['CoreMetadataInvocation']['t2iAdapters'] = [];
|
||||
|
||||
for (const t2iAdapter of t2iAdapters) {
|
||||
if (!t2iAdapter.model) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
id,
|
||||
controlImage,
|
||||
processedControlImage,
|
||||
beginStepPct,
|
||||
endStepPct,
|
||||
resizeMode,
|
||||
model,
|
||||
processorType,
|
||||
weight,
|
||||
} = t2iAdapter;
|
||||
|
||||
const t2iAdapterNode: Invocation<'t2i_adapter'> = {
|
||||
id: `t2i_adapter_${id}`,
|
||||
type: 't2i_adapter',
|
||||
is_intermediate: true,
|
||||
begin_step_percent: beginStepPct,
|
||||
end_step_percent: endStepPct,
|
||||
resize_mode: resizeMode,
|
||||
t2i_adapter_model: model,
|
||||
weight: weight,
|
||||
image: buildControlImage(controlImage, processedControlImage, processorType),
|
||||
};
|
||||
|
||||
graph.nodes[t2iAdapterNode.id] = t2iAdapterNode;
|
||||
|
||||
t2iAdapterMetadata.push(buildT2IAdapterMetadata(t2iAdapter));
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: t2iAdapterNode.id, field: 't2i_adapter' },
|
||||
destination: {
|
||||
node_id: T2I_ADAPTER_COLLECT,
|
||||
field: 'item',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
upsertMetadata(graph, { t2iAdapters: t2iAdapterMetadata });
|
||||
}
|
||||
};
|
||||
|
||||
const buildControlImage = (
|
||||
controlImage: string | null,
|
||||
processedControlImage: string | null,
|
||||
processorType: ControlAdapterProcessorType
|
||||
): ImageField => {
|
||||
let image: ImageField | null = null;
|
||||
if (processedControlImage && processorType !== 'none') {
|
||||
// We've already processed the image in the app, so we can just use the processed image
|
||||
image = {
|
||||
image_name: processedControlImage,
|
||||
};
|
||||
} else if (controlImage) {
|
||||
// The control image is preprocessed
|
||||
image = {
|
||||
image_name: controlImage,
|
||||
};
|
||||
}
|
||||
assert(image, 'T2I Adapter image is required');
|
||||
return image;
|
||||
};
|
||||
|
||||
const buildT2IAdapterMetadata = (t2iAdapter: T2IAdapterConfig): S['T2IAdapterMetadataField'] => {
|
||||
const { controlImage, processedControlImage, beginStepPct, endStepPct, resizeMode, model, processorType, weight } =
|
||||
t2iAdapter;
|
||||
|
||||
assert(model, 'T2I Adapter model is required');
|
||||
|
||||
const processed_image =
|
||||
processedControlImage && processorType !== 'none'
|
||||
? {
|
||||
image_name: processedControlImage,
|
||||
}
|
||||
: null;
|
||||
|
||||
assert(controlImage, 'T2I Adapter image is required');
|
||||
|
||||
return {
|
||||
t2i_adapter_model: model,
|
||||
weight,
|
||||
begin_step_percent: beginStepPct,
|
||||
end_step_percent: endStepPct,
|
||||
resize_mode: resizeMode,
|
||||
image: {
|
||||
image_name: controlImage,
|
||||
},
|
||||
processed_image,
|
||||
};
|
||||
};
|
@ -1,175 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
CANVAS_INPAINT_GRAPH,
|
||||
CANVAS_OUTPAINT_GRAPH,
|
||||
CANVAS_OUTPUT,
|
||||
CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
CONTROL_LAYERS_GRAPH,
|
||||
IMAGE_TO_LATENTS,
|
||||
INPAINT_CREATE_MASK,
|
||||
INPAINT_IMAGE,
|
||||
LATENTS_TO_IMAGE,
|
||||
MAIN_MODEL_LOADER,
|
||||
SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
SDXL_CANVAS_INPAINT_GRAPH,
|
||||
SDXL_CANVAS_OUTPAINT_GRAPH,
|
||||
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
SDXL_CONTROL_LAYERS_GRAPH,
|
||||
SDXL_REFINER_SEAMLESS,
|
||||
SEAMLESS,
|
||||
VAE_LOADER,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import type { NonNullableGraph } from 'services/api/types';
|
||||
|
||||
export const addVAEToGraph = async (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
modelLoaderNodeId: string = MAIN_MODEL_LOADER
|
||||
): Promise<void> => {
|
||||
const { vae, seamlessXAxis, seamlessYAxis } = state.canvasV2.params;
|
||||
const { boundingBoxScaleMethod } = state.canvas;
|
||||
const { refinerModel } = state.canvasV2.params;
|
||||
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
const isAutoVae = !vae;
|
||||
const isSeamlessEnabled = seamlessXAxis || seamlessYAxis;
|
||||
const isSDXL = Boolean(graph.id?.includes('sdxl'));
|
||||
const isUsingRefiner = isSDXL && Boolean(refinerModel);
|
||||
|
||||
if (!isAutoVae && !isSeamlessEnabled) {
|
||||
graph.nodes[VAE_LOADER] = {
|
||||
type: 'vae_loader',
|
||||
id: VAE_LOADER,
|
||||
is_intermediate: true,
|
||||
vae_model: vae,
|
||||
};
|
||||
}
|
||||
|
||||
if (graph.id === CONTROL_LAYERS_GRAPH || graph.id === SDXL_CONTROL_LAYERS_GRAPH) {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: isSeamlessEnabled
|
||||
? isUsingRefiner
|
||||
? SDXL_REFINER_SEAMLESS
|
||||
: SEAMLESS
|
||||
: isAutoVae
|
||||
? modelLoaderNodeId
|
||||
: VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'vae',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
graph.id === CANVAS_TEXT_TO_IMAGE_GRAPH ||
|
||||
graph.id === CANVAS_IMAGE_TO_IMAGE_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH
|
||||
) {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: isSeamlessEnabled
|
||||
? isUsingRefiner
|
||||
? SDXL_REFINER_SEAMLESS
|
||||
: SEAMLESS
|
||||
: isAutoVae
|
||||
? modelLoaderNodeId
|
||||
: VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: isUsingScaledDimensions ? LATENTS_TO_IMAGE : CANVAS_OUTPUT,
|
||||
field: 'vae',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(graph.id === CONTROL_LAYERS_GRAPH ||
|
||||
graph.id === SDXL_CONTROL_LAYERS_GRAPH ||
|
||||
graph.id === CANVAS_IMAGE_TO_IMAGE_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH) &&
|
||||
Boolean(graph.nodes[IMAGE_TO_LATENTS])
|
||||
) {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: isSeamlessEnabled
|
||||
? isUsingRefiner
|
||||
? SDXL_REFINER_SEAMLESS
|
||||
: SEAMLESS
|
||||
: isAutoVae
|
||||
? modelLoaderNodeId
|
||||
: VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: IMAGE_TO_LATENTS,
|
||||
field: 'vae',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
graph.id === CANVAS_INPAINT_GRAPH ||
|
||||
graph.id === CANVAS_OUTPAINT_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_INPAINT_GRAPH ||
|
||||
graph.id === SDXL_CANVAS_OUTPAINT_GRAPH
|
||||
) {
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: isSeamlessEnabled
|
||||
? isUsingRefiner
|
||||
? SDXL_REFINER_SEAMLESS
|
||||
: SEAMLESS
|
||||
: isAutoVae
|
||||
? modelLoaderNodeId
|
||||
: VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'vae',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: isSeamlessEnabled ? SEAMLESS : isAutoVae ? modelLoaderNodeId : VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'vae',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
source: {
|
||||
node_id: isSeamlessEnabled
|
||||
? isUsingRefiner
|
||||
? SDXL_REFINER_SEAMLESS
|
||||
: SEAMLESS
|
||||
: isAutoVae
|
||||
? modelLoaderNodeId
|
||||
: VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'vae',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (vae) {
|
||||
upsertMetadata(graph, { vae });
|
||||
}
|
||||
};
|
@ -1,60 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { LATENTS_TO_IMAGE, NSFW_CHECKER, WATERMARKER } from 'features/nodes/util/graph/constants';
|
||||
import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { Invocation, NonNullableGraph } from 'services/api/types';
|
||||
|
||||
export const addWatermarkerToGraph = (
|
||||
state: RootState,
|
||||
graph: NonNullableGraph,
|
||||
nodeIdToAddTo = LATENTS_TO_IMAGE
|
||||
): void => {
|
||||
const nodeToAddTo = graph.nodes[nodeIdToAddTo] as Invocation<'l2i'> | undefined;
|
||||
|
||||
const nsfwCheckerNode = graph.nodes[NSFW_CHECKER] as Invocation<'img_nsfw'> | undefined;
|
||||
|
||||
if (!nodeToAddTo) {
|
||||
// something has gone terribly awry
|
||||
return;
|
||||
}
|
||||
|
||||
const watermarkerNode: Invocation<'img_watermark'> = {
|
||||
id: WATERMARKER,
|
||||
type: 'img_watermark',
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
};
|
||||
|
||||
graph.nodes[WATERMARKER] = watermarkerNode;
|
||||
|
||||
// no matter the situation, we want the l2i node to be intermediate
|
||||
nodeToAddTo.is_intermediate = true;
|
||||
nodeToAddTo.use_cache = true;
|
||||
|
||||
if (nsfwCheckerNode) {
|
||||
// if we are using NSFW checker, we need to "disable" it output by marking it intermediate,
|
||||
// then connect it to the watermark node
|
||||
nsfwCheckerNode.is_intermediate = true;
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: NSFW_CHECKER,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: WATERMARKER,
|
||||
field: 'image',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// otherwise we just connect to the watermark node
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: nodeIdToAddTo,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: WATERMARKER,
|
||||
field: 'image',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
@ -1,57 +0,0 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { ImageDTO, NonNullableGraph } from 'services/api/types';
|
||||
|
||||
import { buildCanvasImageToImageGraph } from './buildCanvasImageToImageGraph';
|
||||
import { buildCanvasInpaintGraph } from './buildCanvasInpaintGraph';
|
||||
import { buildCanvasOutpaintGraph } from './buildCanvasOutpaintGraph';
|
||||
import { buildCanvasSDXLImageToImageGraph } from './buildCanvasSDXLImageToImageGraph';
|
||||
import { buildCanvasSDXLInpaintGraph } from './buildCanvasSDXLInpaintGraph';
|
||||
import { buildCanvasSDXLOutpaintGraph } from './buildCanvasSDXLOutpaintGraph';
|
||||
import { buildCanvasSDXLTextToImageGraph } from './buildCanvasSDXLTextToImageGraph';
|
||||
import { buildCanvasTextToImageGraph } from './buildCanvasTextToImageGraph';
|
||||
|
||||
export const buildCanvasGraph = async (
|
||||
state: RootState,
|
||||
generationMode: 'txt2img' | 'img2img' | 'inpaint' | 'outpaint',
|
||||
canvasInitImage: ImageDTO | undefined,
|
||||
canvasMaskImage: ImageDTO | undefined
|
||||
): Promise<NonNullableGraph> => {
|
||||
let graph: NonNullableGraph;
|
||||
|
||||
if (generationMode === 'txt2img') {
|
||||
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
|
||||
graph = await buildCanvasSDXLTextToImageGraph(state);
|
||||
} else {
|
||||
graph = await buildCanvasTextToImageGraph(state);
|
||||
}
|
||||
} else if (generationMode === 'img2img') {
|
||||
if (!canvasInitImage) {
|
||||
throw new Error('Missing canvas init image');
|
||||
}
|
||||
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
|
||||
graph = await buildCanvasSDXLImageToImageGraph(state, canvasInitImage);
|
||||
} else {
|
||||
graph = await buildCanvasImageToImageGraph(state, canvasInitImage);
|
||||
}
|
||||
} else if (generationMode === 'inpaint') {
|
||||
if (!canvasInitImage || !canvasMaskImage) {
|
||||
throw new Error('Missing canvas init and mask images');
|
||||
}
|
||||
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
|
||||
graph = await buildCanvasSDXLInpaintGraph(state, canvasInitImage, canvasMaskImage);
|
||||
} else {
|
||||
graph = await buildCanvasInpaintGraph(state, canvasInitImage, canvasMaskImage);
|
||||
}
|
||||
} else {
|
||||
if (!canvasInitImage) {
|
||||
throw new Error('Missing canvas init image');
|
||||
}
|
||||
if (state.canvasV2.params.model && state.canvasV2.params.model.base === 'sdxl') {
|
||||
graph = await buildCanvasSDXLOutpaintGraph(state, canvasInitImage, canvasMaskImage);
|
||||
} else {
|
||||
graph = await buildCanvasOutpaintGraph(state, canvasInitImage, canvasMaskImage);
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,374 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
CANVAS_OUTPUT,
|
||||
CLIP_SKIP,
|
||||
DENOISE_LATENTS,
|
||||
IMAGE_TO_LATENTS,
|
||||
IMG2IMG_RESIZE,
|
||||
LATENTS_TO_IMAGE,
|
||||
MAIN_MODEL_LOADER,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
|
||||
import { isNonRefinerMainModelConfig } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addLoRAsToGraph } from './addLoRAsToGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Image to Image graph.
|
||||
*/
|
||||
export const buildCanvasImageToImageGraph = async (
|
||||
state: RootState,
|
||||
initialImage: ImageDTO
|
||||
): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
seed,
|
||||
steps,
|
||||
img2imgStrength: strength,
|
||||
vaePrecision,
|
||||
clipSkip,
|
||||
shouldUseCpuNoise,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
const is_intermediate = true;
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
let modelLoaderNodeId = MAIN_MODEL_LOADER;
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
/**
|
||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
||||
* ids.
|
||||
*
|
||||
* The only thing we need extra logic for is handling randomized seed, control net, and for img2img,
|
||||
* the `fit` param. These are added to the graph at the end.
|
||||
*/
|
||||
|
||||
// copy-pasted graph from node editor, filled in with state values & friendly node ids
|
||||
const graph: NonNullableGraph = {
|
||||
id: CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
nodes: {
|
||||
[modelLoaderNodeId]: {
|
||||
type: 'main_model_loader',
|
||||
id: modelLoaderNodeId,
|
||||
is_intermediate,
|
||||
model,
|
||||
},
|
||||
[CLIP_SKIP]: {
|
||||
type: 'clip_skip',
|
||||
id: CLIP_SKIP,
|
||||
is_intermediate,
|
||||
skipped_layers: clipSkip,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: positivePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: negativePrompt,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
is_intermediate,
|
||||
use_cpu,
|
||||
seed,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
},
|
||||
[IMAGE_TO_LATENTS]: {
|
||||
type: 'i2l',
|
||||
id: IMAGE_TO_LATENTS,
|
||||
is_intermediate,
|
||||
},
|
||||
[DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 1 - strength,
|
||||
denoising_end: 1,
|
||||
},
|
||||
[CANVAS_OUTPUT]: {
|
||||
type: 'l2i',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
use_cache: false,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader to CLIP Skip and UNet
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
// Connect CLIP Skip To Conditioning
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
// Connect Everything To Denoise Latents
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: IMAGE_TO_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Decode Latents To Image & Handle Scaled Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
graph.nodes[IMG2IMG_RESIZE] = {
|
||||
id: IMG2IMG_RESIZE,
|
||||
type: 'img_resize',
|
||||
is_intermediate,
|
||||
image: initialImage,
|
||||
width: scaledBoundingBoxDimensions.width,
|
||||
height: scaledBoundingBoxDimensions.height,
|
||||
};
|
||||
graph.nodes[LATENTS_TO_IMAGE] = {
|
||||
id: LATENTS_TO_IMAGE,
|
||||
type: 'l2i',
|
||||
is_intermediate,
|
||||
fp32,
|
||||
};
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
id: CANVAS_OUTPUT,
|
||||
type: 'img_resize',
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
width: width,
|
||||
height: height,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: IMG2IMG_RESIZE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: IMAGE_TO_LATENTS,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'image',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
type: 'l2i',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
fp32,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
(graph.nodes[IMAGE_TO_LATENTS] as Invocation<'i2l'>).image = initialImage;
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'img2img',
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
positive_prompt: positivePrompt,
|
||||
negative_prompt: negativePrompt,
|
||||
model: getModelMetadataField(modelConfig),
|
||||
seed,
|
||||
steps,
|
||||
rand_device: use_cpu ? 'cpu' : 'cuda',
|
||||
scheduler,
|
||||
clip_skip: clipSkip,
|
||||
strength,
|
||||
init_image: initialImage.image_name,
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// add LoRA support
|
||||
await addLoRAsToGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// optionally add custom VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
await addT2IAdaptersToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,469 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { addCoreMetadataNode } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_INPAINT_GRAPH,
|
||||
CANVAS_OUTPUT,
|
||||
CLIP_SKIP,
|
||||
DENOISE_LATENTS,
|
||||
INPAINT_CREATE_MASK,
|
||||
INPAINT_IMAGE,
|
||||
INPAINT_IMAGE_RESIZE_DOWN,
|
||||
INPAINT_IMAGE_RESIZE_UP,
|
||||
LATENTS_TO_IMAGE,
|
||||
MAIN_MODEL_LOADER,
|
||||
MASK_RESIZE_DOWN,
|
||||
MASK_RESIZE_UP,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addLoRAsToGraph } from './addLoRAsToGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Inpaint graph.
|
||||
*/
|
||||
export const buildCanvasInpaintGraph = async (
|
||||
state: RootState,
|
||||
canvasInitImage: ImageDTO,
|
||||
canvasMaskImage: ImageDTO
|
||||
): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
img2imgStrength: strength,
|
||||
seed,
|
||||
vaePrecision,
|
||||
shouldUseCpuNoise,
|
||||
clipSkip,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
canvasCoherenceMode,
|
||||
canvasCoherenceMinDenoise,
|
||||
canvasCoherenceEdgeSize,
|
||||
maskBlur,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
// We may need to set the inpaint width and height to scale the image
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
const is_intermediate = true;
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
let modelLoaderNodeId = MAIN_MODEL_LOADER;
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
const graph: NonNullableGraph = {
|
||||
id: CANVAS_INPAINT_GRAPH,
|
||||
nodes: {
|
||||
[modelLoaderNodeId]: {
|
||||
type: 'main_model_loader',
|
||||
id: modelLoaderNodeId,
|
||||
is_intermediate,
|
||||
model,
|
||||
},
|
||||
[CLIP_SKIP]: {
|
||||
type: 'clip_skip',
|
||||
id: CLIP_SKIP,
|
||||
is_intermediate,
|
||||
skipped_layers: clipSkip,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: positivePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: negativePrompt,
|
||||
},
|
||||
[INPAINT_IMAGE]: {
|
||||
type: 'i2l',
|
||||
id: INPAINT_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
use_cpu,
|
||||
seed,
|
||||
is_intermediate,
|
||||
},
|
||||
[INPAINT_CREATE_MASK]: {
|
||||
type: 'create_gradient_mask',
|
||||
id: INPAINT_CREATE_MASK,
|
||||
is_intermediate,
|
||||
coherence_mode: canvasCoherenceMode,
|
||||
minimum_denoise: canvasCoherenceMinDenoise,
|
||||
edge_radius: canvasCoherenceEdgeSize,
|
||||
tiled: false,
|
||||
fp32: fp32,
|
||||
},
|
||||
[DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - strength,
|
||||
denoising_end: 1,
|
||||
},
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
id: LATENTS_TO_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[CANVAS_OUTPUT]: {
|
||||
type: 'canvas_paste_back',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
mask_blur: maskBlur,
|
||||
source_image: canvasInitImage,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader to CLIP Skip and UNet
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
// Connect CLIP Skip to Conditioning
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
// Connect Everything To Inpaint Node
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
},
|
||||
// Decode Inpainted Latents To Image
|
||||
{
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Handle Scale Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
const scaledWidth: number = scaledBoundingBoxDimensions.width;
|
||||
const scaledHeight: number = scaledBoundingBoxDimensions.height;
|
||||
|
||||
// Add Scaling Nodes
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
image: canvasInitImage,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
image: canvasMaskImage,
|
||||
};
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight;
|
||||
|
||||
// Connect Nodes
|
||||
graph.edges.push(
|
||||
// Scale Inpaint Image and Mask
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'mask',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Resize Down
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'expanded_mask_area',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Paste Back
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'mask',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Add Images To Nodes
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = width;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = height;
|
||||
|
||||
graph.nodes[INPAINT_IMAGE] = {
|
||||
...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>),
|
||||
image: canvasInitImage,
|
||||
};
|
||||
|
||||
graph.nodes[INPAINT_CREATE_MASK] = {
|
||||
...(graph.nodes[INPAINT_CREATE_MASK] as Invocation<'create_gradient_mask'>),
|
||||
mask: canvasMaskImage,
|
||||
};
|
||||
|
||||
// Paste Back
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
...(graph.nodes[CANVAS_OUTPUT] as Invocation<'canvas_paste_back'>),
|
||||
mask: canvasMaskImage,
|
||||
};
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'inpaint',
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// Add VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add LoRA support
|
||||
await addLoRAsToGraph(state, graph, DENOISE_LATENTS, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
await addT2IAdaptersToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,629 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { addCoreMetadataNode } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_OUTPAINT_GRAPH,
|
||||
CANVAS_OUTPUT,
|
||||
CLIP_SKIP,
|
||||
DENOISE_LATENTS,
|
||||
INPAINT_CREATE_MASK,
|
||||
INPAINT_IMAGE,
|
||||
INPAINT_IMAGE_RESIZE_DOWN,
|
||||
INPAINT_IMAGE_RESIZE_UP,
|
||||
INPAINT_INFILL,
|
||||
INPAINT_INFILL_RESIZE_DOWN,
|
||||
LATENTS_TO_IMAGE,
|
||||
MAIN_MODEL_LOADER,
|
||||
MASK_COMBINE,
|
||||
MASK_FROM_ALPHA,
|
||||
MASK_RESIZE_DOWN,
|
||||
MASK_RESIZE_UP,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addLoRAsToGraph } from './addLoRAsToGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Outpaint graph.
|
||||
*/
|
||||
export const buildCanvasOutpaintGraph = async (
|
||||
state: RootState,
|
||||
canvasInitImage: ImageDTO,
|
||||
canvasMaskImage?: ImageDTO
|
||||
): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
img2imgStrength: strength,
|
||||
seed,
|
||||
vaePrecision,
|
||||
shouldUseCpuNoise,
|
||||
infillTileSize,
|
||||
infillPatchmatchDownscaleSize,
|
||||
infillMethod,
|
||||
// infillMosaicTileWidth,
|
||||
// infillMosaicTileHeight,
|
||||
// infillMosaicMinColor,
|
||||
// infillMosaicMaxColor,
|
||||
infillColorValue,
|
||||
clipSkip,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
canvasCoherenceMode,
|
||||
canvasCoherenceMinDenoise,
|
||||
canvasCoherenceEdgeSize,
|
||||
maskBlur,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
// We may need to set the inpaint width and height to scale the image
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
const is_intermediate = true;
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
let modelLoaderNodeId = MAIN_MODEL_LOADER;
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
const graph: NonNullableGraph = {
|
||||
id: CANVAS_OUTPAINT_GRAPH,
|
||||
nodes: {
|
||||
[modelLoaderNodeId]: {
|
||||
type: 'main_model_loader',
|
||||
id: modelLoaderNodeId,
|
||||
is_intermediate,
|
||||
model,
|
||||
},
|
||||
[CLIP_SKIP]: {
|
||||
type: 'clip_skip',
|
||||
id: CLIP_SKIP,
|
||||
is_intermediate,
|
||||
skipped_layers: clipSkip,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: positivePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: negativePrompt,
|
||||
},
|
||||
[MASK_FROM_ALPHA]: {
|
||||
type: 'tomask',
|
||||
id: MASK_FROM_ALPHA,
|
||||
is_intermediate,
|
||||
image: canvasInitImage,
|
||||
},
|
||||
[MASK_COMBINE]: {
|
||||
type: 'mask_combine',
|
||||
id: MASK_COMBINE,
|
||||
is_intermediate,
|
||||
mask2: canvasMaskImage,
|
||||
},
|
||||
[INPAINT_IMAGE]: {
|
||||
type: 'i2l',
|
||||
id: INPAINT_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
use_cpu,
|
||||
seed,
|
||||
is_intermediate,
|
||||
},
|
||||
[INPAINT_CREATE_MASK]: {
|
||||
type: 'create_gradient_mask',
|
||||
id: INPAINT_CREATE_MASK,
|
||||
is_intermediate,
|
||||
coherence_mode: canvasCoherenceMode,
|
||||
edge_radius: canvasCoherenceEdgeSize,
|
||||
minimum_denoise: canvasCoherenceMinDenoise,
|
||||
tiled: false,
|
||||
fp32: fp32,
|
||||
},
|
||||
[DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - strength,
|
||||
denoising_end: 1,
|
||||
},
|
||||
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
id: LATENTS_TO_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[CANVAS_OUTPUT]: {
|
||||
type: 'canvas_paste_back',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
use_cache: false,
|
||||
mask_blur: maskBlur,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader To UNet & Clip Skip
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
// Connect CLIP Skip to Conditioning
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
// Connect Infill Result To Inpaint Image
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Combine Mask from Init Image with User Painted Mask
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_FROM_ALPHA,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_COMBINE,
|
||||
field: 'mask1',
|
||||
},
|
||||
},
|
||||
// Plug Everything Into Inpaint Node
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
// Create Inpaint Mask
|
||||
{
|
||||
source: {
|
||||
node_id: isUsingScaledDimensions ? MASK_RESIZE_UP : MASK_COMBINE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'mask',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Add Infill Nodes
|
||||
if (infillMethod === 'patchmatch') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_patchmatch',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
downscale: infillPatchmatchDownscaleSize,
|
||||
};
|
||||
}
|
||||
|
||||
if (infillMethod === 'lama') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_lama',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
};
|
||||
}
|
||||
|
||||
if (infillMethod === 'cv2') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_cv2',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
};
|
||||
}
|
||||
|
||||
if (infillMethod === 'tile') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_tile',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
tile_size: infillTileSize,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: add mosaic back
|
||||
// if (infillMethod === 'mosaic') {
|
||||
// graph.nodes[INPAINT_INFILL] = {
|
||||
// type: 'infill_mosaic',
|
||||
// id: INPAINT_INFILL,
|
||||
// is_intermediate,
|
||||
// tile_width: infillMosaicTileWidth,
|
||||
// tile_height: infillMosaicTileHeight,
|
||||
// min_color: infillMosaicMinColor,
|
||||
// max_color: infillMosaicMaxColor,
|
||||
// };
|
||||
// }
|
||||
|
||||
if (infillMethod === 'color') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_rgba',
|
||||
id: INPAINT_INFILL,
|
||||
color: infillColorValue,
|
||||
is_intermediate,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle Scale Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
const scaledWidth: number = scaledBoundingBoxDimensions.width;
|
||||
const scaledHeight: number = scaledBoundingBoxDimensions.height;
|
||||
|
||||
// Add Scaling Nodes
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
image: canvasInitImage,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
};
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
graph.nodes[INPAINT_INFILL_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_INFILL_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight;
|
||||
|
||||
// Connect Nodes
|
||||
graph.edges.push(
|
||||
// Scale Inpaint Image
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Take combined mask and resize
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_COMBINE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Resize Results Down
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'expanded_mask_area',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_INFILL_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Paste Back
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'source_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'expanded_mask_area',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'mask',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Add Images To Nodes
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
...(graph.nodes[INPAINT_INFILL] as Invocation<'infill_tile'> | Invocation<'infill_patchmatch'>),
|
||||
image: canvasInitImage,
|
||||
};
|
||||
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = width;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = height;
|
||||
|
||||
graph.nodes[INPAINT_IMAGE] = {
|
||||
...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>),
|
||||
image: canvasInitImage,
|
||||
};
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'source_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_COMBINE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'mask',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'outpaint',
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// Add VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add LoRA support
|
||||
await addLoRAsToGraph(state, graph, DENOISE_LATENTS, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
await addT2IAdaptersToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,382 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_OUTPUT,
|
||||
IMAGE_TO_LATENTS,
|
||||
IMG2IMG_RESIZE,
|
||||
LATENTS_TO_IMAGE,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
SDXL_DENOISE_LATENTS,
|
||||
SDXL_MODEL_LOADER,
|
||||
SDXL_REFINER_SEAMLESS,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
|
||||
import { isNonRefinerMainModelConfig } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph';
|
||||
import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Image to Image graph.
|
||||
*/
|
||||
export const buildCanvasSDXLImageToImageGraph = async (
|
||||
state: RootState,
|
||||
initialImage: ImageDTO
|
||||
): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
seed,
|
||||
steps,
|
||||
vaePrecision,
|
||||
shouldUseCpuNoise,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
img2imgStrength: strength,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
const { refinerModel, refinerStart } = state.canvasV2.params;
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
const is_intermediate = true;
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
// Model Loader ID
|
||||
let modelLoaderNodeId = SDXL_MODEL_LOADER;
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
// Construct Style Prompt
|
||||
const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
/**
|
||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
||||
* ids.
|
||||
*
|
||||
* The only thing we need extra logic for is handling randomized seed, control net, and for img2img,
|
||||
* the `fit` param. These are added to the graph at the end.
|
||||
*/
|
||||
|
||||
// copy-pasted graph from node editor, filled in with state values & friendly node ids
|
||||
const graph: NonNullableGraph = {
|
||||
id: SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH,
|
||||
nodes: {
|
||||
[modelLoaderNodeId]: {
|
||||
type: 'sdxl_model_loader',
|
||||
id: modelLoaderNodeId,
|
||||
model,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
prompt: positivePrompt,
|
||||
style: positiveStylePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
prompt: negativePrompt,
|
||||
style: negativeStylePrompt,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
is_intermediate,
|
||||
use_cpu,
|
||||
seed,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
},
|
||||
[IMAGE_TO_LATENTS]: {
|
||||
type: 'i2l',
|
||||
id: IMAGE_TO_LATENTS,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[SDXL_DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
||||
denoising_end: refinerModel ? refinerStart : 1,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader To UNet & CLIP
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
// Connect Everything to Denoise Latents
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: IMAGE_TO_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Decode Latents To Image & Handle Scaled Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
graph.nodes[IMG2IMG_RESIZE] = {
|
||||
id: IMG2IMG_RESIZE,
|
||||
type: 'img_resize',
|
||||
is_intermediate,
|
||||
image: initialImage,
|
||||
width: scaledBoundingBoxDimensions.width,
|
||||
height: scaledBoundingBoxDimensions.height,
|
||||
};
|
||||
graph.nodes[LATENTS_TO_IMAGE] = {
|
||||
id: LATENTS_TO_IMAGE,
|
||||
type: 'l2i',
|
||||
is_intermediate,
|
||||
fp32,
|
||||
};
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
id: CANVAS_OUTPUT,
|
||||
type: 'img_resize',
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
width: width,
|
||||
height: height,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: IMG2IMG_RESIZE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: IMAGE_TO_LATENTS,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'image',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
type: 'l2i',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
(graph.nodes[IMAGE_TO_LATENTS] as Invocation<'i2l'>).image = initialImage;
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'img2img',
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
positive_prompt: positivePrompt,
|
||||
negative_prompt: negativePrompt,
|
||||
model: getModelMetadataField(modelConfig),
|
||||
seed,
|
||||
steps,
|
||||
rand_device: use_cpu ? 'cpu' : 'cuda',
|
||||
scheduler,
|
||||
strength,
|
||||
init_image: initialImage.image_name,
|
||||
positive_style_prompt: positiveStylePrompt,
|
||||
negative_style_prompt: negativeStylePrompt,
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// Add Refiner if enabled
|
||||
if (refinerModel) {
|
||||
await addSDXLRefinerToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
modelLoaderNodeId = SDXL_REFINER_SEAMLESS;
|
||||
}
|
||||
}
|
||||
|
||||
// optionally add custom VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add LoRA support
|
||||
await addSDXLLoRAsToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
await addT2IAdaptersToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,487 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { addCoreMetadataNode } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_OUTPUT,
|
||||
INPAINT_CREATE_MASK,
|
||||
INPAINT_IMAGE,
|
||||
INPAINT_IMAGE_RESIZE_DOWN,
|
||||
INPAINT_IMAGE_RESIZE_UP,
|
||||
LATENTS_TO_IMAGE,
|
||||
MASK_RESIZE_DOWN,
|
||||
MASK_RESIZE_UP,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SDXL_CANVAS_INPAINT_GRAPH,
|
||||
SDXL_DENOISE_LATENTS,
|
||||
SDXL_MODEL_LOADER,
|
||||
SDXL_REFINER_SEAMLESS,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph';
|
||||
import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Inpaint graph.
|
||||
*/
|
||||
export const buildCanvasSDXLInpaintGraph = async (
|
||||
state: RootState,
|
||||
canvasInitImage: ImageDTO,
|
||||
canvasMaskImage: ImageDTO
|
||||
): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
img2imgStrength: strength,
|
||||
seed,
|
||||
vaePrecision,
|
||||
shouldUseCpuNoise,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
canvasCoherenceMode,
|
||||
canvasCoherenceMinDenoise,
|
||||
canvasCoherenceEdgeSize,
|
||||
maskBlur,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
const { refinerModel, refinerStart } = state.canvasV2.params;
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
// We may need to set the inpaint width and height to scale the image
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const is_intermediate = true;
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
let modelLoaderNodeId = SDXL_MODEL_LOADER;
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
// Construct Style Prompt
|
||||
const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
const graph: NonNullableGraph = {
|
||||
id: SDXL_CANVAS_INPAINT_GRAPH,
|
||||
nodes: {
|
||||
[modelLoaderNodeId]: {
|
||||
type: 'sdxl_model_loader',
|
||||
id: modelLoaderNodeId,
|
||||
is_intermediate,
|
||||
model,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: positivePrompt,
|
||||
style: positiveStylePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: negativePrompt,
|
||||
style: negativeStylePrompt,
|
||||
},
|
||||
[INPAINT_IMAGE]: {
|
||||
type: 'i2l',
|
||||
id: INPAINT_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
use_cpu,
|
||||
seed,
|
||||
is_intermediate,
|
||||
},
|
||||
[INPAINT_CREATE_MASK]: {
|
||||
type: 'create_gradient_mask',
|
||||
id: INPAINT_CREATE_MASK,
|
||||
is_intermediate,
|
||||
coherence_mode: canvasCoherenceMode,
|
||||
minimum_denoise: refinerModel ? Math.max(0.2, canvasCoherenceMinDenoise) : canvasCoherenceMinDenoise,
|
||||
edge_radius: canvasCoherenceEdgeSize,
|
||||
tiled: false,
|
||||
fp32: fp32,
|
||||
},
|
||||
[SDXL_DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
||||
denoising_end: refinerModel ? refinerStart : 1,
|
||||
},
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
id: LATENTS_TO_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[CANVAS_OUTPUT]: {
|
||||
type: 'canvas_paste_back',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
mask_blur: maskBlur,
|
||||
source_image: canvasInitImage,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader to UNet and CLIP
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
// Connect Everything To Inpaint Node
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
},
|
||||
// Decode Inpainted Latents To Image
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Handle Scale Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
const scaledWidth: number = scaledBoundingBoxDimensions.width;
|
||||
const scaledHeight: number = scaledBoundingBoxDimensions.height;
|
||||
|
||||
// Add Scaling Nodes
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
image: canvasInitImage,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
image: canvasMaskImage,
|
||||
};
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight;
|
||||
|
||||
// Connect Nodes
|
||||
graph.edges.push(
|
||||
// Scale Inpaint Image and Mask
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'mask',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Resize Down
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'expanded_mask_area',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Paste Back
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'mask',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Add Images To Nodes
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = width;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = height;
|
||||
|
||||
graph.nodes[INPAINT_IMAGE] = {
|
||||
...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>),
|
||||
image: canvasInitImage,
|
||||
};
|
||||
|
||||
graph.nodes[INPAINT_CREATE_MASK] = {
|
||||
...(graph.nodes[INPAINT_CREATE_MASK] as Invocation<'create_gradient_mask'>),
|
||||
mask: canvasMaskImage,
|
||||
};
|
||||
|
||||
// Paste Back
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
...(graph.nodes[CANVAS_OUTPUT] as Invocation<'canvas_paste_back'>),
|
||||
mask: canvasMaskImage,
|
||||
};
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'sdxl_inpaint',
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// Add Refiner if enabled
|
||||
if (refinerModel) {
|
||||
await addSDXLRefinerToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
modelLoaderNodeId = SDXL_REFINER_SEAMLESS;
|
||||
}
|
||||
}
|
||||
|
||||
// Add VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add LoRA support
|
||||
await addSDXLLoRAsToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
await addT2IAdaptersToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,644 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { addCoreMetadataNode } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_OUTPUT,
|
||||
INPAINT_CREATE_MASK,
|
||||
INPAINT_IMAGE,
|
||||
INPAINT_IMAGE_RESIZE_DOWN,
|
||||
INPAINT_IMAGE_RESIZE_UP,
|
||||
INPAINT_INFILL,
|
||||
INPAINT_INFILL_RESIZE_DOWN,
|
||||
LATENTS_TO_IMAGE,
|
||||
MASK_COMBINE,
|
||||
MASK_FROM_ALPHA,
|
||||
MASK_RESIZE_DOWN,
|
||||
MASK_RESIZE_UP,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SDXL_CANVAS_OUTPAINT_GRAPH,
|
||||
SDXL_DENOISE_LATENTS,
|
||||
SDXL_MODEL_LOADER,
|
||||
SDXL_REFINER_SEAMLESS,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph';
|
||||
import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Outpaint graph.
|
||||
*/
|
||||
export const buildCanvasSDXLOutpaintGraph = async (
|
||||
state: RootState,
|
||||
canvasInitImage: ImageDTO,
|
||||
canvasMaskImage?: ImageDTO
|
||||
): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
img2imgStrength: strength,
|
||||
seed,
|
||||
vaePrecision,
|
||||
shouldUseCpuNoise,
|
||||
infillTileSize,
|
||||
infillPatchmatchDownscaleSize,
|
||||
infillMethod,
|
||||
// infillMosaicTileWidth,
|
||||
// infillMosaicTileHeight,
|
||||
// infillMosaicMinColor,
|
||||
// infillMosaicMaxColor,
|
||||
infillColorValue,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
canvasCoherenceMode,
|
||||
canvasCoherenceMinDenoise,
|
||||
canvasCoherenceEdgeSize,
|
||||
maskBlur,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
const { refinerModel, refinerStart } = state.canvasV2.params;
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
// We may need to set the inpaint width and height to scale the image
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
const is_intermediate = true;
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
let modelLoaderNodeId = SDXL_MODEL_LOADER;
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
// Construct Style Prompt
|
||||
const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
const graph: NonNullableGraph = {
|
||||
id: SDXL_CANVAS_OUTPAINT_GRAPH,
|
||||
nodes: {
|
||||
[SDXL_MODEL_LOADER]: {
|
||||
type: 'sdxl_model_loader',
|
||||
id: SDXL_MODEL_LOADER,
|
||||
model,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: positivePrompt,
|
||||
style: positiveStylePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: negativePrompt,
|
||||
style: negativeStylePrompt,
|
||||
},
|
||||
[MASK_FROM_ALPHA]: {
|
||||
type: 'tomask',
|
||||
id: MASK_FROM_ALPHA,
|
||||
is_intermediate,
|
||||
image: canvasInitImage,
|
||||
},
|
||||
[MASK_COMBINE]: {
|
||||
type: 'mask_combine',
|
||||
id: MASK_COMBINE,
|
||||
is_intermediate,
|
||||
mask2: canvasMaskImage,
|
||||
},
|
||||
[INPAINT_IMAGE]: {
|
||||
type: 'i2l',
|
||||
id: INPAINT_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
use_cpu,
|
||||
seed,
|
||||
is_intermediate,
|
||||
},
|
||||
[INPAINT_CREATE_MASK]: {
|
||||
type: 'create_gradient_mask',
|
||||
id: INPAINT_CREATE_MASK,
|
||||
is_intermediate,
|
||||
coherence_mode: canvasCoherenceMode,
|
||||
edge_radius: canvasCoherenceEdgeSize,
|
||||
minimum_denoise: refinerModel ? Math.max(0.2, canvasCoherenceMinDenoise) : canvasCoherenceMinDenoise,
|
||||
tiled: false,
|
||||
fp32: fp32,
|
||||
},
|
||||
[SDXL_DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
||||
denoising_end: refinerModel ? refinerStart : 1,
|
||||
},
|
||||
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
id: LATENTS_TO_IMAGE,
|
||||
is_intermediate,
|
||||
fp32,
|
||||
},
|
||||
[CANVAS_OUTPUT]: {
|
||||
type: 'canvas_paste_back',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
use_cache: false,
|
||||
mask_blur: maskBlur,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader To UNet and CLIP
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_MODEL_LOADER,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_MODEL_LOADER,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_MODEL_LOADER,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_MODEL_LOADER,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_MODEL_LOADER,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
// Connect Infill Result To Inpaint Image
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Combine Mask from Init Image with User Painted Mask
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_FROM_ALPHA,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_COMBINE,
|
||||
field: 'mask1',
|
||||
},
|
||||
},
|
||||
// Plug Everything Into Inpaint Node
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
// Create Inpaint Mask
|
||||
{
|
||||
source: {
|
||||
node_id: isUsingScaledDimensions ? MASK_RESIZE_UP : MASK_COMBINE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'mask',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'denoise_mask',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Add Infill Nodes
|
||||
if (infillMethod === 'patchmatch') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_patchmatch',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
downscale: infillPatchmatchDownscaleSize,
|
||||
};
|
||||
}
|
||||
|
||||
if (infillMethod === 'lama') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_lama',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
};
|
||||
}
|
||||
|
||||
if (infillMethod === 'cv2') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_cv2',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
};
|
||||
}
|
||||
|
||||
if (infillMethod === 'tile') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_tile',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
tile_size: infillTileSize,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: add mosaic back
|
||||
// if (infillMethod === 'mosaic') {
|
||||
// graph.nodes[INPAINT_INFILL] = {
|
||||
// type: 'infill_mosaic',
|
||||
// id: INPAINT_INFILL,
|
||||
// is_intermediate,
|
||||
// tile_width: infillMosaicTileWidth,
|
||||
// tile_height: infillMosaicTileHeight,
|
||||
// min_color: infillMosaicMinColor,
|
||||
// max_color: infillMosaicMaxColor,
|
||||
// };
|
||||
// }
|
||||
|
||||
if (infillMethod === 'color') {
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
type: 'infill_rgba',
|
||||
id: INPAINT_INFILL,
|
||||
is_intermediate,
|
||||
color: infillColorValue,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle Scale Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
const scaledWidth: number = scaledBoundingBoxDimensions.width;
|
||||
const scaledHeight: number = scaledBoundingBoxDimensions.height;
|
||||
|
||||
// Add Scaling Nodes
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
image: canvasInitImage,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_UP] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_UP,
|
||||
is_intermediate,
|
||||
width: scaledWidth,
|
||||
height: scaledHeight,
|
||||
};
|
||||
graph.nodes[INPAINT_IMAGE_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
graph.nodes[INPAINT_INFILL_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: INPAINT_INFILL_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
graph.nodes[MASK_RESIZE_DOWN] = {
|
||||
type: 'img_resize',
|
||||
id: MASK_RESIZE_DOWN,
|
||||
is_intermediate,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight;
|
||||
|
||||
// Connect Nodes
|
||||
graph.edges.push(
|
||||
// Scale Inpaint Image
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Take combined mask and resize
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_COMBINE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_RESIZE_UP,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Resize Results Down
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'expanded_mask_area',
|
||||
},
|
||||
destination: {
|
||||
node_id: MASK_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: INPAINT_INFILL_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
// Paste Back
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'source_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_IMAGE_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: MASK_RESIZE_DOWN,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'mask',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Add Images To Nodes
|
||||
graph.nodes[INPAINT_INFILL] = {
|
||||
...(graph.nodes[INPAINT_INFILL] as Invocation<'infill_tile'> | Invocation<'infill_patchmatch'>),
|
||||
image: canvasInitImage,
|
||||
};
|
||||
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).width = width;
|
||||
(graph.nodes[NOISE] as Invocation<'noise'>).height = height;
|
||||
|
||||
graph.nodes[INPAINT_IMAGE] = {
|
||||
...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>),
|
||||
image: canvasInitImage,
|
||||
};
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_INFILL,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'source_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'target_image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: INPAINT_CREATE_MASK,
|
||||
field: 'expanded_mask_area',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'mask',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'sdxl_outpaint',
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// Add Refiner if enabled
|
||||
if (refinerModel) {
|
||||
await addSDXLRefinerToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
modelLoaderNodeId = SDXL_REFINER_SEAMLESS;
|
||||
}
|
||||
}
|
||||
|
||||
// Add VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add LoRA support
|
||||
await addSDXLLoRAsToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
await addT2IAdaptersToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,341 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_OUTPUT,
|
||||
LATENTS_TO_IMAGE,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
SDXL_DENOISE_LATENTS,
|
||||
SDXL_MODEL_LOADER,
|
||||
SDXL_REFINER_SEAMLESS,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph';
|
||||
import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Text to Image graph.
|
||||
*/
|
||||
export const buildCanvasSDXLTextToImageGraph = async (state: RootState): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
seed,
|
||||
steps,
|
||||
vaePrecision,
|
||||
shouldUseCpuNoise,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
const is_intermediate = true;
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
const { refinerModel, refinerStart } = state.canvasV2.params;
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
let modelLoaderNodeId = SDXL_MODEL_LOADER;
|
||||
|
||||
// Construct Style Prompt
|
||||
const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
/**
|
||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
||||
* ids.
|
||||
*
|
||||
* The only thing we need extra logic for is handling randomized seed, control net, and for img2img,
|
||||
* the `fit` param. These are added to the graph at the end.
|
||||
*/
|
||||
|
||||
// copy-pasted graph from node editor, filled in with state values & friendly node ids
|
||||
const graph: NonNullableGraph = {
|
||||
id: SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
nodes: {
|
||||
[modelLoaderNodeId]: {
|
||||
type: 'sdxl_model_loader',
|
||||
id: modelLoaderNodeId,
|
||||
is_intermediate,
|
||||
model,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: positivePrompt,
|
||||
style: positiveStylePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'sdxl_compel_prompt',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: negativePrompt,
|
||||
style: negativeStylePrompt,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
is_intermediate,
|
||||
seed,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
use_cpu,
|
||||
},
|
||||
[SDXL_DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 0,
|
||||
denoising_end: refinerModel ? refinerStart : 1,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader to UNet and CLIP
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip2',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip2',
|
||||
},
|
||||
},
|
||||
// Connect everything to Denoise Latents
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Decode Latents To Image & Handle Scaled Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
graph.nodes[LATENTS_TO_IMAGE] = {
|
||||
id: LATENTS_TO_IMAGE,
|
||||
type: 'l2i',
|
||||
is_intermediate,
|
||||
fp32,
|
||||
};
|
||||
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
id: CANVAS_OUTPUT,
|
||||
type: 'img_resize',
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
width: width,
|
||||
height: height,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'image',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
type: 'l2i',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
fp32,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: SDXL_DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'txt2img',
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
positive_prompt: positivePrompt,
|
||||
negative_prompt: negativePrompt,
|
||||
positive_style_prompt: positiveStylePrompt,
|
||||
negative_style_prompt: negativeStylePrompt,
|
||||
model: getModelMetadataField(modelConfig),
|
||||
seed,
|
||||
steps,
|
||||
rand_device: use_cpu ? 'cpu' : 'cuda',
|
||||
scheduler,
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// Add Refiner if enabled
|
||||
if (refinerModel) {
|
||||
await addSDXLRefinerToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
modelLoaderNodeId = SDXL_REFINER_SEAMLESS;
|
||||
}
|
||||
}
|
||||
|
||||
// add LoRA support
|
||||
await addSDXLLoRAsToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
||||
|
||||
// optionally add custom VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
await addT2IAdaptersToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
||||
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,324 +0,0 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata';
|
||||
import {
|
||||
CANVAS_OUTPUT,
|
||||
CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
CLIP_SKIP,
|
||||
DENOISE_LATENTS,
|
||||
LATENTS_TO_IMAGE,
|
||||
MAIN_MODEL_LOADER,
|
||||
NEGATIVE_CONDITIONING,
|
||||
NOISE,
|
||||
POSITIVE_CONDITIONING,
|
||||
SEAMLESS,
|
||||
} from 'features/nodes/util/graph/constants';
|
||||
import {
|
||||
getBoardField,
|
||||
getIsIntermediate,
|
||||
getPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types';
|
||||
|
||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
||||
import { addLoRAsToGraph } from './addLoRAsToGraph';
|
||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
||||
import { addVAEToGraph } from './addVAEToGraph';
|
||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Text to Image graph.
|
||||
*/
|
||||
export const buildCanvasTextToImageGraph = async (state: RootState): Promise<NonNullableGraph> => {
|
||||
const log = logger('nodes');
|
||||
const {
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
seed,
|
||||
steps,
|
||||
vaePrecision,
|
||||
clipSkip,
|
||||
shouldUseCpuNoise,
|
||||
seamlessXAxis,
|
||||
seamlessYAxis,
|
||||
} = state.canvasV2.params;
|
||||
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const fp32 = vaePrecision === 'fp32';
|
||||
const is_intermediate = true;
|
||||
const isUsingScaledDimensions = ['auto', 'manual'].includes(boundingBoxScaleMethod);
|
||||
|
||||
if (!model) {
|
||||
log.error('No model found in state');
|
||||
throw new Error('No model found in state');
|
||||
}
|
||||
|
||||
const use_cpu = shouldUseCpuNoise;
|
||||
|
||||
let modelLoaderNodeId = MAIN_MODEL_LOADER;
|
||||
|
||||
const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state);
|
||||
|
||||
/**
|
||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
||||
* ids.
|
||||
*
|
||||
* The only thing we need extra logic for is handling randomized seed, control net, and for img2img,
|
||||
* the `fit` param. These are added to the graph at the end.
|
||||
*/
|
||||
|
||||
// copy-pasted graph from node editor, filled in with state values & friendly node ids
|
||||
const graph: NonNullableGraph = {
|
||||
id: CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||
nodes: {
|
||||
[modelLoaderNodeId]: {
|
||||
type: 'main_model_loader',
|
||||
id: modelLoaderNodeId,
|
||||
is_intermediate,
|
||||
model,
|
||||
},
|
||||
[CLIP_SKIP]: {
|
||||
type: 'clip_skip',
|
||||
id: CLIP_SKIP,
|
||||
is_intermediate,
|
||||
skipped_layers: clipSkip,
|
||||
},
|
||||
[POSITIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: POSITIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: positivePrompt,
|
||||
},
|
||||
[NEGATIVE_CONDITIONING]: {
|
||||
type: 'compel',
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
is_intermediate,
|
||||
prompt: negativePrompt,
|
||||
},
|
||||
[NOISE]: {
|
||||
type: 'noise',
|
||||
id: NOISE,
|
||||
is_intermediate,
|
||||
seed,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
use_cpu,
|
||||
},
|
||||
[DENOISE_LATENTS]: {
|
||||
type: 'denoise_latents',
|
||||
id: DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 0,
|
||||
denoising_end: 1,
|
||||
},
|
||||
},
|
||||
edges: [
|
||||
// Connect Model Loader to UNet & CLIP Skip
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'unet',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'unet',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: modelLoaderNodeId,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
// Connect CLIP Skip to Conditioning
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: CLIP_SKIP,
|
||||
field: 'clip',
|
||||
},
|
||||
destination: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'clip',
|
||||
},
|
||||
},
|
||||
// Connect everything to Denoise Latents
|
||||
{
|
||||
source: {
|
||||
node_id: POSITIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NEGATIVE_CONDITIONING,
|
||||
field: 'conditioning',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Decode Latents To Image & Handle Scaled Before Processing
|
||||
if (isUsingScaledDimensions) {
|
||||
graph.nodes[LATENTS_TO_IMAGE] = {
|
||||
id: LATENTS_TO_IMAGE,
|
||||
type: 'l2i',
|
||||
is_intermediate,
|
||||
fp32,
|
||||
};
|
||||
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
id: CANVAS_OUTPUT,
|
||||
type: 'img_resize',
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
width: width,
|
||||
height: height,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'image',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
graph.nodes[CANVAS_OUTPUT] = {
|
||||
type: 'l2i',
|
||||
id: CANVAS_OUTPUT,
|
||||
is_intermediate: getIsIntermediate(state),
|
||||
board: getBoardField(state),
|
||||
fp32,
|
||||
use_cache: false,
|
||||
};
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: CANVAS_OUTPUT,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
|
||||
|
||||
addCoreMetadataNode(
|
||||
graph,
|
||||
{
|
||||
generation_mode: 'txt2img',
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
width: !isUsingScaledDimensions ? width : scaledBoundingBoxDimensions.width,
|
||||
height: !isUsingScaledDimensions ? height : scaledBoundingBoxDimensions.height,
|
||||
positive_prompt: positivePrompt,
|
||||
negative_prompt: negativePrompt,
|
||||
model: getModelMetadataField(modelConfig),
|
||||
seed,
|
||||
steps,
|
||||
rand_device: use_cpu ? 'cpu' : 'cuda',
|
||||
scheduler,
|
||||
clip_skip: clipSkip,
|
||||
_canvas_objects: state.canvas.layerState.objects,
|
||||
},
|
||||
CANVAS_OUTPUT
|
||||
);
|
||||
|
||||
// Add Seamless To Graph
|
||||
if (seamlessXAxis || seamlessYAxis) {
|
||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
||||
modelLoaderNodeId = SEAMLESS;
|
||||
}
|
||||
|
||||
// optionally add custom VAE
|
||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
||||
|
||||
// add LoRA support
|
||||
await addLoRAsToGraph(state, graph, DENOISE_LATENTS, modelLoaderNodeId);
|
||||
|
||||
// add controlnet, mutating `graph`
|
||||
await addControlNetToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// Add IP Adapter
|
||||
await addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
await addT2IAdaptersToLinearGraph(state, graph, DENOISE_LATENTS);
|
||||
|
||||
// NSFW & watermark - must be last thing added to graph
|
||||
if (state.system.shouldUseNSFWChecker) {
|
||||
// must add before watermarker!
|
||||
addNSFWCheckerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
if (state.system.shouldUseWatermarker) {
|
||||
// must add after nsfw checker!
|
||||
addWatermarkerToGraph(state, graph, CANVAS_OUTPUT);
|
||||
}
|
||||
|
||||
return graph;
|
||||
};
|
@ -1,66 +0,0 @@
|
||||
import type { JSONObject } from 'common/types';
|
||||
import type { ModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { METADATA } from 'features/nodes/util/graph/constants';
|
||||
import type { AnyModelConfig, NonNullableGraph, S } from 'services/api/types';
|
||||
|
||||
export const addCoreMetadataNode = (
|
||||
graph: NonNullableGraph,
|
||||
metadata: Partial<S['CoreMetadataInvocation']>,
|
||||
nodeId: string
|
||||
): void => {
|
||||
graph.nodes[METADATA] = {
|
||||
id: METADATA,
|
||||
type: 'core_metadata',
|
||||
...metadata,
|
||||
};
|
||||
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: METADATA,
|
||||
field: 'metadata',
|
||||
},
|
||||
destination: {
|
||||
node_id: nodeId,
|
||||
field: 'metadata',
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export const upsertMetadata = (
|
||||
graph: NonNullableGraph,
|
||||
metadata: Partial<S['CoreMetadataInvocation']> | JSONObject
|
||||
): void => {
|
||||
const metadataNode = graph.nodes[METADATA] as S['CoreMetadataInvocation'] | undefined;
|
||||
|
||||
if (!metadataNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.assign(metadataNode, metadata);
|
||||
};
|
||||
|
||||
export const removeMetadata = (graph: NonNullableGraph, key: keyof S['CoreMetadataInvocation']): void => {
|
||||
const metadataNode = graph.nodes[METADATA] as S['CoreMetadataInvocation'] | undefined;
|
||||
|
||||
if (!metadataNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete metadataNode[key];
|
||||
};
|
||||
|
||||
export const getHasMetadata = (graph: NonNullableGraph): boolean => {
|
||||
const metadataNode = graph.nodes[METADATA] as S['CoreMetadataInvocation'] | undefined;
|
||||
|
||||
return Boolean(metadataNode);
|
||||
};
|
||||
|
||||
export const getModelMetadataField = ({ key, hash, name, base, type }: AnyModelConfig): ModelIdentifierField => ({
|
||||
key,
|
||||
hash,
|
||||
name,
|
||||
base,
|
||||
type,
|
||||
});
|
Loading…
Reference in New Issue
Block a user