Source code for Cameras.CameraClassDef

# -*- coding: utf-8 -*- 

"""
Define generic camera driver class CameraClass
"""

import numpy as np
from .Config import camerasConfigs

[docs] class CameraClass: """Top Parent Class for controlling camera : used as an interface"""
[docs] def __init__(self, cameraNumber, triggerMode=0, exposurems=1., gaindB=0., camROI=None, loadDefault=False ): """Initialize the Camera object Args: cameraNumber (int) : index of camera in camerasConfigs list Keyword Args: triggerMode (int) : 0 for hardware/external, 1 for software/internal exposurems (float) : Exposition duration (exposure) in ms. gaindB (float) : hardware gain of the camera in dB. camROI (None or [int]*4) : Camera region of interest to read from sensor [x offset , y offset , x size , y size ] (binning is not implemented) loadDefault (bool): Decide if default values from cameraConfigs should be set at creation Return: CameraClass object """ self.cameraDriverInstalled = False self.cameraConnected = False # define common variables of acquisition self._cameraNumber = cameraNumber if self._cameraNumber > len(camerasConfigs)-1 : raise NameError('Camera number higher than config array') elif camerasConfigs[self._cameraNumber] is None : if self._cameraNumber > 0 : print('ERROR ! : No config for this camera number') self.cameraConfig = camerasConfigs[self._cameraNumber] else : self.cameraConfig = camerasConfigs[self._cameraNumber] if ('defaultTrigger' in self.cameraConfig) and loadDefault : if self.cameraConfig['defaultTrigger'] == 'external': triggerMode = 0 else : triggerMode = 1 if ('defaultGaindB' in self.cameraConfig) and loadDefault: gaindB = self.cameraConfig['defaultGaindB'] if ('defaultExposurems' in self.cameraConfig) and loadDefault: exposurems = self.cameraConfig['defaultExposurems'] if ('defaultCamROI' in self.cameraConfig) and loadDefault: camROI = self.cameraConfig['defaultCamROI'] self.triggerMode = triggerMode # 0='external' , 1='software' self.triggerModeTextList = ['external','software'] self.gaindB = gaindB #gain of the camera in dB self.gaindBMin = None # minimum gain in dB self.gaindBMax = None # maximum gain in dB self.exposurems = exposurems # exposure in milliseconds self.exposuremsMin = None self.exposuremsMax = None self.exposureAutoAvLevel = None self.exposureAutoAvRange = None self.exposureAutoMaxLevel = None self.exposureAutoMaxRange = None self.exposureIncreaseHardwareGain = None self.timeout = 10 #timeout in seconds self.imageAcqLastFailed = False # image size self.wpxmax = 1 # width max of camera self.hpxmax = 1 # height max of camera self.wpx = self.wpxmax # width of camera self.hpx = self.hpxmax # height of camera self.camROI = camROI #Camera region of interest (read part of sensor) [OffsetX, OffsetY, Width, Height] self.imageSize = (self.hpx,self.wpx) #in numpy axes are reversed self.pixelCalXumperpx = 1. # calibration from pixels to micrometers along X axis self.pixelCalYumperpx = 1. # calibration from pixels to micrometers along Y axis self.imageLimits = [-self.wpx/2*self.pixelCalXumperpx, self.wpx/2*self.pixelCalXumperpx, -self.hpx/2*self.pixelCalYumperpx, self.hpx/2*self.pixelCalYumperpx] # in micrometers self.reversedAxes = [False,False] #image arrays self.imageBitDepth = 8 # read sensor bit depth self.maxLevel = 2**self.imageBitDepth - 1 self.imageDtype = np.uint8 # image bith depth (dtype in numpy) : typically 8 or 16 bits => uint8 or uint16 self.image = np.array(self.imageSize, dtype=self.imageDtype) #last image taken self._image = np.array(self.imageSize, dtype=self.imageDtype) #PROTECTED pointer for loading camera image
[docs] def __del__(self): """Delete the Camera object by calling close function Close the Camera and free memory""" del(self._image, self.image)
[docs] def setTriggerMode(self, triggerMode): """Set the trigger mode Args: triggerMode (int) : 0 for hardware/external, 1 for software/internal """ raise NotImplementedError('Funtion SetTriggerMode not defined')
[docs] def sendSoftwareTrigger(self): """Send a software trigger to the camera to start an exposure""" raise NotImplementedError('Funtion sendSoftwareTrigger not defined')
[docs] def setExposurems(self,exposurems): """Set the duration of the exposition Args: exposurems (float) : Exposition duration (exposure) in ms. """ raise NotImplementedError('Function setExposure not defined')
[docs] def setGaindB(self,gaindB): """Set the hardware gain of camera readout Args: gaindB (float) : hardware gain of the camera in dB. """ raise NotImplementedError('Function setGaindB not defined')
[docs] def setCamROI(self, ROI=None) : """Set the ROI of camera (without binning) Keyword Args: ROI ( None or [int]*4) : [ x offset, y offset, x size, y size], if None, set to [0, 0, max Width, max height] Return: Camera ROI ([int]*4) : [x offset, y offset, x size, y size] """ raise NotImplementedError('Function setCamROI not defined')
[docs] def startAcquisition(self): """Start acquisition or restart if already running """ raise NotImplementedError('Function startAcquisition not defined')
[docs] def clearBuffer(self) : """ Clear camera buffer from images. Use before imaging scans to make sure no previously acquired image is present in buffer""" raise NotImplementedError('Function startAcquisition not defined')
[docs] def grabArray(self): """Get an image from the camera. Wait for trigger if external or generate one if internal. Return: Camera image (numpy 2D array) """ raise NotImplementedError('Function grabArray not defined')
[docs] def exposureLevelAutoAdjust(self, attemptsMax=20, Optimization = 'Max'): """Automatic adjustment of exposure for setting image 'Max' or 'Average' to a given level. Can use gaindB increase to reach specified level if maximum exposure is not sufficient. Keyword Args: attemptsMax (int) : Maximum number of steps (images) in optimization Optimization (str) : Select image value to optimize 'Max' or 'Average' Return: Optimized image value (int) """ self.setGaindB(0) attempts = 0 image = self.grabArray() if self.imageAcqLastFailed : return False else : if Optimization == 'Average': level = self.exposureAutoAvLevel/100. * self.maxLevel acceptedRange = self.exposureAutoAvRange/100. * self.maxLevel imageValue = image.mean() elif Optimization == 'Max': level = self.exposureAutoMaxLevel/100. * self.maxLevel acceptedRange = self.exposureAutoMaxRange/100. * self.maxLevel imageValue = image.max() else : raise NameError('Optimisation criterion not defined in exposureLevelAutoAdjust') exposure = self.exposurems old_exposure = 0. new_exposure = 0. while abs(imageValue - level) > acceptedRange \ and attempts < attemptsMax: if new_exposure < self.exposuremsMin \ and old_exposure == exposure: print("WARNING ! : Camera : exposureLevelAutoAdjust \n"\ + "Could not adjust exposure to specified level \n"\ + "Ideal point lower than exposure minimum") break if new_exposure > self.exposuremsMax \ and old_exposure == exposure: if self.increaseGain : if self.gaindB > (self.gaindBMax-0.1) : print("WARNING ! : Camera : exposureLevelAutoAdjust"\ +"\n Could not adjust exposure to specified" \ +"max level \n Ideal point Higher than "\ +"exposure maximum\n Maximum hardware gain"\ +"was used") break else: self.setGaindB(self.gaindB + 3) old_exposure = 0. continue else : print("WARNING !: Camera : exposure_max_level_auto_adjust"\ +"\n Could not adjust exposure to specified level"\ +"level \n Ideal point Higher than exposure"\ +"maximum\n Consider increasing hardware gain") break attempts += 1 self.setExposurems(new_exposure) exposure = self.exposurems # return previous image sometime (unknow reason) image = self.grabArray() if Optimization == 'Average': imageValue = image.mean() elif Optimization == 'Max': imageValue = image.max() if imageValue == self.maxLevel : new_exposure = exposure/4. else : new_exposure = exposure * level / (imageValue+ 0.01) if attempts == attemptsMax : print("WARNING ! : Camera : exposure_max_level_auto_adjust \n"\ +"Could not adjust exposure to specified max level") return False return imageValue