From 91aeb2ca7a6ac3402c9d0d972311c16c5db0f8b7 Mon Sep 17 00:00:00 2001 From: Dominic Canare Date: Tue, 27 Aug 2024 07:44:54 -0400 Subject: [PATCH 1/5] Implements tag-frame component --- psychopy_eyetracker_pupil_labs/__init__.py | 148 ------------ .../classic/apriltag_frame.png | Bin 0 -> 4873 bytes .../classic/apriltag_frame@2x.png | Bin 0 -> 4817 bytes .../dark/apriltag_frame.png | Bin 0 -> 4873 bytes .../dark/apriltag_frame@2x.png | Bin 0 -> 4817 bytes .../light/apriltag_frame.png | Bin 0 -> 4873 bytes .../light/apriltag_frame@2x.png | Bin 0 -> 4817 bytes .../pupil_labs/components.py | 212 ++++++++++++++++++ .../pupil_labs/stimuli.py | 123 ++++++++++ pyproject.toml | 3 +- 10 files changed, 337 insertions(+), 149 deletions(-) create mode 100644 psychopy_eyetracker_pupil_labs/classic/apriltag_frame.png create mode 100644 psychopy_eyetracker_pupil_labs/classic/apriltag_frame@2x.png create mode 100644 psychopy_eyetracker_pupil_labs/dark/apriltag_frame.png create mode 100644 psychopy_eyetracker_pupil_labs/dark/apriltag_frame@2x.png create mode 100644 psychopy_eyetracker_pupil_labs/light/apriltag_frame.png create mode 100644 psychopy_eyetracker_pupil_labs/light/apriltag_frame@2x.png create mode 100644 psychopy_eyetracker_pupil_labs/pupil_labs/components.py create mode 100644 psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py diff --git a/psychopy_eyetracker_pupil_labs/__init__.py b/psychopy_eyetracker_pupil_labs/__init__.py index d14af8c..9baacce 100644 --- a/psychopy_eyetracker_pupil_labs/__init__.py +++ b/psychopy_eyetracker_pupil_labs/__init__.py @@ -3,151 +3,3 @@ # Copyright (C) 2012-2020 iSolver Software Solutions (C) 2021 Open Science Tools Ltd. # Distributed under the terms of the GNU General Public License (GPL). -from pathlib import Path - -from psychopy.visual import ImageStim -from psychopy.experiment.components import BaseVisualComponent, Param, getInitVals -from psychopy.localization import _translate - -import numpy as np - -from pupil_labs.real_time_screen_gaze import marker_generator - - -class AprilTagStim(ImageStim): - def __init__(self, marker_id=0, contrast=1.0, *args, **kwargs): - self.marker_id = marker_id - - marker_data = marker_generator.generate_marker(marker_id, flip_x=True).astype(float) - marker_data[marker_data == 0] = -contrast - marker_data[marker_data > 0] = contrast - - marker_data = np.pad(marker_data, pad_width=1, mode="constant", constant_values=contrast) - - super().__init__(image=marker_data, *args, **kwargs) - - def get_marker_verts(self): - vertices_in_pixels = self._vertices.pix - size_with_margin = ( - abs(vertices_in_pixels[1][0] - vertices_in_pixels[0][0]), - abs(vertices_in_pixels[2][1] - vertices_in_pixels[0][1]) - ) - size_without_margin = [v * 0.8 for v in size_with_margin] - padding = size_with_margin[0] * 0.1 - - top_left_pos = vertices_in_pixels[2] - - top_left = ( - top_left_pos[0] + padding, - top_left_pos[1] - padding - ) - bottom_right = ( - top_left[0] + size_without_margin[0], - top_left[1] - size_without_margin[1] - ) - - return ( - top_left, - (bottom_right[0], top_left[1]), - bottom_right, - (top_left[0], bottom_right[1]), - ) - - -class AprilTagComponent(BaseVisualComponent): - targets = ['PsychoPy'] - categories = ['Eyetracking'] - iconFile = Path(__file__).parent / 'apriltag.png' - tooltip = _translate('AprilTag: Markers to identify a screen surface') - - _instances = [] - _routine_start_written = False - - def __init__(self, exp, parentName, marker_id=0, anchor="center", *args, **kwargs): - super(AprilTagComponent, self).__init__(exp, parentName, *args, **kwargs) - - self.type = 'Image' - self.url = "https://april.eecs.umich.edu/software/apriltag.html" - self.exp.requirePsychopyLibs(['visual']) - self.exp.requireImport('AprilTagStim', 'psychopy_eyetracker_pupil_labs') - self.exp.requireImport('convertToPix', 'psychopy.tools.monitorunittools') - - self.order += ['marker_id'] - - self.params['marker_id'] = Param(marker_id, - valType='int', inputType="spin", categ='Basic', - updates='constant', allowedVals=[0, 512], - allowedUpdates=['constant', 'set every repeat', 'set every frame'], - hint=_translate("The ID of the AprilTag marker to display"), - label=_translate("Marker ID") - ) - - self.params['anchor'] = Param( - anchor, valType='str', inputType="choice", categ='Layout', - allowedVals=['center', - 'top-center', - 'bottom-center', - 'center-left', - 'center-right', - 'top-left', - 'top-right', - 'bottom-left', - 'bottom-right', - ], - updates='constant', - hint=_translate("Which point on the stimulus should be anchored to its exact position?"), - label=_translate("Anchor") - ) - - del self.params['color'] - del self.params['colorSpace'] - del self.params['fillColor'] - del self.params['borderColor'] - del self.params['opacity'] - del self.params['ori'] - - self.marker_id = marker_id - AprilTagComponent._instances.append(self) - - def writeInitCode(self, buff): - AprilTagComponent._routine_start_written = False - - # replace variable params with defaults - inits = getInitVals(self.params, 'PsychoPy') - code = ("{inits[name]} = AprilTagStim(\n" - " win=win,\n" - " name='{inits[name]}', units={inits[units]},\n" - " contrast={inits[contrast]},\n" - " marker_id=int({inits[marker_id]}), anchor={inits[anchor]},\n" - " pos={inits[pos]}, size={inits[size]}" - .format(inits=inits)) - - depth = -self.getPosInRoutine() - code += ", depth=%.1f)\n" % depth - - buff.writeIndentedLines(code) - - def writeRoutineStartCode(self, buff): - """Write the code that will be called at the beginning of - a routine (e.g. to update stimulus parameters) - """ - if AprilTagComponent._routine_start_written: - return - - code = ("if eyetracker is not None and hasattr(eyetracker, 'register_surface'):\n" - " tag_verts = {\n") - - routine = self.exp.routines[self.parentName] - tag_comps = [comp for comp in routine if not comp == routine.settings] - tag_comps = filter(lambda comp: isinstance(comp, AprilTagComponent), tag_comps) - - for component in tag_comps: - inits = getInitVals(component.params, 'PsychoPy') - code += " str({inits[name]}.marker_id): {inits[name]}.get_marker_verts(),\n".format(inits=inits) - - code += " }\n" - code += " mac_friendly_win_size = convertToPix(np.array([0, 0]), np.array([2, 2]), 'norm', win)\n" - code += " eyetracker.register_surface(tag_verts, mac_friendly_win_size)\n" - buff.writeIndentedLines(code) - - AprilTagComponent._routine_start_written = True diff --git a/psychopy_eyetracker_pupil_labs/classic/apriltag_frame.png b/psychopy_eyetracker_pupil_labs/classic/apriltag_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..6b50e93c5b959964e2b4372735e9e0262d4a5519 GIT binary patch literal 4873 zcmeHLe^3)w9^XI(io&5%DHX4K!CzQ5+1)H5SqO>*BtR4~0?G{MW_JS%B%3A+37oa! zk5(#ED|lLe6g#K`ZQ-u86h|3Pta4Bts%Lw6p68F^-Hp;J*SV`_ZNYoH2?55=+%QxB zV>2)Bz3=;eKkt1%-|zdvrY3vkijd$Jf*}YBF{JBrK#z(oC;)ut`F;O71j)kLe1pYc zSONtB*>oUy29kO8WaGwuGJnV)$m>80mH9!lK>s#qvw&RG7uyT6X;S|_7|->NKLX>$ zULDaM1GXWcKM5KR#^Ipv1&w>%v+v))SbTz{Pl|)31eSqaoKOs}Fc`C;A7db>0IZMp zq7cRv7^+Zd03wPLB&s2C9LC~F0wd!H$Paq0E)a?YHjhA)y*qYZF!eo8?&SXRpcUwE z<5E(x4Jj$G-N9S9G8Tf|hhEvOOFz3Ts{7Xk$5NMs=FVK*oBhr=jq*1?2~KSeE7_hq zFH14JWnqZnjh#nst+)S+ZX3Kc(DK3758$9pn>7iVn3L%>k28Z2bE4*cPv>uTb-Fg6 zUQNA!^|?S(AAb3>ZEy9p2A|2>hVGEn%I9Ni4$tlmPwcs(+qm)6&TnIfoUguFxPZsI z7KQcfx8^BBla!XpuSB-pXFDzL1PqJ_3{I(qnc7(x4Uc;As7(6hi4w&avir&C6|rCi#FenxHj^<|CY{WAKR|o z?CH3kDD%76_1pdjd2_qIS#(?0+NC%qE_K@}x|9`QGh4*jbn>Cj({h+&bn?77BWkp#u*F<@ zm4jVVwbDdamC{;9zBDN~(MYLgwhxybxfflNSquom47aE|Xm=vgQ2;Oqj&+h7rsGV|qvPA&)Q@aXxhcB9b;Z*zKC0DLIjlwFA_ zP^HzX9B<(iQY!$6_dq{s;WSm+S!E9EIA!D6SfrZrO!g7pop{DB`ZMo1WkdnjFC0rY~qUK&iI1>b06*m?NQ$)WuRp=l6syl z7msJq>*V78B*W7jLrO!fnbD#Yh9k6@ibn_wqe9G-)`G;xVOo_&MKP3$8V6;tIR(l_ zvmz7#S8xC)UWJ-576ej_s?7*N6LE+ZQ)v+vqiD4SLoHgfdK|wDwPFc;%SXqqrq4z-UG$ZWE$_VQXrh1l`3MDc3Y9Ohe$Y? zoNds_aRoXi$u6S=3oy{hvp8G1du+kPS=luLC9;Xds;311eqve zN)=Wz7sEmVX8^I3n5O_hk^^5zsf_Xt6VI3FU!IFtah ziRez=BDg3An_L8V1YCjil>Uyvi={xtOw@K2vtkqgVF;>0aNJ86hI$E8K7AK8D524+ zLFr;~nnnngzz{7C{M4E)j0Iz8g1|g^;3M#7aWM{fEaimP#YCl87Dt1csC**q9))pr z6j0-lf%*)}z0z0jy_|vbKF!CQZclRy7@pc>Qv6QQHAUB?7?_muRCZ0#H7N!rr972g zKO0@aV{eSC4g90$0`G$(!!?EAZ8E@|xk3*;7C-NvYN`j4XYJ|hoDigl7Msj{Y-t4$ z1_}maYT%uqInTwY2QJN>4@8kdYQB)dTg4X-=!u^d@~T54z^@AH4fBZtBmQ^nRm5ljM6r}@Pq>{TJcH$fq=hOHoTQo+)|g5q5tw8(TlZqoxc|m zu*+lg$hn+v~=x!FXrr? zu=-_paswV@)b}i?ue;eB_0;N@2|_a@QXdG7qw^I?t8xdoaLo|C(PKS4~fXEab)vh zzmi{@U7EHjV%Fux1AB8aiksp#ocd~U@0FOBI==Ym$c?i(0o8EWGa`7Vmj1ReLtDKz zZC6`^s`hwV|GsjO*K5 zRq}ajxbayozH+2w^ZIkHzx>`bxVztEZfKJAQX@xb*27y%QtNK EAEDxjwg3PC literal 0 HcmV?d00001 diff --git a/psychopy_eyetracker_pupil_labs/classic/apriltag_frame@2x.png b/psychopy_eyetracker_pupil_labs/classic/apriltag_frame@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..18b56f28dee3d6f9a259f26336ab84708f8611b1 GIT binary patch literal 4817 zcmeHKd2kcg8DGgZ7B+`TfZ#yOB6FER(rUG`Rzd|8tbUEwq(lT zU_#9inmPmn3D|?fU=A|`2Oqcuhd>~dBaksP2`wI{!%#zUJAf&Nq2I3WA(;*{&3|6a z(|hlG-}igp_j}*_q}`Y@ch=yrx5E$w8Ej58&WCtjAxNc0aG~WNJA&0`%9j2?;6Y zgap*%b=%n-20;QJul}PU@ypmTXMeMJpJ~dF)M4|^r|iDBSGx6hn5iNn``y%ECCS^@ zO&o0Aw7I-#k>_9Zr!7s*hYuAVLM3Yo$Z2HsiNuXB=Y+{J_2m6?^}qW=y}#hpJnG3HRW(l^fz*Ppv#Shecp=6_FZ^{w5yY=Rqam=tkt zrz`!;=rgL1wqMRKB~Q;Tj$bubPkj@zacrHu{`psRcaD}iW^PGZxrdCOI(%CC{pG2+ zwV7?M*qgnr^~#CEp1(C8PR%}?oBZOmXW8P6+qz4`#(!Egp(p|^-+)g!8d1J5>a1~B z+nc|BL7h_+&zYDOxn)dvMf|&e|IZgMoBTC)D*KuxY*NAfw|;mrDrjAOY?bSWTj#3( zsuzi`G<y`=DXDsTSX<)8ov2J9$6d}lJzt^q^W(&=h2}L zZ;1^nT<*%%A5TXjTc+d&jTs~U(R5+if;EPB~?BXEO4D(<7Xten!AU(JL4Z&14*`%OGv7KP5$3n?ahUwqO=d0+YoiuJkes zR?fB3D_783n{>wXFnvG=08WOZ(10_?<?my$*#^tJNwnT!G^<(2)7^ zT^tpVxqMMPLw(xPLJOJr*=qD|F)_f16n9um!xn7zvjdUJ$uY_x*OVVt1q zgI$$FpH{WdkUc z7RND+1}M7xLP0|9Cjn>U{dz};K>(3#_NMj4YjgLq1+J5g;wU3jbpY+I*4D0SEyJLG zceu~PS==_(p8vnJ`OAaqK_8Ahk@azIZ@wVvxKRt3-*(J8ra7!|l~7c;6grCT2*F3? zF*d36fB&Xbq0{ zMfbVwoS*VCaSq@SxPtT)e#g+s!a+s%)%IsG{3!rokgqZv?;;GQ0%3~IfE9er=!#gc zc!d+apwO$xfL(_SiWk&EMRzgm zU9ZK!Ybp0<*Z+;Ku%6c`#s&X^{P42$u*Jc@_z%dOGs}p)Ih2D{j91%Tr?6Kx$Ad2Kn>0E-_$^R=yUU}zEduPa72qF$K8{@2j z@Bg_w&h;fWD7bR(%j&iF_pFMJeZP9&z1cer6SU)v+Y{!QBC8+huT++A{qg9^v5#xl z#EfrjeN=Y5t>ryk;b*JoEQx4}kKLv$`S!-$s}HVTXm0s%XL<9c=0Zdxeld+axM6x_ zU537NqAE(cIaGX8QnBZ0%c4U^+7~t-t3?}*habGYBRoVBIaShpzk&+|lWtZzqhl=FEwGW?VSeDA=1B6 zB>7pl3LF12;lO+0;xm~gpOA8iXI#^P!h=hTPpXdjm*iagXg5+Lo87p@zjJZLS0a%s z*!tL8$L!j5F{gO)rlG*6r*z<-W7-e24g5Bw@_I;JZe-&vTS{x~pQdF5i>hv}Z(Ka* zmg`8~$)T|gQ-dq^Ph7e`rivtk?BfcS7VoM>V-L@$P^)p{%O{s1YY?+(uJK6x@^$|O Dj#rfg literal 0 HcmV?d00001 diff --git a/psychopy_eyetracker_pupil_labs/dark/apriltag_frame.png b/psychopy_eyetracker_pupil_labs/dark/apriltag_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..6b50e93c5b959964e2b4372735e9e0262d4a5519 GIT binary patch literal 4873 zcmeHLe^3)w9^XI(io&5%DHX4K!CzQ5+1)H5SqO>*BtR4~0?G{MW_JS%B%3A+37oa! zk5(#ED|lLe6g#K`ZQ-u86h|3Pta4Bts%Lw6p68F^-Hp;J*SV`_ZNYoH2?55=+%QxB zV>2)Bz3=;eKkt1%-|zdvrY3vkijd$Jf*}YBF{JBrK#z(oC;)ut`F;O71j)kLe1pYc zSONtB*>oUy29kO8WaGwuGJnV)$m>80mH9!lK>s#qvw&RG7uyT6X;S|_7|->NKLX>$ zULDaM1GXWcKM5KR#^Ipv1&w>%v+v))SbTz{Pl|)31eSqaoKOs}Fc`C;A7db>0IZMp zq7cRv7^+Zd03wPLB&s2C9LC~F0wd!H$Paq0E)a?YHjhA)y*qYZF!eo8?&SXRpcUwE z<5E(x4Jj$G-N9S9G8Tf|hhEvOOFz3Ts{7Xk$5NMs=FVK*oBhr=jq*1?2~KSeE7_hq zFH14JWnqZnjh#nst+)S+ZX3Kc(DK3758$9pn>7iVn3L%>k28Z2bE4*cPv>uTb-Fg6 zUQNA!^|?S(AAb3>ZEy9p2A|2>hVGEn%I9Ni4$tlmPwcs(+qm)6&TnIfoUguFxPZsI z7KQcfx8^BBla!XpuSB-pXFDzL1PqJ_3{I(qnc7(x4Uc;As7(6hi4w&avir&C6|rCi#FenxHj^<|CY{WAKR|o z?CH3kDD%76_1pdjd2_qIS#(?0+NC%qE_K@}x|9`QGh4*jbn>Cj({h+&bn?77BWkp#u*F<@ zm4jVVwbDdamC{;9zBDN~(MYLgwhxybxfflNSquom47aE|Xm=vgQ2;Oqj&+h7rsGV|qvPA&)Q@aXxhcB9b;Z*zKC0DLIjlwFA_ zP^HzX9B<(iQY!$6_dq{s;WSm+S!E9EIA!D6SfrZrO!g7pop{DB`ZMo1WkdnjFC0rY~qUK&iI1>b06*m?NQ$)WuRp=l6syl z7msJq>*V78B*W7jLrO!fnbD#Yh9k6@ibn_wqe9G-)`G;xVOo_&MKP3$8V6;tIR(l_ zvmz7#S8xC)UWJ-576ej_s?7*N6LE+ZQ)v+vqiD4SLoHgfdK|wDwPFc;%SXqqrq4z-UG$ZWE$_VQXrh1l`3MDc3Y9Ohe$Y? zoNds_aRoXi$u6S=3oy{hvp8G1du+kPS=luLC9;Xds;311eqve zN)=Wz7sEmVX8^I3n5O_hk^^5zsf_Xt6VI3FU!IFtah ziRez=BDg3An_L8V1YCjil>Uyvi={xtOw@K2vtkqgVF;>0aNJ86hI$E8K7AK8D524+ zLFr;~nnnngzz{7C{M4E)j0Iz8g1|g^;3M#7aWM{fEaimP#YCl87Dt1csC**q9))pr z6j0-lf%*)}z0z0jy_|vbKF!CQZclRy7@pc>Qv6QQHAUB?7?_muRCZ0#H7N!rr972g zKO0@aV{eSC4g90$0`G$(!!?EAZ8E@|xk3*;7C-NvYN`j4XYJ|hoDigl7Msj{Y-t4$ z1_}maYT%uqInTwY2QJN>4@8kdYQB)dTg4X-=!u^d@~T54z^@AH4fBZtBmQ^nRm5ljM6r}@Pq>{TJcH$fq=hOHoTQo+)|g5q5tw8(TlZqoxc|m zu*+lg$hn+v~=x!FXrr? zu=-_paswV@)b}i?ue;eB_0;N@2|_a@QXdG7qw^I?t8xdoaLo|C(PKS4~fXEab)vh zzmi{@U7EHjV%Fux1AB8aiksp#ocd~U@0FOBI==Ym$c?i(0o8EWGa`7Vmj1ReLtDKz zZC6`^s`hwV|GsjO*K5 zRq}ajxbayozH+2w^ZIkHzx>`bxVztEZfKJAQX@xb*27y%QtNK EAEDxjwg3PC literal 0 HcmV?d00001 diff --git a/psychopy_eyetracker_pupil_labs/dark/apriltag_frame@2x.png b/psychopy_eyetracker_pupil_labs/dark/apriltag_frame@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..18b56f28dee3d6f9a259f26336ab84708f8611b1 GIT binary patch literal 4817 zcmeHKd2kcg8DGgZ7B+`TfZ#yOB6FER(rUG`Rzd|8tbUEwq(lT zU_#9inmPmn3D|?fU=A|`2Oqcuhd>~dBaksP2`wI{!%#zUJAf&Nq2I3WA(;*{&3|6a z(|hlG-}igp_j}*_q}`Y@ch=yrx5E$w8Ej58&WCtjAxNc0aG~WNJA&0`%9j2?;6Y zgap*%b=%n-20;QJul}PU@ypmTXMeMJpJ~dF)M4|^r|iDBSGx6hn5iNn``y%ECCS^@ zO&o0Aw7I-#k>_9Zr!7s*hYuAVLM3Yo$Z2HsiNuXB=Y+{J_2m6?^}qW=y}#hpJnG3HRW(l^fz*Ppv#Shecp=6_FZ^{w5yY=Rqam=tkt zrz`!;=rgL1wqMRKB~Q;Tj$bubPkj@zacrHu{`psRcaD}iW^PGZxrdCOI(%CC{pG2+ zwV7?M*qgnr^~#CEp1(C8PR%}?oBZOmXW8P6+qz4`#(!Egp(p|^-+)g!8d1J5>a1~B z+nc|BL7h_+&zYDOxn)dvMf|&e|IZgMoBTC)D*KuxY*NAfw|;mrDrjAOY?bSWTj#3( zsuzi`G<y`=DXDsTSX<)8ov2J9$6d}lJzt^q^W(&=h2}L zZ;1^nT<*%%A5TXjTc+d&jTs~U(R5+if;EPB~?BXEO4D(<7Xten!AU(JL4Z&14*`%OGv7KP5$3n?ahUwqO=d0+YoiuJkes zR?fB3D_783n{>wXFnvG=08WOZ(10_?<?my$*#^tJNwnT!G^<(2)7^ zT^tpVxqMMPLw(xPLJOJr*=qD|F)_f16n9um!xn7zvjdUJ$uY_x*OVVt1q zgI$$FpH{WdkUc z7RND+1}M7xLP0|9Cjn>U{dz};K>(3#_NMj4YjgLq1+J5g;wU3jbpY+I*4D0SEyJLG zceu~PS==_(p8vnJ`OAaqK_8Ahk@azIZ@wVvxKRt3-*(J8ra7!|l~7c;6grCT2*F3? zF*d36fB&Xbq0{ zMfbVwoS*VCaSq@SxPtT)e#g+s!a+s%)%IsG{3!rokgqZv?;;GQ0%3~IfE9er=!#gc zc!d+apwO$xfL(_SiWk&EMRzgm zU9ZK!Ybp0<*Z+;Ku%6c`#s&X^{P42$u*Jc@_z%dOGs}p)Ih2D{j91%Tr?6Kx$Ad2Kn>0E-_$^R=yUU}zEduPa72qF$K8{@2j z@Bg_w&h;fWD7bR(%j&iF_pFMJeZP9&z1cer6SU)v+Y{!QBC8+huT++A{qg9^v5#xl z#EfrjeN=Y5t>ryk;b*JoEQx4}kKLv$`S!-$s}HVTXm0s%XL<9c=0Zdxeld+axM6x_ zU537NqAE(cIaGX8QnBZ0%c4U^+7~t-t3?}*habGYBRoVBIaShpzk&+|lWtZzqhl=FEwGW?VSeDA=1B6 zB>7pl3LF12;lO+0;xm~gpOA8iXI#^P!h=hTPpXdjm*iagXg5+Lo87p@zjJZLS0a%s z*!tL8$L!j5F{gO)rlG*6r*z<-W7-e24g5Bw@_I;JZe-&vTS{x~pQdF5i>hv}Z(Ka* zmg`8~$)T|gQ-dq^Ph7e`rivtk?BfcS7VoM>V-L@$P^)p{%O{s1YY?+(uJK6x@^$|O Dj#rfg literal 0 HcmV?d00001 diff --git a/psychopy_eyetracker_pupil_labs/light/apriltag_frame.png b/psychopy_eyetracker_pupil_labs/light/apriltag_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..6b50e93c5b959964e2b4372735e9e0262d4a5519 GIT binary patch literal 4873 zcmeHLe^3)w9^XI(io&5%DHX4K!CzQ5+1)H5SqO>*BtR4~0?G{MW_JS%B%3A+37oa! zk5(#ED|lLe6g#K`ZQ-u86h|3Pta4Bts%Lw6p68F^-Hp;J*SV`_ZNYoH2?55=+%QxB zV>2)Bz3=;eKkt1%-|zdvrY3vkijd$Jf*}YBF{JBrK#z(oC;)ut`F;O71j)kLe1pYc zSONtB*>oUy29kO8WaGwuGJnV)$m>80mH9!lK>s#qvw&RG7uyT6X;S|_7|->NKLX>$ zULDaM1GXWcKM5KR#^Ipv1&w>%v+v))SbTz{Pl|)31eSqaoKOs}Fc`C;A7db>0IZMp zq7cRv7^+Zd03wPLB&s2C9LC~F0wd!H$Paq0E)a?YHjhA)y*qYZF!eo8?&SXRpcUwE z<5E(x4Jj$G-N9S9G8Tf|hhEvOOFz3Ts{7Xk$5NMs=FVK*oBhr=jq*1?2~KSeE7_hq zFH14JWnqZnjh#nst+)S+ZX3Kc(DK3758$9pn>7iVn3L%>k28Z2bE4*cPv>uTb-Fg6 zUQNA!^|?S(AAb3>ZEy9p2A|2>hVGEn%I9Ni4$tlmPwcs(+qm)6&TnIfoUguFxPZsI z7KQcfx8^BBla!XpuSB-pXFDzL1PqJ_3{I(qnc7(x4Uc;As7(6hi4w&avir&C6|rCi#FenxHj^<|CY{WAKR|o z?CH3kDD%76_1pdjd2_qIS#(?0+NC%qE_K@}x|9`QGh4*jbn>Cj({h+&bn?77BWkp#u*F<@ zm4jVVwbDdamC{;9zBDN~(MYLgwhxybxfflNSquom47aE|Xm=vgQ2;Oqj&+h7rsGV|qvPA&)Q@aXxhcB9b;Z*zKC0DLIjlwFA_ zP^HzX9B<(iQY!$6_dq{s;WSm+S!E9EIA!D6SfrZrO!g7pop{DB`ZMo1WkdnjFC0rY~qUK&iI1>b06*m?NQ$)WuRp=l6syl z7msJq>*V78B*W7jLrO!fnbD#Yh9k6@ibn_wqe9G-)`G;xVOo_&MKP3$8V6;tIR(l_ zvmz7#S8xC)UWJ-576ej_s?7*N6LE+ZQ)v+vqiD4SLoHgfdK|wDwPFc;%SXqqrq4z-UG$ZWE$_VQXrh1l`3MDc3Y9Ohe$Y? zoNds_aRoXi$u6S=3oy{hvp8G1du+kPS=luLC9;Xds;311eqve zN)=Wz7sEmVX8^I3n5O_hk^^5zsf_Xt6VI3FU!IFtah ziRez=BDg3An_L8V1YCjil>Uyvi={xtOw@K2vtkqgVF;>0aNJ86hI$E8K7AK8D524+ zLFr;~nnnngzz{7C{M4E)j0Iz8g1|g^;3M#7aWM{fEaimP#YCl87Dt1csC**q9))pr z6j0-lf%*)}z0z0jy_|vbKF!CQZclRy7@pc>Qv6QQHAUB?7?_muRCZ0#H7N!rr972g zKO0@aV{eSC4g90$0`G$(!!?EAZ8E@|xk3*;7C-NvYN`j4XYJ|hoDigl7Msj{Y-t4$ z1_}maYT%uqInTwY2QJN>4@8kdYQB)dTg4X-=!u^d@~T54z^@AH4fBZtBmQ^nRm5ljM6r}@Pq>{TJcH$fq=hOHoTQo+)|g5q5tw8(TlZqoxc|m zu*+lg$hn+v~=x!FXrr? zu=-_paswV@)b}i?ue;eB_0;N@2|_a@QXdG7qw^I?t8xdoaLo|C(PKS4~fXEab)vh zzmi{@U7EHjV%Fux1AB8aiksp#ocd~U@0FOBI==Ym$c?i(0o8EWGa`7Vmj1ReLtDKz zZC6`^s`hwV|GsjO*K5 zRq}ajxbayozH+2w^ZIkHzx>`bxVztEZfKJAQX@xb*27y%QtNK EAEDxjwg3PC literal 0 HcmV?d00001 diff --git a/psychopy_eyetracker_pupil_labs/light/apriltag_frame@2x.png b/psychopy_eyetracker_pupil_labs/light/apriltag_frame@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..18b56f28dee3d6f9a259f26336ab84708f8611b1 GIT binary patch literal 4817 zcmeHKd2kcg8DGgZ7B+`TfZ#yOB6FER(rUG`Rzd|8tbUEwq(lT zU_#9inmPmn3D|?fU=A|`2Oqcuhd>~dBaksP2`wI{!%#zUJAf&Nq2I3WA(;*{&3|6a z(|hlG-}igp_j}*_q}`Y@ch=yrx5E$w8Ej58&WCtjAxNc0aG~WNJA&0`%9j2?;6Y zgap*%b=%n-20;QJul}PU@ypmTXMeMJpJ~dF)M4|^r|iDBSGx6hn5iNn``y%ECCS^@ zO&o0Aw7I-#k>_9Zr!7s*hYuAVLM3Yo$Z2HsiNuXB=Y+{J_2m6?^}qW=y}#hpJnG3HRW(l^fz*Ppv#Shecp=6_FZ^{w5yY=Rqam=tkt zrz`!;=rgL1wqMRKB~Q;Tj$bubPkj@zacrHu{`psRcaD}iW^PGZxrdCOI(%CC{pG2+ zwV7?M*qgnr^~#CEp1(C8PR%}?oBZOmXW8P6+qz4`#(!Egp(p|^-+)g!8d1J5>a1~B z+nc|BL7h_+&zYDOxn)dvMf|&e|IZgMoBTC)D*KuxY*NAfw|;mrDrjAOY?bSWTj#3( zsuzi`G<y`=DXDsTSX<)8ov2J9$6d}lJzt^q^W(&=h2}L zZ;1^nT<*%%A5TXjTc+d&jTs~U(R5+if;EPB~?BXEO4D(<7Xten!AU(JL4Z&14*`%OGv7KP5$3n?ahUwqO=d0+YoiuJkes zR?fB3D_783n{>wXFnvG=08WOZ(10_?<?my$*#^tJNwnT!G^<(2)7^ zT^tpVxqMMPLw(xPLJOJr*=qD|F)_f16n9um!xn7zvjdUJ$uY_x*OVVt1q zgI$$FpH{WdkUc z7RND+1}M7xLP0|9Cjn>U{dz};K>(3#_NMj4YjgLq1+J5g;wU3jbpY+I*4D0SEyJLG zceu~PS==_(p8vnJ`OAaqK_8Ahk@azIZ@wVvxKRt3-*(J8ra7!|l~7c;6grCT2*F3? zF*d36fB&Xbq0{ zMfbVwoS*VCaSq@SxPtT)e#g+s!a+s%)%IsG{3!rokgqZv?;;GQ0%3~IfE9er=!#gc zc!d+apwO$xfL(_SiWk&EMRzgm zU9ZK!Ybp0<*Z+;Ku%6c`#s&X^{P42$u*Jc@_z%dOGs}p)Ih2D{j91%Tr?6Kx$Ad2Kn>0E-_$^R=yUU}zEduPa72qF$K8{@2j z@Bg_w&h;fWD7bR(%j&iF_pFMJeZP9&z1cer6SU)v+Y{!QBC8+huT++A{qg9^v5#xl z#EfrjeN=Y5t>ryk;b*JoEQx4}kKLv$`S!-$s}HVTXm0s%XL<9c=0Zdxeld+axM6x_ zU537NqAE(cIaGX8QnBZ0%c4U^+7~t-t3?}*habGYBRoVBIaShpzk&+|lWtZzqhl=FEwGW?VSeDA=1B6 zB>7pl3LF12;lO+0;xm~gpOA8iXI#^P!h=hTPpXdjm*iagXg5+Lo87p@zjJZLS0a%s z*!tL8$L!j5F{gO)rlG*6r*z<-W7-e24g5Bw@_I;JZe-&vTS{x~pQdF5i>hv}Z(Ka* zmg`8~$)T|gQ-dq^Ph7e`rivtk?BfcS7VoM>V-L@$P^)p{%O{s1YY?+(uJK6x@^$|O Dj#rfg literal 0 HcmV?d00001 diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/components.py b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py new file mode 100644 index 0000000..1b17b57 --- /dev/null +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py @@ -0,0 +1,212 @@ +from pathlib import Path + +from psychopy.experiment.components import BaseVisualComponent, Param, getInitVals +from psychopy.localization import _translate + + +class AprilTagComponent(BaseVisualComponent): + targets = ['PsychoPy'] + categories = ['Eyetracking'] + iconFile = Path(__file__).parent.parent / 'apriltag.png' + tooltip = _translate('AprilTag: Markers to identify a screen surface') + + _instances = [] + _routine_start_written = False + + def __init__(self, exp, parentName, marker_id=0, anchor="center", *args, **kwargs): + super().__init__(exp, parentName, *args, **kwargs) + + self.type = 'Image' + self.url = "https://april.eecs.umich.edu/software/apriltag.html" + self.exp.requirePsychopyLibs(['visual']) + self.exp.requireImport('AprilTagStim', 'psychopy_eyetracker_pupil_labs.pupil_labs') + self.exp.requireImport('convertToPix', 'psychopy.tools.monitorunittools') + + self.order += ['marker_id'] + + self.params['marker_id'] = Param(marker_id, + valType='int', inputType="spin", categ='Basic', + updates='constant', allowedVals=[0, 512], + allowedUpdates=['constant', 'set every repeat', 'set every frame'], + hint=_translate("The ID of the AprilTag marker to display"), + label=_translate("Marker ID") + ) + + self.params['anchor'] = Param( + anchor, valType='str', inputType="choice", categ='Layout', + allowedVals=['center', + 'top-center', + 'bottom-center', + 'center-left', + 'center-right', + 'top-left', + 'top-right', + 'bottom-left', + 'bottom-right', + ], + updates='constant', + hint=_translate("Which point on the stimulus should be anchored to its exact position?"), + label=_translate("Anchor") + ) + + del self.params['color'] + del self.params['colorSpace'] + del self.params['fillColor'] + del self.params['borderColor'] + del self.params['opacity'] + del self.params['ori'] + + self.marker_id = marker_id + AprilTagComponent._instances.append(self) + + def writeInitCode(self, buff): + AprilTagComponent._routine_start_written = False + + # replace variable params with defaults + inits = getInitVals(self.params, 'PsychoPy') + code = ("{inits[name]} = AprilTagStim(\n" + " win=win,\n" + " name='{inits[name]}', units={inits[units]},\n" + " contrast={inits[contrast]},\n" + " marker_id=int({inits[marker_id]}), anchor={inits[anchor]},\n" + " pos={inits[pos]}, size={inits[size]}" + .format(inits=inits)) + + depth = -self.getPosInRoutine() + code += ", depth=%.1f)\n" % depth + + buff.writeIndentedLines(code) + + def writeRoutineStartCode(self, buff): + """Write the code that will be called at the beginning of + a routine (e.g. to update stimulus parameters) + """ + if AprilTagComponent._routine_start_written: + return + + code = ("if eyetracker is not None and hasattr(eyetracker, 'register_surface'):\n" + " tag_verts = {\n") + + routine = self.exp.routines[self.parentName] + tag_comps = [comp for comp in routine if not comp == routine.settings] + tag_comps = filter(lambda comp: isinstance(comp, AprilTagComponent), tag_comps) + + for component in tag_comps: + inits = getInitVals(component.params, 'PsychoPy') + code += " str({inits[name]}.marker_id): {inits[name]}.get_marker_verts(),\n".format(inits=inits) + + code += " }\n" + code += " win_size_pix = convertToPix(np.array([2, 2]), [0, 0], 'norm', win)\n" + code += " eyetracker.register_surface(tag_verts, win_size_pix)\n" + buff.writeIndentedLines(code) + + AprilTagComponent._routine_start_written = True + + +class AprilTagFrameComponent(BaseVisualComponent): + targets = ['PsychoPy'] + categories = ['Eyetracking'] + iconFile = Path(__file__).parent.parent / 'apriltag_frame.png' + tooltip = _translate('AprilTag: Markers to identify a screen surface') + + def __init__(self, exp, parentName, h_count=3, v_count=3, marker_ids='', marker_size=0.125, marker_units="from exp settings", anchor="center", size=[2, 2], units="norm", *args, **kwargs): + super().__init__(exp, parentName, size=size, units=units, *args, **kwargs) + + self.type = 'Image' + self.url = "https://april.eecs.umich.edu/software/apriltag.html" + self.exp.requirePsychopyLibs(['visual']) + self.exp.requireImport('AprilTagFrameStim', 'psychopy_eyetracker_pupil_labs.pupil_labs.stimuli') + self.exp.requireImport('convertToPix', 'psychopy.tools.monitorunittools') + + self.params['h_count'] = Param(h_count, + valType='int', inputType="spin", categ='Basic', + updates='constant', allowedVals=[0, 64], + allowedUpdates=['constant'], + hint=_translate("The number of AprilTag markers to display along the horizontal edges of the display"), + label=_translate("Horizontal Count")) + + self.params['v_count'] = Param(v_count, + valType='int', inputType="spin", categ='Basic', + updates='constant', allowedVals=[0, 64], + allowedUpdates=['constant'], + hint=_translate("The number of AprilTag markers to display along the vertical edges of the display"), + label=_translate("Vertical Count")) + + self.params['marker_ids'] = Param(marker_ids, + valType='str', categ='Basic', + updates='constant', + allowedUpdates=['constant'], + hint=_translate("The IDs of the AprilTag marker to display"), + label=_translate("Marker IDs")) + + self.params['marker_size'] = Param(marker_size, + valType='int', inputType="single", categ='Layout', + updates='constant', allowedTypes=[], + allowedUpdates=['constant'], + hint=_translate("The size of each AprilTag marker"), + label=_translate("Marker size [w,h]")) + + self.params['marker_units'] = Param(marker_units, + valType='str', inputType="choice", categ='Layout', + allowedVals=['from exp settings', 'deg', 'cm', 'pix', 'norm', + 'height', 'degFlatPos', 'degFlat'], + hint=_translate("Marker size spatial units"), + label=_translate("Marker size spatial units")) + + self.params['anchor'] = Param( + anchor, valType='str', inputType="choice", categ='Layout', + allowedVals=['center', + 'top-center', + 'bottom-center', + 'center-left', + 'center-right', + 'top-left', + 'top-right', + 'bottom-left', + 'bottom-right', + ], + updates='constant', + hint=_translate("Which point on the stimulus should be anchored to its exact position?"), + label=_translate("Anchor")) + + del self.params['color'] + del self.params['colorSpace'] + del self.params['fillColor'] + del self.params['borderColor'] + del self.params['opacity'] + del self.params['ori'] + + def writeInitCode(self, buff): + inits = getInitVals(self.params, 'PsychoPy') + if inits['marker_ids'] in ('', 'None'): + marker_count = 2 * (int(inits['h_count'].val) + int(inits['v_count'].val)) - 4 + marker_ids = list(range(marker_count)) + else: + marker_ids = [v.strip() for v in inits['marker_ids'].val.split(',')] + + if inits['marker_units'].val == 'from exp settings': + marker_units = self.exp.settings.params['Units'] + else: + marker_units = inits['marker_units'] + + code = (f"{inits['name']} = AprilTagFrameStim(\n" + f" win=win,\n" + f" name='{inits['name']}', units={inits['units']},\n" + f" contrast={inits['contrast']},\n" + f" h_count={inits['h_count']}, v_count={inits['v_count']},\n" + f" marker_ids={marker_ids}, anchor={inits['anchor']},\n" + f" marker_size={inits['marker_size']}, marker_units={marker_units},\n" + f" pos={inits['pos']}, size={inits['size']})") + + buff.writeIndentedLines(code) + + def writeRoutineStartCode(self, buff): + """Write the code that will be called at the beginning of + a routine (e.g. to update stimulus parameters) + """ + inits = getInitVals(self.params, 'PsychoPy') + code = (f"if eyetracker is not None and hasattr(eyetracker, 'register_surface'):\n" + f" win_size_pix = convertToPix(np.array([2, 2]), [0, 0], 'norm', win)\n" + f" eyetracker.register_surface({inits['name']}.marker_verts, win_size_pix)\n") + + buff.writeIndentedLines(code) diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py b/psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py new file mode 100644 index 0000000..304de80 --- /dev/null +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py @@ -0,0 +1,123 @@ +import numpy as np +import cv2 + +from psychopy.visual import ImageStim +from psychopy.tools.monitorunittools import convertToPix + +from pupil_labs.real_time_screen_gaze import marker_generator + + +class AprilTagStim(ImageStim): + def __init__(self, marker_id=0, contrast=1.0, *args, **kwargs): + self.marker_id = marker_id + + marker_data = marker_generator.generate_marker(marker_id, flip_x=True).astype(float) + marker_data[marker_data == 0] = -contrast + marker_data[marker_data > 0] = contrast + + marker_data = np.pad(marker_data, pad_width=1, mode="constant", constant_values=contrast) + + super().__init__(image=marker_data, *args, **kwargs) + + def get_marker_verts(self): + vertices_in_pixels = self._vertices.pix + size_with_margin = ( + abs(vertices_in_pixels[1][0] - vertices_in_pixels[0][0]), + abs(vertices_in_pixels[2][1] - vertices_in_pixels[0][1]) + ) + size_without_margin = [v * 0.8 for v in size_with_margin] + padding = size_with_margin[0] * 0.1 + + top_left_pos = vertices_in_pixels[2] + + top_left = ( + top_left_pos[0] + padding, + top_left_pos[1] - padding + ) + bottom_right = ( + top_left[0] + size_without_margin[0], + top_left[1] - size_without_margin[1] + ) + + return ( + top_left, + (bottom_right[0], top_left[1]), + bottom_right, + (top_left[0], bottom_right[1]), + ) + + +class AprilTagFrameStim(ImageStim): + def __init__(self, h_count=3, v_count=3, marker_ids=None, marker_size=0.125, marker_units='', contrast=1.0, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.marker_verts = {} + + win_size_pix = convertToPix(np.array([2, 2]), [0, 0], 'norm', self.win).astype(int) + marker_size_pix = convertToPix(np.array([marker_size, marker_size]), [0, 0], marker_units, self.win).astype(int) + marker_padding = marker_size_pix[0] / 10 + image_data = np.zeros((win_size_pix[1], win_size_pix[0], 4)) + + marker_positions_pix = self._frame_positions(win_size_pix, marker_size_pix[0], [h_count, v_count]).astype(int) + + if marker_ids is None: + marker_count = 2 * (h_count + v_count) - 4 + marker_ids = list(range(marker_count)) + + self.marker_ids = marker_ids + + for marker_id, position_pix in zip(marker_ids, marker_positions_pix): + marker_data = marker_generator.generate_marker(marker_id, flip_x=True) + marker_data = np.pad(marker_data, pad_width=1, mode="constant", constant_values=255) + marker_data = cv2.cvtColor(marker_data.astype(np.float32), cv2.COLOR_GRAY2RGBA) + marker_data[:, :, 3] = 255 + marker_data = cv2.resize(marker_data, marker_size_pix, fx=0, fy=0, interpolation=cv2.INTER_NEAREST) + + top_left = position_pix + w = marker_size_pix[0] + bottom_right = [top_left[0] + w, top_left[1] + w] + + image_data[top_left[1]: bottom_right[1], top_left[0]: bottom_right[0]] = marker_data + + # save marker verts for surface registration + top_left = [v + marker_padding for v in top_left] + bottom_right = [v - marker_padding for v in bottom_right] + + self.marker_verts[marker_id] = ( + top_left, + (bottom_right[0], top_left[1]), + bottom_right, + (top_left[0], bottom_right[1]), + ) + + # Convert to psychopy color space + image_data[image_data > 0] = 0.5 + contrast / 2 + image_data[image_data == 0] = 0.5 - contrast / 2 + + self.image = image_data + + def _frame_grid(self, h_count, v_count): + counts = (h_count, v_count) + direction_offsets = [ + [1, 0], + [0, 1], + [-1, 0], + [0, -1], + ] + positions = [[0, 0]] + + for direction_idx, offset in enumerate(direction_offsets): + for count in range(counts[direction_idx % 2] - 1): + last_pos = positions[-1] + p = [v[0] + v[1] for v in zip(last_pos, offset)] + positions.append(p) + + return positions[:-1] + + def _frame_positions(self, frame_size, marker_size, grid_counts): + spacing = np.array([ + (frame_size[axis] - marker_size) / (grid_counts[axis] - 1) + for axis in [0, 1] + ]) + + return spacing * np.array(self._frame_grid(*grid_counts)) diff --git a/pyproject.toml b/pyproject.toml index 4ba078a..543d33b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,4 +63,5 @@ NeonEyeTracker = "psychopy_eyetracker_pupil_labs.pupil_labs.neon.eyetracker:Neon PupilCoreEyeTracker = "psychopy_eyetracker_pupil_labs.pupil_labs.pupil_core.eyetracker:PupilCoreEyeTracker" [project.entry-points."psychopy.experiment.components"] -AprilTagComponent = "psychopy_eyetracker_pupil_labs:AprilTagComponent" +AprilTagComponent = "psychopy_eyetracker_pupil_labs.pupil_labs.components:AprilTagComponent" +AprilTagFrameComponent = "psychopy_eyetracker_pupil_labs.pupil_labs.components:AprilTagFrameComponent" From 23047e01f9953c54833d9e2d65ce94b081dc7e5c Mon Sep 17 00:00:00 2001 From: Dominic Canare Date: Mon, 2 Sep 2024 15:30:58 -0400 Subject: [PATCH 2/5] Fix import --- psychopy_eyetracker_pupil_labs/pupil_labs/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/components.py b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py index 1b17b57..0fe1f29 100644 --- a/psychopy_eyetracker_pupil_labs/pupil_labs/components.py +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py @@ -19,7 +19,7 @@ def __init__(self, exp, parentName, marker_id=0, anchor="center", *args, **kwarg self.type = 'Image' self.url = "https://april.eecs.umich.edu/software/apriltag.html" self.exp.requirePsychopyLibs(['visual']) - self.exp.requireImport('AprilTagStim', 'psychopy_eyetracker_pupil_labs.pupil_labs') + self.exp.requireImport('AprilTagStim', 'psychopy_eyetracker_pupil_labs.pupil_labs.stimuli') self.exp.requireImport('convertToPix', 'psychopy.tools.monitorunittools') self.order += ['marker_id'] From acead613c369d452ee47a0421d89c63f51c0fdb3 Mon Sep 17 00:00:00 2001 From: Dominic Canare Date: Mon, 2 Sep 2024 15:31:20 -0400 Subject: [PATCH 3/5] Fix bug introduced from multimon setup --- psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py b/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py index 285cb4f..c35afb0 100644 --- a/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py @@ -277,7 +277,7 @@ def _poll(self): gaze_in_pix = [ surface_gaze.x * self._window_size[0], - (surface_gaze.y + 1.0) * self._window_size[1] + surface_gaze.y * self._window_size[1], ] gaze_in_display_units = self._eyeTrackerToDisplayCoords(gaze_in_pix) From af9c8c60f61477be0095f4f011205ee62305d946 Mon Sep 17 00:00:00 2001 From: Dominic Canare Date: Mon, 2 Sep 2024 19:13:20 -0400 Subject: [PATCH 4/5] working tagframe --- .../pupil_labs/components.py | 2 +- .../pupil_labs/neon/eyetracker.py | 9 ++++++--- .../pupil_labs/stimuli.py | 16 +++++++++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/components.py b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py index 0fe1f29..113e95e 100644 --- a/psychopy_eyetracker_pupil_labs/pupil_labs/components.py +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py @@ -93,7 +93,7 @@ def writeRoutineStartCode(self, buff): for component in tag_comps: inits = getInitVals(component.params, 'PsychoPy') - code += " str({inits[name]}.marker_id): {inits[name]}.get_marker_verts(),\n".format(inits=inits) + code += " str({inits[name]}.marker_id): {inits[name]}.marker_verts,\n".format(inits=inits) code += " }\n" code += " win_size_pix = convertToPix(np.array([2, 2]), [0, 0], 'norm', win)\n" diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py b/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py index c35afb0..de42ee8 100644 --- a/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/neon/eyetracker.py @@ -4,13 +4,12 @@ # Distributed under the terms of the GNU General Public License (GPL). import logging from typing import Optional, Dict, Tuple, Union -import json from psychopy.iohub.constants import EyeTrackerConstants from psychopy.iohub.devices import Computer, Device from psychopy.iohub.devices.eyetracker import EyeTrackerDevice from psychopy.iohub.errors import printExceptionDetailsToStdErr -from psychopy.iohub.constants import EventConstants, EyeTrackerConstants +from psychopy.iohub.constants import EventConstants from pupil_labs.realtime_api.simple import Device as CompanionDevice from pupil_labs.real_time_screen_gaze.gaze_mapper import GazeMapper @@ -191,7 +190,11 @@ def setRecordingState(self, should_be_recording: bool) -> bool: if should_be_recording: self._device.recording_start() else: - self._device.recording_stop_and_save() + try: + self._device.recording_stop_and_save() + except Exception as exc: + logging.error(f"Failed to stop recording: {exc}") + printExceptionDetailsToStdErr() self._actively_recording = should_be_recording diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py b/psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py index 304de80..b498e27 100644 --- a/psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/stimuli.py @@ -19,7 +19,8 @@ def __init__(self, marker_id=0, contrast=1.0, *args, **kwargs): super().__init__(image=marker_data, *args, **kwargs) - def get_marker_verts(self): + @property + def marker_verts(self): vertices_in_pixels = self._vertices.pix size_with_margin = ( abs(vertices_in_pixels[1][0] - vertices_in_pixels[0][0]), @@ -83,11 +84,16 @@ def __init__(self, h_count=3, v_count=3, marker_ids=None, marker_size=0.125, mar top_left = [v + marker_padding for v in top_left] bottom_right = [v - marker_padding for v in bottom_right] - self.marker_verts[marker_id] = ( - top_left, - (bottom_right[0], top_left[1]), - bottom_right, + top_left[0] -= win_size_pix[0] / 2 + top_left[1] -= win_size_pix[1] / 2 + bottom_right[0] -= win_size_pix[0] / 2 + bottom_right[1] -= win_size_pix[1] / 2 + + self.marker_verts[str(marker_id)] = ( (top_left[0], bottom_right[1]), + bottom_right, + (bottom_right[0], top_left[1]), + top_left, ) # Convert to psychopy color space From b672fe73875c21ab128a3cc5a76b5bcd8d011f15 Mon Sep 17 00:00:00 2001 From: Dominic Canare Date: Mon, 2 Sep 2024 19:27:31 -0400 Subject: [PATCH 5/5] Better defaults --- psychopy_eyetracker_pupil_labs/pupil_labs/components.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/psychopy_eyetracker_pupil_labs/pupil_labs/components.py b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py index 113e95e..8ef3da4 100644 --- a/psychopy_eyetracker_pupil_labs/pupil_labs/components.py +++ b/psychopy_eyetracker_pupil_labs/pupil_labs/components.py @@ -13,8 +13,8 @@ class AprilTagComponent(BaseVisualComponent): _instances = [] _routine_start_written = False - def __init__(self, exp, parentName, marker_id=0, anchor="center", *args, **kwargs): - super().__init__(exp, parentName, *args, **kwargs) + def __init__(self, exp, parentName, marker_id=0, anchor="center", size=(0.2, 0.2), startType='time (s)', startVal=0.0, *args, **kwargs): + super().__init__(exp, parentName, size=size, startType=startType, startVal=startVal, *args, **kwargs) self.type = 'Image' self.url = "https://april.eecs.umich.edu/software/apriltag.html" @@ -109,8 +109,8 @@ class AprilTagFrameComponent(BaseVisualComponent): iconFile = Path(__file__).parent.parent / 'apriltag_frame.png' tooltip = _translate('AprilTag: Markers to identify a screen surface') - def __init__(self, exp, parentName, h_count=3, v_count=3, marker_ids='', marker_size=0.125, marker_units="from exp settings", anchor="center", size=[2, 2], units="norm", *args, **kwargs): - super().__init__(exp, parentName, size=size, units=units, *args, **kwargs) + def __init__(self, exp, parentName, h_count=3, v_count=3, marker_ids='', marker_size=0.125, marker_units="from exp settings", anchor="center", size=[2, 2], units="norm", startType='time (s)', startVal=0.0, *args, **kwargs): + super().__init__(exp, parentName, size=size, units=units, startType=startType, startVal=startVal, *args, **kwargs) self.type = 'Image' self.url = "https://april.eecs.umich.edu/software/apriltag.html"