{"id":15161671,"url":"https://github.com/jsonfm/remio","last_synced_at":"2026-01-21T15:33:00.434Z","repository":{"id":45899417,"uuid":"474790335","full_name":"jsonfm/remio","owner":"jsonfm","description":"🔥 A library for managing concurrent socketio, cv2, and pyserial processes. Useful for making robots or devices with Arduinos and Raspberry Pi.  💻","archived":false,"fork":false,"pushed_at":"2022-08-11T19:38:35.000Z","size":1307,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-07T23:16:52.842Z","etag":null,"topics":["arduino","mjpeg","pyserial","python","raspberry-pi","socket-io"],"latest_commit_sha":null,"homepage":"https://jsonfm.github.io/remio/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jsonfm.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-03-28T00:03:24.000Z","updated_at":"2022-08-28T21:46:02.000Z","dependencies_parsed_at":"2022-08-12T12:21:31.397Z","dependency_job_id":null,"html_url":"https://github.com/jsonfm/remio","commit_stats":null,"previous_names":["hikki12/remio"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/jsonfm/remio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonfm%2Fremio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonfm%2Fremio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonfm%2Fremio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonfm%2Fremio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsonfm","download_url":"https://codeload.github.com/jsonfm/remio/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsonfm%2Fremio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28635890,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T15:01:31.228Z","status":"ssl_error","status_checked_at":"2026-01-21T14:42:58.942Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["arduino","mjpeg","pyserial","python","raspberry-pi","socket-io"],"created_at":"2024-09-27T00:42:47.230Z","updated_at":"2026-01-21T15:33:00.403Z","avatar_url":"https://github.com/jsonfm.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\u003ch1\u003e REMIO \u003c/h1\u003e\u003c/div\u003e\n\u003cdiv align=\"center\"\u003e\n\n[Documentation][docs] \u0026nbsp;\u0026nbsp;\u0026nbsp;|\u0026nbsp;\u0026nbsp;\u0026nbsp; [License](#copyright)\n\n[![Code Style][black-badge]][black]\n[![Codecov branch][codecov]][codecov-repo]\n[![PyPi version][pypi-badge]][pypi] \n\u003c/div\u003e\n\n# Table of Contents\n1. [Introduction](#introduction)\n2. [Features](#features)\n3. [Installation](#installation)\n4. [Development](#development)\n5. [Simplejpeg API](#simplejpeg-api)\n6. [Simple MJPEG Server](#simple-mjpeg-server)\n7. [Single Camera API (Multithread)](#single-camera-api)\n8. [Single Camera API (not Multithread)](#single-camera-api-not-multithread)\n8. [Background Processing API](#background-processing-api)\n7. [Multiple Cameras API](#multiple-cameras-api)\n8. [Multiple Serial API](#multiple-serial-api)\n9. [Examples](#examples)\n\n## Introduction\nREMIO is a library for managing concurrent socketio, cv2, and pyserial processes. Useful for making robots or devices with Arduinos and Raspberry Pi. It was born in the context of remote laboratories, hence its name, where I used and developed several prototypes where the code began to redound. That's where I extracted the modules from this library. The hardware architecture that I used to employ was the following:\n\n\u003cimg src=\"./docs/assets/images/arch-1.png\" style=\"margin: 1rem 0;\"\u003e\n\nSo I programmed the following architecture\n\u003cimg src=\"./docs/assets/images/modules-arch.png\" style=\"margin: 2rem 0;\"\u003e\n\n## Features\n- Multiple Camera API\n- Multiple Serial API\n- Event-driven programming API for Serial.\n- Event-driven programming API for Cameras.\n- MJPEG streamer with SocketIO\n\n\u003c!-- ----------------------------------------- --\u003e\n\n## Installation\n\nFirst you need to create a virtualenv:\n```\npython3 -m venv venv\n```\nThen you should active it:\n```\nsource venv/bin/activate\n```\nAfter choose an option for install remio, for example using pip:\n```\n# Pypi source\npip install remio\n\n# Github source\npip install \"git+https://github.com/Hikki12/remio\"\n```\nOr if you prefer, clone the repository:\n```\ngit clone https://github.com/Hikki12/remio\n\ncd remio\n\npip install .\n```\n\n\u003c!-- ----------------------------------------- --\u003e\n\n## Development\nIf you are a devolper, install the library as follows:\n```\npip install -e .\n```\n\n\u003c!-- ----------------------------------------- --\u003e\n## Single Camera API\nA `Camera` instance creates a background loop for reading and processing frames when you call `start()` method.\n\n```py\nimport time\nfrom remio import Camera\n\n# Initialize Single Camera device\ncamera = Camera(name=\"webcam\", src=0, size=[400, 400])\ncamera.start()\n\n# Also you could use\n# camera = Camera(name=\"webcam\", src=0, size=[400, 400]).start()\n\n\nwhile True:\n    print(\"Doing some other tasks...\")\n    time.sleep(2)\n```\n\n## Single Camera API not Multithread\nBut If you prefer you could manage reading and procesing on main thread, for that, you must change `start()` method by `loadDevice()`.\n```py\nimport time\nfrom remio import Camera\n\n# Initialize Single Camera device\ncamera = Camera(name=\"webcam\", src=0, size=[400, 400])\n\n# Loads camera device but doesn't start background read loop\ncamera.loadDevice()\n\nwhile True:\n    # Now the processing ocurrs on the main thread.\n    frame = camera.read()\n    if frame is not None:\n        print(\"A frame is available!\")\n    time.sleep(1 /10)\n\n```\n\n\u003c!-- ----------------------------------------- --\u003e\n## Background Processing API\nYou could set a processing function which will run on background. Use `setProcessing()` method for that.\n```py\n\nimport time\nimport cv2\nfrom remio import Camera\n\n\ndef processing(frame, *args, **kwargs):\n    \"\"\"Applies some image processing.\n\n    Args:\n        frame: a numpy array\n    \"\"\"\n\n    if 'color' in kwargs: # if you pass a param named `color` it will print it\n        print(kwargs['color'])\n\n    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n    return frame\n\n\n# Intialize Camera manager\ncamera = Camera(src=0, size=[600, 400])\n\n# set processing function passing to it some params\ncamera.setProcessing(processing, color='red')\n\n# Also you could do\n# camera.setProcessing(processing)\n\n# Start device(s) read loop on background\ncamera.start()\n\n# Set a FPS speed to display image(s)\nFPS = 20\nT = 1 / FPS # Sampling period\n\n\nwhile True:\n\n    t0 = time.time()\n\n    frame = camera.read()\n    camera.clearFrame()  # to avoid repeated frames, this line it's optional\n\n    if frame is not None:\n        cv2.imshow(\"webcam1\", frame)\n\n    if cv2.waitKey(1) \u0026 0xFF == ord(\"q\"):\n        break\n\n    t1 = time.time()\n\n    readtime = t1 - t0 # reading and display time\n\n    # Get a fixed delay value, where sampling period should be T = readtime + delay\n    delay = abs(T - readtime)\n    time.sleep(delay)\n\n\n# Close all Windows\ncv2.destroyAllWindows()\n\n# Stop all Running devices\ncamera.stop()\n\n\n```\n\n\u003c!-- ----------------------------------------- --\u003e\n\n## Multiple Cameras API\n```python\nimport time\nimport cv2\nfrom remio import Cameras\n\n\n# Define devices\ndevices = {\n    \"webcam1\": {\n        \"src\": 0,\n        \"size\": [400, 300],\n        \"fps\": None,\n        \"reconnectDelay\": 5,\n        \"backgroundIsEnabled\": True,\n        \"emitterIsEnabled\": False,\n    },\n    \"webcam2\": {\n        \"src\": \"http://192.168.100.70:3000/video/mjpeg\",\n        \"size\": [400, 300],\n        \"fps\": None,\n        \"reconnectDelay\": 5,\n        \"backgroundIsEnabled\": True,\n        \"emitterIsEnabled\": False,\n    },\n}\n\n# Intialize Serial manager\ncamera = Cameras(devices=devices)\n\n# Start device(s) connection on background\ncamera.startAll()\n\n# Set a FPS speed to display image(s)\nFPS = 20\nT = 1 / FPS\n\nwhile True:\n\n    t0 = time.time()\n\n    webcam1, webcam2 = camera.read(asDict=False)\n    camera.clearAllFrames()  # to avoid repeated frames\n\n    if webcam1 is not None:\n        cv2.imshow(\"webcam1\", webcam1)\n\n    if webcam2 is not None:\n        cv2.imshow(\"webcam2\", webcam2)\n\n    if cv2.waitKey(1) \u0026 0xFF == ord(\"q\"):\n        break\n\n    t1 = time.time()\n\n    # Get a fixed delay value (t1 - t0) + delay = T\n    delay = abs(T - (t1 - t0))\n    time.sleep(delay)\n\n\n# Close all Windows\ncv2.destroyAllWindows()\n\n# Stop all Running devices\ncamera.stopAll()\n\n```\n\u003c!-- ----------------------------------------- --\u003e\n\n## Multiple Serial API\n```python\n\"\"\"Multiple serial devices management.\"\"\"\nimport time\nfrom remio import Serials\n\n\n# Define devices\ndevices = {\n    \"arduino1\": {\n        \"port\": \"/dev/cu.usbserial-1440\",\n        \"baudrate\": 9600,\n        \"emitterIsEnabled\": True,  # Enable on/emit callbacks\n        \"reconnectDelay\": 5,\n    },\n    \"arduino2\": {\n        \"port\": \"COM2\",\n        \"baudrate\": 9600,\n        \"emitterIsEnabled\": True,\n        \"reconnectDelay\": 5,\n    },\n}\n\n# Intialize Serial manager\nserial = Serials(devices=devices)\n\n# Configure callbacks\nserial.on(\"connection\", lambda status: print(f\"serial connected: {status}\"))\n\n# Start device(s) connection on background\nserial.startAll()\n\n\nwhile True:\n    print(\"Doing some tasks...\")\n    time.sleep(1)\n\n```\n\u003c!-- ----------------------------------------- --\u003e\n\n## Simplejpeg API\nREMIO uses [simplejpeg](https://gitlab.com/jfolz/simplejpeg) library for encode camera images. You could used its API as follows:\n```python\nimport time\nfrom remio import Camera\n\n# Initialize camera device\ncamera = Camera(src=0, fps=15, size=[800, 600], flipX=True)\n\nwhile True:\n    jpeg = camera.jpeg()\n    time.sleep(1/10)\n```\n\u003c!-- ----------------------------------------- --\u003e\n## Simple MJPEG Server\nYou could server your camera image with the MJPEG server, with a few lines:\n```python\n\"\"\"A simple MJPEG.\"\"\"\nfrom remio import Camera, MJPEGServer\n\n\nencoderParams = {\n    \"quality\": 90,\n    \"colorspace\": \"bgr\",\n    \"colorsubsampling\": \"422\",\n    \"fastdct\": True,\n}\n\n\n# Initialize camera device\ncamera = Camera(src=0, fps=15, size=[800, 600], flipX=True, encoderParams=encoderParams)\n\n# Configure MJPEG Server\nserver = MJPEGServer(\n    camera=camera, ip=\"0.0.0.0\", port=8080, endpoint=\"/video/mjpeg\", fps=15\n)\n\ntry:\n    server.run(display_url=True, start_camera=True)\nexcept KeyboardInterrupt:\n    server.stop(stop_camera=True)\n```\n```bash\n# The video must be accessible through the generated link\n\u003e\u003e MJPEG server running on http://0.0.0.0:8080/video/mjpeg\n```\n\n\u003c!-- ----------------------------------------- --\u003e\n\n## Examples\nYou could see more examples [here](https://github.com/Hikki12/remio/tree/master/examples).\n\n\nResources\n---------\n- [Changelog](./CHANGELOG.md)\n\n## Copyright\n**Copyright © hikki12 2022** \u003cbr/\u003e\nThis library is released under the **[Apache 2.0 License][license]**.\n\n\n\u003c!--\nExternal URLs\n--\u003e\n[black]: https://github.com/psf/black\n[pypi]: https://pypi.org/project/remio/\n\n\n[docs]: https://hikki12.github.io/remio/\n[license]: https://github.com/Hikki12/remio/blob/master/LICENSE\n[codecov-repo]:https://codecov.io/gh/Hikki12/remio\n\u003c!--\nBadges\n--\u003e\n[black-badge]: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge\u0026logo=github\n[pypi-badge]: https://img.shields.io/pypi/v/remio?style=for-the-badge\u0026logo=pypi\n[codecov]: https://img.shields.io/codecov/c/gh/Hikki12/remio?logo=codecov\u0026style=for-the-badge\u0026token=RQZV5HOILN","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsonfm%2Fremio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsonfm%2Fremio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsonfm%2Fremio/lists"}