fix(ui): fix canvas img2img if no init image selected

This commit is contained in:
psychedelicious 2023-05-11 11:12:39 +10:00
parent f488b1a7f2
commit 8ef49c2640
7 changed files with 31 additions and 127 deletions

View File

@ -1,26 +1,20 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { validateSeedWeights } from 'common/util/seedWeightPairs'; import { validateSeedWeights } from 'common/util/seedWeightPairs';
import { initialCanvasImageSelector } from 'features/canvas/store/canvasSelectors';
import { generationSelector } from 'features/parameters/store/generationSelectors'; import { generationSelector } from 'features/parameters/store/generationSelectors';
import { systemSelector } from 'features/system/store/systemSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
export const readinessSelector = createSelector( export const readinessSelector = createSelector(
[ [generationSelector, systemSelector, activeTabNameSelector],
generationSelector, (generation, system, activeTabName) => {
systemSelector,
initialCanvasImageSelector,
activeTabNameSelector,
],
(generation, system) => {
const { const {
prompt, prompt,
shouldGenerateVariations, shouldGenerateVariations,
seedWeights, seedWeights,
initialImage, initialImage,
seed, seed,
isImageToImageEnabled,
} = generation; } = generation;
const { isProcessing, isConnected } = system; const { isProcessing, isConnected } = system;
@ -34,7 +28,7 @@ export const readinessSelector = createSelector(
reasonsWhyNotReady.push('Missing prompt'); reasonsWhyNotReady.push('Missing prompt');
} }
if (isImageToImageEnabled && !initialImage) { if (activeTabName === 'img2img' && !initialImage) {
isReady = false; isReady = false;
reasonsWhyNotReady.push('No initial image selected'); reasonsWhyNotReady.push('No initial image selected');
} }
@ -64,10 +58,5 @@ export const readinessSelector = createSelector(
// All good // All good
return { isReady, reasonsWhyNotReady }; return { isReady, reasonsWhyNotReady };
}, },
{ defaultSelectorOptions
memoizeOptions: {
equalityCheck: isEqual,
resultEqualityCheck: isEqual,
},
}
); );

View File

@ -79,8 +79,6 @@ export const buildCanvasGraphAndBlobs = async (
moduleLog.debug( moduleLog.debug(
{ {
data: { data: {
// baseDataURL,
// maskDataURL,
baseIsPartiallyTransparent, baseIsPartiallyTransparent,
baseIsFullyTransparent, baseIsFullyTransparent,
doesMaskHaveBlackPixels, doesMaskHaveBlackPixels,

View File

@ -5,8 +5,8 @@ import {
ImageToImageInvocation, ImageToImageInvocation,
TextToImageInvocation, TextToImageInvocation,
} from 'services/api'; } from 'services/api';
import { initialImageSelector } from 'features/parameters/store/generationSelectors';
import { O } from 'ts-toolbelt'; import { O } from 'ts-toolbelt';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
export const buildImg2ImgNode = ( export const buildImg2ImgNode = (
state: RootState, state: RootState,
@ -15,6 +15,8 @@ export const buildImg2ImgNode = (
const nodeId = uuidv4(); const nodeId = uuidv4();
const { generation } = state; const { generation } = state;
const activeTabName = activeTabNameSelector(state);
const { const {
prompt, prompt,
negativePrompt, negativePrompt,
@ -33,11 +35,6 @@ export const buildImg2ImgNode = (
// const initialImage = initialImageSelector(state); // const initialImage = initialImageSelector(state);
if (!initialImage) {
// TODO: handle this
throw 'no initial image';
}
const imageToImageNode: ImageToImageInvocation = { const imageToImageNode: ImageToImageInvocation = {
id: nodeId, id: nodeId,
type: 'img2img', type: 'img2img',
@ -48,14 +45,23 @@ export const buildImg2ImgNode = (
cfg_scale: cfgScale, cfg_scale: cfgScale,
scheduler: sampler as ImageToImageInvocation['scheduler'], scheduler: sampler as ImageToImageInvocation['scheduler'],
model, model,
image: {
image_name: initialImage.name,
image_type: initialImage.type,
},
strength, strength,
fit, fit,
}; };
// on Canvas tab, we do not manually specific init image
if (activeTabName === 'img2img') {
if (!initialImage) {
// TODO: handle this more better
throw 'no initial image';
}
imageToImageNode.image = {
image_name: initialImage.name,
image_type: initialImage.type,
};
}
if (!shouldRandomizeSeed) { if (!shouldRandomizeSeed) {
imageToImageNode.seed = seed; imageToImageNode.seed = seed;
} }

View File

@ -1,5 +1,6 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { generationSelector } from 'features/parameters/store/generationSelectors'; import { generationSelector } from 'features/parameters/store/generationSelectors';
import { setImg2imgStrength } from 'features/parameters/store/generationSlice'; import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
@ -13,32 +14,25 @@ const selector = createSelector(
(generation, hotkeys, config) => { (generation, hotkeys, config) => {
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } = const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
config.sd.img2imgStrength; config.sd.img2imgStrength;
const { img2imgStrength, isImageToImageEnabled } = generation; const { img2imgStrength } = generation;
const step = hotkeys.shift ? fineStep : coarseStep; const step = hotkeys.shift ? fineStep : coarseStep;
return { return {
img2imgStrength, img2imgStrength,
isImageToImageEnabled,
initial, initial,
min, min,
sliderMax, sliderMax,
inputMax, inputMax,
step, step,
}; };
} },
defaultSelectorOptions
); );
const ImageToImageStrength = () => { const ImageToImageStrength = () => {
const { const { img2imgStrength, initial, min, sliderMax, inputMax, step } =
img2imgStrength, useAppSelector(selector);
isImageToImageEnabled,
initial,
min,
sliderMax,
inputMax,
step,
} = useAppSelector(selector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
@ -64,7 +58,6 @@ const ImageToImageStrength = () => {
withInput withInput
withSliderMarks withSliderMarks
withReset withReset
isDisabled={!isImageToImageEnabled}
sliderNumberInputProps={{ max: inputMax }} sliderNumberInputProps={{ max: inputMax }}
/> />
); );

View File

@ -1,49 +0,0 @@
import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAISwitch from 'common/components/IAISwitch';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { shouldShowImageParametersChanged } from 'features/ui/store/uiSlice';
import { ChangeEvent } from 'react';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[uiSelector, generationSelector],
(ui, generation) => {
const { isImageToImageEnabled } = generation;
const { shouldShowImageParameters } = ui;
return {
isImageToImageEnabled,
shouldShowImageParameters,
};
},
defaultSelectorOptions
);
export default function ImageToImageToggle() {
const { shouldShowImageParameters } = useAppSelector(selector);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
dispatch(shouldShowImageParametersChanged(e.target.checked));
return (
<Flex py={1.5} px={4} borderRadius={4}>
<IAISwitch
label={t('parameters.initialImage')}
isChecked={shouldShowImageParameters}
width="full"
onChange={handleChange}
justifyContent="space-between"
formLabelProps={{
fontWeight: 400,
}}
/>
</Flex>
);
}

View File

@ -5,28 +5,27 @@ import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
import { useGetUrl } from 'common/util/getUrl'; import { useGetUrl } from 'common/util/getUrl';
import { clearInitialImage } from 'features/parameters/store/generationSlice'; import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { addToast } from 'features/system/store/systemSlice'; import { addToast } from 'features/system/store/systemSlice';
import { isEqual } from 'lodash-es';
import { DragEvent, useCallback, useState } from 'react'; import { DragEvent, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ImageType } from 'services/api'; import { ImageType } from 'services/api';
import ImageToImageOverlay from 'common/components/ImageToImageOverlay'; import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
import { generationSelector } from 'features/parameters/store/generationSelectors'; import { generationSelector } from 'features/parameters/store/generationSelectors';
import { initialImageSelected } from 'features/parameters/store/actions'; import { initialImageSelected } from 'features/parameters/store/actions';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
const selector = createSelector( const selector = createSelector(
[generationSelector], [generationSelector],
(generation) => { (generation) => {
const { initialImage, isImageToImageEnabled } = generation; const { initialImage } = generation;
return { return {
initialImage, initialImage,
isImageToImageEnabled,
}; };
}, },
{ memoizeOptions: { resultEqualityCheck: isEqual } } defaultSelectorOptions
); );
const InitialImagePreview = () => { const InitialImagePreview = () => {
const { initialImage, isImageToImageEnabled } = useAppSelector(selector); const { initialImage } = useAppSelector(selector);
const { getUrl } = useGetUrl(); const { getUrl } = useGetUrl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
@ -73,8 +72,6 @@ const InitialImagePreview = () => {
sx={{ sx={{
height: 'full', height: 'full',
width: 'full', width: 'full',
opacity: isImageToImageEnabled ? 1 : 0.5,
filter: isImageToImageEnabled ? 'none' : 'auto',
blur: '5px', blur: '5px',
position: 'relative', position: 'relative',
alignItems: 'center', alignItems: 'center',
@ -107,26 +104,6 @@ const InitialImagePreview = () => {
)} )}
{!initialImage?.url && <SelectImagePlaceholder />} {!initialImage?.url && <SelectImagePlaceholder />}
</Flex> </Flex>
{/* {!isImageToImageEnabled && (
<Flex
sx={{
w: 'full',
h: 'full',
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
}}
>
<Text
fontWeight={500}
fontSize="md"
userSelect="none"
color="base.200"
>
Image to Image is Disabled
</Text>
</Flex>
)} */}
</Flex> </Flex>
); );
}; };

View File

@ -35,7 +35,6 @@ export interface GenerationState {
shouldUseSymmetry: boolean; shouldUseSymmetry: boolean;
horizontalSymmetrySteps: number; horizontalSymmetrySteps: number;
verticalSymmetrySteps: number; verticalSymmetrySteps: number;
isImageToImageEnabled: boolean;
model: string; model: string;
shouldUseSeamless: boolean; shouldUseSeamless: boolean;
seamlessXAxis: boolean; seamlessXAxis: boolean;
@ -71,7 +70,6 @@ export const initialGenerationState: GenerationState = {
shouldUseSymmetry: false, shouldUseSymmetry: false,
horizontalSymmetrySteps: 0, horizontalSymmetrySteps: 0,
verticalSymmetrySteps: 0, verticalSymmetrySteps: 0,
isImageToImageEnabled: false,
model: '', model: '',
shouldUseSeamless: false, shouldUseSeamless: false,
seamlessXAxis: true, seamlessXAxis: true,
@ -233,10 +231,6 @@ export const generationSlice = createSlice({
}, },
initialImageChanged: (state, action: PayloadAction<InvokeAI.Image>) => { initialImageChanged: (state, action: PayloadAction<InvokeAI.Image>) => {
state.initialImage = action.payload; state.initialImage = action.payload;
state.isImageToImageEnabled = true;
},
isImageToImageEnabledChanged: (state, action: PayloadAction<boolean>) => {
state.isImageToImageEnabled = action.payload;
}, },
modelSelected: (state, action: PayloadAction<string>) => { modelSelected: (state, action: PayloadAction<string>) => {
state.model = action.payload; state.model = action.payload;
@ -249,9 +243,6 @@ export const {
clearInitialImage, clearInitialImage,
resetParametersState, resetParametersState,
resetSeed, resetSeed,
setAllImageToImageParameters,
setAllParameters,
setAllTextToImageParameters,
setCfgScale, setCfgScale,
setHeight, setHeight,
setImg2imgStrength, setImg2imgStrength,
@ -282,7 +273,6 @@ export const {
setHorizontalSymmetrySteps, setHorizontalSymmetrySteps,
setVerticalSymmetrySteps, setVerticalSymmetrySteps,
initialImageChanged, initialImageChanged,
isImageToImageEnabledChanged,
modelSelected, modelSelected,
setShouldUseNoiseSettings, setShouldUseNoiseSettings,
setSeamless, setSeamless,