Example #1
0
def test_zscaler_1():
    """
    Test ZScaler for floating point round off issues.
    """
    zs = mPSFUtils.ZScaler(0.6, 0.2)
    assert (zs.getMaxZ() == 7)

    diff = 1.0e-24
    zs = mPSFUtils.ZScaler(0.6 + diff, 0.2 - diff)
    assert (zs.getMaxZ() == 7)

    zs = mPSFUtils.ZScaler(0.6 - diff, 0.2 + diff)
    assert (zs.getMaxZ() == 7)
Example #2
0
def measurePSF(movie_name,
               zfile_name,
               movie_h5_name,
               psf_name,
               want2d=False,
               aoi_size=12,
               pixel_size=0.1,
               z_range=0.75,
               z_step=0.05):
    """
    movie_name - The name of the movie file.
    zfile_name - The name of the text file containing z offset data. If this does not exist
                 then the localizations z value will be used.
    movie_h5_name - The name of the HDF5 file containing the localization information.
    psf_name - The name of the file to save the measured PSF in.
    want2d - Measure a 2D PSF.
    aoi_size - The final AOI size will 2x this number (in pixels).
    pixel_size - The pixel size in microns.
    z_range - The z range of the PSF (in microns). The actual z range is 2x z_range (i.e. 
                 from -z_range to z_range).
    z_step - The z granularity of the PSF (in microns).
    """
    # Create z scaling object.
    z_sclr = measurePSFUtils.ZScaler(z_range, z_step)

    # Load dax file, z offset file and molecule list file.
    dax_data = datareader.inferReader(movie_name)
    z_off = None
    if os.path.exists(zfile_name):
        data = numpy.loadtxt(zfile_name, ndmin=2)
        valid = data[:, 0]
        z_off = data[:, 1]

    if want2d:
        print("Measuring 2D PSF")
    else:
        print("Measuring 3D PSF")

    # Go through the frames identifying good peaks and adding them
    # to the average psf.
    #
    max_z = z_sclr.getMaxZ()

    average_psf = numpy.zeros((max_z, 2 * aoi_size, 2 * aoi_size))
    peaks_used = 0
    totals = numpy.zeros(max_z, dtype=numpy.int)

    with saH5Py.SAH5Py(movie_h5_name) as h5:
        [dax_x, dax_y, dax_l] = dax_data.filmSize()
        for curf, locs in h5.localizationsIterator():

            # Select localizations in current frame & not near the edges.
            mask = (locs['x'] >
                    aoi_size) & (locs['x'] < (dax_x - aoi_size - 1)) & (
                        locs['y'] > aoi_size) & (locs['y'] <
                                                 (dax_y - aoi_size - 1))
            xr = locs['y'][mask] + 1
            yr = locs['x'][mask] + 1

            # Use the z offset file if it was specified, otherwise use localization z positions.
            if z_off is None:
                if (curf == 0):
                    print("Using fit z locations.")
                zr = locs['z'][mask]
            else:
                if (curf == 0):
                    print("Using z offset file.")
                if (abs(valid[curf]) < 1.0e-6):
                    continue
                zr = numpy.ones(xr.size) * z_off[curf]

            ht = locs['height'][mask]

            # Remove localizations that are too close to each other.
            mask = iaUtilsC.removeNeighborsMask(xr, yr, 2.0 * aoi_size)
            print(curf, "peaks in", xr.size, ", peaks out",
                  numpy.count_nonzero(mask))

            xr = xr[mask]
            yr = yr[mask]
            zr = zr[mask]
            ht = ht[mask]

            # Use remaining localizations to calculate spline.
            image = dax_data.loadAFrame(curf).astype(numpy.float64)

            for i in range(xr.size):
                xf = xr[i]
                yf = yr[i]
                zf = zr[i]
                if want2d:
                    zi = 0
                else:
                    zi = z_sclr.convert(zf)

                # Check that the z value is in range
                if z_sclr.inRange(zi):

                    # Extract PSF.
                    psf = measurePSFUtils.extractAOI(image, aoi_size, xf, yf)

                    # Add to average psf accumulator
                    average_psf[zi, :, :] += psf
                    totals[zi] += 1

    # Check that we got at least one valid measurement.
    #
    assert (numpy.max(totals) > 0)

    # Set the PSF to have zero average on the X/Y boundaries.
    #
    for i in range(max_z):
        edge = numpy.concatenate((average_psf[i, 0, :], average_psf[i, -1, :],
                                  average_psf[i, :, 0], average_psf[i, :, -1]))
        average_psf[i, :, :] -= numpy.mean(edge)

    # Normalize the PSF.
    #
    if want2d:
        max_z = 1

    # Note: I think it makes sense to normalize to a sum of 1.0 here as the user may
    #       be using the images of single localizations as the inputs. Unlike beads
    #       we can't assume that they are all the same brightness so normalizing by
    #       the number of events would make even less sense.
    #
    for i in range(max_z):
        print("z plane {0:0d} has {1:0d} samples".format(i, totals[i]))
        if (totals[i] > 0.0):
            average_psf[i, :, :] = average_psf[i, :, :] / numpy.sum(
                numpy.abs(average_psf[i, :, :]))

    # Normalize to unity maximum height.
    if (numpy.max(average_psf) > 0.0):
        average_psf = average_psf / numpy.max(average_psf)
    else:
        print("Warning! Measured PSF maxima is zero or negative!")

    # Save PSF (in image form).
    if True:
        with tifffile.TiffWriter("psf.tif") as tf:
            for i in range(max_z):
                tf.save(average_psf[i, :, :].astype(numpy.float32))

    # Save PSF.
    #
    #  At least for now the PSFs use nanometers, not microns.
    #
    z_range = z_range * 1.0e+3
    z_step = z_step * 1.0e+3

    if want2d:
        psf_dict = {
            "psf": average_psf[0, :, :],
            "pixel_size": pixel_size,
            "type": "2D",
            "version": 2.0
        }

    else:
        cur_z = -z_range
        z_vals = []
        for i in range(max_z):
            z_vals.append(cur_z)
            cur_z += z_step

        psf_dict = {
            "psf": average_psf,
            "pixel_size": pixel_size,
            "type": "3D",
            "version": 2.0,
            "zmin": -z_range,
            "zmax": z_range,
            "zvals": z_vals
        }

    with open(psf_name, 'wb') as fp:
        pickle.dump(psf_dict, fp)
Example #3
0
def measurePSF(zstack_name,
               zfile_name,
               psf_name,
               pixel_size=0.1,
               refine=False,
               z_range=0.75,
               z_step=0.050,
               normalize=False):
    """
    zstack_name - The name of the file containing the 2x up-sampled z-stacks.
    zfile_name - The text file containing the z offsets (in microns) for each frame.
    psf_name - The name of the file to save the measured PSF in (as a pickled Python dictionary).
    pixel_size - The pixel size in microns.
    refine - Align the measured PSF for each bead to the average PSF.
    z_range - The range the PSF should cover in microns.
    z_step - The z step size of the PSF.
    normalize - If true, normalize the PSF to unit height.
    """

    # Create z scaling object.
    z_sclr = measurePSFUtils.ZScaler(z_range, z_step)
    max_z = z_sclr.getMaxZ()

    # Load z-stacks.
    zstacks = numpy.load(zstack_name)
    x_size = zstacks[0].shape[0]
    y_size = zstacks[0].shape[1]

    # Load z-offsets.
    z_offset_data = numpy.loadtxt(zfile_name, ndmin=2)
    is_valid = z_offset_data[:, 0]
    z_offsets = z_offset_data[:, 1]

    # Check if the z-stack has fewer frames than the offset file.
    n_frames = z_offsets.size
    if (n_frames > zstacks[0].shape[2]):
        print("Warning! z stack has", n_frames - zstacks[0].shape[2],
              "fewer frames than the z offset file.")
        n_frames = zstacks[0].shape[2]

    # Average stacks in z.
    psfs = []
    for i in range(len(zstacks)):
        psf = numpy.zeros((max_z, x_size, y_size))
        totals = numpy.zeros(max_z, dtype=numpy.int)

        for j in range(n_frames):

            # 0.0 = invalid, 1.0 = valid.
            if (is_valid[j] > 1.0e-3):
                zi = z_sclr.convert(z_offsets[j])
                if z_sclr.inRange(zi):
                    psf[zi, :, :] += zstacks[i][:, :, j]
                    totals[zi] += 1

        # Check that we got at least one valid measurement. Totals
        # is expected to be the same for each PSF.
        if (i == 0):
            assert (numpy.max(totals) > 0)

        # Normalize each PSF z plane by the total counts in the plane.
        for j in range(max_z):
            if (i == 0):
                print("z plane {0:0d} has {1:0d} samples".format(j, totals[j]))
            if (totals[j] > 0):
                psf[j, :, :] = psf[j, :, :] / float(totals[j])

        # Append to list of PSFs.
        psfs.append(psf)

    # Align the PSFs to each other. This should hopefully correct for
    # any small errors in the input locations, and also for fields of
    # view that are not completely flat.
    #
    if refine:
        print("Refining PSF alignment.")
        [average_psf, i_score] = measurePSFUtils.alignPSFs(psfs)
    else:
        average_psf = measurePSFUtils.averagePSF(psfs)

    # Normalize to unity height, if requested.
    if normalize and (numpy.amax(average_psf) > 0.0):
        print("Normalizing PSF.")
        average_psf = average_psf / numpy.amax(average_psf)

    if not (numpy.amax(average_psf) > 0.0):
        print("Warning! Measured PSF maxima is zero or negative!")

    # Save PSF.
    #
    #  At least for now the PSFs use nanometers, not microns.
    #
    z_range = z_range * 1.0e+3
    z_step = z_step * 1.0e+3

    cur_z = -z_range
    z_vals = []
    for i in range(max_z):
        z_vals.append(cur_z)
        cur_z += z_step

    psf_dict = {
        "maximum": numpy.amax(average_psf),
        "psf": average_psf,
        "pixel_size": pixel_size,
        "type": "3D",
        "version": 2.0,
        "zmin": -z_range,
        "zmax": z_range,
        "zvals": z_vals
    }

    with open(psf_name, 'wb') as fp:
        pickle.dump(psf_dict, fp)

    # Save (normalized) z_stack as tif for inspection purposes.
    if (numpy.amax(average_psf) > 0.0):
        average_psf = average_psf / numpy.amax(average_psf)
    average_psf = average_psf.astype(numpy.float32)
    with tifffile.TiffWriter(os.path.splitext(psf_name)[0] + ".tif") as tf:
        for i in range(max_z):
            tf.save(average_psf[i, :, :])