Habe nochmal Dateipfade anpassen müssen, jetzt läuft alles finalmaster
@@ -0,0 +1,15 @@ | |||
""" | |||
Parameter: | |||
-minimale und maximale Frequenz | |||
-Alpha-Wert | |||
Autor: Roberto Gelsinger | |||
Datum: 07.12.2023 | |||
Version: Modulversion | |||
""" | |||
freq_min = 1 # Minimale Frequenzgrenze | |||
freq_max = 3 # Maximale Frequenzgrenze | |||
alpha = 100 # Alpha-Wert für die Analyse |
@@ -0,0 +1,52 @@ | |||
""" | |||
Abhängigkeiten: | |||
- numpy | |||
- scipy.signal (butter, lfilter) | |||
- constants (für die Verwendung von 'alpha') | |||
Autor: Roberto Gelsinger | |||
Datum: 07.12.2023 | |||
Version: Modulversion | |||
""" | |||
import numpy as np | |||
from scipy.signal import butter, lfilter | |||
from constants import alpha | |||
def fft_filter(video, freq_min, freq_max, fps): | |||
""" | |||
Diese Funktion nimmt Videodaten, eine Frequenzbandbreite und die Bildrate (FPS) des Videos entgegen. | |||
Sie filtert das Video, um nur Frequenzen im angegebenen Band zu verstärken. Das verstärkte Video, die FFT-Daten | |||
und die Frequenzen werden zurückgegeben. | |||
Args: | |||
video (ndarray): Die Videodaten als ndarray. | |||
freq_min (float): Die untere Grenzfrequenz des zu verstärkenden Frequenzbands. | |||
freq_max (float): Die obere Grenzfrequenz des zu verstärkenden Frequenzbands. | |||
fps (int): Die Bildrate (Frames pro Sekunde) des Videos. | |||
Returns: | |||
tuple: Ein Tupel, bestehend aus: | |||
- amplified_video (ndarray): Das verstärkte Videodaten als ndarray. | |||
- fft (ndarray): Die FFT-Daten des verstärkten Videos. | |||
- frequencies (ndarray): Die zugehörigen Frequenzen der FFT. | |||
""" | |||
nyquist = 0.5 * fps | |||
low = freq_min / nyquist | |||
high = freq_max / nyquist | |||
# Min-Max-Frequenzen filtern | |||
b, a = butter(4, [low, high], btype='band') | |||
filtered_video = np.zeros_like(video) | |||
for i in range(video.shape[2]): | |||
filtered_video[:, :, i] = lfilter(b, a, video[:, :, i]) | |||
# Verstärkung | |||
amplified_video = np.abs(filtered_video) * alpha | |||
fft = np.fft.fft(amplified_video, axis=0) | |||
frequencies = np.fft.fftfreq(amplified_video.shape[0], d=1.0 / fps) | |||
return amplified_video, fft, frequencies |
@@ -0,0 +1,151 @@ | |||
import openpyxl | |||
def excel_row_to_string(file_path): | |||
# Öffne die Excel-Datei | |||
workbook = openpyxl.load_workbook(file_path) | |||
# Wähle das Arbeitsblatt aus | |||
sheet = workbook['Sheet1'] | |||
# Erhalte die angegebene Zeile als Liste von Zellen | |||
row_values = [cell.value for cell in sheet[2]] | |||
# Ergebnisse werden ab Spalte 5 eingetragen | |||
selected_columns = list(range(4, len(row_values))) | |||
# Wähle nur die gewünschten Spalten aus | |||
selected_values = [row_values[col] for col in selected_columns] | |||
# Schließe die Excel-Datei | |||
workbook.close() | |||
# Konvertiere die Liste von Zellen in einen String | |||
row_string = ', '.join(str(value) for value in selected_values) | |||
return row_string | |||
def write_subdivided_string_to_excel(file_path, input_string): | |||
# Öffne die Excel-Datei | |||
workbook = openpyxl.load_workbook(file_path) | |||
# Wähle das Arbeitsblatt aus | |||
sheet = workbook['Sheet1'] | |||
# Teile den String nach jedem Komma auf | |||
parts = input_string.split(',') | |||
# Trage jeden Teil des Strings in eine neue Zeile ein | |||
for i, part in enumerate(parts, 1): | |||
#Spalte 17 kann sich je nach Tabellenstruktur ändern! | |||
sheet.cell(row=2 + i - 1, column=17, value=part.strip()) # strip entfernt mögliche Leerzeichen | |||
# Speichere die Änderungen | |||
workbook.save(file_path) | |||
# Schließe die Excel-Datei | |||
workbook.close() | |||
def read_columns(file_path): | |||
# Öffne die Excel-Datei | |||
workbook = openpyxl.load_workbook(file_path) | |||
# Wähle das Arbeitsblatt aus | |||
sheet = workbook['Sheet1'] | |||
# Lese die Werte der beiden Spalten aus | |||
values_column1 = [cell.value for cell in sheet['O']][1:] | |||
values_column2 = [cell.value for cell in sheet['Q']][1:] | |||
# Schließe die Excel-Datei | |||
workbook.close() | |||
return values_column1, values_column2 | |||
def calculate_deviation(liste1, liste2): | |||
# Überprüfe, ob die Listen die gleiche Länge haben | |||
if len(liste1) != len(liste2): | |||
raise ValueError("Die Listen müssen die gleiche Länge haben") | |||
# Berechne die prozentuale Abweichung zwischen den Werten | |||
deviations = [((abs(float(b) - float(a)) / float(a)) * 100) if float(a) != 0 else None for a, b in zip(liste1, liste2)] | |||
return deviations | |||
def write_string_to_excel(file_path, input_string, column): | |||
# Öffne die Excel-Datei | |||
workbook = openpyxl.load_workbook(file_path) | |||
# Wähle das Arbeitsblatt aus | |||
sheet = workbook['Sheet1'] | |||
# Trage jeden Buchstaben des Strings in eine eigene Zeile ein | |||
for i, char in enumerate(input_string, 1): | |||
sheet.cell(row=2 + i - 1, column=column, value=char) | |||
# Speichere die Änderungen | |||
workbook.save(file_path) | |||
# Schließe die Excel-Datei | |||
workbook.close() | |||
def copy_header(input_sheet, output_sheet): | |||
# Kopiere den Header manuell in das Ausgabe-Arbeitsblatt | |||
for row in input_sheet.iter_rows(min_row=1, max_row=1, values_only=True): | |||
output_sheet.append(row) | |||
def sort_excel(input_file_path, output_file_path, ): | |||
# Öffne die Eingabe-Excel-Datei | |||
input_workbook = openpyxl.load_workbook(input_file_path) | |||
input_sheet = input_workbook['Sheet1'] | |||
# Erstelle eine neue Excel-Tabelle für die sortierten Zeilen | |||
output_workbook = openpyxl.Workbook() | |||
output_sheet = output_workbook.active | |||
# Kopiere den Header ins Ausgabe-Arbeitsblatt | |||
copy_header(input_sheet, output_sheet) | |||
# Lese die Daten-Zeilen aus der Tabelle | |||
data_rows = list(input_sheet.iter_rows(min_row=2, values_only=True)) | |||
# Sortiere die Daten-Zeilen nach dem Wert der angegebenen Spalte | |||
sorted_data_rows = sorted(data_rows, key=lambda x: x[18 - 1]) # -1, da Listenindizes bei 0 beginnen | |||
# Schreibe die sortierten Daten-Zeilen in die neue Tabelle | |||
for row in sorted_data_rows: | |||
output_sheet.append(row) | |||
# Speichere die Änderungen in der neuen Excel-Datei | |||
output_workbook.save(output_file_path) | |||
# Schließe die Excel-Dateien | |||
input_workbook.close() | |||
output_workbook.close() | |||
#Sollten mehrere Testruns ausgewertet werden wollen, müssen die enthaltenen Funktionen umstrukturiert werden | |||
#Aktuell wird nur der Testrun in Zeile 1 ausgewertet | |||
#Eine Weitere Funktion, die zwei Tabellenzeilen tauscht, wäre der einfachste workaround | |||
def evaluation(testcases, testruns): | |||
#liest die Ergebnisse des Testruns aus | |||
#bei mehreren Testruns muss diese Funktion angepasst werden! | |||
input_string = excel_row_to_string(testruns) | |||
#schreibt die Berechneten Ergebnisse in die Testcases-Tabelle | |||
write_subdivided_string_to_excel(testcases, input_string) | |||
#liest die gemessenen und die errechneten Werte aus den Testcases | |||
values_col1, values_col2 = read_columns(testcases) | |||
#berechnet aus diesen Werten die prozentuale Abweichung | |||
deviations = calculate_deviation(values_col1, values_col2) | |||
#Trägt die prozentualen Abweichungen in die Testcases-Tabelle | |||
#je nach Tabellenstruktur kann sich die 18 ändern! | |||
write_string_to_excel(testcases, deviations, 18) | |||
#Gibt die eine Kopie der Testcases-Tabelle sortiert nach Genauigkeit aus | |||
sort_excel(testcases, 'Testcases_nach_Genauigkeit.xlsx') | |||
@@ -0,0 +1,138 @@ | |||
""" | |||
Abhängigkeiten: | |||
- pyramids (für den Aufbau der Bildpyramiden) | |||
- heartrate (zur Berechnung der Herzfrequenz) | |||
- preprocessing (für die Video-Vorverarbeitung) | |||
- eulerian (für die Euler'sche Video-Magnifikation) | |||
- tkinter und constants (für die GUI und Konstantenverwaltung) | |||
Autor: Roberto Gelsinger | |||
Datum: 07.12.2023 | |||
Version: Modulversion | |||
""" | |||
import pyramids | |||
import heartrate | |||
import facedetection | |||
import eulerian | |||
from constants import freq_max, freq_min | |||
import pandas as pd | |||
from excel_update import color_cells_based_on_deviation | |||
from excel_evaluation import evaluation | |||
def process_video_for_excel(selected_video_name): | |||
""" | |||
Verarbeitet ein ausgewähltes Video, um die Herzfrequenz der abgebildeten Person zu ermitteln. | |||
Dieser Prozess umfasst die Vorverarbeitung des Videos, den Aufbau einer Laplace-Pyramide, | |||
die Anwendung von FFT-Filterung und Euler'scher Magnifikation, und schließlich die Berechnung | |||
der Herzfrequenz aus den Video-Daten. | |||
Args: | |||
selected_video_name (str): Der Name des zu verarbeitenden Videos. | |||
Returns: | |||
None: Die Funktion gibt direkt die berechnete Herzfrequenz auf der Konsole aus. | |||
""" | |||
print("Reading + preprocessing video...") | |||
video_frames, frame_ct, fps = facedetection.read_video("code/videos/"+selected_video_name) | |||
print("Building Laplacian video pyramid...") | |||
lap_video = pyramids.build_video_pyramid(video_frames) | |||
print(len(lap_video)) | |||
for i, video in enumerate(lap_video): | |||
print("test") | |||
if i == 0 or i == len(lap_video)-1: | |||
continue | |||
print("Running FFT and Eulerian magnification...") | |||
result, fft, frequencies = eulerian.fft_filter(video, freq_min, freq_max, fps) | |||
lap_video[i] += result | |||
print("Calculating heart rate...") | |||
heart_rate = heartrate.find_heart_rate(fft, frequencies, freq_min, freq_max) | |||
print("Heart rate: ", heart_rate*0.7, "bpm") | |||
return heart_rate *0.7 | |||
def process_all_videos_and_save_results(testcase_excel_file_path, testruns_excel_file_path, code_version, kommentar): | |||
try: | |||
df_testruns = pd.read_excel(testruns_excel_file_path) | |||
except FileNotFoundError: | |||
df_testruns = pd.DataFrame() | |||
df_testcases = pd.read_excel(testcase_excel_file_path) | |||
existing_testcases = [col for col in df_testruns.columns if col.startswith('Testcase_')] | |||
new_testcases = [f'Testcase_{tc}' for tc in df_testcases['Testcase'] if f'Testcase_{tc}' not in existing_testcases] | |||
if df_testruns.empty: | |||
df_testruns = pd.DataFrame(columns=['Testnummer', 'Codeversion', 'Kommentar', 'Abweichung']) | |||
for col in new_testcases: | |||
df_testruns[col] = None | |||
df_testruns.to_excel(testruns_excel_file_path, index=False) | |||
if new_testcases: | |||
print(f"Folgende neue Testcases wurden hinzugefügt: {new_testcases}") | |||
else: | |||
print("Keine neuen Testcases zum Hinzufügen gefunden.") | |||
next_testcase_index = len(df_testruns) + 1 | |||
new_run = { | |||
'Testnummer': next_testcase_index, | |||
'Codeversion': code_version, | |||
'Kommentar': kommentar, | |||
'Abweichung': 'Wert_für_Abweichung' | |||
} | |||
for index, row in df_testcases.iterrows(): | |||
video_name = row['VideoName'] | |||
heart_rate = process_video_for_excel(video_name) | |||
testcase_column_name = f'Testcase_{row["Testcase"]}' | |||
new_run[testcase_column_name] = heart_rate | |||
try: | |||
df_testruns = df_testruns._append(new_run, ignore_index=True) | |||
except TypeError: | |||
pass | |||
df_testruns.to_excel(testruns_excel_file_path, index=False) | |||
print("Testrun wurde verarbeitet und das Ergebnis in der Testruns-Excel-Datei gespeichert.") | |||
color_cells_based_on_deviation(testruns_excel_file_path, testcase_excel_file_path) | |||
print("Zellen gefärbt") | |||
evaluation(testcase_excel_file_path, testruns_excel_file_path) | |||
print("Testcases sortiert") |
@@ -0,0 +1,56 @@ | |||
import openpyxl | |||
from openpyxl.styles import PatternFill | |||
import pandas as pd | |||
def fill_cell(ws, cell, color): | |||
fill = PatternFill(start_color=color, end_color=color, fill_type='solid') | |||
cell.fill = fill | |||
def calculate_and_fill_deviation(ws, row, absolute_deviations): | |||
if absolute_deviations: | |||
average_deviation = sum(absolute_deviations) / len(absolute_deviations) | |||
deviation_cell = ws.cell(row=row[0].row, column=4) # Angenommen, die 'Abweichung'-Spalte ist Spalte D | |||
deviation_cell.value = average_deviation | |||
# Färbe die Zelle basierend auf der durchschnittlichen Abweichung | |||
if average_deviation < 5: | |||
fill_color = 'FF00FF00' # Grün | |||
elif 5 <= average_deviation < 10: | |||
fill_color = 'FFFFFF00' # Gelb | |||
else: | |||
fill_color = 'FFFF0000' # Rot | |||
fill_cell(ws, deviation_cell, fill_color) | |||
def color_cells_based_on_deviation(testruns_excel_file_path, testcases_excel_file_path): | |||
wb_testruns = openpyxl.load_workbook(testruns_excel_file_path) | |||
ws_testruns = wb_testruns.active | |||
df_testcases = pd.read_excel(testcases_excel_file_path) | |||
for row in ws_testruns.iter_rows(min_row=2, max_row=ws_testruns.max_row): | |||
deviations = [] | |||
absolute_deviations = [] | |||
for cell in row[4:]: | |||
header_cell_value = ws_testruns.cell(row=1, column=cell.column).value | |||
if header_cell_value and "Testcase" in header_cell_value: | |||
testcase_num = int(header_cell_value.split('_')[1]) | |||
expected_pulse_row = df_testcases[df_testcases['Testcase'] == testcase_num] | |||
if not expected_pulse_row.empty: | |||
expected_pulse = expected_pulse_row.iloc[0]['Puls'] | |||
actual_pulse = cell.value | |||
if actual_pulse is not None and expected_pulse is not None: | |||
relative_deviation = (actual_pulse - expected_pulse) / expected_pulse * 100 | |||
absolute_deviation = abs(relative_deviation) | |||
deviations.append(relative_deviation) | |||
absolute_deviations.append(absolute_deviation) | |||
if absolute_deviation < 5: | |||
fill_color = 'FF00FF00' # Grün | |||
elif 5 <= absolute_deviation < 10: | |||
fill_color = 'FFFFA500' if relative_deviation < 0 else 'FFFFFF00' # Orange für niedriger, Gelb für höher | |||
else: | |||
fill_color = 'FFC0CB' if relative_deviation < 0 else 'FFFF0000' # Rosa für niedriger, Rot für höher | |||
fill_cell(ws_testruns, cell, fill_color) | |||
calculate_and_fill_deviation(ws_testruns, row, absolute_deviations) | |||
wb_testruns.save(testruns_excel_file_path) |