Skip to content

instamatic.gridmontage

Classes

GridMontage(ctrl)

Set up an automated montage map.

Source code in src/instamatic/gridmontage.py
16
17
18
19
20
21
22
def __init__(self, ctrl):
    super().__init__()
    self.ctrl = ctrl
    gridspec = defaults.gridmontage['gridspec']
    self.direction = gridspec['direction']
    self.zigzag = gridspec['zigzag']
    self.flip = gridspec['flip']

Methods:

plot()

Simple plot of the stage coordinates.

Source code in src/instamatic/gridmontage.py
197
198
199
200
201
202
203
204
205
206
def plot(self):
    """Simple plot of the stage coordinates."""
    coords = self.stagecoords / 1000  # nm -> Ξm
    plt.scatter(*coords.T, marker='.', color='red')
    for i, coord in enumerate(coords):
        plt.text(*coord, s=f' {i}')
    plt.axis('equal')
    plt.title('Stage coordinates for grid montage')
    plt.xlabel('x (Ξm)')
    plt.ylabel('y (Ξm)')
save(drc=None)

Save the data to the given directory.

drc : str Path of the output directory. If None, it defaults to the instamatic data directory defined in the config.

Source code in src/instamatic/gridmontage.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
def save(self, drc: str = None):
    """Save the data to the given directory.

    drc : str
        Path of the output directory. If `None`, it defaults to the instamatic data directory defined in the config.
    """
    from instamatic.formats import write_tiff
    from instamatic.io import get_new_work_subdirectory

    if not drc:
        drc = get_new_work_subdirectory('montage')

    fns = []
    for i, (img, h) in enumerate(self.buffer):
        name = f'mont_{i:04d}.tiff'
        write_tiff(drc / name, img, header=h)
        fns.append(name)

    n_images = i + 1

    d = {
        'stagecoords': self.stagecoords.tolist(),
        'stagematrix': self.stagematrix.tolist(),
        'gridshape': [self.nx, self.ny],
        'direction': self.direction,
        'zigzag': self.zigzag,
        'overlap': self.overlap,
        'filenames': fns,
        'magnification': self.magnification,
        'abs_mag_index': self.abs_mag_index,
        'mode': self.mode,
        'spotsize': self.spotsize,
        'flip': self.flip,
        'image_binning': self.binning,
        'pixelsize': self.pixelsize,
    }

    import yaml

    yaml.dump(d, stream=open(drc / 'montage.yaml', 'w'))
    print(f' >> Wrote {n_images} montage images to {drc}')
setup(nx, ny, overlap=defaults.gridmontage['overlap'], stage_shift=(0.0, 0.0), binning=None)

Set up the experiment, run GridMontage.start to acquire data.

Parameters:

  • nx (int) –

    Number of images to collect int he x/y directions.

  • ny (int) –

    Number of images to collect int he x/y directions.

  • overlap (float, default: gridmontage['overlap'] ) –

    How much the images should overlap to calculate the shift between the images.

  • stage_shift (tuple, default: (0.0, 0.0) ) –

    Apply a shift to the calculated stage coordinates. For example, to set the origin. Otherwise, the origin is taken at x=0, y=0.

  • binning (int, default: None ) –

    Binning for the images.

Returns:

  • coords ( array ) –

    Stage coordinates for the montage acquisition

Source code in src/instamatic/gridmontage.py
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def setup(
    self,
    nx: int,
    ny: int,
    overlap: float = defaults.gridmontage['overlap'],
    stage_shift: tuple = (0.0, 0.0),
    binning: int = None,
) -> 'np.array':
    """Set up the experiment, run `GridMontage.start` to acquire data.

    Parameters
    ----------
    nx, ny : int
        Number of images to collect int he x/y directions.
    overlap : float
        How much the images should overlap to calculate the shift between the images.
    stage_shift : tuple
        Apply a shift to the calculated stage coordinates. For example, to set the origin. Otherwise, the origin is taken at x=0, y=0.
    binning : int
        Binning for the images.

    Returns
    -------
    coords : np.array
        Stage coordinates for the montage acquisition
    """
    self.nx = nx
    self.ny = ny
    self.overlap = overlap

    if not binning:
        binning = self.ctrl.cam.get_binning()

    res_x, res_y = self.ctrl.cam.get_image_dimensions()

    overlap_x = int(res_x * overlap)
    overlap_y = int(res_y * overlap)

    vect = np.array((res_x - overlap_x, res_y - overlap_y))

    grid = make_grid((nx, ny), direction=self.direction, zigzag=self.zigzag)
    grid_indices = sorted_grid_indices(grid)
    px_coords = grid_indices * vect

    px_center = vect * ((np.array(grid.shape) / 2) - 0.5)

    self.stagematrix = self.ctrl.get_stagematrix(binning=binning)

    stage_center = np.dot(px_center, self.stagematrix) + stage_shift
    stagepos = np.dot(px_coords, self.stagematrix)

    coords = (stagepos - stage_center).astype(int)

    mode = self.ctrl.mode.get()
    magnification = self.ctrl.magnification.value
    self.stagecoords = coords
    self.grid = grid
    self.mode = mode
    self.magnification = magnification
    self.abs_mag_index = self.ctrl.magnification.absolute_index
    self.spotsize = self.ctrl.spotsize
    self.binning = binning
    self.pixelsize = config.calibration[mode]['pixelsize'][magnification]  # unbinned

    print('Setting up gridscan.')
    print(f'  Mag: {self.magnification}x')
    print(f'  Mode: `{self.mode}`')
    print(
        f'  Grid: {nx} x {ny}; {self.direction}; zigzag: {self.zigzag}; flip: {self.flip}'
    )
    print(f'  Overlap: {self.overlap}')
    print()
    print(f'  Image shape: {res_x} x {res_y}')
    print(f'  Pixel center: {px_center}')
    print(f'  Spot size: {self.spotsize}')
    print(f'  Binning: {self.binning}')
start()

Start the experiment.

Source code in src/instamatic/gridmontage.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def start(self):
    """Start the experiment."""
    ctrl = self.ctrl

    buffer = []

    def eliminate_backlash(ctrl):
        print('Attempting to eliminate backlash...')
        ctrl.stage.eliminate_backlash_xy()

    def acquire_image(ctrl):
        img, h = ctrl.get_image()
        buffer.append((img, h))

    def post_acquire(ctrl):
        pass

    ctrl.acquire_at_items(
        self.stagecoords,
        acquire=acquire_image,
        pre_acquire=eliminate_backlash,
        post_acquire=None,
    )

    self.buffer = buffer

    self.save()
to_montage()

Convert the experimental data to a Montage object.

Source code in src/instamatic/gridmontage.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def to_montage(self):
    """Convert the experimental data to a `Montage` object."""
    images = [im for im, h in self.buffer]
    m = Montage(
        images=images,
        gridspec=self.gridspec,
        overlap=self.overlap,
        stagematrix=self.stagematrix,
        stagecoords=self.stagecoords,
        pixelsize=self.pixelsize,
    )
    m.update_gridspec(flip=not self.flip)  # BUG: Work-around for gridspec madness
    # Possibly related is that images are rotated 90 deg. in SerialEM mrc files

    return m

InstamaticMontage

Bases: Montage

Methods:

export(outfile='stitched.tiff')

Export the stitched image to a tiff file.

Parameters:

  • outfile (str, default: 'stitched.tiff' ) –

    Name of the image file.

Source code in src/instamatic/montage.py
61
62
63
64
65
66
67
68
69
70
71
def export(self, outfile: str = 'stitched.tiff') -> None:
    """Export the stitched image to a tiff file.

    Parameters
    ----------
    outfile : str
        Name of the image file.
    """
    from instamatic.formats import write_tiff

    write_tiff(outfile, self.stitched)
from_montage_yaml(filename='montage.yaml') classmethod

Load montage from a series of tiff files + montage.yaml

Source code in src/instamatic/montage.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@classmethod
def from_montage_yaml(cls, filename: str = 'montage.yaml'):
    """Load montage from a series of tiff files + `montage.yaml`"""
    import yaml

    from instamatic.formats import read_tiff

    p = Path(filename)
    drc = p.parent

    d = yaml.safe_load(open(p))
    fns = (drc / fn for fn in d['filenames'])

    d['stagecoords'] = np.array(d['stagecoords'])
    d['stagematrix'] = np.array(d['stagematrix'])

    images = [read_tiff(fn)[0] for fn in fns]

    gridspec = {
        k: v for k, v in d.items() if k in ('gridshape', 'direction', 'zigzag', 'flip')
    }

    m = cls(images=images, gridspec=gridspec, **d)
    m.update_gridspec(flip=not d['flip'])  # BUG: Work-around for gridspec madness
    # Possibly related is that images are rotated 90 deg. in SerialEM mrc files

    return m
set_calibration(mode, magnification)

Set the calibration parameters for the montage map. Sets the pixelsize and stagematrix from the config files.

Parameters:

  • mode (str) –

    The TEM mode used, i.e. lowmag, mag1, samag

  • magnification (int) –

    The magnification used

Source code in src/instamatic/montage.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def set_calibration(self, mode: str, magnification: int) -> None:
    """Set the calibration parameters for the montage map. Sets the
    pixelsize and stagematrix from the config files.

    Parameters
    ----------
    mode : str
        The TEM mode used, i.e. `lowmag`, `mag1`, `samag`
    magnification : int
        The magnification used
    """
    from instamatic import config

    pixelsize = config.calibration[mode]['pixelsize'][magnification]

    stagematrix = config.calibration[mode]['stagematrix'][magnification]
    stagematrix = np.array(stagematrix).reshape(2, 2)

    self.set_pixelsize(pixelsize)
    self.set_stagematrix(stagematrix)
    self.mode = mode
    self.magnification = magnification

Modules