|
|
@ -1,13 +1,70 @@
|
|
|
|
import hashlib
|
|
|
|
import hashlib
|
|
|
|
import json
|
|
|
|
import json
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
import os.path
|
|
|
|
import random
|
|
|
|
import random
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
|
|
|
|
|
|
import numpy
|
|
|
|
import numpy
|
|
|
|
from PIL.Image import Exif
|
|
|
|
import requests
|
|
|
|
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
from urllib3.exceptions import LocationParseError
|
|
|
|
|
|
|
|
from urllib3.util import parse_url
|
|
|
|
|
|
|
|
|
|
|
|
from imaginairy.utils import get_device, get_device_name
|
|
|
|
from imaginairy.utils import get_device, get_device_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InvalidUrlError(ValueError):
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LazyLoadingImage:
|
|
|
|
|
|
|
|
def __init__(self, *, filepath=None, url=None):
|
|
|
|
|
|
|
|
if not filepath and not url:
|
|
|
|
|
|
|
|
raise ValueError("You must specify a url or filepath")
|
|
|
|
|
|
|
|
if filepath and url:
|
|
|
|
|
|
|
|
raise ValueError("You cannot specify a url and filepath")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# validate file exists
|
|
|
|
|
|
|
|
if filepath and not os.path.exists(filepath):
|
|
|
|
|
|
|
|
raise FileNotFoundError(f"File does not exist: {filepath}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# validate url is valid url
|
|
|
|
|
|
|
|
if url:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
parsed_url = parse_url(url)
|
|
|
|
|
|
|
|
except LocationParseError:
|
|
|
|
|
|
|
|
raise InvalidUrlError(f"Invalid url: {url}")
|
|
|
|
|
|
|
|
if parsed_url.scheme not in {"http", "https"} or not parsed_url.host:
|
|
|
|
|
|
|
|
raise InvalidUrlError(f"Invalid url: {url}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._lazy_filepath = filepath
|
|
|
|
|
|
|
|
self._lazy_url = url
|
|
|
|
|
|
|
|
self._img = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __getattr__(self, key):
|
|
|
|
|
|
|
|
if key == "_img":
|
|
|
|
|
|
|
|
# http://nedbatchelder.com/blog/201010/surprising_getattr_recursion.html
|
|
|
|
|
|
|
|
raise AttributeError()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self._lazy_filepath:
|
|
|
|
|
|
|
|
self._img = Image.open(self._lazy_filepath)
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
f"Loaded input 🖼 of size {self._img.size} from {self._lazy_filepath}"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
elif self._lazy_url:
|
|
|
|
|
|
|
|
self._img = Image.open(requests.get(self._lazy_url, stream=True).raw)
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
|
|
|
|
f"Loaded input 🖼 of size {self._img.size} from {self._lazy_url}"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return getattr(self._img, key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
|
|
|
return self._lazy_filepath or self._lazy_url
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WeightedPrompt:
|
|
|
|
class WeightedPrompt:
|
|
|
|
def __init__(self, text, weight=1):
|
|
|
|
def __init__(self, text, weight=1):
|
|
|
@ -23,7 +80,7 @@ class ImaginePrompt:
|
|
|
|
self,
|
|
|
|
self,
|
|
|
|
prompt=None,
|
|
|
|
prompt=None,
|
|
|
|
prompt_strength=7.5,
|
|
|
|
prompt_strength=7.5,
|
|
|
|
init_image=None,
|
|
|
|
init_image=None, # Pillow Image, LazyLoadingImage, or filepath str
|
|
|
|
init_image_strength=0.3,
|
|
|
|
init_image_strength=0.3,
|
|
|
|
seed=None,
|
|
|
|
seed=None,
|
|
|
|
steps=50,
|
|
|
|
steps=50,
|
|
|
@ -40,6 +97,8 @@ class ImaginePrompt:
|
|
|
|
self.prompts = prompt
|
|
|
|
self.prompts = prompt
|
|
|
|
self.prompts.sort(key=lambda p: p.weight, reverse=True)
|
|
|
|
self.prompts.sort(key=lambda p: p.weight, reverse=True)
|
|
|
|
self.prompt_strength = prompt_strength
|
|
|
|
self.prompt_strength = prompt_strength
|
|
|
|
|
|
|
|
if isinstance(init_image, str):
|
|
|
|
|
|
|
|
init_image = LazyLoadingImage(filepath=init_image)
|
|
|
|
self.init_image = init_image
|
|
|
|
self.init_image = init_image
|
|
|
|
self.init_image_strength = init_image_strength
|
|
|
|
self.init_image_strength = init_image_strength
|
|
|
|
self.seed = random.randint(1, 1_000_000_000) if seed is None else seed
|
|
|
|
self.seed = random.randint(1, 1_000_000_000) if seed is None else seed
|
|
|
@ -68,7 +127,7 @@ class ImaginePrompt:
|
|
|
|
"software": "imaginairy",
|
|
|
|
"software": "imaginairy",
|
|
|
|
"prompts": prompts,
|
|
|
|
"prompts": prompts,
|
|
|
|
"prompt_strength": self.prompt_strength,
|
|
|
|
"prompt_strength": self.prompt_strength,
|
|
|
|
"init_image": self.init_image,
|
|
|
|
"init_image": str(self.init_image),
|
|
|
|
"init_image_strength": self.init_image_strength,
|
|
|
|
"init_image_strength": self.init_image_strength,
|
|
|
|
"seed": self.seed,
|
|
|
|
"seed": self.seed,
|
|
|
|
"steps": self.steps,
|
|
|
|
"steps": self.steps,
|
|
|
@ -116,7 +175,7 @@ class ImagineResult:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def _exif(self):
|
|
|
|
def _exif(self):
|
|
|
|
exif = Exif()
|
|
|
|
exif = Image.Exif()
|
|
|
|
exif[ExifCodes.ImageDescription] = self.prompt.prompt_description()
|
|
|
|
exif[ExifCodes.ImageDescription] = self.prompt.prompt_description()
|
|
|
|
exif[ExifCodes.UserComment] = json.dumps(self.metadata_dict())
|
|
|
|
exif[ExifCodes.UserComment] = json.dumps(self.metadata_dict())
|
|
|
|
# help future web scrapes not ingest AI generated art
|
|
|
|
# help future web scrapes not ingest AI generated art
|
|
|
|