stable-diffusion-webui icon indicating copy to clipboard operation
stable-diffusion-webui copied to clipboard

[Feature Request]: request API to save local image

Open Kilvoctu opened this issue 2 years ago • 5 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues and checked the recent builds/commits

What would your feature do ?

When sending a request to API to run txtimg, img2img, etc,, I would like the ability to tell it to save the image locally. The Web UI already has a robust, customizable file saving feature. The problem this solves is someone handling a frontend doesn't need to do extra work creating code that already exists on the backend.

Proposed workflow

The frontend can send a payload to /sdapi/v1/txt2img or wherever, which can optionally contain an additional key. Something like "save_local_image": True/False If set to True, the API will save an image as per the user's Web UI settings in addition to sending the normal API response back for frontend to do what they want with it.

Additional information

If this already exists in the API, could someone kindly point me to it?

Kilvoctu avatar Oct 29 '22 21:10 Kilvoctu

Auto purposely wanted to not have the API save any images

ArcticFaded avatar Oct 30 '22 03:10 ArcticFaded

Auto purposely wanted to not have the API save any images

How about adding an option "--api-save-images" so the server can choose to do it

BlinkDL avatar Oct 30 '22 09:10 BlinkDL

Well the API is purposefully preventing the SD processing pipelines from saving the images, as you can see in the api module for txt2img : https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/3715ece0adce7bf7c5e9c5ab3710b2fdc3848f39/modules/api/api.py#L183-L184 or img2img : https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/3715ece0adce7bf7c5e9c5ab3710b2fdc3848f39/modules/api/api.py#L223-L224

So if you wanna make it save them set the to false:

"do_not_save_samples": False,  
"do_not_save_grid": False

that's a quick work around, but of course you can go further and add a flag and so on.

radosmn avatar Feb 17 '23 23:02 radosmn

So if you wanna make it save them set the to false:

"do_not_save_samples": False,
"do_not_save_grid": False

It doesn't work. After I changed to False and completed txt2img by api, the error was appeared:

ERROR:    Exception in ASGI application████████████████████████████████████████████████| 70/70 [01:16<00:00,  1.89s/it]
Traceback (most recent call last):
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\anyio\streams\memory.py", line 94, in receive
    return self.receive_nowait()
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\anyio\streams\memory.py", line 89, in receive_nowait
    raise WouldBlock
anyio.WouldBlock

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\base.py", line 77, in call_next
    message = await recv_stream.receive()
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\anyio\streams\memory.py", line 114, in receive
    raise EndOfStream
anyio.EndOfStream

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 407, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\fastapi\applications.py", line 270, in __call__
    await super().__call__(scope, receive, send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\errors.py", line 184, in __call__
    raise exc
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\base.py", line 106, in __call__
    response = await self.dispatch_func(request, call_next)
  File "C:\repos\stable-diffusion-webui\modules\api\api.py", line 96, in log_and_time
    res: Response = await call_next(req)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\base.py", line 80, in call_next
    raise app_exc
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\base.py", line 69, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\gzip.py", line 24, in __call__
    await responder(scope, receive, send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\gzip.py", line 44, in __call__
    await self.app(scope, receive, self.send_with_gzip)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\exceptions.py", line 79, in __call__
    raise exc
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\middleware\exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 21, in __call__
    raise e
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\routing.py", line 706, in __call__
    await route.handle(scope, receive, send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\routing.py", line 66, in app
    response = await func(request)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\fastapi\routing.py", line 237, in app
    raw_response = await run_endpoint_function(
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\fastapi\routing.py", line 165, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\starlette\concurrency.py", line 41, in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\anyio\to_thread.py", line 31, in run_sync
    return await get_asynclib().run_sync_in_worker_thread(
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\anyio\_backends\_asyncio.py", line 937, in run_sync_in_worker_thread
    return await future
  File "C:\repos\stable-diffusion-webui\venv\lib\site-packages\anyio\_backends\_asyncio.py", line 867, in run
    result = context.run(func, *args)
  File "C:\repos\stable-diffusion-webui\modules\api\api.py", line 203, in text2imgapi
    processed = process_images(p)
  File "C:\repos\stable-diffusion-webui\modules\processing.py", line 485, in process_images
    res = process_images_inner(p)
  File "C:\repos\stable-diffusion-webui\modules\processing.py", line 675, in process_images_inner
    images.save_image(image, p.outpath_samples, "", seeds[i], prompts[i], opts.samples_format, info=infotext(n, i), p=p)
  File "C:\repos\stable-diffusion-webui\modules\images.py", line 494, in save_image
    os.makedirs(path, exist_ok=True)
  File "C:\Programs\Python\Python310\lib\os.py", line 210, in makedirs
    head, tail = path.split(name)
  File "C:\Programs\Python\Python310\lib\ntpath.py", line 211, in split
    p = os.fspath(p)
TypeError: expected str, bytes or os.PathLike object, not NoneType

Minebot17 avatar Feb 18 '23 14:02 Minebot17

😃, sorry, my bad, you see I did this weeks, ago, and I forget some, stuff, this errors is linked to the saving path, well, you have to include the saving folder for the pipelines, in my case, I just saved them to the default folders as the GUI, so the code should looks like this :

for tzt2img pipeline : https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/3715ece0adce7bf7c5e9c5ab3710b2fdc3848f39/modules/api/api.py#L181-L186

it becomes

populate = txt2imgreq.copy(update={ # Override __init__ params
            "sampler_name": validate_sampler_name(txt2imgreq.sampler_name or txt2imgreq.sampler_index),
            "do_not_save_samples": False,	
            "do_not_save_grid": False,
            "outpath_samples": opts.outdir_txt2img_samples,	#you can set another folder if you want, I didn't test it though 
            "outpath_grids": opts.outdir_txt2img_grids,
            }
        )

for the im2img : https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/3715ece0adce7bf7c5e9c5ab3710b2fdc3848f39/modules/api/api.py#L221-L227

same :

populate = img2imgreq.copy(update={ # Override __init__ params
            "sampler_name": validate_sampler_name(img2imgreq.sampler_name or img2imgreq.sampler_index),
            "do_not_save_samples": False,	
            "do_not_save_grid": False,
            "outpath_samples": opts.outdir_txt2img_samples,	 # put another path if you want, 
            "outpath_grids": opts.outdir_txt2img_grids,
            "mask": mask
            }
        )

try again, with this, I might try later, when I can and share if there is a error.

radosmn avatar Feb 18 '23 17:02 radosmn

do_not_save_samples

is there any way to save it in a different folder like /api or something

fal3n-4ngel avatar Dec 30 '23 06:12 fal3n-4ngel

do_not_save_samples

is there any way to save it in a different folder like /api or something

I managed to save a generated image in a custom path. Not sure if you want something like this:

# Send the payload to the API and process the response
response = requests.post(url=f'{url}/sdapi/v1/txt2img', json=payload)
r = response.json()
image = Image.open(io.BytesIO(base64.b64decode(r['images'][0])))

# Save the image with the specified suffix
output_filename = f"{model_name}.{suffix}.png"
output_path = output_directory / output_filename
image.save(output_path)
print(f"Thumbnail generated and saved as {output_path}")

MNeMoNiCuZ avatar Jan 07 '24 16:01 MNeMoNiCuZ