@@ -54,6 +54,20 @@ class GenTLCameraBackend(CameraBackend):
5454 r"C:\Program Files\The Imaging Source Europe GmbH\TIS Camera SDK\bin\win64_x64\*.cti" ,
5555 r"C:\Program Files (x86)\The Imaging Source Europe GmbH\TIS Grabber\bin\win64_x64\*.cti" ,
5656 )
57+ _COLOR_PIXEL_FORMATS : ClassVar [tuple [str , ...]] = (
58+ "BGR8" ,
59+ "RGB8" ,
60+ "BayerRG8" ,
61+ "BayerGB8" ,
62+ "BayerGR8" ,
63+ "BayerBG8" ,
64+ )
65+ _MONO_PIXEL_FORMATS : ClassVar [tuple [str , ...]] = (
66+ "Mono8" ,
67+ "Mono10" ,
68+ "Mono12" ,
69+ "Mono16" ,
70+ )
5771
5872 # Source marker stored in properties["gentl"]["cti_files_source"].
5973 # auto: persisted by auto-discovery; may be stale and can fall back.
@@ -77,7 +91,8 @@ def __init__(self, settings):
7791 self ._device_id : str | None = str (raw_device_id ).strip () if raw_device_id else None
7892 self ._serial_number : str | None = self ._serial_from_identity (self ._device_id , legacy_serial )
7993
80- self ._pixel_format : str = ns .get ("pixel_format" ) or props .get ("pixel_format" , "Mono8" )
94+ self ._pixel_format : str = ns .get ("pixel_format" ) or props .get ("pixel_format" , "auto" )
95+ self ._pixel_format = str (self ._pixel_format ).strip ()
8196 self ._rotate : int = int (ns .get ("rotate" , props .get ("rotate" , 0 ))) % 360
8297 self ._crop : tuple [int , int , int , int ] | None = self ._parse_crop (ns .get ("crop" , props .get ("crop" )))
8398
@@ -913,17 +928,56 @@ def _create_acquirer(self, serial: str | None, index: int):
913928
914929 def _configure_pixel_format (self , node_map ) -> None :
915930 try :
916- if self ._pixel_format in node_map .PixelFormat .symbolics :
917- node_map .PixelFormat .value = self ._pixel_format
918- actual = node_map .PixelFormat .value
919- if actual != self ._pixel_format :
920- LOG .warning ("Pixel format mismatch: requested '%s', got '%s'" , self ._pixel_format , actual )
931+ pixel_format_node = getattr (node_map , "PixelFormat" , None )
932+ if pixel_format_node is None :
933+ return
934+
935+ available = list (getattr (pixel_format_node , "symbolics" , []) or [])
936+ if not available :
937+ return
938+
939+ requested = str (self ._pixel_format or "auto" ).strip ()
940+
941+ if requested .lower () == "auto" :
942+ selected = None
943+
944+ for fmt in self ._COLOR_PIXEL_FORMATS :
945+ if fmt in available :
946+ selected = fmt
947+ break
948+
949+ if selected is None :
950+ for fmt in self ._MONO_PIXEL_FORMATS :
951+ if fmt in available :
952+ selected = fmt
953+ break
954+
955+ if selected is None :
956+ selected = available [0 ]
957+
921958 else :
922- LOG .warning (
923- "Pixel format '%s' not in available formats: %s" , self ._pixel_format , node_map .PixelFormat .symbolics
924- )
959+ selected = requested
960+ if selected not in available :
961+ LOG .warning (
962+ "Pixel format '%s' not available. Available formats: %s. Falling back to auto." ,
963+ selected ,
964+ available ,
965+ )
966+ selected = None
967+ for fmt in self ._COLOR_PIXEL_FORMATS + self ._MONO_PIXEL_FORMATS :
968+ if fmt in available :
969+ selected = fmt
970+ break
971+ if selected is None :
972+ selected = available [0 ]
973+
974+ pixel_format_node .value = selected
975+ self ._pixel_format = str (pixel_format_node .value )
976+
977+ LOG .debug ("GenTL pixel format selected: %s" , self ._pixel_format )
978+
925979 except Exception as e :
926- LOG .warning ("Failed to set pixel format '%s': %s" , self ._pixel_format , e )
980+ LOG .warning ("Failed to configure pixel format '%s': %s" , self ._pixel_format , e )
927981
928982 def _configure_trigger (self , node_map ) -> None :
929983 try :
@@ -1056,10 +1110,24 @@ def _convert_frame(self, frame: np.ndarray) -> np.ndarray:
10561110 scale = 255.0 / max_val if max_val > 0.0 else 1.0
10571111 frame = np .clip (frame * scale , 0 , 255 ).astype (np .uint8 )
10581112
1113+ fmt = str (self ._pixel_format or "" ).strip ()
1114+
10591115 if frame .ndim == 2 :
1060- frame = cv2 .cvtColor (frame , cv2 .COLOR_GRAY2BGR )
1061- elif frame .ndim == 3 and frame .shape [2 ] == 3 and self ._pixel_format == "RGB8" :
1062- frame = cv2 .cvtColor (frame , cv2 .COLOR_RGB2BGR )
1116+ if fmt == "BayerRG8" :
1117+ frame = cv2 .cvtColor (frame , cv2 .COLOR_BayerRG2BGR )
1118+ elif fmt == "BayerGB8" :
1119+ frame = cv2 .cvtColor (frame , cv2 .COLOR_BayerGB2BGR )
1120+ elif fmt == "BayerGR8" :
1121+ frame = cv2 .cvtColor (frame , cv2 .COLOR_BayerGR2BGR )
1122+ elif fmt == "BayerBG8" :
1123+ frame = cv2 .cvtColor (frame , cv2 .COLOR_BayerBG2BGR )
1124+ else :
1125+ frame = cv2 .cvtColor (frame , cv2 .COLOR_GRAY2BGR )
1126+
1127+ elif frame .ndim == 3 and frame .shape [2 ] == 3 :
1128+ if fmt == "RGB8" :
1129+ frame = cv2 .cvtColor (frame , cv2 .COLOR_RGB2BGR )
1130+ # BGR8 is already OpenCV-native.
10631131
10641132 if self ._crop is not None :
10651133 top , bottom , left , right = (int (v ) for v in self ._crop )
0 commit comments