From 40a568c06094c4b9856db2737c0afea54314771f Mon Sep 17 00:00:00 2001
From: glibesyck <71762371+glibesyck@users.noreply.github.com>
Date: Sat, 14 Oct 2023 13:25:07 +0300
Subject: [PATCH 1/2] Hide Metadata in Info View (#4787)
* #4665 hides value of the corresponding metadata item by click on arrow
* #4787 return recall button back:)
* #4787 optional hide of metadata item, truncation and scrolling
* remove unused import
* #4787 recall parameters as separate tab in panel
* #4787 remove debug code
* fix(ui): undo changes to dist/locales/en.json
This file is autogenerated by our translation system and shouldn't be modified directly
* feat(ui): use scrollbar-enabled component for parameter recall tab
* fix(ui): revert unnecessary changes to DataViewer component
---------
Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
---
invokeai/frontend/web/public/locales/en.json | 2 ++
.../ImageMetadataViewer.tsx | 20 ++++++++++++++++---
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index e0baefcf9e..dd96193d5d 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -559,8 +559,10 @@
"negativePrompt": "Negative Prompt",
"noImageDetails": "No image details found",
"noMetaData": "No metadata found",
+ "noRecallParameters": "No parameters to recall found",
"perlin": "Perlin Noise",
"positivePrompt": "Positive Prompt",
+ "recallParameters": "Recall Parameters",
"scheduler": "Scheduler",
"seamless": "Seamless",
"seed": "Seed",
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx
index 8c7e69b2b1..e9cb3ffcaf 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx
@@ -18,6 +18,7 @@ import ImageMetadataActions from './ImageMetadataActions';
import { useAppSelector } from '../../../../app/store/storeHooks';
import { configSelector } from '../../../system/store/configSelectors';
import { useTranslation } from 'react-i18next';
+import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
type ImageMetadataViewerProps = {
image: ImageDTO;
@@ -65,19 +66,32 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
-
-
+ {t('metadata.recallParameters')}
{t('metadata.metadata')}
{t('metadata.imageDetails')}
{t('metadata.workflow')}
+
+ {metadata ? (
+
+
+
+ ) : (
+
+ )}
+
{metadata ? (
From 75663ec81e9ca0f6d11619027e12188f5e62ce63 Mon Sep 17 00:00:00 2001
From: Paul Curry
Date: Sat, 14 Oct 2023 03:34:41 -0700
Subject: [PATCH 2/2] feat (ui, generation): High Resolution Fix MVP in
Text2Image Linear Flow (#4819)
* added HrfScale type with initial value
* working
* working
* working
* working
* working
* added addHrfToGraph
* continueing to implement this
* working on this
* comments
* working
* made hrf into its own collapse
* working on adding strength slider
* working
* working
* refactoring
* working
* change of this working: 0
* removed onnx support since apparently its not used
* working
* made scale integer
* trying out psycicpebbles idea
* working
* working on this
* working
* added toggle
* comments
* self review
* fixing things
* remove 'any' type
* fixing typing
* changed initial strength value to 3 (large values cause issues)
* set denoising start to be 1 - strength to resemble image to image
* set initial value
* added image to image
* pr1
* pr2
* updating to resolution finding
* working
* working
* working
* working
* working
* working
* working
* working
* working
* use memo
* connect rescale hw to noise
* working
* fixed min bug
* nit
* hides elements conditionally
* style
* feat(ui): add config for HRF, disable if feature disabled or ONNX model in use
* fix(ui): use `useCallback` for HRF toggle
---------
Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
---
invokeai/app/invocations/metadata.py | 12 +
.../frontend/web/src/app/types/invokeai.ts | 11 +-
.../IAIInformationalPopover/constants.ts | 1 +
.../nodes/util/graphBuilders/addHrfToGraph.ts | 247 ++++++++++++++++++
.../util/graphBuilders/addSaveImageNode.ts | 9 +
.../buildLinearTextToImageGraph.ts | 14 +
.../nodes/util/graphBuilders/constants.ts | 4 +
.../HighResFix/ParamHrfCollapse.tsx | 65 +++++
.../Parameters/HighResFix/ParamHrfHeight.tsx | 87 ++++++
.../HighResFix/ParamHrfStrength.tsx | 64 +++++
.../Parameters/HighResFix/ParamHrfToggle.tsx | 27 ++
.../Parameters/HighResFix/ParamHrfWidth.tsx | 84 ++++++
.../parameters/store/generationSlice.ts | 24 ++
.../parameters/types/parameterSchemas.ts | 9 +
.../src/features/system/store/configSlice.ts | 8 +
.../TextToImage/TextToImageTabParameters.tsx | 2 +
.../frontend/web/src/services/api/schema.d.ts | 15 ++
.../frontend/web/src/services/api/types.ts | 1 +
18 files changed, 683 insertions(+), 1 deletion(-)
create mode 100644 invokeai/frontend/web/src/features/nodes/util/graphBuilders/addHrfToGraph.ts
create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfCollapse.tsx
create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfHeight.tsx
create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfStrength.tsx
create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfToggle.tsx
create mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfWidth.tsx
diff --git a/invokeai/app/invocations/metadata.py b/invokeai/app/invocations/metadata.py
index 54e385feb3..cecc01096a 100644
--- a/invokeai/app/invocations/metadata.py
+++ b/invokeai/app/invocations/metadata.py
@@ -156,6 +156,18 @@ class MetadataAccumulatorInvocation(BaseInvocation):
description="The VAE used for decoding, if the main model's default was not used",
)
+ # High resolution fix metadata.
+ hrf_width: Optional[int] = InputField(
+ description="The high resolution fix height and width multipler.",
+ )
+ hrf_height: Optional[int] = InputField(
+ description="The high resolution fix height and width multipler.",
+ )
+ hrf_strength: Optional[float] = InputField(
+ default=None,
+ description="The high resolution fix img2img strength used in the upscale pass.",
+ )
+
# SDXL
positive_style_prompt: Optional[str] = InputField(
default=None,
diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts
index 8331e9c979..39e4ffd27a 100644
--- a/invokeai/frontend/web/src/app/types/invokeai.ts
+++ b/invokeai/frontend/web/src/app/types/invokeai.ts
@@ -39,7 +39,8 @@ export type SDFeature =
| 'hires'
| 'lora'
| 'embedding'
- | 'vae';
+ | 'vae'
+ | 'hrf';
/**
* Configuration options for the InvokeAI UI.
@@ -110,6 +111,14 @@ export type AppConfig = {
fineStep: number;
coarseStep: number;
};
+ hrfStrength: {
+ initial: number;
+ min: number;
+ sliderMax: number;
+ inputMax: number;
+ fineStep: number;
+ coarseStep: number;
+ };
dynamicPrompts: {
maxPrompts: {
initial: number;
diff --git a/invokeai/frontend/web/src/common/components/IAIInformationalPopover/constants.ts b/invokeai/frontend/web/src/common/components/IAIInformationalPopover/constants.ts
index f2398483bf..197f5f4068 100644
--- a/invokeai/frontend/web/src/common/components/IAIInformationalPopover/constants.ts
+++ b/invokeai/frontend/web/src/common/components/IAIInformationalPopover/constants.ts
@@ -2,6 +2,7 @@ import { PopoverProps } from '@chakra-ui/react';
export type Feature =
| 'clipSkip'
+ | 'hrf'
| 'paramNegativeConditioning'
| 'paramPositiveConditioning'
| 'paramScheduler'
diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addHrfToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addHrfToGraph.ts
new file mode 100644
index 0000000000..4b4a8a8a03
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addHrfToGraph.ts
@@ -0,0 +1,247 @@
+import { RootState } from 'app/store/store';
+import { NonNullableGraph } from 'features/nodes/types/types';
+import {
+ DenoiseLatentsInvocation,
+ ResizeLatentsInvocation,
+ NoiseInvocation,
+ LatentsToImageInvocation,
+ Edge,
+} from 'services/api/types';
+import {
+ LATENTS_TO_IMAGE,
+ DENOISE_LATENTS,
+ NOISE,
+ MAIN_MODEL_LOADER,
+ METADATA_ACCUMULATOR,
+ LATENTS_TO_IMAGE_HRF,
+ DENOISE_LATENTS_HRF,
+ RESCALE_LATENTS,
+ NOISE_HRF,
+ VAE_LOADER,
+} from './constants';
+import { logger } from 'app/logging/logger';
+
+// Copy certain connections from previous DENOISE_LATENTS to new DENOISE_LATENTS_HRF.
+function copyConnectionsToDenoiseLatentsHrf(graph: NonNullableGraph): void {
+ const destinationFields = [
+ 'vae',
+ 'control',
+ 'ip_adapter',
+ 'metadata',
+ 'unet',
+ 'positive_conditioning',
+ 'negative_conditioning',
+ ];
+ const newEdges: Edge[] = [];
+
+ // Loop through the existing edges connected to DENOISE_LATENTS
+ graph.edges.forEach((edge: Edge) => {
+ if (
+ edge.destination.node_id === DENOISE_LATENTS &&
+ destinationFields.includes(edge.destination.field)
+ ) {
+ // Add a similar connection to DENOISE_LATENTS_HRF
+ newEdges.push({
+ source: {
+ node_id: edge.source.node_id,
+ field: edge.source.field,
+ },
+ destination: {
+ node_id: DENOISE_LATENTS_HRF,
+ field: edge.destination.field,
+ },
+ });
+ }
+ });
+ graph.edges = graph.edges.concat(newEdges);
+}
+
+// Adds the high-res fix feature to the given graph.
+export const addHrfToGraph = (
+ state: RootState,
+ graph: NonNullableGraph
+): void => {
+ // Double check hrf is enabled.
+ if (
+ !state.generation.hrfEnabled ||
+ state.config.disabledSDFeatures.includes('hrf') ||
+ state.generation.model?.model_type === 'onnx' // TODO: ONNX support
+ ) {
+ return;
+ }
+ const log = logger('txt2img');
+
+ const { vae } = state.generation;
+ const isAutoVae = !vae;
+ const hrfWidth = state.generation.hrfWidth;
+ const hrfHeight = state.generation.hrfHeight;
+
+ // 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 - state.generation.hrfStrength,
+ denoising_end: 1,
+ };
+
+ // 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,
+ field: 'latents',
+ },
+ },
+ {
+ source: {
+ node_id: METADATA_ACCUMULATOR,
+ field: 'metadata',
+ },
+ destination: {
+ node_id: LATENTS_TO_IMAGE_HRF,
+ field: 'metadata',
+ },
+ },
+ {
+ source: {
+ node_id: isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER,
+ field: 'vae',
+ },
+ destination: {
+ node_id: LATENTS_TO_IMAGE_HRF,
+ field: 'vae',
+ },
+ }
+ );
+
+ copyConnectionsToDenoiseLatentsHrf(graph);
+};
diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addSaveImageNode.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addSaveImageNode.ts
index 738c69faff..d5a6addf8a 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addSaveImageNode.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/addSaveImageNode.ts
@@ -2,6 +2,7 @@ import { NonNullableGraph } from 'features/nodes/types/types';
import {
CANVAS_OUTPUT,
LATENTS_TO_IMAGE,
+ LATENTS_TO_IMAGE_HRF,
METADATA_ACCUMULATOR,
NSFW_CHECKER,
SAVE_IMAGE,
@@ -82,6 +83,14 @@ export const addSaveImageNode = (
},
destination,
});
+ } else if (LATENTS_TO_IMAGE_HRF in graph.nodes) {
+ graph.edges.push({
+ source: {
+ node_id: LATENTS_TO_IMAGE_HRF,
+ field: 'image',
+ },
+ destination,
+ });
} else if (LATENTS_TO_IMAGE in graph.nodes) {
graph.edges.push({
source: {
diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts
index c3c7fcdf1b..e692e12fa4 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/buildLinearTextToImageGraph.ts
@@ -6,6 +6,7 @@ import {
ONNXTextToLatentsInvocation,
} from 'services/api/types';
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
+import { addHrfToGraph } from './addHrfToGraph';
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
import { addLoRAsToGraph } from './addLoRAsToGraph';
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
@@ -47,6 +48,10 @@ export const buildLinearTextToImageGraph = (
seamlessXAxis,
seamlessYAxis,
seed,
+ hrfWidth,
+ hrfHeight,
+ hrfStrength,
+ hrfEnabled: hrfEnabled,
} = state.generation;
const use_cpu = shouldUseCpuNoise;
@@ -254,6 +259,9 @@ export const buildLinearTextToImageGraph = (
ipAdapters: [], // populated in addIPAdapterToLinearGraph
t2iAdapters: [], // populated in addT2IAdapterToLinearGraph
clip_skip: clipSkip,
+ hrf_width: hrfEnabled ? hrfWidth : undefined,
+ hrf_height: hrfEnabled ? hrfHeight : undefined,
+ hrf_strength: hrfEnabled ? hrfStrength : undefined,
};
graph.edges.push({
@@ -287,6 +295,12 @@ export const buildLinearTextToImageGraph = (
addT2IAdaptersToLinearGraph(state, graph, DENOISE_LATENTS);
+ // High resolution fix.
+ // NOTE: Not supported for onnx models.
+ if (state.generation.hrfEnabled && !isUsingOnnxModel) {
+ addHrfToGraph(state, graph);
+ }
+
// NSFW & watermark - must be last thing added to graph
if (state.system.shouldUseNSFWChecker) {
// must add before watermarker!
diff --git a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts
index e538094731..7d547d09e6 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graphBuilders/constants.ts
@@ -2,11 +2,14 @@
export const POSITIVE_CONDITIONING = 'positive_conditioning';
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 SAVE_IMAGE = 'save_image';
export const NSFW_CHECKER = 'nsfw_checker';
export const WATERMARKER = 'invisible_watermark';
export const NOISE = 'noise';
+export const NOISE_HRF = 'noise_hrf';
export const RANDOM_INT = 'rand_int';
export const RANGE_OF_SIZE = 'range_of_size';
export const ITERATE = 'iterate';
@@ -18,6 +21,7 @@ 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';
diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfCollapse.tsx
new file mode 100644
index 0000000000..ef0db1af6d
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfCollapse.tsx
@@ -0,0 +1,65 @@
+import { Flex } from '@chakra-ui/react';
+import { createSelector } from '@reduxjs/toolkit';
+import { RootState, stateSelector } from 'app/store/store';
+import { useAppSelector } from 'app/store/storeHooks';
+import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
+import IAICollapse from 'common/components/IAICollapse';
+import { useMemo } from 'react';
+import ParamHrfStrength from './ParamHrfStrength';
+import ParamHrfToggle from './ParamHrfToggle';
+import ParamHrfWidth from './ParamHrfWidth';
+import ParamHrfHeight from './ParamHrfHeight';
+import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
+
+const selector = createSelector(
+ stateSelector,
+ (state: RootState) => {
+ const { hrfEnabled } = state.generation;
+
+ return { hrfEnabled };
+ },
+ defaultSelectorOptions
+);
+
+export default function ParamHrfCollapse() {
+ const isHRFFeatureEnabled = useFeatureStatus('hrf').isFeatureEnabled;
+ const { hrfEnabled } = useAppSelector(selector);
+ const activeLabel = useMemo(() => {
+ if (hrfEnabled) {
+ return 'On';
+ } else {
+ return 'Off';
+ }
+ }, [hrfEnabled]);
+
+ if (!isHRFFeatureEnabled) {
+ return null;
+ }
+
+ return (
+
+
+
+ {hrfEnabled && (
+
+
+
+
+ )}
+ {hrfEnabled && }
+
+
+ );
+}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfHeight.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfHeight.tsx
new file mode 100644
index 0000000000..053da11a5a
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfHeight.tsx
@@ -0,0 +1,87 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { stateSelector } from 'app/store/store';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
+import IAISlider, { IAIFullSliderProps } from 'common/components/IAISlider';
+import { roundToMultiple } from 'common/util/roundDownToMultiple';
+import {
+ setHrfHeight,
+ setHrfWidth,
+} from 'features/parameters/store/generationSlice';
+import { memo, useCallback } from 'react';
+
+function findPrevMultipleOfEight(n: number): number {
+ return Math.floor((n - 1) / 8) * 8;
+}
+
+const selector = createSelector(
+ [stateSelector],
+ ({ generation, hotkeys, config }) => {
+ const { min, fineStep, coarseStep } = config.sd.height;
+ const { model, height, hrfHeight, aspectRatio, hrfEnabled } = generation;
+
+ const step = hotkeys.shift ? fineStep : coarseStep;
+
+ return {
+ model,
+ height,
+ hrfHeight,
+ min,
+ step,
+ aspectRatio,
+ hrfEnabled,
+ };
+ },
+ defaultSelectorOptions
+);
+
+type ParamHeightProps = Omit<
+ IAIFullSliderProps,
+ 'label' | 'value' | 'onChange'
+>;
+
+const ParamHrfHeight = (props: ParamHeightProps) => {
+ const { height, hrfHeight, min, step, aspectRatio, hrfEnabled } =
+ useAppSelector(selector);
+ const dispatch = useAppDispatch();
+ const maxHrfHeight = Math.max(findPrevMultipleOfEight(height), min);
+
+ const handleChange = useCallback(
+ (v: number) => {
+ dispatch(setHrfHeight(v));
+ if (aspectRatio) {
+ const newWidth = roundToMultiple(v * aspectRatio, 8);
+ dispatch(setHrfWidth(newWidth));
+ }
+ },
+ [dispatch, aspectRatio]
+ );
+
+ const handleReset = useCallback(() => {
+ dispatch(setHrfHeight(maxHrfHeight));
+ if (aspectRatio) {
+ const newWidth = roundToMultiple(maxHrfHeight * aspectRatio, 8);
+ dispatch(setHrfWidth(newWidth));
+ }
+ }, [dispatch, maxHrfHeight, aspectRatio]);
+
+ return (
+
+ );
+};
+
+export default memo(ParamHrfHeight);
diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfStrength.tsx
new file mode 100644
index 0000000000..ba17bc2f36
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfStrength.tsx
@@ -0,0 +1,64 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { useAppSelector, useAppDispatch } from 'app/store/storeHooks';
+import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
+import { memo, useCallback } from 'react';
+import { stateSelector } from 'app/store/store';
+import { setHrfStrength } from 'features/parameters/store/generationSlice';
+import IAISlider from 'common/components/IAISlider';
+
+const selector = createSelector(
+ [stateSelector],
+ ({ generation, hotkeys, config }) => {
+ const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
+ config.sd.hrfStrength;
+ const { hrfStrength, hrfEnabled } = generation;
+ const step = hotkeys.shift ? fineStep : coarseStep;
+
+ return {
+ hrfStrength,
+ initial,
+ min,
+ sliderMax,
+ inputMax,
+ step,
+ hrfEnabled,
+ };
+ },
+ defaultSelectorOptions
+);
+
+const ParamHrfStrength = () => {
+ const { hrfStrength, initial, min, sliderMax, step, hrfEnabled } =
+ useAppSelector(selector);
+ const dispatch = useAppDispatch();
+
+ const handleHrfStrengthReset = useCallback(() => {
+ dispatch(setHrfStrength(initial));
+ }, [dispatch, initial]);
+
+ const handleHrfStrengthChange = useCallback(
+ (v: number) => {
+ dispatch(setHrfStrength(v));
+ },
+ [dispatch]
+ );
+
+ return (
+
+ );
+};
+
+export default memo(ParamHrfStrength);
diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfToggle.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfToggle.tsx
new file mode 100644
index 0000000000..8a68ead652
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfToggle.tsx
@@ -0,0 +1,27 @@
+import { RootState } from 'app/store/store';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import IAISwitch from 'common/components/IAISwitch';
+import { setHrfEnabled } from 'features/parameters/store/generationSlice';
+import { ChangeEvent, useCallback } from 'react';
+
+export default function ParamHrfToggle() {
+ const dispatch = useAppDispatch();
+
+ const hrfEnabled = useAppSelector(
+ (state: RootState) => state.generation.hrfEnabled
+ );
+
+ const handleHrfEnabled = useCallback(
+ (e: ChangeEvent) =>
+ dispatch(setHrfEnabled(e.target.checked)),
+ [dispatch]
+ );
+
+ return (
+
+ );
+}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfWidth.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfWidth.tsx
new file mode 100644
index 0000000000..09615d5154
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/HighResFix/ParamHrfWidth.tsx
@@ -0,0 +1,84 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { stateSelector } from 'app/store/store';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
+import IAISlider, { IAIFullSliderProps } from 'common/components/IAISlider';
+import { roundToMultiple } from 'common/util/roundDownToMultiple';
+import {
+ setHrfHeight,
+ setHrfWidth,
+} from 'features/parameters/store/generationSlice';
+import { memo, useCallback } from 'react';
+
+function findPrevMultipleOfEight(n: number): number {
+ return Math.floor((n - 1) / 8) * 8;
+}
+
+const selector = createSelector(
+ [stateSelector],
+ ({ generation, hotkeys, config }) => {
+ const { min, fineStep, coarseStep } = config.sd.width;
+ const { model, width, hrfWidth, aspectRatio, hrfEnabled } = generation;
+
+ const step = hotkeys.shift ? fineStep : coarseStep;
+
+ return {
+ model,
+ width,
+ hrfWidth,
+ min,
+ step,
+ aspectRatio,
+ hrfEnabled,
+ };
+ },
+ defaultSelectorOptions
+);
+
+type ParamWidthProps = Omit;
+
+const ParamHrfWidth = (props: ParamWidthProps) => {
+ const { width, hrfWidth, min, step, aspectRatio, hrfEnabled } =
+ useAppSelector(selector);
+ const dispatch = useAppDispatch();
+ const maxHrfWidth = Math.max(findPrevMultipleOfEight(width), min);
+
+ const handleChange = useCallback(
+ (v: number) => {
+ dispatch(setHrfWidth(v));
+ if (aspectRatio) {
+ const newHeight = roundToMultiple(v / aspectRatio, 8);
+ dispatch(setHrfHeight(newHeight));
+ }
+ },
+ [dispatch, aspectRatio]
+ );
+
+ const handleReset = useCallback(() => {
+ dispatch(setHrfWidth(maxHrfWidth));
+ if (aspectRatio) {
+ const newHeight = roundToMultiple(maxHrfWidth / aspectRatio, 8);
+ dispatch(setHrfHeight(newHeight));
+ }
+ }, [dispatch, maxHrfWidth, aspectRatio]);
+
+ return (
+
+ );
+};
+
+export default memo(ParamHrfWidth);
diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts
index 84d26b6476..63d545662a 100644
--- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts
+++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts
@@ -27,6 +27,10 @@ import {
} from '../types/parameterSchemas';
export interface GenerationState {
+ hrfHeight: HeightParam;
+ hrfWidth: WidthParam;
+ hrfEnabled: boolean;
+ hrfStrength: StrengthParam;
cfgScale: CfgScaleParam;
height: HeightParam;
img2imgStrength: StrengthParam;
@@ -69,6 +73,10 @@ export interface GenerationState {
}
export const initialGenerationState: GenerationState = {
+ hrfHeight: 64,
+ hrfWidth: 64,
+ hrfStrength: 0.75,
+ hrfEnabled: false,
cfgScale: 7.5,
height: 512,
img2imgStrength: 0.75,
@@ -271,6 +279,18 @@ export const generationSlice = createSlice({
setClipSkip: (state, action: PayloadAction) => {
state.clipSkip = action.payload;
},
+ setHrfHeight: (state, action: PayloadAction) => {
+ state.hrfHeight = action.payload;
+ },
+ setHrfWidth: (state, action: PayloadAction) => {
+ state.hrfWidth = action.payload;
+ },
+ setHrfStrength: (state, action: PayloadAction) => {
+ state.hrfStrength = action.payload;
+ },
+ setHrfEnabled: (state, action: PayloadAction) => {
+ state.hrfEnabled = action.payload;
+ },
shouldUseCpuNoiseChanged: (state, action: PayloadAction) => {
state.shouldUseCpuNoise = action.payload;
},
@@ -355,6 +375,10 @@ export const {
setSeamlessXAxis,
setSeamlessYAxis,
setClipSkip,
+ setHrfHeight,
+ setHrfWidth,
+ setHrfStrength,
+ setHrfEnabled,
shouldUseCpuNoiseChanged,
setAspectRatio,
setShouldLockAspectRatio,
diff --git a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
index e548b9aeaf..7a6b6b8a5c 100644
--- a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
+++ b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
@@ -210,6 +210,15 @@ export type HeightParam = z.infer;
export const isValidHeight = (val: unknown): val is HeightParam =>
zHeight.safeParse(val).success;
+/**
+ * Zod schema for resolution parameter
+ */
+export const zResolution = z.tuple([zWidth, zHeight]);
+/**
+ * Type alias for resolution parameter, inferred from its zod schema
+ */
+export type ResolutionParam = z.infer;
+
export const zBaseModel = z.enum([
'any',
'sd-1',
diff --git a/invokeai/frontend/web/src/features/system/store/configSlice.ts b/invokeai/frontend/web/src/features/system/store/configSlice.ts
index 30eebab5d5..78d540203b 100644
--- a/invokeai/frontend/web/src/features/system/store/configSlice.ts
+++ b/invokeai/frontend/web/src/features/system/store/configSlice.ts
@@ -69,6 +69,14 @@ export const initialConfigState: AppConfig = {
fineStep: 0.01,
coarseStep: 0.05,
},
+ hrfStrength: {
+ initial: 0.7,
+ min: 0,
+ sliderMax: 1,
+ inputMax: 1,
+ fineStep: 0.01,
+ coarseStep: 0.05,
+ },
dynamicPrompts: {
maxPrompts: {
initial: 100,
diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx
index 0c153fd7eb..8ec8c9caed 100644
--- a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx
+++ b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImage/TextToImageTabParameters.tsx
@@ -3,6 +3,7 @@ import ParamLoraCollapse from 'features/lora/components/ParamLoraCollapse';
import ParamAdvancedCollapse from 'features/parameters/components/Parameters/Advanced/ParamAdvancedCollapse';
import ControlAdaptersCollapse from 'features/controlAdapters/components/ControlAdaptersCollapse';
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
+import ParamHrfCollapse from 'features/parameters/components/Parameters/HighResFix/ParamHrfCollapse';
import { memo } from 'react';
import ParamPromptArea from '../../../../parameters/components/Parameters/Prompt/ParamPromptArea';
import TextToImageTabCoreParameters from './TextToImageTabCoreParameters';
@@ -16,6 +17,7 @@ const TextToImageTabParameters = () => {
+
>
);
diff --git a/invokeai/frontend/web/src/services/api/schema.d.ts b/invokeai/frontend/web/src/services/api/schema.d.ts
index 6da1fa5154..d71d01e6af 100644
--- a/invokeai/frontend/web/src/services/api/schema.d.ts
+++ b/invokeai/frontend/web/src/services/api/schema.d.ts
@@ -6190,6 +6190,21 @@ export type components = {
* @description The VAE used for decoding, if the main model's default was not used
*/
vae?: components["schemas"]["VAEModelField"];
+ /**
+ * High Resolution Fix Width
+ * @description The high resolution fix height and width multiplier.
+ */
+ hrf_width?: number;
+ /**
+ * High Resolution Fix Height
+ * @description The high resolution fix height and width multiplier.
+ */
+ hrf_height?: number;
+ /**
+ * High Resolution Strength
+ * @description The high resolution fix img2img strength used in the upscale pass.
+ */
+ hrf_strength?: number;
/**
* Positive Style Prompt
* @description The positive style prompt parameter
diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts
index da56d2782f..6fda849b89 100644
--- a/invokeai/frontend/web/src/services/api/types.ts
+++ b/invokeai/frontend/web/src/services/api/types.ts
@@ -137,6 +137,7 @@ export type CompelInvocation = s['CompelInvocation'];
export type DynamicPromptInvocation = s['DynamicPromptInvocation'];
export type NoiseInvocation = s['NoiseInvocation'];
export type DenoiseLatentsInvocation = s['DenoiseLatentsInvocation'];
+export type ResizeLatentsInvocation = s['ResizeLatentsInvocation'];
export type ONNXTextToLatentsInvocation = s['ONNXTextToLatentsInvocation'];
export type SDXLLoraLoaderInvocation = s['SDXLLoraLoaderInvocation'];
export type ImageToLatentsInvocation = s['ImageToLatentsInvocation'];