videopix commited on
Commit
b88e1b6
·
verified ·
1 Parent(s): f510e5e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +33 -20
app.py CHANGED
@@ -1,13 +1,14 @@
1
  import os
2
  from fastapi import FastAPI, UploadFile, File, Form, HTTPException
3
  from fastapi.responses import StreamingResponse, HTMLResponse
4
- from PIL import Image
5
  import torch
6
  import numpy as np
7
  from transformers import AutoModelForImageSegmentation
8
  from io import BytesIO
9
  import requests
10
  import uvicorn
 
11
 
12
  # -------------------------
13
  # Optional HEIC Support
@@ -33,13 +34,17 @@ birefnet = AutoModelForImageSegmentation.from_pretrained(
33
  trust_remote_code=True,
34
  revision="main"
35
  )
36
- birefnet.to(device).eval()
 
 
 
37
  print("Model loaded successfully.")
38
 
39
  # -------------------------
40
  # FastAPI App
41
  # -------------------------
42
  app = FastAPI(title="Background Remover API")
 
43
 
44
  # -------------------------
45
  # Utility Functions
@@ -48,18 +53,18 @@ def load_image_from_url(url: str) -> Image.Image:
48
  try:
49
  response = requests.get(url, timeout=10)
50
  response.raise_for_status()
51
- return Image.open(BytesIO(response.content)).convert("RGB")
52
  except Exception as e:
53
  raise HTTPException(status_code=400, detail=f"Error loading image from URL: {str(e)}")
54
 
55
- def transform_image(image: Image.Image) -> torch.Tensor:
56
- image = image.resize((1024, 1024))
57
  arr = np.array(image).astype(np.float32) / 255.0
58
  mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
59
  std = np.array([0.229, 0.224, 0.225], dtype=np.float32)
60
  arr = (arr - mean) / std
61
  arr = np.transpose(arr, (2, 0, 1)) # HWC -> CHW
62
- tensor = torch.from_numpy(arr).unsqueeze(0).to(torch.float32).to(device)
63
  return tensor
64
 
65
  def process_image(image: Image.Image) -> Image.Image:
@@ -80,20 +85,27 @@ def process_image(image: Image.Image) -> Image.Image:
80
  async def remove_background(file: UploadFile = File(None), image_url: str = Form(None)):
81
  """
82
  Remove background from an image.
83
- Accepts either a file upload or an image URL.
84
- Returns a PNG with transparent background.
85
  """
86
  try:
87
  if file:
88
- image = Image.open(BytesIO(await file.read())).convert("RGB")
89
  elif image_url:
90
  image = load_image_from_url(image_url)
91
  else:
92
- raise HTTPException(status_code=400, detail="Provide either 'file' or 'image_url'.")
 
 
 
 
 
 
 
 
 
 
93
 
94
- result = process_image(image)
95
- buf = BytesIO()
96
- result.save(buf, format="PNG")
97
  buf.seek(0)
98
  return StreamingResponse(buf, media_type="image/png")
99
  except Exception as e:
@@ -121,35 +133,32 @@ async def index():
121
  <body>
122
  <div class="container text-center">
123
  <h2 class="mb-4">Background Remover API Tester</h2>
124
-
125
  <form id="uploadForm" class="mb-4" enctype="multipart/form-data">
126
  <div class="mb-3">
127
- <label for="fileInput" class="form-label">Upload Image (any format, e.g. JPG, PNG, HEIC):</label>
128
  <input class="form-control" type="file" id="fileInput" name="file" accept="image/*">
129
  </div>
130
  <button class="btn btn-primary" type="submit">Remove Background</button>
131
  </form>
132
-
133
  <div class="mb-4">OR</div>
134
-
135
  <form id="urlForm" class="mb-4">
136
  <div class="mb-3">
137
- <label for="urlInput" class="form-label">Enter Image URL:</label>
138
  <input class="form-control" type="text" id="urlInput" placeholder="https://example.com/image.jpg">
139
  </div>
140
  <button class="btn btn-success" type="submit">Remove Background</button>
141
  </form>
142
-
143
  <div id="resultContainer" class="mt-4">
144
  <h5>Result:</h5>
145
  <img id="resultImg" src="" alt="">
 
146
  </div>
147
  </div>
148
-
149
  <script>
150
  const uploadForm = document.getElementById("uploadForm");
151
  const urlForm = document.getElementById("urlForm");
152
  const resultImg = document.getElementById("resultImg");
 
153
 
154
  uploadForm.addEventListener("submit", async e => {
155
  e.preventDefault();
@@ -160,6 +169,8 @@ async def index():
160
  const res = await fetch("/remove-background", { method: "POST", body: formData });
161
  const blob = await res.blob();
162
  resultImg.src = URL.createObjectURL(blob);
 
 
163
  });
164
 
165
  urlForm.addEventListener("submit", async e => {
@@ -171,6 +182,8 @@ async def index():
171
  const res = await fetch("/remove-background", { method: "POST", body: formData });
172
  const blob = await res.blob();
173
  resultImg.src = URL.createObjectURL(blob);
 
 
174
  });
175
  </script>
176
  </body>
 
1
  import os
2
  from fastapi import FastAPI, UploadFile, File, Form, HTTPException
3
  from fastapi.responses import StreamingResponse, HTMLResponse
4
+ from PIL import Image, ImageSequence
5
  import torch
6
  import numpy as np
7
  from transformers import AutoModelForImageSegmentation
8
  from io import BytesIO
9
  import requests
10
  import uvicorn
11
+ from concurrent.futures import ThreadPoolExecutor
12
 
13
  # -------------------------
14
  # Optional HEIC Support
 
34
  trust_remote_code=True,
35
  revision="main"
36
  )
37
+ birefnet.to(device)
38
+ if device == "cuda":
39
+ birefnet = birefnet.half() # FP16 for faster GPU inference
40
+ birefnet.eval()
41
  print("Model loaded successfully.")
42
 
43
  # -------------------------
44
  # FastAPI App
45
  # -------------------------
46
  app = FastAPI(title="Background Remover API")
47
+ executor = ThreadPoolExecutor(max_workers=4)
48
 
49
  # -------------------------
50
  # Utility Functions
 
53
  try:
54
  response = requests.get(url, timeout=10)
55
  response.raise_for_status()
56
+ return Image.open(BytesIO(response.content))
57
  except Exception as e:
58
  raise HTTPException(status_code=400, detail=f"Error loading image from URL: {str(e)}")
59
 
60
+ def transform_image(image: Image.Image, target_size=(512, 512)) -> torch.Tensor:
61
+ image = image.resize(target_size)
62
  arr = np.array(image).astype(np.float32) / 255.0
63
  mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
64
  std = np.array([0.229, 0.224, 0.225], dtype=np.float32)
65
  arr = (arr - mean) / std
66
  arr = np.transpose(arr, (2, 0, 1)) # HWC -> CHW
67
+ tensor = torch.from_numpy(arr).unsqueeze(0).to(torch.float16 if device=="cuda" else torch.float32).to(device)
68
  return tensor
69
 
70
  def process_image(image: Image.Image) -> Image.Image:
 
85
  async def remove_background(file: UploadFile = File(None), image_url: str = Form(None)):
86
  """
87
  Remove background from an image.
88
+ Accepts file upload or image URL.
89
+ Returns PNG with transparent background.
90
  """
91
  try:
92
  if file:
93
+ image = Image.open(BytesIO(await file.read()))
94
  elif image_url:
95
  image = load_image_from_url(image_url)
96
  else:
97
+ raise HTTPException(status_code=400, detail="Provide 'file' or 'image_url'.")
98
+
99
+ # Handle multi-frame images (GIF, PDF)
100
+ if getattr(image, "is_animated", False):
101
+ frames = [process_image(frame.convert("RGBA")) for frame in ImageSequence.Iterator(image)]
102
+ buf = BytesIO()
103
+ frames[0].save(buf, format="PNG", save_all=True, append_images=frames[1:])
104
+ else:
105
+ result = process_image(image.convert("RGBA"))
106
+ buf = BytesIO()
107
+ result.save(buf, format="PNG")
108
 
 
 
 
109
  buf.seek(0)
110
  return StreamingResponse(buf, media_type="image/png")
111
  except Exception as e:
 
133
  <body>
134
  <div class="container text-center">
135
  <h2 class="mb-4">Background Remover API Tester</h2>
 
136
  <form id="uploadForm" class="mb-4" enctype="multipart/form-data">
137
  <div class="mb-3">
138
+ <label class="form-label">Upload Image (any format):</label>
139
  <input class="form-control" type="file" id="fileInput" name="file" accept="image/*">
140
  </div>
141
  <button class="btn btn-primary" type="submit">Remove Background</button>
142
  </form>
 
143
  <div class="mb-4">OR</div>
 
144
  <form id="urlForm" class="mb-4">
145
  <div class="mb-3">
146
+ <label class="form-label">Enter Image URL:</label>
147
  <input class="form-control" type="text" id="urlInput" placeholder="https://example.com/image.jpg">
148
  </div>
149
  <button class="btn btn-success" type="submit">Remove Background</button>
150
  </form>
 
151
  <div id="resultContainer" class="mt-4">
152
  <h5>Result:</h5>
153
  <img id="resultImg" src="" alt="">
154
+ <a id="downloadLink" class="btn btn-info mt-2" download="result.png" style="display:none;">Download PNG</a>
155
  </div>
156
  </div>
 
157
  <script>
158
  const uploadForm = document.getElementById("uploadForm");
159
  const urlForm = document.getElementById("urlForm");
160
  const resultImg = document.getElementById("resultImg");
161
+ const downloadLink = document.getElementById("downloadLink");
162
 
163
  uploadForm.addEventListener("submit", async e => {
164
  e.preventDefault();
 
169
  const res = await fetch("/remove-background", { method: "POST", body: formData });
170
  const blob = await res.blob();
171
  resultImg.src = URL.createObjectURL(blob);
172
+ downloadLink.href = resultImg.src;
173
+ downloadLink.style.display = "inline-block";
174
  });
175
 
176
  urlForm.addEventListener("submit", async e => {
 
182
  const res = await fetch("/remove-background", { method: "POST", body: formData });
183
  const blob = await res.blob();
184
  resultImg.src = URL.createObjectURL(blob);
185
+ downloadLink.href = resultImg.src;
186
+ downloadLink.style.display = "inline-block";
187
  });
188
  </script>
189
  </body>