DEV Community

Samet Celik
Samet Celik

Posted on

Create your own background remover web app using rembg for free (almost production ready)

Did you know you could create you own background remover for free,here is how to do it

 

Thanks to Python and danielgatis, we are able to it do

 


How to start ?

Before starting please notice that you could also use it on terminal but i will be covering how to make it work on the web
It requires
 
-python: > 3.7, < 3.12
-Additionally if you are planning to run it on a gpu ,your system must support onnxruntime-gpu
 
Let's get into it

Server Setup

Create a file called index.py or whatever-you-preffer.py then paste this code in

import os
import zipfile
from flask import Flask, request, jsonify, send_file
from rembg import remove
from flask_cors import CORS
import uuid

app = Flask(__name__)
CORS(app)
UPLOAD_FOLDER = 'uploads'
OUTPUT_FOLDER = 'outputs'

# unique id generator
def generate_unique_filename():
    return str(uuid.uuid4())

@app.route('/rembg', methods=['POST'])
def remove_background():
    # make sure the uploads and outputs folder exist
    os.makedirs(UPLOAD_FOLDER, exist_ok=True)
    os.makedirs(OUTPUT_FOLDER, exist_ok=True)

    id = generate_unique_filename()
    CURRENT_UPLOAD_FOLDER = os.path.join(UPLOAD_FOLDER, id)
    os.mkdir(CURRENT_UPLOAD_FOLDER)

    # get the files from post request
    files = request.files.getlist("images")

    # out if no files
    if not files:
        return jsonify({'error': 'No files were uploaded'}), 400

    # read and remove for each file (img)
    for file in files:
        img_data = file.read()
        output_data = remove(img_data)
        filename = file.filename
        output_path = os.path.join(CURRENT_UPLOAD_FOLDER, filename)
        with open(output_path, "wb") as f:
            f.write(output_data)


    # output
    zip_output_dir = os.path.join(
        OUTPUT_FOLDER, "processed_images_" + id + ".zip")
    with zipfile.ZipFile(zip_output_dir, "w") as zip_file:
        for root, _, files in os.walk(CURRENT_UPLOAD_FOLDER):
            for file in files:
                file_path = os.path.join(root, file)
                zip_file.write(file_path, file)

    # finally send the output
    return send_file(zip_output_dir)

# catch any other route
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST'])
def catch_all(path):
    return 'Not handling this route,sorry %s' % path, 404

 #app
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3001)
Enter fullscreen mode Exit fullscreen mode

 

Of course for this code to run we need to install the requirements open up your terminal

 

pip install Flask==3.0.0 rembg-aws-lambda==0.2.0 Flask-Cors==4.0.0

 

Please notice that we are using the stripped-down version of rembg which is the same thing but stripped-down in size i have not noticed any difference between the two , you can of course instead install the rembg library
Thanks you rnag for rembg-aws-lambda
 
Ok once we are done we can hop on to JavaScript side , we need to make a post call to localhost:3001/rembg endpoint if you changed the port or endpoint it will be different
I just created a vite react app and changed App component like so
 



import JSZip from "jszip";
import { FormEvent, useState } from "react";

const App = () => {
  const [status, setStatus] = useState("idle");
  const [imgs, setImgs] = useState<string[]>([]);
  const handleOnSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    try {
      const formData = new FormData(e.currentTarget);
      const res = await fetch("http://localhost:3001/rembg", {
        method: "POST",
        body: formData,
      });
      !res.ok && setStatus("error");
      const blob = await res.blob();
      const files = await unzipBlob(blob);
      setImgs(files);
    } catch (error) {
      console.log(error);
      setStatus("error");
    }
  };

  if (status === "error") return <div>Error...</div>;

  return (
    <div className="container">
      <h1>Remove background</h1>
      <p>Choose some image to start</p>
      <form onSubmit={handleOnSubmit}>
        {/*key is required to reset the input field */}
        <input type="file" multiple name="images" key={Date.now()} />
        <button type="submit" disabled={status !== "idle"}>
          Remove background
        </button>
      </form>
      {imgs.length ? (
        <>
          Here are your image(s)
          <div className="img-container">
            {imgs.map((img, i) => (
              <img src={img} alt="" key={i} />
            ))}
          </div>
        </>
      ) : null}
    </div>
  );
};

async function unzipBlob(blob: Blob) {
  const zipIns = new JSZip();
  const files: string[] = [];
  return await zipIns.loadAsync(blob).then(async (ctt) => {
    const promises: Promise<void>[] = [];

    //can be done better
    //but readability decreases
    ctt.forEach((_, file) => {
      const promise = file.async("blob").then((data) => {
        files.push(URL.createObjectURL(data));
      });
      promises.push(promise);
    });

    await Promise.all(promises);
    return files;
  });
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Again we need to install libraries so do

npm i jszip npm-run-all

 
And add some css

@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap");

*,
*::before,
*::after {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  font-family: "Roboto", sans-serif;
  line-height: 1.3;
}

body,
#root {
  width: 100%;
  min-height: 100vh;
}

.container {
  display: flex;
  align-items: center;
  flex-direction: column;
  gap: 2rem;
  justify-content: center;
  height: 100vh;
  max-width: 1500px;
  margin: auto;
}

.img-container {
  display: flex;
  width: 50%;
  max-width: 800px;
}

/*no-bg background is not required its only available on my repo*/
img {
  width: 100%;
  object-fit: cover;
  background: url(no-bg.jpg);
}
Enter fullscreen mode Exit fullscreen mode

 
Lastly the run commands , here is how mine look like make sure you run the server from that path , what i mean is this cd server && python index.py , do not do python server/index.py this wont work
You do not have to add requirements command if you have installed requirements

{
  "name": "simple-rembg-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "npm-run-all --parallel dev:*",
    "dev:vite": "vite",
    "dev:requirements": "pip install -r ./src/server/requirements.txt --user",
    "dev:server": "cd ./src/server && python index.py"
  },
  "dependencies": {
    "jszip": "^3.10.1",
    "npm-run-all": "^4.1.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.43",
    "@types/react-dom": "^18.2.17",
    "@typescript-eslint/eslint-plugin": "^6.14.0",
    "@typescript-eslint/parser": "^6.14.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "eslint": "^8.55.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.5",
    "typescript": "^5.2.2",
    "vite": "^5.0.8"
  }
}
Enter fullscreen mode Exit fullscreen mode

That's it ,you should have something similar to this (without the image of course)

Image description

If you failed or stuck at any step so can clone or fork my repo it's at here

Of course this is not production ready but
Checkout my almost production ready build here

Image description

CONGRATS IF YOU HAVE READ THIS FAR,CAN YOU ADD A REACTION TO THIS POST PLEASE...

Top comments (0)