Skip to content

Commit

Permalink
camera config classes (#73)
Browse files Browse the repository at this point in the history
* camera config classes

* camera config classes updated

* updated camera_comfig

* updated camera config

* updated config

* updated camera config

* styling errors fix

* styling fixes

* fixed errors

* styling issues

* fixed styling

* fixed styling:

* fixed styling
  • Loading branch information
joeyhlu authored Dec 4, 2024
1 parent c82c31b commit 1c31b5c
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 13 deletions.
7 changes: 6 additions & 1 deletion modules/camera/base_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import numpy as np

from . import camera_configurations


class BaseCameraDevice(abc.ABC):
"""
Expand All @@ -15,7 +17,10 @@ class BaseCameraDevice(abc.ABC):
@classmethod
@abc.abstractmethod
def create(
cls, width: int, height: int
cls,
width: int,
height: int,
config: camera_configurations.PiCameraConfig | camera_configurations.OpenCVCameraConfig,
) -> "tuple[True, BaseCameraDevice] | tuple[False, None]":
"""
Abstract create method.
Expand Down
62 changes: 62 additions & 0 deletions modules/camera/camera_configurations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""
Camera configuration
"""

try:
from libcamera import controls # This is a pre-installed library on the Rpi5
except ImportError:
pass


class PiCameraConfig:
"""
Configuration for the PiCamera.
This class allows specifying parameters such as exposure time, gain, and contrast.
"""

def __init__(
self,
exposure_time: int = 250,
analogue_gain: float = 64.0,
contrast: float = 1.0,
lens_position: float = None,
) -> None:
"""
Args:
exposure_time (int)
analogue_gain (float)
contrast (float)
lens_position (float)
"""
self.exposure_time = exposure_time
self.analogue_gain = analogue_gain
self.contrast = contrast
self.lens_position = lens_position

def to_dict(self) -> dict[str, int | float | None]:
"""
Dictionary containing camera controls.
"""
camera_controls: dict[str, int | float] = {}
if self.exposure_time is not None:
camera_controls["ExposureTime"] = self.exposure_time
if self.analogue_gain is not None:
camera_controls["AnalogueGain"] = self.analogue_gain
if self.contrast is not None:
camera_controls["Contrast"] = self.contrast
if self.lens_position is not None:
camera_controls["LensPosition"] = self.lens_position
camera_controls["AfMode"] = controls.AfModeEnum.Manual
else:
camera_controls["LensPosition"] = 0.0
camera_controls["AfMode"] = controls.AfModeEnum.Auto

return camera_controls


class OpenCVCameraConfig:
"""
Placeholder
"""

pass # pylint: disable=unnecessary-pass
10 changes: 7 additions & 3 deletions modules/camera/camera_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import enum

from . import base_camera
from . import camera_configurations
from . import camera_opencv
from . import camera_picamera2

Expand All @@ -19,7 +20,10 @@ class CameraOption(enum.Enum):


def create_camera(
camera_option: CameraOption, width: int, height: int
camera_option: CameraOption,
width: int,
height: int,
config: camera_configurations.PiCameraConfig | camera_configurations.OpenCVCameraConfig,
) -> tuple[True, base_camera.BaseCameraDevice] | tuple[False, None]:
"""
Create a camera object based off of given parameters.
Expand All @@ -28,8 +32,8 @@ def create_camera(
"""
match camera_option:
case CameraOption.OPENCV:
return camera_opencv.CameraOpenCV.create(width, height)
return camera_opencv.CameraOpenCV.create(width, height, config)
case CameraOption.PICAM2:
return camera_picamera2.CameraPiCamera2.create(width, height)
return camera_picamera2.CameraPiCamera2.create(width, height, config)

return False, None
9 changes: 8 additions & 1 deletion modules/camera/camera_opencv.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import numpy as np

from . import base_camera
from . import camera_configurations


class CameraOpenCV(base_camera.BaseCameraDevice):
Expand All @@ -16,15 +17,21 @@ class CameraOpenCV(base_camera.BaseCameraDevice):
__create_key = object()

@classmethod
def create(cls, width: int, height: int) -> "tuple[True, CameraOpenCV] | tuple[False, None]":
def create(
cls, width: int, height: int, config: camera_configurations.OpenCVCameraConfig = None
) -> "tuple[True, CameraOpenCV] | tuple[False, None]":
"""
OpenCV Camera.
width: Width of the camera.
height: Height of the camera.
config (OpenCVCameraConfig, optional): Configuration of the camera.
Return: Success, camera object.
"""

# TODO: apply camera configs to camera here
_ = config # placeholder
camera = cv2.VideoCapture(0)
if not camera.isOpened():
return False, None
Expand Down
15 changes: 11 additions & 4 deletions modules/camera/camera_picamera2.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
pass

from . import base_camera
from . import camera_configurations


# TODO: pass in as constructor parameter
CAMERA_TIMEOUT = 1
Expand All @@ -24,23 +26,28 @@ class CameraPiCamera2(base_camera.BaseCameraDevice):
__create_key = object()

@classmethod
def create(cls, width: int, height: int) -> "tuple[True, CameraPiCamera2] | tuple[False, None]":
def create(
cls, width: int, height: int, config: camera_configurations.PiCameraConfig = None
) -> "tuple[True, CameraPiCamera2] | tuple[False, None]":
"""
Picamera2 Camera.
width: Width of the camera.
height: Height of the camera.
config (PiCameraConfig): Configuration object
Return: Success, camera object.
"""
try:
camera = picamera2.Picamera2()

config = camera.create_still_configuration(
camera_config = camera.create_preview_configuration(
{"size": (width, height), "format": "RGB888"}
)
camera.configure(config)
camera.configure(camera_config)
camera.start()
if config:
controls = config.to_dict()
camera.set_controls(controls)
return True, CameraPiCamera2(cls.__create_key, camera)
except RuntimeError:
return False, None
Expand Down
8 changes: 7 additions & 1 deletion tests/integration/test_camera_opencv.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import cv2

from modules.camera import camera_configurations
from modules.camera import camera_factory


Expand All @@ -17,7 +18,12 @@ def main() -> int:
"""
Main function.
"""
result, device = camera_factory.create_camera(camera_factory.CameraOption.OPENCV, 640, 480)
config = camera_configurations.OpenCVCameraConfig()
assert config is not None

result, device = camera_factory.create_camera(
camera_factory.CameraOption.OPENCV, 640, 480, config=config
)
if not result:
print("OpenCV camera creation error.")
return -1
Expand Down
16 changes: 14 additions & 2 deletions tests/integration/test_camera_picamera2.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""
Test Picamera2 camera physically.
Test Picamera2 camera physically and verifies configuration.
"""

import pathlib

import cv2

from modules.camera import camera_configurations
from modules.camera import camera_factory


Expand All @@ -17,7 +18,18 @@ def main() -> int:
"""
Main function.
"""
result, device = camera_factory.create_camera(camera_factory.CameraOption.PICAM2, 640, 480)

config = camera_configurations.PiCameraConfig(
exposure_time=250, contrast=1.0, analogue_gain=64.0
)
assert config.exposure_time == 250
assert config.contrast == 1.0
assert config.analogue_gain == 64.0
assert config.lens_position is None

result, device = camera_factory.create_camera(
camera_factory.CameraOption.PICAM2, 640, 480, config
)
if not result:
print("Picamera2 camera creation error.")
return -1
Expand Down
6 changes: 5 additions & 1 deletion tests/integration/test_camera_qr_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import cv2

from modules.camera import camera_configurations
from modules.camera import camera_factory
from modules.qr import qr_scanner

Expand All @@ -12,7 +13,10 @@ def main() -> int:
"""
Main function.
"""
result, camera = camera_factory.create_camera(camera_factory.CameraOption.OPENCV, 640, 480)
config = camera_configurations.OpenCVCameraConfig()
result, camera = camera_factory.create_camera(
camera_factory.CameraOption.OPENCV, 640, 480, config=config
)
if not result:
print("OpenCV camera creation error.")
return -1
Expand Down

0 comments on commit 1c31b5c

Please sign in to comment.