@@ -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