def saveImage(self):
        """Dump the current frame to a file"""
        outfile = datetime.datetime.now().strftime(
            "%Y%m%d-%H%M%S.%f") + ".tiff"

        if self.app:
            module_io = self.app.get_module("io")

            drc = module_io.get_experiment_directory()
            drc.mkdir(exist_ok=True, parents=True)

            outfile = drc / outfile

            try:
                flatfield, h = read_tiff(module_io.get_flatfield())
                frame = apply_flatfield_correction(self.frame, flatfield)
            except:
                frame = self.frame
                h = {}
        else:
            frame = self.frame
            h = {}

        write_tiff(outfile, frame, header=h)
        print(" >> Wrote file:", outfile)
示例#2
0
    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)
示例#3
0
 def write_image_data(self, buffer: list):
     """Write image data in the buffer.
     The image buffer is passed as a list of tuples, where each tuple contains the
     index (int), image data (2D numpy array), metadata/header (dict)."""
     if buffer:
         drc = self.path / "tiff_image"
         drc.mkdir(exist_ok=True)
         while len(buffer) != 0:
             i, img, h = buffer.pop(0)
             fn = drc / f"{i:05d}.tiff"
             write_tiff(fn, img, header=h)
示例#4
0
def test_tiff(data, header):
    out = 'out.tiff'

    formats.write_tiff(out, data, header)

    assert os.path.exists(out)

    img, h = formats.read_image(out)

    assert np.allclose(img, data)
    assert header == h
示例#5
0
    def write_tiff(self, path: str, i: int) -> str:
        """Write the image+header with sequence number `i` to the directory `path` in TIFF format.
        Returns the path to the written image."""
        img = self.data[i]
        h = self.headers[i]

        # PETS reads only 16bit unsignt integer TIFF
        img = np.round(img, 0).astype(np.uint16)

        fn = path / f"{i:05d}.tiff"
        write_tiff(fn, img, header=h)
        return fn
示例#6
0
def save_image(controller, **kwargs):
    frame = kwargs.get('frame')

    module_io = controller.app.get_module('io')

    drc = module_io.get_experiment_directory()
    drc.mkdir(exist_ok=True, parents=True)

    timestamp = datetime.now().strftime(
        '%H-%M-%S.%f')[:-3]  # cut last 3 digits for ms resolution
    outfile = drc / f'frame_{timestamp}.tiff'

    try:
        flatfield, h = read_tiff(module_io.get_flatfield())
        frame = apply_flatfield_correction(frame, flatfield)
    except BaseException:
        frame = frame
        h = {}

    write_tiff(outfile, frame, header=h)
    print('Wrote file:', outfile)
示例#7
0
    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}')
示例#8
0
    def start_collection(self,
                         target_angle: float,
                         start_angle: float = None,
                         manual_control: bool = False):
        """
        manual_control : bool
            Control the rotation using the buttons or pedals
        """
        angle_tolerance = 0.5  # degrees

        self.now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        self.stage_positions = []
        interval = 0.7  # interval at which it check for rotation to end / does tracking

        if self.track:
            start_angle, target_angle = self.prepare_tracking()

        if start_angle:
            print(f'Going to starting angle: {start_angle:.1f}')
            self.ctrl.stage.a = start_angle

        self.start_position = self.ctrl.stage.get()
        start_angle = self.start_position.a

        if self.track:
            # Center crystal position
            if self.mode == 'diff':
                self.ctrl.difffocus.defocus(self.defocus_offset)
            self.ctrl.beam.unblank()

            input(
                'Move SAED aperture to crystal and press <ENTER> to measure! ')

            # cannot do this while lieview is running
            # img1 = self.ctrl.get_rotated_image()
            # write_tiff(self.path / "image_before.tiff", img1)

            self.ctrl.beam.blank()
            if self.mode == 'diff':
                self.ctrl.difffocus.refocus()
            time.sleep(3)

        self.ctrl.beam.unblank(
            delay=0.2
        )  # give the beamblank some time to dissappear to avoid weak first frame

        # with autoincrement(False), otherwise use `get_next_empty_image_index()`
        # start_index is set to 1, because EMMENU always takes a single image (0) when liveview is activated
        start_index = 1
        # start_index = self.emmenu.get_next_empty_image_index()

        if manual_control:
            start_angle = self.manual_activation()
            last_angle = 999
        elif self.rotation_speed:
            self.ctrl.stage.set_a_with_speed(a=target_angle,
                                             speed=self.rotation_speed,
                                             wait=False)
        else:
            self.ctrl.stage.set(a=target_angle, wait=False)

        self.emmenu.start_record()  # start recording

        t0 = time.perf_counter()
        t_delta = t0

        n = 0

        print('Acquiring data...')

        while True:
            t = time.perf_counter()

            if not manual_control:
                if abs(self.ctrl.stage.a - target_angle) < angle_tolerance:
                    print('Target angle reached!')
                    break

            if t - t_delta > interval:

                n += 1
                x, y, z, a, _ = pos = self.ctrl.stage.get()
                self.stage_positions.append((t, pos))
                t_delta = t
                # print(t, pos)

                if manual_control:
                    current_angle = a
                    if last_angle == current_angle:
                        print(
                            f'Manual rotation was interrupted (current: {current_angle:.2f} | last {last_angle:.2f})'
                        )
                        break
                    last_angle = current_angle

                    print(f' >> Current angle: {a:.2f}', end='      \r')

                if self.track:
                    self.track_crystal(n=n, angle=a)

            # Stop/interrupt and go to next crystal
            if msvcrt.kbhit():
                key = msvcrt.getch().decode()
                if key == ' ':
                    print('Stopping the stage!')
                    self.ctrl.stage.stop()
                    break
                if key == 'q':
                    self.ctrl.stage.stop()
                    raise InterruptedError('Data collection was interrupted!')

        t1 = time.perf_counter()
        self.emmenu.stop_liveview()

        if self.ctrl.beam.is_blanked:
            self.ctrl.beam.blank()

        self.end_position = self.ctrl.stage.get()
        end_angle = self.end_position.a

        end_index = self.emmenu.get_image_index()

        self.t_start = t0
        self.t_end = t1
        self.total_time = t1 - t0

        self.nframes = nframes = end_index - start_index + 1
        if nframes < 1:
            print('No frames measured??')
            return

        self.osc_angle = abs(end_angle - start_angle) / nframes

        # acquisition_time = total_time / nframes
        self.total_angle = abs(end_angle - start_angle)
        self.rotation_axis = config.camera.camera_rotation_vs_stage_xy
        self.camera_length = int(self.ctrl.magnification.get())
        self.spotsize = self.ctrl.spotsize
        self.rotation_speed = (end_angle - start_angle) / self.total_time
        self.exposure_time = self.emmenu.get_exposure()
        self.start_angle, self.end_angle = start_angle, end_angle

        try:
            # sometimes breaks with:
            # AttributeError: 'NoneType' object has no attribute 'EMVector'
            timestamps = self.emmenu.get_timestamps(start_index, end_index)
        except AttributeError as e:
            print(e)
            print(f'Timestamps from {start_index} to {end_index}')
            timestamps = [1, 2, 3, 4, 5]  # just to make it work

        self.timings = get_acquisition_time(timestamps,
                                            exp_time=self.exposure_time,
                                            savefig=True,
                                            drc=self.path)

        self.log_end_status()
        self.log_stage_positions()

        print('Writing data files...')
        path_data = self.path / 'tiff'
        path_data.mkdir(exist_ok=True, parents=True)

        self.emmenu.writeTiffs(start_index, end_index, path=path_data)

        if self.track:
            # Center crystal position
            if self.mode == 'diff':
                self.ctrl.difffocus.defocus(self.defocus_offset)
            self.ctrl.beam.unblank()

            img2 = self.ctrl.get_rotated_image()
            write_tiff(self.path / 'image_after.tiff', img2)

            self.ctrl.beam.blank()
            if self.mode == 'diff':
                self.ctrl.difffocus.refocus()

        print(
            f'Wrote {nframes} images (#{start_index}->#{end_index}) to {path_data}'
        )

        if self.track:
            print(f'Done with this crystal (number #{self.crystal_number})!')
        else:
            print('Done with this crystal!')
示例#9
0
def main_entry():
    import argparse
    from instamatic.formats import write_tiff

    description = """Simple program to acquire image data from the camera."""

    parser = argparse.ArgumentParser(
        description=description,
        formatter_class=argparse.RawDescriptionHelpFormatter)

    parser.add_argument(
        '-b',
        '--binsize',
        action='store',
        type=int,
        metavar='N',
        dest='binsize',
        help="""Binsize to use. Must be one of 1, 2, or 4 (default 1)""")

    parser.add_argument('-e',
                        '--exposure',
                        action='store',
                        type=float,
                        metavar='N',
                        dest='exposure',
                        help="""Exposure time (default 0.5)""")

    parser.add_argument('-o',
                        '--out',
                        action='store',
                        type=str,
                        metavar='image.png',
                        dest='outfile',
                        help="""Where to store image""")

    parser.add_argument('-d',
                        '--display',
                        action='store_true',
                        dest='show_fig',
                        help="""Show the image (default True)""")

    parser.add_argument(
        '-s',
        '--series',
        action='store_true',
        dest='take_series',
        help="""Enable mode to take a series of images (default False)""")

    parser.set_defaults(
        binsize=1,
        exposure=1,
        outfile=None,
        show_fig=False,
        test=False,
        take_series=False,
    )

    options = parser.parse_args()

    binsize = options.binsize
    exposure = options.exposure

    outfile = options.outfile
    show_fig = options.show_fig

    take_series = options.take_series

    from instamatic import TEMController
    ctrl = TEMController.initialize()

    if take_series:
        i = 1
        print('\nUsage:')
        print('    set b/e/i X -> set binsize/exposure/file number to X')
        print('    XXX         -> Add comment to header')
        print('    exit        -> exit the program')
    while take_series:
        outfile = f'image_{i:04d}'
        inp = input(f'\nHit enter to take an image: \n >> [{outfile}] ')
        if inp == 'exit':
            break
        elif inp.startswith('set'):
            try:
                key, value = inp.split()[1:3]
            except ValueError:
                print('Input not understood')
                continue
            if key == 'e':
                try:
                    value = float(value)
                except ValueError as e:
                    print(e)
                if value > 0:
                    exposure = value
            elif key == 'b':
                try:
                    value = int(value)
                except ValueError as e:
                    print(e)
                if value in (1, 2, 4):
                    binsize = value
            elif key == 'i':
                try:
                    value = int(value)
                except ValueError as e:
                    print(e)
                if value > 0:
                    i = value
            print(f'binsize = {binsize} | exposure = {exposure} | file #{i}')
        else:
            arr, h = ctrl.get_image(binsize=binsize,
                                    exposure=exposure,
                                    comment=inp)

            write_tiff(outfile, arr, header=h)

            i += 1
    else:
        import matplotlib.pyplot as plt

        arr, h = ctrl.get_image(binsize=binsize, exposure=exposure)

        if show_fig:
            plt.imshow(arr, cmap='gray', interpolation='none')
            plt.show()

        if outfile:
            write_tiff(outfile, arr, header=h)
        else:
            write_tiff('out', arr, header=h)
示例#10
0
    def start_collection(self,
                         target_angle: float,
                         start_angle: float = None,
                         manual_control: bool = False):
        """
        manual_control : bool
            Control the rotation using the buttons or pedals
        """
        angle_tolerance = 0.5  # degrees

        self.now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        self.stage_positions = []
        interval = 0.7  # interval at which it check for rotation to end / does tracking

        if self.track:
            start_angle, target_angle = self.prepare_tracking()

        if start_angle:
            print(f'Going to starting angle: {start_angle:.1f}')
            self.ctrl.stage.a = start_angle

        self.start_position = self.ctrl.stage.get()
        start_angle = self.start_position.a

        if self.track:
            # Center crystal position
            if self.mode == 'diff':
                self.ctrl.difffocus.defocus(self.defocus_offset)
            self.ctrl.beam.unblank()

            input(
                'Move SAED aperture to crystal and press <ENTER> to measure! ')

            # cannot do this while lieview is running
            # img1 = self.ctrl.get_raw_image()
            # write_tiff(self.path / "image_before.tiff", img1)

            self.ctrl.beam.blank()
            if self.mode == 'diff':
                self.ctrl.difffocus.refocus()
            time.sleep(3)

        self.ctrl.beam.unblank()(
            delay=0.2
        )  # give the beamblank some time to dissappear to avoid weak first frame

        if manual_control:
            start_angle = self.manual_activation()
            last_angle = 999
        elif self.rotation_speed:
            self.ctrl.stage.set_a_with_speed(a=target_angle,
                                             speed=self.rotation_speed,
                                             wait=False)
        else:
            self.ctrl.stage.set(a=target_angle, wait=False)

        self.cam.start_record()  # start recording

        t0 = time.perf_counter()
        t_delta = t0

        n = 0

        print('Acquiring data...')

        while True:
            t = time.perf_counter()

            if not manual_control:
                if abs(self.ctrl.stage.a - target_angle) < angle_tolerance:
                    print('Target angle reached!')
                    break

            if t - t_delta > interval:

                n += 1
                x, y, z, a, _ = pos = self.ctrl.stage.get()
                self.stage_positions.append((t, pos))
                t_delta = t
                # print(t, pos)

                if manual_control:
                    current_angle = a
                    if last_angle == current_angle:
                        print(
                            f'Manual rotation was interrupted (current: {current_angle:.2f} | last {last_angle:.2f})'
                        )
                        break
                    last_angle = current_angle

                    print(f' >> Current angle: {a:.2f}', end='      \r')

                if self.track:
                    self.track_crystal(n=n, angle=a)

            # Stop/interrupt and go to next crystal
            if msvcrt.kbhit():
                key = msvcrt.getch().decode()
                if key == ' ':
                    print('Stopping the stage!')
                    self.ctrl.stage.stop()
                    break
                if key == 'q':
                    self.ctrl.stage.stop()
                    raise InterruptedError('Data collection was interrupted!')

        t1 = time.perf_counter()
        self.cam.stop_record()

        if self.ctrl.beam.is_blanked:
            self.ctrl.beam.blank()

        self.end_position = self.ctrl.stage.get()
        end_angle = self.end_position.a

        self.t_start = t0
        self.t_end = t1
        self.total_time = t1 - t0

        print('Waiting for DM to finish...')
        while not self.cam.get_tag('finish_acquire'):
            time.sleep(0.2)

        self.gatan_readout = self.cam.readout()

        self.nframes = nframes = self.gatan_readout['nframes']
        if nframes < 1:
            print('No frames measured??')
            return

        self.osc_angle = abs(end_angle - start_angle) / nframes

        # acquisition_time = total_time / nframes
        self.total_angle = abs(end_angle - start_angle)
        self.rotation_axis = config.camera.camera_rotation_vs_stage_xy
        self.camera_length = int(self.ctrl.magnification.get())
        self.spotsize = self.ctrl.spotsize
        self.rotation_speed = (end_angle - start_angle) / self.total_time
        # self.exposure_time = self.cam.get_exposure()
        self.start_angle, self.end_angle = start_angle, end_angle

        self.log_end_status()
        self.log_stage_positions()

        print('Writing data files...')
        path_data = self.path / 'tiff'
        path_data.mkdir(exist_ok=True, parents=True)

        if self.track:
            # Center crystal position
            if self.mode == 'diff':
                self.ctrl.difffocus.defocus(self.defocus_offset)
            self.ctrl.beam.unblank()

            img2 = self.ctrl.get_rotated_image()
            write_tiff(self.path / 'image_after.tiff', img2)

            self.ctrl.beam.blank()
            if self.mode == 'diff':
                self.ctrl.difffocus.refocus()

        print(f'Wrote {nframes} images to {path_data}')

        if self.track:
            print(f'Done with this crystal (number #{self.crystal_number})!')
        else:
            print('Done with this crystal!')
示例#11
0
    def start_collection(self, exposure_time: float, tilt_range: float, stepsize: float):
        """Start or continue data collection for `tilt_range` degrees with steps given by `stepsize`,
        To finalize data collection and write data files, run `self.finalize`.

        The number of images collected is defined by `tilt_range / stepsize`.

        exposure_time:
            Exposure time for each image in seconds
        tilt_range:
            Tilt range starting from the current angle in degrees. Must be positive.
        stepsize:
            Step size for the angle in degrees, controls the direction and can be positive or negative

        """
        self.spotsize = self.ctrl.spotsize
        self.now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.logger.info("Data recording started at: {self.now}")
        self.logger.info(f"Exposure time: {exposure_time} s, Tilt range: {tilt_range}, step size: {stepsize}")

        ctrl = self.ctrl

        if stepsize < 0:
            tilt_range = -abs(tilt_range)
        else:
            tilt_range =  abs(tilt_range)

        if self.current_angle is None:
            self.start_angle = start_angle = ctrl.stageposition.a
        else:
            start_angle = self.current_angle + stepsize

        tilt_positions = np.arange(start_angle, start_angle+tilt_range, stepsize)
        print(f"\nStart_angle: {start_angle:.3f}")
        # print "Angles:", tilt_positions

        image_mode = ctrl.mode
        if image_mode != "diff":
            fn = self.tiff_image_path / f"image_{self.offset}.tiff"
            img, h = self.ctrl.getImage(exposure_time / 5)
            write_tiff(fn, img, header=h)
            ctrl.mode_diffraction()
            time.sleep(1.0)  # add some delay to account for beam lag

        if ctrl.cam.streamable:
            ctrl.cam.block()
    
        # for i, a in enumerate(tilt_positions):
        for i, angle in enumerate(tqdm.tqdm(tilt_positions)):
            ctrl.stageposition.a = angle

            j = i + self.offset

            img, h = self.ctrl.getImage(exposure_time)

            self.buffer.append((j, img, h))

        self.offset += len(tilt_positions)
        self.nframes = j

        self.end_angle = end_angle = ctrl.stageposition.a

        if ctrl.cam.streamable:
            ctrl.cam.unblock()

        self.camera_length = camera_length = int(self.ctrl.magnification.get())
        self.stepsize = stepsize
        self.exposure_time = exposure_time

        with open(self.path / "summary.txt", "a") as f:
            print(f"{self.now}: Data collected from {start_angle:.2f} degree to {end_angle:.2f} degree in {len(tilt_positions)} frames.", file=f)
            print(f"Data collected from {start_angle:.2f} degree to {end_angle:.2f} degree in {len(tilt_positions)} frames.")

        self.logger.info("Data collected from {start_angle:.2f} degree to {end_angle:.2f} degree (camera length: {camera_length} mm).")
        
        self.current_angle = angle
        print(f"Done, current angle = {self.current_angle:.2f} degrees")

        if image_mode != "diff":
            ctrl.mode = image_mode
示例#12
0
def do_experiment(cam,
                  beam_ctrl,
                  strength,
                  grid_x,
                  grid_y,
                  dwell_time,
                  rotation,
                  exposure,
                  shuffle_coords=False,
                  blocksize=1024,
                  stream_latency=None,
                  hardware_latency=0.0,
                  expdir=None,
                  plot=False,
                  gui=True):

    channels = beam_ctrl.n_channels
    fs = beam_ctrl.fs

    print("starting experiment")
    print()
    print(f"    Dwell time: {dwell_time}")
    print(f"    Exposure:   {exposure}")
    print(f"    Latency:    {stream_latency} + {hardware_latency}")
    print(f"    Exposure:   {exposure}")
    print(f"    Grid x:     {grid_x}")
    print(f"    Grid y:     {grid_y}")
    print(f"    Rotation:   {rotation}")
    print(f"    Strength:   {strength}")
    print()

    ft = 1 / fs

    if blocksize == 0:
        blocksize = int(dwell_time / ft)

    nblocks = int((dwell_time / ft) // blocksize)
    dwell_time = ft * blocksize * nblocks

    print(f"Block duration: {blocksize*ft:.3f} ms")
    print(f"N blocks: {nblocks}")
    print(
        f"Adjusted dwell time: {dwell_time:.3f} s @ {blocksize} frames/block")
    print()

    assert dwell_time > exposure, f"Dwell time ({dwell_time} s) must be larger than exposure ({exposure} s)"

    i = 0

    def callback(outdata, frames, streamtime, status):
        assert frames == blocksize

        if status.output_underflow:
            print('Output underflow: reduce exposure?')
            raise sd.CallbackAbort
        elif status.priming_output:
            pass
        else:
            assert not status, str(status)

        try:
            collect, coord = next(gen_coords)
            data.reshape(-1)[0::channels] = coord[0]
            data.reshape(-1)[1::channels] = coord[1]
        except StopIteration:
            raise sd.CallbackStop

        if collect:
            # print(f"{streamtime.currentTime-start_time:.3f} - {streamtime.outputBufferDacTime-start_time:.3f}, {streamtime.outputBufferDacTime-streamtime.currentTime:.3f} ")
            queue.append(streamtime.outputBufferDacTime)
        # else:
        # print(f"Collect: False @ {streamtime.outputBufferDacTime:6.3f}")

        outdata[:] = data

    data = np.zeros(blocksize * channels,
                    dtype=np.float32).reshape(-1, channels)

    event = threading.Event()

    try:
        stream_latency = float(stream_latency)
    except TypeError:
        if stream_latency is None:
            stream_latency = beam_ctrl.latency
    except ValueError:
        # must be 'low' or 'high'
        pass

    # dither: Add random noise to make signal less determininistic, I assume we do not want this
    stream = sd.OutputStream(samplerate=fs,
                             blocksize=blocksize,
                             latency=stream_latency,
                             device=beam_ctrl.device,
                             channels=beam_ctrl.n_channels,
                             dtype=beam_ctrl.dtype,
                             callback=callback,
                             finished_callback=event.set,
                             extra_settings=beam_ctrl.extra_settings,
                             dither_off=True,
                             prime_output_buffers_using_stream_callback=True)

    coords = get_coords(grid_x, grid_y, strength, rotation)

    if shuffle_coords:
        permutation = np.random.permutation(len(coords))
        coords = coords[permutation]

    gen_coords = signal_generator(coords, repeat=nblocks)

    queue = deque()
    buffer = []

    waiting_times = []
    frame_start_times = []
    frame_times = []
    missed = []
    empty = np.zeros((516, 516), dtype=np.uint16)

    i = 0

    time.sleep(0.25)

    if gui:
        cam.block()

    with stream:
        start_time = stream.time
        window_start = start_time

        print(f"Reported stream latency: {stream.latency:.3f}")
        print(f"Stream start time: {start_time:.3f}")
        print()

        while stream.active or len(queue):
            previous_window_start = window_start

            try:
                next_frame_time = queue.popleft()
            except IndexError:
                # print(f" -> No frames @ {stream.time-start_time:6.3f}, continuing!")
                time.sleep(dwell_time / 2)
                continue
            else:
                i += 1

            s = f"\r {i:4d} "

            current_time = stream.time

            window_start = next_frame_time + hardware_latency
            window_end = window_start + dwell_time

            s += f"[ c: {current_time - start_time:.3f} | n: {window_start - start_time:.3f} | d: {window_start - previous_window_start:.3f} ] -> "

            if current_time < window_start:
                diff = window_start - current_time
                s += f"Waiting: {diff:.3f} s"
                time.sleep(window_start - current_time)
            elif current_time > window_end - exposure:
                s += f"        +{current_time - window_start:.3f} s -> Miss :( "
                missed.append(i)
                buffer.append(
                    empty)  # insert empty to maintain correct data shape
                print(s, end='')
                continue
            else:
                s += f"        +{current_time - window_start:.3f} s"

            arr = cam.getImage(exposure).astype(
                np.uint16, copy=False
            )  # copy=False ensures that no copy is made if dtype is already satisfied
            buffer.append(arr)
            s += f" -> OK!     [ Miss: {len(missed)} ]         "
            print(s, end='')

            frame_time = window_start - previous_window_start

            frame_times.append(frame_time)
            frame_start_times.append(window_start)
            waiting_times.append(window_start - current_time)

        event.wait()  # Wait until playback is finished

    if gui:
        cam.unblock()

    ntot = len(coords)
    nmissed = len(missed)
    print()
    print("Scanning done!")
    print(f"Stream latency:     {1000*stream.latency:.2f} ms")
    print(
        f"Average wait:       {1000*np.mean(waiting_times[1:]):.2f} +- {1000*np.std(waiting_times[1:]):.2f} ms"
    )
    print(
        f"Average frame time: {1000*np.mean(frame_times[1:]):.2f} +- {1000*np.std(frame_times[1:]):.2f} ms"
    )

    dt = max(frame_start_times) - min(frame_start_times)
    print(f"Time taken:         {dt:.1f} s ({ntot} frames)")
    print(f"Frametime:          {1000*(dt)/ntot:.1f} ms ({ntot/(dt):.1f} fps)")
    print(f"Missed frames:      {nmissed} ({nmissed/ntot:.1%})")

    if not expdir:
        expdir = Path.cwd()

    write_output = True
    write_to_hdf5 = True
    write_to_tiff = False

    if shuffle_coords:
        idx = np.argsort(permutation)
    else:
        idx = np.arange(len(buffer))

    if write_output:
        if write_to_tiff:
            for i in idx:
                arr = buffer[i]
                write_tiff(expdir / f"{i:04d}.tiff", arr)
                printer(f"{i} / {len(buffer)}")

        if write_to_hdf5:
            x, y = buffer[0].shape
            dtype = buffer[0].dtype
            fn = expdir / f"data.h5"
            f = h5.File(fn)
            d = f.create_dataset("STEM-diffraction",
                                 shape=(len(buffer), x, y),
                                 dtype=dtype)
            for i in idx:
                arr = buffer[i]
                printer(f"{i} / {len(buffer)}")
                d[i] = arr
            f.close()

            printer(f"Wrote buffer to {fn}\n")

        im = np.zeros((grid_x, grid_y))
        im_view = im.ravel()
        for i in idx:
            arr = buffer[i]
            im_view[i] = arr.sum()

        h = {
            "scan_strength": strength,
            "scan_grid_x": grid_x,
            "scan_grid_y": grid_y,
            "scan_dwell_time": dwell_time,
            "scan_rotation": rotation,
            "scan_exposure": exposure,
        }

        image_fn = expdir / "image.tiff"
        write_tiff(expdir / image_fn, im, header=h)
        print(f"Wrote image to {image_fn}")

        with open(expdir / "scan_log.txt", "w") as f:
            print(time.ctime(), file=f)
            print(file=f)
            print("dwell_time:", dwell_time, file=f)
            print("exposure:", exposure, file=f)
            print("strength:", strength, file=f)
            print("grid_x:", grid_x, file=f)
            print("grid_y:", grid_y, file=f)
            print("rotation:", rotation, file=f)
            print(file=f)
            print(beam_ctrl.info(), file=f)
            print(file=f)
            print("Missed", file=f)
            print(missed, file=f)
            print(file=f)
            print("Coords", file=f)
            print(str(coords), file=f)
            print(file=f)

            print("Wrote info to", f.name)
def calibrate_stage_from_stageshifts(ctrl,
                                     *args,
                                     plot: bool = False,
                                     drc=None,
                                     ) -> np.array:
    """Run the calibration algorithm on the given X/Y ranges. An image will be
    taken at each position for cross correlation with the previous. An affine
    transformation matrix defines the relation between the pixel shift and the
    difference in stage position.

    The stagematrix takes the image binning into account.

    Parameters
    ----------
    ctrl: `TEMController`
        TEM control object to allow stage movement to different coordinates.
    ranges: np.array (Nx2)
        Each range is a List of tuples with X/Y stage shifts (i.e.
        displacements from the current position). Multiple ranges can be
        specified to be run in sequence.
    plot: bool
        Plot the fitting result.

    Returns
    -------
    stagematrix: np.ndarray (2x2)
        Stage matrix used to transform the camera coordinates to stage
        coordinates

    Usage
    -----
    >>> x_shifts = [3, (10000, 0)]
    >>> y_shifts = [3, (0, 10000)]
    >>> stagematrix = calibrate_stage_from_stageshifts(ctrl, x_shifts, y_shifts)
    """
    if drc:
        drc = Path(drc)

    stage_x, stage_y = ctrl.stage.xy

    stage_shifts = []  # um

    mag = ctrl.magnification.value
    mode = ctrl.mode.get()
    binning = ctrl.cam.getBinning()

    pairs = []

    for i, (n_steps, step) in enumerate(args):
        j = 0

        current_stage_pos = ctrl.stage
        dx, dy = step

        last_img, _ = ctrl.get_image()

        if drc:
            write_tiff(drc / f'{i}_{j}.tiff', last_img)

        for j in range(1, n_steps):
            new_x_pos = current_stage_pos.x + dx
            new_y_pos = current_stage_pos.y + dy
            ctrl.stage.set_xy_with_backlash_correction(x=new_x_pos, y=new_y_pos)

            img, _ = ctrl.get_image()

            if drc:
                write_tiff(drc / f'{i}_{j}.tiff', img)

            pairs.append((last_img, img))
            stage_shifts.append((dx, dy))

            current_stage_pos = ctrl.stage

            print(f'{i:02d}-{j:02d}: {current_stage_pos}')

            last_img = img

        # return to original position
        ctrl.stage.xy = (stage_x, stage_y)

    translations = cross_correlate_image_pairs(pairs)

    # Filter outliers
    sel = get_outlier_filter(translations)
    stage_shifts = np.array(stage_shifts)[sel]
    translations = np.array(translations)[sel]

    # Fit stagematrix
    fit_result = fit_affine_transformation(translations, stage_shifts, verbose=True)
    r = fit_result.r
    t = fit_result.t

    if drc:
        d = {
            'n_ranges': len(args),
            'stage_x': stage_x,
            'stage_y': stage_y,
            'mode': mode,
            'magnification': mag,
            'args': args,
            'translations': translations,
            'stage_shifts': stage_shifts,
            'r': r,
            't': t,
            'binning': binning,
        }
        yaml.dump(d, open(drc / 'log.yaml', 'w'))

    if plot:
        r_i = np.linalg.inv(r)
        translations_ = np.dot(stage_shifts, r_i)

        plt.scatter(*translations.T, marker='<', label='Pixel translations (CC)')
        plt.scatter(*translations_.T, marker='>', label='Calculated pixel coordinates')
        plt.legend()
        plt.show()

    stagematrix = r / binning

    return stagematrix
示例#14
0
def main_entry():
    import argparse
    from instamatic.formats import write_tiff
    # usage = """acquire"""

    description = """Program to acquire image data from gatan gatan ccd camera"""

    parser = argparse.ArgumentParser(  # usage=usage,
        description=description,
        formatter_class=argparse.RawDescriptionHelpFormatter)

    # parser.add_argument("args",
    #                     type=str, metavar="FILE",
    #                     help="Path to save cif")

    parser.add_argument(
        "-b",
        "--binsize",
        action="store",
        type=int,
        metavar="N",
        dest="binsize",
        help="""Binsize to use. Must be one of 1, 2, or 4 (default 1)""")

    parser.add_argument("-e",
                        "--exposure",
                        action="store",
                        type=float,
                        metavar="N",
                        dest="exposure",
                        help="""Exposure time (default 0.5)""")

    parser.add_argument("-o",
                        "--out",
                        action="store",
                        type=str,
                        metavar="image.png",
                        dest="outfile",
                        help="""Where to store image""")

    parser.add_argument("-d",
                        "--display",
                        action="store_true",
                        dest="show_fig",
                        help="""Show the image (default True)""")

    # parser.add_argument("-t", "--tem",
    #                     action="store", type=str, dest="tem",
    #                     help="""Simulate microscope connection (default False)""")

    parser.add_argument(
        "-u",
        "--simulate",
        action="store_true",
        dest="simulate",
        help="""Simulate camera/microscope connection (default False)""")

    parser.add_argument(
        "-s",
        "--series",
        action="store_true",
        dest="take_series",
        help="""Enable mode to take a series of images (default False)""")

    parser.set_defaults(binsize=1,
                        exposure=1,
                        outfile=None,
                        show_fig=False,
                        test=False,
                        simulate=False,
                        camera="simulate",
                        take_series=False)

    options = parser.parse_args()

    binsize = options.binsize
    exposure = options.exposure

    outfile = options.outfile
    show_fig = options.show_fig

    take_series = options.take_series

    from instamatic import TEMController
    ctrl = TEMController.initialize()

    if take_series:
        i = 1
        print("\nUsage:")
        print("    set b/e/i X -> set binsize/exposure/file number to X")
        print("    XXX         -> Add comment to header")
        print("    exit        -> exit the program")
    while take_series:
        outfile = f"image_{i:04d}"
        inp = input(f"\nHit enter to take an image: \n >> [{outfile}] ")
        if inp == "exit":
            break
        elif inp.startswith("set"):
            try:
                key, value = inp.split()[1:3]
            except ValueError:
                print("Input not understood")
                continue
            if key == "e":
                try:
                    value = float(value)
                except ValueError as e:
                    print(e)
                if value > 0:
                    exposure = value
            elif key == "b":
                try:
                    value = int(value)
                except ValueError as e:
                    print(e)
                if value in (1, 2, 4):
                    binsize = value
            elif key == "i":
                try:
                    value = int(value)
                except ValueError as e:
                    print(e)
                if value > 0:
                    i = value
            print(f"binsize = {binsize} | exposure = {exposure} | file #{i}")
        else:
            arr, h = ctrl.getImage(binsize=binsize,
                                   exposure=exposure,
                                   comment=inp)

            write_tiff(outfile, arr, header=h)

            i += 1
    else:
        import matplotlib.pyplot as plt

        arr, h = ctrl.getImage(binsize=binsize, exposure=exposure)

        if show_fig:
            # save_header(sys.stdout, h)
            plt.imshow(arr, cmap="gray", interpolation="none")
            plt.show()

        if outfile:
            write_tiff(outfile, arr, header=h)
        else:
            write_tiff("out", arr, header=h)
示例#15
0
    def get_image(
        self,
        exposure: float = None,
        binsize: int = None,
        comment: str = '',
        out: str = None,
        plot: bool = False,
        verbose: bool = False,
        header_keys: Tuple[str] = 'all',
    ) -> Tuple[np.ndarray, dict]:
        """Retrieve image as numpy array from camera. If the exposure and
        binsize are not given, the default values are read from the config
        file.

        Parameters
        ----------
        exposure: float
            Exposure time in seconds
        binsize: int
            Binning to use for the image, must be 1, 2, or 4, etc
        comment: str
            Arbitrary comment to add to the header file under 'ImageComment'
        out: str
            Path or filename to which the image/header is saved (defaults to tiff)
        plot: bool
            Toggle whether to show the image using matplotlib after acquisition
        full_header: bool
            Return the full header

        Returns
        -------
        image: np.ndarray, headerfile: dict
            Tuple of the image as numpy array and dictionary with all the tem parameters and image attributes

        Usage:
            img, h = self.get_image()
        """
        if not self.cam:
            raise AttributeError(
                f"{self.__class__.__name__} object has no attribute 'cam' (Camera has not been initialized)"
            )

        if not binsize:
            binsize = self.cam.default_binsize
        if not exposure:
            exposure = self.cam.default_exposure

        if not header_keys:
            h = {}
        else:
            h = self.to_dict(header_keys)

        if self.autoblank:
            self.beam.unblank()

        h['ImageGetTimeStart'] = time.perf_counter()

        arr = self.get_rotated_image(exposure=exposure, binsize=binsize)

        h['ImageGetTimeEnd'] = time.perf_counter()

        if self.autoblank:
            self.beam.blank()

        h['ImageGetTime'] = time.time()
        h['ImageExposureTime'] = exposure
        h['ImageBinsize'] = binsize
        h['ImageResolution'] = arr.shape
        # k['ImagePixelsize'] = config.calibration[mode]['pixelsize'][mag] * binsize
        # k['ImageRotation'] = config.calibration[mode]['rotation'][mag]
        h['ImageComment'] = comment
        h['ImageCameraName'] = self.cam.name
        h['ImageCameraDimensions'] = self.cam.getCameraDimensions()

        if verbose:
            print(
                f'Image acquired - shape: {arr.shape}, size: {arr.nbytes / 1024:.0f} kB'
            )

        if out:
            write_tiff(out, arr, header=h)

        if plot:
            import matplotlib.pyplot as plt
            plt.imshow(arr)
            plt.show()

        return arr, h
示例#16
0
    def getImage(self, exposure: float=0.5, binsize: int=1, comment: str="", out: str=None, plot: bool=False, verbose: bool=False, header_keys: Tuple[str]="all") -> Tuple[np.ndarray, dict]:
        """Retrieve image as numpy array from camera

        Parameters:
            exposure: float, 
                exposure time in seconds
            binsize: int, 
                which binning to use for the image, must be 1, 2, or 4
            comment: str, 
                arbitrary comment to add to the header file under 'ImageComment'
            out: str, 
                path or filename to which the image/header is saved (defaults to tiff)
            plot: bool, 
                toggle whether to show the image using matplotlib after acquisition
            full_header: bool,
                return the full header

        Returns:
            image: np.ndarray, headerfile: dict
                a tuple of the image as numpy array and dictionary with all the tem parameters and image attributes

        Usage:
            img, h = self.getImage()
        """

        if not self.cam:
            raise AttributeError(f"{self.__class__.__name__} object has no attribute 'cam' (Camera has not been initialized)")

        if not header_keys:
            h = {}
        else:
            h = self.to_dict(header_keys)

        if self.autoblank and self.beamblank:
            self.beamblank = False
        
        h["ImageGetTimeStart"] = time.perf_counter()

        arr = self.cam.getImage(exposure=exposure, binsize=binsize)
        
        h["ImageGetTimeEnd"] = time.perf_counter()
        
        if self.autoblank:
            self.beamblank = True

        h["ImageGetTime"] = time.time()
        h["ImageExposureTime"] = exposure
        h["ImageBinSize"] = binsize
        h["ImageResolution"] = arr.shape
        h["ImageComment"] = comment
        h["ImageCameraName"] = self.cam.name
        h["ImageCameraDimensions"] = self.cam.dimensions

        if verbose:
            print(f"Image acquired - shape: {arr.shape}, size: {arr.nbytes / 1024:.0f} kB")

        if out:
            write_tiff(out, arr, header=h)

        if plot:
            import matplotlib.pyplot as plt
            plt.imshow(arr)
            plt.show()

        return arr, h