forked from extern/easydiffusion
Initial commit
This commit is contained in:
commit
4fbd3479f3
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 cmdr2
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
28
README.md
Normal file
28
README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
A simple browser UI for generating images from text prompts, using [Stable Diffusion](https://replicate.com/stability-ai/stable-diffusion). Designed for running locally on your computer.
|
||||||
|
|
||||||
|
This project runs Stable Diffusion in a docker container behind the scenes, using Stable Diffusion's official Docker image on replicate.com.
|
||||||
|
|
||||||
|
![Screenshot of tool](shot1.jpeg?raw=true)
|
||||||
|
|
||||||
|
# System Requirements
|
||||||
|
1. Requires `docker` and `python3.6` (or higher).
|
||||||
|
2. Linux or Windows 11 (with WSL) or macOS. Basically if your system can run [Stable Diffusion](https://replicate.com/stability-ai/stable-diffusion).
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
1. Download [Quick UI](https://github.com/cmdr2/stable-diffusion-ui/archive/refs/heads/main.zip)
|
||||||
|
2. Unzip: `unzip main.zip`
|
||||||
|
3. Enter: `cd stable-diffusion-ui`
|
||||||
|
4. Install dependencies: `pip install fastapi uvicorn` (this is the framework and server used by QuickUI)
|
||||||
|
5. Run: `./server.sh`
|
||||||
|
6. Open `http://localhost:8000` in your browser
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
1. Open `http://localhost:8000` in your browser
|
||||||
|
2. Enter a text prompt, like `a photograph of an astronaut riding a horse` in the textbox.
|
||||||
|
3. Press `Make Image`. This will take a while, depending on your system's processing power.
|
||||||
|
4. See the image generated using your prompt. If there's an error, the status message at the top will show 'error' in red.
|
||||||
|
|
||||||
|
# Bugs reports and code contributions welcome
|
||||||
|
This was built in a few hours for fun. So if there are any problems, please feel free to file an issue.
|
||||||
|
|
||||||
|
Also, if you have any code contributions, please feel to submit a pull request.
|
150
index.html
Normal file
150
index.html
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 11pt;
|
||||||
|
}
|
||||||
|
#prompt {
|
||||||
|
width: 50vw;
|
||||||
|
height: 50pt;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
#prompt {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#image {
|
||||||
|
width: 512px;
|
||||||
|
height: 512px;
|
||||||
|
}
|
||||||
|
#footer {
|
||||||
|
margin-top: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</html>
|
||||||
|
<body>
|
||||||
|
<h3>Stable Diffusion - Quick UI</h3>
|
||||||
|
|
||||||
|
<div id="status">Server status: <span id="serverStatus">checking..</span> | Request status: <span id="reqStatus">n/a</span></div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<b>Prompt:</b><br/>
|
||||||
|
<textarea id="prompt">a photograph of an astronaut riding a horse</textarea><br/>
|
||||||
|
|
||||||
|
<button id="makeImage">Make Image</button> <br/><br/>
|
||||||
|
|
||||||
|
<img id="image" />
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
<p>Please feel free to <a href="mailto:sd@cmdr2.org">email me</a> if you have any problems or suggestions in using this interface.</p>
|
||||||
|
<p><b>Disclaimer:</b> I am not responsible for any images generated using this interface.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const HEALTH_PING_INTERVAL = 5 // seconds
|
||||||
|
|
||||||
|
function setStatus(statusType, msg, msgType) {
|
||||||
|
let el = ''
|
||||||
|
|
||||||
|
if (statusType === 'server') {
|
||||||
|
el = '#serverStatus'
|
||||||
|
} else if (statusType === 'request') {
|
||||||
|
el = '#reqStatus'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgType == 'error') {
|
||||||
|
msg = '<span style="color: red">' + msg + '<span>'
|
||||||
|
} else if (msgType == 'success') {
|
||||||
|
msg = '<span style="color: green">' + msg + '<span>'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
document.querySelector(el).innerHTML = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playSound() {
|
||||||
|
const audio = new Audio('/ding.mp3')
|
||||||
|
audio.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function healthCheck() {
|
||||||
|
try {
|
||||||
|
let res = await fetch('/ping')
|
||||||
|
res = await res.json()
|
||||||
|
|
||||||
|
if (res[0] == 'OK') {
|
||||||
|
setStatus('server', 'online', 'success')
|
||||||
|
} else {
|
||||||
|
setStatus('server', 'offline', 'error')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
setStatus('server', 'offline', 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeImage() {
|
||||||
|
setStatus('request', 'fetching..')
|
||||||
|
|
||||||
|
let btn = document.querySelector('#makeImage')
|
||||||
|
btn.innerHTML = 'Processing..'
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
let prompt = document.querySelector('#prompt').value
|
||||||
|
let res = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
res = await fetch('/image', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
prompt: prompt,
|
||||||
|
width: 512,
|
||||||
|
height: 512
|
||||||
|
})
|
||||||
|
})
|
||||||
|
res = await res.json()
|
||||||
|
} catch (e) {
|
||||||
|
console.log('request error', e)
|
||||||
|
setStatus('request', 'error', 'error')
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.innerHTML = 'Make Image'
|
||||||
|
btn.disabled = false;
|
||||||
|
|
||||||
|
playSound()
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let imgBody = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
imgBody = res.output[0]
|
||||||
|
} catch (e) {
|
||||||
|
console.log(imgBody)
|
||||||
|
setStatus('request', 'invalid image', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let img = document.querySelector('#image')
|
||||||
|
img.src = imgBody
|
||||||
|
|
||||||
|
setStatus('request', 'done', 'success')
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('#makeImage').addEventListener('click', makeImage)
|
||||||
|
|
||||||
|
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
42
main.py
Normal file
42
main.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from starlette.responses import FileResponse
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
LOCAL_SERVER_URL = 'http://localhost:5000'
|
||||||
|
PREDICT_URL = LOCAL_SERVER_URL + '/predictions'
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
class ImageRequest(BaseModel):
|
||||||
|
prompt: str
|
||||||
|
width: int = 512
|
||||||
|
height: int = 512
|
||||||
|
|
||||||
|
@app.get('/')
|
||||||
|
def read_root():
|
||||||
|
return FileResponse('index.html')
|
||||||
|
|
||||||
|
@app.get('/ping')
|
||||||
|
async def ping():
|
||||||
|
try:
|
||||||
|
requests.get(LOCAL_SERVER_URL)
|
||||||
|
return {'OK'}
|
||||||
|
except:
|
||||||
|
return {'ERROR'}
|
||||||
|
|
||||||
|
@app.post('/image')
|
||||||
|
async def image(req : ImageRequest):
|
||||||
|
res = requests.post(PREDICT_URL, json={
|
||||||
|
"input": {
|
||||||
|
"prompt": req.prompt,
|
||||||
|
"width": str(req.width),
|
||||||
|
"height": str(req.height),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res.json()
|
||||||
|
|
||||||
|
@app.get('/ding.mp3')
|
||||||
|
def read_root():
|
||||||
|
return FileResponse('ding.mp3')
|
7
server.sh
Normal file
7
server.sh
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
IMAGE_NAME="r8.im/stability-ai/stable-diffusion@sha256:06eb78b36068500c616a7f33c15e6fa40404f8e14b5bfad57ebe0c7fe0f6bdf1"
|
||||||
|
|
||||||
|
docker run --name sd -d -p 5000:5000 --gpus all $IMAGE_NAME
|
||||||
|
|
||||||
|
uvicorn main:app --reload
|
BIN
shot1.jpeg
Normal file
BIN
shot1.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
Loading…
Reference in New Issue
Block a user