From 563d07a58fbba6ca29e8355fa8afdeecb51ad2aa Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Mon, 12 Aug 2024 15:25:09 +0100 Subject: [PATCH] devices: imx708: Add a new IMX708 helper class This class provides a convenient helper function set enable/disable sensor HDR mode without relying on external tools. Usage example: ----- from picamera2.devices.imx708 import IMX708 from picamera2 import Picamera2 camera_num = 0 c = IMX708(camera_num) c.set_sensor_hdr_mode(True) picam2 = Picamera2(camera_num) ----- Note that after calling IMX708.set_hdr_mode(), you must re-initialise the Picamera2 instance. Signed-off-by: Naushir Patuck --- picamera2/devices/__init__.py | 1 + picamera2/devices/imx708/__init__.py | 1 + picamera2/devices/imx708/imx708.py | 64 ++++++++++++++++++++++++++++ setup.py | 3 +- tests/imx708_device.py | 33 ++++++++++++++ tests/test_list.txt | 1 + 6 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 picamera2/devices/__init__.py create mode 100644 picamera2/devices/imx708/__init__.py create mode 100644 picamera2/devices/imx708/imx708.py create mode 100755 tests/imx708_device.py diff --git a/picamera2/devices/__init__.py b/picamera2/devices/__init__.py new file mode 100644 index 00000000..6157cbb4 --- /dev/null +++ b/picamera2/devices/__init__.py @@ -0,0 +1 @@ +from .imx708 import IMX708 diff --git a/picamera2/devices/imx708/__init__.py b/picamera2/devices/imx708/__init__.py new file mode 100644 index 00000000..6157cbb4 --- /dev/null +++ b/picamera2/devices/imx708/__init__.py @@ -0,0 +1 @@ +from .imx708 import IMX708 diff --git a/picamera2/devices/imx708/imx708.py b/picamera2/devices/imx708/imx708.py new file mode 100644 index 00000000..9ac4d070 --- /dev/null +++ b/picamera2/devices/imx708/imx708.py @@ -0,0 +1,64 @@ +import fcntl +import os + +from picamera2 import Picamera2 +from v4l2 import v4l2_control, VIDIOC_S_CTRL + +HDR_CTRL_ID = 0x009a0915 + + +class IMX708: + def __init__(self, camera_num=None): + self.device_fd = None + + camera_info = Picamera2.global_camera_info() + if camera_num is None: + camera_id = next((c['Id'] for c in camera_info if c['Model'] == 'imx708'), None) + else: + camera_id = next((c['Id'] for c in camera_info if c['Num'] == camera_num), None) + + if camera_id is None: + raise RuntimeError(f'IMX708: Requested IMX708 camera device not be found') + + for i in range(16): + test_dir = f'/sys/class/video4linux/v4l-subdev{i}/device' + module_dir = f'{test_dir}/driver/module' + id_dir = f'{test_dir}/of_node' + if os.path.exists(module_dir) and os.path.islink(module_dir) and 'imx708' in os.readlink(module_dir): + if os.path.islink(id_dir) and camera_id in os.readlink(id_dir): + self.device_fd = open(f'/dev/v4l-subdev{i}', 'rb+', buffering=0) + break + + if self.device_fd is None: + raise RuntimeError('IMX708: Requested camera v4l2 device node not found') + + def __del__(self): + self.close() + + def close(self): + if self.device_fd: + self.device_fd.close() + self.device_fd = None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + self.close() + + def set_sensor_hdr_mode(self, enable: bool): + """ + Set the sensor HDR mode (True/False) on the IMX708 device. Note that after changing the HDR mode, you must + re-initialise the Picamera2 object to cache the updated sensor modes. + """ + ctrl = v4l2_control() + ctrl.id = HDR_CTRL_ID + ctrl.value = int(enable) + + try: + fcntl.ioctl(self.device_fd, VIDIOC_S_CTRL, ctrl) + except OSError as err: + print(f'IMX708: Unable to set HDR control in the device node: {err}') + + # Must reset the camera manager so that cached sensor modes can be refreshed. + Picamera2._cm.reset() diff --git a/setup.py b/setup.py index 423fccf8..9b4f6ae9 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,8 @@ "Programming Language :: Python :: 3.9", "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", ], - packages=['picamera2', 'picamera2.encoders', 'picamera2.outputs', 'picamera2.previews', 'picamera2.allocators'], + packages=['picamera2', 'picamera2.devices', 'picamera2.encoders', 'picamera2.outputs', 'picamera2.previews', + 'picamera2.allocators'], python_requires='>=3.9', licence='BSD 2-Clause License', install_requires=['numpy', 'PiDNG', 'piexif', 'pillow', 'simplejpeg', 'v4l2-python3', 'python-prctl', 'av'], diff --git a/tests/imx708_device.py b/tests/imx708_device.py new file mode 100755 index 00000000..7f80416f --- /dev/null +++ b/tests/imx708_device.py @@ -0,0 +1,33 @@ +#!/bin/python3 + +from picamera2.devices.imx708 import IMX708 +from picamera2 import Picamera2 +import time + +camera_info = Picamera2.global_camera_info() +camera_num = next((c['Num'] for c in camera_info if c['Model'] == 'imx708'), None) + +if camera_num is not None: + with IMX708(camera_num) as cam: + cam.set_sensor_hdr_mode(True) + picam2 = Picamera2(camera_num) + print(picam2.sensor_modes) + + picam2.start(show_preview=True) + time.sleep(1) + picam2.close() + + cam.set_sensor_hdr_mode(False) + picam2 = Picamera2(camera_num) + print(picam2.sensor_modes) + + picam2.start(show_preview=True) + time.sleep(1) + picam2.close() + + cam = IMX708(camera_num) + cam.set_sensor_hdr_mode(False) + picam2 = Picamera2(camera_num) + picam2.start(show_preview=True) + time.sleep(1) + picam2.close() diff --git a/tests/test_list.txt b/tests/test_list.txt index cd2cafaf..4b9725f3 100644 --- a/tests/test_list.txt +++ b/tests/test_list.txt @@ -68,6 +68,7 @@ tests/easy_video2.py tests/egl_leak.py tests/encoder_start_stop.py tests/ffmpeg_abort.py +tests/imx708_device.py tests/large_datagram.py tests/mjpeg_server.py tests/no_raw.py