diff --git a/core/camera/base_camera.py b/core/camera/base_camera.py index acaea6b..56b92b1 100644 --- a/core/camera/base_camera.py +++ b/core/camera/base_camera.py @@ -5,6 +5,11 @@ class BaseCamera(ABC): def __init__(self) -> None: self.error_msg = None + @staticmethod + @abstractmethod + def detect() -> dict: + raise NotImplementedError + @abstractmethod def connect(self, index: int | None = None) -> bool: raise NotImplementedError diff --git a/core/camera/gphoto_camera.py b/core/camera/gphoto_camera.py index a6ceb2c..46b1f88 100644 --- a/core/camera/gphoto_camera.py +++ b/core/camera/gphoto_camera.py @@ -24,20 +24,21 @@ class GPhotoCamera(BaseCamera): super().__init__() self.camera = None self.configs: List[dict] = [] - self.camera_list = [] + self.camera_index = 0 - def detect(self) -> list: - self.camera_list.clear() + @staticmethod + def detect() -> dict: cameras = gp.check_result(gp.gp_camera_autodetect()) # type: ignore # cameras = gp.Camera().autodetect() if not cameras or cameras.count() == 0: # type: ignore - return [] + return {} + camera_list = {} for i in range(cameras.count()): # type: ignore name = cameras.get_name(i) # type: ignore port = cameras.get_value(i) # type: ignore - self.camera_list.append({"name": name, "port": port}) - return self.camera_list + camera_list[i] = {"name": name, "port": port} + return camera_list def connect(self, index: int | None = None) -> bool: self.error_msg = None @@ -45,10 +46,12 @@ class GPhotoCamera(BaseCamera): try: if index: + self.camera_index = index + camera_list = GPhotoCamera.detect() port_info_list = gp.PortInfoList() port_info_list.load() - port_address = self.camera_list[index]["port"] + port_address = camera_list[index]["port"] port_index = port_info_list.lookup_path(port_address) self.camera.set_port_info(port_info_list[port_index]) diff --git a/core/camera/opencv_camera.py b/core/camera/opencv_camera.py index a6a20b5..edcb678 100644 --- a/core/camera/opencv_camera.py +++ b/core/camera/opencv_camera.py @@ -5,7 +5,7 @@ from typing import List from .base_camera import BaseCamera -class CvCamera(BaseCamera): +class OpenCvCamera(BaseCamera): """Kamera oparta na cv2.VideoCapture""" config_map = { @@ -24,13 +24,28 @@ class CvCamera(BaseCamera): self.camera_list = [] self.camera_index = 0 - def detect(self) -> list: - self.camera_list.clear() - self.camera_list = enumerate_cameras(cv2.CAP_ANY) + @staticmethod + def detect(): + camera_list = enumerate_cameras(cv2.CAP_ANY) + result = {} + seen_ports = set() - result = [] - for camera in self.camera_list: - result.append({"name": camera.name, "port": camera.path}) + for camera in camera_list: + # unikamy duplikatów tego samego /dev/videoX albo tej samej ścieżki na Windows/macOS + if camera.path in seen_ports: + continue + + cap = cv2.VideoCapture(camera.index, camera.backend) # próbujemy otworzyć + ret, frame = cap.read() + cap.release() + + if ret and frame is not None and frame.size > 0: + result[camera.index] = { + "name": camera.name, + "port": camera.path, + "backend": camera.backend, + } + seen_ports.add(camera.path) return result @@ -38,7 +53,7 @@ class CvCamera(BaseCamera): self.error_msg = None try: if index: - self.camera_index = self.camera_list[index].index + self.camera_index = index self.cap = cv2.VideoCapture(self.camera_index)