@@ -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" )
@@ -184,10 +186,20 @@ def actual_pixel_format(self) -> str | None:
184186 """Camera/native pixel format selected on the GenICam PixelFormat node."""
185187 return self ._camera_pixel_format or (self ._pixel_format if self ._pixel_format != "auto" else None )
186188
189+ @property
190+ def recommended_preserve_mono (self ) -> bool | None :
191+ if not self ._camera_pixel_format :
192+ return None
193+ return self ._is_camera_mono ()
194+
187195 @property
188196 def actual_output_format (self ) -> str | None :
189- """Current GenTL backend emits OpenCV-native BGR uint8 frames."""
190- return self ._actual_output_format or "BGR8"
197+ """Backend output frame format emitted to the app, e.g. 'Mono8' or 'BGR8'."""
198+ if self ._actual_output_format :
199+ return self ._actual_output_format
200+ if not self ._camera_pixel_format :
201+ return None
202+ return "Mono8" if self ._should_output_mono () else "BGR8"
191203
192204 @classmethod
193205 def is_available (cls ) -> bool :
@@ -203,6 +215,7 @@ def static_capabilities(cls) -> dict[str, SupportLevel]:
203215 "device_discovery" : SupportLevel .SUPPORTED ,
204216 "stable_identity" : SupportLevel .SUPPORTED ,
205217 "hardware_trigger" : SupportLevel .BEST_EFFORT ,
218+ "preserve_mono" : SupportLevel .SUPPORTED ,
206219 }
207220
208221 def _debug_trigger_nodes (self , node_map , * , context : str = "" ) -> None :
@@ -600,6 +613,13 @@ def waits_for_hardware_trigger(self) -> bool:
600613 role = str (self ._trigger_attr (getattr (self , "_trigger" , None ), "role" , "off" ) or "off" ).lower ()
601614 return role in {"external" , "follower" }
602615
616+ def _is_camera_mono (self ) -> bool :
617+ fmt = str (self ._camera_pixel_format or self ._pixel_format or "" ).strip ()
618+ return fmt .startswith ("Mono" )
619+
620+ def _should_output_mono (self ) -> bool :
621+ return bool (self ._preserve_mono and self ._is_camera_mono ())
622+
603623 @staticmethod
604624 def _output_format_for_frame (frame : np .ndarray ) -> str :
605625 if frame .ndim == 2 :
@@ -654,6 +674,25 @@ def read(self) -> CapturedFrame:
654674 except Exception :
655675 pass
656676 self ._actual_output_format = self ._output_format_for_frame (frame )
677+ try :
678+ ns = self ._ensure_settings_ns ()
679+ ns ["actual_output_format" ] = self ._actual_output_format
680+ ns ["preserve_mono" ] = self ._preserve_mono
681+ except Exception :
682+ pass
683+ if not self ._logged_first_frame :
684+ self ._logged_first_frame = True
685+ LOG .info (
686+ "[GenTL] first frame device_id=%s shape=%s dtype=%s nbytes=%.2f MB "
687+ "camera_pixel_format=%s output_format=%s preserve_mono=%s" ,
688+ self ._device_id ,
689+ frame .shape ,
690+ frame .dtype ,
691+ frame .nbytes / (1024 * 1024 ),
692+ self ._camera_pixel_format ,
693+ self .actual_output_format ,
694+ self ._preserve_mono ,
695+ )
657696
658697 return CapturedFrame (
659698 frame = frame ,
@@ -1346,6 +1385,14 @@ def _configure_pixel_format(self, node_map) -> None:
13461385 pixel_format_node .value = selected
13471386 self ._pixel_format = str (pixel_format_node .value )
13481387 self ._camera_pixel_format = self ._pixel_format
1388+ try :
1389+ ns = self ._ensure_settings_ns ()
1390+ ns ["actual_pixel_format" ] = self ._camera_pixel_format
1391+ ns ["detected_pixel_format" ] = self ._camera_pixel_format
1392+ ns ["actual_output_format" ] = self .actual_output_format
1393+ ns ["preserve_mono" ] = self ._preserve_mono
1394+ except Exception :
1395+ pass
13491396
13501397 LOG .debug ("GenTL pixel format selected: %s" , self ._pixel_format )
13511398
@@ -1851,6 +1898,9 @@ def _convert_frame(self, frame: np.ndarray) -> np.ndarray:
18511898 frame = cv2 .cvtColor (frame , cv2 .COLOR_BayerGR2BGR )
18521899 elif fmt == "BayerBG8" :
18531900 frame = cv2 .cvtColor (frame , cv2 .COLOR_BayerBG2BGR )
1901+ elif self ._should_output_mono ():
1902+ # Keep Mono* cameras as 2D uint8 frames when explicitly requested.
1903+ pass
18541904 else :
18551905 frame = cv2 .cvtColor (frame , cv2 .COLOR_GRAY2BGR )
18561906
0 commit comments