feat(ui): move strength to init image layer

This further splits the control layers state into its own thing.
This commit is contained in:
psychedelicious 2024-05-07 10:10:03 +10:00
parent e8d60e8d83
commit 4adc592657
7 changed files with 64 additions and 21 deletions

View File

@ -8,6 +8,7 @@ import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerT
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle'; import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper'; import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
import { import {
iiLayerDenoisingStrengthChanged,
iiLayerImageChanged, iiLayerImageChanged,
layerSelected, layerSelected,
selectIILayerOrThrow, selectIILayerOrThrow,
@ -36,6 +37,13 @@ export const IILayer = memo(({ layerId }: Props) => {
[dispatch, layerId] [dispatch, layerId]
); );
const onChangeDenoisingStrength = useCallback(
(denoisingStrength: number) => {
dispatch(iiLayerDenoisingStrengthChanged({ layerId, denoisingStrength }));
},
[dispatch, layerId]
);
const droppableData = useMemo<IILayerImageDropData>( const droppableData = useMemo<IILayerImageDropData>(
() => ({ () => ({
actionType: 'SET_II_LAYER_IMAGE', actionType: 'SET_II_LAYER_IMAGE',
@ -67,7 +75,7 @@ export const IILayer = memo(({ layerId }: Props) => {
</Flex> </Flex>
{isOpen && ( {isOpen && (
<Flex flexDir="column" gap={3} px={3} pb={3}> <Flex flexDir="column" gap={3} px={3} pb={3}>
<ImageToImageStrength /> <ImageToImageStrength value={layer.denoisingStrength} onChange={onChangeDenoisingStrength} />
<InitialImagePreview <InitialImagePreview
image={layer.image} image={layer.image}
onChangeImage={onChangeImage} onChangeImage={onChangeImage}

View File

@ -49,7 +49,7 @@ import type {
} from './types'; } from './types';
export const initialControlLayersState: ControlLayersState = { export const initialControlLayersState: ControlLayersState = {
_version: 1, _version: 2,
selectedLayerId: null, selectedLayerId: null,
brushSize: 100, brushSize: 100,
layers: [], layers: [],
@ -642,6 +642,7 @@ export const controlLayersSlice = createSlice({
isEnabled: true, isEnabled: true,
image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null,
isSelected: true, isSelected: true,
denoisingStrength: 0.75,
}; };
state.layers.push(layer); state.layers.push(layer);
state.selectedLayerId = layer.id; state.selectedLayerId = layer.id;
@ -666,6 +667,11 @@ export const controlLayersSlice = createSlice({
const layer = selectIILayerOrThrow(state, layerId); const layer = selectIILayerOrThrow(state, layerId);
layer.opacity = opacity; layer.opacity = opacity;
}, },
iiLayerDenoisingStrengthChanged: (state, action: PayloadAction<{ layerId: string; denoisingStrength: number }>) => {
const { layerId, denoisingStrength } = action.payload;
const layer = selectIILayerOrThrow(state, layerId);
layer.denoisingStrength = denoisingStrength;
},
//#endregion //#endregion
//#region Globals //#region Globals
@ -841,6 +847,7 @@ export const {
iiLayerAdded, iiLayerAdded,
iiLayerImageChanged, iiLayerImageChanged,
iiLayerOpacityChanged, iiLayerOpacityChanged,
iiLayerDenoisingStrengthChanged,
// Globals // Globals
positivePromptChanged, positivePromptChanged,
negativePromptChanged, negativePromptChanged,
@ -860,6 +867,10 @@ export const selectControlLayersSlice = (state: RootState) => state.controlLayer
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const migrateControlLayersState = (state: any): any => { const migrateControlLayersState = (state: any): any => {
if (state._version === 1) {
// Reset state for users on v1 (e.g. beta users), some changes could cause
return deepClone(initialControlLayersState);
}
return state; return state;
}; };

View File

@ -79,12 +79,13 @@ export type InitialImageLayer = RenderableLayerBase & {
type: 'initial_image_layer'; type: 'initial_image_layer';
opacity: number; opacity: number;
image: ImageWithDims | null; image: ImageWithDims | null;
denoisingStrength: number;
}; };
export type Layer = RegionalGuidanceLayer | ControlAdapterLayer | IPAdapterLayer | InitialImageLayer; export type Layer = RegionalGuidanceLayer | ControlAdapterLayer | IPAdapterLayer | InitialImageLayer;
export type ControlLayersState = { export type ControlLayersState = {
_version: 1; _version: 2;
selectedLayerId: string | null; selectedLayerId: string | null;
layers: Layer[]; layers: Layer[];
brushSize: number; brushSize: number;

View File

@ -15,13 +15,13 @@ export const addInitialImageToLinearGraph = (
denoiseNodeId: string denoiseNodeId: string
): boolean => { ): boolean => {
// Remove Existing UNet Connections // Remove Existing UNet Connections
const { img2imgStrength, vaePrecision, model } = state.generation; const { vaePrecision, model } = state.generation;
const { refinerModel, refinerStart } = state.sdxl; const { refinerModel, refinerStart } = state.sdxl;
const { width, height } = state.controlLayers.present.size; const { width, height } = state.controlLayers.present.size;
const initialImageLayer = state.controlLayers.present.layers.find(isInitialImageLayer); const initialImageLayer = state.controlLayers.present.layers.find(isInitialImageLayer);
const initialImage = initialImageLayer?.isEnabled ? initialImageLayer?.image : null; const initialImage = initialImageLayer?.isEnabled ? initialImageLayer?.image : null;
if (!initialImage) { if (!initialImage || !initialImageLayer) {
return false; return false;
} }
@ -31,7 +31,10 @@ export const addInitialImageToLinearGraph = (
const denoiseNode = graph.nodes[denoiseNodeId]; const denoiseNode = graph.nodes[denoiseNodeId];
assert(denoiseNode?.type === 'denoise_latents', `Missing denoise node or incorrect type: ${denoiseNode?.type}`); assert(denoiseNode?.type === 'denoise_latents', `Missing denoise node or incorrect type: ${denoiseNode?.type}`);
denoiseNode.denoising_start = useRefinerStartEnd ? Math.min(refinerStart, 1 - img2imgStrength) : 1 - img2imgStrength; const { denoisingStrength } = initialImageLayer;
denoiseNode.denoising_start = useRefinerStartEnd
? Math.min(refinerStart, 1 - denoisingStrength)
: 1 - denoisingStrength;
denoiseNode.denoising_end = useRefinerStartEnd ? refinerStart : 1; denoiseNode.denoising_end = useRefinerStartEnd ? refinerStart : 1;
// We conditionally hook the image in depending on if a resize is needed // We conditionally hook the image in depending on if a resize is needed
@ -122,7 +125,7 @@ export const addInitialImageToLinearGraph = (
upsertMetadata(graph, { upsertMetadata(graph, {
generation_mode: isSDXL ? 'sdxl_img2img' : 'img2img', generation_mode: isSDXL ? 'sdxl_img2img' : 'img2img',
strength: img2imgStrength, strength: denoisingStrength,
init_image: initialImage.imageName, init_image: initialImage.imageName,
}); });

View File

@ -0,0 +1,20 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength';
import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
import { memo, useCallback } from 'react';
const ParamImageToImageStrength = () => {
const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength);
const dispatch = useAppDispatch();
const onChange = useCallback(
(v: number) => {
dispatch(setImg2imgStrength(v));
},
[dispatch]
);
return <ImageToImageStrength value={img2imgStrength} onChange={onChange} />;
};
export default memo(ParamImageToImageStrength);

View File

@ -1,14 +1,17 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library'; import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { setImg2imgStrength } from 'features/parameters/store/generationSlice'; import { memo } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const marks = [0, 0.5, 1]; const marks = [0, 0.5, 1];
const ImageToImageStrength = () => { type Props = {
const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength); value: number;
onChange: (v: number) => void;
};
const ImageToImageStrength = ({ value, onChange }: Props) => {
const initial = useAppSelector((s) => s.config.sd.img2imgStrength.initial); const initial = useAppSelector((s) => s.config.sd.img2imgStrength.initial);
const sliderMin = useAppSelector((s) => s.config.sd.img2imgStrength.sliderMin); const sliderMin = useAppSelector((s) => s.config.sd.img2imgStrength.sliderMin);
const sliderMax = useAppSelector((s) => s.config.sd.img2imgStrength.sliderMax); const sliderMax = useAppSelector((s) => s.config.sd.img2imgStrength.sliderMax);
@ -16,11 +19,8 @@ const ImageToImageStrength = () => {
const numberInputMax = useAppSelector((s) => s.config.sd.img2imgStrength.numberInputMax); const numberInputMax = useAppSelector((s) => s.config.sd.img2imgStrength.numberInputMax);
const coarseStep = useAppSelector((s) => s.config.sd.img2imgStrength.coarseStep); const coarseStep = useAppSelector((s) => s.config.sd.img2imgStrength.coarseStep);
const fineStep = useAppSelector((s) => s.config.sd.img2imgStrength.fineStep); const fineStep = useAppSelector((s) => s.config.sd.img2imgStrength.fineStep);
const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const handleChange = useCallback((v: number) => dispatch(setImg2imgStrength(v)), [dispatch]);
return ( return (
<FormControl> <FormControl>
<InformationalPopover feature="paramDenoisingStrength"> <InformationalPopover feature="paramDenoisingStrength">
@ -31,8 +31,8 @@ const ImageToImageStrength = () => {
fineStep={fineStep} fineStep={fineStep}
min={sliderMin} min={sliderMin}
max={sliderMax} max={sliderMax}
onChange={handleChange} onChange={onChange}
value={img2imgStrength} value={value}
defaultValue={initial} defaultValue={initial}
marks={marks} marks={marks}
/> />
@ -41,8 +41,8 @@ const ImageToImageStrength = () => {
fineStep={fineStep} fineStep={fineStep}
min={numberInputMin} min={numberInputMin}
max={numberInputMax} max={numberInputMax}
onChange={handleChange} onChange={onChange}
value={img2imgStrength} value={value}
defaultValue={initial} defaultValue={initial}
/> />
</FormControl> </FormControl>

View File

@ -9,7 +9,7 @@ import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing'; import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
import ParamScaledHeight from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight'; import ParamScaledHeight from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight';
import ParamScaledWidth from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth'; import ParamScaledWidth from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth';
import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength'; import ParamImageToImageStrength from 'features/parameters/components/Canvas/ParamImageToImageStrength';
import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput'; import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput';
import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize'; import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize';
import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle'; import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle';
@ -87,7 +87,7 @@ export const ImageSettingsAccordion = memo(() => {
<Flex px={4} pt={4} w="full" h="full" flexDir="column" data-testid="image-settings-accordion"> <Flex px={4} pt={4} w="full" h="full" flexDir="column" data-testid="image-settings-accordion">
<Flex flexDir="column" gap={4}> <Flex flexDir="column" gap={4}>
{activeTabName === 'canvas' ? <ImageSizeCanvas /> : <ImageSizeLinear />} {activeTabName === 'canvas' ? <ImageSizeCanvas /> : <ImageSizeLinear />}
{activeTabName === 'canvas' && <ImageToImageStrength />} {activeTabName === 'canvas' && <ParamImageToImageStrength />}
</Flex> </Flex>
<Expander label={t('accordions.advanced.options')} isOpen={isOpenExpander} onToggle={onToggleExpander}> <Expander label={t('accordions.advanced.options')} isOpen={isOpenExpander} onToggle={onToggleExpander}>
<Flex gap={4} pb={4} flexDir="column"> <Flex gap={4} pb={4} flexDir="column">