Marcus Vinicius Zerbini Canhaço commited on
Commit
87bc3e0
·
1 Parent(s): a4d24c9

ajustes finais para T4 e doc

Browse files
README.md CHANGED
@@ -7,7 +7,7 @@ sdk: gradio
7
  sdk_version: 5.15.0
8
  app_file: app.py
9
  pinned: false
10
- license: mit
11
  tags:
12
  - security
13
  - computer-vision
@@ -55,7 +55,9 @@ GPU/CPU otimizado.
55
  - Processamento otimizado em GPU (NVIDIA T4) e CPU
56
  - Interface web intuitiva com Gradio
57
  - API REST para integração
58
- - Suporte a webhooks para notificações
 
 
59
  - Métricas detalhadas de processamento
60
 
61
  ## Requisitos
@@ -170,7 +172,7 @@ src/
170
 
171
  ## Licença
172
 
173
- Este projeto está licenciado sob a MIT License - veja o arquivo [LICENSE](LICENSE)
174
  para detalhes.
175
 
176
  ## Contribuição
 
7
  sdk_version: 5.15.0
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
11
  tags:
12
  - security
13
  - computer-vision
 
55
  - Processamento otimizado em GPU (NVIDIA T4) e CPU
56
  - Interface web intuitiva com Gradio
57
  - API REST para integração
58
+ - Sistema de notificações:
59
+ - Webhook para integrações personalizadas
60
+ - E-mail para alertas diretos
61
  - Métricas detalhadas de processamento
62
 
63
  ## Requisitos
 
172
 
173
  ## Licença
174
 
175
+ Este projeto está licenciado sob a Apache License 2.0 - veja o arquivo [LICENSE](LICENSE)
176
  para detalhes.
177
 
178
  ## Contribuição
docs/architecture/overview.md CHANGED
@@ -163,11 +163,59 @@ class NewDetector(DetectorInterface):
163
  ### 3. Sistema de Notificações
164
 
165
  ```python
166
- class NewNotificationService(NotificationService):
167
- """Novo serviço de notificação."""
168
  def send_notification(self):
169
  # Implementação específica
170
  pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  ```
172
 
173
  ## Fluxo de Processamento
 
163
  ### 3. Sistema de Notificações
164
 
165
  ```python
166
+ class NotificationService {
167
+ """Serviço de notificação abstrato."""
168
  def send_notification(self):
169
  # Implementação específica
170
  pass
171
+ }
172
+
173
+ class EmailNotification(NotificationService):
174
+ """Serviço de notificação por e-mail."""
175
+ def send_notification(self, detection_data: dict, target: str):
176
+ # Envia e-mail com detalhes da detecção
177
+ pass
178
+
179
+ class WebhookNotification(NotificationService):
180
+ """Serviço de notificação via webhook."""
181
+ def send_notification(self, detection_data: dict, webhook_url: str):
182
+ # Envia POST request para o webhook configurado
183
+ pass
184
+ ```
185
+
186
+ #### Tipos de Notificação Implementados
187
+
188
+ 1. **E-mail**
189
+ - Envio de alertas por e-mail
190
+ - Suporte a templates HTML
191
+ - Detalhes das detecções incluídos
192
+ - Configurável via variáveis de ambiente
193
+
194
+ 2. **Webhook**
195
+ - Integração com sistemas externos
196
+ - Payload JSON customizável
197
+ - Suporte a autenticação
198
+ - Headers configuráveis
199
+ - Retry com backoff exponencial
200
+
201
+ #### Fluxo de Notificações
202
+
203
+ ```mermaid
204
+ sequenceDiagram
205
+ participant D as Detector
206
+ participant NS as NotificationService
207
+ participant E as EmailService
208
+ participant W as WebhookService
209
+
210
+ D->>NS: Detecção Encontrada
211
+ alt Email Configurado
212
+ NS->>E: Envia Alerta
213
+ E-->>NS: Status Envio
214
+ else Webhook Configurado
215
+ NS->>W: Envia POST
216
+ W-->>NS: Status Request
217
+ end
218
+ NS-->>D: Resultado
219
  ```
220
 
221
  ## Fluxo de Processamento
src/domain/detectors/gpu.py CHANGED
@@ -136,7 +136,6 @@ class WeaponDetectorGPU(BaseDetector):
136
  gc.collect()
137
 
138
  def process_video(self, video_path: str, fps: int = None, threshold: float = 0.3, resolution: int = 640) -> Tuple[str, Dict]:
139
- """Processa um vídeo."""
140
  metrics = {
141
  "total_time": 0,
142
  "frame_extraction_time": 0,
@@ -165,7 +164,7 @@ class WeaponDetectorGPU(BaseDetector):
165
 
166
  # Processar frames em batch
167
  t0 = time.time()
168
- batch_size = 16 # Aumentado para T4 dedicada
169
  detections_by_frame = []
170
 
171
  for i in range(0, len(frames), batch_size):
@@ -179,61 +178,73 @@ class WeaponDetectorGPU(BaseDetector):
179
  frame_pil = self._preprocess_image(frame_pil)
180
  batch_pil_frames.append(frame_pil)
181
 
182
- # Processar batch
183
- batch_inputs = self.owlv2_processor(
184
- images=batch_pil_frames,
185
- return_tensors="pt",
186
- padding=True
187
- )
188
- batch_inputs = {
189
- key: val.to(self.device)
190
- for key, val in batch_inputs.items()
191
- }
192
-
193
- # Inferência em batch
194
- with torch.no_grad():
195
- inputs = {**batch_inputs, **self.processed_text}
196
- outputs = self.owlv2_model(**inputs)
197
-
198
- target_sizes = torch.tensor(
199
- [frame.size[::-1] for frame in batch_pil_frames],
200
- device=self.device
201
  )
202
- results = self.owlv2_processor.post_process_grounded_object_detection(
203
- outputs=outputs,
204
- target_sizes=target_sizes,
205
- threshold=threshold
206
- )
207
-
208
- # Processar resultados do batch
209
- for frame_idx, frame_results in enumerate(results):
210
- if len(frame_results["scores"]) > 0:
211
- scores = frame_results["scores"]
212
- boxes = frame_results["boxes"]
213
- labels = frame_results["labels"]
214
 
215
- frame_detections = []
216
- for score, box, label in zip(scores, boxes, labels):
217
- score_val = score.item()
218
- if score_val >= threshold:
219
- label_idx = min(label.item(), len(self.text_queries) - 1)
220
- label_text = self.text_queries[label_idx]
221
- frame_detections.append({
222
- "confidence": round(score_val * 100, 2),
223
- "box": [int(x) for x in box.tolist()],
224
- "label": label_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  })
226
-
227
- if frame_detections:
228
- frame_detections = self._apply_nms(frame_detections)
229
- detections_by_frame.append({
230
- "frame": i + frame_idx,
231
- "detections": frame_detections
232
- })
233
 
234
- # Liberar memória do batch
235
- del batch_inputs, outputs
236
- torch.cuda.empty_cache()
 
 
 
 
 
 
 
 
 
 
237
 
238
  # Atualizar métricas finais
239
  metrics["analysis_time"] = time.time() - t0
 
136
  gc.collect()
137
 
138
  def process_video(self, video_path: str, fps: int = None, threshold: float = 0.3, resolution: int = 640) -> Tuple[str, Dict]:
 
139
  metrics = {
140
  "total_time": 0,
141
  "frame_extraction_time": 0,
 
164
 
165
  # Processar frames em batch
166
  t0 = time.time()
167
+ batch_size = 8 # Reduzido para evitar erros de memória
168
  detections_by_frame = []
169
 
170
  for i in range(0, len(frames), batch_size):
 
178
  frame_pil = self._preprocess_image(frame_pil)
179
  batch_pil_frames.append(frame_pil)
180
 
181
+ try:
182
+ # Processar batch
183
+ batch_inputs = self.owlv2_processor(
184
+ images=batch_pil_frames,
185
+ return_tensors="pt",
186
+ padding=True
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  )
188
+ batch_inputs = {
189
+ key: val.to(self.device)
190
+ for key, val in batch_inputs.items()
191
+ }
192
+
193
+ # Inferência em batch
194
+ with torch.no_grad():
195
+ inputs = {**batch_inputs, **self.processed_text}
196
+ outputs = self.owlv2_model(**inputs)
 
 
 
197
 
198
+ target_sizes = torch.tensor(
199
+ [frame.size[::-1] for frame in batch_pil_frames],
200
+ device=self.device
201
+ )
202
+ results = self.owlv2_processor.post_process_grounded_object_detection(
203
+ outputs=outputs,
204
+ target_sizes=target_sizes,
205
+ threshold=threshold
206
+ )
207
+
208
+ # Processar resultados do batch
209
+ for frame_idx, frame_results in enumerate(results):
210
+ if len(frame_results["scores"]) > 0:
211
+ scores = frame_results["scores"]
212
+ boxes = frame_results["boxes"]
213
+ labels = frame_results["labels"]
214
+
215
+ frame_detections = []
216
+ for score, box, label in zip(scores, boxes, labels):
217
+ score_val = score.item()
218
+ if score_val >= threshold:
219
+ label_idx = min(label.item(), len(self.text_queries) - 1)
220
+ label_text = self.text_queries[label_idx]
221
+ frame_detections.append({
222
+ "confidence": round(score_val * 100, 2),
223
+ "box": [int(x) for x in box.tolist()],
224
+ "label": label_text,
225
+ "timestamp": (i + frame_idx) / (fps or 2)
226
+ })
227
+
228
+ if frame_detections:
229
+ frame_detections = self._apply_nms(frame_detections)
230
+ detections_by_frame.append({
231
+ "frame": i + frame_idx,
232
+ "detections": frame_detections
233
  })
 
 
 
 
 
 
 
234
 
235
+ except RuntimeError as e:
236
+ logger.error(f"Erro no processamento do batch: {str(e)}")
237
+ if "out of memory" in str(e):
238
+ torch.cuda.empty_cache()
239
+ gc.collect()
240
+ continue
241
+
242
+ finally:
243
+ # Liberar memória do batch
244
+ del batch_inputs
245
+ if 'outputs' in locals():
246
+ del outputs
247
+ torch.cuda.empty_cache()
248
 
249
  # Atualizar métricas finais
250
  metrics["analysis_time"] = time.time() - t0
src/presentation/web/gradio_interface.py CHANGED
@@ -190,7 +190,7 @@ class GradioInterface:
190
  )
191
 
192
  submit_btn = gr.Button(
193
- "Analisar Vídeo",
194
  variant="primary",
195
  scale=2
196
  )
@@ -280,24 +280,24 @@ class GradioInterface:
280
  response = self.use_case.execute(request)
281
 
282
  # Formatar saída para o Gradio
283
- status_color = "#ff0000" if response.detections else "#00ff00"
284
  status_html = f"""
285
  <div style='padding: 1em; background: {status_color}20; border-radius: 8px;'>
286
  <h3 style='color: {status_color}; margin: 0;'>
287
- {"⚠️ RISCO DETECTADO" if response.detections else "✅ SEGURO"}
288
  </h3>
289
  <p style='margin: 0.5em 0;'>
290
- Processado em: {response.device_type}<br>
291
- Total de detecções: {len(response.detections)}<br>
292
- Frames analisados: {response.frames_analyzed}<br>
293
- Tempo total: {response.total_time:.2f}s
294
  </p>
295
  </div>
296
  """
297
 
298
- if response.detections:
299
  status_html += "<div style='margin-top: 1em;'><h4>Detecções:</h4><ul>"
300
- for det in response.detections[:5]: # Mostrar até 5 detecções
301
  confidence_pct = det.confidence * 100 if det.confidence <= 1.0 else det.confidence
302
  status_html += f"""
303
  <li style='margin: 0.5em 0;'>
@@ -305,8 +305,8 @@ class GradioInterface:
305
  Confiança: {confidence_pct:.1f}%<br>
306
  Frame: {det.frame}
307
  </li>"""
308
- if len(response.detections) > 5:
309
- status_html += f"<li>... e mais {len(response.detections) - 5} detecção(ões)</li>"
310
  status_html += "</ul></div>"
311
 
312
  return (
 
190
  )
191
 
192
  submit_btn = gr.Button(
193
+ "Detectar",
194
  variant="primary",
195
  scale=2
196
  )
 
280
  response = self.use_case.execute(request)
281
 
282
  # Formatar saída para o Gradio
283
+ status_color = "#ff0000" if response.detection_result.detections else "#00ff00"
284
  status_html = f"""
285
  <div style='padding: 1em; background: {status_color}20; border-radius: 8px;'>
286
  <h3 style='color: {status_color}; margin: 0;'>
287
+ {"⚠️ RISCO DETECTADO" if response.detection_result.detections else "✅ SEGURO"}
288
  </h3>
289
  <p style='margin: 0.5em 0;'>
290
+ Processado em: {response.detection_result.device_type}<br>
291
+ Total de detecções: {len(response.detection_result.detections)}<br>
292
+ Frames analisados: {response.detection_result.frames_analyzed}<br>
293
+ Tempo total: {response.detection_result.total_time:.2f}s
294
  </p>
295
  </div>
296
  """
297
 
298
+ if response.detection_result.detections:
299
  status_html += "<div style='margin-top: 1em;'><h4>Detecções:</h4><ul>"
300
+ for det in response.detection_result.detections[:5]: # Mostrar até 5 detecções
301
  confidence_pct = det.confidence * 100 if det.confidence <= 1.0 else det.confidence
302
  status_html += f"""
303
  <li style='margin: 0.5em 0;'>
 
305
  Confiança: {confidence_pct:.1f}%<br>
306
  Frame: {det.frame}
307
  </li>"""
308
+ if len(response.detection_result.detections) > 5:
309
+ status_html += f"<li>... e mais {len(response.detection_result.detections) - 5} detecção(ões)</li>"
310
  status_html += "</ul></div>"
311
 
312
  return (