class Camera(): # Initialize SDK and Camera objects in order to use ThorLabs API functions def __init__(self): self.camSesh = TLCameraSDK() camList = self.camSesh.discover_available_cameras() camSerialNum = '08153' self.cam = self.camSesh.open_camera(camSerialNum) self.cam.operation_mode = thorlabs_tsi_sdk.tl_camera_enums.OPERATION_MODE.SOFTWARE_TRIGGERED # Set camera exposure in units of microseconds expose_time = 36 self.cam.exposure_time_us = expose_time self.cam.frames_per_trigger_zero_for_unlimited = 1 self.isStreaming = False # Call destructors for SDK and Camera objects def __del__(self): self.cam.dispose() self.camSesh.dispose() # Takes a picture and returns an np.array of the image def takePicture(self): self.cam.frames_per_trigger_zero_for_unlimited = 1 self.cam.arm(1) self.cam.issue_software_trigger() temp = self.cam.get_pending_frame_or_null() image = np.copy(temp.image_buffer) # Extract middle of x coordinates to get square photo (photos are 4096x2160) image = image[:, 968:3128] self.cam.disarm() return image # Initialize camera for video recording def startVideoStream(self): if self.isStreaming: print("Video is already streaming") return None self.isStreaming = True self.cam.frames_per_trigger_zero_for_unlimited = 0 self.cam.arm(1) self.cam.issue_software_trigger() self.vidThread = threading.Thread(target=self.getVideoStream) self.vidThread.start() return self # Start def getVideoStream(self): temp = self.cam.get_pending_frame_or_null() image = np.copy(temp.image_buffer) image = image[:, 968:3128] return image # End a video feed for a camera def endVideoStream(self): self.isStreaming = False self.vidThread.join() self.cam.frames_per_trigger_zero_for_unlimited = 1 self.cam.disarm()
class TLCamera: def __init__(self, timeout=500): self.sdk = TLCameraSDK() cameralist = self.sdk.discover_available_cameras() if len(cameralist) == 0: print("Error: no cameras detected!") self.camera = self.sdk.open_camera(cameralist[0]) # setup the camera for continuous acquisition self.camera.frames_per_trigger_zero_for_unlimited = 0 self.camera.image_poll_timeout_ms = 2000 # 2 second timeout self.camera.arm(2) self.camera.exposure_time_us = 100 # save these values to place in our custom TIFF tags later bit_depth = self.camera.bit_depth exposure = self.camera.exposure_time_us # need to save the image width and height for color processing image_width = self.camera.image_width_pixels image_height = self.camera.image_height_pixels print(image_width) print(image_height) self.image = None def get_image(self): self.camera.issue_software_trigger() frame = self.camera.get_pending_frame_or_null() self.image = frame.image_buffer return self.image def close(self): self.camera.disarm() return def set_exposure(self, exposure_us): self.camera.exposure_time_us = exposure_us return def get_exposure(self): return self.camera.exposure_time_us
class TSI_Library: """Builds the functionality of the Thorlabs Scientific Imaging (TSI) camera SDK library. """ def __init__(self): """Constructor. Takes no arguments but tries to automatically configure the system path and creates a list of all connected cameras. """ self._cam_list = [] self._sn = None self._model = None self._swidth = 0 self._sheight = 0 self._rgb = 0 self._image = None self._imgID = None self.configure_path() self.get_cameras() def configure_path(self): """Configure the system path to include the directory containing the dlls. User must specify the correct path for the indicated variable within the function code. """ logger.debug("Configure system path..") is_64bits = sys.maxsize > 2**32 # change the value below to define dll system path path_to_dlls = r"C:\Program Files\Thorlabs\Scientific Imaging\Scientific Camera Support"\ r"\Scientific_Camera_Interfaces-Rev_G\Scientific Camera Interfaces\SDK"\ r"\Native Compact Camera Toolkit\dlls" if is_64bits: path_to_dlls += r"\Native_64_lib" else: path_to_dlls += r"\Native_32_lib" os.environ['PATH'] = path_to_dlls + os.pathsep + os.environ['PATH'] logger.info((path_to_dlls + " added to system path")) def get_cameras(self): """Finds the connected TSI cameras. """ with TLCameraSDK() as sdk: available_cameras = sdk.discover_available_cameras() nCams = len(available_cameras) logger.info(("Found %d tsi camera(s)" % nCams)) if nCams > 0: self._cam_list = available_cameras def connect(self, sn): """Connect to the camera with the given serial number. :param str sn: Camera serial number. """ self.sdk = TLCameraSDK() self.camera = self.sdk.open_camera(sn) self._sn = self.camera.serial_number self._model = self.camera.model self._swidth = self.camera.sensor_width_pixels self._sheight = self.camera.sensor_height_pixels self._rgb = (self.camera.camera_sensor_type == 1) self._bitsperpixel = self.camera.bit_depth logger.info( ("Sensor: %d x %d pixels, RGB = %d, %d bits/px" % (self._swidth, self._sheight, self._rgb, self._bitsperpixel))) self.camera.operation_mode = 0 # software-triggered self.camera.frames_per_trigger_zero_for_unlimited = 1 # single frame per trigger self.camera.is_frame_rate_control_enabled = False # disable frame rate control self.fpsmin = self.camera.frame_rate_control_value_range.min # min fps self.fpsmax = self.camera.frame_rate_control_value_range.max # max fps self.expmin = self.camera.exposure_time_range_us.min / 1000. # exposure min in ms self.expmax = self.camera.exposure_time_range_us.max / 1000. # exposure max in ms logger.info(("Valid exposure times: %fms to %fms" % (self.expmin, self.expmax))) self.set_exposure(self.expmin) # set exposure to min value self.camera.image_poll_timeout_ms = 5000 # 5 second polling timeout self.camera.arm(2) # arm the camera with a two-frame buffer def disconnect(self): """Disconnect a currently connected camera. """ self.camera.disarm() self.camera.dispose() self.sdk.dispose() def get_pixel_width(self): """Gets pixel width in microns. """ return self.camera.sensor_pixel_width_um def get_sensor_size(self): """Returns the sensor size in pixels as a tuple: (width, height) If not connected yet, it returns a zero tuple. """ return self._swidth, self._sheight def get_image_size(self): """Returns the image size in pixels as a tuple: (width, height) """ return self.camera.image_width_pixels, self.camera.image_height_pixels def get_frame_rate_limits(self): """Returns the frame rate limits in fps (min, max, increment=0.1). """ return self.camera.frame_rate_control_value_range.min, self.camera.frame_rate_control_value_range.max, 0.1 def set_gain(self, gain): """Set the hardware gain. :param int gain: New gain setting. """ self.camera.gain = int(gain) def get_gain(self): """Returns current gain setting. """ return self.camera.gain def get_gain_limits(self): """Returns gain limits (min, max, increment=1). """ return self.camera.gain_range.min, self.camera.gain_range.max, 1 def set_blacklevel(self, blck): """Set blacklevel. :param int blck: New blacklevel setting. """ self.camera.black_level = int(blck) def get_blacklevel_limits(self): """Returns blacklevel limts (min, max). """ return self.camera.black_level_range.min, self.camera.black_level_range.max def set_exposure(self, exp): """Set exposure time in milliseconds. :param int exp: New exposure time. """ self.camera.exposure_time_us = int(exp * 1000) def get_exposure(self): """Returns current exposure time in milliseconds. """ return self.camera.exposure_time_us / 1000. def get_exposure_limits(self): """Returns the supported limits for the exposure time in ms (min, max, increment). """ _min = self.camera.exposure_time_range_us.min / 1000. # exposure min in ms _max = self.camera.exposure_time_range_us.max / 1000. # exposure max in ms if self.camera.usb_port_type == 2: inc = 1000 / (1125 * self.camera.frame_rate_control_value_range.max ) # exposure step size for usb3.0 else: inc = 1000 / (582 * self.camera.frame_rate_control_value_range.max ) # exposure step size for usb2.0 return _min, _max, inc def set_roi(self, posx, posy, width, height): """Set the ROI. """ self.camera.roi = (posx, posy, width + posx, height + posy) def get_roi(self): """Returns the current ROI. """ return self.camera.roi def arm(self, N=2): """Arms the camera with an N-frame buffer. """ self.camera.arm(N) def trigger(self): """Issues a software trigger. """ self.camera.issue_software_trigger() def disarm(self): """Disarms the camera. """ self.camera.disarm() def get_is_armed(self): """Returns True if the camera is armed, False if unarmed. """ return self.camera.is_armed def acquire(self, N=1, native=False): """Synchronously captures some frames from the camera using the current settings and returns the averaged image. :param int N: Number of frames to acquire (>= 1). :returns: Averaged image. """ logger.debug(("acquire %d frames" % N)) data = None for i in range(int(N)): self.camera.issue_software_trigger() logger.debug(" wait for data..") frame = self.camera.get_pending_frame_or_null() self._image = frame.image_buffer self._imgID = frame.frame_count logger.debug(" read data..") if data is None: if native: data = self._image else: data = self._image.astype(float) else: data = data + self._image if not native: data = data / float(N) return data def acquireBinned(self, N=1): """Record N frames from the camera using the current settings and return fully binned 1d arrays averaged over the N frames. :param int N: Number of images to acquire. :returns: - Averaged 1d array fully binned over the x-axis. - Averaged 1d array fully binned over the y-axis. - Maximum pixel intensity before binning, e.g. to detect over illumination. """ data = self.acquire(N) return np.sum(data, axis=0), np.sum(data, axis=1), np.amax(data) def acquireMax(self, N=1): """Record N frames from the camera using the current settings and return the column / row with the maximum intensity. :param int N: Number of images to acquire. :returns: - Column with maximum intensity (1d array). - Row with maximum intensity (1d array). """ data = self.acquire(N) return data[ np.argmax(np.data, axis=0), :], data[:, np.argmax(np.data, axis=1)]
class thorcamDevice(device): """ Class providing support for Thorlabs scientific cameras. """ def __init__(self,params={},quiet=True,**kwargs): self.config = {} # user-variable configuration parameters go here (ie scale, offset, eng. units) self.params = params # fixed configuration paramaters go here (ie USB PID & VID, raw device units) self.driverConnected = False # Goes to True when scan method succeeds self.name = "uninitialized" self.lastValue = None # Last known value (for logging) self.lastValueTimestamp = None # Time when last value was obtained self.driver = self.params['driver'].split('/')[1:] self.quiet = quiet if not 'live_preview' in self.params: if not 'live_preview' in kwargs: self.live_preview=False else: self.live_preview=kwargs['live_preview'] else: self.live_preview=self.params['live_preview'] if not 'ID' in self.params: self.params['ID'] = None # default OpenCV camera ID number if not 'debugMode' in self.params: self.params['debugMode']=False if params is not {}: self.scan(quiet=quiet) return # Detect if device is present def scan(self,override_params=None,quiet=False): if override_params is not None: self.params = override_params # check USB device exists try: self.usbdev = usb.core.find(idVendor=self.params['vid'], idProduct=self.params['pid']) self.activate(quiet=quiet) except: raise return # Establish connection to device def activate(self,quiet=False): # begin camera id block self.userChoseStream = False validCamRange = [None, None] self.camera_sdk = TLCameraSDK() self.mono_to_color_sdk = MonoToColorProcessorSDK() try: self.available_cameras = self.camera_sdk.discover_available_cameras() except UnicodeDecodeError: cprint("Thorlabs SDK Error: Please power cycle camera",'red',attrs=['bold']) exit() if len(self.available_cameras) < 1: cprint("no cameras detected! Check udev rules",'red',attrs=['bold']) return elif len(self.available_cameras) > 1: for i in range(len(self.available_cameras)): print("%i: %s" % (i,self.available_cameras[i])) while not self.params['ID'] in validCamRange: try: self.params['ID']=int(raw_input("\tPlease select camera ID [0-%i]:" % (len(self.available_cameras)-1))) self.userChoseStream = True except: pass else: self.params['ID']=0 # End camera id block # Set up configuration for number of frames to capture per query event if 'n_frames' in self.params: self.config['n_frames'] = self.params['n_frames'] else: self.config['n_frames'] = 1 # Make first query to get units, description, etc. self.query(reset=True) if not quiet: self.pprint() return # Deactivate connection to device (close serial port) def deactivate(self): self.driverConnected=False del self.opencvdev return # Apply configuration changes to the driver (subdriver-specific) def apply_config(self): subdriver = self.params['driver'].split('/')[1:] try: assert(self.deviceClass) if self.deviceClass is None: raise pyLabDataLoggerIOError("Could not access device.") # Apply subdriver-specific variable writes if subdriver=='?': #... pass else: raise RuntimeError("I don't know what to do with a device driver %s" % self.params['driver']) except ValueError: cprint( "%s - Invalid setting requested" % self.name, 'red', attrs=['bold']) return # Update configuration (ie change sample rate or number of samples) def update_config(self,config_keys={}): for key in config_keys.keys(): self.config[key]=self.config_keys[key] self.apply_config() return def updateTimestamp(self): self.lastValueTimestamp = datetime.datetime.now() # Re-establish connection to device. def reset(self): self.deactivate() self.scan() if self.driverConnected: self.activate() else: cprint( "Error resetting %s: device is not detected" % self.name, 'red', attrs=['bold']) # Handle query for values def query(self, reset=False): # If first time or reset, get configuration (ie units) if not 'raw_units' in self.params.keys() or reset: # Set up device self.name = self.params['name'] # from usbDevice lookup table, usb descriptor data often empty if self.config['n_frames'] == 1: self.config['channel_names']=['Image'] else: self.config['channel_names']=['Frame %i' % j for j in range(self.config['n_frames'])] self.params['raw_units']=['Intensity'] self.config['eng_units']=['Intensity'] self.config['scale']=[1.] self.config['offset']=[0.] self.params['n_channels']=self.config['n_frames'] self.frame_counter = -1 # reset frame counter. It'll be incremented up to zero before the first save. # Try to open camera stream. self.camera = self.camera_sdk.open_camera(self.available_cameras[self.params['ID']]) self.camera.frames_per_trigger_zero_for_unlimited = 0 # start camera in continuous mode self.camera.image_poll_timeout_ms = 2000 # 2 second timeout self.driverConnected=True # Setup post-processor for colour images self.mono_to_color_processor = self.mono_to_color_sdk.create_mono_to_color_processor( self.camera.camera_sensor_type, self.camera.color_filter_array_phase, self.camera.get_color_correction_matrix(), self.camera.get_default_white_balance_matrix(), self.camera.bit_depth) self.mono_to_color_processor.color_space = COLOR_SPACE.SRGB # sRGB color space self.mono_to_color_processor.output_format = FORMAT.RGB_PIXEL # data is returned as sequential RGB values self.params['red gain']=red_gain=self.mono_to_color_processor.red_gain self.params['green gain']=green_gain=self.mono_to_color_processor.green_gain self.params['blue gain']=blue_gain=self.mono_to_color_processor.blue_gain if self.live_preview: self.fig = plt.figure() self.ax = self.fig.add_subplot(111) self.ax.axis('off') #plt.ion() plt.show(block=False) plt.pause(.1) # Check try: assert(self.camera) if self.camera is None: raise pyLabDataLoggerIOError except: cprint( "Connection to the device is not open.", 'red', attrs=['bold']) # Get Image(s) rawFrames = [] self.camera.arm(self.config['n_frames']) self.params['width'] = self.camera.image_width_pixels self.params['height'] = self.camera.image_height_pixels for j in range(self.config['n_frames']): self.camera.issue_software_trigger() rawframe = self.camera.get_pending_frame_or_null() self.frame_counter += 1 # increment counter even if image not returned rawFrames.append(rawframe) if rawframe is None: cprint("\tNo frame arrived from Thorlabs camera within the timeout!",'red') self.camera.disarm() if not self.quiet: cprint("\tCaptured %i/%i frames" % (len([f for f in rawFrames if f is not None]),self.config['n_frames']),'green') # Post process image(s) self.lastValue=[] livePreviewImg=None for rawframe in rawFrames: if rawframe is None: self.lastValue.append(np.nan) else: # this will give us a resulting image with 3 channels (RGB) and 16 bits per channel, resulting in 48 bpp color_image_16bit = self.mono_to_color_processor.transform_to_48(rawframe.image_buffer, self.params['width'], self.params['height']) # this will give us a resulting image with 4 channels (RGBA) and 8 bits per channel, resulting in 32 bpp #color_image = self.mono_to_color_processor.transform_to_32((rawframe.image_buffer, self.params['width'], self.params['height']) # this will give us a resulting image with 3 channels (RGB) and 8 bits per channel, resulting in 24 bpp color_image_8bit = self.mono_to_color_processor.transform_to_24(rawframe.image_buffer, self.params['width'], self.params['height']) # copy to lastValue if livePreviewImg is None: livePreviewImg = color_image_8bit[...].reshape(self.params['height'], self.params['width'], 3) self.lastValue.append(color_image_16bit[...].reshape(self.params['height'], self.params['width'], 3)) # Set up live preview mode if requested if self.live_preview: cprint("\tlive_preview: displaying frame_%08i %s" % (self.frame_counter,livePreviewImg.shape), 'green') try: assert self.imshow self.imshow.set_data(livePreviewImg) except: self.imshow = self.ax.imshow(livePreviewImg) try: self.ax.set_title("Frame %06i : %s" % (self.frame_counter,self.lastValueTimestamp)) self.fig.canvas.draw() #plt.show(block=False) plt.pause(0.01) # 10 ms for window to refresh itself. except: cprint( "\tError updating Thorlabs preview window", 'red', attrs=['bold']) # Generate scaled values. Convert non-numerics to NaN lastValueSanitized = [] for v in self.lastValue: if v is None: lastValueSanitized.append(np.nan) else: lastValueSanitized.append(v) self.lastScaled = np.array(lastValueSanitized) * self.config['scale'] + self.config['offset'] self.updateTimestamp() return self.lastValue # log to HDF5 file - overload function def log_hdf5(self, filename, max_records=None): try: import h5py except ImportError: cprint( "Please install h5py", 'red', attrs=['bold']) return # Open file with h5py.File(filename, 'a') as fh: # Load group for this device. if 'name' in self.config: devname = self.config['name'] elif 'name' in self.params: devname = self.params['name'] else: devname = self.name if devname in fh: dg = fh[devname] else: dg = fh.create_group(devname) # On creation, add attributes from config and params for attr in self.params.keys(): dg.attrs[attr] = repr(self.params[attr]) for attr in self.config.keys(): dg.attrs[attr] = repr(self.config[attr]) # Create/update timestamp dataset # Each device has a lastValueTimestamp which can vary a bit between devices due to latency issues. if 'timestamp' in dg: td = dg['timestamp'] td.resize([td.shape[0]+1,]) td[-1] = str(self.lastValueTimestamp) else: dg.create_dataset('timestamp',data=[str(self.lastValueTimestamp)],maxshape=(max_records,)) # Write images into dg... for j in range(len(self.lastValue)): dsname = 'frame_%08i' % (self.frame_counter-len(self.lastValue)+j+1) if dsname in dg: cprint( "\tOverwriting image %s in HDF5 log file!" % dsname, 'yellow') del dg[dsname] # Flip colours in dataset - 21/5/20 # Swap to uint16, this might not work in HDFView but keep all the bits anyway! 17/9/20 dset=dg.create_dataset(dsname, data=np.flip(self.lastValue[j],axis=2), dtype='uint16', chunks=True,\ compression='gzip', compression_opts=1) # apply fast compression #Set the image attributes dset.attrs.create('CLASS', 'IMAGE') dset.attrs.create('IMAGE_VERSION', '1.2') dset.attrs.create('IMAGE_SUBCLASS', 'IMAGE_TRUECOLOR') dset.attrs.create('INTERLACE_MODE', 'INTERLACE_PIXEL') #dset.attrs['IMAGE_COLORMODEL'] = 'RGB' fh.close() # log to text file - overload function def log_text(self, filename): # In "text" mode, perhaps just save a JPEG file? raise RuntimeError("Not implemented")
class TLCamera(): def __init__(self): #variables self.FILECOUNTER = 0 self.counter = 0 # 0 = background Image ; 1 = wave Image self.singleTriggerMode = True self.maxIntensity = 65536 self.minIntensity = 0 #open camera and setup settings: self.sdk = TLCameraSDK() cameras = self.sdk.discover_available_cameras() if len(cameras) == 0: print("Error: no cameras detected!") self.camera = self.sdk.open_camera(cameras[0]) self.camera.frames_per_trigger_zero_for_unlimited = 1 self.camera.operation_mode = OPERATION_MODE.BULB # set Hardwaretrigger self.camera.trigger_polarity = TRIGGER_POLARITY.ACTIVE_HIGH # Trigger auf active HIGH #self.camera.exposure_time_us = 10 print("Exposure Time :", self.camera.exposure_time_us) print("Bit Depth: ", self.camera.bit_depth) print(self.camera.operation_mode) print(self.camera.trigger_polarity) self.camera.image_poll_timeout_ms = 1000 # 1 second timeout self.camera.arm(2) def setSingleTriggerMode(self): self.singleTriggerMode = True cv2.destroyWindow('Live') self.camera.image_poll_timeout_ms = 1000 # 1 second timeout def setContinuousTriggerMode(self): self.singleTriggerMode = False cv2.destroyWindow('BGimg_1') cv2.destroyWindow('BGimg_2') cv2.destroyWindow('WVimg') cv2.destroyWindow('DifferenceImage') self.camera.image_poll_timeout_ms = 1000 def setGain(self, value): self.camera.gain(value) def snapImg(self): frame = self.camera.get_pending_frame_or_null() if frame is None: raise TimeoutError( "Timeout was reached while polling for a frame, program will now exit" ) image_data = frame.image_buffer #self.ImProc.imageProcess(self.image_data) self.showImage(image_data) def closeCamera(self): self.camera.disarm() self.camera.dispose() def showSettings(self): print("Settings:") print("Exposure Time :", self.camera.exposure_time_us) print("Bit Depth: ", self.camera.bit_depth) print(self.camera.operation_mode) print(self.camera.trigger_polarity) def showImage(self, image): if self.singleTriggerMode == True: if self.counter == 0: self.BGimg_1 = image cv2.imshow("BGimg_1", self.BGimg_1) cv2.waitKey(1) self.counter = 1 elif self.counter == 1: self.WVimg = image cv2.imshow("WVimg", self.WVimg) self.Diffimg = cv2.absdiff(self.BGimg_1, self.WVimg) self.diff_scaled = cv2.normalize(self.Diffimg, None, self.minIntensity, self.maxIntensity, norm_type=cv2.NORM_MINMAX) cv2.imshow('DifferenceImage', self.diff_scaled) cv2.waitKey(1) self.counter = 2 else: self.BGimg_2 = image cv2.imshow("BGimg_2", self.BGimg_2) cv2.waitKey(1) self.counter = 0 else: cv2.imshow("Live", image) cv2.waitKey(1) def saveImage(self, output_directory): tiff = tifffile.TiffWriter(output_directory + os.sep + str(self.FILECOUNTER).zfill(2) + ".tif", append=True) tiff.save( data=self.BGimg_1, # np.ushort image data array from the camera compress= 0, # amount of compression (0-9), by default it is uncompressed (0) extratags=[ (TAG_BITDEPTH, 'I', 1, self.camera.bit_depth, False), # custom TIFF tag for bit depth (TAG_EXPOSURE, 'I', 1, self.camera.exposure_time_us, False) ] # custom TIFF tag for exposure ) tiff = tifffile.TiffWriter(output_directory + os.sep + str(self.FILECOUNTER).zfill(2) + ".tif", append=True) tiff.save( data=self.WVimg, # np.ushort image data array from the camera compress= 0, # amount of compression (0-9), by default it is uncompressed (0) extratags=[ (TAG_BITDEPTH, 'I', 1, self.camera.bit_depth, False), # custom TIFF tag for bit depth (TAG_EXPOSURE, 'I', 1, self.camera.exposure_time_us, False) ] # custom TIFF tag for exposure ) tiff = tifffile.TiffWriter(output_directory + os.sep + str(self.FILECOUNTER).zfill(2) + ".tif", append=True) tiff.save( data=self.BGimg_2, # np.ushort image data array from the camera compress= 0, # amount of compression (0-9), by default it is uncompressed (0) extratags=[ (TAG_BITDEPTH, 'I', 1, self.camera.bit_depth, False), # custom TIFF tag for bit depth (TAG_EXPOSURE, 'I', 1, self.camera.exposure_time_us, False) ] # custom TIFF tag for exposure ) tiff = tifffile.TiffWriter(output_directory + os.sep + str(self.FILECOUNTER).zfill(2) + ".tif", append=True) tiff.save( data=self.Diffimg, # np.ushort image data array from the camera compress= 0, # amount of compression (0-9), by default it is uncompressed (0) extratags=[ (TAG_BITDEPTH, 'I', 1, self.camera.bit_depth, False), # custom TIFF tag for bit depth (TAG_EXPOSURE, 'I', 1, self.camera.exposure_time_us, False) ] # custom TIFF tag for exposure ) self.FILECOUNTER = self.FILECOUNTER + 1 def setMaxIntensity(self, maxIntensity): self.maxIntensity = maxIntensity def setMinIntensity(self, minIntensity): self.minIntensity = minIntensity