@@ -1137,31 +1137,33 @@ async def _acquire_image(
11371137
11381138 assert self .imaging_config is not None , "Need to set imaging_config first"
11391139
1140- self .cam .BeginAcquisition ()
1141- try :
1142- num_tries = 0
1143- while num_tries < self .imaging_config .max_image_read_attempts :
1144- node_softwaretrigger_cmd = PySpin .CCommandPtr (nodemap .GetNode ("TriggerSoftware" ))
1145- if not PySpin .IsWritable (node_softwaretrigger_cmd ):
1146- raise RuntimeError ("unable to execute software trigger" )
1147- node_softwaretrigger_cmd .Execute ()
1140+ num_tries = 0
1141+ while num_tries < self .imaging_config .max_image_read_attempts :
1142+ node_softwaretrigger_cmd = PySpin .CCommandPtr (nodemap .GetNode ("TriggerSoftware" ))
1143+ if not PySpin .IsWritable (node_softwaretrigger_cmd ):
1144+ raise RuntimeError ("unable to execute software trigger" )
1145+ node_softwaretrigger_cmd .Execute ()
11481146
1149- try :
1150- image_result = self .cam .GetNextImage (1000 )
1151- if not image_result .IsIncomplete ():
1152- processor = PySpin .ImageProcessor ()
1153- processor .SetColorProcessing (color_processing_algorithm )
1154- image_converted = processor .Convert (image_result , pixel_format )
1155- image_result .Release ()
1156- return image_converted .GetNDArray ().tolist () # type: ignore
1157- except SpinnakerException as e :
1158- # the image is not ready yet, try again
1159- logger .debug ("Failed to get image: %s" , e )
1160- num_tries += 1
1161- await asyncio .sleep (0.3 )
1162- raise TimeoutError ("max_image_read_attempts reached" )
1163- finally :
1164- self .cam .EndAcquisition ()
1147+ try :
1148+ t0 = time .time ()
1149+ image_result = self .cam .GetNextImage (1000 )
1150+ t1 = time .time ()
1151+ logger .debug ("[cytation5] GetNextImage took %.2f seconds" , t1 - t0 )
1152+ if not image_result .IsIncomplete ():
1153+ t0 = time .time ()
1154+ processor = PySpin .ImageProcessor ()
1155+ processor .SetColorProcessing (color_processing_algorithm )
1156+ image_converted = processor .Convert (image_result , pixel_format )
1157+ image_result .Release ()
1158+ logger .debug ("[cytation5] acquired image in %d tries" , num_tries + 1 )
1159+ logger .debug ("[cytation5] Convert took %.2f seconds for BS" , time .time () - t0 )
1160+ return image_converted .GetNDArray ().tolist () # type: ignore
1161+ except SpinnakerException as e :
1162+ # the image is not ready yet, try again
1163+ logger .debug ("Failed to get image: %s" , e )
1164+ num_tries += 1
1165+ await asyncio .sleep (0.3 )
1166+ raise TimeoutError ("max_image_read_attempts reached" )
11651167
11661168 async def capture (
11671169 self ,
@@ -1202,58 +1204,81 @@ async def capture(
12021204 if self .cam is None :
12031205 raise ValueError ("Camera not initialized. Run setup(use_cam=True) first." )
12041206
1205- await self .set_objective (objective )
1206- await self .set_imaging_mode (mode , led_intensity = led_intensity )
1207- await self .select (row , column )
1208- await self .set_exposure (exposure_time )
1209- await self .set_gain (gain )
1210- await self .set_focus (focal_height )
1211-
1212- def image_size (magnification : float ) -> Tuple [float , float ]:
1213- # "wide fov" is an option in gen5.exe, but in reality it takes the same pictures. So we just
1214- # simply take the wide fov option.
1215- # um to mm (plr unit)
1216- if magnification == 4 :
1217- return (3474 / 1000 , 3474 / 1000 )
1218- if magnification == 20 :
1219- return (694 / 1000 , 694 / 1000 )
1220- if magnification == 40 :
1221- return (347 / 1000 , 347 / 1000 )
1222- raise ValueError (f"Don't know image size for magnification { magnification } " )
1223-
1224- if self ._objective is None :
1225- raise RuntimeError ("Objective not set. Run set_objective() first." )
1226- magnification = self ._objective .magnification
1227- img_width , img_height = image_size (magnification )
1228-
1229- first_well = plate .get_item (0 )
1230- well_size_x , well_size_y = (first_well .get_size_x (), first_well .get_size_y ())
1231- if coverage == "full" :
1232- coverage = (
1233- math .ceil (well_size_x / image_size (magnification )[0 ]),
1234- math .ceil (well_size_y / image_size (magnification )[1 ]),
1235- )
1236- rows , cols = coverage
1237-
1238- # Get positions, centered around enter_position
1239- if center_position is None :
1240- center_position = (0 , 0 )
1241- # Going in a snake pattern is not faster (strangely)
1242- positions = [
1243- (x * img_width + center_position [0 ], - y * img_height + center_position [1 ])
1244- for y in [i - (rows - 1 ) / 2 for i in range (rows )]
1245- for x in [i - (cols - 1 ) / 2 for i in range (cols )]
1246- ]
1247-
1248- images : List [Image ] = []
1249- for x_pos , y_pos in positions :
1250- await self .set_position (x = x_pos , y = y_pos )
1251- await asyncio .sleep (0.1 )
1252- images .append (
1253- await self ._acquire_image (
1254- color_processing_algorithm = color_processing_algorithm , pixel_format = pixel_format
1207+ self .cam .BeginAcquisition ()
1208+ try :
1209+ t0 = time .time ()
1210+ await self .set_objective (objective )
1211+ t_objective = time .time ()
1212+ logger .debug ("[cytation5] set objective in %.2f seconds" , t_objective - t0 )
1213+ await self .set_imaging_mode (mode , led_intensity = led_intensity )
1214+ t_imaging_mode = time .time ()
1215+ logger .debug ("[cytation5] set imaging mode in %.2f seconds" , t_imaging_mode - t_objective )
1216+ await self .select (row , column )
1217+ t_select = time .time ()
1218+ logger .debug ("[cytation5] selected well in %.2f seconds" , t_select - t_imaging_mode )
1219+ await self .set_exposure (exposure_time )
1220+ t_exposure = time .time ()
1221+ logger .debug ("[cytation5] set exposure in %.2f seconds" , t_exposure - t_select )
1222+ await self .set_gain (gain )
1223+ t_gain = time .time ()
1224+ logger .debug ("[cytation5] set gain in %.2f seconds" , t_gain - t_exposure )
1225+ await self .set_focus (focal_height )
1226+ t_focus = time .time ()
1227+ logger .debug ("[cytation5] set focus in %.2f seconds" , t_focus - t_gain )
1228+
1229+ def image_size (magnification : float ) -> Tuple [float , float ]:
1230+ # "wide fov" is an option in gen5.exe, but in reality it takes the same pictures. So we just
1231+ # simply take the wide fov option.
1232+ # um to mm (plr unit)
1233+ if magnification == 4 :
1234+ return (3474 / 1000 , 3474 / 1000 )
1235+ if magnification == 20 :
1236+ return (694 / 1000 , 694 / 1000 )
1237+ if magnification == 40 :
1238+ return (347 / 1000 , 347 / 1000 )
1239+ raise ValueError (f"Don't know image size for magnification { magnification } " )
1240+
1241+ if self ._objective is None :
1242+ raise RuntimeError ("Objective not set. Run set_objective() first." )
1243+ magnification = self ._objective .magnification
1244+ img_width , img_height = image_size (magnification )
1245+
1246+ first_well = plate .get_item (0 )
1247+ well_size_x , well_size_y = (first_well .get_size_x (), first_well .get_size_y ())
1248+ if coverage == "full" :
1249+ coverage = (
1250+ math .ceil (well_size_x / image_size (magnification )[0 ]),
1251+ math .ceil (well_size_y / image_size (magnification )[1 ]),
12551252 )
1256- )
1253+ rows , cols = coverage
1254+
1255+ # Get positions, centered around enter_position
1256+ if center_position is None :
1257+ center_position = (0 , 0 )
1258+ # Going in a snake pattern is not faster (strangely)
1259+ positions = [
1260+ (x * img_width + center_position [0 ], - y * img_height + center_position [1 ])
1261+ for y in [i - (rows - 1 ) / 2 for i in range (rows )]
1262+ for x in [i - (cols - 1 ) / 2 for i in range (cols )]
1263+ ]
1264+
1265+ images : List [Image ] = []
1266+ for x_pos , y_pos in positions :
1267+ await self .set_position (x = x_pos , y = y_pos )
1268+ await asyncio .sleep (0.1 )
1269+ t0 = time .time ()
1270+ images .append (
1271+ await self ._acquire_image (
1272+ color_processing_algorithm = color_processing_algorithm , pixel_format = pixel_format
1273+ )
1274+ )
1275+ t1 = time .time ()
1276+ logger .debug (
1277+ "[cytation5] acquired image in %.2f seconds at position" ,
1278+ t1 - t0 ,
1279+ )
1280+ finally :
1281+ self .cam .EndAcquisition ()
12571282
12581283 exposure_ms = float (self .cam .ExposureTime .GetValue ()) / 1000
12591284 assert self ._focal_height is not None , "Focal height not set. Run set_focus() first."
0 commit comments