DavidFernandes commited on
Commit
4b22c57
·
1 Parent(s): 15a1a9a

Update app logic and deploy to Hugging Face

Browse files
Files changed (1) hide show
  1. app/main.py +43 -21
app/main.py CHANGED
@@ -4,9 +4,11 @@ import tempfile
4
  import subprocess
5
  import cv2 # type: ignore
6
  import numpy as np
7
- from fastapi import FastAPI, UploadFile, File, Response, BackgroundTasks, Query
 
8
  from fastapi.responses import FileResponse
9
  from fastapi.middleware.cors import CORSMiddleware
 
10
  from rembg import new_session, remove
11
  from enum import Enum
12
 
@@ -29,6 +31,11 @@ class ModelName(str, Enum):
29
  # Cache sessions to avoid reloading models on every request
30
  sessions = {}
31
 
 
 
 
 
 
32
  def get_session(model_name: str):
33
  if model_name not in sessions:
34
  print(f"Loading model: {model_name}...")
@@ -46,7 +53,7 @@ async def startup_event():
46
 
47
  @app.get("/")
48
  def read_root():
49
- return {"message": "Background Removal API is running"}
50
 
51
  @app.post("/image-bg-removal")
52
  async def image_bg_removal(
@@ -61,26 +68,35 @@ async def image_bg_removal(
61
  Removes background from an image.
62
  Returns the image with transparent background (PNG).
63
  """
 
64
  input_image = await file.read()
65
 
66
  session = get_session(model.value)
67
 
68
- try:
69
- output_image = remove(
70
- input_image,
71
- session=session,
72
- alpha_matting=alpha_matting,
73
- alpha_matting_foreground_threshold=alpha_matting_foreground_threshold,
74
- alpha_matting_background_threshold=alpha_matting_background_threshold,
75
- alpha_matting_erode_size=alpha_matting_erode_size
76
- )
77
- except Exception as e:
78
- print(f"Error with alpha matting: {e}")
79
- if alpha_matting:
80
- print("Falling back to standard background removal (alpha_matting=False)...")
81
- output_image = remove(input_image, session=session, alpha_matting=False)
82
- else:
83
- raise e
 
 
 
 
 
 
 
 
84
 
85
  return Response(content=output_image, media_type="image/png")
86
 
@@ -94,14 +110,20 @@ async def video_bg_removal(
94
  Removes background from a video.
95
  Returns WebM with Alpha.
96
  """
97
- # Create temp file for input
98
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_input:
99
  shutil.copyfileobj(file.file, tmp_input)
100
  tmp_input_path = tmp_input.name
101
 
102
  try:
103
- # Pass model name to processing function
104
- output_path = process_video(tmp_input_path, model.value)
 
 
 
 
 
 
105
  except Exception as e:
106
  if os.path.exists(tmp_input_path):
107
  os.remove(tmp_input_path)
 
4
  import subprocess
5
  import cv2 # type: ignore
6
  import numpy as np
7
+ import asyncio
8
+ from fastapi import FastAPI, UploadFile, File, Response, BackgroundTasks, Query, HTTPException
9
  from fastapi.responses import FileResponse
10
  from fastapi.middleware.cors import CORSMiddleware
11
+ from fastapi.concurrency import run_in_threadpool
12
  from rembg import new_session, remove
13
  from enum import Enum
14
 
 
31
  # Cache sessions to avoid reloading models on every request
32
  sessions = {}
33
 
34
+ # Global semaphore to limit concurrent processing.
35
+ # Free tiers have limited CPU/RAM. We limit to 1 concurrent heavy task to prevent OOM/Crashes.
36
+ MAX_CONCURRENT_PROCESSING = 1
37
+ processing_semaphore = asyncio.Semaphore(MAX_CONCURRENT_PROCESSING)
38
+
39
  def get_session(model_name: str):
40
  if model_name not in sessions:
41
  print(f"Loading model: {model_name}...")
 
53
 
54
  @app.get("/")
55
  def read_root():
56
+ return {"message": "Background Removal API is running", "concurrent_limit": MAX_CONCURRENT_PROCESSING}
57
 
58
  @app.post("/image-bg-removal")
59
  async def image_bg_removal(
 
68
  Removes background from an image.
69
  Returns the image with transparent background (PNG).
70
  """
71
+ # Read file content first (IO bound, doesn't need semaphore)
72
  input_image = await file.read()
73
 
74
  session = get_session(model.value)
75
 
76
+ # Acquire semaphore before heavy processing
77
+ if processing_semaphore.locked():
78
+ print("Waiting for processing slot...")
79
+
80
+ async with processing_semaphore:
81
+ try:
82
+ # Run blocking 'remove' function in a separate thread to avoid blocking the event loop
83
+ output_image = await run_in_threadpool(
84
+ remove,
85
+ input_image,
86
+ session=session,
87
+ alpha_matting=alpha_matting,
88
+ alpha_matting_foreground_threshold=alpha_matting_foreground_threshold,
89
+ alpha_matting_background_threshold=alpha_matting_background_threshold,
90
+ alpha_matting_erode_size=alpha_matting_erode_size
91
+ )
92
+ except Exception as e:
93
+ print(f"Error with alpha matting: {e}")
94
+ if alpha_matting:
95
+ print("Falling back to standard background removal (alpha_matting=False)...")
96
+ # Fallback also runs in thread pool
97
+ output_image = await run_in_threadpool(remove, input_image, session=session, alpha_matting=False)
98
+ else:
99
+ raise e
100
 
101
  return Response(content=output_image, media_type="image/png")
102
 
 
110
  Removes background from a video.
111
  Returns WebM with Alpha.
112
  """
113
+ # Create temp file for input (IO bound)
114
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_input:
115
  shutil.copyfileobj(file.file, tmp_input)
116
  tmp_input_path = tmp_input.name
117
 
118
  try:
119
+ # Acquire semaphore for the heavy video processing
120
+ if processing_semaphore.locked():
121
+ print("Waiting for video processing slot...")
122
+
123
+ async with processing_semaphore:
124
+ # Pass model name to processing function, run in thread pool
125
+ output_path = await run_in_threadpool(process_video, tmp_input_path, model.value)
126
+
127
  except Exception as e:
128
  if os.path.exists(tmp_input_path):
129
  os.remove(tmp_input_path)