2023-02-02 14:31:35 +00:00
|
|
|
from hashlib import sha256
|
2023-02-02 04:20:40 +00:00
|
|
|
from json import dumps
|
2023-02-02 14:31:35 +00:00
|
|
|
from logging import getLogger
|
2023-02-12 15:51:35 +00:00
|
|
|
from os import path
|
2023-02-02 14:31:35 +00:00
|
|
|
from struct import pack
|
|
|
|
from time import time
|
2023-08-26 04:33:41 +00:00
|
|
|
from typing import Any, Dict, List, Optional, Tuple
|
2023-02-02 04:20:40 +00:00
|
|
|
|
2023-06-26 12:03:06 +00:00
|
|
|
from piexif import ExifIFD, ImageIFD, dump
|
|
|
|
from piexif.helper import UserComment
|
|
|
|
from PIL import Image, PngImagePlugin
|
2023-02-05 13:53:26 +00:00
|
|
|
|
2023-12-22 03:34:03 +00:00
|
|
|
from .convert.utils import resolve_tensor
|
2023-04-01 16:26:10 +00:00
|
|
|
from .params import Border, HighresParams, ImageParams, Param, Size, UpscaleParams
|
2023-02-19 02:28:21 +00:00
|
|
|
from .server import ServerContext
|
2023-12-22 03:34:03 +00:00
|
|
|
from .server.load import get_extra_hashes
|
2023-02-19 02:28:21 +00:00
|
|
|
from .utils import base_join
|
2023-02-02 04:20:40 +00:00
|
|
|
|
2023-02-02 14:31:35 +00:00
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
2023-06-26 22:24:34 +00:00
|
|
|
HASH_BUFFER_SIZE = 2**22 # 4MB
|
|
|
|
|
|
|
|
|
|
|
|
def hash_file(name: str):
|
|
|
|
sha = sha256()
|
|
|
|
with open(name, "rb") as f:
|
|
|
|
while True:
|
|
|
|
data = f.read(HASH_BUFFER_SIZE)
|
|
|
|
if not data:
|
|
|
|
break
|
|
|
|
|
|
|
|
sha.update(data)
|
|
|
|
|
|
|
|
return sha.hexdigest()
|
|
|
|
|
2023-02-02 14:47:37 +00:00
|
|
|
|
2023-03-01 03:44:52 +00:00
|
|
|
def hash_value(sha, param: Optional[Param]):
|
2023-02-02 14:31:35 +00:00
|
|
|
if param is None:
|
|
|
|
return
|
2023-02-22 05:08:13 +00:00
|
|
|
elif isinstance(param, bool):
|
|
|
|
sha.update(bytearray(pack("!B", param)))
|
2023-02-02 14:31:35 +00:00
|
|
|
elif isinstance(param, float):
|
2023-02-05 13:53:26 +00:00
|
|
|
sha.update(bytearray(pack("!f", param)))
|
2023-02-02 14:31:35 +00:00
|
|
|
elif isinstance(param, int):
|
2023-02-05 13:53:26 +00:00
|
|
|
sha.update(bytearray(pack("!I", param)))
|
2023-02-02 14:31:35 +00:00
|
|
|
elif isinstance(param, str):
|
2023-02-05 13:53:26 +00:00
|
|
|
sha.update(param.encode("utf-8"))
|
2023-02-02 14:31:35 +00:00
|
|
|
else:
|
2023-07-16 00:00:52 +00:00
|
|
|
logger.warning("cannot hash param: %s, %s", param, type(param))
|
2023-02-02 14:31:35 +00:00
|
|
|
|
2023-02-02 04:20:40 +00:00
|
|
|
|
|
|
|
def json_params(
|
2023-02-21 14:14:04 +00:00
|
|
|
outputs: List[str],
|
2023-02-02 04:20:40 +00:00
|
|
|
params: ImageParams,
|
|
|
|
size: Size,
|
|
|
|
upscale: Optional[UpscaleParams] = None,
|
|
|
|
border: Optional[Border] = None,
|
2023-04-01 16:26:10 +00:00
|
|
|
highres: Optional[HighresParams] = None,
|
2023-12-03 18:53:50 +00:00
|
|
|
parent: Optional[Dict] = None,
|
2023-02-02 04:20:40 +00:00
|
|
|
) -> Any:
|
2023-02-02 04:31:01 +00:00
|
|
|
json = {
|
2023-08-26 04:33:41 +00:00
|
|
|
"input_size": size.tojson(),
|
2023-02-21 14:14:04 +00:00
|
|
|
"outputs": outputs,
|
2023-02-05 13:53:26 +00:00
|
|
|
"params": params.tojson(),
|
2023-02-02 04:31:01 +00:00
|
|
|
}
|
|
|
|
|
2023-02-12 15:51:35 +00:00
|
|
|
json["params"]["model"] = path.basename(params.model)
|
2023-02-26 16:15:12 +00:00
|
|
|
json["params"]["scheduler"] = params.scheduler
|
2023-02-12 15:51:35 +00:00
|
|
|
|
2023-08-26 04:33:41 +00:00
|
|
|
# calculate final output size
|
2023-06-26 12:03:06 +00:00
|
|
|
output_size = size
|
2023-02-19 16:27:05 +00:00
|
|
|
if border is not None:
|
|
|
|
json["border"] = border.tojson()
|
2023-06-26 12:03:06 +00:00
|
|
|
output_size = output_size.add_border(border)
|
2023-02-02 04:25:53 +00:00
|
|
|
|
2023-04-01 16:26:10 +00:00
|
|
|
if highres is not None:
|
|
|
|
json["highres"] = highres.tojson()
|
2023-06-26 12:03:06 +00:00
|
|
|
output_size = highres.resize(output_size)
|
2023-04-01 16:26:10 +00:00
|
|
|
|
2023-02-02 04:25:53 +00:00
|
|
|
if upscale is not None:
|
2023-02-05 13:53:26 +00:00
|
|
|
json["upscale"] = upscale.tojson()
|
2023-06-26 12:03:06 +00:00
|
|
|
output_size = upscale.resize(output_size)
|
2023-02-02 04:25:53 +00:00
|
|
|
|
2023-06-26 12:03:06 +00:00
|
|
|
json["size"] = output_size.tojson()
|
2023-02-02 04:31:01 +00:00
|
|
|
|
2023-02-02 04:35:10 +00:00
|
|
|
return json
|
2023-02-02 04:20:40 +00:00
|
|
|
|
|
|
|
|
2023-06-26 12:03:06 +00:00
|
|
|
def str_params(
|
2023-06-26 22:24:34 +00:00
|
|
|
server: ServerContext,
|
2023-06-26 12:03:06 +00:00
|
|
|
params: ImageParams,
|
|
|
|
size: Size,
|
2023-06-26 12:48:39 +00:00
|
|
|
inversions: List[Tuple[str, float]] = None,
|
|
|
|
loras: List[Tuple[str, float]] = None,
|
2023-06-26 12:03:06 +00:00
|
|
|
) -> str:
|
2023-06-26 22:24:34 +00:00
|
|
|
model_name = path.basename(path.normpath(params.model))
|
2023-06-27 12:09:40 +00:00
|
|
|
logger.debug("getting model hash for %s", model_name)
|
|
|
|
|
2023-06-27 12:28:59 +00:00
|
|
|
model_hash = get_extra_hashes().get(model_name, None)
|
|
|
|
if model_hash is None:
|
|
|
|
model_hash_path = path.join(params.model, "hash.txt")
|
|
|
|
if path.exists(model_hash_path):
|
|
|
|
with open(model_hash_path, "r") as f:
|
2023-06-27 12:33:23 +00:00
|
|
|
model_hash = f.readline().rstrip(",. \n\t\r")
|
2023-06-27 12:28:59 +00:00
|
|
|
|
|
|
|
model_hash = model_hash or "unknown"
|
2023-06-26 22:24:34 +00:00
|
|
|
hash_map = {
|
|
|
|
model_name: model_hash,
|
|
|
|
}
|
|
|
|
|
|
|
|
inversion_hashes = ""
|
|
|
|
if inversions is not None:
|
|
|
|
inversion_pairs = [
|
|
|
|
(
|
|
|
|
name,
|
|
|
|
hash_file(
|
|
|
|
resolve_tensor(path.join(server.model_path, "inversion", name))
|
|
|
|
).upper(),
|
|
|
|
)
|
|
|
|
for name, _weight in inversions
|
|
|
|
]
|
|
|
|
inversion_hashes = ",".join(
|
|
|
|
[f"{name}: {hash}" for name, hash in inversion_pairs]
|
|
|
|
)
|
|
|
|
hash_map.update(dict(inversion_pairs))
|
|
|
|
|
|
|
|
lora_hashes = ""
|
|
|
|
if loras is not None:
|
|
|
|
lora_pairs = [
|
|
|
|
(
|
|
|
|
name,
|
|
|
|
hash_file(
|
|
|
|
resolve_tensor(path.join(server.model_path, "lora", name))
|
|
|
|
).upper(),
|
|
|
|
)
|
|
|
|
for name, _weight in loras
|
|
|
|
]
|
|
|
|
lora_hashes = ",".join([f"{name}: {hash}" for name, hash in lora_pairs])
|
|
|
|
hash_map.update(dict(lora_pairs))
|
2023-06-26 12:48:39 +00:00
|
|
|
|
2023-06-26 12:03:06 +00:00
|
|
|
return (
|
2023-07-09 16:51:14 +00:00
|
|
|
f"{params.prompt or ''}\nNegative prompt: {params.negative_prompt or ''}\n"
|
2023-06-26 12:03:06 +00:00
|
|
|
f"Steps: {params.steps}, Sampler: {params.scheduler}, CFG scale: {params.cfg}, "
|
2023-06-26 12:48:39 +00:00
|
|
|
f"Seed: {params.seed}, Size: {size.width}x{size.height}, "
|
2023-06-26 22:24:34 +00:00
|
|
|
f"Model hash: {model_hash}, Model: {model_name}, "
|
|
|
|
f"Tool: onnx-web, Version: {server.server_version}, "
|
|
|
|
f'Inversion hashes: "{inversion_hashes}", '
|
2023-06-26 12:48:39 +00:00
|
|
|
f'Lora hashes: "{lora_hashes}", '
|
2023-06-26 22:24:34 +00:00
|
|
|
f"Hashes: {dumps(hash_map)}"
|
2023-06-26 12:03:06 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-02-02 14:31:35 +00:00
|
|
|
def make_output_name(
|
2023-04-10 01:33:03 +00:00
|
|
|
server: ServerContext,
|
2023-02-02 14:31:35 +00:00
|
|
|
mode: str,
|
|
|
|
params: ImageParams,
|
|
|
|
size: Size,
|
2023-03-01 03:44:52 +00:00
|
|
|
extras: Optional[List[Optional[Param]]] = None,
|
2023-04-14 03:51:59 +00:00
|
|
|
count: Optional[int] = None,
|
2023-09-10 21:35:16 +00:00
|
|
|
offset: int = 0,
|
2023-02-20 14:35:18 +00:00
|
|
|
) -> List[str]:
|
2023-04-14 03:51:59 +00:00
|
|
|
count = count or params.batch
|
2023-02-02 14:31:35 +00:00
|
|
|
now = int(time())
|
|
|
|
sha = sha256()
|
|
|
|
|
|
|
|
hash_value(sha, mode)
|
|
|
|
hash_value(sha, params.model)
|
2023-04-13 03:58:48 +00:00
|
|
|
hash_value(sha, params.pipeline)
|
2023-02-26 16:15:12 +00:00
|
|
|
hash_value(sha, params.scheduler)
|
2023-02-02 14:31:35 +00:00
|
|
|
hash_value(sha, params.prompt)
|
|
|
|
hash_value(sha, params.negative_prompt)
|
|
|
|
hash_value(sha, params.cfg)
|
|
|
|
hash_value(sha, params.seed)
|
2023-02-22 05:08:13 +00:00
|
|
|
hash_value(sha, params.steps)
|
|
|
|
hash_value(sha, params.eta)
|
|
|
|
hash_value(sha, params.batch)
|
2023-02-02 14:31:35 +00:00
|
|
|
hash_value(sha, size.width)
|
|
|
|
hash_value(sha, size.height)
|
|
|
|
|
|
|
|
if extras is not None:
|
|
|
|
for param in extras:
|
|
|
|
hash_value(sha, param)
|
|
|
|
|
2023-02-20 14:35:18 +00:00
|
|
|
return [
|
2023-04-10 01:33:03 +00:00
|
|
|
f"{mode}_{params.seed}_{sha.hexdigest()}_{now}_{i}.{server.image_format}"
|
2023-09-10 21:35:16 +00:00
|
|
|
for i in range(offset, count + offset)
|
2023-02-20 14:35:18 +00:00
|
|
|
]
|
2023-02-02 14:31:35 +00:00
|
|
|
|
|
|
|
|
2023-06-26 12:03:06 +00:00
|
|
|
def save_image(
|
|
|
|
server: ServerContext,
|
|
|
|
output: str,
|
|
|
|
image: Image.Image,
|
|
|
|
params: Optional[ImageParams] = None,
|
|
|
|
size: Optional[Size] = None,
|
|
|
|
upscale: Optional[UpscaleParams] = None,
|
|
|
|
border: Optional[Border] = None,
|
|
|
|
highres: Optional[HighresParams] = None,
|
2023-06-26 12:48:39 +00:00
|
|
|
inversions: List[Tuple[str, float]] = None,
|
|
|
|
loras: List[Tuple[str, float]] = None,
|
2023-06-26 12:03:06 +00:00
|
|
|
) -> str:
|
2023-04-10 01:33:03 +00:00
|
|
|
path = base_join(server.output_path, output)
|
2023-06-26 12:03:06 +00:00
|
|
|
|
|
|
|
if server.image_format == "png":
|
|
|
|
exif = PngImagePlugin.PngInfo()
|
|
|
|
|
|
|
|
if params is not None:
|
2023-08-02 22:43:17 +00:00
|
|
|
exif.add_text("make", "onnx-web")
|
2023-06-26 12:03:06 +00:00
|
|
|
exif.add_text(
|
2023-08-02 22:43:17 +00:00
|
|
|
"maker note",
|
2023-06-26 12:14:32 +00:00
|
|
|
dumps(
|
|
|
|
json_params(
|
|
|
|
[output],
|
|
|
|
params,
|
|
|
|
size,
|
|
|
|
upscale=upscale,
|
|
|
|
border=border,
|
|
|
|
highres=highres,
|
|
|
|
)
|
2023-06-26 12:03:06 +00:00
|
|
|
),
|
|
|
|
)
|
2023-08-02 22:43:17 +00:00
|
|
|
exif.add_text("model", server.server_version)
|
2023-06-26 12:48:39 +00:00
|
|
|
exif.add_text(
|
2023-08-02 22:43:17 +00:00
|
|
|
"parameters",
|
2023-06-26 22:24:34 +00:00
|
|
|
str_params(server, params, size, inversions=inversions, loras=loras),
|
2023-06-26 12:48:39 +00:00
|
|
|
)
|
2023-06-26 12:03:06 +00:00
|
|
|
|
|
|
|
image.save(path, format=server.image_format, pnginfo=exif)
|
|
|
|
else:
|
|
|
|
exif = dump(
|
|
|
|
{
|
|
|
|
"0th": {
|
2023-06-26 12:14:32 +00:00
|
|
|
ExifIFD.MakerNote: UserComment.dump(
|
|
|
|
dumps(
|
|
|
|
json_params(
|
|
|
|
[output],
|
|
|
|
params,
|
|
|
|
size,
|
|
|
|
upscale=upscale,
|
|
|
|
border=border,
|
|
|
|
highres=highres,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
encoding="unicode",
|
|
|
|
),
|
2023-06-26 12:24:25 +00:00
|
|
|
ExifIFD.UserComment: UserComment.dump(
|
2023-06-26 22:24:34 +00:00
|
|
|
str_params(
|
|
|
|
server, params, size, inversions=inversions, loras=loras
|
|
|
|
),
|
2023-06-26 12:48:39 +00:00
|
|
|
encoding="unicode",
|
2023-06-26 12:24:25 +00:00
|
|
|
),
|
2023-06-26 12:03:06 +00:00
|
|
|
ImageIFD.Make: "onnx-web",
|
2023-06-26 22:24:34 +00:00
|
|
|
ImageIFD.Model: server.server_version,
|
2023-06-26 12:03:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
image.save(path, format=server.image_format, exif=exif)
|
|
|
|
|
|
|
|
if params is not None:
|
2023-06-26 12:14:32 +00:00
|
|
|
save_params(
|
|
|
|
server,
|
|
|
|
output,
|
|
|
|
params,
|
|
|
|
size,
|
|
|
|
upscale=upscale,
|
|
|
|
border=border,
|
|
|
|
highres=highres,
|
|
|
|
)
|
2023-06-26 12:03:06 +00:00
|
|
|
|
2023-02-05 13:53:26 +00:00
|
|
|
logger.debug("saved output image to: %s", path)
|
2023-02-02 04:20:40 +00:00
|
|
|
return path
|
|
|
|
|
|
|
|
|
|
|
|
def save_params(
|
2023-04-10 01:33:03 +00:00
|
|
|
server: ServerContext,
|
2023-02-02 04:20:40 +00:00
|
|
|
output: str,
|
|
|
|
params: ImageParams,
|
|
|
|
size: Size,
|
|
|
|
upscale: Optional[UpscaleParams] = None,
|
|
|
|
border: Optional[Border] = None,
|
2023-04-01 16:26:10 +00:00
|
|
|
highres: Optional[HighresParams] = None,
|
2023-02-02 04:20:40 +00:00
|
|
|
) -> str:
|
2023-04-10 01:33:03 +00:00
|
|
|
path = base_join(server.output_path, f"{output}.json")
|
2023-04-01 17:06:31 +00:00
|
|
|
json = json_params(
|
|
|
|
output, params, size, upscale=upscale, border=border, highres=highres
|
|
|
|
)
|
2023-02-05 13:53:26 +00:00
|
|
|
with open(path, "w") as f:
|
2023-02-02 04:20:40 +00:00
|
|
|
f.write(dumps(json))
|
2023-02-05 13:53:26 +00:00
|
|
|
logger.debug("saved image params to: %s", path)
|
2023-02-02 14:31:35 +00:00
|
|
|
return path
|