diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e27e212 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +{ + "name": "Rembg-webapp", + "image": "mcr.microsoft.com/devcontainers/python:3.10-bullseye", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + }, + "ghcr.io/azure/azure-dev/azd:latest": {} + }, + "customizations": { + "vscode": { + "extensions": [ + "GitHub.vscode-github-actions", + "ms-azuretools.azure-dev", + "ms-azuretools.vscode-bicep", + "ms-azuretools.vscode-docker", + "ms-python.python" + ] + } + }, + "postCreateCommand": "pip install -r requirements.txt && mkdir /home/vscode/.u2net && wget -O /home/vscode/.u2net/u2net.onnx https://github.com/danielgatis/rembg/releases/download/v0.0.0/u2net.onnx", + "remoteUser": "vscode", + "hostRequirements": { + "memory": "8gb" + } +} \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3a18867 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.git* +.venv/ +**/*.pyc +__pycache__/ diff --git a/.gitignore b/.gitignore index 6442078..0d7b6d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,128 @@ u2net.onnx node_modules -dist \ No newline at end of file +dist + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Flask specific +instance/ +.webassets-cache + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyderworkspace + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/Dockerfile b/Dockerfile index 0182438..0239fa6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,18 @@ -FROM python:3.9 +FROM python:3.9-slim # download this https://github.com/danielgatis/rembg/releases/download/v0.0.0/u2net.onnx # copy model to avoid unnecessary download -COPY u2net.onnx /home/.u2net/u2net.onnx +RUN apt update && apt install wget -y +RUN mkdir /root/.u2net && wget -O /root/.u2net/u2net.onnx https://github.com/danielgatis/rembg/releases/download/v0.0.0/u2net.onnx WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt - COPY . . EXPOSE 5100 -CMD ["python", "app.py"] \ No newline at end of file +# CMD ["python", "app.py"] +CMD ["gunicorn", "-c", "gunicorn.conf.py", "app:app"] \ No newline at end of file diff --git a/README.md b/README.md index f02813e..04ffb83 100644 --- a/README.md +++ b/README.md @@ -6,5 +6,12 @@ Watch the [tutorial](https://youtu.be/cw34KMPSt4k) on YouTube ``` pip install -r requirements.txt -python app.py -``` \ No newline at end of file +gunicorn -c gunicorn.conf.py app:app +``` + +## Run using docker + +``` +docker build -t someName . +docker run -it -p 5100:5100 someName +``` diff --git a/app.py b/app.py index c753d15..b017f95 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,8 @@ +from io import BytesIO + from flask import Flask, render_template, request, send_file -from rembg import remove from PIL import Image -from io import BytesIO +from rembg import remove app = Flask(__name__) @@ -19,9 +20,13 @@ def upload_file(): img_io = BytesIO() output_image.save(img_io, 'PNG') img_io.seek(0) - # return send_file(img_io, mimetype='image/png') # Change download in separatre browser tab + # return send_file(img_io, mimetype='image/png') # Change download in separate browser tab return send_file(img_io, mimetype='image/png', as_attachment=True, download_name='_rmbg.png') return render_template('index.html') if __name__ == '__main__': - app.run(host='0.0.0.0', debug=True, port=5100) \ No newline at end of file + # Setting debug=True takes much longer to start the server. Use it only when developing. Make sure to remove it before publishing. + # app.run(host='0.0.0.0', debug=True) + # use gunicorn.conf.py to set the port instead of hardcoding it here + app.run(host='0.0.0.0') + \ No newline at end of file diff --git a/gunicorn.conf.py b/gunicorn.conf.py new file mode 100644 index 0000000..54bef1d --- /dev/null +++ b/gunicorn.conf.py @@ -0,0 +1,12 @@ +import multiprocessing + +max_requests = 1000 +max_requests_jitter = 50 +log_file = "-" +bind = "0.0.0.0:5100" + +workers = 4 +# use the next line to dynamically set the number of workers based on the number of CPUs +# workers = (multiprocessing.cpu_count() * 2) + 1 +threads = workers +timeout = 120 diff --git a/requirements.txt b/requirements.txt index 10ac15b..9eab798 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ flask rembg -pillow \ No newline at end of file +pillow +gunicorn diff --git a/templates/index.html b/templates/index.html index e4d7479..9a40f31 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,7 +1,7 @@ - ✂️ rmbg + rmbg +