Files
2026-03-15 13:27:50 +00:00

1583 lines
67 KiB
Python

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("<FocusOut>", lambda event: verificarcampospreenchidos())
janela_principal.mainloop()
Login()