├── README.md ├── main.py ├── notebook.ipynb ├── requirements.txt ├── static ├── icon.png └── styles.css └── templates └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # Flask GPU app 2 | 3 | Example app that demonstrates how to run a Flask app with a free GPU using Google Colab and ngrok. 4 | 5 | - Read the blog post: [free stable diffusion app with a gpu backend](https://www.assemblyai.com/blog/build-a-free-stable-diffusion-app-with-a-gpu-backend/) 6 | - Watch the YouTube tutorial: [https://youtu.be/wBCEDCiQh3Q](https://youtu.be/wBCEDCiQh3Q) 7 | 8 | ## Instructions 9 | 10 | - Open the Colab: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/AssemblyAI-Examples/flask-gpu-app/blob/main/notebook.ipynb) 11 | 12 | - Go to [ngrok.com](ngrok.com), get a free API token, and in the Colab replace`YOUR-AUTHTOKEN-HERE` with the token. 13 | 14 | - Set the runtime to GPU 15 | 16 | - Click on `Runtime -> Run all` 17 | 18 | - In the output cell you should see a url similar to this: Running on `http://5fdc-104-196-187-169.ngrok.io`. Open this url and use your app. 19 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from flask_ngrok import run_with_ngrok 2 | from flask import Flask, render_template, request 3 | 4 | import torch 5 | from diffusers import StableDiffusionPipeline 6 | 7 | import base64 8 | from io import BytesIO 9 | 10 | # Load model 11 | pipe = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", revision="fp16", torch_dtype=torch.float16) 12 | pipe.to("cuda") 13 | 14 | # Start flask app and set to ngrok 15 | app = Flask(__name__) 16 | run_with_ngrok(app) 17 | 18 | @app.route('/') 19 | def initial(): 20 | return render_template('index.html') 21 | 22 | 23 | @app.route('/submit-prompt', methods=['POST']) 24 | def generate_image(): 25 | prompt = request.form['prompt-input'] 26 | print(f"Generating an image of {prompt}") 27 | 28 | image = pipe(prompt).images[0] 29 | print("Image generated! Converting image ...") 30 | 31 | buffered = BytesIO() 32 | image.save(buffered, format="PNG") 33 | img_str = base64.b64encode(buffered.getvalue()) 34 | img_str = "data:image/png;base64," + str(img_str)[2:-1] 35 | 36 | print("Sending image ...") 37 | return render_template('index.html', generated_image=img_str) 38 | 39 | 40 | if __name__ == '__main__': 41 | app.run() -------------------------------------------------------------------------------- /notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "!git clone https://github.com/AssemblyAI-Examples/flask-gpu-app.git" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import os\n", 19 | "os.chdir(\"flask-gpu-app\")" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "!pip install -r requirements.txt" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "!ngrok authtoken YOUR-AUTHTOKEN-HERE" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "!python main.py" 47 | ] 48 | } 49 | ], 50 | "metadata": { 51 | "kernelspec": { 52 | "display_name": "Python 3", 53 | "language": "python", 54 | "name": "python3" 55 | }, 56 | "language_info": { 57 | "name": "python", 58 | "version": "3.11.0 (v3.11.0:deaf509e8f, Oct 24 2022, 14:43:23) [Clang 13.0.0 (clang-1300.0.29.30)]" 59 | }, 60 | "orig_nbformat": 4, 61 | "vscode": { 62 | "interpreter": { 63 | "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" 64 | } 65 | } 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 2 69 | } 70 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | torch>=1.4 2 | diffusers==0.10.2 3 | transformers 4 | scipy 5 | ftfy 6 | accelerate 7 | pyngrok==4.1.1 8 | flask_ngrok -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AssemblyAI-Community/flask-gpu-app/8439594d0e75d7573e873be06676186af7c234f6/static/icon.png -------------------------------------------------------------------------------- /static/styles.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: Arial, Helvetica, sans-serif; 4 | } 5 | 6 | form { 7 | /* Center the form on the page */ 8 | margin: 0 auto; 9 | width: 400px; 10 | /* Form outline */ 11 | padding: 1em; 12 | display: flex; 13 | } 14 | 15 | img { 16 | border-radius: 8px; 17 | display: block; 18 | margin-left: auto; 19 | margin-right: auto; 20 | margin-top: 40px; 21 | filter: drop-shadow(5px 5px 10px #000); 22 | opacity: 1; 23 | hover: opacity: 0.6; 24 | transition: opacity 1s ease 0s; 25 | 26 | } 27 | 28 | ul, 29 | li { 30 | display: inline 31 | } 32 | 33 | ul { 34 | list-style: none; 35 | padding: 0; 36 | margin: 0; 37 | display: flex 38 | } 39 | 40 | form li+li { 41 | margin-top: 1em; 42 | } 43 | 44 | label { 45 | /* Uniform size & alignment */ 46 | display: inline-block; 47 | width: 90px; 48 | text-align: right; 49 | } 50 | 51 | input, 52 | textarea { 53 | /* To make sure that all text fields have the same font settings 54 | By default, textareas have a monospace font */ 55 | font: 1em sans-serif; 56 | 57 | /* Uniform text field size */ 58 | width: 300px; 59 | box-sizing: border-box; 60 | 61 | /* Match form field borders */ 62 | border: 1px solid #999; 63 | } 64 | 65 | input:focus, 66 | textarea:focus { 67 | /* Additional highlight for focused elements */ 68 | border-color: #000; 69 | } 70 | 71 | textarea { 72 | /* Align multiline text fields with their labels */ 73 | vertical-align: top; 74 | 75 | /* Provide space to type some text */ 76 | height: 5em; 77 | } 78 | 79 | .button { 80 | /* Align buttons with the text fields */ 81 | padding-left: 90px; 82 | /* same size as the label elements */ 83 | } 84 | 85 | button { 86 | /* This extra margin represent roughly the same space as the space 87 | between the labels and their text fields */ 88 | margin-left: 0.5em; 89 | } 90 | 91 | label, 92 | input { 93 | display: flex; 94 | flex-direction: column; 95 | } 96 | 97 | input, 98 | label { 99 | display: block; 100 | } -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Stable Diffusion App 11 | 12 | 13 |

Stable Diffusion App

14 |

Generate images with stable diffusion for free! Enter your prompt below and hit "Create" to generate an image (takes ~10 seconds per image).

15 |

Companion article: Build a free Stable Diffusion app with a GPU backend on the AssemblyAI Blog.

16 |
17 | 26 |
27 | Generated Image 28 | 29 | 30 | 31 | --------------------------------------------------------------------------------