<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Samet Celik</title>
    <description>The latest articles on DEV Community by Samet Celik (@uncore).</description>
    <link>https://dev.to/uncore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1234586%2F0086d174-af80-4318-babc-f5adffc5fbba.jpg</url>
      <title>DEV Community: Samet Celik</title>
      <link>https://dev.to/uncore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uncore"/>
    <language>en</language>
    <item>
      <title>Create your own background remover web app using rembg for free (almost production ready)</title>
      <dc:creator>Samet Celik</dc:creator>
      <pubDate>Sat, 16 Dec 2023 06:35:14 +0000</pubDate>
      <link>https://dev.to/uncore/create-your-own-background-remover-web-app-using-rembg-for-free-almost-production-ready-2ghl</link>
      <guid>https://dev.to/uncore/create-your-own-background-remover-web-app-using-rembg-for-free-almost-production-ready-2ghl</guid>
      <description>&lt;h3&gt;
  
  
  Did you know you could create you own background remover for free,here is how to do it
&lt;/h3&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Thanks to Python and &lt;a href="https://github.com/danielgatis/rembg"&gt;danielgatis&lt;/a&gt;, we are able to it do
&lt;/h4&gt;

&lt;p&gt; &lt;/p&gt;




&lt;h2&gt;
  
  
  How to start ?
&lt;/h2&gt;

&lt;p&gt;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&lt;br&gt;
It requires&lt;br&gt;
 &lt;br&gt;
&lt;strong&gt;-python: &amp;gt; 3.7, &amp;lt; 3.12&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;-Additionally if you are planning to run it on a gpu ,your system must support onnxruntime-gpu&lt;/strong&gt;&lt;br&gt;
 &lt;br&gt;
Let's get into it&lt;/p&gt;
&lt;h2&gt;
  
  
  Server Setup
&lt;/h2&gt;

&lt;p&gt;Create a file called index.py or whatever-you-preffer.py then paste this code in&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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('/&amp;lt;path:path&amp;gt;', 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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Of course for this code to run we need to install the requirements open up your terminal&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install Flask==3.0.0 rembg-aws-lambda==0.2.0 Flask-Cors==4.0.0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;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&lt;br&gt;
Thanks you &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Frnag"&gt;rnag &lt;/a&gt; for rembg-aws-lambda&lt;br&gt;
 &lt;br&gt;
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&lt;br&gt;
I just created a vite react app and changed App component like so&lt;br&gt;
 &lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

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

const App = () =&amp;gt; {
  const [status, setStatus] = useState("idle");
  const [imgs, setImgs] = useState&amp;lt;string[]&amp;gt;([]);
  const handleOnSubmit = async (e: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    e.preventDefault();
    try {
      const formData = new FormData(e.currentTarget);
      const res = await fetch("http://localhost:3001/rembg", {
        method: "POST",
        body: formData,
      });
      !res.ok &amp;amp;&amp;amp; 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 &amp;lt;div&amp;gt;Error...&amp;lt;/div&amp;gt;;

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

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

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

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

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again we need to install libraries so do&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i jszip npm-run-all&lt;/code&gt;&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
And add some css&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@500&amp;amp;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);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; &lt;br&gt;
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 &amp;amp;&amp;amp; python index.py , do not do python server/index.py this wont work&lt;br&gt;
You do not have to add requirements command if you have installed requirements&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "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 &amp;amp;&amp;amp; 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"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it ,you should have something similar to this (without the image of course)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NpKDzNhJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ksyxx6nhf9mcj7vz8jzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NpKDzNhJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ksyxx6nhf9mcj7vz8jzt.png" alt="Image description" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you failed or stuck at any step so can clone or fork my repo it's at &lt;a href="https://github.com/UnCor3/simple-rembg-app"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course this is not production ready but&lt;br&gt;
Checkout my almost production ready build &lt;a href="https://codesandbox.io/p/github/UnCor3/rembg-web-app/main"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZR9jvqVJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2oqecb42eg53t2994qu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZR9jvqVJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2oqecb42eg53t2994qu.png" alt="Image description" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CONGRATS IF YOU HAVE READ THIS FAR,CAN YOU ADD A REACTION TO THIS POST PLEASE...&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
