def calibrate_mag1_from_image_fn(center_fn, other_fn): """Calibrate pixel->stageposition coordinates from a set of images. center_fn: `str` Reference image at the center of the grid (with the clover in the middle) other_fn: `tuple` of `str` Set of images to cross correlate to the first reference image return: instance of Calibration class with conversion methods """ img_cent, h_cent = read_image(center_fn) # binsize = h_cent["ImageBinsize"] cam_dimensions = h_cent['ImageCameraDimensions'] bin_x, bin_y = cam_dimensions / np.array(img_cent.shape) assert bin_x == bin_y, 'Binsizes do not match {bin_x} != {bin_y}' binsize = int(bin_x) img_cent, scale = autoscale(img_cent, maxdim=512) x_cent, y_cent, _, _, _ = h_cent['StagePosition'] xy_cent = np.array([x_cent, y_cent]) print('Center:', center_fn) print('Stageposition: x={:.0f} | y={:.0f}'.format(*xy_cent)) print() shifts = [] stagepos = [] for i, fn in enumerate(other_fn): img, h = read_image(fn) img = imgscale(img, scale) x_xobs, yobs, _, _, _ = h_cent['StagePosition'] print('Image:', fn) print(f'Stageposition: x={xobs:.0f} | y={yobs:.0f}') shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) print('Shift:', shift) print() stagepos.append((xobs, yobs)) shifts.append(shift) # correct for binsize, store as binsize=1 shifts = np.array(shifts) * binsize / scale stagepos = np.array(stagepos) - xy_cent c = CalibStage.from_data(shifts, stagepos, reference_position=xy_cent, camera_dimensions=cam_dimensions) c.plot() c.to_file() return c
def calibrate_stage_lowmag_from_image_fn(center_fn, other_fn): """Calibrate pixel->stageposition coordinates from a set of images. center_fn: `str` Reference image at the center of the grid (with the clover in the middle) other_fn: `tuple` of `str` Set of images to cross correlate to the first reference image return: instance of Calibration class with conversion methods """ img_cent, h_cent = read_image(center_fn) img_cent, scale = autoscale(img_cent, maxdim=512) x_cent, y_cent, _, _, _ = h_cent['StagePosition'] xy_cent = np.array([x_cent, y_cent]) print('Center:', center_fn) print('Stageposition: x={:.0f} | y={:.0f}'.format(*xy_cent)) print() binsize = h_cent['ImageBinsize'] shifts = [] stagepos = [] for fn in other_fn: img, h = read_image(fn) img = imgscale(img, scale) xobs, yobs, _, _, _ = h['StagePosition'] print('Image:', fn) print(f'Stageposition: x={xobs:.0f} | y={yobs:.0f}') print() shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) stagepos.append((xobs, yobs)) shifts.append(shift) # correct for binsize, store as binsize=1 shifts = np.array(shifts) * binsize / scale stagepos = np.array(stagepos) - xy_cent c = CalibStage.from_data(shifts, stagepos, reference_position=xy_cent, header=h_cent) c.plot() return c
def calibrate_directbeam_from_file(center_fn, other_fn, key='DiffShift'): print() print('Center:', center_fn) img_cent, h_cent = load_img(center_fn) readout_cent = np.array(h_cent[key]) img_cent, scale = autoscale(img_cent, maxdim=512) binsize = h_cent['ImageBinsize'] print('{}: x={} | y={}'.format(key, *readout_cent)) shifts = [] readouts = [] for i, fn in enumerate(other_fn): print(fn) img, h = load_img(fn) img = imgscale(img, scale) readout = np.array(h[key]) print() print('Image:', fn) print('{}: dx={} | dy={}'.format(key, *readout)) shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) readouts.append(readout) shifts.append(shift) # correct for binsize, store in binsize=1 shifts = np.array(shifts) * binsize / scale readouts = np.array(readouts) - np.array(readout_cent) c = CalibDirectBeam.from_data(shifts, readouts, key, header=h_cent, **refine_params[key]) c.plot(key) return c
def find_holes_entry(): from formats import read_image for fn in sys.argv[1:]: img, h = read_image(fn) img_zoomed, scale = autoscale(img, maxdim=512) binsize = h['ImageBinsize'] magnification = h['Magnification'] d = 150 area = calculate_hole_area(d, magnification, img_scale=scale, binsize=binsize) holes = find_holes(img_zoomed, area=area, plot=True) print() for hole in holes: x, y = hole.centroid px = py = calibration['lowmag']['pixelsize'][magnification] / 1000 # nm -> um area = hole.area * px * py / scale**2 d = 2 * (area / np.pi)**0.5 print(f'x: {x*scale:.2f}, y: {y*scale:.2f}, d: {d:.2f} um')
def __init__(self, cam='simulate'): threading.Thread.__init__(self) if isinstance(cam, str): self.cam = Camera(name=cam, as_stream=False) else: self.cam = cam self.lock = threading.Lock() self.default_exposure = self.cam.default_exposure self.default_binsize = self.cam.default_binsize self.dimensions = self.cam.dimensions self.name = self.cam.name self.frametime = self.default_exposure self.streamable = self.cam.streamable self.display_dim = 512 self.frame, scale = autoscale(np.ones(self.dimensions), maxdim=self.display_dim)
def calibrate_brightness_from_image_fn(fns): """Calibrate pixel->brightness (size of beam) from a set of images. fns: `str` Set of images to determine size of beam from return: instance of Calibration class with conversion methods """ values = [] for fn in fns: print() print('Image:', fn) img, h = load_img(fn) brightness = float(h['Brightness']) binsize = float(h['ImageBinsize']) img, scale = autoscale(img) holes = find_holes(img, plot=False, fname=None, verbose=False, max_eccentricity=0.8) size = max(hole.equivalent_diameter for hole in holes) * binsize / scale print( f'Brightness: {brightness:.0f}, equivalent diameter: {size:.1f}px') values.append((brightness, size)) values = np.array(values) c = CalibBrightness.from_data(*values.T) c.plot() return c
def calibrate_stage_lowmag_live(ctrl, gridsize=5, stepsize=50000, save_images=False, **kwargs): """Calibrate pixel->stageposition coordinates live on the microscope. ctrl: instance of `TEMController` contains tem + cam interface gridsize: `int` Number of grid points to take, gridsize=5 results in 25 points stepsize: `float` Size of steps for stage position along x and y exposure: `float` exposure time binsize: `int` return: instance of Calibration class with conversion methods """ exposure = kwargs.get('exposure', ctrl.cam.default_exposure) binsize = kwargs.get('binsize', ctrl.cam.default_binsize) outfile = 'calib_start' if save_images else None # Accurate reading fo the center positions is needed so that we can come back to it, # because this will be our anchor point img_cent, h_cent = ctrl.get_image(exposure=exposure, binsize=binsize, out=outfile, comment='Center image (start)') x_cent, y_cent, _, _, _ = h_cent['StagePosition'] xy_cent = np.array([x_cent, y_cent]) img_cent, scale = autoscale(img_cent) stagepos = [] shifts = [] n = int((gridsize - 1) / 2) # number of points = n*(n+1) x_grid, y_grid = np.meshgrid( np.arange(-n, n + 1) * stepsize, np.arange(-n, n + 1) * stepsize) tot = gridsize * gridsize i = 0 for dx, dy in np.stack([x_grid, y_grid]).reshape(2, -1).T: print() print(f'Position {i+1}/{tot}: x: {x_cent+dx:.0f}, y: {y_cent+dy:.0f}') ctrl.stage.set(x=x_cent + dx, y=y_cent + dy) print(ctrl.stage) outfile = f'calib_{i:04d}' if save_images else None comment = comment img, h = ctrl.get_image(exposure=exposure, binsize=binsize, out=outfile, comment=comment, header_keys='StagePosition') img = imgscale(img, scale) shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) xobs, yobs, _, _, _ = h['StagePosition'] stagepos.append((xobs, yobs)) shifts.append(shift) i += 1 print(' >> Reset to center') ctrl.stage.set(x=x_cent, y=y_cent) ctrl.stage.reset_xy() # correct for binsize, store as binsize=1 shifts = np.array(shifts) * binsize / scale stagepos = np.array(stagepos) - np.array((x_cent, y_cent)) m = gridsize**2 // 2 if gridsize % 2 and stagepos[m].max() > 50: print( f' >> Warning: Large difference between image {m}, and center image. These should be close for a good calibration.' ) print(' Difference:', stagepos[m]) print() if save_images: ctrl.get_image(exposure=exposure, binsize=binsize, out='calib_end', comment='Center image (end)') c = CalibStage.from_data(shifts, stagepos, reference_position=xy_cent, header=h_cent) # Calling c.plot with videostream crashes program if not hasattr(ctrl.cam, 'VideoLoop'): c.plot(key) return c
def calibrate_mag1_live(ctrl, gridsize=5, stepsize=5000, minimize_backlash=True, save_images=False, **kwargs): """Calibrate pixel->stageposition coordinates live on the microscope. ctrl: instance of `TEMController` contains tem + cam interface gridsize: `int` Number of grid points to take, gridsize=5 results in 25 points stepsize: `float` Size of steps for stage position along x and y minimize_backlash: bool, Attempt to minimize backlash by overshooting a bit Follows the routine from Oostergetel (1998): https://doi.org/10.1016/S0304-3991(98)00022-9 exposure: `float` Exposure time in seconds binsize: `int` return: instance of Calibration class with conversion methods """ work_drc = get_new_work_subdirectory(stem='calib_mag1') settle_delay = 1.0 # seconds # make sure the angle == 0.0 for _ in range(3): ctrl.stage.a = 0.0 time.sleep(settle_delay) exposure = kwargs.get('exposure', config.camera.default_exposure) binsize = kwargs.get('binsize', config.camera.default_binsize) if minimize_backlash: ctrl.stage.eliminate_backlash_xy(step=stepsize, settle_delay=settle_delay) outfile = work_drc / 'calib_start' if save_images else None # Accurate reading fo the center positions is needed so that we can come back to it, # because this will be our anchor point img_cent, h_cent = ctrl.get_image(exposure=exposure, binsize=binsize, out=outfile, comment='Center image (start)') stage_cent = ctrl.stage.get() cam_dimensions = h_cent['ImageCameraDimensions'] bin_x, bin_y = cam_dimensions / np.array(img_cent.shape) assert bin_x == bin_y, 'Binsizes do not match {bin_x} != {bin_y}' binsize = int(bin_x) x_cent = stage_cent.x y_cent = stage_cent.y xy_cent = np.array([x_cent, y_cent]) img_cent, scale = autoscale(img_cent) stagepos = [] shifts = [] n = int((gridsize - 1) / 2) # number of points = n*(n+1) x_grid, y_grid = np.meshgrid(np.arange(-n, n + 1) * stepsize, np.arange(-n, n + 1) * stepsize) tot = gridsize * gridsize i = 0 x_range = np.arange(-n, n + 1) * stepsize y_range = np.arange(-n, n + 1) * stepsize if minimize_backlash: xtarget = x_cent + x_range[0] ytarget = y_cent + y_range[0] ctrl.stage.set(x=xtarget - stepsize, y=ytarget - stepsize) time.sleep(settle_delay) print('(minimize_backlash) Overshoot a bit in XY: ', ctrl.stage.xy) for dx in x_range: for dy in y_range: ctrl.stage.set(x=x_cent + dx, y=y_cent + dy) time.sleep(settle_delay) stage = ctrl.stage.get() print() print(f'Position {I+1}/{tot}') print(stage) outfile = work_drc / f'calib_{i:04d}' if save_images else None comment = f'Calib image {i}: dx={dx} - dy={dy}' img, h = ctrl.get_image(exposure=exposure, binsize=binsize, out=outfile, comment=comment) img = imgscale(img, scale) shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) xobs = stage.x yobs = stage.y stagepos.append((xobs, yobs)) shifts.append(shift) i += 1 if minimize_backlash: ytarget = y_cent + y_range[0] ctrl.stage.set(y=ytarget - stepsize) time.sleep(settle_delay) print('(minimize_backlash) Overshoot a bit in Y: ', ctrl.stage.xy) print(' >> Reset to center') ctrl.stage.set(x=x_cent, y=y_cent) time.sleep(settle_delay) # ctrl.stage.reset_xy() # correct for binsize, store as binsize=1 shifts = np.array(shifts) * binsize / scale stagepos = np.array(stagepos) - np.array((x_cent, y_cent)) m = gridsize**2 // 2 if gridsize % 2 and stagepos[m].max() > 50: print(f' >> Warning: Large difference between image {m}, and center image. These should be close for a good calibration.') print(' Difference:', stagepos[m]) print() if save_images: outfile = work_drc / 'calib_end' ctrl.get_image(exposure=exposure, binsize=binsize, out=outfile, comment='Center image (end)') c = CalibStage.from_data(shifts, stagepos, reference_position=xy_cent, camera_dimensions=cam_dimensions) c.plot() c.to_file(work_drc / 'calib.pickle') return c
def getImage(self, exposure=None, binsize=None): frame = self.cam.getImage(exposure=exposure, binsize=binsize) self.frame, scale = autoscale(frame, maxdim=self.display_dim) return frame
def calibrate_brightness_live(ctrl, step=1000, save_images=False, **kwargs): """Calibrate pixel->brightness coordinates live on the microscope. ctrl: instance of `TEMController` contains tem + cam interface start: `float` start value for calibration (0.0 - 1.0) end: `float` end value for calibration (0.0 - 1.0) exposure: `float` exposure time binsize: `int` return: instance of CalibBrightness class with conversion methods """ raise NotImplementedError( 'calibrate_brightness_live function needs fixing...') exposure = kwargs.get('exposure', ctrl.cam.default_exposure) binsize = kwargs.get('binsize', ctrl.cam.default_binsize) values = [] start = ctrl.brightness.value for i in range(10): target = start + i * step ctrl.brightness.value = int(target) outfile = f'calib_brightness_{i:04d}' if save_images else None comment = f'Calib image {i}: brightness={target}' img, h = ctrl.get_image(exposure=exposure, out=outfile, comment=comment, header_keys='Brightness') img, scale = autoscale(img) brightness = float(h['Brightness']) holes = find_holes(img, plot=False, verbose=False, max_eccentricity=0.8) if len(holes) == 0: print(' >> No holes found, continuing...') continue size = max(hole.equivalent_diameter for hole in holes) * binsize / scale print(f'Brightness: {brightness:.f}, equivalent diameter: {size:.1f}') values.append((brightness, size)) values = np.array(values) c = CalibBrightness.from_data(*values.T) # Calling c.plot with videostream crashes program if not hasattr(ctrl.cam, 'VideoLoop'): c.plot() return c
def calibrate_directbeam_live(ctrl, key='DiffShift', gridsize=None, stepsize=None, save_images=False, outdir='.', **kwargs): """Calibrate pixel->beamshift coordinates live on the microscope. ctrl: instance of `TEMController` contains tem + cam interface key: `str` Name of property to calibrate gridsize: `int` or None Number of grid points to take, gridsize=5 results in 25 points stepsize: `float` or None Size of steps for property along x and y exposure: `float` or None exposure time binsize: `int` or None In case paramers are not defined, camera specific default parameters are return: instance of Calibration class with conversion methods """ if ctrl.mode != 'diff': print(' >> Switching to diffraction mode') ctrl.mode.set('diff') exposure = kwargs.get('exposure', ctrl.cam.default_exposure) binsize = kwargs.get('binsize', ctrl.cam.default_binsize) if not gridsize: gridsize = config.camera.calib_directbeam.get(key, {}).get( 'gridsize', 5) # dat syntax... if not stepsize: stepsize = config.camera.calib_directbeam.get(key, {}).get( 'stepsize', 750) # just to fit everything on 1 line =) attr = getattr(ctrl, key.lower()) outfile = os.path.join(outdir, f'calib_db_{key}_0000') if save_images else None img_cent, h_cent = ctrl.get_image(exposure=exposure, binsize=binsize, comment='Beam in center of image', out=outfile) x_cent, y_cent = readout_cent = np.array(h_cent[key]) img_cent, scale = autoscale(img_cent) print('{}: x={} | y={}'.format(key, *readout_cent)) shifts = [] readouts = [] n = int((gridsize - 1) / 2) # number of points = n*(n+1) x_grid, y_grid = np.meshgrid( np.arange(-n, n + 1) * stepsize, np.arange(-n, n + 1) * stepsize) tot = gridsize * gridsize for i, (dx, dy) in enumerate(np.stack([x_grid, y_grid]).reshape(2, -1).T): i += 1 attr.set(x=x_cent + dx, y=y_cent + dy) printer(f'Position: {i}/{tot}: {attr}') outfile = os.path.join( outdir, f'calib_db_{key}_{i:04d}') if save_images else None comment = f'Calib image {i}: dx={dx} - dy={dy}' img, h = ctrl.get_image(exposure=exposure, binsize=binsize, out=outfile, comment=comment, header_keys=key) img = imgscale(img, scale) shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) readout = np.array(h[key]) readouts.append(readout) shifts.append(shift) print('') # print "\nReset to center" attr.set(*readout_cent) # correct for binsize, store in binsize=1 shifts = np.array(shifts) * binsize / scale readouts = np.array(readouts) - np.array(readout_cent) c = CalibDirectBeam.from_data(shifts, readouts, key, header=h_cent, **refine_params[key]) # Calling c.plot with videostream crashes program # if not hasattr(ctrl.cam, "VideoLoop"): # c.plot(key) return c
def find_crystals(img, magnification, spread=2.0, plot=False, **kwargs): """Function for finding crystals in a low contrast images. Used adaptive thresholds to find local features. Edges are detected, and rejected, on the basis of a histogram. Kmeans clustering is used to spread points over the segmented area. img: 2d np.ndarray Input image to locate crystals on magnification: float value indicating the magnification used, needed in order to determine the size of the crystals spread: float Value in micrometer to roughly indicate the desired spread of centroids over individual regions plot: bool Whether to plot the results or not **kwargs: keywords to pass to segment_crystals """ img, scale = autoscale(img, maxdim=256) # scale down for faster # segment the image, and find objects arr, seg = segment_crystals(img, **kwargs) labels, numlabels = ndimage.label(seg) props = measure.regionprops(labels, img) # calculate the pixel dimensions in micrometer px = py = calibration['mag1']['pixelsize'][magnification] / 1000 # nm -> um iters = 20 crystals = [] for prop in props: area = prop.area * px * py bbox = np.array(prop.bbox) # origin of the prop origin = bbox[0:2] # edge detection if isedge(prop): continue # number of centroids for kmeans clustering nclust = int(area // spread) + 1 if nclust > 1: # use skmeans clustering to segment large blobs coordinates = np.argwhere(prop.image) # kmeans needs normalized data (w), store std to calculate coordinates after w, std = whiten(coordinates) # nclust must be an integer for some reason cluster_centroids, closest_centroids = kmeans2(w, nclust, iter=iters, minit='points') # convert to image coordinates xy = (cluster_centroids * std + origin[0:2]) / scale crystals.extend([ CrystalPosition(x, y, False, nclust, area, prop.area) for x, y in xy ]) else: x, y = prop.centroid crystals.append( CrystalPosition(x / scale, y / scale, True, nclust, area, prop.area)) if plot: plt.imshow(img) plt.contour(seg, [0.5], linewidths=1.2, colors='yellow') if len(crystals) > 0: x, y = np.array([(crystal.x * scale, crystal.y * scale) for crystal in crystals]).T plt.scatter(y, x, color='red') ax = plt.axes() ax.set_axis_off() plt.show() return crystals
def calibrate_beamshift_from_image_fn(center_fn, other_fn): """Calibrate pixel->beamshift coordinates from a set of images. center_fn: `str` Reference image with the beam at the center of the image other_fn: `tuple` of `str` Set of images to cross correlate to the first reference image return: instance of Calibration class with conversion methods """ print() print('Center:', center_fn) img_cent, h_cent = load_img(center_fn) beamshift_cent = np.array(h_cent['BeamShift']) img_cent, scale = autoscale(img_cent, maxdim=512) binsize = h_cent['ImageBinsize'] holes = find_holes(img_cent, plot=False, verbose=False, max_eccentricity=0.8) pixel_cent = np.array(holes[0].centroid) * binsize / scale print('Beamshift: x={} | y={}'.format(*beamshift_cent)) print('Pixel: x={:.2f} | y={:.2f}'.format(*pixel_cent)) shifts = [] beampos = [] for fn in other_fn: img, h = load_img(fn) img = imgscale(img, scale) beamshift = np.array(h['BeamShift']) print() print('Image:', fn) print('Beamshift: x={} | y={}'.format(*beamshift)) shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) beampos.append(beamshift) shifts.append(shift) # correct for binsize, store as binsize=1 shifts = np.array(shifts) * binsize / scale beampos = np.array(beampos) - beamshift_cent c = CalibBeamShift.from_data(shifts, beampos, reference_shift=beamshift_cent, reference_pixel=pixel_cent, header=h_cent) c.plot() return c
def calibrate_beamshift_live(ctrl, gridsize=None, stepsize=None, save_images=False, outdir='.', **kwargs): """Calibrate pixel->beamshift coordinates live on the microscope. ctrl: instance of `TEMController` contains tem + cam interface gridsize: `int` or None Number of grid points to take, gridsize=5 results in 25 points stepsize: `float` or None Size of steps for beamshift along x and y Defined at a magnification of 2500, scales stepsize down for other mags. exposure: `float` or None exposure time binsize: `int` or None In case paramers are not defined, camera specific default parameters are retrieved return: instance of Calibration class with conversion methods """ exposure = kwargs.get('exposure', ctrl.cam.default_exposure) binsize = kwargs.get('binsize', ctrl.cam.default_binsize) if not gridsize: gridsize = config.camera.calib_beamshift.get('gridsize', 5) if not stepsize: stepsize = config.camera.calib_beamshift.get('stepsize', 250) img_cent, h_cent = ctrl.get_image(exposure=exposure, binsize=binsize, comment='Beam in center of image') x_cent, y_cent = beamshift_cent = np.array(h_cent['BeamShift']) magnification = h_cent['Magnification'] stepsize = 2500.0 / magnification * stepsize print(f'Gridsize: {gridsize} | Stepsize: {stepsize:.2f}') img_cent, scale = autoscale(img_cent) outfile = os.path.join(outdir, 'calib_beamcenter') if save_images else None pixel_cent = find_beam_center(img_cent) * binsize / scale print('Beamshift: x={} | y={}'.format(*beamshift_cent)) print('Pixel: x={} | y={}'.format(*pixel_cent)) shifts = [] beampos = [] n = int((gridsize - 1) / 2) # number of points = n*(n+1) x_grid, y_grid = np.meshgrid( np.arange(-n, n + 1) * stepsize, np.arange(-n, n + 1) * stepsize) tot = gridsize * gridsize i = 0 for dx, dy in np.stack([x_grid, y_grid]).reshape(2, -1).T: ctrl.beamshift.set(x=x_cent + dx, y=y_cent + dy) printer('Position: {}/{}: {}'.format(i + 1, tot, ctrl.beamshift)) outfile = os.path.join( outdir, 'calib_beamshift_{i:04d}') if save_images else None comment = f'Calib image {i}: dx={dx} - dy={dy}' img, h = ctrl.get_image(exposure=exposure, binsize=binsize, out=outfile, comment=comment, header_keys='BeamShift') img = imgscale(img, scale) shift, error, phasediff = phase_cross_correlation(img_cent, img, upsample_factor=10) beamshift = np.array(h['BeamShift']) beampos.append(beamshift) shifts.append(shift) i += 1 print('') # print "\nReset to center" ctrl.beamshift.set(*beamshift_cent) # correct for binsize, store in binsize=1 shifts = np.array(shifts) * binsize / scale beampos = np.array(beampos) - np.array(beamshift_cent) c = CalibBeamShift.from_data(shifts, beampos, reference_shift=beamshift_cent, reference_pixel=pixel_cent, header=h_cent) # Calling c.plot with videostream crashes program # if not hasattr(ctrl.cam, "VideoLoop"): # c.plot() return c