Chaoses-Ib / ComfyScript

A Python front end and library for ComfyUI
MIT License
287 stars 15 forks source link

Generation preview #36

Open ambocclusion opened 2 months ago

ambocclusion commented 2 months ago

Is it possible to get previews of an in-progress generation, similar to what is shown in the web ui, using comfyscript? Ideally I'd like some form of stream I can process per-step.

Chaoses-Ib commented 2 months ago

TaskQueue._watch() currently doesn't handle previews. I'll add callbacks for them later.

Chaoses-Ib commented 2 months ago

v0.5.0a1 is released. You can get previews like this:

from PIL import Image

# Prevent displaying previews
queue.watch_display(False, False, False)

with Workflow() as wf:
    model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')
    conditioning = CLIPTextEncode('beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', clip)
    conditioning2 = CLIPTextEncode('text, watermark', clip)
    latent = EmptyLatentImage(512, 512, 1)
    latent = KSampler(model, 12, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)
    image = VAEDecode(latent, vae)
    SaveImage(image, 'ComfyUI')

def preview(task: Task, node_id: str, image: Image.Image):
    # 'KSampler.0'
    print(node_id)
wf.task.add_preview_callback(preview)

A stream form (sync/async iter) is also possible. But I'm not sure if it‘s worth the effort, as one can get similar results with a callback and a wait.

ambocclusion commented 2 months ago

Holy crap, you rock! Works perfectly!

Chaoses-Ib commented 2 months ago

There are still some things left to do, like reporting progress and preview together, and fixing Jupyter Notebook display issues.

ambocclusion commented 2 months ago

Ah sorry

ambocclusion commented 2 months ago

https://github.com/Chaoses-Ib/ComfyScript/assets/10687655/57d92290-eefb-49b2-80e8-ceefd76425c1

Here are the results of your preview work! Again, I'm really grateful for your work on Comfyscript! Thank you

Chaoses-Ib commented 2 months ago

@madriss-mojo

Is there a way to get the output of other types of nodes that have finished execution. For example if I have several SaveImage or VHS_VideoCombine could we get their output file while the prompt is still in progress ? Either using this or via the ComfyUI API ? Thanks

For SaveImage:

from PIL import Image

with Workflow() as wf:
    model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')
    conditioning = CLIPTextEncode('beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', clip)
    conditioning2 = CLIPTextEncode('text, watermark', clip)
    latent = EmptyLatentImage(512, 512, 1)
    latent = KSampler(model, 12, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)
    image = VAEDecode(latent, vae)
    image = SaveImage(image, 'ComfyUI')

# or: await image
image_batch = image.wait()

# Get the first image
# or: await image_batch.get(0)
image: Image.Image = image_batch[0]
display(image)

# Get all images in the batch
images: list[Image.Image] = image_batch.wait()

For VHS_VideoCombine, see https://github.com/Chaoses-Ib/ComfyScript/issues/22#issuecomment-1971187462.

ghostsquad commented 2 weeks ago

v0.5.0a1 is released. You can get previews like this:

from PIL import Image

# Prevent displaying previews
queue.watch_display(False, False, False)

with Workflow() as wf:
    model, clip, vae = CheckpointLoaderSimple('v1-5-pruned-emaonly.ckpt')
    conditioning = CLIPTextEncode('beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', clip)
    conditioning2 = CLIPTextEncode('text, watermark', clip)
    latent = EmptyLatentImage(512, 512, 1)
    latent = KSampler(model, 12, 20, 8, 'euler', 'normal', conditioning, conditioning2, latent, 1)
    image = VAEDecode(latent, vae)
    SaveImage(image, 'ComfyUI')

def preview(task: Task, node_id: str, image: Image.Image):
    # 'KSampler.0'
    print(node_id)
wf.task.add_preview_callback(preview)

A stream form (sync/async iter) is also possible. But I'm not sure if it‘s worth the effort, as one can get similar results with a callback and a wait.

this code block doesn't make entire sense. Maybe I'm just a bit rusty with python. wf would only be valid within the context of the with block, right?

Chaoses-Ib commented 2 weeks ago

with block is justing calling wf.__enter__() and wf.__exit__(). And __exit__() for Workflow is just queuing the workflow to the server. So using it after __exit__() is fine.

But you are right, this is not a very intuitive API. Maybe I should move it as an arg of Workflow(), of allow set the callback in the with block.