|
|
import gradio as gr |
|
|
import cv2 |
|
|
import numpy as np |
|
|
from skimage.metrics import structural_similarity as ssim |
|
|
import matplotlib.pyplot as plt |
|
|
from PIL import Image |
|
|
import imagehash |
|
|
import torch |
|
|
from scipy.stats import pearsonr |
|
|
import os |
|
|
|
|
|
|
|
|
|
|
|
CLIP_AVAILABLE, SALIENCY_AVAILABLE = False, False |
|
|
try: |
|
|
from transformers import CLIPProcessor, CLIPModel |
|
|
MODEL_ID = "openai/clip-vit-base-patch32" |
|
|
clip_model = CLIPModel.from_pretrained(MODEL_ID) |
|
|
clip_processor = CLIPProcessor.from_pretrained(MODEL_ID) |
|
|
CLIP_AVAILABLE = True |
|
|
print("Modelo CLIP carregado com sucesso.") |
|
|
except Exception as e: |
|
|
print(f"AVISO: Modelo CLIP não carregado. Teste de Inteligência desabilitado. Erro: {e}") |
|
|
|
|
|
try: |
|
|
saliency_detector = cv2.saliency.StaticSaliencySpectralResidual_create() |
|
|
SALIENCY_AVAILABLE = True |
|
|
print("Módulo de Saliência carregado com sucesso.") |
|
|
except AttributeError: |
|
|
print("AVISO: Módulo de Saliência não encontrado. Análise de Foco Móvel desabilitada.") |
|
|
print("Certifique-se de que 'opencv-contrib-python-headless' está no requirements.txt") |
|
|
except Exception as e: |
|
|
print(f"AVISO: Módulo de Saliência não carregado. Erro: {e}") |
|
|
|
|
|
|
|
|
|
|
|
def analisar_fidelidade(video_path): |
|
|
cap = cv2.VideoCapture(video_path) |
|
|
frames, ssim_scores, phash_distances = [], [], [] |
|
|
fps = cap.get(cv2.CAP_PROP_FPS) or 30 |
|
|
|
|
|
while True: |
|
|
ret, frame = cap.read() |
|
|
if not ret: break |
|
|
frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) |
|
|
cap.release() |
|
|
|
|
|
for i in range(len(frames) - 1): |
|
|
gray1 = cv2.cvtColor(frames[i], cv2.COLOR_RGB2GRAY) |
|
|
gray2 = cv2.cvtColor(frames[i+1], cv2.COLOR_RGB2GRAY) |
|
|
ssim_val, _ = ssim(gray1, gray2, full=True, data_range=gray1.max() - gray1.min()) |
|
|
|
|
|
pil_img1 = Image.fromarray(frames[i]) |
|
|
pil_img2 = Image.fromarray(frames[i+1]) |
|
|
phash_dist = imagehash.phash(pil_img1) - imagehash.phash(pil_img2) |
|
|
|
|
|
ssim_scores.append(ssim_val) |
|
|
phash_distances.append(phash_dist) |
|
|
|
|
|
return frames, fps, ssim_scores, phash_distances |
|
|
|
|
|
def analisar_cor_iluminacao(frames): |
|
|
lum_corr_scores, color_corr_scores = [], [] |
|
|
for i in range(len(frames) - 1): |
|
|
frame1, frame2 = frames[i], frames[i+1] |
|
|
gray1, gray2 = cv2.cvtColor(frame1, cv2.COLOR_RGB2GRAY), cv2.cvtColor(frame2, cv2.COLOR_RGB2GRAY) |
|
|
hist1_lum = cv2.calcHist([gray1], [0], None, [256], [0,256]) |
|
|
hist2_lum = cv2.calcHist([gray2], [0], None, [256], [0,256]) |
|
|
cv2.normalize(hist1_lum, hist1_lum, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX) |
|
|
cv2.normalize(hist2_lum, hist2_lum, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX) |
|
|
lum_corr = cv2.compareHist(hist1_lum, hist2_lum, cv2.HISTCMP_CORREL) |
|
|
lum_corr_scores.append(lum_corr) |
|
|
|
|
|
corrs = [] |
|
|
for chan in range(3): |
|
|
hist1 = cv2.calcHist([frame1],[chan],None,[256],[0,256]) |
|
|
hist2 = cv2.calcHist([frame2],[chan],None,[256],[0,256]) |
|
|
cv2.normalize(hist1, hist1, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX) |
|
|
cv2.normalize(hist2, hist2, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX) |
|
|
corrs.append(cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)) |
|
|
color_corr_scores.append(np.mean(corrs)) |
|
|
return lum_corr_scores, color_corr_scores |
|
|
|
|
|
def analisar_anomalias_movimento(frames): |
|
|
magnitude_scores, orientation_variance_scores = [], [] |
|
|
if not frames: return [], [] |
|
|
prev_gray = cv2.cvtColor(frames[0], cv2.COLOR_RGB2GRAY) |
|
|
for i in range(1, len(frames)): |
|
|
current_gray = cv2.cvtColor(frames[i], cv2.COLOR_RGB2GRAY) |
|
|
flow = cv2.calcOpticalFlowFarneback(prev_gray, current_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0) |
|
|
magnitude, angle = cv2.cartToPolar(flow[...,0], flow[...,1], angleInDegrees=True) |
|
|
magnitude_scores.append(np.mean(magnitude)) |
|
|
orientation_variance_scores.append(np.var(angle)) |
|
|
prev_gray = current_gray |
|
|
return magnitude_scores, orientation_variance_scores |
|
|
|
|
|
def analisar_estabilidade_foco(frames): |
|
|
ssim_foco_scores, jitter_foco_scores = [], [] |
|
|
last_roi_center = None |
|
|
for i in range(len(frames) - 1): |
|
|
frame1_cv, frame2_cv = cv2.cvtColor(frames[i], cv2.COLOR_RGB2BGR), cv2.cvtColor(frames[i+1], cv2.COLOR_RGB2BGR) |
|
|
try: |
|
|
_, saliencyMap1 = saliency_detector.computeSaliency(frame1_cv) |
|
|
_, saliencyMap2 = saliency_detector.computeSaliency(frame2_cv) |
|
|
saliencyMap1_8bit, saliencyMap2_8bit = (saliencyMap1 * 255).astype("uint8"), (saliencyMap2 * 255).astype("uint8") |
|
|
_, thresh1 = cv2.threshold(saliencyMap1_8bit, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) |
|
|
_, thresh2 = cv2.threshold(saliencyMap2_8bit, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) |
|
|
contours1, _ = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
|
|
contours2, _ = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
|
|
|
|
|
if contours1 and contours2: |
|
|
x1, y1, w1, h1 = cv2.boundingRect(max(contours1, key=cv2.contourArea)) |
|
|
x2, y2, w2, h2 = cv2.boundingRect(max(contours2, key=cv2.contourArea)) |
|
|
roi1_gray, roi2_gray = cv2.cvtColor(frames[i][y1:y1+h1, x1:x1+w1], cv2.COLOR_RGB2GRAY), cv2.cvtColor(frames[i+1][y2:y2+h2, x2:x2+w2], cv2.COLOR_RGB2GRAY) |
|
|
if roi1_gray.size == 0 or roi2_gray.size == 0: raise ValueError("ROI vazia") |
|
|
roi2_gray_resized = cv2.resize(roi2_gray, (roi1_gray.shape[1], roi1_gray.shape[0])) |
|
|
|
|
|
ssim_foco, _ = ssim(roi1_gray, roi2_gray_resized, full=True, data_range=255) if min(roi1_gray.shape) > 7 else (0, None) |
|
|
ssim_foco_scores.append(ssim_foco) |
|
|
|
|
|
center = (x1 + w1/2, y1 + h1/2) |
|
|
jitter_foco_scores.append(np.linalg.norm(np.array(center) - np.array(last_roi_center)) if last_roi_center else 0) |
|
|
last_roi_center = center |
|
|
else: |
|
|
ssim_foco_scores.append(0); jitter_foco_scores.append(0) |
|
|
except Exception: |
|
|
ssim_foco_scores.append(0); jitter_foco_scores.append(0) |
|
|
return ssim_foco_scores, jitter_foco_scores |
|
|
|
|
|
def executar_teste_semantico(phash_distances, descriptions_text): |
|
|
|
|
|
return None, "Função ainda não implementada completamente no template" |
|
|
|
|
|
|
|
|
|
|
|
def plot_to_file(fig, filename): |
|
|
path = f"{filename}.png" |
|
|
fig.savefig(path) |
|
|
plt.close(fig) |
|
|
return path |
|
|
|
|
|
def gerar_grafico_fidelidade(ssim, phash, num_frames, fps): |
|
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 10), sharex=True) |
|
|
x_axis = [i/fps for i in range(len(ssim))] |
|
|
ax1.plot(x_axis, ssim, label='SSIM') |
|
|
ax2.plot(x_axis, phash, label='pHash Distance', color='red') |
|
|
ax1.set_title('Fidelidade Estrutural (SSIM)'); ax2.set_title('Mudança Perceptual (pHash)') |
|
|
return plot_to_file(fig, "fidelidade") |
|
|
|
|
|
def gerar_grafico_cor(lum, color, num_frames, fps): |
|
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 10), sharex=True) |
|
|
x_axis = [i/fps for i in range(len(lum))] |
|
|
ax1.plot(x_axis, lum, label='Luminância', color='gold') |
|
|
ax2.plot(x_axis, color, label='Cor (RGB)', color='magenta') |
|
|
ax1.set_title('Consistência de Iluminação'); ax2.set_title('Consistência de Cor') |
|
|
return plot_to_file(fig, "cor") |
|
|
|
|
|
def gerar_grafico_anomalias(mag, var, num_frames, fps): |
|
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 10), sharex=True) |
|
|
x_axis = [i/fps for i in range(len(mag))] |
|
|
ax1.plot(x_axis, mag, label='Magnitude') |
|
|
ax2.plot(x_axis, var, label='Variância de Orientação', color='red') |
|
|
ax1.set_title('Magnitude do Movimento'); ax2.set_title('Incoerência de Movimento (Glitches)') |
|
|
return plot_to_file(fig, "anomalias") |
|
|
|
|
|
def gerar_grafico_foco(ssim_global, ssim_foco, jitter, num_frames, fps): |
|
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 10), sharex=True) |
|
|
x_axis = [i/fps for i in range(len(ssim_global))] |
|
|
ax1.plot(x_axis, ssim_global, label='Global') |
|
|
ax1.plot(x_axis, ssim_foco, label='Foco') |
|
|
ax2.plot(x_axis, jitter, label='Jitter', color='coral') |
|
|
ax1.set_title('Estabilidade de Foco (SSIM)'); ax2.set_title('Jitter de Foco') |
|
|
return plot_to_file(fig, "foco") |
|
|
|
|
|
|
|
|
|
|
|
def run_full_analysis(video_path, descriptions_text, progress=gr.Progress()): |
|
|
if video_path is None: raise gr.Error("Por favor, faça o upload de um vídeo.") |
|
|
|
|
|
progress(0, desc="Analisando fidelidade...") |
|
|
frames, fps, ssim_scores, phash_distances = analisar_fidelidade(video_path) |
|
|
|
|
|
progress(0.2, desc="Gerando gráfico de fidelidade...") |
|
|
fidelidade_plot_path = gerar_grafico_fidelidade(ssim_scores, phash_distances, len(frames), fps) |
|
|
|
|
|
progress(0.3, desc="Analisando cor e iluminação...") |
|
|
lum_scores, color_scores = analisar_cor_iluminacao(frames) |
|
|
cor_plot_path = gerar_grafico_cor(lum_scores, color_scores, len(frames), fps) |
|
|
|
|
|
progress(0.4, desc="Analisando glitches de movimento...") |
|
|
mag_scores, var_scores = analisar_anomalias_movimento(frames) |
|
|
anomalias_plot_path = gerar_grafico_anomalias(mag_scores, var_scores, len(frames), fps) |
|
|
|
|
|
foco_plot_path = None |
|
|
if SALIENCY_AVAILABLE: |
|
|
progress(0.6, desc="Analisando foco móvel...") |
|
|
ssim_foco, jitter_foco = analisar_estabilidade_foco(frames) |
|
|
foco_plot_path = gerar_grafico_foco(ssim_scores, ssim_foco, jitter_foco, len(frames), fps) |
|
|
|
|
|
semantico_path = None |
|
|
if CLIP_AVAILABLE and descriptions_text.strip(): |
|
|
progress(0.8, desc="Executando teste semântico...") |
|
|
semantico_path, error_msg = executar_teste_semantico(phash_distances, descriptions_text) |
|
|
if error_msg: gr.Warning(error_msg) |
|
|
|
|
|
progress(1.0, desc="Análise completa!") |
|
|
return fidelidade_plot_path, cor_plot_path, foco_plot_path, semantico_path, anomalias_plot_path |
|
|
|
|
|
|
|
|
with gr.Blocks(theme=gr.themes.Soft()) as demo: |
|
|
gr.Markdown("# Suíte de Validação Completa para Geração de Vídeo (ADUC-SDR)") |
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
video_input = gr.Video(label="1. Upload do vídeo") |
|
|
descriptions_input = gr.Textbox(lines=5, label="2. Descrições (Opcional)", placeholder="Uma descrição por cena para o teste de inteligência...") |
|
|
analyze_button = gr.Button("3. Executar Análise Completa", variant="primary") |
|
|
with gr.Tabs(): |
|
|
with gr.TabItem("1. Fidelidade e Coerência"): |
|
|
plot_fidelidade = gr.Image(label="Gráfico de Análise de Fidelidade (SSIM e pHash)") |
|
|
with gr.TabItem("2. Cor e Iluminação"): |
|
|
plot_cor = gr.Image(label="Gráfico de Análise de Cor e Luminância") |
|
|
with gr.TabItem("3. Foco (Vídeo Móvel)"): |
|
|
plot_foco = gr.Image(label="Gráfico de Análise de Foco e Jitter") |
|
|
with gr.TabItem("4. Glitches de Movimento"): |
|
|
plot_anomalias = gr.Image(label="Gráfico do Detector de Anomalias de Movimento") |
|
|
with gr.TabItem("5. Inteligência Adaptativa"): |
|
|
plot_semantico = gr.Image(label="Gráfico de Estresse Semântico") |
|
|
|
|
|
analyze_button.click( |
|
|
fn=run_full_analysis, |
|
|
inputs=[video_input, descriptions_input], |
|
|
outputs=[plot_fidelidade, plot_cor, plot_foco, plot_semantico, plot_anomalias] |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.queue().launch() |