Preserve workflow metadata in archives
Browse files- app.py +9 -2
- workflow_api.py +27 -0
app.py
CHANGED
|
@@ -23,6 +23,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
| 23 |
from fastapi.responses import FileResponse
|
| 24 |
from gradio.routes import App
|
| 25 |
from PIL import Image
|
|
|
|
| 26 |
from pydantic import BaseModel, Field
|
| 27 |
|
| 28 |
|
|
@@ -227,13 +228,16 @@ def generate_core(prompt, width=1152, height=896, steps=16):
|
|
| 227 |
positive = state["encode"].encode(clip=state["clip"], text=f"{prompt},{SUFFIX}")[0]
|
| 228 |
negative = state["encode"].encode(clip=state["negative_clip"], text=NEGATIVE)[0]
|
| 229 |
latent = state["latent"].generate(width=width, height=height, batch_size=1)[0]
|
|
|
|
| 230 |
samples = state["sample"].sample(
|
| 231 |
-
model=state["model"], seed=
|
| 232 |
sampler_name="euler_ancestral", scheduler="karras", positive=positive,
|
| 233 |
negative=negative, latent_image=latent, denoise=1,
|
| 234 |
)[0]
|
| 235 |
image = state["decode"].decode(vae=state["vae"], samples=samples)[0][0]
|
| 236 |
result = Image.fromarray(np.clip(image.detach().cpu().numpy() * 255, 0, 255).astype(np.uint8))
|
|
|
|
|
|
|
| 237 |
backup_pool.submit(archive_image, result)
|
| 238 |
return result
|
| 239 |
|
|
@@ -249,7 +253,10 @@ def archive_image(image):
|
|
| 249 |
shutil.copy2(path, local)
|
| 250 |
mode = "a" if local.is_file() else "w"
|
| 251 |
data = BytesIO()
|
| 252 |
-
|
|
|
|
|
|
|
|
|
|
| 253 |
with py7zr.SevenZipFile(local, mode=mode, password=PASSWORD) as archive:
|
| 254 |
archive.set_encrypted_header(True)
|
| 255 |
name = f"{len(archive.getnames()) + 1}ST.png"
|
|
|
|
| 23 |
from fastapi.responses import FileResponse
|
| 24 |
from gradio.routes import App
|
| 25 |
from PIL import Image
|
| 26 |
+
from PIL.PngImagePlugin import PngInfo
|
| 27 |
from pydantic import BaseModel, Field
|
| 28 |
|
| 29 |
|
|
|
|
| 228 |
positive = state["encode"].encode(clip=state["clip"], text=f"{prompt},{SUFFIX}")[0]
|
| 229 |
negative = state["encode"].encode(clip=state["negative_clip"], text=NEGATIVE)[0]
|
| 230 |
latent = state["latent"].generate(width=width, height=height, batch_size=1)[0]
|
| 231 |
+
seed = random.randrange(2**64)
|
| 232 |
samples = state["sample"].sample(
|
| 233 |
+
model=state["model"], seed=seed, steps=steps, cfg=4,
|
| 234 |
sampler_name="euler_ancestral", scheduler="karras", positive=positive,
|
| 235 |
negative=negative, latent_image=latent, denoise=1,
|
| 236 |
)[0]
|
| 237 |
image = state["decode"].decode(vae=state["vae"], samples=samples)[0][0]
|
| 238 |
result = Image.fromarray(np.clip(image.detach().cpu().numpy() * 255, 0, 255).astype(np.uint8))
|
| 239 |
+
from workflow_api import image_metadata
|
| 240 |
+
result.info.update(image_metadata(prompt, width, height, steps, seed))
|
| 241 |
backup_pool.submit(archive_image, result)
|
| 242 |
return result
|
| 243 |
|
|
|
|
| 253 |
shutil.copy2(path, local)
|
| 254 |
mode = "a" if local.is_file() else "w"
|
| 255 |
data = BytesIO()
|
| 256 |
+
pnginfo = PngInfo()
|
| 257 |
+
for key in ("prompt", "workflow"):
|
| 258 |
+
pnginfo.add_text(key, image.info[key])
|
| 259 |
+
image.save(data, format="PNG", pnginfo=pnginfo)
|
| 260 |
with py7zr.SevenZipFile(local, mode=mode, password=PASSWORD) as archive:
|
| 261 |
archive.set_encrypted_header(True)
|
| 262 |
name = f"{len(archive.getnames()) + 1}ST.png"
|
workflow_api.py
CHANGED
|
@@ -588,6 +588,33 @@ def build_extra_pnginfo() -> dict[str, Any] | None:
|
|
| 588 |
}
|
| 589 |
|
| 590 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
workflow = build_workflow()
|
| 592 |
prompt = json.loads(json.dumps(workflow))
|
| 593 |
extra_pnginfo = build_extra_pnginfo()
|
|
|
|
| 588 |
}
|
| 589 |
|
| 590 |
|
| 591 |
+
def image_metadata(text, width, height, steps, seed):
|
| 592 |
+
api = build_workflow()
|
| 593 |
+
api["118"]["inputs"]["string_a"] = text
|
| 594 |
+
api["27"]["inputs"].update(width=width, height=height)
|
| 595 |
+
api["5"]["inputs"].update(seed=seed, steps=steps)
|
| 596 |
+
api["28"]["inputs"].update(model=["10", 0], clip=["10", 1])
|
| 597 |
+
api.pop("38")
|
| 598 |
+
|
| 599 |
+
workflow = build_extra_pnginfo()["workflow"]
|
| 600 |
+
nodes = {node["id"]: node for node in workflow["nodes"]}
|
| 601 |
+
nodes[118]["widgets_values"][0] = text
|
| 602 |
+
nodes[27]["widgets_values"][:2] = [width, height]
|
| 603 |
+
nodes[5]["widgets_values"][0] = seed
|
| 604 |
+
nodes[5]["widgets_values"][2] = steps
|
| 605 |
+
nodes[10]["outputs"][0]["links"] = [27]
|
| 606 |
+
nodes[10]["outputs"][1]["links"] = [28]
|
| 607 |
+
workflow["nodes"] = [node for node in workflow["nodes"] if node["id"] != 38]
|
| 608 |
+
workflow["links"] = [
|
| 609 |
+
[link[0], 10, *link[2:]] if link[0] in {27, 28} else link
|
| 610 |
+
for link in workflow["links"] if link[0] not in {29, 30}
|
| 611 |
+
]
|
| 612 |
+
return {
|
| 613 |
+
"prompt": json.dumps(api, separators=(",", ":")),
|
| 614 |
+
"workflow": json.dumps(workflow, separators=(",", ":")),
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
|
| 618 |
workflow = build_workflow()
|
| 619 |
prompt = json.loads(json.dumps(workflow))
|
| 620 |
extra_pnginfo = build_extra_pnginfo()
|