def OnExtract(self, event): from PYME.DSView import View3D #print 'extracting ...' mdh = self.image.mdh #dark = deTile.genDark(self.vp.do.ds, self.image.mdh) dark = mdh.getEntry('Camera.ADOffset') #split = False frames = mdh.getEntry('Protocol.PrebleachFrames') dt = self.image.data[:, :, frames[0]:frames[1]].astype('f').mean(2) - dark ROIX1 = mdh.getEntry('Camera.ROIPosX') ROIY1 = mdh.getEntry('Camera.ROIPosY') ROIX2 = ROIX1 + mdh.getEntry('Camera.ROIWidth') ROIY2 = ROIY1 + mdh.getEntry('Camera.ROIHeight') if self.split: from PYME.Acquire.Hardware import splitter unmux = splitter.Unmixer( [mdh.getEntry('chroma.dx'), mdh.getEntry('chroma.dy')], 1e3 * mdh.getEntry('voxelsize.x')) dt = unmux.Unmix(dt, self.mixmatrix, 0, [ROIX1, ROIY1, ROIX2, ROIY2]) View3D(dt, 'Prebleach Image') else: View3D(dt, 'Prebleach Image')
def OnExtract(self, event): from PYME.DSView import View3D from PYME.IO.MetaDataHandler import get_camera_roi_origin #print 'extracting ...' mdh = self.image.mdh #dark = deTile.genDark(self.vp.do.ds, self.image.mdh) dark = mdh.getEntry('Camera.ADOffset') #split = False frames = mdh.getEntry('Protocol.PrebleachFrames') dt = self.image.data[:, :, frames[0]:frames[1]].astype('f').mean(2) - dark roi_x0, roi_y0 = get_camera_roi_origin(mdh) ROIX1 = roi_x0 + 1 ROIY1 = roi_y0 + 1 ROIX2 = ROIX1 + mdh.getEntry('Camera.ROIWidth') ROIY2 = ROIY1 + mdh.getEntry('Camera.ROIHeight') if self.split: from PYME.Acquire.Hardware import splitter unmux = splitter.Unmixer( [mdh.getEntry('chroma.dx'), mdh.getEntry('chroma.dy')], mdh.voxelsize_nm.x) dt = unmux.Unmix(dt, self.mixmatrix, 0, [ROIX1, ROIY1, ROIX2, ROIY2]) View3D(dt, 'Prebleach Image') else: View3D(dt, 'Prebleach Image')
def tile_pyramid(out_folder, ds, xm, ym, mdh, split=False, skipMoveFrames=False, shiftfield=None, mixmatrix=[[1., 0.], [0., 1.]], correlate=False, dark=None, flat=None, pyramid_tile_size=256): """Create a tile pyramid from which an ImagePyramid can be created Parameters ---------- out_folder : str directory to save pyramid tiles(/directories) ds : PYME.IO.DataSources.BaseDataSource, np.ndarray array-like image xm : np.ndarray or PYME.Analysis.piecewiseMapping.piecewiseMap x positions of frames in ds. Raw stage positions in [um]. ImagePyramid origin will be at at minimum x, and offset to camera chip origin will be handled in SupertileDatasource tile_coords_um method. to the camera chip origin. ym : np.ndarray or PYME.Analysis.piecewiseMapping.piecewiseMap y positions of frames in ds. Raw stage positions in [um]. ImagePyramid origin will be at at minimum y, and offset to camera chip origin will be handled in SupertileDatasource tile_coords_um method. mdh : PYME.IO.MetaDataHandler.MDataHandlerBase metadata for ds split : bool, optional whether this is a splitter datasource and should be treated like one, by default False skipMoveFrames : bool, optional flag to drop frames which are the first frame acquired at a given position, by default False shiftfield : [type], optional required for splitter data, see PYME.Acquire.Hardware.splitter, by default None mixmatrix : list, optional for splitter data, see PYME.Acquire.Hardware.splitter, by default [[1., 0.], [0., 1.]] correlate : bool, optional whether to add a 300 pixel padding to the edges, by default False dark : ndarray, float, optional (appropriately-cropped or scalar) dark frame (analog-digital offset) calibration to subtract when adding frames to the pyramid, by default None, in which case Camera.ADOffset from metadata will be used, if available flat : ndarray, optional (appropriately-cropped or scalar) flatfield calibration to apply to frames when adding them to the pyramid, by default None pyramid_tile_size : int, optional base tile size, by default 256 pixels Returns ------- ImagePyramid coalesced/averaged/etc multilevel ImagePyramid instance Notes ----- Code is currently somewhat alpha in that the splitter functionality is more or less untested, and we only get tile orientations right for primary cameras (i.e. when the stage is registered with multipliers to match the camera, rather than camera registered with orientation metadata to match it to the stage) """ frameSizeX, frameSizeY, numFrames = ds.shape[:3] if split: from PYME.Acquire.Hardware import splitter frameSizeY /= 2 nchans = 2 unmux = splitter.Unmixer(shiftfield, mdh.voxelsize_nm.x) else: nchans = 1 #x & y positions of each frame xps = xm(np.arange(numFrames)) if not isinstance(xm, np.ndarray) else xm yps = ym(np.arange(numFrames)) if not isinstance(ym, np.ndarray) else ym #give some room at the edges bufSize = 0 if correlate: bufSize = 300 # to avoid building extra, empty tiles, the pyramid origin is the minimum # x and y position present in the tiles x0_pyramid, y0_pyramid = xps.min(), yps.min() xps -= x0_pyramid yps -= y0_pyramid # calculate origin independent of the camera ROI setting to store in # metadata for use in e.g. SupertileDatasource.DataSource.tile_coords_um x0_cam, y0_cam = get_camera_physical_roi_origin(mdh) x0 = x0_pyramid + mdh.voxelsize_nm.x / 1e3 * x0_cam y0 = y0_pyramid + mdh.voxelsize_nm.y / 1e3 * y0_cam #convert to pixels xdp = (bufSize + (xps / (mdh.getEntry('voxelsize.x'))).round()).astype('i') ydp = (bufSize + (yps / (mdh.getEntry('voxelsize.y'))).round()).astype('i') #calculate a weighting matrix (to allow feathering at the edges - TODO) weights = np.ones((frameSizeX, frameSizeY, nchans)) #weights[:, :10, :] = 0 #avoid splitter edge artefacts #weights[:, -10:, :] = 0 #print weights[:20, :].shape edgeRamp = min(100, int(.25 * ds.shape[0])) weights[:edgeRamp, :, :] *= np.linspace(0, 1, edgeRamp)[:, None, None] weights[-edgeRamp:, :, :] *= np.linspace(1, 0, edgeRamp)[:, None, None] weights[:, :edgeRamp, :] *= np.linspace(0, 1, edgeRamp)[None, :, None] weights[:, -edgeRamp:, :] *= np.linspace(1, 0, edgeRamp)[None, :, None] # get splitter ROI coordinates in units of pixels ROIX1 = x0_cam + 1 # TODO - is splitter 1-indexed? ROIY1 = y0_cam + 1 ROIX2 = ROIX1 + mdh.getEntry('Camera.ROIWidth') ROIY2 = ROIY1 + mdh.getEntry('Camera.ROIHeight') if dark is None: dark = float(mdh.getOrDefault('Camera.ADOffset', 0)) P = ImagePyramid(out_folder, pyramid_tile_size, x0=x0, y0=y0, pixel_size=mdh.getEntry('voxelsize.x')) logger.debug('Adding base tiles ...') t1 = time.time() for i in range(int(mdh.getEntry('Protocol.DataStartsAt')), numFrames): if xdp[i - 1] == xdp[i] or not skipMoveFrames: x_i = xdp[i] y_i = ydp[i] d = ds[:, :, i].astype('f') - dark if not flat is None: d = d * flat if split: d = np.concatenate( unmux.Unmix(d, mixmatrix, dark, [ROIX1, ROIY1, ROIX2, ROIY2]), 2) d_weighted = weights * d # TODO - account for orientation so this works for non-primary cams P.add_base_tile(x_i, y_i, d_weighted.squeeze(), weights.squeeze()) t2 = time.time() logger.debug('Added base tiles in %fs' % (t2 - t1)) #P._occ.flush() logger.debug(time.time() - t2) logger.debug('Updating pyramid ...') P.update_pyramid() logger.debug(time.time() - t2) logger.debug('Done') with open(os.path.join(P.base_dir, 'metadata.json'), 'w') as f: f.write(P.mdh.to_JSON()) return P
def tile_pyramid(out_folder, ds, xm, ym, mdh, split=False, skipMoveFrames=False, shiftfield=None, mixmatrix=[[1., 0.], [0., 1.]], correlate=False, dark=None, flat=None, pyramid_tile_size=256): frameSizeX, frameSizeY, numFrames = ds.shape[:3] if split: from PYME.Acquire.Hardware import splitter frameSizeY /= 2 nchans = 2 unmux = splitter.Unmixer(shiftfield, 1e3 * mdh.getEntry('voxelsize.x')) else: nchans = 1 #x & y positions of each frame xps = xm(np.arange(numFrames)) yps = ym(np.arange(numFrames)) if mdh.getOrDefault('CameraOrientation.FlipX', False): xps = -xps if mdh.getOrDefault('CameraOrientation.FlipY', False): yps = -yps rotate_cam = mdh.getOrDefault('CameraOrientation.Rotate', False) #give some room at the edges bufSize = 0 if correlate: bufSize = 300 x0 = xps.min() y0 = yps.min() xps -= x0 yps -= y0 #convert to pixels xdp = (bufSize + (xps / (mdh.getEntry('voxelsize.x'))).round()).astype('i') ydp = (bufSize + (yps / (mdh.getEntry('voxelsize.y'))).round()).astype('i') #calculate a weighting matrix (to allow feathering at the edges - TODO) weights = np.ones((frameSizeX, frameSizeY, nchans)) #weights[:, :10, :] = 0 #avoid splitter edge artefacts #weights[:, -10:, :] = 0 #print weights[:20, :].shape edgeRamp = min(100, int(.25 * ds.shape[0])) weights[:edgeRamp, :, :] *= np.linspace(0, 1, edgeRamp)[:, None, None] weights[-edgeRamp:, :, :] *= np.linspace(1, 0, edgeRamp)[:, None, None] weights[:, :edgeRamp, :] *= np.linspace(0, 1, edgeRamp)[None, :, None] weights[:, -edgeRamp:, :] *= np.linspace(1, 0, edgeRamp)[None, :, None] roi_x0, roi_y0 = get_camera_roi_origin(mdh) ROIX1 = roi_x0 + 1 ROIY1 = roi_y0 + 1 ROIX2 = ROIX1 + mdh.getEntry('Camera.ROIWidth') ROIY2 = ROIY1 + mdh.getEntry('Camera.ROIHeight') if dark is None: offset = float(mdh.getEntry('Camera.ADOffset')) else: offset = 0. P = ImagePyramid(out_folder, pyramid_tile_size, x0=x0, y0=y0, pixel_size=mdh.getEntry('voxelsize.x')) logger.debug('Adding base tiles ...') t1 = time.time() for i in range(int(mdh.getEntry('Protocol.DataStartsAt')), numFrames): if xdp[i - 1] == xdp[i] or not skipMoveFrames: x_i = xdp[i] y_i = ydp[i] d = ds[:, :, i].astype('f') if not dark is None: d = d - dark if not flat is None: d = d * flat if split: d = np.concatenate(unmux.Unmix(d, mixmatrix, offset, [ROIX1, ROIY1, ROIX2, ROIY2]), 2) d_weighted = weights * d # orient frame - TODO - check if we need to flip x and y?! if rotate_cam: #print('adding base tile from frame %d [transposed]' % i) P.add_base_tile(x_i, y_i, d_weighted.T.squeeze(), weights.T.squeeze()) else: #print('adding base tile from frame %d' % i) P.add_base_tile(x_i, y_i, d_weighted.squeeze(), weights.squeeze()) t2 = time.time() logger.debug('Added base tiles in %fs' % (t2 - t1)) #P._occ.flush() logger.debug(time.time() - t2) logger.debug('Updating pyramid ...') P.update_pyramid() logger.debug(time.time() - t2) logger.debug('Done') return P
def tile(ds, xm, ym, mdh, split=True, skipMoveFrames=True, shiftfield=None, mixmatrix=[[1., 0.], [0., 1.]], correlate=False, dark=None, flat=None): frameSizeX, frameSizeY, numFrames = ds.shape[:3] if split: frameSizeY /= 2 nchans = 2 unmux = splitter.Unmixer(shiftfield, 1e3 * mdh.getEntry('voxelsize.x')) else: nchans = 1 #x & y positions of each frame xps = xm(np.arange(numFrames)) yps = ym(np.arange(numFrames)) #give some room at the edges bufSize = 0 if correlate: bufSize = 300 #convert to pixels xdp = bufSize + ((xps - xps.min()) / (1e-3 * mdh.getEntry('voxelsize.x'))).round() ydp = bufSize + ((yps - yps.min()) / (1e-3 * mdh.getEntry('voxelsize.y'))).round() #work out how big our tiled image is going to be imageSizeX = np.ceil(xdp.max() + frameSizeX + bufSize) imageSizeY = np.ceil(ydp.max() + frameSizeY + bufSize) #allocate an empty array for the image im = np.zeros([imageSizeX, imageSizeY, nchans]) # and to record occupancy (to normalise overlapping tiles) occupancy = np.zeros([imageSizeX, imageSizeY, nchans]) #calculate a weighting matrix (to allow feathering at the edges - TODO) weights = np.ones((frameSizeX, frameSizeY, nchans)) weights[:, :10, :] = 0 #avoid splitter edge artefacts weights[:, -10:, :] = 0 #print weights[:20, :].shape weights[:100, :, :] *= linspace(0, 1, 100)[:, None, None] weights[-100:, :, :] *= linspace(1, 0, 100)[:, None, None] weights[:, 10:110, :] *= linspace(0, 1, 100)[None, :, None] weights[:, -110:-10, :] *= linspace(1, 0, 100)[None, :, None] ROIX1 = mdh.getEntry('Camera.ROIPosX') ROIY1 = mdh.getEntry('Camera.ROIPosY') ROIX2 = ROIX1 + mdh.getEntry('Camera.ROIWidth') ROIY2 = ROIY1 + mdh.getEntry('Camera.ROIHeight') if dark == None: offset = mdh.getEntry('Camera.ADOffset') else: offset = 0 # #get a sorted list of x and y values # xvs = list(set(xdp)) # xvs.sort() # # yvs = list(set(ydp)) # yvs.sort() for i in range(mdh.getEntry('Protocol.DataStartsAt'), numFrames): if xdp[i - 1] == xdp[i] or not skipMoveFrames: d = ds[:, :, i] if not dark == None: d = d - dark if not flat == None: d = d * flat if split: d = np.concatenate( unmux.Unmix(d, mixmatrix, offset, [ROIX1, ROIY1, ROIX2, ROIY2]), 2) #else: #d = d.reshape(list(d.shape) + [1]) imr = (im[xdp[i]:(xdp[i] + frameSizeX), ydp[i]:(ydp[i] + frameSizeY), :] / occupancy[xdp[i]:(xdp[i] + frameSizeX), ydp[i]:(ydp[i] + frameSizeY), :]) alreadyThere = (weights * occupancy[xdp[i]:(xdp[i] + frameSizeX), ydp[i]: (ydp[i] + frameSizeY), :]).sum(2) > 0 #d_ = d.sum(2) if split: r0 = imr[:, :, 0][alreadyThere].sum() r1 = imr[:, :, 1][alreadyThere].sum() if r0 == 0: r0 = 1 else: r0 = r0 / (d[:, :, 0][alreadyThere]).sum() if r1 == 0: r1 = 1 else: r1 = r1 / (d[:, :, 1][alreadyThere]).sum() rt = array([r0, r1]) imr = imr.sum(2) else: rt = imr[:, :, 0][alreadyThere].sum() if rt == 0: rt = 1 else: rt = rt / (d[:, :, 0][alreadyThere]).sum() rt = array([rt]) #print rt if correlate: if (alreadyThere.sum() > 50): dx = 0 dy = 0 rois = findRectangularROIs(alreadyThere) for r in rois: x0, y0, x1, y1 = r #print r dx_, dy_ = calcCorrShift( d.sum(2)[x0:x1, y0:y1], imr[x0:x1, y0:y1]) print(('d_', dx_, dy_)) dx += dx_ dy += dy_ dx = np.round(dx / len(rois)) dy = np.round(dy / len(rois)) print((dx, dy)) #dx, dy = (0,0) else: dx, dy = (0, 0) im[(xdp[i] + dx):(xdp[i] + frameSizeX + dx), (ydp[i] + dy):(ydp[i] + frameSizeY + dy), :] += weights * d * rt[None, None, :] occupancy[(xdp[i] + dx):(xdp[i] + frameSizeX + dx), (ydp[i] + dy):(ydp[i] + frameSizeY + dy), :] += weights else: #print weights.shape, rt.shape, d.shape im[xdp[i]:(xdp[i] + frameSizeX), ydp[i]:(ydp[i] + frameSizeY), :] += weights * d * rt[None, None, :] occupancy[xdp[i]:(xdp[i] + frameSizeX), ydp[i]:(ydp[i] + frameSizeY), :] += weights ret = (im / occupancy).squeeze() #print ret.shape, occupancy.shape ret[occupancy.squeeze() == 0] = 0 #fix up /0s return ret