Skip to content

Commit 1c31b5c

Browse files
authored
camera config classes (#73)
* 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
1 parent c82c31b commit 1c31b5c

File tree

8 files changed

+120
-13
lines changed

8 files changed

+120
-13
lines changed

modules/camera/base_camera.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import numpy as np
88

9+
from . import camera_configurations
10+
911

1012
class BaseCameraDevice(abc.ABC):
1113
"""
@@ -15,7 +17,10 @@ class BaseCameraDevice(abc.ABC):
1517
@classmethod
1618
@abc.abstractmethod
1719
def create(
18-
cls, width: int, height: int
20+
cls,
21+
width: int,
22+
height: int,
23+
config: camera_configurations.PiCameraConfig | camera_configurations.OpenCVCameraConfig,
1924
) -> "tuple[True, BaseCameraDevice] | tuple[False, None]":
2025
"""
2126
Abstract create method.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""
2+
Camera configuration
3+
"""
4+
5+
try:
6+
from libcamera import controls # This is a pre-installed library on the Rpi5
7+
except ImportError:
8+
pass
9+
10+
11+
class PiCameraConfig:
12+
"""
13+
Configuration for the PiCamera.
14+
This class allows specifying parameters such as exposure time, gain, and contrast.
15+
"""
16+
17+
def __init__(
18+
self,
19+
exposure_time: int = 250,
20+
analogue_gain: float = 64.0,
21+
contrast: float = 1.0,
22+
lens_position: float = None,
23+
) -> None:
24+
"""
25+
Args:
26+
exposure_time (int)
27+
analogue_gain (float)
28+
contrast (float)
29+
lens_position (float)
30+
"""
31+
self.exposure_time = exposure_time
32+
self.analogue_gain = analogue_gain
33+
self.contrast = contrast
34+
self.lens_position = lens_position
35+
36+
def to_dict(self) -> dict[str, int | float | None]:
37+
"""
38+
Dictionary containing camera controls.
39+
"""
40+
camera_controls: dict[str, int | float] = {}
41+
if self.exposure_time is not None:
42+
camera_controls["ExposureTime"] = self.exposure_time
43+
if self.analogue_gain is not None:
44+
camera_controls["AnalogueGain"] = self.analogue_gain
45+
if self.contrast is not None:
46+
camera_controls["Contrast"] = self.contrast
47+
if self.lens_position is not None:
48+
camera_controls["LensPosition"] = self.lens_position
49+
camera_controls["AfMode"] = controls.AfModeEnum.Manual
50+
else:
51+
camera_controls["LensPosition"] = 0.0
52+
camera_controls["AfMode"] = controls.AfModeEnum.Auto
53+
54+
return camera_controls
55+
56+
57+
class OpenCVCameraConfig:
58+
"""
59+
Placeholder
60+
"""
61+
62+
pass # pylint: disable=unnecessary-pass

modules/camera/camera_factory.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import enum
66

77
from . import base_camera
8+
from . import camera_configurations
89
from . import camera_opencv
910
from . import camera_picamera2
1011

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

2021

2122
def create_camera(
22-
camera_option: CameraOption, width: int, height: int
23+
camera_option: CameraOption,
24+
width: int,
25+
height: int,
26+
config: camera_configurations.PiCameraConfig | camera_configurations.OpenCVCameraConfig,
2327
) -> tuple[True, base_camera.BaseCameraDevice] | tuple[False, None]:
2428
"""
2529
Create a camera object based off of given parameters.
@@ -28,8 +32,8 @@ def create_camera(
2832
"""
2933
match camera_option:
3034
case CameraOption.OPENCV:
31-
return camera_opencv.CameraOpenCV.create(width, height)
35+
return camera_opencv.CameraOpenCV.create(width, height, config)
3236
case CameraOption.PICAM2:
33-
return camera_picamera2.CameraPiCamera2.create(width, height)
37+
return camera_picamera2.CameraPiCamera2.create(width, height, config)
3438

3539
return False, None

modules/camera/camera_opencv.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numpy as np
77

88
from . import base_camera
9+
from . import camera_configurations
910

1011

1112
class CameraOpenCV(base_camera.BaseCameraDevice):
@@ -16,15 +17,21 @@ class CameraOpenCV(base_camera.BaseCameraDevice):
1617
__create_key = object()
1718

1819
@classmethod
19-
def create(cls, width: int, height: int) -> "tuple[True, CameraOpenCV] | tuple[False, None]":
20+
def create(
21+
cls, width: int, height: int, config: camera_configurations.OpenCVCameraConfig = None
22+
) -> "tuple[True, CameraOpenCV] | tuple[False, None]":
2023
"""
2124
OpenCV Camera.
2225
2326
width: Width of the camera.
2427
height: Height of the camera.
28+
config (OpenCVCameraConfig, optional): Configuration of the camera.
2529
2630
Return: Success, camera object.
2731
"""
32+
33+
# TODO: apply camera configs to camera here
34+
_ = config # placeholder
2835
camera = cv2.VideoCapture(0)
2936
if not camera.isOpened():
3037
return False, None

modules/camera/camera_picamera2.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
pass
1212

1313
from . import base_camera
14+
from . import camera_configurations
15+
1416

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

2628
@classmethod
27-
def create(cls, width: int, height: int) -> "tuple[True, CameraPiCamera2] | tuple[False, None]":
29+
def create(
30+
cls, width: int, height: int, config: camera_configurations.PiCameraConfig = None
31+
) -> "tuple[True, CameraPiCamera2] | tuple[False, None]":
2832
"""
2933
Picamera2 Camera.
3034
3135
width: Width of the camera.
3236
height: Height of the camera.
33-
37+
config (PiCameraConfig): Configuration object
3438
Return: Success, camera object.
3539
"""
3640
try:
3741
camera = picamera2.Picamera2()
3842

39-
config = camera.create_still_configuration(
43+
camera_config = camera.create_preview_configuration(
4044
{"size": (width, height), "format": "RGB888"}
4145
)
42-
camera.configure(config)
46+
camera.configure(camera_config)
4347
camera.start()
48+
if config:
49+
controls = config.to_dict()
50+
camera.set_controls(controls)
4451
return True, CameraPiCamera2(cls.__create_key, camera)
4552
except RuntimeError:
4653
return False, None

tests/integration/test_camera_opencv.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import cv2
88

9+
from modules.camera import camera_configurations
910
from modules.camera import camera_factory
1011

1112

@@ -17,7 +18,12 @@ def main() -> int:
1718
"""
1819
Main function.
1920
"""
20-
result, device = camera_factory.create_camera(camera_factory.CameraOption.OPENCV, 640, 480)
21+
config = camera_configurations.OpenCVCameraConfig()
22+
assert config is not None
23+
24+
result, device = camera_factory.create_camera(
25+
camera_factory.CameraOption.OPENCV, 640, 480, config=config
26+
)
2127
if not result:
2228
print("OpenCV camera creation error.")
2329
return -1

tests/integration/test_camera_picamera2.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"""
2-
Test Picamera2 camera physically.
2+
Test Picamera2 camera physically and verifies configuration.
33
"""
44

55
import pathlib
66

77
import cv2
88

9+
from modules.camera import camera_configurations
910
from modules.camera import camera_factory
1011

1112

@@ -17,7 +18,18 @@ def main() -> int:
1718
"""
1819
Main function.
1920
"""
20-
result, device = camera_factory.create_camera(camera_factory.CameraOption.PICAM2, 640, 480)
21+
22+
config = camera_configurations.PiCameraConfig(
23+
exposure_time=250, contrast=1.0, analogue_gain=64.0
24+
)
25+
assert config.exposure_time == 250
26+
assert config.contrast == 1.0
27+
assert config.analogue_gain == 64.0
28+
assert config.lens_position is None
29+
30+
result, device = camera_factory.create_camera(
31+
camera_factory.CameraOption.PICAM2, 640, 480, config
32+
)
2133
if not result:
2234
print("Picamera2 camera creation error.")
2335
return -1

tests/integration/test_camera_qr_example.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import cv2
66

7+
from modules.camera import camera_configurations
78
from modules.camera import camera_factory
89
from modules.qr import qr_scanner
910

@@ -12,7 +13,10 @@ def main() -> int:
1213
"""
1314
Main function.
1415
"""
15-
result, camera = camera_factory.create_camera(camera_factory.CameraOption.OPENCV, 640, 480)
16+
config = camera_configurations.OpenCVCameraConfig()
17+
result, camera = camera_factory.create_camera(
18+
camera_factory.CameraOption.OPENCV, 640, 480, config=config
19+
)
1620
if not result:
1721
print("OpenCV camera creation error.")
1822
return -1

0 commit comments

Comments
 (0)