Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dlclivegui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
MultiCameraSettings,
RecordingSettings,
)
from .gui.camera_config_dialog import CameraConfigDialog
from .gui.camera_config.camera_config_dialog import CameraConfigDialog
from .gui.main_window import DLCLiveMainWindow
from .main import main
from .services.multi_camera_controller import MultiCameraController, MultiFrameData
Expand Down
3 changes: 2 additions & 1 deletion dlclivegui/cameras/backends/basler_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def __init__(self, settings):

self._props: dict = settings.properties if isinstance(settings.properties, dict) else {}

# Optional fast-start hint for probe workers (best-effort; doesn't change behavior yet)
# Optional fast-start hint for probe workers
# (may skip StartGrabbing and converter setup for faster capability probing; not suitable for normal capture)
self._fast_start: bool = bool(self.ns.get("fast_start", False))

# Stable identity (serial-based). Prefer new namespace; fall back to legacy keys read-only.
Expand Down
46 changes: 46 additions & 0 deletions dlclivegui/cameras/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,3 +415,49 @@ def _resolve_backend(name: str) -> type[CameraBackend]:
"Tip: enable strict import failures with DLC_CAMERA_BACKENDS_STRICT_IMPORT=1"
)
raise RuntimeError(msg) from exc


# -------------------------------
# Camera identity utilities
# -------------------------------


def apply_detected_identity(cam: CameraSettings, detected: DetectedCamera, backend: str) -> None:
"""Persist stable identity from a detected camera into cam.properties under backend namespace."""
if not isinstance(cam.properties, dict):
cam.properties = {}

ns = cam.properties.get(backend.lower())
if not isinstance(ns, dict):
ns = {}
cam.properties[backend.lower()] = ns

# Store whatever we have (backend-specific but written generically)
if getattr(detected, "device_id", None):
ns["device_id"] = detected.device_id
if getattr(detected, "vid", None) is not None:
ns["device_vid"] = int(detected.vid)
if getattr(detected, "pid", None) is not None:
ns["device_pid"] = int(detected.pid)
if getattr(detected, "path", None):
ns["device_path"] = detected.path

# Optional: store human name for matching fallback
if getattr(detected, "label", None):
ns["device_name"] = detected.label

# Optional: store backend_hint if you expose it (e.g., CAP_DSHOW)
if getattr(detected, "backend_hint", None) is not None:
ns["backend_hint"] = int(detected.backend_hint)


def camera_identity_key(cam: CameraSettings) -> tuple:
backend = (cam.backend or "").lower()
props = cam.properties if isinstance(cam.properties, dict) else {}
ns = props.get(backend, {}) if isinstance(props, dict) else {}
device_id = ns.get("device_id")

# Prefer stable identity if present, otherwise fallback
if device_id:
return (backend, "device_id", device_id)
return (backend, "index", int(cam.index))
26 changes: 26 additions & 0 deletions dlclivegui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,32 @@ def apply_defaults(self) -> CameraSettings:

return self

@staticmethod
def check_diff(old: CameraSettings, new: CameraSettings) -> dict:
keys = (
"width",
"height",
"fps",
"exposure",
"gain",
"rotation",
"crop_x0",
"crop_y0",
"crop_x1",
"crop_y1",
"enabled",
)
out = {}
for k in keys:
try:
ov = getattr(old, k, None)
nv = getattr(new, k, None)
if ov != nv:
out[k] = (ov, nv)
except Exception:
pass
return out


class MultiCameraSettings(BaseModel):
cameras: list[CameraSettings] = Field(default_factory=list)
Expand Down
Loading