import os os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0' import tkinter as tk from tkinter import filedialog import customtkinter import cv2 from cv2 import dnn from pydub import AudioSegment from PIL import Image, ImageTk import threading from glob import glob import sounddevice as sd import wavio import re import pygame from tkinter import ttk, Canvas import whisper import webbrowser import pyaudio import wave from moviepy.editor import * import librosa from librosa import load import librosa.display from pycallgraph2 import PyCallGraph from pycallgraph2.output import GraphvizOutput from librosa.feature import mfcc from transformers import pipeline import IPython.display as ipd from collections import defaultdict from tkcalendar import DateEntry import speech_recognition as sr import numpy as np import sys import shutil import geocoder import logging import time from tensorflow.keras.models import load_model from tensorflow.image import resize from math import ceil import subprocess import matplotlib.pyplot as plt from pyannote.audio import Pipeline import matplotlib matplotlib.use("Agg") from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from reportlab.lib.pagesizes import letter, A4, landscape from moviepy.video.fx.all import speedx from reportlab.pdfgen import canvas from reportlab.lib.colors import red, black, Color from PyPDF2 import PdfMerger from reportlab.platypus import SimpleDocTemplate, Table, TableStyle,Spacer, PageBreak, Image as ReportImage from reportlab.platypus.flowables import PageBreak from reportlab.lib import colors import pandas as pd import tabula import PyPDF2 import torch import string from pdf2docx import Converter import urllib.parse from fpdf import FPDF import pdfplumber from datetime import datetime import fitz import warnings from reportlab.lib.units import inch from docx import Document from moviepy.config import change_settings import joblib import spacy change_settings({"IMAGEMAGICK_BINARY": "Dependencias\\ImageMagick-7.1.1-Q16-HDRI\\magick.exe"}) image_mean = np.array([127, 127, 127]) image_std = 128.0 iou_threshold = 0.3 center_variance = 0.1 size_variance = 0.2 min_boxes = [[10.0, 16.0, 24.0], [32.0, 48.0], [64.0, 96.0], [128.0, 192.0, 256.0]] strides = [8.0, 16.0, 32.0, 64.0] threshold = 0.5 customtkinter.set_appearance_mode("Dark") pdf_path ="" g = geocoder.ip('me') Local="PJM Lisboa" City=g.city agora = datetime.now() Datainterrogatorio = agora Datainterrogatorio = agora.strftime("%d/%m/%Y") Hour=agora.strftime("%H") Minutes=agora.strftime("%M") hora = agora.strftime("%H:%M") resultados={} Nome, nim, posto, DTnasc, cc, ccval, nif, pai, mae, morada, imagem,Interrogador, vetoremocoes,vetoremocoesFER,a, codigo_postal, freguesia, concelho, Nacionalidade, data_atual =[None]*20 def preprocess_frase_pt(frase): nlp = spacy.load("pt_core_news_sm") portuguesstopwords = set(nlp.Defaults.stop_words) if not isinstance(frase, str): raise ValueError("A entrada deve ser uma string.") frase = frase.lower() frase = frase.translate(str.maketrans('', '', string.punctuation)) frase = ''.join([char for char in frase if not char.isdigit()]) frase = ' '.join(frase.split()) frase = ' '.join([word for word in frase.split() if word not in portuguesstopwords]) frase_lemmatizada = ' '.join([token.lemma_ for token in nlp(frase)]) return frase_lemmatizada def preencheraintestemunha(caminho_docx, dados, caminho_saida): doc = Document(caminho_docx) for paragrafo in doc.paragraphs: for marcador, valor in dados.items(): if marcador in paragrafo.text: for run in paragrafo.runs: if marcador in run.text: run.text = run.text.replace(marcador, valor) for tabela in doc.tables: for linha in tabela.rows: for celula in linha.cells: for marcador, valor in dados.items(): if marcador in celula.text: for paragrafo in celula.paragraphs: for run in paragrafo.runs: if marcador in run.text: run.text = run.text.replace(marcador, valor) for section in doc.sections: header = section.header footer = section.footer for paragrafo in header.paragraphs: for marcador, valor in dados.items(): if marcador in paragrafo.text: for run in paragrafo.runs: if marcador in run.text: run.text = run.text.replace(marcador, valor) for paragrafo in footer.paragraphs: for marcador, valor in dados.items(): if marcador in paragrafo.text: for run in paragrafo.runs: if marcador in run.text: run.text = run.text.replace(marcador, valor) doc.save(caminho_saida) print(f"Documento preenchido salvo em: {caminho_saida}") def juntapdfs2(): pdf1="transcricao_audio.pdf" pdf2="Anexo_A.pdf" pdf3 = "ANEXO_B_C.pdf" outputpdf="TranscriptWithAER&FER.pdf" try: merger = PdfMerger() merger.append(pdf1) merger.append(pdf2) merger.append(pdf3) with open(outputpdf, 'wb') as output_file: merger.write(output_file) merger.close() print(f"PDFs combinados com sucesso em {outputpdf}") except Exception as e: print(f"Erro ao juntar os PDFs: {e}") def define_img_size(image_size): shrinkage_list = [] feature_map_w_h_list = [] for size in image_size: feature_map = [int(ceil(size / stride)) for stride in strides] feature_map_w_h_list.append(feature_map) for i in range(0, len(image_size)): shrinkage_list.append(strides) priors = generate_priors( feature_map_w_h_list, shrinkage_list, image_size, min_boxes ) return priors def generate_priors(feature_map_list, shrinkage_list, image_size, min_boxes): priors = [] for index in range(0, len(feature_map_list[0])): scale_w = image_size[0] / shrinkage_list[0][index] scale_h = image_size[1] / shrinkage_list[1][index] for j in range(0, feature_map_list[1][index]): for i in range(0, feature_map_list[0][index]): x_center = (i + 0.5) / scale_w y_center = (j + 0.5) / scale_h for min_box in min_boxes[index]: w = min_box / image_size[0] h = min_box / image_size[1] priors.append([x_center,y_center,w,h]) print("priors nums:{}".format(len(priors))) return np.clip(priors, 0.0, 1.0) def hard_nms(box_scores, iou_threshold, top_k=-1, candidate_size=200): scores = box_scores[:, -1] boxes = box_scores[:, :-1] picked = [] indexes = np.argsort(scores) indexes = indexes[-candidate_size:] while len(indexes) > 0: current = indexes[-1] picked.append(current) if 0 < top_k == len(picked) or len(indexes) == 1: break current_box = boxes[current, :] indexes = indexes[:-1] rest_boxes = boxes[indexes, :] iou = iou_of(rest_boxes,np.expand_dims(current_box, axis=0),) indexes = indexes[iou <= iou_threshold] return box_scores[picked, :] def area_of(left_top, right_bottom): hw = np.clip(right_bottom - left_top, 0.0, None) return hw[..., 0] * hw[..., 1] def iou_of(boxes0, boxes1, eps=1e-5): overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2]) overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:]) overlap_area = area_of(overlap_left_top, overlap_right_bottom) area0 = area_of(boxes0[..., :2], boxes0[..., 2:]) area1 = area_of(boxes1[..., :2], boxes1[..., 2:]) return overlap_area / (area0 + area1 - overlap_area + eps) def predict(width,height,confidences,boxes, prob_threshold,iou_threshold=0.3,top_k=-1): boxes = boxes[0] confidences = confidences[0] picked_box_probs = [] picked_labels = [] for class_index in range(1, confidences.shape[1]): probs = confidences[:, class_index] mask = probs > prob_threshold probs = probs[mask] if probs.shape[0] == 0: continue subset_boxes = boxes[mask, :] box_probs = np.concatenate( [subset_boxes, probs.reshape(-1, 1)], axis=1) box_probs = hard_nms(box_probs,iou_threshold=iou_threshold, top_k=top_k,) picked_box_probs.append(box_probs) picked_labels.extend([class_index] * box_probs.shape[0]) if not picked_box_probs: return np.array([]), np.array([]), np.array([]) picked_box_probs = np.concatenate(picked_box_probs) picked_box_probs[:, 0] *= width picked_box_probs[:, 1] *= height picked_box_probs[:, 2] *= width picked_box_probs[:, 3] *= height return ( picked_box_probs[:, :4].astype(np.int32), np.array(picked_labels), picked_box_probs[:, 4] ) def convert_locations_to_boxes(locations, priors, center_variance, size_variance): if len(priors.shape) + 1 == len(locations.shape): priors = np.expand_dims(priors, 0) return np.concatenate([ locations[..., :2] * center_variance * priors[..., 2:] + priors[..., :2], np.exp(locations[..., 2:] * size_variance) * priors[..., 2:] ], axis=len(locations.shape) - 1) def center_form_to_corner_form(locations): return np.concatenate( [locations[..., :2] - locations[..., 2:] / 2, locations[..., :2] + locations[..., 2:] / 2], len(locations.shape) - 1 ) def FER_live_cam(): emotion_dict = {0: 'neutral', 1: 'happiness', 2: 'surprise', 3: 'sadness',4: 'anger', 5: 'disgust', 6: 'fear'} def modelemotionface(parent_frame,parent_frame2): cap = cv2.VideoCapture(1) frame_width = int(cap.get(3)) frame_height = int(cap.get(4)) size = (frame_width, frame_height) result = cv2.VideoWriter('FERVideo.mp4', cv2.VideoWriter_fourcc(*'MJPG'), 10, size) emotion_dict = {0: 'neutral', 1: 'happiness', 2: 'surprise', 3: 'sadness', 4: 'anger', 5: 'disgust', 6: 'fear'} emotion_colors = {'neutral': (255, 255, 255), 'happiness': (0, 255, 0), 'surprise': (255, 255, 0), 'sadness': (0, 0, 255), 'anger': (0, 0, 128), 'disgust': (128, 0, 128), 'fear': (255, 0, 0)} model = cv2.dnn.readNetFromONNX('Scripts/Modelos/emotion-ferplus-8.onnx') proto_path = 'Scripts/Modelos/RFB-320.prototxt' model_path = 'Scripts/Modelos/RFB-320.caffemodel' net = cv2.dnn.readNetFromCaffe(proto_path, model_path) emotion_counter = {emotion: 0 for emotion in emotion_dict.values()} current_emotion = None audio = pyaudio.PyAudio() stream = audio.open(input=True, format=pyaudio.paInt16, channels=2, rate=16000, frames_per_buffer=1024) frames_audio = [] input_size = [320, 240] width, height = input_size priors = define_img_size(input_size) while cap.isOpened(): ret, frame = cap.read() if ret: img_ori = frame rect = cv2.resize(img_ori, (width, height)) rect = cv2.cvtColor(rect, cv2.COLOR_BGR2RGB) net.setInput(cv2.dnn.blobFromImage(rect, 1 / 127.5, (width, height), 127)) boxes, scores = net.forward(["boxes", "scores"]) boxes = np.expand_dims(np.reshape(boxes, (-1, 4)), axis=0) scores = np.expand_dims(np.reshape(scores, (-1, 2)), axis=0) boxes = convert_locations_to_boxes(boxes, priors, 0.1, 0.2) boxes = center_form_to_corner_form(boxes) boxes, labels, probs = predict(img_ori.shape[1], img_ori.shape[0], scores, boxes, 0.5) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) for (x1, y1, x2, y2) in boxes: w, h = x2 - x1, y2 - y1 cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2) face = cv2.resize(gray[y1:y1 + h, x1:x1 + w], (64, 64)).reshape(1, 1, 64, 64) model.setInput(face) output = model.forward() pred_index = np.argmax(output[0]) pred_emotion = emotion_dict[pred_index] color = emotion_colors[pred_emotion] if pred_emotion != current_emotion: current_emotion = pred_emotion emotion_counter[pred_emotion] += 1 cv2.rectangle(img_ori, (x1, y1), (x2, y2), color, 2, lineType=cv2.LINE_AA) cv2.putText(frame, pred_emotion, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2, lineType=cv2.LINE_AA) result.write(frame) cv2.imshow('Camera', frame) audio_data = stream.read(1024) frames_audio.append(audio_data) if cv2.waitKey(1) & 0xFF == ord('q'): break else: break cap.release() result.release() stream.stop_stream() stream.close() audio.terminate() audiopath = "audio.mp3" with wave.open(audiopath, 'wb') as wf: wf.setnchannels(2) wf.setsampwidth(audio.get_sample_size(pyaudio.paInt16)) wf.setframerate(16000) wf.writeframes(b''.join(frames_audio)) atualizar_grafico_emocoes(emotion_counter, parent_frame) print("Emotions detected during the video:") for emotion, count in emotion_counter.items(): print(f"{emotion}: {count}") video_path='FERVideo.mp4' anexoA(audiopath) dividirvideoclips(video_path) clipes = dividiraudioclips(audiopath) classificar_clipes(clipes,parent_frame2) classificar_videoclipes() sincronizarvideoaudio() criarvetoremocoesFER() juntapdfs2() copiarficheiro() def modelemotionface_video(video_path,parent_frame): cap = cv2.VideoCapture(video_path) frame_width = int(cap.get(3)) frame_height = int(cap.get(4)) size = (frame_width, frame_height) result = cv2.VideoWriter('Video.avi', cv2.VideoWriter_fourcc(*'MJPG'), 10, size) emotion_dict = {0: 'neutral', 1: 'happiness', 2: 'surprise', 3: 'sadness', 4: 'anger', 5: 'disgust', 6: 'fear'} emotion_colors = {'neutral': (255, 255, 255), 'happiness': (0, 255, 0), 'surprise': (255, 255, 0),'sadness': (0, 0, 255), 'anger': (0, 0, 128), 'disgust': (128, 0, 128), 'fear': (255, 0, 0)} proto_path = 'Scripts/Modelos/RFB-320.prototxt' model_path = 'Scripts/Modelos/RFB-320.caffemodel' emotion_model = cv2.dnn.readNetFromONNX('Scripts/Modelos/emotion-ferplus-8.onnx') net = cv2.dnn.readNetFromCaffe(proto_path, model_path) input_size = [320, 240] width, height = input_size priors = define_img_size(input_size) emotion_counter = {emotion: 0 for emotion in emotion_dict.values()} current_emotion = None while cap.isOpened(): ret, frame = cap.read() if not ret: break rect = cv2.resize(frame, (width, height)) rect_rgb = cv2.cvtColor(rect, cv2.COLOR_BGR2RGB) net.setInput(cv2.dnn.blobFromImage(rect_rgb, 1 / 127.5, (width, height), 127)) boxes, scores = net.forward(["boxes", "scores"]) boxes = np.expand_dims(np.reshape(boxes, (-1, 4)), axis=0) scores = np.expand_dims(np.reshape(scores, (-1, 2)), axis=0) boxes = convert_locations_to_boxes(boxes, priors, 0.1, 0.2) boxes = center_form_to_corner_form(boxes) boxes, labels, probs = predict(frame.shape[1], frame.shape[0], scores, boxes, 0.5) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) for (x1, y1, x2, y2) in boxes: w, h = x2 - x1, y2 - y1 cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2) face_region = cv2.resize(gray[y1:y1 + h, x1:x1 + w], (64, 64)).reshape(1, 1, 64, 64) emotion_model.setInput(face_region) output = emotion_model.forward() pred_index = np.argmax(output[0]) pred_emotion = emotion_dict[pred_index] color = emotion_colors[pred_emotion] if pred_emotion != current_emotion: current_emotion = pred_emotion emotion_counter[pred_emotion] += 1 cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2, lineType=cv2.LINE_AA) cv2.putText(frame, pred_emotion, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2, lineType=cv2.LINE_AA) result.write(frame) cv2.imshow('Video FER', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() result.release() atualizar_grafico_emocoes(emotion_counter, parent_frame) video_clip = VideoFileClip('Video.avi') video_clip.write_videofile("FERVideo.mp4", codec="libx264", audio_codec="aac") print("Contagem de emoções detectadas durante o vídeo:") for emotion, count in emotion_counter.items(): print(f"{emotion}: {count}") if __name__ == "__main__": FER_live_cam() def wrap_text(text, max_length=400): words = text.split() lines = [] current_line = [] for word in words: if len(" ".join(current_line + [word])) <= max_length: current_line.append(word) else: lines.append(" ".join(current_line)) current_line = [word] if current_line: lines.append(" ".join(current_line)) return "\n".join(lines) def mostrar_dados(parent_frame): dados = Nome, nim, posto, DTnasc, cc, ccval, nif, pai, mae, morada, imagem, codigo_postal, freguesia, concelho, Morada, Nacionalidade dados_para_preencher = { "NUIPC1": "1234", "Equipa1": "Equipe A", "NomeIC": "João Silva", "DTD": str(Datainterrogatorio), "Hora1": str(hora), "LOCAL1": str(Local), "Name1": f"{Nome.title()}", "Posto1": str(posto), "NIM1": str(nim), "Filiacao1": f"{pai.title()} e de {mae.title()}", "Morada1": str(morada), "CP1": str(codigo_postal), "Freguesia1": str(freguesia), "Conc1": f"{concelho.title()}", "DN": str(DTnasc), "NCC": str(cc), "CCVal": str(ccval), "NIF1": str(nif), "Nacionalidade1":str(Nacionalidade) } caminho_saida = "Dataset/6_ainqtestemunha_Final.docx" preencheraintestemunha("Dataset/6_ainqtestemunha.docx", dados_para_preencher,caminho_saida) caminho_saida2 = "Dataset/3_aintarg_Final.docx" preencheraintestemunha("Dataset/3_aintarg.docx", dados_para_preencher, caminho_saida2) caminho_saida3 = "Dataset/2_auto_de_denuncia_Final.docx" preencheraintestemunha("Dataset/2_auto_de_denuncia.docx", dados_para_preencher, caminho_saida3) for widget in parent_frame.winfo_children(): widget.destroy() canvas = Canvas(parent_frame, width=600, height=400, bg="#2B2B2B", highlightthickness=0) canvas.grid(row=0, column=0, sticky="nsew") Postonimnome=posto+" "+nim+ " " +Nome cceval=cc+" válido até "+ccval labels = [("Nome", Postonimnome),("Data de Nascimento", DTnasc),("Cartão de Cidadão", cceval),("NIF", nif),("Pai", pai),("Mãe", mae),("Morada", Morada)] y_position=10 for label_text, value in labels: label = f"{label_text}: {value}" wrapped_label = wrap_text(label, max_length=80) canvas.create_text(10, y_position, anchor="nw", text=wrapped_label, font=("Arial", 10), fill="white") y_position += 40 try: if imagem: img = Image.open(imagem) img = img.resize((150, 150)) img_tk = ImageTk.PhotoImage(img) canvas.create_image(550, 10, anchor="ne", image=img_tk) canvas.image = img_tk except Exception as e: print(f"Erro ao carregar a imagem: {e}") def graficos(emotion_counter, parent_frame): emotions=list(emotion_counter.keys()) counts=list(emotion_counter.values()) fig, ax = plt.subplots(figsize=(5, 3)) fig.patch.set_facecolor('#2B2B2B') ax.set_facecolor('#2B2B2B') bars= ax.bar(emotions, counts, color=['White', 'green', 'yellow', 'blue', 'darkred', 'purple', 'red']) for bar in bars: yval = bar.get_height() ax.text(bar.get_x() + bar.get_width() / 2, yval, int(yval), ha='center', va='bottom', color='white') ax.set_xlabel('Emoções', color='white') ax.set_ylabel('Contagem', color='white') ax.set_title('FER', color='white') ax.tick_params(colors='white') ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['left'].set_color('white') ax.spines['bottom'].set_color('white') plt.xticks(rotation=45, color='white') plt.xticks(color='white') plt.tight_layout() parent_frame.grid_rowconfigure(0, weight=1) parent_frame.grid_columnconfigure(0, weight=1) canvas = FigureCanvasTkAgg(fig, master=parent_frame) canvas.draw() canvas.get_tk_widget().grid(row=0, column=0, padx=10, pady=10) def graficosAER(emotion_counter, parent_frame): emotions=list(emotion_counter.keys()) counts=list(emotion_counter.values()) fig, ax = plt.subplots(figsize=(5, 3)) fig.patch.set_facecolor('#2B2B2B') ax.set_facecolor('#2B2B2B') bars= ax.bar(emotions, counts, color=['White', 'green', 'yellow', 'blue', 'darkred', 'purple', 'red']) for bar in bars: yval = bar.get_height() ax.text(bar.get_x() + bar.get_width() / 2, yval, int(yval), ha='center', va='bottom', color='white') ax.set_xlabel('Emoções', color='white') ax.set_ylabel('Contagem', color='white') ax.set_title('AER', color='white') ax.tick_params(colors='white') ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['left'].set_color('white') ax.spines['bottom'].set_color('white') plt.xticks(rotation=45, color='white') plt.xticks(color='white') plt.tight_layout() parent_frame.grid_rowconfigure(0, weight=1) parent_frame.grid_columnconfigure(0, weight=1) canvas = FigureCanvasTkAgg(fig, master=parent_frame) canvas.draw() canvas.get_tk_widget().grid(row=0, column=0, padx=10, pady=10) def atualizar_grafico_emocoes(emotion_counter, parent_frame): for widget in parent_frame.winfo_children(): widget.destroy() graficos(emotion_counter, parent_frame) def atualizar_graficosAER(emotion_counter, parent_frame): for widget in parent_frame.winfo_children(): widget.destroy() graficosAER(emotion_counter, parent_frame) def extrairinfo(pdf_path, keyword): with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text = page.extract_text() for line in text.split('\n'): if keyword in line: extracted_text = line.split(keyword, 1)[1].strip() if "AUTENTICAÇÃO" in extracted_text: extracted_text = extracted_text.split("AUTENTICAÇÃO", 1)[0].strip() return extracted_text return None def extrairinfomaisdificil(pdf_path, keyword): resultados = [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text = page.extract_text() for line in text.split('\n'): if keyword in line: extracted_text = line.split(keyword, 1)[1].strip() if "AUTENTICAÇÃO" in extracted_text: extracted_text = extracted_text.split("AUTENTICAÇÃO", 1)[0].strip() resultados.append(extracted_text) return resultados def extrairinfoabaixo(pdf_path, keyword): resultados = [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text = page.extract_text() lines = text.split('\n') for i, line in enumerate(lines): if keyword in line: if i + 1 < len(lines): extracted_text = lines[i + 1].strip() resultados.append(extracted_text) return resultados def extrairinfoabaixo3(pdf_path, keyword): resultados = [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text = page.extract_text() lines = text.split('\n') for i, line in enumerate(lines): if keyword in line: for j in range(1, 4): if i + j < len(lines): extracted_text = lines[i + j].strip() resultados.append(extracted_text) return resultados def Login(): janela = customtkinter.CTk() janela.title("Login") janela.geometry("400x250") janela.resizable(False, False) texto = customtkinter.CTkLabel(janela, text="Login") texto.pack(padx=10, pady=10) user = customtkinter.CTkEntry(janela, placeholder_text="User") user.pack (padx=10, pady=10) password = customtkinter.CTkEntry(janela, placeholder_text="Password", show='*') password.pack (padx=10, pady=10) botao = customtkinter.CTkButton(janela, text="Login", command=lambda: validar_login(user, password, janela)) botao.pack(padx=10, pady=10) janela.mainloop() def validar_login(user, password, janela): userverifica = user.get() passwordverifica = password.get() if userverifica == "kl3z" and passwordverifica == "12345": janela.destroy() janelaprincipal() else: show_messagebox("Erro", "User ou password incorretos!") def extrairimagemdopdfFM(pdf_path, page_number=0): pdf_path = os.path.normpath(pdf_path) doc = fitz.open(pdf_path) pagina = doc[page_number] imagem_extraida = False print("Extrair imagens do PDF") for img_index, img in enumerate(pagina.get_images(full=True)): try: xref = img[0] base_image = doc.extract_image(xref) image_bytes = base_image["image"] image_ext = base_image["ext"] image_filename = f"imagem_extraida_{img_index}.{image_ext}" with open(image_filename, "wb") as img_file: img_file.write(image_bytes) imagem = Image.open(image_filename) imagem.save(f"Imagens\\imagem_corrigida_{img_index}.jpeg") imagem_extraida = True print(f"Imagem {img_index} extraída e corrigida: {image_filename}") except Exception as e: print(f"Erro ao extrair a imagem {img_index}: {e}") continue if not imagem_extraida: print("Nenhuma imagem foi extraída.") doc.close() def extrair_imagem_do_pdf(pdf_path): doc = fitz.open(pdf_path) for page_num in range(len(doc)): pagina = doc[page_num] for img_index, img in enumerate(pagina.get_images(full=True)): xref = img[0] base_image = doc.extract_image(xref) image_bytes = base_image["image"] image_ext = base_image["ext"] image_filename = f"Imagens\\imagem_extraida_{page_num + 1}_{img_index + 1}.{image_ext}" with open(image_filename, "wb") as img_file: img_file.write(image_bytes) print(f"Imagem extraída e salva como: {image_filename}") imagem=image_filename doc.close() return imagem def carregar_ficheiro(user,parent_frame): pdf_path = filedialog.askopenfilename(filetypes=[("PDF files", "*.pdf")]) if pdf_path: user.configure(fg_color="green", text="File Loaded") retirarinfodafolha(pdf_path) mostrar_dados(parent_frame) def retirarinfodafolha(pdf_path): global Nome, nim, posto, DTnasc, cc, ccval, nif, pai, mae, morada, imagem, a, codigo_postal, freguesia, concelho, Morada valor100=extrairinfo(pdf_path, "MINISTÉRIO DA DEFESA NACIONAL") if valor100 is not None: pai = extrairinfo(pdf_path, "Pai:") or "" mae = extrairinfo(pdf_path, "Mãe:") or "" posto = extrairinfo(pdf_path, "Posto:") or "" nim = extrairinfo(pdf_path, "NIM:") or "" Nome = extrairinfo(pdf_path, "Nome:") or "" valor5 = extrairinfo(pdf_path, "Data de Nascimento:") or "" DTnasc = re.search(r"\d{2}-\d{2}-\d{4}", valor5).group() if valor5 else "" Freg = extrairinfo(pdf_path, "Freguesia:") or "" valor7 = extrairinfo(pdf_path, "Concelho:") or "" valor7= re.split(r"\s+Distrito", valor7)[0].strip() if valor7 else "" valor8 = extrairinfo(pdf_path, "Distrito:") or "" valor9 = extrairinfo(pdf_path, "País:")or "" valor9= re.split(r"\s+Posto", valor9)[0].strip() if valor9 else "" valor10 = extrairinfo(pdf_path, "Endereço:") or "" valor11 = extrairinfo(pdf_path, "Código Postal:") or "" valor12 = re.split(r"\s+Localidade:", valor11)[1].strip() if "Localidade:" in valor11 else "" valor11 = re.split(r"\s+Localidade:", valor11)[0].strip() if "Localidade:" in valor11 else "" valor13 = extrairinfo(pdf_path, "Cartão do Cidadão") or "" ccval = re.search(r"\d{2}-\d{2}-\d{4}", valor13).group() if valor13 else "" cc = valor13.replace(ccval, "").strip() if valor13 else "" nif = extrairinfo(pdf_path, "Número de Identificação Fiscal") or "" valor16 = extrairinfo(pdf_path, "Cartão da Segurança Social") or "" valor17 = extrairinfo(pdf_path, "Telemóvel:") or "" valor17 = re.split(r"\s+email:", valor17)[0].strip() if "email:" in valor17 else "" valor18 = extrairinfo(pdf_path, "email:") or "" valor19 = extrairinfomaisdificil(pdf_path, "Freguesia:")[1] if extrairinfomaisdificil(pdf_path, "Freguesia:") else "" valor20 = extrairinfomaisdificil(pdf_path, "Concelho:")[1] if extrairinfomaisdificil(pdf_path, "Concelho:") else "" valor21 = extrairinfomaisdificil(pdf_path, "Distrito:")[1] if extrairinfomaisdificil(pdf_path, "Distrito:") else "" valor22 = extrairinfomaisdificil(pdf_path, "País:")[1] if extrairinfomaisdificil(pdf_path, "País:") else "" valor22 = re.split(r"\s+Posto", valor22)[0].strip() if valor22 else "" Morada = valor10 +","+ " "+ valor11 +","+ " "+ valor12+","+ " "+ valor19 +","+ " "+ valor20 extrairimagemdopdfFM(pdf_path, page_number=0) imagem= "Imagens\\imagem_corrigida_1.jpeg" a = pdf_path partes = Morada.split(",") morada = partes[0].strip().title() if len(partes) > 0 else "" codigo_postal = f"{partes[1].strip()}, {partes[2].strip()}" if len(partes) > 2 else "" freguesia = partes[3].strip().title() if len(partes) > 3 else "" concelho = partes[4].strip().title() if len(partes) > 4 else "" Nacionalidade = extrairinfo(pdf_path, "País:") or "" Nacionalidade = re.sub(r"Posto Consular:\S*.+", "", Nacionalidade).strip() Nacionalidade = " ".join(Nacionalidade.split()[:-2]) if Nacionalidade else "" else: valor1 = extrairinfoabaixo(pdf_path, "Apelido(s)") or "" valor2 = extrairinfoabaixo(pdf_path, "Nome(s)") or "" valor3 = valor2+valor1 valor3 = " ".join(valor3) Nome=valor3 nim = "" posto = "" valor4 = extrairinfoabaixo(pdf_path, "Nascimento") or "" valor4 = re.search(r"\d{2}-\d{2}-\d{4}", "".join(valor4)).group() if valor4 else "" DTnasc = valor4 valor5 = extrairinfoabaixo(pdf_path, "Nº cartão") or "" valor5 = "".join(valor5) or "" valor6 = re.search(r"\d{2}-\d{2}-\d{4}", valor5).group() if valor5 else "" valor7 = valor5.replace(valor6, "").strip() if valor5 else "" valor8 = extrairinfoabaixo3(pdf_path, "Morada")[:2] if extrairinfoabaixo3(pdf_path, "Morada") else ["", ""] Morada = " ".join(valor8).strip() valor10 = extrairinfoabaixo3(pdf_path, "Filiação")[2] if len(extrairinfoabaixo3(pdf_path, "Filiação")) > 2 else "" mae = valor10 valor11 = extrairinfoabaixo3(pdf_path, "Filiação")[1] if len(extrairinfoabaixo3(pdf_path, "Filiação")) > 1 else "" valor12 = re.findall(r"\d+", valor11) if valor11 else [] valor13 = valor12[0] if len(valor12) > 0 else "" cc = valor7 ccval = valor6 nif = valor13 valor14 = valor12[1] if len(valor12) > 1 else "" valor15 = valor12[2] if len(valor12) > 2 else "" valor11 = re.findall(r"[A-Za-zçÇáÁéÉíÍóÓúÚâÂêÊîÎôÔûÛãÃõÕàÀèÈìÌòÒùÙäÄëËïÏöÖüÜñÑ]+", valor11) if valor11 else [] valor11 = " ".join(valor11) pai = valor11 extrair_imagem_do_pdf(pdf_path) imagem = "Imagens\\imagem_extraida_1_2.jpeg" a = pdf_path codigo_postal = re.search(r'\b\d{4}-\d{3}\b', Morada).group() if Morada else "" freguesia = re.search(r'\b([A-Za-zçãáéíóúâêô]*)\b(?=\s\d{4}-\d{3})', Morada).group() if Morada else "" concelho = re.search(r'\b[A-Z]{2,}\b$', Morada).group() if Morada else "" morada = re.search(r'^(.*?)(?=\s\d{4}-\d{3})', Morada).group() if Morada else "" Nacionalidade = extrairinfoabaixo(pdf_path, "Nascimento") Nacionalidade = "".join(Nacionalidade) Nacionalidade = Nacionalidade.split()[2] return Nome, nim, posto, DTnasc, cc, ccval, nif, pai, mae, morada, imagem, a, codigo_postal, freguesia, concelho, Morada, Nacionalidade def resumir_texto(texto, max_length=320, min_length=30): tamanhodotexto = len(texto.split()) max_length=min(max_length, tamanhodotexto) max_length=int(max_length) min_length=min(min_length, tamanhodotexto/4) min_length=int(min_length) summarizer = pipeline("summarization", model="rhaymison/flan-t5-portuguese-small-summarization", tokenizer="rhaymison/flan-t5-portuguese-small-summarization", device=0) resumo = summarizer(texto, max_length=max_length, min_length=min_length, do_sample=False) return resumo[0]['summary_text'] def anexoA(audio_path): model=whisper.load_model("large") result = model.transcribe(audio_path, language='pt') textoarranjado = result['text'] textoformatado='' for char in textoarranjado: if char.isupper() and textoformatado: textoformatado += '\n' textoformatado += char texto_resumido = resumir_texto(textoarranjado) dados_para_preencher = {"ATT": textoarranjado} caminho_saida = "Dataset/6_ainqtestemunha_Final.docx" preencheraintestemunha("Dataset/6_ainqtestemunha_Final.docx", dados_para_preencher,caminho_saida) caminho_saida2 = "Dataset/3_aintarg_Final.docx" preencheraintestemunha("Dataset/3_aintarg_Final.docx", dados_para_preencher, caminho_saida2) caminho_saida3 = "Dataset/2_auto_de_denuncia_Final.docx" preencheraintestemunha("Dataset/2_auto_de_denuncia_Final.docx", dados_para_preencher, caminho_saida3) transcrever_audio(audio_path,texto_resumido) caminho_pdf = 'Anexo_A.pdf' c = canvas.Canvas(caminho_pdf, pagesize=letter) largura, altura = letter caminho_imagem = 'Icons/logo_presidencia_republica.jpg' cabecalho = [("RESERVADO",red),('MINISTÉRIO DA DEFESA NACIONAL', black),('EXÉRCITO PORTUGUÊS',black),('ESTADO-MAIOR GENERAL DAS FORÇAS ARMADAS',black), ('RELATÓRIO DE ENTERVISTA - PJM',black), ('ANEXO A - TRANSCRIÇÃO DO INTERREGOTÓRIO', black)] y_position = altura - 72 def check_y_position(): nonlocal y_position if y_position < 40: c.showPage() y_position = altura - 72 adicionar_cabecalho() def adicionar_cabecalho(): nonlocal y_position c.setFont("Helvetica-Bold", 12) c.setFillColor(black) y_position = altura - 72 c.setFillColor(cabecalho[0][1]) c.setFont("Helvetica-Bold", 14) text_width = c.stringWidth(cabecalho[0][0], "Helvetica", 12) c.drawString((largura - text_width) / 2, y_position, cabecalho[0][0]) y_position -= 30 c.drawImage(caminho_imagem, (largura - 50) / 2, y_position - 20, width=40, height=40) y_position -= 50 for line, color in cabecalho[1:]: check_y_position() c.setFillColor(color) text_width = c.stringWidth(line, "Helvetica-Bold", 14) c.drawString((largura - text_width) / 2, y_position, line) y_position -= 20 adicionar_cabecalho() for line in textoarranjado.strip().split('\n'): y_position -= 40 words = line.split(' ') current_line = "" for word in words: test_line = current_line + word + " " test_line_width = c.stringWidth(test_line, "Helvetica", 12) if test_line_width <= largura - 160: current_line = test_line else: check_y_position() c.drawString(36, y_position, current_line.strip()) y_position -= 15 current_line = word + " " if current_line.strip(): check_y_position() c.drawString(36, y_position, current_line.strip()) y_position -= 15 c.save() print("anexo criado") def transcrever_audio(audio_path,texto_resumido): caminho_pdf = 'transcricao_audio.pdf' c = canvas.Canvas(caminho_pdf, pagesize=letter) largura, altura = letter caminho_imagem = 'Icons/logo_presidencia_republica.jpg' cabecalho = [("RESERVADO",red),('MINISTÉRIO DA DEFESA NACIONAL', black),('FORÇAS ARMADAS',black),('ESTADO-MAIOR DAS FORÇAS ARMADAS',black), ('RELATÓRIO DE ENTERVISTA - PJM',black)] y_position = altura - 72 def check_y_position(): nonlocal y_position if y_position < 40: c.showPage() y_position = altura - 72 adicionar_cabecalho() def adicionar_cabecalho(): nonlocal y_position c.setFont("Helvetica-Bold", 12) c.setFillColor(black) y_position = altura - 72 c.setFillColor(cabecalho[0][1]) c.setFont("Helvetica-Bold", 14) text_width = c.stringWidth(cabecalho[0][0], "Helvetica", 12) c.drawString((largura - text_width) / 2, y_position, cabecalho[0][0]) y_position -= 30 c.drawImage(caminho_imagem, (largura - 50) / 2, y_position - 20, width=40, height=40) y_position -= 50 for line, color in cabecalho[1:]: check_y_position() c.setFillColor(color) text_width = c.stringWidth(line, "Helvetica-Bold", 14) c.drawString((largura - text_width) / 2, y_position, line) y_position -= 20 adicionar_cabecalho() c.setFillColor(black) c.setFont("Helvetica-Bold", 12) horamins = (Hour if Hour else "00") + ":" + (Minutes if Minutes else "00") informacoes_texto = f""" DADOS DO INTERROGADO Posto: {posto} NIM: {nim} Nome: {Nome} Data de Nascimento: {DTnasc} Cartão de Cidadão: {cc} Validade: {ccval} NIF: {nif} DADOS DO INTERROGADOR Interrogador: {Interrogador} Data do Interrogatório: {Datainterrogatorio}, Horas: {horamins} Local: {Local} , Cidade: {City} """ for linha in informacoes_texto.strip().split('\n'): y_position -= 20 c.drawString(36, y_position, linha.strip()) paibem=pai.title() maebem=mae.title() Moradabem=Morada.title() idade = datetime.strptime(DTnasc, "%d-%m-%Y") dataatual = datetime.now() idadeanos = dataatual.year - idade.year - ((dataatual.month, dataatual.day) < (idade.month, idade.day)) introducao= f""" Aos {Datainterrogatorio}, no {Local} em {City} compareceu perante mim, {Interrogador}, o {posto} {nim} {Nome} portador do Cartão de Cidadão n.º {cc}, válido até {ccval}. Declarou ser filho de {paibem} e de {maebem}, ter {idadeanos} anos de idade, residir na {Moradabem}.--/ RESUMO DA ENTREVISTA {texto_resumido} """ for line in introducao.strip().split('\n'): y_position -= 40 words = line.split(' ') current_line = "" for word in words: test_line = current_line + word + " " test_line_width = c.stringWidth(test_line, "Helvetica", 12) if test_line_width <= largura - 140: current_line = test_line else: check_y_position() c.drawString(36, y_position, current_line.strip()) y_position -= 15 current_line = word + " " if current_line.strip(): check_y_position() c.drawString(36, y_position, current_line.strip()) y_position -= 15 c.save() print("PDF criado com sucesso!") def show_messagebox(title, message, box_type="Erro"): Janela_erro = customtkinter.CTkToplevel() Janela_erro.title(title) Janela_erro.geometry("300x150+500+300") label_title = customtkinter.CTkLabel(Janela_erro, text=title, font=('Arial', 16, 'bold')) label_title.pack(pady=10) label_message = customtkinter.CTkLabel(Janela_erro, text=message) label_message.pack(pady=10) close_button = customtkinter.CTkButton(Janela_erro, text="OK", command=Janela_erro.destroy) close_button.pack(pady=10) def update_frame(video_clip): for frame in video_clip.iter_frames(fps=24, dtype='uint8'): frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) img = Image.fromarray(frame) img = img.resize((600, 400)) imgTk = ImageTk.PhotoImage(image=img) if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows() def play_video(video_path,parent_frame,parent_frame2): audiopath='audio.mp3' video_clip = VideoFileClip(video_path) video_clip.audio.write_audiofile(audiopath) print('audio gravado') update_frame(video_clip) modelemotionface_video(video_path,parent_frame) anexoA(audiopath) dividirvideoclips(video_path) clipes = dividiraudioclips(audiopath) classificar_clipes(clipes,parent_frame2) classificar_videoclipes() sincronizarvideoaudio() criarvetoremocoesFER() juntapdfs2() copiarficheiro() def criarvetoremocoesFER(): video_clips = "video_clips" audio_clips = "clips" vetoremocoesFER = [] vetoremocoesAER = [] vetor_sentimentos = [] try: video_clips = sorted([f for f in os.listdir(video_clips) if f.endswith('.mp4')]) audio_clips = sorted([f for f in os.listdir(audio_clips) if f.endswith('.mp3')]) for video_clip in video_clips: parts = os.path.basename(video_clip).replace('.mp4', '').split('_') if len(parts) >= 4: intervalo = f"{parts[1]}-{parts[2]}" emocao = parts[3] vetoremocoesFER.append([f"({intervalo})", emocao]) print("Vetor de emoções (FER):", vetoremocoesFER) for audio_clip in audio_clips: parts = os.path.basename(audio_clip).replace('.mp3', '').split('_') if len(parts) >= 5: intervalo = f"{parts[1]}-{parts[2]}" emocao = parts[3] sentimento = parts[4] vetoremocoesAER.append([f"({intervalo})", emocao, sentimento]) vetor_sentimentos.append(sentimento) print("Vetor de emoções e sentimentos (AER):", vetoremocoesAER) print("Vetor de sentimentos:", vetor_sentimentos) except Exception as e: print(f"Erro ao criar os vetores de emoções: {e}") criarpdfemocoes(vetoremocoesAER, vetoremocoesFER, vetor_sentimentos) return vetoremocoesFER, vetoremocoesAER, vetor_sentimentos def sincronizarvideoaudio(): video_path = "FERVideo.mp4" audio_path = "audio.mp3" outputpath = "VideoFINAL.mp4" audio_clips = sorted([ os.path.join("clips", f) for f in os.listdir("clips") if f.endswith('.mp3') ]) video_clips = sorted([ os.path.join("video_clips", f) for f in os.listdir("video_clips") if f.endswith('.mp4') ]) emotion_colors = { 'neutral': 'white', 'happiness': 'green', 'surprise': 'yellow', 'sadness': 'lightblue', 'anger': 'red', 'disgust': 'purple', 'fear': 'red' } try: video_clip = VideoFileClip(video_path) audio_clip = AudioFileClip(audio_path) video_duration = video_clip.duration audio_duration = audio_clip.duration speed_factor = video_duration / audio_duration video_clip = speedx(video_clip, factor=speed_factor).set_audio(audio_clip) annotated_clips = [] clip_duration = 10 for i, (audio_clip_path, video_clip_path) in enumerate(zip(audio_clips, video_clips)): aer_label_match = re.search(r'_([^_]+)_([^_]+)\.mp3$', audio_clip_path) fer_label_match = re.search(r'_([^_]+)\.mp4$', video_clip_path) aer_label = aer_label_match.group(1) if aer_label_match else 'neutral' sentiment_label = aer_label_match.group(2) if aer_label_match else 'neutral' fer_label = fer_label_match.group(1) if fer_label_match else 'neutral' aer_color = emotion_colors.get(aer_label, 'white') fer_color = emotion_colors.get(fer_label, 'white') sentiment_color = 'orange' aer_text = TextClip(f"AER: {aer_label}", fontsize=24, color=aer_color, bg_color="black") fer_text = TextClip(f"FER: {fer_label}", fontsize=24, color=fer_color, bg_color="black") sentiment_text = TextClip(f"Sentiment: {sentiment_label}", fontsize=24, color=sentiment_color, bg_color="black") video_height = video_clip.h aer_text = aer_text.set_position(("left", video_height - 90)).set_duration(clip_duration) fer_text = fer_text.set_position(("left", video_height - 60)).set_duration(clip_duration) sentiment_text = sentiment_text.set_position(("left", video_height - 30)).set_duration(clip_duration) start_time = i * clip_duration end_time = min((i + 1) * clip_duration, video_clip.duration) video_segment = video_clip.subclip(start_time, end_time) annotated_segment = CompositeVideoClip([video_segment, aer_text, fer_text, sentiment_text]) annotated_clips.append(annotated_segment) final_video = concatenate_videoclips(annotated_clips, method="compose") final_video.write_videofile(outputpath, codec="libx264", audio_codec="aac") print(f"Vídeo com áudio sincronizado salvo em: {outputpath}") except Exception as e: print(f"Erro ao sincronizar vídeo e áudio: {e}") def carregar_video(parent_frame,parent_frame2): video_path = filedialog.askopenfilename(filetypes=[("Carregar Vídeo", "*.mp4;*.avi;*.mov")]) if video_path: video_path = os.path.normpath(video_path) threading.Thread(target=play_video, args=(video_path,parent_frame,parent_frame2)).start() def criarpdfemocoes(vetoremocoesAER, vetoremocoesFER, vetor_sentimentos): output_pdf_path = "ANEXO_B_C.pdf" try: def cabecalho(canvas, doc): canvas.saveState() largura, altura = A4 caminho_imagem = 'Icons/logo_presidencia_republica.jpg' canvas.setFillColor(colors.red) canvas.setFont("Helvetica-Bold", 16) text_width = canvas.stringWidth("RESERVADO", "Helvetica-Bold", 16) canvas.drawString((largura - text_width) / 2, altura - 40, "RESERVADO") canvas.drawImage(caminho_imagem, largura / 2 - 20, altura - 100, width=40, height=40) cabecalho_texto = [ "MINISTÉRIO DA DEFESA NACIONAL", "EXÉRCITO PORTUGUÊS", "ESTADO-MAIOR GENERAL DAS FORÇAS ARMADAS", "RELATÓRIO DE ENTREVISTA - PJM", "ANEXOS B, C e D - SUMMARY AER, FER e Sentiment Analysis", ] y_position = altura - 120 for linha in cabecalho_texto: canvas.setFillColor(colors.black) canvas.setFont("Helvetica-Bold", 12) text_width = canvas.stringWidth(linha, "Helvetica-Bold", 12) canvas.drawString((largura - text_width) / 2, y_position, linha) y_position -= 15 canvas.restoreState() tabela_cabecalho = ['Tempo (SER)', 'SER', 'FER', 'Sentiment Analysis'] dados_combinados = [ [aer[0], aer[1], fer[1], sentimento] for aer, fer, sentimento in zip(vetoremocoesAER, vetoremocoesFER, vetor_sentimentos) ] dados_tabela = [tabela_cabecalho] + dados_combinados largura_tabela = A4[0] - inch col_widths = [largura_tabela / 4] * 4 tabela_combinada = Table(dados_tabela, colWidths=col_widths) tabela_combinada.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), colors.grey), ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), ('ALIGN', (0, 0), (-1, -1), 'CENTER'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, -1), 10), ('BOTTOMPADDING', (0, 0), (-1, 0), 12), ('GRID', (0, 0), (-1, -1), 0.5, colors.black), ])) doc = SimpleDocTemplate(output_pdf_path, pagesize=A4) elementos = [Spacer(1, 120)] elementos.append(tabela_combinada) doc.build( elementos, onFirstPage=cabecalho, onLaterPages=cabecalho ) print(f"PDF gerado com sucesso em: {output_pdf_path}") except Exception as e: print(f"Erro ao criar o PDF: {e}") def carregar_audio(parent_frame): audio_path1 = filedialog.askopenfilename(filetypes=[("Carregar Áudio", "*.mp3;*.wav;*.ogg")]) if audio_path1: audio_path = os.path.normpath(audio_path1) anexoA(audio_path1) clipes = dividiraudioclips(audio_path) classificar_clipes(clipes,parent_frame) criarvetoremocoesFER() juntapdfs2() copiarficheiro() def dividiraudioclips(audio_path, duracaoclipe=10, outputdir="clips"): audio = AudioSegment.from_file(audio_path) duracao_total = len(audio) clipescriados = [] os.makedirs(outputdir, exist_ok=True) for i in range(0, duracao_total, duracaoclipe * 1000): clipe = audio[i:i + duracaoclipe * 1000] clipe_path = os.path.join(outputdir, f"clipe_{i // 1000}s_{(i + duracaoclipe * 1000) // 1000}s.mp3") clipe.export(clipe_path, format="mp3") clipescriados.append(clipe_path) return clipescriados def dividirvideoclips(video_path, duracaoclipe=10, outputdir="video_clips"): video = VideoFileClip(video_path) duracao_total = video.duration clipes_criados = [] os.makedirs(outputdir, exist_ok=True) for i in range(0, int(duracao_total), duracaoclipe): start_time = i end_time = min(i + duracaoclipe, duracao_total) clipe = video.subclip(start_time, end_time) clipe_path = os.path.join(outputdir, f"clipe_{start_time}s_{end_time}s.mp4") clipe.write_videofile(clipe_path, codec="libx264", audio_codec="aac") clipes_criados.append(clipe_path) video.close() return clipes_criados def preprocessar_video(video_path, frame_size=(64, 64)): cap = cv2.VideoCapture(video_path) frames = [] while cap.isOpened(): ret, frame = cap.read() if not ret: break resized_frame = cv2.resize(frame, frame_size) gray_frame = cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY) gray_frame = gray_frame.astype(np.uint8) frames.append(gray_frame) cap.release() return frames def classificar_videoclipes(): modelo = cv2.dnn.readNetFromONNX('Scripts/Modelos/emotion-ferplus-8.onnx') clipes = sorted([os.path.join("video_clips", f) for f in os.listdir("video_clips") if f.endswith('.mp4')]) emotion_labels= {0: 'neutral', 1: 'happiness', 2: 'surprise', 3: 'sadness', 4: 'anger', 5: 'disgust', 6: 'fear'} resultados = {} emotion_counter = {label: 0 for label in emotion_labels.values()} for clipe in clipes: try: frames = preprocessar_video(clipe, frame_size=(64, 64)) predicoes = [] for frame in frames: var= cv2.dnn.blobFromImage(frame, scalefactor=1.0, size=(64, 64), mean=(0, 0, 0), swapRB=False, crop=False) modelo.setInput(var) output = modelo.forward() predicoes.append(output) predicao_media = np.mean(predicoes, axis=0) classe = np.argmax(predicao_media) rotulo = emotion_labels.get(classe, "Unknown") resultados[clipe] = rotulo if rotulo in emotion_counter: emotion_counter[rotulo] += 1 pasta, nome_original = os.path.split(clipe) nome, extensao = os.path.splitext(nome_original) novo_nome = f"{nome}_{rotulo}{extensao}" novo_caminho = os.path.join(pasta, novo_nome) os.rename(clipe, novo_caminho) print(f"Clipe {clipe} classificado como: {rotulo} e renomeado para {novo_caminho}") except Exception as e: print(f"Erro ao classificar o clipe {clipe}: {e}") resultados[clipe] = None return resultados, emotion_counter def preprocessar_audio(caminho_audio, target_shape=(400, 480)): y, sr = librosa.load(caminho_audio, sr=16000) mfcc_features = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=40) resized_features = resize(np.expand_dims(mfcc_features, axis=-1),target_shape).numpy() return np.expand_dims(resized_features, axis=0) def classificar_clipes(clipes, parent_frame): modelocaminho = "Scripts\\Modelos\\model.h5" modelo_emocao = load_model(modelocaminho) vectorizer_path = "Scripts\\Modelos\\vectorizer2.joblib" modelo_sentiment_path = "Scripts\\Modelos\\logistic_regression_model2.joblib" vectorizer = joblib.load(vectorizer_path) modelo_sentiment = joblib.load(modelo_sentiment_path) emotion_labels = {1: 'Neutral',2: 'Neutral',3: 'happiness',4: 'sadness',5: 'anger',6: 'fear',7: 'Disgust',8: 'surprise'} sentiment_labels = {0: "Sadness", 1: "Happiness", 2: "Disgust", 3: "Anger", 4: "Fear", 5: "Surprise"} resultados = {} emotion_counter = {label: 0 for label in emotion_labels.values()} sentiment_counter = {label: 0 for label in sentiment_labels.values()} for clipe in clipes: try: features = preprocessar_audio(clipe) predicao_emocao = modelo_emocao.predict(features) classe_emocao = np.argmax(predicao_emocao) + 1 if classe_emocao in emotion_labels: rotulo_emocao = emotion_labels[classe_emocao] else: rotulo_emocao = "Unknown" if rotulo_emocao in emotion_counter: emotion_counter[rotulo_emocao] += 1 texto_audio = transcrever_audio_para_texto(clipe) texto_processado = preprocess_frase_pt(texto_audio) texto_vetorizado = vectorizer.transform([texto_processado]) predicao_sentiment = modelo_sentiment.predict(texto_vetorizado) classe_sentiment = predicao_sentiment[0] rotulo_sentiment = sentiment_labels.get(classe_sentiment, "Unknown") if rotulo_sentiment in sentiment_counter: sentiment_counter[rotulo_sentiment] += 1 pasta, nome_original = os.path.split(clipe) nome, extensao = os.path.splitext(nome_original) novo_nome = f"{nome}_{rotulo_emocao}_{rotulo_sentiment}{extensao}" novo_caminho = os.path.join(pasta, novo_nome) os.rename(clipe, novo_caminho) resultados[clipe] = {"Emotion": rotulo_emocao, "Sentiment": rotulo_sentiment} print(f"Clipe {clipe} classificado como: {rotulo_emocao} (Emoção) e {rotulo_sentiment} (Sentimento)") print(f"Arquivo renomeado para: {novo_caminho}") except Exception as e: print(f"Erro ao classificar o clipe {clipe}: {e}") resultados[clipe] = None atualizar_graficosAER(emotion_counter, parent_frame) return resultados, emotion_counter, sentiment_counter def transcrever_audio_para_texto(audio_path): try: model = whisper.load_model("large") result = model.transcribe(audio_path, language='pt') return result['text'].encode('utf-8', errors='ignore').decode('utf-8') except Exception as e: print(f"Erro na transcrição do áudio {audio_path}: {e}") return "" def atribuivalores(user2, local, city, data_entry, horas, minutos): global Datainterrogatorio, Hour, Minutes, Local, City, Interrogador Datainterrogatorio = data_entry.get_date() if data_entry.get_date() else None Hour = horas.get() Minutes = minutos.get() Local = local.get() City = city.get() Interrogador = user2.get() return Datainterrogatorio, Hour, Minutes, Local, City, Interrogador def abrir_email_com_anexos(): global data_atual, cc assunto = "Relatório PJM" corpo = f"Segue em anexo o relatório gerado pelo programa PJM em {data_atual}. Por favor, anexe o arquivo localizado no caminho abaixo:\n\n" nome_pasta = f"{cc}_{data_atual}" destino_base = "Corpus" ficheiro1 = "TranscriptWithAER&FER.pdf" caminho_destino = os.path.join(destino_base, nome_pasta, ficheiro1) corpo += f"{caminho_destino}\n\nAtenciosamente," mailto_link = f"mailto:?subject={urllib.parse.quote(assunto)}&body={urllib.parse.quote(corpo)}" webbrowser.open(mailto_link) def abrir_site_PJM(): url = "https://www.defesa.gov.pt/pt/defesa/organizacao/sc/pjm" webbrowser.open(url) def abrir_pdf_final(): pdf_path = "TranscriptWithAER&FER.pdf" try: os.startfile(pdf_path) except Exception as e: print(f"Erro ao abrir o PDF: {e}") def abrir_CR(): word_path = "Dataset\\2_auto_de_denuncia_Final.docx" try: os.startfile(word_path) except Exception as e: print(f"Erro ao abrir o PDF: {e}") def abrir_WER(): word_path = "Dataset\\6_ainqtestemunha_Final.docx" try: os.startfile(word_path) except Exception as e: print(f"Erro ao abrir o PDF: {e}") def abrir_IRD(): word_path = "Dataset\\3_aintarg_Final.docx" try: os.startfile(word_path) except Exception as e: print(f"Erro ao abrir o PDF: {e}") def copiarficheiro(): global data_atual, cc ficheiros_origem = [ "VideoFINAL.mp4", "TranscriptWithAER&FER.pdf", "Dataset\\6_ainqtestemunha_Final.docx", "Dataset\\3_aintarg_Final.docx", "Dataset\\2_auto_de_denuncia_Final.docx" ] destino_base = "Corpus" data_atual = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") nome_pasta = f"{cc}_{data_atual}" caminho_destino = os.path.join(destino_base, nome_pasta) if not os.path.exists(caminho_destino): os.makedirs(caminho_destino) for ficheiro_origem in ficheiros_origem: try: nome_original = os.path.basename(ficheiro_origem) nome, extensao = os.path.splitext(nome_original) novo_nome = f"{nome}_{cc}{extensao}" caminho_novo = os.path.join(caminho_destino, novo_nome) shutil.copy(ficheiro_origem, caminho_novo) print(f"Arquivo {nome_original} copiado e renomeado para {novo_nome}.") except Exception as e: print(f"Erro ao copiar e renomear {ficheiro_origem}: {e}") print(f"Arquivos copiados com sucesso para {caminho_destino}") def excluir_arquivos(pasta): for nome_arquivo in os.listdir(pasta): caminho_arquivo = os.path.join(pasta, nome_arquivo) try: if os.path.isfile(caminho_arquivo): os.remove(caminho_arquivo) print(f"Arquivo {caminho_arquivo} apagado com sucesso.") except Exception as e: print(f"Erro ao tentar deletar {caminho_arquivo}: {e}") pastaclips = "clips" pastavideo = "video_clips" pastaImagens = "Imagens" excluir_arquivos(pastaclips) excluir_arquivos(pastavideo) excluir_arquivos(pastaImagens) arquivos_para_apagar = [ "audio.mp3", "ANEXO_B_C.pdf", "Anexo_A.pdf", "transcricao_audio.pdf", "VideoFINAL.mp4", "Video.avi", "imagem_extraida_0.jpeg", "imagem_extraida_1.jpeg", "FERVideo.mp4" ] for arquivo in arquivos_para_apagar: try: if os.path.exists(arquivo): os.remove(arquivo) print(f"Arquivo {arquivo} apagado com sucesso.") else: print(f"Arquivo {arquivo} não encontrado.") except Exception as e: print(f"Erro ao tentar deletar o arquivo {arquivo}: {e}") return data_atual def janelaprincipal(): janela_principal = customtkinter.CTk() janela_principal.geometry("1200x800") janela_principal.title("PJM IA") janela_principal.resizable(False, False) janela_principal.grid_columnconfigure(0, weight=0) janela_principal.grid_columnconfigure(1, weight=0) janela_principal.grid_rowconfigure(0, weight=0) janela_principal.grid_rowconfigure(1, weight=0) customtkinter.set_appearance_mode("Dark") lado_esquerdo1 = customtkinter.CTkFrame(janela_principal, width=600, height=400) lado_esquerdo1.grid(row=0, column=0, padx=10, pady=10, sticky="nsew") lado_direito1 = customtkinter.CTkFrame(janela_principal, width=600, height=400) lado_direito1.grid(row=0, column=1, padx=10, pady=10, sticky="nsew") lado_esquerdo2 = customtkinter.CTkFrame(janela_principal, width=600, height=400) lado_esquerdo2.grid(row=1, column=1, padx=10, pady=10, sticky="nsew") lado_direito2 = customtkinter.CTkFrame(janela_principal, width=600, height=400) lado_direito2.grid(row=1, column=0, padx=10, pady=10, sticky="nsew") janela_principal.grid_columnconfigure((0,1), weight=1) janela_principal.grid_rowconfigure((0,1), weight=1) barradetarefas=tk.Menu(janela_principal) Carregar=tk.Menu(barradetarefas, tearoff=0) Carregar.add_command(label='Carregar Video', command=lambda:carregar_video(lado_direito2,lado_esquerdo2)) Carregar.add_command(label='Carregar gravação som',command=lambda:carregar_audio(lado_esquerdo2)) Carregar.add_command(label='Usar a Camera', command=lambda:modelemotionface(lado_direito2,lado_esquerdo2)) Carregar.add_separator() Carregar.add_command(label="Sair", command=janela_principal.destroy) barradetarefas.add_cascade(label="Ficheiros", menu=Carregar) janela_principal.config(menu=barradetarefas) pdfimage = Image.open("Icons\\pdf.png").resize((50,50), Image.Resampling.LANCZOS) pdficon=ImageTk.PhotoImage(pdfimage) pdfspeachFER = tk.Button(lado_direito1, image=pdficon, borderwidth=0, highlightthickness=0, bg="#2B2B2B",command=abrir_pdf_final) pdfspeachFER.grid(row=0, column=3, padx=6) labelpdfspeachFER = tk.Label(lado_direito1, text="Emotions\n Analysis", fg="white", bg="#2B2B2B") labelpdfspeachFER.grid(row=1, column=3, pady=(0, 5)) wordimage = Image.open("Icons\\word.png").resize((50,50), Image.Resampling.LANCZOS) wordicon=ImageTk.PhotoImage(wordimage) aintargaicon = tk.Button(lado_direito1, image=wordicon, borderwidth=0, highlightthickness=0, bg="#2B2B2B", command=abrir_CR) aintargaicon.grid(row=0, column=0, padx=10) aintarg = tk.Label(lado_direito1, text="Complaint\nReport", fg="white", bg="#2B2B2B") aintarg.grid(row=1, column=0, pady=(5, 10)) PJMimage = Image.open("Icons\\PJM.png").resize((60,80), Image.Resampling.LANCZOS) PJMicon=ImageTk.PhotoImage(PJMimage) PJMiconb = tk.Button(lado_direito1, image=PJMicon, borderwidth=0, highlightthickness=0, bg="#2B2B2B",command=abrir_site_PJM) PJMiconb.grid(row=8, column=0, padx=10) mailimage = Image.open("Icons\\mail.png").resize((50,50), Image.Resampling.LANCZOS) mailicon=ImageTk.PhotoImage(mailimage) mailiconb = tk.Button(lado_direito1, image=mailicon, borderwidth=0, highlightthickness=0, bg="#2B2B2B",command=abrir_email_com_anexos) mailiconb.grid(row=8, column=1, padx=10) ainttesticon = tk.Button(lado_direito1, image=wordicon, borderwidth=0, highlightthickness=0, bg="#2B2B2B", command=abrir_WER) ainttesticon.grid(row=0, column=1, padx=10) ainttest = tk.Label(lado_direito1, text="Witness examination\nreport", fg="white", bg="#2B2B2B") ainttest.grid(row=1, column=1, pady=(5, 10)) adenicon = tk.Button(lado_direito1, image=wordicon, borderwidth=0, highlightthickness=0, bg="#2B2B2B", command=abrir_IRD) adenicon.grid(row=0, column=2, padx=10) aden = tk.Label(lado_direito1, text="Interrogation report\nof defendant", fg="white", bg="#2B2B2B") aden.grid(row=1, column=2, pady=(5, 10)) user = customtkinter.CTkButton(lado_direito1,text="Carregar Ficheiro PDF",command=lambda:carregar_ficheiro(user,lado_esquerdo1),fg_color="gray") user.grid(row=4, column=0, padx=10) userlabel = tk.Label(lado_direito1, text="FM/CC", fg="white", bg="#2B2B2B") userlabel.grid(row=5, column=0, pady=(5, 10)) opcoes = ["OF3 12345678 Tomás", "OF2 17645234 Doe", "OF4 12827412 Smith", "OF3 13235467 Johnson", "OF2 12314558 Wilson"] user2 = customtkinter.CTkOptionMenu(lado_direito1, values=opcoes,fg_color="#2B2B2B") user2.grid(row=4, column=1, padx=10) userlabel2 = tk.Label(lado_direito1, text="NIM/NIF", fg="white", bg="#2B2B2B") userlabel2.grid(row=5, column=1, pady=(5, 10)) data_entry = DateEntry(lado_direito1, width=15, background="#2B2B2B", foreground="white", borderwidth=2, date_pattern='dd/mm/yyyy') data_entry.grid(row=4, column=2, padx=(0, 10), pady=(5, 10)) datalabel = tk.Label(lado_direito1, text="Date\n(dd/mm/yyyy)", fg="white", bg="#2B2B2B") datalabel.grid(row=5, column=2, pady=(5, 10)) horas= ttk.Combobox(lado_direito1, width=3, values=[f"{i:02d}" for i in range(24)]) horas.grid(row=4, column=3, sticky="w", padx=(10)) horas.set("HH") minutos = ttk.Combobox(lado_direito1, width=3, values=[f"{i:02d}" for i in range(60)]) minutos.grid(row=4, column=3, padx=(55)) minutos.set("MM") timelabel = tk.Label(lado_direito1, text="Hour", fg="white", bg="#2B2B2B") timelabel.grid(row=5, column=3, sticky="w", padx=35) local = customtkinter.CTkEntry(lado_direito1, placeholder_text="Local") local.grid(row=6, column=0, padx=10) locallabel = tk.Label(lado_direito1, text="Local", fg="white", bg="#2B2B2B") locallabel.grid(row=7, column=0, pady=(5, 10)) city = customtkinter.CTkEntry(lado_direito1, placeholder_text="City") city.grid(row=6, column=1, padx=10) citylabel = tk.Label(lado_direito1, text="City", fg="white", bg="#2B2B2B") citylabel.grid(row=7, column=1, pady=(5, 10)) validar = customtkinter.CTkButton(lado_direito1, text="Need Validation", fg_color="gray") validar.grid(row=6, column=2) def verificarcampospreenchidos(): fm_cc_valor = user2.get() local_valor = local.get() city_valor = city.get() data_valor = data_entry.get_date() if data_entry.get_date() else None hora_valor = horas.get() minuto_valor = minutos.get() if all([fm_cc_valor, local_valor, city_valor, data_valor, hora_valor, minuto_valor]): validar.configure(fg_color="green", text="Validated", command=lambda: atribuivalores(user2, local, city, data_entry, horas, minutos)) else: validar.configure(fg_color="gray", text="Need Validation") for campo in [user2,data_entry,horas,minutos,local,city]: campo.bind("", lambda event: verificarcampospreenchidos()) janela_principal.mainloop() Login()