diff --git a/README.md b/README.md index 48cd538..a1a1c78 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,17 @@ with prompt-based masking.

+Want just quickly have some fun? Try `--suprise-me` to apply some pre-defined edits. +```bash +>> aimg edit --gif --suprise-me pearl_girl.jpg +>> aimg edit --gif --suprise-me mona-lisa.jpg +>> aimg edit --gif --suprise-me luke.jpg +>> aimg edit --gif --suprise-me spock.jpg +``` +
+
+
+ ### Prompt Based Masking [by clipseg](https://github.com/timojl/clipseg) Specify advanced text based masks using boolean logic and strength modifiers. @@ -274,6 +285,8 @@ docker run -it --gpus all -v $HOME/.cache/huggingface:/root/.cache/huggingface - **8.0.0** - feature: 🎉 edit images with instructions alone! +- feature: when editing an image add `--gif` to create a comparision gif +- feature: `aimg edit --suprise-me --gif my-image.jpg` for some fun pre-programmed edits - feature: prune-ckpt command also removes the non-ema weights **7.6.0** diff --git a/assets/gg-bridge-suprise.gif b/assets/gg-bridge-suprise.gif new file mode 100644 index 0000000..849dcbe Binary files /dev/null and b/assets/gg-bridge-suprise.gif differ diff --git a/assets/gg-bridge.jpg b/assets/gg-bridge.jpg new file mode 100644 index 0000000..94be687 Binary files /dev/null and b/assets/gg-bridge.jpg differ diff --git a/assets/girl_with_a_pearl_earring_suprise.gif b/assets/girl_with_a_pearl_earring_suprise.gif new file mode 100644 index 0000000..55f0913 Binary files /dev/null and b/assets/girl_with_a_pearl_earring_suprise.gif differ diff --git a/assets/luke-suprise.gif b/assets/luke-suprise.gif new file mode 100644 index 0000000..302e676 Binary files /dev/null and b/assets/luke-suprise.gif differ diff --git a/assets/luke.jpg b/assets/luke.jpg new file mode 100644 index 0000000..33da95c Binary files /dev/null and b/assets/luke.jpg differ diff --git a/assets/mona-lisa-suprise.gif b/assets/mona-lisa-suprise.gif new file mode 100644 index 0000000..209452d Binary files /dev/null and b/assets/mona-lisa-suprise.gif differ diff --git a/assets/shire-suprise.gif b/assets/shire-suprise.gif new file mode 100644 index 0000000..512294b Binary files /dev/null and b/assets/shire-suprise.gif differ diff --git a/assets/shire.jpg b/assets/shire.jpg new file mode 100644 index 0000000..0e407c1 Binary files /dev/null and b/assets/shire.jpg differ diff --git a/assets/spock-suprise.gif b/assets/spock-suprise.gif new file mode 100644 index 0000000..e41ec4f Binary files /dev/null and b/assets/spock-suprise.gif differ diff --git a/assets/spock.jpg b/assets/spock.jpg new file mode 100644 index 0000000..7e676b8 Binary files /dev/null and b/assets/spock.jpg differ diff --git a/imaginairy/api.py b/imaginairy/api.py index 3a0b335..68e684f 100755 --- a/imaginairy/api.py +++ b/imaginairy/api.py @@ -13,7 +13,11 @@ from imaginairy.enhancers.clip_masking import get_img_mask from imaginairy.enhancers.describe_image_blip import generate_caption from imaginairy.enhancers.face_restoration_codeformer import enhance_faces from imaginairy.enhancers.upscale_realesrgan import upscale_image -from imaginairy.img_utils import pillow_fit_image_within, pillow_img_to_torch_image +from imaginairy.img_utils import ( + make_gif_image, + pillow_fit_image_within, + pillow_img_to_torch_image, +) from imaginairy.log_utils import ( ImageLoggingContext, log_conditioning, @@ -55,6 +59,8 @@ def imagine_image_files( record_step_images=False, output_file_extension="jpg", print_caption=False, + make_comparison_gif=False, + return_filename_type="generated", ): generated_imgs_path = os.path.join(outdir, "generated") os.makedirs(generated_imgs_path, exist_ok=True) @@ -74,6 +80,7 @@ def imagine_image_files( draw.text((10, 10), str(description)) img.save(destination) + result_filenames = [] for result in imagine( prompts, precision=precision, @@ -97,9 +104,25 @@ def imagine_image_files( ) result.save(filepath, image_type=image_type) logger.info(f"🖼 [{image_type}] saved to: {filepath}") + if image_type == return_filename_type: + result_filenames.append(filepath) + if make_comparison_gif and prompt.init_image: + subpath = os.path.join(outdir, "gif") + os.makedirs(subpath, exist_ok=True) + filepath = os.path.join(subpath, f"{basefilename}.gif") + resized_init_image = pillow_fit_image_within( + prompt.init_image, prompt.width, prompt.height + ) + make_gif_image( + filepath, + imgs=[result.images["generated"], resized_init_image], + duration=1750, + ) base_count += 1 del result + return result_filenames + def imagine( prompts, diff --git a/imaginairy/cmds.py b/imaginairy/cmds.py index 2701d55..07f50de 100644 --- a/imaginairy/cmds.py +++ b/imaginairy/cmds.py @@ -11,6 +11,7 @@ from imaginairy.enhancers.prompt_expansion import expand_prompts from imaginairy.log_utils import configure_logging from imaginairy.samplers import SAMPLER_TYPE_OPTIONS from imaginairy.schema import ImaginePrompt +from imaginairy.suprise_me import create_suprise_me_images from imaginairy.train import train_diffusion_model from imaginairy.training_tools.image_prep import ( create_class_images, @@ -220,6 +221,9 @@ logger = logging.getLogger(__name__) is_flag=True, help="Print the version and exit.", ) +@click.option( + "--gif", "make_gif", default=False, is_flag=True, help="Generate a gif of the edit." +) @click.pass_context def imagine_cmd( ctx, @@ -255,6 +259,7 @@ def imagine_cmd( model_config_path, prompt_library_path, version, # noqa + make_gif, ): """Have the AI generate images. alias:imagine.""" return _imagine_cmd( @@ -291,6 +296,7 @@ def imagine_cmd( model_config_path, prompt_library_path, version, # noqa + make_gif ) @@ -487,8 +493,22 @@ def imagine_cmd( is_flag=True, help="Print the version and exit.", ) +@click.option( + "--gif", + "make_gif", + default=False, + is_flag=True, + help="Generate a gif comparing the original image to the modified one.", +) +@click.option( + "--suprise-me", + "suprise_me", + default=False, + is_flag=True, + help="make some fun edits to the provided image", +) @click.pass_context -def edit_image( +def edit_image( # noqa ctx, init_image, prompt_texts, @@ -521,8 +541,21 @@ def edit_image( model_config_path, prompt_library_path, version, # noqa + make_gif, + suprise_me, ): init_image_strength = 1 + if suprise_me and prompt_texts: + raise ValueError("Cannot use suprise_me and prompt_texts together") + + if suprise_me: + if quiet: + log_level = "ERROR" + configure_logging(log_level) + create_suprise_me_images(init_image, outdir=outdir, make_gif=make_gif) + + return + return _imagine_cmd( ctx, prompt_texts, @@ -557,6 +590,7 @@ def edit_image( model_config_path, prompt_library_path, version, # noqa + make_gif, ) @@ -593,7 +627,8 @@ def _imagine_cmd( model_weights_path, model_config_path, prompt_library_path, - version, # noqa + version=False, # noqa + make_gif=False, ): """Have the AI generate images. alias:imagine.""" if ctx.invoked_subcommand is not None: @@ -670,6 +705,7 @@ def _imagine_cmd( output_file_extension="jpg", print_caption=caption, precision=precision, + make_comparison_gif=make_gif, ) diff --git a/data/DejaVuSans.ttf b/imaginairy/data/DejaVuSans.ttf similarity index 100% rename from data/DejaVuSans.ttf rename to imaginairy/data/DejaVuSans.ttf diff --git a/imaginairy/img_utils.py b/imaginairy/img_utils.py index 165a0c7..9cb9bcb 100644 --- a/imaginairy/img_utils.py +++ b/imaginairy/img_utils.py @@ -70,3 +70,15 @@ def pillow_img_to_model_latent(model, img, batch_size=1, half=True): model.encode_first_stage(init_image.half()) ) return model.get_first_stage_encoding(model.encode_first_stage(init_image)) + + +def make_gif_image(filepath, imgs, duration=1000, loop=0): + + imgs[0].save( + filepath, + save_all=True, + append_images=imgs[1:], + duration=duration, + loop=loop, + optimize=False, + ) diff --git a/imaginairy/modules/diffusion/ddpm.py b/imaginairy/modules/diffusion/ddpm.py index 18f0578..340be2a 100644 --- a/imaginairy/modules/diffusion/ddpm.py +++ b/imaginairy/modules/diffusion/ddpm.py @@ -32,6 +32,7 @@ from imaginairy.modules.diffusion.util import ( ) from imaginairy.modules.distributions import DiagonalGaussianDistribution from imaginairy.modules.ema import LitEma +from imaginairy.paths import PKG_ROOT from imaginairy.samplers.kdiff import DPMPP2MSampler from imaginairy.utils import instantiate_from_config @@ -47,7 +48,7 @@ def log_txt_as_img(wh, xc, size=10): for bi in range(b): txt = Image.new("RGB", wh, color="white") draw = ImageDraw.Draw(txt) - font = ImageFont.truetype("data/DejaVuSans.ttf", size=size) + font = ImageFont.truetype(f"{PKG_ROOT}/data/DejaVuSans.ttf", size=size) nc = int(40 * (wh[0] / 256)) lines = "\n".join( xc[bi][start : start + nc] for start in range(0, len(xc[bi]), nc) diff --git a/imaginairy/suprise_me.py b/imaginairy/suprise_me.py new file mode 100644 index 0000000..79dc26f --- /dev/null +++ b/imaginairy/suprise_me.py @@ -0,0 +1,128 @@ +""" + + +aimg. +""" + +import os.path + +from PIL import ImageDraw, ImageFont + +from imaginairy import ImaginePrompt, LazyLoadingImage, imagine_image_files +from imaginairy.enhancers.facecrop import detect_faces +from imaginairy.img_utils import make_gif_image, pillow_fit_image_within +from imaginairy.paths import PKG_ROOT + +generic_prompts = [ + ("make it anime style", 18, ""), + ("make it pen and ink style", 20, ""), + ("make it a thomas kinkade painting", 20, ""), + ("make it pixar style", 20, ""), + ("make it look like a marble statue", 15, ""), + ("make it look like a golden statue", 15, ""), + ("make it look like a snowstorm", 20, ""), + ("make it night", 15, ""), + ("make it a sunny day", 15, ""), + ("put fireworks in the background", 15, ""), + ("make it in a forest", 15, ""), +] + +person_prompts = [ + ("make the person close their eyes", 10, ""), + ("make the person wear clown makeup", 10, ""), + ("make the person a cyborg", 14, ""), + ("make the person wear shiny metal clothes", 14, ""), + ("make the person wear a tie-dye shirt", 7.5, ""), + ("make the person wear a suit", 7.5, ""), + ("make the person bald", 7.5, ""), + ("change the hair to pink", 7.5, ""), + ( + "make it a color professional photo headshot. Canon EOS, sharp focus", + 10, + "old, ugly", + ), + ("make the face smiling", 5, ""), + # ("make the person sad", 20, ""), + # ("make the person angry", 20, ""), + ("make the person look like a celebrity", 10, ""), + ("make the person younger", 10, ""), + ("make the person older", 10, ""), + ("make the person a disney cartoon character", 7.5, ""), +] + + +def suprise_me_prompts(img, person=None): + prompts = [] + if isinstance(img, str): + if img.startswith("http"): + img = LazyLoadingImage(url=img) + else: + img = LazyLoadingImage(filepath=img) + + if person is None: + person = bool(detect_faces(img)) + + if person: + for prompt_text, strength, neg_prompt_text in person_prompts: + prompts.append( + ImaginePrompt( + prompt_text, + init_image=img, + prompt_strength=strength, + negative_prompt=neg_prompt_text, + model="edit", + steps=20, + ) + ) + + for prompt_text, strength, neg_prompt_text in generic_prompts: + prompts.append( + ImaginePrompt( + prompt_text, + init_image=img, + prompt_strength=strength, + negative_prompt=neg_prompt_text, + model="edit", + steps=20, + ) + ) + + return prompts + + +def create_suprise_me_images(img, outdir, person=None, make_gif=True): + if isinstance(img, str): + if img.startswith("http"): + img = LazyLoadingImage(url=img) + else: + img = LazyLoadingImage(filepath=img) + prompts = suprise_me_prompts(img, person=person) + generated_filenames = imagine_image_files( + prompts, + outdir=outdir, + record_step_images=False, + output_file_extension="jpg", + print_caption=False, + make_comparison_gif=make_gif, + ) + if make_gif: + imgs_path = os.path.join(outdir, "compilations") + os.makedirs(imgs_path, exist_ok=True) + base_count = len(os.listdir(imgs_path)) + new_filename = os.path.join(imgs_path, f"suprise_me_{base_count:03d}.gif") + simg = pillow_fit_image_within(img, prompts[0].width, prompts[0].height) + gif_imgs = [simg] + for prompt, filename in zip(prompts, generated_filenames): + gen_img = LazyLoadingImage(filepath=filename) + draw = ImageDraw.Draw(gen_img) + + font_size = 16 + font = ImageFont.truetype(f"{PKG_ROOT}/data/DejaVuSans.ttf", font_size) + + x = 15 + y = 485 + + draw.text((x, y), prompt.prompt_text, font=font, fill=(255, 255, 255)) + gif_imgs.append(gen_img) + + make_gif_image(new_filename, gif_imgs) diff --git a/setup.py b/setup.py index f61d597..32428d6 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ setup( package_data={ "imaginairy": [ "configs/*.yaml", + "data/*.*", "enhancers/phraselists/*.txt", "vendored/clip/*.txt.gz", "vendored/clipseg/*.pth",