Last active
July 6, 2023 03:25
-
-
Save dtxe/694b3cabd034bc560c458d844d47fd56 to your computer and use it in GitHub Desktop.
Convert psychopy visual degrees to monitor pixels. Originally designed for iohub tobii eyetracker recordings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
from typing import Tuple | |
def vdeg_to_px( | |
gaze_vdeg:Tuple[float, float], | |
screen_size_cm:Tuple[float, float], | |
screen_size_px:Tuple[int, int], | |
viewer_distance_cm:float, | |
) -> Tuple[float, float]: | |
''' | |
Convert from psychopy visdeg (0,0 at center, +y up, +x right) | |
to normalized screen coordinates (0,0 at topleft, +y down, +x right) | |
:param gaze_vdeg: (x,y) coordinates in visual degrees | |
:param screen_size_cm: (width, height) of screen in cm | |
:param screen_size_px: (width, height) of screen in px | |
:param viewer_distance_cm: distance from screen to viewer in cm | |
:return: (x,y) coordinates in px | |
''' | |
# to normalized screen coordinates | |
norm_psychopy = [math.tan(math.radians(this_vdeg)) * viewer_distance_cm / this_scn_sz_cm | |
for this_vdeg, this_scn_sz_cm | |
in zip(gaze_vdeg, screen_size_cm)] | |
# change from psychopy coordinate frame (0,0 at center, +y up, +x right) | |
# to normalized monitor coordinate frame (0,0 at topleft, +y down, +x right) | |
norm_monitor = [norm_psychopy[0] + 0.5, norm_psychopy[1] * -1 + 0.5] | |
# to pixels: norm x/y * screen resolution in pixels | |
px = [this_norm_monitor * this_scn_sz_px | |
for this_norm_monitor, this_scn_sz_px | |
in zip(norm_monitor, screen_size_px)] | |
return px | |
################################################################## | |
# test the function | |
vdeg_to_px_args = { | |
'screen_size_cm' : (34.5, 19.5), | |
'screen_size_px' : (1920, 1080), | |
'viewer_distance_cm' : 65, | |
} | |
coords_to_test_visdeg = [ | |
(0, 0), # center of the screen | |
(0, 8.5), # near top, horizontally centered | |
(14, 0), # near left, vertically centered | |
(0, -8.5), # near bottom, horizontally centered | |
(-14, 0), # near right, vertically centered | |
] | |
for coord in coords_to_test_visdeg: | |
print(f"{str(coord):<12}=> {vdeg_to_px(coord, **vdeg_to_px_args)}") | |
# (0, 0) => [960.0, 540.0] | |
# (0, 8.5) => [960.0, 1.976395143139884] | |
# (14, 0) => [1861.9169494153318, 540.0] | |
# (0, -8.5) => [960.0, 1078.0236048568602] | |
# (-14, 0) => [58.08305058466807, 540.0] | |
################################################################## | |
# example with pandas | |
import pandas as pd | |
df = pd.DataFrame(coords_to_test_visdeg, columns=['gaze_x_deg', 'gaze_y_deg']) | |
print('Pre-conversion:') | |
print(df) | |
# convert to pixels | |
df[['gaze_x_px', 'gaze_y_px']] = df.apply(lambda row: vdeg_to_px((row['gaze_x_deg'], row['gaze_y_deg']), **vdeg_to_px_args), axis=1).apply(pd.Series) | |
print('\n\nPost-conversion:') | |
print(df) | |
# Pre-conversion: | |
# gaze_x_deg gaze_y_deg | |
# 0 0 0.0 | |
# 1 0 8.5 | |
# 2 14 0.0 | |
# 3 0 -8.5 | |
# 4 -14 0.0 | |
# Post-conversion: | |
# gaze_x_deg gaze_y_deg gaze_x_px gaze_y_px | |
# 0 0 0.0 960.000000 540.000000 | |
# 1 0 8.5 960.000000 1.976395 | |
# 2 14 0.0 1861.916949 540.000000 | |
# 3 0 -8.5 960.000000 1078.023605 | |
# 4 -14 0.0 58.083051 540.000000 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment