Пример #1
0
def _pixel_to_subpixel_one_im(im, peak_dim, locs):
    """
    This is a subtle calculation.

    locs is given as an *integer* position (only has pixel accuracy).
    We then extract out a sub-image using an *integer* half width.
    Peak_dim is typically odd. Suppose it is (11, 11)
    That makes half_peak_mea_i be 11 // 2 = 5

    Suppose that a peak is at (17.5, 17.5).

    Suppose that peak was found a (integer) location (17, 17)
    which is within 1 pixel of its center as expected.

    We extract the sub-image at (17 - 5, 17 - 5) = (12:23, 12:23)

    The Center-of-mass calculation should return (5.5, 5.5) because that is
    relative to the sub-image which was extracted

    We wish to return (17.5, 17.5). So that's the lower left
    (17 - 5) of the peak plus the COM found.
    """
    check.array_t(locs, dtype=int)
    assert peak_dim[0] == peak_dim[1]
    half_peak_mea_i = peak_dim[0] // 2
    lower_left_locs = locs - half_peak_mea_i
    com_per_loc = np.zeros(locs.shape)
    for loc_i, loc in enumerate(lower_left_locs):
        peak_im = imops.crop(im, off=YX(loc), dim=peak_dim, center=False)
        com_per_loc[loc_i] = imops.com(peak_im**2)
    return lower_left_locs + com_per_loc
Пример #2
0
 def _subregion(im, pos):
     if subsize is None:
         return im
     else:
         return imops.crop(im,
                           off=pos,
                           dim=WH(subsize, subsize),
                           center=False)
Пример #3
0
def _psf_accumulate(im,
                    locs,
                    mea,
                    keep_dist=8,
                    threshold_abs=None,
                    return_reasons=True):
    """
    Given a single im, typically a regional sub-image, accumulate
    PSF evidence from each locs that meets a set of criteria.

    Any one image may not produce enough (or any) candidate spots and it
    is therefore expected that this function is called over a large number
    of fields to get sufficient samples.

    Arguments:
        im: Expected to be a single field, channel, cycle (BG already removed).
        locs: array (n, 2) in coordinates of im. Expected to be well-separated
        mea: The peak_measure (must be odd)
        threshold_abs: The average pixel brightness to accept the peak
        keep_dist: Pixels distance to determine crowding

    Returns:
        psf: ndarray (mea, mea) image
        reason_counts: An array of masks of why peaks were accepted/rejected
            See PSFEstimateMaskFields for the columns
    """
    from scipy.spatial.distance import cdist  # Defer slow import

    n_locs = len(locs)
    dist = cdist(locs, locs, metric="euclidean")
    dist[dist == 0.0] = np.nan

    if not np.all(np.isnan(dist)):
        closest_dist = np.nanmin(dist, axis=1)
    else:
        closest_dist = np.zeros(n_locs)

    # Aligned peaks will accumulate into this psf matrix
    dim = (mea, mea)
    dim2 = (mea + 2, mea + 2)
    psf = np.zeros(dim)

    n_reason_mask_fields = len(PSFEstimateMaskFields)
    reason_masks = np.zeros((n_locs, n_reason_mask_fields))

    for i, (loc, closest_neighbor_dist) in enumerate(zip(locs, closest_dist)):
        reason_masks[i, PSFEstimateMaskFields.considered] = 1

        # EXTRACT a peak with extra pixels around the edges (dim2 not dim)
        peak_im = imops.crop(im, off=YX(loc), dim=HW(dim2), center=True)

        if peak_im.shape != dim2:
            # Skip near edges
            reason_masks[i, PSFEstimateMaskFields.skipped_near_edges] = 1
            continue

        if closest_neighbor_dist < keep_dist:
            reason_masks[i, PSFEstimateMaskFields.skipped_too_crowded] = 1
            continue

        if np.any(np.isnan(peak_im)):
            reason_masks[i, PSFEstimateMaskFields.skipped_has_nan] = 1
            continue

        # Sub-pixel align the peak to the center
        assert not np.any(np.isnan(peak_im))
        centered_peak_im = sub_pixel_center(peak_im.astype(np.float64))

        # Removing ckipping as the noise should cancel out
        # centered_peak_im = np.clip(centered_peak_im, a_min=0.0, a_max=None)
        peak_max = np.max(centered_peak_im)
        if peak_max == 0.0:
            reason_masks[i, PSFEstimateMaskFields.skipped_empty] = 1
            continue

        if threshold_abs is not None and peak_max < threshold_abs:
            # Reject spots that are not active
            reason_masks[i, PSFEstimateMaskFields.skipped_too_dark] = 1
            continue

        r = imops.distribution_aspect_ratio(centered_peak_im)
        if r > 2.0:
            reason_masks[i, PSFEstimateMaskFields.skipped_too_oval] = 1
            continue

        # TRIM off the extra now
        centered_peak_im = centered_peak_im[1:-1, 1:-1]

        psf += centered_peak_im / np.sum(centered_peak_im)
        reason_masks[i, PSFEstimateMaskFields.accepted] = 1

    n_accepted = np.sum(reason_masks[:, PSFEstimateMaskFields.accepted])
    if n_accepted > 0:
        psf /= np.sum(psf)

    if return_reasons:
        return psf, reason_masks

    return psf
Пример #4
0
 def it_crops():
     src = np.array([[1, 1, 1, 1], [1, 2, 2, 1], [1, 2, 2, 1], [1, 1, 1,
                                                                1]])
     inner = imops.crop(src, XY(1, 1), WH(2, 2))
     assert np.array_equal(inner, np.array([[2, 2], [2, 2]]))
Пример #5
0
def _radiometry(chcy_ims, locs, ch_z_reg_psfs, cycle_to_z_index):
    """
    Use the PSFs to compute the Area-Under-Curve of the data in chcy_ims
    for each peak location of locs.

    Arguments:
        chcy_ims: (n_output_channels, n_cycles, width, height)
        locs: (n_peaks, 2). The second dimension is in (y, x) order
        ch_z_reg_psfs: (n_output_channels, n_z_slices, divs, divs, psf_mea, psf_mea)
        cycle_to_z_index: (n_cycles).
            This is the best z-slice of the ch_z_reg_psfs to use for
            each cycle determined by a focal fit.
    """
    check.array_t(chcy_ims, ndim=4)
    check.array_t(locs, ndim=2, shape=(None, 2))
    check.array_t(ch_z_reg_psfs,
                  shape=(chcy_ims.shape[0], None, None, None, None, None))
    check.array_t(cycle_to_z_index, shape=(chcy_ims.shape[1], ))

    n_locs = len(locs)
    n_channels, n_cycles = chcy_ims.shape[0:2]
    psf_divs = ch_z_reg_psfs.shape[2]
    assert psf_divs == ch_z_reg_psfs.shape[3]
    psf_dim = ch_z_reg_psfs.shape[-2:]
    psf_mea = psf_dim[0]
    assert psf_mea == psf_dim[1]

    radmat = np.full((n_locs, n_channels, n_cycles, 2),
                     np.nan)  # 2 is (sig, noi)

    center_weighted_mask = imops.generate_center_weighted_tanh(psf_mea,
                                                               radius=2.0)

    for ch_i in range(n_channels):
        for cy_i in range(n_cycles):
            reg_psfs = ch_z_reg_psfs[ch_i, cycle_to_z_index[cy_i]]

            im = chcy_ims[ch_i, cy_i]

            for loc_i, loc in enumerate(locs):
                peak_im = imops.crop(im,
                                     off=YX(loc),
                                     dim=HW(psf_dim),
                                     center=True)
                if peak_im.shape != psf_dim:
                    # Skip near edges
                    continue

                if np.any(np.isnan(peak_im)):
                    # Skip nan collisions
                    continue

                # There is a small issue here -- when the regional PSFs
                # are computed they divide up the image over the full width
                # but the locs here are actually referring to the aligned
                # space which is typically a little smaller. This might
                # cause problems if alignment is very poor but is probably
                # too small of an effect to worry about in typical operations.
                psf_kernel = reg_psfs[int(psf_divs * loc[0] / im.shape[0]),
                                      int(psf_divs * loc[1] / im.shape[1]), ]

                signal, noise = _peak_radiometry(
                    peak_im,
                    psf_kernel,
                    center_weighted_mask=center_weighted_mask)
                radmat[loc_i, ch_i, cy_i, :] = (signal, noise)

    return radmat