You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
imaginAIry/imaginairy/cmds.py

272 lines
7.3 KiB
Python

import logging
import math
import click
from click_shell import shell
from imaginairy import LazyLoadingImage, generate_caption
from imaginairy.api import imagine_image_files
from imaginairy.enhancers.prompt_expansion import expand_prompts
from imaginairy.log_utils import configure_logging
from imaginairy.samplers.base import SAMPLER_TYPE_OPTIONS
from imaginairy.schema import ImaginePrompt
logger = logging.getLogger(__name__)
@click.command()
@click.argument("prompt_texts", nargs=-1)
@click.option(
"--prompt-strength",
default=7.5,
show_default=True,
help="How closely to follow the prompt. Image looks unnatural at higher values",
)
@click.option(
"--init-image",
help="Starting image. filepath or url",
)
@click.option(
"--init-image-strength",
default=0.6,
show_default=True,
help="Starting image.",
)
@click.option("--outdir", default="./outputs", help="where to write results to")
@click.option(
"-r",
"--repeats",
default=1,
type=int,
help="How many times to repeat the renders. If you provide two prompts and --repeat=3 then six images will be generated",
)
@click.option(
"-h",
"--height",
default=512,
type=int,
help="image height. should be multiple of 64",
)
@click.option(
"-w", "--width", default=512, type=int, help="image width. should be multiple of 64"
)
@click.option(
"--steps",
default=15,
type=int,
show_default=True,
help="How many diffusion steps to run. More steps, more detail, but with diminishing returns",
)
@click.option(
"--seed",
default=None,
type=int,
help="What seed to use for randomness. Allows reproducible image renders",
)
@click.option("--upscale", is_flag=True)
@click.option("--fix-faces", is_flag=True)
@click.option(
"--fix-faces-fidelity",
default=None,
help="How faithful to the original should face enhancement be. 1 = best fidelity, 0 = best looking face",
)
@click.option(
"--sampler-type",
"--sampler",
default="k_dpmpp_2m",
type=click.Choice(SAMPLER_TYPE_OPTIONS),
help="What sampling strategy to use",
)
@click.option(
"--log-level",
default="INFO",
type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR"]),
help="What level of logs to show.",
)
@click.option(
"--quiet",
"-q",
is_flag=True,
help="Suppress logs. Alias of `--log-level ERROR`",
)
@click.option(
"--show-work",
default=False,
is_flag=True,
help="Output a debug images to `steps` folder.",
)
@click.option(
"--tile",
is_flag=True,
help="Any images rendered will be tileable.",
)
@click.option(
"--mask-image",
help="A mask to use for inpainting. White gets painted, Black is left alone.",
)
@click.option(
"--mask-prompt",
help=(
"Describe what you want masked and the AI will mask it for you. "
"You can describe complex masks with AND, OR, NOT keywords and parentheses. "
"The strength of each mask can be modified with {*1.5} notation. \n\n"
"Examples: \n"
"car AND (wheels{*1.1} OR trunk OR engine OR windows OR headlights) AND NOT (truck OR headlights){*10}\n"
"fruit|fruit stem"
),
)
@click.option(
"--mask-mode",
default="replace",
type=click.Choice(["keep", "replace"]),
help="Should we replace the masked area or keep it?",
)
@click.option(
"--mask-modify-original",
default=True,
is_flag=True,
help="After the inpainting is done, apply the changes to a copy of the original image",
)
@click.option(
"--caption",
default=False,
is_flag=True,
help="Generate a text description of the generated image",
)
@click.option(
"--precision",
help="evaluate at this precision",
type=click.Choice(["full", "autocast"]),
default="autocast",
)
@click.option(
"--model-weights-path",
"--model",
help="Model to use. Should be one of SD-1.4, SD-1.5, or a path to custom weights. Defaults to SD-1.5",
default=None,
)
@click.option(
"--prompt-library-path",
help="path to folder containing phaselists in txt files. use txt filename in prompt: {_filename_}",
type=click.Path(exists=True),
default=None,
multiple=True,
)
@click.pass_context
def imagine_cmd(
ctx,
prompt_texts,
prompt_strength,
init_image,
init_image_strength,
outdir,
repeats,
height,
width,
steps,
seed,
upscale,
fix_faces,
fix_faces_fidelity,
sampler_type,
log_level,
quiet,
show_work,
tile,
mask_image,
mask_prompt,
mask_mode,
mask_modify_original,
caption,
precision,
model_weights_path,
prompt_library_path,
):
"""Have the AI generate images. alias:imagine"""
if ctx.invoked_subcommand is not None:
return
if quiet:
log_level = "ERROR"
configure_logging(log_level)
total_image_count = len(prompt_texts) * repeats
logger.info(
f"🤖🧠 imaginAIry received {len(prompt_texts)} prompt(s) and will repeat them {repeats} times to create {total_image_count} images."
)
if init_image and init_image.startswith("http"):
init_image = LazyLoadingImage(url=init_image)
if mask_image and mask_image.startswith("http"):
mask_image = LazyLoadingImage(url=mask_image)
if fix_faces_fidelity is not None:
fix_faces_fidelity = float(fix_faces_fidelity)
prompts = []
prompt_expanding_iterators = {}
for _ in range(repeats):
for prompt_text in prompt_texts:
if prompt_text not in prompt_expanding_iterators:
prompt_expanding_iterators[prompt_text] = expand_prompts(
n=math.inf,
prompt_text=prompt_text,
prompt_library_paths=prompt_library_path,
)
prompt_iterator = prompt_expanding_iterators[prompt_text]
prompt = ImaginePrompt(
next(prompt_iterator),
prompt_strength=prompt_strength,
init_image=init_image,
init_image_strength=init_image_strength,
seed=seed,
sampler_type=sampler_type,
steps=steps,
height=height,
width=width,
mask_image=mask_image,
mask_prompt=mask_prompt,
mask_mode=mask_mode,
mask_modify_original=mask_modify_original,
upscale=upscale,
fix_faces=fix_faces,
fix_faces_fidelity=fix_faces_fidelity,
tile_mode=tile,
model=model_weights_path,
)
prompts.append(prompt)
imagine_image_files(
prompts,
outdir=outdir,
record_step_images=show_work,
output_file_extension="jpg",
print_caption=caption,
precision=precision,
)
@shell(prompt="imaginAIry> ", intro="Starting imaginAIry...")
def aimg():
pass
@click.argument("image_filepaths", nargs=-1)
@aimg.command()
def describe(image_filepaths):
"""Generate text descriptions of images"""
imgs = []
for p in image_filepaths:
if p.startswith("http"):
img = LazyLoadingImage(url=p)
else:
img = LazyLoadingImage(filepath=p)
imgs.append(img)
for img in imgs:
print(generate_caption(img.copy()))
aimg.add_command(imagine_cmd, name="imagine")
if __name__ == "__main__":
imagine_cmd() # noqa