| 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) |
| 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", |
| ) |
|
|
| |
| 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 |
|
|