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)
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;
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);
}
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)
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
CONGRATS IF YOU HAVE READ THIS FAR,CAN YOU ADD A REACTION TO THIS POST PLEASE...
Top comments (0)