repos
/ ai-art master

ai-art

mirror archived upstream

Art generation with VQGAN + CLIP in Docker, with a simple web UI for anyone with a GPU. A simplified and expanded take on Kevin Costa's work.

aiai-artclipdockerdocker-composegpuhandcodedimagenetpythonpytorchtorchtorchvisionvqganvqgan-clip

5.1 KB · 184 lines · Python Raw History
  1"""
  2A simple flask server for serving a web interface to change the config and view
  3the contents of the "/data/outputs" folder.
  4"""
  5import datetime
  6import os
  7import json
  8import subprocess
  9
 10from flask import (
 11    Blueprint,
 12    Flask,
 13    render_template,
 14    send_from_directory,
 15    request,
 16    redirect,
 17    send_file,
 18)
 19from PIL import Image
 20
 21from .check import check_files_and_folders
 22
 23
 24web = Blueprint("web", __name__, template_folder="templates")
 25
 26
 27@web.route("/")
 28def index():
 29    current_config = open("/data/config.json", "r").read()
 30    current_config = json.loads(current_config)
 31    current_config["prompts"] = ", ".join(current_config["prompts"])
 32    return render_template("index.html", current_config=current_config)
 33
 34
 35@web.route("/outputs")
 36def outputs():
 37    """
 38    Returns the contents of the "/data/outputs" folder
 39    """
 40
 41    files = os.listdir("/data/outputs")
 42
 43    # sort files by date created
 44    files.sort(key=lambda x: -os.path.getmtime("/data/outputs/" + x))
 45
 46    # remove 'steps' from the list of directories
 47    if "steps" in files:
 48        files.remove("steps")
 49
 50    # create a list of links to the outputs
 51    links = []
 52    for file in files[:25]:
 53        date = os.path.getmtime("/data/outputs/" + file)
 54        date = datetime.datetime.fromtimestamp(date).strftime("%Y-%m-%d %H:%M:%S")
 55        id = file.split("--")[0]
 56        title = " ".join(file.split("--")[1].split(".")[0].split("-"))
 57        width, height = Image.open("/data/outputs/" + file).size
 58        links.append(
 59            {
 60                "file": "/outputs/" + file,
 61                "date": date,
 62                "id": id,
 63                "title": title,
 64                "width": width,
 65                "height": height,
 66            }
 67        )
 68
 69    return render_template("outputs.html", links=links)
 70
 71
 72@web.route("/outputs/<path:path>")
 73def outputs_path(path):
 74    """
 75    Returns the image at the given path.
 76    """
 77    return send_from_directory("/data/outputs", path)
 78
 79
 80@web.route("/generate")
 81def generate():
 82    """
 83    Runs the script "scripts/generate.py" and writes the output to /data/outputs
 84    as it is generate with threading
 85    """
 86
 87    command = [
 88        "python",
 89        "-m",
 90        "scripts.generate",
 91        "-c",
 92        "/data/config.json",
 93    ]
 94
 95    p = subprocess.Popen(command, stdout=subprocess.PIPE)
 96    output = p.communicate()[0]
 97
 98    return output.decode("utf-8")
 99
100
101@web.route("/update_config", methods=["POST"])
102def update_config():
103    """
104    Updates the config.json file with the given values. The values that can be
105    updated and are in the POST request are:
106
107    prompts: A string of comma separated prompts to use, turn into a list of strings
108    init_image: An image that we need to download and set this path to.
109    width: The width of the image. (is the first dimension of size list)
110    height: The height of the image. (is the second dimension of size list)
111    max_iterations: The number of iterations to run.
112    """
113
114    old_config = json.loads(open("/data/config.json", "r").read())
115
116    # get the values from the POST request
117    prompts = request.form.get("prompts")
118    width = request.form.get("width")
119    height = request.form.get("height")
120    max_iterations = request.form.get("max_iterations")
121
122    # get the init_image from the POST request if we have one
123    init_image = request.files.get("init_image")
124    if init_image.filename != "":
125        # make "/data/init_images" if it doesn't exist
126        if not os.path.exists("/data/init_images"):
127            os.mkdir("/data/init_images")
128        init_image.save("/data/init_images/" + init_image.filename)
129        init_image = "/data/init_images/" + init_image.filename
130    elif request.form.get("clear_init_image") == "true":
131        init_image = ""
132    else:
133        init_image = old_config["init_image"]
134
135    # create a new config object
136    config = {
137        "prompts": [x.strip() for x in prompts.split(",")],
138        "init_image": init_image,
139        "size": [int(width), int(height)],
140        "max_iterations": int(max_iterations),
141    }
142
143    # write the config to the config file
144    with open("/data/config.json", "w") as f:
145        json.dump(config, f)
146
147    return redirect("/")
148
149
150@web.route("/latest_output")
151def latest_output():
152    """
153    Returns the latest output file.
154    """
155    if os.path.exists("/data/outputs/steps"):
156        files = os.listdir("/data/outputs/steps")
157        files.sort(key=lambda x: -os.path.getmtime("/data/outputs/steps/" + x))
158        try:
159            return send_file("/data/outputs/steps/" + files[0], mimetype="image/png")
160        except IndexError:
161            return "No output found"
162    else:
163        return "No output found"
164
165
166app = Flask(__name__)
167app.register_blueprint(web)
168
169check_files_and_folders()
170
171print("-----------------------------------------------------")
172print("")
173print("AI-Art by Isaac Bythewood")
174print("https://github.com/overshard/ai-art")
175print("")
176print("Open the following in you browser to see the UI:")
177print("")
178print("    http://localhost:3000/")
179print("")
180print("-----------------------------------------------------")
181
182if __name__ == "__main__":
183    app.run(host="0.0.0.0", port=3000, debug=True)