diff --git a/.gitignore b/.gitignore index 2fe6848..32bbe7c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ *.mat dist -build \ No newline at end of file +build +MANIFEST diff --git a/MANIFEST b/MANIFEST deleted file mode 100644 index 2311d0c..0000000 --- a/MANIFEST +++ /dev/null @@ -1,3 +0,0 @@ -# file GENERATED by distutils, do NOT edit -setup.py -src/__init__.py diff --git a/README.md b/README.md index c6b56e4..4350f31 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # python-aer-lib -Set of functions to read AER data freely in python (aedat extention) +Set of functions and classes to read Address Event Representation data freely in python (aedat extention) ## Install @@ -9,6 +9,6 @@ Dependencies (all via pip) - matplotlib - Pillow -`pip install git+git://github.com/darioml/pAER-python-aer-lib` +`pip install git+git://github.com/bio-modelling/py-aer` ***Coming to PyPI soon*** diff --git a/examples/all_four_directions.py b/examples/all_four_directions.py index 8c81e00..0b61764 100644 --- a/examples/all_four_directions.py +++ b/examples/all_four_directions.py @@ -1,51 +1,70 @@ -import paer +"""Load data from four directions in separate files""" + +from __future__ import print_function import numpy as np +import pyaer -base_dir = '/path/to/some/dir/' -file1 = 'left_to_right_1.aedat' -file2 = 'right_to_left_1.aedat' -file3 = 'top_to_bottom_1.aedat' -file4 = 'bottom_to_top_1.aedat' +BASE_DIR = '/path/to/some/dir/' +FILE1 = 'left_to_right_1.aedat' +FILE2 = 'right_to_left_1.aedat' +FILE3 = 'top_to_bottom_1.aedat' +FILE4 = 'bottom_to_top_1.aedat' # Each ball movement should be .5s long -animation_time = 0.2 +ANIMATION_TIME = 0.2 + +# 3,280 events per second for 16*16 is reasonable +# for ball movement (might be even too high!) +NUM_EVENTS_P_S = 3280 + -# 3,280 events per second for 16*16 is reasonable for ball movement (might be even too high!) -num_events_p_s = 3280 +def get_data(file, min, max, animation_time=ANIMATION_TIME, + num_events=NUM_EVENTS_P_S*ANIMATION_TIME, offset=0): + """ + Helper function to read a file. -# Helper function to read a file. Given (min,max) which are data ranges for extraction, this will return a cropped and -# suitably sparse output. -def get_data(file, min, max, animation_time=animation_time, num_events=num_events_p_s*animation_time, offset=0): - aefile = paer.aefile(file, max_events=max+1) - aedata = paer.aedata(aefile) - print 'Points: %i, Time: %0.2f. Sparsity: %i' % (len(aefile.data), (aefile.timestamp[-1]-aefile.timestamp[0])/1000000, - np.floor(len(aefile.data)/num_events)) + Given (min,max) which are data ranges for extraction, this will return a + cropped and suitable sparse output. + """ + aefile = pyaer.AEFile(file, max_events=max+1) + aedata = pyaer.AEData(aefile) + print("Points: {}, Time: {:0.2f}, Sparsity: {}".format( + len(aefile.data), + (aefile.timestamp[-1] - aefile.timestamp[0]) / 1000000, + np.floor(len(aefile.data)/num_events))) sparse = aedata[min:max].make_sparse(np.floor(len(aefile.data)/num_events)) actual_time = (sparse.ts[-1]-sparse.ts[0])/1000000 scale = actual_time/animation_time - sparse.ts = (offset * 1000000) + np.round((sparse.ts-sparse.ts[0])/scale) - # print sparse_ts[0], sparse_ts[-1], sparse_ts[-1]-sparse_ts[0], (sparse_ts[-1]-sparse_ts[0])/1000000 + sparse.ts = ((offset * 1000000) + + np.round((sparse.timestamp - sparse.timestamp[0]) / scale)) return sparse -# Loop through all files - indexes are extrapolated. -d1 = get_data(file1, 300000, 750000, offset=0*animation_time) -d2 = get_data(file2, 300000, 600000, offset=1*animation_time) -d3 = get_data(file3, 85000, 140000, offset=2*animation_time) -d4 = get_data(file4, 65200, 131800, offset=3*animation_time) -# Need to pre-load a file, to get the correct headers when writing! -lib = paer.aefile(file1, max_events=1) +def main(): + """Entry point of example""" + # Loop through all files - indexes are extrapolated. + data1 = get_data(FILE1, 300000, 750000, offset=0*ANIMATION_TIME) + data2 = get_data(FILE2, 300000, 600000, offset=1*ANIMATION_TIME) + data3 = get_data(FILE3, 85000, 140000, offset=2*ANIMATION_TIME) + data4 = get_data(FILE4, 65200, 131800, offset=3*ANIMATION_TIME) + + # Need to pre-load a file, to get the correct headers when writing! + lib = pyaer.AEFile(FILE1, max_events=1) + + final = pyaer.concatenate((data1, data2, data3, data4)) + final_16 = final.downsample((16, 16)) + + lib.save(final, 'test.aedat') + lib.save(final_16, 'test_16.aedat') -final = paer.concatenate( (d1,d2,d3,d4) ) -final_16 = final.downsample((16,16)) + data1.save_to_mat('test_1.mat') + data2.save_to_mat('test_2.mat') + data3.save_to_mat('test_3.mat') + data4.save_to_mat('test_4.mat') -lib.save(final, 'test.aedat') -lib.save(final_16, 'test_16.aedat') -d1.save_to_mat('test_1.mat') -d2.save_to_mat('test_2.mat') -d3.save_to_mat('test_3.mat') -d4.save_to_mat('test_4.mat') \ No newline at end of file +if __name__ == '__main__': + main() diff --git a/examples/animate_dvs.py b/examples/animate_dvs.py index 411c4b1..bd3883e 100644 --- a/examples/animate_dvs.py +++ b/examples/animate_dvs.py @@ -1,6 +1,14 @@ -import paer +"""Example of DVS animation""" -data = paer.aedata(paer.aefile('/path/to/left_to_right_1.aedat', max_events=1000000)) +import pyaer -data = data.make_sparse(64).downsample((16,16)) -data.interactive_animation(step=1000,limits=(0,16)) \ No newline at end of file + +def main(): + """Entry point of animation example""" + data = pyaer.AEData(pyaer.AEFile('/path/to/left_to_right_1.aedat', + max_events=1000000)) + data = data.make_sparse(64).downsample((16, 16)) + data.interactive_animation(step=1000, limits=(0, 16)) + +if __name__ == '__main__': + main() diff --git a/examples/downsampling.py b/examples/downsampling.py index 6511cd0..b68084b 100644 --- a/examples/downsampling.py +++ b/examples/downsampling.py @@ -1,11 +1,20 @@ -import paer +"""Example of downsampling AEData from file""" -file = '/path/to/left_to_right_1.aedat' +import pyaer -lib = paer.aefile(file, max_events=750001) -data = paer.aedata(lib) +FILE = '/path/to/left_to_right_1.aedat' -sparse = data[300000:750000].make_sparse(64).downsample((16,16)) -sparse.save_to_mat('downsample_left_to_right_1_1.mat') -lib.save(sparse, 'downsample_left_to_right_1_1.aedat') +def main(): + """Entry point of downsampling example""" + + lib = pyaer.AEFile(FILE, max_events=750001) + data = pyaer.AEData(lib) + + sparse = data[300000:750000].make_sparse(64).downsample((16, 16)) + + sparse.save_to_mat('downsample_left_to_right_1_1.mat') + lib.save(sparse, 'downsample_left_to_right_1_1.aedat') + +if __name__ == '__main__': + main() diff --git a/examples/make_pngs.py b/examples/make_pngs.py index 9cfcfa4..f4b2c93 100644 --- a/examples/make_pngs.py +++ b/examples/make_pngs.py @@ -1,14 +1,23 @@ +"""Example to create PNG files from data""" + from os import listdir from os.path import isfile, join -import paer +import pyaer + +MYPATH = '/path/to/fyp-aedata-matlab' +ONLYFILES = [f for f in listdir(MYPATH) + if isfile(join(MYPATH, f)) and f.endswith('.aedat')] -mypath = '/path/to/fyp-aedata-matlab' -onlyfiles = [ f for f in listdir(mypath) if isfile(join(mypath,f)) and f.endswith('.aedat')] -for file in onlyfiles: - ae = paer.aefile('path/to/fyp-aedata-matlab/' + str(file)) - aed= paer.aedata(ae).downsample((16,16)) +def main(): + """Entry point of PNG file example""" - paer.create_pngs(aed, '16x16_' + str(file) + '_',path='testing_something',step=3000, dim=(16,16)) + for _file in ONLYFILES: + aef = pyaer.AEFile('path/to/fyp-aedata-matlab/' + str(_file)) + aed = pyaer.AEData(aef).downsample((16, 16)) + pyaer.create_pngs(aed, '16x16_' + str(_file) + '_', + path='testing_something', step=3000, dim=(16, 16)) +if __name__ == '__main__': + main() diff --git a/pyaer/AEData.py b/pyaer/AEData.py new file mode 100644 index 0000000..7c4ac2f --- /dev/null +++ b/pyaer/AEData.py @@ -0,0 +1,211 @@ +""" +Author: Dario ML, bdevans, Michael Hart +Program: pyaer/AEData.py +Description: AEData class definition for PyAER +""" + + +import numpy as np +from pyaer import AEFile + + +class AEData(object): + """Class representation of AER data""" + def __init__(self, ae_file=None): + self.dimensions = (128, 128) + if isinstance(ae_file, AEFile): + self.x_data, self.y_data, self.t_data = ae_file.unpack() + self.timestamp = ae_file.timestamp + elif isinstance(ae_file, AEData): + self.x_data, self.y_data, self.t_data = (ae_file.x_data, + ae_file.y_data, + ae_file.t_data) + self.timestamp = ae_file.timestamp + else: + self.x_data, self.y_data = np.array([]), np.array([]) + self.t_data, self.timestamp = np.array([]), np.array([]) + + def __getitem__(self, item): + rtn = AEData() + rtn.x_data = self.x_data[item] + rtn.y_data = self.y_data[item] + rtn.t_data = self.t_data[item] + rtn.timestamp = self.timestamp[item] + return rtn + + def __setitem__(self, key, value): + self.x_data[key] = value.x_data + self.y_data[key] = value.y_data + self.t_data[key] = value.t_data + self.timestamp[key] = value.timestamp + + def __delitem__(self, key): + self.x_data = np.delete(self.x_data, key) + self.y_data = np.delete(self.y_data, key) + self.t_data = np.delete(self.t_data, key) + self.timestamp = np.delete(self.timestamp, key) + + def save_to_mat(self, filename): + """Save object data to .mat file using scipy""" + scipy.io.savemat(filename, {'X': self.x_data, 'Y': self.y_data, + 't': self.timestamp, 'ts': self.timestamp}) + + def pack(self): + """Packs data into binary format""" + no_data = len(self.x_data) + packed = np.zeros(no_data) + for i in range(no_data): + packed[i] = (int(self.t_data[i]) & 0x1) + packed[i] += (int(128-self.x_data[i]) & 0x7F) << 0x1 + packed[i] += (int(self.y_data[i]) & 0x7F) << 0x8 + + return packed + + # TODO + # performance here can be improved by allowing indexing in the AE data. + # For now, I expect this not to be done often + def make_sparse(self, ratio): + """Random selection of loaded data to produce sparse data set + + Keyword arguments: + ratio -- the divisor of the length, e.g. 5 gives 1/5 of the data + """ + indexes = np.random.randint(0, len(self.x_data), + math.floor(len(self.x_data) / ratio)) + indexes.sort() + + rtn = AEData() + rtn.x_data = self.x_data[indexes] + rtn.y_data = self.y_data[indexes] + rtn.t_data = self.t_data[indexes] + rtn.timestamp = self.timestamp[indexes] + + return rtn + + def __repr__(self): + return "{} total [x,y,t,ts]: [{}, {}, {}, {}]".format(len(self.x_data), + self.x_data, + self.y_data, + self.t_data, + self.timestamp) + + def __len__(self): + return len(self.x_data) + + def interactive_animation(self, step=5000, limits=(0, 128), pause=0): + """Present data in a graph that allows interaction""" + plt.ion() + fig = plt.figure(figsize=(6, 6)) + plt.show() + axes = fig.add_subplot(111) + + start = 0 + end = step - 1 + while start < len(self.x_data): + axes.clear() + axes.scatter(self.x_data[start:end], self.y_data[start:end], + s=20, c=self.t_data[start:end], marker='o', + cmap='jet') + axes.set_xlim(limits) + axes.set_ylim(limits) + start += step + end += step + plt.draw() + time.sleep(pause) + + def downsample(self, new_dimensions=(16, 16)): + """Reduces resolution to given dimensions + + Keyword arguments: + new_dimensions -- tuple of new dimensions, default (16, 16) + """ + # TODO + # Make this cleaner + assert self.dimensions[0] % new_dimensions[0] is 0 + assert self.dimensions[1] % new_dimensions[1] is 0 + + rtn = AEData() + rtn.timestamp = self.timestamp + rtn.t_data = self.t_data + rtn.x_data = np.floor(self.x_data / + (self.dimensions[0] / new_dimensions[0])) + rtn.y_data = np.floor(self.y_data / + (self.dimensions[1] / new_dimensions[1])) + + return rtn + + def to_matrix(self, dim=(128, 128)): + """Returns a matrix of the given dimensions containing AER data + + Keyword arguments: + dim -- tuple of matrix dimensions, default (128, 128) + """ + return make_matrix(self.x_data, self.y_data, self.t_data, dim=dim) + + def filter_events(self, evt_type): + """Returns new AEData object with given event type removed + + Keyword arguments: + evt_type -- string of event type, either 'ON' or 'OFF'""" + if evt_type == 'ON': + allow = 0 + elif evt_type == 'OFF': + allow = 1 + else: + print('Invalid event type for filter') + return None + + rtn = AEData() + + for idx, _type in enumerate(self): + if _type == allow: + rtn.timestamp = np.append(rtn.timestamp, [self.timestamp[idx]]) + rtn.t_data = np.append(rtn.t_data, [allow]) + rtn.x_data = np.append(rtn.x_data, [self.x_data[idx]]) + rtn.y_data = np.append(rtn.y_data, [self.y_data[idx]]) + return rtn + + def merge_events(self): + """Set all events to the same type, packaged in new AEData object""" + rtn = copy.deepcopy(self) + rtn.t_data = np.zeros(len(self.t_data)) + return rtn + + def take(self, n_elements): + """Returns n_elements from data set by random selection""" + if n_elements <= len(self): + return self.make_sparse(len(self)/n_elements) + else: + print('Number of desired elements more than available') + return None + + def take_v2(self, n_elements): + """Takes n_elements elements with more even distribution""" + if n_elements > len(self): + print('Number of desired elements more than available') + return None + step = len(self)/n_elements + temp = 0 + rtn = AEData() + i = 0.0 + while i < len(self): + numt = int(np.floor(i)) + if temp != numt: + rtn.x_data = np.append(rtn.x_data, self.x_data[numt]) + rtn.y_data = np.append(rtn.y_data, self.y_data[numt]) + rtn.t_data = np.append(rtn.t_data, self.t_data[numt]) + rtn.timestamp = np.append(rtn.timestamp, self.timestamp[numt]) + temp = numt + i += step + return rtn + + def change_timescale(self, length, start=None): + """Shifts and rescales timestamps, keeps starting point by default""" + rtn = copy.deepcopy(self) + _min = np.min(rtn.ts) + if start is None: + start = _min + rtn.timestamp = np.floor( + (rtn.timestamp - _min) / + ((np.max(rtn.timestamp) - _min) / length) + start) + return rtn diff --git a/pyaer/AEFile.py b/pyaer/AEFile.py new file mode 100644 index 0000000..085c063 --- /dev/null +++ b/pyaer/AEFile.py @@ -0,0 +1,112 @@ +""" +Author: Dario ML, bdevans, Michael Hart +Program: pyaer/AEFile.py +Description: PyAER AEFile class definition +""" + +import math +import numpy as np +from pyaer import AEData + + +class AEFile(object): + """Class representing AER data file""" + def __init__(self, filename, max_events=1e6): + self.filename = filename + self.max_events = max_events + self.header = [] + self.data, self.timestamp = self.read() + + def load(self): + """Alias for read""" + return self.read() + + def read(self): + """Read data from file, ignoring comments""" + with open(self.filename, 'r') as aef: + line = aef.readline() + while line[0] == '#': + self.header.append(line) + if line[0:9] == '#!AER-DAT': + aer_version = line[9] + current = aef.tell() + line = aef.readline() + + if aer_version != '2': + raise Exception('Invalid AER version. ' + 'Expected 2, got {}'.format(aer_version)) + + aef.seek(0, 2) + num_events = math.floor((aef.tell() - current) / 8) + + if num_events > self.max_events: + print('There are {} events, but max_events is set to {}. ' + 'Will only use {} events.'.format(num_events, + self.max_events, + self.max_events)) + num_events = self.max_events + + aef.seek(current) + j = 0 + timestamps = np.zeros(num_events) + data = np.zeros(num_events) + + # print(num_events) + for i in range(int(num_events)): + aef.seek(current+8*i) + # data[i] = int(aef.read(4).encode('hex'), 16) + # timestamps[i] = int(aef.read(4).encode('hex'), 16) + + cur_data = int(aef.read(4).encode('hex'), 16) + cur_timestamp = int(aef.read(4).encode('hex'), 16) + if j > 0: + time_diff = cur_timestamp - timestamps[j-1] + if 0 <= time_diff <= 1e8: + data[j] = cur_data + timestamps[j] = cur_timestamp + j += 1 + + elif j == 0: + data[j] = cur_data + timestamps[j] = cur_timestamp + j += 1 + + return data, timestamps + + def save(self, data=None, filename=None, ext='aedat'): + """Saves given data to file name""" + if filename is None: + filename = self.filename + if data is None: + data = AEData(self) + if ext is 'aedat': + # unpack our 'data' + timestamp = data.timestamp + data = data.pack() + + with open(filename, 'w') as aef: + for item in self.header: + aef.write(item) + # print('\n\n') + # aef.write('\n\n') # Was this meant to write to the file? + current = aef.tell() + num_items = len(data) + for i in range(num_items): + aef.seek(current+8*i) + aef.write( + hex(int(data[i]))[2:].zfill(8).decode('hex')) + aef.write( + hex(int(timestamp[i]))[2:].zfill(8).decode('hex')) + + def unpack(self): + """Unpack x, y axes and time data from loaded data""" + no_data = len(self.data) + x_data = np.zeros(no_data) + y_data = np.zeros(no_data) + t_data = np.zeros(no_data) + + for idx, data in enumerate(no_data): + t_data[idx] = data & 0x1 + x_data[idx] = 128-((data >> 0x1) & 0x7F) + y_data[idx] = (data >> 0x8) & 0x7F + return x_data, y_data, t_data diff --git a/pyaer/__init__.py b/pyaer/__init__.py new file mode 100644 index 0000000..538e2c7 --- /dev/null +++ b/pyaer/__init__.py @@ -0,0 +1,9 @@ +""" +Author: Dario ML, bdevans, Michael Hart +Program: pyaer/__init__.py +Description: Initialise file for PyAER +""" + +from pyaer.AEFile import AEFile +from pyaer.AEData import AEData +from pyaer.utils import make_matrix, create_pngs, concatenate diff --git a/pyaer/utils.py b/pyaer/utils.py new file mode 100644 index 0000000..47f6815 --- /dev/null +++ b/pyaer/utils.py @@ -0,0 +1,69 @@ +""" +Author: Dario ML, bdevans, Michael Hart +Program: pyaer/utils.py +Description: Miscellaneous utility functions for PyAER +""" + +import os +from PIL import Image +import numpy as np +from pyaer import AEData + + +def make_matrix(x_data, y_data, t_data, dim=(128, 128)): + """Form matrix from x, y, t data with given dimensions + + Keyword arguments: + x_data -- x axis data + y_data -- y axis data + t_data -- t axis data + dim -- tuple of matrix dimensions, default (128, 128) + """ + image = np.zeros(dim) + events = np.zeros(dim) + + for idx, _time in enumerate(t_data): + image[y_data[idx]-1, x_data[idx]-1] -= _time - 0.5 + events[y_data[idx]-1, x_data[idx]-1] += 1 + + # http://stackoverflow.com/questions/26248654/numpy-return-0-with-divide-by-zero + np.seterr(divide='ignore', invalid='ignore') + + result = 0.5 + (image / events) + result[events == 0] = 0.5 + return result + + +def create_pngs(data, prepend, path="", step=3000, dim=(128, 128)): + """Create PNG files at given location with given dimensions""" + if not os.path.exists(path): + os.makedirs(path) + + idx = 0 + start = 0 + end = step - 1 + while start < len(data.x_data): + image = make_matrix(data.x_data[start:end], data.y_data[start:end], + data.t_data[start:end], dim=dim) + img_arr = (image*255).astype('uint8') + im_data = Image.fromarray(img_arr) + im_data.save(path + os.path.sep + prepend + ("%05d" % idx) + ".png") + idx += 1 + + start += step + end += step + + +def concatenate(a_tuple): + """Concatenate all tuple points into an AEData object""" + rtn = AEData() + n_points = len(a_tuple) + rtn.x_data = np.concatenate( + tuple([a_tuple[i].x_data for i in range(n_points)])) + rtn.y_data = np.concatenate( + tuple([a_tuple[i].y_data for i in range(n_points)])) + rtn.t_data = np.concatenate( + tuple([a_tuple[i].t_data for i in range(n_points)])) + rtn.timestamp = np.concatenate( + tuple([a_tuple[i].timestamp for i in range(n_points)])) + return rtn diff --git a/setup.py b/setup.py index fcdffb2..50882b4 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #! /usr/bin/env python """ -Author: Dario ML +Author: Dario ML, bdevans Program: SETUP.PY Date: Saturday, June 06, 2014 Description: Setup and install TD algorithms. @@ -8,19 +8,26 @@ from distutils.core import setup -setup(name='python-aer', - version='0.1.2', +setup( + name='python-aer', + version='0.1.3', author="Dario Magliocchetti", author_email="darioml1911@gmail.com", - url="https://github.com/darioml/pAER-python-aer-lib", + url="https://github.com/bio-modelling/py-aer", description='Python Address Event Representation (AER) Library', - long_description='This package provides tools required to visulate, manipulate and use address event representational data (.aedat format). ', - package_dir={"paer" : "src"}, - packages=["paer"], + long_description='''This package provides tools required to visualise, + manipulate and use address event representational data + (.aedat format). ''', + packages=["pyaer"], license="GPL 2.0", classifiers=[ + "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", "Environment :: Console", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Intended Audience :: Developers", @@ -30,6 +37,7 @@ install_requires=[ 'numpy', 'scipy', - 'matplotlib' + 'matplotlib', + 'Pillow' # N.B. This cannot coexist with PIL ] ) diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index 3d0f5bd..0000000 --- a/src/__init__.py +++ /dev/null @@ -1,237 +0,0 @@ -""" -Author: Dario ML -Program: src/__init__.py -Description: main file for python-ae -""" - -from PIL import Image -import math -import numpy as np -import scipy.io -import os,time -import matplotlib.pyplot as plt -from matplotlib import cm - -class aefile(object): - def __init__(self, filename, max_events=1e6): - self.filename = filename - self.max_events = max_events - self.header = [] - self.data, self.timestamp = self.read() - - # alias for read - def load(self): - return self.read() - - def read(self): - with open(self.filename, 'r') as f: - line = f.readline() - while line[0] == '#': - self.header.append(line) - if line[0:9] == '#!AER-DAT': - aer_version = line[9]; - current = f.tell() - line = f.readline() - - - if aer_version != '2': - raise Exception('Invalid AER version. Expected 2, got %s' % aer_version) - - f.seek(0,2) - numEvents = math.floor( ( f.tell() - current ) / 8 ) - - if numEvents > self.max_events: - print 'There are %i events, but max_events is set to %i. Will only use %i events.' % (numEvents, self.max_events, self.max_events) - numEvents = self.max_events - - f.seek(current) - - timestamps = np.zeros( numEvents ) - data = np.zeros( numEvents ) - - for i in range(int(numEvents)): - data[i] = int(f.read(4).encode('hex'), 16) - timestamps[i] = int(f.read(4).encode('hex'), 16) - - return data, timestamps - - def save(self, data=None, filename=None, ext='aedat'): - if filename is None: - filename = self.filename - if data is None: - data = aedata(self) - if ext is 'aedat': - # unpack our 'data' - ts = data.ts - data = data.pack() - - with open(filename, 'w') as f: - for item in self.header: - f.write(item) - print - print - no_items = len(data) - for i in range(no_items): - f.write(hex(int(data[i]))[2:].zfill(8).decode('hex')) - f.write(hex(int(ts[i]))[2:].zfill(8).decode('hex')) - - def unpack(self): - noData = len(self.data) - - x = np.zeros(noData) - y = np.zeros(noData) - t = np.zeros(noData) - - for i in range(noData): - - d = int(self.data[i]) - - t[i] = d & 0x1 - x[i] = 128-((d >> 0x1) & 0x7F) - y[i] = (d >> 0x8) & 0x7F - return x,y,t - - -class aedata(object): - def __init__(self, ae_file=None): - self.dimensions = (128,128) - if isinstance(ae_file, aefile): - self.x, self.y, self.t = ae_file.unpack() - self.ts = ae_file.timestamp - elif isinstance(ae_file, aedata): - self.x, self.y, self.t = aedata.x, aedata.y, aedata.t - self.ts = ae_file.ts - else: - self.x, self.y, self.t, self.ts = np.array([]),np.array([]),np.array([]),np.array([]) - - def __getitem__(self, item): - rtn = aedata() - rtn.x = self.x[item] - rtn.y = self.y[item] - rtn.t = self.t[item] - rtn.ts= self.ts[item] - return rtn - - def __setitem__(self, key, value): - self.x[key] = value.x - self.y[key] = value.y - self.t[key] = value.t - self.ts[key] = value.ts - - def __delitem__(self, key): - self.x = np.delete(self.x, key) - self.y = np.delete(self.y, key) - self.t = np.delete(self.t, key) - self.ts = np.delete(self.ts, key) - - def save_to_mat(self, filename): - scipy.io.savemat(filename, {'X':self.x, 'Y':self.y, 't': self.t, 'ts': self.ts}) - - def pack(self): - noData = len(self.x) - packed = np.zeros(noData) - for i in range(noData): - packed[i] = (int(self.t[i]) & 0x1) - packed[i] += (int(128-self.x[i]) & 0x7F) << 0x1 - packed[i] += (int(self.y[i]) & 0x7F) << 0x8 - - return packed - - # TODO - # performance here can be improved by allowing indexing in the AE data. - # For now, I expect this not to be done often - def make_sparse(self, ratio): - indexes = np.random.randint(0,len(self.x),math.floor(len(self.x)/ratio)) - indexes.sort() - - rtn = aedata() - rtn.x = self.x[indexes] - rtn.y = self.y[indexes] - rtn.t = self.t[indexes] - rtn.ts = self.ts[indexes] - - return rtn - - def __repr__(self): - return "%i total [x,y,t,ts]: [%s, %s, %s, %s]" % (len(self.x), self.x, self.y, self.t, self.ts) - - def __len__(self): - return len(self.x) - - def interactive_animation(self, step=5000, limits=(0,128), pause=0): - plt.ion() - fig = plt.figure(figsize=(6,6)) - plt.show() - ax = fig.add_subplot(111) - - start = 0 - end = step-1 - while(start < len(self.x)): - ax.clear() - ax.scatter(self.x[start:end],self.y[start:end],s=20,c=self.t[start:end], marker = 'o', cmap = cm.jet ); - ax.set_xlim(limits) - ax.set_ylim(limits) - start += step - end += step - plt.draw() - time.sleep(pause) - - def downsample(self,new_dimensions=(16,16)): - # TODO - # Make this cleaner - assert self.dimensions[0]%new_dimensions[0] is 0 - assert self.dimensions[1]%new_dimensions[1] is 0 - - rtn = aedata() - - rtn.ts = self.ts - rtn.t = self.t - rtn.x = np.floor(self.x / (self.dimensions[0] / new_dimensions[0])) - rtn.y = np.floor(self.y / (self.dimensions[1] / new_dimensions[1])) - - return rtn - - def to_matrix(self, dim=(128,128)): - return make_matrix(self.x, self.y, self.t, dim=dim) - -def make_matrix(x, y, t, dim=(128,128)): - image = np.zeros(dim) - events= np.zeros(dim) - - for i in range(len(x)): - image[y[i]-1,x[i]-1] -= t[i]-0.5 - events[y[i]-1,x[i]-1] += 1 - - # http://stackoverflow.com/questions/26248654/numpy-return-0-with-divide-by-zero - np.seterr(divide='ignore', invalid='ignore') - - result = 0.5+(image / events) - result[events == 0] = 0.5 - return result - -def create_pngs(data,prepend,path="",step=3000,dim=(128,128)): - if not os.path.exists(path): - os.makedirs(path) - - idx = 0 - start = 0; - end = step-1; - while(start < len(data.x)): - image = make_matrix(data.x[start:end],data.y[start:end],data.t[start:end], dim=dim) - img_arr = (image*255).astype('uint8') - im = Image.fromarray(img_arr) - im.save(path+'/'+prepend+("%05d" % idx)+".png") - idx += 1 - - start += step - end += step - -def concatenate(a_tuple): - rtn = aedata() - rtn.x = np.concatenate(tuple([a_tuple[i].x for i in range(len(a_tuple))])) - rtn.y = np.concatenate(tuple([a_tuple[i].y for i in range(len(a_tuple))])) - rtn.t = np.concatenate(tuple([a_tuple[i].t for i in range(len(a_tuple))])) - rtn.ts = np.concatenate(tuple([a_tuple[i].ts for i in range(len(a_tuple))])) - return rtn - - # np.concatenate(a_tuple) \ No newline at end of file