mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat (ui, generation): High Resolution Fix- added automatic resolution toggle and replaced latent upscale with two improved methods (#4905)
* working * added selector for method * refactoring graph * added ersgan method * fixing yarn build * add tooltips * a conjuction * rephrase * removed manual sliders, set HRF to calculate dimensions automatically to match 512^2 pixels * working * working * working * fixed tooltip * add hrf to use all parameters * adding hrf method to parameters * working on parameter recall * working on parameter recall * cleaning * fix(ui): fix unnecessary casts in addHrfToGraph * chore(ui): use camelCase in addHrfToGraph * fix(ui): do not add HRF metadata unless HRF is added to graph * fix(ui): remove unused imports in addHrfToGraph * feat(ui): do not hide HRF params when disabled, only disable them * fix(ui): remove unused vars in addHrfToGraph * feat(ui): default HRF str to 0.35, method ESRGAN * fix(ui): use isValidBoolean to check hrfEnabled param * fix(nodes): update CoreMetadataInvocation fields for HRF * feat(ui): set hrf strength default to 0.45 * fix(ui): set default hrf strength in configSlice * feat(ui): use translations for HRF features --------- Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
This commit is contained in:
@ -1,22 +1,26 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||
import {
|
||||
DenoiseLatentsInvocation,
|
||||
ESRGANInvocation,
|
||||
Edge,
|
||||
LatentsToImageInvocation,
|
||||
NoiseInvocation,
|
||||
ResizeLatentsInvocation,
|
||||
} from 'services/api/types';
|
||||
import {
|
||||
DENOISE_LATENTS,
|
||||
DENOISE_LATENTS_HRF,
|
||||
ESRGAN_HRF,
|
||||
IMAGE_TO_LATENTS_HRF,
|
||||
LATENTS_TO_IMAGE,
|
||||
LATENTS_TO_IMAGE_HRF,
|
||||
LATENTS_TO_IMAGE_HRF_HR,
|
||||
LATENTS_TO_IMAGE_HRF_LR,
|
||||
MAIN_MODEL_LOADER,
|
||||
NOISE,
|
||||
NOISE_HRF,
|
||||
RESCALE_LATENTS,
|
||||
RESIZE_HRF,
|
||||
VAE_LOADER,
|
||||
} from './constants';
|
||||
import { upsertMetadata } from './metadata';
|
||||
@ -56,6 +60,52 @@ function copyConnectionsToDenoiseLatentsHrf(graph: NonNullableGraph): void {
|
||||
graph.edges = graph.edges.concat(newEdges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the new resolution for high-resolution features (HRF) based on base model type.
|
||||
* Adjusts the width and height to maintain the aspect ratio and constrains them by the model's dimension limits,
|
||||
* rounding down to the nearest multiple of 8.
|
||||
*
|
||||
* @param {string} baseModel The base model type, which determines the base dimension used in calculations.
|
||||
* @param {number} width The current width to be adjusted for HRF.
|
||||
* @param {number} height The current height to be adjusted for HRF.
|
||||
* @return {{newWidth: number, newHeight: number}} The new width and height, adjusted and rounded as needed.
|
||||
*/
|
||||
function calculateHrfRes(
|
||||
baseModel: string,
|
||||
width: number,
|
||||
height: number
|
||||
): { newWidth: number; newHeight: number } {
|
||||
const aspect = width / height;
|
||||
let dimension;
|
||||
if (baseModel == 'sdxl') {
|
||||
dimension = 1024;
|
||||
} else {
|
||||
dimension = 512;
|
||||
}
|
||||
|
||||
const minDimension = Math.floor(dimension * 0.5);
|
||||
const modelArea = dimension * dimension; // Assuming square images for model_area
|
||||
|
||||
let initWidth;
|
||||
let initHeight;
|
||||
|
||||
if (aspect > 1.0) {
|
||||
initHeight = Math.max(minDimension, Math.sqrt(modelArea / aspect));
|
||||
initWidth = initHeight * aspect;
|
||||
} else {
|
||||
initWidth = Math.max(minDimension, Math.sqrt(modelArea * aspect));
|
||||
initHeight = initWidth / aspect;
|
||||
}
|
||||
// Cap initial height and width to final height and width.
|
||||
initWidth = Math.min(width, initWidth);
|
||||
initHeight = Math.min(height, initHeight);
|
||||
|
||||
const newWidth = roundToMultiple(Math.floor(initWidth), 8);
|
||||
const newHeight = roundToMultiple(Math.floor(initHeight), 8);
|
||||
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
// Adds the high-res fix feature to the given graph.
|
||||
export const addHrfToGraph = (
|
||||
state: RootState,
|
||||
@ -71,151 +121,61 @@ export const addHrfToGraph = (
|
||||
}
|
||||
const log = logger('txt2img');
|
||||
|
||||
const { vae, hrfWidth, hrfHeight, hrfStrength } = state.generation;
|
||||
const { vae, hrfStrength, hrfEnabled, hrfMethod } = state.generation;
|
||||
const isAutoVae = !vae;
|
||||
const width = state.generation.width;
|
||||
const height = state.generation.height;
|
||||
const baseModel = state.generation.model
|
||||
? state.generation.model.base_model
|
||||
: 'sd1';
|
||||
const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(
|
||||
baseModel,
|
||||
width,
|
||||
height
|
||||
);
|
||||
|
||||
// Pre-existing (original) graph nodes.
|
||||
const originalDenoiseLatentsNode = graph.nodes[DENOISE_LATENTS] as
|
||||
| DenoiseLatentsInvocation
|
||||
| undefined;
|
||||
const originalNoiseNode = graph.nodes[NOISE] as NoiseInvocation | undefined;
|
||||
// Original latents to image should pick this up.
|
||||
const originalLatentsToImageNode = graph.nodes[LATENTS_TO_IMAGE] as
|
||||
| LatentsToImageInvocation
|
||||
| undefined;
|
||||
// Check if originalDenoiseLatentsNode is undefined and log an error
|
||||
if (!originalDenoiseLatentsNode) {
|
||||
log.error('originalDenoiseLatentsNode is undefined');
|
||||
return;
|
||||
}
|
||||
// Check if originalNoiseNode is undefined and log an error
|
||||
if (!originalNoiseNode) {
|
||||
log.error('originalNoiseNode is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if originalLatentsToImageNode is undefined and log an error
|
||||
if (!originalLatentsToImageNode) {
|
||||
log.error('originalLatentsToImageNode is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
// Change height and width of original noise node to initial resolution.
|
||||
if (originalNoiseNode) {
|
||||
originalNoiseNode.width = hrfWidth;
|
||||
originalNoiseNode.height = hrfHeight;
|
||||
}
|
||||
|
||||
// Define new nodes.
|
||||
// Denoise latents node to be run on upscaled latents.
|
||||
const denoiseLatentsHrfNode: DenoiseLatentsInvocation = {
|
||||
type: 'denoise_latents',
|
||||
id: DENOISE_LATENTS_HRF,
|
||||
is_intermediate: originalDenoiseLatentsNode?.is_intermediate,
|
||||
cfg_scale: originalDenoiseLatentsNode?.cfg_scale,
|
||||
scheduler: originalDenoiseLatentsNode?.scheduler,
|
||||
steps: originalDenoiseLatentsNode?.steps,
|
||||
denoising_start: 1 - hrfStrength,
|
||||
denoising_end: 1,
|
||||
// Define new nodes and their connections, roughly in order of operations.
|
||||
graph.nodes[LATENTS_TO_IMAGE_HRF_LR] = {
|
||||
type: 'l2i',
|
||||
id: LATENTS_TO_IMAGE_HRF_LR,
|
||||
fp32: originalLatentsToImageNode?.fp32,
|
||||
is_intermediate: true,
|
||||
};
|
||||
|
||||
// New base resolution noise node.
|
||||
const hrfNoiseNode: NoiseInvocation = {
|
||||
type: 'noise',
|
||||
id: NOISE_HRF,
|
||||
seed: originalNoiseNode?.seed,
|
||||
use_cpu: originalNoiseNode?.use_cpu,
|
||||
is_intermediate: originalNoiseNode?.is_intermediate,
|
||||
};
|
||||
|
||||
const rescaleLatentsNode: ResizeLatentsInvocation = {
|
||||
id: RESCALE_LATENTS,
|
||||
type: 'lresize',
|
||||
width: state.generation.width,
|
||||
height: state.generation.height,
|
||||
};
|
||||
|
||||
// New node to convert latents to image.
|
||||
const latentsToImageHrfNode: LatentsToImageInvocation | undefined =
|
||||
originalLatentsToImageNode
|
||||
? {
|
||||
type: 'l2i',
|
||||
id: LATENTS_TO_IMAGE_HRF,
|
||||
fp32: originalLatentsToImageNode?.fp32,
|
||||
is_intermediate: originalLatentsToImageNode?.is_intermediate,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
// Add new nodes to graph.
|
||||
graph.nodes[LATENTS_TO_IMAGE_HRF] =
|
||||
latentsToImageHrfNode as LatentsToImageInvocation;
|
||||
graph.nodes[DENOISE_LATENTS_HRF] =
|
||||
denoiseLatentsHrfNode as DenoiseLatentsInvocation;
|
||||
graph.nodes[NOISE_HRF] = hrfNoiseNode as NoiseInvocation;
|
||||
graph.nodes[RESCALE_LATENTS] = rescaleLatentsNode as ResizeLatentsInvocation;
|
||||
|
||||
// Connect nodes.
|
||||
graph.edges.push(
|
||||
{
|
||||
// Set up rescale latents.
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: RESCALE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
// Set up new noise node
|
||||
{
|
||||
source: {
|
||||
node_id: RESCALE_LATENTS,
|
||||
field: 'height',
|
||||
},
|
||||
destination: {
|
||||
node_id: NOISE_HRF,
|
||||
field: 'height',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: RESCALE_LATENTS,
|
||||
field: 'width',
|
||||
},
|
||||
destination: {
|
||||
node_id: NOISE_HRF,
|
||||
field: 'width',
|
||||
},
|
||||
},
|
||||
// Set up new denoise node.
|
||||
{
|
||||
source: {
|
||||
node_id: RESCALE_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS_HRF,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE_HRF,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS_HRF,
|
||||
field: 'noise',
|
||||
},
|
||||
},
|
||||
// Set up new latents to image node.
|
||||
{
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS_HRF,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE_HRF,
|
||||
node_id: LATENTS_TO_IMAGE_HRF_LR,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
@ -225,17 +185,188 @@ export const addHrfToGraph = (
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE_HRF,
|
||||
node_id: LATENTS_TO_IMAGE_HRF_LR,
|
||||
field: 'vae',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
upsertMetadata(graph, {
|
||||
hrf_height: hrfHeight,
|
||||
hrf_width: hrfWidth,
|
||||
hrf_strength: hrfStrength,
|
||||
});
|
||||
graph.nodes[RESIZE_HRF] = {
|
||||
id: RESIZE_HRF,
|
||||
type: 'img_resize',
|
||||
is_intermediate: true,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
if (hrfMethod == 'ESRGAN') {
|
||||
let model_name: ESRGANInvocation['model_name'] = 'RealESRGAN_x2plus.pth';
|
||||
if ((width * height) / (hrfWidth * hrfHeight) > 2) {
|
||||
model_name = 'RealESRGAN_x4plus.pth';
|
||||
}
|
||||
graph.nodes[ESRGAN_HRF] = {
|
||||
id: ESRGAN_HRF,
|
||||
type: 'esrgan',
|
||||
model_name,
|
||||
is_intermediate: true,
|
||||
};
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE_HRF_LR,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: ESRGAN_HRF,
|
||||
field: 'image',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: ESRGAN_HRF,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: RESIZE_HRF,
|
||||
field: 'image',
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE_HRF_LR,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: RESIZE_HRF,
|
||||
field: 'image',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
graph.nodes[NOISE_HRF] = {
|
||||
type: 'noise',
|
||||
id: NOISE_HRF,
|
||||
seed: originalNoiseNode?.seed,
|
||||
use_cpu: originalNoiseNode?.use_cpu,
|
||||
is_intermediate: true,
|
||||
};
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: RESIZE_HRF,
|
||||
field: 'height',
|
||||
},
|
||||
destination: {
|
||||
node_id: NOISE_HRF,
|
||||
field: 'height',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: RESIZE_HRF,
|
||||
field: 'width',
|
||||
},
|
||||
destination: {
|
||||
node_id: NOISE_HRF,
|
||||
field: 'width',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
graph.nodes[IMAGE_TO_LATENTS_HRF] = {
|
||||
type: 'i2l',
|
||||
id: IMAGE_TO_LATENTS_HRF,
|
||||
is_intermediate: true,
|
||||
};
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: IMAGE_TO_LATENTS_HRF,
|
||||
field: 'vae',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: RESIZE_HRF,
|
||||
field: 'image',
|
||||
},
|
||||
destination: {
|
||||
node_id: IMAGE_TO_LATENTS_HRF,
|
||||
field: 'image',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
graph.nodes[DENOISE_LATENTS_HRF] = {
|
||||
type: 'denoise_latents',
|
||||
id: DENOISE_LATENTS_HRF,
|
||||
is_intermediate: true,
|
||||
cfg_scale: originalDenoiseLatentsNode?.cfg_scale,
|
||||
scheduler: originalDenoiseLatentsNode?.scheduler,
|
||||
steps: originalDenoiseLatentsNode?.steps,
|
||||
denoising_start: 1 - state.generation.hrfStrength,
|
||||
denoising_end: 1,
|
||||
};
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: IMAGE_TO_LATENTS_HRF,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS_HRF,
|
||||
field: 'latents',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: NOISE_HRF,
|
||||
field: 'noise',
|
||||
},
|
||||
destination: {
|
||||
node_id: DENOISE_LATENTS_HRF,
|
||||
field: 'noise',
|
||||
},
|
||||
}
|
||||
);
|
||||
copyConnectionsToDenoiseLatentsHrf(graph);
|
||||
|
||||
graph.nodes[LATENTS_TO_IMAGE_HRF_HR] = {
|
||||
type: 'l2i',
|
||||
id: LATENTS_TO_IMAGE_HRF_HR,
|
||||
fp32: originalLatentsToImageNode?.fp32,
|
||||
is_intermediate: true,
|
||||
};
|
||||
graph.edges.push(
|
||||
{
|
||||
source: {
|
||||
node_id: isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER,
|
||||
field: 'vae',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE_HRF_HR,
|
||||
field: 'vae',
|
||||
},
|
||||
},
|
||||
{
|
||||
source: {
|
||||
node_id: DENOISE_LATENTS_HRF,
|
||||
field: 'latents',
|
||||
},
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE_HRF_HR,
|
||||
field: 'latents',
|
||||
},
|
||||
}
|
||||
);
|
||||
upsertMetadata(graph, {
|
||||
hrf_strength: hrfStrength,
|
||||
hrf_enabled: hrfEnabled,
|
||||
hrf_method: hrfMethod,
|
||||
});
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import { SaveImageInvocation } from 'services/api/types';
|
||||
import {
|
||||
CANVAS_OUTPUT,
|
||||
LATENTS_TO_IMAGE,
|
||||
LATENTS_TO_IMAGE_HRF,
|
||||
LATENTS_TO_IMAGE_HRF_HR,
|
||||
NSFW_CHECKER,
|
||||
SAVE_IMAGE,
|
||||
WATERMARKER,
|
||||
@ -62,10 +62,10 @@ export const addSaveImageNode = (
|
||||
},
|
||||
destination,
|
||||
});
|
||||
} else if (LATENTS_TO_IMAGE_HRF in graph.nodes) {
|
||||
} else if (LATENTS_TO_IMAGE_HRF_HR in graph.nodes) {
|
||||
graph.edges.push({
|
||||
source: {
|
||||
node_id: LATENTS_TO_IMAGE_HRF,
|
||||
node_id: LATENTS_TO_IMAGE_HRF_HR,
|
||||
field: 'image',
|
||||
},
|
||||
destination,
|
||||
|
@ -4,7 +4,11 @@ export const NEGATIVE_CONDITIONING = 'negative_conditioning';
|
||||
export const DENOISE_LATENTS = 'denoise_latents';
|
||||
export const DENOISE_LATENTS_HRF = 'denoise_latents_hrf';
|
||||
export const LATENTS_TO_IMAGE = 'latents_to_image';
|
||||
export const LATENTS_TO_IMAGE_HRF = 'latents_to_image_hrf';
|
||||
export const LATENTS_TO_IMAGE_HRF_HR = 'latents_to_image_hrf_hr';
|
||||
export const LATENTS_TO_IMAGE_HRF_LR = 'latents_to_image_hrf_lr';
|
||||
export const IMAGE_TO_LATENTS_HRF = 'image_to_latents_hrf';
|
||||
export const RESIZE_HRF = 'resize_hrf';
|
||||
export const ESRGAN_HRF = 'esrgan_hrf';
|
||||
export const SAVE_IMAGE = 'save_image';
|
||||
export const NSFW_CHECKER = 'nsfw_checker';
|
||||
export const WATERMARKER = 'invisible_watermark';
|
||||
@ -21,7 +25,6 @@ export const CLIP_SKIP = 'clip_skip';
|
||||
export const IMAGE_TO_LATENTS = 'image_to_latents';
|
||||
export const LATENTS_TO_LATENTS = 'latents_to_latents';
|
||||
export const RESIZE = 'resize_image';
|
||||
export const RESCALE_LATENTS = 'rescale_latents';
|
||||
export const IMG2IMG_RESIZE = 'img2img_resize';
|
||||
export const CANVAS_OUTPUT = 'canvas_output';
|
||||
export const INPAINT_IMAGE = 'inpaint_image';
|
||||
|
Reference in New Issue
Block a user