mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Fixes save to gallery including empty area, adds download and copy image
This commit is contained in:
parent
635e7da05d
commit
19322fc1ec
@ -165,20 +165,16 @@ class InvokeAIWebServer:
|
|||||||
|
|
||||||
pil_image = Image.open(file_path)
|
pil_image = Image.open(file_path)
|
||||||
|
|
||||||
# visible_image_bbox = pil_image.getbbox()
|
if "cropVisible" in data and data["cropVisible"] == True:
|
||||||
# pil_image = pil_image.crop(visible_image_bbox)
|
visible_image_bbox = pil_image.getbbox()
|
||||||
# pil_image.save(file_path)
|
pil_image = pil_image.crop(visible_image_bbox)
|
||||||
# if "cropVisible" in data and data["cropVisible"] == True:
|
pil_image.save(file_path)
|
||||||
# visible_image_bbox = pil_image.getbbox()
|
|
||||||
# pil_image = pil_image.crop(visible_image_bbox)
|
|
||||||
# pil_image.save(file_path)
|
|
||||||
|
|
||||||
(width, height) = pil_image.size
|
(width, height) = pil_image.size
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
"url": self.get_url_from_image_path(file_path),
|
"url": self.get_url_from_image_path(file_path),
|
||||||
"mtime": mtime,
|
"mtime": mtime,
|
||||||
# "bbox": visible_image_bbox,
|
|
||||||
"width": width,
|
"width": width,
|
||||||
"height": height,
|
"height": height,
|
||||||
}
|
}
|
||||||
@ -607,6 +603,12 @@ class InvokeAIWebServer:
|
|||||||
|
|
||||||
actual_generation_mode = generation_parameters["generation_mode"]
|
actual_generation_mode = generation_parameters["generation_mode"]
|
||||||
original_bounding_box = None
|
original_bounding_box = None
|
||||||
|
|
||||||
|
progress = Progress(generation_parameters=generation_parameters)
|
||||||
|
|
||||||
|
self.socketio.emit("progressUpdate", progress.to_formatted_dict())
|
||||||
|
eventlet.sleep(0)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
TODO:
|
TODO:
|
||||||
If a result image is used as an init image, and then deleted, we will want to be
|
If a result image is used as an init image, and then deleted, we will want to be
|
||||||
@ -658,8 +660,6 @@ class InvokeAIWebServer:
|
|||||||
initial_image, mask_image
|
initial_image, mask_image
|
||||||
)
|
)
|
||||||
|
|
||||||
print(initial_image, mask_image)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Apply the mask to the init image, creating a "mask" image with
|
Apply the mask to the init image, creating a "mask" image with
|
||||||
transparency where inpainting should occur. This is the kind of
|
transparency where inpainting should occur. This is the kind of
|
||||||
@ -708,11 +708,6 @@ class InvokeAIWebServer:
|
|||||||
init_img_path = self.get_image_path_from_url(init_img_url)
|
init_img_path = self.get_image_path_from_url(init_img_url)
|
||||||
generation_parameters["init_img"] = init_img_path
|
generation_parameters["init_img"] = init_img_path
|
||||||
|
|
||||||
progress = Progress(generation_parameters=generation_parameters)
|
|
||||||
|
|
||||||
self.socketio.emit("progressUpdate", progress.to_formatted_dict())
|
|
||||||
eventlet.sleep(0)
|
|
||||||
|
|
||||||
def image_progress(sample, step):
|
def image_progress(sample, step):
|
||||||
if self.canceled.is_set():
|
if self.canceled.is_set():
|
||||||
raise CanceledException
|
raise CanceledException
|
||||||
@ -968,6 +963,8 @@ class InvokeAIWebServer:
|
|||||||
|
|
||||||
progress.set_current_iteration(progress.current_iteration + 1)
|
progress.set_current_iteration(progress.current_iteration + 1)
|
||||||
|
|
||||||
|
print(generation_parameters)
|
||||||
|
|
||||||
self.generate.prompt2image(
|
self.generate.prompt2image(
|
||||||
**generation_parameters,
|
**generation_parameters,
|
||||||
step_callback=image_progress,
|
step_callback=image_progress,
|
||||||
|
@ -78,7 +78,6 @@ const IAICanvasOutpaintingControls = () => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
mergeAndUploadCanvas({
|
mergeAndUploadCanvas({
|
||||||
canvasImageLayerRef,
|
canvasImageLayerRef,
|
||||||
saveToGallery: false,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@ -89,7 +88,11 @@ const IAICanvasOutpaintingControls = () => {
|
|||||||
icon={<FaSave />}
|
icon={<FaSave />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
mergeAndUploadCanvas({ canvasImageLayerRef, saveToGallery: true })
|
mergeAndUploadCanvas({
|
||||||
|
canvasImageLayerRef,
|
||||||
|
cropVisible: true,
|
||||||
|
saveToGallery: true,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -97,11 +100,29 @@ const IAICanvasOutpaintingControls = () => {
|
|||||||
aria-label="Copy Selection"
|
aria-label="Copy Selection"
|
||||||
tooltip="Copy Selection"
|
tooltip="Copy Selection"
|
||||||
icon={<FaCopy />}
|
icon={<FaCopy />}
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(
|
||||||
|
mergeAndUploadCanvas({
|
||||||
|
canvasImageLayerRef,
|
||||||
|
cropVisible: true,
|
||||||
|
copyAfterSaving: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
aria-label="Download Selection"
|
aria-label="Download Selection"
|
||||||
tooltip="Download Selection"
|
tooltip="Download Selection"
|
||||||
icon={<FaDownload />}
|
icon={<FaDownload />}
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(
|
||||||
|
mergeAndUploadCanvas({
|
||||||
|
canvasImageLayerRef,
|
||||||
|
cropVisible: true,
|
||||||
|
downloadAfterSaving: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
<ButtonGroup isAttached>
|
<ButtonGroup isAttached>
|
||||||
|
34
frontend/src/features/canvas/util/copyImage.ts
Normal file
34
frontend/src/features/canvas/util/copyImage.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copies an image to the clipboard by drawing it to a canvas and then
|
||||||
|
* calling toBlob() on the canvas.
|
||||||
|
*/
|
||||||
|
const copyImage = (url: string, width: number, height: number) => {
|
||||||
|
const imageElement = document.createElement('img');
|
||||||
|
|
||||||
|
imageElement.addEventListener('load', () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
if (!context) return;
|
||||||
|
|
||||||
|
context.drawImage(imageElement, 0, 0);
|
||||||
|
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
blob &&
|
||||||
|
navigator.clipboard.write([
|
||||||
|
new ClipboardItem({
|
||||||
|
[blob.type]: blob,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.remove();
|
||||||
|
imageElement.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
imageElement.src = url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default copyImage;
|
14
frontend/src/features/canvas/util/downloadFile.ts
Normal file
14
frontend/src/features/canvas/util/downloadFile.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Downloads a file, given its URL.
|
||||||
|
*/
|
||||||
|
const downloadFile = (url: string) => {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = '';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
a.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default downloadFile;
|
@ -5,17 +5,28 @@ import { MutableRefObject } from 'react';
|
|||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/invokeai';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import layerToDataURL from './layerToDataURL';
|
import layerToDataURL from './layerToDataURL';
|
||||||
|
import downloadFile from './downloadFile';
|
||||||
|
import copyImage from './copyImage';
|
||||||
|
|
||||||
export const mergeAndUploadCanvas = createAsyncThunk(
|
export const mergeAndUploadCanvas = createAsyncThunk(
|
||||||
'canvas/mergeAndUploadCanvas',
|
'canvas/mergeAndUploadCanvas',
|
||||||
async (
|
async (
|
||||||
args: {
|
args: {
|
||||||
canvasImageLayerRef: MutableRefObject<Konva.Layer | null>;
|
canvasImageLayerRef: MutableRefObject<Konva.Layer | null>;
|
||||||
saveToGallery: boolean;
|
cropVisible?: boolean;
|
||||||
|
saveToGallery?: boolean;
|
||||||
|
downloadAfterSaving?: boolean;
|
||||||
|
copyAfterSaving?: boolean;
|
||||||
},
|
},
|
||||||
thunkAPI
|
thunkAPI
|
||||||
) => {
|
) => {
|
||||||
const { canvasImageLayerRef, saveToGallery } = args;
|
const {
|
||||||
|
canvasImageLayerRef,
|
||||||
|
saveToGallery,
|
||||||
|
downloadAfterSaving,
|
||||||
|
cropVisible,
|
||||||
|
copyAfterSaving,
|
||||||
|
} = args;
|
||||||
|
|
||||||
const { getState } = thunkAPI;
|
const { getState } = thunkAPI;
|
||||||
|
|
||||||
@ -40,7 +51,7 @@ export const mergeAndUploadCanvas = createAsyncThunk(
|
|||||||
dataURL,
|
dataURL,
|
||||||
filename: 'merged_canvas.png',
|
filename: 'merged_canvas.png',
|
||||||
kind: saveToGallery ? 'result' : 'temp',
|
kind: saveToGallery ? 'result' : 'temp',
|
||||||
cropVisible: saveToGallery,
|
cropVisible,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -52,12 +63,15 @@ export const mergeAndUploadCanvas = createAsyncThunk(
|
|||||||
const { url, mtime, width, height } =
|
const { url, mtime, width, height } =
|
||||||
(await response.json()) as InvokeAI.ImageUploadResponse;
|
(await response.json()) as InvokeAI.ImageUploadResponse;
|
||||||
|
|
||||||
// const newBoundingBox = {
|
if (downloadAfterSaving) {
|
||||||
// x: bbox[0],
|
downloadFile(url);
|
||||||
// y: bbox[1],
|
return;
|
||||||
// width: bbox[2],
|
}
|
||||||
// height: bbox[3],
|
|
||||||
// };
|
if (copyAfterSaving) {
|
||||||
|
copyImage(url, width, height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newImage: InvokeAI.Image = {
|
const newImage: InvokeAI.Image = {
|
||||||
uuid: uuidv4(),
|
uuid: uuidv4(),
|
||||||
@ -72,7 +86,6 @@ export const mergeAndUploadCanvas = createAsyncThunk(
|
|||||||
image: newImage,
|
image: newImage,
|
||||||
kind: saveToGallery ? 'merged_canvas' : 'temp_merged_canvas',
|
kind: saveToGallery ? 'merged_canvas' : 'temp_merged_canvas',
|
||||||
originalBoundingBox,
|
originalBoundingBox,
|
||||||
// newBoundingBox,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -29,7 +29,6 @@ export const uploadImage = createAsyncThunk(
|
|||||||
kind: 'init',
|
kind: 'init',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// formData.append('kind', 'init');
|
|
||||||
|
|
||||||
const response = await fetch(window.location.origin + '/upload', {
|
const response = await fetch(window.location.origin + '/upload', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -39,13 +38,6 @@ export const uploadImage = createAsyncThunk(
|
|||||||
const { url, mtime, width, height } =
|
const { url, mtime, width, height } =
|
||||||
(await response.json()) as InvokeAI.ImageUploadResponse;
|
(await response.json()) as InvokeAI.ImageUploadResponse;
|
||||||
|
|
||||||
// const newBoundingBox = {
|
|
||||||
// x: bbox[0],
|
|
||||||
// y: bbox[1],
|
|
||||||
// width: bbox[2],
|
|
||||||
// height: bbox[3],
|
|
||||||
// };
|
|
||||||
|
|
||||||
const newImage: InvokeAI.Image = {
|
const newImage: InvokeAI.Image = {
|
||||||
uuid: uuidv4(),
|
uuid: uuidv4(),
|
||||||
url,
|
url,
|
||||||
|
Loading…
Reference in New Issue
Block a user