├── requirements.txt ├── res ├── pyaer_cover_img.png └── appveyor.yml ├── docs ├── css │ └── highlight-fix.css ├── README.md ├── Makefile ├── mkdocs.yml └── pages │ ├── dvs128.md │ ├── edvs.md │ ├── index.md │ ├── davis.md │ └── dynapse.md ├── scripts ├── device_discovery.py ├── configs │ ├── edvs_config.json │ ├── evk_config.json │ ├── dvs_noise_filter_config.json │ ├── dvxplorer_config.json │ ├── dvs128_config.json │ ├── davis240c_config.json │ ├── davis346_config.json │ └── dynapse_config.json ├── aer_comm │ ├── sample_viewer.yml │ ├── sample_recorder.yml │ ├── sample_config.yml │ ├── test_hdf5_reader.py │ ├── aer_hub │ ├── aer_lstopic │ ├── aer_subscriber │ ├── aer_pubsuber │ ├── custom_comm.py │ ├── aer_publisher │ ├── aer_saver │ └── aer_launch ├── dvs128_test.py ├── dynapse_test.py ├── dvxplorer_test.py ├── edvs_test.py ├── davis240_test.py ├── event_container_test.py ├── dvs_noise_filter_test.py ├── dvs128_thread_test.py ├── dvs128_glumpy.py ├── davis346_test.py ├── davis346_color_test.py ├── davis346_color_events.py ├── dvs128_vispy.py └── timer.py ├── pyaer ├── __about__.py ├── __init__.py ├── log.py ├── container.py ├── pyfragments.swg ├── filters.py ├── utils.py ├── edvs.py └── dvs128.py ├── custom_build ├── compile.conf.bak ├── README.md ├── install-libcaer.sh ├── .travis.yml └── compile ├── LICENSE ├── install-udev.sh ├── README-RPI.md ├── INSTALL_FROM_SOURCE.md ├── .gitignore ├── Makefile ├── .github └── workflows │ └── main.yml ├── setup.py └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | pyzmq 3 | h5py 4 | -------------------------------------------------------------------------------- /res/pyaer_cover_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duguyue100/pyaer/HEAD/res/pyaer_cover_img.png -------------------------------------------------------------------------------- /docs/css/highlight-fix.css: -------------------------------------------------------------------------------- 1 | pre { 2 | background-color: transparent; 3 | border: none; 4 | } 5 | 6 | code { 7 | background-color: transparent; 8 | } 9 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Generate docs 2 | 3 | ## Generate and push to website 4 | 5 | ``` 6 | $ mkdocs gh-deploy --config-file ../pyaer/docs/mkdocs.yml --remote-branch master 7 | ``` 8 | -------------------------------------------------------------------------------- /scripts/device_discovery.py: -------------------------------------------------------------------------------- 1 | """Check device discovery.""" 2 | 3 | from pyaer import libcaer 4 | 5 | 6 | discovered_result = libcaer.device_discover_new(-1) 7 | 8 | print(discovered_result.deviceInfo.davisInfo.deviceString) 9 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Generate docs 2 | # 3 | # Author: Yuhuang Hu 4 | # Email : yuhuang.hu@ini.uzh.ch 5 | 6 | serve-docs: 7 | python ./autogen.py 8 | mkdocs serve 9 | 10 | build-docs: 11 | python ./autogen.py 12 | mkdocs build 13 | -------------------------------------------------------------------------------- /pyaer/__about__.py: -------------------------------------------------------------------------------- 1 | """About page.""" 2 | __all__ = ["__version__", "__author__", "__author_email__", "__url__"] 3 | 4 | __version__ = "0.2.7a0" 5 | __author__ = "Yuhuang Hu" 6 | __author_email__ = "duguyue100@gmail.com" 7 | __url__ = "https://github.com/duguyue100/pyaer" 8 | -------------------------------------------------------------------------------- /scripts/configs/edvs_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "cas": 54, 3 | "injGnd": 1108364, 4 | "reqPd": 16777215, 5 | "puX": 8159221, 6 | "diffOff": 132, 7 | "req": 159147, 8 | "refr": 6, 9 | "puY": 16777215, 10 | "diffOn": 482443, 11 | "diff": 30153, 12 | "foll": 51, 13 | "Pr": 3 14 | } 15 | -------------------------------------------------------------------------------- /scripts/configs/evk_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bias_simple": "default", 3 | "global_hold_enable": true, 4 | "global_reset_enable": false, 5 | "global_reset_during_readout": false, 6 | "fixed_read_time_enable": false, 7 | "event_flatten": false, 8 | "event_on_only": false, 9 | "event_off_only": false, 10 | "subsample_enable": false, 11 | "area_blocking_enable": false, 12 | "dual_binning_enable": false 13 | } 14 | -------------------------------------------------------------------------------- /scripts/aer_comm/sample_viewer.yml: -------------------------------------------------------------------------------- 1 | "Hub": 2 | "use_default": true 3 | 4 | "Subscriber-frame": 5 | "topic": "" 6 | "use_default_sub": false 7 | "custom_sub": "./custom_comm.py/DVViewerSubscriber" 8 | "name": "dv_subscriber" 9 | 10 | "Publisher-davis": 11 | "master_topic": "davis_1" 12 | "use_default_pub": true 13 | "device": "DAVIS" 14 | "noise_filter": true 15 | "bias_file": "../configs/davis346_config.json" 16 | "name": "dvxplorer_publisher" 17 | -------------------------------------------------------------------------------- /scripts/configs/dvs_noise_filter_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "sw_background_activity_two_levels": true, 3 | "sw_background_activity_check_polarity": true, 4 | "sw_background_activity_support_min": 2, 5 | "sw_background_activity_support_max": 8, 6 | "sw_background_activity_time": 2000, 7 | "sw_background_activity_enable": true, 8 | "sw_refractory_period_time": 200, 9 | "sw_refractory_period_enable": true, 10 | "sw_hotpixel_enable": true, 11 | "sw_hotpixel_learn": true 12 | } 13 | -------------------------------------------------------------------------------- /custom_build/compile.conf.bak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Turn it to true if you are not building the repository for the first time 4 | REBUILDING=false 5 | 6 | # defien python version (experimental) 7 | # only support python 2 at this moment 8 | PYTHON_VERSION=2 9 | 10 | # define python executable 11 | CONDA_LIB_PATH=$HOME/anaconda2/lib 12 | CONDA_PKG_CONFIG_PATH=$CONDA_LIB_PATH/pkgconfig 13 | 14 | # false if build libcaer locally 15 | # true if you have a system installed libcaer 16 | LIBCAER_INSTALLED=false 17 | -------------------------------------------------------------------------------- /scripts/aer_comm/sample_recorder.yml: -------------------------------------------------------------------------------- 1 | "Hub": 2 | "use_default": true 3 | 4 | "Saver-hdf5": 5 | "name": "aer_saver" 6 | "topic": "" 7 | "filename": "~/data/dark/dark_1.hdf5" 8 | 9 | "Subscriber-frame": 10 | "name": "frame_subscriber" 11 | "topic": "" 12 | "use_default_sub": false 13 | "custom_sub": "./custom_comm.py/CustomSubscriber" 14 | 15 | "Publisher-davis": 16 | "name": "davis_publisher" 17 | "master_topic": "davis_1" 18 | "use_default_pub": true 19 | # "custom_pub": "./custom_comm.py/CustomPublisher" 20 | "device": "DAVIS" 21 | "bias_file": "../configs/davis346_config.json" 22 | "noise_filter": true 23 | -------------------------------------------------------------------------------- /scripts/configs/dvxplorer_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mux_timestamp_reset": false, 3 | "drop_extinput_on_transfer_stall": true, 4 | "mux_drop_dvs_on_transfer_stall": false, 5 | "extinput_detect_rising_edges": false, 6 | "extinput_detect_falling_edges": false, 7 | "extinput_detect_pulses": true, 8 | "extinput_detect_pulse_polarity": true, 9 | "extinput_detect_pulse_length": 10, 10 | "extinput_ran_generator": false, 11 | "extinput_generate_pulse_polarity": true, 12 | "extinput_generate_pulse_interval": 10, 13 | "extinput_generate_pulse_length": 5, 14 | "extinput_generate_inject_on_rising_edge": false, 15 | "extinput_generate_inject_on_falling_edge": false, 16 | "usb_early_packet_delay": 8, 17 | "bias_simple": "default" 18 | } 19 | -------------------------------------------------------------------------------- /scripts/configs/dvs128_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "cas": 54, 3 | "injGnd": 1108364, 4 | "reqPd": 16777215, 5 | "puX": 8159221, 6 | "diffOff": 132, 7 | "req": 159147, 8 | "refr": 6, 9 | "puY": 16777215, 10 | "diffOn": 482443, 11 | "diff": 30153, 12 | "foll": 51, 13 | "Pr": 3, 14 | "noise_filter_configs": { 15 | "sw_background_activity_two_levels": true, 16 | "sw_background_activity_check_polarity": true, 17 | "sw_background_activity_support_min": 2, 18 | "sw_background_activity_support_max": 8, 19 | "sw_background_activity_time": 2000, 20 | "sw_background_activity_enable": true, 21 | "sw_refractory_period_time": 200, 22 | "sw_refractory_period_enable": true, 23 | "sw_hotpixel_enable": true, 24 | "sw_hotpixel_learn": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: PyAER Documentation 2 | theme: readthedocs 3 | 4 | markdown_extensions: 5 | - codehilite: 6 | guess_lang: false 7 | use_pygments: true 8 | noclasses: true 9 | 10 | # theme_dir: theme 11 | docs_dir: sources 12 | repo_url: https://github.com/duguyue100/pyaer 13 | site_url: https://dgyblog.com/pyaer-doc 14 | site_description: 'Documentation for PyAER.' 15 | 16 | markdown_extensions: 17 | - codehilite 18 | 19 | dev_addr: '0.0.0.0:8000' 20 | 21 | extra_css: 22 | - css/highlight-fix.css 23 | 24 | pages: 25 | - Home: index.md 26 | - USB Device: usb-device.md 27 | - Serial Device: serial-device.md 28 | - DVS128: dvs128.md 29 | - DAVIS: davis.md 30 | - eDVS: edvs.md 31 | - DYNAPSE: dynapse.md 32 | - Filters: filters.md 33 | - Utils: utils.md 34 | - Logging: log.md 35 | -------------------------------------------------------------------------------- /docs/pages/dvs128.md: -------------------------------------------------------------------------------- 1 | {{autogenerated}} 2 | 3 | --- 4 | 5 | ## Bias Example 6 | 7 | ```json 8 | { 9 | "cas": 54, 10 | "injGnd": 1108364, 11 | "reqPd": 16777215, 12 | "puX": 8159221, 13 | "diffOff": 132, 14 | "req": 159147, 15 | "refr": 6, 16 | "puY": 16777215, 17 | "diffOn": 482443, 18 | "diff": 30153, 19 | "foll": 51, 20 | "Pr": 3, 21 | "noise_filter_configs": { 22 | "sw_background_activity_two_levels": true, 23 | "sw_background_activity_check_polarity": true, 24 | "sw_background_activity_support_min": 2, 25 | "sw_background_activity_support_max": 8, 26 | "sw_background_activity_time": 2000, 27 | "sw_background_activity_enable": true, 28 | "sw_refractory_period_time": 200, 29 | "sw_refractory_period_enable": true, 30 | "sw_hotpixel_enable": true, 31 | "sw_hotpixel_learn": true 32 | } 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/pages/edvs.md: -------------------------------------------------------------------------------- 1 | 2 | {{autogenerated}} 3 | 4 | --- 5 | 6 | ## Bias Example 7 | 8 | ```json 9 | { 10 | "cas": 54, 11 | "injGnd": 1108364, 12 | "reqPd": 16777215, 13 | "puX": 8159221, 14 | "diffOff": 132, 15 | "req": 159147, 16 | "refr": 6, 17 | "puY": 16777215, 18 | "diffOn": 482443, 19 | "diff": 30153, 20 | "foll": 51, 21 | "Pr": 3, 22 | "noise_filter_configs": { 23 | "sw_background_activity_two_levels": true, 24 | "sw_background_activity_check_polarity": true, 25 | "sw_background_activity_support_min": 2, 26 | "sw_background_activity_support_max": 8, 27 | "sw_background_activity_time": 2000, 28 | "sw_background_activity_enable": true, 29 | "sw_refractory_period_time": 200, 30 | "sw_refractory_period_enable": true, 31 | "sw_hotpixel_enable": true, 32 | "sw_hotpixel_learn": true 33 | } 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /scripts/aer_comm/sample_config.yml: -------------------------------------------------------------------------------- 1 | "Hub": 2 | "use_default": true 3 | 4 | "Subscriber-frame": 5 | "use_default": true 6 | "url": "tcp://127.0.0.1" 7 | "port": 5099 8 | "name": "frame-subscriber" 9 | "topic": "" 10 | "use_default_sub": false 11 | "custom_sub": "./custom_comm.py" 12 | "custom_class": "CustomSubscriber" 13 | "custom_1": 1234 14 | "custom_2": "something else" 15 | "custom_3": true 16 | 17 | "Publisher-davis": 18 | "use_default": true 19 | "url": "tcp://127.0.0.1" 20 | "port": 5100 21 | "master_topic": "davis_1" 22 | "name": "davis_publisher" 23 | "device": "DAVIS" 24 | "noise_filter": true 25 | "use_default_pub": false 26 | "bias_file": "../configs/davis346_config.json" 27 | "custom_pub": "./custom_comm.py" 28 | "custom_class": "CustomPublisher" 29 | "custom_1": 123 30 | "custom_2": "something" 31 | "custom_3": false 32 | -------------------------------------------------------------------------------- /pyaer/__init__.py: -------------------------------------------------------------------------------- 1 | """Properly init the package. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import absolute_import 7 | from __future__ import print_function 8 | 9 | import os 10 | 11 | from pyaer import log 12 | from pyaer.__about__ import __author__ # noqa 13 | from pyaer.__about__ import __version__ # noqa 14 | 15 | FILE_PATH = os.path.realpath(__file__) 16 | CURR_PATH = os.path.dirname(os.path.realpath(__file__)) 17 | PKG_PATH = os.path.dirname(CURR_PATH) 18 | 19 | # System logging level 20 | LOG_LEVEL = log.DEBUG 21 | 22 | try: 23 | from pyaer import libcaer_wrap as libcaer # noqa 24 | except ImportError: 25 | raise ImportError( 26 | "libcaer might not be in the LD_LIBRARY_PATH " 27 | "or your numpy might not be the required version. " 28 | "Try to load _libcaer_wrap.so from the package " 29 | "directory, this will provide more information." 30 | ) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2022 Yuhuang Hu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/aer_comm/test_hdf5_reader.py: -------------------------------------------------------------------------------- 1 | """Test the HDF5 reader. 2 | 3 | Author: Yuhuang Hu 4 | Email : yuhuang.hu@ini.uzh.ch 5 | """ 6 | 7 | from __future__ import print_function, absolute_import 8 | 9 | import os 10 | from contextlib import suppress 11 | from pyaer.comm import AERHDF5Reader 12 | 13 | 14 | data_path = os.path.join( 15 | os.environ["HOME"], "data", "pyaer_test.hdf5") 16 | 17 | reader = AERHDF5Reader(data_path) 18 | 19 | for device, groups in reader.get_keys().items(): 20 | for group_name in groups: 21 | 22 | frame = reader.get_frame(device, group_name) 23 | events = reader.get_polarity_events(device, group_name) 24 | imu = reader.get_imu_events(device, group_name) 25 | special = reader.get_special_events(device, group_name) 26 | 27 | print("-"*50) 28 | with suppress(Exception): 29 | print("Frame:", frame.shape) 30 | 31 | with suppress(Exception): 32 | print("Events:", events.shape) 33 | 34 | with suppress(Exception): 35 | print("IMU:", imu.shape) 36 | 37 | with suppress(Exception): 38 | print("Special:", special.shape) 39 | -------------------------------------------------------------------------------- /install-udev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | touch /tmp/65-inivation.rules 3 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="152a", ATTR{idProduct}=="84[0-1]?", MODE="0666" 4 | ' >> /tmp/65-inivation.rules 5 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014", MODE="0666"' >> /tmp/65-inivation.rules 6 | 7 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1134", ATTR{idProduct}=="8001", MODE="0666"' >> /tmp/65-inivation.rules 8 | 9 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="8613", MODE="0666"' >> /tmp/65-inivation.rules 10 | 11 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="0053", MODE="0666"' >> /tmp/65-inivation.rules 12 | 13 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="00f3", MODE="0666"' >> /tmp/65-inivation.rules 14 | 15 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="00f1", MODE="0666"' >> /tmp/65-inivation.rules 16 | 17 | # copied from https://gitlab.com/inivation/libcaer 18 | echo "Copying udev rule (needs root privileges)." 19 | sudo cp /tmp/65-inivation.rules /etc/udev/rules.d/ 20 | 21 | echo "Reloading udev rules." 22 | sudo udevadm control --reload-rules 23 | sudo udevadm trigger 24 | 25 | echo "Done!" 26 | -------------------------------------------------------------------------------- /README-RPI.md: -------------------------------------------------------------------------------- 1 | # Build PyAER on Raspberry Pi 2 | 3 | This is a working build instruction on Raspberry Pi 3B with Raspbian Stretch. 4 | 5 | ## Install dependencies 6 | 7 | ```bash 8 | sudo apt-get install build-essential cmake pkg-config libusb-1.0-0-dev 9 | sudo apt-get install automake bison libpcre3-dev 10 | ``` 11 | 12 | ## Install `libcaer` 13 | 14 | The `libcaer` can be installed as follows 15 | 16 | ```bash 17 | git clone https://gitlab.com/inivation/dv/libcaer.git 18 | cd libcaer 19 | cmake -DCMAKE_INSTALL_PREFIX=/usr . 20 | make -j4 21 | sudo make install 22 | ``` 23 | 24 | ## Install `swig` 25 | 26 | You can compile `swig` with the following steps: 27 | 28 | ```bash 29 | git clone https://github.com/duguyue100/swig 30 | cd swig 31 | ./autogen.sh 32 | # choose one of the configure settings 33 | ./configure --with-python=$(command -v python) --without-python2 --without-pcre # for python 3 34 | make -j4 35 | sudo make install 36 | ``` 37 | 38 | ## Compile `pyaer` 39 | 40 | Compile `pyaer` with the following steps: 41 | 42 | ```bash 43 | git clone https://github.com/duguyue100/pyaer 44 | sudo make install 45 | ``` 46 | 47 | ## Contacts 48 | 49 | Yuhuang Hu 50 | Email: duguyue100@gmail.com 51 | -------------------------------------------------------------------------------- /pyaer/log.py: -------------------------------------------------------------------------------- 1 | """Logger for PyAER. 2 | 3 | NOTE: this is different from libcaer's logger. 4 | 5 | Author: Yuhuang Hu 6 | Email : duguyue100@gmail.com 7 | """ 8 | import logging 9 | from logging import Logger 10 | from typing import Optional 11 | from typing import TextIO 12 | 13 | # Remaps logging levels for easy access. 14 | NOTSET = logging.NOTSET 15 | DEBUG = logging.DEBUG 16 | INFO = logging.INFO 17 | WARNING = logging.WARNING 18 | ERROR = logging.ERROR 19 | CRITICAL = logging.CRITICAL 20 | 21 | 22 | def get_logger( 23 | logger_name: Optional[str], logger_level: int, stream: Optional[TextIO] = None 24 | ) -> Logger: 25 | """Gets a logger for the script. 26 | 27 | Args: 28 | logger_name: the name of the logger. 29 | logger_level: the minimal level that trigger the logger. 30 | stream: If None, sys.stderr will be used. 31 | 32 | Returns: 33 | A logger to handel the logging in the script. 34 | """ 35 | logger = logging.getLogger(name=logger_name) 36 | logger.setLevel(level=logger_level) 37 | 38 | ch = logging.StreamHandler(stream) 39 | ch.setLevel(level=logger_level) 40 | 41 | formatter = logging.Formatter( 42 | fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", 43 | datefmt="%Y/%m/%d %I:%M:%S %p", 44 | ) 45 | ch.setFormatter(formatter) 46 | 47 | logger.addHandler(ch) 48 | 49 | return logger 50 | -------------------------------------------------------------------------------- /INSTALL_FROM_SOURCE.md: -------------------------------------------------------------------------------- 1 | # Install PyAER from Source 2 | 3 | ## Install `libcaer` 4 | 5 | ```bash 6 | # for Ubuntu 7 | sudo apt-get install libcaer-dev 8 | # for macOS 9 | brew install libcaer 10 | ``` 11 | 12 | ## Install `swig` 13 | 14 | This repository uses SWIG to create Python bindings. And you will need to 15 | compile the latest SWIG from source. The reason is because current SWIG 16 | cannot handle some cases in `libcaer`, we made a modified SWIG for this purpose. 17 | 18 | 1. Install compilation dependency 19 | 20 | ```bash 21 | sudo apt-get install automake 22 | sudo apt-get install bison 23 | ``` 24 | 25 | _There might be other dependencies for compiling SWIG_. 26 | 27 | 2. Compile SIWG 28 | 29 | ```bash 30 | git clone https://github.com/duguyue100/swig 31 | cd swig 32 | ./autogen.sh 33 | ./configure 34 | # For compiling SWIG with Python 35 | ./configure --without-alllang --with-python=$(command -v python) 36 | make -j4 37 | sudo make install 38 | ``` 39 | 40 | __NOTE:__ For ARM-based Linux, you may also add `--without-pcre` to ignore the error during compilation. 41 | 42 | __NOTE:__ If you are not compile the SWIG with system Python distribution, 43 | it won't link to the custom Python automatically. 44 | 45 | You will need to configure `LD_LIBRARY_PATH` for swig running properly, i.e., 46 | 47 | ```bash 48 | LD_LIBRARY_PATH=$HOME/miniconda/lib:$LD_LIBRARY_PATH swig 49 | ``` 50 | 51 | ## Build `pyaer` from source 52 | 53 | ```bash 54 | git clone https://github.com/duguyue100/pyaer.git 55 | cd pyaer 56 | make develop 57 | ``` 58 | -------------------------------------------------------------------------------- /scripts/dvs128_test.py: -------------------------------------------------------------------------------- 1 | """DVS128 Test. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import numpy as np 9 | import cv2 10 | 11 | from pyaer.dvs128 import DVS128 12 | 13 | device = DVS128() 14 | 15 | print ("Device ID:", device.device_id) 16 | if device.device_is_master: 17 | print ("Device is master.") 18 | else: 19 | print ("Device is slave.") 20 | print ("Device Serial Number:", device.device_serial_number) 21 | print ("Device String:", device.device_string) 22 | print ("Device USB bus Number:", device.device_usb_bus_number) 23 | print ("Device USB device address:", device.device_usb_device_address) 24 | print ("Device size X:", device.dvs_size_X) 25 | print ("Device size Y:", device.dvs_size_Y) 26 | print ("Logic Version:", device.logic_version) 27 | 28 | device.start_data_stream() 29 | # load new config 30 | device.set_bias_from_json("./scripts/configs/dvs128_config.json") 31 | print (device.get_bias()) 32 | 33 | clip_value = 3 34 | histrange = [(0, v) for v in (128, 128)] 35 | 36 | while True: 37 | try: 38 | (pol_events, num_pol_event, 39 | special_events, num_special_event) = \ 40 | device.get_event("events_hist") 41 | if num_pol_event != 0: 42 | img = pol_events[..., 1]-pol_events[..., 0] 43 | img = np.clip(img, -clip_value, clip_value) 44 | img = img+clip_value 45 | 46 | cv2.imshow("image", img/float(clip_value*2)) 47 | print ("Number of events:", num_pol_event, "Number of special events:", 48 | num_special_event) 49 | 50 | if cv2.waitKey(1) & 0xFF == ord('q'): 51 | break 52 | 53 | except KeyboardInterrupt: 54 | device.shutdown() 55 | break 56 | -------------------------------------------------------------------------------- /scripts/dynapse_test.py: -------------------------------------------------------------------------------- 1 | """DYNAP-SE Test Example. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | from pyaer.dynapse import DYNAPSE 9 | from pyaer import utils 10 | 11 | device = DYNAPSE() 12 | 13 | print ("Device ID:", device.device_id) 14 | if device.device_is_master: 15 | print ("Device is master.") 16 | else: 17 | print ("Device is slave.") 18 | print ("Device Serial Number:", device.device_serial_number) 19 | print ("Device String:", device.device_string) 20 | print ("Device USB bus Number:", device.device_usb_bus_number) 21 | print ("Device USB device address:", device.device_usb_device_address) 22 | print ("Logic Version:", device.logic_version) 23 | print ("Logic Clock:", device.logic_clock) 24 | print ("Chip ID:", device.chip_id) 25 | print ("AER has statistics:", device.aer_has_statistics) 26 | print ("MUX has statistics:", device.mux_has_statistics) 27 | 28 | device.start_data_stream() 29 | 30 | bias_obj = utils.load_dynapse_bias("./scripts/configs/dynapse_config.json") 31 | 32 | # set bias from json file 33 | scope = { 34 | 0: [0, 1, 2, 3], 35 | } 36 | 37 | device.set_bias_from_json("./scripts/configs/dynapse_config.json", 38 | clear_sram=False, setup_sram=False, 39 | fpga_bias=True, scope=scope) 40 | # print FPGA biases 41 | print (device.get_fpga_bias()) 42 | 43 | # set biases for a single chip 44 | device.set_chip_bias(bias_obj, chip_id=1) 45 | 46 | while True: 47 | try: 48 | events = device.get_event() 49 | 50 | if events is not None: 51 | print ("Number of events from DYNAPSE : %d" % 52 | (events[1])) 53 | except KeyboardInterrupt: 54 | print ("Device shutting down...") 55 | device.shutdown() 56 | break 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | swigpy/ 2 | libcaer_wrap.py 3 | compile.conf 4 | pyflags_wrap.c 5 | *.lprof 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | .hypothesis/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # dotenv 89 | .env 90 | 91 | # virtualenv 92 | .venv 93 | venv/ 94 | ENV/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | docs/site 105 | docs/sources 106 | 107 | # mypy 108 | .mypy_cache/ 109 | 110 | # Mac 111 | .DS_Store 112 | 113 | # idea (jetbrains's ide) 114 | .idea 115 | -------------------------------------------------------------------------------- /scripts/aer_comm/aer_hub: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """AER Hub. 4 | 5 | Author: Yuhuang Hu 6 | Email : yuhuang.hu@ini.uzh.ch 7 | """ 8 | 9 | from __future__ import print_function, absolute_import 10 | 11 | import json 12 | import argparse 13 | 14 | from pyaer.comm import AERHub 15 | 16 | parser = argparse.ArgumentParser("AER Hub") 17 | 18 | parser.add_argument("--url", type=str, 19 | default="tcp://127.0.0.1", 20 | help="AERHub URL") 21 | 22 | # Note that the publisher port and subscriber port are 23 | # hub_sub_port and hub_pub_port respectively. 24 | # This reversed order is intentional. 25 | # User doesn't need to know. 26 | parser.add_argument("--publisher_port", type=int, 27 | default=5100, 28 | help="the port that connects all publishers") 29 | parser.add_argument("--subscriber_port", type=int, 30 | default=5099, 31 | help="the port that connects all subscribers") 32 | 33 | parser.add_argument("--aer_hub_name", type=str, 34 | default="PyAER Message Hub") 35 | 36 | args = parser.parse_args() 37 | 38 | # print all options 39 | print("="*50) 40 | print(json.dumps(args.__dict__, indent=4, sort_keys=True)) 41 | print("="*50) 42 | 43 | 44 | aer_hub = AERHub(url=args.url, 45 | hub_pub_port=args.subscriber_port, 46 | hub_sub_port=args.publisher_port, 47 | aer_hub_name=args.aer_hub_name) 48 | 49 | aer_hub.logger.info("="*50) 50 | aer_hub.logger.info("Tools") 51 | aer_hub.logger.info("aer_hub: launch a central message relay hub") 52 | aer_hub.logger.info("aer_lstopic: display all published topics") 53 | aer_hub.logger.info("aer_publisher: add a custom publisher") 54 | aer_hub.logger.info("aer_subscriber: add a custom subscriber") 55 | aer_hub.logger.info("aer_saver: add an AER Saver") 56 | aer_hub.logger.info("="*50) 57 | 58 | # run the hub 59 | aer_hub.run() 60 | -------------------------------------------------------------------------------- /scripts/aer_comm/aer_lstopic: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """List all available topics. 4 | 5 | Author: Yuhuang Hu 6 | Email : yuhuang.hu@ini.uzh.ch 7 | """ 8 | 9 | from __future__ import print_function, absolute_import 10 | 11 | import os 12 | import argparse 13 | from pyaer.comm import AERSubscriber 14 | 15 | 16 | class ListSubscriber(AERSubscriber): 17 | def __init__(self, url="tcp://127.0.0.1", 18 | port=5099, topic='', name="Topic List"): 19 | """ListSubscriber. 20 | 21 | Used for list all the topics. 22 | """ 23 | super().__init__(url=url, port=port, topic=topic, name=name) 24 | 25 | def run(self): 26 | topic_list = [] 27 | while True: 28 | data = self.socket.recv_multipart() 29 | 30 | topic_name = self.unpack_data_name( 31 | data[:2], topic_name_only=True) 32 | 33 | if topic_name not in topic_list: 34 | topic_list.append(topic_name) 35 | topic_list.sort() 36 | 37 | # Clear screen and write 38 | os.system('cls' if os.name == 'nt' else 'clear') 39 | print("="*50) 40 | print("List of topic names") 41 | print("="*50) 42 | for tn in topic_list: 43 | print(tn) 44 | print("="*50) 45 | 46 | 47 | parser = argparse.ArgumentParser("AER List Topics") 48 | 49 | parser.add_argument("--url", type=str, 50 | default="tcp://127.0.0.1", 51 | help="AER list topic URL") 52 | 53 | # Note that the publisher port and subscriber port are 54 | # hub_sub_port and hub_pub_port respectively. 55 | # This reversed order is intentional. 56 | # User doesn't need to know. 57 | parser.add_argument("--port", type=int, 58 | default=5099, 59 | help="the port that connects all subscribers") 60 | 61 | args = parser.parse_args() 62 | 63 | list_sub = ListSubscriber(url=args.url, port=args.port) 64 | 65 | list_sub.run() 66 | -------------------------------------------------------------------------------- /scripts/dvxplorer_test.py: -------------------------------------------------------------------------------- 1 | """DVXplorer Test. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function, absolute_import 7 | 8 | import numpy as np 9 | import cv2 10 | 11 | from pyaer.dvxplorer import DVXPLORER 12 | 13 | device = DVXPLORER() 14 | 15 | print("Device ID:", device.device_id) 16 | print("Device Serial Number:", device.device_serial_number) 17 | print("Device USB bus Number:", device.device_usb_bus_number) 18 | print("Device USB device address:", device.device_usb_device_address) 19 | print("Device String:", device.device_string) 20 | print("Device Firmware Version:", device.firmware_version) 21 | print("Logic Version:", device.logic_version) 22 | print("Device Chip ID:", device.chip_id) 23 | if device.device_is_master: 24 | print("Device is master.") 25 | else: 26 | print("Device is slave.") 27 | print("MUX has statistics:", device.mux_has_statistics) 28 | print("Device size X:", device.dvs_size_X) 29 | print("Device size Y:", device.dvs_size_Y) 30 | print("DVS has statistics:", device.dvs_has_statistics) 31 | print("IMU Type:", device.imu_type) 32 | print("EXT input has generator:", device.ext_input_has_generator) 33 | 34 | clip_value = 3 35 | histrange = [(0, v) for v in (device.dvs_size_Y, device.dvs_size_X)] 36 | 37 | device.start_data_stream() 38 | # load new config 39 | device.set_bias_from_json("./scripts/configs/dvxplorer_config.json") 40 | 41 | print(device.get_bias()) 42 | 43 | while True: 44 | try: 45 | (pol_events, num_pol_event, 46 | special_events, num_special_event, 47 | imu_events, num_imu_event) = \ 48 | device.get_event("events_hist") 49 | 50 | print("Number of events:", num_pol_event) 51 | 52 | if num_pol_event != 0: 53 | img = pol_events[..., 1]-pol_events[..., 0] 54 | img = np.clip(img, -clip_value, clip_value) 55 | img = (img+clip_value)/float(clip_value*2) 56 | 57 | cv2.imshow("image", img) 58 | 59 | cv2.waitKey(1) 60 | 61 | except KeyboardInterrupt: 62 | device.shutdown() 63 | cv2.destroyAllWindows() 64 | break 65 | -------------------------------------------------------------------------------- /scripts/edvs_test.py: -------------------------------------------------------------------------------- 1 | """DVS128 Test. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import numpy as np 9 | import cv2 10 | 11 | from pyaer.edvs import eDVS 12 | 13 | device = eDVS() 14 | 15 | print ("Device ID:", device.device_id) 16 | if device.device_is_master: 17 | print ("Device is master.") 18 | else: 19 | print ("Device is slave.") 20 | print ("Device String:", device.device_string) 21 | print ("Device size X:", device.dvs_size_X) 22 | print ("Device size Y:", device.dvs_size_Y) 23 | 24 | device.start_data_stream() 25 | # load new config 26 | device.set_bias_from_json("./scripts/configs/edvs_config.json") 27 | print (device.get_bias()) 28 | 29 | device.close() 30 | 31 | clip_value = 1 32 | histrange = [(0, v) for v in (128, 128)] 33 | 34 | 35 | # @profile 36 | def get_event(device): 37 | (pol_events, num_pol_event, 38 | special_events, num_special_event) = \ 39 | device.get_event() 40 | if num_pol_event != 0: 41 | pol_on = (pol_events[:, 3] == 1) 42 | pol_off = np.logical_not(pol_on) 43 | img_on, _, _ = np.histogram2d( 44 | pol_events[pol_on, 2], pol_events[pol_on, 1], 45 | bins=(128, 128), range=histrange) 46 | img_off, _, _ = np.histogram2d( 47 | pol_events[pol_off, 2], pol_events[pol_off, 1], 48 | bins=(128, 128), range=histrange) 49 | if clip_value is not None: 50 | integrated_img = np.clip( 51 | (img_on-img_off), -clip_value, clip_value) 52 | else: 53 | integrated_img = (img_on-img_off) 54 | img = integrated_img+clip_value 55 | 56 | cv2.imshow("image", img/float(clip_value*2)) 57 | print ("Number of events:", num_pol_event, "Number of special events:", 58 | num_special_event) 59 | del pol_events, num_pol_event, special_events, num_special_event 60 | 61 | if cv2.waitKey(1) & 0xFF == ord('q'): 62 | return 63 | 64 | 65 | while True: 66 | try: 67 | get_event(device) 68 | 69 | except KeyboardInterrupt: 70 | device.shutdown() 71 | break 72 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This is a Python template Makefile, do modification as you want 2 | # 3 | # Project: PyAER 4 | # Author: Yuhuang Hu 5 | # Email : duguyue100@gmail.com 6 | 7 | PYTHONPATH="$(shell printenv PYTHONPATH):$(PWD)" 8 | PYTHONLIBPATH="$(shell python -c 'from sysconfig import get_paths; print(get_paths()["stdlib"]+"/..")')" 9 | PYTHONLIBPATHWIN="$(shell python -c 'from sysconfig import get_paths; print(get_paths()["stdlib"]+"\..")')" 10 | 11 | clean: 12 | find . -name '*.pyc' -exec rm --force {} + 13 | find . -name '*.pyo' -exec rm --force {} + 14 | find . -name '*~' -exec rm --force {} + 15 | 16 | dvs128-test: 17 | python ./scripts/dvs128_test.py 18 | 19 | edvs-test: 20 | python ./scripts/edvs_test.py 21 | 22 | dvs128-thread-test: 23 | python ./scripts/dvs128_thread_test.py 24 | 25 | dvs128-vispy: 26 | python ./scripts/dvs128_vispy.py 27 | 28 | dvs128-glumpy: 29 | python ./scripts/dvs128_glumpy.py 30 | 31 | davis240-test: 32 | python ./scripts/davis240_test.py 33 | 34 | davis346-test: 35 | python ./scripts/davis346_test.py 36 | 37 | davis346-color-test: 38 | python ./scripts/davis346_color_test.py 39 | 40 | davis346-color-events: 41 | python ./scripts/davis346_color_events.py 42 | 43 | dvxplorer-test: 44 | python ./scripts/dvxplorer_test.py 45 | 46 | dynapse-test: 47 | python ./scripts/dynapse_test.py 48 | 49 | dvs-noise-filter-test: 50 | python ./scripts/dvs_noise_filter_test.py 51 | 52 | device-discovery: 53 | python ./scripts/device_discovery.py 54 | 55 | event-container-test: 56 | python ./scripts/event_container_test.py 57 | 58 | install: 59 | LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(PYTHONLIBPATH) python setup.py install 60 | 61 | develop: 62 | LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(PYTHONLIBPATH) python setup.py develop 63 | 64 | develop-uninstall: 65 | LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(PYTHONLIBPATH) python setup.py develop --uninstall 66 | 67 | build-pyaer: 68 | LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(PYTHONLIBPATH) python setup.py build 69 | 70 | build-win: 71 | LD_LIBRARY_PATH=$(PYTHONLIBPATHWIN) python setup.py build -cmingw32 72 | 73 | build-wheel: 74 | LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(PYTHONLIBPATH) python setup.py bdist_wheel 75 | 76 | cleanall: 77 | -------------------------------------------------------------------------------- /scripts/configs/davis240c_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "DiffBn_coarse": 4, 3 | "DiffBn_fine": 39, 4 | "ONBn_coarse": 6, 5 | "ONBn_fine": 200, 6 | "OFFBn_coarse": 4, 7 | "OFFBn_fine": 0, 8 | "APSCasEPC_coarse": 5, 9 | "APSCasEPC_fine": 185, 10 | "DiffCasBNC_coarse": 5, 11 | "DiffCasBNC_fine": 115, 12 | "APSROSFBn_coarse": 6, 13 | "APSROSFBn_fine": 219, 14 | "LocalBufBn_coarse": 5, 15 | "LocalBufBn_fine": 164, 16 | "PixInvBn_coarse": 5, 17 | "PixInvBn_fine": 129, 18 | "PrBp_coarse": 2, 19 | "PrBp_fine": 58, 20 | "PrSFBp_coarse": 1, 21 | "PrSFBp_fine": 33, 22 | "RefrBp_coarse": 4, 23 | "RefrBp_fine": 25, 24 | "AEPdBn_coarse": 6, 25 | "AEPdBn_fine": 91, 26 | "LcolTimeoutBn_coarse": 5, 27 | "LcolTimeoutBn_fine": 49, 28 | "AEPuXBp_coarse": 4, 29 | "AEPuXBp_fine": 80, 30 | "AEPuYBp_coarse": 7, 31 | "AEPuYBp_fine": 152, 32 | "IFThrBn_coarse": 5, 33 | "IFThrBn_fine": 255, 34 | "IFRefrBn_coarse": 5, 35 | "IFRefrBn_fine": 255, 36 | "PadFollBn_coarse": 7, 37 | "PadFollBn_fine": 215, 38 | "APSOverflowLevelBn_coarse": 6, 39 | "APSOverflowLevelBn_fine": 253, 40 | "BiasBuffer_coarse": 5, 41 | "BiasBuffer_fine": 254, 42 | "aps_enabled": true, 43 | "dvs_enabled": true, 44 | "exposure": 4000, 45 | "autoexposure": true, 46 | "frame_interval": 0, 47 | "imu_enabled": true, 48 | "imu_acc_scale": 3, 49 | "imu_gyro_scale": 3, 50 | "imu_low_pass_filter": 0, 51 | "noise_filter_configs": { 52 | "sw_background_activity_two_levels": true, 53 | "sw_background_activity_check_polarity": true, 54 | "sw_background_activity_support_min": 2, 55 | "sw_background_activity_support_max": 8, 56 | "sw_background_activity_time": 2000, 57 | "sw_background_activity_enable": true, 58 | "sw_refractory_period_time": 200, 59 | "sw_refractory_period_enable": true, 60 | "sw_hotpixel_enable": true, 61 | "sw_hotpixel_learn": true 62 | }, 63 | "ext_input_enabled": false, 64 | "detect_falling_edges": false, 65 | "detect_rising_edges": false, 66 | "detect_pulses": false, 67 | "detect_pulse_length": 60, 68 | "detect_pulse_polarity": false 69 | } 70 | -------------------------------------------------------------------------------- /custom_build/README.md: -------------------------------------------------------------------------------- 1 | # Customize Building Process 2 | 3 | This folder contains a compile script that performs 4 | customized build for pyaer. 5 | We replaced this version of build to a easier building process. 6 | This script is here for the legacy reasons. 7 | 8 | 9 | ## Some old logs 10 | 11 | 1. Install `libcaer` dependency 12 | 13 | ``` 14 | $ sudo apt-get install libusb-1.0-0-dev 15 | ``` 16 | 17 | __NOTE:__ For more information, see [`libcaer` repo](https://github.com/inilabs/libcaer). 18 | 19 | 2. Download this repo 20 | 21 | ``` 22 | $ git clone --recursive https://github.com/duguyue100/pyaer-beta.git 23 | ``` 24 | 3. Change `compile.conf.bak` to `compile.conf` and configure Python executable 25 | 26 | ``` 27 | cd pyaer-beta 28 | cp compile.conf.bak compile.conf 29 | ``` 30 | 31 | The configuration file looks like 32 | 33 | ```bash 34 | # Turn it to true if you are building the repository for the first time 35 | REBUILDING=false 36 | 37 | # defien python version (experimental) 38 | # only support python 2 at this moment 39 | PYTHON_VERSION=2 40 | 41 | # define python executable 42 | CONDA_LIB_PATH=$HOME/anaconda2/lib 43 | CONDA_PKG_CONFIG_PATH=$CONDA_LIB_PATH/pkgconfig 44 | 45 | # false if build libcaer locally 46 | # true if you have a system installed libcaer 47 | LIBCAER_INSTALLED=false 48 | ``` 49 | 50 | 4. Make this repository and install it! 51 | 52 | ``` 53 | $ ./compile make 54 | $ ./compile make.install 55 | ``` 56 | 57 | __NOTE:__ This repository is designed to be compiled and run within local 58 | directory, nothing to pollute your system because this is in active 59 | development. 60 | 61 | 5. (Optional) if you want to clean the installation, just type 62 | 63 | ``` 64 | $ ./compile clean 65 | ``` 66 | 67 | 6. (Optional) The available commands for the compilation script are: 68 | 69 | + `make`: make `libcaer` and SWIG binding 70 | + `make.swig`: make SWIG binding only 71 | + `make.lib` : make `libcaer` 72 | + `make.install`: Install compiled wrapper to the package 73 | + `clean`: clean SWIG compilation, `libcaer` and installation 74 | + `clean.swig`: clean SWIG 75 | + `clean.lib` : clean `libcaer` compilation 76 | + `clean.install` : clean installation 77 | + `help`: print help info 78 | -------------------------------------------------------------------------------- /scripts/aer_comm/aer_subscriber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """AER Subscriber 4 | 5 | Author: Yuhuang Hu 6 | Email : duguyue100@gmail.com 7 | """ 8 | 9 | from __future__ import print_function, absolute_import 10 | 11 | import json 12 | import argparse 13 | 14 | from pyaer.utils import expandpath, import_custom_module 15 | from pyaer.utils import parse_custom_args 16 | from pyaer.comm import AERSubscriber 17 | 18 | parser = argparse.ArgumentParser() 19 | parser.add_argument("--url", type=str, 20 | default="tcp://127.0.0.1", 21 | help="AER Subscriber URL") 22 | parser.add_argument("--port", type=int, 23 | default=5099, 24 | help="the port that connects this subscriber") 25 | parser.add_argument("--topic", type=str, 26 | default="", 27 | help="Topic to subscribe") 28 | parser.add_argument("--name", type=str, 29 | default="", 30 | help="Name of the subscriber") 31 | 32 | parser.add_argument("--use_default_sub", action="store_true") 33 | 34 | parser.add_argument("--custom_sub", type=expandpath, 35 | default="", 36 | help="path to the custom publisher class") 37 | parser.add_argument("--custom_class", type=str, 38 | default="", 39 | help="custom publisher class name") 40 | 41 | 42 | args, custom_args = parser.parse_known_args() 43 | 44 | custom_args_dict = parse_custom_args(custom_args) 45 | 46 | # print all options 47 | print("="*50) 48 | print(json.dumps( 49 | {**args.__dict__, **custom_args_dict}, 50 | indent=4, sort_keys=True)) 51 | print("="*50) 52 | 53 | # define subscriber 54 | if args.use_default_sub: 55 | # fall back to the default publisher 56 | subscriber = AERSubscriber( 57 | url=args.url, port=args.port, topic=args.topic, 58 | name=args.name) 59 | subscriber.logger.info("Use default subscriber") 60 | else: 61 | # use custom publisher 62 | CustomSubscriber = import_custom_module(args.custom_sub, args.custom_class) 63 | subscriber = CustomSubscriber( 64 | url=args.url, port=args.port, topic=args.topic, name=args.name, 65 | **custom_args_dict) 66 | subscriber.logger.info( 67 | "Use custom subscriber {}".format(args.custom_class)) 68 | 69 | # Start sending data 70 | subscriber.run() 71 | -------------------------------------------------------------------------------- /scripts/davis240_test.py: -------------------------------------------------------------------------------- 1 | """DAVIS240 test example. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import numpy as np 9 | import cv2 10 | 11 | from pyaer import libcaer 12 | from pyaer.davis import DAVIS 13 | 14 | device = DAVIS(noise_filter=True) 15 | 16 | print ("Device ID:", device.device_id) 17 | if device.device_is_master: 18 | print ("Device is master.") 19 | else: 20 | print ("Device is slave.") 21 | print ("Device Serial Number:", device.device_serial_number) 22 | print ("Device String:", device.device_string) 23 | print ("Device USB bus Number:", device.device_usb_bus_number) 24 | print ("Device USB device address:", device.device_usb_device_address) 25 | print ("Device size X:", device.dvs_size_X) 26 | print ("Device size Y:", device.dvs_size_Y) 27 | print ("Logic Version:", device.logic_version) 28 | print ("Background Activity Filter:", 29 | device.dvs_has_background_activity_filter) 30 | 31 | 32 | device.start_data_stream() 33 | # set new bias after data streaming 34 | device.set_bias_from_json("./scripts/configs/davis240c_config.json") 35 | 36 | clip_value = 3 37 | histrange = [(0, v) for v in (180, 240)] 38 | 39 | 40 | def get_event(device): 41 | data = device.get_event("events_hist") 42 | 43 | return data 44 | 45 | 46 | # num_packet_before_disable = 1000 47 | 48 | while True: 49 | try: 50 | data = get_event(device) 51 | if data is not None: 52 | (pol_events, num_pol_event, 53 | special_events, num_special_event, 54 | frames_ts, frames, imu_events, 55 | num_imu_event) = data 56 | if frames.shape[0] != 0: 57 | cv2.imshow("frame", frames[0]) 58 | 59 | print ("Number of events:", num_pol_event, "Number of Frames:", 60 | frames.shape, "Exposure:", 61 | device.get_config( 62 | libcaer.DAVIS_CONFIG_APS, 63 | libcaer.DAVIS_CONFIG_APS_EXPOSURE)) 64 | 65 | if num_pol_event != 0: 66 | img = pol_events[..., 1]-pol_events[..., 0] 67 | img = np.clip(img, -clip_value, clip_value) 68 | img = img+clip_value 69 | 70 | cv2.imshow("image", img/float(clip_value*2)) 71 | 72 | if cv2.waitKey(1) & 0xFF == ord('q'): 73 | break 74 | 75 | else: 76 | pass 77 | 78 | except KeyboardInterrupt: 79 | device.shutdown() 80 | break 81 | -------------------------------------------------------------------------------- /scripts/configs/davis346_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ADC_RefHigh_volt": 32, 3 | "ADC_RefHigh_curr": 7, 4 | "ADC_RefLow_volt": 1, 5 | "ADC_RefLow_curr": 7, 6 | "LocalBufBn_coarse": 5, 7 | "LocalBufBn_fine": 164, 8 | "PadFollBn_coarse": 7, 9 | "PadFollBn_fine": 215, 10 | "DiffBn_coarse": 4, 11 | "DiffBn_fine": 39, 12 | "ONBn_coarse": 6, 13 | "ONBn_fine": 255, 14 | "OFFBn_coarse": 4, 15 | "OFFBn_fine": 1, 16 | "PixInvBn_coarse": 5, 17 | "PixInvBn_fine": 129, 18 | "PrBp_coarse": 2, 19 | "PrBp_fine": 58, 20 | "PrSFBp_coarse": 1, 21 | "PrSFBp_fine": 16, 22 | "RefrBp_coarse": 4, 23 | "RefrBp_fine": 25, 24 | "ReadoutBufBp_coarse": 6, 25 | "ReadoutBufBp_fine": 20, 26 | "APSROSFBn_coarse": 6, 27 | "APSROSFBn_fine": 219, 28 | "ADCCompBp_coarse": 5, 29 | "ADCCompBp_fine": 20, 30 | "COLSELLowBn_coarse": 0, 31 | "COLSELLowBn_fine": 1, 32 | "DACBufBp_coarse": 6, 33 | "DACBufBp_fine": 60, 34 | "LcolTimeoutBn_coarse": 5, 35 | "LcolTimeoutBn_fine": 49, 36 | "AEPdBn_coarse": 6, 37 | "AEPdBn_fine": 91, 38 | "AEPuXBp_coarse": 4, 39 | "AEPuXBp_fine": 80, 40 | "AEPuYBp_coarse": 7, 41 | "AEPuYBp_fine": 152, 42 | "IFRefrBn_coarse": 5, 43 | "IFRefrBn_fine": 255, 44 | "IFThrBn_coarse": 5, 45 | "IFThrBn_fine": 255, 46 | "BiasBuffer_coarse": 5, 47 | "BiasBuffer_fine": 254, 48 | "aps_enabled": true, 49 | "dvs_enabled": true, 50 | "exposure": 30000, 51 | "autoexposure": false, 52 | "frame_interval": 0, 53 | "imu_enabled": true, 54 | "imu_acc_scale": 3, 55 | "imu_gyro_scale": 3, 56 | "imu_low_pass_filter": 0, 57 | "background_activity_filter_enabled": true, 58 | "background_activity_filter_time": 80, 59 | "refractory_period_enabled": true, 60 | "refractory_period_time": 2, 61 | "noise_filter_configs": { 62 | "sw_background_activity_two_levels": true, 63 | "sw_background_activity_check_polarity": true, 64 | "sw_background_activity_support_min": 2, 65 | "sw_background_activity_support_max": 8, 66 | "sw_background_activity_time": 2000, 67 | "sw_background_activity_enable": true, 68 | "sw_refractory_period_time": 200, 69 | "sw_refractory_period_enable": true, 70 | "sw_hotpixel_enable": true, 71 | "sw_hotpixel_learn": true 72 | }, 73 | "ext_input_enabled": false, 74 | "detect_falling_edges": false, 75 | "detect_rising_edges": false, 76 | "detect_pulses": false, 77 | "detect_pulse_length": 60, 78 | "detect_pulse_polarity": false 79 | } 80 | -------------------------------------------------------------------------------- /scripts/aer_comm/aer_pubsuber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """AER PubSuber. 4 | 5 | Because PubSuber mainly serves as a processing unit, 6 | so we support only custom implementation. 7 | 8 | Author: Yuhuang Hu 9 | Email : duguyue100@gmail.com 10 | """ 11 | 12 | from __future__ import print_function, absolute_import 13 | 14 | import argparse 15 | import json 16 | 17 | from pyaer.utils import expandpath, import_custom_module 18 | from pyaer.utils import parse_custom_args 19 | 20 | parser = argparse.ArgumentParser() 21 | parser.add_argument("--url", type=str, 22 | default="tcp://127.0.0.1", 23 | help="AER Publisher URL") 24 | 25 | parser.add_argument("--pub_port", type=int, 26 | default=5100, 27 | help="the port that connects this publisher") 28 | parser.add_argument("--pub_topic", type=str, 29 | default="device", 30 | help="publish topic name for the publisher") 31 | parser.add_argument("--pub_name", type=str, 32 | default="", 33 | help="Name of the publisher") 34 | 35 | parser.add_argument("--sub_port", type=int, 36 | default=5099, 37 | help="the port that connects this subscriber") 38 | parser.add_argument("--sub_topic", type=str, 39 | default="", 40 | help="subscriber topic name for the subscriber") 41 | parser.add_argument("--sub_name", type=str, 42 | default="", 43 | help="Name of the subscriber") 44 | 45 | parser.add_argument("--custom_pubsuber", type=expandpath, 46 | default="", 47 | help="path to the custom PubSuber class") 48 | parser.add_argument("--custom_class", type=str, 49 | default="", 50 | help="custom publisher class name") 51 | 52 | args, custom_args = parser.parse_known_args() 53 | 54 | custom_args_dict = parse_custom_args(custom_args) 55 | 56 | # print all options 57 | print("="*50) 58 | print(json.dumps( 59 | {**args.__dict__, **custom_args_dict}, 60 | indent=4, sort_keys=True)) 61 | print("="*50) 62 | 63 | 64 | # define publisher 65 | # use custom publisher 66 | CustomPubSuber = import_custom_module(args.custom_pubsuber, args.custom_class) 67 | pubsuber = CustomPubSuber( 68 | url=args.url, 69 | pub_port=args.pub_port, pub_topic=args.pub_topic, pub_name=args.pub_name, 70 | sub_port=args.sub_port, sub_topic=args.sub_topic, sub_name=args.sub_name, 71 | **custom_args_dict) 72 | pubsuber.logger.info("Use custom PubSuber {}".format(args.custom_class)) 73 | 74 | # Start sending data 75 | pubsuber.run() 76 | -------------------------------------------------------------------------------- /custom_build/install-libcaer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This file has become a legacy script for CI building. 4 | # Now we are using the official libcaer build for CI/CD 5 | 6 | CI_BUILD=false 7 | # TODO: To support 8 | LIBSERIAL_PORT_OPTION=false 9 | 10 | # parse option 11 | if [ ! -z "$1" -a "$1" = "ci" ]; then 12 | echo "[MESSAGE] This is a CI build." 13 | CI_BUILD=true 14 | elif [ ! -z "$1" -a "$1" = "libserial" ]; then 15 | echo "[MESSAGE] Enable libserial." 16 | LIBSERIAL_PORT_OPTION=true 17 | elif [ ! -z "$1" -a "$1" = "ci.libserial" ]; then 18 | echo "[MESSAGE] This is a CI build and enable libserial." 19 | CI_BUILD=true 20 | LIBSERIAL_PORT_OPTION=true 21 | fi 22 | 23 | case "$(uname -s)" in 24 | Darwin) 25 | echo 'Installing libcaer to a macOS' 26 | if [ $CI_BUILD = false ]; then 27 | brew install cmake pkg-config libusb 28 | git clone https://gitlab.com/inivation/dv/libcaer /tmp/libcaer 29 | cd /tmp/libcaer 30 | else 31 | git clone https://gitlab.com/inivation/dv/libcaer 32 | cd libcaer 33 | fi 34 | cmake -DCMAKE_INSTALL_PREFIX=/usr/local . 35 | make -j2 36 | sudo make install 37 | ;; 38 | 39 | Linux) 40 | echo 'Installing libcaer to a Debian platform' 41 | if [ $CI_BUILD = false ]; then 42 | sudo apt-get update 43 | sudo apt-get install build-essential pkg-config libusb-1.0-0-dev 44 | git clone https://gitlab.com/inivation/dv/libcaer /tmp/libcaer 45 | cd /tmp/libcaer 46 | else 47 | git clone https://gitlab.com/inivation/dv/libcaer 48 | cd libcaer 49 | fi 50 | # wget https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.sh -O $HOME/cmake.sh 51 | # sudo bash $HOME/cmake.sh --prefix=/usr/local --skip-license 52 | cmake -DCMAKE_INSTALL_PREFIX=/usr . 53 | make -j2 54 | sudo make install 55 | ;; 56 | 57 | CYGWIN*|MINGW*|MSYS*) 58 | echo 'Installing libcaer to MS Windows' 59 | if [ $CI_BUILD = false ]; then 60 | pacman --noconfirm -S mingw-w64-x86_64-gcc 61 | pacman --noconfirm -S make mingw-w64-x86_64-cmake 62 | pacman --noconfirm -S mingw-w64-x86_64-pkg-config 63 | pacman --noconfirm -S mingw-w64-x86_64-libusb 64 | pacman --noconfirm -S automake bison 65 | git clone https://gitlab.com/inivation/dv/libcaer /home/libcaer 66 | fi 67 | cd /home/libcaer 68 | cmake -G 'MSYS Makefiles' -DCMAKE_INSTALL_PREFIX=/mingw64 . 69 | make -j2 70 | make install 71 | ;; 72 | 73 | *) 74 | echo 'other OS' 75 | ;; 76 | esac 77 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: PyAER Build and Test 2 | 3 | on: 4 | push: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, macos-latest] 15 | python-version: ["3.8", "3.9", "3.10"] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | - name: Install Ubuntu dependencies 24 | if: ${{ matrix.os == 'ubuntu-latest' }} 25 | run: | 26 | gcc --version; 27 | sudo apt-get install build-essential -y; 28 | sudo apt-get install libusb-1.0-0-dev -y; 29 | sudo apt-get install cmake -y; 30 | sudo add-apt-repository ppa:inivation-ppa/inivation -y; 31 | sudo apt-get update; 32 | sudo apt-get install libcaer-dev -y; 33 | - name: Install macos dependencies 34 | if: ${{ matrix.os == 'macos-latest' }} 35 | run: | 36 | gcc --version; 37 | brew install automake; 38 | brew install bison; 39 | brew tap inivation/inivation; 40 | brew install libcaer --with-libserialport --with-opencv; 41 | - name: Install PyAER specific dependencies 42 | run: | 43 | git clone https://github.com/duguyue100/swig 44 | cd swig 45 | ./autogen.sh 46 | ./configure --without-alllang --with-python=$(command -v python) --without-pcre 47 | make -j4 48 | sudo make install 49 | cd .. 50 | pip install pip -U 51 | pip install numpy 52 | pip install wheel 53 | - name: Build PyAER 54 | run: | 55 | make build-wheel 56 | make build-wheel 57 | make install 58 | - name: Find and manage file 59 | run: | 60 | cd $GITHUB_WORKSPACE/dist 61 | ls -a 62 | for file in *.whl ; do mv $file ${file//linux/manylinux1} ; done; 63 | rm *.egg; 64 | ls -a 65 | cd .. 66 | - name: Publish package to Github releases 67 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 68 | uses: svenstaro/upload-release-action@v2 69 | with: 70 | repo_token: ${{ secrets.GITHUB_TOKEN }} 71 | file: dist/*.* 72 | tag: ${{ github.ref }} 73 | file_glob: true 74 | - name: Publish package to PyPI 75 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 76 | run: | 77 | pip install twine; 78 | twine upload -u __token__ -p ${{ secrets.PYPI_API_TOKEN }} dist/*.whl; 79 | -------------------------------------------------------------------------------- /scripts/event_container_test.py: -------------------------------------------------------------------------------- 1 | """Testing Event Container. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from pyaer import libcaer 12 | from pyaer.davis import DAVIS 13 | 14 | from timer import Timer 15 | 16 | device = DAVIS(noise_filter=True) 17 | 18 | print("Device ID:", device.device_id) 19 | if device.device_is_master: 20 | print("Device is master.") 21 | else: 22 | print("Device is slave.") 23 | print("Device Serial Number:", device.device_serial_number) 24 | print("Device String:", device.device_string) 25 | print("Device USB bus Number:", device.device_usb_bus_number) 26 | print("Device USB device address:", device.device_usb_device_address) 27 | print("Device size X:", device.dvs_size_X) 28 | print("Device size Y:", device.dvs_size_Y) 29 | print("Logic Version:", device.logic_version) 30 | print("Background Activity Filter:", 31 | device.dvs_has_background_activity_filter) 32 | print("Color Filter", device.aps_color_filter, type(device.aps_color_filter)) 33 | print(device.aps_color_filter == 1) 34 | 35 | device.start_data_stream() 36 | # setting bias after data stream started 37 | device.set_bias_from_json("./scripts/configs/davis346_config.json") 38 | 39 | clip_value = 3 40 | histrange = [(0, v) for v in (260, 346)] 41 | 42 | 43 | def get_event(device): 44 | data = device.get_event() 45 | 46 | return data 47 | 48 | 49 | num_packet_before_disable = 1000 50 | 51 | while True: 52 | try: 53 | with Timer("container_test", show_hist=True): 54 | event_container = device.get_event_container() 55 | 56 | if event_container is not None: 57 | print("Duration (s): {}, Num Events: {}, Event Rate (ev/s): {}" 58 | .format( 59 | event_container.pol_event_duration, 60 | event_container.num_pol_events, 61 | event_container.pol_event_rate)) 62 | print("Signal Rate (ev/s): {}, Noise Rate (ev/s): {}" 63 | .format( 64 | event_container.valid_pol_events_rate, 65 | event_container.invalid_pol_events_rate)) 66 | 67 | # print("Number of events:", num_pol_event, "Number of Frames:", 68 | # frames.shape, "Exposure:", 69 | # device.get_config( 70 | # libcaer.DAVIS_CONFIG_APS, 71 | # libcaer.DAVIS_CONFIG_APS_EXPOSURE), 72 | # "Autoexposure:", device.get_config( 73 | # libcaer.DAVIS_CONFIG_APS, 74 | # libcaer.DAVIS_CONFIG_APS_AUTOEXPOSURE)) 75 | else: 76 | pass 77 | 78 | except KeyboardInterrupt: 79 | device.shutdown() 80 | break 81 | -------------------------------------------------------------------------------- /pyaer/container.py: -------------------------------------------------------------------------------- 1 | """Event Container. 2 | 3 | Motivation: After the events are read from the event packet, 4 | it's difficult to use and less informative to return them 5 | as single variables, therefore, I decide to introduce a 6 | container that can manage all the returned Python variables. 7 | 8 | Author: Yuhuang Hu 9 | Email : duguyue100@gmail.com 10 | """ 11 | 12 | from typing import Optional 13 | 14 | import numpy as np 15 | 16 | 17 | class EventContainer(object): 18 | """Event container that packs everything. 19 | 20 | Args: 21 | pol_events: polarity events array. 22 | special_events: special events. 23 | frames: APS frame events. 24 | frames_ts: APS frame time stamp. 25 | imu_events: IMU events. 26 | """ 27 | 28 | def __init__( 29 | self, 30 | pol_events: np.ndarray, 31 | special_events: Optional[np.ndarray] = None, 32 | frames: Optional[np.ndarray] = None, 33 | frames_ts: Optional[np.ndarray] = None, 34 | imu_events: Optional[np.ndarray] = None, 35 | ) -> None: 36 | self.pol_events = pol_events 37 | self.num_pol_events = 0 if pol_events is None else pol_events.shape[0] 38 | self.compute_event_stats() 39 | 40 | self.special_events = special_events 41 | self.num_special_events = ( 42 | None if special_events is None else special_events.shape[0] 43 | ) 44 | 45 | self.frames = frames 46 | self.frames_ts = frames_ts 47 | 48 | self.imu_events = imu_events 49 | self.num_imu_events = None if imu_events is None else imu_events.shape[0] 50 | 51 | def compute_event_stats(self) -> None: 52 | """Calculate event stats.""" 53 | self.pol_event_duration = None 54 | self.pol_event_rate = None 55 | 56 | self.num_valid_pol_events = None 57 | self.num_invalid_pol_events = None 58 | self.valid_pol_events_rate = None 59 | self.invalid_pol_events_rate = None 60 | 61 | if self.num_pol_events > 1: 62 | # in seconds 63 | self.pol_event_duration = ( 64 | self.pol_events[-1, 0] - self.pol_events[0, 0] 65 | ) / 1e6 66 | 67 | self.pol_event_rate = self.num_pol_events / self.pol_event_duration 68 | 69 | # additional stats if has background filter 70 | if self.pol_events.shape[1] == 5: 71 | self.num_valid_pol_events = self.pol_events[:, -1].sum() 72 | self.num_invalid_pol_events = ( 73 | self.num_pol_events - self.num_valid_pol_events 74 | ) 75 | 76 | self.valid_pol_events_rate = ( 77 | self.num_valid_pol_events / self.pol_event_duration 78 | ) 79 | self.invalid_pol_events_rate = ( 80 | self.num_invalid_pol_events / self.pol_event_duration 81 | ) 82 | -------------------------------------------------------------------------------- /scripts/dvs_noise_filter_test.py: -------------------------------------------------------------------------------- 1 | """Testing DVS Noise filter implementation. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function, absolute_import 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from pyaer.dvs128 import DVS128 12 | 13 | device = DVS128(noise_filter=True) 14 | 15 | print ("Device ID:", device.device_id) 16 | if device.device_is_master: 17 | print ("Device is master.") 18 | else: 19 | print ("Device is slave.") 20 | print ("Device Serial Number:", device.device_serial_number) 21 | print ("Device String:", device.device_string) 22 | print ("Device USB bus Number:", device.device_usb_bus_number) 23 | print ("Device USB device address:", device.device_usb_device_address) 24 | print ("Device size X:", device.dvs_size_X) 25 | print ("Device size Y:", device.dvs_size_Y) 26 | print ("Logic Version:", device.logic_version) 27 | 28 | device.start_data_stream() 29 | # load new config 30 | device.set_bias_from_json("./scripts/configs/dvs128_config.json") 31 | print (device.get_bias()) 32 | 33 | clip_value = 1 34 | histrange = [(0, v) for v in (128, 128)] 35 | 36 | 37 | num_packet_before_disable = 1000 38 | 39 | 40 | def get_event(device): 41 | global num_packet_before_disable 42 | (pol_events, num_pol_event, 43 | special_events, num_special_event) = \ 44 | device.get_event() 45 | if num_pol_event != 0: 46 | if num_packet_before_disable > 0: 47 | pol_events = pol_events[pol_events[:, 4] == 1] 48 | num_packet_before_disable -= 1 49 | else: 50 | device.disable_noise_filter() 51 | print ("Noise filter disabled") 52 | 53 | pol_on = (pol_events[:, 3] == 1) 54 | pol_off = np.logical_not(pol_on) 55 | img_on, _, _ = np.histogram2d( 56 | pol_events[pol_on, 2], pol_events[pol_on, 1], 57 | bins=(128, 128), range=histrange) 58 | img_off, _, _ = np.histogram2d( 59 | pol_events[pol_off, 2], pol_events[pol_off, 1], 60 | bins=(128, 128), range=histrange) 61 | if clip_value is not None: 62 | integrated_img = np.clip( 63 | (img_on-img_off), -clip_value, clip_value) 64 | else: 65 | integrated_img = (img_on-img_off) 66 | img = integrated_img+clip_value 67 | 68 | cv2.imshow("image", img/float(clip_value*2)) 69 | print ("Number of events:", num_pol_event, "Number of special events:", 70 | num_special_event) 71 | del pol_events, num_pol_event, special_events, num_special_event 72 | 73 | if cv2.waitKey(1) & 0xFF == ord('q'): 74 | return 75 | 76 | 77 | while True: 78 | try: 79 | get_event(device) 80 | 81 | except KeyboardInterrupt: 82 | hot_pixels = device.noise_filter.get_hot_pixels() 83 | print (hot_pixels) 84 | print (device.noise_filter.get_bias()) 85 | device.shutdown() 86 | break 87 | -------------------------------------------------------------------------------- /docs/pages/index.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [![GitHub release](https://img.shields.io/github/release/duguyue100/pyaer.svg?style=flat-square)](https://github.com/duguyue100/pyaer) 4 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyaer.svg?style=flat-square)](https://pypi.org/project/pyaer/) 5 | [![Build Status](https://api.travis-ci.org/duguyue100/pyaer.svg?branch=master)](https://travis-ci.org/duguyue100/pyaer) 6 | [![Build status](https://ci.appveyor.com/api/projects/status/uf13jm7690abu11i/branch/master?svg=true)](https://ci.appveyor.com/project/duguyue100/pyaer/branch/master) 7 | [![license](https://img.shields.io/github/license/duguyue100/pyaer.svg)](https://github.com/duguyue100/pyaer/blob/master/LICENSE) 8 | 9 | ![Ubuntu](https://img.shields.io/badge/OS-Ubuntu-orange.svg) 10 | ![macOS](https://img.shields.io/badge/OS-macOS-orange.svg) 11 | ![Raspbian](https://img.shields.io/badge/OS-Raspbian%20Stretch-orange.svg) 12 | ![Windows](https://img.shields.io/badge/OS-Windows-orange.svg) 13 | 14 | PyAER with Swig Bindings 15 | 16 | Special thanks to [iniLabs](http://inilabs.com/) for making this possible. 17 | 18 | The project is in its Beta development stage, please submit an [issue](https://github.com/duguyue100/pyaer/issues) if you need our help. 19 | 20 | ## Design Principle 21 | 22 | + Minimum installation effort 23 | + Keep Python 2 and 3 in mind 24 | + Clean, simple, easy to manage 25 | + Well documented, human-readable code 26 | 27 | ## Installation 28 | 29 | #### 1. Install bleeding-edge `libcaer` dependency (RECOMMEND) 30 | 31 | ```bash 32 | $ bash <(curl -s https://raw.githubusercontent.com/duguyue100/pyaer/master/install-libcaer.sh) 33 | ``` 34 | 35 | __NOTE:__ To build `libcaer` on Windows, please follow [this description](https://github.com/inilabs/libcaer/blob/master/README.Windows). 36 | 37 | __NOTE:__ For more information, see [`libcaer` repo](https://github.com/inilabs/libcaer). 38 | 39 | __NOTE:__ From 0.1.0a18, we support eDVS, you will need to install `libserialport` so that the package can work properly, follow the building instructions from [here](https://sigrok.org/wiki/Libserialport). Currently, this support is not built into the release since we are not clear how useful is this feature. If you are interested, you can build the project from scratch. 40 | 41 | #### 2. Directly install from pypi (RECOMMEND) 42 | 43 | ```bash 44 | $ pip install pyaer -U 45 | ``` 46 | 47 | #### 3. Install from source 48 | 49 | ```bash 50 | $ git clone https://github.com/duguyue100/pyaer.git 51 | $ make install 52 | ``` 53 | 54 | ## Got a Linux? 55 | 56 | `libcaer` relies on `libusb` based driver, you won't be able 57 | to access the camera unless fixing the `udev` rules. Refer to details 58 | at [here](https://inivation.com/support/hardware/davis240/#linux). 59 | 60 | ```bash 61 | $ bash <(curl -s https://raw.githubusercontent.com/duguyue100/pyaer/master/install-udev.sh) 62 | ``` 63 | 64 | 65 | -------------------------------------------------------------------------------- /scripts/aer_comm/custom_comm.py: -------------------------------------------------------------------------------- 1 | """Custom Publisher and Subscriber. 2 | 3 | Author: Yuhuang Hu 4 | Email : yuhuang.hu@ini.uzh.ch 5 | """ 6 | 7 | from __future__ import print_function, absolute_import 8 | 9 | import time 10 | import cv2 11 | 12 | from pyaer.comm import AERPublisher, AERSubscriber 13 | 14 | 15 | class CustomPublisher(AERPublisher): 16 | 17 | def __init__(self, device, url, port, master_topic, name, **kwargs): 18 | super().__init__( 19 | device=device, url=url, port=port, master_topic=master_topic, 20 | **kwargs) 21 | 22 | def run_once(self, verbose=False): 23 | data = self.device.get_event() 24 | 25 | if data is not None: 26 | # data = self.pack_frame_events 27 | # 28 | # self.socket.send_multipart(data) 29 | 30 | t = time.localtime() 31 | 32 | curr_time = time.strftime("%H:%M:%S", t) 33 | 34 | print("Publishing {}".format(curr_time)) 35 | 36 | 37 | class CustomSubscriber(AERSubscriber): 38 | 39 | def __init__(self, url, port, topic, name, **kwargs): 40 | super().__init__(url, port, topic, name, **kwargs) 41 | 42 | for arg in kwargs.values(): 43 | print(arg) 44 | 45 | def run_once(self, verbose=False): 46 | data = self.socket.recv_multipart() 47 | 48 | topic_name = self.unpack_data_name( 49 | data[:2], topic_name_only=True) 50 | 51 | if "frame" in topic_name: 52 | data_id, frame_events, frame_ts = \ 53 | self.unpack_frame_events(data) 54 | 55 | if frame_events is not None: 56 | try: 57 | frame = cv2.cvtColor( 58 | frame_events[0], 59 | cv2.COLOR_BGR2RGB) 60 | frame = cv2.resize(frame, (1384, 1040)) 61 | cv2.imshow("frame", frame) 62 | if cv2.waitKey(1) & 0xFF == ord('q'): 63 | return 64 | except Exception: 65 | pass 66 | 67 | 68 | class DVViewerSubscriber(AERSubscriber): 69 | 70 | def __init__(self, url, port, topic, name, **kwargs): 71 | super().__init__(url, port, topic, name, **kwargs) 72 | 73 | for arg in kwargs.values(): 74 | print(arg) 75 | 76 | def run_once(self, verbose=False): 77 | data = self.socket.recv_multipart() 78 | 79 | topic_name = self.unpack_data_name( 80 | data[:2], topic_name_only=True) 81 | 82 | if "frame" in topic_name: 83 | data_id, frame_events, frame_ts = \ 84 | self.unpack_frame_events(data) 85 | 86 | if frame_events is not None: 87 | try: 88 | cv2.imshow( 89 | "frame", 90 | cv2.cvtColor( 91 | frame_events[0], 92 | cv2.COLOR_BGR2RGB)) 93 | if cv2.waitKey(1) & 0xFF == ord('q'): 94 | return 95 | except Exception: 96 | pass 97 | -------------------------------------------------------------------------------- /scripts/dvs128_thread_test.py: -------------------------------------------------------------------------------- 1 | """DVS128 Threaded Test. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | from queue import Queue 9 | import threading 10 | import numpy as np 11 | import cv2 12 | 13 | from pyaer.dvs128 import DVS128 14 | 15 | device = DVS128() 16 | 17 | print ("Device ID:", device.device_id) 18 | if device.device_is_master: 19 | print ("Device is master.") 20 | else: 21 | print ("Device is slave.") 22 | print ("Device Serial Number:", device.device_serial_number) 23 | print ("Device String:", device.device_string) 24 | print ("Device USB bus Number:", device.device_usb_bus_number) 25 | print ("Device USB device address:", device.device_usb_device_address) 26 | print ("Device size X:", device.dvs_size_X) 27 | print ("Device size Y:", device.dvs_size_Y) 28 | print ("Logic Version:", device.logic_version) 29 | 30 | # load new config 31 | device.set_bias_from_json("./scripts/configs/dvs128_config.json") 32 | print (device.get_bias()) 33 | 34 | device.start_data_stream() 35 | 36 | # global variable 37 | clip_value = 3 38 | histrange = [(0, v) for v in (128, 128)] 39 | 40 | 41 | def drawing_func(in_q): 42 | while threading.currentThread().isAlive(): 43 | try: 44 | (pol_events, num_pol_event, 45 | special_events, num_special_event) = in_q.get() 46 | if num_pol_event != 0: 47 | pol_on = (pol_events[:, 3] == 1) 48 | pol_off = np.logical_not(pol_on) 49 | img_on, _, _ = np.histogram2d( 50 | pol_events[pol_on, 2], pol_events[pol_on, 1], 51 | bins=(128, 128), range=histrange) 52 | img_off, _, _ = np.histogram2d( 53 | pol_events[pol_off, 1], pol_events[pol_off, 0], 54 | bins=(128, 128), range=histrange) 55 | if clip_value is not None: 56 | integrated_img = np.clip( 57 | (img_on-img_off), -clip_value, clip_value) 58 | else: 59 | integrated_img = (img_on-img_off) 60 | img = integrated_img+clip_value 61 | cv2.imshow("image", img/float(clip_value*2)) 62 | cv2.waitKey(1) 63 | except KeyboardInterrupt: 64 | device.shutdown() 65 | break 66 | 67 | 68 | def fetching_func(out_q): 69 | while threading.currentThread().isAlive(): 70 | try: 71 | event_packet = device.get_event() 72 | print ("Number of events:", event_packet[1], 73 | "Number of special events:", 74 | event_packet[3]) 75 | out_q.put(event_packet) 76 | except KeyboardInterrupt: 77 | device.shutdown() 78 | break 79 | 80 | 81 | if __name__ == "__main__": 82 | # define thread 83 | q = Queue(maxsize=1) 84 | drawer = threading.Thread( 85 | name="drawer", target=drawing_func, args=(q, )) 86 | fetcher = threading.Thread( 87 | name="fetcher", target=fetching_func, args=(q, )) 88 | 89 | fetcher.start() 90 | print ("fetcher started") 91 | drawer.start() 92 | print ("drawer started") 93 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Setup script for the pyaer package. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import absolute_import 7 | from __future__ import print_function 8 | 9 | import os 10 | from sys import platform 11 | from sysconfig import get_paths 12 | 13 | import numpy 14 | from setuptools import find_packages 15 | from setuptools import setup 16 | from setuptools.extension import Extension 17 | 18 | classifiers = """ 19 | Development Status :: 4 - Beta 20 | Intended Audience :: Science/Research 21 | Natural Language :: English 22 | Operating System :: OS Independent 23 | Programming Language :: Python :: 3.8 24 | Programming Language :: Python :: 3.9 25 | Programming Language :: Python :: 3.10 26 | Topic :: Utilities 27 | Topic :: Scientific/Engineering 28 | Topic :: Scientific/Engineering :: Artificial Intelligence 29 | Topic :: Software Development :: Libraries :: Python Modules 30 | License :: OSI Approved :: MIT License 31 | """ 32 | 33 | try: 34 | from pyaer import __about__ 35 | 36 | about = __about__.__dict__ 37 | except ImportError: 38 | about = dict() 39 | exec(open("pyaer/__about__.py").read(), about) 40 | 41 | python_paths = get_paths() 42 | 43 | try: 44 | numpy_include = numpy.get_include() 45 | except AttributeError: 46 | numpy_include = numpy.get_numpy_include() # type: ignore 47 | 48 | if platform in ["linux", "linux2"]: 49 | libcaer_include = "/usr/include" 50 | libcaer_lib = "/usr/lib/x86_64-linux-gnu" 51 | 52 | # for Raspberry Pi support 53 | if os.uname()[1] == "raspberrypi": 54 | libcaer_lib = "/usr/lib/arm-linux-gnueabihf" 55 | elif platform == "darwin": 56 | libcaer_include = "/opt/homebrew/include" 57 | libcaer_lib = "/opt/homebrew/lib" 58 | elif "win" in platform: 59 | libcaer_include = "C:/msys64/mingw64/include" 60 | libcaer_lib = "C:/msys64/mingw64/lib" 61 | 62 | include_dirs = [libcaer_include, python_paths["include"], numpy_include] 63 | library_dirs = [libcaer_lib, python_paths["stdlib"]] 64 | swig_opts = ["-I" + libcaer_include] 65 | if platform == "darwin": 66 | include_dirs += ["/usr/local/include"] 67 | library_dirs += ["/usr/local/lib"] 68 | swig_opts += ["-I/usr/local/include"] 69 | 70 | libcaer_wrap = Extension( 71 | name="pyaer._libcaer_wrap", 72 | sources=["./pyaer/pyflags.i"], 73 | include_dirs=include_dirs, 74 | library_dirs=library_dirs, 75 | swig_opts=swig_opts, 76 | # extra_compile_args=["-std=c11"], 77 | extra_link_args=["-lcaer"], 78 | ) 79 | 80 | setup( 81 | name="pyaer", 82 | version=about["__version__"], 83 | author=about["__author__"], 84 | author_email=about["__author_email__"], 85 | url=about["__url__"], 86 | install_requires=["numpy"], 87 | packages=find_packages(), 88 | ext_modules=[libcaer_wrap], 89 | scripts=[ 90 | "scripts/aer_comm/aer_hub", 91 | "scripts/aer_comm/aer_lstopic", 92 | "scripts/aer_comm/aer_publisher", 93 | "scripts/aer_comm/aer_subscriber", 94 | "scripts/aer_comm/aer_pubsuber", 95 | "scripts/aer_comm/aer_launch", 96 | "scripts/aer_comm/aer_saver", 97 | ], 98 | classifiers=list(filter(None, classifiers.split("\n"))), 99 | description="PyAER: Low-level Python APIs for Accessing Neuromorphic Devices.", 100 | ) 101 | -------------------------------------------------------------------------------- /pyaer/pyfragments.swg: -------------------------------------------------------------------------------- 1 | /*-*- C -*-*/ 2 | 3 | /**********************************************************************/ 4 | 5 | /* For numpy versions prior to 1.0, the names of certain data types 6 | * are different than in later versions. This fragment provides macro 7 | * substitutions that allow us to support old and new versions of 8 | * numpy. 9 | */ 10 | 11 | /**********************************************************************/ 12 | 13 | /* Override the SWIG_AsVal_frag(long) fragment so that it also checks 14 | * for numpy scalar array types. The code through the %#endif is 15 | * essentially cut-and-paste from pyprimtype.swg 16 | */ 17 | 18 | %fragment(SWIG_AsVal_frag(long), "header", 19 | fragment="SWIG_CanCastAsInteger", 20 | fragment="NumPy_Backward_Compatibility") 21 | { 22 | SWIGINTERN int 23 | SWIG_AsVal_dec(long)(PyObject * obj, long * val) 24 | { 25 | if (PyLong_Check(obj)) { 26 | long v = PyLong_AsLong(obj); 27 | if (v != -1 || !PyErr_Occurred()) { 28 | if (val) *val = v; 29 | return SWIG_OK; 30 | } else { 31 | PyErr_Clear(); 32 | } 33 | } 34 | %#ifdef SWIG_PYTHON_CAST_MODE 35 | { 36 | int dispatch = 0; 37 | long v = PyLong_AsLong(obj); 38 | if (v != -1 || !PyErr_Occurred()) { 39 | if (val) *val = v; 40 | return SWIG_AddCast(SWIG_OK); 41 | } else { 42 | PyErr_Clear(); 43 | } 44 | if (!dispatch) { 45 | double d; 46 | int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d)); 47 | if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { 48 | if (val) *val = (long)(d); 49 | return res; 50 | } 51 | } 52 | } 53 | %#endif 54 | if (!PyArray_IsScalar(obj,Integer)) return SWIG_TypeError; 55 | PyArray_Descr * longDescr = PyArray_DescrFromType(NPY_LONG); 56 | PyArray_CastScalarToCtype(obj, (void*)val, longDescr); 57 | Py_DECREF(longDescr); 58 | return SWIG_OK; 59 | } 60 | } 61 | 62 | 63 | /* Override the SWIG_AsVal_frag(unsigned long) fragment so that it 64 | * also checks for numpy scalar array types. The code through the 65 | * %#endif is essentially cut-and-paste from pyprimtype.swg 66 | */ 67 | 68 | %fragment(SWIG_AsVal_frag(unsigned long),"header", 69 | fragment="SWIG_CanCastAsInteger", 70 | fragment="NumPy_Backward_Compatibility") 71 | { 72 | SWIGINTERN int 73 | SWIG_AsVal_dec(unsigned long)(PyObject *obj, unsigned long *val) 74 | { 75 | if (PyLong_Check(obj)) { 76 | unsigned long v = PyLong_AsUnsignedLong(obj); 77 | if (!PyErr_Occurred()) { 78 | if (val) *val = v; 79 | return SWIG_OK; 80 | } else { 81 | PyErr_Clear(); 82 | } 83 | } 84 | %#ifdef SWIG_PYTHON_CAST_MODE 85 | { 86 | int dispatch = 0; 87 | unsigned long v = PyLong_AsUnsignedLong(obj); 88 | if (!PyErr_Occurred()) { 89 | if (val) *val = v; 90 | return SWIG_AddCast(SWIG_OK); 91 | } else { 92 | PyErr_Clear(); 93 | } 94 | if (!dispatch) { 95 | double d; 96 | int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d)); 97 | if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) { 98 | if (val) *val = (unsigned long)(d); 99 | return res; 100 | } 101 | } 102 | } 103 | %#endif 104 | if (!PyArray_IsScalar(obj,Integer)) return SWIG_TypeError; 105 | PyArray_Descr * ulongDescr = PyArray_DescrFromType(NPY_ULONG); 106 | PyArray_CastScalarToCtype(obj, (void*)val, ulongDescr); 107 | Py_DECREF(ulongDescr); 108 | return SWIG_OK; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /scripts/dvs128_glumpy.py: -------------------------------------------------------------------------------- 1 | """DVS128 Test. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import threading 9 | 10 | import numpy as np 11 | from glumpy import app, gloo, gl 12 | 13 | from pyaer.dvs128 import DVS128 14 | 15 | 16 | device = DVS128() 17 | 18 | print ("Device ID:", device.device_id) 19 | if device.device_is_master: 20 | print ("Device is master.") 21 | else: 22 | print ("Device is slave.") 23 | print ("Device Serial Number:", device.device_serial_number) 24 | print ("Device String:", device.device_string) 25 | print ("Device USB bus Number:", device.device_usb_bus_number) 26 | print ("Device USB device address:", device.device_usb_device_address) 27 | print ("Device size X:", device.dvs_size_X) 28 | print ("Device size Y:", device.dvs_size_Y) 29 | print ("Logic Version:", device.logic_version) 30 | 31 | data_stream = False 32 | 33 | lock = threading.Lock() 34 | 35 | clip_value = 3 36 | histrange = [(0, v) for v in (128, 128)] 37 | 38 | vertex = """ 39 | attribute vec2 position; 40 | attribute vec2 texcoord; 41 | varying vec2 v_texcoord; 42 | void main() 43 | { 44 | gl_Position = vec4(position, 0.0, 1.0); 45 | v_texcoord = texcoord; 46 | } 47 | """ 48 | 49 | fragment = """ 50 | uniform sampler2D texture; 51 | varying vec2 v_texcoord; 52 | void main() 53 | { 54 | gl_FragColor = texture2D(texture, v_texcoord); 55 | } 56 | """ 57 | 58 | window = app.Window(width=1024, height=1024, aspect=1) 59 | 60 | img_array = (np.random.uniform( 61 | 0, 1, (128, 128, 3))*250).astype(np.uint8) 62 | 63 | 64 | @window.event 65 | def on_close(): 66 | global device 67 | 68 | print ("Shutting down the device") 69 | device.shutdown() 70 | del device 71 | 72 | 73 | @window.event 74 | def on_draw(dt): 75 | global data_stream, device, event_list 76 | window.clear() 77 | 78 | if data_stream is False: 79 | device.start_data_stream() 80 | # setting bias after data stream 81 | device.set_bias_from_json("./scripts/configs/dvs128_config.json") 82 | data_stream = True 83 | 84 | lock.acquire() 85 | (pol_events, num_pol_event, 86 | special_events, num_special_event) = \ 87 | device.get_event() 88 | 89 | if num_pol_event != 0: 90 | pol_on = (pol_events[:, 3] == 1) 91 | pol_off = np.logical_not(pol_on) 92 | img_on, _, _ = np.histogram2d( 93 | pol_events[pol_on, 2], pol_events[pol_on, 1], 94 | bins=(128, 128), range=histrange) 95 | img_off, _, _ = np.histogram2d( 96 | pol_events[pol_off, 2], pol_events[pol_off, 1], 97 | bins=(128, 128), range=histrange) 98 | if clip_value is not None: 99 | integrated_img = np.clip( 100 | (img_on-img_off), -clip_value, clip_value) 101 | else: 102 | integrated_img = (img_on-img_off) 103 | 104 | img_array = ((integrated_img+clip_value)/float( 105 | clip_value*2)*255).astype(np.uint8) 106 | img_array = img_array[..., np.newaxis].repeat(3, axis=2) 107 | else: 108 | img_array = (np.random.uniform( 109 | 0, 1, (128, 128, 3))*250).astype(np.uint8) 110 | 111 | quad["texture"] = img_array 112 | quad.draw(gl.GL_TRIANGLE_STRIP) 113 | lock.release() 114 | 115 | 116 | quad = gloo.Program(vertex, fragment, count=4) 117 | quad['position'] = [(-1, -1), (-1, +1), (+1, -1), (+1, +1)] 118 | quad['texcoord'] = [(0, 1), (0, 0), (1, 1), (1, 0)] 119 | quad['texture'] = img_array 120 | app.run(framerate=150) 121 | -------------------------------------------------------------------------------- /scripts/aer_comm/aer_publisher: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """AER Publisher. 4 | 5 | Author: Yuhuang Hu 6 | Email : duguyue100@gmail.com 7 | """ 8 | 9 | from __future__ import print_function, absolute_import 10 | 11 | import argparse 12 | import json 13 | 14 | from pyaer.davis import DAVIS 15 | from pyaer.dvs128 import DVS128 16 | from pyaer.dvxplorer import DVXPLORER 17 | from pyaer.utils import expandpath, import_custom_module 18 | from pyaer.utils import parse_custom_args 19 | from pyaer.comm import AERPublisher 20 | 21 | parser = argparse.ArgumentParser() 22 | parser.add_argument("--url", type=str, 23 | default="tcp://127.0.0.1", 24 | help="AER Publisher URL") 25 | parser.add_argument("--port", type=int, 26 | default=5100, 27 | help="the port that connects this publisher") 28 | parser.add_argument("--master_topic", type=str, 29 | default="device", 30 | help="Master topic name for the publisher") 31 | parser.add_argument("--name", type=str, 32 | default="", 33 | help="Name of the publisher") 34 | 35 | parser.add_argument("--device", type=str, 36 | default="DAVIS", 37 | help="Currently supported options: DAVIS, DVS, DVXPLORER") 38 | parser.add_argument("--noise_filter", action="store_true", 39 | help="Add option to enable noise filter.") 40 | parser.add_argument("--bias_file", type=expandpath, 41 | default=None, 42 | help="Optional bias file") 43 | 44 | parser.add_argument("--use_default_pub", action="store_true") 45 | 46 | parser.add_argument("--custom_pub", type=expandpath, 47 | default="", 48 | help="path to the custom publisher class") 49 | parser.add_argument("--custom_class", type=str, 50 | default="", 51 | help="custom publisher class name") 52 | 53 | args, custom_args = parser.parse_known_args() 54 | 55 | custom_args_dict = parse_custom_args(custom_args) 56 | 57 | # print all options 58 | print("="*50) 59 | print(json.dumps( 60 | {**args.__dict__, **custom_args_dict}, 61 | indent=4, sort_keys=True)) 62 | print("="*50) 63 | 64 | # open the device 65 | if args.device == "None": 66 | device = None 67 | else: 68 | if args.device == "DAVIS": 69 | device = DAVIS(noise_filter=args.noise_filter) 70 | elif args.device == "DVS": 71 | device = DVS128(noise_filter=args.noise_filter) 72 | elif args.device == "DVXPLORER": 73 | device = DVXPLORER(noise_filter=args.noise_filter) 74 | 75 | device.start_data_stream() 76 | if args.bias_file is not None: 77 | device.set_bias_from_json(args.bias_file) 78 | 79 | # define publisher 80 | if args.use_default_pub: 81 | # fall back to the default publisher 82 | publisher = AERPublisher(device=device, 83 | url=args.url, 84 | port=args.port, 85 | master_topic=args.master_topic, 86 | name=args.name) 87 | publisher.logger.info("Use default publisher") 88 | else: 89 | # use custom publisher 90 | CustomPublisher = import_custom_module(args.custom_pub, args.custom_class) 91 | publisher = CustomPublisher( 92 | device=device, 93 | url=args.url, port=args.port, master_topic=args.master_topic, 94 | name=args.name, 95 | **custom_args_dict) 96 | publisher.logger.info("Use custom publisher {}".format(args.custom_class)) 97 | 98 | # Start sending data 99 | publisher.run() 100 | -------------------------------------------------------------------------------- /scripts/davis346_test.py: -------------------------------------------------------------------------------- 1 | """DAVIS346 test example. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from pyaer import libcaer 12 | from pyaer.davis import DAVIS 13 | 14 | device = DAVIS(noise_filter=True) 15 | 16 | print ("Device ID:", device.device_id) 17 | if device.device_is_master: 18 | print ("Device is master.") 19 | else: 20 | print ("Device is slave.") 21 | print ("Device Serial Number:", device.device_serial_number) 22 | print ("Device String:", device.device_string) 23 | print ("Device USB bus Number:", device.device_usb_bus_number) 24 | print ("Device USB device address:", device.device_usb_device_address) 25 | print ("Device size X:", device.dvs_size_X) 26 | print ("Device size Y:", device.dvs_size_Y) 27 | print ("Logic Version:", device.logic_version) 28 | print ("Background Activity Filter:", 29 | device.dvs_has_background_activity_filter) 30 | 31 | device.start_data_stream() 32 | # setting bias after data stream started 33 | device.set_bias_from_json("./scripts/configs/davis346_config.json") 34 | 35 | clip_value = 3 36 | histrange = [(0, v) for v in (260, 346)] 37 | 38 | 39 | def get_event(device): 40 | data = device.get_event() 41 | 42 | return data 43 | 44 | 45 | num_packet_before_disable = 1000 46 | 47 | while True: 48 | try: 49 | data = get_event(device) 50 | if data is not None: 51 | (pol_events, num_pol_event, 52 | special_events, num_special_event, 53 | frames_ts, frames, imu_events, 54 | num_imu_event) = data 55 | if frames.shape[0] != 0: 56 | cv2.imshow("frame", frames[0]) 57 | 58 | print("Number of events:", num_pol_event, "Number of Frames:", 59 | frames.shape, "Exposure:", 60 | device.get_config( 61 | libcaer.DAVIS_CONFIG_APS, 62 | libcaer.DAVIS_CONFIG_APS_EXPOSURE), 63 | "Autoexposure:", device.get_config( 64 | libcaer.DAVIS_CONFIG_APS, 65 | libcaer.DAVIS_CONFIG_APS_AUTOEXPOSURE)) 66 | 67 | if num_pol_event != 0: 68 | if num_packet_before_disable > 0: 69 | print(pol_events[:, 4].sum()) 70 | pol_events = pol_events[pol_events[:, 4] == 1] 71 | num_packet_before_disable -= 1 72 | else: 73 | device.disable_noise_filter() 74 | print("Noise filter disabled") 75 | pol_on = (pol_events[:, 3] == 1) 76 | pol_off = np.logical_not(pol_on) 77 | img_on, _, _ = np.histogram2d( 78 | pol_events[pol_on, 2], pol_events[pol_on, 1], 79 | bins=(260, 346), range=histrange) 80 | img_off, _, _ = np.histogram2d( 81 | pol_events[pol_off, 2], pol_events[pol_off, 1], 82 | bins=(260, 346), range=histrange) 83 | if clip_value is not None: 84 | integrated_img = np.clip( 85 | (img_on-img_off), -clip_value, clip_value) 86 | else: 87 | integrated_img = (img_on-img_off) 88 | img = integrated_img+clip_value 89 | 90 | cv2.imshow("image", img/float(clip_value*2)) 91 | 92 | if cv2.waitKey(1) & 0xFF == ord('q'): 93 | break 94 | else: 95 | pass 96 | 97 | except KeyboardInterrupt: 98 | device.shutdown() 99 | break 100 | -------------------------------------------------------------------------------- /scripts/davis346_color_test.py: -------------------------------------------------------------------------------- 1 | """DAVIS346 test example. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from pyaer import libcaer 12 | from pyaer.davis import DAVIS 13 | 14 | device = DAVIS(noise_filter=True, color_filter=True) 15 | 16 | print("Device ID:", device.device_id) 17 | if device.device_is_master: 18 | print("Device is master.") 19 | else: 20 | print("Device is slave.") 21 | print("Device Serial Number:", device.device_serial_number) 22 | print("Device String:", device.device_string) 23 | print("Device USB bus Number:", device.device_usb_bus_number) 24 | print("Device USB device address:", device.device_usb_device_address) 25 | print("Device size X:", device.dvs_size_X) 26 | print("Device size Y:", device.dvs_size_Y) 27 | print("Logic Version:", device.logic_version) 28 | print("Background Activity Filter:", 29 | device.dvs_has_background_activity_filter) 30 | print("Color Filter", device.aps_color_filter, type(device.aps_color_filter)) 31 | print(device.aps_color_filter == 1) 32 | 33 | device.start_data_stream() 34 | # setting bias after data stream started 35 | device.set_bias_from_json("./scripts/configs/davis346_config.json") 36 | 37 | clip_value = 3 38 | histrange = [(0, v) for v in (260, 346)] 39 | 40 | 41 | def get_event(device): 42 | data = device.get_event() 43 | 44 | return data 45 | 46 | 47 | num_packet_before_disable = 1000 48 | 49 | while True: 50 | try: 51 | data = get_event(device) 52 | if data is not None: 53 | (pol_events, num_pol_event, 54 | special_events, num_special_event, 55 | frames_ts, frames, imu_events, 56 | num_imu_event) = data 57 | if frames.shape[0] != 0: 58 | frame = cv2.cvtColor(frames[0], cv2.COLOR_BGR2RGB) 59 | cv2.imshow("frame", frame) 60 | 61 | if pol_events is not None: 62 | print("Number of events:", pol_events.shape, 63 | "Number of Frames:", 64 | frames.shape, "Exposure:", 65 | device.get_config( 66 | libcaer.DAVIS_CONFIG_APS, 67 | libcaer.DAVIS_CONFIG_APS_EXPOSURE), 68 | "Autoexposure:", device.get_config( 69 | libcaer.DAVIS_CONFIG_APS, 70 | libcaer.DAVIS_CONFIG_APS_AUTOEXPOSURE), 71 | "Color:", pol_events[0, 5]) 72 | 73 | if num_pol_event != 0: 74 | if num_packet_before_disable > 0: 75 | print(pol_events[:, 4].sum()) 76 | pol_events = pol_events[pol_events[:, 4] == 1] 77 | num_packet_before_disable -= 1 78 | else: 79 | device.disable_noise_filter() 80 | print("Noise filter disabled") 81 | pol_on = (pol_events[:, 3] == 1) 82 | pol_off = np.logical_not(pol_on) 83 | img_on, _, _ = np.histogram2d( 84 | pol_events[pol_on, 2], pol_events[pol_on, 1], 85 | bins=(260, 346), range=histrange) 86 | img_off, _, _ = np.histogram2d( 87 | pol_events[pol_off, 2], pol_events[pol_off, 1], 88 | bins=(260, 346), range=histrange) 89 | if clip_value is not None: 90 | integrated_img = np.clip( 91 | (img_on-img_off), -clip_value, clip_value) 92 | else: 93 | integrated_img = (img_on-img_off) 94 | img = integrated_img+clip_value 95 | 96 | cv2.imshow("image", img/float(clip_value*2)) 97 | 98 | if cv2.waitKey(1) & 0xFF == ord('q'): 99 | break 100 | else: 101 | pass 102 | 103 | except KeyboardInterrupt: 104 | device.shutdown() 105 | break 106 | -------------------------------------------------------------------------------- /scripts/aer_comm/aer_saver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """AER Saver. 4 | 5 | Author: Yuhuang Hu 6 | Email : duguyue100@gmail.com 7 | """ 8 | 9 | from __future__ import print_function, absolute_import 10 | 11 | import json 12 | import argparse 13 | 14 | from pyaer.utils import expandpath 15 | from pyaer.comm import AERSubscriber 16 | from pyaer.comm import AERHDF5Saver, AERZarrSaver 17 | 18 | 19 | class AERSaverSubscriber(AERSubscriber): 20 | def __init__(self, url, port, topic, name): 21 | super().__init__(url=url, port=port, topic=topic, name=name) 22 | 23 | def set_saver(self, saver): 24 | self.saver = saver 25 | 26 | def run(self): 27 | while True: 28 | try: 29 | data = self.socket.recv_multipart() 30 | 31 | topic_name = self.unpack_data_name( 32 | data[:2], topic_name_only=True) 33 | 34 | # you can select some of these functions to use 35 | if "polarity" in topic_name: 36 | data_id, polarity_events = \ 37 | self.unpack_polarity_events(data) 38 | if polarity_events is not None: 39 | self.saver.save(data_id, polarity_events) 40 | elif "special" in topic_name: 41 | data_id, special_events = self.unpack_special_events(data) 42 | if special_events is not None: 43 | self.saver.save(data_id, special_events) 44 | elif "frame" in topic_name: 45 | data_id, frame_events, frame_ts = \ 46 | self.unpack_frame_events(data) 47 | if frame_events is not None: 48 | self.saver.save(data_id, frame_events) 49 | elif "imu" in topic_name: 50 | data_id, imu_events = self.unpack_imu_events(data) 51 | if imu_events is not None: 52 | self.saver.save(data_id, imu_events) 53 | except KeyboardInterrupt: 54 | self.logger.info("Closing saver before shutting down") 55 | self.saver.close() 56 | break 57 | 58 | 59 | parser = argparse.ArgumentParser() 60 | parser.add_argument("--url", type=str, 61 | default="tcp://127.0.0.1", 62 | help="AER Subscriber URL") 63 | parser.add_argument("--port", type=int, 64 | default=5099, 65 | help="the port that connects this subscriber") 66 | parser.add_argument("--topic", type=str, 67 | default="", 68 | help="Topic to subscribe") 69 | parser.add_argument("--name", type=str, 70 | default="") 71 | 72 | parser.add_argument("--filename", type=expandpath, 73 | default="record.hdf5", 74 | help="Path to save record") 75 | parser.add_argument("--mode", type=str, 76 | default="w-", 77 | help="opening mode") 78 | 79 | parser.add_argument("--hdf5", action="store_true", 80 | help="use HDF5 as saver") 81 | parser.add_argument("--zarr", action="store_true", 82 | help="use Zarr as saver") 83 | 84 | # HDF5 specific arguments 85 | parser.add_argument("--libver", type=str, 86 | default="latest", 87 | help="HDF5 library version.") 88 | 89 | # Zarr specific arguments 90 | 91 | 92 | args = parser.parse_args() 93 | 94 | # print all options 95 | print("="*50) 96 | print(json.dumps(args.__dict__, indent=4, sort_keys=True)) 97 | print("="*50) 98 | 99 | 100 | if args.hdf5: 101 | # use HDF5 as saver 102 | saver = AERHDF5Saver(filename=args.filename, 103 | mode=args.mode, 104 | libver=args.libver) 105 | elif args.zarr: 106 | # use Zarr as saver 107 | saver = AERZarrSaver(filename=args.filename, mode=args.mode) 108 | raise ValueError("It's not so useful right now") 109 | else: 110 | raise ValueError("No saver selected, use --hdf5 or --zarr") 111 | 112 | saver_sub = AERSaverSubscriber( 113 | url=args.url, port=args.port, topic=args.topic, 114 | name=args.name) 115 | 116 | # set saver 117 | saver_sub.set_saver(saver) 118 | 119 | saver_sub.logger.info("AER Saver initialized.") 120 | 121 | # Start sending data 122 | saver_sub.run() 123 | -------------------------------------------------------------------------------- /docs/pages/davis.md: -------------------------------------------------------------------------------- 1 | {{autogenerated}} 2 | 3 | --- 4 | 5 | ## DAVIS240C Bias Example 6 | 7 | ```json 8 | { 9 | "DiffBn_coarse": 4, 10 | "DiffBn_fine": 39, 11 | "ONBn_coarse": 6, 12 | "ONBn_fine": 200, 13 | "OFFBn_coarse": 4, 14 | "OFFBn_fine": 0, 15 | "APSCasEPC_coarse": 5, 16 | "APSCasEPC_fine": 185, 17 | "DiffCasBNC_coarse": 5, 18 | "DiffCasBNC_fine": 115, 19 | "APSROSFBn_coarse": 6, 20 | "APSROSFBn_fine": 219, 21 | "LocalBufBn_coarse": 5, 22 | "LocalBufBn_fine": 164, 23 | "PixInvBn_coarse": 5, 24 | "PixInvBn_fine": 129, 25 | "PrBp_coarse": 2, 26 | "PrBp_fine": 58, 27 | "PrSFBp_coarse": 1, 28 | "PrSFBp_fine": 33, 29 | "RefrBp_coarse": 4, 30 | "RefrBp_fine": 25, 31 | "AEPdBn_coarse": 6, 32 | "AEPdBn_fine": 91, 33 | "LcolTimeoutBn_coarse": 5, 34 | "LcolTimeoutBn_fine": 49, 35 | "AEPuXBp_coarse": 4, 36 | "AEPuXBp_fine": 80, 37 | "AEPuYBp_coarse": 7, 38 | "AEPuYBp_fine": 152, 39 | "IFThrBn_coarse": 5, 40 | "IFThrBn_fine": 255, 41 | "IFRefrBn_coarse": 5, 42 | "IFRefrBn_fine": 255, 43 | "PadFollBn_coarse": 7, 44 | "PadFollBn_fine": 215, 45 | "APSOverflowLevelBn_coarse": 6, 46 | "APSOverflowLevelBn_fine": 253, 47 | "BiasBuffer_coarse": 5, 48 | "BiasBuffer_fine": 254, 49 | "aps_enabled": true, 50 | "dvs_enabled": true, 51 | "exposure": 4000, 52 | "autoexposure": true, 53 | "frame_delay": 0, 54 | "imu_enabled": true, 55 | "imu_acc_scale": 3, 56 | "imu_gyro_scale": 3, 57 | "imu_low_pass_filter": 0, 58 | "noise_filter_configs": { 59 | "sw_background_activity_two_levels": true, 60 | "sw_background_activity_check_polarity": true, 61 | "sw_background_activity_support_min": 2, 62 | "sw_background_activity_support_max": 8, 63 | "sw_background_activity_time": 2000, 64 | "sw_background_activity_enable": true, 65 | "sw_refractory_period_time": 200, 66 | "sw_refractory_period_enable": true, 67 | "sw_hotpixel_enable": true, 68 | "sw_hotpixel_learn": true 69 | } 70 | } 71 | ``` 72 | 73 | ## DAVIS346B Bias Example 74 | 75 | ```json 76 | { 77 | "ADC_RefHigh_volt": 24, 78 | "ADC_RefHigh_curr": 7, 79 | "ADC_RefLow_volt": 1, 80 | "ADC_RefLow_curr": 7, 81 | "LocalBufBn_coarse": 5, 82 | "LocalBufBn_fine": 164, 83 | "PadFollBn_coarse": 7, 84 | "PadFollBn_fine": 215, 85 | "DiffBn_coarse": 4, 86 | "DiffBn_fine": 39, 87 | "ONBn_coarse": 6, 88 | "ONBn_fine": 255, 89 | "OFFBn_coarse": 4, 90 | "OFFBn_fine": 0, 91 | "PixInvBn_coarse": 5, 92 | "PixInvBn_fine": 129, 93 | "PrBp_coarse": 2, 94 | "PrBp_fine": 255, 95 | "PrSFBp_coarse": 1, 96 | "PrSFBp_fine": 199, 97 | "RefrBp_coarse": 3, 98 | "RefrBp_fine": 7, 99 | "ReadoutBufBp_coarse": 6, 100 | "ReadoutBufBp_fine": 20, 101 | "APSROSFBn_coarse": 6, 102 | "APSROSFBn_fine": 219, 103 | "ADCCompBp_coarse": 5, 104 | "ADCCompBp_fine": 20, 105 | "COLSELLowBn_coarse": 0, 106 | "COLSELLowBn_fine": 1, 107 | "DACBufBp_coarse": 6, 108 | "DACBufBp_fine": 60, 109 | "LcolTimeoutBn_coarse": 5, 110 | "LcolTimeoutBn_fine": 49, 111 | "AEPdBn_coarse": 6, 112 | "AEPdBn_fine": 91, 113 | "AEPuXBp_coarse": 4, 114 | "AEPuXBp_fine": 80, 115 | "AEPuYBp_coarse": 7, 116 | "AEPuYBp_fine": 152, 117 | "IFRefrBn_coarse": 5, 118 | "IFRefrBn_fine": 255, 119 | "IFThrBn_coarse": 5, 120 | "IFThrBn_fine": 255, 121 | "BiasBuffer_coarse": 5, 122 | "BiasBuffer_fine": 254, 123 | "aps_enabled": true, 124 | "dvs_enabled": true, 125 | "exposure": 4000, 126 | "autoexposure": true, 127 | "frame_delay": 0, 128 | "imu_enabled": true, 129 | "imu_acc_scale": 3, 130 | "imu_gyro_scale": 3, 131 | "imu_low_pass_filter": 0, 132 | "background_activity_filter_enabled": false, 133 | "background_activity_filter_time": 80, 134 | "refractory_period_enabled": true, 135 | "refractory_period_time": 2, 136 | "noise_filter_configs": { 137 | "sw_background_activity_two_levels": true, 138 | "sw_background_activity_check_polarity": true, 139 | "sw_background_activity_support_min": 2, 140 | "sw_background_activity_support_max": 8, 141 | "sw_background_activity_time": 2000, 142 | "sw_background_activity_enable": true, 143 | "sw_refractory_period_time": 200, 144 | "sw_refractory_period_enable": true, 145 | "sw_hotpixel_enable": true, 146 | "sw_hotpixel_learn": true 147 | } 148 | } 149 | ``` 150 | -------------------------------------------------------------------------------- /res/appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - compiler: msys2 4 | ARCH: x64 5 | PYTHON_VERSION: "2.7" 6 | PYTHON: python3 7 | MSYS2_ARCH: x86_64 8 | MSYS2_DIR: msys64 9 | MSYSTEM: MINGW64 10 | MINICONDA: Miniconda-x64 11 | # - compiler: msys2 12 | # ARCH: x64 13 | # PYTHON_VERSION: "3.4" 14 | # PYTHON: python3 15 | # MSYS2_ARCH: x86_64 16 | # MSYS2_DIR: msys64 17 | # MSYSTEM: MINGW64 18 | # MINICONDA: Miniconda-x64 19 | - compiler: msys2 20 | ARCH: x64 21 | PYTHON_VERSION: "3.5" 22 | PYTHON: python3 23 | MSYS2_ARCH: x86_64 24 | MSYS2_DIR: msys64 25 | MSYSTEM: MINGW64 26 | MINICONDA: Miniconda-x64 27 | - compiler: msys2 28 | ARCH: x64 29 | PYTHON_VERSION: "3.6" 30 | PYTHON: python3 31 | MSYS2_ARCH: x86_64 32 | MSYS2_DIR: msys64 33 | MSYSTEM: MINGW64 34 | MINICONDA: Miniconda-x64 35 | - compiler: msys2 36 | ARCH: x64 37 | PYTHON_VERSION: "3.7" 38 | PYTHON: python3 39 | MSYS2_ARCH: x86_64 40 | MSYS2_DIR: msys64 41 | MSYSTEM: MINGW64 42 | MINICONDA: Miniconda-x64 43 | deploy: 44 | provider: GitHub 45 | description: '' 46 | auth_token: 47 | secure: QE/sriy9Y07epK5S7dQvJqzStTbHuncavO4ODI7tJVRhR26YDL819doQed6tfT2C 48 | artifact: /.*\.whl/ # upload all wheel files to release assets 49 | draft: false 50 | prerelease: false 51 | on: 52 | appveyor_repo_tag: true # deploy on tag push only 53 | 54 | platform: 55 | - x64 56 | 57 | matrix: 58 | fast_finish: true 59 | 60 | install: 61 | - set PATH=C:\%MINICONDA%\Scripts;%PATH% 62 | - cd C:\%MSYS2_DIR%\home 63 | - git clone https://github.com/inilabs/libcaer 64 | - git clone https://github.com/duguyue100/swig 65 | - conda config --set always_yes yes --set changeps1 no 66 | - conda update --yes conda 67 | - conda create -q --name python%PYTHON_VERSION% python=%PYTHON_VERSION% -c conda-forge 68 | - activate python%PYTHON_VERSION% 69 | - pip install numpy==1.16.1 70 | - pip install future -U 71 | - conda list 72 | - if [%PYTHON_VERSION%]==[3.5] ( 73 | C:\%MSYS2_DIR%\usr\bin\bash -lc "cp /c/projects/pyaer/res/cygwinccompiler-py35.py /c/%MINICONDA%/envs/python%PYTHON_VERSION%/Lib/distutils/cygwinccompiler.py" 74 | && 75 | C:\%MSYS2_DIR%\usr\bin\bash -lc "cp /c/%MINICONDA%/envs/python%PYTHON_VERSION%/vcruntime140.dll /c/%MINICONDA%/envs/python%PYTHON_VERSION%/libs" 76 | && 77 | C:\%MSYS2_DIR%\usr\bin\bash -lc "echo 'File copied'" 78 | ) 79 | - if [%PYTHON_VERSION%]==[3.6] ( 80 | C:\%MSYS2_DIR%\usr\bin\bash -lc "cp /c/projects/pyaer/res/cygwinccompiler-py36.py /c/%MINICONDA%/envs/python%PYTHON_VERSION%/Lib/distutils/cygwinccompiler.py" 81 | && 82 | C:\%MSYS2_DIR%\usr\bin\bash -lc "cp /c/%MINICONDA%/envs/python%PYTHON_VERSION%/vcruntime140.dll /c/%MINICONDA%/envs/python%PYTHON_VERSION%/libs" 83 | && 84 | C:\%MSYS2_DIR%\usr\bin\bash -lc "echo 'File copied'" 85 | ) 86 | - if [%PYTHON_VERSION%]==[3.7] ( 87 | C:\%MSYS2_DIR%\usr\bin\bash -lc "cp /c/projects/pyaer/res/cygwinccompiler-py37.py /c/%MINICONDA%/envs/python%PYTHON_VERSION%/Lib/distutils/cygwinccompiler.py" 88 | && 89 | C:\%MSYS2_DIR%\usr\bin\bash -lc "cp /c/%MINICONDA%/envs/python%PYTHON_VERSION%/vcruntime140.dll /c/%MINICONDA%/envs/python%PYTHON_VERSION%/libs" 90 | && 91 | C:\%MSYS2_DIR%\usr\bin\bash -lc "echo 'File copied'" 92 | ) 93 | - C:\%MSYS2_DIR%\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-gcc 94 | - C:\%MSYS2_DIR%\usr\bin\pacman --noconfirm -S make mingw-w64-x86_64-cmake 95 | - C:\%MSYS2_DIR%\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-pkg-config 96 | - C:\%MSYS2_DIR%\usr\bin\pacman --noconfirm -S mingw-w64-x86_64-libusb 97 | - C:\%MSYS2_DIR%\usr\bin\pacman --noconfirm -S automake bison 98 | - C:\%MSYS2_DIR%\usr\bin\bash -lc "/c/projects/pyaer/install-libcaer.sh ci" 99 | - C:\%MSYS2_DIR%\usr\bin\bash -lc "cd /home/swig && ./autogen.sh && ./configure --without-alllang --with-python=$(command -v python) --without-pcre && make -j4 && make install" 100 | - C:\%MSYS2_DIR%\usr\bin\bash -lc "cd /c/projects/pyaer && make build-win && make build-wheel && make build-wheel" 101 | - > 102 | IF "%APPVEYOR_REPO_TAG%" == "true" 103 | ( 104 | pip install twine 105 | && 106 | cd C:\projects\pyaer 107 | && 108 | twine upload -u %TWINE_USERNAME% -p %TWINE_PASSWORD% dist/*.whl 109 | ) 110 | 111 | build: off 112 | 113 | artifacts: 114 | - path: "dist\\*.whl" 115 | name: Wheels 116 | -------------------------------------------------------------------------------- /scripts/davis346_color_events.py: -------------------------------------------------------------------------------- 1 | """DAVIS346 test example. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import cv2 9 | import numpy as np 10 | 11 | from pyaer import libcaer 12 | from pyaer.davis import DAVIS 13 | 14 | device = DAVIS(noise_filter=False, color_filter=True) 15 | 16 | print("Device ID:", device.device_id) 17 | if device.device_is_master: 18 | print("Device is master.") 19 | else: 20 | print("Device is slave.") 21 | print("Device Serial Number:", device.device_serial_number) 22 | print("Device String:", device.device_string) 23 | print("Device USB bus Number:", device.device_usb_bus_number) 24 | print("Device USB device address:", device.device_usb_device_address) 25 | print("Device size X:", device.dvs_size_X) 26 | print("Device size Y:", device.dvs_size_Y) 27 | print("Logic Version:", device.logic_version) 28 | print("Background Activity Filter:", 29 | device.dvs_has_background_activity_filter) 30 | print("Color Filter", device.aps_color_filter, type(device.aps_color_filter)) 31 | print(device.aps_color_filter == 1) 32 | 33 | device.start_data_stream() 34 | # setting bias after data stream started 35 | device.set_bias_from_json("./scripts/configs/davis346_config.json") 36 | 37 | clip_value = 1 38 | histrange = [(0, v) for v in (260, 346)] 39 | 40 | 41 | def get_event(device): 42 | data = device.get_event() 43 | 44 | return data 45 | 46 | 47 | empty_img = np.zeros((260, 346, 3), dtype=np.float) 48 | 49 | 50 | while True: 51 | try: 52 | data = get_event(device) 53 | if data is not None: 54 | (pol_events, num_pol_event, 55 | special_events, num_special_event, 56 | frames_ts, frames, imu_events, 57 | num_imu_event) = data 58 | if frames.shape[0] != 0: 59 | frame = cv2.cvtColor(frames[0], cv2.COLOR_BGR2RGB) 60 | frame = cv2.resize(frame, dsize=(692, 520), 61 | interpolation=cv2.INTER_LINEAR) 62 | cv2.imshow("frame", frame) 63 | 64 | if pol_events is not None: 65 | print("Number of events:", pol_events.shape, 66 | "Number of Frames:", 67 | frames.shape, "Exposure:", 68 | device.get_config( 69 | libcaer.DAVIS_CONFIG_APS, 70 | libcaer.DAVIS_CONFIG_APS_EXPOSURE), 71 | "Autoexposure:", device.get_config( 72 | libcaer.DAVIS_CONFIG_APS, 73 | libcaer.DAVIS_CONFIG_APS_AUTOEXPOSURE), 74 | "Color:", pol_events[0, 4]) 75 | 76 | if num_pol_event != 0: 77 | # extract color events 78 | pol_g_1 = (pol_events[:, 4] == 1) # lower left green 79 | pol_b = (pol_events[:, 4] == 2) # lower right blue 80 | pol_r = (pol_events[:, 4] == 3) # upper left red 81 | pol_g_2 = (pol_events[:, 4] == 4) # upper right green 82 | 83 | g_1, _, _ = np.histogram2d( 84 | pol_events[pol_g_1, 2], pol_events[pol_g_1, 1], 85 | bins=(260, 346), range=histrange) 86 | b, _, _ = np.histogram2d( 87 | pol_events[pol_b, 2], pol_events[pol_b, 1], 88 | bins=(260, 346), range=histrange) 89 | r, _, _ = np.histogram2d( 90 | pol_events[pol_r, 2], pol_events[pol_r, 1], 91 | bins=(260, 346), range=histrange) 92 | g_2, _, _ = np.histogram2d( 93 | pol_events[pol_g_2, 2], pol_events[pol_g_2, 1], 94 | bins=(260, 346), range=histrange) 95 | 96 | g_1 = np.clip(g_1, None, clip_value)/float(clip_value) 97 | b = np.clip(b, None, clip_value)/float(clip_value) 98 | r = np.clip(r, None, clip_value)/float(clip_value) 99 | g_2 = np.clip(g_2, None, clip_value)/float(clip_value) 100 | 101 | ig_1 = np.zeros((260, 346, 3), dtype=np.float) 102 | ib = np.zeros((260, 346, 3), dtype=np.float) 103 | ir = np.zeros((260, 346, 3), dtype=np.float) 104 | ig_2 = np.zeros((260, 346, 3), dtype=np.float) 105 | 106 | # ig_1[..., 0], ig_1[..., 2] = g_1, g_1 107 | # ib[..., 1], ib[..., 2] = b, b 108 | # ir[..., 0], ir[..., 1] = r, r 109 | # ig_2[..., 0], ig_2[..., 2] = g_2, g_2 110 | 111 | ig_1[..., 1] = g_1 112 | ib[..., 0], ib[..., 1] = b, b 113 | ir[..., 2] = r 114 | ig_2[..., 1] = g_2 115 | 116 | img = np.vstack(( 117 | np.hstack((ir, ig_2)), 118 | np.hstack((ig_1, ib)))) 119 | 120 | cv2.imshow("image", img) 121 | 122 | if cv2.waitKey(1) & 0xFF == ord('q'): 123 | break 124 | else: 125 | pass 126 | 127 | except KeyboardInterrupt: 128 | device.shutdown() 129 | break 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![PyAER](./res/pyaer_cover_img.png) 2 | 3 | --- 4 | 5 | [![GitHub release](https://img.shields.io/github/release/duguyue100/pyaer.svg?style=flat-square)](https://github.com/duguyue100/pyaer/releases/latest) 6 | [![PyPI - Version](https://img.shields.io/pypi/v/pyaer)]((https://pypi.org/project/pyaer/)) 7 | [![build](https://github.com/duguyue100/pyaer/actions/workflows/main.yml/badge.svg)](https://github.com/duguyue100/pyaer/actions/workflows/main.yml) 8 | [![license](https://img.shields.io/github/license/duguyue100/pyaer.svg)](https://github.com/duguyue100/pyaer/blob/master/LICENSE) 9 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1346279.svg)](https://doi.org/10.5281/zenodo.1346279) 10 | 11 | ![Linux](https://img.shields.io/badge/OS-Linux-orange.svg) 12 | ![macOS](https://img.shields.io/badge/OS-macOS-orange.svg) 13 | ![AMD64](https://img.shields.io/badge/ARCH-AMD64-orange.svg) 14 | ![ARM](https://img.shields.io/badge/ARCH-ARM64-orange.svg) 15 | 16 | ![DVS128](https://img.shields.io/badge/DEVICE-DVS128-blueviolet.svg) 17 | ![eDVS](https://img.shields.io/badge/DEVICE-eDVS-blueviolet.svg) 18 | ![DVS240C](https://img.shields.io/badge/DEVICE-DVS240C-blueviolet.svg) 19 | ![DVS346](https://img.shields.io/badge/DEVICE-DVS346-blueviolet.svg) 20 | ![DVXLite](https://img.shields.io/badge/DEVICE-DVXplorer%20Lite-blueviolet.svg) 21 | ![DVX](https://img.shields.io/badge/DEVICE-DVXplorer-blueviolet.svg) 22 | ![EVK](https://img.shields.io/badge/DEVICE-Samsung%20EVK-blueviolet.svg) 23 | 24 | __WARNING__: This repository is under major refactor. There is no specific timeline. 25 | You are advised to build from scratch from the master branch. 26 | 27 | Special thanks to [iniVation](https://inivation.com/) for making this possible! 28 | 29 | The project is in its Beta development stage, please submit an 30 | [issue](https://github.com/duguyue100/pyaer/issues) if you encountered a problem. 31 | 32 | __NEWS__ As from 0.2.5, we switched CI/CD from Travis CI to Github Actions because the 33 | building on travis-ci.org is ceased. We are looking for solutions to produce ARM64 build. 34 | 35 | ## Why PyAER? 36 | 37 | iniVation has released [DV](https://gitlab.com/inivation/dv), a new platform and an SDK 38 | for accessing and developing with event cameras. 39 | 40 | For robotics projects, you may find [rpg_dvs_ros](https://github.com/uzh-rpg/rpg_dvs_ros) 41 | when you use ROS. 42 | 43 | So the natural question is: why PyAER? 44 | 45 | In a nutshell, PyAER is a combination of a Pythonic `libcaer` and a light-weight "ROS". 46 | PyAER serves as an agile package that focus on fast development and extensibility. 47 | In fact, in some scenario, e.g., edge devices, PyAER is more user friendly than other 48 | alternatives. 49 | 50 | ### Design Principle 51 | 52 | + Minimum installation effort. 53 | + Clean, simple, easy to manage. 54 | + Well documented, human-readable code. 55 | 56 | ## Installation 57 | 58 | 1. Install `libcaer` dependency (RECOMMEND) 59 | 60 | ```bash 61 | # for Ubuntu 62 | sudo apt-get install libcaer-dev 63 | # for macOS 64 | brew tap inivation/inivation 65 | brew install libcaer --with-libserialport --with-opencv 66 | ``` 67 | 68 | Update `udev` rules if you use a Linux system: 69 | 70 | ```bash 71 | $ bash <(curl -s https://raw.githubusercontent.com/duguyue100/pyaer/master/install-udev.sh) 72 | ``` 73 | 74 | __NOTE__: The `libcaer` installation has taken care of the `udev` update. 75 | However, if the problem persists, please try it. 76 | 77 | 2. Install `pyaer` from pypi (RECOMMEND) 78 | 79 | ```bash 80 | $ pip install pyaer 81 | ``` 82 | 83 | 3. Install `pyzmq` and `h5py` 84 | ``` 85 | $ pip install pyzmq 86 | $ pip install h5py 87 | ``` 88 | 89 | __NOTE:__ `pyzmq` is not available on ARM-based computer, you will need to build 90 | yourself. 91 | 92 | ### Development 93 | 94 | For development purpose, you might build `pyaer` from source. 95 | Please follow the instructions in [INSTALL_FROM_SOURCE.md](./INSTALL_FROM_SOURCE.md) 96 | 97 | ## Running Examples 98 | 99 | + The [scripts](./scripts) folder provides some examples for you to play with. 100 | 101 | + Extra more advanced demos are available at [pyaer-demo](https://github.com/duguyue100/pyaer-demo). 102 | 103 | ## Limitations and Notes 104 | 105 | + __2022-08-05__: Address the building issue with the latest `libcaer` release. 106 | 107 | + __2022-06-07__: We now switch to follow `libcaer` official release. 108 | 109 | + __2021-12-01__: For DAVIS346 Color model, we now support color events. 110 | 111 | + __2021-01-13__: DVXplorer cameras are officially supported 112 | (Thanks [iniVation](https://inivation.com/)) 113 | for borrowing devices. Samsung EVK support is yet to be tested. 114 | 115 | + __2020-12-02__: From 0.2.0, we support a `zeromq`-based communication 116 | module that allow users to leverage multiple processes during development. 117 | It can support multiple devices and concurrent logging and 118 | visualization. If you are familiar with ROS, you should 119 | find this feature comfortable. 120 | 121 | + DYNAP is generally supported. We are currently looking for the correct 122 | bias configuration mechanism so that it can easily support the use of the 123 | device. We have mapped some core functions that are essential to device 124 | configuration. 125 | 126 | ## Contacts 127 | 128 | Yuhuang Hu 129 | Email: duguyue100@gmail.com 130 | -------------------------------------------------------------------------------- /scripts/dvs128_vispy.py: -------------------------------------------------------------------------------- 1 | """DVS128 Test. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from __future__ import print_function 7 | 8 | import numpy as np 9 | import vispy 10 | from vispy import app, scene, visuals, gloo 11 | from vispy.util.transforms import ortho 12 | 13 | from pyaer.dvs128 import DVS128 14 | 15 | 16 | device = DVS128() 17 | 18 | print ("Device ID:", device.device_id) 19 | if device.device_is_master: 20 | print ("Device is master.") 21 | else: 22 | print ("Device is slave.") 23 | print ("Device Serial Number:", device.device_serial_number) 24 | print ("Device String:", device.device_string) 25 | print ("Device USB bus Number:", device.device_usb_bus_number) 26 | print ("Device USB device address:", device.device_usb_device_address) 27 | print ("Device size X:", device.dvs_size_X) 28 | print ("Device size Y:", device.dvs_size_Y) 29 | print ("Logic Version:", device.logic_version) 30 | 31 | # load new config 32 | device.set_bias_from_json("./scripts/configs/dvs128_config.json") 33 | print (device.get_bias()) 34 | 35 | device.start_data_stream() 36 | 37 | clip_value = 3 38 | histrange = [(0, v) for v in (128, 128)] 39 | 40 | app.use_app("pyside") 41 | 42 | W, H = 128, 128 43 | img_array = np.random.uniform(0, 1, (W, H)).astype(np.float32) 44 | 45 | data = np.zeros(4, dtype=[('a_position', np.float32, 2), 46 | ('a_texcoord', np.float32, 2)]) 47 | data['a_position'] = np.array([[0, 0], [W, 0], [0, H], [W, H]]) 48 | data['a_texcoord'] = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) 49 | 50 | VERT_SHADER = """ 51 | // Uniforms 52 | uniform mat4 u_model; 53 | uniform mat4 u_view; 54 | uniform mat4 u_projection; 55 | uniform float u_antialias; 56 | // Attributes 57 | attribute vec2 a_position; 58 | attribute vec2 a_texcoord; 59 | // Varyings 60 | varying vec2 v_texcoord; 61 | // Main 62 | void main (void) 63 | { 64 | v_texcoord = a_texcoord; 65 | gl_Position = u_projection * u_view * u_model * vec4(a_position,0.0,1.0); 66 | } 67 | """ 68 | 69 | FRAG_SHADER = """ 70 | uniform sampler2D u_texture; 71 | varying vec2 v_texcoord; 72 | void main() 73 | { 74 | gl_FragColor = texture2D(u_texture, v_texcoord); 75 | gl_FragColor.a = 1.0; 76 | } 77 | """ 78 | 79 | 80 | class Canvas(vispy.app.Canvas): 81 | def __init__(self): 82 | vispy.app.Canvas.__init__(self, keys='interactive', size=(300, 300)) 83 | self.program = gloo.Program(VERT_SHADER, FRAG_SHADER) 84 | self.texture = gloo.Texture2D( 85 | img_array, interpolation="linear") 86 | 87 | self.program['u_texture'] = self.texture 88 | self.program.bind(gloo.VertexBuffer(data)) 89 | 90 | self.view = np.eye(4, dtype=np.float32) 91 | self.model = np.eye(4, dtype=np.float32) 92 | self.projection = np.eye(4, dtype=np.float32) 93 | 94 | self.program['u_model'] = self.model 95 | self.program['u_view'] = self.view 96 | self.projection = ortho(0, W, 0, H, -1, 1) 97 | self.program['u_projection'] = self.projection 98 | 99 | gloo.set_clear_color('white') 100 | 101 | self._timer = app.Timer('auto', connect=self.update, start=True) 102 | self.show() 103 | 104 | # @profile 105 | def on_draw(self, ev): 106 | gloo.clear(color=True, depth=True) 107 | 108 | (pol_events, num_pol_event, 109 | special_events, num_special_event) = \ 110 | device.get_event() 111 | 112 | if num_pol_event != 0: 113 | pol_on = (pol_events[:, 3] == 1) 114 | pol_off = np.logical_not(pol_on) 115 | img_on, _, _ = np.histogram2d( 116 | pol_events[pol_on, 1], 127-pol_events[pol_on, 2], 117 | bins=(128, 128), range=histrange) 118 | img_off, _, _ = np.histogram2d( 119 | pol_events[pol_off, 1], 127-pol_events[pol_off, 2], 120 | bins=(128, 128), range=histrange) 121 | if clip_value is not None: 122 | integrated_img = np.clip( 123 | (img_on-img_off), -clip_value, clip_value) 124 | else: 125 | integrated_img = (img_on-img_off) 126 | 127 | img_array = ((integrated_img+clip_value)/float( 128 | clip_value*2)).astype(np.float32) 129 | else: 130 | img_array[...] = np.zeros( 131 | (128, 128), dtype=np.uint8).astype(np.float32) 132 | 133 | self.texture.set_data(img_array) 134 | self.program.draw('triangle_strip') 135 | 136 | # @profile 137 | def on_resize(self, event): 138 | width, height = event.physical_size 139 | gloo.set_viewport(0, 0, width, height) 140 | self.projection = ortho(0, width, 0, height, -100, 100) 141 | self.program['u_projection'] = self.projection 142 | 143 | # Compute thje new size of the quad 144 | r = width / float(height) 145 | R = W / float(H) 146 | if r < R: 147 | w, h = width, width / R 148 | x, y = 0, int((height - h) / 2) 149 | else: 150 | w, h = height * R, height 151 | x, y = int((width - w) / 2), 0 152 | data['a_position'] = np.array( 153 | [[x, y], [x + w, y], [x, y + h], [x + w, y + h]]) 154 | self.program.bind(gloo.VertexBuffer(data)) 155 | 156 | # @profile 157 | def run(): 158 | win = Canvas() 159 | app.run() 160 | 161 | 162 | run() 163 | -------------------------------------------------------------------------------- /custom_build/.travis.yml: -------------------------------------------------------------------------------- 1 | cache: 2 | directories: 3 | - $HOME/download 4 | - $HOME/.cache/pip 5 | 6 | language: generic 7 | 8 | os: linux 9 | 10 | env: 11 | global: 12 | - TWINE_USERNAME=duguyue100 13 | 14 | jobs: 15 | include: 16 | - os: linux 17 | language: python 18 | python: "3.5" 19 | env: TOXENV=py35 20 | - os: linux 21 | language: python 22 | python: "3.6" 23 | env: TOXENV=py36 24 | - os: linux 25 | language: python 26 | python: "3.7" 27 | env: TOXENV=py37 28 | - os: linux 29 | language: python 30 | python: "3.8" 31 | env: TOXENV=py38 32 | # - os: linux 33 | # language: python 34 | # python: "3.9" 35 | # env: TOXENV=py39 36 | - os: osx 37 | env: 38 | - TOXENV=3.5 39 | - HOMEBREW_NO_AUTO_UPDATE=1 40 | - os: osx 41 | env: 42 | - TOXENV=3.6 43 | - HOMEBREW_NO_AUTO_UPDATE=1 44 | - os: osx 45 | env: 46 | - TOXENV=3.7 47 | - HOMEBREW_NO_AUTO_UPDATE=1 48 | - os: osx 49 | env: 50 | - TOXENV=3.8 51 | - HOMEBREW_NO_AUTO_UPDATE=1 52 | # - os: osx 53 | # env: 54 | # - TOXENV=3.9 55 | # - HOMEBREW_NO_AUTO_UPDATE=1 56 | - os: linux 57 | arch: arm64 58 | language: python 59 | python: "3.5" 60 | env: TOXENV=py35 61 | - os: linux 62 | arch: arm64 63 | language: python 64 | python: "3.6" 65 | env: TOXENV=py36 66 | - os: linux 67 | arch: arm64 68 | language: python 69 | python: "3.7" 70 | env: TOXENV=py37 71 | - os: linux 72 | arch: arm64 73 | language: python 74 | python: "3.8" 75 | env: TOXENV=py38 76 | # - os: linux 77 | # arch: arm64 78 | # language: python 79 | # python: "3.9" 80 | # env: TOXENV=py39 81 | 82 | deploy: 83 | provider: releases 84 | token: $GITHUB_TOKEN 85 | file_glob: true 86 | file: dist/*.whl 87 | skip_cleanup: true 88 | on: 89 | tags: true 90 | 91 | before_install: 92 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 93 | mkdir -p download; 94 | cd download; 95 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh; 96 | chmod +x miniconda.sh; 97 | ./miniconda.sh -b -p $HOME/miniconda; 98 | export PATH=$HOME/miniconda/bin:$PATH; 99 | conda info -a; 100 | conda update --yes conda; 101 | conda create -n pyenv python=$TOXENV --yes; 102 | source activate pyenv; 103 | cd ..; 104 | fi 105 | - python --version 106 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 107 | sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test"; 108 | sudo apt-get -qq update; 109 | sudo apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-5 g++-5; 110 | sudo unlink /usr/bin/gcc && sudo ln -s /usr/bin/gcc-5 /usr/bin/gcc; 111 | sudo unlink /usr/bin/g++ && sudo ln -s /usr/bin/g++-5 /usr/bin/g++; 112 | gcc --version; 113 | fi 114 | - if [[ "$TRAVIS_CPU_ARCH" == "arm64" ]]; then 115 | sudo apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-7 g++-7; 116 | sudo unlink /usr/bin/gcc && sudo ln -s /usr/bin/gcc-7 /usr/bin/gcc; 117 | sudo unlink /usr/bin/g++ && sudo ln -s /usr/bin/g++-7 /usr/bin/g++; 118 | gcc --version; 119 | fi 120 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 121 | sudo apt-get install build-essential -y; 122 | sudo apt-get install libusb-1.0-0-dev -y; 123 | sudo apt-get install automake -y; 124 | sudo apt-get install bison -y; 125 | 126 | sudo apt-get install snapd; 127 | sudo snap install cmake --classic; 128 | export PATH=/snap/bin:$PATH; 129 | else 130 | brew install libusb; 131 | brew install automake; 132 | brew install bison; 133 | fi 134 | - git clone git://sigrok.org/libserialport 135 | - cd libserialport 136 | - ./autogen.sh 137 | - ./configure 138 | - make -j4 139 | - sudo make install 140 | - cd .. 141 | - $HOME/build/duguyue100/pyaer/install-libcaer.sh ci 142 | - git clone https://github.com/duguyue100/swig 143 | - cd swig 144 | - ./autogen.sh 145 | - ./configure --without-alllang --with-python=$(command -v python) --without-pcre 146 | - make 147 | - sudo make install 148 | - cd .. 149 | 150 | install: 151 | - pip install pip -U 152 | - pip install numpy==1.18.0 153 | - pip install wheel 154 | - if [[ $TRAVIS_TAG ]]; then 155 | pip install twine; 156 | fi 157 | 158 | script: 159 | - make build-wheel 160 | - make build-wheel 161 | - make install 162 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 163 | cd dist; 164 | for file in *.whl ; do mv $file ${file//linux/manylinux1} ; done; 165 | cd ..; 166 | fi 167 | - if [[ $TRAVIS_TAG && "$TRAVIS_CPU_ARCH" != "arm64" ]]; then 168 | twine upload -u ${TWINE_USERNAME} -p ${TWINE_PASSWORD} dist/*.whl; 169 | fi 170 | 171 | branches: 172 | except: 173 | - refactor-compiling 174 | 175 | notifications: 176 | email: false 177 | -------------------------------------------------------------------------------- /scripts/timer.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from engineering_notation import EngNumber as eng # only from pip 6 | import atexit 7 | 8 | 9 | LOGGING_LEVEL = logging.INFO 10 | 11 | 12 | class CustomFormatter(logging.Formatter): 13 | """Logging Formatter to add colors and count warning / errors""" 14 | 15 | grey = "\x1b[38;21m" 16 | yellow = "\x1b[33;21m" 17 | red = "\x1b[31;21m" 18 | bold_red = "\x1b[31;1m" 19 | reset = "\x1b[0m" 20 | format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)" 21 | 22 | FORMATS = { 23 | logging.DEBUG: grey + format + reset, 24 | logging.INFO: grey + format + reset, 25 | logging.WARNING: yellow + format + reset, 26 | logging.ERROR: red + format + reset, 27 | logging.CRITICAL: bold_red + format + reset 28 | } 29 | 30 | def format(self, record): 31 | log_fmt = self.FORMATS.get(record.levelno) 32 | formatter = logging.Formatter(log_fmt) 33 | return formatter.format(record) 34 | 35 | 36 | def my_logger(name): 37 | # logging.basicConfig(stream=sys.stdout, level=logging.INFO) 38 | logger = logging.getLogger(name) 39 | logger.setLevel(LOGGING_LEVEL) 40 | # create console handler 41 | ch = logging.StreamHandler() 42 | ch.setFormatter(CustomFormatter()) 43 | logger.addHandler(ch) 44 | return logger 45 | 46 | 47 | log = my_logger(__name__) 48 | 49 | timers = {} 50 | times = {} 51 | 52 | 53 | class Timer: 54 | def __init__(self, timer_name='', delay=None, 55 | show_hist=False, numpy_file=None): 56 | """ Make a Timer() in a _with_ statement for a block of code. 57 | The timer is started when the block is entered and stopped when exited. 58 | The Timer _must_ be used in a with statement. 59 | :param timer_name: the str by which this timer is repeatedly called 60 | and which it is named when summary is printed on exit 61 | :param delay: set this to a value to simply accumulate 62 | this externally determined interval 63 | :param show_hist: whether to plot a histogram with pyplot 64 | :param numpy_file: optional numpy file path 65 | """ 66 | self.timer_name = timer_name 67 | self.show_hist = show_hist 68 | self.numpy_file = numpy_file 69 | self.delay = delay 70 | 71 | if self.timer_name not in timers.keys(): 72 | timers[self.timer_name] = self 73 | if self.timer_name not in times.keys(): 74 | times[self.timer_name] = [] 75 | 76 | def __enter__(self): 77 | if self.delay is None: 78 | self.start = time.time() 79 | return self 80 | 81 | def __exit__(self, *args): 82 | if self.delay is None: 83 | self.end = time.time() 84 | self.interval = self.end - self.start # measured in seconds 85 | else: 86 | self.interval = self.delay 87 | times[self.timer_name].append(self.interval) 88 | 89 | def print_timing_info(self, logger=None): 90 | """ Prints the timing information accumulated for this Timer 91 | :param logger: write to the supplied logger, 92 | otherwise use the built-in logger 93 | """ 94 | if len(times) == 0: 95 | log.error(f'Timer {self.timer_name} has no statistics; was it used without a "with" statement?') 96 | return 97 | a = np.array(times[self.timer_name]) 98 | timing_mean = np.mean(a) # todo use built in print method for timer 99 | timing_std = np.std(a) 100 | timing_median = np.median(a) 101 | timing_min = np.min(a) 102 | timing_max = np.max(a) 103 | s='{} n={}: {}s +/- {}s (median {}s, min {}s max {}s)'.format(self.timer_name, len(a), 104 | eng(timing_mean), eng(timing_std), 105 | eng(timing_median), eng(timing_min), 106 | eng(timing_max)) 107 | 108 | if logger is not None: 109 | logger.info(s) 110 | else: 111 | log.info(s) 112 | 113 | 114 | def print_timing_info(): 115 | for k, v in times.items(): # k is the name, v is the list of times 116 | a = np.array(v) 117 | timing_mean = np.mean(a) 118 | timing_std = np.std(a) 119 | timing_median = np.median(a) 120 | timing_min = np.min(a) 121 | timing_max = np.max(a) 122 | log.info('== Timing statistics from all Timer ==\n{} n={}: {}s +/- {}s (median {}s, min {}s max {}s)'.format(k, len(a), 123 | eng(timing_mean), eng(timing_std), 124 | eng(timing_median), eng(timing_min), 125 | eng(timing_max))) 126 | if timers[k].numpy_file is not None: 127 | try: 128 | log.info(f'saving timing data for {k} in numpy file {timers[k].numpy_file}') 129 | log.info('there are {} times'.format(len(a))) 130 | np.save(timers[k].numpy_file, a) 131 | except Exception as e: 132 | log.error(f'could not save numpy file {timers[k].numpy_file}; caught {e}') 133 | 134 | if timers[k].show_hist: 135 | 136 | def plot_loghist(x, bins): 137 | hist, bins = np.histogram(x, bins=bins) # histogram x linearly 138 | if len(bins)<2 or bins[0]<=0: 139 | log.error(f'cannot plot histogram since bins={bins}') 140 | return 141 | logbins = np.logspace(np.log10(bins[0]), np.log10(bins[-1]), len(bins)) # use resulting bin ends to get log bins 142 | plt.hist(x, bins=logbins) # now again histogram x, but with the log-spaced bins, and plot this histogram 143 | plt.xscale('log') 144 | 145 | dt = np.clip(a,1e-6, None) 146 | # logbins = np.logspace(np.log10(bins[0]), np.log10(bins[-1]), len(bins)) 147 | try: 148 | plot_loghist(dt,bins=100) 149 | plt.xlabel('interval[ms]') 150 | plt.ylabel('frequency') 151 | plt.title(k) 152 | plt.show() 153 | except Exception as e: 154 | log.error(f'could not plot histogram: got {e}') 155 | 156 | 157 | atexit.register(print_timing_info) 158 | -------------------------------------------------------------------------------- /scripts/configs/dynapse_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mux_timestamp_reset": false, 3 | "mux_force_chip_bias_enable": false, 4 | "mux_drop_aer_on_transfer_stall": false, 5 | "aer_ack_delay": 0, 6 | "aer_ack_extension": 0, 7 | "aer_wait_on_transfer_stall": false, 8 | "aer_external_aer_control": false, 9 | "chip_req_delay": 30, 10 | "chip_req_extension": 30, 11 | "usb_early_packet_delay": 8, 12 | 13 | "c0_if_buf_p_coarse": 3, 14 | "c0_if_buf_p_fine": 80, 15 | "c0_if_rfr_n_coarse": 3, 16 | "c0_if_rfr_n_fine": 3, 17 | "c0_if_nmda_n_coarse": 7, 18 | "c0_if_nmda_n_fine": 0, 19 | "c0_if_dc_p_coarse": 7, 20 | "c0_if_dc_p_fine": 30, 21 | "c0_if_tau1_coarse": 7, 22 | "c0_if_tau1_fine": 5, 23 | "c0_if_tau2_coarse": 6, 24 | "c0_if_tau2_fine": 100, 25 | "c0_if_thr_n_coarse": 4, 26 | "c0_if_thr_n_fine": 120, 27 | "c0_if_ahw_p_coarse": 7, 28 | "c0_if_ahw_p_fine": 0, 29 | "c0_if_ahtau_n_coarse": 7, 30 | "c0_if_ahtau_n_fine": 35, 31 | "c0_if_ahthr_n_coarse": 7, 32 | "c0_if_ahthr_n_fine": 0, 33 | "c0_if_casc_n_coarse": 7, 34 | "c0_if_casc_n_fine": 0, 35 | "c0_pulse_pwlk_p_coarse": 3, 36 | "c0_pulse_pwlk_p_fine": 106, 37 | "c0_ps_weight_inh_s_n_coarse": 7, 38 | "c0_ps_weight_inh_s_n_fine": 0, 39 | "c0_ps_weight_inh_f_n_coarse": 7, 40 | "c0_ps_weight_inh_f_n_fine": 0, 41 | "c0_ps_weight_exc_s_n_coarse": 7, 42 | "c0_ps_weight_exc_s_n_fine": 0, 43 | "c0_ps_weight_exc_f_n_coarse": 7, 44 | "c0_ps_weight_exc_f_n_fine": 0, 45 | "c0_npdpii_tau_s_p_coarse": 7, 46 | "c0_npdpii_tau_s_p_fine": 40, 47 | "c0_npdpii_tau_f_p_coarse": 7, 48 | "c0_npdpii_tau_f_p_fine": 0, 49 | "c0_npdpii_thr_s_p_coarse": 7, 50 | "c0_npdpii_thr_s_p_fine": 40, 51 | "c0_npdpii_thr_f_p_coarse": 7, 52 | "c0_npdpii_thr_f_p_fine": 0, 53 | "c0_npdpie_tau_s_p_coarse": 7, 54 | "c0_npdpie_tau_s_p_fine": 0, 55 | "c0_npdpie_tau_f_p_coarse": 7, 56 | "c0_npdpie_tau_f_p_fine": 40, 57 | "c0_npdpie_thr_s_p_coarse": 7, 58 | "c0_npdpie_thr_s_p_fine": 0, 59 | "c0_npdpie_thr_f_p_coarse": 7, 60 | "c0_npdpie_thr_f_p_fine": 0, 61 | "c0_r2r_p_coarse": 4, 62 | "c0_r2r_p_fine": 85, 63 | 64 | "c1_if_buf_p_coarse": 3, 65 | "c1_if_buf_p_fine": 80, 66 | "c1_if_rfr_n_coarse": 3, 67 | "c1_if_rfr_n_fine": 3, 68 | "c1_if_nmda_n_coarse": 7, 69 | "c1_if_nmda_n_fine": 0, 70 | "c1_if_dc_p_coarse": 7, 71 | "c1_if_dc_p_fine": 30, 72 | "c1_if_tau1_coarse": 0, 73 | "c1_if_tau1_fine": 5, 74 | "c1_if_tau2_coarse": 6, 75 | "c1_if_tau2_fine": 100, 76 | "c1_if_thr_n_coarse": 4, 77 | "c1_if_thr_n_fine": 120, 78 | "c1_if_ahw_p_coarse": 7, 79 | "c1_if_ahw_p_fine": 0, 80 | "c1_if_ahtau_n_coarse": 7, 81 | "c1_if_ahtau_n_fine": 35, 82 | "c1_if_ahthr_n_coarse": 7, 83 | "c1_if_ahthr_n_fine": 0, 84 | "c1_if_casc_n_coarse": 7, 85 | "c1_if_casc_n_fine": 0, 86 | "c1_pulse_pwlk_p_coarse": 3, 87 | "c1_pulse_pwlk_p_fine": 106, 88 | "c1_ps_weight_inh_s_n_coarse": 7, 89 | "c1_ps_weight_inh_s_n_fine": 0, 90 | "c1_ps_weight_inh_f_n_coarse": 7, 91 | "c1_ps_weight_inh_f_n_fine": 0, 92 | "c1_ps_weight_exc_s_n_coarse": 7, 93 | "c1_ps_weight_exc_s_n_fine": 0, 94 | "c1_ps_weight_exc_f_n_coarse": 7, 95 | "c1_ps_weight_exc_f_n_fine": 0, 96 | "c1_npdpii_tau_s_p_coarse": 7, 97 | "c1_npdpii_tau_s_p_fine": 40, 98 | "c1_npdpii_tau_f_p_coarse": 7, 99 | "c1_npdpii_tau_f_p_fine": 0, 100 | "c1_npdpii_thr_s_p_coarse": 7, 101 | "c1_npdpii_thr_s_p_fine": 40, 102 | "c1_npdpii_thr_f_p_coarse": 7, 103 | "c1_npdpii_thr_f_p_fine": 0, 104 | "c1_npdpie_tau_s_p_coarse": 7, 105 | "c1_npdpie_tau_s_p_fine": 0, 106 | "c1_npdpie_tau_f_p_coarse": 7, 107 | "c1_npdpie_tau_f_p_fine": 40, 108 | "c1_npdpie_thr_s_p_coarse": 7, 109 | "c1_npdpie_thr_s_p_fine": 0, 110 | "c1_npdpie_thr_f_p_coarse": 7, 111 | "c1_npdpie_thr_f_p_fine": 0, 112 | "c1_r2r_p_coarse": 4, 113 | "c1_r2r_p_fine": 85, 114 | 115 | "c2_if_buf_p_coarse": 3, 116 | "c2_if_buf_p_fine": 80, 117 | "c2_if_rfr_n_coarse": 3, 118 | "c2_if_rfr_n_fine": 3, 119 | "c2_if_nmda_n_coarse": 7, 120 | "c2_if_nmda_n_fine": 0, 121 | "c2_if_dc_p_coarse": 7, 122 | "c2_if_dc_p_fine": 30, 123 | "c2_if_tau1_coarse": 0, 124 | "c2_if_tau1_fine": 5, 125 | "c2_if_tau2_coarse": 6, 126 | "c2_if_tau2_fine": 100, 127 | "c2_if_thr_n_coarse": 4, 128 | "c2_if_thr_n_fine": 120, 129 | "c2_if_ahw_p_coarse": 7, 130 | "c2_if_ahw_p_fine": 0, 131 | "c2_if_ahtau_n_coarse": 7, 132 | "c2_if_ahtau_n_fine": 35, 133 | "c2_if_ahthr_n_coarse": 7, 134 | "c2_if_ahthr_n_fine": 0, 135 | "c2_if_casc_n_coarse": 7, 136 | "c2_if_casc_n_fine": 0, 137 | "c2_pulse_pwlk_p_coarse": 3, 138 | "c2_pulse_pwlk_p_fine": 106, 139 | "c2_ps_weight_inh_s_n_coarse": 7, 140 | "c2_ps_weight_inh_s_n_fine": 0, 141 | "c2_ps_weight_inh_f_n_coarse": 7, 142 | "c2_ps_weight_inh_f_n_fine": 0, 143 | "c2_ps_weight_exc_s_n_coarse": 7, 144 | "c2_ps_weight_exc_s_n_fine": 0, 145 | "c2_ps_weight_exc_f_n_coarse": 7, 146 | "c2_ps_weight_exc_f_n_fine": 0, 147 | "c2_npdpii_tau_s_p_coarse": 7, 148 | "c2_npdpii_tau_s_p_fine": 40, 149 | "c2_npdpii_tau_f_p_coarse": 7, 150 | "c2_npdpii_tau_f_p_fine": 0, 151 | "c2_npdpii_thr_s_p_coarse": 7, 152 | "c2_npdpii_thr_s_p_fine": 40, 153 | "c2_npdpii_thr_f_p_coarse": 7, 154 | "c2_npdpii_thr_f_p_fine": 0, 155 | "c2_npdpie_tau_s_p_coarse": 7, 156 | "c2_npdpie_tau_s_p_fine": 0, 157 | "c2_npdpie_tau_f_p_coarse": 7, 158 | "c2_npdpie_tau_f_p_fine": 40, 159 | "c2_npdpie_thr_s_p_coarse": 7, 160 | "c2_npdpie_thr_s_p_fine": 0, 161 | "c2_npdpie_thr_f_p_coarse": 7, 162 | "c2_npdpie_thr_f_p_fine": 0, 163 | "c2_r2r_p_coarse": 4, 164 | "c2_r2r_p_fine": 85, 165 | 166 | "c3_if_buf_p_coarse": 3, 167 | "c3_if_buf_p_fine": 80, 168 | "c3_if_rfr_n_coarse": 3, 169 | "c3_if_rfr_n_fine": 3, 170 | "c3_if_nmda_n_coarse": 7, 171 | "c3_if_nmda_n_fine": 0, 172 | "c3_if_dc_p_coarse": 7, 173 | "c3_if_dc_p_fine": 30, 174 | "c3_if_tau1_coarse": 0, 175 | "c3_if_tau1_fine": 5, 176 | "c3_if_tau2_coarse": 6, 177 | "c3_if_tau2_fine": 100, 178 | "c3_if_thr_n_coarse": 4, 179 | "c3_if_thr_n_fine": 120, 180 | "c3_if_ahw_p_coarse": 7, 181 | "c3_if_ahw_p_fine": 0, 182 | "c3_if_ahtau_n_coarse": 7, 183 | "c3_if_ahtau_n_fine": 35, 184 | "c3_if_ahthr_n_coarse": 7, 185 | "c3_if_ahthr_n_fine": 0, 186 | "c3_if_casc_n_coarse": 7, 187 | "c3_if_casc_n_fine": 0, 188 | "c3_pulse_pwlk_p_coarse": 3, 189 | "c3_pulse_pwlk_p_fine": 106, 190 | "c3_ps_weight_inh_s_n_coarse": 7, 191 | "c3_ps_weight_inh_s_n_fine": 0, 192 | "c3_ps_weight_inh_f_n_coarse": 7, 193 | "c3_ps_weight_inh_f_n_fine": 0, 194 | "c3_ps_weight_exc_s_n_coarse": 7, 195 | "c3_ps_weight_exc_s_n_fine": 0, 196 | "c3_ps_weight_exc_f_n_coarse": 7, 197 | "c3_ps_weight_exc_f_n_fine": 0, 198 | "c3_npdpii_tau_s_p_coarse": 7, 199 | "c3_npdpii_tau_s_p_fine": 40, 200 | "c3_npdpii_tau_f_p_coarse": 7, 201 | "c3_npdpii_tau_f_p_fine": 0, 202 | "c3_npdpii_thr_s_p_coarse": 7, 203 | "c3_npdpii_thr_s_p_fine": 40, 204 | "c3_npdpii_thr_f_p_coarse": 7, 205 | "c3_npdpii_thr_f_p_fine": 0, 206 | "c3_npdpie_tau_s_p_coarse": 7, 207 | "c3_npdpie_tau_s_p_fine": 0, 208 | "c3_npdpie_tau_f_p_coarse": 7, 209 | "c3_npdpie_tau_f_p_fine": 40, 210 | "c3_npdpie_thr_s_p_coarse": 7, 211 | "c3_npdpie_thr_s_p_fine": 0, 212 | "c3_npdpie_thr_f_p_coarse": 7, 213 | "c3_npdpie_thr_f_p_fine": 0, 214 | "c3_r2r_p_coarse": 4, 215 | "c3_r2r_p_fine": 85, 216 | 217 | "d_buffer_coarse": 1, 218 | "d_buffer_fine": 2, 219 | "d_ssp_coarse": 0, 220 | "d_ssp_fine": 7, 221 | "d_ssn_coarse": 0, 222 | "d_ssn_fine": 15, 223 | "u_buffer_coarse": 1, 224 | "u_buffer_fine": 2, 225 | "u_ssp_coarse": 0, 226 | "u_ssp_fine": 7, 227 | "u_ssn_coarse": 0, 228 | "u_ssn_fine": 15 229 | } 230 | -------------------------------------------------------------------------------- /docs/pages/dynapse.md: -------------------------------------------------------------------------------- 1 | {{autogenerated}} 2 | 3 | --- 4 | 5 | ## Bias Example 6 | 7 | ```json 8 | { 9 | "mux_timestamp_reset": false, 10 | "mux_force_chip_bias_enable": false, 11 | "mux_drop_aer_on_transfer_stall": false, 12 | "aer_ack_delay": 0, 13 | "aer_ack_extension": 0, 14 | "aer_wait_on_transfer_stall": false, 15 | "aer_external_aer_control": false, 16 | "chip_req_delay": 30, 17 | "chip_req_extension": 30, 18 | "usb_early_packet_delay": 8, 19 | 20 | "c0_if_buf_p_coarse": 3, 21 | "c0_if_buf_p_fine": 80, 22 | "c0_if_rfr_n_coarse": 3, 23 | "c0_if_rfr_n_fine": 3, 24 | "c0_if_nmda_n_coarse": 7, 25 | "c0_if_nmda_n_fine": 0, 26 | "c0_if_dc_p_coarse": 7, 27 | "c0_if_dc_p_fine": 30, 28 | "c0_if_tau1_coarse": 7, 29 | "c0_if_tau1_fine": 5, 30 | "c0_if_tau2_coarse": 6, 31 | "c0_if_tau2_fine": 100, 32 | "c0_if_thr_n_coarse": 4, 33 | "c0_if_thr_n_fine": 120, 34 | "c0_if_ahw_p_coarse": 7, 35 | "c0_if_ahw_p_fine": 0, 36 | "c0_if_ahtau_n_coarse": 7, 37 | "c0_if_ahtau_n_fine": 35, 38 | "c0_if_ahthr_n_coarse": 7, 39 | "c0_if_ahthr_n_fine": 0, 40 | "c0_if_casc_n_coarse": 7, 41 | "c0_if_casc_n_fine": 0, 42 | "c0_pulse_pwlk_p_coarse": 3, 43 | "c0_pulse_pwlk_p_fine": 106, 44 | "c0_ps_weight_inh_s_n_coarse": 7, 45 | "c0_ps_weight_inh_s_n_fine": 0, 46 | "c0_ps_weight_inh_f_n_coarse": 7, 47 | "c0_ps_weight_inh_f_n_fine": 0, 48 | "c0_ps_weight_exc_s_n_coarse": 7, 49 | "c0_ps_weight_exc_s_n_fine": 0, 50 | "c0_ps_weight_exc_f_n_coarse": 7, 51 | "c0_ps_weight_exc_f_n_fine": 0, 52 | "c0_npdpii_tau_s_p_coarse": 7, 53 | "c0_npdpii_tau_s_p_fine": 40, 54 | "c0_npdpii_tau_f_p_coarse": 7, 55 | "c0_npdpii_tau_f_p_fine": 0, 56 | "c0_npdpii_thr_s_p_coarse": 7, 57 | "c0_npdpii_thr_s_p_fine": 40, 58 | "c0_npdpii_thr_f_p_coarse": 7, 59 | "c0_npdpii_thr_f_p_fine": 0, 60 | "c0_npdpie_tau_s_p_coarse": 7, 61 | "c0_npdpie_tau_s_p_fine": 0, 62 | "c0_npdpie_tau_f_p_coarse": 7, 63 | "c0_npdpie_tau_f_p_fine": 40, 64 | "c0_npdpie_thr_s_p_coarse": 7, 65 | "c0_npdpie_thr_s_p_fine": 0, 66 | "c0_npdpie_thr_f_p_coarse": 7, 67 | "c0_npdpie_thr_f_p_fine": 0, 68 | "c0_r2r_p_coarse": 4, 69 | "c0_r2r_p_fine": 85, 70 | 71 | "c1_if_buf_p_coarse": 3, 72 | "c1_if_buf_p_fine": 80, 73 | "c1_if_rfr_n_coarse": 3, 74 | "c1_if_rfr_n_fine": 3, 75 | "c1_if_nmda_n_coarse": 7, 76 | "c1_if_nmda_n_fine": 0, 77 | "c1_if_dc_p_coarse": 7, 78 | "c1_if_dc_p_fine": 30, 79 | "c1_if_tau1_coarse": 0, 80 | "c1_if_tau1_fine": 5, 81 | "c1_if_tau2_coarse": 6, 82 | "c1_if_tau2_fine": 100, 83 | "c1_if_thr_n_coarse": 4, 84 | "c1_if_thr_n_fine": 120, 85 | "c1_if_ahw_p_coarse": 7, 86 | "c1_if_ahw_p_fine": 0, 87 | "c1_if_ahtau_n_coarse": 7, 88 | "c1_if_ahtau_n_fine": 35, 89 | "c1_if_ahthr_n_coarse": 7, 90 | "c1_if_ahthr_n_fine": 0, 91 | "c1_if_casc_n_coarse": 7, 92 | "c1_if_casc_n_fine": 0, 93 | "c1_pulse_pwlk_p_coarse": 3, 94 | "c1_pulse_pwlk_p_fine": 106, 95 | "c1_ps_weight_inh_s_n_coarse": 7, 96 | "c1_ps_weight_inh_s_n_fine": 0, 97 | "c1_ps_weight_inh_f_n_coarse": 7, 98 | "c1_ps_weight_inh_f_n_fine": 0, 99 | "c1_ps_weight_exc_s_n_coarse": 7, 100 | "c1_ps_weight_exc_s_n_fine": 0, 101 | "c1_ps_weight_exc_f_n_coarse": 7, 102 | "c1_ps_weight_exc_f_n_fine": 0, 103 | "c1_npdpii_tau_s_p_coarse": 7, 104 | "c1_npdpii_tau_s_p_fine": 40, 105 | "c1_npdpii_tau_f_p_coarse": 7, 106 | "c1_npdpii_tau_f_p_fine": 0, 107 | "c1_npdpii_thr_s_p_coarse": 7, 108 | "c1_npdpii_thr_s_p_fine": 40, 109 | "c1_npdpii_thr_f_p_coarse": 7, 110 | "c1_npdpii_thr_f_p_fine": 0, 111 | "c1_npdpie_tau_s_p_coarse": 7, 112 | "c1_npdpie_tau_s_p_fine": 0, 113 | "c1_npdpie_tau_f_p_coarse": 7, 114 | "c1_npdpie_tau_f_p_fine": 40, 115 | "c1_npdpie_thr_s_p_coarse": 7, 116 | "c1_npdpie_thr_s_p_fine": 0, 117 | "c1_npdpie_thr_f_p_coarse": 7, 118 | "c1_npdpie_thr_f_p_fine": 0, 119 | "c1_r2r_p_coarse": 4, 120 | "c1_r2r_p_fine": 85, 121 | 122 | "c2_if_buf_p_coarse": 3, 123 | "c2_if_buf_p_fine": 80, 124 | "c2_if_rfr_n_coarse": 3, 125 | "c2_if_rfr_n_fine": 3, 126 | "c2_if_nmda_n_coarse": 7, 127 | "c2_if_nmda_n_fine": 0, 128 | "c2_if_dc_p_coarse": 7, 129 | "c2_if_dc_p_fine": 30, 130 | "c2_if_tau1_coarse": 0, 131 | "c2_if_tau1_fine": 5, 132 | "c2_if_tau2_coarse": 6, 133 | "c2_if_tau2_fine": 100, 134 | "c2_if_thr_n_coarse": 4, 135 | "c2_if_thr_n_fine": 120, 136 | "c2_if_ahw_p_coarse": 7, 137 | "c2_if_ahw_p_fine": 0, 138 | "c2_if_ahtau_n_coarse": 7, 139 | "c2_if_ahtau_n_fine": 35, 140 | "c2_if_ahthr_n_coarse": 7, 141 | "c2_if_ahthr_n_fine": 0, 142 | "c2_if_casc_n_coarse": 7, 143 | "c2_if_casc_n_fine": 0, 144 | "c2_pulse_pwlk_p_coarse": 3, 145 | "c2_pulse_pwlk_p_fine": 106, 146 | "c2_ps_weight_inh_s_n_coarse": 7, 147 | "c2_ps_weight_inh_s_n_fine": 0, 148 | "c2_ps_weight_inh_f_n_coarse": 7, 149 | "c2_ps_weight_inh_f_n_fine": 0, 150 | "c2_ps_weight_exc_s_n_coarse": 7, 151 | "c2_ps_weight_exc_s_n_fine": 0, 152 | "c2_ps_weight_exc_f_n_coarse": 7, 153 | "c2_ps_weight_exc_f_n_fine": 0, 154 | "c2_npdpii_tau_s_p_coarse": 7, 155 | "c2_npdpii_tau_s_p_fine": 40, 156 | "c2_npdpii_tau_f_p_coarse": 7, 157 | "c2_npdpii_tau_f_p_fine": 0, 158 | "c2_npdpii_thr_s_p_coarse": 7, 159 | "c2_npdpii_thr_s_p_fine": 40, 160 | "c2_npdpii_thr_f_p_coarse": 7, 161 | "c2_npdpii_thr_f_p_fine": 0, 162 | "c2_npdpie_tau_s_p_coarse": 7, 163 | "c2_npdpie_tau_s_p_fine": 0, 164 | "c2_npdpie_tau_f_p_coarse": 7, 165 | "c2_npdpie_tau_f_p_fine": 40, 166 | "c2_npdpie_thr_s_p_coarse": 7, 167 | "c2_npdpie_thr_s_p_fine": 0, 168 | "c2_npdpie_thr_f_p_coarse": 7, 169 | "c2_npdpie_thr_f_p_fine": 0, 170 | "c2_r2r_p_coarse": 4, 171 | "c2_r2r_p_fine": 85, 172 | 173 | "c3_if_buf_p_coarse": 3, 174 | "c3_if_buf_p_fine": 80, 175 | "c3_if_rfr_n_coarse": 3, 176 | "c3_if_rfr_n_fine": 3, 177 | "c3_if_nmda_n_coarse": 7, 178 | "c3_if_nmda_n_fine": 0, 179 | "c3_if_dc_p_coarse": 7, 180 | "c3_if_dc_p_fine": 30, 181 | "c3_if_tau1_coarse": 0, 182 | "c3_if_tau1_fine": 5, 183 | "c3_if_tau2_coarse": 6, 184 | "c3_if_tau2_fine": 100, 185 | "c3_if_thr_n_coarse": 4, 186 | "c3_if_thr_n_fine": 120, 187 | "c3_if_ahw_p_coarse": 7, 188 | "c3_if_ahw_p_fine": 0, 189 | "c3_if_ahtau_n_coarse": 7, 190 | "c3_if_ahtau_n_fine": 35, 191 | "c3_if_ahthr_n_coarse": 7, 192 | "c3_if_ahthr_n_fine": 0, 193 | "c3_if_casc_n_coarse": 7, 194 | "c3_if_casc_n_fine": 0, 195 | "c3_pulse_pwlk_p_coarse": 3, 196 | "c3_pulse_pwlk_p_fine": 106, 197 | "c3_ps_weight_inh_s_n_coarse": 7, 198 | "c3_ps_weight_inh_s_n_fine": 0, 199 | "c3_ps_weight_inh_f_n_coarse": 7, 200 | "c3_ps_weight_inh_f_n_fine": 0, 201 | "c3_ps_weight_exc_s_n_coarse": 7, 202 | "c3_ps_weight_exc_s_n_fine": 0, 203 | "c3_ps_weight_exc_f_n_coarse": 7, 204 | "c3_ps_weight_exc_f_n_fine": 0, 205 | "c3_npdpii_tau_s_p_coarse": 7, 206 | "c3_npdpii_tau_s_p_fine": 40, 207 | "c3_npdpii_tau_f_p_coarse": 7, 208 | "c3_npdpii_tau_f_p_fine": 0, 209 | "c3_npdpii_thr_s_p_coarse": 7, 210 | "c3_npdpii_thr_s_p_fine": 40, 211 | "c3_npdpii_thr_f_p_coarse": 7, 212 | "c3_npdpii_thr_f_p_fine": 0, 213 | "c3_npdpie_tau_s_p_coarse": 7, 214 | "c3_npdpie_tau_s_p_fine": 0, 215 | "c3_npdpie_tau_f_p_coarse": 7, 216 | "c3_npdpie_tau_f_p_fine": 40, 217 | "c3_npdpie_thr_s_p_coarse": 7, 218 | "c3_npdpie_thr_s_p_fine": 0, 219 | "c3_npdpie_thr_f_p_coarse": 7, 220 | "c3_npdpie_thr_f_p_fine": 0, 221 | "c3_r2r_p_coarse": 4, 222 | "c3_r2r_p_fine": 85, 223 | 224 | "d_buffer_coarse": 1, 225 | "d_buffer_fine": 2, 226 | "d_ssp_coarse": 0, 227 | "d_ssp_fine": 7, 228 | "d_ssn_coarse": 0, 229 | "d_ssn_fine": 15, 230 | "u_buffer_coarse": 1, 231 | "u_buffer_fine": 2, 232 | "u_ssp_coarse": 0, 233 | "u_ssp_fine": 7, 234 | "u_ssn_coarse": 0, 235 | "u_ssn_fine": 15 236 | } 237 | ``` 238 | -------------------------------------------------------------------------------- /pyaer/filters.py: -------------------------------------------------------------------------------- 1 | """Implementation of software filters in libcaer. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | from typing import Any 7 | from typing import Dict 8 | 9 | from pyaer import libcaer 10 | from pyaer import utils 11 | 12 | 13 | class DVSNoise(object): 14 | """Software DVS background activity filter. 15 | 16 | # Args: 17 | size_x: maximum X axis resolution. 18 | size_y: maximum Y axis resolution. 19 | """ 20 | 21 | def __init__(self, size_x: int, size_y: int) -> None: 22 | """DVS Noise.""" 23 | self.size_x = size_x 24 | self.size_y = size_y 25 | self.handle = None 26 | 27 | self.initialize() 28 | 29 | if self.handle is None: 30 | raise ValueError( 31 | "Software background activity filter" "initialization failed." 32 | ) 33 | 34 | def initialize(self) -> None: 35 | """Initializes.""" 36 | if self.handle is None: 37 | self.handle = libcaer.caerFilterDVSNoiseInitialize( 38 | sizeX=self.size_x, sizeY=self.size_y 39 | ) 40 | 41 | def destroy(self) -> None: 42 | """Destroys DVS noise filter to free up memory.""" 43 | libcaer.caerFilterDVSNoiseDestroy(self.handle) 44 | 45 | def set_bias_from_json(self, file_path: str, verbose: bool = False) -> None: 46 | """Sets bias from loading JSON configuration file. 47 | 48 | # Args: 49 | file_path: absolute path of the JSON bias file. 50 | verbose: print verbosely if True. 51 | """ 52 | bias_obj = utils.load_dvs_bias(file_path, verbose) 53 | self.set_bias(bias_obj) 54 | 55 | def set_bias(self, bias_obj: Dict[str, Any]) -> None: 56 | """Configures filter. 57 | 58 | # Args: 59 | bias_obj: A dictionary that contains the configuration of the filter. 60 | """ 61 | self.set_config( 62 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_TWO_LEVELS, 63 | bias_obj["sw_background_activity_two_levels"], 64 | ) 65 | self.set_config( 66 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_CHECK_POLARITY, 67 | bias_obj["sw_background_activity_check_polarity"], 68 | ) 69 | self.set_config( 70 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_SUPPORT_MIN, 71 | bias_obj["sw_background_activity_support_min"], 72 | ) 73 | self.set_config( 74 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_SUPPORT_MAX, 75 | bias_obj["sw_background_activity_support_max"], 76 | ) 77 | self.set_config( 78 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_TIME, 79 | bias_obj["sw_background_activity_time"], 80 | ) 81 | self.set_config( 82 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_ENABLE, 83 | bias_obj["sw_background_activity_enable"], 84 | ) 85 | 86 | self.set_config( 87 | libcaer.CAER_FILTER_DVS_REFRACTORY_PERIOD_TIME, 88 | bias_obj["sw_refractory_period_time"], 89 | ) 90 | self.set_config( 91 | libcaer.CAER_FILTER_DVS_REFRACTORY_PERIOD_ENABLE, 92 | bias_obj["sw_refractory_period_enable"], 93 | ) 94 | 95 | self.set_config( 96 | libcaer.CAER_FILTER_DVS_HOTPIXEL_ENABLE, bias_obj["sw_hotpixel_enable"] 97 | ) 98 | 99 | if bias_obj["sw_hotpixel_enable"] is True: 100 | self.set_config( 101 | libcaer.CAER_FILTER_DVS_HOTPIXEL_LEARN, bias_obj["sw_hotpixel_learn"] 102 | ) 103 | # self.set_config( 104 | # libcaer.CAER_FILTER_DVS_HOTPIXEL_TIME, 105 | # bias_obj["sw_hotpixel_time"]) 106 | # self.set_config( 107 | # libcaer.CAER_FILTER_DVS_HOTPIXEL_COUNT, 108 | # bias_obj["sw_hotpixel_count"]) 109 | 110 | def get_bias(self) -> Dict[str, Any]: 111 | """Exports configuration. 112 | 113 | # Returns: 114 | bias_obj: A dictionary that contains the configuration of the filter. 115 | """ 116 | bias_obj = {} 117 | 118 | bias_obj["sw_background_activity_two_levels"] = bool( 119 | self.get_config(libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_TWO_LEVELS) 120 | ) 121 | bias_obj["sw_background_activity_check_polarity"] = bool( 122 | self.get_config(libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_CHECK_POLARITY) 123 | ) 124 | bias_obj["sw_background_activity_support_min"] = self.get_config( 125 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_SUPPORT_MIN 126 | ) 127 | bias_obj["sw_background_activity_support_max"] = self.get_config( 128 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_SUPPORT_MAX 129 | ) 130 | bias_obj["sw_background_activity_time"] = self.get_config( 131 | libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_TIME 132 | ) 133 | bias_obj["sw_background_activity_enable"] = bool( 134 | self.get_config(libcaer.CAER_FILTER_DVS_BACKGROUND_ACTIVITY_ENABLE) 135 | ) 136 | 137 | bias_obj["sw_refractory_period_time"] = self.get_config( 138 | libcaer.CAER_FILTER_DVS_REFRACTORY_PERIOD_TIME 139 | ) 140 | bias_obj["sw_refractory_period_enable"] = bool( 141 | self.get_config(libcaer.CAER_FILTER_DVS_REFRACTORY_PERIOD_ENABLE) 142 | ) 143 | 144 | bias_obj["sw_hotpixel_enable"] = bool( 145 | self.get_config(libcaer.CAER_FILTER_DVS_HOTPIXEL_ENABLE) 146 | ) 147 | 148 | bias_obj["sw_hotpixel_learn"] = bool( 149 | self.get_config(libcaer.CAER_FILTER_DVS_HOTPIXEL_LEARN) 150 | ) 151 | if bias_obj["sw_hotpixel_enable"] is True: 152 | bias_obj["sw_hotpixel_time"] = self.get_config( 153 | libcaer.CAER_FILTER_DVS_HOTPIXEL_TIME 154 | ) 155 | bias_obj["sw_hotpixel_count"] = self.get_config( 156 | libcaer.CAER_FILTER_DVS_HOTPIXEL_COUNT 157 | ) 158 | 159 | return bias_obj 160 | 161 | def save_bias_to_json(self, file_path: str) -> bool: 162 | """Saves filter configuration to JSON. 163 | 164 | # Args: 165 | file_path: the absolute path to the destiation. 166 | 167 | # Returns: 168 | flag: returns True if success in writing, False otherwise. 169 | """ 170 | bias_obj = self.get_bias() 171 | return utils.write_json(file_path, bias_obj) 172 | 173 | def set_config(self, param_addr: str, param: Any) -> bool: 174 | """Sets configuration. 175 | 176 | # Args: 177 | param_addr: a configuration parameter address, see defines 178 | `CAER_FILTER_DVS_*`. 179 | param: a configuration parameter value integer. 180 | 181 | # Returns: 182 | True if operation successful, false otherwise. 183 | """ 184 | if self.handle is not None: 185 | set_sucess = libcaer.caerFilterDVSNoiseConfigSet( 186 | noiseFilter=self.handle, paramAddr=param_addr, param=param 187 | ) 188 | return set_sucess 189 | else: 190 | return False 191 | 192 | def get_config(self, param_addr: str) -> Any: 193 | """Gets configuration. 194 | 195 | # Args: 196 | param_addr: a configuration parameter address, see defines 197 | `CAER_FILTER_DVS_*`. 198 | 199 | # Returns: 200 | The value of the configuration. 201 | """ 202 | if self.handle is not None: 203 | return libcaer.caerFilterDVSNoiseConfigGet(self.handle, param_addr) 204 | else: 205 | return None 206 | 207 | def apply(self, event_packet: Any) -> Any: 208 | """Apply the filter to a event_packet. 209 | 210 | # Args: 211 | event_packet: `caerEventPacket`
212 | the event packet to filter. 213 | 214 | # Returns: 215 | The filtered event packet. 216 | """ 217 | return libcaer.apply_dvs_noise_filter(self.handle, event_packet) 218 | -------------------------------------------------------------------------------- /pyaer/utils.py: -------------------------------------------------------------------------------- 1 | """Utilities Functions. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | import importlib.util as imutil 7 | import json 8 | import os 9 | import time 10 | from collections import OrderedDict 11 | 12 | import numpy as np 13 | import yaml 14 | 15 | import pyaer 16 | from pyaer import libcaer 17 | from pyaer import log 18 | 19 | logger = log.get_logger("utils", pyaer.LOG_LEVEL) 20 | 21 | 22 | def get_nanotime(): 23 | return str(int(time.time() * 1e9)).encode("utf-8") 24 | 25 | 26 | def import_custom_module(custom_file, custom_class): 27 | """Load custom module by file path. 28 | 29 | # Arguments 30 | custom_file: str 31 | absolute file path to the custom module file. 32 | custom_class: str 33 | the class name to import that is in the custom_file 34 | """ 35 | module_name = os.path.basename(custom_file).split(".")[0] 36 | spec = imutil.spec_from_file_location(module_name, custom_file) 37 | custom_pub = imutil.module_from_spec(spec) 38 | spec.loader.exec_module(custom_pub) 39 | 40 | return getattr(custom_pub, custom_class) 41 | 42 | 43 | def parse_type(custom_str): 44 | """Parse custom string to its corresponding type.""" 45 | 46 | # check integer 47 | try: 48 | return int(custom_str) 49 | except ValueError: 50 | pass 51 | 52 | # check float 53 | try: 54 | return float(custom_str) 55 | except ValueError: 56 | pass 57 | 58 | # check boolean 59 | if custom_str in ["True", "False"]: 60 | return custom_str == "True" 61 | 62 | # Return string 63 | return custom_str 64 | 65 | 66 | def parse_custom_args(custom_args): 67 | """Parse custom arguments. 68 | 69 | NOTE: DO NOT USE "-" IN YOUR CUSTOM ARGUMENTS 70 | 71 | # Arguments 72 | custom_args: list 73 | the custom args supplied by parse_known_args() 74 | function 75 | 76 | # Returns 77 | custom_args_dict: dict 78 | a dictionary that contains formatted custom args. 79 | """ 80 | # empty list 81 | if len(custom_args) == 0: 82 | return {} 83 | 84 | custom_args_dict = dict( 85 | zip( 86 | [opt.replace("-", "") for opt in custom_args[::2]], 87 | [parse_type(val) for val in custom_args[1::2]], 88 | ) 89 | ) 90 | 91 | return custom_args_dict 92 | 93 | 94 | def ordered_yml_load(stream, Loader=yaml.SafeLoader, object_pairs_hook=OrderedDict): 95 | """Load YAML configs in order.""" 96 | 97 | class OrderedLoader(Loader): 98 | pass 99 | 100 | def construct_mapping(loader, node): 101 | loader.flatten_mapping(node) 102 | return object_pairs_hook(loader.construct_pairs(node)) 103 | 104 | OrderedLoader.add_constructor( 105 | yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping 106 | ) 107 | 108 | return yaml.load(stream, OrderedLoader) 109 | 110 | 111 | def ordered_yml_dump(data, stream=None, Dumper=yaml.SafeDumper, **kwds): 112 | """Dump YAML configs in order.""" 113 | 114 | class OrderedDumper(Dumper): 115 | pass 116 | 117 | def _dict_representer(dumper, data): 118 | return dumper.represent_mapping( 119 | yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, data.items() 120 | ) 121 | 122 | OrderedDumper.add_representer(OrderedDict, _dict_representer) 123 | return yaml.dump(data, stream, OrderedDumper, **kwds) 124 | 125 | 126 | def expandpath(path): 127 | return os.path.abspath(os.path.expandvars(os.path.expanduser(path))) 128 | 129 | 130 | def load_json(file_path): 131 | """Load JSON string. 132 | 133 | # Arguments 134 | file_path: `str`
135 | the absolute path to the JSON string. 136 | 137 | # Returns 138 | json_obj: `dict`
139 | A JSON object 140 | """ 141 | try: 142 | json_obj = json.load(open(file_path)) 143 | return json_obj 144 | except IOError: 145 | return None 146 | 147 | 148 | def write_json(file_path, json_obj): 149 | """Write JSON string. 150 | 151 | # Arguments 152 | file_path: `str`
153 | the absolute path to the JSON string. 154 | json_obj: `dict`
155 | a dictionary 156 | 157 | # Returns 158 | flag : bool 159 | True if saved successfully 160 | False otherwise 161 | """ 162 | try: 163 | with open(file_path, "w") as f: 164 | json.dump(json_obj, f) 165 | f.close() 166 | return True 167 | except IOError: 168 | return False 169 | 170 | 171 | def load_dvs_bias(file_path, verbose=False): 172 | """Load bias for DVS128. 173 | 174 | # Arguments 175 | file_path : `str`
` 176 | the absolute path to the JSON string. 177 | 178 | # Returns 179 | bias_obj: `dict`
180 | A dictionary that contains valid DVS128 bias. 181 | """ 182 | bias_obj = load_json(file_path) 183 | 184 | if bias_obj is not None: 185 | if verbose: 186 | for key, value in bias_obj.iteritems(): 187 | logger.debug("%s: %d" % (key, value)) 188 | # TODO: to check validity of the bias file 189 | return bias_obj 190 | else: 191 | return None 192 | 193 | 194 | def load_davis_bias(file_path, verbose=False): 195 | """Load DAVIS bias. 196 | 197 | TODO: to investigate bias differences between 240C and 346. 198 | 199 | # Arguments 200 | file_path : `str`
` 201 | the absolute path to the JSON string. 202 | 203 | # Returns 204 | bias_obj: `dict`
205 | A dictionary that contains valid DAVIS bias. 206 | """ 207 | bias_obj = load_json(file_path) 208 | 209 | if bias_obj is not None: 210 | if verbose: 211 | for key, value in bias_obj.iteritems(): 212 | logger.debug("%s: %d" % (key, value)) 213 | # TODO: to check validity of the bias file 214 | return bias_obj 215 | else: 216 | return None 217 | 218 | 219 | def load_dvxplorer_bias(file_path, verbose=False): 220 | """Load DVXPLORER bias. 221 | 222 | # Arguments 223 | file_path : `str`
` 224 | the absolute path to the JSON string. 225 | 226 | # Returns 227 | bias_obj: `dict`
228 | A dictionary that contains valid DVXPLORER bias. 229 | """ 230 | bias_obj = load_json(file_path) 231 | 232 | if bias_obj is not None: 233 | if verbose: 234 | for key, value in bias_obj.iteritems(): 235 | logger.debug("%s: %d" % (key, value)) 236 | # TODO: to check validity of the bias file 237 | return bias_obj 238 | else: 239 | return None 240 | 241 | 242 | def load_evk_bias(file_path, verbose=False): 243 | """Load EVK bias. 244 | 245 | # Arguments 246 | file_path : `str`
` 247 | the absolute path to the JSON string. 248 | 249 | # Returns 250 | bias_obj: `dict`
251 | A dictionary that contains valid EVK bias. 252 | """ 253 | bias_obj = load_json(file_path) 254 | 255 | if bias_obj is not None: 256 | if verbose: 257 | for key, value in bias_obj.iteritems(): 258 | logger.debug("%s: %d" % (key, value)) 259 | # TODO: to check validity of the bias file 260 | return bias_obj 261 | else: 262 | return None 263 | 264 | 265 | def load_dynapse_bias(file_path, verbose=False): 266 | """Load DYNAPSE bias. 267 | 268 | # Arguments 269 | file_path: `str`
` 270 | the absolute path to the JSON string. 271 | 272 | # Returns 273 | bias_obj: `dict`
274 | A dictionary that contains valid DYNAPSE bias. 275 | """ 276 | bias_obj = load_json(file_path) 277 | 278 | if bias_obj is not None: 279 | if verbose: 280 | for key, value in bias_obj.iteritems(): 281 | logger.debug("%s: %d" % (key, value)) 282 | # TODO: to check validity of the bias file 283 | return bias_obj 284 | else: 285 | return None 286 | 287 | 288 | def discover_devices(device_type, max_devices=100): 289 | """Automatic discover devices. 290 | 291 | # Arguments 292 | device_type: `int`
293 | * -1 - CAER_DEVICE_DISCOVER_ALL
294 | * 0 - CAER_DEVICE_DVS128
295 | * 1 - CAER_DEVICE_DAVIS_FX2
296 | * 2 - CAER_DEVICE_DAVIS_FX3
297 | * 3 - CAER_DEVICE_DYNAPSE
298 | * 4 - CAER_DEVICE_DAVIS
299 | * 5 - CAER_DEVICE_EDVS
300 | * 6 - CAER_DEVICE_DAVIS_RPI
301 | 302 | # Returns 303 | discovered_devices: `numpy.ndarray`
304 | a (num_devices, 3) array
305 | the first column is device type
306 | the second column is device USB bus number or 307 | serial port name for EDVS 308 | (cannot detect string, set to 0)
309 | the third column is device USB device address or 310 | serial Baud rate (if EDVS)
311 | discovered devices type with the order 312 | Note that the array has the data type uint64, 313 | please reformat the number if necessary. 314 | num_devices: `int`
315 | number of available devices 316 | """ 317 | discovered_devices = libcaer.device_discover(device_type, (max_devices + 1) * 3) 318 | 319 | discovered_devices = discovered_devices.reshape((max_devices + 1), 3) 320 | num_devices = np.argwhere(discovered_devices == 42)[0][0] 321 | 322 | return discovered_devices[:num_devices], num_devices 323 | -------------------------------------------------------------------------------- /scripts/aer_comm/aer_launch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Parse and Launch AER Communication Programs. 4 | 5 | Author: Yuhuang Hu 6 | Email : yuhuang.hu@ini.uzh.ch 7 | """ 8 | 9 | from __future__ import print_function, absolute_import 10 | 11 | import os 12 | import sys 13 | import argparse 14 | import time 15 | 16 | from pyaer.utils import expandpath 17 | from pyaer.utils import ordered_yml_load 18 | from pyaer.comm import AERProcess 19 | from pyaer import log 20 | 21 | HUB = "Hub" 22 | PUB = "Publisher" 23 | SUB = "Subscriber" 24 | PUBSUB = "PubSuber" 25 | SAVER = "Saver" 26 | 27 | USE_DEFAULT = "use_default" 28 | USE_DEFAULT_PUB = "use_default_pub" 29 | USE_DEFAULT_SUB = "use_default_sub" 30 | DEFAULT_URL = "tcp://127.0.0.1" 31 | DEFAULT_PUBLISHER_PORT = "5100" 32 | DEFAULT_SUBSCRIBER_PORT = "5099" 33 | DEFAULT_AER_HUB_NAME = "PyAER Message Hub" 34 | DEFAULT_AER_PUBLISHER_PROGRAM = "aer_publisher" 35 | DEFAULT_AER_SUBSCRIBER_PROGRAM = "aer_subscriber" 36 | DEFAULT_AER_PUBSUBER_PROGRAM = "aer_pubsuber" 37 | 38 | DEFAULT_HDF5_MODE = "w" 39 | DEFAULT_LIBVER_VERSION = "earliest" 40 | 41 | PROGRAM_KEY = "program" 42 | 43 | launch_logger = log.get_logger( 44 | "AER Launcher", 45 | log.INFO, stream=sys.stdout) 46 | 47 | 48 | def parse_hub(hub_desc): 49 | parsed_cmd = ["aer_hub"] 50 | 51 | if hub_desc[USE_DEFAULT] is True: 52 | parsed_cmd += ["--url", DEFAULT_URL] 53 | parsed_cmd += ["--publisher_port", DEFAULT_PUBLISHER_PORT] 54 | parsed_cmd += ["--subscriber_port", DEFAULT_SUBSCRIBER_PORT] 55 | parsed_cmd += ["--aer_hub_name", DEFAULT_AER_HUB_NAME] 56 | 57 | return parsed_cmd 58 | 59 | parsed_cmd += ["--url", hub_desc["url"]] 60 | parsed_cmd += ["--publisher_port", str(hub_desc["publisher_port"])] 61 | parsed_cmd += ["--subscriber_port", str(hub_desc["subscriber_port"])] 62 | parsed_cmd += ["--aer_hub_name", hub_desc["aer_hub_name"]] 63 | 64 | return parsed_cmd 65 | 66 | 67 | def parse_publisher(pub_desc): 68 | parsed_cmd = [] 69 | 70 | # select program 71 | if PROGRAM_KEY in pub_desc: 72 | parsed_cmd += [expandpath(pub_desc[PROGRAM_KEY])] 73 | else: 74 | parsed_cmd += [DEFAULT_AER_PUBLISHER_PROGRAM] 75 | 76 | parsed_cmd += [ 77 | "--url", 78 | DEFAULT_URL if "url" not in pub_desc else pub_desc.pop("url")] 79 | parsed_cmd += [ 80 | "--port", 81 | DEFAULT_PUBLISHER_PORT if "port" not in pub_desc else 82 | str(pub_desc.pop("port"))] 83 | 84 | parsed_cmd += ["--name", pub_desc.pop("name")] 85 | parsed_cmd += ["--master_topic", pub_desc.pop("master_topic")] 86 | 87 | if "custom_pub" in pub_desc: 88 | # parse custom command 89 | # follows the form /file/path/class_name 90 | custom_pub, custom_class = os.path.split(pub_desc.pop("custom_pub")) 91 | 92 | parsed_cmd += ["--custom_pub", expandpath(custom_pub)] 93 | parsed_cmd += ["--custom_class", custom_class] 94 | else: 95 | parsed_cmd += ["--use_default_pub"] 96 | 97 | pub_desc.pop(USE_DEFAULT_PUB) 98 | 99 | try: 100 | parsed_cmd += ["--device", pub_desc.pop("device")] 101 | if pub_desc.pop("noise_filter", default=False): 102 | parsed_cmd += ["--noise_filter"] 103 | if "bias_file" in pub_desc: 104 | parsed_cmd += [ 105 | "--bias_file", expandpath(pub_desc.pop("bias_file"))] 106 | except Exception: 107 | pass 108 | 109 | try: 110 | for (option, value) in pub_desc.items(): 111 | parsed_cmd += ["--{}".format(option), str(value)] 112 | except Exception: 113 | pass 114 | 115 | return parsed_cmd 116 | 117 | 118 | def parse_subscriber(sub_desc): 119 | parsed_cmd = [] 120 | 121 | # select program 122 | if PROGRAM_KEY in sub_desc: 123 | parsed_cmd += [expandpath(sub_desc[PROGRAM_KEY])] 124 | else: 125 | parsed_cmd += [DEFAULT_AER_SUBSCRIBER_PROGRAM] 126 | 127 | parsed_cmd += [ 128 | "--url", 129 | DEFAULT_URL if "url" not in sub_desc else sub_desc.pop("url")] 130 | parsed_cmd += [ 131 | "--port", 132 | DEFAULT_SUBSCRIBER_PORT if "port" not in sub_desc else 133 | str(sub_desc.pop("port"))] 134 | parsed_cmd += ["--name", sub_desc.pop("name")] 135 | parsed_cmd += ["--topic", sub_desc.pop("topic")] 136 | 137 | if "custom_sub" in sub_desc: 138 | # parse custom command 139 | # follows the form /file/path/class_name 140 | custom_sub, custom_class = os.path.split(sub_desc.pop("custom_sub")) 141 | 142 | parsed_cmd += ["--custom_sub", expandpath(custom_sub)] 143 | parsed_cmd += ["--custom_class", custom_class] 144 | else: 145 | parsed_cmd += ["--use_default_sub"] 146 | 147 | sub_desc.pop(USE_DEFAULT_SUB) 148 | 149 | try: 150 | for (option, value) in sub_desc.items(): 151 | parsed_cmd += ["--{}".format(option), str(value)] 152 | except Exception: 153 | pass 154 | 155 | return parsed_cmd 156 | 157 | 158 | def parse_pubsuber(pubsuber_desc): 159 | parsed_cmd = [] 160 | 161 | # select program 162 | if PROGRAM_KEY in pubsuber_desc: 163 | parsed_cmd += [expandpath(pubsuber_desc[PROGRAM_KEY])] 164 | else: 165 | parsed_cmd += [DEFAULT_AER_PUBSUBER_PROGRAM] 166 | 167 | parsed_cmd += [ 168 | "--url", 169 | DEFAULT_URL if "url" not in pubsuber_desc else 170 | pubsuber_desc.pop("url")] 171 | parsed_cmd += [ 172 | "--pub_port", 173 | DEFAULT_PUBLISHER_PORT if "pub_port" not in pubsuber_desc else 174 | str(pubsuber_desc.pop("pub_port"))] 175 | parsed_cmd += ["--pub_name", pubsuber_desc.pop("pub_name")] 176 | parsed_cmd += ["--pub_topic", pubsuber_desc.pop("pub_topic")] 177 | 178 | parsed_cmd += [ 179 | "--sub_port", 180 | DEFAULT_SUBSCRIBER_PORT if "sub_port" not in pubsuber_desc else 181 | str(pubsuber_desc.pop("sub_port"))] 182 | parsed_cmd += ["--sub_name", pubsuber_desc.pop("sub_name")] 183 | parsed_cmd += ["--sub_topic", pubsuber_desc.pop("sub_topic")] 184 | 185 | custom_pubsuber, custom_class = os.path.split( 186 | pubsuber_desc.pop("custom_pubsuber")) 187 | parsed_cmd += ["--custom_pubsuber", expandpath(custom_pubsuber)] 188 | parsed_cmd += ["--custom_class", custom_class] 189 | 190 | # Leave this here, maybe useful 191 | try: 192 | parsed_cmd += ["--device", pubsuber_desc.pop("device")] 193 | if pubsuber_desc.pop("noise_filter", default=False): 194 | parsed_cmd += ["--noise_filter"] 195 | if "bias_file" in pubsuber_desc: 196 | parsed_cmd += ["--bias_file", expandpath( 197 | pubsuber_desc.pop("bias_file"))] 198 | except Exception: 199 | pass 200 | 201 | try: 202 | for (option, value) in pubsuber_desc.items(): 203 | parsed_cmd += ["--{}".format(option), str(value)] 204 | except Exception: 205 | pass 206 | 207 | return parsed_cmd 208 | 209 | 210 | def parse_saver(saver_desc): 211 | parsed_cmd = ["aer_saver"] 212 | 213 | parsed_cmd += [ 214 | "--url", 215 | DEFAULT_URL if "url" not in saver_desc else saver_desc["url"]] 216 | parsed_cmd += [ 217 | "--port", 218 | DEFAULT_SUBSCRIBER_PORT if "port" not in saver_desc else 219 | str(saver_desc["port"])] 220 | parsed_cmd += ["--name", saver_desc["name"]] 221 | parsed_cmd += ["--topic", saver_desc["topic"]] 222 | 223 | parsed_cmd += ["--filename", expandpath(saver_desc["filename"])] 224 | 225 | parsed_cmd += [ 226 | "--mode", 227 | DEFAULT_HDF5_MODE if "mode" not in saver_desc else 228 | saver_desc["mode"]] 229 | parsed_cmd += [ 230 | "--libver", 231 | DEFAULT_LIBVER_VERSION if "libver" not in saver_desc else 232 | saver_desc["libver"]] 233 | 234 | # Use HDF5 as the default saver 235 | try: 236 | if saver_desc["zarr"] is True: 237 | parsed_cmd += ["--zarr"] 238 | except Exception: 239 | parsed_cmd += ["--hdf5"] 240 | 241 | return parsed_cmd 242 | 243 | 244 | parser = argparse.ArgumentParser("AER Launch") 245 | parser.add_argument("--launch_file", type=expandpath, 246 | help="AER Launch File") 247 | 248 | args = parser.parse_args() 249 | 250 | # load launch file 251 | with open(args.launch_file, "r") as launch_file: 252 | launch_desc = ordered_yml_load(launch_file) 253 | 254 | # main parsing loop 255 | process_collector = [] # collect all active processes 256 | 257 | try: 258 | for pg_i, (pg_type, pg_desc) in enumerate(launch_desc.items()): 259 | # if the first one is not a hub type, then start the default hub 260 | if pg_i == 0 and pg_type != HUB: 261 | parsed_hub_cmd = parse_hub({"use_default": True}) 262 | process_collector.append( 263 | AERProcess(parsed_hub_cmd, daemon=True)) 264 | 265 | if pg_type == HUB: 266 | parsed_hub_cmd = parse_hub(pg_desc) 267 | process_collector.append( 268 | AERProcess(parsed_hub_cmd)) 269 | elif PUB in pg_type: 270 | parsed_pub_cmd = parse_publisher(pg_desc) 271 | process_collector.append( 272 | AERProcess(parsed_pub_cmd)) 273 | elif SUB in pg_type: 274 | parsed_sub_cmd = parse_subscriber(pg_desc) 275 | process_collector.append( 276 | AERProcess(parsed_sub_cmd)) 277 | elif PUBSUB in pg_type: 278 | parsed_pubsuber_cmd = parse_pubsuber(pg_desc) 279 | process_collector.append( 280 | AERProcess(parsed_pubsuber_cmd)) 281 | elif SAVER in pg_type: 282 | parsed_saver_cmd = parse_saver(pg_desc) 283 | process_collector.append( 284 | AERProcess(parsed_saver_cmd)) 285 | else: 286 | launch_logger.error("Unsupported Type {}".format(pg_type)) 287 | 288 | # launching 289 | for process in process_collector: 290 | process.run() 291 | except Exception: 292 | launch_logger.info("Error launching, terminating all processes.") 293 | for process in reversed(process_collector): 294 | try: 295 | process.stop() 296 | except Exception: 297 | pass 298 | del process 299 | sys.exit(1) 300 | 301 | while True: 302 | try: 303 | time.sleep(1) 304 | except Exception: 305 | launch_logger.info("Exiting launcher, terminating all processes.") 306 | for process in reversed(process_collector): 307 | try: 308 | process.stop() 309 | except Exception: 310 | pass 311 | del process 312 | break 313 | -------------------------------------------------------------------------------- /custom_build/compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script is to compile libcaer library and Python 2 interface 4 | # Author: Yuhuang Hu 5 | # Email : duguyue100@gmail.com 6 | 7 | # option 8 | fn=$1 9 | 10 | # for installation check 11 | INSTALL_OPT="no" 12 | ENABLE_INSTALL=false 13 | 14 | # customize this to the right configuration 15 | REBUILDING=false 16 | PYTHON_VERSION=2 # TODO: support Python 3 as well 17 | CONDA_LIB_PATH=$HOME/anaconda2/lib 18 | CONDA_PKG_CONFIG_PATH=$CONDA_LIB_PATH/pkgconfig 19 | LIBCAER_INSTALLED=false 20 | 21 | # Color Profile 22 | RED='\033[0;31m' 23 | LIGHT_BLUE='\033[1;34m' 24 | BLUE='\033[0;34m' 25 | GREEN='\033[0;32m' 26 | CYAN='\033[0;36m' 27 | PURPLE='\033[0;35m' 28 | COLOR_END='\033[0m' 29 | 30 | print_help() 31 | { 32 | echo -e "${PURPLE} --------------------------------------------------------------------${COLOR_END}" 33 | echo -e "${PURPLE} Compilation Sequences for PyAER${COLOR_END}" 34 | echo -e "${PURPLE} This script is developed by Yuhuang Hu@INI@UZH/ETHz${COLOR_END}" 35 | echo -e "${PURPLE} Contact: yuhuang.hu@ini.uzh.ch${COLOR_END}" 36 | echo -e "${PURPLE} ${COLOR_END}" 37 | echo -e "${PURPLE} Available functions:${COLOR_END}" 38 | echo -e "${PURPLE} make : make libcaer and SWIG Python bindings${COLOR_END}" 39 | echo -e "${PURPLE} make.lib : make libcaer${COLOR_END}" 40 | echo -e "${PURPLE} make.swig : make Python bindings based on SWIG${COLOR_END}" 41 | echo -e "${PURPLE} make.install : Install Python bindings${COLOR_END}" 42 | echo -e "${PURPLE} clean : Clean installation and compilation files.${COLOR_END}" 43 | echo -e "${PURPLE} clean.lib : Clean libcaer compilation files${COLOR_END}" 44 | echo -e "${PURPLE} clean.swig : Clean SWIG bindings${COLOR_END}" 45 | echo -e "${PURPLE} clean.install : Clean installation${COLOR_END}" 46 | echo -e "${PURPLE} help : Print help${COLOR_END}" 47 | echo -e "${PURPLE} ${COLOR_END}" 48 | echo -e "${PURPLE} Available configuration:${COLOR_END}" 49 | echo -e "${PURPLE} REBUILDING (bool) : false if first time, true if not${COLOR_END}" 50 | echo -e "${PURPLE} PYTHON_VERSION (int) : 2 for Python 2, 3 for Python 3${COLOR_END}" 51 | echo -e "${PURPLE} CONDA_LIB_PATH (str) : Path to custom Python library${COLOR_END}" 52 | echo -e "${PURPLE} CONDA_PKG_CONFIG_PATH (str) : Path to custom Python pkg-config files${COLOR_END}" 53 | echo -e "${PURPLE} LIBCAER_INSTALLED (bool) : false if build libcaer locally,${COLOR_END}" 54 | echo -e "${PURPLE} true if you have libcaer installed in system${COLOR_END}" 55 | echo -e "${PURPLE} --------------------------------------------------------------------${COLOR_END}" 56 | } 57 | 58 | is_yes() 59 | { 60 | yesses={y,Y,yes,Yes,YES} 61 | if [[ $yesses =~ $1 ]]; then 62 | echo 1 63 | fi 64 | } 65 | 66 | print_conf() 67 | { 68 | echo -e "${LIGHT_BLUE}PYTHON VERSION : $PYTHON_VERSION${COLOR_END}" 69 | echo -e "${LIGHT_BLUE}CONDA LIB PATH : $CONDA_LIB_PATH${COLOR_END}" 70 | echo -e "${LIGHT_BLUE}CONDA PKG CONFIG PATH: $CONDA_PKG_CONFIG_PATH${COLOR_END}" 71 | } 72 | 73 | 74 | continue_install() 75 | { 76 | echo -e "${RED}[MESSAGE] Continue installation? (yes/no) [$INSTALL_OPT]${COLOR_END}" 77 | read opt 78 | if [[ $opt == "" ]]; then 79 | opt=$INSTALL_OPT 80 | fi 81 | 82 | if [[ $(is_yes $opt) ]]; then 83 | ENABLE_INSTALL=true 84 | print_conf 85 | fi 86 | } 87 | 88 | 89 | config_installation() 90 | { 91 | if [ -f $PWD/compile.conf ]; then 92 | source $PWD/compile.conf 93 | echo -e "${RED}[MESSAGE] Using customize configuration in the file compile.conf.${COLOR_END}" 94 | continue_install 95 | else 96 | echo -e "${RED}[MESSAGE] No compile.conf found, use default configuration settings if continue installation.${COLOR_END}" 97 | echo -e "${RED}[MESSAGE] Use:${COLOR_END}" 98 | echo -e "${BLUE} cp compile.conf.bak compile.conf${COLOR_END}" 99 | echo -e "${RED} to create your configuration file if you need.${COLOR_END}" 100 | continue_install 101 | fi 102 | } 103 | 104 | compile_swigpy() 105 | { 106 | echo -e "${BLUE}[MESSAGE] Compiling libcaer swig binding${COLOR_END}" 107 | if [ ! -d "$PWD/swigpy" ]; then 108 | if [ $LIBCAER_INSTALLED == false ]; then 109 | export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$PWD/libcaer/build/lib/pkgconfig 110 | fi 111 | export PKG_CONFIG_PATH=$CONDA_PKG_CONFIG_PATH:$PKG_CONFIG_PATH 112 | export LD_LIBRARY_PATH=$CONDA_LIB_PATH:$(pkg-config --variable=libdir libcaer):$LD_LIBRARY_PATH 113 | 114 | # summary information 115 | echo -e "${LIGHT_BLUE}[MESSAGE] libcaer include directory: "$(pkg-config --variable=includedir libcaer)${COLOR_END} 116 | echo -e "${LIGHT_BLUE}[MESSAGE] libcaer library directory: "$(pkg-config --variable=libdir libcaer)${COLOR_END} 117 | echo -e "${LIGHT_BLUE}[MESSAGE] LD_LIBRARY_PATH : "$LD_LIBRARY_PATH ${COLOR_END} 118 | 119 | # compile swig interface 120 | mkdir swigpy 121 | cd swigpy 122 | cp $PWD/../libcaer/bindings/python_swig/pyflags.i . 123 | swig -python -I$(pkg-config --variable=includedir libcaer) -cpperraswarn pyflags.i 124 | if [ $PYTHON_VERSION == 2 ]; then 125 | echo -e "${BLUE}[MESSAGE] Your swig installation should be compile with python 2 library${COLOR_END}" 126 | if [ "$(uname)" == "Darwin" ]; then 127 | # gcc -std=c11 -O2 -fPIC -c pyflags_wrap.c $(pkg-config --cflags python2) -I$(pkg-config --variable=includedir libcaer) -v 128 | clang -O2 -fPIC -c pyflags_wrap.c -I$HOME/anaconda/include/python2.7 -I$(pkg-config --variable=includedir libcaer) 129 | # cc -stdlib=libstdc++ -lstdc++ -arch x86_64 -O2 -fPIC -c pyflags_wrap.c $(pkg-config --cflags python2) -I$(pkg-config --variable=includedir libcaer) -v 130 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 131 | gcc -std=c11 -O2 -fPIC -c pyflags_wrap.c $(pkg-config --cflags python2) -I$(pkg-config --variable=includedir libcaer) 132 | fi 133 | elif [ $PYTHON_VERSION == 3]; then 134 | echo -e "${BLUE}[MESSAGE] Your swig installation should be compile with python 3 library${COLOR_END}" 135 | gcc -std=c11 -O2 -fPIC -c pyflags_wrap.c $(pkg-config --cflags python3) -I$(pkg-config --variable=includedir libcaer) 136 | fi 137 | if [ "$(uname)" == "Darwin" ]; then 138 | # ld -macosx_version_min 10.10.0 -L $(pkg-config --variable=libdir libcaer) -lcaer pyflags_wrap.o -o _libcaer_wrap.so 139 | clang pyflags_wrap.o -L$(pkg-config --variable=libdir libcaer) -lpython -lcaer -o _libcaer_wrap.so 140 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 141 | ld -L $(pkg-config --variable=libdir libcaer) -shared -lcaer pyflags_wrap.o -o _libcaer_wrap.so 142 | fi 143 | 144 | # configure path 145 | if [ $LIBCAER_INSTALLED == false ]; then 146 | if [[ $REBUILDING == false ]]; then 147 | NEW_LD_PATH="export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"$(pkg-config --variable=libdir libcaer) 148 | # for .bashrc 149 | if [ -f $HOME/.bashrc ]; then 150 | cp $HOME/.bashrc $HOME/.bashrc.pyaer.bak 151 | echo $NEW_LD_PATH >> $HOME/.bashrc 152 | echo -e "${GREEN}LD_LIBRARY_PATH is appended in "$HOME/.bashrc${COLOR_END} 153 | fi 154 | # for .zshrc 155 | if [ -f $HOME/.zshrc ]; then 156 | cp $HOME/.zshrc $HOME/.zshrc.pyaer.bak 157 | echo $NEW_LD_PATH >> $HOME/.zshrc 158 | echo -e "${GREEN}LD_LIBRARY_PATH is appended in "$HOME/.zshrc${COLOR_END} 159 | fi 160 | echo -e "${BLUE}[MESSAGE] LD_LIBRARY_PATH configured.${COLOR_END}" 161 | echo -e "${BLUE}[MESSAGE] Please restart your terminal or source your shell profile.${COLOR_END}" 162 | else 163 | # not appending LD_LIBRARY_PATH if true 164 | echo -e "${RED}[MESSAGE] Rebuilding enabled, no LD_LIBRARY_PATH appended.${COLOR_END}" 165 | fi 166 | fi 167 | else 168 | if [ $LIBCAER_INSTALLED == false ]; then 169 | export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$PWD/libcaer/build/lib/pkgconfig 170 | fi 171 | export PKG_CONFIG_PATH=$CONDA_PKG_CONFIG_PATH:$PKG_CONFIG_PATH 172 | export LD_LIBRARY_PATH=$CONDA_LIB_PATH:$(pkg-config --variable=libdir libcaer):$LD_LIBRARY_PATH 173 | 174 | echo -e "${LIGHT_BLUE}[MESSAGE] libcaer include directory: "$(pkg-config --variable=includedir libcaer)${COLOR_END} 175 | echo -e "${LIGHT_BLUE}[MESSAGE] libcaer library directory: "$(pkg-config --variable=libdir libcaer)${COLOR_END} 176 | echo -e "${LIGHT_BLUE}[MESSAGE] LD_LIBRARY_PATH : "$LD_LIBRARY_PATH ${COLOR_END} 177 | fi 178 | } 179 | 180 | compile_libcaer() 181 | { 182 | echo -e "${BLUE}[MESSAGE] Compiling libcaer${COLOR_END}" 183 | cd libcaer 184 | if [ ! -d "$PWD/libcaer/build" ]; then 185 | mkdir compiled 186 | mkdir build 187 | fi 188 | cd compiled 189 | cmake -DCMAKE_INSTALL_PREFIX=../build .. 190 | make -j8 191 | make install 192 | cd ../.. 193 | rm -rf $PWD/libcaer/compiled 194 | } 195 | 196 | make_install() 197 | { 198 | echo -e "${BLUE}[MESSAGE] Installing Python bindings...${COLOR_END}" 199 | cp $PWD/swigpy/_libcaer_wrap.so $PWD/pyaer 200 | cp $PWD/swigpy/libcaer_wrap.py $PWD/pyaer 201 | echo -e "${BLUE}[MESSAGE] Installation completed.${COLOR_END}" 202 | } 203 | 204 | # cleaning functions 205 | clean_swigpy() 206 | { 207 | echo -e "${BLUE}[MESSAGE] Cleaning SWIG files.${COLOR_END}" 208 | rm -rf $PWD/swigpy 209 | echo -e "${BLUE}[MESSAGE] SWIG files removed.${COLOR_END}" 210 | } 211 | 212 | clean_libcaer() 213 | { 214 | echo -e "${BLUE}[MESSAGE] Cleaning libcaer installation.${COLOR_END}" 215 | rm -rf $PWD/libcaer/build 216 | echo -e "${BLUE}[MESSAGE] libcaer files removed.${COLOR_END}" 217 | } 218 | 219 | clean_install() 220 | { 221 | echo -e "${BLUE}[MESSAGE] Cleaning Compiled interface.${COLOR_END}" 222 | rm $PWD/pyaer/_libcaer_wrap.so 223 | rm $PWD/pyaer/libcaer_wrap.py 224 | 225 | # restore bash profile 226 | if [ -f $HOME/.bashrc.pyaer.bak ]; then 227 | cp $HOME/.bashrc.pyaer.bak $HOME/.bashrc 228 | fi 229 | if [ -f $HOME/.zshrc.pyaer.bak ]; then 230 | cp $HOME/.zshrc.pyaer.bask $HOME/.zshrc 231 | fi 232 | 233 | echo -e "${BLUE}[MESSAGE] Installation removed.${COLOR_END}" 234 | } 235 | 236 | if [ "$fn" == "help" ]; then 237 | print_help 238 | exit 1 239 | fi 240 | 241 | config_installation 242 | 243 | # main work flow 244 | if [ $ENABLE_INSTALL = true ]; then 245 | case "$fn" in 246 | "make.swig") 247 | compile_swigpy 248 | ;; 249 | 250 | "clean.swig") 251 | clean_swigpy 252 | ;; 253 | 254 | "make.lib") 255 | compile_libcaer 256 | ;; 257 | 258 | "clean.lib") 259 | clean_libcaer 260 | ;; 261 | 262 | "make.install") 263 | make_install 264 | ;; 265 | 266 | "clean.install") 267 | clean_install 268 | ;; 269 | 270 | "make") 271 | # compile libcaer 272 | if [ $LIBCAER_INSTALLED == false ]; then 273 | compile_libcaer 274 | fi 275 | 276 | # compile swigpy 277 | compile_swigpy 278 | ;; 279 | 280 | "clean") 281 | # SWIG 282 | clean_swigpy 283 | 284 | # remove compiled SWIG 285 | clean_install 286 | 287 | # remove libcaer files 288 | clean_libcaer 289 | ;; 290 | esac 291 | else 292 | echo -e "${RED}[MESSAGE] Installation interrupted.${COLOR_END}" 293 | fi 294 | -------------------------------------------------------------------------------- /pyaer/edvs.py: -------------------------------------------------------------------------------- 1 | """eDVS. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | import numpy as np 7 | 8 | from pyaer import libcaer 9 | from pyaer import utils 10 | from pyaer.device import SerialDevice 11 | from pyaer.filters import DVSNoise 12 | 13 | 14 | class eDVS(SerialDevice): 15 | """eDVS. 16 | 17 | # Arguments 18 | device_id: `int`
19 | a unique ID to identify the device from others. 20 | Will be used as the source for EventPackets being 21 | generate from its data.
22 | `default is 1`. 23 | serial_port_name: `str`
24 | name of the serial port device to open.
25 | `default is /dev/ttyUSB0` 26 | serial_baud_rate: `uint32_t` 27 | baud-rate for serial port communication.
28 | `default is 12M` 29 | """ 30 | 31 | def __init__( 32 | self, 33 | device_id=1, 34 | serial_port_name="/dev/ttyUSB0", 35 | serial_baud_rate=libcaer.CAER_HOST_CONFIG_SERIAL_BAUD_RATE_12M, 36 | noise_filter=False, 37 | ): 38 | """eDVS.""" 39 | super(eDVS, self).__init__() 40 | # open device 41 | self.open(device_id, serial_port_name, serial_baud_rate) 42 | # get camera information 43 | self.obtain_device_info(self.handle) 44 | 45 | # noise filter 46 | self.filter_noise = noise_filter 47 | if noise_filter is True: 48 | self.noise_filter = DVSNoise(self.dvs_size_X, self.dvs_size_Y) 49 | else: 50 | self.noise_filter = None 51 | 52 | self.configs_list = [ 53 | ("cas", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_CAS), 54 | ("injGnd", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_INJGND), 55 | ("reqPd", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_REQPD), 56 | ("puX", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_PUX), 57 | ("diffOff", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_DIFFOFF), 58 | ("req", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_REQ), 59 | ("refr", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_REFR), 60 | ("puY", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_PUY), 61 | ("diffOn", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_DIFFON), 62 | ("diff", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_DIFF), 63 | ("foll", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_FOLL), 64 | ("Pr", libcaer.EDVS_CONFIG_BIAS, libcaer.EDVS_CONFIG_BIAS_PR), 65 | ] 66 | 67 | def set_noise_filter(self, noise_filter): 68 | """Set noise filter. 69 | 70 | # Arguments 71 | noise_filter: `filters.DVSNoise`
72 | A valid `DVSNoise` object. This filter implements 73 | software-level background activity filter. 74 | """ 75 | if noise_filter is not None: 76 | self.noise_filter = noise_filter 77 | 78 | def enable_noise_filter(self): 79 | """Enalbe DVS noise filter. 80 | 81 | This function enables the DVS noise filter. Note that this function will 82 | initialize a `DVSNoise` filter if there is None. 83 | """ 84 | if self.filter_noise is False: 85 | self.filter_noise = True 86 | 87 | if self.noise_filter is None: 88 | self.noise_filter = DVSNoise(self.dvs_size_X, self.dvs_size_Y) 89 | 90 | def disable_noise_filter(self): 91 | """Disable noise filter. 92 | 93 | This method disable the noise filter. Note that this function doesn't destroy 94 | the existed noise filter. It simply switches off the function. 95 | """ 96 | if self.filter_noise is True: 97 | self.filter_noise = False 98 | 99 | def obtain_device_info(self, handle): 100 | """Obtain eDVS info. 101 | 102 | This function collects the following information from the device: 103 | 104 | - Deveice ID 105 | - Device string 106 | - If the device is a master camera 107 | - Camera width 108 | - Camera height 109 | 110 | # Arguments 111 | handle: `caerDeviceHandle`
112 | a valid device handle that can be used with the other 113 | `libcaer` functions, or `None` on error. 114 | """ 115 | if handle is not None: 116 | info = libcaer.caerEDVSInfoGet(handle) 117 | 118 | # port all info data field out 119 | self.device_id = info.deviceID 120 | self.device_string = info.deviceString 121 | self.device_is_master = info.deviceIsMaster 122 | self.dvs_size_X = info.dvsSizeX 123 | self.dvs_size_Y = info.dvsSizeY 124 | self.serial_port_name = info.serialPortName 125 | self.serial_baud_rate = info.serialBaudRate 126 | 127 | def open( 128 | self, 129 | device_id=1, 130 | serial_port_name="/dev/ttyUSB0", 131 | serial_baud_rate=libcaer.CAER_HOST_CONFIG_SERIAL_BAUD_RATE_12M, 132 | ): 133 | """Open device. 134 | 135 | # Arguments 136 | device_id: `int`
137 | a unique ID to identify the device from others. 138 | Will be used as the source for EventPackets being 139 | generate from its data.
140 | `default is 1`. 141 | serial_port_name: `str`
142 | name of the serial port device to open.
143 | `default is /dev/ttyUSB0` 144 | serial_baud_rate: `uint32_t` 145 | baud-rate for serial port communication.
146 | `default is 12M` 147 | """ 148 | super(eDVS, self).open( 149 | libcaer.CAER_DEVICE_EDVS, device_id, serial_port_name, serial_baud_rate 150 | ) 151 | 152 | def set_bias_from_json(self, file_path, verbose=False): 153 | """Set bias from loading JSON configuration file. 154 | 155 | # Arguments 156 | file_path: `str`
157 | absolute path of the JSON bias file. 158 | verbose: `bool`
159 | optional debugging message. 160 | """ 161 | bias_obj = utils.load_dvs_bias(file_path, verbose) 162 | self.set_bias(bias_obj) 163 | 164 | def set_bias(self, bias_obj): 165 | """Set bias from bias dictionary. 166 | 167 | # Arguments 168 | bias_obj: `dict`
169 | dictionary that contains eDVS biases. 170 | 171 | # Returns 172 | flag: `bool`
173 | True if set successful, False otherwise. 174 | """ 175 | for bias_name, module_address, parameter_address in self.configs_list: 176 | self.set_config(module_address, parameter_address, bias_obj[bias_name]) 177 | 178 | def get_bias(self): 179 | """Get bias settings. 180 | 181 | # Returns 182 | bias_obj: `dict`
183 | dictionary that contains eDVS current bias settings. 184 | """ 185 | bias_obj = {} 186 | 187 | for bias_name, module_address, parameter_address in self.configs_list: 188 | bias_obj[bias_name] = self.get_config(module_address, parameter_address) 189 | 190 | return bias_obj 191 | 192 | def save_bias_to_json(self, file_path): 193 | """Save bias to JSON. 194 | 195 | # Arguments 196 | file_path: `str`
197 | the absolute path to the destiation. 198 | 199 | # Returns 200 | flag: `bool`
201 | returns True if success in writing, False otherwise. 202 | """ 203 | bias_obj = self.get_bias() 204 | return utils.write_json(file_path, bias_obj) 205 | 206 | def start_data_stream(self): 207 | """Start streaming data. 208 | 209 | # Arguments 210 | send_default_config: `bool`
211 | send default config to the device before starting 212 | the data streaming.
213 | `default is True` 214 | """ 215 | self.data_start() 216 | self.set_data_exchange_blocking() 217 | 218 | def get_polarity_event(self, packet_header, noise_filter=False): 219 | """Get a packet of polarity event. 220 | 221 | # Arguments 222 | packet_header: `caerEventPacketHeader`
223 | the header that represents a event packet 224 | noise_filter: `bool`
225 | the background activity filter is applied if True. 226 | 227 | # Returns 228 | events: `numpy.ndarray`
229 | a 2-D array that has the shape of (N, 4) where N 230 | is the number of events in the event packet. 231 | Each row in the array represents a single polarity event. 232 | The first number is the timestamp. 233 | The second number is the X position of the event. 234 | The third number is the Y position of the event. 235 | The fourth number represents the polarity of the event 236 | (positive or negative).
237 | If the `noise_filter` option is set to `True`, 238 | this array has an additional column at the end. 239 | The last column represents the validity of the corresponding 240 | event. Filtered events will be marked as 0. 241 | num_events: `int`
242 | number of the polarity events available in the packet. 243 | """ 244 | num_events, polarity = self.get_event_packet( 245 | packet_header, libcaer.POLARITY_EVENT 246 | ) 247 | 248 | if noise_filter is True: 249 | polarity = self.noise_filter.apply(polarity) 250 | 251 | events = libcaer.get_filtered_polarity_event( 252 | polarity, num_events * 5 253 | ).reshape(num_events, 5) 254 | else: 255 | events = libcaer.get_polarity_event(polarity, num_events * 4).reshape( 256 | num_events, 4 257 | ) 258 | 259 | return events, num_events 260 | 261 | def get_event(self): 262 | """Get event. 263 | 264 | # Returns 265 | pol_events: `numpy.ndarray`
266 | a 2-D array that has the shape of (N, 4) where N 267 | is the number of events in the event packet. 268 | Each row in the array represents a single polarity event. 269 | The first number is the timestamp. 270 | The second number is the X position of the event. 271 | The third number is the Y position of the event. 272 | The fourth number represents the polarity of the event 273 | (positive or negative).
274 | If the `noise_filter` option is set to `True`, 275 | this array has an additional column at the end. 276 | The last column represents the validity of the corresponding 277 | event. Filtered events will be marked as 0. 278 | num_pol_events: `int`
279 | number of the polarity events available in the packet. 280 | special_events: `numpy.ndarray`
281 | a 2-D array that has the shape of (N, 2) where N 282 | is the number of events in the event packet. 283 | Each row in the array represents a single special event. 284 | The first value is the timestamp of the event. 285 | The second value is the special event data. 286 | num_special_events: `int`
287 | number of the special events in the packet. 288 | """ 289 | packet_container, packet_number = self.get_packet_container() 290 | if packet_container is not None: 291 | num_pol_event = 0 292 | num_special_event = 0 293 | pol_events = None 294 | special_events = None 295 | for packet_id in range(packet_number): 296 | packet_header, packet_type = self.get_packet_header( 297 | packet_container, packet_id 298 | ) 299 | if packet_type == libcaer.POLARITY_EVENT: 300 | events, num_events = self.get_polarity_event(packet_header) 301 | pol_events = ( 302 | np.hstack((pol_events, events)) 303 | if pol_events is not None 304 | else events 305 | ) 306 | num_pol_event += num_events 307 | elif packet_type == libcaer.SPECIAL_EVENT: 308 | events, num_events = self.get_special_event(packet_header) 309 | special_events = ( 310 | np.hstack((special_events, events)) 311 | if special_events is not None 312 | else events 313 | ) 314 | num_special_event += num_events 315 | libcaer.caerEventPacketContainerFree(packet_container) 316 | 317 | return (pol_events, num_pol_event, special_events, num_special_event) 318 | else: 319 | return None 320 | -------------------------------------------------------------------------------- /pyaer/dvs128.py: -------------------------------------------------------------------------------- 1 | """DVS128. 2 | 3 | Author: Yuhuang Hu 4 | Email : duguyue100@gmail.com 5 | """ 6 | import numpy as np 7 | 8 | from pyaer import libcaer 9 | from pyaer import utils 10 | from pyaer.device import USBDevice 11 | from pyaer.filters import DVSNoise 12 | 13 | 14 | class DVS128(USBDevice): 15 | """DVS128. 16 | 17 | # Arguments 18 | device_id: `int`
19 | a unique ID to identify the device from others. 20 | Will be used as the source for EventPackets being 21 | generate from its data.
22 | `default is 1` 23 | bus_number_restrict: `int`
24 | restrict the search for viable devices to only this USB 25 | bus number.
26 | `default is 0` 27 | dev_address_restrict: `int`
28 | restrict the search for viable devices to only this USB 29 | device address.
30 | `default is 0` 31 | serial_number: `str`
32 | restrict the search for viable devices to only devices which do 33 | possess the given Serial Number in their USB 34 | SerialNumber descriptor.
35 | `default is ""` 36 | noise_filter: `bool`
37 | if enable noise filter,
38 | `default is False`. 39 | """ 40 | 41 | def __init__( 42 | self, 43 | device_id=1, 44 | bus_number_restrict=0, 45 | dev_address_restrict=0, 46 | serial_number="", 47 | noise_filter=False, 48 | ): 49 | """DVS128.""" 50 | super(DVS128, self).__init__() 51 | # open device 52 | self.open(device_id, bus_number_restrict, dev_address_restrict, serial_number) 53 | # get camera information 54 | self.obtain_device_info(self.handle) 55 | 56 | # noise filter 57 | self.filter_noise = noise_filter 58 | if noise_filter is True: 59 | self.noise_filter = DVSNoise(self.dvs_size_X, self.dvs_size_Y) 60 | else: 61 | self.noise_filter = None 62 | 63 | # Bias configuration list 64 | self.configs_list = [ 65 | ("cas", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_CAS), 66 | ("injGnd", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_INJGND), 67 | ("reqPd", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_REQPD), 68 | ("puX", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_PUX), 69 | ("diffOff", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_DIFFOFF), 70 | ("req", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_REQ), 71 | ("refr", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_REFR), 72 | ("puY", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_PUY), 73 | ("diffOn", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_DIFFON), 74 | ("diff", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_DIFF), 75 | ("foll", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_FOLL), 76 | ("Pr", libcaer.DVS128_CONFIG_BIAS, libcaer.DVS128_CONFIG_BIAS_PR), 77 | ] 78 | 79 | def set_noise_filter(self, noise_filter): 80 | """Set noise filter. 81 | 82 | # Arguments 83 | noise_filter: `filters.DVSNoise`
84 | A valid `DVSNoise` object. This filter implements 85 | software-level background activity filter. 86 | """ 87 | if noise_filter is not None: 88 | self.noise_filter = noise_filter 89 | 90 | def enable_noise_filter(self): 91 | """Enalbe DVS noise filter. 92 | 93 | This function enables the DVS noise filter. Note that this function will 94 | initialize a `DVSNoise` filter if there is None. 95 | """ 96 | if self.filter_noise is False: 97 | self.filter_noise = True 98 | 99 | if self.noise_filter is None: 100 | self.noise_filter = DVSNoise(self.dvs_size_X, self.dvs_size_Y) 101 | 102 | def disable_noise_filter(self): 103 | """Disable noise filter. 104 | 105 | This method disable the noise filter. Note that this function doesn't destroy 106 | the existed noise filter. It simply switches off the function. 107 | """ 108 | if self.filter_noise is True: 109 | self.filter_noise = False 110 | 111 | def obtain_device_info(self, handle): 112 | """Obtain DVS128 info. 113 | 114 | This function collects the following information from the device: 115 | 116 | - Deveice ID 117 | - If the device is a master camera 118 | - Device string 119 | - Device USB bus number 120 | - Device USB device address 121 | - Camera width 122 | - Camera height 123 | - Logic version 124 | 125 | # Arguments 126 | handle: `caerDeviceHandle`
127 | a valid device handle that can be used with the other 128 | `libcaer` functions, or `None` on error. 129 | """ 130 | if handle is not None: 131 | info = libcaer.caerDVS128InfoGet(handle) 132 | 133 | # port all info data field out 134 | self.device_id = info.deviceID 135 | self.device_serial_number = info.deviceSerialNumber 136 | self.device_usb_bus_number = info.deviceUSBBusNumber 137 | self.device_usb_device_address = info.deviceUSBDeviceAddress 138 | self.device_string = info.deviceString 139 | self.firmware_version = info.firmwareVersion 140 | self.device_is_master = info.deviceIsMaster 141 | self.dvs_size_X = info.dvsSizeX 142 | self.dvs_size_Y = info.dvsSizeY 143 | 144 | def open( 145 | self, 146 | device_id=1, 147 | bus_number_restrict=0, 148 | dev_address_restrict=0, 149 | serial_number="", 150 | ): 151 | """Open device. 152 | 153 | # Arguments 154 | device_id: `int`
155 | a unique ID to identify the device from others. 156 | Will be used as the source for EventPackets being 157 | generate from its data.
158 | `default is 1`. 159 | bus_number_restrict: `int`
160 | restrict the search for viable devices to only this USB 161 | bus number.
162 | `default is 0`. 163 | dev_address_restrict: `int`
164 | restrict the search for viable devices to only this USB 165 | device address.
166 | `default is 0`. 167 | serial_number: `str`
168 | restrict the search for viable devices to only devices which do 169 | possess the given Serial Number in their USB 170 | SerialNumber descriptor.
171 | `default is ""` 172 | """ 173 | super(DVS128, self).open( 174 | libcaer.CAER_DEVICE_DVS128, 175 | device_id, 176 | bus_number_restrict, 177 | dev_address_restrict, 178 | serial_number, 179 | ) 180 | 181 | def set_bias_from_json(self, file_path, verbose=False): 182 | """Set bias from loading JSON configuration file. 183 | 184 | # Arguments 185 | file_path: `str`
186 | absolute path of the JSON bias file. 187 | verbose: `bool`
188 | optional debugging message. 189 | """ 190 | bias_obj = utils.load_dvs_bias(file_path, verbose) 191 | self.set_bias(bias_obj) 192 | 193 | def set_bias(self, bias_obj): 194 | """Set bias from bias dictionary. 195 | 196 | # Arguments 197 | bias_obj: `dict`
198 | dictionary that contains DVS128 biases. 199 | 200 | # Returns 201 | flag: `bool`
202 | True if set successful, False otherwise. 203 | """ 204 | for bias_name, module_address, parameter_address in self.configs_list: 205 | self.set_config(module_address, parameter_address, bias_obj[bias_name]) 206 | 207 | # setting for noise filter 208 | if self.filter_noise is True: 209 | self.noise_filter.set_bias(bias_obj["noise_filter_configs"]) 210 | 211 | def get_bias(self): 212 | """Get bias settings. 213 | 214 | # Returns 215 | bias_obj: `dict`
216 | dictionary that contains DVS128 current bias settings. 217 | """ 218 | bias_obj = {} 219 | 220 | for bias_name, module_address, parameter_address in self.configs_list: 221 | bias_obj[bias_name] = self.get_config(module_address, parameter_address) 222 | 223 | # get noise filter configs 224 | if self.noise_filter is not None: 225 | bias_obj["noise_filter_configs"] = self.noise_filter.get_bias() 226 | 227 | return bias_obj 228 | 229 | def save_bias_to_json(self, file_path): 230 | """Save bias to JSON. 231 | 232 | # Arguments 233 | file_path: `str`
234 | the absolute path to the destiation. 235 | 236 | # Returns 237 | flag: `bool`
238 | returns True if success in writing, False otherwise. 239 | """ 240 | bias_obj = self.get_bias() 241 | return utils.write_json(file_path, bias_obj) 242 | 243 | def start_data_stream( 244 | self, send_default_config=True, max_packet_size=None, max_packet_interval=None 245 | ): 246 | """Start streaming data. 247 | 248 | # Arguments 249 | send_default_config: `bool`
250 | send default config to the device before starting 251 | the data streaming.
252 | `default is True` 253 | max_packet_size: `int`
254 | set the maximum number of events any of a packet container's 255 | packets may hold before it's made available to the user. 256 | Set to zero to disable.
257 | The default is `None` (use default setting: 0). 258 | max_packet_interval: `int`
259 | set the time interval between subsequent packet containers. 260 | Must be at least 1 microsecond. 261 | The value is in microseconds, and is checked across all 262 | types of events contained in the EventPacketContainer.
263 | The default is `None` (use default setting: 10ms) 264 | """ 265 | if send_default_config is True: 266 | self.send_default_config() 267 | 268 | if max_packet_size is not None: 269 | self.set_max_container_packet_size(max_packet_size) 270 | if max_packet_interval is not None: 271 | self.set_max_container_interval(max_packet_interval) 272 | 273 | self.data_start() 274 | self.set_data_exchange_blocking() 275 | 276 | def get_polarity_event(self, packet_header, noise_filter=False): 277 | """Get a packet of polarity event. 278 | 279 | # Arguments 280 | packet_header: `caerEventPacketHeader`
281 | the header that represents a event packet 282 | noise_filter: `bool`
283 | the background activity filter is applied if True. 284 | 285 | # Returns 286 | events: `numpy.ndarray`
287 | a 2-D array that has the shape of (N, 4) where N 288 | is the number of events in the event packet. 289 | Each row in the array represents a single polarity event. 290 | The first number is the timestamp. 291 | The second number is the X position of the event. 292 | The third number is the Y position of the event. 293 | The fourth number represents the polarity of the event 294 | (positive or negative).
295 | If the `noise_filter` option is set to `True`, 296 | this array has an additional column at the end. 297 | The last column represents the validity of the corresponding 298 | event. Filtered events will be marked as 0. 299 | num_events: `int`
300 | number of the polarity events available in the packet. 301 | """ 302 | num_events, polarity = self.get_event_packet( 303 | packet_header, libcaer.POLARITY_EVENT 304 | ) 305 | 306 | if noise_filter is True: 307 | polarity = self.noise_filter.apply(polarity) 308 | 309 | events = libcaer.get_filtered_polarity_event( 310 | polarity, num_events * 5 311 | ).reshape(num_events, 5) 312 | else: 313 | events = libcaer.get_polarity_event(polarity, num_events * 4).reshape( 314 | num_events, 4 315 | ) 316 | 317 | return events, num_events 318 | 319 | def get_event(self, mode="events"): 320 | """Get event. 321 | 322 | # Returns 323 | pol_events: `numpy.ndarray`
324 | a 2-D array that has the shape of (N, 4) where N 325 | is the number of events in the event packet. 326 | Each row in the array represents a single polarity event. 327 | The first number is the timestamp. 328 | The second number is the X position of the event. 329 | The third number is the Y position of the event. 330 | The fourth number represents the polarity of the event 331 | (positive or negative).
332 | If the `noise_filter` option is set to `True`, 333 | this array has an additional column at the end. 334 | The last column represents the validity of the corresponding 335 | event. Filtered events will be marked as 0. 336 | num_pol_events: `int`
337 | number of the polarity events available in the packet. 338 | special_events: `numpy.ndarray`
339 | a 2-D array that has the shape of (N, 2) where N 340 | is the number of events in the event packet. 341 | Each row in the array represents a single special event. 342 | The first value is the timestamp of the event. 343 | The second value is the special event data. 344 | num_special_events: `int`
345 | number of the special events in the packet. 346 | """ 347 | packet_container, packet_number = self.get_packet_container() 348 | if packet_container is not None: 349 | num_pol_event = 0 350 | num_special_event = 0 351 | pol_events = None 352 | special_events = None 353 | for packet_id in range(packet_number): 354 | packet_header, packet_type = self.get_packet_header( 355 | packet_container, packet_id 356 | ) 357 | if packet_type == libcaer.POLARITY_EVENT: 358 | if mode == "events": 359 | events, num_events = self.get_polarity_event( 360 | packet_header, self.filter_noise 361 | ) 362 | pol_events = ( 363 | np.hstack((pol_events, events)) 364 | if pol_events is not None 365 | else events 366 | ) 367 | elif mode == "events_hist": 368 | hist, num_events = self.get_polarity_hist( 369 | packet_header, device_type="DVS128" 370 | ) 371 | pol_events = hist if pol_events is None else pol_events + hist 372 | 373 | num_pol_event += num_events 374 | elif packet_type == libcaer.SPECIAL_EVENT: 375 | events, num_events = self.get_special_event(packet_header) 376 | special_events = ( 377 | np.hstack((special_events, events)) 378 | if special_events is not None 379 | else events 380 | ) 381 | num_special_event += num_events 382 | libcaer.caerEventPacketContainerFree(packet_container) 383 | 384 | return (pol_events, num_pol_event, special_events, num_special_event) 385 | else: 386 | return None 387 | --------------------------------------------------------------------------------