Esempio n. 1
0
    def test_binning(self):
        """
        Changing the binning shouldn't affect the position
        binning x2 => same position (so shift in pixel / 2)
        """
        # At pos 0, 0
        im0 = self.camera.data.get()

        # Move a little bit in X,Y => should have a different image shifted by the same amount
        self.camera.updateMetadata({model.MD_POS: (10e-6, 20e-6)})
        im_move1 = self.camera.data.get()

        # Change to binning 2
        self.camera.binning.value = (2, 2)
        self.camera.updateMetadata({model.MD_PIXEL_SIZE: (2e-6, 2e-6)
                                    })  # Normally updated by the mdupdater

        im_move2 = self.camera.data.get()
        assert_tuple_almost_equal((0, 0),
                                  MeasureShift(im_move1[::2, ::2], im_move2,
                                               10),
                                  delta=0.5)
        assert_tuple_almost_equal((5, -10),
                                  MeasureShift(im0[::2, ::2], im_move2, 10),
                                  delta=0.5)
Esempio n. 2
0
    def test_different_precisions(self):
        """
        Tests for image drifted by random drift value using different precisions.
        """
        drift = MeasureShift(self.data[0], self.data_random_drifted, 1)
        numpy.testing.assert_almost_equal(drift, (self.deltac, self.deltar), 0)

        drift = MeasureShift(self.data[0], self.data_random_drifted, 10)
        numpy.testing.assert_almost_equal(drift, (self.deltac, self.deltar), 1)

        drift = MeasureShift(self.data[0], self.data_random_drifted, 100)
        numpy.testing.assert_almost_equal(drift, (self.deltac, self.deltar), 2)

        drift = MeasureShift(self.data[0], self.data_random_drifted, 1000)
        numpy.testing.assert_almost_equal(drift, (self.deltac, self.deltar), 3)
Esempio n. 3
0
 def test_small_random_drift_noisy(self):
     """
     Tests for image drifted by random drift value after noise is added.
     """
     drift = MeasureShift(self.small_data,
                          self.small_data_random_drifted_noisy, 10)
     numpy.testing.assert_almost_equal(
         drift, (self.small_deltac, self.small_deltar), 0)
Esempio n. 4
0
    def estimate(self):
        """
        Estimate the additional drift since previous acquisition+estimation.
        Note: It should be only called once after every acquisition.
        To read the value again, use .drift.
        To read the total drift since the first acquisition, use .tot_drift
        return (float, float): estimated extra drift in X/Y SEM px since last
          estimation.
        """
        # Calculate the drift between the last two frames and
        # between the last and first frame
        if len(self.raw) > 1:
            # Note: prev_drift and drift, don't represent exactly the same
            # value as the previous image also had drifted. So we need to
            # include also the drift of the previous image.
            # Also, MeasureShift return the shift in image pixels, which is
            # different (usually bigger) from the SEM px.
            prev_drift = MeasureShift(self.raw[-2], self.raw[-1], 10)
            prev_drift = (prev_drift[0] * self._scale[0] + self.drift[0],
                          prev_drift[1] * self._scale[1] + self.drift[1])

            orig_drift = MeasureShift(self.raw[0], self.raw[-1], 10)
            self.drift = (orig_drift[0] * self._scale[0],
                          orig_drift[1] * self._scale[1])

            logging.debug("Current drift: %s", self.drift)
            logging.debug("Previous frame diff: %s", prev_drift)
            if (abs(self.drift[0] - prev_drift[0]) > 5 * self._scale[0] or
                abs(self.drift[1] - prev_drift[1]) > 5 * self._scale[1]):
                # TODO: in such case, add the previous and current image to .raw
                logging.warning("Drift cannot be measured precisely, "
                                "hesitating between %s and %s px",
                                self.drift, prev_drift)

            # Update drift since the original position
            self.tot_drift = (self.tot_drift[0] + self.drift[0],
                              self.tot_drift[1] + self.drift[1])

            # Update maximum drift
            if math.hypot(*self.tot_drift) > math.hypot(*self.max_drift):
                self.max_drift = self.tot_drift

        return self.drift
Esempio n. 5
0
    def test_move_around(self):
        """
        Test that moving the "stage" moves the image
        """
        # At pos 0, 0
        im0 = self.camera.data.get()
        self.assertEqual(self.camera.resolution.value[::-1], im0.shape[:2])
        im1 = self.camera.data.get()
        assert_tuple_almost_equal((0, 0),
                                  MeasureShift(im0, im1, 10),
                                  delta=0.5)

        # Move a little bit in X,Y => should have a different image shifted by the same amount
        self.camera.updateMetadata({model.MD_POS: (10e-6, 20e-6)})
        im_move1 = self.camera.data.get()
        # Y is opposite direction in pixels, compared to physical
        assert_tuple_almost_equal((10, -20),
                                  MeasureShift(im0, im_move1, 10),
                                  delta=0.5)

        # Move a little bit => should have a different image
        self.camera.updateMetadata({model.MD_POS: (100e-6, 200e-6)})
        im_move1 = self.camera.data.get()
        # Note: images are always different, due to synthetic noise
        test.assert_array_not_equal(im0, im_move1)

        # Move the opposite direction
        self.camera.updateMetadata({model.MD_POS: (-100e-6, -200e-6)})
        im_move2 = self.camera.data.get()
        test.assert_array_not_equal(im0, im_move2)
        test.assert_array_not_equal(im_move1, im_move2)

        # Move far => should "block" on the border
        self.camera.updateMetadata({model.MD_POS: (-1000e-6, -2000e-6)})
        im_move_f1 = self.camera.data.get()
        #         test.assert_array_not_equal(im0, im_move_f1)

        # Move even further => no change
        self.camera.updateMetadata({model.MD_POS: (-10000e-6, -20000e-6)})
        im_move_f2 = self.camera.data.get()
Esempio n. 6
0
def measure_shift(da, db, use_md=True):
    """
    use_md (bool): if False, will not use metadata and assume the 2 images are
      of the same area
    return (float, float): shift of the second image compared to the first one,
     in pixels of the first image.
    """
    da_res = da.shape[1], da.shape[0] # X/Y are inverted
    db_res = db.shape[1], db.shape[0]
    if any(sa < sb for sa, sb in zip(da_res, db_res)):
        logging.warning("Comparing a large image %s to a small image %s, you should do the opposite", db_res, da_res)

    # if db FoV is smaller than da, crop da
    if use_md:
        dafov = [pxs * s for pxs, s in zip(da.metadata[model.MD_PIXEL_SIZE], da_res)]
        dbfov = [pxs * s for pxs, s in zip(db.metadata[model.MD_PIXEL_SIZE], db_res)]
        fov_ratio = [fa / fb for fa, fb in zip(dafov, dbfov)]
    else:
        fov_ratio = (1, 1)
    if any(r < 1 for r in fov_ratio):
        logging.warning("Cannot compare an image with a large FoV %g to a small FoV %g",
                        dbfov, dafov)
        shift_px = measure_shift(db, da)
        return [-s for s in shift_px]

    crop_res = [int(s / r) for s, r in zip(da_res, fov_ratio)]
    logging.debug("Cropping da to %s", crop_res)
    da_ctr = [s // 2 for s in da_res]
    da_lt = [int(c - r // 2) for c, r in zip(da_ctr, crop_res)]
    da_crop = da[da_lt[1]: da_lt[1] + crop_res[1],
                 da_lt[0]: da_lt[0] + crop_res[0]]

    scale = [sa / sb for sa, sb in zip(da_crop.shape, db.shape)]
    if scale[0] != scale[1]:
        raise ValueError("Comparing images with different zooms levels %s on each axis is not supported" % (scale,))

    # Resample the smaller image to fit the resolution of the larger image
    if scale[0] < 1:
        # The "big" image has actually less pixels than the small FoV image
        # => zoom the big image, and compensate later for the shift
        logging.info("Rescaling large FoV image by scale %f", 1 / scale[0])
        da_crop = zoom(da_crop, 1 / scale[0])
        db_big = db
        shift_ratio = scale[0]
    else:
        logging.info("Rescaling small FoV image by scale %f", scale[0])
        db_big = zoom(db, scale[0])
        shift_ratio = 1
    # Apply phase correlation
    shift_px = MeasureShift(da_crop, db_big, 10)

    return shift_px[0] * shift_ratio, shift_px[1] * shift_ratio
Esempio n. 7
0
 def test_small_identical_inputs_noisy(self):
     """
     Tests for input of identical images after noise is added.
     """
     drift = MeasureShift(self.small_data, self.small_data_noisy, 1)
     numpy.testing.assert_almost_equal(drift, (0, 0), 0)
Esempio n. 8
0
 def test_small_identical_inputs(self):
     """
     Tests for input of identical images.
     """
     drift = MeasureShift(self.small_data, self.small_data, 1)
     numpy.testing.assert_almost_equal(drift, (0, 0), 0)
Esempio n. 9
0
 def test_known_drift_noisy(self):
     """
     Tests for image drifted by known drift value after noise is added.
     """
     drift = MeasureShift(self.data[0], self.data_drifted_noisy, 1)
     numpy.testing.assert_almost_equal(drift, (-3, 5), 1)
Esempio n. 10
0
 def test_random_drift(self):
     """
     Tests for image drifted by random drift value.
     """
     drift = MeasureShift(self.data[0], self.data_random_drifted, 10)
     numpy.testing.assert_almost_equal(drift, (self.deltac, self.deltar), 1)
Esempio n. 11
0
 def test_known_drift(self):
     """
     Tests for image drifted by known drift value.
     """
     drift = MeasureShift(self.data[0], self.data_drifted[0], 1)
     numpy.testing.assert_almost_equal(drift, (-3, 5), 1)
Esempio n. 12
0
def read_timelapse(infn, emfn, fmfn):
    """
    infn (str): pattern for input filename
    emfn: sem output filename
    fmfn: fluorescence output filename
    """

    infiles = sorted(glob.glob(infn))

    if not infiles:
        raise ValueError("No file fitting '%s'" % (infn, ))

    emdata = {}  # timestamp -> tuple of info (X/Y, overlay X/Y)
    fmdata = {}  # timestamp -> tuple of info (X/Y)
    emda_prev = emda0 = fmda_prev = fmda0 = None
    for i, infl in enumerate(infiles):
        logging.info("Processing %s (%d/%d)", infl, i + 1, len(infiles))

        try:
            # Read the file
            reader = dataio.find_fittest_converter(infl)
            das = reader.read_data(infl)

            # Read the metadata (we expect one fluo image and one SEM image)
            fmpos = empos = emda = fmda = fmfoc = emfoc = None
            for da in das:
                if model.MD_IN_WL in da.metadata:  # Fluo image
                    fmpos = da.metadata[
                        model.
                        MD_POS]  # this one has the overlay translation included
                    fmdate = da.metadata[model.MD_ACQ_DATE]
                    fmda = da
                    fmpxs = da.metadata[model.MD_PIXEL_SIZE]
                    fmfoc = autofocus.MeasureOpticalFocus(da)
                else:  # SEM
                    empos = da.metadata[model.MD_POS]
                    emdate = da.metadata[model.MD_ACQ_DATE]
                    emda = da
                    empxs = da.metadata[model.MD_PIXEL_SIZE]
                    emfoc = autofocus.MeasureSEMFocus(da)

            # Overlay translation
            ovlpos = fmpos[0] - empos[0], fmpos[1] - empos[1]

            # Compute drift from first image and previous image
            if i == 0:
                emda0 = emda
                fmda0 = fmda
                emdriftm = 0, 0
                empdriftm = 0, 0
                fmdriftm = 0, 0
                fmpdriftm = 0, 0
            else:
                emdrift = MeasureShift(emda0, emda, 10)  # in pixels
                emdriftm = emdrift[0] * empxs[0], emdrift[1] * empxs[1]
                logging.info("Computed total EM drift of %s px = %s m",
                             emdrift, emdriftm)

                empdrift = MeasureShift(emda_prev, emda, 10)  # in pixels
                empdriftm = empdrift[0] * empxs[0], empdrift[1] * empxs[1]
                logging.info("Computed previous EM drift of %s px = %s m",
                             empdrift, empdriftm)

                fmdrift = MeasureShift(fmda0, fmda, 10)  # in pixels
                fmdriftm = fmdrift[0] * fmpxs[0], fmdrift[1] * fmpxs[1]
                logging.info("Computed total FM drift of %s px = %s m",
                             fmdrift, fmdriftm)

                fmpdrift = MeasureShift(fmda_prev, fmda, 10)  # in pixels
                fmpdriftm = fmpdrift[0] * fmpxs[0], fmpdrift[1] * fmpxs[1]
                logging.info("Computed previous FM drift of %s px = %s m",
                             fmpdrift, fmpdriftm)

            emdata[emdate] = (empos[0], empos[1], ovlpos[0], ovlpos[1],
                              emdriftm[0], emdriftm[1], empdriftm[0],
                              empdriftm[1], emfoc)
            fmdata[fmdate] = (fmpos[0], fmpos[1], fmdriftm[0], fmdriftm[1],
                              fmpdriftm[0], fmpdriftm[1], fmfoc)

            emda_prev = emda
            fmda_prev = fmda
        except KeyboardInterrupt:
            logging.info("Closing after only %d images processed", i)
            return
        except Exception:
            logging.exception("Failed to read %s", infl)

    # export the data
    logging.info("Exporting the data...")
    export_csv(
        emdata, emfn,
        "timestamp, posx, posy, overlay x, overlay y, total drift x, total drift y, prev drift x, prev drift y, focus level\n"
    )
    export_csv(
        fmdata, fmfn,
        "timestamp, posx, posy, total drift x, total drift y, prev drift x, prev drift y, focus level\n"
    )

    return 0