Skip to content

Commit

Permalink
Merge pull request #2140 from pupil-labs/export-surface-tracker-marke…
Browse files Browse the repository at this point in the history
…r-detections

Export surface tracker marker detections
  • Loading branch information
papr authored May 12, 2021
2 parents 87e2c99 + f0d43c2 commit c6efbce
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 1 deletion.
4 changes: 3 additions & 1 deletion pupil_src/shared_modules/file_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def __init__(self, file_path, *args, **kwargs):
super().__init__(*args, **kwargs)
self.file_path = os.path.expanduser(file_path)
try:
self.update(**load_object(self.file_path, allow_legacy=False))
if os.path.getsize(file_path) > 0:
# Only try to load object if file is not empty
self.update(**load_object(self.file_path, allow_legacy=False))
except IOError:
logger.debug(
f"Session settings file '{self.file_path}' not found."
Expand Down
59 changes: 59 additions & 0 deletions pupil_src/shared_modules/surface_tracker/background_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

import background_helper
import player_methods
import file_methods

from .surface_marker import Surface_Marker


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -221,6 +225,7 @@ def get_export_proxy(
gaze_positions,
fixations,
camera_model,
marker_cache_path,
mp_context,
):
exporter = Exporter(
Expand All @@ -231,6 +236,7 @@ def get_export_proxy(
gaze_positions,
fixations,
camera_model,
marker_cache_path,
)
proxy = background_helper.IPC_Logging_Task_Proxy(
"Offline Surface Tracker Exporter",
Expand All @@ -250,6 +256,7 @@ def __init__(
gaze_positions,
fixations,
camera_model,
marker_cache_path,
):
self.export_range = export_range
self.metrics_dir = os.path.join(export_dir, "surfaces")
Expand All @@ -260,6 +267,7 @@ def __init__(
self.camera_model = camera_model
self.gaze_on_surfaces = None
self.fixations_on_surfaces = None
self.marker_cache_path = marker_cache_path

def save_surface_statisics_to_file(self):
logger.info("exporting metrics to {}".format(self.metrics_dir))
Expand Down Expand Up @@ -298,6 +306,17 @@ def save_surface_statisics_to_file(self):
"Saved surface gaze and fixation data for '{}'".format(surface.name)
)

# Cleanup surface related data to release memory
self.surfaces = None
self.fixations = None
self.gaze_positions = None
self.gaze_on_surfaces = None
self.fixations_on_surfaces = None

# Perform marker export *after* surface data is released
# to avoid holding everything in memory all at once.
self._export_marker_detections()

logger.info("Done exporting reference surface data.")
return

Expand Down Expand Up @@ -338,6 +357,46 @@ def _map_gaze_and_fixations(self):

return gaze_on_surface, fixations_on_surface

def _export_marker_detections(self):

# Load the temporary marker cache created by the offline surface tracker
marker_cache = file_methods.Persistent_Dict(self.marker_cache_path)
marker_cache = marker_cache["marker_cache"]

try:
file_path = os.path.join(self.metrics_dir, "marker_detections.csv")
with open(file_path, "w", encoding="utf-8", newline="") as csv_file:
csv_writer = csv.writer(csv_file, delimiter=",")
csv_writer.writerow(
(
"world_index",
"marker_uid",
"corner_0_x",
"corner_0_y",
"corner_1_x",
"corner_1_y",
"corner_2_x",
"corner_2_y",
"corner_3_x",
"corner_3_y",
)
)
for idx, serialized_markers in enumerate(marker_cache):
for m in map(Surface_Marker.deserialize, serialized_markers):
flat_corners = [x for c in m.verts_px for x in c[0]]
assert len(flat_corners) == 8 # sanity check
csv_writer.writerow(
(
idx,
m.uid,
*flat_corners,
)
)
finally:
# Delete the temporary marker cache created by the offline surface tracker
os.remove(self.marker_cache_path)
self.marker_cache_path = None

def _export_surface_visibility(self):
with open(
os.path.join(self.metrics_dir, "surface_visibility.csv"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import multiprocessing
import os
import platform
import tempfile
import time
import typing as T

Expand Down Expand Up @@ -559,6 +560,16 @@ def on_notify(self, notification):
break

elif notification["subject"] == "should_export":
# Create new marker cache temporary file
# Backgroud exporter is responsible of removing the temporary file when finished
file_handle, marker_cache_path = tempfile.mkstemp()
os.close(file_handle) # https://bugs.python.org/issue42830

# Save marker cache into the new temporary file
temp_marker_cache = file_methods.Persistent_Dict(marker_cache_path)
temp_marker_cache["marker_cache"] = self.marker_cache
temp_marker_cache.save()

proxy = background_tasks.get_export_proxy(
notification["export_dir"],
notification["range"],
Expand All @@ -567,6 +578,7 @@ def on_notify(self, notification):
self.g_pool.gaze_positions,
self.g_pool.fixations,
self.camera_model,
marker_cache_path,
mp_context,
)
self.export_proxies.add(proxy)
Expand Down

0 comments on commit c6efbce

Please sign in to comment.