feat: return json struct with output path instead of image data, load images from outputs endpoint
This commit is contained in:
parent
50221af55a
commit
466884113f
|
@ -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
|
||||
|
||||
|
|
29
api/serve.py
29
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/<path:filename>')
|
||||
def output(filename):
|
||||
return send_from_directory(output_path, filename, as_attachment=False)
|
||||
|
|
|
@ -11,30 +11,34 @@ export interface Txt2ImgParams {
|
|||
}
|
||||
|
||||
export interface ApiResponse {
|
||||
output: string;
|
||||
params: Txt2ImgParams;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface ApiClient {
|
||||
txt2img(params: Txt2ImgParams): Promise<string>;
|
||||
txt2img(params: Txt2ImgParams): Promise<ApiResponse>;
|
||||
}
|
||||
|
||||
export const STATUS_SUCCESS = 200;
|
||||
|
||||
export async function imageFromResponse(res: Response) {
|
||||
export async function imageFromResponse(root: string, res: Response): Promise<ApiResponse> {
|
||||
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<string> | undefined;
|
||||
let pending: Promise<ApiResponse> | undefined;
|
||||
|
||||
return {
|
||||
async txt2img(params: Txt2ImgParams): Promise<string> {
|
||||
async txt2img(params: Txt2ImgParams): Promise<ApiResponse> {
|
||||
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;
|
||||
});
|
||||
|
||||
|
|
|
@ -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<ImageParams>({
|
||||
|
@ -23,16 +23,18 @@ export function Txt2Img(props: Txt2ImgProps) {
|
|||
});
|
||||
const [scheduler, setScheduler] = useState('euler-a');
|
||||
|
||||
const [result, setResult] = useState<ApiResponse | undefined>();
|
||||
|
||||
async function getImage() {
|
||||
const data = await client.txt2img({ ...params, prompt, scheduler });
|
||||
setImage(data);
|
||||
setResult(data);
|
||||
}
|
||||
|
||||
function renderImage() {
|
||||
if (image === '') {
|
||||
return <div>No image</div>;
|
||||
if (doesExist(result)) {
|
||||
return <img src={result.output} />;
|
||||
} else {
|
||||
return <img src={image} />;
|
||||
return <div>No result. Press Generate.</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,11 +49,11 @@ export function Txt2Img(props: Txt2ImgProps) {
|
|||
>
|
||||
<MenuItem value='ddim'>DDIM</MenuItem>
|
||||
<MenuItem value='ddpm'>DDPM</MenuItem>
|
||||
<MenuItem value='pndm'>PNDM</MenuItem>
|
||||
<MenuItem value='lms-discrete'>LMS</MenuItem>
|
||||
<MenuItem value='dpm-multi'>DPM Multistep</MenuItem>
|
||||
<MenuItem value='euler'>Euler</MenuItem>
|
||||
<MenuItem value='euler-a'>Euler A</MenuItem>
|
||||
<MenuItem value='dpm-multi'>DPM</MenuItem>
|
||||
<MenuItem value='euler-a'>Euler Ancestral</MenuItem>
|
||||
<MenuItem value='lms-discrete'>LMS Discrete</MenuItem>
|
||||
<MenuItem value='pndm'>PNDM</MenuItem>
|
||||
</Select>
|
||||
<ImageControl params={params} onChange={(newParams) => {
|
||||
setParams(newParams);
|
||||
|
|
Loading…
Reference in New Issue