def addActiveRegion(self, rgnType): rgn = pTypes.SimpleParameter(name=rgnType, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True) self.params.param('Active Regions').addChild(rgn) pos = self.getViewBox().viewRect().center() size = self.params.param('spacing').value()*4 if rgnType == 'Rectangle': roi = pg.ROI(pos=pos, size=size, angle=self.angle()) roi.addScaleHandle([0, 0], [1, 1]) roi.addScaleHandle([1, 1], [0, 0]) roi.addRotateHandle([0, 1], [0.5, 0.5]) roi.addRotateHandle([1, 0], [0.5, 0.5]) elif rgnType == 'Polygon': roi = pg.PolyLineROI((pos, pos+pg.Point(0,1)*size, pos+pg.Point(1,0)*size), closed=True) else: raise Exception('Not sure how to add region of type:%s' % rgnType) rgn.item = roi self.rgns.append(rgn) self.getViewBox().addItem(roi) roi.sigRegionChanged.connect(self.invalidatePoints) roi.sigRegionChangeFinished.connect(self.stateChangeFinished) rgn.sigValueChanged.connect(self.rgnToggled) self.invalidatePoints() self.stateChangeFinished()
def addNew(self): rgn = ptree.Parameter.create(name='region', autoIncrementName=True, renamable=True, removable=True, type='bool', value=True, children=[ dict(name='DB Column', type='str', value='Region'), dict(name='Color', type='color'), ]) self.addChild(rgn) ## find the center of the view view = self.canvas.view center = view.viewRect().center() size = [x * 50 for x in view.viewPixelSize()] pts = [ center, center + pg.Point(size[0], 0), center + pg.Point(0, size[1]) ] roi = pg.PolyLineROI(pts, closed=True) roi.setZValue(1000) view.addItem(roi) rgn.roi = roi roi.rgn = rgn roi.sigRegionChangeFinished.connect(self.regionChanged)
def saveState(self): ## Saves the position/configuration of the cortexROI, as well as transforms for mapping to atlas coordinates if self.sliceDir is None: return ## get state of ROI cortexROI = self.roi.saveState() quads = self.roi.getQuadrilaterals() newQuads = [] for q in quads: newQuads.append([(p.x(), p.y()) for p in q]) rects = self.roi.getNormalizedRects() matrices = [] for i, q in enumerate(quads): matrix = self.atlas.solveBilinearTransform([pg.Point(x) for x in q], [pg.Point(x) for x in rects[i]]) matrices.append([list(matrix[0]), list(matrix[1])]) state = { 'cortexROI':cortexROI, 'quadrilaterals':newQuads, 'normalizedRects': rects, 'transformMatrices': matrices } ## write to slice directory meta-info atlasInfo = self.sliceDir.info().get('atlas', {}).deepcopy() atlasInfo[self.atlas.name()] = state self.sliceDir.setInfo(atlas=atlasInfo) ## set state for atlas object self.atlas.setState(state)
def getBackground(self): if self.background is None: w, h = self.params['sensorSize'] tr = self.globalTransform() tr = pg.SRTTransform(tr) m = QtGui.QTransform() m.scale(3e6, 3e6) m.translate(0.0005, 0.0005) tr = tr * m origin = tr.map(pg.Point(0, 0)) x = (tr.map(pg.Point(1, 0)) - origin) y = (tr.map(pg.Point(0, 1)) - origin) origin = np.array([origin.x(), origin.y()]) x = np.array([x.x(), x.y()]) y = np.array([y.x(), y.y()]) ## slice fractal from pre-rendered data vectors = (x, y) self.background = pg.affineSlice(self.bgData, (w, h), origin, vectors, (0, 1), order=1) return self.background
def transformChanged(self): # manipulator's global transform has changed; update the center arrow and orientation axis dev = self.getDevice() pos = dev.mapToGlobal([0, 0, 0]) x = dev.mapToGlobal([1, 0, 0]) p1 = pg.Point(x[:2]) p2 = pg.Point(pos[:2]) p3 = pg.Point(1, 0) angle = (p1 - p2).angle(p3) if angle is None: angle = 0 self.centerArrow.setPos(pos[0], pos[1]) self.centerArrow.setStyle(angle=180 - angle) # self.depthLine.setValue(pos[2]) self.depthArrow.setPos(0, pos[2]) self.target.setLabelAngle(dev.getYawAngle()) if self.ui.setOrientationBtn.isChecked(): return with pg.SignalBlock(self.calibrateAxis.sigRegionChangeFinished, self.calibrateAxisChanged): self.calibrateAxis.setPos(pos[:2]) self.calibrateAxis.setAngle(angle) ys = self.calibrateAxis.size()[1]
def addROI(self, roiType): pen = pg.mkPen(pg.intColor(len(self.ROIs))) center = self.view.viewRect().center() #print 'camerawindow.py: addROI:: ', self.view.viewPixelSize() size = [x * 50 for x in self.view.viewPixelSize()] if roiType == 'rect': roi = PlotROI(center, size) elif roiType == 'ellipse': roi = pg.EllipseROI(center, size, removable=True) elif roiType == 'polygon': pts = [ center, center + pg.Point(0, size[1]), center + pg.Point(size[0], 0) ] roi = pg.PolyLineROI(pts, closed=True, removable=True) elif roiType == 'ruler': pts = [center, center + pg.Point(size[0], size[1])] roi = RulerROI(pts, removable=True) else: raise ValueError("Invalid ROI type %s" % roiType) roi.setZValue(40000) roi.setPen(pen) self.view.addItem(roi) plot = self.roiPlot.plot(pen=pen) self.ROIs.append({'roi': roi, 'plot': plot, 'vals': [], 'times': []}) roi.sigRemoveRequested.connect(self.removeROI)
def boundingRect(self): w = self.pixelLength(pg.Point(1, 0)) if w is None: return QtCore.QRectF() w *= 25 h = 25 * self.pixelLength(pg.Point(0, 1)) r = QtCore.QRectF(-w * 2, -h * 2, w * 4, h * 4) return r
def pixelVectors(self): tr = self.globalTransform() origin = tr.map(pg.Point(0, 0)) x = (tr.map(pg.Point(1, 0)) - origin) y = (tr.map(pg.Point(0, 1)) - origin) origin = np.array([origin.x(), origin.y()]) x = np.array([x.x(), x.y()]) y = np.array([y.x(), y.y()]) return x, y
def setScanPosFromRoi(self): # Update the position of the scan rectangle from the ROI roi = self.currentRoi w, h = roi.size() # get top-left ROI corner in global coordinates p0 = roi.mapToView(pg.Point(0,h)) if p0 is None: # could not map point; probably view has been closed. return rparam = self.scanProgram.components[0].ctrlParameter() rparam.system.p0 = pg.Point(p0) # top-left rparam.system.p1 = pg.Point(roi.mapToView(pg.Point(w,h))) # rop-right
def viewTransformChanged(self): w = self.pixelLength(pg.Point(1, 0)) if w is None: self._pxLen = [None, None] return h = self.pixelLength(pg.Point(0, 1)) if self.size()[1] < 0: h = -h self._pxLen = [w, h] self.blockSignals(True) try: self.setSize([w * 50, h * 50]) finally: self.blockSignals(False) self.updateText() self.prepareGeometryChange()
def restoreROI(self): if self.storedROI is not None: (width, height, x, y) = self.storedROI self.currentRoi.setSize([width, height]) self.currentRoi.setPos([x, y]) self.roiChanged() else: cpos = self.cameraModule.ui.view.viewRect().center() # center position, stage coordinates csize = pg.Point([x*400 for x in self.cameraModule.ui.view.viewPixelSize()]) width = csize[0]*2 # width is x in M height = csize[1]*2 csize = pg.Point(width, height) cpos = cpos - csize/2. self.currentRoi.setSize([width, height]) self.currentRoi.setPos(cpos)
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 getSnapPosition(self, pos): ## Given that pos has been requested, return the nearest snap-to position ## optionally, snap may be passed in to specify a rectangular snap grid. ## override this function for more interesting snap functionality.. layout = self.params['layout'] spacing = self.params['spacing'] if layout == 'Square': snap = pg.Point(spacing, spacing) w = round(pos[0] / snap[0]) * snap[0] h = round(pos[1] / snap[1]) * snap[1] return pg.Point(w, h) elif layout == 'Hexagonal': snap1 = pg.Point(spacing, spacing*3.0**0.5) dx = 0.5*snap1[0] dy = 0.5*snap1[1] w1 = round(pos[0] / snap1[0]) * snap1[0] h1 = round(pos[1] / snap1[1]) * snap1[1] w2 = round((pos[0]-dx) / snap1[0]) * snap1[0] + dx h2 = round((pos[1]-dy) / snap1[1]) * snap1[1] + dy if (pg.Point(w1, h1)-pos).length() < (pg.Point(w2,h2) - pos).length(): return pg.Point(w1, h1) else: return pg.Point(w2, h2)
def saveState(self): center = self.roi.mapToView(pg.Point(0, 0)) return { 'startTime': self.params['startTime'], 'duration': self.params['duration'], 'radius': self.params['radius'], 'thickness': self.params['thickness'], 'spacing': self.params['spacing'], 'center': (center.x(), center.y()), }
def targetChanged(self, dev, pos): self.target.setPos(pg.Point(pos[:2])) self.depthTarget.setPos(0, pos[2]) self.target.setVisible(True) self._haveTarget = True self.depthTarget.setVisible(True) self.ui.targetBtn.setEnabled(True) self.ui.approachBtn.setEnabled(True) self.ui.setTargetBtn.setChecked(False) self.focusChanged()
def generatePoints(self): layout = self.params['layout'] # get x/y point spacing sepx = sepy = self.params['spacing'] if layout == 'Hexagonal': sepy *= 3 ** 0.5 # find grid points inside each active region points = [] for rgn in self.rgns: if not rgn.value(): continue roi = rgn.item rect = self.mapFromItem(roi, roi.boundingRect()).boundingRect() ## find 'snap' position of first spot newPos = self.getSnapPosition((rect.x(), rect.y())) x = newPos.x() - sepx y = newPos.y() - sepy w = rect.width() + 2*sepx h = rect.height() + 2*sepy if layout == "Hexagonal": # make every other row of the grid starting from top points.append(self.generateGrid([x, y], [x+w, y+h], [sepx, sepy])) # make every other row of the grid starting with 2nd row points.append(self.generateGrid([x+0.5*sepx, y+0.5*sepy], [x+w, y+h], [sepx, sepy])) elif layout == "Square": points.append(self.generateGrid([x, y], [x+w, y+h], [sepx, sepx])) if len(points) == 0: return np.empty((0,2), dtype=float) # stack all point arrays together points = np.ascontiguousarray(np.vstack(points)) # do some rounding to make it easier to detect duplicate points dec = int(-np.log10(sepx) + 4) points = np.round(points, dec) # keep only unique points points = np.unique(points.view(dtype=[('x', float), ('y', float)])) # convert back to normal array points = points.view(dtype=float).reshape(len(points), 2) # get shape of total active region activeArea = Qt.QPainterPath() for rgn in self.rgns: if rgn.value(): roi = rgn.item activeArea |= self.mapFromItem(roi, roi.shape()) # filter for all points within active region mask = np.array([activeArea.contains(pg.Point(*pt)) for pt in points], dtype=bool) return points[mask]
def addItem(self, item, pos=(0,0), scale=(1,1), z=None): """Adds an item into the scene. The item is placed in the global coordinate system; it will stay fixed on the subject even if the scope moves or changes objective.""" self.view.addItem(item) if pos is None: pos = self.view.viewRect().center() item.setPos(pg.Point(pos)) item.scale(scale[0], scale[1]) if z is not None: item.setZValue(z)
def analyzeTransform(self): """Return the position and yaw angle of the device transform """ p = pg.debug.Profiler(disabled=True) dev = self.getDevice() p('getdev') pos = dev.mapToGlobal([0, 0, 0]) p('map1') x = dev.mapToGlobal([1, 0, 0]) p('map2') p1 = pg.Point(x[:2]) p2 = pg.Point(pos[:2]) p3 = pg.Point(1, 0) p('points') angle = (p1 - p2).angle(p3) if angle is None: angle = 0 p('angle') return pos, angle
def paint(self, p, *args): pg.LineSegmentROI.paint(self, p, *args) h1 = self.handles[0]['item'].pos() h2 = self.handles[1]['item'].pos() p1 = p.transform().map(h1) p2 = p.transform().map(h2) vec = pg.Point(h2) - pg.Point(h1) length = vec.length() angle = vec.angle(pg.Point(1, 0)) pvec = p2 - p1 pvecT = pg.Point(pvec.y(), -pvec.x()) pos = 0.5 * (p1 + p2) + pvecT * 40 / pvecT.length() p.resetTransform() txt = pg.siFormat(length, suffix='m') + '\n%0.1f deg' % angle p.drawText(QtCore.QRectF(pos.x() - 50, pos.y() - 50, 100, 100), QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, txt)
def roiChanged(self): """ read the ROI rectangle width and height and repost in the parameter tree """ if self.ignoreRoiChange: return self.scanVoltageCache = None # invalidate cache # update scan position self.setScanPosFromRoi() # update scan shape if needed roi = self.currentRoi state = roi.getState() w, h = state['size'] rparam = self.scanProgram.components[0].ctrlParameter() param = self.param.child('Scan Control') rows = int(param['Image Width'] * h / w) if param['Image Height'] != rows: # update image height; this will cause acq thread protocol to be updated with param.treeChangeBlocker(): param['Image Height'] = rows else: # ..otherwise we need to request the update here. if self.imagingThread.isRunning(): self.updateImagingProtocol() # record position of ROI in Scanner's local coordinate system # we can use this later to allow the ROI to track stage movement tr = self.scannerDev.inverseGlobalTransform() # maps from global to device local pt1 = pg.Point(*state['pos']) pt2 = pt1 + pg.Point(*state['size']) self.currentRoi.scannerCoords = [ tr.map(pt1), tr.map(pt2), ]
def segmentClicked(self, segment, ev=None, pos=None): ## ev/pos should be in this item's coordinate system if ev != None: pos = ev.pos() elif pos != None: pos = pos else: raise Exception("Either an event or a position must be specified") ## figure out which segment to add corresponding handle to n = len(self.segments) ind = self.segments.index(segment) #mirrorInd = (n - ind) - 2 mirrorInd= n-ind ## figure out position at which to add second handle: h1 = pg.Point(self.mapFromItem(segment, segment.handles[0]['item'].pos())) h2 = pg.Point(self.mapFromItem(segment, segment.handles[1]['item'].pos())) dist = (h1-pos).length()/(h1-h2).length() h3 = pg.Point(self.mapFromItem(self.segments[mirrorInd], self.segments[mirrorInd].handles[0]['item'].pos())) h4 = pg.Point(self.mapFromItem(self.segments[mirrorInd], self.segments[mirrorInd].handles[1]['item'].pos())) mirrorPos = h4 - (h4-h3)*dist ## add handles: if mirrorInd > ind: ROI.PolyLineROI.segmentClicked(self, self.segments[mirrorInd], pos=mirrorPos) ROI.PolyLineROI.segmentClicked(self, segment, pos=pos) ROI.LineSegmentROI([pos, mirrorPos], [0,0], handles=(self.segments[ind].handles[1]['item'], self.segments[mirrorInd+1].handles[1]['item']), pen=self.pen, movable=False, parent=self) else: ROI.PolyLineROI.segmentClicked(self, segment, pos=pos) ROI.PolyLineROI.segmentClicked(self, self.segments[mirrorInd], pos=mirrorPos) ROI.LineSegmentROI([mirrorPos, pos], [0,0], handles=(self.segments[mirrorInd].handles[1]['item'], self.segments[ind+1].handles[1]['item']), pen=self.pen, movable=False, parent=self)
def createROI(self, roiColor='r'): # the initial ROI will be nearly as big as the field, and centered. cpos = self.scannerDev.mapToGlobal((0,0)) # get center position in scanner coordinates csize = self.scannerDev.mapToGlobal((self.fieldSize, self.fieldSize)) objScale = self.scannerDev.parentDevice().getObjective().scale().x() height = width = self.fieldSize*objScale csize = pg.Point(width, height) cpos = cpos - csize/2. roiColor = self.getObjectiveColor(self.scopeDev.currentObjective) # pick up an objective color... roi = RegionCtrl(cpos, csize, roiColor) # Note that the position actually gets over ridden by the camera additem below.. self.cameraModule.window().addItem(roi) roi.setPos(cpos) roi.sigRegionChangeFinished.connect(self.roiChanged) return roi
def roiChanged(self): """ read the ROI rectangle width and height and repost in the parameter tree """ state = self.roi.getState() w, h = state['size'] # Remember: ROI origin is in bottom-left because camera module has +y pointing upward. self.params.system.p0 = pg.Point(self.roi.mapToView(pg.Point( 0, h))) # top-left self.params.system.p1 = pg.Point(self.roi.mapToView(pg.Point( w, h))) # rop-right self.params.system.p2 = pg.Point(self.roi.mapToView(pg.Point( 0, 0))) # bottom-left self.params.updateSystem()
def writeArray(self, array, mapping=None): # Compute start/end indexes start, npts = self._arrayIndexes() # Generate spiral path sg = self.spiralGeometry() path = sg.path(npts, uniform=True) # Move to center position center = self.roi.mapToView(pg.Point(0, 0)) path += np.array([center.x(), center.y()]) # map to scanner voltage and write into array x, y = (path[:, 0], path[:, 1]) if mapping is not None: x, y = mapping(x, y) array[start:start + npts, 0] = x array[start:start + npts, 1] = y return start, start + npts
def getMatrix(self, pos): """Return the transformMatrix to use for the given pos.""" quads = self.state['quadrilaterals'] ind=None for i, q in enumerate(quads): if QtGui.QPolygonF([QtCore.QPointF(*x) for x in q]).containsPoint(QtCore.QPointF(pos), QtCore.Qt.OddEvenFill): ind = i if ind == None: ## in case pos is outside the quadrilaterals bestMin = 1000 for i, q in enumerate(quads): dist = [pg.Point(x-pos).length() for x in q] minDist = min(dist) dist.remove(minDist) minDist += min(dist) if minDist < bestMin: bestMin = minDist ind = i m = self.state['transformMatrices'][ind] return np.array([m[0], m[1]])
def paint(self, p, *args): p.setRenderHint(p.Antialiasing) px = self.pixelLength(pg.Point(1, 0)) py = self.pixelLength(pg.Point(0, 1)) w = 5 * px h = 5 * py r = QtCore.QRectF(-w, -h, w * 2, h * 2) p.setPen(pg.mkPen('y')) p.setBrush(pg.mkBrush(0, 0, 255, 100)) p.drawEllipse(r) p.drawLine(pg.Point(-w * 2, 0), pg.Point(w * 2, 0)) p.drawLine(pg.Point(0, -h * 2), pg.Point(0, h * 2)) if self.label is not None: angle = self.labelAngle * np.pi / 180. pos = p.transform().map(QtCore.QPointF( 0, 0)) + 15 * QtCore.QPointF(np.cos(angle), -np.sin(angle)) p.resetTransform() p.drawText(QtCore.QRectF(pos.x() - 10, pos.y() - 10, 20, 20), QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, self.label)
def runCalibration(self): """The scanner calibration routine: 1) Measure background frame, then scan mirrors while collecting frames as fast as possible (self.scan()) 2) Locate spot in every frame using gaussian fit 3) Map image spot locations to coordinate system of Scanner device's parent 3) Do parabolic fit to determine mapping between voltage and position """ camera = str(self.ui.cameraCombo.currentText()) laser = str(self.ui.laserCombo.currentText()) blurRadius = 5 ## Do fast scan of entire allowed command range (background, cameraResult, positions) = self.scan() #self.calibrationResult = {'bg': background, 'frames': cameraResult, 'pos': positions} with pg.ProgressDialog("Calibrating scanner: Computing spot positions...", 0, 100) as dlg: dlg.show() dlg.raise_() # Not sure why this is needed here.. ## Forget first 2 frames since some cameras can't seem to get these right. frames = cameraResult.asArray() frames = frames[2:] positions = positions[2:] ## Do background subtraction ## take out half the data until it can do the calculation without having a MemoryError. finished = False gc.collect() while not finished: try: frames = frames.astype(np.float32) frames -= background.astype(np.float32) finished=True except MemoryError: frames = frames[::2,:,:] positions = positions[::2] finished = False ## Find a frame with a spot close to the center (within center 1/3) cx = frames.shape[1] / 3 cy = frames.shape[2] / 3 centerSlice = blur(frames[:, cx:cx*2, cy:cy*2], (0, 5, 5)).max(axis=1).max(axis=1) maxIndex = argmax(centerSlice) maxFrame = frames[maxIndex] dlg.setValue(5) ## Determine spot intensity and width mfBlur = blur(maxFrame, blurRadius) amp = mfBlur.max() - median(mfBlur) ## guess intensity of spot (x, y) = argwhere(mfBlur == mfBlur.max())[0] ## guess location of spot fit = fitGaussian2D(maxFrame, [amp, x, y, maxFrame.shape[0] / 10, 0.])[0] ## gaussian fit to locate spot exactly # convert sigma to full width at 1/e fit[3] = abs(2 * (2 ** 0.5) * fit[3]) ## sometimes the fit for width comes out negative. *shrug* someFrame = cameraResult.frames()[0] frameTransform = pg.SRTTransform(someFrame.globalTransform()) pixelSize = someFrame.info()['pixelSize'][0] spotAmplitude = fit[0] spotWidth = abs(fit[3] * pixelSize) size = self.spotSize(mfBlur) dlg.setValue(50) ## Determine location of spot within each frame, ## ignoring frames where the spot is too dim or too close to the frame edge spotLocations = [] globalSpotLocations = [] spotCommands = [] spotFrames = [] margin = fit[3] for i in range(len(positions)): dlg.setValue(50. + 50. * i / frames.shape[0]) if dlg.wasCanceled(): raise HelpfulException('Calibration canceled by user.', msgType='warning') frame = frames[i] fBlur = blur(frame.astype(np.float32), blurRadius) mx = fBlur.max() diff = mx - fBlur.min() ss = self.spotSize(fBlur) if ss < size * 0.6: #print "Ignoring spot:", ss continue #else: #print "Keeping spot:", ss (x, y) = argwhere(fBlur == mx)[0] # guess location of spot if x < margin or x > frame.shape[0] - margin: #print " ..skipping; too close to edge", x, y continue if y < margin or y > frame.shape[1] - margin: #print " ..skipping; too close to edge", x, y continue frame[x,y] = -1 ## mark location of peak in image ## convert pixel location to coordinate system of scanner's parent globalPos = frameTransform.map(pg.Point(x, y)) ## Map from frame pixel location to global coordinates localPos = self.dev.mapGlobalToParent(globalPos) ## map from global to parent coordinate system. This is the position we calibrate to. #print (x, y), (globalPos.x(), globalPos.y()), (localPos.x(), localPos.y()) spotLocations.append([localPos.x(), localPos.y()]) globalSpotLocations.append([globalPos.x(), globalPos.y()]) spotCommands.append(positions[i]) spotFrames.append(frame[newaxis]) ## sanity check on spot frame if len(spotFrames) == 0: self.ui.view.setImage(frames) raise HelpfulException('Calibration never detected laser spot! Looking for spots that are %f pixels wide.'% fit[3], reasons=['shutter is disabled', 'mirrors are disabled', 'objective is not clean', 'spot is not visible or not bright enough when shutter is open']) spotFrameMax = concatenate(spotFrames).max(axis=0) self.ui.view.setImage(spotFrameMax, transform=frameTransform) self.clearSpots() for sl in globalSpotLocations: #self.addSpot(sl, fit[3]*binning[0]) self.addSpot(sl, spotWidth) self.ui.view.autoRange() if len(spotFrames) < 10: raise HelpfulException('Calibration detected only %d frames with laser spot; need minimum of 10.' % len(spotFrames), reasons=['spot is too dim for camera sensitivity', 'objective is not clean', 'mirrors are scanning too quickly', 'mirror scanning region is not within the camera\'s view']) ## Fit all data to a map function mapParams = self.generateMap(array(spotLocations), array(spotCommands)) #print #print "Map parameters:", mapParams if spotWidth < 0: raise Exception() return (mapParams, (spotAmplitude, spotWidth))
def getBackground(self): if self.background is None: w, h = self.params['sensorSize'] tr = self.globalTransform() if isinstance(self.bgData, dict): # select data based on objective obj = self.getObjective() data = self.bgData[obj] info = self.bgInfo[obj] px = info['pixelSize'] pz = info['depths'][1] - info['depths'][0] m = pg.QtGui.QMatrix4x4() pos = info['transform']['pos'] m.scale(1 / px[0], 1 / px[1], 1 / pz) m.translate(-pos[0], -pos[1], -info['depths'][0]) tr2 = m * tr origin = tr2.map(pg.Vector(0, 0, 0)) #print(origin) origin = [int(origin.x()), int(origin.y()), origin.z()] ## slice data camRect = QtCore.QRect(origin[0], origin[1], w, h) dataRect = QtCore.QRect(0, 0, data.shape[1], data.shape[2]) overlap = camRect.intersected(dataRect) tl = overlap.topLeft() - camRect.topLeft() z = origin[2] z1 = np.floor(z) z2 = np.ceil(z) s = (z - z1) / (z2 - z1) z1 = int(np.clip(z1, 0, data.shape[0] - 1)) z2 = int(np.clip(z2, 0, data.shape[0] - 1)) src1 = data[z1, overlap.left():overlap.left() + overlap.width(), overlap.top():overlap.top() + overlap.height()] src2 = data[z2, overlap.left():overlap.left() + overlap.width(), overlap.top():overlap.top() + overlap.height()] src = src1 * (1 - s) + src2 * s bg = np.empty((w, h), dtype=data.dtype) bg[:] = 100 bg[tl.x():tl.x() + overlap.width(), tl.y():tl.y() + overlap.height()] = src self.background = bg #vectors = ([1, 0, 0], [0, 1, 0]) #self.background = pg.affineSlice(data, (w,h), origin, vectors, (1, 2, 0), order=1) else: tr = pg.SRTTransform(tr) m = QtGui.QTransform() m.scale(3e6, 3e6) m.translate(0.0005, 0.0005) tr = tr * m origin = tr.map(pg.Point(0, 0)) x = (tr.map(pg.Point(1, 0)) - origin) y = (tr.map(pg.Point(0, 1)) - origin) origin = np.array([origin.x(), origin.y()]) x = np.array([x.x(), x.y()]) y = np.array([y.x(), y.y()]) ## slice fractal from pre-rendered data vectors = (x, y) self.background = pg.affineSlice(self.bgData, (w, h), origin, vectors, (0, 1), order=1) return self.background
def generateVoltageArray(self, arr): """ Use a polyline to make a scan that covers multiple regions in an interleaved fashion. Alternate line segments of the polyline are either the "scan" or the "interscan", allowing rapid movement between successive points. Interscan intervals are green on the line, scan intervals are white """ pts = list(map(pg.Point, cmd['points'])) startPos = pts[0] stopPos = pts[-1] #scanPoints = cmd['sweepDuration']/dt # in point indices, not time. #interTracePoints = cmd['intertraceDuration']/dt #scanPause = np.ones(int(interTracePoints)) #cmd['samplesPerScan'] = scanPoints #cmd['samplesPerPause'] = interTracePoints sweepSpeed = 1000 * cmd['sweepSpeed'] # in m/msec interSweepSpeed = 1000 * cmd['interSweepSpeed'] ScanFlag = False xp = np.array([]) yp = np.array([]) pockels = np.array([]) nSegmentScans = 0 nIntersegmentScans = 0 scanPointList = [] interScanFlag = False for k in range(len(pts)): # loop through the list of points k2 = k + 1 if k2 > len(pts) - 1: k2 = 0 dist = (pg.Point(pts[k] - pts[k2])).length() if interScanFlag is False: scanPoints = int((dist / sweepSpeed) / dt) xPos = np.linspace(pts[k].x(), pts[k2].x(), scanPoints) yPos = np.linspace(pts[k].y(), pts[k2].y(), scanPoints) pockels = np.append(pockels, np.ones(scanPoints)) nSegmentScans += 1 scanPointList.append(scanPoints) else: interSweepPoints = int((dist / interSweepSpeed) / dt) xPos = np.linspace(pts[k].x(), pts[k2].x(), interSweepPoints) yPos = np.linspace(pts[k].y(), pts[k2].y(), interSweepPoints) pockels = np.append(pockels, np.zeros(interSweepPoints)) nIntersegmentScans += 1 scanPointList.append(interSweepPoints) x, y = dev.mapToScanner(xPos, yPos, cmd['laser']) xp = np.append(xp, x) yp = np.append(yp, y) interScanFlag = not interScanFlag cmd['nSegmentScans'] = nSegmentScans cmd['nIntersegmentScans'] = nIntersegmentScans cmd['scanPointList'] = scanPointList x = np.tile(xp, cmd['nScans']) y = np.tile(yp, cmd['nScans']) arr[0, startInd:startInd + len(x)] = x arr[1, startInd:startInd + len(y)] = y arr[0, startInd + len(x):stopInd] = arr[ 0, startInd + len(x) - 1] # fill in any unused sample on this scan section arr[1, startInd + len(y):stopInd] = arr[1, startInd + len(y) - 1] lastPos = (x[-1], y[-1]) return stopInd
def show(dh=None): """ Display a graphic of the currently selected slice / cell """ global v, g, atlas if dh is None: dh = man.currentFile v.clear() if 'cell' in dh.shortName().lower(): cd = dh sd = cd.parent() else: sd = dh cd = None atlas.loadState(sd) g = atlas.schematicGraphicsItems() v.addItem(g) if cd is not None: ## small image to go over slice schematic imgf = cd['morphology.png'] imgd = pg.colorToAlpha(imgf.read(), np.array([255,255,255])) mimg1 = pg.ImageItem(imgd) tr = pg.SRTTransform(imgf.info()['userTransform']) mimg1.setTransform(tr) mimg1.setParentItem(g.sliceGroup) g.cellImg1 = mimg1 ## larger image to be displayed above cellGroup = pg.ItemGroup() g.cellGroup = cellGroup mimg2 = pg.ImageItem(imgd) mimg2.setParentItem(cellGroup) mimg2.setTransform(tr * g.sliceGroup.transform()) mimg2.scale(1.0 / g.sliceScaleFactor, 1.0 / g.sliceScaleFactor) #angle = pg.SRTTransform(g.sliceGroup.transform()).getRotation() #mimg2.rotate(angle) cellScale = 50. cellGroup.scale(cellScale, cellScale) g.cellImg2 = mimg2 ## reposition image above slice schematic b1 = g.atlasGroup.mapRectToParent(g.atlasGroup.childrenBoundingRect()) b2 = g.sliceClip.mapRectToParent(g.sliceClip.boundingRect()) bounds = b1 | b2 cellGroup.setParentItem(g) imgBounds = g.mapRectFromItem(mimg2, mimg2.boundingRect()) pos = pg.Point(bounds.center().x() - imgBounds.center().x(), bounds.top()-imgBounds.bottom()) cellGroup.setPos(pos) ## add scale bar sbLength = 25e-6 g.cellScale = Qt.QGraphicsLineItem(0.0, 0.0, sbLength, 0.0) g.cellScale.setPen(pg.mkPen(color=0.0, width=100e-6/cellScale, cosmetic=False)) g.cellScale.setParentItem(cellGroup) g.cellScale.setZValue(10) g.cellScale.text = pg.TextItem(u"25 µm", anchor=(0.5, 1), color=(0,0,0)) g.cellScale.text.setParentItem(g.cellScale) g.cellScale.text.setPos(sbLength*0.5, -50e-6/cellScale) corner = mimg2.mapToParent(mimg2.boundingRect()).boundingRect().bottomRight() g.cellScale.setPos(corner + pg.Point(-sbLength/2., -sbLength/3.)) cell = dh sl = cell.parent() day = sl.parent() name = day.shortName() + "_" + sl.shortName() + "_" + cell.shortName() g.cellName = pg.TextItem(name, color=(0,0,0)) g.cellName.setParentItem(cellGroup) g.cellName.setPos(corner + pg.Point(-sbLength*4,-sbLength/4.))