From 2dd9e94653a225ece7cdc9cedc1d91f9209203bf Mon Sep 17 00:00:00 2001 From: Ben Evans Date: Wed, 5 Oct 2016 12:45:07 +0100 Subject: [PATCH 01/12] Make Python 3 compatible, bump version and PEP8 work --- setup.py | 16 +++++--- src/__init__.py | 104 ++++++++++++++++++++++++++---------------------- 2 files changed, 68 insertions(+), 52 deletions(-) diff --git a/setup.py b/setup.py index fcdffb2..a8ae6fe 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,25 @@ 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", 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"}, + long_description='''This package provides tools required to visulise, + manipulate and use address event representational data + (.aedat format). ''', + package_dir={"paer": "src"}, packages=["paer"], 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.5", "Environment :: Console", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Intended Audience :: Developers", diff --git a/src/__init__.py b/src/__init__.py index 3d0f5bd..fac431d 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,17 +1,23 @@ """ -Author: Dario ML +Author: Dario ML, bdevans Program: src/__init__.py Description: main file for python-ae """ +from __future__ import print_function from PIL import Image import math import numpy as np import scipy.io -import os,time +import os +import time import matplotlib.pyplot as plt from matplotlib import cm +# TODO: Generalise to process off-events +# TODO: Add function to combine on and off-events + + class aefile(object): def __init__(self, filename, max_events=1e6): self.filename = filename @@ -29,25 +35,24 @@ def read(self): while line[0] == '#': self.header.append(line) if line[0:9] == '#!AER-DAT': - aer_version = line[9]; + 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 ) - + 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) + 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 ) + timestamps = np.zeros(numEvents) + data = np.zeros(numEvents) for i in range(int(numEvents)): data[i] = int(f.read(4).encode('hex'), 16) @@ -68,33 +73,30 @@ def save(self, data=None, filename=None, ext='aedat'): 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): + # print('\n\n') + # f.write('\n\n') # Was this meant to write to the file? + num_items = len(data) + for i in range(num_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 + return x, y, t class aedata(object): def __init__(self, ae_file=None): - self.dimensions = (128,128) + self.dimensions = (128, 128) if isinstance(ae_file, aefile): self.x, self.y, self.t = ae_file.unpack() self.ts = ae_file.timestamp @@ -102,14 +104,15 @@ def __init__(self, ae_file=None): 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([]) + self.x, self.y = np.array([]), np.array([]) + self.t, self.ts = 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] + rtn.ts = self.ts[item] return rtn def __setitem__(self, key, value): @@ -119,19 +122,20 @@ def __setitem__(self, key, value): 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) + 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}) + 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(self.t[i]) & 0x1) packed[i] += (int(128-self.x[i]) & 0x7F) << 0x1 packed[i] += (int(self.y[i]) & 0x7F) << 0x8 @@ -141,7 +145,8 @@ def pack(self): # 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 = np.random.randint(0, len(self.x), + math.floor(len(self.x) / ratio)) indexes.sort() rtn = aedata() @@ -158,17 +163,18 @@ def __repr__(self): def __len__(self): return len(self.x) - def interactive_animation(self, step=5000, limits=(0,128), pause=0): + def interactive_animation(self, step=5000, limits=(0, 128), pause=0): plt.ion() - fig = plt.figure(figsize=(6,6)) + fig = plt.figure(figsize=(6, 6)) plt.show() ax = fig.add_subplot(111) start = 0 - end = step-1 + 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.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 @@ -176,11 +182,11 @@ def interactive_animation(self, step=5000, limits=(0,128), pause=0): plt.draw() time.sleep(pause) - def downsample(self,new_dimensions=(16,16)): + 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 + assert self.dimensions[0] % new_dimensions[0] is 0 + assert self.dimensions[1] % new_dimensions[1] is 0 rtn = aedata() @@ -191,41 +197,45 @@ def downsample(self,new_dimensions=(16,16)): return rtn - def to_matrix(self, dim=(128,128)): + 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)): + +def make_matrix(x, y, t, dim=(128, 128)): image = np.zeros(dim) - events= 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 + 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 = 0.5 + (image / events) result[events == 0] = 0.5 return result -def create_pngs(data,prepend,path="",step=3000,dim=(128,128)): + +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; + 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) + 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") + im.save(path + os.path.sep + 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))])) @@ -234,4 +244,4 @@ def concatenate(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 + # np.concatenate(a_tuple) From e75b9b73550cc44a5176be5ffead6c780335c603 Mon Sep 17 00:00:00 2001 From: Ben Evans Date: Wed, 5 Oct 2016 12:53:54 +0100 Subject: [PATCH 02/12] Broke up long strings - more PEP8 --- src/__init__.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index fac431d..cd7d654 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -40,13 +40,17 @@ def read(self): line = f.readline() if aer_version != '2': - raise Exception('Invalid AER version. Expected 2, got %s' % aer_version) + raise Exception('Invalid AER version. ' + 'Expected 2, got {}'.format(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)) + print('There are {} events, but max_events is set to {}. ' + 'Will only use {} events.'.format(numEvents, + self.max_events, + self.max_events)) numEvents = self.max_events f.seek(current) @@ -158,7 +162,9 @@ def make_sparse(self, ratio): 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) + return "%i total [x,y,t,ts]: [%s, %s, %s, %s]".format(len(self.x), + self.x, self.y, + self.t, self.ts) def __len__(self): return len(self.x) @@ -238,10 +244,11 @@ def create_pngs(data, prepend, path="", step=3000, dim=(128, 128)): 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))])) + n = len(a_tuple) + rtn.x = np.concatenate(tuple([a_tuple[i].x for i in range(n)])) + rtn.y = np.concatenate(tuple([a_tuple[i].y for i in range(n)])) + rtn.t = np.concatenate(tuple([a_tuple[i].t for i in range(n)])) + rtn.ts = np.concatenate(tuple([a_tuple[i].ts for i in range(n)])) return rtn # np.concatenate(a_tuple) From e3ba11b0eacea46efdc351ba96718b93d86ba06c Mon Sep 17 00:00:00 2001 From: Ben Evans Date: Wed, 5 Oct 2016 22:53:14 +0100 Subject: [PATCH 03/12] Update installation instructions and tweak introduction --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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*** From e45006929d57294b3ae15aab56923d45559d48cb Mon Sep 17 00:00:00 2001 From: Ben Evans Date: Wed, 5 Oct 2016 13:07:48 +0100 Subject: [PATCH 04/12] Add Pillow as a dependency and support for Python 3.3, 3.4 --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a8ae6fe..098d5ca 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,8 @@ "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)", @@ -36,6 +38,7 @@ install_requires=[ 'numpy', 'scipy', - 'matplotlib' + 'matplotlib', + 'Pillow' # N.B. This cannot coexist with PIL ] ) From bd74837636b3bbe3402944007f9676ebf0308b6f Mon Sep 17 00:00:00 2001 From: Ben Evans Date: Wed, 12 Oct 2016 18:35:48 +0100 Subject: [PATCH 05/12] Incorporate Hanyi's changes --- src/__init__.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index cd7d654..fd8cbad 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -54,13 +54,29 @@ def read(self): numEvents = self.max_events f.seek(current) - + j = 0 timestamps = np.zeros(numEvents) data = np.zeros(numEvents) + # print(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) + f.seek(current+8*i) + # data[i] = int(f.read(4).encode('hex'), 16) + # timestamps[i] = int(f.read(4).encode('hex'), 16) + + cur_data = int(f.read(4).encode('hex'), 16) + cur_timestamp = int(f.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 @@ -79,8 +95,10 @@ def save(self, data=None, filename=None, ext='aedat'): f.write(item) # print('\n\n') # f.write('\n\n') # Was this meant to write to the file? + current = f.tell() num_items = len(data) for i in range(num_items): + f.seek(current+8*i) f.write(hex(int(data[i]))[2:].zfill(8).decode('hex')) f.write(hex(int(ts[i]))[2:].zfill(8).decode('hex')) From e533ee80ada2dfeb9a06cb82093e59349540ecee Mon Sep 17 00:00:00 2001 From: roberttoth02 Date: Sun, 16 Oct 2016 17:26:09 +0100 Subject: [PATCH 06/12] Implemented methods for handling event types --- src/__init__.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/__init__.py b/src/__init__.py index fd8cbad..7f112b2 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -224,6 +224,33 @@ def downsample(self, new_dimensions=(16, 16)): def to_matrix(self, dim=(128, 128)): return make_matrix(self.x, self.y, self.t, dim=dim) + # Returns new aedata object with given event type removed + def filter_events(self, type): + if type == 'ON': + tp = 0 + elif type == 'OFF': + tp = 1 + else: + print('Invalid event type for filter') + return None + + rtn = aedata() + + for i in range(len(self)): + if self.t[i] == tp: + rtn.ts = np.append(rtn.ts, [self.ts[i]]) + rtn.t = np.append(rtn.t, [tp]) + rtn.x = np.append(rtn.x, [self.x[i]]) + rtn.y = np.append(rtn.y, [self.y[i]]) + return rtn + + # Discards type information by setting type to 0 for every event, + # returns result in new aedata object + def merge_events(self): + rtn = copy.deepcopy(self) + rtn.t = np.zeros(len(self.t)) + return rtn + def make_matrix(x, y, t, dim=(128, 128)): image = np.zeros(dim) From 682f821f3002adaab90c99b3c86dcd474044b5b1 Mon Sep 17 00:00:00 2001 From: roberttoth02 Date: Sun, 16 Oct 2016 17:32:30 +0100 Subject: [PATCH 07/12] Implemented two methods Hanyi was missing --- src/__init__.py | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index 7f112b2..c400244 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -14,9 +14,6 @@ import matplotlib.pyplot as plt from matplotlib import cm -# TODO: Generalise to process off-events -# TODO: Add function to combine on and off-events - class aefile(object): def __init__(self, filename, max_events=1e6): @@ -250,6 +247,45 @@ def merge_events(self): rtn = copy.deepcopy(self) rtn.t = np.zeros(len(self.t)) return rtn + + # Takes n elements from dataset + def take(self, n): + if (n <= len(self)): + return self.make_sparse(len(self)/n) + else: + print('Number of desired elements more than available') + return None + + # Takes n elements from dataset + # Alternative version, slightly slower, but returned timestamps + # are distributed more uniformly + def take_v2(self, n): + if n > len(self): + print('Number of desired elements more than available') + return None + step = len(self)/n + temp = 0 + rtn = aedata() + i = 0.0 + while(i < len(self)): + numt = int(np.floor(i)) + if temp != numt: + rtn.x = np.append(rtn.x, self.x[numt]) + rtn.y = np.append(rtn.y, self.y[numt]) + rtn.t = np.append(rtn.t, self.t[numt]) + rtn.ts = np.append(rtn.ts, self.ts[numt]) + temp = numt + i += step + return rtn + + # Shifts and rescales timestamps, keeps starting point by default + def change_timescale(self, length, start=None): + rtn = copy.deepcopy(self) + min = np.min(rtn.ts) + if start is None: + start = min + rtn.ts = np.floor((rtn.ts-min)/((np.max(rtn.ts)-min)/length)+start) + return rtn def make_matrix(x, y, t, dim=(128, 128)): From 19205e74b89a12ea4d06a1b06efcef8ad511f1b6 Mon Sep 17 00:00:00 2001 From: Michael Hart Date: Tue, 14 Feb 2017 13:03:37 +0000 Subject: [PATCH 08/12] Changed variables, formatting, docstrings to match linter --- src/__init__.py | 345 +++++++++++++++++++++++++++--------------------- 1 file changed, 192 insertions(+), 153 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index c400244..4aafc54 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -5,64 +5,67 @@ """ from __future__ import print_function -from PIL import Image +import copy import math -import numpy as np -import scipy.io import os import time +from PIL import Image +import numpy as np +import scipy.io import matplotlib.pyplot as plt from matplotlib import cm -class aefile(object): +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() - # alias for read def load(self): + """Alias for read""" return self.read() def read(self): - with open(self.filename, 'r') as f: - line = f.readline() + """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 = f.tell() - line = f.readline() + current = aef.tell() + line = aef.readline() if aer_version != '2': raise Exception('Invalid AER version. ' 'Expected 2, got {}'.format(aer_version)) - f.seek(0, 2) - numEvents = math.floor((f.tell() - current) / 8) + aef.seek(0, 2) + num_events = math.floor((aef.tell() - current) / 8) - if numEvents > self.max_events: + if num_events > self.max_events: print('There are {} events, but max_events is set to {}. ' - 'Will only use {} events.'.format(numEvents, + 'Will only use {} events.'.format(num_events, self.max_events, self.max_events)) - numEvents = self.max_events + num_events = self.max_events - f.seek(current) + aef.seek(current) j = 0 - timestamps = np.zeros(numEvents) - data = np.zeros(numEvents) + timestamps = np.zeros(num_events) + data = np.zeros(num_events) - # print(numEvents) - for i in range(int(numEvents)): - f.seek(current+8*i) - # data[i] = int(f.read(4).encode('hex'), 16) - # timestamps[i] = int(f.read(4).encode('hex'), 16) + # 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(f.read(4).encode('hex'), 16) - cur_timestamp = int(f.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: @@ -78,85 +81,91 @@ def read(self): 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) + data = AEData(self) if ext is 'aedat': # unpack our 'data' - ts = data.ts + timestamp = data.timestamp data = data.pack() - with open(filename, 'w') as f: + with open(filename, 'w') as aef: for item in self.header: - f.write(item) + aef.write(item) # print('\n\n') - # f.write('\n\n') # Was this meant to write to the file? - current = f.tell() + # 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): - f.seek(current+8*i) - f.write(hex(int(data[i]))[2:].zfill(8).decode('hex')) - f.write(hex(int(ts[i]))[2:].zfill(8).decode('hex')) + 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): - noData = len(self.data) - x = np.zeros(noData) - y = np.zeros(noData) - t = np.zeros(noData) + """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 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 + 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 -class aedata(object): +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, 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 + 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, self.y = np.array([]), np.array([]) - self.t, self.ts = np.array([]), np.array([]) + 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 = self.x[item] - rtn.y = self.y[item] - rtn.t = self.t[item] - rtn.ts = self.ts[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[key] = value.x - self.y[key] = value.y - self.t[key] = value.t - self.ts[key] = value.ts + 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 = 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) + 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): - scipy.io.savemat(filename, {'X': self.x, 'Y': self.y, - 't': self.t, 'ts': self.ts}) + """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): - 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 + """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 @@ -164,137 +173,167 @@ def pack(self): # 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)) + """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 = self.x[indexes] - rtn.y = self.y[indexes] - rtn.t = self.t[indexes] - rtn.ts = self.ts[indexes] + 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 "%i total [x,y,t,ts]: [%s, %s, %s, %s]".format(len(self.x), - self.x, self.y, - self.t, self.ts) + 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) + 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() - ax = fig.add_subplot(111) + axes = 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) + 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.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])) + 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)): - return make_matrix(self.x, self.y, self.t, dim=dim) - - # Returns new aedata object with given event type removed - def filter_events(self, type): - if type == 'ON': - tp = 0 - elif type == 'OFF': - tp = 1 + """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() + rtn = AEData() - for i in range(len(self)): - if self.t[i] == tp: - rtn.ts = np.append(rtn.ts, [self.ts[i]]) - rtn.t = np.append(rtn.t, [tp]) - rtn.x = np.append(rtn.x, [self.x[i]]) - rtn.y = np.append(rtn.y, [self.y[i]]) + 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 - # Discards type information by setting type to 0 for every event, - # returns result in new aedata object def merge_events(self): + """Set all events to the same type, packaged in new AEData object""" rtn = copy.deepcopy(self) - rtn.t = np.zeros(len(self.t)) + rtn.t_data = np.zeros(len(self.t_data)) return rtn - - # Takes n elements from dataset - def take(self, n): - if (n <= len(self)): - return self.make_sparse(len(self)/n) + + 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 - # Takes n elements from dataset - # Alternative version, slightly slower, but returned timestamps - # are distributed more uniformly - def take_v2(self, n): - if n > len(self): + 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 + step = len(self)/n_elements temp = 0 - rtn = aedata() + rtn = AEData() i = 0.0 - while(i < len(self)): + while i < len(self): numt = int(np.floor(i)) if temp != numt: - rtn.x = np.append(rtn.x, self.x[numt]) - rtn.y = np.append(rtn.y, self.y[numt]) - rtn.t = np.append(rtn.t, self.t[numt]) - rtn.ts = np.append(rtn.ts, self.ts[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 - # Shifts and rescales timestamps, keeps starting point by default 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) + _min = np.min(rtn.ts) if start is None: - start = min - rtn.ts = np.floor((rtn.ts-min)/((np.max(rtn.ts)-min)/length)+start) + start = _min + rtn.timestamp = np.floor( + (rtn.timestamp - _min) / + ((np.max(rtn.timestamp) - _min) / length) + start) return rtn -def make_matrix(x, y, t, dim=(128, 128)): +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 i in range(len(x)): - image[y[i]-1, x[i]-1] -= t[i]-0.5 - events[y[i]-1, x[i]-1] += 1 + 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') @@ -305,18 +344,19 @@ def make_matrix(x, y, t, dim=(128, 128)): 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)): - image = make_matrix(data.x[start:end], data.y[start:end], - data.t[start:end], dim=dim) + 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 = Image.fromarray(img_arr) - im.save(path + os.path.sep + prepend + ("%05d" % idx) + ".png") + im_data = Image.fromarray(img_arr) + im_data.save(path + os.path.sep + prepend + ("%05d" % idx) + ".png") idx += 1 start += step @@ -324,12 +364,11 @@ def create_pngs(data, prepend, path="", step=3000, dim=(128, 128)): def concatenate(a_tuple): - rtn = aedata() - n = len(a_tuple) - rtn.x = np.concatenate(tuple([a_tuple[i].x for i in range(n)])) - rtn.y = np.concatenate(tuple([a_tuple[i].y for i in range(n)])) - rtn.t = np.concatenate(tuple([a_tuple[i].t for i in range(n)])) - rtn.ts = np.concatenate(tuple([a_tuple[i].ts for i in range(n)])) + """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 - - # np.concatenate(a_tuple) From db7e9c872544c0efbb7cb0cae596cdbfc8662ec9 Mon Sep 17 00:00:00 2001 From: Michael Hart Date: Tue, 14 Feb 2017 13:20:24 +0000 Subject: [PATCH 09/12] Changed paths to recognise package name, and updated examples to match linter update --- examples/all_four_directions.py | 79 ++++++++++++++++++++------------- examples/animate_dvs.py | 14 ++++-- examples/downsampling.py | 22 ++++++--- examples/make_pngs.py | 21 ++++++--- {src => pyaer}/__init__.py | 0 setup.py | 7 ++- 6 files changed, 89 insertions(+), 54 deletions(-) rename {src => pyaer}/__init__.py (100%) diff --git a/examples/all_four_directions.py b/examples/all_four_directions.py index 8c81e00..2b389cb 100644 --- a/examples/all_four_directions.py +++ b/examples/all_four_directions.py @@ -1,51 +1,66 @@ -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 +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) +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)) -# Need to pre-load a file, to get the correct headers when writing! -lib = paer.aefile(file1, max_events=1) + 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..2ca6542 100644 --- a/examples/animate_dvs.py +++ b/examples/animate_dvs.py @@ -1,6 +1,12 @@ -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..6adbd8f 100644 --- a/examples/downsampling.py +++ b/examples/downsampling.py @@ -1,11 +1,19 @@ -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)) +def main(): + """Entry point of downsampling example""" -sparse.save_to_mat('downsample_left_to_right_1_1.mat') -lib.save(sparse, 'downsample_left_to_right_1_1.aedat') + 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..40daa48 100644 --- a/examples/make_pngs.py +++ b/examples/make_pngs.py @@ -1,14 +1,21 @@ +"""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/src/__init__.py b/pyaer/__init__.py similarity index 100% rename from src/__init__.py rename to pyaer/__init__.py diff --git a/setup.py b/setup.py index 098d5ca..50882b4 100644 --- a/setup.py +++ b/setup.py @@ -13,13 +13,12 @@ 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 visulise, + long_description='''This package provides tools required to visualise, manipulate and use address event representational data (.aedat format). ''', - package_dir={"paer": "src"}, - packages=["paer"], + packages=["pyaer"], license="GPL 2.0", classifiers=[ "Programming Language :: Python :: 2", From 08ae5a907ddc2095c89341a575ebe1a96d8ea607 Mon Sep 17 00:00:00 2001 From: Michael Hart Date: Tue, 14 Feb 2017 13:27:20 +0000 Subject: [PATCH 10/12] File rearrange to separate classes --- pyaer/AEData.py | 210 ++++++++++++++++++++++++++ pyaer/AEFile.py | 110 ++++++++++++++ pyaer/__init__.py | 377 +--------------------------------------------- pyaer/utils.py | 59 ++++++++ 4 files changed, 385 insertions(+), 371 deletions(-) create mode 100644 pyaer/AEData.py create mode 100644 pyaer/AEFile.py create mode 100644 pyaer/utils.py diff --git a/pyaer/AEData.py b/pyaer/AEData.py new file mode 100644 index 0000000..d68dc96 --- /dev/null +++ b/pyaer/AEData.py @@ -0,0 +1,210 @@ +""" +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 \ No newline at end of file diff --git a/pyaer/AEFile.py b/pyaer/AEFile.py new file mode 100644 index 0000000..50e2096 --- /dev/null +++ b/pyaer/AEFile.py @@ -0,0 +1,110 @@ +""" +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 index 4aafc54..538e2c7 100644 --- a/pyaer/__init__.py +++ b/pyaer/__init__.py @@ -1,374 +1,9 @@ """ -Author: Dario ML, bdevans -Program: src/__init__.py -Description: main file for python-ae +Author: Dario ML, bdevans, Michael Hart +Program: pyaer/__init__.py +Description: Initialise file for PyAER """ -from __future__ import print_function -import copy -import math -import os -import time -from PIL import Image -import numpy as np -import scipy.io -import matplotlib.pyplot as plt -from matplotlib import cm - - -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 - - -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 - - -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 +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..a65175f --- /dev/null +++ b/pyaer/utils.py @@ -0,0 +1,59 @@ +""" +Author: Dario ML, bdevans, Michael Hart +Program: pyaer/utils.py +Description: Miscellaneous utility functions for PyAER +""" + +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 From 8e4a221778019b22733873604e56238c97c8ce88 Mon Sep 17 00:00:00 2001 From: Michael Hart Date: Tue, 14 Feb 2017 14:27:39 +0000 Subject: [PATCH 11/12] PEP8 corrections and missing utils imports --- examples/all_four_directions.py | 10 +++++++--- examples/animate_dvs.py | 4 +++- examples/downsampling.py | 1 + examples/make_pngs.py | 6 ++++-- pyaer/AEData.py | 17 +++++++++-------- pyaer/AEFile.py | 8 +++++--- pyaer/utils.py | 18 ++++++++++++++---- 7 files changed, 43 insertions(+), 21 deletions(-) diff --git a/examples/all_four_directions.py b/examples/all_four_directions.py index 2b389cb..0b61764 100644 --- a/examples/all_four_directions.py +++ b/examples/all_four_directions.py @@ -13,10 +13,12 @@ # Each ball movement should be .5s long ANIMATION_TIME = 0.2 -# 3,280 events per second for 16*16 is reasonable for ball movement (might be even too high!) +# 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, + +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. @@ -35,10 +37,12 @@ def get_data(file, min, max, animation_time=ANIMATION_TIME, actual_time = (sparse.ts[-1]-sparse.ts[0])/1000000 scale = actual_time/animation_time - sparse.ts = (offset * 1000000) + np.round((sparse.timestamp-sparse.timestamp[0])/scale) + sparse.ts = ((offset * 1000000) + + np.round((sparse.timestamp - sparse.timestamp[0]) / scale)) return sparse + def main(): """Entry point of example""" # Loop through all files - indexes are extrapolated. diff --git a/examples/animate_dvs.py b/examples/animate_dvs.py index 2ca6542..bd3883e 100644 --- a/examples/animate_dvs.py +++ b/examples/animate_dvs.py @@ -2,9 +2,11 @@ import pyaer + def main(): """Entry point of animation example""" - data = pyaer.AEData(pyaer.AEFile('/path/to/left_to_right_1.aedat', max_events=1000000)) + 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)) diff --git a/examples/downsampling.py b/examples/downsampling.py index 6adbd8f..b68084b 100644 --- a/examples/downsampling.py +++ b/examples/downsampling.py @@ -4,6 +4,7 @@ FILE = '/path/to/left_to_right_1.aedat' + def main(): """Entry point of downsampling example""" diff --git a/examples/make_pngs.py b/examples/make_pngs.py index 40daa48..f4b2c93 100644 --- a/examples/make_pngs.py +++ b/examples/make_pngs.py @@ -5,7 +5,9 @@ import pyaer MYPATH = '/path/to/fyp-aedata-matlab' -ONLYFILES = [f for f in listdir(MYPATH) if isfile(join(MYPATH, f)) and f.endswith('.aedat')] +ONLYFILES = [f for f in listdir(MYPATH) + if isfile(join(MYPATH, f)) and f.endswith('.aedat')] + def main(): """Entry point of PNG file example""" @@ -14,7 +16,7 @@ def main(): aef = pyaer.AEFile('path/to/fyp-aedata-matlab/' + str(_file)) aed = pyaer.AEData(aef).downsample((16, 16)) - pyaer.create_pngs(aed, '16x16_' + str(_file) + '_', + pyaer.create_pngs(aed, '16x16_' + str(_file) + '_', path='testing_something', step=3000, dim=(16, 16)) if __name__ == '__main__': diff --git a/pyaer/AEData.py b/pyaer/AEData.py index d68dc96..7c4ac2f 100644 --- a/pyaer/AEData.py +++ b/pyaer/AEData.py @@ -8,6 +8,7 @@ import numpy as np from pyaer import AEFile + class AEData(object): """Class representation of AER data""" def __init__(self, ae_file=None): @@ -42,7 +43,7 @@ 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) + self.timestamp = np.delete(self.timestamp, key) def save_to_mat(self, filename): """Save object data to .mat file using scipy""" @@ -83,9 +84,9 @@ def make_sparse(self, ratio): def __repr__(self): return "{} total [x,y,t,ts]: [{}, {}, {}, {}]".format(len(self.x_data), - self.x_data, + self.x_data, self.y_data, - self.t_data, + self.t_data, self.timestamp) def __len__(self): @@ -126,9 +127,9 @@ def downsample(self, new_dimensions=(16, 16)): rtn = AEData() rtn.timestamp = self.timestamp rtn.t_data = self.t_data - rtn.x_data = np.floor(self.x_data / + rtn.x_data = np.floor(self.x_data / (self.dimensions[0] / new_dimensions[0])) - rtn.y_data = np.floor(self.y_data / + rtn.y_data = np.floor(self.y_data / (self.dimensions[1] / new_dimensions[1])) return rtn @@ -153,9 +154,9 @@ def filter_events(self, evt_type): 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]]) @@ -207,4 +208,4 @@ def change_timescale(self, length, start=None): rtn.timestamp = np.floor( (rtn.timestamp - _min) / ((np.max(rtn.timestamp) - _min) / length) + start) - return rtn \ No newline at end of file + return rtn diff --git a/pyaer/AEFile.py b/pyaer/AEFile.py index 50e2096..085c063 100644 --- a/pyaer/AEFile.py +++ b/pyaer/AEFile.py @@ -8,6 +8,7 @@ import numpy as np from pyaer import AEData + class AEFile(object): """Class representing AER data file""" def __init__(self, filename, max_events=1e6): @@ -92,8 +93,10 @@ def save(self, data=None, filename=None, ext='aedat'): 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')) + 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""" @@ -107,4 +110,3 @@ def unpack(self): x_data[idx] = 128-((data >> 0x1) & 0x7F) y_data[idx] = (data >> 0x8) & 0x7F return x_data, y_data, t_data - diff --git a/pyaer/utils.py b/pyaer/utils.py index a65175f..47f6815 100644 --- a/pyaer/utils.py +++ b/pyaer/utils.py @@ -4,6 +4,12 @@ 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 @@ -52,8 +58,12 @@ 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)])) + 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 From cc75531dce7d0b3e0a2eea5e7d51e7015d71cdd8 Mon Sep 17 00:00:00 2001 From: Michael Hart Date: Tue, 14 Feb 2017 14:30:06 +0000 Subject: [PATCH 12/12] Removed manifest generated by pip and added to git ignore --- .gitignore | 3 ++- MANIFEST | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 MANIFEST 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