andevs commited on
Commit
606a135
·
verified ·
1 Parent(s): a5d69e8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -190
app.py CHANGED
@@ -1,248 +1,101 @@
1
  import io
2
  import time
3
  import logging
 
4
  from pathlib import Path
5
  from fastapi import FastAPI, File, UploadFile, HTTPException
6
- from fastapi.responses import StreamingResponse, JSONResponse
7
  from fastapi.middleware.cors import CORSMiddleware
8
  from PIL import Image
9
- import os
10
 
11
  # Setup logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
- # Set environment variables BEFORE importing rembg
16
- os.environ["U2NET_HOME"] = "/tmp/.u2net" # Tell rembg where to cache models
17
- os.environ["OMP_NUM_THREADS"] = "1" # Optimize for single CPU
18
 
19
  AI_AVAILABLE = False
 
 
 
20
  try:
21
- # Pre-download the model on startup
22
- logger.info("🚀 Initializing Background Remover...")
23
-
24
- # First, create cache directory
25
- cache_dir = "/tmp/.u2net"
26
- os.makedirs(cache_dir, exist_ok=True)
27
-
28
- # Try to import with pre-load
29
  from rembg import remove, new_session
30
 
31
- # Use smallest model for faster loading
32
- logger.info("📥 Loading AI model (this might take a moment on first run)...")
33
- session = new_session("u2netp") # u2netp is smaller than u2net
 
 
34
  AI_AVAILABLE = True
35
- logger.info("✅ Rembg AI Available and Model Loaded!")
36
 
37
  except Exception as e:
38
- logger.error(f"❌ Failed to load AI: {e}")
39
  AI_AVAILABLE = False
40
- session = None
41
- remove = None
42
-
43
- # Import CPU fallback
44
- import numpy as np
45
- import cv2
46
 
47
- app = FastAPI(title="Background Remover API")
48
 
49
- # Enable CORS
50
  app.add_middleware(
51
  CORSMiddleware,
52
  allow_origins=["*"],
53
- allow_credentials=True,
54
  allow_methods=["*"],
55
  allow_headers=["*"],
56
  )
57
 
58
- def cpu_fallback(input_image: Image.Image) -> Image.Image:
59
- """A simple CPU-based fallback using OpenCV."""
60
- try:
61
- # Convert PIL to numpy array for OpenCV
62
- img_np = np.array(input_image.convert('RGB'))
63
- h, w = img_np.shape[:2]
64
-
65
- # If image is too large, resize for performance
66
- if h > 1000 or w > 1000:
67
- scale = 1000 / max(h, w)
68
- new_h, new_w = int(h * scale), int(w * scale)
69
- img_np = cv2.resize(img_np, (new_w, new_h))
70
- h, w = new_h, new_w
71
-
72
- # Create a mask
73
- mask = np.zeros((h, w), np.uint8)
74
- bgd_model = np.zeros((1, 65), np.float64)
75
- fgd_model = np.zeros((1, 65), np.float64)
76
-
77
- # Define rectangle (10% border)
78
- border = 0.1
79
- rect = (
80
- int(w * border),
81
- int(h * border),
82
- int(w * (1 - border * 2)),
83
- int(h * (1 - border * 2))
84
- )
85
-
86
- # Apply GrabCut
87
- cv2.grabCut(img_np, mask, rect, bgd_model, fgd_model, 3, cv2.GC_INIT_WITH_RECT)
88
-
89
- # Create final mask
90
- mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
91
-
92
- # Create output with white background
93
- output = np.ones((h, w, 3), dtype=np.uint8) * 255
94
-
95
- # Apply mask to get foreground
96
- foreground = img_np * mask2[:, :, np.newaxis]
97
-
98
- # Combine white background with foreground
99
- output = output * (1 - mask2[:, :, np.newaxis]) + foreground
100
-
101
- return Image.fromarray(output.astype(np.uint8))
102
-
103
- except Exception as e:
104
- logger.error(f"CPU fallback failed: {e}")
105
- # Return original image with white background
106
- return input_image.convert('RGB')
107
-
108
- @app.post("/api/process")
109
  async def process_image(file: UploadFile = File(...)):
110
- """
111
- Removes the background from an uploaded image.
112
- """
113
- start_time = time.time()
114
- method_used = "unknown"
115
-
116
  try:
117
- # Validate file
118
- if not file.filename:
119
- raise HTTPException(status_code=400, detail="No file provided")
120
-
121
- # Check file size (max 10MB)
122
- file.file.seek(0, 2) # Seek to end
123
- file_size = file.file.tell()
124
- file.file.seek(0) # Reset to beginning
125
-
126
- if file_size > 10 * 1024 * 1024: # 10MB
127
- raise HTTPException(status_code=400, detail="File too large (max 10MB)")
128
-
129
- # Read and validate image
130
- contents = await file.read()
131
- if len(contents) == 0:
132
- raise HTTPException(status_code=400, detail="Empty file")
133
-
134
- logger.info(f"📥 Processing: {file.filename} ({len(contents)} bytes)")
135
 
136
  # Open image
137
- try:
138
- image = Image.open(io.BytesIO(contents))
139
- except Exception:
140
- raise HTTPException(status_code=400, detail="Invalid image file")
141
 
142
- # Process image
143
- output_image = None
 
 
144
 
 
145
  if AI_AVAILABLE and session and remove:
146
- try:
147
- logger.info("🤖 Using AI method...")
148
- # Resize if too large for AI
149
- max_size = 1024
150
- if max(image.size) > max_size:
151
- image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
152
-
153
- # Remove background
154
- output_image = remove(image, session=session)
155
- method_used = "AI (u2netp)"
156
-
157
- except Exception as ai_error:
158
- logger.warning(f"AI failed: {ai_error}, using CPU fallback")
159
- output_image = cpu_fallback(image)
160
- method_used = "CPU (GrabCut - AI failed)"
161
  else:
162
- logger.info("⚙️ Using CPU method (AI not available)...")
163
- output_image = cpu_fallback(image)
164
- method_used = "CPU (GrabCut)"
165
 
166
  # Convert to bytes
167
  output_buffer = io.BytesIO()
168
- output_image.save(output_buffer, format="PNG")
169
  output_buffer.seek(0)
170
 
171
- processing_time = time.time() - start_time
172
- logger.info(f"✅ Success in {processing_time:.2f}s using {method_used}")
173
-
174
- # Return the image
175
  return StreamingResponse(
176
  output_buffer,
177
  media_type="image/png",
178
  headers={
179
- "X-Processing-Method": method_used,
180
- "X-Processing-Time": f"{processing_time:.2f}s",
181
- "Content-Disposition": f'attachment; filename="no_bg_{Path(file.filename).stem}.png"'
182
  }
183
  )
184
 
185
- except HTTPException:
186
- raise
187
  except Exception as e:
188
- logger.error(f"❌ Processing error: {e}")
189
- raise HTTPException(status_code=500, detail=f"Processing failed: {str(e)[:100]}")
190
-
191
- @app.post("/api/process_simple")
192
- async def process_image_simple(file: UploadFile = File(...)):
193
- """
194
- Simplified version without CPU fallback - for testing
195
- """
196
- try:
197
- if AI_AVAILABLE and session and remove:
198
- contents = await file.read()
199
- image = Image.open(io.BytesIO(contents))
200
-
201
- # Resize for speed
202
- image.thumbnail((512, 512))
203
-
204
- result = remove(image, session=session)
205
-
206
- output_buffer = io.BytesIO()
207
- result.save(output_buffer, format="PNG")
208
- output_buffer.seek(0)
209
-
210
- return StreamingResponse(output_buffer, media_type="image/png")
211
- else:
212
- return JSONResponse(
213
- status_code=503,
214
- content={"error": "AI service unavailable"}
215
- )
216
- except Exception as e:
217
- logger.error(f"Simple process error: {e}")
218
- raise HTTPException(status_code=500, detail=str(e))
219
-
220
- @app.get("/health")
221
- async def health_check():
222
- """Health check endpoint"""
223
- return {
224
- "status": "healthy" if AI_AVAILABLE else "degraded",
225
- "ai_available": AI_AVAILABLE,
226
- "session_loaded": session is not None,
227
- "timestamp": time.time()
228
- }
229
 
230
  @app.get("/")
231
- async def root():
232
- """Simple homepage with form"""
233
  return {
234
  "message": "Background Remover API",
235
  "status": "running",
236
- "ai": "available" if AI_AVAILABLE else "unavailable",
237
- "endpoints": {
238
- "POST /api/process": "Remove background from image",
239
- "GET /health": "Health check"
240
- },
241
- "instructions": "Upload an image to /api/process to remove background"
242
  }
243
 
244
- # Optional: Add a test endpoint
245
- @app.get("/test")
246
- async def test_endpoint():
247
- """Test that the server is responding"""
248
- return {"status": "ok", "message": "Server is running"}
 
1
  import io
2
  import time
3
  import logging
4
+ import os
5
  from pathlib import Path
6
  from fastapi import FastAPI, File, UploadFile, HTTPException
7
+ from fastapi.responses import StreamingResponse
8
  from fastapi.middleware.cors import CORSMiddleware
9
  from PIL import Image
 
10
 
11
  # Setup logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
+ # Set environment for Hugging Face
16
+ os.environ["U2NET_HOME"] = "/tmp/.u2net"
 
17
 
18
  AI_AVAILABLE = False
19
+ session = None
20
+ remove = None
21
+
22
  try:
23
+ logger.info("Loading AI model...")
 
 
 
 
 
 
 
24
  from rembg import remove, new_session
25
 
26
+ # Create cache directory
27
+ os.makedirs("/tmp/.u2net", exist_ok=True)
28
+
29
+ # Load model - use u2netp (smaller, faster)
30
+ session = new_session("u2netp")
31
  AI_AVAILABLE = True
32
+ logger.info("✅ AI model loaded successfully!")
33
 
34
  except Exception as e:
35
+ logger.error(f"AI failed to load: {e}")
36
  AI_AVAILABLE = False
 
 
 
 
 
 
37
 
38
+ app = FastAPI(title="Background Remover")
39
 
 
40
  app.add_middleware(
41
  CORSMiddleware,
42
  allow_origins=["*"],
 
43
  allow_methods=["*"],
44
  allow_headers=["*"],
45
  )
46
 
47
+ @app.post("/process")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  async def process_image(file: UploadFile = File(...)):
49
+ """Process uploaded image and remove background"""
 
 
 
 
 
50
  try:
51
+ # Read image
52
+ image_data = await file.read()
53
+ if not image_data:
54
+ raise HTTPException(400, "No image data")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  # Open image
57
+ image = Image.open(io.BytesIO(image_data))
 
 
 
58
 
59
+ # Resize if too large (for performance)
60
+ max_size = 1024
61
+ if max(image.size) > max_size:
62
+ image.thumbnail((max_size, max_size), Image.LANCZOS)
63
 
64
+ # Process with AI if available
65
  if AI_AVAILABLE and session and remove:
66
+ output = remove(image, session=session)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  else:
68
+ raise HTTPException(503, "AI service not available")
 
 
69
 
70
  # Convert to bytes
71
  output_buffer = io.BytesIO()
72
+ output.save(output_buffer, format="PNG")
73
  output_buffer.seek(0)
74
 
 
 
 
 
75
  return StreamingResponse(
76
  output_buffer,
77
  media_type="image/png",
78
  headers={
79
+ "Content-Disposition": f"attachment; filename=no_bg_{Path(file.filename).stem}.png"
 
 
80
  }
81
  )
82
 
 
 
83
  except Exception as e:
84
+ logger.error(f"Error: {e}")
85
+ raise HTTPException(500, f"Processing failed: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
  @app.get("/")
88
+ async def home():
89
+ """Simple homepage"""
90
  return {
91
  "message": "Background Remover API",
92
  "status": "running",
93
+ "ai_available": AI_AVAILABLE,
94
+ "endpoint": "POST /process",
95
+ "example": "curl -X POST -F 'file=@your_image.jpg' https://your-space.hf.space/process -o result.png"
 
 
 
96
  }
97
 
98
+ @app.get("/health")
99
+ async def health():
100
+ """Health check"""
101
+ return {"status": "ok", "ai_loaded": AI_AVAILABLE}