mirror of
https://github.com/easydiffusion/easydiffusion.git
synced 2024-11-22 00:03:20 +01:00
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