1
0
Fork 0
onnx-web/api/onnx_web/output.py

193 lines
5.3 KiB
Python
Raw Normal View History

2023-02-02 14:31:35 +00:00
from hashlib import sha256
from json import dumps
2023-02-02 14:31:35 +00:00
from logging import getLogger
from time import time
from typing import List, Optional
from piexif import ExifIFD, ImageIFD, dump
from piexif.helper import UserComment
from PIL import Image, PngImagePlugin
2023-02-05 13:53:26 +00:00
from .chain.result import ImageMetadata, StageResult
from .params import ImageParams, Param, Size
2023-02-19 02:28:21 +00:00
from .server import ServerContext
from .utils import base_join, hash_value
2023-02-02 14:31:35 +00:00
logger = getLogger(__name__)
2024-01-04 02:54:11 +00:00
def make_output_names(
server: ServerContext,
2024-01-04 02:54:11 +00:00
job_name: str,
count: int = 1,
offset: int = 0,
2024-01-15 02:03:16 +00:00
extension: Optional[str] = None,
2024-01-29 01:45:37 +00:00
suffix: Optional[str] = None,
) -> List[str]:
2024-01-29 01:45:37 +00:00
if suffix is not None:
job_name = f"{job_name}_{suffix}"
return [
2024-01-15 02:03:16 +00:00
f"{job_name}_{i}.{extension or server.image_format}"
for i in range(offset, count + offset)
]
def make_job_name(
mode: str,
params: ImageParams,
size: Size,
extras: Optional[List[Optional[Param]]] = None,
) -> str:
2023-02-02 14:31:35 +00:00
now = int(time())
sha = sha256()
hash_value(sha, mode)
hash_value(sha, params.model)
hash_value(sha, params.pipeline)
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)
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)
return f"{mode}_{params.seed}_{sha.hexdigest()}_{now}"
def save_result(
server: ServerContext,
result: StageResult,
base_name: str,
2024-01-29 01:45:37 +00:00
save_thumbnails: bool = False,
) -> List[str]:
2024-01-06 02:11:58 +00:00
images = result.as_images()
2024-01-29 02:04:47 +00:00
result.outputs = make_output_names(server, base_name, len(images))
logger.debug("saving %s images: %s", len(images), result.outputs)
2024-01-29 01:45:37 +00:00
outputs = []
2024-01-29 02:04:47 +00:00
for image, metadata, filename in zip(images, result.metadata, result.outputs):
2024-01-29 01:45:37 +00:00
outputs.append(
save_image(
server,
2024-01-04 02:54:11 +00:00
filename,
image,
metadata,
)
)
2024-01-29 01:45:37 +00:00
if save_thumbnails:
2024-01-29 02:04:47 +00:00
result.thumbnails = make_output_names(
2024-01-29 01:45:37 +00:00
server,
base_name,
len(images),
suffix="thumbnail",
)
2024-01-29 02:04:47 +00:00
logger.debug("saving %s thumbnails: %s", len(images), result.thumbnails)
2024-01-29 01:45:37 +00:00
thumbnails = []
2024-01-29 02:04:47 +00:00
for image, filename in zip(images, result.thumbnails):
2024-01-29 01:45:37 +00:00
thumbnail = image.copy()
thumbnail.thumbnail((server.thumbnail_size, server.thumbnail_size))
thumbnails.append(
save_image(
server,
filename,
thumbnail,
2024-01-29 01:45:37 +00:00
)
)
return outputs
2023-02-02 14:31:35 +00:00
def save_image(
server: ServerContext,
output: str,
image: Image.Image,
metadata: Optional[ImageMetadata] = None,
) -> str:
path = base_join(server.output_path, output)
if server.image_format == "png":
exif = PngImagePlugin.PngInfo()
if metadata is not None:
exif.add_text("make", "onnx-web")
exif.add_text(
"maker note",
dumps(metadata.tojson(server, [output])),
)
exif.add_text("model", server.server_version)
2023-06-26 12:48:39 +00:00
exif.add_text(
"parameters",
2024-01-14 19:19:09 +00:00
metadata.to_exif(server),
2023-06-26 12:48:39 +00:00
)
image.save(path, format=server.image_format, pnginfo=exif)
else:
exif = dump(
{
"0th": {
ExifIFD.MakerNote: UserComment.dump(
dumps(metadata.tojson(server, [output])),
encoding="unicode",
),
2023-06-26 12:24:25 +00:00
ExifIFD.UserComment: UserComment.dump(
2024-01-14 19:19:09 +00:00
metadata.to_exif(server),
2023-06-26 12:48:39 +00:00
encoding="unicode",
2023-06-26 12:24:25 +00:00
),
ImageIFD.Make: "onnx-web",
ImageIFD.Model: server.server_version,
}
}
)
image.save(path, format=server.image_format, exif=exif)
if metadata is not None:
save_metadata(
server,
output,
2024-01-04 02:22:38 +00:00
metadata,
)
2023-02-05 13:53:26 +00:00
logger.debug("saved output image to: %s", path)
return path
def save_metadata(
server: ServerContext,
output: str,
metadata: ImageMetadata,
) -> str:
path = base_join(server.output_path, f"{output}.json")
json = metadata.tojson(server, [output])
2023-02-05 13:53:26 +00:00
with open(path, "w") as f:
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
def read_metadata(
image: Image.Image,
) -> Optional[ImageMetadata]:
2024-01-14 02:19:37 +00:00
exif_data = image.getexif()
if ImageIFD.Make in exif_data and exif_data[ImageIFD.Make] == "onnx-web":
return ImageMetadata.from_json(exif_data[ExifIFD.MakerNote])
if ExifIFD.UserComment in exif_data:
return ImageMetadata.from_exif(exif_data[ExifIFD.UserComment])
# this could return ImageMetadata.unknown_image(), but that would not indicate whether the input
# had metadata or not, so it's easier to return None and follow the call with `or ImageMetadata.unknown_image()`
return None