Marcus Vinicius Zerbini Canhaço commited on
Commit
397303b
·
1 Parent(s): 75a5413

Limpeza de codigo

Browse files
src/domain/detectors/gpu.py CHANGED
@@ -24,7 +24,6 @@ class WeaponDetectorGPU(BaseDetector):
24
  self._initialize()
25
 
26
  def _initialize(self):
27
- """Inicializa o modelo."""
28
  try:
29
  # Configurar device
30
  if not torch.cuda.is_available():
@@ -55,11 +54,7 @@ class WeaponDetectorGPU(BaseDetector):
55
  text=self.text_queries,
56
  return_tensors="pt",
57
  padding=True
58
- )
59
- self.processed_text = {
60
- key: val.to(self.device)
61
- for key, val in self.processed_text.items()
62
- }
63
 
64
  logger.info("Inicialização GPU completa!")
65
  self._initialized = True
@@ -78,16 +73,11 @@ class WeaponDetectorGPU(BaseDetector):
78
  image_inputs = self.owlv2_processor(
79
  images=image,
80
  return_tensors="pt"
81
- )
82
- image_inputs = {
83
- key: val.to(self.device)
84
- for key, val in image_inputs.items()
85
- }
86
 
87
  # Inferência
88
  with torch.no_grad():
89
- inputs = {**image_inputs, **self.processed_text}
90
- outputs = self.owlv2_model(**inputs)
91
 
92
  target_sizes = torch.tensor([image.size[::-1]], device=self.device)
93
  results = self.owlv2_processor.post_process_grounded_object_detection(
@@ -99,26 +89,19 @@ class WeaponDetectorGPU(BaseDetector):
99
  # Processar detecções
100
  detections = []
101
  if len(results["scores"]) > 0:
102
- scores = results["scores"]
103
- boxes = results["boxes"]
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)
121
- return detections
122
 
123
  except Exception as e:
124
  logger.error(f"Erro em detect_objects: {str(e)}")
@@ -136,6 +119,7 @@ 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
  metrics = {
140
  "total_time": 0,
141
  "frame_extraction_time": 0,
@@ -175,80 +159,7 @@ class WeaponDetectorGPU(BaseDetector):
175
 
176
  # Processar frames
177
  t0 = time.time()
178
- detections_by_frame = []
179
-
180
- # Pré-alocar tensores para evitar alocações frequentes
181
- with torch.cuda.device(self.device):
182
- torch.cuda.empty_cache()
183
-
184
- for i, frame in enumerate(frames):
185
- try:
186
- # Preparar frame
187
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
188
- frame_pil = Image.fromarray(frame_rgb)
189
- frame_pil = self._preprocess_image(frame_pil)
190
-
191
- # Processar frame
192
- inputs = self.owlv2_processor(
193
- images=frame_pil,
194
- return_tensors="pt"
195
- )
196
- inputs = {
197
- key: val.to(self.device)
198
- for key, val in inputs.items()
199
- }
200
-
201
- # Inferência
202
- with torch.no_grad():
203
- model_inputs = {**inputs, **self.processed_text}
204
- outputs = self.owlv2_model(**model_inputs)
205
-
206
- target_sizes = torch.tensor([frame_pil.size[::-1]], device=self.device)
207
- results = self.owlv2_processor.post_process_grounded_object_detection(
208
- outputs=outputs,
209
- target_sizes=target_sizes,
210
- threshold=threshold
211
- )[0]
212
-
213
- # Processar resultados
214
- if len(results["scores"]) > 0:
215
- scores = results["scores"]
216
- boxes = results["boxes"]
217
- labels = results["labels"]
218
-
219
- frame_detections = []
220
- for score, box, label in zip(scores, boxes, labels):
221
- score_val = score.item()
222
- if score_val >= threshold:
223
- label_idx = min(label.item(), len(self.text_queries) - 1)
224
- label_text = self.text_queries[label_idx]
225
- frame_detections.append({
226
- "confidence": round(score_val * 100, 2),
227
- "box": [int(x) for x in box.tolist()],
228
- "label": label_text,
229
- "frame": i,
230
- "timestamp": i / (fps or 2)
231
- })
232
-
233
- if frame_detections:
234
- frame_detections = self._apply_nms(frame_detections)
235
- detections_by_frame.extend(frame_detections)
236
-
237
- except Exception as e:
238
- logger.error(f"Erro ao processar frame {i}: {str(e)}")
239
- continue
240
-
241
- finally:
242
- # Liberar memória
243
- if 'inputs' in locals():
244
- del inputs
245
- if 'outputs' in locals():
246
- del outputs
247
- torch.cuda.empty_cache()
248
-
249
- # Log de progresso
250
- if i % 10 == 0:
251
- logger.info(f"Processados {i}/{len(frames)} frames")
252
 
253
  # Atualizar métricas finais
254
  metrics["analysis_time"] = time.time() - t0
@@ -261,6 +172,68 @@ class WeaponDetectorGPU(BaseDetector):
261
  logger.error(f"Erro ao processar vídeo: {str(e)}")
262
  return video_path, metrics
263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  def _preprocess_image(self, image: Image.Image) -> Image.Image:
265
  """Pré-processa a imagem para o formato esperado pelo modelo."""
266
  try:
 
24
  self._initialize()
25
 
26
  def _initialize(self):
 
27
  try:
28
  # Configurar device
29
  if not torch.cuda.is_available():
 
54
  text=self.text_queries,
55
  return_tensors="pt",
56
  padding=True
57
+ ).to(self.device)
 
 
 
 
58
 
59
  logger.info("Inicialização GPU completa!")
60
  self._initialized = True
 
73
  image_inputs = self.owlv2_processor(
74
  images=image,
75
  return_tensors="pt"
76
+ ).to(self.device)
 
 
 
 
77
 
78
  # Inferência
79
  with torch.no_grad():
80
+ outputs = self.owlv2_model(**{**image_inputs, **self.processed_text})
 
81
 
82
  target_sizes = torch.tensor([image.size[::-1]], device=self.device)
83
  results = self.owlv2_processor.post_process_grounded_object_detection(
 
89
  # Processar detecções
90
  detections = []
91
  if len(results["scores"]) > 0:
92
+ for score, box, label in zip(results["scores"], results["boxes"], results["labels"]):
 
 
 
 
93
  score_val = score.item()
94
  if score_val >= threshold:
 
95
  label_idx = min(label.item(), len(self.text_queries) - 1)
 
96
  detections.append({
97
+ "confidence": round(score_val * 100, 2),
98
  "box": [int(x) for x in box.tolist()],
99
+ "label": self.text_queries[label_idx]
100
  })
101
+ logger.debug(f"Detecção: {self.text_queries[label_idx]} ({score_val * 100:.2f}%)")
102
 
103
  # Aplicar NMS nas detecções
104
+ return self._apply_nms(detections)
 
105
 
106
  except Exception as e:
107
  logger.error(f"Erro em detect_objects: {str(e)}")
 
119
  gc.collect()
120
 
121
  def process_video(self, video_path: str, fps: int = None, threshold: float = 0.3, resolution: int = 640) -> Tuple[str, Dict]:
122
+ """Processa um vídeo para detecção de objetos."""
123
  metrics = {
124
  "total_time": 0,
125
  "frame_extraction_time": 0,
 
159
 
160
  # Processar frames
161
  t0 = time.time()
162
+ detections_by_frame = self._process_frames(frames, fps, threshold)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  # Atualizar métricas finais
165
  metrics["analysis_time"] = time.time() - t0
 
172
  logger.error(f"Erro ao processar vídeo: {str(e)}")
173
  return video_path, metrics
174
 
175
+ def _process_frames(self, frames: List[np.ndarray], fps: int, threshold: float) -> List[Dict]:
176
+ """Processa frames do vídeo para detecção."""
177
+ detections_by_frame = []
178
+
179
+ for i, frame in enumerate(frames):
180
+ try:
181
+ # Preparar frame
182
+ frame_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
183
+ frame_pil = self._preprocess_image(frame_pil)
184
+
185
+ # Processar frame
186
+ inputs = self.owlv2_processor(
187
+ images=frame_pil,
188
+ return_tensors="pt"
189
+ ).to(self.device)
190
+
191
+ # Inferência
192
+ with torch.no_grad():
193
+ outputs = self.owlv2_model(**{**inputs, **self.processed_text})
194
+ target_sizes = torch.tensor([frame_pil.size[::-1]], device=self.device)
195
+ results = self.owlv2_processor.post_process_grounded_object_detection(
196
+ outputs=outputs,
197
+ target_sizes=target_sizes,
198
+ threshold=threshold
199
+ )[0]
200
+
201
+ # Processar resultados
202
+ if len(results["scores"]) > 0:
203
+ frame_detections = []
204
+ for score, box, label in zip(results["scores"], results["boxes"], results["labels"]):
205
+ score_val = score.item()
206
+ if score_val >= threshold:
207
+ label_idx = min(label.item(), len(self.text_queries) - 1)
208
+ frame_detections.append({
209
+ "confidence": round(score_val * 100, 2),
210
+ "box": [int(x) for x in box.tolist()],
211
+ "label": self.text_queries[label_idx],
212
+ "frame": i,
213
+ "timestamp": i / (fps or 2)
214
+ })
215
+
216
+ if frame_detections:
217
+ detections_by_frame.extend(self._apply_nms(frame_detections))
218
+
219
+ except Exception as e:
220
+ logger.error(f"Erro ao processar frame {i}: {str(e)}")
221
+ continue
222
+
223
+ finally:
224
+ # Liberar memória
225
+ if 'inputs' in locals():
226
+ del inputs
227
+ if 'outputs' in locals():
228
+ del outputs
229
+ torch.cuda.empty_cache()
230
+
231
+ # Log de progresso
232
+ if i % 10 == 0:
233
+ logger.info(f"Processados {i}/{len(frames)} frames")
234
+
235
+ return detections_by_frame
236
+
237
  def _preprocess_image(self, image: Image.Image) -> Image.Image:
238
  """Pré-processa a imagem para o formato esperado pelo modelo."""
239
  try:
src/infrastructure/services/weapon_detector.py CHANGED
@@ -15,25 +15,22 @@ class WeaponDetectorService(DetectorInterface):
15
 
16
  def __init__(self):
17
  try:
18
- # Usar o Factory Pattern do domínio para criar o detector apropriado
19
- self.detector = WeaponDetector.get_instance() # Usar get_instance ao invés do construtor direto
20
  if not self.detector:
21
  raise RuntimeError("Falha ao criar o detector")
22
 
23
  self.device_type = "GPU" if torch.cuda.is_available() else "CPU"
24
  logger.info(f"Detector inicializado em modo {self.device_type}")
25
 
26
- # Manter referência à implementação específica para otimizações
27
- if hasattr(self.detector, '_instance') and self.detector._instance is not None:
28
- self._specific_detector = self.detector._instance
29
- else:
30
- self._specific_detector = self.detector
31
 
32
- # Verificar se o detector foi inicializado corretamente
33
  if not hasattr(self._specific_detector, 'process_video'):
34
  raise RuntimeError("Detector não possui método process_video")
35
 
36
- # Garantir que o detector está inicializado
37
  if hasattr(self._specific_detector, 'initialize'):
38
  self._specific_detector.initialize()
39
 
@@ -53,7 +50,6 @@ class WeaponDetectorService(DetectorInterface):
53
  if not self._specific_detector:
54
  raise RuntimeError("Detector não inicializado")
55
 
56
- # Garantir que o detector está inicializado
57
  if hasattr(self._specific_detector, 'initialize'):
58
  self._specific_detector.initialize()
59
 
@@ -68,7 +64,6 @@ class WeaponDetectorService(DetectorInterface):
68
  logger.warning("Nenhuma métrica retornada pelo detector")
69
  metrics = {}
70
 
71
- # Converter detecções para entidades do domínio
72
  detections = []
73
  for detection in metrics.get('detections', []):
74
  try:
@@ -82,7 +77,6 @@ class WeaponDetectorService(DetectorInterface):
82
  except Exception as e:
83
  logger.error(f"Erro ao processar detecção: {str(e)}")
84
 
85
- # Criar resultado com informações técnicas
86
  result = DetectionResult(
87
  video_path=output_path or video_path,
88
  detections=detections,
@@ -121,7 +115,6 @@ class WeaponDetectorService(DetectorInterface):
121
  if hasattr(self._specific_detector, 'clean_memory'):
122
  self._specific_detector.clean_memory()
123
 
124
- # Forçar coleta de lixo
125
  gc.collect()
126
  if torch.cuda.is_available():
127
  torch.cuda.empty_cache()
@@ -180,7 +173,6 @@ class WeaponDetectorService(DetectorInterface):
180
  return self._get_empty_cache_stats()
181
 
182
  def _get_empty_cache_stats(self) -> dict:
183
- """Retorna estatísticas vazias do cache."""
184
  return {
185
  "cache_size": 0,
186
  "max_size": 0,
 
15
 
16
  def __init__(self):
17
  try:
18
+ self.detector = WeaponDetector.get_instance()
 
19
  if not self.detector:
20
  raise RuntimeError("Falha ao criar o detector")
21
 
22
  self.device_type = "GPU" if torch.cuda.is_available() else "CPU"
23
  logger.info(f"Detector inicializado em modo {self.device_type}")
24
 
25
+ self._specific_detector = (
26
+ self.detector._instance
27
+ if hasattr(self.detector, '_instance') and self.detector._instance is not None
28
+ else self.detector
29
+ )
30
 
 
31
  if not hasattr(self._specific_detector, 'process_video'):
32
  raise RuntimeError("Detector não possui método process_video")
33
 
 
34
  if hasattr(self._specific_detector, 'initialize'):
35
  self._specific_detector.initialize()
36
 
 
50
  if not self._specific_detector:
51
  raise RuntimeError("Detector não inicializado")
52
 
 
53
  if hasattr(self._specific_detector, 'initialize'):
54
  self._specific_detector.initialize()
55
 
 
64
  logger.warning("Nenhuma métrica retornada pelo detector")
65
  metrics = {}
66
 
 
67
  detections = []
68
  for detection in metrics.get('detections', []):
69
  try:
 
77
  except Exception as e:
78
  logger.error(f"Erro ao processar detecção: {str(e)}")
79
 
 
80
  result = DetectionResult(
81
  video_path=output_path or video_path,
82
  detections=detections,
 
115
  if hasattr(self._specific_detector, 'clean_memory'):
116
  self._specific_detector.clean_memory()
117
 
 
118
  gc.collect()
119
  if torch.cuda.is_available():
120
  torch.cuda.empty_cache()
 
173
  return self._get_empty_cache_stats()
174
 
175
  def _get_empty_cache_stats(self) -> dict:
 
176
  return {
177
  "cache_size": 0,
178
  "max_size": 0,
src/presentation/web/gradio_interface.py CHANGED
@@ -21,31 +21,8 @@ class GradioInterface:
21
  self.default_resolution = "640" if self.detector.device_type == "GPU" else "480"
22
  self.is_huggingface = os.getenv('SPACE_ID') is not None
23
 
24
- # Configurar dataset apenas no ambiente Hugging Face
25
  if self.is_huggingface:
26
- self.dataset_id = "marcuscanhaco/weapon-test"
27
- self.cache_dir = os.path.join(tempfile.gettempdir(), 'weapon_detection_videos')
28
- os.makedirs(self.cache_dir, exist_ok=True)
29
-
30
- # Configurar API do Hugging Face
31
- self.hf_token = os.getenv('HF_TOKEN')
32
- self.api = HfApi(token=self.hf_token)
33
-
34
- # Listar arquivos do dataset
35
- try:
36
- files = self.api.list_repo_files(self.dataset_id, repo_type="dataset")
37
- self.sample_videos = [
38
- {
39
- 'path': f,
40
- 'name': Path(f).stem.replace('_', ' ').title(),
41
- 'ground_truth': '🚨 Vídeo de Teste'
42
- }
43
- for f in files if f.lower().endswith(('.mp4', '.avi', '.mov', '.mkv'))
44
- ]
45
- logger.info(f"Encontrados {len(self.sample_videos)} vídeos no dataset")
46
- except Exception as e:
47
- logger.error(f"Erro ao listar arquivos do dataset: {str(e)}")
48
- self.sample_videos = []
49
 
50
  self.use_case = ProcessVideoUseCase(
51
  detector=self.detector,
@@ -54,6 +31,30 @@ class GradioInterface:
54
  default_resolution=int(self.default_resolution)
55
  )
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  def _download_video(self, video_path: str) -> str:
58
  """Baixa um vídeo do dataset e retorna o caminho local."""
59
  try:
@@ -75,39 +76,47 @@ class GradioInterface:
75
  """Lista os vídeos de exemplo do dataset ou da pasta local."""
76
  try:
77
  if self.is_huggingface:
78
- logger.info("Ambiente Hugging Face detectado")
79
- videos = []
80
- for video in self.sample_videos:
81
- local_path = self._download_video(video['path'])
82
- if local_path:
83
- videos.append({
84
- 'path': local_path,
85
- 'name': video['name'],
86
- 'ground_truth': video['ground_truth']
87
- })
88
- return videos
89
  else:
90
- logger.info("Ambiente local detectado, usando pasta videos")
91
- video_extensions = ['.mp4', '.avi', '.mov', '.mkv']
92
- videos = []
93
- base_dir = Path("videos")
94
- if not base_dir.exists():
95
- os.makedirs(base_dir)
96
- logger.info(f"Diretório videos criado: {base_dir}")
97
-
98
- for ext in video_extensions:
99
- for video_path in base_dir.glob(f'*{ext}'): # Removido o glob recursivo
100
- videos.append({
101
- 'path': str(video_path),
102
- 'name': video_path.name,
103
- 'ground_truth': '📼 Vídeo de Teste'
104
- })
105
-
106
- return videos
107
 
108
  except Exception as e:
109
  logger.error(f"Erro ao listar vídeos: {str(e)}")
110
  return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
  def load_sample_video(self, video_path: str) -> str:
113
  """Carrega um vídeo de exemplo."""
@@ -135,121 +144,139 @@ class GradioInterface:
135
  theme=gr.themes.Ocean(),
136
  css="footer {display: none !important}"
137
  ) as demo:
138
- gr.Markdown(f"""# 🎯 {title} 🔪🔫
139
-
140
- Faça upload de um vídeo para detectar objetos perigosos.
141
- Opcionalmente, configure notificações para receber alertas em caso de detecções.
 
 
 
 
 
 
 
 
 
 
142
 
143
- **Importante para melhor performance:**
144
- - Vídeos de até 60 segundos
145
- - FPS entre 1-2 para análise com maior performance
146
- - FPS maior que 2 para análise com maior precisão
147
- """)
148
- with gr.Group():
149
- gr.Markdown("""### Configuração de Processamento""")
150
- with gr.Row():
151
- threshold = gr.Slider(
152
- minimum=0.1,
153
- maximum=1.0,
154
- value=0.5,
155
- step=0.1,
156
- label="Limiar de Detecção",
157
- )
158
- fps = gr.Slider(
159
- minimum=1,
160
- maximum=5,
161
- value=self.default_fps,
162
- step=1,
163
- label="Frames por Segundo",
164
- )
165
- resolution = gr.Radio(
166
- choices=["480", "640", "768"],
167
- value=self.default_resolution,
168
- label="Resolução de Processamento",
169
  )
170
- with gr.Group():
171
- gr.Markdown("""### Configuração de Notificações de Detecção (Opcional)""")
172
- with gr.Row():
173
- notification_type = gr.Radio(
174
- choices=self.notification_factory.get_available_services(),
175
- value="email",
176
- label="Tipo de Notificação",
177
- interactive=True,
178
- )
179
- notification_target = gr.Textbox(
180
- label="Destino da Notificação (E-mail)",
181
- placeholder="[email protected]",
182
- )
 
 
 
 
183
  with gr.Row():
184
- with gr.Column(scale=2):
185
- input_video = gr.Video(
186
- label="Vídeo de Entrada",
187
- format="mp4",
188
- interactive=True,
189
- height=400
190
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
- submit_btn = gr.Button(
193
- "Detectar",
194
- variant="primary",
195
- scale=2
196
- )
197
- with gr.Column(scale=1):
198
- status = gr.Textbox(
199
- label="Status da Detecção",
200
- lines=4,
201
- show_copy_button=True
202
- )
203
- with gr.Accordion("Detalhes Técnicos", open=False):
204
- json_output = gr.JSON(
205
- label="Detalhes Técnicos",
206
- )
207
-
208
- # Informações adicionais
209
- with gr.Accordion("Informações Adicionais", open=False):
210
- gr.Markdown("""
211
- ### Sobre o Detector
212
- Este sistema utiliza um modelo de IA avançado para detectar objetos perigosos em vídeos.
213
-
214
- ### Tipos de Objetos Detectados
215
- - Armas de fogo (pistolas, rifles, etc.)
216
- - Armas brancas (facas, canivetes, etc.)
217
- - Objetos perigosos (bastões, objetos pontiagudos, etc.)
218
-
219
- ### Recomendações
220
- - Use vídeos com boa iluminação
221
- - Evite vídeos muito longos
222
- - Mantenha os objetos visíveis e em foco
223
- """)
224
- # Vídeos de exemplo
225
- if sample_videos:
226
- gr.Markdown("### Vídeos de Exemplo")
227
- examples = [
228
- [video['path']] for video in sample_videos
229
- ]
230
- gr.Examples(
231
- examples=examples,
232
- inputs=input_video,
233
- outputs=input_video,
234
- fn=self.load_sample_video,
235
- label="Clique em um vídeo para carregá-lo"
236
  )
237
 
238
- # Configurar callback do botão
239
- submit_btn.click(
240
- fn=lambda *args: self._process_video(*args),
241
- inputs=[
242
- input_video,
243
- threshold,
244
- fps,
245
- resolution,
246
- notification_type,
247
- notification_target
248
- ],
249
- outputs=[status, json_output]
250
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
- return demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
  def _process_video(
255
  self,
@@ -265,7 +292,6 @@ class GradioInterface:
265
  if not video_path:
266
  return "Erro: Nenhum vídeo fornecido", {}
267
 
268
- # Usar valores padrão se não especificados
269
  fps = fps or self.default_fps
270
  resolution = resolution or self.default_resolution
271
 
@@ -279,39 +305,8 @@ class GradioInterface:
279
  )
280
 
281
  response = self.use_case.execute(request)
282
-
283
- # Formatar mensagem de status
284
  status_msg = self._format_status_message(response.detection_result)
285
-
286
- # Preparar JSON técnico
287
- technical_data = {
288
- "device_info": {
289
- "type": response.detection_result.device_type,
290
- "memory": response.memory_info,
291
- "details": response.device_info
292
- },
293
- "processing_stats": {
294
- "total_detections": len(response.detection_result.detections),
295
- "frames_analyzed": response.detection_result.frames_analyzed,
296
- "total_time": round(response.detection_result.total_time, 2),
297
- "frame_extraction_time": round(response.detection_result.frame_extraction_time, 2),
298
- "analysis_time": round(response.detection_result.analysis_time, 2),
299
- "fps": fps,
300
- "resolution": resolution
301
- },
302
- "detections": [],
303
- "cache_stats": response.cache_stats if hasattr(response, 'cache_stats') else {}
304
- }
305
-
306
- # Adicionar detecções ao JSON com informações temporais
307
- for det in response.detection_result.detections[:10]:
308
- technical_data["detections"].append({
309
- "label": det.label,
310
- "confidence": round(det.confidence * 100 if det.confidence <= 1.0 else det.confidence, 2),
311
- "frame": det.frame,
312
- "timestamp": f"{int(det.timestamp // 60):02d}:{int(det.timestamp % 60):02d}",
313
- "box": det.box if hasattr(det, "box") else None
314
- })
315
 
316
  return status_msg, technical_data
317
 
@@ -323,6 +318,43 @@ class GradioInterface:
323
  "total_detections": 0,
324
  "frames_analyzed": 0
325
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
  def _format_status_message(self, result) -> str:
328
  """Formata a mensagem de status do processamento."""
 
21
  self.default_resolution = "640" if self.detector.device_type == "GPU" else "480"
22
  self.is_huggingface = os.getenv('SPACE_ID') is not None
23
 
 
24
  if self.is_huggingface:
25
+ self._setup_huggingface_environment()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  self.use_case = ProcessVideoUseCase(
28
  detector=self.detector,
 
31
  default_resolution=int(self.default_resolution)
32
  )
33
 
34
+ def _setup_huggingface_environment(self):
35
+ """Configura o ambiente Hugging Face."""
36
+ self.dataset_id = "marcuscanhaco/weapon-test"
37
+ self.cache_dir = os.path.join(tempfile.gettempdir(), 'weapon_detection_videos')
38
+ os.makedirs(self.cache_dir, exist_ok=True)
39
+
40
+ self.hf_token = os.getenv('HF_TOKEN')
41
+ self.api = HfApi(token=self.hf_token)
42
+
43
+ try:
44
+ files = self.api.list_repo_files(self.dataset_id, repo_type="dataset")
45
+ self.sample_videos = [
46
+ {
47
+ 'path': f,
48
+ 'name': Path(f).stem.replace('_', ' ').title(),
49
+ 'ground_truth': '🚨 Vídeo de Teste'
50
+ }
51
+ for f in files if f.lower().endswith(('.mp4', '.avi', '.mov', '.mkv'))
52
+ ]
53
+ logger.info(f"Encontrados {len(self.sample_videos)} vídeos no dataset")
54
+ except Exception as e:
55
+ logger.error(f"Erro ao listar arquivos do dataset: {str(e)}")
56
+ self.sample_videos = []
57
+
58
  def _download_video(self, video_path: str) -> str:
59
  """Baixa um vídeo do dataset e retorna o caminho local."""
60
  try:
 
76
  """Lista os vídeos de exemplo do dataset ou da pasta local."""
77
  try:
78
  if self.is_huggingface:
79
+ return self._list_huggingface_videos()
 
 
 
 
 
 
 
 
 
 
80
  else:
81
+ return self._list_local_videos()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
  except Exception as e:
84
  logger.error(f"Erro ao listar vídeos: {str(e)}")
85
  return []
86
+
87
+ def _list_huggingface_videos(self) -> list:
88
+ """Lista vídeos do ambiente Hugging Face."""
89
+ logger.info("Ambiente Hugging Face detectado")
90
+ videos = []
91
+ for video in self.sample_videos:
92
+ local_path = self._download_video(video['path'])
93
+ if local_path:
94
+ videos.append({
95
+ 'path': local_path,
96
+ 'name': video['name'],
97
+ 'ground_truth': video['ground_truth']
98
+ })
99
+ return videos
100
+
101
+ def _list_local_videos(self) -> list:
102
+ """Lista vídeos do ambiente local."""
103
+ logger.info("Ambiente local detectado, usando pasta videos")
104
+ video_extensions = ['.mp4', '.avi', '.mov', '.mkv']
105
+ videos = []
106
+ base_dir = Path("videos")
107
+ if not base_dir.exists():
108
+ os.makedirs(base_dir)
109
+ logger.info(f"Diretório videos criado: {base_dir}")
110
+
111
+ for ext in video_extensions:
112
+ for video_path in base_dir.glob(f'*{ext}'):
113
+ videos.append({
114
+ 'path': str(video_path),
115
+ 'name': video_path.name,
116
+ 'ground_truth': '📼 Vídeo de Teste'
117
+ })
118
+
119
+ return videos
120
 
121
  def load_sample_video(self, video_path: str) -> str:
122
  """Carrega um vídeo de exemplo."""
 
144
  theme=gr.themes.Ocean(),
145
  css="footer {display: none !important}"
146
  ) as demo:
147
+ self._create_header(title)
148
+ self._create_processing_config()
149
+ self._create_notification_config()
150
+ self._create_video_interface()
151
+ self._create_sample_videos(sample_videos)
152
+
153
+ return demo
154
+
155
+ def _create_header(self, title: str):
156
+ """Cria o cabeçalho da interface."""
157
+ gr.Markdown(f"""# 🎯 {title} 🔪🔫
158
+
159
+ Faça upload de um vídeo para detectar objetos perigosos.
160
+ Opcionalmente, configure notificações para receber alertas em caso de detecções.
161
 
162
+ **Importante para melhor performance:**
163
+ - Vídeos de até 60 segundos
164
+ - FPS entre 1-2 para análise com maior performance
165
+ - FPS maior que 2 para análise com maior precisão
166
+ """)
167
+
168
+ def _create_processing_config(self):
169
+ """Cria a seção de configuração de processamento."""
170
+ with gr.Group():
171
+ gr.Markdown("""### Configuração de Processamento""")
172
+ with gr.Row():
173
+ self.threshold = gr.Slider(
174
+ minimum=0.1,
175
+ maximum=1.0,
176
+ value=0.5,
177
+ step=0.1,
178
+ label="Limiar de Detecção",
 
 
 
 
 
 
 
 
 
179
  )
180
+ self.fps = gr.Slider(
181
+ minimum=1,
182
+ maximum=5,
183
+ value=self.default_fps,
184
+ step=1,
185
+ label="Frames por Segundo",
186
+ )
187
+ self.resolution = gr.Radio(
188
+ choices=["480", "640", "768"],
189
+ value=self.default_resolution,
190
+ label="Resolução de Processamento",
191
+ )
192
+
193
+ def _create_notification_config(self):
194
+ """Cria a seção de configuração de notificações."""
195
+ with gr.Group():
196
+ gr.Markdown("""### Configuração de Notificações de Detecção (Opcional)""")
197
  with gr.Row():
198
+ self.notification_type = gr.Radio(
199
+ choices=self.notification_factory.get_available_services(),
200
+ value="email",
201
+ label="Tipo de Notificação",
202
+ interactive=True,
203
+ )
204
+ self.notification_target = gr.Textbox(
205
+ label="Destino da Notificação (E-mail)",
206
+ placeholder="[email protected]",
207
+ )
208
+
209
+ def _create_video_interface(self):
210
+ """Cria a interface de vídeo."""
211
+ with gr.Row():
212
+ with gr.Column(scale=2):
213
+ self.input_video = gr.Video(
214
+ label="Vídeo de Entrada",
215
+ format="mp4",
216
+ interactive=True,
217
+ height=400
218
+ )
219
 
220
+ self.submit_btn = gr.Button(
221
+ "Detectar",
222
+ variant="primary",
223
+ scale=2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  )
225
 
226
+ with gr.Column(scale=1):
227
+ self.status = gr.Textbox(
228
+ label="Status da Detecção",
229
+ lines=4,
230
+ show_copy_button=True
231
+ )
232
+ with gr.Accordion("Detalhes Técnicos", open=False):
233
+ self.json_output = gr.JSON(
234
+ label="Detalhes Técnicos",
235
+ )
236
+
237
+ with gr.Accordion("Informações Adicionais", open=False):
238
+ gr.Markdown("""
239
+ ### Sobre o Detector
240
+ Este sistema utiliza um modelo de IA avançado para detectar objetos perigosos em vídeos.
241
+
242
+ ### Tipos de Objetos Detectados
243
+ - Armas de fogo (pistolas, rifles, etc.)
244
+ - Armas brancas (facas, canivetes, etc.)
245
+ - Objetos perigosos (bastões, objetos pontiagudos, etc.)
246
+
247
+ ### Recomendações
248
+ - Use vídeos com boa iluminação
249
+ - Evite vídeos muito longos
250
+ - Mantenha os objetos visíveis e em foco
251
+ """)
252
 
253
+ self.submit_btn.click(
254
+ fn=lambda *args: self._process_video(*args),
255
+ inputs=[
256
+ self.input_video,
257
+ self.threshold,
258
+ self.fps,
259
+ self.resolution,
260
+ self.notification_type,
261
+ self.notification_target
262
+ ],
263
+ outputs=[self.status, self.json_output]
264
+ )
265
+
266
+ def _create_sample_videos(self, sample_videos: list):
267
+ """Cria a seção de vídeos de exemplo."""
268
+ if sample_videos:
269
+ gr.Markdown("### Vídeos de Exemplo")
270
+ examples = [
271
+ [video['path']] for video in sample_videos
272
+ ]
273
+ gr.Examples(
274
+ examples=examples,
275
+ inputs=self.input_video,
276
+ outputs=self.input_video,
277
+ fn=self.load_sample_video,
278
+ label="Clique em um vídeo para carregá-lo"
279
+ )
280
 
281
  def _process_video(
282
  self,
 
292
  if not video_path:
293
  return "Erro: Nenhum vídeo fornecido", {}
294
 
 
295
  fps = fps or self.default_fps
296
  resolution = resolution or self.default_resolution
297
 
 
305
  )
306
 
307
  response = self.use_case.execute(request)
 
 
308
  status_msg = self._format_status_message(response.detection_result)
309
+ technical_data = self._format_technical_data(response, fps, resolution)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
 
311
  return status_msg, technical_data
312
 
 
318
  "total_detections": 0,
319
  "frames_analyzed": 0
320
  }
321
+
322
+ def _format_technical_data(
323
+ self,
324
+ response: Any,
325
+ fps: int,
326
+ resolution: str
327
+ ) -> Dict[str, Any]:
328
+ """Formata os dados técnicos do processamento."""
329
+ technical_data = {
330
+ "device_info": {
331
+ "type": response.detection_result.device_type,
332
+ "memory": response.memory_info,
333
+ "details": response.device_info
334
+ },
335
+ "processing_stats": {
336
+ "total_detections": len(response.detection_result.detections),
337
+ "frames_analyzed": response.detection_result.frames_analyzed,
338
+ "total_time": round(response.detection_result.total_time, 2),
339
+ "frame_extraction_time": round(response.detection_result.frame_extraction_time, 2),
340
+ "analysis_time": round(response.detection_result.analysis_time, 2),
341
+ "fps": fps,
342
+ "resolution": resolution
343
+ },
344
+ "detections": [],
345
+ "cache_stats": response.cache_stats if hasattr(response, 'cache_stats') else {}
346
+ }
347
+
348
+ for det in response.detection_result.detections[:10]:
349
+ technical_data["detections"].append({
350
+ "label": det.label,
351
+ "confidence": round(det.confidence * 100 if det.confidence <= 1.0 else det.confidence, 2),
352
+ "frame": det.frame,
353
+ "timestamp": f"{int(det.timestamp // 60):02d}:{int(det.timestamp % 60):02d}",
354
+ "box": det.box if hasattr(det, "box") else None
355
+ })
356
+
357
+ return technical_data
358
 
359
  def _format_status_message(self, result) -> str:
360
  """Formata a mensagem de status do processamento."""