diff --git a/README.md b/README.md index f2dea1d3..93002edf 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,9 @@ Install Git and Python 3.10 for your environment: - https://www.python.org/downloads/ - https://gitforwindows.org/ -The latest version of git should be fine. Python must be 3.10 or earlier, 3.10 seems to work well. +The latest version of git should be fine. Python must be 3.10 or earlier, 3.10 seems to work well. If you already have +Python installed for another form of Stable Diffusion, that should work, but make sure to verify the version in the next +step. ### Create a virtual environment diff --git a/api/serve.py b/api/serve.py index 4519d22e..4c995501 100644 --- a/api/serve.py +++ b/api/serve.py @@ -8,9 +8,8 @@ from diffusers import ( LMSDiscreteScheduler, PNDMScheduler, ) -from flask import Flask, make_response, request, send_file, send_from_directory +from flask import Flask, jsonify, request, send_from_directory from stringcase import spinalcase -from io import BytesIO from os import environ, path, makedirs import numpy as np @@ -60,6 +59,7 @@ def get_from_map(args, key, values, default): return values[default] +# TODO: credit this function def get_latents_from_seed(seed: int, width: int, height: int) -> np.ndarray: # 1 is batch size latents_shape = (1, 4, height // 8, width // 8) @@ -123,19 +123,26 @@ def txt2img(): latents=latents ).images[0] - output = '%s/txt2img_%s_%s.png' % (output_path, - seed, spinalcase(prompt[0:64])) - print("txt2img output: %s" % (output)) - image.save(output) + output_file = "txt2img_%s_%s.png" % (seed, spinalcase(prompt[0:64])) + output_full = '%s/%s' % (output_path, output_file) + print("txt2img output: %s" % output_full) + image.save(output_full) - img_io = BytesIO() - image.save(img_io, 'PNG', quality=100) - img_io.seek(0) - - res = make_response(send_file(img_io, mimetype='image/png')) + res = jsonify({ + 'output': output_file, + 'params': { + 'cfg': cfg, + 'steps': steps, + 'height': height, + 'width': width, + 'prompt': prompt, + 'seed': seed + } + }) res.headers.add('Access-Control-Allow-Origin', '*') return res + @app.route('/output/') def output(filename): return send_from_directory(output_path, filename, as_attachment=False) diff --git a/gui/src/api/client.ts b/gui/src/api/client.ts index 210d929c..4c032b63 100644 --- a/gui/src/api/client.ts +++ b/gui/src/api/client.ts @@ -11,30 +11,34 @@ export interface Txt2ImgParams { } export interface ApiResponse { + output: string; params: Txt2ImgParams; - path: string; } export interface ApiClient { - txt2img(params: Txt2ImgParams): Promise; + txt2img(params: Txt2ImgParams): Promise; } export const STATUS_SUCCESS = 200; -export async function imageFromResponse(res: Response) { +export async function imageFromResponse(root: string, res: Response): Promise { if (res.status === STATUS_SUCCESS) { - const imageBlob = await res.blob(); - return URL.createObjectURL(imageBlob); + const data = await res.json() as ApiResponse; + const output = new URL(['output', data.output].join('/'), root).toString(); + return { + output, + params: data.params, + }; } else { throw new Error('request error'); } } export function makeClient(root: string, f = fetch): ApiClient { - let pending: Promise | undefined; + let pending: Promise | undefined; return { - async txt2img(params: Txt2ImgParams): Promise { + async txt2img(params: Txt2ImgParams): Promise { if (doesExist(pending)) { return pending; } @@ -61,7 +65,7 @@ export function makeClient(root: string, f = fetch): ApiClient { url.searchParams.append('prompt', params.prompt); - pending = f(url).then((res) => imageFromResponse(res)).finally(() => { + pending = f(url).then((res) => imageFromResponse(root, res)).finally(() => { pending = undefined; }); diff --git a/gui/src/components/Txt2Img.tsx b/gui/src/components/Txt2Img.tsx index 510c3cd0..72fdc22e 100644 --- a/gui/src/components/Txt2Img.tsx +++ b/gui/src/components/Txt2Img.tsx @@ -1,7 +1,8 @@ +import { doesExist } from '@apextoaster/js-utils'; import { Box, Button, MenuItem, Select, Stack, TextField } from '@mui/material'; import * as React from 'react'; -import { ApiClient } from '../api/client.js'; +import { ApiClient, ApiResponse } from '../api/client.js'; import { ImageControl, ImageParams } from './ImageControl.js'; const { useState } = React; @@ -12,7 +13,6 @@ export interface Txt2ImgProps { export function Txt2Img(props: Txt2ImgProps) { const { client } = props; - const [image, setImage] = useState(''); const [prompt, setPrompt] = useState('an astronaut eating a hamburger'); const [params, setParams] = useState({ @@ -23,16 +23,18 @@ export function Txt2Img(props: Txt2ImgProps) { }); const [scheduler, setScheduler] = useState('euler-a'); + const [result, setResult] = useState(); + async function getImage() { const data = await client.txt2img({ ...params, prompt, scheduler }); - setImage(data); + setResult(data); } function renderImage() { - if (image === '') { - return
No image
; + if (doesExist(result)) { + return ; } else { - return ; + return
No result. Press Generate.
; } } @@ -47,11 +49,11 @@ export function Txt2Img(props: Txt2ImgProps) { > DDIM DDPM - PNDM - LMS + DPM Multistep Euler - Euler A - DPM + Euler Ancestral + LMS Discrete + PNDM { setParams(newParams);