Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
|
| 2 |
|
| 3 |
import gradio as gr
|
| 4 |
import cv2
|
|
@@ -8,7 +8,7 @@ import matplotlib.pyplot as plt
|
|
| 8 |
from PIL import Image
|
| 9 |
import imagehash
|
| 10 |
import torch
|
| 11 |
-
|
| 12 |
import os
|
| 13 |
|
| 14 |
# --- MÓDULO DE INICIALIZAÇÃO ---
|
|
@@ -36,9 +36,8 @@ except Exception as e:
|
|
| 36 |
|
| 37 |
# --- FUNÇÕES DE ANÁLISE ---
|
| 38 |
|
| 39 |
-
# --- MODIFICADO ---
|
| 40 |
-
# Agora aceita start_frame e end_frame para analisar apenas um trecho.
|
| 41 |
def analisar_fidelidade(video_path, start_frame=0, end_frame=0):
|
|
|
|
| 42 |
cap = cv2.VideoCapture(video_path)
|
| 43 |
if not cap.isOpened():
|
| 44 |
raise gr.Error("Não foi possível abrir o arquivo de vídeo.")
|
|
@@ -47,7 +46,7 @@ def analisar_fidelidade(video_path, start_frame=0, end_frame=0):
|
|
| 47 |
fps = cap.get(cv2.CAP_PROP_FPS) or 30
|
| 48 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 49 |
|
| 50 |
-
# Valida e ajusta os frames de início e fim
|
| 51 |
process_full_video = (start_frame == 0 and end_frame == 0) or (start_frame >= end_frame)
|
| 52 |
|
| 53 |
if process_full_video:
|
|
@@ -160,15 +159,13 @@ def executar_teste_semantico(phash_distances, descriptions_text):
|
|
| 160 |
# (Função como definida anteriormente)
|
| 161 |
return None, "Função ainda não implementada completamente no template"
|
| 162 |
|
| 163 |
-
# --- NOVA FUNÇÃO ---
|
| 164 |
-
# Extrai frames de um trecho e os une em uma única imagem.
|
| 165 |
def extrair_e_visualizar_frames(video_path, start_frame, end_frame):
|
|
|
|
| 166 |
if not video_path or start_frame >= end_frame:
|
| 167 |
return None
|
| 168 |
|
| 169 |
cap = cv2.VideoCapture(video_path)
|
| 170 |
-
if not cap.isOpened():
|
| 171 |
-
return None
|
| 172 |
|
| 173 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 174 |
start_frame = max(0, start_frame)
|
|
@@ -176,27 +173,41 @@ def extrair_e_visualizar_frames(video_path, start_frame, end_frame):
|
|
| 176 |
|
| 177 |
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
|
| 178 |
|
| 179 |
-
extracted_frames =
|
| 180 |
for i in range(start_frame, end_frame):
|
| 181 |
ret, frame = cap.read()
|
| 182 |
-
if not ret:
|
| 183 |
-
|
| 184 |
-
|
| 185 |
frame_num_text = f"Frame: {i}"
|
| 186 |
cv2.putText(frame, frame_num_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
|
| 187 |
-
extracted_frames
|
| 188 |
|
| 189 |
cap.release()
|
|
|
|
| 190 |
|
| 191 |
-
|
| 192 |
-
|
|
|
|
|
|
|
| 193 |
|
| 194 |
-
#
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
|
| 197 |
-
# Salva
|
| 198 |
pil_img = Image.fromarray(combined_image)
|
| 199 |
-
path = "
|
| 200 |
pil_img.save(path)
|
| 201 |
return path
|
| 202 |
|
|
@@ -246,14 +257,13 @@ def gerar_grafico_foco(ssim_global, ssim_foco, jitter, num_frames, fps):
|
|
| 246 |
return plot_to_file(fig, "foco")
|
| 247 |
|
| 248 |
|
| 249 |
-
# --- FUNÇÃO DE CALLBACK PRINCIPAL
|
| 250 |
def run_full_analysis(video_path, descriptions_text, start_frame, end_frame, progress=gr.Progress()):
|
| 251 |
if video_path is None: raise gr.Error("Por favor, faça o upload de um vídeo.")
|
| 252 |
|
| 253 |
-
# Converte para inteiros
|
| 254 |
start_frame, end_frame = int(start_frame), int(end_frame)
|
| 255 |
|
| 256 |
-
progress(0, desc="Extraindo
|
| 257 |
frames_imagem_path = extrair_e_visualizar_frames(video_path, start_frame, end_frame)
|
| 258 |
|
| 259 |
progress(0.1, desc="Analisando fidelidade do trecho...")
|
|
@@ -283,10 +293,9 @@ def run_full_analysis(video_path, descriptions_text, start_frame, end_frame, pro
|
|
| 283 |
if error_msg: gr.Warning(error_msg)
|
| 284 |
|
| 285 |
progress(1.0, desc="Análise completa!")
|
| 286 |
-
# --- MODIFICADO --- Retorna o caminho da nova imagem de frames também
|
| 287 |
return frames_imagem_path, fidelidade_plot_path, cor_plot_path, foco_plot_path, semantico_path, anomalias_plot_path
|
| 288 |
|
| 289 |
-
# --- INTERFACE GRADIO
|
| 290 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 291 |
gr.Markdown("# Suíte de Validação Completa para Geração de Vídeo (ADUC-SDR)")
|
| 292 |
with gr.Row():
|
|
@@ -294,7 +303,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 294 |
video_input = gr.Video(label="1. Upload do vídeo")
|
| 295 |
descriptions_input = gr.Textbox(lines=5, label="3. Descrições (Opcional)", placeholder="Uma descrição por cena para o teste de inteligência...")
|
| 296 |
|
| 297 |
-
# --- NOVOS COMPONENTES ---
|
| 298 |
with gr.Row():
|
| 299 |
start_frame_input = gr.Number(label="Frame Inicial", value=0, precision=0, info="Deixe 0 e 0 para analisar o vídeo inteiro.")
|
| 300 |
end_frame_input = gr.Number(label="Frame Final", value=0, precision=0, info="Ex: 88 e 94 para analisar esse trecho.")
|
|
@@ -302,9 +310,8 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 302 |
analyze_button = gr.Button("4. Executar Análise Completa", variant="primary")
|
| 303 |
|
| 304 |
with gr.Tabs():
|
| 305 |
-
# --- NOVA ABA ---
|
| 306 |
with gr.TabItem("Frames Extraídos"):
|
| 307 |
-
extracted_frames_img = gr.Image(label="Visualização dos Frames
|
| 308 |
with gr.TabItem("1. Fidelidade e Coerência"):
|
| 309 |
plot_fidelidade = gr.Image(label="Gráfico de Análise de Fidelidade (SSIM e pHash)")
|
| 310 |
with gr.TabItem("2. Cor e Iluminação"):
|
|
@@ -316,11 +323,10 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 316 |
with gr.TabItem("5. Inteligência Adaptativa"):
|
| 317 |
plot_semantico = gr.Image(label="Gráfico de Estresse Semântico")
|
| 318 |
|
| 319 |
-
# --- MODIFICADO --- Atualiza inputs e outputs do botão
|
| 320 |
analyze_button.click(
|
| 321 |
fn=run_full_analysis,
|
| 322 |
inputs=[video_input, descriptions_input, start_frame_input, end_frame_input],
|
| 323 |
-
outputs=[extracted_frames_img, plot_fidelidade, plot_cor, plot_foco, plot_semantico,
|
| 324 |
)
|
| 325 |
|
| 326 |
if __name__ == "__main__":
|
|
|
|
| 1 |
+
--- START OF FILE app.py ---
|
| 2 |
|
| 3 |
import gradio as gr
|
| 4 |
import cv2
|
|
|
|
| 8 |
from PIL import Image
|
| 9 |
import imagehash
|
| 10 |
import torch
|
| 11 |
+
import collections # Adicionado para a nova lógica de visualização
|
| 12 |
import os
|
| 13 |
|
| 14 |
# --- MÓDULO DE INICIALIZAÇÃO ---
|
|
|
|
| 36 |
|
| 37 |
# --- FUNÇÕES DE ANÁLISE ---
|
| 38 |
|
|
|
|
|
|
|
| 39 |
def analisar_fidelidade(video_path, start_frame=0, end_frame=0):
|
| 40 |
+
"""Lê um trecho do vídeo e calcula as métricas de fidelidade (SSIM, pHash)."""
|
| 41 |
cap = cv2.VideoCapture(video_path)
|
| 42 |
if not cap.isOpened():
|
| 43 |
raise gr.Error("Não foi possível abrir o arquivo de vídeo.")
|
|
|
|
| 46 |
fps = cap.get(cv2.CAP_PROP_FPS) or 30
|
| 47 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 48 |
|
| 49 |
+
# Valida e ajusta os frames de início e fim. Se inválido, analisa o vídeo todo.
|
| 50 |
process_full_video = (start_frame == 0 and end_frame == 0) or (start_frame >= end_frame)
|
| 51 |
|
| 52 |
if process_full_video:
|
|
|
|
| 159 |
# (Função como definida anteriormente)
|
| 160 |
return None, "Função ainda não implementada completamente no template"
|
| 161 |
|
|
|
|
|
|
|
| 162 |
def extrair_e_visualizar_frames(video_path, start_frame, end_frame):
|
| 163 |
+
"""Extrai frames de um trecho e os organiza em uma grade alinhada a múltiplos de 8."""
|
| 164 |
if not video_path or start_frame >= end_frame:
|
| 165 |
return None
|
| 166 |
|
| 167 |
cap = cv2.VideoCapture(video_path)
|
| 168 |
+
if not cap.isOpened(): return None
|
|
|
|
| 169 |
|
| 170 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 171 |
start_frame = max(0, start_frame)
|
|
|
|
| 173 |
|
| 174 |
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
|
| 175 |
|
| 176 |
+
extracted_frames, frame_shape = {}, None
|
| 177 |
for i in range(start_frame, end_frame):
|
| 178 |
ret, frame = cap.read()
|
| 179 |
+
if not ret: break
|
| 180 |
+
if frame_shape is None: frame_shape = frame.shape
|
| 181 |
+
|
| 182 |
frame_num_text = f"Frame: {i}"
|
| 183 |
cv2.putText(frame, frame_num_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
|
| 184 |
+
extracted_frames[i] = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 185 |
|
| 186 |
cap.release()
|
| 187 |
+
if not extracted_frames: return None
|
| 188 |
|
| 189 |
+
# Cria um frame preto para usar como preenchimento
|
| 190 |
+
h, w, c = frame_shape
|
| 191 |
+
black_frame = np.zeros((h, w, c), dtype=np.uint8)
|
| 192 |
+
cv2.putText(black_frame, "VAZIO", (w//2 - 50, h//2), cv2.FONT_HERSHEY_SIMPLEX, 1, (100, 100, 100), 2)
|
| 193 |
|
| 194 |
+
# Determina o início e o fim da grade, alinhados a múltiplos de 8
|
| 195 |
+
grid_start = (start_frame // 8) * 8
|
| 196 |
+
grid_end = ((end_frame - 1) // 8 + 1) * 8
|
| 197 |
+
|
| 198 |
+
# Preenche um dicionário com os frames reais ou de preenchimento
|
| 199 |
+
rows_dict = collections.defaultdict(list)
|
| 200 |
+
for i in range(grid_start, grid_end):
|
| 201 |
+
row_index = i // 8
|
| 202 |
+
rows_dict[row_index].append(extracted_frames.get(i, black_frame))
|
| 203 |
+
|
| 204 |
+
# Monta as linhas horizontalmente e depois as empilha verticalmente
|
| 205 |
+
image_rows = [np.hstack(rows_dict[row_index]) for row_index in sorted(rows_dict.keys())]
|
| 206 |
+
combined_image = np.vstack(image_rows)
|
| 207 |
|
| 208 |
+
# Salva e retorna o caminho da imagem final
|
| 209 |
pil_img = Image.fromarray(combined_image)
|
| 210 |
+
path = "frames_extraidos_grid.png"
|
| 211 |
pil_img.save(path)
|
| 212 |
return path
|
| 213 |
|
|
|
|
| 257 |
return plot_to_file(fig, "foco")
|
| 258 |
|
| 259 |
|
| 260 |
+
# --- FUNÇÃO DE CALLBACK PRINCIPAL ---
|
| 261 |
def run_full_analysis(video_path, descriptions_text, start_frame, end_frame, progress=gr.Progress()):
|
| 262 |
if video_path is None: raise gr.Error("Por favor, faça o upload de um vídeo.")
|
| 263 |
|
|
|
|
| 264 |
start_frame, end_frame = int(start_frame), int(end_frame)
|
| 265 |
|
| 266 |
+
progress(0, desc="Extraindo e visualizando frames...")
|
| 267 |
frames_imagem_path = extrair_e_visualizar_frames(video_path, start_frame, end_frame)
|
| 268 |
|
| 269 |
progress(0.1, desc="Analisando fidelidade do trecho...")
|
|
|
|
| 293 |
if error_msg: gr.Warning(error_msg)
|
| 294 |
|
| 295 |
progress(1.0, desc="Análise completa!")
|
|
|
|
| 296 |
return frames_imagem_path, fidelidade_plot_path, cor_plot_path, foco_plot_path, semantico_path, anomalias_plot_path
|
| 297 |
|
| 298 |
+
# --- INTERFACE GRADIO ---
|
| 299 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 300 |
gr.Markdown("# Suíte de Validação Completa para Geração de Vídeo (ADUC-SDR)")
|
| 301 |
with gr.Row():
|
|
|
|
| 303 |
video_input = gr.Video(label="1. Upload do vídeo")
|
| 304 |
descriptions_input = gr.Textbox(lines=5, label="3. Descrições (Opcional)", placeholder="Uma descrição por cena para o teste de inteligência...")
|
| 305 |
|
|
|
|
| 306 |
with gr.Row():
|
| 307 |
start_frame_input = gr.Number(label="Frame Inicial", value=0, precision=0, info="Deixe 0 e 0 para analisar o vídeo inteiro.")
|
| 308 |
end_frame_input = gr.Number(label="Frame Final", value=0, precision=0, info="Ex: 88 e 94 para analisar esse trecho.")
|
|
|
|
| 310 |
analyze_button = gr.Button("4. Executar Análise Completa", variant="primary")
|
| 311 |
|
| 312 |
with gr.Tabs():
|
|
|
|
| 313 |
with gr.TabItem("Frames Extraídos"):
|
| 314 |
+
extracted_frames_img = gr.Image(label="Visualização dos Frames em Grade")
|
| 315 |
with gr.TabItem("1. Fidelidade e Coerência"):
|
| 316 |
plot_fidelidade = gr.Image(label="Gráfico de Análise de Fidelidade (SSIM e pHash)")
|
| 317 |
with gr.TabItem("2. Cor e Iluminação"):
|
|
|
|
| 323 |
with gr.TabItem("5. Inteligência Adaptativa"):
|
| 324 |
plot_semantico = gr.Image(label="Gráfico de Estresse Semântico")
|
| 325 |
|
|
|
|
| 326 |
analyze_button.click(
|
| 327 |
fn=run_full_analysis,
|
| 328 |
inputs=[video_input, descriptions_input, start_frame_input, end_frame_input],
|
| 329 |
+
outputs=[extracted_frames_img, plot_fidelidade, plot_cor, plot_foco, plot_semantico, anomalias_plot_path]
|
| 330 |
)
|
| 331 |
|
| 332 |
if __name__ == "__main__":
|