def test_zoom_samesize_recentered(imsize,
                                  upsample_factor,
                                  offset,
                                  doplot=False,
                                  ndim=2):
    """
    Test that zooming in by some factor with the same input & output sizes

    allow for non-centered input images, AND zoom back in on the center
    """
    inds = np.indices([imsize] * ndim)
    rr = ((inds[0] - (imsize - 1) / 2. - offset[0])**2 +
          (inds[1] - (imsize - 1) / 2. - offset[1])**2)**0.5
    gg = gaussian(rr)
    xz, zz = zoom.zoomnd(gg,
                         usfac=upsample_factor,
                         return_xouts=True,
                         offsets=offset)
    xr = ((xz[0] - (imsize - 1) / 2. - offset[0])**2 +
          (xz[1] - (imsize - 1) / 2. - offset[1])**2)**0.5

    expected_accuracy = (
        (upsample_factor**1.1) * 6.2e-4 * (imsize % 2 == 1) +  # odd case
        (upsample_factor**2) * 1.5e-4 * (imsize % 2 == 0) +  # even case
        0.002)  # constant offset because the above is a fit

    assert ((gaussian(xr) - zz)**2).sum() < expected_accuracy
Example #2
0
def test_inds(imsize, ndim):
    """
    Make sure output indices are correct (don't care about zoomed values for this test)
    """
    upsample_factor = 1
    inds = np.indices([imsize]*ndim)
    rr = np.sum([(ind - (imsize-1)/2.)**2 for ind in inds],axis=0)**0.5
    gg = gaussian(rr)
    xz,zz = zoom.zoomnd(gg,usfac=upsample_factor,return_xouts=True)
    assert np.all(inds==xz)
def test_inds(imsize, ndim):
    """
    Make sure output indices are correct (don't care about zoomed values for this test)
    """
    upsample_factor = 1
    inds = np.indices([imsize] * ndim)
    rr = np.sum([(ind - (imsize - 1) / 2.)**2 for ind in inds], axis=0)**0.5
    gg = gaussian(rr)
    xz, zz = zoom.zoomnd(gg, usfac=upsample_factor, return_xouts=True)
    assert np.all(inds == xz)
Example #4
0
def measure_zoom_fullsize(imsize, upsample_factor,doplot=False,ndim=2):
    """
    """
    inds = np.indices([imsize]*ndim)
    rr = ((inds-(imsize-1)/2.)**2).sum(axis=0)**0.5
    gg = gaussian(rr)
    outshape = [s*upsample_factor for s in gg.shape]
    xz,zz = zoom.zoomnd(gg,upsample_factor,outshape=outshape,return_xouts=True)
    xr = ((xz - (imsize-1.)/2.)**2).sum(axis=0)**0.5
                          
    return ((gaussian(xr)-zz)**2).sum() 
Example #5
0
def measure_difference_zoom_samesize(imsize, upsample_factor,doplot=False,ndim=2):
    """
    Test that zooming in by some factor with the same input & output sizes
    works
    """
    inds = np.indices([imsize]*ndim)
    rr = ((inds-(imsize-1)/2.)**2).sum(axis=0)**0.5
    gg = gaussian(rr)
    xz,zz = zoom.zoomnd(gg,upsample_factor,return_xouts=True)
    xr = ((xz - (imsize-1.)/2.)**2).sum(axis=0)**0.5

    return ((gaussian(xr)-zz)**2).sum() 
Example #6
0
def test_inds(imsize, upsample_factor, ndim):
    offset = [0]*ndim
    inds = np.indices([imsize]*ndim)
    rr = np.sum([(ind - (imsize-1)/2.)**2 for ind in inds],axis=0)**0.5
    gg = gaussian(rr)
    outsize = imsize*upsample_factor
    xz,zz = zoom.zoomnd(gg,usfac=upsample_factor,outshape=[outsize]*ndim, return_xouts=True,offsets=offset)
    # wrong newinds = np.indices([imsize]*ndim)/float(imsize*upsample_factor-1)*(imsize-1)
    newinds = np.linspace(-0.5+1./upsample_factor/2.,(imsize-1)+0.5-1./upsample_factor/2.,outsize)

    for dnum in range(ndim):
        slices = [np.newaxis]*(dnum) + [slice(None)] + [np.newaxis]*(ndim-dnum-1)
        np.testing.assert_array_almost_equal(newinds[slices]*np.ones(xz[dnum].shape),xz[dnum])
Example #7
0
def test_zoom_fullsize(imsize, upsample_factor,doplot=False,ndim=2):
    """
    Test that zooming in by some factor with output size = input size * upsample factor
    """
    inds = np.indices([imsize]*ndim)
    rr = ((inds-(imsize-1)/2.)**2).sum(axis=0)**0.5
    gg = gaussian(rr)
    outshape = [s*upsample_factor for s in gg.shape]
    xz,zz = zoom.zoomnd(gg,usfac=upsample_factor,outshape=outshape,return_xouts=True)
    xr = ((xz - (imsize-1.)/2.)**2).sum(axis=0)**0.5

    expected_accuracy = ( (upsample_factor**2) * 1.2e-4 + # odd case
                          (upsample_factor**2) * 4.9e-4 * (imsize%2==0)   + # even case
                          0.002 ) # constant offset because the above is a fit
                          

    assert ((gaussian(xr)-zz)**2).sum() < expected_accuracy
Example #8
0
def test_zoom_samesize(imsize, upsample_factor,doplot=False,ndim=2):
    """
    Test that zooming in by some factor with the same input & output sizes
    works
    """
    inds = np.indices([imsize]*ndim)
    rr = ((inds-(imsize-1)/2.)**2).sum(axis=0)**0.5
    gg = gaussian(rr)
    xz,zz = zoom.zoomnd(gg,usfac=upsample_factor,return_xouts=True)
    xr = ((xz - (imsize-1.)/2.)**2).sum(axis=0)**0.5

    expected_accuracy = ( (upsample_factor**1.1) * 6.2e-4 * (imsize%2==1) + # odd case
                          (upsample_factor**2) * 1.5e-4 * (imsize%2==0)   + # even case
                          0.002 ) # constant offset because the above is a fit
                          

    assert ((gaussian(xr)-zz)**2).sum() < expected_accuracy
def test_zoom_samesize(imsize, upsample_factor, doplot=False, ndim=2):
    """
    Test that zooming in by some factor with the same input & output sizes
    works
    """
    inds = np.indices([imsize] * ndim)
    rr = ((inds - (imsize - 1) / 2.)**2).sum(axis=0)**0.5
    gg = gaussian(rr)
    xz, zz = zoom.zoomnd(gg, usfac=upsample_factor, return_xouts=True)
    xr = ((xz - (imsize - 1.) / 2.)**2).sum(axis=0)**0.5

    expected_accuracy = (
        (upsample_factor**1.1) * 6.2e-4 * (imsize % 2 == 1) +  # odd case
        (upsample_factor**2) * 1.5e-4 * (imsize % 2 == 0) +  # even case
        0.002)  # constant offset because the above is a fit

    assert ((gaussian(xr) - zz)**2).sum() < expected_accuracy
Example #10
0
def test_zoom_samesize_uncentered(imsize, upsample_factor, offset, doplot=False,ndim=2):
    """
    Test that zooming in by some factor with the same input & output sizes

    allow for non-centered input images
    """
    inds = np.indices([imsize]*ndim)
    rr = ((inds[0] - (imsize-1)/2. - offset[0])**2  + (inds[1] - (imsize-1)/2. - offset[1])**2)**0.5
    gg = gaussian(rr)
    xz,zz = zoom.zoomnd(gg,usfac=upsample_factor,return_xouts=True)
    xr = ((xz[0] - (imsize-1)/2. - offset[0])**2  + (xz[1] - (imsize-1)/2. - offset[1])**2)**0.5

    expected_accuracy = ( (upsample_factor**1.1) * 6.2e-4 * (imsize%2==1) + # odd case
                          (upsample_factor**2) * 1.5e-4 * (imsize%2==0)   + # even case
                          0.002 ) # constant offset because the above is a fit
                          

    assert ((gaussian(xr)-zz)**2).sum() < expected_accuracy
def test_zoom_fullsize(imsize, upsample_factor, doplot=False, ndim=2):
    """
    Test that zooming in by some factor with output size = input size * upsample factor
    """
    inds = np.indices([imsize] * ndim)
    rr = ((inds - (imsize - 1) / 2.)**2).sum(axis=0)**0.5
    gg = gaussian(rr)
    outshape = [s * upsample_factor for s in gg.shape]
    xz, zz = zoom.zoomnd(gg,
                         usfac=upsample_factor,
                         outshape=outshape,
                         return_xouts=True)
    xr = ((xz - (imsize - 1.) / 2.)**2).sum(axis=0)**0.5

    expected_accuracy = (
        (upsample_factor**2) * 1.2e-4 +  # odd case
        (upsample_factor**2) * 4.9e-4 * (imsize % 2 == 0) +  # even case
        0.002)  # constant offset because the above is a fit

    assert ((gaussian(xr) - zz)**2).sum() < expected_accuracy
def test_inds(imsize, upsample_factor, ndim):
    offset = [0] * ndim
    inds = np.indices([imsize] * ndim)
    rr = np.sum([(ind - (imsize - 1) / 2.)**2 for ind in inds], axis=0)**0.5
    gg = gaussian(rr)
    outsize = imsize * upsample_factor
    xz, zz = zoom.zoomnd(gg,
                         usfac=upsample_factor,
                         outshape=[outsize] * ndim,
                         return_xouts=True,
                         offsets=offset)
    # wrong newinds = np.indices([imsize]*ndim)/float(imsize*upsample_factor-1)*(imsize-1)
    newinds = np.linspace(-0.5 + 1. / upsample_factor / 2.,
                          (imsize - 1) + 0.5 - 1. / upsample_factor / 2.,
                          outsize)

    for dnum in range(ndim):
        slices = [np.newaxis] * (dnum) + [slice(None)
                                          ] + [np.newaxis] * (ndim - dnum - 1)
        np.testing.assert_array_almost_equal(
            newinds[slices] * np.ones(xz[dnum].shape), xz[dnum])
def chi2_shift(im1,
               im2,
               err=None,
               upsample_factor='auto',
               boundary='wrap',
               nthreads=1,
               use_numpy_fft=False,
               zeromean=False,
               nfitted=2,
               verbose=False,
               return_error=True,
               return_chi2array=False,
               max_auto_size=512,
               max_nsig=1.1):
    """
    Find the offsets between image 1 and image 2 using the DFT upsampling method
    (http://www.mathworks.com/matlabcentral/fileexchange/18401-efficient-subpixel-image-registration-by-cross-correlation/content/html/efficient_subpixel_registration.html)
    combined with :math:`\chi^2` to measure the errors on the fit

    Equation 1 gives the :math:`\chi^2` value as a function of shift, where Y
    is the model as a function of shift:

    .. math::
            \chi^2(dx,dy) & = & \Sigma_{ij} \\frac{(X_{ij}-Y_{ij}(dx,dy))^2}{\sigma_{ij}^2} \\\\
                          
    ..                         
          & = & \Sigma_{ij} \left[ X_{ij}^2/\sigma_{ij}^2 - 2X_{ij}Y_{ij}(dx,dy)/\sigma_{ij}^2 + Y_{ij}(dx,dy)^2/\sigma_{ij}^2 \\right]  \\\\

    Equation 2-4:

    .. math::
            Term~1: f(dx,dy) & = & \Sigma_{ij} \\frac{X_{ij}^2}{\sigma_{ij}^2}  \\\\
                    f(dx,dy) & = & f(0,0) ,  \\forall dx,dy \\\\
            Term~2: g(dx,dy) & = & -2 \Sigma_{ij} \\frac{X_{ij}Y_{ij}(dx,dy)}{\sigma_{ij}^2} = -2 \Sigma_{ij} \left(\\frac{X_{ij}}{\sigma_{ij}^2}\\right) Y_{ij}(dx,dy) \\\\
            Term~3: h(dx,dy) & = & \Sigma_{ij} \\frac{Y_{ij}(dx,dy)^2}{\sigma_{ij}^2} = \Sigma_{ij} \left(\\frac{1}{\sigma_{ij}^2}\\right) Y^2_{ij}(dx,dy)

    The cross-correlation can be computed with fourier transforms, and is defined

    .. math::
            CC_{m,n}(x,y) = \Sigma_{ij} x^*_{ij} y_{(n+i)(m+j)}

    which can then be applied to our problem, noting that the cross-correlation
    has the same form as term 2 and 3 in :math:`\chi^2` (term 1 is a constant,
    with no dependence on the shift)

    .. math::
            Term~2: & CC(X/\sigma^2,Y)[dx,dy] & = & \Sigma_{ij} \left(\\frac{X_{ij}}{\sigma_{ij}^2}\\right)^* Y_{ij}(dx,dy) \\\\
            Term~3: & CC(\sigma^{-2},Y^2)[dx,dy] & = & \Sigma_{ij} \left(\\frac{1}{\sigma_{ij}^2}\\right)^* Y^2_{ij}(dx,dy) \\\\

    Technically, only terms 2 and 3 has any effect on the resulting image,
    since term 1 is the same for all shifts, and the quantity of interest is
    :math:`\Delta \chi^2` when determining the best-fit shift and error.
    
    
    Parameters
    ----------
    im1 : np.ndarray
    im2 : np.ndarray
        The images to register. 
    err : np.ndarray
        Per-pixel error in image 2
    boundary : 'wrap','constant','reflect','nearest'
        Option to pass to map_coordinates for determining what to do with
        shifts outside of the boundaries.  
    upsample_factor : int or 'auto'
        upsampling factor; governs accuracy of fit (1/usfac is best accuracy)
        (can be "automatically" determined based on chi^2 error)
    return_error : bool
        Returns the "fit error" (1-sigma in x and y) based on the delta-chi2
        values
    return_chi2_array : bool
        Returns the x and y shifts and the chi2 as a function of those shifts
        in addition to other returned parameters.  i.e., the last return from
        this function will be a tuple (x, y, chi2)
    zeromean : bool
        Subtract the mean from the images before cross-correlating?  If no, you
        may get a 0,0 offset because the DC levels are strongly correlated.
    verbose : bool
        Print error message if upsampling factor is inadequate to measure errors
    use_numpy_fft : bool
        Force use numpy's fft over fftw?  (only matters if you have fftw
        installed)
    nthreads : bool
        Number of threads to use for fft (only matters if you have fftw
        installed)
    nfitted : int
        number of degrees of freedom in the fit (used for chi^2 computations).
        Should probably always be 2.
    max_auto_size : int
        Maximum zoom image size to create when using auto-upsampling


    Returns
    -------
    dx,dy : float,float
        Measures the amount im2 is offset from im1 (i.e., shift im2 by -1 *
        these #'s to match im1)
    errx,erry : float,float
        optional, error in x and y directions
    xvals,yvals,chi2n_upsampled : ndarray,ndarray,ndarray,
        x,y positions (in original chi^2 coordinates) of the chi^2 values and
        their corresponding chi^2 value

    Examples
    --------
    Create a 2d array, 
    shift it in both directions,
    then use chi2_shift to determine the shift

    >>> rr = ((np.indices([100,100]) - np.array([50.,50.])[:,None,None])**2).sum(axis=0)**0.5
    >>> image = np.exp(-rr**2/(3.**2*2.)) * 20
    >>> shifted = np.roll(np.roll(image,12,0),5,1) + np.random.randn(100,100)
    >>> dx,dy,edx,edy = chi2_shift(image, shifted, upsample_factor='auto')
    >>> shifted2 = image_registration.fft_tools.shift2d(image,3.665,-4.25) + np.random.randn(100,100)
    >>> dx2,dy2,edx2,edy2 = chi2_shift(image, shifted2, upsample_factor='auto')
    
    """
    chi2, term1, term2, term3 = chi2n_map(im1,
                                          im2,
                                          err,
                                          boundary=boundary,
                                          nthreads=nthreads,
                                          zeromean=zeromean,
                                          use_numpy_fft=use_numpy_fft,
                                          return_all=True,
                                          reduced=False)
    ymax, xmax = np.unravel_index(chi2.argmin(), chi2.shape)

    # needed for ffts
    im1 = np.nan_to_num(im1)
    im2 = np.nan_to_num(im2)

    ylen, xlen = im1.shape
    xcen = xlen / 2 - (1 - xlen % 2)
    ycen = ylen / 2 - (1 - ylen % 2)

    # original shift calculation
    yshift = ymax - ycen  # shift im2 by these numbers to get im1
    xshift = xmax - xcen

    if verbose:
        print "Coarse xmax/ymax = %i,%i, for offset %f,%f" % (xmax, ymax,
                                                              xshift, yshift)

    # below is sub-pixel zoom-in stuff

    # find delta-chi^2 limiting values for varying DOFs
    try:
        import scipy.stats
        # 1,2,3-sigma delta-chi2 levels
        m1 = scipy.stats.chi2.ppf(1 - scipy.stats.norm.sf(1) * 2, nfitted)
        m2 = scipy.stats.chi2.ppf(1 - scipy.stats.norm.sf(2) * 2, nfitted)
        m3 = scipy.stats.chi2.ppf(1 - scipy.stats.norm.sf(3) * 2, nfitted)
        m_auto = scipy.stats.chi2.ppf(1 - scipy.stats.norm.sf(max_nsig) * 2,
                                      nfitted)
    except ImportError:
        # assume m=2 (2 degrees of freedom)
        m1 = 2.2957489288986364
        m2 = 6.1800743062441734
        m3 = 11.829158081900793
        m_auto = 2.6088233328527037  # slightly >1 sigma

    # biggest scale = where chi^2/n ~ 9 or 11.8 for M=2?
    if upsample_factor == 'auto':
        # deltachi2 is not reduced deltachi2
        deltachi2_lowres = (chi2 - chi2.min())
        if verbose:
            print "Minimum chi2: %g   Max delta-chi2 (lowres): %g  Min delta-chi2 (lowres): %g" % (
                chi2.min(), deltachi2_lowres.max(),
                deltachi2_lowres[deltachi2_lowres > 0].min())
        sigmamax_area = deltachi2_lowres < m_auto
        if sigmamax_area.sum() > 1:
            yy, xx = np.indices(sigmamax_area.shape)
            xvals = xx[sigmamax_area]
            yvals = yy[sigmamax_area]
            xvrange = xvals.max() - xvals.min()
            yvrange = yvals.max() - yvals.min()
            size = max(xvrange, yvrange)
        else:
            size = 1
        upsample_factor = max_auto_size / 2. / size
        if upsample_factor < 1:
            upsample_factor = 1
        s1 = s2 = max_auto_size
        # zoom factor = s1 / upsample_factor = 2*size
        zoom_factor = 2. * size
        if verbose:
            print "Selected upsample factor %0.1f for image size %i and zoom factor %0.1f (max-sigma range was %i for area %i)" % (
                upsample_factor, s1, zoom_factor, size, sigmamax_area.sum())
    else:
        s1, s2 = im1.shape

        zoom_factor = s1 / upsample_factor
        if zoom_factor <= 1:
            zoom_factor = 2
            s1 = zoom_factor * upsample_factor
            s2 = zoom_factor * upsample_factor

    (yshifts_corrections,
     xshifts_corrections), chi2_ups = zoom.zoomnd(chi2,
                                                  usfac=upsample_factor,
                                                  outshape=[s1, s2],
                                                  offsets=[yshift, xshift],
                                                  return_xouts=True)

    # deltachi2 is not reduced deltachi2
    deltachi2_ups = (chi2_ups - chi2_ups.min())
    if verbose:
        print "Minimum chi2_ups: %g   Max delta-chi2 (highres): %g  Min delta-chi2 (highres): %g" % (
            chi2_ups.min(), deltachi2_ups.max(),
            deltachi2_ups[deltachi2_ups > 0].min())
        if verbose > 1:
            pass
            #if hasattr(term3_ups,'len'):
            #    print "term3_ups has shape ",term3_ups.shape," term2: ",term2_ups.shape," term1=",term1
            #else:
            #    print "term2 shape: ",term2.shape," term1: ",term1," term3: ",term3_ups
    # THE UPSAMPLED BEST-FIT HAS BEEN FOUND

    # BELOW IS TO COMPUTE THE ERROR

    errx_low, errx_high, erry_low, erry_high = chi2map_to_errors(
        chi2_ups, upsample_factor)

    yshift_corr = yshifts_corrections.flat[chi2_ups.argmin()] - ycen
    xshift_corr = xshifts_corrections.flat[chi2_ups.argmin()] - xcen

    shift_xvals = xshifts_corrections - xcen
    shift_yvals = yshifts_corrections - ycen

    returns = [-xshift_corr, -yshift_corr]
    if return_error:
        returns.append((errx_low + errx_high) / 2.)
        returns.append((erry_low + erry_high) / 2.)
    if return_chi2array:
        returns.append((shift_xvals, shift_yvals, chi2_ups))

    return returns
def chi2_shift_iterzoom(im1,
                        im2,
                        err=None,
                        upsample_factor='auto',
                        boundary='wrap',
                        nthreads=1,
                        use_numpy_fft=False,
                        zeromean=False,
                        verbose=False,
                        return_error=True,
                        return_chi2array=False,
                        zoom_shape=[10, 10],
                        rezoom_shape=[100, 100],
                        rezoom_factor=5,
                        mindiff=1,
                        **kwargs):
    """
    Find the offsets between image 1 and image 2 using an iterative DFT
    upsampling method combined with :math:`\chi^2` to measure the errors on the
    fit

    A simpler version of :func:`chi2_shift` that only computes the
    :math:`\chi^2` array on the largest scales, then uses a fourier upsampling
    technique to zoom in.
    
    
    Parameters
    ----------
    im1 : np.ndarray
    im2 : np.ndarray
        The images to register. 
    err : np.ndarray
        Per-pixel error in image 2
    boundary : 'wrap','constant','reflect','nearest'
        Option to pass to map_coordinates for determining what to do with
        shifts outside of the boundaries.  
    upsample_factor : int or 'auto'
        upsampling factor; governs accuracy of fit (1/usfac is best accuracy)
        (can be "automatically" determined based on chi^2 error)
    zeromean : bool
        Subtract the mean from the images before cross-correlating?  If no, you
        may get a 0,0 offset because the DC levels are strongly correlated.
    verbose : bool
        Print error message if upsampling factor is inadequate to measure errors
    use_numpy_fft : bool
        Force use numpy's fft over fftw?  (only matters if you have fftw
        installed)
    nthreads : bool
        Number of threads to use for fft (only matters if you have fftw
        installed)
    nfitted : int
        number of degrees of freedom in the fit (used for chi^2 computations).
        Should probably always be 2.
    zoom_shape : [int,int]
        Shape of iterative zoom image
    rezoom_shape : [int,int]
        Shape of the final output chi^2 map to use for determining the errors
    rezoom_factor : int
        Amount to zoom above the last zoom factor.  Should be <=
        rezoom_shape/zoom_shape

    Other Parameters
    ----------------
    return_error : bool
        Returns the "fit error" (1-sigma in x and y) based on the delta-chi2
        values
    return_chi2_array : bool
        Returns the x and y shifts and the chi2 as a function of those shifts
        in addition to other returned parameters.  i.e., the last return from
        this function will be a tuple (x, y, chi2)

    Returns
    -------
    dx,dy : float,float
        Measures the amount im2 is offset from im1 (i.e., shift im2 by -1 *
        these #'s to match im1)
    errx,erry : float,float
        optional, error in x and y directions
    xvals,yvals,chi2n_upsampled : ndarray,ndarray,ndarray,
        x,y positions (in original chi^2 coordinates) of the chi^2 values and
        their corresponding chi^2 value

    Examples
    --------
    Create a 2d array, 
    shift it in both directions,
    then use chi2_shift_iterzoom to determine the shift

    >>> np.random.seed(42) # so the doctest will pass
    >>> image = np.random.randn(50,55)
    >>> shifted = np.roll(np.roll(image,12,0),5,1)
    >>> dx,dy,edx,edy = chi2_shift_iterzoom(image, shifted, upsample_factor='auto')
    >>> shifted2 = image_registration.fft_tools.shift2d(image,3.665,-4.25)
    >>> dx2,dy2,edx2,edy2 = chi2_shift_iterzoom(image, shifted2, upsample_factor='auto')
    
    """
    chi2, term1, term2, term3 = chi2n_map(im1,
                                          im2,
                                          err,
                                          boundary=boundary,
                                          nthreads=nthreads,
                                          zeromean=zeromean,
                                          use_numpy_fft=use_numpy_fft,
                                          return_all=True,
                                          reduced=False)
    # at this point, the chi2 map contains ALL of the information!

    # below is sub-pixel zoom-in stuff

    chi2zoom, zf, offsets = iterative_zoom.iterative_zoom(chi2,
                                                          mindiff=mindiff,
                                                          zoomshape=zoom_shape,
                                                          return_zoomed=True,
                                                          verbose=verbose,
                                                          return_center=False,
                                                          **kwargs)

    if np.all(chi2zoom == 0):
        # if you've over-zoomed & broken things, you can zoom in by the same
        # factor but with a bigger field of view
        (yy, xx), chi2_rezoom = zoom.zoomnd(chi2,
                                            usfac=zf,
                                            offsets=offsets,
                                            outshape=rezoom_shape,
                                            middle_convention=np.floor,
                                            return_xouts=True,
                                            **kwargs)
    else:
        (yy, xx), chi2_rezoom = zoom.zoomnd(chi2,
                                            usfac=zf * rezoom_factor,
                                            offsets=offsets,
                                            outshape=rezoom_shape,
                                            middle_convention=np.floor,
                                            return_xouts=True,
                                            **kwargs)

    # x and y are swapped and negative
    returns = [-off for off in offsets[::-1]]

    if return_error:
        errx_low, errx_high, erry_low, erry_high = chi2map_to_errors(
            chi2_rezoom, zf * rezoom_factor)
        returns.append((errx_low + errx_high) / 2.)
        returns.append((erry_low + erry_high) / 2.)
    if return_chi2array:
        yy = (chi2.shape[0] - 1) / 2 - yy
        xx = (chi2.shape[1] - 1) / 2 - xx
        returns.append((xx, yy, chi2_rezoom))

    return returns
Example #15
0
def chi2_shift(im1, im2, err=None, upsample_factor='auto', boundary='wrap',
        nthreads=1, use_numpy_fft=False, zeromean=False, nfitted=2,
        verbose=False, return_error=True, return_chi2array=False,
        max_auto_size=512, max_nsig=1.1):
    """
    Find the offsets between image 1 and image 2 using the DFT upsampling method
    (http://www.mathworks.com/matlabcentral/fileexchange/18401-efficient-subpixel-image-registration-by-cross-correlation/content/html/efficient_subpixel_registration.html)
    combined with :math:`\chi^2` to measure the errors on the fit

    Equation 1 gives the :math:`\chi^2` value as a function of shift, where Y
    is the model as a function of shift:

    .. math::
            \chi^2(dx,dy) & = & \Sigma_{ij} \\frac{(X_{ij}-Y_{ij}(dx,dy))^2}{\sigma_{ij}^2} \\\\
                          
    ..                         
          & = & \Sigma_{ij} \left[ X_{ij}^2/\sigma_{ij}^2 - 2X_{ij}Y_{ij}(dx,dy)/\sigma_{ij}^2 + Y_{ij}(dx,dy)^2/\sigma_{ij}^2 \\right]  \\\\

    Equation 2-4:

    .. math::
            Term~1: f(dx,dy) & = & \Sigma_{ij} \\frac{X_{ij}^2}{\sigma_{ij}^2}  \\\\
                    f(dx,dy) & = & f(0,0) ,  \\forall dx,dy \\\\
            Term~2: g(dx,dy) & = & -2 \Sigma_{ij} \\frac{X_{ij}Y_{ij}(dx,dy)}{\sigma_{ij}^2} = -2 \Sigma_{ij} \left(\\frac{X_{ij}}{\sigma_{ij}^2}\\right) Y_{ij}(dx,dy) \\\\
            Term~3: h(dx,dy) & = & \Sigma_{ij} \\frac{Y_{ij}(dx,dy)^2}{\sigma_{ij}^2} = \Sigma_{ij} \left(\\frac{1}{\sigma_{ij}^2}\\right) Y^2_{ij}(dx,dy)

    The cross-correlation can be computed with fourier transforms, and is defined

    .. math::
            CC_{m,n}(x,y) = \Sigma_{ij} x^*_{ij} y_{(n+i)(m+j)}

    which can then be applied to our problem, noting that the cross-correlation
    has the same form as term 2 and 3 in :math:`\chi^2` (term 1 is a constant,
    with no dependence on the shift)

    .. math::
            Term~2: & CC(X/\sigma^2,Y)[dx,dy] & = & \Sigma_{ij} \left(\\frac{X_{ij}}{\sigma_{ij}^2}\\right)^* Y_{ij}(dx,dy) \\\\
            Term~3: & CC(\sigma^{-2},Y^2)[dx,dy] & = & \Sigma_{ij} \left(\\frac{1}{\sigma_{ij}^2}\\right)^* Y^2_{ij}(dx,dy) \\\\

    Technically, only terms 2 and 3 has any effect on the resulting image,
    since term 1 is the same for all shifts, and the quantity of interest is
    :math:`\Delta \chi^2` when determining the best-fit shift and error.
    
    
    Parameters
    ----------
    im1 : np.ndarray
    im2 : np.ndarray
        The images to register. 
    err : np.ndarray
        Per-pixel error in image 2
    boundary : 'wrap','constant','reflect','nearest'
        Option to pass to map_coordinates for determining what to do with
        shifts outside of the boundaries.  
    upsample_factor : int or 'auto'
        upsampling factor; governs accuracy of fit (1/usfac is best accuracy)
        (can be "automatically" determined based on chi^2 error)
    return_error : bool
        Returns the "fit error" (1-sigma in x and y) based on the delta-chi2
        values
    return_chi2_array : bool
        Returns the x and y shifts and the chi2 as a function of those shifts
        in addition to other returned parameters.  i.e., the last return from
        this function will be a tuple (x, y, chi2)
    zeromean : bool
        Subtract the mean from the images before cross-correlating?  If no, you
        may get a 0,0 offset because the DC levels are strongly correlated.
    verbose : bool
        Print error message if upsampling factor is inadequate to measure errors
    use_numpy_fft : bool
        Force use numpy's fft over fftw?  (only matters if you have fftw
        installed)
    nthreads : bool
        Number of threads to use for fft (only matters if you have fftw
        installed)
    nfitted : int
        number of degrees of freedom in the fit (used for chi^2 computations).
        Should probably always be 2.
    max_auto_size : int
        Maximum zoom image size to create when using auto-upsampling


    Returns
    -------
    dx,dy : float,float
        Measures the amount im2 is offset from im1 (i.e., shift im2 by -1 *
        these #'s to match im1)
    errx,erry : float,float
        optional, error in x and y directions
    xvals,yvals,chi2n_upsampled : ndarray,ndarray,ndarray,
        x,y positions (in original chi^2 coordinates) of the chi^2 values and
        their corresponding chi^2 value

    Examples
    --------
    Create a 2d array, 
    shift it in both directions,
    then use chi2_shift to determine the shift

    >>> rr = ((np.indices([100,100]) - np.array([50.,50.])[:,None,None])**2).sum(axis=0)**0.5
    >>> image = np.exp(-rr**2/(3.**2*2.)) * 20
    >>> shifted = np.roll(np.roll(image,12,0),5,1) + np.random.randn(100,100)
    >>> dx,dy,edx,edy = chi2_shift(image, shifted, upsample_factor='auto')
    >>> shifted2 = image_registration.fft_tools.shift2d(image,3.665,-4.25) + np.random.randn(100,100)
    >>> dx2,dy2,edx2,edy2 = chi2_shift(image, shifted2, upsample_factor='auto')
    
    """
    chi2,term1,term2,term3 = chi2n_map(im1, im2, err, boundary=boundary,
            nthreads=nthreads, zeromean=zeromean, use_numpy_fft=use_numpy_fft,
            return_all=True, reduced=False)
    ymax, xmax = np.unravel_index(chi2.argmin(), chi2.shape)

    # needed for ffts
    im1 = np.nan_to_num(im1)
    im2 = np.nan_to_num(im2)

    ylen,xlen = im1.shape
    xcen = xlen/2-(1-xlen%2) 
    ycen = ylen/2-(1-ylen%2) 

    # original shift calculation
    yshift = ymax-ycen # shift im2 by these numbers to get im1
    xshift = xmax-xcen

    if verbose:
        print "Coarse xmax/ymax = %i,%i, for offset %f,%f" % (xmax,ymax,xshift,yshift)

    # below is sub-pixel zoom-in stuff

    # find delta-chi^2 limiting values for varying DOFs
    try:
        import scipy.stats
        # 1,2,3-sigma delta-chi2 levels
        m1 = scipy.stats.chi2.ppf( 1-scipy.stats.norm.sf(1)*2, nfitted )
        m2 = scipy.stats.chi2.ppf( 1-scipy.stats.norm.sf(2)*2, nfitted )
        m3 = scipy.stats.chi2.ppf( 1-scipy.stats.norm.sf(3)*2, nfitted )
        m_auto = scipy.stats.chi2.ppf( 1-scipy.stats.norm.sf(max_nsig)*2, nfitted )
    except ImportError:
        # assume m=2 (2 degrees of freedom)
        m1 = 2.2957489288986364
        m2 = 6.1800743062441734 
        m3 = 11.829158081900793
        m_auto = 2.6088233328527037 # slightly >1 sigma

    # biggest scale = where chi^2/n ~ 9 or 11.8 for M=2?
    if upsample_factor=='auto':
        # deltachi2 is not reduced deltachi2
        deltachi2_lowres = (chi2 - chi2.min())
        if verbose:
            print "Minimum chi2: %g   Max delta-chi2 (lowres): %g  Min delta-chi2 (lowres): %g" % (chi2.min(),deltachi2_lowres.max(),deltachi2_lowres[deltachi2_lowres>0].min())
        sigmamax_area = deltachi2_lowres<m_auto
        if sigmamax_area.sum() > 1:
            yy,xx = np.indices(sigmamax_area.shape)
            xvals = xx[sigmamax_area]
            yvals = yy[sigmamax_area]
            xvrange = xvals.max()-xvals.min()
            yvrange = yvals.max()-yvals.min()
            size = max(xvrange,yvrange)
        else:
            size = 1
        upsample_factor = max_auto_size/2. / size
        if upsample_factor < 1:
            upsample_factor = 1
        s1 = s2 = max_auto_size
        # zoom factor = s1 / upsample_factor = 2*size
        zoom_factor = 2.*size
        if verbose:
            print "Selected upsample factor %0.1f for image size %i and zoom factor %0.1f (max-sigma range was %i for area %i)" % (upsample_factor, s1, zoom_factor, size, sigmamax_area.sum())
    else:
        s1,s2 = im1.shape

        zoom_factor = s1/upsample_factor
        if zoom_factor <= 1:
            zoom_factor = 2
            s1 = zoom_factor*upsample_factor
            s2 = zoom_factor*upsample_factor

    (yshifts_corrections,xshifts_corrections),chi2_ups = zoom.zoomnd(chi2,
            usfac=upsample_factor, outshape=[s1,s2], offsets=[yshift,xshift],
            return_xouts=True)

    # deltachi2 is not reduced deltachi2
    deltachi2_ups = (chi2_ups - chi2_ups.min())
    if verbose:
        print "Minimum chi2_ups: %g   Max delta-chi2 (highres): %g  Min delta-chi2 (highres): %g" % (chi2_ups.min(),deltachi2_ups.max(),deltachi2_ups[deltachi2_ups>0].min())
        if verbose > 1:
            pass
            #if hasattr(term3_ups,'len'):
            #    print "term3_ups has shape ",term3_ups.shape," term2: ",term2_ups.shape," term1=",term1
            #else:
            #    print "term2 shape: ",term2.shape," term1: ",term1," term3: ",term3_ups
    # THE UPSAMPLED BEST-FIT HAS BEEN FOUND

    # BELOW IS TO COMPUTE THE ERROR

    errx_low,errx_high,erry_low,erry_high = chi2map_to_errors(chi2_ups, upsample_factor)

    yshift_corr = yshifts_corrections.flat[chi2_ups.argmin()]-ycen
    xshift_corr = xshifts_corrections.flat[chi2_ups.argmin()]-xcen

    shift_xvals = xshifts_corrections-xcen
    shift_yvals = yshifts_corrections-ycen

    returns = [-xshift_corr,-yshift_corr]
    if return_error:
        returns.append( (errx_low+errx_high)/2. )
        returns.append( (erry_low+erry_high)/2. )
    if return_chi2array:
        returns.append((shift_xvals,shift_yvals,chi2_ups))

    return returns
Example #16
0
def chi2_shift_iterzoom(im1, im2, err=None, upsample_factor='auto',
        boundary='wrap', nthreads=1, use_numpy_fft=False, zeromean=False,
        verbose=False, return_error=True, return_chi2array=False,
        zoom_shape=[10,10], rezoom_shape=[100,100], rezoom_factor=5,
        mindiff=1, **kwargs):
    """
    Find the offsets between image 1 and image 2 using an iterative DFT
    upsampling method combined with :math:`\chi^2` to measure the errors on the
    fit

    A simpler version of :func:`chi2_shift` that only computes the
    :math:`\chi^2` array on the largest scales, then uses a fourier upsampling
    technique to zoom in.
    
    
    Parameters
    ----------
    im1 : np.ndarray
    im2 : np.ndarray
        The images to register. 
    err : np.ndarray
        Per-pixel error in image 2
    boundary : 'wrap','constant','reflect','nearest'
        Option to pass to map_coordinates for determining what to do with
        shifts outside of the boundaries.  
    upsample_factor : int or 'auto'
        upsampling factor; governs accuracy of fit (1/usfac is best accuracy)
        (can be "automatically" determined based on chi^2 error)
    zeromean : bool
        Subtract the mean from the images before cross-correlating?  If no, you
        may get a 0,0 offset because the DC levels are strongly correlated.
    verbose : bool
        Print error message if upsampling factor is inadequate to measure errors
    use_numpy_fft : bool
        Force use numpy's fft over fftw?  (only matters if you have fftw
        installed)
    nthreads : bool
        Number of threads to use for fft (only matters if you have fftw
        installed)
    nfitted : int
        number of degrees of freedom in the fit (used for chi^2 computations).
        Should probably always be 2.
    zoom_shape : [int,int]
        Shape of iterative zoom image
    rezoom_shape : [int,int]
        Shape of the final output chi^2 map to use for determining the errors
    rezoom_factor : int
        Amount to zoom above the last zoom factor.  Should be <=
        rezoom_shape/zoom_shape

    Other Parameters
    ----------------
    return_error : bool
        Returns the "fit error" (1-sigma in x and y) based on the delta-chi2
        values
    return_chi2_array : bool
        Returns the x and y shifts and the chi2 as a function of those shifts
        in addition to other returned parameters.  i.e., the last return from
        this function will be a tuple (x, y, chi2)

    Returns
    -------
    dx,dy : float,float
        Measures the amount im2 is offset from im1 (i.e., shift im2 by -1 *
        these #'s to match im1)
    errx,erry : float,float
        optional, error in x and y directions
    xvals,yvals,chi2n_upsampled : ndarray,ndarray,ndarray,
        x,y positions (in original chi^2 coordinates) of the chi^2 values and
        their corresponding chi^2 value

    Examples
    --------
    Create a 2d array, 
    shift it in both directions,
    then use chi2_shift_iterzoom to determine the shift

    >>> np.random.seed(42) # so the doctest will pass
    >>> image = np.random.randn(50,55)
    >>> shifted = np.roll(np.roll(image,12,0),5,1)
    >>> dx,dy,edx,edy = chi2_shift_iterzoom(image, shifted, upsample_factor='auto')
    >>> shifted2 = image_registration.fft_tools.shift2d(image,3.665,-4.25)
    >>> dx2,dy2,edx2,edy2 = chi2_shift_iterzoom(image, shifted2, upsample_factor='auto')
    
    """
    chi2,term1,term2,term3 = chi2n_map(im1, im2, err, boundary=boundary,
            nthreads=nthreads, zeromean=zeromean, use_numpy_fft=use_numpy_fft,
            return_all=True, reduced=False)
    # at this point, the chi2 map contains ALL of the information!

    # below is sub-pixel zoom-in stuff

    chi2zoom, zf, offsets = iterative_zoom.iterative_zoom(chi2,
            mindiff=mindiff, zoomshape=zoom_shape, return_zoomed=True,
            verbose=verbose, return_center=False, **kwargs)

    if np.all(chi2zoom==0):
        # if you've over-zoomed & broken things, you can zoom in by the same
        # factor but with a bigger field of view
        (yy,xx),chi2_rezoom = zoom.zoomnd(chi2, usfac=zf, offsets=offsets,
                outshape=rezoom_shape, middle_convention=np.floor,
                return_xouts=True, **kwargs)
    else:
        (yy,xx),chi2_rezoom = zoom.zoomnd(chi2, usfac=zf*rezoom_factor,
                offsets=offsets, outshape=rezoom_shape,
                middle_convention=np.floor, return_xouts=True, 
                **kwargs)

    # x and y are swapped and negative
    returns = [-off for off in offsets[::-1]]

    if return_error:
        errx_low,errx_high,erry_low,erry_high = chi2map_to_errors(chi2_rezoom, zf*rezoom_factor)
        returns.append( (errx_low+errx_high)/2. )
        returns.append( (erry_low+erry_high)/2. )
    if return_chi2array:
        yy = (chi2.shape[0]-1)/2 - yy
        xx = (chi2.shape[1]-1)/2 - xx
        returns.append((xx,yy,chi2_rezoom))

    return returns