Carlexx commited on
Commit
4a58828
·
verified ·
1 Parent(s): 72d4148

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -135
app.py CHANGED
@@ -5,162 +5,165 @@ from skimage.metrics import structural_similarity as ssim
5
  import matplotlib.pyplot as plt
6
  from PIL import Image, ImageDraw, ImageFont
7
  import imagehash
 
 
8
  import os
9
- import math
10
-
11
- # --- Constantes ---
12
- THUMBNAIL_HEIGHT = 100 # Altura de cada miniatura na timeline
13
-
14
- # --- Funções de Análise (mesmas de antes, mas adaptadas para Gradio) ---
15
-
16
- def analisar_video(video_path, progress=gr.Progress()):
17
- """Função principal que processa o vídeo e gera os artefatos de saída."""
18
- if video_path is None:
19
- raise gr.Error("Por favor, faça o upload de um arquivo de vídeo.")
20
-
21
- # 1. Extrair Frames
22
- progress(0, desc="Carregando vídeo e extraindo frames...")
23
- cap = cv2.VideoCapture(video_path)
24
- frames = []
25
- fps = cap.get(cv2.CAP_PROP_FPS)
26
- if fps == 0:
27
- fps = 30 # Define um padrão caso não consiga ler o FPS
28
-
29
- while True:
30
- ret, frame = cap.read()
31
- if not ret:
32
- break
33
- frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) # Gradio usa RGB
34
- cap.release()
35
-
36
- if not frames:
37
- raise gr.Error("Não foi possível ler os frames do vídeo.")
38
 
39
- # 2. Análise Frame-a-Frame
40
- ssim_scores = []
41
- phash_distances = []
42
- num_frames = len(frames)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- for i in progress.tqdm(range(num_frames - 1), desc="Analisando transições..."):
45
- frame_atual = frames[i]
46
- proximo_frame = frames[i+1]
47
-
48
- gray1 = cv2.cvtColor(frame_atual, cv2.COLOR_RGB2GRAY)
49
- gray2 = cv2.cvtColor(proximo_frame, cv2.COLOR_RGB2GRAY)
50
- ssim_val, _ = ssim(gray1, gray2, full=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- pil_img1 = Image.fromarray(frame_atual)
53
- pil_img2 = Image.fromarray(proximo_frame)
54
- phash_dist = imagehash.phash(pil_img1) - imagehash.phash(pil_img2)
55
 
56
- ssim_scores.append(ssim_val)
57
- phash_distances.append(phash_dist)
58
 
59
- # 3. Gerar Gráfico
60
- progress(0.8, desc="Gerando gráfico de análise...")
61
- grafico_path = gerar_grafico_analise(ssim_scores, phash_distances, num_frames, fps)
62
 
63
- # 4. Gerar Timeline Visual
64
- progress(0.9, desc="Criando timeline de miniaturas...")
65
- timeline_path = gerar_timeline_visual(frames, num_frames, fps)
66
-
67
- return grafico_path, timeline_path
68
-
69
- def gerar_grafico_analise(ssim_scores, phash_distances, num_frames, fps):
70
- x_axis_frames = range(num_frames - 1)
71
- x_axis_time = [i / fps for i in x_axis_frames]
72
-
73
  fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 10), sharex=True)
74
- fig.suptitle('Análise Quantitativa da Coerência do Vídeo', fontsize=16)
75
 
76
- ax1.plot(x_axis_time, ssim_scores, color='tab:blue', lw=2)
77
- ax1.set_ylabel('Índice SSIM (1.0 = Perfeito)')
78
- ax1.set_title('Métrica 1: Fidelidade do Eco e Continuidade da Cena')
79
- ax1.grid(True, linestyle='--', alpha=0.6)
80
  ax1.set_ylim(0, 1.05)
81
- ax1.axhline(y=0.9, color='green', linestyle='--', label='Limiar de Alta Fidelidade (0.9)')
82
- ax1.legend()
83
-
84
- ax2.plot(x_axis_time, phash_distances, color='tab:red', lw=2)
85
- ax2.set_ylabel('Distância pHash (0 = Idêntico)')
86
- ax2.set_title('Métrica 2: Detecção de Transições Semânticas e Cortes')
87
- ax2.grid(True, linestyle='--', alpha=0.6)
88
- ax2.axhline(y=10, color='orange', linestyle='--', label='Limiar de Corte Forte (>10)')
89
- ax2.legend()
90
 
 
 
 
91
  ax2.set_xlabel('Tempo (segundos)')
 
 
92
 
93
  plt.tight_layout(rect=[0, 0.03, 1, 0.95])
94
-
95
- grafico_path = 'analise_fidelidade.png'
96
- plt.savefig(grafico_path)
97
  plt.close()
98
- return grafico_path
 
99
 
100
- def gerar_timeline_visual(frames, num_frames, fps):
101
- intervalo_frames = 8
102
- indices_selecionados = list(range(0, num_frames, intervalo_frames))
103
 
104
- thumbnails = []
105
- for i in indices_selecionados:
106
- frame = Image.fromarray(frames[i])
107
- aspect_ratio = frame.width / frame.height
108
- new_width = int(THUMBNAIL_HEIGHT * aspect_ratio)
109
- thumbnail = frame.resize((new_width, THUMBNAIL_HEIGHT), Image.Resampling.LANCZOS)
110
- thumbnails.append(thumbnail)
111
-
112
- total_width = sum(t.width for t in thumbnails)
113
- max_height = THUMBNAIL_HEIGHT
114
 
115
- timeline_img = Image.new('RGB', (total_width, max_height + 40), (20, 20, 20)) # Fundo escuro
116
- draw = ImageDraw.Draw(timeline_img)
117
- try:
118
- font = ImageFont.truetype("DejaVuSans.ttf", 15)
119
- except IOError:
120
- font = ImageFont.load_default()
121
-
122
- current_x = 0
123
- for i, thumb in enumerate(thumbnails):
124
- timeline_img.paste(thumb, (current_x, 0))
125
- frame_idx = indices_selecionados[i]
126
- timestamp = frame_idx / fps
127
- draw.text((current_x + 5, max_height + 5), f"{timestamp:.1f}s", fill=(255,255,255), font=font) # Texto branco
128
- current_x += thumb.width
129
-
130
- timeline_path = 'timeline_visual.png'
131
- timeline_img.save(timeline_path)
132
- return timeline_path
133
-
134
- # --- Interface Gradio ---
135
-
136
- with gr.Blocks(theme=gr.themes.Soft(), title="Analisador de Coerência ADUC-SDR") as demo:
137
- gr.Markdown(
138
- """
139
- # Prova Científica da Arquitetura ADUC-SDR
140
- ### Validação Quantitativa da Coerência Narrativa e Fidelidade do Eco
141
- Faça o upload de um vídeo gerado para mapear matematicamente sua estabilidade e transições.
142
- Este Space analisa cada frame para provar a continuidade mantida pela arquitetura.
143
- Desenvolvido a partir do projeto: [carlex22/Aduc-sdr](https://github.com/carlex22/Aduc-sdr).
144
- """
145
- )
146
 
 
 
 
 
 
 
 
147
  with gr.Row():
148
  with gr.Column(scale=1):
149
- video_input = gr.Video(label="Upload do Vídeo", sources=["upload"])
150
- analyze_button = gr.Button("Analisar Vídeo", variant="primary")
151
-
152
- with gr.Column(scale=2):
153
- gr.Markdown("### **Resultados da Análise**")
154
- plot_output = gr.Image(label="Gráfico de Análise (SSIM e pHash)", type="filepath")
155
- timeline_output = gr.Image(label="Timeline Visual (A cada 8 frames)", type="filepath")
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
 
 
 
 
 
157
  analyze_button.click(
158
- fn=analisar_video,
159
- inputs=[video_input],
160
- outputs=[plot_output, timeline_output]
161
  )
162
-
163
- # O bloco gr.Examples foi removido daqui.
164
-
165
- if __name__ == "__main__":
166
- demo.queue().launch(share=True)
 
5
  import matplotlib.pyplot as plt
6
  from PIL import Image, ImageDraw, ImageFont
7
  import imagehash
8
+ import torch
9
+ from scipy.stats import pearsonr
10
  import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # --- MÓDULOS DE ANÁLISE (CLIP E SALIÊNCIA) ---
13
+ CLIP_AVAILABLE, SALIENCY_AVAILABLE = False, False
14
+ try:
15
+ from transformers import CLIPProcessor, CLIPModel
16
+ MODEL_ID = "openai/clip-vit-base-patch32"
17
+ clip_model = CLIPModel.from_pretrained(MODEL_ID)
18
+ clip_processor = CLIPProcessor.from_pretrained(MODEL_ID)
19
+ CLIP_AVAILABLE = True
20
+ print("Modelo CLIP carregado.")
21
+ except Exception as e:
22
+ print(f"Aviso CLIP: {e}")
23
+
24
+ try:
25
+ saliency = cv2.saliency.StaticSaliencySpectralResidual_create()
26
+ SALIENCY_AVAILABLE = True
27
+ print("Módulo de Saliência carregado.")
28
+ except Exception as e:
29
+ print(f"Aviso Saliência: {e}")
30
+
31
+
32
+ # --- FUNÇÕES DE ANÁLISE CORE ---
33
+ # (As funções de análise de fidelidade, foco e semântica permanecem as mesmas)
34
+ def analisar_cor_iluminacao(frames, progress):
35
+ """Executa o novo teste de consistência cromática e de iluminação."""
36
+ lum_corr_scores, color_corr_scores = [], []
37
 
38
+ progress(0.3, desc="Analisando cor e iluminação...")
39
+ for i in range(len(frames) - 1):
40
+ frame1 = frames[i]
41
+ frame2 = frames[i+1]
42
+
43
+ # 1. Análise de Luminância (Brilho)
44
+ gray1 = cv2.cvtColor(frame1, cv2.COLOR_RGB2GRAY)
45
+ gray2 = cv2.cvtColor(frame2, cv2.COLOR_RGB2GRAY)
46
+ hist1_lum = cv2.calcHist([gray1], [0], None, [256], [0, 256])
47
+ hist2_lum = cv2.calcHist([gray2], [0], None, [256], [0, 256])
48
+ cv2.normalize(hist1_lum, hist1_lum, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)
49
+ cv2.normalize(hist2_lum, hist2_lum, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)
50
+ lum_corr = cv2.compareHist(hist1_lum, hist2_lum, cv2.HISTCMP_CORREL)
51
+ lum_corr_scores.append(lum_corr)
52
+
53
+ # 2. Análise de Cor (RGB)
54
+ corr_r, corr_g, corr_b = 0, 0, 0
55
+ for chan in range(3):
56
+ hist1_color = cv2.calcHist([frame1], [chan], None, [256], [0, 256])
57
+ hist2_color = cv2.calcHist([frame2], [chan], None, [256], [0, 256])
58
+ cv2.normalize(hist1_color, hist1_color, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)
59
+ cv2.normalize(hist2_color, hist2_color, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)
60
+ if chan == 0: corr_r = cv2.compareHist(hist1_color, hist2_color, cv2.HISTCMP_CORREL)
61
+ if chan == 1: corr_g = cv2.compareHist(hist1_color, hist2_color, cv2.HISTCMP_CORREL)
62
+ if chan == 2: corr_b = cv2.compareHist(hist1_color, hist2_color, cv2.HISTCMP_CORREL)
63
 
64
+ # Média da correlação dos 3 canais de cor
65
+ avg_color_corr = (corr_r + corr_g + corr_b) / 3.0
66
+ color_corr_scores.append(avg_color_corr)
67
 
68
+ return lum_corr_scores, color_corr_scores
 
69
 
 
 
 
70
 
71
+ # --- FUNÇÕES DE PLOTAGEM ADICIONAIS ---
72
+ def gerar_grafico_cor(lum_scores, color_scores, num_frames, fps):
73
+ x_axis_time = [i / fps for i in range(num_frames - 1)]
 
 
 
 
 
 
 
74
  fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 10), sharex=True)
75
+ fig.suptitle('Análise de Consistência Cromática e de Iluminação', fontsize=16)
76
 
77
+ ax1.plot(x_axis_time, color_scores, color='magenta', lw=2, label='Correlação de Cor (RGB)')
78
+ ax1.set_ylabel('Correlação de Histograma')
79
+ ax1.set_title('Consistência da Paleta de Cores da Cena')
 
80
  ax1.set_ylim(0, 1.05)
81
+ ax1.grid(True, linestyle='--'); ax1.legend()
 
 
 
 
 
 
 
 
82
 
83
+ ax2.plot(x_axis_time, lum_scores, color='gold', lw=2, label='Correlação de Luminância (Brilho)')
84
+ ax2.set_ylabel('Correlação de Histograma')
85
+ ax2.set_title('Consistência da Iluminação da Cena')
86
  ax2.set_xlabel('Tempo (segundos)')
87
+ ax2.set_ylim(0, 1.05)
88
+ ax2.grid(True, linestyle='--'); ax2.legend()
89
 
90
  plt.tight_layout(rect=[0, 0.03, 1, 0.95])
91
+ path = 'analise_cor_luz.png'
92
+ plt.savefig(path)
 
93
  plt.close()
94
+ return path
95
+
96
 
97
+ # --- FUNÇÃO DE CALLBACK PRINCIPAL ---
98
+ def run_full_analysis(video_path, descriptions_text, progress=gr.Progress()):
99
+ if video_path is None: raise gr.Error("Faça o upload de um vídeo.")
100
 
101
+ # Análise de Fidelidade
102
+ frames, fps, ssim_scores, phash_distances = analisar_fidelidade(video_path, progress)
103
+ fidelidade_plot = gerar_grafico_fidelidade_plot(ssim_scores, phash_distances, len(frames), fps)
104
+ timeline_path = gerar_timeline_visual(frames, len(frames), fps)
 
 
 
 
 
 
105
 
106
+ # Análise de Cor e Iluminação
107
+ lum_scores, color_scores = analisar_cor_iluminacao(frames, progress)
108
+ cor_plot_path = gerar_grafico_cor(lum_scores, color_scores, len(frames), fps)
109
+
110
+ # Análise de Foco Móvel
111
+ foco_plot_path = None
112
+ if SALIENCY_AVAILABLE:
113
+ ssim_foco, jitter_foco = analisar_estabilidade_foco(frames, progress)
114
+ foco_plot_path = gerar_grafico_foco(ssim_scores, ssim_foco, jitter_foco, len(frames), fps)
115
+
116
+ # Análise Semântica
117
+ semantico_path = None
118
+ if CLIP_AVAILABLE and descriptions_text.strip():
119
+ progress(0.8, desc="Executando teste semântico...")
120
+ semantico_path, error_msg = executar_teste_semantico(phash_distances, descriptions_text)
121
+ if error_msg: gr.Warning(error_msg)
122
+
123
+ state_data = {"frames": frames, "fps": fps, "ssim_scores": ssim_scores, "phash_distances": phash_distances}
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
+ return fidelidade_plot, timeline_path, semantico_path, foco_plot_path, cor_plot_path, state_data, None, None, ""
126
+
127
+ # --- INTERFACE GRADIO COMPLETA ---
128
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
129
+ gr.Markdown("# Suíte de Validação Completa para Geração de Vídeo (ADUC-SDR)")
130
+ shared_state = gr.State(value={})
131
+
132
  with gr.Row():
133
  with gr.Column(scale=1):
134
+ video_input = gr.Video(label="1. Upload do vídeo")
135
+ descriptions_input = gr.Textbox(lines=5, label="2. Descrições (Opcional)", placeholder="Uma descrição por cena...")
136
+ analyze_button = gr.Button("3. Executar Análise Completa", variant="primary")
137
+
138
+ with gr.Tabs():
139
+ with gr.TabItem("1. Análise Principal e Diagnóstico"):
140
+ plot_fidelidade = gr.Plot(label="Gráfico de Análise de Fidelidade (Clique para inspecionar)")
141
+ with gr.Row():
142
+ frame_antes = gr.Image(label="Frame Antes")
143
+ frame_depois = gr.Image(label="Frame Depois")
144
+ info_box = gr.Textbox(label="Dados do Ponto Selecionado")
145
+ timeline_output = gr.Image(label="Timeline Visual", interactive=False)
146
+
147
+ with gr.TabItem("2. Análise de Cor e Iluminação"):
148
+ gr.Markdown("### Teste de Consistência Cromática e de Iluminação")
149
+ plot_cor = gr.Image(label="Gráfico de Análise de Cor e Luminância")
150
+
151
+ with gr.TabItem("3. Análise para Vídeo Móvel"):
152
+ gr.Markdown("### Teste de Estabilidade de Foco")
153
+ plot_foco = gr.Image(label="Gráfico de Análise de Foco e Jitter")
154
 
155
+ with gr.TabItem("4. Teste de Inteligência Adaptativa"):
156
+ gr.Markdown("### Validação da Decisão Adaptativa")
157
+ plot_semantico = gr.Image(label="Gráfico de Estresse Semântico")
158
+
159
+ # --- Lógica de Eventos ---
160
  analyze_button.click(
161
+ fn=run_full_analysis,
162
+ inputs=[video_input, descriptions_input],
163
+ outputs=[plot_fidelidade, timeline_output, plot_semantico, plot_foco, plot_cor, shared_state, frame_antes, frame_depois, info_box]
164
  )
165
+ plot_fidelidade.select(
166
+ fn=inspect_transition,
167
+ inputs=[shared_state],
168
+ outputs=[frame_antes, frame_depois, info_box]
169
+ )