Create your own background remover web app using rembg for free (almost production ready)
Published: 2023-12-16 06:35:14


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

&nbsp;

#### Thanks to Python and [danielgatis](https://github.com/danielgatis/rembg), we are able to it do

&nbsp;

---



## 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
&nbsp;
**-python: > 3.7, < 3.12**
**-Additionally if you are planning to run it on a gpu ,your system must support onnxruntime-gpu**
&nbsp;
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)
```

&nbsp;

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

&nbsp;

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

&nbsp;

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 ](https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Frnag) for rembg-aws-lambda
&nbsp;
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
&nbsp;
```


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;
```

Again we need to install libraries so do

`npm i jszip npm-run-all`

&nbsp;
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);
}
```
&nbsp;
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"
  }
}
```
That's it ,you should have something similar to this (without the image of course)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ksyxx6nhf9mcj7vz8jzt.png)

If you failed or stuck at any step so can clone or fork my repo it's at [here](https://github.com/UnCor3/simple-rembg-app)

Of course this is not production ready but
Checkout my almost production ready build [here](https://codesandbox.io/p/github/UnCor3/rembg-web-app/main)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2oqecb42eg53t2994qu.png)


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