def acquireFrame(self, allowBlanking=True): """Acquire one frame and emit sigNewFrame. """ with self.lock: prot = self.protocol meta = self.metainfo rectSystem = self.system # Need to build task from a deep copy of the protocol because # it will be modified after execution. task = self.manager.createTask(copy.deepcopy(prot)) dur = prot['protocol']['duration'] start = pg.ptime.time() endtime = start + dur - 0.005 # Start the task task.execute(block = False) # Wait until the task has finished while not task.isDone(): with self.lock: abort = self._abort if abort: task.abort() self._abort = False raise Exception("Imaging acquisition aborted") now = pg.ptime.time() if now < endtime: # long sleep until we expect the protocol to be almost done time.sleep(min(0.1, endtime-now)) else: time.sleep(5e-3) # Get acquired data and generate metadata data = task.getResult() pdDevice, pdChannel = meta['Photodetector'] pmtData = data[pdDevice][pdChannel].view(np.ndarray) info = meta.copy() info['time'] = start info['deviceTranform'] = pg.SRTTransform3D(self.scannerDev.globalTransform()) tr = rectSystem.imageTransform() info['transform'] = pg.SRTTransform3D(tr) frame = ImagingFrame(pmtData, rectSystem.copy(), info) self.sigNewFrame.emit(frame)
def newFrames(self): """Return a list of all frames acquired since the last call to newFrames.""" now = ptime.time() dt = now - self.lastFrameTime exp = self.getParam('exposure') bin = self.getParam('binning') fps = 1.0 / (exp + (40e-3 / (bin[0] * bin[1]))) nf = int(dt * fps) if nf == 0: return [] region = self.getParam('region') bg = self.getBackground()[region[0]:region[0] + region[2], region[1]:region[1] + region[3]] ## update cells spikes = np.random.poisson(min(dt, 0.4) * self.cells['rate']) self.cells['value'] *= np.exp(-dt / self.cells['decayTau']) self.cells['value'] = np.clip(self.cells['value'] + spikes * 0.2, 0, 1) shape = region[2:] self.lastFrameTime = now + exp data = self.getNoise(shape) data[data < 0] = 0 data += bg * (exp * 1000) ## draw cells px = (self.pixelVectors()[0]**2).sum()**0.5 ## Generate transform that maps grom global coordinates to image coordinates cameraTr = pg.SRTTransform3D(self.inverseGlobalTransform()) # note we use binning=(1,1) here because the image is downsampled later. frameTr = self.makeFrameTransform(region, [1, 1]).inverted()[0] tr = pg.SRTTransform(frameTr * cameraTr) for cell in self.cells: w = cell['size'] / px pos = pg.Point(cell['x'], cell['y']) imgPos = tr.map(pos) start = (int(imgPos.x()), int(imgPos.y())) stop = (int(start[0] + w), int(start[1] + w)) val = cell['intensity'] * cell['value'] * self.getParam('exposure') data[max(0, start[0]):max(0, stop[0]), max(0, start[1]):max(0, stop[1])] += val data = fn.downsample(data, bin[0], axis=0) data = fn.downsample(data, bin[1], axis=1) data = data.astype(np.uint16) self.frameId += 1 frames = [] for i in range(nf): frames.append({ 'data': data, 'time': now + (i / fps), 'id': self.frameId }) return frames
def posChanged(self, pos): """Handle device position changes by updating the device transform and emitting sigPositionChanged. Subclasses must call this method when the device position has changed. """ with self.lock: rel = [0] * len(self.pos) rel[:len(pos)] = [pos[i] - self.pos[i] for i in range(len(pos))] self.pos[:len(pos)] = pos self._stageTransform = pg.SRTTransform3D() self._stageTransform.translate(*self.pos) self._invStageTransform = pg.SRTTransform3D() self._invStageTransform.translate(*[-x for x in self.pos]) self._updateTransform() self.sigPositionChanged.emit({'rel': rel, 'abs': self.pos[:]}) self.checkSwitchChange(self.pos)
def setDeviceTransform(self, tr): with self.__lock: self.__transform = pg.SRTTransform3D(tr) self.invalidateCachedTransforms() #print "setDeviceTransform", self #print " -> emit sigTransformChanged" #import traceback #traceback.print_stack() self.sigTransformChanged.emit(self)
def setStageOrientation(self, angle, inverty): tr = pg.SRTTransform3D(self.parentDevice().baseTransform()) tr.setScale(1, -1 if inverty else 1) tr.setRotate(angle) self.parentDevice().setBaseTransform(tr) cal = self.readConfigFile('calibration') cal['angle'] = angle cal['inverty'] = inverty self._stageOrientation = cal self.writeConfigFile(cal, 'calibration')
def posChanged(self, data): with self.lock: rel = [0] * len(self.pos) if 'rel' in data: rel[:len(data['rel'])] = data['rel'] else: rel[:len(data['abs'])] = [ data['abs'][i] - self.pos[i] for i in range(len(data['abs'])) ] self.pos[:len(data['abs'])] = data['abs'] self.sigPositionChanged.emit({'rel': rel, 'abs': self.pos[:]}) tr = pg.SRTTransform3D() tr.translate(*self.pos) self.setDeviceTransform( tr) ## this informs rigidly-connected devices that they have moved
def __computeGlobalTransform(self, subdev=None, inverse=False): ## subdev must be a dict with self.__lock: devices = self.parentDevices() transform = pg.SRTTransform3D() for d in devices: tr = d.deviceTransform(subdev) if tr is None: self.__globalTransform = None return None transform = tr * transform if inverse: inv, invertible = transform.inverted() if not invertible: raise Exception("Transform is not invertible.") return inv else: return transform
def __init__(self, dm, config, name): object.__init__(self) ## create proxy object and wrap in its signals self.__sigProxy = OptomechDevice.SignalProxyObject() self.sigTransformChanged = self.__sigProxy.sigTransformChanged self.sigGlobalTransformChanged = self.__sigProxy.sigGlobalTransformChanged self.sigSubdeviceTransformChanged = self.__sigProxy.sigSubdeviceTransformChanged self.sigGlobalSubdeviceTransformChanged = self.__sigProxy.sigGlobalSubdeviceTransformChanged self.sigSubdeviceChanged = self.__sigProxy.sigSubdeviceChanged self.sigGlobalSubdeviceChanged = self.__sigProxy.sigGlobalSubdeviceChanged self.sigSubdeviceListChanged = self.__sigProxy.sigSubdeviceListChanged self.sigGlobalSubdeviceListChanged = self.__sigProxy.sigGlobalSubdeviceListChanged self.__devManager = dm self.__config = config self.__children = [] self.__parent = None self.__globalTransform = 0 ## 0 indicates the cache is invalid. None indicates the transform is non-affine. self.__inverseGlobalTransform = 0 self.__transform = pg.SRTTransform3D() self.__inverseTransform = 0 self.__lock = Mutex(recursive=True) self.__subdevices = collections.OrderedDict() self.__subdevice = None self.__name = name self.sigTransformChanged.connect(self.__emitGlobalTransformChanged) self.sigSubdeviceTransformChanged.connect(self.__emitGlobalSubdeviceTransformChanged) self.sigSubdeviceChanged.connect(self.__emitGlobalSubdeviceChanged) self.sigSubdeviceListChanged.connect(self.__emitGlobalSubdeviceListChanged) if config is not None: if 'parentDevice' in config: try: self.setParentDevice(config['parentDevice']) except Exception as ex: if "No device named" in ex.message: print "Cannot set parent device %s; no device by that name." % config['parentDevice'] else: raise if 'transform' in config: self.setDeviceTransform(config['transform'])
def __computeGlobalTransform(self, subdev=None, inverse=False): ## subdev must be a dict parent = self.parentDevice() if parent is None: parentTr = pg.SRTTransform3D() else: parentTr = parent.globalTransform(subdev) if parentTr is None: return None deviceTr = self.deviceTransform(subdev) if deviceTr is None: return None transform = parentTr * deviceTr if inverse: inv, invertible = transform.inverted() if not invertible: raise Exception("Transform is not invertible.") return inv else: return transform
def setPosition(self, pos): self.pos = pos tr = pg.SRTTransform3D() tr.translate(pos) self.setDeviceTransform(tr) self.sigPositionChanged.emit(self)
def deviceTransform(self): return pg.SRTTransform3D(OptomechDevice.deviceTransform(self))
def setDeviceTransform(self, tr): OptomechDevice.setDeviceTransform(self, tr) cal = self.readConfigFile('calibration') cal['transform'] = pg.SRTTransform3D(tr) self.writeConfigFile(cal, 'calibration')
def __init__(self, dm, config, name): object.__init__(self) ## create proxy object and wrap in its signals self.__sigProxy = OptomechDevice.SignalProxyObject() self.sigTransformChanged = self.__sigProxy.sigTransformChanged self.sigGlobalTransformChanged = self.__sigProxy.sigGlobalTransformChanged self.sigSubdeviceTransformChanged = self.__sigProxy.sigSubdeviceTransformChanged self.sigGlobalSubdeviceTransformChanged = self.__sigProxy.sigGlobalSubdeviceTransformChanged self.sigOpticsChanged = self.__sigProxy.sigOpticsChanged self.sigGlobalOpticsChanged = self.__sigProxy.sigGlobalOpticsChanged self.sigSubdeviceChanged = self.__sigProxy.sigSubdeviceChanged self.sigGlobalSubdeviceChanged = self.__sigProxy.sigGlobalSubdeviceChanged self.sigSubdeviceListChanged = self.__sigProxy.sigSubdeviceListChanged self.sigGlobalSubdeviceListChanged = self.__sigProxy.sigGlobalSubdeviceListChanged self.__devManager = dm self.__config = config self.__name = name # __ports is a list of port names to which devices may be attached. # Most devices will have a single port called 'default' to which all children attach. # In some cases, the device has a specific set of named ports where children # can be attached (eg: a dichroic filter cube might have "excitation" and "emission" ports). self.__ports = [] # All devices have 0 or 1 parent self.__parent = None # When connected to a parent device, we also optionally specify *where* on # the device to connect (usually this is just 'default'). self.__parentPort = None # keep track of children so that we can inform them quickly when a parent transform has changed self.__children = [] # Cached transforms from this device to global # 0 indicates the cache is invalid. None indicates the transform is non-affine, # and might not be cacheable. self.__globalTransform = 0 self.__inverseGlobalTransform = 0 # Transformation from this device to its parent (or to global if there is no parent) self.__transform = pg.SRTTransform3D() # Cached inverse of __transform self.__inverseTransform = 0 # Contains {port: [list of optics]} describing the optics (usually filters) for each port self.__optics = {} # Swappable sub-devices. (eg: objective changers, filter wheels) self.__subdevices = collections.OrderedDict() self.__subdevice = None self.__lock = Mutex(recursive=True) self.sigTransformChanged.connect(self.__emitGlobalTransformChanged) self.sigSubdeviceTransformChanged.connect( self.__emitGlobalSubdeviceTransformChanged) self.sigOpticsChanged.connect(self.__emitGlobalOpticsChanged) self.sigSubdeviceChanged.connect(self.__emitGlobalSubdeviceChanged) self.sigSubdeviceListChanged.connect( self.__emitGlobalSubdeviceListChanged) if config is not None: if 'parentDevice' in config: try: parent = config['parentDevice'] if isinstance(parent, str): self.setParentDevice(parent) elif isinstance(parent, dict) and 'name' in parent: self.setParentDevice(parent['name'], port=parent.get( 'port', 'default')) else: raise TypeError( "Invalid parent device specification: %s" % repr(parent)) except Exception as ex: if "No device named" in ex.args[0]: print( "Cannot set parent device %s; no device by that name." % repr(config['parentDevice'])) else: raise if 'transform' in config: self.setDeviceTransform(config['transform']) self.__ports = config.get('ports', ['default']) assert isinstance(self.__ports, list) self.__optics = config.get('optics', {'default': []}) assert isinstance(self.__optics, dict) # declare that this device supports the OptomechDevice API self.addInterface('OptomechDevice')
def update(self): self.lastResult = [] frame = self.lastFrame if frame is None: self.clear() return # imageDownSample = self.ui.downSampling.value() # this is the "image" downsample, # get the downsample for the daq. This is far more complicated than it should be... # Get PMT signal(s) pmtdata = [] for detector in self.params.param('detectors'): data = frame['result'][detector.value()]["Channel":'Input'] t = data.xvals('Time') pmtdata.append(data.asarray()) if len(pmtdata) == 0: return # parse program options scanCmd = frame['cmd'][self.params['scanner']] if 'program' not in scanCmd: return progs = scanCmd['program'] if len(progs) == 0: self.image.setImage(np.zeros((1,1))) return # Update list so user can select program component supportedTypes = ['rect'] progs = dict([(prog['name'], prog) for prog in progs if prog['type'] in supportedTypes]) self.params.child('scanProgram').setLimits(progs.keys()) selectedProg = self.params['scanProgram'] if selectedProg not in progs: return prog = progs[selectedProg] if prog['type'] == 'rect': # keep track of some analysis in case it should be stored later result = { 'params': self.params.saveState(filter='user')['children'], 'pmtdata': pmtdata, } self.lastResult.append(result) rs = RectScan() rs.restoreState(prog['scanInfo']) result['scanParams'] = rs decomb = self.params['decomb'] # Extract from PMT array imageData = [] for chan in pmtdata: chanImage = rs.extractImage(chan, offset=decomb, subpixel=self.params['decomb', 'subpixel']) imageData.append(chanImage.reshape(chanImage.shape + (1,))) if len(imageData) == 1: imageData = imageData[0] levelMode = 'mono' else: if len(imageData) == 2: imageData.append(np.zeros(imageData[0].shape, dtype=imageData[0].dtype)) imageData = np.concatenate(imageData, axis=-1) levelMode = 'rgba' if imageData.size == 0: self.clear() raise Exception('image Data has zero size') # Downsample ds = self.params['downsample'] if ds > 1: imageData = pg.downsample(imageData, ds, axis=2) # Collected as (frame, row, col) but pg prefers images like (frame, col, row) imageData = imageData.transpose((0, 2, 1, 3)[:imageData.ndim]) result['image'] = imageData # compute global transform tr = rs.imageTransform() st = pg.QtGui.QTransform() st.scale(self.params['downsample'], 1) tr = st * tr result['transform'] = pg.SRTTransform3D(tr) frameTimes = rs.frameTimes() # Display image locally self.imageView.setImage(imageData, xvals=frameTimes, levelMode=levelMode) self.imageView.getView().setAspectLocked(True) # self.imageView.imageItem.setRect(QtCore.QRectF(0., 0., rs.width, rs.height)) # TODO: rs.width and rs.height might not be correct! self.imageView.imageItem.resetTransform() self.imageView.imageItem.scale((rs.width/rs.height)/(imageData.shape[1]/imageData.shape[2]), 1.0) self.imageView.autoRange() # Display image remotely (in the same camera module as used by the scanner device) if self.params['display']: self.img.setVisible(True) sd = self.pr.getDevice(self.params['scanner']) camMod = sd.cameraModule().window() camMod.addItem(self.img, z=1000) self.img.setImage(imageData.mean(axis=0)) self.img.setTransform(tr) else: self.img.setVisible(False) else: raise Exception("Imaging module only supports rect scans (not %s)." % prog['type'])
def __init__(self, image=None, **opts): """ CanvasItem displaying an image. The image may be 2 or 3-dimensional. Options: image: May be a fileHandle, ndarray, or GraphicsItem. handle: May optionally be specified in place of image """ ## If no image was specified, check for a file handle.. if image is None: image = opts.get('handle', None) item = None self.data = None self.currentT = None if isinstance(image, QtGui.QGraphicsItem): item = image elif isinstance(image, np.ndarray): self.data = image elif isinstance(image, DataManager.FileHandle): opts['handle'] = image self.handle = image self.data = self.handle.read() if 'name' not in opts: opts['name'] = self.handle.shortName() try: if 'transform' in self.handle.info(): tr = pg.SRTTransform3D(self.handle.info()['transform']) tr = pg.SRTTransform(tr) ## convert to 2D opts['pos'] = tr.getTranslation() opts['scale'] = tr.getScale() opts['angle'] = tr.getRotation() else: ## check for older info formats if 'imagePosition' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] opts['pos'] = self.handle.info()['imagePosition'] elif 'Downsample' in self.handle.info(): ### Needed to support an older format stored by 2p imager if 'pixelSize' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] if 'microscope' in self.handle.info(): m = self.handle.info()['microscope'] print 'm: ', m print 'mpos: ', m['position'] opts['pos'] = m['position'][0:2] else: info = self.data._info[-1] opts['pos'] = info.get('imagePosition', None) elif hasattr(self.data, '_info'): info = self.data._info[-1] opts['scale'] = info.get('pixelSize', None) opts['pos'] = info.get('imagePosition', None) else: opts['defaultUserTransform'] = {'scale': (1e-5, 1e-5)} opts['scalable'] = True except: debug.printExc( 'Error reading transformation for image file %s:' % image.name()) if item is None: item = pg.ImageItem() CanvasItem.__init__(self, item, **opts) self.histogram = pg.PlotWidget() self.blockHistogram = False self.histogram.setMaximumHeight(100) self.levelRgn = pg.LinearRegionItem() self.histogram.addItem(self.levelRgn) self.updateHistogram(autoLevels=True) # addWidget arguments: row, column, rowspan, colspan self.layout.addWidget(self.histogram, self.layout.rowCount(), 0, 1, 3) self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) #self.timeSlider.setMinimum(0) #self.timeSlider.setMaximum(self.data.shape[0]-1) self.layout.addWidget(self.timeSlider, self.layout.rowCount(), 0, 1, 3) self.timeSlider.valueChanged.connect(self.timeChanged) self.timeSlider.sliderPressed.connect(self.timeSliderPressed) self.timeSlider.sliderReleased.connect(self.timeSliderReleased) thisRow = self.layout.rowCount() self.edgeBtn = QtGui.QPushButton('Edge') self.edgeBtn.clicked.connect(self.edgeClicked) self.layout.addWidget(self.edgeBtn, thisRow, 0, 1, 1) self.meanBtn = QtGui.QPushButton('Mean') self.meanBtn.clicked.connect(self.meanClicked) self.layout.addWidget(self.meanBtn, thisRow + 1, 0, 1, 1) self.tvBtn = QtGui.QPushButton('tv denoise') self.tvBtn.clicked.connect(self.tvClicked) self.layout.addWidget(self.tvBtn, thisRow + 2, 0, 1, 1) self.maxBtn = QtGui.QPushButton('Max no Filter') self.maxBtn.clicked.connect(self.maxClicked) self.layout.addWidget(self.maxBtn, thisRow, 1, 1, 1) self.maxBtn2 = QtGui.QPushButton('Max w/Gaussian') self.maxBtn2.clicked.connect(self.max2Clicked) self.layout.addWidget(self.maxBtn2, thisRow + 1, 1, 1, 1) self.maxMedianBtn = QtGui.QPushButton('Max w/Median') self.maxMedianBtn.clicked.connect(self.maxMedianClicked) self.layout.addWidget(self.maxMedianBtn, thisRow + 2, 1, 1, 1) self.filterOrder = QtGui.QComboBox() self.filterLabel = QtGui.QLabel('Order') for n in range(1, 11): self.filterOrder.addItem("%d" % n) self.layout.addWidget(self.filterLabel, thisRow + 3, 2, 1, 1) self.layout.addWidget(self.filterOrder, thisRow + 3, 3, 1, 1) self.zPlanes = QtGui.QComboBox() self.zPlanesLabel = QtGui.QLabel('# planes') for s in ['All', '1', '2', '3', '4', '5']: self.zPlanes.addItem("%s" % s) self.layout.addWidget(self.zPlanesLabel, thisRow + 3, 0, 1, 1) self.layout.addWidget(self.zPlanes, thisRow + 3, 1, 1, 1) ## controls that only appear if there is a time axis self.timeControls = [ self.timeSlider, self.edgeBtn, self.maxBtn, self.meanBtn, self.maxBtn2, self.maxMedianBtn, self.filterOrder, self.zPlanes ] if self.data is not None: self.updateImage(self.data) self.graphicsItem().sigImageChanged.connect(self.updateHistogram) self.levelRgn.sigRegionChanged.connect(self.levelsChanged) self.levelRgn.sigRegionChangeFinished.connect( self.levelsChangeFinished)
def __init__(self, image=None, **opts): ## If no image was specified, check for a file handle.. if image is None: image = opts.get('handle', None) item = None self.data = None if isinstance(image, QtGui.QGraphicsItem): item = image elif isinstance(image, np.ndarray): self.data = image elif isinstance(image, DataManager.FileHandle): opts['handle'] = image self.handle = image self.data = self.handle.read() if 'name' not in opts: opts['name'] = self.handle.shortName() try: if 'transform' in self.handle.info(): tr = pg.SRTTransform3D(self.handle.info()['transform']) tr = pg.SRTTransform(tr) ## convert to 2D opts['pos'] = tr.getTranslation() opts['scale'] = tr.getScale() opts['angle'] = tr.getRotation() else: ## check for older info formats if 'imagePosition' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] opts['pos'] = self.handle.info()['imagePosition'] elif 'Downsample' in self.handle.info(): ### Needed to support an older format stored by 2p imager if 'pixelSize' in self.handle.info(): opts['scale'] = self.handle.info()['pixelSize'] if 'microscope' in self.handle.info(): m = self.handle.info()['microscope'] opts['pos'] = m['position'][0:2] else: info = self.data._info[-1] opts['pos'] = info.get('imagePosition', None) elif hasattr(self.data, '_info'): info = self.data._info[-1] opts['scale'] = info.get('pixelSize', None) opts['pos'] = info.get('imagePosition', None) else: opts['defaultUserTransform'] = {'scale': (1e-5, 1e-5)} opts['scalable'] = True except: debug.printExc( 'Error reading transformation for image file %s:' % image.name()) if item is None: item = pg.ImageItem() CanvasItem.__init__(self, item, **opts) self.splitter = QtGui.QSplitter() self.splitter.setOrientation(QtCore.Qt.Vertical) self.layout.addWidget(self.splitter, self.layout.rowCount(), 0, 1, 2) self.filterGroup = pg.GroupBox("Image Filter") fgl = QtGui.QGridLayout() fgl.setContentsMargins(3, 3, 3, 3) fgl.setSpacing(1) self.filterGroup.setLayout(fgl) self.filter = ImageFilterWidget() self.filter.sigStateChanged.connect(self.filterStateChanged) fgl.addWidget(self.filter) self.splitter.addWidget(self.filterGroup) self.histogram = pg.HistogramLUTWidget() self.histogram.setImageItem(self.graphicsItem()) # addWidget arguments: row, column, rowspan, colspan self.splitter.addWidget(self.histogram) self.imgModeCombo = QtGui.QComboBox() self.imgModeCombo.addItems( ['SourceOver', 'Overlay', 'Plus', 'Multiply']) self.layout.addWidget(self.imgModeCombo, self.layout.rowCount(), 0, 1, 1) self.imgModeCombo.currentIndexChanged.connect(self.imgModeChanged) self.autoBtn = QtGui.QPushButton("Auto") self.autoBtn.setCheckable(True) self.autoBtn.setChecked(True) self.layout.addWidget(self.autoBtn, self.layout.rowCount() - 1, 1, 1, 1) self.timeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) self.layout.addWidget(self.timeSlider, self.layout.rowCount(), 0, 1, 2) self.timeSlider.valueChanged.connect(self.timeChanged) # ## controls that only appear if there is a time axis self.timeControls = [self.timeSlider] if self.data is not None: if isinstance(self.data, pg.metaarray.MetaArray): self.filter.setInput(self.data.asarray()) else: self.filter.setInput(self.data) self.updateImage() # Needed to ensure selection box wraps the image properly tr = self.saveTransform() self.resetUserTransform() self.restoreTransform(tr)
def setDeviceTransform(self, tr): with self.__lock: self.__transform = pg.SRTTransform3D(tr) self.invalidateCachedTransforms() self.sigTransformChanged.emit(self)