Thibaut Claude Happy commited on
Commit
de141f7
·
1 Parent(s): d032bfc

Clean up temporary and debug files

Browse files

Remove temporary files created during development:
- debug_cvat_labels.py
- src/app.py.backup.20260113
- .temp/ directory (all analysis and test scripts)

Repository is now clean and production-ready.

🤖 Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

Files changed (2) hide show
  1. debug_cvat_labels.py +0 -61
  2. src/app.py.backup.20260113 +0 -231
debug_cvat_labels.py DELETED
@@ -1,61 +0,0 @@
1
- """Debug script to inspect CVAT labels and annotations."""
2
-
3
- import os
4
- from dotenv import load_dotenv
5
- from metrics_evaluation.cvat_api.client import CvatApiClient
6
-
7
- load_dotenv()
8
-
9
- # Connect to CVAT
10
- client = CvatApiClient(
11
- cvat_host="https://app.cvat.ai",
12
- cvat_username=os.getenv("CVAT_USERNAME"),
13
- cvat_password=os.getenv("CVAT_PASSWORD"),
14
- cvat_organization="Logiroad",
15
- )
16
-
17
- # Find the training project
18
- projects = client.projects.list()
19
- training_project = None
20
- for project in projects:
21
- if "Entrainement" in project.name:
22
- training_project = project
23
- break
24
-
25
- if not training_project:
26
- print("No training project found")
27
- exit(1)
28
-
29
- print(f"Project: {training_project.name} (ID: {training_project.id})")
30
-
31
- # Get project labels
32
- labels = client.projects.get_project_labels(training_project.id)
33
- print(f"\nProject labels ({len(labels)}):")
34
- for label in labels:
35
- print(f" - {label.name} (ID: {label.id})")
36
-
37
- # Get tasks
38
- tasks = client.tasks.list(project_id=training_project.id)
39
- print(f"\nTasks: {len(tasks)}")
40
-
41
- # Check first few tasks for annotations
42
- for i, task in enumerate(tasks[:3]):
43
- print(f"\n--- Task {task.id}: {task.name} ---")
44
-
45
- # Get jobs for this task
46
- jobs = client.jobs.list(task_id=task.id)
47
- print(f"Jobs: {len(jobs)}")
48
-
49
- for job in jobs[:1]: # Just check first job
50
- print(f" Job {job.id}:")
51
-
52
- # Get annotations
53
- annotations = client.annotations.get_job_annotations(job.id)
54
-
55
- print(f" Tags: {len(annotations.tags)}")
56
- print(f" Shapes: {len(annotations.shapes)}")
57
- print(f" Tracks: {len(annotations.tracks)}")
58
-
59
- # Show first few shapes
60
- for j, shape in enumerate(annotations.shapes[:3]):
61
- print(f" Shape {j}: type={shape.type}, label_id={shape.label_id}, label={shape.label}, frame={shape.frame}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app.py.backup.20260113 DELETED
@@ -1,231 +0,0 @@
1
- """
2
- SAM3 Static Image Segmentation - Correct Implementation
3
-
4
- Uses Sam3Model (not Sam3VideoModel) for text-prompted static image segmentation.
5
- """
6
- import base64
7
- import io
8
- import asyncio
9
- import torch
10
- import numpy as np
11
- from PIL import Image
12
- from fastapi import FastAPI, HTTPException
13
- from pydantic import BaseModel
14
- from transformers import AutoProcessor, AutoModel
15
- import logging
16
-
17
- logging.basicConfig(level=logging.INFO)
18
- logger = logging.getLogger(__name__)
19
-
20
- # Load SAM3 model for STATIC IMAGES
21
- processor = AutoProcessor.from_pretrained("./model", trust_remote_code=True)
22
- model = AutoModel.from_pretrained(
23
- "./model",
24
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
25
- trust_remote_code=True
26
- )
27
-
28
- model.eval()
29
- if torch.cuda.is_available():
30
- model.cuda()
31
- logger.info(f"GPU: {torch.cuda.get_device_name()}")
32
- logger.info(f"VRAM: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB")
33
-
34
- logger.info(f"✓ Loaded {model.__class__.__name__} for static image segmentation")
35
-
36
- # Simple concurrency control
37
- class VRAMManager:
38
- def __init__(self):
39
- self.semaphore = asyncio.Semaphore(2)
40
- self.processing_count = 0
41
-
42
- def get_vram_status(self):
43
- if not torch.cuda.is_available():
44
- return {}
45
- return {
46
- "total_gb": torch.cuda.get_device_properties(0).total_memory / 1e9,
47
- "allocated_gb": torch.cuda.memory_allocated() / 1e9,
48
- "free_gb": (torch.cuda.get_device_properties(0).total_memory - torch.cuda.memory_reserved()) / 1e9,
49
- "processing_now": self.processing_count
50
- }
51
-
52
- async def acquire(self, rid):
53
- await self.semaphore.acquire()
54
- self.processing_count += 1
55
-
56
- def release(self, rid):
57
- self.processing_count -= 1
58
- self.semaphore.release()
59
- if torch.cuda.is_available():
60
- torch.cuda.empty_cache()
61
-
62
- vram_manager = VRAMManager()
63
- app = FastAPI(title="SAM3 Static Image API")
64
-
65
- class Request(BaseModel):
66
- inputs: str
67
- parameters: dict
68
-
69
-
70
- def run_inference(image_b64: str, classes: list, request_id: str):
71
- """
72
- Sam3Model inference for static images with text prompts
73
-
74
- According to HuggingFace docs, Sam3Model uses:
75
- - processor(images=image, text=text_prompts)
76
- - model.forward(pixel_values, input_ids, ...)
77
- """
78
- try:
79
- # Decode image
80
- image_bytes = base64.b64decode(image_b64)
81
- pil_image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
82
- logger.info(f"[{request_id}] Image: {pil_image.size}, Classes: {classes}")
83
-
84
- # Process with Sam3Processor
85
- # Sam3Model expects: batch of images matching text prompts
86
- # For multiple objects in ONE image, repeat the image for each class
87
- images_batch = [pil_image] * len(classes)
88
- inputs = processor(
89
- images=images_batch, # Repeat image for each text prompt
90
- text=classes, # List of text prompts
91
- return_tensors="pt"
92
- )
93
- logger.info(f"[{request_id}] Processing {len(classes)} classes with batched images")
94
-
95
- # Move to GPU and match model dtype
96
- if torch.cuda.is_available():
97
- model_dtype = next(model.parameters()).dtype
98
- inputs = {
99
- k: v.cuda().to(model_dtype) if isinstance(v, torch.Tensor) and v.dtype.is_floating_point else v.cuda() if isinstance(v, torch.Tensor) else v
100
- for k, v in inputs.items()
101
- }
102
- logger.info(f"[{request_id}] Moved inputs to GPU (float tensors to {model_dtype})")
103
-
104
- logger.info(f"[{request_id}] Input keys: {list(inputs.keys())}")
105
-
106
- # Sam3Model Inference
107
- with torch.no_grad():
108
- # Sam3Model.forward() accepts pixel_values, input_ids, etc.
109
- outputs = model(**inputs)
110
- logger.info(f"[{request_id}] Forward pass successful!")
111
-
112
- logger.info(f"[{request_id}] Output type: {type(outputs)}")
113
- logger.info(f"[{request_id}] Output attributes: {dir(outputs)}")
114
-
115
- # Extract masks from outputs
116
- # Sam3Model returns masks in outputs.pred_masks
117
- if hasattr(outputs, 'pred_masks'):
118
- pred_masks = outputs.pred_masks
119
- logger.info(f"[{request_id}] pred_masks shape: {pred_masks.shape}")
120
- elif hasattr(outputs, 'masks'):
121
- pred_masks = outputs.masks
122
- logger.info(f"[{request_id}] masks shape: {pred_masks.shape}")
123
- elif isinstance(outputs, dict) and 'pred_masks' in outputs:
124
- pred_masks = outputs['pred_masks']
125
- logger.info(f"[{request_id}] pred_masks shape: {pred_masks.shape}")
126
- else:
127
- logger.error(f"[{request_id}] Unexpected output format")
128
- logger.error(f"Output attributes: {dir(outputs) if not isinstance(outputs, dict) else outputs.keys()}")
129
- raise ValueError("Cannot find masks in model output")
130
-
131
- # Process masks
132
- results = []
133
-
134
- # pred_masks typically: [batch, num_objects, height, width]
135
- batch_size = pred_masks.shape[0]
136
- num_masks = pred_masks.shape[1] if len(pred_masks.shape) > 1 else 1
137
-
138
- logger.info(f"[{request_id}] Batch size: {batch_size}, Num masks: {num_masks}")
139
-
140
- for i, cls in enumerate(classes):
141
- if i < num_masks:
142
- # Get mask for this class/object
143
- if len(pred_masks.shape) == 4: # [batch, num, h, w]
144
- mask_tensor = pred_masks[0, i] # [h, w]
145
- elif len(pred_masks.shape) == 3: # [num, h, w]
146
- mask_tensor = pred_masks[i]
147
- else:
148
- mask_tensor = pred_masks
149
-
150
- # Resize to original size if needed
151
- if mask_tensor.shape[-2:] != pil_image.size[::-1]:
152
- mask_tensor = torch.nn.functional.interpolate(
153
- mask_tensor.unsqueeze(0).unsqueeze(0),
154
- size=pil_image.size[::-1],
155
- mode='bilinear',
156
- align_corners=False
157
- ).squeeze()
158
-
159
- # Convert to binary mask
160
- binary_mask = (mask_tensor > 0.0).float().cpu().numpy().astype("uint8") * 255
161
- else:
162
- # No mask available for this class
163
- binary_mask = np.zeros(pil_image.size[::-1], dtype="uint8")
164
-
165
- # Convert to PNG
166
- pil_mask = Image.fromarray(binary_mask, mode="L")
167
- buf = io.BytesIO()
168
- pil_mask.save(buf, format="PNG")
169
- mask_b64 = base64.b64encode(buf.getvalue()).decode("utf-8")
170
-
171
- # Get confidence score if available
172
- score = 1.0
173
- if hasattr(outputs, 'pred_scores') and i < outputs.pred_scores.shape[1]:
174
- score = float(outputs.pred_scores[0, i].cpu())
175
- elif hasattr(outputs, 'scores') and i < len(outputs.scores):
176
- score = float(outputs.scores[i].cpu() if hasattr(outputs.scores[i], 'cpu') else outputs.scores[i])
177
-
178
- results.append({
179
- "label": cls,
180
- "mask": mask_b64,
181
- "score": score
182
- })
183
-
184
- logger.info(f"[{request_id}] Completed: {len(results)} masks generated")
185
- return results
186
-
187
- except Exception as e:
188
- logger.error(f"[{request_id}] Failed: {str(e)}")
189
- import traceback
190
- traceback.print_exc()
191
- raise
192
-
193
-
194
- @app.post("/")
195
- async def predict(req: Request):
196
- request_id = str(id(req))[:8]
197
- try:
198
- await vram_manager.acquire(request_id)
199
- try:
200
- results = await asyncio.to_thread(
201
- run_inference,
202
- req.inputs,
203
- req.parameters.get("classes", []),
204
- request_id
205
- )
206
- return results
207
- finally:
208
- vram_manager.release(request_id)
209
- except Exception as e:
210
- logger.error(f"[{request_id}] Error: {str(e)}")
211
- raise HTTPException(status_code=500, detail=str(e))
212
-
213
-
214
- @app.get("/health")
215
- async def health():
216
- return {
217
- "status": "healthy",
218
- "model": model.__class__.__name__,
219
- "gpu_available": torch.cuda.is_available(),
220
- "vram": vram_manager.get_vram_status()
221
- }
222
-
223
-
224
- @app.get("/metrics")
225
- async def metrics():
226
- return vram_manager.get_vram_status()
227
-
228
-
229
- if __name__ == "__main__":
230
- import uvicorn
231
- uvicorn.run(app, host="0.0.0.0", port=7860, workers=1)