Skip to content

Commit 351af18

Browse files
committed
Add mono-preserving output mode to GenTL
Adds a `preserve_mono` option to the GenTL camera backend so Mono pixel formats can remain 2D Mono8 output instead of always converting to BGR. The backend now reports preserve-mono capability, exposes recommended/actual output format based on camera format, and persists detected pixel/output format metadata in settings. It also logs first-frame format details to make runtime format behavior easier to inspect.
1 parent a717d78 commit 351af18

1 file changed

Lines changed: 52 additions & 2 deletions

File tree

‎dlclivegui/cameras/backends/gentl_backend.py‎

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ def __init__(self, settings):
9090
ns = {}
9191

9292
self._fast_start: bool = bool(ns.get("fast_start", False))
93+
self._preserve_mono: bool = bool(getattr(settings, "preserve_mono", False) or ns.get("preserve_mono", False))
94+
self._logged_first_frame: bool = False
9395

9496
raw_device_id = ns.get("device_id") or props.get("device_id")
9597
legacy_serial = ns.get("serial_number") or ns.get("serial") or props.get("serial_number") or props.get("serial")
@@ -177,10 +179,20 @@ def actual_pixel_format(self) -> str | None:
177179
"""Camera/native pixel format selected on the GenICam PixelFormat node."""
178180
return self._camera_pixel_format or (self._pixel_format if self._pixel_format != "auto" else None)
179181

182+
@property
183+
def recommended_preserve_mono(self) -> bool | None:
184+
if not self._camera_pixel_format:
185+
return None
186+
return self._is_camera_mono()
187+
180188
@property
181189
def actual_output_format(self) -> str | None:
182-
"""Current GenTL backend emits OpenCV-native BGR uint8 frames."""
183-
return self._actual_output_format or "BGR8"
190+
"""Backend output frame format emitted to the app, e.g. 'Mono8' or 'BGR8'."""
191+
if self._actual_output_format:
192+
return self._actual_output_format
193+
if not self._camera_pixel_format:
194+
return None
195+
return "Mono8" if self._should_output_mono() else "BGR8"
184196

185197
@classmethod
186198
def is_available(cls) -> bool:
@@ -196,6 +208,7 @@ def static_capabilities(cls) -> dict[str, SupportLevel]:
196208
"device_discovery": SupportLevel.SUPPORTED,
197209
"stable_identity": SupportLevel.SUPPORTED,
198210
"hardware_trigger": SupportLevel.BEST_EFFORT,
211+
"preserve_mono": SupportLevel.SUPPORTED,
199212
}
200213

201214
def _debug_trigger_nodes(self, node_map, *, context: str = "") -> None:
@@ -606,6 +619,13 @@ def waits_for_hardware_trigger(self) -> bool:
606619
role = str(self._trigger_attr(getattr(self, "_trigger", None), "role", "off") or "off").lower()
607620
return role in {"external", "follower"}
608621

622+
def _is_camera_mono(self) -> bool:
623+
fmt = str(self._camera_pixel_format or self._pixel_format or "").strip()
624+
return fmt.startswith("Mono")
625+
626+
def _should_output_mono(self) -> bool:
627+
return bool(self._preserve_mono and self._is_camera_mono())
628+
609629
@staticmethod
610630
def _output_format_for_frame(frame: np.ndarray) -> str:
611631
if frame.ndim == 2:
@@ -660,6 +680,25 @@ def read(self) -> CapturedFrame:
660680
except Exception:
661681
pass
662682
self._actual_output_format = self._output_format_for_frame(frame)
683+
try:
684+
ns = self._ensure_settings_ns()
685+
ns["actual_output_format"] = self._actual_output_format
686+
ns["preserve_mono"] = self._preserve_mono
687+
except Exception:
688+
pass
689+
if not self._logged_first_frame:
690+
self._logged_first_frame = True
691+
LOG.info(
692+
"[GenTL] first frame device_id=%s shape=%s dtype=%s nbytes=%.2f MB "
693+
"camera_pixel_format=%s output_format=%s preserve_mono=%s",
694+
self._device_id,
695+
frame.shape,
696+
frame.dtype,
697+
frame.nbytes / (1024 * 1024),
698+
self._camera_pixel_format,
699+
self.actual_output_format,
700+
self._preserve_mono,
701+
)
663702

664703
return CapturedFrame(
665704
frame=frame,
@@ -1374,6 +1413,14 @@ def _configure_pixel_format(self, node_map) -> None:
13741413
pixel_format_node.value = selected
13751414
self._pixel_format = str(pixel_format_node.value)
13761415
self._camera_pixel_format = self._pixel_format
1416+
try:
1417+
ns = self._ensure_settings_ns()
1418+
ns["actual_pixel_format"] = self._camera_pixel_format
1419+
ns["detected_pixel_format"] = self._camera_pixel_format
1420+
ns["actual_output_format"] = self.actual_output_format
1421+
ns["preserve_mono"] = self._preserve_mono
1422+
except Exception:
1423+
pass
13771424

13781425
LOG.debug("GenTL pixel format selected: %s", self._pixel_format)
13791426

@@ -1879,6 +1926,9 @@ def _convert_frame(self, frame: np.ndarray) -> np.ndarray:
18791926
frame = cv2.cvtColor(frame, cv2.COLOR_BayerGR2BGR)
18801927
elif fmt == "BayerBG8":
18811928
frame = cv2.cvtColor(frame, cv2.COLOR_BayerBG2BGR)
1929+
elif self._should_output_mono():
1930+
# Keep Mono* cameras as 2D uint8 frames when explicitly requested.
1931+
pass
18821932
else:
18831933
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
18841934

0 commit comments

Comments
 (0)