diff --git a/invokeai/app/api/routers/images.py b/invokeai/app/api/routers/images.py index 55f1a2f036..453c114a28 100644 --- a/invokeai/app/api/routers/images.py +++ b/invokeai/app/api/routers/images.py @@ -23,6 +23,16 @@ async def get_image( filename = ApiDependencies.invoker.services.images.get_path(image_type, image_name) return FileResponse(filename) +@images_router.get("/{image_type}/thumbnails/{image_name}", operation_id="get_thumbnail") +async def get_thumbnail( + image_type: ImageType = Path(description="The type of image to get"), + image_name: str = Path(description="The name of the image to get"), +): + """Gets a thumbnail""" + # TODO: This is not really secure at all. At least make sure only output results are served + filename = ApiDependencies.invoker.services.images.get_path(image_type, 'thumbnails/' + image_name) + return FileResponse(filename) + @images_router.post( "/uploads/", diff --git a/invokeai/app/services/image_storage.py b/invokeai/app/services/image_storage.py index ad0ff23f14..c80a4bfb31 100644 --- a/invokeai/app/services/image_storage.py +++ b/invokeai/app/services/image_storage.py @@ -9,6 +9,7 @@ from queue import Queue from typing import Dict from PIL.Image import Image +from invokeai.app.util.save_thumbnail import save_thumbnail from invokeai.backend.image_util import PngWriter @@ -66,6 +67,9 @@ class DiskImageStorage(ImageStorageBase): Path(os.path.join(output_folder, image_type)).mkdir( parents=True, exist_ok=True ) + Path(os.path.join(output_folder, image_type, "thumbnails")).mkdir( + parents=True, exist_ok=True + ) def get(self, image_type: ImageType, image_name: str) -> Image: image_path = self.get_path(image_type, image_name) @@ -87,7 +91,11 @@ class DiskImageStorage(ImageStorageBase): self.__pngWriter.save_image_and_prompt_to_png( image, "", image_subpath, None ) # TODO: just pass full path to png writer - + save_thumbnail( + image=image, + filename=image_name, + path=os.path.join(self.__output_folder, image_type, "thumbnails"), + ) image_path = self.get_path(image_type, image_name) self.__set_cache(image_path, image) diff --git a/invokeai/app/util/save_thumbnail.py b/invokeai/app/util/save_thumbnail.py new file mode 100644 index 0000000000..e817e2e95c --- /dev/null +++ b/invokeai/app/util/save_thumbnail.py @@ -0,0 +1,28 @@ +import os +from PIL import Image + + +def save_thumbnail( + image: Image.Image, + filename: str, + path: str, + size: int = 256, +) -> str: + """ + Saves a thumbnail of an image, returning its path. + """ + base_filename = os.path.splitext(filename)[0] + thumbnail_path = os.path.join(path, base_filename + ".webp") + + if os.path.exists(thumbnail_path): + return thumbnail_path + + thumbnail_width = size + thumbnail_height = round(size * (image.height / image.width)) + + image_copy = image.copy() + image_copy.thumbnail(size=(thumbnail_width, thumbnail_height)) + + image_copy.save(thumbnail_path, "WEBP") + + return thumbnail_path