Marcus Vinicius Zerbini Canhaço commited on
Commit
1ccfc24
·
1 Parent(s): 62fec37

feat: atualização do detector com otimizações para GPU T4

Browse files
src/domain/detectors/base.py CHANGED
@@ -155,7 +155,7 @@ class BaseDetector(ABC):
155
  """Retorna as queries otimizadas para detecção de objetos perigosos."""
156
  firearms = ["handgun", "rifle", "shotgun", "machine gun", "firearm"]
157
  edged_weapons = ["knife", "dagger", "machete", "box cutter", "sword"]
158
- ranged_weapons = ["crossbow", "bow"]
159
  sharp_objects = ["blade", "razor", "glass shard", "screwdriver", "metallic pointed object"]
160
 
161
  firearm_contexts = ["close-up", "clear view", "detailed"]
 
155
  """Retorna as queries otimizadas para detecção de objetos perigosos."""
156
  firearms = ["handgun", "rifle", "shotgun", "machine gun", "firearm"]
157
  edged_weapons = ["knife", "dagger", "machete", "box cutter", "sword"]
158
+ ranged_weapons = ["crossbow", "bow","arrow"]
159
  sharp_objects = ["blade", "razor", "glass shard", "screwdriver", "metallic pointed object"]
160
 
161
  firearm_contexts = ["close-up", "clear view", "detailed"]
src/domain/detectors/gpu.py CHANGED
@@ -9,6 +9,7 @@ from PIL import Image
9
  from typing import List, Dict, Any, Tuple
10
  from transformers import Owlv2Processor, Owlv2ForObjectDetection
11
  from .base import BaseDetector
 
12
 
13
  logger = logging.getLogger(__name__)
14
 
@@ -48,6 +49,8 @@ class WeaponDetectorGPU(BaseDetector):
48
 
49
  # Processar queries
50
  self.text_queries = self._get_detection_queries()
 
 
51
  self.processed_text = self.owlv2_processor(
52
  text=self.text_queries,
53
  return_tensors="pt",
@@ -101,12 +104,17 @@ class WeaponDetectorGPU(BaseDetector):
101
  labels = results["labels"]
102
 
103
  for score, box, label in zip(scores, boxes, labels):
104
- if score.item() >= threshold:
 
 
 
 
105
  detections.append({
106
- "confidence": score.item(),
107
  "box": [int(x) for x in box.tolist()],
108
- "label": self.text_queries[label]
109
  })
 
110
 
111
  # Aplicar NMS nas detecções
112
  detections = self._apply_nms(detections)
@@ -131,25 +139,59 @@ class WeaponDetectorGPU(BaseDetector):
131
  """Processa um vídeo."""
132
  metrics = {
133
  "total_time": 0,
 
 
134
  "frames_analyzed": 0,
 
 
135
  "detections": []
136
  }
137
 
138
  try:
 
 
 
 
139
  frames = self.extract_frames(video_path, fps or 2, resolution)
 
140
  metrics["frames_analyzed"] = len(frames)
141
 
 
 
 
 
 
 
 
 
 
142
  for i, frame in enumerate(frames):
143
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
144
  frame_pil = Image.fromarray(frame_rgb)
145
 
146
  detections = self.detect_objects(frame_pil, threshold)
147
- if detections:
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  metrics["detections"].append({
149
  "frame": i,
150
- "detections": detections
151
  })
152
- return video_path, metrics
 
 
 
153
 
154
  return video_path, metrics
155
 
 
9
  from typing import List, Dict, Any, Tuple
10
  from transformers import Owlv2Processor, Owlv2ForObjectDetection
11
  from .base import BaseDetector
12
+ import time
13
 
14
  logger = logging.getLogger(__name__)
15
 
 
49
 
50
  # Processar queries
51
  self.text_queries = self._get_detection_queries()
52
+ logger.info(f"Queries carregadas: {self.text_queries}") # Log das queries
53
+
54
  self.processed_text = self.owlv2_processor(
55
  text=self.text_queries,
56
  return_tensors="pt",
 
104
  labels = results["labels"]
105
 
106
  for score, box, label in zip(scores, boxes, labels):
107
+ score_val = score.item()
108
+ if score_val >= threshold:
109
+ # Garantir que o índice está dentro dos limites
110
+ label_idx = min(label.item(), len(self.text_queries) - 1)
111
+ label_text = self.text_queries[label_idx]
112
  detections.append({
113
+ "confidence": round(score_val * 100, 2), # Converter para porcentagem
114
  "box": [int(x) for x in box.tolist()],
115
+ "label": label_text
116
  })
117
+ logger.debug(f"Detecção: {label_text} ({score_val * 100:.2f}%)")
118
 
119
  # Aplicar NMS nas detecções
120
  detections = self._apply_nms(detections)
 
139
  """Processa um vídeo."""
140
  metrics = {
141
  "total_time": 0,
142
+ "frame_extraction_time": 0,
143
+ "analysis_time": 0,
144
  "frames_analyzed": 0,
145
+ "video_duration": 0,
146
+ "device_type": "GPU",
147
  "detections": []
148
  }
149
 
150
  try:
151
+ start_time = time.time()
152
+
153
+ # Extrair frames
154
+ t0 = time.time()
155
  frames = self.extract_frames(video_path, fps or 2, resolution)
156
+ metrics["frame_extraction_time"] = time.time() - t0
157
  metrics["frames_analyzed"] = len(frames)
158
 
159
+ if not frames:
160
+ logger.warning("Nenhum frame extraído do vídeo")
161
+ return video_path, metrics
162
+
163
+ # Calcular duração do vídeo
164
+ metrics["video_duration"] = len(frames) / (fps or 2)
165
+
166
+ # Processar frames
167
+ t0 = time.time()
168
  for i, frame in enumerate(frames):
169
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
170
  frame_pil = Image.fromarray(frame_rgb)
171
 
172
  detections = self.detect_objects(frame_pil, threshold)
173
+
174
+ # Filtrar apenas detecções válidas (sem filtrar unknown)
175
+ valid_detections = [
176
+ {
177
+ "confidence": d["confidence"],
178
+ "box": d["box"],
179
+ "label": d["label"],
180
+ "timestamp": i / (fps or 2)
181
+ }
182
+ for d in detections
183
+ if d["confidence"] > threshold
184
+ ]
185
+
186
+ if valid_detections:
187
  metrics["detections"].append({
188
  "frame": i,
189
+ "detections": valid_detections
190
  })
191
+
192
+ # Atualizar métricas finais
193
+ metrics["analysis_time"] = time.time() - t0
194
+ metrics["total_time"] = time.time() - start_time
195
 
196
  return video_path, metrics
197
 
src/main.py CHANGED
@@ -4,6 +4,7 @@ from src.presentation.web.gradio_interface import GradioInterface
4
  import logging
5
  import torch
6
  import gc
 
7
  from src.domain.factories.detector_factory import force_gpu_init, is_gpu_available
8
 
9
  # Configurar logging
@@ -13,78 +14,83 @@ logging.basicConfig(
13
  )
14
  logger = logging.getLogger(__name__)
15
 
16
- def check_cuda_environment():
17
- """Verifica e configura o ambiente CUDA."""
18
  try:
19
- # Verificar variáveis de ambiente CUDA
20
- cuda_path = os.getenv('CUDA_HOME') or os.getenv('CUDA_PATH')
21
- if not cuda_path:
22
- logger.warning("Variáveis de ambiente CUDA não encontradas")
23
- return False
24
-
25
- # Verificar se CUDA está disponível no PyTorch
26
- if not torch.cuda.is_available():
27
- logger.warning("PyTorch não detectou CUDA")
28
- return False
29
-
30
- # Tentar obter informações da GPU
31
- try:
32
- device_count = torch.cuda.device_count()
33
- if device_count > 0:
34
- device_name = torch.cuda.get_device_name(0)
35
- logger.info(f"GPU detectada: {device_name}")
36
- return True
37
- except Exception as e:
38
- logger.warning(f"Erro ao obter informações da GPU: {str(e)}")
39
 
40
- return False
41
  except Exception as e:
42
- logger.error(f"Erro ao verificar ambiente CUDA: {str(e)}")
43
- return False
 
 
 
 
 
44
 
45
- def setup_zero_gpu():
46
- """Configurações otimizadas para Zero-GPU."""
47
  try:
48
- # Verificar ambiente CUDA primeiro
49
- if not check_cuda_environment():
50
- logger.warning("Ambiente CUDA não está configurado corretamente")
51
  return False
52
 
53
- # Tentar inicializar GPU
54
- if is_gpu_available():
55
- # Configurar ambiente
56
- os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'
57
- os.environ['CUDA_VISIBLE_DEVICES'] = '0'
58
-
59
- # Limpar memória
60
- torch.cuda.empty_cache()
61
- gc.collect()
62
-
63
- # Configurações de memória e performance
64
- os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:128'
65
  torch.backends.cuda.matmul.allow_tf32 = True
66
  torch.backends.cudnn.benchmark = True
67
  torch.backends.cudnn.allow_tf32 = True
68
-
69
- # Configurar fração de memória
70
  torch.cuda.set_per_process_memory_fraction(0.9)
 
71
 
72
- # Verificar se a configuração foi bem sucedida
73
- try:
74
- device = torch.device('cuda')
75
- dummy = torch.zeros(1, device=device)
76
- del dummy
77
- logger.info(f"Configurações Zero-GPU aplicadas com sucesso na GPU: {torch.cuda.get_device_name(0)}")
78
- return True
79
- except Exception as e:
80
- logger.error(f"Erro ao configurar GPU: {str(e)}")
81
- return False
82
- else:
83
- logger.warning("GPU não disponível para configuração Zero-GPU. O sistema operará em modo CPU.")
 
 
 
 
 
84
  return False
 
85
  except Exception as e:
86
- logger.error(f"Erro ao configurar Zero-GPU: {str(e)}")
87
- logger.warning("Fallback para modo CPU devido a erro na configuração da GPU.")
88
  return False
89
 
90
  def main():
@@ -97,7 +103,15 @@ def main():
97
  if IS_HUGGINGFACE:
98
  load_dotenv('.env.huggingface')
99
  logger.info("Ambiente HuggingFace detectado")
100
- gpu_available = setup_zero_gpu()
 
 
 
 
 
 
 
 
101
  else:
102
  load_dotenv('.env')
103
  logger.info("Ambiente local detectado")
@@ -108,21 +122,20 @@ def main():
108
  demo = interface.create_interface()
109
 
110
  if IS_HUGGINGFACE:
111
- # Configurar com base na disponibilidade da GPU
112
- if gpu_available:
113
- gpu_mem = torch.cuda.get_device_properties(0).total_memory / (1024**3)
114
- max_concurrent = 1 # Forçar single worker para Zero-GPU
115
- logger.info(f"GPU Memory: {gpu_mem:.1f}GB, Max Concurrent: {max_concurrent}")
116
  else:
117
- max_concurrent = 1
118
- logger.warning("GPU não disponível. O sistema está operando em modo CPU. " +
119
- "Todas as funcionalidades estão disponíveis, mas o processamento será mais lento.")
120
 
121
  # Configurar fila
122
  demo = demo.queue(
123
  api_open=False,
 
124
  status_update_rate="auto",
125
- max_size=5 # Reduzir tamanho da fila para economizar memória
126
  )
127
 
128
  # Launch
@@ -130,7 +143,7 @@ def main():
130
  server_name="0.0.0.0",
131
  server_port=7860,
132
  share=False,
133
- max_threads=2 # Reduzir número de threads
134
  )
135
  else:
136
  demo.launch(
 
4
  import logging
5
  import torch
6
  import gc
7
+ import nvidia_smi
8
  from src.domain.factories.detector_factory import force_gpu_init, is_gpu_available
9
 
10
  # Configurar logging
 
14
  )
15
  logger = logging.getLogger(__name__)
16
 
17
+ def check_gpu_type():
18
+ """Verifica o tipo de GPU disponível no ambiente Hugging Face."""
19
  try:
20
+ nvidia_smi.nvmlInit()
21
+ handle = nvidia_smi.nvmlDeviceGetHandleByIndex(0)
22
+ info = nvidia_smi.nvmlDeviceGetMemoryInfo(handle)
23
+ gpu_name = nvidia_smi.nvmlDeviceGetName(handle)
24
+ total_memory = info.total / (1024**3) # Converter para GB
25
+
26
+ logger.info(f"GPU detectada: {gpu_name}")
27
+ logger.info(f"Memória total: {total_memory:.2f}GB")
28
+
29
+ # T4 dedicada tem tipicamente 16GB
30
+ if "T4" in gpu_name and total_memory > 14:
31
+ return "t4_dedicated"
32
+ # Zero-GPU compartilhada tem tipicamente menos memória
33
+ elif total_memory < 14:
34
+ return "zero_gpu_shared"
35
+ else:
36
+ return "unknown"
 
 
 
37
 
 
38
  except Exception as e:
39
+ logger.error(f"Erro ao verificar tipo de GPU: {str(e)}")
40
+ return "unknown"
41
+ finally:
42
+ try:
43
+ nvidia_smi.nvmlShutdown()
44
+ except:
45
+ pass
46
 
47
+ def setup_gpu_environment(gpu_type: str) -> bool:
48
+ """Configura o ambiente GPU com base no tipo detectado."""
49
  try:
50
+ # Verificar ambiente CUDA
51
+ if not torch.cuda.is_available():
52
+ logger.warning("CUDA não está disponível")
53
  return False
54
 
55
+ # Configurações comuns
56
+ os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'
57
+ os.environ['CUDA_VISIBLE_DEVICES'] = '0'
58
+
59
+ # Limpar memória
60
+ torch.cuda.empty_cache()
61
+ gc.collect()
62
+
63
+ if gpu_type == "t4_dedicated":
64
+ # Configurações para T4 dedicada
65
+ logger.info("Configurando para T4 dedicada")
 
66
  torch.backends.cuda.matmul.allow_tf32 = True
67
  torch.backends.cudnn.benchmark = True
68
  torch.backends.cudnn.allow_tf32 = True
69
+ # Usar mais memória pois temos GPU dedicada
 
70
  torch.cuda.set_per_process_memory_fraction(0.9)
71
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:512'
72
 
73
+ elif gpu_type == "zero_gpu_shared":
74
+ # Configurações para Zero-GPU compartilhada
75
+ logger.info("Configurando para Zero-GPU compartilhada")
76
+ torch.backends.cudnn.benchmark = False
77
+ # Limitar uso de memória
78
+ torch.cuda.set_per_process_memory_fraction(0.6)
79
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:128'
80
+
81
+ # Verificar configuração
82
+ try:
83
+ device = torch.device('cuda')
84
+ dummy = torch.zeros(1, device=device)
85
+ del dummy
86
+ logger.info(f"Configurações GPU aplicadas com sucesso para: {gpu_type}")
87
+ return True
88
+ except Exception as e:
89
+ logger.error(f"Erro ao configurar GPU: {str(e)}")
90
  return False
91
+
92
  except Exception as e:
93
+ logger.error(f"Erro ao configurar ambiente GPU: {str(e)}")
 
94
  return False
95
 
96
  def main():
 
103
  if IS_HUGGINGFACE:
104
  load_dotenv('.env.huggingface')
105
  logger.info("Ambiente HuggingFace detectado")
106
+
107
+ # Identificar e configurar GPU
108
+ gpu_type = check_gpu_type()
109
+ gpu_available = setup_gpu_environment(gpu_type)
110
+
111
+ if gpu_available:
112
+ logger.info(f"GPU configurada com sucesso: {gpu_type}")
113
+ else:
114
+ logger.warning("GPU não disponível ou não configurada corretamente")
115
  else:
116
  load_dotenv('.env')
117
  logger.info("Ambiente local detectado")
 
122
  demo = interface.create_interface()
123
 
124
  if IS_HUGGINGFACE:
125
+ # Configurar com base no tipo de GPU
126
+ if gpu_type == "t4_dedicated":
127
+ max_concurrent = 2 # T4 pode lidar com mais requisições
128
+ queue_size = 10
 
129
  else:
130
+ max_concurrent = 1 # Zero-GPU precisa ser mais conservadora
131
+ queue_size = 5
 
132
 
133
  # Configurar fila
134
  demo = demo.queue(
135
  api_open=False,
136
+ max_size=queue_size,
137
  status_update_rate="auto",
138
+ concurrency_count=max_concurrent
139
  )
140
 
141
  # Launch
 
143
  server_name="0.0.0.0",
144
  server_port=7860,
145
  share=False,
146
+ max_threads=max_concurrent
147
  )
148
  else:
149
  demo.launch(
src/presentation/web/gradio_interface.py CHANGED
@@ -228,23 +228,23 @@ class GradioInterface:
228
  with gr.Row():
229
  with gr.Column(scale=3):
230
  gr.Markdown("#### Vídeo")
231
- with gr.Column(scale=1):
232
- gr.Markdown("#### Tipo")
233
  with gr.Column(scale=1):
234
  gr.Markdown("#### Ação")
235
 
236
  for video in sample_videos:
237
  with gr.Row():
238
- with gr.Column(scale=3):
239
- gr.Video(
240
  value=video['path'],
241
  format="mp4",
242
  height=150,
243
- interactive=False,
244
- show_label=False
245
- )
246
- with gr.Column(scale=1):
247
- gr.Markdown(video['ground_truth'])
 
 
248
  with gr.Column(scale=1, min_width=100):
249
  gr.Button(
250
  "📥 Carregar",
 
228
  with gr.Row():
229
  with gr.Column(scale=3):
230
  gr.Markdown("#### Vídeo")
 
 
231
  with gr.Column(scale=1):
232
  gr.Markdown("#### Ação")
233
 
234
  for video in sample_videos:
235
  with gr.Row():
236
+ with gr.Column(scale=3):
237
+ gr.PlayableVideo(
238
  value=video['path'],
239
  format="mp4",
240
  height=150,
241
+ interactive=True,
242
+ show_label=True).click(
243
+ fn=self.load_sample_video,
244
+ inputs=[gr.State(video['path'])],
245
+ outputs=[input_video]
246
+ )
247
+
248
  with gr.Column(scale=1, min_width=100):
249
  gr.Button(
250
  "📥 Carregar",