Package: event-stream
Event StreamThis repository contains anEvent StreamPython reader implemented in C++, as well as Matlab bindings.PythonMatlabPythonpip3 install event_streamDocumentationTheevent_streamlibrary provides three classes:Decoder,IndexedDecoderandEncoder:Decoderreads constant-size byte buffers from an Event Stream file and returns variable-size event buffersIndexedDecoderreads the entire file when created (without storing events in memory) to build an index, and can be used to fetch events at arbitrary timestampsUdpDecoderreads event-stream encoded UDP packets (each packet must start with a uint64 little-endian absolute timestamp then contain an ES compliant stream)Encoderwrites event buffers to a fileUseDecoderif you want to process every event without delay. UseIndexedDecoderif you need to move back and forth while reading the file, for example if your are writing a file player with a clickable timeline.The first argument toDecoder,IndexedDecoderandEncoderis a file name. It must be apath-like object.IndexedDecodertakes a second argument, the keyframe duration in µs.Encodertakes three other arguments, the evvent type and the sensor's width and height.All three classes are contexts managers compatible with thewithoperator.The detailed documentation for each class consists in a commented example (see below). There are more examples in theexamplesdirectory. Runexamples/download.pyfirst to download the media files used by the example scripts (12.8 MB).Decoderimportevent_stream# Decoder's only argument is an Event Stream file path# decoder is an iterator with 3 additional properties: type, width and height#     type is one of 'generic', 'dvs', 'atis' and 'color'#     if type is 'generic', both width and height are None#     otherwise, width and height represent the sensor size in pixelsdecoder=event_stream.Decoder('/path/to/file.es')ifdecoder.type=='generic':print('generic events')else:print(f'{decoder.type}events,{decoder.width}x{decoder.height}sensor')# chunk is a numpy array whose dtype depends on the decoder type:#     generic: [('t', '<u8'), ('bytes', 'object')]#     dvs: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('on', '?')]#     atis: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('exposure', '?'), ('polarity', '?')]#     color: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('r', '?'), ('g', '?'), ('b', '?')]# chunk always contains at least one eventforchunkindecoder:print('{}events, ts = [{}µs,{}µs]'.format(len(chunk),chunk['t'][0],chunk['t'][-1]))IndexedDecoderimportevent_stream# IndexedDecoder's first argument is an Event Stream file path#     its second argument is the duration of each keyframe in µs#     the first keyframe starts with the first event#     all the keyframes are offset accordingly# decoder is an object with 3 properties: type, width and height#     type is one of 'generic', 'dvs', 'atis' and 'color'#     if type is 'generic', both width and height are None#     otherwise, width and height represent the sensor size in pixels# decoder has two methods: keyframes and chunkdecoder=event_stream.IndexedDecoder('/path/to/file.es',40000)ifdecoder.type=='generic':print('generic events')else:print(f'{decoder.type}events,{decoder.width}x{decoder.height}sensor')# number of generated keyframes (one every 40000 µs here)keyframes=decoder.keyframes()forkeyframe_indexinrange(0,keyframes):# keyframe_index must be in the range [0, keyframes[# the returned events have timestamps in the range#     [keyframe_index * T, (keyframe_index + 1) * T[#     where T is the second argument passed to IndexedDecoder# chunk is a numpy array whose dtype depends on the decoder type:#     generic: [('t', '<u8'), ('bytes', 'object')]#     dvs: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('on', '?')]#     atis: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('exposure', '?'), ('polarity', '?')]#     color: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('r', '?'), ('g', '?'), ('b', '?')]# chunk may be emptychunk=decoder.chunk(keyframe_index)iflen(chunk)>0:print('{}/{},{}events, ts = [{}µs,{}µs]'.format(keyframe_index+1,keyframes,len(chunk),chunk['t'][0],chunk['t'][-1]))else:print('{}/{}, 0 events'.format(keyframe_index+1,keyframes))UdpDecoderimportevent_stream# UdpDecoder's only argument is the port to which to bind# decoder is an iterator with 3 additional properties: type, width and height#     type is one of 'generic', 'dvs', 'atis' and 'color'#     if type is 'generic', both width and height are None#     otherwise, width and height represent the sensor size in pixels# The additional properties are updated everytime a packet is receiveddecoder=event_stream.UdpDecoder(12345)# chunk is a numpy array whose dtype depends on the packet type:#     generic: [('t', '<u8'), ('bytes', 'object')]#     dvs: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('on', '?')]#     atis: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('exposure', '?'), ('polarity', '?')]#     color: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('r', '?'), ('g', '?'), ('b', '?')]forchunkindecoder:print('{}events, ts = [{}µs,{}µs]'.format(len(chunk),chunk['t'][0],chunk['t'][-1]))Encoder# Encoder's first argument is an Event Stream file path#     its second argument is the event type, one of 'generic', 'dvs', 'atis' and 'color'#     its third and fourth arguments are the sensor's width and height in pixels#     The width and height are ignored if type is 'generic'encoder=event_stream.Encoder('/path/to/file.es','dvs',1280,720)# write adds an event buffer to the file# the events must be sorted in order of increasing timestamp# the chunk passed to write must be a structured numpy array whose dtype depends on the event type:#     generic: [('t', '<u8'), ('bytes', 'object')]#     dvs: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('on', '?')]#     atis: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('exposure', '?'), ('polarity', '?')]#     color: [('t', '<u8'), ('x', '<u2'), ('y', '<u2'), ('r', '?'), ('g', '?'), ('b', '?')]first_chunk=numpy.array([(0,50,100,True),(100,1203,641,False),(200,73,288,False),(300,901,99,True),],dtype=[('t','<u8'),('x','<u2'),('y','<u2'),('on','?')])encoder.write(first_chunk)# for convenience, event_stream provides dtype constants:#     generic_dtype, dvs_dtype, atis_dtype and color_dtypesecond_chunk=numpy.array([(400,50,100,True),(400,1203,641,False),(401,73,288,False),(401,901,99,True),],dtype=event_stream.dvs_dtype)encoder.write(second_chunk)MatlabSetupAfter downloading this repository (zip file), run the following commands in Matlab:cd/path/to/event_streamcdmatlabmexevent_stream_decode.cppmexevent_stream_encode.cppmexevent_stream_udp.cppThe generated files (extension.mexa64,.mexmaci64or.mexw64depending on your operating system) can be placed in any directory. They contain the functionsevent_stream_decode,event_stream_encodeandevent_stream_udp. You can remove the rest of the repositrory from your machine if you want.Documentationevent_stream_decodeevent_stream_decodereads events from a file.[header,events]=event_stream_decode('/path/to/file.es');header=structwithfields:type:'dvs'width:320height:240events=structwithfields:t:[473225×1uint64]x:[473225×1uint16]y:[473225×1uint16]on:[473225×1logical]headeris a struct with at least one field,type.header.typeis either'generic','dvs','atis'or'color'. Unlessheader.typeis'generic',headerhas two extra fields,widthandheight. They encode the sensor size in pixels.eventsis a struct whose fields are numerical arrays of equal length. Each array encodes one property of the events in the file (for example the timestampt). The number of fields and their names depend onheader.type:'generic':t: [n×1 uint64]bytes: [n×1 string]'dvs':t: [n×1 uint64]x: [nx1 uint16]y: [nx1 uint16]on: [nx1 logical]'atis':t: [n×1 uint64]x: [nx1 uint16]y: [nx1 uint16]exposure: [nx1 logical]polarity: [nx1 logical]'color':t: [n×1 uint64]x: [nx1 uint16]y: [nx1 uint16]r: [nx1 uint8]g: [nx1 uint8]b: [nx1 uint8]event_stream_encodeevent_stream_encodewrites events to a file. The fields names and types must match those returned byevent_stream_decode.header=struct(...'type','dvs',...'width',uint16(320),...'height',uint16(240))events=struct(...'t',uint64([100;200;300]),...'x',uint16([303;4;42]),...'y',uint16([105;201;6]),...'on',logical([true;true;false]))event_stream_encode('/path/to/file.es',header,events);event_stream_udpudp_receiver=udpport("datagram","IPV4","LocalPort",12345,"EnablePortSharing",true,"Timeout",3600);whiletruepacket=read(udp_receiver,1,"uint8");[header,events]=event_stream_udp(packet.Data)endheaderandeventshave the same structure as the data returned byevent_stream_decode.ContributeTo format the code, run:clang-format-isepia.hpppython/*.cppmatlab/*.cppPublishBump the version number insetup.py.Create a new release on GitHub.