Habe nochmal Dateipfade anpassen müssen, jetzt läuft alles finalmaster
""" | |||||
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 |
""" | |||||
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 |
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') | |||||
""" | |||||
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") |
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) |