itsbava commited on
Commit
aad25b1
·
verified ·
1 Parent(s): 8cad55a

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +19 -0
  2. app.py +222 -0
  3. requirements.txt +12 -0
README.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Orcan VisionTrace GPU Service
2
+
3
+ GPU-accelerated face recognition and FAISS indexing service for Orcan VisionTrace.
4
+
5
+ ## Features
6
+ - Batch face embedding extraction using InsightFace
7
+ - GPU-accelerated FAISS index creation
8
+ - Image enhancement for poor quality inputs
9
+ - High-performance search capabilities
10
+
11
+ ## Hardware Requirements
12
+ - NVIDIA GPU with CUDA support
13
+ - Minimum 8GB GPU memory recommended
14
+
15
+ ## API Endpoints
16
+ - POST /extract_embeddings_batch - Batch face embedding extraction
17
+ - POST /create_faiss_index - GPU-accelerated index creation
18
+ - POST /search_faiss - Fast similarity search
19
+ - GET /health - Service health check
app.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import numpy as np
4
+ import cv2
5
+ import faiss
6
+ import torch
7
+ import insightface
8
+ from fastapi import FastAPI, HTTPException
9
+ from pydantic import BaseModel
10
+ from typing import List, Dict, Any, Optional
11
+ from PIL import Image, ImageOps
12
+ import io
13
+ import logging
14
+
15
+ app = FastAPI(title="Orcan VisionTrace GPU Service", version="1.0.0")
16
+
17
+ # Global models
18
+ face_app = None
19
+ gpu_resources = None
20
+
21
+ class BatchEmbeddingRequest(BaseModel):
22
+ images: List[str] # Base64 encoded images
23
+ enhance_quality: bool = True
24
+ aggressive_enhancement: bool = False
25
+
26
+ class IndexCreationRequest(BaseModel):
27
+ embeddings: List[List[float]]
28
+ dataset_size: int
29
+ dimension: int = 512
30
+
31
+ @app.on_event("startup")
32
+ async def startup_event():
33
+ global face_app, gpu_resources
34
+
35
+ # Initialize FAISS GPU resources
36
+ if torch.cuda.is_available():
37
+ gpu_resources = faiss.StandardGpuResources()
38
+
39
+ # Initialize InsightFace with GPU
40
+ face_app = insightface.app.FaceAnalysis(
41
+ providers=['CUDAExecutionProvider', 'CPUExecutionProvider'],
42
+ allowed_modules=['detection', 'recognition']
43
+ )
44
+ face_app.prepare(ctx_id=0, det_size=(640, 640))
45
+
46
+ @app.get("/health")
47
+ async def health_check():
48
+ return {
49
+ "status": "healthy",
50
+ "gpu_available": torch.cuda.is_available(),
51
+ "face_model_loaded": face_app is not None
52
+ }
53
+
54
+ @app.post("/extract_embeddings_batch")
55
+ async def extract_embeddings_batch(request: BatchEmbeddingRequest):
56
+ try:
57
+ embeddings = []
58
+ extraction_info = []
59
+
60
+ for img_b64 in request.images:
61
+ try:
62
+ # Decode base64 image
63
+ img_data = base64.b64decode(img_b64)
64
+ img_array = np.frombuffer(img_data, dtype=np.uint8)
65
+ img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
66
+
67
+ if img is None:
68
+ embeddings.append(None)
69
+ extraction_info.append({"error": "Failed to decode image"})
70
+ continue
71
+
72
+ # Apply enhancement if requested
73
+ if request.enhance_quality:
74
+ img = enhance_image_gpu(img, request.aggressive_enhancement)
75
+
76
+ # Extract face embeddings
77
+ faces = face_app.get(img)
78
+
79
+ if len(faces) == 0:
80
+ embeddings.append(None)
81
+ extraction_info.append({
82
+ "face_count": 0,
83
+ "strategy_used": "gpu_batch",
84
+ "enhancement_used": request.enhance_quality
85
+ })
86
+ continue
87
+
88
+ # Get best face
89
+ face = max(faces, key=lambda x: (x.bbox[2] - x.bbox[0]) * (x.bbox[3] - x.bbox[1]))
90
+ embedding = face.embedding
91
+ embedding = embedding / np.linalg.norm(embedding)
92
+
93
+ embeddings.append(embedding.tolist())
94
+ extraction_info.append({
95
+ "face_count": len(faces),
96
+ "confidence": float(np.linalg.norm(embedding)),
97
+ "strategy_used": "gpu_batch",
98
+ "enhancement_used": request.enhance_quality,
99
+ "quality_score": 0.8 # Placeholder
100
+ })
101
+
102
+ except Exception as e:
103
+ embeddings.append(None)
104
+ extraction_info.append({"error": str(e)})
105
+
106
+ return {
107
+ "embeddings": embeddings,
108
+ "extraction_info": extraction_info,
109
+ "total_processed": len(request.images),
110
+ "successful": len([e for e in embeddings if e is not None])
111
+ }
112
+
113
+ except Exception as e:
114
+ raise HTTPException(status_code=500, detail=str(e))
115
+
116
+ def enhance_image_gpu(img, aggressive=False):
117
+ """GPU-accelerated image enhancement"""
118
+ if aggressive:
119
+ # Strong enhancement for poor quality images
120
+ img = cv2.bilateralFilter(img, 15, 90, 90)
121
+ lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
122
+ l, a, b = cv2.split(lab)
123
+ clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(8,8))
124
+ l = clahe.apply(l)
125
+ img = cv2.merge([l, a, b])
126
+ img = cv2.cvtColor(img, cv2.COLOR_LAB2BGR)
127
+
128
+ # Sharpening
129
+ kernel = np.array([[-1,-1,-1], [-1, 12,-1], [-1,-1,-1]])
130
+ img = cv2.filter2D(img, -1, kernel)
131
+ else:
132
+ # Standard enhancement
133
+ img = cv2.bilateralFilter(img, 9, 75, 75)
134
+ kernel = np.array([[-1,-1,-1], [-1, 9,-1], [-1,-1,-1]])
135
+ img = cv2.filter2D(img, -1, kernel)
136
+
137
+ return img
138
+
139
+ @app.post("/create_faiss_index")
140
+ async def create_faiss_index(request: IndexCreationRequest):
141
+ try:
142
+ embeddings_array = np.array(request.embeddings, dtype='float32')
143
+
144
+ # Choose index type based on dataset size
145
+ if request.dataset_size < 1000:
146
+ index = faiss.IndexFlatL2(request.dimension)
147
+ index_type = "IndexFlatL2"
148
+ elif request.dataset_size < 50000:
149
+ nlist = max(4, min(request.dataset_size // 39, 100))
150
+ quantizer = faiss.IndexFlatL2(request.dimension)
151
+ index = faiss.IndexIVFFlat(quantizer, request.dimension, nlist)
152
+ index_type = "IndexIVFFlat"
153
+ else:
154
+ nlist = max(100, min(request.dataset_size // 39, 1000))
155
+ quantizer = faiss.IndexFlatL2(request.dimension)
156
+ index = faiss.IndexIVFPQ(quantizer, request.dimension, nlist, 64, 8)
157
+ index_type = "IndexIVFPQ"
158
+
159
+ # Move to GPU if available
160
+ if torch.cuda.is_available() and gpu_resources is not None:
161
+ if hasattr(index, 'train'):
162
+ # Train on GPU
163
+ index_gpu = faiss.index_cpu_to_gpu(gpu_resources, 0, index)
164
+ if not index_gpu.is_trained:
165
+ index_gpu.train(embeddings_array)
166
+ index_gpu.add(embeddings_array)
167
+ # Move back to CPU for serialization
168
+ index = faiss.index_gpu_to_cpu(index_gpu)
169
+ else:
170
+ # Flat index - direct GPU processing
171
+ index_gpu = faiss.index_cpu_to_gpu(gpu_resources, 0, index)
172
+ index_gpu.add(embeddings_array)
173
+ index = faiss.index_gpu_to_cpu(index_gpu)
174
+ else:
175
+ # CPU fallback
176
+ if hasattr(index, 'train') and not index.is_trained:
177
+ index.train(embeddings_array)
178
+ index.add(embeddings_array)
179
+
180
+ # Serialize index
181
+ index_data = faiss.serialize_index(index)
182
+ index_b64 = base64.b64encode(index_data).decode()
183
+
184
+ return {
185
+ "index_data": index_b64,
186
+ "index_type": f"GPU_{index_type}",
187
+ "index_params": {"nlist": getattr(index, 'nlist', 0)},
188
+ "vectors_added": index.ntotal
189
+ }
190
+
191
+ except Exception as e:
192
+ raise HTTPException(status_code=500, detail=str(e))
193
+
194
+ @app.post("/search_faiss")
195
+ async def search_faiss(request: dict):
196
+ try:
197
+ # Deserialize index
198
+ index_data = base64.b64decode(request["index_data"])
199
+ index = faiss.deserialize_index(np.frombuffer(index_data, dtype=np.uint8))
200
+
201
+ query_embedding = np.array([request["query_embedding"]], dtype='float32')
202
+ k = request.get("k", 25)
203
+
204
+ # Move to GPU for search
205
+ if torch.cuda.is_available() and gpu_resources is not None:
206
+ index_gpu = faiss.index_cpu_to_gpu(gpu_resources, 0, index)
207
+ distances, indices = index_gpu.search(query_embedding, k)
208
+ else:
209
+ distances, indices = index.search(query_embedding, k)
210
+
211
+ return {
212
+ "distances": distances[0].tolist(),
213
+ "indices": indices[0].tolist(),
214
+ "total_vectors": index.ntotal
215
+ }
216
+
217
+ except Exception as e:
218
+ raise HTTPException(status_code=500, detail=str(e))
219
+
220
+ if __name__ == "__main__":
221
+ import uvicorn
222
+ uvicorn.run(app, host="0.0.0.0", port=8000)
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn[standard]==0.24.0
3
+ torch==2.1.0
4
+ torchvision==0.16.0
5
+ faiss-gpu==1.7.4
6
+ insightface==0.7.3
7
+ opencv-python==4.8.1.78
8
+ Pillow==10.1.0
9
+ numpy==1.24.3
10
+ pydantic==2.4.2
11
+ python-multipart==0.0.6
12
+ onnxruntime-gpu==1.16.0