Closed ChiwoongLEE closed 2 years ago
and I think it is related to two heat map modes, but I can select the mode on the pupil player, but it is impossible to select the mode on the pupil capture.
Is there a way to select the mode in the pupil capture?
@ChiwoongLEE Hi! For every detected surface, the surface tracker plugin will map the main scene video gaze into each corresponding surface coordinate system. If both x and y norma pos values of a given surface-mapped gaze datum are between 0 and 1 then you know that that gaze point is on the given surface.
Check out Player's gaze_on_surface<name>.csv
export files. It has a on_surf column indicating wether gaze is on a surface <name>
or not.
If this did not answer your question, please provide a visualization/screenshot of the issue.
image url below:
I checked about "on_sulf" through the csv file. However, in the world process, the gaze point is pointed to the middle screen, while the csv file recognizes the left surface.
I want to make sure that when the gaze point recognizes the center, the surface also recognizes the center.
while the csv file recognizes the left surface.
Which csv file are you referring to in this case?
Gaze_positions_on_surface.csv.
I am currently developing a plug-in that extracts the surface datum's norm_pos and surface name in real time.
In https://github.com/pupil-labs/pupil/issues/2246 you have been able to extract the norm_pos for each surface already, correct?
The only thing missing to check if gaze is on the corresponding surface is to check its values: If:
(0.0 <= norm_pos[0] <= 1.0) and (0.0 <= norm_pos[1] <= 1.0)
then the gaze point is on the surface.
Note: Since gaze points have a higher frame rate than the scene video you might encounter cases where one gaze point is on one surface, and another point on another surface, e.g. during a saccade.
I would also recommend discarding low confidence gaze points (confidence < 0.6).
I understood what you explained above.
What I want is to get information about surface1 when my gaze looks at surface1, but if there are three surfaces as shown in the attached screenshot, the surface is randomly recognized regardless of the point of view
It's part of the code under development.
try:
if 'gaze_on_surfaces' in surfaces[0]:
if surfaces[0]['name']=='left':
if surfaces[0]['gaze_on_surfaces'][0]['confidence']>0.6 and surfaces[0]['gaze_on_surfaces'][0]['on_surf']==True:
surface_name='left'
norm_gp_x= str(1920*surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][0])
norm_gp_y= str(1080*(1-surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][1]))
elif surfaces[0]['name']=='mid':
if surfaces[0]['gaze_on_surfaces'][0]['confidence']>0.6 and surfaces[0]['gaze_on_surfaces'][0]['on_surf']==True:
surface_name='mid'
norm_gp_x= str(3840-(1920*surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][0]))
norm_gp_y= str(1080*(1-surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][1]))
elif surfaces[0]['name']=='right':
if surfaces[0]['gaze_on_surfaces'][0]['confidence']>0.6 and surfaces[0]['gaze_on_surfaces'][0]['on_surf']==True:
surface_name='right'
norm_gp_x= str(5760-(1920*surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][0]))
norm_gp_y= str(1080*(1-surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][1]))
Ah, there is a misunderstanding. Surfaces in surfaces
are not ordered by the fact that the subject is looking at them or not. Instead, they are ordered in definition order. You are always looking at the first surface with surfaces[0]
and therefore, your algorithm always returns the same surface name (if at all).
You might rather want something like this:
surface_subject_is_looking_at = []
for surface in surfaces:
for gaze in surface["gaze_on_surfaces"]:
if gaze['confidence'] > 0.6 and gaze['on_surf'] == True:
gaze_time_and_surface_name = (gaze["timestamp"], surface["name"])
surface_subject_is_looking_at.append(gaze_time_and_surface_name)
if gaze_time_and_surface_name:
gaze_time_and_surface_name.sort()
surface_name = gaze_time_and_surface_name[-1][1]
else:
surface_name = None
What this does is to check all gaze points (not just the first one) if they are on their corresponding surface. If this is the case, I store the gaze timestamp and the surface name in a tuple. When you sort a list of tuples in Python it will use the first element (in our case the gaze timestamp) to determine the element order. So after sorting, the newest element will be in the last position of the list. With gaze_time_and_surface_name[-1][1]
, we access the last object and the second field of that object: The surface name.
So even if the subject changes their gaze from one surface to the next within a single frame, the algorithm above will make sure to return the most up-to-date surface with gaze.
what is mean "c.append(gaze_time_and_surface_name)" ?? I understood the code you suggested above and corrected it as follows.
c=[]
if surfaces[0]['gaze_on_surfaces'][0]['confidence']>0.6 and surfaces[0]['gaze_on_surfaces'][0]['on_surf']==True:
gaze_time_and_surface_name=surfaces[0]['gaze_on_surfaces'][0]['timestamp'],surfaces[0]['name']#튜플로저장
gaze_time_and_surface_name=list(gaze_time_and_surface_name)
c.append(gaze_time_and_surface_name)
if gaze_time_and_surface_name:
c.sort()
surface_name=c[-1][1]
else:
surface_name=None
And as a result of checking the stored csv file, there was a problem that the first surface was mainly output like last time.
Ah, thanks for catching that. c
is meant to be surface_subject_is_looking_at
. Fixed it in the original code.
And as a result of checking the stored csv file, there was a problem that the first surface was mainly output like last time.
That is expected because you are not looping over all surfaces but, again, are only looking at the first detected surface (surfaces[0]
).
Yes, as a result of modifying the given code, there is still a problem that only the first surface 1 is output, even though there are all surfaces in the world frame. If I'm looking at surface 2 or 3, it's only output to None.
Can you clarify which code you are using?
This is the result of modifying the "def recent_event" part of the code of "surface_tracker.py" to suit my purpose.
def recent_events(self, events):
frame = events.get("frame")
self.current_frame = frame
if not frame:
return
self._update_markers(frame)
self._update_surface_locations(frame.index)
self._update_surface_corners()
#events["surfaces"] = self._create_surface_events(events, frame.timestamp)
events["surfaces"] = self._create_surface_events(events, frame.timestamp)
##
surfaces=events["surfaces"]
norm_gp_x=0.0 #초기값설정
norm_gp_y=0.0 #초기값설정
norm_pos = [0.0, 0.0]
confidence=0
try:
if self.gaze_flag:
try:
if 'gaze_on_surfaces' in surfaces[0]:
gaze_time_and_surface_name=()
c=[]
if surfaces[0]['gaze_on_surfaces'][0]['confidence']>0.6 and surfaces[0]['gaze_on_surfaces'][0]['on_surf']==True:
gaze_time_and_surface_name=surfaces[0]['gaze_on_surfaces'][0]['timestamp'],surfaces[0]['name']#튜플로저장
gaze_time_and_surface_name=list(gaze_time_and_surface_name)
#print(gaze_time_and_surface_name)
#a=sorted(gaze_time_and_surface_name)
#print(a)
c.append(gaze_time_and_surface_name)
if gaze_time_and_surface_name:
c.sort()
#gaze_time_and_surface_name.sort()
surface_name=c[-1][1]
else:
surface_name=None
if surface_name=='left':
norm_gp_x= str(1920*surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][0])
norm_gp_y= str(1080*(1-surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][1]))
elif surface_name=='mid':
norm_gp_x= str(3840-(1920*surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][0]))
norm_gp_y= str(1080*(1-surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][1]))
elif surface_name=='right':
norm_gp_x= str(5760-(1920*surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][0]))
norm_gp_y= str(1080*(1-surfaces[0]['gaze_on_surfaces'][0]['norm_pos'][1]))
with open("/home/kaai/pupil/recordings/gaze_point102.csv","a") as f:
while True:
f.write(str(c)+','+str(surface_name)+','+str(norm_gp_x)+ ',' +str(norm_gp_y) + '\n')
#f.write(str(norm_gp_x)+ ',' +str(norm_gp_y) + '\n')
if KeyboardInterrupt:
break
self.pub_socket.send("ROS_gaze:norm_pos:".encode() + ":".join(str(e) for e in str(norm_gp_x)).encode())
self.pub_socket.send("ROS_gaze:norm_pos:".encode() + ":".join(str(e) for e in str(norm_gp_y)).encode())
except IndexError:
#surfaces[0]['name']="OUT OF SIGHT"
norm_gp_x1= 0.0
norm_gp_y1= 0.0
norm_gp_x=str(norm_gp_x1)
norm_gp_y=str(norm_gp_y1)
with open("/home/kaai/pupil/recordings/gaze_point102.csv","a") as f:
while True:
#f.write(str(surfaces[0]['name'])+','+str(norm_gp_x)+ ',' +str(norm_gp_y) + '\n')
f.write('OUT OF SIGHT'+','+str(norm_gp_x)+ ',' +str(norm_gp_y) + '\n')
#f.write(str(norm_gp_x)+ ',' +str(norm_gp_y) + '\n')
if KeyboardInterrupt:
break
self.pub_socket.send("ROS_gaze:norm_pos:".encode() + ":".join(str(e) for e in norm_gp_x).encode())
self.pub_socket.send("ROS_gaze:norm_pos:".encode() + ":".join(str(e) for e in norm_gp_y).encode())
except KeyboardInterrupt:
self.pub_socket.close()
Please have a closer look at the surfaces
object. It is a list that contains all detected surfaces. This list can be empty (in which case you get the index error -> everything out of sight), have one, or multiple entries.
In any case, you are always looking at the first entry and ignoring all other with this code:
if 'gaze_on_surfaces' in surfaces[0]:
gaze_time_and_surface_name=()
c=[]
if surfaces[0]['gaze_on_surfaces'][0]['confidence']>0.6 and surfaces[0]['gaze_on_surfaces'][0]['on_surf']==True:
....
Note the difference between a for-loop
(looking at all entries, see my code example) and an if-statement (only looking at a single entry).
When you adjusted my code example, you removed the most important part: The for loop.
for surface in surfaces:
for gaze in surface["gaze_on_surfaces"]:
...
Thanks for your advice.
HI When three aoi regions defined using surface trackers are viewed in the world camera, for example, when surface1 is viewed, gaze point refers to surface1, but surface datum recognizes surface2.
In other words, if three defined surfaces are visible on one world camera, is there any way to clearly define the surface facing the gaze?
i use tag36h11 surface1: id 0,1,2,3 surface2: id 4,5,6,7 surface3: id 8,9,10,11