import copy import eyekit as ek import numpy as np import pandas as pd from PIL import Image MEASURES_DICT = { "number_of_fixations": [], "initial_fixation_duration": [], "first_of_many_duration": [], "total_fixation_duration": [], "gaze_duration": [], "go_past_duration": [], "second_pass_duration": [], "initial_landing_position": [], "initial_landing_distance": [], "landing_distances": [], "number_of_regressions_in": [], } def get_fix_seq_and_text_block( dffix, trial, x_txt_start=None, y_txt_start=None, font_face="Courier New", font_size=None, line_height=None, use_corrected_fixations=True, correction_algo="warp", ): if use_corrected_fixations and correction_algo is not None: fixations_tuples = [ ( (x[1]["x"], x[1][f"y_{correction_algo}"], x[1]["corrected_start_time"], x[1]["corrected_end_time"]) if x[1]["corrected_start_time"] < x[1]["corrected_end_time"] else (x[1]["x"], x[1]["y"], x[1]["corrected_start_time"], x[1]["corrected_end_time"] + 1) ) for x in dffix.iterrows() ] else: fixations_tuples = [ ( (x[1]["x"], x[1]["y"], x[1]["corrected_start_time"], x[1]["corrected_end_time"]) if x[1]["corrected_start_time"] < x[1]["corrected_end_time"] else (x[1]["x"], x[1]["y"], x[1]["corrected_start_time"], x[1]["corrected_end_time"] + 1) ) for x in dffix.iterrows() ] try: fixation_sequence = ek.FixationSequence(fixations_tuples) except Exception as e: print(e) print(f"Creating fixation failed for {trial['trial_id']} {trial['filename']}") return dffix if "display_coords" in trial: display_coords = trial["display_coords"] else: display_coords = (0, 0, 1920, 1080) screen_size = ((display_coords[2] - display_coords[0]), (display_coords[3] - display_coords[1])) y_diffs = np.unique(trial["line_heights"]) if len(y_diffs) == 1: y_diff = y_diffs[0] else: y_diff = np.min(y_diffs) chars_list = trial["chars_list"] max_line = int(chars_list[-1]["assigned_line"]) words_on_lines = {x: [] for x in range(int(max_line) + 1)} [words_on_lines[x["assigned_line"]].append(x["char"]) for x in chars_list] sentence_list = ["".join([s for s in v]) for idx, v in words_on_lines.items()] if x_txt_start is None: x_txt_start = float(chars_list[0]["char_xmin"]) if y_txt_start is None: y_txt_start = float(chars_list[0]["char_ymax"]) if font_face is None and "font" in trial: font_face = trial["font"] elif font_face is None: font_face = "DejaVu Sans Mono" if font_size is None and "font_size" in trial: font_size = trial["font_size"] elif font_size is None: font_size = float(y_diff * 0.333) # pixel to point conversion if line_height is None: line_height = float(y_diff) textblock = ek.TextBlock( sentence_list, position=(float(x_txt_start), float(y_txt_start)), font_face=font_face, line_height=line_height, font_size=font_size, anchor="left", align="left", ) # eyekit_plot(textblock, fixation_sequence, screen_size) ek.io.save(fixation_sequence, f'results/fixation_sequence_eyekit_{trial["trial_id"]}.json', compress=False) ek.io.save(textblock, f'results/textblock_eyekit_{trial["trial_id"]}.json', compress=False) return fixation_sequence, textblock, screen_size def eyekit_plot(textblock, fixation_sequence, screen_size): img = ek.vis.Image(*screen_size) img.draw_text_block(textblock) for word in textblock.words(): img.draw_rectangle(word, color="hotpink") img.draw_fixation_sequence(fixation_sequence) img.save("temp_eyekit_img.png", crop_margin=200) img_png = Image.open("temp_eyekit_img.png") return img_png def plot_with_measure(textblock, fixation_sequence, screen_size, measure, use_characters=False): eyekitplot_img = eyekit_plot(textblock, fixation_sequence, screen_size) eyekitplot_img = ek.vis.Image(*screen_size) eyekitplot_img.draw_text_block(textblock) if use_characters: measure_results = getattr(ek.measure, measure)(textblock.characters(), fixation_sequence) enum = textblock.characters() else: measure_results = getattr(ek.measure, measure)(textblock.words(), fixation_sequence) enum = textblock.words() for word in enum: eyekitplot_img.draw_rectangle(word, color="lightseagreen") x = word.onset y = word.y_br - 3 label = f"{measure_results[word.id]}" eyekitplot_img.draw_annotation((x, y), label, color="lightseagreen", font_face="Arial bold", font_size=15) eyekitplot_img.draw_fixation_sequence(fixation_sequence, color="gray") eyekitplot_img.save("multiline_passage_piccol.png", crop_margin=100) img_png = Image.open("multiline_passage_piccol.png") return img_png def get_eyekit_measures(_txt, _seq, get_char_measures=False): measures = copy.deepcopy(MEASURES_DICT) words = [] for w in _txt.words(): words.append(w.text) for m in measures.keys(): measures[m].append(getattr(ek.measure, m)(w, _seq)) word_measures_df = pd.DataFrame(measures) word_measures_df["word_number"] = np.arange(0, len(words)) word_measures_df["word"] = words first_column = word_measures_df.pop("word") word_measures_df.insert(0, "word", first_column) first_column = word_measures_df.pop("word_number") word_measures_df.insert(0, "word_number", first_column) if get_char_measures: measures = copy.deepcopy(MEASURES_DICT) characters = [] for c in _txt.characters(): characters.append(c.text) for m in measures.keys(): measures[m].append(getattr(ek.measure, m)(c, _seq)) character_measures_df = pd.DataFrame(measures) character_measures_df["char_number"] = np.arange(0, len(characters)) character_measures_df["character"] = characters first_column = character_measures_df.pop("character") character_measures_df.insert(0, "character", first_column) first_column = character_measures_df.pop("char_number") character_measures_df.insert(0, "char_number", first_column) else: character_measures_df = None return word_measures_df, character_measures_df