diff --git a/.gitignore b/.gitignore index 3295d3dee6..12ebe74b7e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ Thumbs.db # user generated files will be ignored if in directory named 'recordings...' # recordings/ settings/ +player_settings/ *.db *.npy *.png diff --git a/deploy/finalize_bundle_mac.py b/deploy/finalize_bundle_mac.py deleted file mode 100644 index db4ff3c6d8..0000000000 --- a/deploy/finalize_bundle_mac.py +++ /dev/null @@ -1,8 +0,0 @@ -import shutil -import write_version_file -print "starting version stript:" -write_version_file.main('dist/Pupil Capture.app/Contents/MacOS') -print "created version file in dist folder" - -print "copy starter app" -shutil.copytree('run_pupil_capture_from_mac_terminal.app', 'dist/run_pupil_capture_from_mac_terminal.app') \ No newline at end of file diff --git a/deploy/finalize_bundle_ubuntu.py b/deploy/finalize_bundle_ubuntu.py deleted file mode 100644 index 3e8cc7bf12..0000000000 --- a/deploy/finalize_bundle_ubuntu.py +++ /dev/null @@ -1,24 +0,0 @@ -import sys,os -import write_version_file -import shutil - -distribtution_dir = 'dist' -pupil_capture_dir = os.path.join(distribtution_dir, 'pupil_capture') - - -shutil.copy('patch_uvc_driver.sh',os.path.join(distribtution_dir,'patch_uvc_driver.sh')) -print "Copied a small script to patch uvc driver into the distribution dir" -os.chmod(os.path.join(distribtution_dir,'patch_uvc_driver.sh'),0775) -print "Gave that file excetion rights" - -shutil.copy('make_shortcut.sh',os.path.join(distribtution_dir,'make_shortcut.sh')) -print "Copied a small script that creates a shortcut for the user into distribtution_dir" -os.chmod(os.path.join(distribtution_dir,'make_shortcut.sh'),0775) -print "Gave that file excetion rights" - -print "starting version stript:" -write_version_file.main(pupil_capture_dir) -print "created version file in dist folder" - - - diff --git a/deploy/macOS_10.8.spec b/deploy/macOS_10.8.spec deleted file mode 100644 index 62ce895d64..0000000000 --- a/deploy/macOS_10.8.spec +++ /dev/null @@ -1,35 +0,0 @@ - -# -*- mode: python -*- -from git_version import get_tag_commit - -a = Analysis(['../pupil_src/capture/main.py'], - pathex=['../pupil_src/shared_modules/'], - hiddenimports=[], - hookspath=None, - runtime_hooks=None) -pyz = PYZ(a.pure) -exe = EXE(pyz, - a.scripts, - exclude_binaries=True, - name='pupil_capture', - debug=False, - strip=None, - upx=False, - console=False) - -coll = COLLECT(exe, - a.binaries, - a.zipfiles, - a.datas, - [('methods.so', '../pupil_src/shared_modules/c_methods/methods.so','BINARY')], - [('uvcc.so', '../pupil_src/shared_modules/uvc_capture/mac_video/uvcc.so','BINARY')], - [('libAntTweakBar.dylib', '/usr/local/Cellar/anttweakbar/1.16/lib/libAntTweakBar.dylib','BINARY')], - [('libglfw3.dylib', '/usr/local/Cellar/glfw3/3.0.2/lib/libglfw3.dylib','BINARY')], - strip=None, - upx=True, - name='Pupil Capture') - -app = BUNDLE(coll, - name='Pupil Capture.app', - icon='macos_icon.icns', - version = str(get_tag_commit())) diff --git a/deploy/simple_player.spec b/deploy/simple_player.spec deleted file mode 100644 index 8d1644a157..0000000000 --- a/deploy/simple_player.spec +++ /dev/null @@ -1,31 +0,0 @@ - -# -*- mode: python -*- -from git_version import get_tag_commit - -a = Analysis(['../pupil_src/simple_player/simple_circle.py'], - # pathex=['../pupil_src/shared_modules/'], - hiddenimports=['numpy'], - hookspath=None, - runtime_hooks=None) -pyz = PYZ(a.pure) -exe = EXE(pyz, - a.scripts, - exclude_binaries=True, - name='pupil_simple_player', - debug=False, - strip=None, - upx=False, - console=False) - -coll = COLLECT(exe, - a.binaries, - a.zipfiles, - a.datas, - strip=None, - upx=True, - name='Pupil Simple Player') - -app = BUNDLE(coll, - name='Pupil Simple Player.app', - icon='macos_icon.icns', - version = str(get_tag_commit())) diff --git a/deploy/simple_player_macOS_10.8_x64.spec b/deploy/simple_player_macOS_10.8_x64.spec deleted file mode 100644 index 8d1644a157..0000000000 --- a/deploy/simple_player_macOS_10.8_x64.spec +++ /dev/null @@ -1,31 +0,0 @@ - -# -*- mode: python -*- -from git_version import get_tag_commit - -a = Analysis(['../pupil_src/simple_player/simple_circle.py'], - # pathex=['../pupil_src/shared_modules/'], - hiddenimports=['numpy'], - hookspath=None, - runtime_hooks=None) -pyz = PYZ(a.pure) -exe = EXE(pyz, - a.scripts, - exclude_binaries=True, - name='pupil_simple_player', - debug=False, - strip=None, - upx=False, - console=False) - -coll = COLLECT(exe, - a.binaries, - a.zipfiles, - a.datas, - strip=None, - upx=True, - name='Pupil Simple Player') - -app = BUNDLE(coll, - name='Pupil Simple Player.app', - icon='macos_icon.icns', - version = str(get_tag_commit())) diff --git a/deploy/ubuntu.spec b/deploy/ubuntu.spec deleted file mode 100644 index b53805f1b7..0000000000 --- a/deploy/ubuntu.spec +++ /dev/null @@ -1,30 +0,0 @@ -# -*- mode: python -*- - -a = Analysis(['../pupil_src/capture/main.py'], - pathex=['../pupil_src/shared_modules/'], - hiddenimports=[], - hookspath=None, - runtime_hooks=None) -pyz = PYZ(a.pure) -exe = EXE(pyz, - a.scripts, - exclude_binaries=True, - name='pupil_capture', - debug=False, - strip=None, - upx=True, - console=True) - -coll = COLLECT(exe, - a.binaries, - a.zipfiles, - a.datas, - [('methods.so', '../pupil_src/shared_modules/c_methods/methods.so','BINARY')], - [('capture.so', '../pupil_src/shared_modules/uvc_capture/linux_video/v4l2_capture/capture.so','BINARY')], - [('libAntTweakBar.so', '/usr/lib/libAntTweakBar.so','BINARY')], - [('libglfw.so', '/usr/local/lib/libglfw.so','BINARY')], - [('v4l2-ctl', '/usr/bin/v4l2-ctl','BINARY')], - [('icon.ico', 'linux_icon.ico','DATA')], - strip=None, - upx=True, - name='pupil_capture') diff --git a/deploy/.gitignore b/deploy_capture/.gitignore similarity index 100% rename from deploy/.gitignore rename to deploy_capture/.gitignore diff --git a/deploy/build_test.spec b/deploy_capture/build_test.spec similarity index 100% rename from deploy/build_test.spec rename to deploy_capture/build_test.spec diff --git a/deploy_capture/bundle.spec b/deploy_capture/bundle.spec new file mode 100644 index 0000000000..6fe5da69b0 --- /dev/null +++ b/deploy_capture/bundle.spec @@ -0,0 +1,71 @@ +# -*- mode: python -*- + + +import platform + +if platform.system() == 'Darwin': + from git_version import get_tag_commit + + a = Analysis(['../pupil_src/capture/main.py'], + pathex=['../pupil_src/shared_modules/'], + hiddenimports=[], + hookspath=None, + runtime_hooks=None) + pyz = PYZ(a.pure) + exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='pupil_capture', + debug=False, + strip=None, + upx=False, + console=False) + + coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + [('methods.so', '../pupil_src/shared_modules/c_methods/methods.so','BINARY')], + [('uvcc.so', '../pupil_src/shared_modules/uvc_capture/mac_video/uvcc.so','BINARY')], + [('libAntTweakBar.dylib', '/usr/local/Cellar/anttweakbar/1.16/lib/libAntTweakBar.dylib','BINARY')], + [('libglfw3.dylib', '/usr/local/Cellar/glfw3/3.0.2/lib/libglfw3.dylib','BINARY')], + strip=None, + upx=True, + name='Pupil Capture') + + app = BUNDLE(coll, + name='Pupil Capture.app', + icon='macos_icon.icns', + version = str(get_tag_commit())) + + +elif platform.system() == 'Linux': + a = Analysis(['../pupil_src/capture/main.py'], + pathex=['../pupil_src/shared_modules/'], + hiddenimports=[], + hookspath=None, + runtime_hooks=None) + pyz = PYZ(a.pure) + exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='pupil_capture', + debug=False, + strip=None, + upx=True, + console=True) + + coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + [('methods.so', '../pupil_src/shared_modules/c_methods/methods.so','BINARY')], + [('capture.so', '../pupil_src/shared_modules/uvc_capture/linux_video/v4l2_capture/capture.so','BINARY')], + [('libAntTweakBar.so', '/usr/lib/libAntTweakBar.so','BINARY')], + [('libglfw.so', '/usr/local/lib/libglfw.so','BINARY')], + [('v4l2-ctl', '/usr/bin/v4l2-ctl','BINARY')], + [('icon.ico', 'linux_icon.ico','DATA')], + strip=None, + upx=True, + name='pupil_capture') + diff --git a/deploy_capture/finalize_bundle.py b/deploy_capture/finalize_bundle.py new file mode 100644 index 0000000000..305016e87e --- /dev/null +++ b/deploy_capture/finalize_bundle.py @@ -0,0 +1,37 @@ +import platform + +if platform.system() == 'Darwin': + import shutil + import write_version_file + print "starting version stript:" + write_version_file.main('dist/Pupil Capture.app/Contents/MacOS') + print "created version file in dist folder" + + print "copy starter app" + shutil.copytree('run_pupil_capture_from_mac_terminal.app', 'dist/run_pupil_capture_from_mac_terminal.app') + +elif platform.system() == 'Linux': + import sys,os + import write_version_file + import shutil + + distribtution_dir = 'dist' + pupil_capture_dir = os.path.join(distribtution_dir, 'pupil_capture') + + + shutil.copy('patch_uvc_driver.sh',os.path.join(distribtution_dir,'patch_uvc_driver.sh')) + print "Copied a small script to patch uvc driver into the distribution dir" + os.chmod(os.path.join(distribtution_dir,'patch_uvc_driver.sh'),0775) + print "Gave that file excetion rights" + + shutil.copy('make_shortcut.sh',os.path.join(distribtution_dir,'make_shortcut.sh')) + print "Copied a small script that creates a shortcut for the user into distribtution_dir" + os.chmod(os.path.join(distribtution_dir,'make_shortcut.sh'),0775) + print "Gave that file excetion rights" + + print "starting version stript:" + write_version_file.main(pupil_capture_dir) + print "created version file in dist folder" + + + diff --git a/deploy/git_version.py b/deploy_capture/git_version.py similarity index 100% rename from deploy/git_version.py rename to deploy_capture/git_version.py diff --git a/deploy/linux_icon.ico b/deploy_capture/linux_icon.ico similarity index 100% rename from deploy/linux_icon.ico rename to deploy_capture/linux_icon.ico diff --git a/deploy/macos_icon.icns b/deploy_capture/macos_icon.icns similarity index 100% rename from deploy/macos_icon.icns rename to deploy_capture/macos_icon.icns diff --git a/deploy/make_shortcut.sh b/deploy_capture/make_shortcut.sh similarity index 100% rename from deploy/make_shortcut.sh rename to deploy_capture/make_shortcut.sh diff --git a/deploy/patch_uvc_driver.sh b/deploy_capture/patch_uvc_driver.sh similarity index 100% rename from deploy/patch_uvc_driver.sh rename to deploy_capture/patch_uvc_driver.sh diff --git a/deploy/readme.md b/deploy_capture/readme.md similarity index 100% rename from deploy/readme.md rename to deploy_capture/readme.md diff --git a/deploy/run_pupil_capture_from_mac_terminal.app/Contents/Info.plist b/deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Info.plist similarity index 100% rename from deploy/run_pupil_capture_from_mac_terminal.app/Contents/Info.plist rename to deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Info.plist diff --git a/deploy/run_pupil_capture_from_mac_terminal.app/Contents/MacOS/applet b/deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/MacOS/applet similarity index 100% rename from deploy/run_pupil_capture_from_mac_terminal.app/Contents/MacOS/applet rename to deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/MacOS/applet diff --git a/deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/Scripts/main.scpt b/deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/Scripts/main.scpt similarity index 100% rename from deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/Scripts/main.scpt rename to deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/Scripts/main.scpt diff --git a/deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.icns b/deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.icns similarity index 100% rename from deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.icns rename to deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.icns diff --git a/deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.rsrc b/deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.rsrc similarity index 100% rename from deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.rsrc rename to deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/applet.rsrc diff --git a/deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/description.rtfd/TXT.rtf b/deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/description.rtfd/TXT.rtf similarity index 100% rename from deploy/run_pupil_capture_from_mac_terminal.app/Contents/Resources/description.rtfd/TXT.rtf rename to deploy_capture/run_pupil_capture_from_mac_terminal.app/Contents/Resources/description.rtfd/TXT.rtf diff --git a/deploy/setup.py b/deploy_capture/setup.py similarity index 100% rename from deploy/setup.py rename to deploy_capture/setup.py diff --git a/deploy/write_version_file.py b/deploy_capture/write_version_file.py similarity index 100% rename from deploy/write_version_file.py rename to deploy_capture/write_version_file.py diff --git a/deploy_player/.gitignore b/deploy_player/.gitignore new file mode 100644 index 0000000000..e1ac25639b --- /dev/null +++ b/deploy_player/.gitignore @@ -0,0 +1,3 @@ +# pyinstaller # +dist/ +build/ \ No newline at end of file diff --git a/deploy_player/bundle.spec b/deploy_player/bundle.spec new file mode 100644 index 0000000000..07dfac5339 --- /dev/null +++ b/deploy_player/bundle.spec @@ -0,0 +1,69 @@ +# -*- mode: python -*- + + +import platform + +if platform.system() == 'Darwin': + from git_version import get_tag_commit + + a = Analysis(['../pupil_src/player/main.py'], + pathex=['../pupil_src/shared_modules/'], + hiddenimports=[], + hookspath=None, + runtime_hooks=None) + pyz = PYZ(a.pure) + exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='pupil_player', + debug=False, + strip=None, + upx=False, + console=False) + + coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + [('methods.so', '../pupil_src/shared_modules/c_methods/methods.so','BINARY')], + [('uvcc.so', '../pupil_src/shared_modules/uvc_capture/mac_video/uvcc.so','BINARY')], + [('libAntTweakBar.dylib', '/usr/local/Cellar/anttweakbar/1.16/lib/libAntTweakBar.dylib','BINARY')], + [('libglfw3.dylib', '/usr/local/Cellar/glfw3/3.0.2/lib/libglfw3.dylib','BINARY')], + strip=None, + upx=True, + name='Pupil Player') + + app = BUNDLE(coll, + name='Pupil Player.app', + icon='macos_icon.icns', + version = str(get_tag_commit())) + +elif platform.system() == 'Linux': + a = Analysis(['../pupil_src/player/main.py'], + pathex=['../pupil_src/shared_modules/'], + hiddenimports=[], + hookspath=None, + runtime_hooks=None) + pyz = PYZ(a.pure) + exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='pupil_player', + debug=False, + strip=None, + upx=True, + console=True) + + coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + [('methods.so', '../pupil_src/shared_modules/c_methods/methods.so','BINARY')], + [('capture.so', '../pupil_src/shared_modules/uvc_capture/linux_video/v4l2_capture/capture.so','BINARY')], + [('libAntTweakBar.so', '/usr/lib/libAntTweakBar.so','BINARY')], + [('libglfw.so', '/usr/local/lib/libglfw.so','BINARY')], + [('v4l2-ctl', '/usr/bin/v4l2-ctl','BINARY')], + [('icon.ico', 'linux_icon.ico','DATA')], + strip=None, + upx=True, + name='pupil_player') diff --git a/deploy_player/finalize_bundle.py b/deploy_player/finalize_bundle.py new file mode 100644 index 0000000000..c602eb1367 --- /dev/null +++ b/deploy_player/finalize_bundle.py @@ -0,0 +1,29 @@ +import platform + +if platform.system() == 'Darwin': + import shutil + import write_version_file + print "starting version stript:" + write_version_file.main('dist/Pupil Player.app/Contents/MacOS') + print "created version file in dist folder" + +elif platform.system() == 'Linux': + import sys,os + import write_version_file + import shutil + + distribtution_dir = 'dist' + pupil_capture_dir = os.path.join(distribtution_dir, 'pupil_player') + + + shutil.copy('make_shortcut.sh',os.path.join(distribtution_dir,'make_shortcut.sh')) + print "Copied a small script that creates a shortcut for the user into distribtution_dir" + os.chmod(os.path.join(distribtution_dir,'make_shortcut.sh'),0775) + print "Gave that file excetion rights" + + print "starting version stript:" + write_version_file.main(pupil_capture_dir) + print "created version file in dist folder" + + + diff --git a/deploy_player/git_version.py b/deploy_player/git_version.py new file mode 100644 index 0000000000..22f7362e69 --- /dev/null +++ b/deploy_player/git_version.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +# Author: Douglas Creager +# This file is placed into the public domain. + +# Calculates the current version number. If possible, this is the +# output of “git describe”, modified to conform to the versioning +# scheme that setuptools uses. If “git describe” returns an error +# (most likely because we're in an unpacked copy of a release tarball, +# rather than in a git working copy), then we fall back on reading the +# contents of the RELEASE-VERSION file. +# +# To use this script, simply import it your setup.py file, and use the +# results of get_git_version() as your package version: +# +# from version import * +# +# setup( +# version=get_git_version(), +# . +# . +# . +# ) +# +# This will automatically update the RELEASE-VERSION file, if +# necessary. Note that the RELEASE-VERSION file should *not* be +# checked into git; please add it to your top-level .gitignore file. +# +# You'll probably want to distribute the RELEASE-VERSION file in your +# sdist tarballs; to do this, just create a MANIFEST.in file that +# contains the following line: +# +# include RELEASE-VERSION + +__all__ = ("get_git_version") + +from subprocess import Popen, PIPE + + +def call_git_describe(abbrev=4): + try: + p = Popen(['git', 'describe', '--abbrev=%d' % abbrev], + stdout=PIPE, stderr=PIPE) + p.stderr.close() + line = p.stdout.readlines()[0] + return line.strip() + + except: + return None + + +def read_release_version(): + try: + f = open("RELEASE-VERSION", "r") + + try: + version = f.readlines()[0] + return version.strip() + + finally: + f.close() + + except: + return None + + +def write_release_version(version): + f = open("RELEASE-VERSION", "w") + f.write("%s\n" % version) + f.close() + + +def get_git_version(abbrev=4): + # Read in the version that's currently in RELEASE-VERSION. + + release_version = read_release_version() + + # First try to get the current version using “git describe”. + + version = call_git_describe(abbrev) + + #adapt to PEP 386 compatible versioning scheme + version = pep386adapt(version) + + # If that doesn't work, fall back on the value that's in + # RELEASE-VERSION. + + if version is None: + version = release_version + + # If we still don't have anything, that's an error. + + if version is None: + raise ValueError("Cannot find the version number!") + + # If the current version is different from what's in the + # RELEASE-VERSION file, update the file to be current. + + # if version != release_version: + # write_release_version(version) + + # Finally, return the current version. + + return version + + +def pep386adapt(version): + if version is not None and '-' in version: + # adapt git-describe version to be in line with PEP 386 + parts = version.split('-') + parts[-2] = 'post'+parts[-2] + version = '.'.join(parts[:-1]) + return version + + + +def get_last_tag(): + try: + p = Popen(['git', 'describe'], + stdout=PIPE, stderr=PIPE) + p.stderr.close() + line = p.stdout.readlines()[0] + return line.strip() + + except: + return None + +def get_tag_commit(): + """ + returns string: 'tag'-'commits since tag'-'7 digit commit id' + """ + try: + p = Popen(['git', 'describe'], + stdout=PIPE, stderr=PIPE) + p.stderr.close() + line = p.stdout.readlines()[0] + return line.strip() + + except: + return None + +if __name__ == "__main__": + print get_git_version() + print get_tag_commit() \ No newline at end of file diff --git a/deploy_player/linux_icon.ico b/deploy_player/linux_icon.ico new file mode 100644 index 0000000000..36fa4fc639 Binary files /dev/null and b/deploy_player/linux_icon.ico differ diff --git a/deploy_player/macos_icon.icns b/deploy_player/macos_icon.icns new file mode 100644 index 0000000000..c206ab04b7 Binary files /dev/null and b/deploy_player/macos_icon.icns differ diff --git a/deploy_player/make_shortcut.sh b/deploy_player/make_shortcut.sh new file mode 100755 index 0000000000..803ce0792e --- /dev/null +++ b/deploy_player/make_shortcut.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +echo $BASEDIR + +TEXT=" +[Desktop Entry]\n +Version=1.0\n +Name=Pupil Player\n +Comment=Pupil Player Software\n +Exec= '${BASEDIR}/pupil_player/pupil_player' %f %d\n +Icon= ${BASEDIR}/pupil_player/icon.ico\n +Terminal=true\n +Type=Application\n +Categories=Application;" + + +echo $TEXT > pupil_player.desktop +chmod 775 pupil_player.desktop \ No newline at end of file diff --git a/deploy_player/readme.md b/deploy_player/readme.md new file mode 100644 index 0000000000..a0811c19ed --- /dev/null +++ b/deploy_player/readme.md @@ -0,0 +1,14 @@ + +# Deploy Workflow +--- +## Bundle using Pyinstaller + pyinstaller -w bundle_**os_name**.spec + +## Create a version file inside the distribution folder,chmod stuff... + python finalize_bundle_**os_name**.py + + +pre-requisits: +brew python (not! system python) +pip install pyinstaller + diff --git a/deploy_player/write_version_file.py b/deploy_player/write_version_file.py new file mode 100644 index 0000000000..cbb85fe001 --- /dev/null +++ b/deploy_player/write_version_file.py @@ -0,0 +1,22 @@ +import os,sys + +def main(target_dir): + modules_path = os.path.join(os.path.abspath(__file__).rsplit(os.path.sep,2)[0],'pupil_src','shared_modules') + sys.path.append(modules_path) + from git_version import get_tag_commit + + version = get_tag_commit() + print "Current version of Pupil: ",version + + with open(os.path.join(target_dir,'_version_string_'),'w') as f: + f.write(version) + print 'Wrote version into: %s' %os.path.join(target_dir,'_version_string_') + +def get_version(): + modules_path = os.path.join(os.path.abspath(__file__).rsplit(os.path.sep,2)[0],'pupil_src','shared_modules') + sys.path.append(modules_path) + from git_version import get_tag_commit + return get_tag_commit() + +if __name__ == '__main__': + main("dist/pupil_capture") \ No newline at end of file diff --git a/pupil_src/capture/marker_detector.py b/pupil_src/capture/marker_detector.py index c92221181b..844a0df406 100644 --- a/pupil_src/capture/marker_detector.py +++ b/pupil_src/capture/marker_detector.py @@ -45,6 +45,7 @@ class Marker_Detector(Plugin): def __init__(self,g_pool,atb_pos=(0,0)): super(Marker_Detector, self).__init__() + self.order = .2 # all markers that are detected in the most recent frame self.markers = [] diff --git a/pupil_src/capture/world.py b/pupil_src/capture/world.py index 8f29868e10..befd11d434 100644 --- a/pupil_src/capture/world.py +++ b/pupil_src/capture/world.py @@ -37,7 +37,7 @@ import calibration_routines import recorder from show_calibration import Show_Calibration -from display_gaze import Display_Gaze +from display_recent_gaze import Display_Recent_Gaze from pupil_server import Pupil_Server from marker_detector import Marker_Detector @@ -144,6 +144,8 @@ def open_calibration(selection,data): new_ref_detector = calibration_routines.detector_by_index[selection](g_pool,atb_pos=bar.next_atb_pos) g.plugins.append(new_ref_detector) + g.plugins.sort(key=lambda p: p.order) + # save the value for atb bar data.value=selection @@ -158,6 +160,7 @@ def toggle_record_video(): new_plugin = recorder.Recorder(g_pool,bar.rec_name.value, bar.fps.value, frame.img.shape, bar.record_eye.value, g_pool.eye_tx) g.plugins.append(new_plugin) + g.plugins.sort(key=lambda p: p.order) def toggle_show_calib_result(): for p in g.plugins: @@ -167,6 +170,7 @@ def toggle_show_calib_result(): new_plugin = Show_Calibration(g_pool,frame.img.shape) g.plugins.append(new_plugin) + g.plugins.sort(key=lambda p: p.order) def toggle_server(): for p in g.plugins: @@ -176,6 +180,7 @@ def toggle_server(): new_plugin = Pupil_Server(g_pool,(10,300)) g.plugins.append(new_plugin) + g.plugins.sort(key=lambda p: p.order) def toggle_ar(): @@ -185,8 +190,8 @@ def toggle_ar(): return new_plugin = Marker_Detector(g_pool,(10,400)) - g.plugins.insert(0,new_plugin) #do this before the server or recorder - + g.plugins.append(new_plugin) + g.plugins.sort(key=lambda p: p.order) atb.init() # add main controls ATB bar @@ -266,7 +271,7 @@ def map_pupil(vector): open_calibration(bar.calibration_type.value,bar.calibration_type) #load gaze_display plugin - g.plugins.append(Display_Gaze(g_pool)) + g.plugins.append(Display_Recent_Gaze(g_pool)) # Event loop while not g_pool.quit.value: diff --git a/pupil_src/capture/display_gaze.py b/pupil_src/player/display_gaze.py similarity index 83% rename from pupil_src/capture/display_gaze.py rename to pupil_src/player/display_gaze.py index d44f91d66a..60846f1214 100644 --- a/pupil_src/capture/display_gaze.py +++ b/pupil_src/player/display_gaze.py @@ -19,16 +19,13 @@ class Display_Gaze(Plugin): def __init__(self, g_pool,atb_pos=None): super(Display_Gaze, self).__init__() self.g_pool = g_pool + self.order = .8 self.atb_pos = atb_pos self.pupil_display_list = [] def update(self,frame,recent_pupil_positions,events): - for pt in recent_pupil_positions: - if pt['norm_gaze'] is not None: - self.pupil_display_list.append(pt['norm_gaze']) - self.pupil_display_list[:-3] = [] + self.pupil_display_list = [pt['norm_gaze'] for pt in recent_pupil_positions if pt['norm_gaze'] is not None] def gl_display(self): draw_gl_points_norm(self.pupil_display_list,size=35,color=(1.,.2,.4,.6)) - diff --git a/pupil_src/player/export_launcher.py b/pupil_src/player/export_launcher.py new file mode 100644 index 0000000000..4685de5130 --- /dev/null +++ b/pupil_src/player/export_launcher.py @@ -0,0 +1,172 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +from plugin import Plugin +import numpy as np +import atb +import os,sys, platform + +import logging +logger = logging.getLogger(__name__) + +from ctypes import c_bool, c_int,create_string_buffer + +if platform.system() == 'Darwin': + from billiard import Process,forking_enable + from billiard.sharedctypes import RawValue +else: + from multiprocessing import Process + forking_enable = lambda x: x #dummy fn + from multiprocessing.sharedctypes import RawValue + +from exporter import export + +def verify_out_file_path(out_file_path,data_dir): + #Out file path verification + if not out_file_path: + out_file_path = os.path.join(data_dir, "world_viz.avi") + else: + file_name = os.path.basename(out_file_path) + dir_name = os.path.dirname(out_file_path) + if not dir_name: + dir_name = data_dir + if not file_name: + file_name = 'world_viz.avi' + out_file_path = os.path.expanduser(os.path.join(dir_name,file_name)) + + if os.path.isfile(out_file_path): + logger.warning("Video out file already exsists. I will overwrite!") + os.remove(out_file_path) + logger.debug("Saving Video to %s"%out_file_path) + + return out_file_path + + +class Export_Launcher(Plugin): + """docstring for Export_Launcher + this plugin can export the video in a seperate process using exporter + + """ + def __init__(self, g_pool,data_dir,frame_count): + super(Export_Launcher, self).__init__() + self.g_pool = g_pool + self.data_dir = data_dir + + self.new_export = None + self.exports = [] + + self.rec_name = create_string_buffer("world_viz.avi",512) + self.start_frame = c_int(0) + self.end_frame = c_int(frame_count) + + + def init_gui(self): + + atb_label = "Export Recording" + atb_pos = 320,10 + + + self._bar = atb.Bar(name =self.__class__.__name__, label=atb_label, + help="export vizualization video", color=(50, 50, 50), alpha=100, + text='light', position=atb_pos,refresh=.1, size=(300, 100)) + + + self.update_bar() + + + + def update_bar(self): + if self._bar: + self._bar.clear() + + + self._bar.add_var('export name',self.rec_name) + self._bar.add_var('start frame',self.start_frame) + self._bar.add_var('end frame',self.end_frame) + self._bar.add_button('new export',self.add_export) + + for job,i in zip(self.exports,range(len(self.exports))): + + self._bar.add_var("%s_out_file"%i,create_string_buffer(512), + getter= self.atb_out_file_path, + data = self.exports[i], + label='file location:', + group=str(i), + ) + self._bar.add_var("%s_progess"%i,create_string_buffer(512), + getter= self.atb_progress, + data = self.exports[i], + label='progess', + group=str(i), + ) + self._bar.add_var("%s_terminate"%i,job.should_terminate,group=str(i),label='cancel') + + def atb_progress(self,job): + if job.current_frame.value == job.frames_to_export.value: + return create_string_buffer("Done",512) + return create_string_buffer("%s / %s" %(job.current_frame.value,job.frames_to_export.value),512) + + def atb_out_file_path(self,job): + return create_string_buffer(job.out_file_path,512) + + def add_export(self): + # on MacOS we will not use os.fork, elsewhere this does nothing. + forking_enable(0) + + logger.debug("Adding new export.") + should_terminate = RawValue(c_bool,False) + frames_to_export = RawValue(c_int,0) + current_frame = RawValue(c_int,0) + + data_dir = self.data_dir + start_frame= self.start_frame.value + end_frame= self.end_frame.value + plugins = [] + + # Here we make clones of every plugin that supports it. + # So it runs in the current config when we lauch the exporter. + for p in self.g_pool.plugins: + try: + p_initializer = p.get_class_name(),p.get_init_dict() + plugins.append(p_initializer) + except AttributeError: + pass + + out_file_path=verify_out_file_path(self.rec_name.value,self.data_dir) + process = Process(target=export, args=(should_terminate,frames_to_export,current_frame, data_dir,start_frame,end_frame,plugins,out_file_path)) + process.should_terminate = should_terminate + process.frames_to_export = frames_to_export + process.current_frame = current_frame + process.out_file_path = out_file_path + self.new_export = process + + def launch_export(self, new_export): + + logger.debug("Starting export as new process %s" %new_export) + new_export.start() + self.exports.append(new_export) + self.update_bar() + + def update(self,frame,recent_pupil_positions,events): + if self.new_export: + self.launch_export(self.new_export) + self.new_export = None + + + def gl_display(self): + pass + + + def cleanup(self): + """ called when the plugin gets terminated. + This happends either voluntary or forced. + if you have an atb bar or glfw window destroy it here. + """ + self._bar.destroy() diff --git a/pupil_src/player/exporter.py b/pupil_src/player/exporter.py new file mode 100644 index 0000000000..835c89b670 --- /dev/null +++ b/pupil_src/player/exporter.py @@ -0,0 +1,217 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +if __name__ == '__main__': + # make shared modules available across pupil_src + from sys import path as syspath + from os import path as ospath + loc = ospath.abspath(__file__).rsplit('pupil_src', 1) + syspath.append(ospath.join(loc[0], 'pupil_src', 'shared_modules')) + del syspath, ospath + + +import os +from time import time +import cv2 +import numpy as np +from uvc_capture import autoCreateCapture +from player_methods import correlate_gaze +from methods import denormalize +#logging +import logging +logger = logging.getLogger(__name__) + +# Plug-ins +from vis_circle import Vis_Circle +from vis_polyline import Vis_Polyline +from vis_light_points import Vis_Light_Points +from scan_path import Scan_Path + +plugin_by_index = ( Vis_Circle, + Vis_Polyline, + Scan_Path, + Vis_Light_Points) + +name_by_index = [p.__name__ for p in plugin_by_index] +index_by_name = dict(zip(name_by_index,range(len(name_by_index)))) +plugin_by_name = dict(zip(name_by_index,plugin_by_index)) +additive_plugins = (Vis_Circle,Vis_Polyline) + + + +def export(should_terminate,frames_to_export,current_frame, data_dir,start_frame=None,end_frame=None,plugin_initializers=[],out_file_path=None): + + #parse and load data dir info + video_path = data_dir + "/world.avi" + timestamps_path = data_dir + "/timestamps.npy" + gaze_positions_path = data_dir + "/gaze_positions.npy" + record_path = data_dir + "/world_viz.avi" + + + #parse info.csv file + with open(data_dir + "/info.csv") as info: + meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) + rec_version = meta_info["Capture Software Version"] + rec_version_int = int(filter(type(rec_version).isdigit, rec_version)[:3])/100 #(get major,minor,fix of version) + logger.debug("Exporting a video from recording with version: %s , %s"%(rec_version,rec_version_int)) + + + #load gaze information + gaze_list = np.load(gaze_positions_path) + timestamps = np.load(timestamps_path) + #correlate data + positions_by_frame = correlate_gaze(gaze_list,timestamps) + + + # Initialize capture, check if it works + cap = autoCreateCapture(video_path,timestamps=timestamps_path) + if cap is None: + logger.error("Did not receive valid Capture") + return + width,height = cap.get_size() + + #Out file path verification, we do this before but if one uses a seperate tool, this will kick in. + if out_file_path is None: + out_file_path = os.path.join(data_dir, "world_viz.avi") + else: + file_name = os.path.basename(out_file_path) + dir_name = os.path.dirname(out_file_path) + if not dir_name: + dir_name = data_dir + if not file_name: + file_name = 'world_viz.avi' + out_file_path = os.path.expanduser(os.path.join(dir_name,file_name)) + + if os.path.isfile(out_file_path): + logger.warning("Video out file already exsists. I will overwrite!") + os.remove(out_file_path) + logger.debug("Saving Video to %s"%out_file_path) + + + #Trim mark verification + #make sure the trim marks (start frame, endframe) make sense: We define them like python list slices,thus we can test them like such. + trimmed_timestamps = timestamps[start_frame:end_frame] + if len(trimmed_timestamps)==0: + logger.warn("Start and end frames are set such that no video will be exported.") + return False + + if start_frame == None: + start_frame = 0 + + #these two vars are shared with the lauching process and give an job lenght and progress report. + frames_to_export.value = len(trimmed_timestamps) + current_frame.value = 0 + logger.debug("Will export from frame %s to frame %s. This means I will export %s frames."%(start_frame,start_frame+frames_to_export.value,frames_to_export.value)) + + + #lets get the avg. framerate for our slice of video: + fps = float(len(trimmed_timestamps))/(trimmed_timestamps[-1] - trimmed_timestamps[0]) + logger.debug("Framerate of export video is %s"%fps) + + + #setup of writer + writer = cv2.VideoWriter(out_file_path, cv2.cv.CV_FOURCC(*'DIVX'), fps, (width,height)) + + cap.seek_to_frame(start_frame) + + start_time = time() + + + plugins = [] + # load plugins from initializers: + for initializer in plugin_initializers: + name, args = initializer + logger.debug("Loading plugin: %s with settings %s"%(name, args)) + try: + p = plugin_by_name[name](**args) + plugins.append(p) + except: + logger.warning("Plugin '%s' failed to load." %name) + + + while frames_to_export.value - current_frame.value > 0: + + if should_terminate.value: + logger.warning("User aborted export. Exported %s frames to %s."%(current_frame.value,out_file_path)) + + #explicit release of VideoWriter + writer.release() + writer = None + return False + + new_frame = cap.get_frame() + #end of video logic: pause at last frame. + if not new_frame: + logger.error("Could not read all frames.") + #explicit release of VideoWriter + writer.release() + writer = None + return False + else: + frame = new_frame + + #new positons and events + current_pupil_positions = positions_by_frame[frame.index] + events = None + + # allow each Plugin to do its work. + for p in plugins: + p.update(frame,current_pupil_positions,events) + + # # render gl visual feedback from loaded plugins + # for p in plugins: + # p.gl_display(frame) + + writer.write(frame.img) + current_frame.value +=1 + + writer.release() + writer = None + + duration = time()-start_time + effective_fps = float(current_frame.value)/duration + + logger.info("Export done: Exported %s frames to %s. This took %s seconds. Exporter ran at %s frames per second"%(current_frame.value,out_file_path,duration,effective_fps)) + + + return True + + + + +if __name__ == '__main__': + + # make shared modules available across pupil_src + from sys import path as syspath + from os import path as ospath + loc = ospath.abspath(__file__).rsplit('pupil_src', 1) + syspath.append(ospath.join(loc[0], 'pupil_src', 'shared_modules')) + del syspath, ospath + + + from ctypes import c_int,c_bool + + + logging.basicConfig(level=logging.DEBUG) + + + should_terminate = c_bool(False) + frame_to_export = c_int(0) + current_frame = c_int(0) + data_dir = '/Users/mkassner/Desktop/2014_01_21/000/' + start_frame=200 + end_frame=300 + plugins=[] + out_file_path="test.avi" + + + export(should_terminate,frame_to_export,current_frame, data_dir,start_frame=start_frame,end_frame=end_frame,plugins=[],out_file_path=out_file_path) + print current_frame.value + diff --git a/pupil_src/player/main.py b/pupil_src/player/main.py new file mode 100644 index 0000000000..2fcbbb002b --- /dev/null +++ b/pupil_src/player/main.py @@ -0,0 +1,447 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +import sys, os,platform +from time import sleep +from ctypes import c_bool, c_int +from multiprocessing import Process, Pipe, Event, Queue +from multiprocessing.sharedctypes import RawValue, Value, Array + +if getattr(sys, 'frozen', False): + if platform.system() == 'Darwin': + # Specifiy user dirs. + user_dir = os.path.expanduser('~/Desktop/pupil_player_settings') + version_file = os.path.join(sys._MEIPASS,'_version_string_') + else: + # Specifiy user dirs. + user_dir = os.path.join(sys._MEIPASS.rsplit(os.path.sep,1)[0],"player_settings") + version_file = os.path.join(sys._MEIPASS,'_version_string_') + +else: + # We are running in a normal Python environment. + # Make all pupil shared_modules available to this Python session. + pupil_base_dir = os.path.abspath(__file__).rsplit('pupil_src', 1)[0] + sys.path.append(os.path.join(pupil_base_dir, 'pupil_src', 'shared_modules')) + # Specifiy user dirs. + user_dir = os.path.join(pupil_base_dir,'player_settings') + + +# create folder for user settings, tmp data +if not os.path.isdir(user_dir): + os.mkdir(user_dir) + + + +import shelve +from time import time,sleep +from ctypes import c_int,c_bool,c_float,create_string_buffer +import numpy as np + +#display +from glfw import * +import atb + + +from uvc_capture import autoCreateCapture +# helpers/utils +from methods import normalize, denormalize,Temp +from player_methods import correlate_gaze,patch_meta_info,is_pupil_rec_dir +from gl_utils import basic_gl_setup, adjust_gl_view, draw_gl_texture, clear_gl_screen, draw_gl_point_norm,draw_gl_texture +# Plug-ins +from vis_circle import Vis_Circle +from vis_polyline import Vis_Polyline +from display_gaze import Display_Gaze +from vis_light_points import Vis_Light_Points +from seek_bar import Seek_Bar +from export_launcher import Export_Launcher +from scan_path import Scan_Path + +plugin_by_index = ( Vis_Circle, + Vis_Polyline, + Scan_Path, + Vis_Light_Points) + +name_by_index = [p.__name__ for p in plugin_by_index] + + +index_by_name = dict(zip(name_by_index,range(len(name_by_index)))) +plugin_by_name = dict(zip(name_by_index,plugin_by_index)) +additive_plugins = (Vis_Circle,Vis_Polyline) + + +import logging +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) +# create file handler which logs even debug messages +fh = logging.FileHandler(os.path.join(user_dir,'player.log'),mode='w') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('Player: %(asctime)s - %(name)s - %(levelname)s - %(message)s') +fh.setFormatter(formatter) +formatter = logging.Formatter('Player [%(levelname)s] %(name)s : %(message)s') +ch.setFormatter(formatter) +# add the handlers to the logger +logger.addHandler(fh) +logger.addHandler(ch) +# mute OpenGL logger +logging.getLogger("OpenGL").propagate = False +logging.getLogger("OpenGL").addHandler(logging.NullHandler()) + +logger = logging.getLogger(__name__) + + +#get the current software version +if getattr(sys, 'frozen', False): + with open(version_file) as f: + version = f.read() +else: + from git_version import get_tag_commit + version = get_tag_commit() + + +def main(): + + # Callback functions + def on_resize(window,w, h): + active_window = glfwGetCurrentContext() + glfwMakeContextCurrent(window) + adjust_gl_view(w,h) + atb.TwWindowSize(w, h) + glfwMakeContextCurrent(active_window) + + def on_key(window, key, scancode, action, mods): + if not atb.TwEventKeyboardGLFW(key,action): + if action == GLFW_PRESS: + if key == GLFW_KEY_ESCAPE: + pass + + + def on_char(window,char): + if not atb.TwEventCharGLFW(char,1): + pass + + def on_button(window,button, action, mods): + if not atb.TwEventMouseButtonGLFW(button,action): + pos = glfwGetCursorPos(window) + pos = normalize(pos,glfwGetWindowSize(main_window)) + pos = denormalize(pos,(frame.img.shape[1],frame.img.shape[0]) ) # Position in img pixels + for p in g.plugins: + p.on_click(pos,button,action) + + def on_pos(window,x, y): + if atb.TwMouseMotion(int(x),int(y)): + pass + + def on_scroll(window,x,y): + if not atb.TwMouseWheel(int(x)): + pass + + def on_close(window): + glfwSetWindowShouldClose(window) + logger.info('Process closing from window') + + + try: + data_folder = sys.argv[1] + except: + #for dev, supply hardcoded dir: + data_folder = "/Users/mkassner/Desktop/2014_01_21/000" + if os.path.isdir(data_folder): + logger.debug("Dev option: Using hadcoded data dir.") + else: + if getattr(sys, 'frozen', False): + logger.warning("You did not supply a data directory when you called this script! \ + \nPlease drag a Pupil recoding directory onto the launch icon.") + else: + logger.warning("You did not supply a data directory when you called this script! \ + \nPlease supply a Pupil recoding directory as first arg when calling Pupil Player.") + return + + if not is_pupil_rec_dir(data_folder): + logger.error("You did not supply a dir with the required files inside.") + return + + #backwards compatibility fn. + patch_meta_info(data_folder) + + #parse and load data folder info + video_path = data_folder + "/world.avi" + timestamps_path = data_folder + "/timestamps.npy" + gaze_positions_path = data_folder + "/gaze_positions.npy" + meta_info_path = data_folder + "/info.csv" + + + #parse info.csv file + with open(meta_info_path) as info: + meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) + rec_version = meta_info["Capture Software Version"] + rec_version_float = int(filter(type(rec_version).isdigit, rec_version)[:3])/100. #(get major,minor,fix of version) + logger.debug("Recording version: %s , %s"%(rec_version,rec_version_float)) + + + #load gaze information + gaze_list = np.load(gaze_positions_path) + timestamps = np.load(timestamps_path) + + #correlate data + positions_by_frame = correlate_gaze(gaze_list,timestamps) + + + # load session persistent settings + session_settings = shelve.open(os.path.join(user_dir,"user_settings"),protocol=2) + def load(var_name,default): + return session_settings.get(var_name,default) + def save(var_name,var): + session_settings[var_name] = var + + + # Initialize capture, check if it works + cap = autoCreateCapture(video_path,timestamps=timestamps_path) + if cap is None: + logger.error("Did not receive valid Capture") + return + width,height = cap.get_size() + + + # Initialize glfw + glfwInit() + main_window = glfwCreateWindow(width, height, "Pupil Player: "+meta_info["Recording Name"]+" - "+ data_folder.split(os.path.sep)[-1], None, None) + glfwMakeContextCurrent(main_window) + + # Register callbacks main_window + glfwSetWindowSizeCallback(main_window,on_resize) + glfwSetWindowCloseCallback(main_window,on_close) + glfwSetKeyCallback(main_window,on_key) + glfwSetCharCallback(main_window,on_char) + glfwSetMouseButtonCallback(main_window,on_button) + glfwSetCursorPosCallback(main_window,on_pos) + glfwSetScrollCallback(main_window,on_scroll) + + # gl_state settings + basic_gl_setup() + + + # create container for globally scoped vars (within world) + g = Temp() + g.plugins = [] + g.play = False + g.new_seek = True + + + + # helpers called by the main atb bar + def update_fps(): + old_time, bar.timestamp = bar.timestamp, time() + dt = bar.timestamp - old_time + if dt: + bar.fps.value += .1 * (1. / dt - bar.fps.value) + + def set_window_size(mode,data): + width,height = cap.get_size() + ratio = (1,.75,.5,.25)[mode] + w,h = int(width*ratio),int(height*ratio) + glfwSetWindowSize(main_window,w,h) + data.value=mode # update the bar.value + + def get_from_data(data): + """ + helper for atb getter and setter use + """ + return data.value + + def get_play(): + return g.play + + def set_play(value): + g.play = value + + + def open_plugin(selection,data): + if plugin_by_index[selection] not in additive_plugins: + for p in g.plugins: + if isinstance(p,plugin_by_index[selection]): + return + + g.plugins = [p for p in g.plugins if p.alive] + logger.debug('Open Plungin: %s'%name_by_index[selection]) + new_plugin = plugin_by_index[selection](g) + g.plugins.append(new_plugin) + g.plugins.sort(key=lambda p: p.order) + + if hasattr(new_plugin,'init_gui'): + new_plugin.init_gui() + # save the value for atb bar + data.value=selection + + def get_from_data(data): + """ + helper for atb getter and setter use + """ + return data.value + + atb.init() + # add main controls ATB bar + bar = atb.Bar(name = "Controls", label="Controls", + help="Scene controls", color=(50, 50, 50), alpha=100,valueswidth=150, + text='light', position=(10, 10),refresh=.1, size=(300, 160)) + bar.next_atb_pos = (10,220) + bar.fps = c_float(0.0) + bar.timestamp = time() + bar.window_size = c_int(load("window_size",0)) + window_size_enum = atb.enum("Display Size",{"Full":0, "Medium":1,"Half":2,"Mini":3}) + bar.version = create_string_buffer(version,512) + bar.recording_version = create_string_buffer(rec_version,512) + bar.add_var("fps", bar.fps, step=1., readonly=True) + bar._fps = c_float(cap.get_fps()) + bar.add_var("recoding fps",bar._fps,readonly=True) + bar.add_var("display size", vtype=window_size_enum,setter=set_window_size,getter=get_from_data,data=bar.window_size) + bar.add_var("play",vtype=c_bool,getter=get_play,setter=set_play,key="space") + bar.add_var("next frame",getter=cap.get_frame_index) + + bar.plugin_to_load = c_int(0) + plugin_type_enum = atb.enum("Plug In",index_by_name) + bar.add_var("plugin",setter=open_plugin,getter=get_from_data,data=bar.plugin_to_load, vtype=plugin_type_enum) + + bar.add_var("version of recording",bar.recording_version, readonly=True, help="version of the capture software used to make this recording") + bar.add_var("version of player",bar.version, readonly=True, help="version of the Pupil Player") + bar.add_button("exit", on_close,data=main_window,key="esc") + + #set the last saved window size + set_window_size(bar.window_size.value,bar.window_size) + on_resize(main_window, *glfwGetFramebufferSize(main_window)) + glfwSetWindowPos(main_window,0,0) + + + #we always load these plugins + g.plugins.append(Export_Launcher(g,data_dir=data_folder,frame_count=len(timestamps))) + g.plugins.append(Seek_Bar(g,capture=cap)) + + #these are loaded based on user settings + for initializer in load('plugins',[]): + name, args = initializer + logger.debug("Loading plugin: %s with settings %s"%(name, args)) + try: + p = plugin_by_name[name](g,**args) + g.plugins.append(p) + except: + logger.warning("Plugin '%s' failed to load from settings file." %name) + + if load('plugins',"_") == "_": + #lets load some default if we dont have presets + g.plugins.append(Scan_Path(g)) + g.plugins.append(Vis_Polyline(g)) + g.plugins.append(Vis_Circle(g)) + # g.plugins.append(Vis_Light_Points(g)) + + #sort by exec order + g.plugins.sort(key=lambda p: p.order) + + #init gui + for p in g.plugins: + if hasattr(p,'init_gui'): + p.init_gui() + + + + while not glfwWindowShouldClose(main_window): + + update_fps() + + #grab new frame + if g.play or g.new_seek: + test_frame = cap.get_frame() + #end of video logic: pause at last frame. + if not test_frame: + g.play=False + else: + new_frame = test_frame + + if g.new_seek: + display_time = new_frame.timestamp + g.new_seek = False + + + frame = new_frame.copy() + + #new positons and events + current_pupil_positions = positions_by_frame[frame.index][:] + events = None + + # allow each Plugin to do its work. + for p in g.plugins: + p.update(frame,current_pupil_positions,events) + + #check if a plugin need to be destroyed + g.plugins = [p for p in g.plugins if p.alive] + + # render camera image + glfwMakeContextCurrent(main_window) + draw_gl_texture(frame.img) + + # render visual feedback from loaded plugins + for p in g.plugins: + p.gl_display() + + #present frames at appropriate speed + wait_time = frame.timestamp - display_time + display_time = frame.timestamp + try: + spent_time = time()-timestamp + sleep(wait_time-spent_time) + except: + pass + timestamp = time() + + + atb.draw() + glfwSwapBuffers(main_window) + glfwPollEvents() + + + plugin_save = [] + for p in g.plugins: + try: + p_initializer = p.get_class_name(),p.get_init_dict() + plugin_save.append(p_initializer) + except AttributeError: + #not all plugins need to be savable, they will not have the init dict. + pass + + # de-init all running plugins + for p in g.plugins: + p.alive = False + #reading p.alive actually runs plug-in cleanup + _ = p.alive + + save('plugins',plugin_save) + save('window_size',bar.window_size.value) + session_settings.close() + + cap.close() + bar.destroy() + glfwDestroyWindow(main_window) + glfwTerminate() + logger.debug("Process done") + + + +if __name__ == '__main__': + if 0: + main() + else: + import cProfile,subprocess,os + cProfile.runctx("main()",{},locals(),"player.pstats") + loc = os.path.abspath(__file__).rsplit('pupil_src', 1) + gprof2dot_loc = os.path.join(loc[0], 'pupil_src', 'shared_modules','gprof2dot.py') + subprocess.call("python "+gprof2dot_loc+" -f pstats player.pstats | dot -Tpng -o player_cpu_time.png", shell=True) + print "created cpu time graph for pupil player . Please check out the png next to the main.py file" diff --git a/pupil_src/player/player_methods.py b/pupil_src/player/player_methods.py new file mode 100644 index 0000000000..d423700a29 --- /dev/null +++ b/pupil_src/player/player_methods.py @@ -0,0 +1,161 @@ + +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + + +import os +import cv2 +#logging +import logging +logger = logging.getLogger(__name__) + + +def correlate_gaze(gaze_list,timestamps): + ''' + gaze_list: gaze x | gaze y | pupil x | pupil y | timestamp + timestamps timestamps to correlate gaze data to + + + this takes a gaze positions list and a timestamps list and makes a new list + with the length of the number of recorded frames. + Each slot conains a list that will have 0, 1 or more assosiated gaze postions. + load gaze information + ''' + gaze_list = list(gaze_list) + timestamps = list(timestamps) + + + positions_by_frame = [[] for i in timestamps] + frame_idx = 0 + data_point = gaze_list.pop(0) + gaze_timestamp = data_point[4] + + while gaze_list: + # if the current gaze point is before the mean of the current world frame timestamp and the next worldframe timestamp + try: + t_between_frames = ( timestamps[frame_idx]+timestamps[frame_idx+1] ) / 2. + except IndexError: + break + if gaze_timestamp <= t_between_frames: + positions_by_frame[frame_idx].append({'norm_gaze':(data_point[0],data_point[1]),'norm_pupil': (data_point[2],data_point[3]), 'timestamp':gaze_timestamp}) + data_point = gaze_list.pop(0) + gaze_timestamp = data_point[4] + else: + frame_idx+=1 + + return positions_by_frame + + +def rec_version(data_dir): + with open(data_folder + "/info.csv") as info: + meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) + rec_version = meta_info["Capture Software Version"] + rec_version_float = int(filter(type(rec_version).isdigit, rec_version)[:3])/100. #(get major,minor,fix of version) + return rec_version_float + + + +def is_pupil_rec_dir(data_dir): + if not os.path.isdir(data_dir): + logger.error("No valid dir supplied") + return False + + required_files = data_dir + "/world.avi",data_dir + "/timestamps.npy",data_dir + "/gaze_positions.npy",data_dir + "/info.csv" + for f in required_files: + if not os.path.isfile(f): + logger.error("Could not find %s"%f) + return False + return True + + + + +# backwards compatibility tools: + +def patch_meta_info(data_dir): + #parse info.csv file + + ''' + This is how we need it: + + Recording Name 2014_01_21 + Start Date 21.01.2014 + Start Time 11:42:24 + Duration Time 00:00:29 + World Camera Frames 710 + World Camera Resolution 1280x720 + Capture Software Version v0.3.7 + User testing + Platform Linux + Machine brosnan + Release 3.5.0-45-generic + Version #68~precise1-Ubuntu SMP Wed Dec 4 16:18:46 UTC 2013 + ''' + proper_names = ['Recording Name', + 'Start Date', + 'Start Time', + 'Duration Time', + 'World Camera Frames', + 'World Camera Resolution', + 'Capture Software Version', + 'User', + 'Platform', + 'Release', + 'Version'] + + with open(data_dir + "/info.csv") as info: + meta_info = [line.strip().split('\t') for line in info.readlines() ] + + for entry in meta_info: + for proper_name in proper_names: + if proper_name == entry[0]: + break + elif proper_name in entry[0]: + logger.info("Permanently updated info.csv field name: '%s' to '%s'."%(entry[0],proper_name)) + entry[0]=proper_name + break + + new_info = '' + for e in meta_info: + new_info += e[0]+'\t'+e[1]+'\n' + + with open(data_dir + "/info.csv",'w') as info: + info.write(new_info) + +def convert_gaze_pos(gaze_list,capture_version): + ''' + util fn to update old gaze pos files to new coordsystem. UNTESTED! + ''' + #let make a copy here so that we are not making inplace edits of the passed array + gaze_list = gaze_list.copy() + if capture_version < .36: + logger.info("Gaze list is from old Recoding, I will update the data to work with new coordinate system.") + gaze_list[:,4:] += 1. #broadcasting + gaze_list[:,4:] /= 2. #broadcasting + return gaze_list + + +def transparent_cirlce(img,center,radius,color,thickness): + center = tuple(map(int,center)) + if thickness > 0: + pad = radius + 2 + thickness + else: + pad = radius + 3 + roi = slice(center[1]-pad,center[1]+pad),slice(center[0]-pad,center[0]+pad) + + try: + overlay = img[roi].copy() + cv2.circle(overlay,(pad,pad), radius=radius, color=color[:3], thickness=thickness, lineType=cv2.cv.CV_AA) + opacity = color[-1]/255. + cv2.addWeighted(overlay, opacity, img[roi], 1. - opacity, 0, img[roi]) + except: + logger.debug("transparent_cirlce would have been partially outise of img. Did not draw it.") + + diff --git a/pupil_src/player/scan_path.py b/pupil_src/player/scan_path.py new file mode 100644 index 0000000000..05d76c1c93 --- /dev/null +++ b/pupil_src/player/scan_path.py @@ -0,0 +1,136 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +import cv2 +from plugin import Plugin +import numpy as np +import atb +from ctypes import c_float +from methods import denormalize,normalize +import logging +logger = logging.getLogger(__name__) + +class Scan_Path(Plugin): + """docstring + using this plugin will extend the recent_pupil_positions by x extra dots from previous frames. + lock recent gaze points onto pixels. + """ + + def __init__(self, g_pool=None,timeframe=1.): + super(Scan_Path, self).__init__() + + #let the plugin work after most other plugins. + self.order = .6 + + #user settings + self.timeframe = c_float(float(timeframe)) + + #algorithm working data + self.prev_frame_idx = -1 + self.past_pupil_positions = [] + self.prev_gray = None + + + #gui + self.timeframe = c_float(3.) + + def update(self,frame,recent_pupil_positions,events): + img = frame.img + img_shape = img.shape[:-1][::-1] # width,height + + succeeding_frame = frame.index-self.prev_frame_idx == 1 + same_frame = frame.index == self.prev_frame_idx + gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + + + #vars for calcOpticalFlowPyrLK + lk_params = dict( winSize = (90, 90), + maxLevel = 3, + criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 20, 0.03)) + + + updated_past_gaze = [] + + #lets update past gaze using optical flow: this is like sticking the gaze points onto the pixels of the img. + if self.past_pupil_positions and succeeding_frame: + past_screen_gaze = np.array([denormalize(ng['norm_gaze'] ,img_shape,flip_y=True) for ng in self.past_pupil_positions],dtype=np.float32) + new_pts, status, err = cv2.calcOpticalFlowPyrLK(self.prev_gray, gray_img,past_screen_gaze,minEigThreshold=0.005,**lk_params) + + for gaze,new_gaze_pt,s,e in zip(self.past_pupil_positions,new_pts,status,err): + if s: + # print "norm,updated",gaze['norm_gaze'], normalize(new_gaze_pt,img_shape[:-1],flip_y=True) + gaze['norm_gaze'] = normalize(new_gaze_pt,img_shape,flip_y=True) + updated_past_gaze.append(gaze) + # logger.debug("updated gaze") + + else: + # logger.debug("dropping gaze") + # Since we will replace self.past_pupil_positions later, + # not appedning tu updated_past_gaze is like deliting this data point. + pass + else: + # we must be seeking, do not try to do optical flow, or pausing: see below. + pass + + if same_frame: + # re-use last result + recent_pupil_positions[:] = self.past_pupil_positions + else: + # trim gaze that is too old + if recent_pupil_positions: + now = recent_pupil_positions[0]['timestamp'] + cutof = now-self.timeframe.value + updated_past_gaze = [g for g in updated_past_gaze if g['timestamp']>cutof] + + #inject the scan path gaze points into recent_pupil_positions + recent_pupil_positions[:] = updated_past_gaze + recent_pupil_positions + recent_pupil_positions.sort(key=lambda x: x['timestamp']) #this may be redundant... + + + #update info for next frame. + self.prev_gray = gray_img + self.prev_frame_idx = frame.index + self.past_pupil_positions = recent_pupil_positions + + + def init_gui(self,pos=None): + pos = 10,390 + import atb + from time import time + + atb_label = "Scan Path" + self._bar = atb.Bar(name =self.__class__.__name__+str(id(self)), label=atb_label, + help="polyline", color=(50, 50, 50), alpha=100, + text='light', position=pos,refresh=.1, size=(300, 70)) + + self._bar.add_var('duration in sec',self.timeframe,min=0) + + + self._bar.add_button('remove',self.unset_alive) + + def unset_alive(self): + self.alive = False + + + def get_init_dict(self): + return {'timeframe':self.timeframe.value} + + + def clone(self): + return Scan_Path(**self.get_init_dict()) + + + def cleanup(self): + """ called when the plugin gets terminated. + This happends either voluntary or forced. + if you have an atb bar or glfw window destroy it here. + """ + self._bar.destroy() + diff --git a/pupil_src/player/seek_bar.py b/pupil_src/player/seek_bar.py new file mode 100644 index 0000000000..2fd79886a6 --- /dev/null +++ b/pupil_src/player/seek_bar.py @@ -0,0 +1,123 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +from gl_utils import draw_gl_polyline,draw_gl_point +from OpenGL.GL import * +from OpenGL.GLU import gluOrtho2D + +from glfw import glfwGetWindowSize,glfwGetCurrentContext,glfwGetCursorPos,GLFW_RELEASE,GLFW_PRESS +from plugin import Plugin +import numpy as np + +from methods import denormalize +import logging +logger = logging.getLogger(__name__) + +class Seek_Bar(Plugin): + """docstring for Seek_Bar + seek bar displays a bar at the bottom of the screen when you hover close to it. + it will show the current positon and allow you to drag to any postion in the video file. + + """ + def __init__(self, g_pool,capture): + super(Seek_Bar, self).__init__() + self.g_pool = g_pool + self.cap = capture + self.current_frame_index = self.cap.get_frame_index() + self.frame_count = self.cap.get_frame_count() + + self.norm_seek_pos = self.current_frame_index/float(self.frame_count) + self.drag_mode = False + self.was_playing = True + #display layout + self.padding = 20. + + def update(self,frame,recent_pupil_positions,events): + self.current_frame_index = frame.index + self.norm_seek_pos = self.current_frame_index/float(self.frame_count) + + if self.drag_mode: + pos = glfwGetCursorPos(glfwGetCurrentContext()) + norm_seek_pos, _ = self.screen_to_seek_bar(pos) + norm_seek_pos = min(1,max(0,norm_seek_pos)) + if abs(norm_seek_pos-self.norm_seek_pos) >=.01: + seek_pos = int(norm_seek_pos*self.frame_count) + self.cap.seek_to_frame(seek_pos) + self.g_pool.new_seek = True + + + def on_click(self,img_pos,button,action): + """ + gets called when the user clicks in the window screen + """ + pos = glfwGetCursorPos(glfwGetCurrentContext()) + #drag the seek point + if action == GLFW_PRESS: + screen_seek_pos = self.seek_bar_to_screen((self.norm_seek_pos,0)) + dist = abs(pos[0]-screen_seek_pos[0])+abs(pos[1]-screen_seek_pos[1]) + if dist < 20: + self.drag_mode=True + self.was_playing = self.g_pool.play + self.g_pool.play = False + + elif action == GLFW_RELEASE: + if self.drag_mode: + norm_seek_pos, _ = self.screen_to_seek_bar(pos) + norm_seek_pos = min(1,max(0,norm_seek_pos)) + seek_pos = int(norm_seek_pos*self.frame_count) + self.cap.seek_to_frame(seek_pos) + self.g_pool.new_seek = True + self.drag_mode=False + self.g_pool.play = self.was_playing + + + def seek_bar_to_screen(self,pos): + width,height = glfwGetWindowSize(glfwGetCurrentContext()) + x,y=pos + y = 1-y + x = x*(width-2*self.padding)+self.padding + y = y*(height-2*self.padding)+self.padding + return x,y + + + def screen_to_seek_bar(self,pos): + width,height = glfwGetWindowSize(glfwGetCurrentContext()) + x,y=pos + x = (x-self.padding)/(width-2*self.padding) + y = (y-self.padding)/(height-2*self.padding) + return x,1-y + + def gl_display(self): + + glMatrixMode(GL_PROJECTION) + glPushMatrix() + glLoadIdentity() + width,height = glfwGetWindowSize(glfwGetCurrentContext()) + h_pad = self.padding/width + v_pad = self.padding/height + gluOrtho2D(-h_pad, 1+h_pad, -v_pad, 1+v_pad) # gl coord convention + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glLoadIdentity() + + if self.drag_mode: + color = (0.,.8,.5,1.) + else: + color = (.1,.8,.8,1.) + + draw_gl_polyline( [(0,0),(1,0)],color=(.5,.5,.5,.5)) + draw_gl_point((self.norm_seek_pos,0),color=color) + + glMatrixMode(GL_PROJECTION) + glPopMatrix() + glMatrixMode(GL_MODELVIEW) + glPopMatrix() + + diff --git a/pupil_src/player/vis_circle.py b/pupil_src/player/vis_circle.py new file mode 100644 index 0000000000..bc22b6f91b --- /dev/null +++ b/pupil_src/player/vis_circle.py @@ -0,0 +1,82 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +from gl_utils import draw_gl_points_norm +from player_methods import transparent_cirlce +from plugin import Plugin +import numpy as np +from ctypes import c_int,c_float,c_bool +import cv2 + +from methods import denormalize + +class Vis_Circle(Plugin): + """docstring for DisplayGaze""" + def __init__(self, g_pool=None,radius=20,color=(1.,.2,.4,.5),thickness=1,full=False): + super(Vis_Circle, self).__init__() + self.g_pool = g_pool + self.order = .9 + + + self.radius = c_int(int(radius)) + self.color = (c_float*4)(*color) + self.thickness = c_int(int(thickness)) + self.full = c_bool(bool(full)) + + + def update(self,frame,recent_pupil_positions,events): + color = map(lambda x:int(x*255),self.color) + color = color[:3][::-1]+color[-1:] + if self.full.value: + thickness= -1 + else: + thickness = self.thickness.value + + radius = self.radius.value + pts = [denormalize(pt['norm_gaze'],frame.img.shape[:-1][::-1],flip_y=True) for pt in recent_pupil_positions if pt['norm_gaze'] is not None] + for pt in pts: + transparent_cirlce(frame.img, pt, radius=radius, color=color, thickness=thickness) + + def init_gui(self,pos=None): + pos = 10,200 + import atb + from time import time + atb_label = "Gaze Circle" + self._bar = atb.Bar(name =self.__class__.__name__+str(id(self)), label=atb_label, + help="circle", color=(50, 50, 50), alpha=100, + text='light', position=pos,refresh=.1, size=(300, 100)) + + self._bar.add_var('color',self.color) + self._bar.add_var('radius',self.radius) + self._bar.add_var('thickness',self.thickness) + self._bar.add_var('full',self.full) + self._bar.add_button('remove',self.unset_alive) + + def unset_alive(self): + self.alive = False + + def gl_display(self): + pass + + def get_init_dict(self): + return {'radius':self.radius.value,'color':self.color[:],'thickness':self.thickness.value,'full':self.full.value} + + def clone(self): + return Vis_Circle(**self.get_init_dict()) + + + def cleanup(self): + """ called when the plugin gets terminated. + This happends either voluntary or forced. + if you have an atb bar or glfw window destroy it here. + """ + self._bar.destroy() + + diff --git a/pupil_src/player/vis_light_points.py b/pupil_src/player/vis_light_points.py new file mode 100644 index 0000000000..e3e2d37423 --- /dev/null +++ b/pupil_src/player/vis_light_points.py @@ -0,0 +1,91 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +import cv2 +from plugin import Plugin +import numpy as np +import atb +from ctypes import c_int +from methods import denormalize +import logging +logger = logging.getLogger(__name__) + +class Vis_Light_Points(Plugin): + """docstring + show gaze dots at light dots on numpy. + + """ + #let the plugin work after most other plugins. + + def __init__(self, g_pool=None): + super(Vis_Light_Points, self).__init__() + self.order = .8 + + def update(self,frame,recent_pupil_positions,events): + + #since we edit the img inplace we should not do it in pause mode... + img = frame.img + img_shape = img.shape[:-1][::-1]#width,height + norm_gaze = [ng['norm_gaze'] for ng in recent_pupil_positions if ng['norm_gaze'] is not None] + screen_gaze = [denormalize(ng,img_shape,flip_y=True) for ng in norm_gaze] + + + overlay = np.ones(img.shape[:-1],dtype=img.dtype) + + # draw recent gaze postions as black dots on an overlay image. + for gaze_point in screen_gaze: + try: + overlay[int(gaze_point[1]),int(gaze_point[0])] = 0 + except: + pass + + out = cv2.distanceTransform(overlay,cv2.cv.CV_DIST_L2, 5) + + # fix for opencv binding incositency + if type(out)==tuple: + out = out[0] + + overlay = 1/(out/20+1) + + img *= cv2.cvtColor(overlay,cv2.COLOR_GRAY2RGB) + + + def init_gui(self,pos=None): + pos = 10,470 + import atb + from time import time + + atb_label = "Light Points" + self._bar = atb.Bar(name =self.__class__.__name__+str(id(self)), label=atb_label, + help="circle", color=(50, 50, 50), alpha=100, + text='light', position=pos,refresh=.1, size=(300, 20)) + + self._bar.add_button('remove',self.unset_alive) + + def unset_alive(self): + self.alive = False + + + def get_init_dict(self): + return {} + + def clone(self): + return Vis_Light_Points(**self.get_init_dict()) + + + + + + def cleanup(self): + """ called when the plugin gets terminated. + This happends either voluntary or forced. + if you have an atb bar or glfw window destroy it here. + """ + self._bar.destroy() diff --git a/pupil_src/player/vis_polyline.py b/pupil_src/player/vis_polyline.py new file mode 100644 index 0000000000..a9da14f6d3 --- /dev/null +++ b/pupil_src/player/vis_polyline.py @@ -0,0 +1,71 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +from gl_utils import draw_gl_points_norm +from plugin import Plugin +import numpy as np +from ctypes import c_int,c_float,c_bool + +import cv2 + +from methods import denormalize + +class Vis_Polyline(Plugin): + """docstring for DisplayGaze""" + def __init__(self, g_pool=None,color =(1.,.2,.4),thickness=1): + super(Vis_Polyline, self).__init__() + self.order = .9 + self.color = (c_float*3)(*color) + self.thickness = c_int(int(thickness)) + + def update(self,frame,recent_pupil_positions,events): + color = map(lambda x:int(x*255),self.color) + color = color[::-1] + + thickness = self.thickness.value + + pts = [denormalize(pt['norm_gaze'],frame.img.shape[:-1][::-1],flip_y=True) for pt in recent_pupil_positions if pt['norm_gaze'] is not None] + if pts: + pts = np.array([pts],dtype=np.int32) + cv2.polylines(frame.img, pts, isClosed=False, color=color, thickness=thickness, lineType=cv2.cv.CV_AA) + + + + + def init_gui(self,pos=None): + pos = 10,310 + import atb + atb_label = "Gaze Polyline" + from time import time + self._bar = atb.Bar(name = self.__class__.__name__+str(id(self)), label=atb_label, + help="polyline", color=(50, 50, 50), alpha=100, + text='light', position=pos,refresh=.1, size=(300, 70)) + + self._bar.add_var('color',self.color) + self._bar.add_var('thickness',self.thickness,min=1) + self._bar.add_button('remove',self.unset_alive) + + def unset_alive(self): + self.alive = False + + def get_init_dict(self): + return {'color':self.color[:],'thickness':self.thickness.value} + + def clone(self): + return Vis_Polyline(**self.get_init_dict()) + + + + def cleanup(self): + """ called when the plugin gets terminated. + This happends either voluntary or forced. + if you have an atb bar or glfw window destroy it here. + """ + self._bar.destroy() \ No newline at end of file diff --git a/pupil_src/shared_modules/display_recent_gaze.py b/pupil_src/shared_modules/display_recent_gaze.py new file mode 100644 index 0000000000..17d76b2e99 --- /dev/null +++ b/pupil_src/shared_modules/display_recent_gaze.py @@ -0,0 +1,35 @@ +''' +(*)~---------------------------------------------------------------------------------- + Pupil - eye tracking platform + Copyright (C) 2012-2013 Moritz Kassner & William Patera + + Distributed under the terms of the CC BY-NC-SA License. + License details are in the file license.txt, distributed as part of this software. +----------------------------------------------------------------------------------~(*) +''' + +from gl_utils import draw_gl_points_norm +from plugin import Plugin +import numpy as np + +from methods import denormalize + +class Display_Recent_Gaze(Plugin): + """docstring for DisplayGaze""" + def __init__(self, g_pool,atb_pos=None): + super(Display_Recent_Gaze, self).__init__() + self.g_pool = g_pool + self.order = .8 + self.atb_pos = atb_pos + self.pupil_display_list = [] + + def update(self,frame,recent_pupil_positions,events): + for pt in recent_pupil_positions: + if pt['norm_gaze'] is not None: + self.pupil_display_list.append(pt['norm_gaze']) + self.pupil_display_list[:-3] = [] + + + def gl_display(self): + draw_gl_points_norm(self.pupil_display_list,size=35,color=(1.,.2,.4,.6)) + diff --git a/pupil_src/shared_modules/gl_utils/utils.py b/pupil_src/shared_modules/gl_utils/utils.py index 557edb9373..0bdb972790 100644 --- a/pupil_src/shared_modules/gl_utils/utils.py +++ b/pupil_src/shared_modules/gl_utils/utils.py @@ -64,8 +64,8 @@ def adjust_gl_view(w,h): glMatrixMode(GL_MODELVIEW) glLoadIdentity() -def draw_gl_polyline((positions),(r,g,b,a),type='Loop'): - glColor4f(r,g,b,a) +def draw_gl_polyline((positions),color,type='Loop'): + glColor4f(*color) if type=='Loop': glBegin(GL_LINE_LOOP) elif type=='Strip': @@ -78,7 +78,8 @@ def draw_gl_polyline((positions),(r,g,b,a),type='Loop'): glVertex3f(x,y,0.0) glEnd() -def draw_gl_polyline_norm((positions),(r,g,b,a),type='Loop'): +def draw_gl_polyline_norm((positions),color,type='Loop'): + glMatrixMode(GL_PROJECTION) glPushMatrix() glLoadIdentity() @@ -86,7 +87,7 @@ def draw_gl_polyline_norm((positions),(r,g,b,a),type='Loop'): glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() - draw_gl_polyline(positions,(r,g,b,a),type) + draw_gl_polyline(positions,color,type) glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) diff --git a/pupil_src/shared_modules/methods.py b/pupil_src/shared_modules/methods.py index 9db2e5d906..6f618d2f70 100644 --- a/pupil_src/shared_modules/methods.py +++ b/pupil_src/shared_modules/methods.py @@ -627,6 +627,7 @@ def filter_subsets(l): return [m for i, m in enumerate(l) if not any(set(m).issubset(set(n)) for n in (l[:i] + l[i+1:]))] + if __name__ == '__main__': # tst = [] # for x in range(10): diff --git a/pupil_src/shared_modules/plugin.py b/pupil_src/shared_modules/plugin.py index 2caa6e74ed..ea4de92fb9 100644 --- a/pupil_src/shared_modules/plugin.py +++ b/pupil_src/shared_modules/plugin.py @@ -20,6 +20,14 @@ class Plugin(object): def __init__(self): self._alive = True + self.order = .5 + # between 0 and 1 this indicated where in the plugin excecution order you plugin lives: + # <.5 are things that add/mofify information that will be used by other plugins and rely on untouched data. + # You should not edit frame.img if you are here! + # == 5 is the default. + # >.5 are things that depend on other plugins work like display , saving and streaming + + @property def alive(self): """This field indicates of the instance should be detroyed @@ -44,15 +52,18 @@ def on_click(self,pos,button,action): def update(self,frame,recent_pupil_positions,events): """ gets called once every frame + if you plan to update the image data, note that this will affact all plugins axecuted after you. + Use self.order to deal with this appropriatly """ pass def gl_display(self): """ - gets called once every frame + gets called once every frame when its time to draw onto the gl canvas. """ pass + def cleanup(self): """gets called when the plugin get terminated. This happends either volunatily or forced. @@ -60,6 +71,10 @@ def cleanup(self): """ pass + def get_class_name(self): + return self.__class__.__name__ + + def __del__(self): self._alive = False @@ -133,12 +148,8 @@ def open_window(self): gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) gl.glEnable(gl.GL_BLEND) gl.glClearColor(1.,1.,1.,0.) - - # refresh speed settings glfwSwapInterval(0) - glfwMakeContextCurrent(active_window) - self.window_should_open = False diff --git a/pupil_src/shared_modules/uvc_capture/__init__.py b/pupil_src/shared_modules/uvc_capture/__init__.py index 43ae304a09..974e87930c 100644 --- a/pupil_src/shared_modules/uvc_capture/__init__.py +++ b/pupil_src/shared_modules/uvc_capture/__init__.py @@ -20,7 +20,7 @@ - on MacOS: uvcc (binary is distributed with this module) """ import os,sys -from cv2 import VideoCapture +import cv2 import numpy as np from os.path import isfile from time import time @@ -45,59 +45,105 @@ # non os specific defines class Frame(object): """docstring of Frame""" - def __init__(self, timestamp,img,compressed_img=None, compressed_pix_fmt=None): + def __init__(self, timestamp,img,index=None,compressed_img=None, compressed_pix_fmt=None): self.timestamp = timestamp + self.index = index self.img = img self.compressed_img = compressed_img self.compressed_pix_fmt = compressed_pix_fmt + def copy(self): + return Frame(self.timestamp,self.img.copy(),self.index) class FileCapture(): """ - simple file capture that can auto_rewind + simple file capture. """ - def __init__(self,src): + def __init__(self,src,timestamps=None): self.auto_rewind = True self.controls = None #No UVC controls available with file capture # we initialize the actual capture based on cv2.VideoCapture - self.cap = VideoCapture(src) - timestamps_loc = os.path.join(src.rsplit(os.path.sep,1)[0],'eye_timestamps.npy') - logger.info("trying to load timestamps with video at: %s"%timestamps_loc) + self.cap = cv2.VideoCapture(src) + if timestamps is None: + timestamps_loc = os.path.join(src.rsplit(os.path.sep,1)[0],'eye_timestamps.npy') + logger.debug("trying to auto load eye_video timestamps with video at: %s"%timestamps_loc) + else: + timestamps_loc = timestamps + logger.debug("trying to load supplied timestamps with video at: %s"%timestamps_loc) try: self.timestamps = np.load(timestamps_loc).tolist() - logger.info("loaded %s timestamps"%len(self.timestamps)) + logger.debug("loaded %s timestamps"%len(self.timestamps)) except: - logger.info("did not find timestamps") + logger.debug("did not find timestamps") self.timestamps = None - self._get_frame_ = self.cap.read def get_size(self): - return self.cap.get(3),self.cap.get(4) + width,height = int(self.cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)),int(self.cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)) + if width == 0: + logger.error("Could not load media size info.") + return width,height def set_fps(self): - pass + logger.warning("You cannot set the Framerate on this File Capture") def get_fps(self): - return None + fps = self.cap.get(cv2.cv.CV_CAP_PROP_FPS) + if fps == 0: + logger.error("Could not load media framerate info.") + return fps - def read(self): - s, img =self._get_frame_() - if self.auto_rewind and not s: - self.rewind() - s, img = self._get_frame_() - return s,img + def get_frame_index(self): + return int(self.cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)) + + def get_frame_count(self): + if self.timestamps is None: + logger.warning("No timestamps file loaded with this recording cannot get framecount") + return None + return len(self.timestamps) def get_frame(self): - s, img = self.read() + idx = int(self.cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)) + s, img = self.cap.read() + if not s: + logger.warning("Reached end of video file.") + return None if self.timestamps: - timestamp = self.timestamps.pop(0) + try: + timestamp = self.timestamps[idx] + except IndexError: + logger.warning("Reached end of timestamps list.") + return None else: timestamp = time() - return Frame(timestamp,img) - - def rewind(self): - self.cap.set(1,0) #seek to the beginning + return Frame(timestamp,img,index=idx) + + def seek_to_frame(self, seek_pos): + logger.debug("seeking to frame: %s"%seek_pos) + if self.cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES,seek_pos): + return True + logger.error("Could not perform seek on cv2.VideoCapture. Command gave negative return.") + return False + + + def seek_to_frame_prefetch(self, seek_pos): + prefetch = 10 + logger.debug("seeking to frame: %s"%seek_pos) + if self.cap.set(cv2.cv.CV_CAP_PROP_POS_FRAMES,int(seek_pos)-prefetch): + while self.cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) < seek_pos: + print "seek:",self.cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) + s,_=self.cap.read() + if not s: + logger.error("Could not seek to position %s" %seek_pos) + return + prefetch -=1 + if prefetch < -10: + logger.error("Could not seek to position %s stepped out of prefetch" %seek_pos) + return + logger.debug("Sucsessful seek to %s" %self.cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)) + return True + logger.error("Could not perform seek on cv2.VideoCapture. Command gave negative return.") + return def create_atb_bar(self,pos): return 0,0 @@ -109,7 +155,7 @@ def close(self): pass -def autoCreateCapture(src,size=(640,480),fps=30): +def autoCreateCapture(src,size=(640,480),fps=30,timestamps=None): # checking src and handling all cases: src_type = type(src) @@ -150,7 +196,7 @@ def autoCreateCapture(src,size=(640,480),fps=30): logger.error('Could not locate VideoFile %s'%src) return logger.info("Using %s as video source"%src) - return FileCapture(src) + return FileCapture(src,timestamps=timestamps) else: raise Exception("autoCreateCapture: Could not create capture, wrong src_type") diff --git a/pupil_src/simple_player/blurred_circle_history.py b/pupil_src/simple_player/blurred_circle_history.py index 7e5e3bdb52..5d64d37def 100644 --- a/pupil_src/simple_player/blurred_circle_history.py +++ b/pupil_src/simple_player/blurred_circle_history.py @@ -17,9 +17,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize - try: data_folder = sys.argv[1] except: @@ -36,19 +33,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -177,18 +161,5 @@ def denormalize(pos, width, height, flip_y=True): y *= height return int(x),int(y) - -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/blurred_circle_history_fixations_only.py b/pupil_src/simple_player/blurred_circle_history_fixations_only.py index 330d128527..eb36c58864 100644 --- a/pupil_src/simple_player/blurred_circle_history_fixations_only.py +++ b/pupil_src/simple_player/blurred_circle_history_fixations_only.py @@ -17,9 +17,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize - # manhattan_dist variable used to check for false positives manhattan_dist = 20 @@ -39,15 +36,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) @@ -196,18 +184,5 @@ def denormalize(pos, width, height, flip_y=True): y *= height return int(x),int(y) - -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/fixations_only.py b/pupil_src/simple_player/fixations_only.py index 0ecc9a2c74..379fad5472 100644 --- a/pupil_src/simple_player/fixations_only.py +++ b/pupil_src/simple_player/fixations_only.py @@ -18,9 +18,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize - try: data_folder = sys.argv[1] except: @@ -38,15 +35,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -189,18 +177,5 @@ def denormalize(pos, width, height, flip_y=True): y *= height return int(x),int(y) - -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/light_points_distance_transform.py b/pupil_src/simple_player/light_points_distance_transform.py index d205df82ed..f153a93526 100644 --- a/pupil_src/simple_player/light_points_distance_transform.py +++ b/pupil_src/simple_player/light_points_distance_transform.py @@ -18,9 +18,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize - try: data_folder = sys.argv[1] except: @@ -39,17 +36,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -175,17 +161,5 @@ def denormalize(pos, width, height, flip_y=True): return int(x),int(y) -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/light_points_distance_transform_fixations_only.py b/pupil_src/simple_player/light_points_distance_transform_fixations_only.py index c2b1d0b240..abf5f08401 100644 --- a/pupil_src/simple_player/light_points_distance_transform_fixations_only.py +++ b/pupil_src/simple_player/light_points_distance_transform_fixations_only.py @@ -18,8 +18,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize # manhattan_dist variable used to check for false positives manhattan_dist = 20 @@ -42,17 +40,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -199,18 +186,5 @@ def denormalize(pos, width, height, flip_y=True): return int(x),int(y) -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/polyline.py b/pupil_src/simple_player/polyline.py index ad7581072f..8d70b3c8d9 100644 --- a/pupil_src/simple_player/polyline.py +++ b/pupil_src/simple_player/polyline.py @@ -17,9 +17,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize - try: data_folder = sys.argv[1] except: @@ -38,17 +35,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -185,18 +171,5 @@ def denormalize(pos, width, height, flip_y=True): return int(x),int(y) -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/polyline_fixations_only.py b/pupil_src/simple_player/polyline_fixations_only.py index fc2ddb5c4b..b9b9320411 100644 --- a/pupil_src/simple_player/polyline_fixations_only.py +++ b/pupil_src/simple_player/polyline_fixations_only.py @@ -17,8 +17,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize # manhattan_dist variable used to check for false positives manhattan_dist = 20 @@ -41,17 +39,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -186,18 +173,5 @@ def denormalize(pos, width, height, flip_y=True): return int(x),int(y) -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/save_svg.py b/pupil_src/simple_player/save_svg.py index 60911e196d..32b8895011 100644 --- a/pupil_src/simple_player/save_svg.py +++ b/pupil_src/simple_player/save_svg.py @@ -19,9 +19,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize - try: data_folder = sys.argv[1] except: @@ -38,17 +35,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -107,7 +93,8 @@ def main(): gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) prevPts = np.array(past_gaze,dtype=np.float32) nextPts = prevPts.copy() - nextPts, status, err = cv.calcOpticalFlowPyrLK(prevgray, gray,prevPts,nextPts) prevgray = gray + nextPts, status, err = cv.calcOpticalFlowPyrLK(prevgray, gray,prevPts,nextPts) + prevgray = gray past_gaze = list(nextPts) #contrain gaze positions to @@ -175,18 +162,5 @@ def denormalize(pos, width, height, flip_y=True): y *= height return int(x),int(y) - -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/simple_circle.py b/pupil_src/simple_player/simple_circle.py index 11270bd12a..22053b2352 100644 --- a/pupil_src/simple_player/simple_circle.py +++ b/pupil_src/simple_player/simple_circle.py @@ -13,16 +13,8 @@ def main(): - save_video = False - # global denormalize for legacy support - global denormalize - - if getattr(sys, 'frozen', False): - save_video = True - - try: data_folder = sys.argv[1] except: @@ -40,17 +32,6 @@ def main(): record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - - if version < 36: - print "under 36" - denormalize = denormalize_legacy - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) timestamps = list(np.load(timestamps_path)) @@ -129,17 +110,5 @@ def denormalize(pos, width, height, flip_y=True): return int(x),int(y) -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - if __name__ == '__main__': main() \ No newline at end of file diff --git a/pupil_src/simple_player/simple_circle_fixations_only.py b/pupil_src/simple_player/simple_circle_fixations_only.py index d30ef4ac4f..f11e87b426 100644 --- a/pupil_src/simple_player/simple_circle_fixations_only.py +++ b/pupil_src/simple_player/simple_circle_fixations_only.py @@ -18,8 +18,6 @@ def main(): save_video = False - # global denormalize for legacy support - global denormalize # manhattan_dist variable used to check for false positives manhattan_dist = 25 @@ -41,17 +39,6 @@ def main(): gaze_positions_path = data_folder + "/gaze_positions.npy" record_path = data_folder + "/world_viz.avi" - #deal with older recordings that use a different coodinate system. - with open(data_folder + "/info.csv") as info: - data = dict( ((line.strip().split('\t')) for line in info.readlines() ) ) - - version = [v for k,v in data.iteritems() if "Capture Software Version" in k ][0] - version = int(filter(type(version).isdigit, version)[:3]) #(get major,minor,fix of version) - if version < 36: - denormalize = denormalize_legacy - - - cap = cv.VideoCapture(video_path) gaze_list = list(np.load(gaze_positions_path)) @@ -176,18 +163,5 @@ def denormalize(pos, width, height, flip_y=True): return int(x),int(y) -def denormalize_legacy(pos, width, height, flip_y=True): - """ - denormalize and return as int - """ - x = pos[0] - y = pos[1] - if flip_y: - y=-y - x = (x * width / 2.) + (width / 2.) - y = (y * height / 2.) + (height / 2.) - return int(x), int(y) - - if __name__ == '__main__': main() \ No newline at end of file