PedroM2626 commited on
Commit
06c0743
·
1 Parent(s): 3db91f6

Fix: implement WebRTC for camera access and update dependencies

Browse files
Files changed (3) hide show
  1. README.md +1 -1
  2. requirements.txt +2 -0
  3. streamlit_app.py +43 -74
README.md CHANGED
@@ -18,7 +18,7 @@ Este projeto é uma aplicação Streamlit para detecção de objetos utilizando
18
 
19
  ## 🚀 Funcionalidades
20
 
21
- - Detecção de objetos em tempo real em imagens carregadas.
22
  - Suporte a 80 classes do dataset COCO.
23
  - Interface amigável com Streamlit.
24
  - Configuração via variáveis de ambiente.
 
18
 
19
  ## 🚀 Funcionalidades
20
 
21
+ - Detecção de objetos em tempo real via Webcam usando WebRTC (compatível com Hugging Face Spaces).
22
  - Suporte a 80 classes do dataset COCO.
23
  - Interface amigável com Streamlit.
24
  - Configuração via variáveis de ambiente.
requirements.txt CHANGED
@@ -11,3 +11,5 @@ requests>=2.32.0
11
  tqdm>=4.66.0
12
  streamlit>=1.35.0
13
  pillow>=10.3.0
 
 
 
11
  tqdm>=4.66.0
12
  streamlit>=1.35.0
13
  pillow>=10.3.0
14
+ streamlit-webrtc>=0.47.0
15
+ av>=12.0.0
streamlit_app.py CHANGED
@@ -1,12 +1,40 @@
1
- # Importações necessárias para Streamlit, OpenCV e processamento de imagem
2
  import streamlit as st
3
  import cv2
4
  import numpy as np
5
  from PIL import Image
6
  from yolo_inference import build_detector_from_env
 
 
7
 
8
  # Configuração inicial da página do Streamlit (Título e Layout)
9
- st.set_page_config(page_title="YOLO Detection - Streamlit", layout="wide", page_icon="🚗")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  def main():
12
  """
@@ -28,112 +56,53 @@ def main():
28
 
29
  st.sidebar.markdown("---")
30
  # Seleção do modo de operação
31
- mode = st.sidebar.radio("📡 Escolha o Modo de Entrada", ["Imagem", "Câmera (Real-time)"])
32
 
33
  # Inicializa o detector YOLO
34
- # A função build_detector_from_env gerencia o download automático dos pesos se necessário.
35
  try:
36
  detector = build_detector_from_env(conf_threshold=conf_threshold, nms_threshold=nms_threshold)
37
  except Exception as e:
38
  st.error(f"❌ Erro ao inicializar detector: {e}")
39
  return
40
 
41
- # Lista de classes do dataset personalizado para monitoramento especial
42
- CUSTOM_CLASSES = {"car", "truck", "bus", "motorbike", "bicycle", "van", "threewheel"}
43
-
44
  if mode == "Imagem":
45
  st.subheader("📁 Upload e Detecção em Imagem")
46
  uploaded_file = st.file_uploader("Arraste ou selecione uma imagem...", type=["jpg", "jpeg", "png"])
47
 
48
  if uploaded_file is not None:
49
- # Converte o arquivo carregado (BytesIO) para uma imagem PIL e depois para array numpy
50
  image = Image.open(uploaded_file)
51
  image_np = np.array(image)
52
-
53
- # Streamlit/PIL trabalham em RGB, mas o detector OpenCV espera BGR
54
  frame_bgr = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
55
 
56
- # Realiza a detecção de objetos
57
  with st.spinner('Processando imagem...'):
58
  detections = detector.detect(frame_bgr)
59
 
60
- # Filtra e exibe classes encontradas que pertencem ao dataset customizado
61
  hits = sorted({d['class_name'] for d in detections if d['class_name'] in CUSTOM_CLASSES})
62
 
63
- # Layout em duas colunas: Imagem original vs Resultado
64
  col1, col2 = st.columns(2)
65
-
66
  with col1:
67
  st.image(image, caption="Imagem Original", use_column_width=True)
68
-
69
  with col2:
70
- # Desenha os retângulos e labels no frame BGR
71
  result_bgr = detector.draw(frame_bgr, detections)
72
- # Converte de volta para RGB para exibição correta no Streamlit
73
  result_rgb = cv2.cvtColor(result_bgr, cv2.COLOR_BGR2RGB)
74
  st.image(result_rgb, caption="Detecções Encontradas", use_column_width=True)
75
 
76
- # Exibe alertas baseados nas classes detectadas
77
  if hits:
78
- st.success(f"✅ Objetos do dataset detectados: **{', '.join(hits)}**")
79
  else:
80
- st.info("ℹ️ Nenhuma classe do dataset específico foi detectada nesta imagem.")
81
 
82
- elif mode == "Câmera (Real-time)":
83
- st.subheader("🎥 Detecção via Webcam em Tempo Real")
84
- st.warning("⚠️ Certifique-se de que sua webcam não está sendo usada por outro aplicativo.")
85
-
86
- # Checkbox para ligar/desligar o loop da câmera
87
- run = st.checkbox("Ativar Câmera")
88
 
89
- # Placeholders para atualização dinâmica do frame e status sem recarregar a página toda
90
- frame_placeholder = st.empty()
91
- status_placeholder = st.empty()
92
-
93
- if run:
94
- # Inicializa a captura de vídeo (ID 0 costuma ser a webcam padrão)
95
- cap = cv2.VideoCapture(0)
96
- if not cap.isOpened():
97
- st.error("Não foi possível acessar a câmera. Verifique as permissões.")
98
- return
99
-
100
- while run:
101
- ret, frame = cap.read()
102
- if not ret:
103
- st.error("Falha ao capturar vídeo.")
104
- break
105
-
106
- # Processa o frame atual
107
- detections = detector.detect(frame)
108
-
109
- # Renderiza as detecções no frame
110
- frame_out = detector.draw(frame, detections)
111
-
112
- # Adiciona overlay de instrução no frame (estilo solicitado anteriormente)
113
- cv2.putText(frame_out, "Desmarque 'Ativar Camera' para sair", (20, 40),
114
- cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
115
-
116
- # Identifica classes do dataset para exibição de status dinâmico
117
- hits = sorted({d['class_name'] for d in detections if d['class_name'] in CUSTOM_CLASSES})
118
- if hits:
119
- status_placeholder.success(f"Detectado: **{', '.join(hits)}**")
120
- else:
121
- status_placeholder.empty()
122
-
123
- # Conversão BGR -> RGB para o Streamlit renderizar corretamente
124
- frame_rgb = cv2.cvtColor(frame_out, cv2.COLOR_BGR2RGB)
125
- frame_placeholder.image(frame_rgb, channels="RGB", use_column_width=True)
126
-
127
- # Pequeno delay opcional para sincronia (cv2.waitKey não é necessário aqui para exibição,
128
- # mas ajuda a liberar CPU)
129
- if cv2.waitKey(1) & 0xFF == ord('q'):
130
- break
131
-
132
- # Libera recursos ao encerrar
133
- cap.release()
134
- st.write("🏁 Captura encerrada.")
135
- else:
136
- st.write("💤 Câmera em espera.")
137
 
138
  if __name__ == "__main__":
139
  main()
 
 
1
  import streamlit as st
2
  import cv2
3
  import numpy as np
4
  from PIL import Image
5
  from yolo_inference import build_detector_from_env
6
+ from streamlit_webrtc import webrtc_streamer, VideoProcessorBase, RTCConfiguration
7
+ import av
8
 
9
  # Configuração inicial da página do Streamlit (Título e Layout)
10
+ st.set_page_config(page_title="YOLO Detection - Streamlit", layout="wide", page_icon="🚀")
11
+
12
+ # Configuração RTC para STUN servers (necessário para conexões fora da rede local)
13
+ RTC_CONFIGURATION = RTCConfiguration(
14
+ {"iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]}
15
+ )
16
+
17
+ # Lista de classes do dataset personalizado para monitoramento especial
18
+ CUSTOM_CLASSES = {"car", "truck", "bus", "motorbike", "bicycle", "van", "threewheel"}
19
+
20
+ class YoloVideoProcessor(VideoProcessorBase):
21
+ def __init__(self, detector):
22
+ self.detector = detector
23
+
24
+ def recv(self, frame):
25
+ img = frame.to_ndarray(format="bgr24")
26
+
27
+ # Realiza a detecção de objetos
28
+ detections = self.detector.detect(img)
29
+
30
+ # Desenha os resultados no frame
31
+ img_out = self.detector.draw(img, detections)
32
+
33
+ # Adiciona overlay de instrução
34
+ cv2.putText(img_out, "YOLO Real-time Detection", (20, 40),
35
+ cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
36
+
37
+ return av.VideoFrame.from_ndarray(img_out, format="bgr24")
38
 
39
  def main():
40
  """
 
56
 
57
  st.sidebar.markdown("---")
58
  # Seleção do modo de operação
59
+ mode = st.sidebar.radio("📡 Escolha o Modo de Entrada", ["Imagem", "Câmera (WebRTC)"])
60
 
61
  # Inicializa o detector YOLO
 
62
  try:
63
  detector = build_detector_from_env(conf_threshold=conf_threshold, nms_threshold=nms_threshold)
64
  except Exception as e:
65
  st.error(f"❌ Erro ao inicializar detector: {e}")
66
  return
67
 
 
 
 
68
  if mode == "Imagem":
69
  st.subheader("📁 Upload e Detecção em Imagem")
70
  uploaded_file = st.file_uploader("Arraste ou selecione uma imagem...", type=["jpg", "jpeg", "png"])
71
 
72
  if uploaded_file is not None:
 
73
  image = Image.open(uploaded_file)
74
  image_np = np.array(image)
 
 
75
  frame_bgr = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
76
 
 
77
  with st.spinner('Processando imagem...'):
78
  detections = detector.detect(frame_bgr)
79
 
 
80
  hits = sorted({d['class_name'] for d in detections if d['class_name'] in CUSTOM_CLASSES})
81
 
 
82
  col1, col2 = st.columns(2)
 
83
  with col1:
84
  st.image(image, caption="Imagem Original", use_column_width=True)
 
85
  with col2:
 
86
  result_bgr = detector.draw(frame_bgr, detections)
 
87
  result_rgb = cv2.cvtColor(result_bgr, cv2.COLOR_BGR2RGB)
88
  st.image(result_rgb, caption="Detecções Encontradas", use_column_width=True)
89
 
 
90
  if hits:
91
+ st.success(f"✅ Objetos detectados: **{', '.join(hits)}**")
92
  else:
93
+ st.info("ℹ️ Nenhuma classe de interesse detectada.")
94
 
95
+ elif mode == "Câmera (WebRTC)":
96
+ st.subheader("🎥 Detecção via Webcam (WebRTC)")
97
+ st.info("Esta opção utiliza o WebRTC para acessar sua câmera diretamente do navegador, ideal para deploy em nuvem (Hugging Face).")
 
 
 
98
 
99
+ webrtc_streamer(
100
+ key="yolo-detection",
101
+ video_processor_factory=lambda: YoloVideoProcessor(detector),
102
+ rtc_configuration=RTC_CONFIGURATION,
103
+ media_stream_constraints={"video": True, "audio": False},
104
+ async_processing=True,
105
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
  if __name__ == "__main__":
108
  main()