{"id":15020716,"url":"https://github.com/larsrollik/rpi_camera_colony","last_synced_at":"2025-10-26T04:30:57.820Z","repository":{"id":41589953,"uuid":"370656006","full_name":"larsrollik/rpi_camera_colony","owner":"larsrollik","description":"Central control for video acquisition with (many) Raspberry Pi cameras","archived":false,"fork":false,"pushed_at":"2023-07-18T08:19:29.000Z","size":302,"stargazers_count":6,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-31T14:22:10.252Z","etag":null,"topics":["multi-camera","raspberry-pi","raspberrypi","remote-cameras","rpi","rpi-camera"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/larsrollik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-25T10:39:30.000Z","updated_at":"2024-06-21T10:10:16.000Z","dependencies_parsed_at":"2024-09-23T03:00:46.477Z","dependency_job_id":"6d391a06-5d96-4a28-96d1-dccceccd4905","html_url":"https://github.com/larsrollik/rpi_camera_colony","commit_stats":{"total_commits":151,"total_committers":5,"mean_commits":30.2,"dds":0.5695364238410596,"last_synced_commit":"4ef3d86ce69fbdcb62f18dba258645ca1f79a41b"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larsrollik%2Frpi_camera_colony","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larsrollik%2Frpi_camera_colony/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larsrollik%2Frpi_camera_colony/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/larsrollik%2Frpi_camera_colony/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/larsrollik","download_url":"https://codeload.github.com/larsrollik/rpi_camera_colony/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238259007,"owners_count":19442511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["multi-camera","raspberry-pi","raspberrypi","remote-cameras","rpi","rpi-camera"],"created_at":"2024-09-24T19:55:28.511Z","updated_at":"2025-10-26T04:30:57.811Z","avatar_url":"https://github.com/larsrollik.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n-*- coding: utf-8 -*-\n\n Author: Lars B. Rollik \u003cL.B.Rollik@protonmail.com\u003e\n License: BSD 3-Clause\n--\u003e\n\n\u003c!-- LINKS --\u003e\n[isc-dhcp-server]: https://ubuntu.com/server/docs/network-dhcp\n[miniconda]: https://docs.conda.io/en/latest/miniconda.html\n[pinout.xyz]: https://pinout.xyz\n[Raspbian]: https://www.raspberrypi.org/documentation/installation/installing-images\n\n[arne-plugin]: https://github.com/arnefmeyer/RPiCameraPlugin\n[deshmukh]: https://github.com/DeshmukhLab/PicameraPaper\n[Vidgear]: https://github.com/abhiTronix/vidgear\n\n\n\u003c!-- Banners --\u003e\n[![DOI](https://zenodo.org/badge/370656006.svg)](https://zenodo.org/badge/latestdoi/370656006)\n[![Website](https://img.shields.io/website?up_message=online\u0026url=https%3A%2F%2Fgithub.com/larsrollik/rpi_camera_colony)](https://github.com/larsrollik/rpi_camera_colony)\n[![PyPI](https://img.shields.io/pypi/v/rpi_camera_colony.svg)](https://pypi.org/project/rpi_camera_colony)\n[![Wheel](https://img.shields.io/pypi/wheel/rpi_camera_colony.svg)](https://pypi.org/project/rpi_camera_colony)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black)\n\n\u003c!--\n[![Development Status](https://img.shields.io/pypi/status/rpi_camera_colony.svg)](https://github.com/larsrollik/rpi_camera_colony)\n[![Tests](https://img.shields.io/github/workflow/status/larsrollik/rpi_camera_colony/tests)](\n    https://github.com/larsrollik/rpi_camera_colony/actions)\n\n[![Python Version](https://img.shields.io/pypi/pyversions/rpi_camera_colony.svg)](https://pypi.org/project/rpi_camera_colony)\n[![Downloads](https://pepy.tech/badge/rpi_camera_colony)](https://pepy.tech/project/rpi_camera_colony)\n--\u003e\n\n# RPi Camera Colony (RCC)\nCentral control for video acquisition with (many) Raspberry Pi cameras\n---\n\nRecord videos in parallel with one or more remote-controlled Raspberry Pi (RPi) cameras. :movie_camera:\n\nA single configuration file and a few lines of code allow specific and reproducible acquisition settings for groups of cameras.\n\n**Example use with Python:**\n\n```python\nimport time\nfrom rpi_camera_colony.control.conductor import Conductor\n\nconductor = Conductor(settings_file=\"configuration_file\")   # Manages remote RPi\nconductor.start_acquisition()                               # Starts recording on all remotes\n \ntime.sleep(20)                                              # Do something else in between or absolutely nothing\n \nconductor.stop_acquisition()                                # Stops recording on all remotes\n \n```\nor on the commandline:\n```shell\nrcc-conductor  --config-data-file CONFIG_DATA_FILE  --acquisition-name ACQUISITION_NAME\n```\n\n\n\n## Features\n#### A centralised control object\nOne central object handles all communication with the remote cameras and transmits the configuration settings to each.\n\n#### A single configuration file to define reproducible multi-camera acquisition\nConfiguration parameters are centrally defined in an easy-to-read file format and then handed down to the cameras.\n\n#### Flexible entrypoints\nMultiple entrypoints for use in python scripts as well as in a single line on the commandline\nAdditionally, all levels are directly accessible: central Conductor, remote control handlers, and on the RPi the acquisition control (see below for details).\n\n#### *NEW:* Network video stream\nAdd an additional output via network video stream directly from the main `config` or via commandline arguments when calling `rcc-acquisition`.\n\n- `config` example to use with `rcc-conductor` as usual:\n```shell\n# ...\n\n[controllers]\n    [[camera_red_60]]\n        description = \"back view\"\n        address = \"192.168.0.22\"\n\n        # Network stream setup:\n        stream_video = True\n        stream_address = \"192.168.0.22\"\n        stream_port = 8001\n\n# ...\n```\n\n- Command-line entrypoint example:\n  - Options:\n  ````shell\n  -s, --stream-video\n  -sip STREAM_IP, --stream-ip STREAM_IP\n                        IP address for video stream. (default: 192.168.100.31)\n  -sport STREAM_PORT, --stream-port STREAM_PORT\n                        Stream port (default: 8001)\n  ````\n\n  - Example call:\n  ```shell\n  rcc-acquisition --auto-start --stream-video --stream-ip 192.168.100.31 --stream-port 9898\n  ```\n\n\n## Installation\n\n### Python dependencies\n- python `\u003e= 3.6`\n- pyzmq\n- configobj\n- tqdm\n- numpy\n- pandas\n\n\n    Note: Use conda to install numpy/pandas to get pre-compiled packages\n    (See below for instructions)\n\n\n\n**On RPi only:**\n- picamera\n- RPi.GPIO\n\n### Other useful packages\n**For video conversion:**\n- gpac  # contains MP4Box tool for video conversion\n\n\n\n### Example hardware architecture\n\n```text\n  [outside world / internet]\n              |\n              |\n      [central machine]\n              |\n              |\n      [network switch]\n      /   |     |    \\\n     |    |     |     | \u003c- network connection\n[rpi #1]  |     |     |     e.g. ethernet cables\n          |     |     |\n     [rpi #2]   |     |\n                |     |\n              [...]   |\n                      |\n                 [rpi #n]\n```\n\n\n\n#### Minimal hardware requirements\n- Central machine, can be RPi itself (as it only holds the control object, but does no computation)\n- Raspberry Pi\n  1. Main RPi board + fast SD card (+ card reader if not available on another machine)\n  2. RPi Camera (+lens?) (depends on your specific acquisition requirements)\n  3. RPi power supply (RPi4 requires USB-C connector)\n  4. Display cable (RPi4 requires mico-HDMI connector)\n- Ethernet cables\n- Network switch (if more than one RPi), e.g. any 1GB or faster\n\n\n\n### Mapping between this package \u0026 hardware\n**One** Conductor to instruct **all** RPi cameras via network communication between the RemoteAcquisitionControl and PiAcquisitionControl.\n```text\n\n    Hardware            \u003c--\u003e        Software\n\n\n    [central machine]   \u003c--\u003e        Conductor\n            |                           |\n            |                       RemoteAcquisitionControl\n            |                           |\n           ...                         ...\n            |                           |\n       [rpi #n]         \u003c--\u003e        PiAcquisitionControl\n                                        |\n                                    Camera\n\n```\n\n\n\n### Raspberry Pi setup\n0. Set up RPi hardware\n    1. Install [Raspbian] -\u003e `NOTE: Use Raspbian Buster for now. There is no PiCamera equivalent readily available for the Raspbian Bullseye libcamera apps.`\n    2. Enable camera, GPIO interfaces, and ssh in `sudo raspi-config` options\n    3. Connect hardware:\n       1. Camera\n       2. Network cable\n       3. GPIO pin connection for TTL in/out (See [pinout.xyz] for **board mode** pins to use)\n\n                Note: adjust pin numbers used in configuration file. Default are pin #8 for frame TTL outputs and #16 for inputs. Choose any free ground pins!\n\n1. Install this package\n    1. Set up python, e.g. with [miniconda]\n    2. Clone this repository or use `distribute_code.sh` script (Replace hostnames for your RPi)\n    3. Install\n      a. From Pypi\n        ```shell\n        pip install rpi_camera_colony[rpi]  # \u003c- Note: `[rpi]` argument adds specific requirements for acquisition on RPi, but is not required for controller\n        ```\n      b. From Github\n        ```shell\n        pip install https://github.com/larsrollik/rpi_camera_colony[rpi]  # \u003c- Note: `[rpi]` argument adds specific requirements for acquisition on RPi, but is not required for controller\n        ```\n\n\n\n### Central control machine setup\n0. DHCP server on central computer. (Description only for Ubuntu)\n    1. Set up static IP address on network interface that serves RPi colony via network switch, with e.g. `/etc/network/interfaces` or `netplan`\n    2. Set up DHCP server with [isc-dhcp-server]\n    3. Set up SSH keys to allow interaction with RPi without password (__otherwise cannot drop remote process!__)\n\n            ssh-keygen  # into standard file if not exists, no passphrase\n            ssh-copy-id -i ~/.ssh/id_rsa HOST  # where HOST = RPi host name\n\n1. Set up python environment, e.g. with [miniconda]\n2. Install this package\n    1. Clone this repository\n    2. Install with\n        ```shell\n        pip install rpi_camera_colony\n        ```\n\n\n\n## Entrypoints \u0026 levels\n\n### Easy access to central Conductor\n```shell\nrcc-conductor --help\n```\n\n\n\n### Use acquisition directly on RPi\n```python\nfrom rpi_camera_colony.acquisition.acquisition_control import PiAcquisitionControl\n```\nor\n```shell\npython rpi_camera_colony/acquisition --help\n# or\npython -m rpi_camera_colony.acquisition --help\n# or\nrcc-acquisition --help\n```\n\n\n\n### One-to-one mapping of local control to remote acquisition\n```python\nfrom rpi_camera_colony.acquisition.remote_control import RemoteAcquisitionControl\n```\nor\n```bash\npython rpi_camera_colony/acquisition --help\n# or\npython -m rpi_camera_colony.acquisition.remote_control --help\n```\n\n\n### Read acquisition metadata \u0026 check for video files\n```python\nfrom rpi_camera_colony import read_session_data\n```\n\n\n### Sandbox Conductor object in separate process (python multiprocessing)\nSee `rpi_camera_colony.control.process_sandbox` for example use of:\n```python\nfrom rpi_camera_colony.control.process_sandbox import ConductorAsProcess\n```\n\n\n\n## Citation\n\n\u003e Rollik, Lars B. (2021). RPi Camera Colony: Central control for video acquisition with (many) Raspberry Pi cameras. doi: [10.5281/zenodo.6414747](https://doi.org/10.5281/zenodo.6414747).\n\n**BibTeX**\n```BibTeX\n@misc{rollik2021rpi,\n    author       = {Lars B. Rollik},\n    title        = {{RPi Camera Colony: Central control for video acquisition with (many) Raspberry Pi cameras}},\n    year         = {2021},\n    month        = jun,\n    publisher    = {Zenodo},\n    url          = {https://doi.org/10.5281/zenodo.6414747},\n    doi          = {10.5281/zenodo.6414747},\n  }\n```\n\n\n## License\nThis software is released under the **[BSD 3-Clause License](https://github.com/larsrollik/rpi_camera_colony/blob/master/LICENSE)**\n\n\n\n## Related projects with similar architectures\n- [Arne Meyer's RPiCameraPlugin for the OpenEphys GUI][arne-plugin]\n\n  Specific API for one-to-one control mappings between OpenEphys GUI plugin instances and remote RPi cameras. Inspiration for use of ØMQ communication and camera TTL integration in encoder class.\n\n\n- [Deshmukh lab's PicameraPaper][deshmukh]\n\n    Video acquisition with multiple RPi synchronised by a central TTL that is recorded with the camera timestamps.\n\n\n- [Vidgear]\n\n    General package for different types of video acquisition and streaming.\n\n\n\n## Configuration file specification\n\n**-\u003e Note:** additional picamera attributes can be used, but not all types implemented. Check below.\n\n**-\u003e Note:** `acquisition_group` is not specified by default, but if `acquisition_name` contains `__` double underscores, then the `acquisition_group` will get auto-populated from the first segment, when split on the `__`. This is to create an acquisition folder organisation like: `/path_to_data/acquisition_group/acquisition_name/[files]`\n\n```shell\n\n[general]\n    acquisition_name = string(default=\"_test_rcc_name_config\")    # base name for recording\n    acquisition_time = string(default=\"dummy_time\")\n    acquisition_group = string(default=\"\")\n    remote_data_path = string(default=\"/home/pi/data/\")             # where to store all recordings on RPi\n    rpi_username = string(default=\"pi\")\n    remote_python_interpreter = string(default=\"/home/pi/miniconda3/envs/py36/bin/python\")      # path to python\n    remote_python_entrypoint = string(default=\"rpi_camera_colony.acquisition\")      # path to __main__ entrypoint\n    max_acquisition_time = integer(0, 7200, default=7200)           # seconds, shut down acquisition after expiration\n    save_data = boolean(default=True)  # if False, then doesn't write files on RPi\n    general_setting_has_priority = boolean(default=True)  # If False, does not patch in general settings\n    general_settings_to_patch_into_controller = string_list(default=list(\"save_data\", \"acquisition_time\", \"acquisition_group\"))  # Add variables here for patching into controllers\n\n[log]\n    address = string(max=15, default=\"192.168.100.10\")\n    port = integer(default=55555)\n    level = string(default=\"DEBUG\")\n    log_to_console = boolean(default=True)\n    log_to_file = boolean(default=True)\n    log_file = string(default=\"/tmp/rpi_camera_colony__logging\")\n\n[control]\n    address = string(default=\"192.168.100.10\")\n    port = integer(default=54545)\n\n[controllers]\n    [[__many__]]\n        description = string(default=\"\")\n        address = string(max=15, default=\"\")\n        save_data = boolean(default=True)\n        ttl_channel_external = integer(default=-1)  # metadata info if recording output TTL on specific channel of other acquisition system\n        ttl_in_pin = integer(default=16)\n        ttl_out_pin = integer(default=8)\n        ttl_out_duration = float(default=.001)\n\n        # See for list of ALL parameters https://picamera.readthedocs.io/en/latest/api_camera.html\n        framerate = integer(min=1, max=90, default=90)\n        resolution = int_list(default=list(640, 480))\n        vflip = boolean(default=False)\n        hflip = boolean(default=False)\n\n        brightness = integer(min=0, max=100, default=50)\n        # color_effects: (128, 128) == black and white acquisition. Default is None.\n        color_effects = int_list(default=list(128, 128))\n        contrast = integer(min=-100, max=100, default=0)\n        image_denoise = boolean(default=True)\n        iso = integer(min=0, max=1600, default=0)\n        led = boolean(default=False)\n        preview_alpha = integer(min=0, max=255, default=255)  # DEPRECATED\n        saturation = integer(min=-100, max=100, default=0)\n        sharpness = integer(min=-100, max=100, default=0)\n        still_stats = boolean(default=False)\n        video_denoise = boolean(default=True)\n        video_stabilization = boolean(default=False)\n        # zoom: (x, y, w, h)\n        zoom = float_list(default=list(0.0, 0.0, 1.0, 1.0))\n\n        # Other picamera attributes / not implemented / not tested, but might work\n        # awb_gains\n        # awb_mode = option(default=\"auto\")\n        # drc_strength\n        # exposure_compensation\n        # exposure_mode\n        # exposure_speed = 0\n        # flash_mode = option(\"off\", \"auto\", \"on\", \"redeye\", \"fillin\", \"torch\", default=\"off\")\n        # framerate_delta  # new in 1.11\n        # framerate_range  # new in 1.13\n        # image_effect = \"none\"\n        # image_effect_params  # https://picamera.readthedocs.io/en/release-1.13/api_camera.html#picamera.PiCamera.image_effect_params\n        # meter_mode = option(\"average\", \"spot\", \"backlit\", \"matrix\")\n        # rotation = option(0, 90, 180, 270)\n        # sensor_mode = integer(default=0)\n        # shutter_speed  # microseconds\n\n\n```\n\n\n\n## Specific install hints \u0026 solutions\n\n### HQ camera for RPi cannot acquire at resolutions or framerates outlined in the technical description\n- `sudo rpi-update` fixes this. - Be careful, this updates the RPi firmware and might have unexpected side effects!\n\n### There is more than one TTL-in detected for long (\u003e50ms) up/down states of the TTL source\n- add a `bouncetime` parameter to the event detection in [camera.py#L196](https://github.com/larsrollik/rpi_camera_colony/blob/master/rpi_camera_colony/acquisition/camera.py#L196)\n- remove surplus TTL-in post-hoc based on the known minimum spacing of TTL\n\n### Ip forwarding and routing on central machine\n\n```bash\n# IP forward\nsysctl -w net.ipv4.ip_forward=1\n# check with\ncat /proc/sys/net/ipv4/ip_forward\n\n# Package routing\n# - outside interface (dhcp): enp7s0\n# - inside interface (static): enp8s0\niptables -A FORWARD -i enp8s0 -o enp7s0 -j ACCEPT\niptables -A FORWARD -i enp7s0 -o enp8s0 -m state --state ESTABLISHED,RELATED -j ACCEPT\niptables -t nat -A POSTROUTING -o enp7s0 -j MASQUERADE\n\n```\n\n### Update time for ssl certificates\n```bash\n# Check with\ntimedatectl status\n\n# force update with NTP\nsudo service ntp stop\nsudo ntpd -gq\nsudo service ntp start\n\n# enable permanent updates\nsudo systemctl restart systemd-timesyncd\n\n```\n\n\n### Install miniconda on RPi\n```bash\n# Installing miniconda on RPi\nwget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-armv7l.sh\nsudo md5sum Miniconda3-latest-Linux-armv7l.sh # (optional) check md5\nbash Miniconda3-latest-Linux-armv7l.sh # -\u003e default directory should be: /home/pi/miniconda3\n\n# Add conda to path\necho 'export PATH=\"/home/pi/miniconda3/bin:$PATH\"' \u003e\u003e .bashrc\nsource .bashrc # or re-connect\n\n# Create conda environment and install basic packages (e.g. dependencies for this package)\nconda config --add channels rpi\nconda create -y -n py36 python=3.6 numpy pandas pyzmq\n\necho 'source activate py36' \u003e\u003e .bashrc\nsource .bashrc # or re-connect\n\n# Re/install RCC with `[rpi]` option to install picamera and GPIO packages on RPi.\npip uninstall rpi_camera_colony -y\npip install --upgrade rpi_camera_colony[rpi]\n\n```\n\n---\nVersion: \"0.5.0\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flarsrollik%2Frpi_camera_colony","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flarsrollik%2Frpi_camera_colony","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flarsrollik%2Frpi_camera_colony/lists"}