Skip to content

Commit 1b5a8af

Browse files
committed
Show detected output format & mono option
Add UI and backend support for reporting the camera backend's detected output format and pixel format. Introduce a detected output label with tooltip and move/rename the "preserve mono" checkbox into an Output row. Store/clear detected_output_format and detected_pixel_format in camera props, read actual_output_format from backends during probing, and set detected_output_format when pixel format indicates Mono. Add a mono indicator to camera list entries and update probe early-return logic to require both resolution and output format before skipping probing.
1 parent aba6c31 commit 1b5a8af

2 files changed

Lines changed: 59 additions & 13 deletions

File tree

dlclivegui/gui/camera_config/camera_config_dialog.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,8 @@ def _set_detected_labels(self, cam: CameraSettings) -> None:
424424

425425
det_res = ns.get("detected_resolution")
426426
det_fps = ns.get("detected_fps")
427+
det_output_format = ns.get("detected_output_format")
428+
det_pixel_format = ns.get("detected_pixel_format")
427429

428430
if isinstance(det_res, (list, tuple)) and len(det_res) == 2:
429431
try:
@@ -439,6 +441,18 @@ def _set_detected_labels(self, cam: CameraSettings) -> None:
439441
else:
440442
self.detected_fps_label.setText("—")
441443

444+
self.detected_output_format_label.setText(str(det_output_format) if det_output_format else "—")
445+
446+
tooltip_parts = []
447+
if det_output_format:
448+
tooltip_parts.append(f"Backend output: {det_output_format}")
449+
if det_pixel_format:
450+
tooltip_parts.append(f"Camera PixelFormat: {det_pixel_format}")
451+
452+
self.detected_output_format_label.setToolTip(
453+
"\n".join(tooltip_parts) if tooltip_parts else "Backend-reported output frame format emitted to the app."
454+
)
455+
442456
def _refresh_camera_labels(self) -> None:
443457
cam_list = getattr(self, "active_cameras_list", None)
444458
if not cam_list:
@@ -467,11 +481,12 @@ def _format_camera_label(self, cam: CameraSettings, index: int = -1) -> str:
467481
status = "✓" if cam.enabled else "○"
468482
this_id = f"{(cam.backend or '').lower()}:{cam.index}"
469483
dlc_indicator = " [DLC]" if this_id == self._dlc_camera_id and cam.enabled else ""
484+
mono_indicator = " [Mono]" if getattr(cam, "preserve_mono", False) else ""
470485

471486
trigger_role = self._trigger_role_for_label(cam)
472487
trigger_indicator = "" if trigger_role in {"off", "disabled"} else f" [{trigger_role}]"
473488

474-
return f"{status} {cam.name} [{cam.backend}:{cam.index}]{trigger_indicator}{dlc_indicator}"
489+
return f"{status} {cam.name} [{cam.backend}:{cam.index}]{trigger_indicator}{dlc_indicator}{mono_indicator}"
475490

476491
def _selected_detected_camera(self) -> DetectedCamera | None:
477492
row = self.available_cameras_list.currentRow()
@@ -1194,6 +1209,8 @@ def _clear_settings_form(self) -> None:
11941209
self.cam_backend_label.setText("")
11951210
self.detected_resolution_label.setText("—")
11961211
self.detected_fps_label.setText("—")
1212+
self.detected_output_format_label.setText("—")
1213+
self.detected_output_format_label.setToolTip("Backend-reported output frame format emitted to the app.")
11971214
self.cam_width.setValue(0)
11981215
self.cam_height.setValue(0)
11991216
self.cam_fps.setValue(0.0)
@@ -1301,6 +1318,8 @@ def _reset_selected_camera(self, *, clear_backend_cache: bool = False) -> None:
13011318
else:
13021319
ns.pop("detected_resolution", None)
13031320
ns.pop("detected_fps", None)
1321+
ns.pop("detected_pixel_format", None)
1322+
ns.pop("detected_output_format", None)
13041323
ns.pop("last_applied_resolution", None)
13051324

13061325
# Update UI immediately to show "Auto" while probing
@@ -1372,12 +1391,16 @@ def _start_probe_for_camera(self, cam: CameraSettings, *, apply_to_requested: bo
13721391
ns = props.get(backend, {}) if isinstance(props.get(backend, None), dict) else {}
13731392
if not apply_to_requested:
13741393
det_res = ns.get("detected_resolution")
1394+
det_output = ns.get("detected_output_format")
1395+
has_res = False
13751396
if isinstance(det_res, (list, tuple)) and len(det_res) == 2:
13761397
try:
1377-
if int(det_res[0]) > 0 and int(det_res[1]) > 0:
1378-
return
1398+
has_res = int(det_res[0]) > 0 and int(det_res[1]) > 0
13791399
except Exception:
1380-
pass
1400+
has_res = False
1401+
1402+
if has_res and det_output:
1403+
return
13811404

13821405
# Start probe worker (settings will be opened in GUI thread for safety)
13831406
self._probe_worker = CameraProbeWorker(cam, self)
@@ -1404,6 +1427,7 @@ def _on_probe_success(self, payload) -> None:
14041427
actual_res = getattr(be, "actual_resolution", None)
14051428
actual_fps = getattr(be, "actual_fps", None)
14061429
actual_pixel_format = getattr(be, "actual_pixel_format", None)
1430+
actual_output_format = getattr(be, "actual_output_format", None)
14071431
recommended_preserve_mono = getattr(be, "recommended_preserve_mono", None)
14081432

14091433
try:
@@ -1427,8 +1451,6 @@ def _on_probe_success(self, payload) -> None:
14271451
# Store regardless of "set_*" support. This is just "what device reports".
14281452
if actual_res and isinstance(actual_res, (list, tuple)) and len(actual_res) == 2:
14291453
ns["detected_resolution"] = [int(actual_res[0]), int(actual_res[1])]
1430-
elif actual_res and isinstance(actual_res, tuple) and len(actual_res) == 2:
1431-
ns["detected_resolution"] = [int(actual_res[0]), int(actual_res[1])]
14321454

14331455
if isinstance(actual_fps, (int, float)) and float(actual_fps) > 0:
14341456
ns["detected_fps"] = float(actual_fps)
@@ -1437,6 +1459,10 @@ def _on_probe_success(self, payload) -> None:
14371459
ns["detected_pixel_format"] = str(actual_pixel_format)
14381460
self._append_status(f"[Probe] PixelFormat={actual_pixel_format}")
14391461

1462+
if actual_output_format:
1463+
ns["detected_output_format"] = str(actual_output_format)
1464+
self._append_status(f"[Probe] OutputFormat={actual_output_format}")
1465+
14401466
if recommended_preserve_mono is not None:
14411467
ns["recommended_preserve_mono"] = bool(recommended_preserve_mono)
14421468

@@ -1450,6 +1476,9 @@ def _on_probe_success(self, payload) -> None:
14501476
c.preserve_mono = True
14511477
self._append_status("[Probe] Mono pixel format detected; enabled Preserve mono frames.")
14521478

1479+
if actual_pixel_format and str(actual_pixel_format).startswith("Mono"):
1480+
ns["detected_output_format"] = "Mono8"
1481+
14531482
# ---- Apply detected -> requested (Reset behavior) ----
14541483
if self._probe_apply_to_requested and self._probe_target_row == i:
14551484
# Only apply resolution if we actually got it

dlclivegui/gui/camera_config/ui_blocks.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,6 @@ def build_settings_group(dlg: CameraConfigDialog) -> QGroupBox:
277277
dlg.settings_form.addRow(detected_row)
278278

279279
# --- Requested resolution/output format controls (Auto = 0) ---
280-
dlg.cam_preserve_mono_checkbox = QCheckBox("Preserve Mono output")
281-
dlg.cam_preserve_mono_checkbox.setToolTip(
282-
"For monochrome cameras, keep frames as single-channel Mono8 instead of converting to BGR. "
283-
"This reduces memory bandwidth and recording overhead. Display/overlay/DLC may convert later if needed."
284-
)
285-
dlg.settings_form.addRow(dlg.cam_preserve_mono_checkbox)
286-
287280
dlg.cam_width = QSpinBox()
288281
dlg.cam_width.setRange(0, 10000)
289282
dlg.cam_width.setValue(0)
@@ -297,6 +290,30 @@ def build_settings_group(dlg: CameraConfigDialog) -> QGroupBox:
297290
res_row = make_two_field_row("W", dlg.cam_width, "H", dlg.cam_height, key_width=30)
298291
dlg.settings_form.addRow("Resolution:", res_row)
299292

293+
# --- Output format controls ---
294+
dlg.cam_preserve_mono_checkbox = QCheckBox("Preserve mono output")
295+
dlg.cam_preserve_mono_checkbox.setToolTip(
296+
"For monochrome cameras, keep frames as single-channel Mono8 instead of converting to BGR. "
297+
"This reduces memory bandwidth and recording overhead. Display/overlay/DLC may convert later if needed."
298+
)
299+
300+
dlg.detected_output_format_label = QLabel("—")
301+
dlg.detected_output_format_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
302+
dlg.detected_output_format_label.setToolTip(
303+
"Backend-reported output frame format emitted to the app, for example Mono8 or BGR8."
304+
)
305+
306+
output_widget = QWidget()
307+
output_layout = QHBoxLayout(output_widget)
308+
output_layout.setContentsMargins(0, 0, 0, 0)
309+
output_layout.setSpacing(8)
310+
output_layout.addWidget(dlg.cam_preserve_mono_checkbox)
311+
output_layout.addStretch(1)
312+
output_layout.addWidget(QLabel("Detected output:"))
313+
output_layout.addWidget(dlg.detected_output_format_label)
314+
315+
dlg.settings_form.addRow("Output:", output_widget)
316+
300317
# --- FPS + Rotation grouped ---
301318
dlg.cam_fps = QDoubleSpinBox()
302319
dlg.cam_fps.setRange(0.0, 240.0)

0 commit comments

Comments
 (0)