Anigor66 commited on
Commit
33ea65f
·
1 Parent(s): e3a8e22

Supbase removed

Browse files
Files changed (2) hide show
  1. app.py +59 -220
  2. requirements.txt +0 -1
app.py CHANGED
@@ -13,21 +13,9 @@ import json
13
  import base64
14
  import os
15
  import uuid
16
- import requests
17
- from urllib.parse import urlparse
18
 
19
  from huggingface_hub import hf_hub_download
20
 
21
- # Try to import Supabase (optional - for embedding storage/retrieval)
22
- try:
23
- from supabase import create_client, Client
24
- SUPABASE_AVAILABLE = True
25
- SupabaseClientType = Client
26
- except ImportError:
27
- SUPABASE_AVAILABLE = False
28
- SupabaseClientType = None # Type placeholder when Supabase not available
29
- print("⚠️ Supabase not available. Embedding storage/retrieval will be disabled.")
30
-
31
  # Import SAM components
32
  from segment_anything import sam_model_registry, SamPredictor, SamAutomaticMaskGenerator
33
 
@@ -73,7 +61,7 @@ print(f"Loading SAM model ({MODEL_TYPE}) from downloaded checkpoint...")
73
  try:
74
  # Ensure we always load onto CPU when no GPU is available
75
  torch.load = patched_torch_load
76
- sam = sam_model_registry[MODEL_TYPE](checkpoint=MODEL_CHECKPOINT)
77
  finally:
78
  torch.load = original_torch_load
79
 
@@ -102,126 +90,11 @@ mask_generator = SamAutomaticMaskGenerator(
102
  print("✓ SamAutomaticMaskGenerator initialized for automatic segmentation")
103
  print("✓ SAM model loaded successfully from HuggingFace Hub!")
104
 
105
- # -----------------------------------------------------------------------------
106
- # Supabase setup (for embedding storage/retrieval)
107
- # -----------------------------------------------------------------------------
108
- supabase = None # Will be set to Supabase Client if available
109
- EMBED_BUCKET = "embeddings"
110
- EMBED_TABLE = "embeddings2"
111
-
112
- if SUPABASE_AVAILABLE:
113
- try:
114
- SUPABASE_URL = os.getenv("SUPABASE_URL") or os.getenv("REACT_APP_SUPABASE_URL")
115
- SUPABASE_KEY = os.getenv("SUPABASE_KEY") or os.getenv("REACT_APP_SUPABASE_ANON_KEY")
116
-
117
- if SUPABASE_URL and SUPABASE_KEY:
118
- supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
119
- print("✓ Supabase client initialized for embedding storage/retrieval")
120
- else:
121
- print("⚠️ Supabase credentials not found in environment variables")
122
- print(" Set SUPABASE_URL and SUPABASE_KEY (or REACT_APP_SUPABASE_URL/REACT_APP_SUPABASE_ANON_KEY)")
123
- except Exception as e:
124
- print(f"⚠️ Failed to initialize Supabase: {e}")
125
- supabase = None
126
- else:
127
- print("⚠️ Supabase not available - embedding features disabled")
128
-
129
 
130
  # =============================================================================
131
- # HELPER FUNCTIONS FOR EMBEDDINGS
132
  # =============================================================================
133
 
134
- def save_embedding_to_supabase(image_id: str, embedding_tensor: torch.Tensor) -> bool:
135
- """
136
- Save SAM image embedding to Supabase (embeddings2 table + embeddings bucket)
137
-
138
- Args:
139
- image_id: Image ID from database
140
- embedding_tensor: Embedding tensor from sam.image_encoder (shape: [1, C, H, W])
141
-
142
- Returns:
143
- True if successful, False otherwise
144
- """
145
- if not supabase:
146
- return False
147
-
148
- try:
149
- # Convert tensor to numpy array (same format as old backend)
150
- arr = embedding_tensor.squeeze(0).cpu().numpy().astype(np.float32)
151
-
152
- # Save to bytes buffer
153
- buf = io.BytesIO()
154
- np.save(buf, arr)
155
- buf.seek(0)
156
-
157
- # Upload to Supabase storage
158
- path = f"{image_id}/{uuid.uuid4().hex}.npy"
159
- supabase.storage.from_(EMBED_BUCKET).upload(path, buf.read())
160
-
161
- # Save metadata to embeddings2 table
162
- supabase.table(EMBED_TABLE).insert({
163
- 'image_id': image_id,
164
- 'file_path': path
165
- }).execute()
166
-
167
- print(f"✓ Saved embedding for image_id={image_id} to Supabase")
168
- return True
169
-
170
- except Exception as e:
171
- print(f"✗ Error saving embedding to Supabase: {e}")
172
- import traceback
173
- traceback.print_exc()
174
- return False
175
-
176
-
177
- def load_embedding_from_supabase(image_id: str) -> torch.Tensor:
178
- """
179
- Load SAM image embedding from Supabase
180
-
181
- Args:
182
- image_id: Image ID from database
183
-
184
- Returns:
185
- Embedding tensor (shape: [1, C, H, W]) or None if not found
186
- """
187
- if not supabase:
188
- return None
189
-
190
- try:
191
- # Query embeddings2 table
192
- response = supabase.table(EMBED_TABLE).select('*').eq('image_id', image_id).execute()
193
-
194
- if not response.data:
195
- print(f"No embedding found for image_id={image_id}")
196
- return None
197
-
198
- embed_record = response.data[0]
199
- file_path = embed_record['file_path']
200
-
201
- # Get signed URL for the embedding file
202
- signed = supabase.storage.from_(EMBED_BUCKET).create_signed_url(file_path, 3600)
203
- signed_url = signed["signedUrl"]
204
-
205
- # Download embedding file
206
- r = requests.get(signed_url)
207
- r.raise_for_status()
208
-
209
- # Load numpy array
210
- embedding_array = np.load(io.BytesIO(r.content))
211
-
212
- # Convert to tensor and add batch dimension
213
- embedding_tensor = torch.from_numpy(embedding_array).unsqueeze(0).to(device)
214
-
215
- print(f"✓ Loaded embedding for image_id={image_id} from Supabase")
216
- return embedding_tensor
217
-
218
- except Exception as e:
219
- print(f"✗ Error loading embedding from Supabase: {e}")
220
- import traceback
221
- traceback.print_exc()
222
- return None
223
-
224
-
225
  def set_predictor_features_from_embedding(embedding_tensor: torch.Tensor, image_shape: tuple):
226
  """
227
  Set SamPredictor's internal features using precomputed embedding
@@ -244,53 +117,44 @@ def set_predictor_features_from_embedding(embedding_tensor: torch.Tensor, image_
244
 
245
  def encode_image(image, request_json):
246
  """
247
- Encode image using SAM image encoder and save embedding to Supabase
248
-
 
 
 
249
  Args:
250
  image: PIL Image
251
- request_json: JSON string with format:
252
  {
253
- "image_id": "uuid-string" # Required: image ID from database
254
  }
255
-
256
  Returns:
257
  JSON string:
258
  {
259
  "success": true/false,
260
- "message": "Embedding saved successfully" or error message,
261
- "image_id": "uuid-string"
 
262
  }
263
  """
264
  try:
265
- # Parse input
266
- data = json.loads(request_json)
267
  image_id = data.get("image_id")
268
 
269
- if not image_id:
270
- return json.dumps({
271
- 'success': False,
272
- 'error': 'image_id is required'
273
- })
274
-
275
- if not supabase:
276
- return json.dumps({
277
- 'success': False,
278
- 'error': 'Supabase not configured. Set SUPABASE_URL and SUPABASE_KEY environment variables.'
279
- })
280
-
281
  # Convert PIL to numpy
282
  image_array = np.array(image)
283
  H, W = image_array.shape[:2]
284
 
285
  # Resize image to SAM's expected input size (1024x1024)
286
- # SAM expects images to be resized before encoding
287
  from skimage import transform
288
  img_resized = transform.resize(
289
- image_array,
290
- (1024, 1024),
291
- order=3,
292
- preserve_range=True,
293
- anti_aliasing=True
294
  ).astype(np.uint8)
295
 
296
  # Normalize image (SAM expects normalized input)
@@ -299,36 +163,45 @@ def encode_image(image, request_json):
299
  )
300
 
301
  # Convert to tensor and add batch dimension
302
- tensor = torch.tensor(img_norm).float().permute(2, 0, 1).unsqueeze(0).to(device)
 
 
 
 
 
 
303
 
304
  # Encode image using SAM image encoder
305
- print(f"Encoding image {image_id} (size: {W}x{H})...")
306
  with torch.no_grad():
307
  embedding = sam.image_encoder(tensor)
308
-
309
- # Save embedding to Supabase
310
- success = save_embedding_to_supabase(image_id, embedding)
311
-
312
- if success:
313
- return json.dumps({
314
- 'success': True,
315
- 'message': f'Embedding saved successfully for image_id={image_id}',
316
- 'image_id': image_id,
317
- 'embedding_shape': list(embedding.shape)
318
- })
319
- else:
320
- return json.dumps({
321
- 'success': False,
322
- 'error': 'Failed to save embedding to Supabase'
323
- })
324
-
 
325
  except Exception as e:
326
  import traceback
327
- return json.dumps({
328
- 'success': False,
329
- 'error': str(e),
330
- 'traceback': traceback.format_exc()
331
- })
 
 
332
 
333
 
334
  def segment_points(image, request_json):
@@ -369,20 +242,8 @@ def segment_points(image, request_json):
369
  image_array = np.array(image)
370
  H, W = image_array.shape[:2]
371
 
372
- # Try to use precomputed embedding if image_id is provided
373
- if image_id and supabase:
374
- embedding_tensor = load_embedding_from_supabase(image_id)
375
- if embedding_tensor is not None:
376
- # Use precomputed embedding
377
- set_predictor_features_from_embedding(embedding_tensor, (H, W))
378
- print(f"Using precomputed embedding for image_id={image_id}")
379
- else:
380
- # Fall back to computing embedding from image
381
- print(f"Embedding not found for image_id={image_id}, computing from image...")
382
- predictor.set_image(image_array)
383
- else:
384
- # No image_id provided or Supabase not available - compute embedding from image
385
- predictor.set_image(image_array)
386
 
387
  # Process each point individually (like backend does)
388
  box_size = 20 # Small box size for point-based segmentation
@@ -481,20 +342,9 @@ def segment_box(image, request_json):
481
 
482
  # Convert PIL to numpy
483
  image_array = np.array(image)
484
- H, W = image_array.shape[:2]
485
 
486
- # Try to use precomputed embedding if image_id is provided
487
- image_id = data.get("image_id")
488
- if image_id and supabase:
489
- embedding_tensor = load_embedding_from_supabase(image_id)
490
- if embedding_tensor is not None:
491
- set_predictor_features_from_embedding(embedding_tensor, (H, W))
492
- print(f"Using precomputed embedding for image_id={image_id}")
493
- else:
494
- print(f"Embedding not found for image_id={image_id}, computing from image...")
495
- predictor.set_image(image_array)
496
- else:
497
- predictor.set_image(image_array)
498
 
499
  # Run prediction with box
500
  masks, scores, logits = predictor.predict(
@@ -564,20 +414,9 @@ def segment_multiple_boxes(image, request_json):
564
 
565
  # Convert PIL to numpy
566
  image_array = np.array(image)
567
- H, W = image_array.shape[:2]
568
 
569
- # Try to use precomputed embedding if image_id is provided
570
- image_id = data.get("image_id")
571
- if image_id and supabase:
572
- embedding_tensor = load_embedding_from_supabase(image_id)
573
- if embedding_tensor is not None:
574
- set_predictor_features_from_embedding(embedding_tensor, (H, W))
575
- print(f"Using precomputed embedding for image_id={image_id}")
576
- else:
577
- print(f"Embedding not found for image_id={image_id}, computing from image...")
578
- predictor.set_image(image_array)
579
- else:
580
- predictor.set_image(image_array)
581
 
582
  print(f"Processing {len(bboxes)} boxes for segmentation")
583
 
 
13
  import base64
14
  import os
15
  import uuid
 
 
16
 
17
  from huggingface_hub import hf_hub_download
18
 
 
 
 
 
 
 
 
 
 
 
19
  # Import SAM components
20
  from segment_anything import sam_model_registry, SamPredictor, SamAutomaticMaskGenerator
21
 
 
61
  try:
62
  # Ensure we always load onto CPU when no GPU is available
63
  torch.load = patched_torch_load
64
+ sam = sam_model_registry[MODEL_TYPE](checkpoint=MODEL_CHECKPOINT)
65
  finally:
66
  torch.load = original_torch_load
67
 
 
90
  print("✓ SamAutomaticMaskGenerator initialized for automatic segmentation")
91
  print("✓ SAM model loaded successfully from HuggingFace Hub!")
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  # =============================================================================
95
+ # HELPER FUNCTIONS FOR EMBEDDINGS (STATELESS)
96
  # =============================================================================
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  def set_predictor_features_from_embedding(embedding_tensor: torch.Tensor, image_shape: tuple):
99
  """
100
  Set SamPredictor's internal features using precomputed embedding
 
117
 
118
  def encode_image(image, request_json):
119
  """
120
+ Encode image using SAM image encoder and return embedding to the client.
121
+
122
+ This is now a stateless API: it does NOT talk to Supabase. The caller
123
+ (your backend) is responsible for storing the embedding if desired.
124
+
125
  Args:
126
  image: PIL Image
127
+ request_json: JSON string with optional fields:
128
  {
129
+ "image_id": "uuid-string" # Optional: image ID from your DB
130
  }
131
+
132
  Returns:
133
  JSON string:
134
  {
135
  "success": true/false,
136
+ "image_id": "uuid-string" or null,
137
+ "embedding_npy_base64": "...", # base64-encoded .npy of [C,H,W]
138
+ "embedding_shape": [1, C, H, W]
139
  }
140
  """
141
  try:
142
+ # Parse input (image_id is optional and just echoed back)
143
+ data = json.loads(request_json) if request_json else {}
144
  image_id = data.get("image_id")
145
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  # Convert PIL to numpy
147
  image_array = np.array(image)
148
  H, W = image_array.shape[:2]
149
 
150
  # Resize image to SAM's expected input size (1024x1024)
 
151
  from skimage import transform
152
  img_resized = transform.resize(
153
+ image_array,
154
+ (1024, 1024),
155
+ order=3,
156
+ preserve_range=True,
157
+ anti_aliasing=True,
158
  ).astype(np.uint8)
159
 
160
  # Normalize image (SAM expects normalized input)
 
163
  )
164
 
165
  # Convert to tensor and add batch dimension
166
+ tensor = (
167
+ torch.tensor(img_norm)
168
+ .float()
169
+ .permute(2, 0, 1)
170
+ .unsqueeze(0)
171
+ .to(device)
172
+ )
173
 
174
  # Encode image using SAM image encoder
175
+ print(f"Encoding image (image_id={image_id}) original size: {W}x{H} -> 1024x1024")
176
  with torch.no_grad():
177
  embedding = sam.image_encoder(tensor)
178
+
179
+ # Convert embedding to numpy [C, Hf, Wf]
180
+ arr = embedding.squeeze(0).cpu().numpy().astype(np.float32)
181
+
182
+ # Serialize as .npy in memory and base64-encode it
183
+ buf = io.BytesIO()
184
+ np.save(buf, arr)
185
+ buf.seek(0)
186
+ embedding_b64 = base64.b64encode(buf.read()).decode("utf-8")
187
+
188
+ return json.dumps(
189
+ {
190
+ "success": True,
191
+ "image_id": image_id,
192
+ "embedding_npy_base64": embedding_b64,
193
+ "embedding_shape": list(embedding.shape),
194
+ }
195
+ )
196
  except Exception as e:
197
  import traceback
198
+ return json.dumps(
199
+ {
200
+ "success": False,
201
+ "error": str(e),
202
+ "traceback": traceback.format_exc(),
203
+ }
204
+ )
205
 
206
 
207
  def segment_points(image, request_json):
 
242
  image_array = np.array(image)
243
  H, W = image_array.shape[:2]
244
 
245
+ # For now, always compute embedding from image (stateless API)
246
+ predictor.set_image(image_array)
 
 
 
 
 
 
 
 
 
 
 
 
247
 
248
  # Process each point individually (like backend does)
249
  box_size = 20 # Small box size for point-based segmentation
 
342
 
343
  # Convert PIL to numpy
344
  image_array = np.array(image)
 
345
 
346
+ # Stateless: always compute embedding from image
347
+ predictor.set_image(image_array)
 
 
 
 
 
 
 
 
 
 
348
 
349
  # Run prediction with box
350
  masks, scores, logits = predictor.predict(
 
414
 
415
  # Convert PIL to numpy
416
  image_array = np.array(image)
 
417
 
418
+ # Stateless: always compute embedding from image
419
+ predictor.set_image(image_array)
 
 
 
 
 
 
 
 
 
 
420
 
421
  print(f"Processing {len(bboxes)} boxes for segmentation")
422
 
requirements.txt CHANGED
@@ -7,5 +7,4 @@ opencv-python>=4.8.0
7
  scikit-image>=0.21.0
8
  segment-anything @ git+https://github.com/facebookresearch/segment-anything.git
9
  huggingface_hub>=0.23.0
10
- supabase-py>=2.0.0
11
  requests>=2.31.0
 
7
  scikit-image>=0.21.0
8
  segment-anything @ git+https://github.com/facebookresearch/segment-anything.git
9
  huggingface_hub>=0.23.0
 
10
  requests>=2.31.0