Skip to content

Commit

Permalink
Merge pull request #47 from phamthanhnam/master
Browse files Browse the repository at this point in the history
Add support for Picamera2 (default on Raspberry Pi OS bullseye)
  • Loading branch information
dkumor authored Jul 24, 2023
2 parents 33407fd + 144f41c commit ad7f6bb
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 1 deletion.
24 changes: 24 additions & 0 deletions docs/camera.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,30 @@ above, and it returns exactly the same data for the frame::

This means that if not using CVDisplay, you don't even need OpenCV installed to stream from you raspberry pi.

PiCamera2
++++++++++++++++

This allows you to use the official raspberry pi camera, with libcamera stack (legacy camera interface disabled).
This is default since Raspberry Pi OS bullseye, PiCamera2 also works with 64-bit OS.
You can use the parameter hflip=1 to flip the camera horizontally, vflip=1 to flip vertically, or both to rotate 180 degrees.
You can use it in exactly the same way as the OpenCV camera above, and it returns exactly the same data for the frame::

import asyncio
from rtcbot import PiCamera2, CVDisplay

camera = PiCamera2()
display = CVDisplay()

display.putSubscription(camera)

try:
asyncio.get_event_loop().run_forever()
finally:
camera.close()
display.close()

This means that if not using CVDisplay, you don't even need OpenCV installed to stream from you raspberry pi.

API
++++++++++++++++

Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"aiortc": ("https://aiortc.readthedocs.io/en/latest/", None),
"inputs": ("https://inputs.readthedocs.io/en/latest/", None),
"picamera": ("https://picamera.readthedocs.io/en/latest/", None),
"picamera2": ("https://datasheets.raspberrypi.com/camera/picamera2-manual.pdf", None),
}

# Add any paths that contain templates here, relative to this directory.
Expand Down
2 changes: 1 addition & 1 deletion rtcbot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .base import SubscriptionClosed
from .connection import RTCConnection
from .websocket import Websocket
from .camera import CVCamera, PiCamera, CVDisplay
from .camera import CVCamera, PiCamera, PiCamera2, CVDisplay
from .audio import Microphone, Speaker
from .inputs import Gamepad, Mouse, Keyboard
from .arduino import SerialConnection
Expand Down
68 changes: 68 additions & 0 deletions rtcbot/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,18 @@ class PiCamera(CVCamera):

_log = logging.getLogger("rtcbot.PiCamera")

# Valid values for rotation are 0, 90, 180, 270
def __init__(self, rotation=0, **kwargs):
super().__init__(**kwargs)
self._rotation = rotation

def _producer(self):
import picamera

with picamera.PiCamera() as cam:
cam.resolution = (self._width, self._height)
cam.framerate = self._fps
cam.rotation = self._rotation
time.sleep(2) # Why is this needed?
self._log.debug("PiCamera Ready")
self._setReady(True)
Expand Down Expand Up @@ -167,6 +173,68 @@ def _producer(self):
self._log.info("Closed camera capture")


class PiCamera2(CVCamera):
"""
Instead of using OpenCV camera support, uses the picamera2 library for direct access to the Raspberry Pi's CSI camera.
The interface is identical to CVCamera. When testing code on a desktop computer, it can be useful to
have the code automatically choose the correct camera::
try:
import picamera2 # picamera2 import will fail if not on pi
cam = PiCamera2()
except ImportError:
cam = CVCamera()
This enables simple drop-in replacement between the two.
You can use the parameter hflip=True to flip the camera horizontally, vflip=True to flip vertically,
or both to rotate 180 degrees.
"""

_log = logging.getLogger("rtcbot.PiCamera2")

def __init__(self, hflip=False, vflip=False, **kwargs):
super().__init__(**kwargs)
self._hflip = hflip
self._vflip = vflip

def _producer(self):
from picamera2 import Picamera2
from libcamera import Transform

with Picamera2() as cam:
cam.preview_configuration.transform = Transform(hflip=self._hflip, vflip=self._vflip)
cam.preview_configuration.main.size = (self._width, self._height)
cam.preview_configuration.display = None
cam.preview_configuration.main.format = 'RGB888'
if self._fps > 0:
cam.video_configuration.controls.FrameDurationLimits = (round(1000000/self._fps), round(1000000/self._fps))
cam.start()
time.sleep(2)
self._log.debug("PiCamera2 Ready")
self._setReady(True)

t = time.time()
i = 0
while not self._shouldClose:
frame = cam.capture_array()

# This optional function is given by the user. default is identity x->x
frame = self._processframe(frame)

# Set the frame arrival event
self._loop.call_soon_threadsafe(self._put_nowait, frame)

i += 1
if time.time() > t + 1:
self._log.debug(" %d fps", i)
i = 0
t = time.time()
self._setReady(False)
self._log.info("Closed camera capture")


class CVDisplay(BaseSubscriptionConsumer):
"""
Displays the frames in an openCV `imshow` window
Expand Down

0 comments on commit ad7f6bb

Please sign in to comment.