Example #1
0
def test_shift(original, dx, dy):
    # Rotation center for all translation tests.
    image_center = np.array(original.shape) / 2.0 - 0.5

    # No rotation for all translation tests.
    rmatrix = np.array([[1.0, 0.0], [0.0, 1.0]])

    # Check a shifted shape against expected outcome
    expected = np.roll(np.roll(original, dx, axis=1), dy, axis=0)
    rcen = image_center - np.array([dx, dy])
    shift = affine_transform(original,
                             rmatrix=rmatrix,
                             recenter=True,
                             image_center=rcen)
    ymin, ymax = max([0, dy]), min([original.shape[1], original.shape[1] + dy])
    xmin, xmax = max([0, dx]), min([original.shape[0], original.shape[0] + dx])
    assert compare_results(expected[ymin:ymax, xmin:xmax], shift[ymin:ymax,
                                                                 xmin:xmax])

    # Check shifted and unshifted shape against original image
    rcen = image_center + np.array([dx, dy])
    unshift = affine_transform(shift,
                               rmatrix=rmatrix,
                               recenter=True,
                               image_center=rcen)
    # Need to ignore the portion of the image cut off by the first shift
    ymin, ymax = max([0,
                      -dy]), min([original.shape[1], original.shape[1] - dy])
    xmin, xmax = max([0,
                      -dx]), min([original.shape[0], original.shape[0] - dx])
    assert compare_results(original[ymin:ymax, xmin:xmax], unshift[ymin:ymax,
                                                                   xmin:xmax])
Example #2
0
def test_all(angle, dx, dy, scale_factor):
    """
    Tests to make sure that combinations of scaling, shifting and rotation
    produce the expected output.
    """
    k = int(angle / 90)
    angle = np.radians(angle)
    image_center = np.array(original.shape) / 2.0 - 0.5

    # Check a shifted, rotated and scaled shape against expected outcome
    c = np.cos(angle)
    s = np.sin(angle)
    rmatrix = np.array([[c, -s], [s, c]])
    scale = tf.rescale(original / original.max(), scale_factor, order=4,
                       mode='constant', multichannel=False, anti_aliasing=False) * original.max()
    new = np.zeros(original.shape)

    # Old width and new center of image
    w = np.array(original.shape[0])/2.0 - 0.5
    new_c = (np.array(scale.shape[0])/2.0 - 0.5)
    upper = int(w+new_c+1)
    if scale_factor > 1:
        lower = int(new_c-w)
        new = scale[lower:upper, lower:upper]
    else:
        lower = int(w-new_c)
        new[lower:upper, lower:upper] = scale
    disp = np.array([dx, dy])
    rcen = image_center + disp
    rot = np.rot90(new, k=k)
    shift = np.roll(np.roll(rot, dx, axis=1), dy, axis=0)
    expected = shift
    rotscaleshift = affine_transform(original, rmatrix=rmatrix, scale=scale_factor,
                        recenter=True, image_center=rcen)
    w = np.array(expected.shape[0])/2.0 - 0.5
    new_c = (np.array(rotscaleshift.shape[0])/2.0 - 0.5)
    upper = int(w+new_c+1)
    if scale_factor > 1:
        lower = int(new_c-w)
        expected = rotscaleshift[lower:upper, lower:upper]
    else:
        lower = int(w-new_c)
        expected[lower:upper, lower:upper] = rotscaleshift
    compare_results(expected, rotscaleshift)

    # Check a rotated/shifted and restored image against original
    transformed = affine_transform(original, rmatrix=rmatrix, scale=1.0, recenter=True,
                      image_center=rcen)
    rcen = image_center - np.dot(rmatrix, np.array([dx, dy]))
    dx, dy = np.asarray(np.dot(rmatrix, disp), dtype=int)
    rmatrix = np.array([[c, s], [-s, c]])
    inverse = affine_transform(transformed, rmatrix=rmatrix, scale=1.0, recenter=True,
                  image_center=rcen)

    # Need to ignore the portion of the image cut off by the first shift
    # (which isn't the portion you'd expect, because of the rotation)
    ymin, ymax = max([0, -dy]), min([original.shape[1], original.shape[1]-dy])
    xmin, xmax = max([0, -dx]), min([original.shape[0], original.shape[0]-dx])
    compare_results(original[ymin:ymax, xmin:xmax], inverse[ymin:ymax, xmin:xmax])
Example #3
0
def test_all(angle, dx, dy, scale_factor):
    """
    Tests to make sure that combinations of scaling, shifting and rotation
    produce the expected output.
    """
    k = int(angle / 90)
    angle = np.radians(angle)
    image_center = np.array(original.shape) / 2.0 - 0.5

    # Check a shifted, rotated and scaled shape against expected outcome
    c = np.cos(angle)
    s = np.sin(angle)
    rmatrix = np.array([[c, -s], [s, c]])
    scale = tf.rescale(original / original.max(), scale_factor, order=4,
                       mode='constant', multichannel=False, anti_aliasing=False) * original.max()
    new = np.zeros(original.shape)

    # Old width and new center of image
    w = np.array(original.shape[0])/2.0 - 0.5
    new_c = (np.array(scale.shape[0])/2.0 - 0.5)
    upper = int(w+new_c+1)
    if scale_factor > 1:
        lower = int(new_c-w)
        new = scale[lower:upper, lower:upper]
    else:
        lower = int(w-new_c)
        new[lower:upper, lower:upper] = scale
    disp = np.array([dx, dy])
    rcen = image_center + disp
    rot = np.rot90(new, k=k)
    shift = np.roll(np.roll(rot, dx, axis=1), dy, axis=0)
    expected = shift
    rotscaleshift = affine_transform(original, rmatrix=rmatrix, scale=scale_factor,
                        recenter=True, image_center=rcen)
    w = np.array(expected.shape[0])/2.0 - 0.5
    new_c = (np.array(rotscaleshift.shape[0])/2.0 - 0.5)
    upper = int(w+new_c+1)
    if scale_factor > 1:
        lower = int(new_c-w)
        expected = rotscaleshift[lower:upper, lower:upper]
    else:
        lower = int(w-new_c)
        expected[lower:upper, lower:upper] = rotscaleshift
    compare_results(expected, rotscaleshift)

    # Check a rotated/shifted and restored image against original
    transformed = affine_transform(original, rmatrix=rmatrix, scale=1.0, recenter=True,
                      image_center=rcen)
    rcen = image_center - np.dot(rmatrix, np.array([dx, dy]))
    dx, dy = np.asarray(np.dot(rmatrix, disp), dtype=int)
    rmatrix = np.array([[c, s], [-s, c]])
    inverse = affine_transform(transformed, rmatrix=rmatrix, scale=1.0, recenter=True,
                  image_center=rcen)

    # Need to ignore the portion of the image cut off by the first shift
    # (which isn't the portion you'd expect, because of the rotation)
    ymin, ymax = max([0, -dy]), min([original.shape[1], original.shape[1]-dy])
    xmin, xmax = max([0, -dx]), min([original.shape[0], original.shape[0]-dx])
    compare_results(original[ymin:ymax, xmin:xmax], inverse[ymin:ymax, xmin:xmax])
Example #4
0
def test_clipping(rot30):
    # Generates a plot to test the clipping the output image to the range of the input image
    image = np.ones((20, 20))
    image[4:-4, 4:-4] = 2

    num_methods = len(_rotation_registry.keys())

    fig = Figure(figsize=(12, 2 * num_methods))
    axs = fig.subplots(nrows=num_methods, ncols=5)

    for i, method in enumerate(_rotation_registry.keys()):
        axs[i, 0].imshow(image, vmin=0, vmax=3)
        axs[i, 1].imshow(affine_transform(image,
                                          rot30,
                                          clip=False,
                                          method=method,
                                          missing=0),
                         vmin=0,
                         vmax=3)
        axs[i, 2].imshow(affine_transform(image,
                                          rot30,
                                          clip=True,
                                          method=method,
                                          missing=0),
                         vmin=0,
                         vmax=3)
        axs[i, 3].imshow(affine_transform(image,
                                          rot30,
                                          clip=True,
                                          method=method,
                                          missing=2),
                         vmin=0,
                         vmax=3)
        axs[i, 4].imshow(affine_transform(image,
                                          rot30,
                                          clip=True,
                                          method=method,
                                          missing=np.nan),
                         vmin=0,
                         vmax=3)
        axs[i, 0].set_ylabel(method)

    axs[0, 0].set_title('Original')
    axs[0, 1].set_title('no clip & missing=0')
    axs[0, 2].set_title('clip & missing=0')
    axs[0, 3].set_title('clip & missing=2')
    axs[0, 4].set_title('clip & missing=NaN')

    return fig
Example #5
0
def test_scipy_rotation(angle, k):
    # Test rotation against expected outcome
    angle = np.radians(angle)
    c = np.cos(angle); s = np.sin(angle)
    rmatrix = np.array([[c, -s], [s, c]])
    expected = np.rot90(original, k=k)
    rot = affine_transform(original, rmatrix=rmatrix, use_scipy=True)
    assert compare_results(expected, rot, allclose=False)

    # TODO: Check incremental 360 degree rotation against original image

    # Check derotated image against original
    derot_matrix = np.array([[c, s], [-s, c]])
    derot = affine_transform(rot, rmatrix=derot_matrix, use_scipy=True)
    assert compare_results(original, derot, allclose=False)
Example #6
0
def test_scipy_rotation(angle, k):
    # Test rotation against expected outcome
    angle = np.radians(angle)
    c = np.cos(angle); s = np.sin(angle)
    rmatrix = np.array([[c, -s], [s, c]])
    expected = np.rot90(original, k=k)
    rot = affine_transform(original, rmatrix=rmatrix, use_scipy=True)
    assert compare_results(expected, rot, allclose=False)
    
    # TODO: Check incremental 360 degree rotation against original image

    # Check derotated image against original
    derot_matrix = np.array([[c, s], [-s, c]])
    derot = affine_transform(rot, rmatrix=derot_matrix, use_scipy=True)
    assert compare_results(original, derot, allclose=False)
Example #7
0
def test_scale(original, scale_factor):
    # No rotation for all scaling tests.
    rmatrix = np.array([[1.0, 0.0], [0.0, 1.0]])

    # Check a scaled image against the expected outcome
    # When we depend on SciPy 1.6, we can replace this with scipy.ndimage.zoom(..., grid_mode=True)
    newim = tf.rescale(original / original.max(),
                       scale_factor,
                       order=1,
                       mode='constant',
                       anti_aliasing=False) * original.max()
    # Old width and new center of image
    w = original.shape[0] / 2.0 - 0.5
    new_c = (newim.shape[0] / 2.0) - 0.5
    expected = np.zeros(original.shape)
    upper = int(w + new_c + 1)
    if scale_factor > 1:
        lower = int(new_c - w)
        expected = newim[lower:upper, lower:upper]
    else:
        lower = int(w - new_c)
        expected[lower:upper, lower:upper] = newim
    scale = affine_transform(original,
                             rmatrix=rmatrix,
                             scale=scale_factor,
                             order=1,
                             missing=0)
    assert compare_results(expected, scale)
Example #8
0
def test_nan_scipy(identity):
    # Test replacement of NaN values for scipy rotation
    in_arr = np.array([[np.nan]])
    with pytest.warns(SunpyUserWarning,
                      match='Setting NaNs to 0 for SciPy rotation.'):
        out_arr = affine_transform(in_arr, rmatrix=identity, use_scipy=True)
    assert not np.all(np.isnan(out_arr))
Example #9
0
def test_int(identity):
    # Test casting of integer array to float array
    in_arr = np.array([[100]], dtype=int)
    with pytest.warns(SunpyUserWarning,
                      match='Integer input data has been cast to float64'):
        out_arr = affine_transform(in_arr, rmatrix=identity)
    assert np.issubdtype(out_arr.dtype, np.floating)
Example #10
0
def test_scale(original, scale_factor):
    # No rotation for all scaling tests.
    rmatrix = np.array([[1.0, 0.0], [0.0, 1.0]])

    # Check a scaled image against the expected outcome
    newim = tf.rescale(original / original.max(),
                       scale_factor,
                       order=4,
                       mode='constant',
                       multichannel=False,
                       anti_aliasing=False) * original.max()
    # Old width and new center of image
    w = original.shape[0] / 2.0 - 0.5
    new_c = (newim.shape[0] / 2.0) - 0.5
    expected = np.zeros(original.shape)
    upper = int(w + new_c + 1)
    if scale_factor > 1:
        lower = int(new_c - w)
        expected = newim[lower:upper, lower:upper]
    else:
        lower = int(w - new_c)
        expected[lower:upper, lower:upper] = newim
    scale = affine_transform(original,
                             rmatrix=rmatrix,
                             scale=scale_factor,
                             order=4)
    assert compare_results(expected, scale)
Example #11
0
def test_nan_skimage_high(identity):
    # Test replacement of NaN values for scikit-image rotation with order >=4
    in_arr = np.array([[np.nan]])
    with pytest.warns(
            SunpyUserWarning,
            match='Setting NaNs to 0 for higher-order scikit-image rotation.'):
        out_arr = affine_transform(in_arr, rmatrix=identity, order=4)
    assert not np.all(np.isnan(out_arr))
Example #12
0
def test_rotation(angle, k):
    # Test rotation against expected outcome
    angle = np.radians(angle)
    c = np.cos(angle); s = np.sin(angle)
    rmatrix = np.array([[c, -s], [s, c]])
    expected = np.rot90(original, k=k)

    #Run the tests at order 4 as it produces more accurate 90 deg rotations
    rot = affine_transform(original, order=4, rmatrix=rmatrix)
    assert compare_results(expected, rot)

    # TODO: Check incremental 360 degree rotation against original image

    # Check derotated image against original
    derot_matrix = np.array([[c, s], [-s, c]])
    derot = affine_transform(rot, order=4, rmatrix=derot_matrix)
    assert compare_results(original, derot)
Example #13
0
def test_rotation(angle, k):
    # Test rotation against expected outcome
    angle = np.radians(angle)
    c = np.cos(angle); s = np.sin(angle)
    rmatrix = np.array([[c, -s], [s, c]])
    expected = np.rot90(original, k=k)

    #Run the tests at order 4 as it produces more accurate 90 deg rotations
    rot = affine_transform(original, order=4, rmatrix=rmatrix)
    assert compare_results(expected, rot)
    
    # TODO: Check incremental 360 degree rotation against original image

    # Check derotated image against original
    derot_matrix = np.array([[c, s], [-s, c]])
    derot = affine_transform(rot, order=4, rmatrix=derot_matrix)
    assert compare_results(original, derot)
Example #14
0
def test_nan_scipy(identity):
    # Test preservation of NaN values for scipy rotation
    in_arr = np.array([[np.nan, 0]])
    out_arr = affine_transform(in_arr,
                               rmatrix=identity,
                               order=0,
                               method='scipy')
    assert np.isnan(out_arr[0, 0])
Example #15
0
def test_endian(method, order, rot30):
    if order not in _rotation_registry[method].allowed_orders:
        return

    # Test that the rotation output values do not change with input byte order
    native = np.ones((10, 10))
    swapped = native.byteswap().newbyteorder()

    rot_native = affine_transform(native,
                                  rot30,
                                  order=order,
                                  method=method,
                                  missing=0)
    rot_swapped = affine_transform(swapped,
                                   rot30,
                                   order=order,
                                   method=method,
                                   missing=0)

    assert compare_results(rot_native, rot_swapped)
Example #16
0
def test_deprecated_args(identity):
    in_arr = np.array([[100]])
    with pytest.warns(SunpyDeprecationWarning,
                      match="The 'use_scipy' argument is deprecated"):
        out_arr = affine_transform(in_arr, rmatrix=identity, use_scipy=True)

    with pytest.warns(SunpyDeprecationWarning,
                      match="The 'use_scipy' argument is deprecated"):
        out_arr = affine_transform(in_arr, rmatrix=identity, use_scipy=False)

    with pytest.raises(ValueError,
                       match="Method blah not in supported methods"):
        out_arr = affine_transform(in_arr, rmatrix=identity, method='blah')

    with pytest.warns(SunpyUserWarning,
                      match="Using scipy instead of skimage for rotation"):
        out_arr = affine_transform(in_arr,
                                   rmatrix=identity,
                                   use_scipy=True,
                                   method='skimage')
Example #17
0
def test_shift(dx, dy):
    # Rotation center for all translation tests.
    image_center = np.array(original.shape)/2.0 - 0.5
    
    # No rotation for all translation tests.
    rmatrix = np.array([[1.0, 0.0], [0.0, 1.0]])

    # Check a shifted shape against expected outcome
    expected = np.roll(np.roll(original, dx, axis=1), dy, axis=0)
    rcen = image_center + np.array([dx, dy])
    shift = affine_transform(original, rmatrix=rmatrix, recenter=True, image_center=rcen)
    ymin, ymax = max([0, dy]), min([original.shape[1], original.shape[1]+dy])
    xmin, xmax = max([0, dx]), min([original.shape[0], original.shape[0]+dx])
    compare_results(expected[ymin:ymax, xmin:xmax], shift[ymin:ymax, xmin:xmax])

    # Check shifted and unshifted shape against original image
    rcen = image_center - np.array([dx, dy])
    unshift = affine_transform(shift, rmatrix=rmatrix, recenter=True, image_center=rcen)
    # Need to ignore the portion of the image cut off by the first shift
    ymin, ymax = max([0, -dy]), min([original.shape[1], original.shape[1]-dy])
    xmin, xmax = max([0, -dx]), min([original.shape[0], original.shape[0]-dx])
    compare_results(original[ymin:ymax, xmin:xmax], unshift[ymin:ymax, xmin:xmax])
Example #18
0
def test_nans(rot30):
    # Generates a plot to test the preservation and expansions of NaNs by the rotation
    image_with_nans = np.ones((23, 23))
    image_with_nans[4:-4, 4:-4] = 2
    image_with_nans[9:-9, 9:-9] = np.nan

    num_methods = len(_rotation_registry.keys())

    fig = Figure(figsize=(16, 2 * num_methods))
    axs = fig.subplots(nrows=num_methods, ncols=7)

    axs[0, 0].set_title('Original (NaNs are white)')

    for j in range(6):
        axs[0, j + 1].set_title(f'order={j}')
    for i, method in enumerate(_rotation_registry.keys()):
        axs[i, 0].imshow(image_with_nans, vmin=-1.1, vmax=1.1)
        for j in range(6):
            if j not in _rotation_registry[method].allowed_orders:
                with pytest.raises(ValueError):
                    affine_transform(image_with_nans,
                                     rot30,
                                     order=j,
                                     method=method,
                                     missing=np.nan)
                axs[i, j + 1].remove()
            else:
                axs[i, j + 1].imshow(affine_transform(image_with_nans,
                                                      rot30,
                                                      order=j,
                                                      method=method,
                                                      missing=np.nan),
                                     vmin=-1.1,
                                     vmax=1.1)
        axs[i, 0].set_ylabel(method)

    return fig
Example #19
0
    def mapRotation(self, zobs, datarec):
        theta = self.__getEtaMinusP(datarec)
        print(theta)
        #### 10/04/2018  ####
        #### correção do calculo da rotacao para radianos ###

        rmatrix = np.ndarray(shape=(2, 2), dtype=float)
        rmatrix[0][0] = m.cos(theta * (np.pi / 180))
        rmatrix[0][1] = m.sin(theta * (np.pi / 180))
        rmatrix[1][0] = -m.sin(theta * (np.pi / 180))
        rmatrix[1][1] = m.cos(theta * (np.pi / 180))

        maprotated = affine_transform(zobs, rmatrix)

        return maprotated
Example #20
0
def test_scale(scale_factor):
    # No rotation for all scaling tests.
    rmatrix = np.array([[1.0, 0.0], [0.0, 1.0]])
    
    # Check a scaled image against the expected outcome
    newim = tf.rescale(original/original.max(), scale_factor, order=4,
                       mode='constant') * original.max()
    # Old width and new center of image
    w = original.shape[0]/2.0 - 0.5
    new_c = (newim.shape[0]/2.0) - 0.5
    expected = np.zeros(original.shape)
    upper = w+new_c+1
    if scale_factor > 1:
        lower = new_c-w
        expected = newim[lower:upper, lower:upper]
    else:
        lower = w-new_c
        expected[lower:upper, lower:upper] = newim
    scale = affine_transform(original, rmatrix=rmatrix, scale=scale_factor)
    compare_results(expected, scale)
Example #21
0
def test_nan_scipy(identity):
    # Test replacement of NaN values for scipy rotation
    in_arr = np.array([[np.nan]])
    out_arr = affine_transform(in_arr, rmatrix=identity, use_scipy=True)
    assert not np.all(np.isnan(out_arr))
Example #22
0
def test_int(identity):
    # Test casting of integer array to float array
    in_arr = np.array([[100]], dtype=int)
    out_arr = affine_transform(in_arr, rmatrix=identity)
    assert np.issubdtype(out_arr.dtype, np.float)
Example #23
0
    def rotate(self, angle=None, rmatrix=None, order=4, scale=1.0,
               rotation_center=(0,0), recenter=False, missing=0.0, use_scipy=False):
        """
        Returns a new rotated and rescaled map.  Specify either a rotation
        angle or a rotation matrix, but not both.  If neither an angle or a
        rotation matrix are specified, the map will be rotated by the rotation
        angle in the metadata.

        Also updates the rotation_matrix attribute and any appropriate header
        data so that they correctly describe the new map.

        Parameters
        ----------
        angle : float
            The angle (degrees) to rotate counterclockwise.
        rmatrix : 2x2
            Linear transformation rotation matrix.
        order : int 0-5
            Interpolation order to be used. When using scikit-image this parameter
            is passed into :func:`skimage.transform.warp` (e.g., 4 corresponds to
            bi-quartic interpolation).
            When using scipy it is passed into
            :func:`scipy.ndimage.interpolation.affine_transform` where it controls
            the order of the spline.
            Faster performance may be obtained at the cost of accuracy by using lower values.
            Default: 4
        scale : float
            A scale factor for the image, default is no scaling
        rotation_center : tuple
            The axis of rotation in data coordinates
            Default: the origin in the data coordinate system
        recenter : bool
            If True, position the axis of rotation at the center of the new map
            Default: False
        missing : float
            The numerical value to fill any missing points after rotation.
            Default: 0.0
        use_scipy : bool
            If True, forces the rotation to use
            :func:`scipy.ndimage.interpolation.affine_transform`, otherwise it
            uses the :func:`skimage.transform.warp`.
            Default: False, unless scikit-image can't be imported

        Returns
        -------
        out : Map
            A new Map instance containing the rotated and rescaled data of the
            original map.

        See Also
        --------
        sunpy.image.transform.affine_transform : The routine this method calls for the rotation.

        Notes
        -----
        This function will remove old CROTA keywords from the header.
        This function will also convert a CDi_j matrix to a PCi_j matrix.

        See :func:`sunpy.image.transform.affine_transform` for details on the
        transformations, situations when the underlying data is modified prior to rotation,
        and differences from IDL's rot().
        """
        if angle is not None and rmatrix is not None:
            raise ValueError("You cannot specify both an angle and a matrix")
        elif angle is None and rmatrix is None:
            rmatrix = self.rotation_matrix

        # Interpolation parameter sanity
        if order not in range(6):
            raise ValueError("Order must be between 0 and 5")

        # Copy Map
        new_map = deepcopy(self)

        if angle is not None:
            # Calulate the parameters for the affine_transform
            c = np.cos(np.deg2rad(angle))
            s = np.sin(np.deg2rad(angle))
            rmatrix = np.matrix([[c, -s], [s, c]])

        # Calculate the shape in pixels to contain all of the image data
        extent = np.max(np.abs(np.vstack((new_map.shape * rmatrix, new_map.shape * rmatrix.T))), axis=0)
        # Calculate the needed padding or unpadding
        diff = np.asarray(np.ceil((extent - new_map.shape) / 2)).ravel()
        # Pad the image array
        pad_x = np.max((diff[1], 0))
        pad_y = np.max((diff[0], 0))
        new_map.data = np.pad(new_map.data,
                              ((pad_y, pad_y), (pad_x, pad_x)),
                              mode='constant',
                              constant_values=(missing, missing))
        new_map.meta['crpix1'] += pad_x
        new_map.meta['crpix2'] += pad_y

        # map_center is swapped compared to the x-y convention
        array_center = (np.array(new_map.data.shape)-1)/2.0

        # pixel_center is swapped compared to the x-y convention
        if recenter:
            # Convert the axis of rotation from data coordinates to pixel coordinates
            x = new_map.data_to_pixel(rotation_center[0], 'x')
            y = new_map.data_to_pixel(rotation_center[1], 'y')
            pixel_center = (y, x)
        else:
            pixel_center = array_center

        # Apply the rotation to the image data
        new_map.data = affine_transform(new_map.data.T,
                                        np.asarray(rmatrix),
                                        order=order, scale=scale,
                                        image_center=pixel_center,
                                        recenter=recenter, missing=missing,
                                        use_scipy=use_scipy).T


        # Calculate new reference pixel and coordinate at the center of the
        # image.
        if recenter:
            new_center = rotation_center
        else:
            # Retrieve old coordinates for the center of the array
            old_center = np.asarray(new_map.pixel_to_data(array_center[1], array_center[0]))

            # Calculate new coordinates for the center of the array
            new_center = rotation_center - np.dot(rmatrix, rotation_center - old_center)
            new_center = np.asarray(new_center)[0]

        # Define a new reference pixel in the rotated space
        new_map.meta['crval1'] = new_center[0]
        new_map.meta['crval2'] = new_center[1]
        new_map.meta['crpix1'] = array_center[1] + 1 # FITS counts pixels from 1
        new_map.meta['crpix2'] = array_center[0] + 1 # FITS counts pixels from 1

        # Unpad the array if necessary
        unpad_x = -np.min((diff[1], 0))
        if unpad_x > 0:
            new_map.data = new_map.data[:, unpad_x:-unpad_x]
            new_map.meta['crpix1'] -= unpad_x
        unpad_y = -np.min((diff[0], 0))
        if unpad_y > 0:
            new_map.data = new_map.data[unpad_y:-unpad_y, :]
            new_map.meta['crpix2'] -= unpad_y

        # Calculate the new rotation matrix to store in the header by
        # "subtracting" the rotation matrix used in the rotate from the old one
        # That being calculate the dot product of the old header data with the
        # inverse of the rotation matrix.
        pc_C = np.dot(new_map.rotation_matrix, rmatrix.I)
        new_map.meta['PC1_1'] = pc_C[0,0]
        new_map.meta['PC1_2'] = pc_C[0,1]
        new_map.meta['PC2_1'] = pc_C[1,0]
        new_map.meta['PC2_2'] = pc_C[1,1]

        # Update pixel size if image has been scaled.
        if scale != 1.0:
            new_map.meta['cdelt1'] = new_map.scale['x'] / scale
            new_map.meta['cdelt2'] = new_map.scale['y'] / scale

        # Remove old CROTA kwargs because we have saved a new PCi_j matrix.
        new_map.meta.pop('CROTA1', None)
        new_map.meta.pop('CROTA2', None)
        # Remove CDi_j header
        new_map.meta.pop('CD1_1', None)
        new_map.meta.pop('CD1_2', None)
        new_map.meta.pop('CD2_1', None)
        new_map.meta.pop('CD2_2', None)

        return new_map
Example #24
0
def test_nan_skimage_high(identity):
    # Test replacement of NaN values for scikit-image rotation with order >=4
    in_arr = np.array([[np.nan]])
    out_arr = affine_transform(in_arr, rmatrix=identity, order=4)
    assert not np.all(np.isnan(out_arr))
Example #25
0
def test_nan_scipy(identity):
    # Test replacement of NaN values for scipy rotation
    in_arr = np.array([[np.nan]])
    out_arr = affine_transform(in_arr, rmatrix=identity, use_scipy=True)
    assert not np.all(np.isnan(out_arr))
Example #26
0
def test_int(identity):
    # Test casting of integer array to float array
    in_arr = np.array([[100]], dtype=int)
    with pytest.warns(SunpyUserWarning, match='Input data has been cast to float64.'):
        out_arr = affine_transform(in_arr, rmatrix=identity)
    assert np.issubdtype(out_arr.dtype, np.floating)
Example #27
0
def test_flat(identity):
    # Test that a flat array can be rotated using scikit-image
    in_arr = np.array([[100]])
    out_arr = affine_transform(in_arr, rmatrix=identity)
    assert np.allclose(in_arr, out_arr, rtol=rtol)
Example #28
0
    def rotate(self, angle=None, rmatrix=None, order=3, scale=1.0,
               image_center=None, recenter=False, missing=0.0, use_scipy=False):
        """
        Returns a new rotated and rescaled map.  Specify either a rotation
        angle or a rotation matrix, but not both.  If neither an angle or a
        rotation matrix are specified, the map will be rotated by the rotation
        angle in the metadata.

        Also updates the rotation_matrix attribute and any appropriate header
        data so that they correctly describe the new map.

        Parameters
        ----------
        angle : float
            The angle (degrees) to rotate counterclockwise.
        rmatrix : 2x2
            Linear transformation rotation matrix.
        order : int 0-5
            Interpolation order to be used. When using scikit-image this parameter
            is passed into :func:`skimage.transform.warp`.
            When using scipy it is passed into
            :func:`scipy.ndimage.interpolation.affine_transform` where it controls
            the order of the spline.
        scale : float
            A scale factor for the image, default is no scaling
        image_center : tuple
            The axis of rotation in pixel coordinates
            Default: the origin in the data coordinate system
        recenter : bool
            If True, position the axis of rotation at the center of the new map
            Default: False
        missing : float
            The numerical value to fill any missing points after rotation.
            Default: 0.0
        use_scipy : bool
            If True, forces the rotation to use
            :func:`scipy.ndimage.interpolation.affine_transform`, otherwise it
            uses the :class:`skimage.transform.AffineTransform` class and
            :func:`skimage.transform.warp`.
            The function will also automatically fall back to
            :func:`scipy.ndimage.interpolation.affine_transform` if scikit-image
            can't be imported.
            Default: False

        Returns
        -------
        out : Map
            A new Map instance containing the rotated and rescaled data of the
            original map.

        Notes
        -----
        This function will remove old CROTA keywords from the header.
        This function will also convert a CDi_j matrix to a PCi_j matrix.

        This function is not numerically equalivalent to IDL's rot() see the
        :func:`sunpy.image.transform.affine_transform` documentation for a
        detailed description of the differences.
        """
        if angle is not None and rmatrix is not None:
            raise ValueError("You cannot specify both an angle and a matrix")
        elif angle is None and rmatrix is None:
            rmatrix = self.rotation_matrix

        # Interpolation parameter sanity
        if order not in range(6):
            raise ValueError("Order must be between 0 and 5")

        # Copy Map
        new_map = deepcopy(self)

        if angle is not None:
            #Calulate the parameters for the affine_transform
            c = np.cos(np.deg2rad(angle))
            s = np.sin(np.deg2rad(angle))
            rmatrix = np.matrix([[c, -s], [s, c]])

        if image_center is None:
            # FITS pixels  count from 1 (curse you, FITS!)
            image_center = (self.shape[1] - self.reference_pixel['x'] + 1,
                            self.reference_pixel['y'])

        # Because map data has the origin at the bottom left not the top left
        # as is convention for images vertically flip the image for the
        # transform and then flip it back again.
        new_map.data = np.flipud(affine_transform(np.flipud(new_map.data),
                                                  np.array(rmatrix),
                                                  order=order, scale=scale,
                                                  image_center=image_center,
                                                  recenter=recenter, missing=missing,
                                                  use_scipy=use_scipy))

        map_center = (np.array(self.shape)/2.0) + 0.5

        # Calculate the new rotation matrix to store in the header by
        # "subtracting" the rotation matrix used in the rotate from the old one
        # That being calculate the dot product of the old header data with the
        # inverse of the rotation matrix.
        pc_C = np.dot(self.rotation_matrix, rmatrix.I)
        new_map.meta['PC1_1'] = pc_C[0,0]
        new_map.meta['PC1_2'] = pc_C[0,1]
        new_map.meta['PC2_1'] = pc_C[1,0]
        new_map.meta['PC2_2'] = pc_C[1,1]
        # Update pixel size if image has been scaled.
        if scale != 1.0:
            new_map.meta['cdelt1'] = self.scale['x'] / scale
            new_map.meta['cdelt2'] = self.scale['y'] / scale

        if recenter:
            # Move the reference pixel based on the image shift.
            # The y coordinate is inverted due to the map having the origin in
            # the lower left rather than the upper left.
            shift = image_center - map_center
            new_map.meta['crpix1'] += shift[0]
            new_map.meta['crpix2'] -= shift[1]

        # Remove old CROTA kwargs because we have saved a new PCi_j matrix.
        new_map.meta.pop('CROTA1', None)
        new_map.meta.pop('CROTA2', None)
        # Remove CDi_j header
        new_map.meta.pop('CD1_1', None)
        new_map.meta.pop('CD1_2', None)
        new_map.meta.pop('CD2_1', None)
        new_map.meta.pop('CD2_2', None)

        return new_map
Example #29
0
def test_nan_scipy(identity):
    # Test replacement of NaN values for scipy rotation
    in_arr = np.array([[np.nan]])
    with pytest.warns(SunpyUserWarning, match='Setting NaNs to 0 for SciPy rotation.'):
        out_arr = affine_transform(in_arr, rmatrix=identity, use_scipy=True)
    assert not np.all(np.isnan(out_arr))
Example #30
0
def test_int(identity):
    # Test casting of integer array to float array
    in_arr = np.array([[100]], dtype=int)
    out_arr = affine_transform(in_arr, rmatrix=identity)
    assert np.issubdtype(out_arr.dtype, np.float)
Example #31
0
def test_nan_skimage_high(identity):
    # Test replacement of NaN values for scikit-image rotation with order >=4
    in_arr = np.array([[np.nan]])
    with pytest.warns(SunpyUserWarning, match='Setting NaNs to 0 for higher-order scikit-image rotation.'):
        out_arr = affine_transform(in_arr, rmatrix=identity, order=4)
    assert not np.all(np.isnan(out_arr))
Example #32
0
def test_nan_skimage_low(identity):
    # Test non-replacement of NaN values for scikit-image rotation with order <= 3
    in_arr = np.array([[np.nan]])
    out_arr = affine_transform(in_arr, rmatrix=identity, order=3)
    assert np.all(np.isnan(out_arr))
Example #33
0
def test_flat(identity):
    # Test that a flat array can be rotated using scikit-image
    in_arr = np.array([[100]], dtype=np.float64)
    out_arr = affine_transform(in_arr, rmatrix=identity)
    assert np.allclose(in_arr, out_arr, rtol=RTOL)
Example #34
0
def test_all(original, angle, dx, dy, scale_factor):
    """
    Tests to make sure that combinations of scaling, shifting and rotation
    produce the expected output.
    """
    k = int(angle / 90)
    angle = np.radians(angle)
    image_center = np.array(original.shape) / 2.0 - 0.5

    # Check a shifted, rotated and scaled shape against expected outcome
    c = np.round(np.cos(angle))
    s = np.round(np.sin(angle))
    rmatrix = np.array([[c, -s], [s, c]])
    scale = tf.rescale(original / original.max(),
                       scale_factor,
                       order=4,
                       mode='constant',
                       multichannel=False,
                       anti_aliasing=False) * original.max()
    new = np.zeros(original.shape)

    disp = np.array([dx, dy])
    dxs, dys = np.asarray(disp * scale_factor, dtype=int)
    # Old width and new center of image
    w = np.array(original.shape[0]) / 2.0 - 0.5
    new_c = (np.array(scale.shape[0]) / 2.0 - 0.5)
    upper = int(w + new_c + 1)
    if scale_factor > 1:
        lower = int(new_c - w)
        new = scale[lower - dys:upper - dys, lower - dxs:upper - dxs]
    else:
        lower = int(w - new_c)
        new[lower + dys:upper + dys, lower + dxs:upper + dxs] = scale
    rcen = image_center - disp
    expected = np.rot90(new, k=k)

    rotscaleshift = affine_transform(original,
                                     rmatrix=rmatrix,
                                     scale=scale_factor,
                                     order=4,
                                     recenter=True,
                                     image_center=rcen)
    assert compare_results(expected, rotscaleshift)

    # Check a rotated/shifted and restored image against original
    transformed = affine_transform(original,
                                   rmatrix=rmatrix,
                                   scale=1.0,
                                   order=4,
                                   recenter=True,
                                   image_center=rcen)
    inv_rcen = image_center + np.dot(rmatrix.T, np.array([dx, dy]))
    inverse = affine_transform(transformed,
                               rmatrix=rmatrix.T,
                               scale=1.0,
                               order=4,
                               recenter=True,
                               image_center=inv_rcen)

    # Need to ignore the portion of the image cut off by the first shift
    ymin, ymax = max([0,
                      -dy]), min([original.shape[1], original.shape[1] - dy])
    xmin, xmax = max([0,
                      -dx]), min([original.shape[0], original.shape[0] - dx])
    assert compare_results(original[ymin:ymax, xmin:xmax], inverse[ymin:ymax,
                                                                   xmin:xmax])
Example #35
0
def test_float32(identity):
    # Check that float32 input remains as float32 output
    # Test casting of integer array to float array
    in_arr = np.array([[100]], dtype=np.float32)
    out_arr = affine_transform(in_arr, rmatrix=identity)
    assert np.issubdtype(out_arr.dtype, np.float32)
Example #36
0
    def rotate(self, angle=None, rmatrix=None, order=3, scale=1.0,
               image_center=(0,0), recenter=False, missing=0.0, use_scipy=False):
        """
        Returns a new rotated and rescaled map.  Specify either a rotation
        angle or a rotation matrix, but not both.  If neither an angle or a
        rotation matrix are specified, the map will be rotated by the rotation
        angle in the metadata.

        Also updates the rotation_matrix attribute and any appropriate header
        data so that they correctly describe the new map.

        Parameters
        ----------
        angle : float
            The angle (degrees) to rotate counterclockwise.
        rmatrix : 2x2
            Linear transformation rotation matrix.
        order : int 0-5
            Interpolation order to be used. When using scikit-image this parameter
            is passed into :func:`skimage.transform.warp`.
            When using scipy it is passed into
            :func:`scipy.ndimage.interpolation.affine_transform` where it controls
            the order of the spline.
            Higher accuracy may be obtained at the cost of performance by using
            higher values.
        scale : float
            A scale factor for the image, default is no scaling
        image_center : tuple
            The axis of rotation in data coordinates
            Default: the origin in the data coordinate system
        recenter : bool
            If True, position the axis of rotation at the center of the new map
            Default: False
        missing : float
            The numerical value to fill any missing points after rotation.
            Default: 0.0
        use_scipy : bool
            If True, forces the rotation to use
            :func:`scipy.ndimage.interpolation.affine_transform`, otherwise it
            uses the :class:`skimage.transform.AffineTransform` class and
            :func:`skimage.transform.warp`.
            The function will also automatically fall back to
            :func:`scipy.ndimage.interpolation.affine_transform` if scikit-image
            can't be imported.
            Default: False

        Returns
        -------
        out : Map
            A new Map instance containing the rotated and rescaled data of the
            original map.

        See Also
        --------
        sunpy.image.transform.affine_transform : The routine this method calls for the rotation.

        Notes
        -----
        This function will remove old CROTA keywords from the header.
        This function will also convert a CDi_j matrix to a PCi_j matrix.

        The scikit-image and scipy affine_transform routines do not use the same algorithm,
        see :func:`sunpy.image.transform.affine_transform` for details.

        This function is not numerically equalivalent to IDL's rot() see the
        :func:`sunpy.image.transform.affine_transform` documentation for a
        detailed description of the differences.
        """
        if angle is not None and rmatrix is not None:
            raise ValueError("You cannot specify both an angle and a matrix")
        elif angle is None and rmatrix is None:
            rmatrix = self.rotation_matrix

        # Interpolation parameter sanity
        if order not in range(6):
            raise ValueError("Order must be between 0 and 5")

        # Copy Map
        new_map = deepcopy(self)

        if angle is not None:
            #Calulate the parameters for the affine_transform
            c = np.cos(np.deg2rad(angle))
            s = np.sin(np.deg2rad(angle))
            rmatrix = np.matrix([[c, -s], [s, c]])

        # map_center is swapped compared to the x-y convention
        array_center = (np.array(self.data.shape)-1)/2.0

        # rotation_center is swapped compared to the x-y convention
        if recenter:
            # Convert the axis of rotation from data coordinates to pixel coordinates
            x = self.data_to_pixel(image_center[0], 'x')
            y = self.data_to_pixel(image_center[1], 'y')
            rotation_center = (y, x)
        else:
            rotation_center = array_center

        #Return a new map
        #Copy Header
        new_map = deepcopy(self)

        new_map.data = affine_transform(new_map.data.T,
                                        np.asarray(rmatrix),
                                        order=order, scale=scale,
                                        image_center=rotation_center,
                                        recenter=recenter, missing=missing,
                                        use_scipy=use_scipy).T


        # Calculate new reference pixel and coordinate at the center of the
        # image.
        if recenter:
            new_center = image_center
        else:
            # Retrieve old coordinates for the center of the array
            old_center = np.asarray(self.pixel_to_data(array_center[1], array_center[0]))

            # Calculate new coordinates for the center of the array
            new_center = image_center - np.dot(rmatrix, image_center - old_center)
            new_center = np.asarray(new_center)[0]

        # Define a new reference pixel in the rotated space
        new_map.meta['crval1'] = new_center[0]
        new_map.meta['crval2'] = new_center[1]
        new_map.meta['crpix1'] = array_center[1] + 1 # FITS counts pixels from 1
        new_map.meta['crpix2'] = array_center[0] + 1 # FITS counts pixels from 1

        # Calculate the new rotation matrix to store in the header by
        # "subtracting" the rotation matrix used in the rotate from the old one
        # That being calculate the dot product of the old header data with the
        # inverse of the rotation matrix.
        pc_C = np.dot(self.rotation_matrix, rmatrix.I)
        new_map.meta['PC1_1'] = pc_C[0,0]
        new_map.meta['PC1_2'] = pc_C[0,1]
        new_map.meta['PC2_1'] = pc_C[1,0]
        new_map.meta['PC2_2'] = pc_C[1,1]

        # Update pixel size if image has been scaled.
        if scale != 1.0:
            new_map.meta['cdelt1'] = self.scale['x'] / scale
            new_map.meta['cdelt2'] = self.scale['y'] / scale

        # Remove old CROTA kwargs because we have saved a new PCi_j matrix.
        new_map.meta.pop('CROTA1', None)
        new_map.meta.pop('CROTA2', None)
        # Remove CDi_j header
        new_map.meta.pop('CD1_1', None)
        new_map.meta.pop('CD1_2', None)
        new_map.meta.pop('CD2_1', None)
        new_map.meta.pop('CD2_2', None)

        return new_map
Example #37
0
    def rotate(self,
               angle=None,
               rmatrix=None,
               order=3,
               scale=1.0,
               image_center=(0, 0),
               recenter=False,
               missing=0.0,
               use_scipy=False):
        """
        Returns a new rotated and rescaled map.  Specify either a rotation
        angle or a rotation matrix, but not both.  If neither an angle or a
        rotation matrix are specified, the map will be rotated by the rotation
        angle in the metadata.

        Also updates the rotation_matrix attribute and any appropriate header
        data so that they correctly describe the new map.

        Parameters
        ----------
        angle : float
            The angle (degrees) to rotate counterclockwise.
        rmatrix : 2x2
            Linear transformation rotation matrix.
        order : int 0-5
            Interpolation order to be used. When using scikit-image this parameter
            is passed into :func:`skimage.transform.warp`.
            When using scipy it is passed into
            :func:`scipy.ndimage.interpolation.affine_transform` where it controls
            the order of the spline.
            Higher accuracy may be obtained at the cost of performance by using
            higher values.
        scale : float
            A scale factor for the image, default is no scaling
        image_center : tuple
            The axis of rotation in data coordinates
            Default: the origin in the data coordinate system
        recenter : bool
            If True, position the axis of rotation at the center of the new map
            Default: False
        missing : float
            The numerical value to fill any missing points after rotation.
            Default: 0.0
        use_scipy : bool
            If True, forces the rotation to use
            :func:`scipy.ndimage.interpolation.affine_transform`, otherwise it
            uses the :class:`skimage.transform.AffineTransform` class and
            :func:`skimage.transform.warp`.
            The function will also automatically fall back to
            :func:`scipy.ndimage.interpolation.affine_transform` if scikit-image
            can't be imported.
            Default: False

        Returns
        -------
        out : Map
            A new Map instance containing the rotated and rescaled data of the
            original map.

        See Also
        --------
        sunpy.image.transform.affine_transform : The routine this method calls for the rotation.

        Notes
        -----
        This function will remove old CROTA keywords from the header.
        This function will also convert a CDi_j matrix to a PCi_j matrix.

        The scikit-image and scipy affine_transform routines do not use the same algorithm,
        see :func:`sunpy.image.transform.affine_transform` for details.

        This function is not numerically equalivalent to IDL's rot() see the
        :func:`sunpy.image.transform.affine_transform` documentation for a
        detailed description of the differences.
        """
        if angle is not None and rmatrix is not None:
            raise ValueError("You cannot specify both an angle and a matrix")
        elif angle is None and rmatrix is None:
            rmatrix = self.rotation_matrix

        # Interpolation parameter sanity
        if order not in range(6):
            raise ValueError("Order must be between 0 and 5")

        # Copy Map
        new_map = deepcopy(self)

        if angle is not None:
            #Calulate the parameters for the affine_transform
            c = np.cos(np.deg2rad(angle))
            s = np.sin(np.deg2rad(angle))
            rmatrix = np.matrix([[c, -s], [s, c]])

        # map_center is swapped compared to the x-y convention
        array_center = (np.array(self.data.shape) - 1) / 2.0

        # rotation_center is swapped compared to the x-y convention
        if recenter:
            # Convert the axis of rotation from data coordinates to pixel coordinates
            x = self.data_to_pixel(image_center[0], 'x')
            y = self.data_to_pixel(image_center[1], 'y')
            rotation_center = (y, x)
        else:
            rotation_center = array_center

        #Return a new map
        #Copy Header
        new_map = deepcopy(self)

        new_map.data = affine_transform(new_map.data.T,
                                        np.asarray(rmatrix),
                                        order=order,
                                        scale=scale,
                                        image_center=rotation_center,
                                        recenter=recenter,
                                        missing=missing,
                                        use_scipy=use_scipy).T

        # Calculate new reference pixel and coordinate at the center of the
        # image.
        if recenter:
            new_center = image_center
        else:
            # Retrieve old coordinates for the center of the array
            old_center = np.asarray(
                self.pixel_to_data(array_center[1], array_center[0]))

            # Calculate new coordinates for the center of the array
            new_center = image_center - np.dot(rmatrix,
                                               image_center - old_center)
            new_center = np.asarray(new_center)[0]

        # Define a new reference pixel in the rotated space
        new_map.meta['crval1'] = new_center[0]
        new_map.meta['crval2'] = new_center[1]
        new_map.meta[
            'crpix1'] = array_center[1] + 1  # FITS counts pixels from 1
        new_map.meta[
            'crpix2'] = array_center[0] + 1  # FITS counts pixels from 1

        # Calculate the new rotation matrix to store in the header by
        # "subtracting" the rotation matrix used in the rotate from the old one
        # That being calculate the dot product of the old header data with the
        # inverse of the rotation matrix.
        pc_C = np.dot(self.rotation_matrix, rmatrix.I)
        new_map.meta['PC1_1'] = pc_C[0, 0]
        new_map.meta['PC1_2'] = pc_C[0, 1]
        new_map.meta['PC2_1'] = pc_C[1, 0]
        new_map.meta['PC2_2'] = pc_C[1, 1]

        # Update pixel size if image has been scaled.
        if scale != 1.0:
            new_map.meta['cdelt1'] = self.scale['x'] / scale
            new_map.meta['cdelt2'] = self.scale['y'] / scale

        # Remove old CROTA kwargs because we have saved a new PCi_j matrix.
        new_map.meta.pop('CROTA1', None)
        new_map.meta.pop('CROTA2', None)
        # Remove CDi_j header
        new_map.meta.pop('CD1_1', None)
        new_map.meta.pop('CD1_2', None)
        new_map.meta.pop('CD2_1', None)
        new_map.meta.pop('CD2_2', None)

        return new_map
Example #38
0
    def rotate(self, angle=None, rmatrix=None, order=4, scale=1.0,
               recenter=False, missing=0.0, use_scipy=False):
        """
        Returns a new rotated and rescaled map.  Specify either a rotation
        angle or a rotation matrix, but not both.  If neither an angle or a
        rotation matrix are specified, the map will be rotated by the rotation
        angle in the metadata.

        The map will be rotated around the reference coordinate defined in the
        meta data.

        Also updates the rotation_matrix attribute and any appropriate header
        data so that they correctly describe the new map.

        Parameters
        ----------
        angle : `~astropy.units.Quantity`
            The angle (degrees) to rotate counterclockwise.
        rmatrix : 2x2
            Linear transformation rotation matrix.
        order : int 0-5
            Interpolation order to be used. When using scikit-image this parameter
            is passed into :func:`skimage.transform.warp` (e.g., 4 corresponds to
            bi-quartic interpolation).
            When using scipy it is passed into
            :func:`scipy.ndimage.interpolation.affine_transform` where it controls
            the order of the spline.
            Faster performance may be obtained at the cost of accuracy by using lower values.
            Default: 4
        scale : float
            A scale factor for the image, default is no scaling
        recenter : bool
            If True, position the axis of rotation at the center of the new map
            Default: False
        missing : float
            The numerical value to fill any missing points after rotation.
            Default: 0.0
        use_scipy : bool
            If True, forces the rotation to use
            :func:`scipy.ndimage.interpolation.affine_transform`, otherwise it
            uses the :func:`skimage.transform.warp`.
            Default: False, unless scikit-image can't be imported

        Returns
        -------
        out : Map
            A new Map instance containing the rotated and rescaled data of the
            original map.

        See Also
        --------
        sunpy.image.transform.affine_transform : The routine this method calls for the rotation.

        Notes
        -----
        This function will remove old CROTA keywords from the header.
        This function will also convert a CDi_j matrix to a PCi_j matrix.

        See :func:`sunpy.image.transform.affine_transform` for details on the
        transformations, situations when the underlying data is modified prior to rotation,
        and differences from IDL's rot().
        """
        if angle is not None and rmatrix is not None:
            raise ValueError("You cannot specify both an angle and a matrix")
        elif angle is None and rmatrix is None:
            rmatrix = self.rotation_matrix

        # This is out of the quantity_input decorator. To allow the angle=None
        # case. See https://github.com/astropy/astropy/issues/3734
        if angle:
            try:
                equivalent = angle.unit.is_equivalent(u.deg)

                if not equivalent:
                    raise u.UnitsError("Argument '{0}' to function '{1}'"
                                       " must be in units convertable to"
                                       " '{2}'.".format('angle', 'rotate',
                                                      u.deg.to_string()))

            # Either there is no .unit or no .is_equivalent
            except AttributeError:
                if hasattr(angle, "unit"):
                    error_msg = "a 'unit' attribute without an 'is_equivalent' method"
                else:
                    error_msg = "no 'unit' attribute"
                raise TypeError("Argument '{0}' to function '{1}' has {2}. "
                      "You may want to pass in an astropy Quantity instead."
                         .format('angle', 'rotate', error_msg))

        # Interpolation parameter sanity
        if order not in range(6):
            raise ValueError("Order must be between 0 and 5")

        # The FITS-WCS transform is by definition defined around the
        # reference coordinate in the header.
        rotation_center = u.Quantity([self.reference_coordinate.x,
                                      self.reference_coordinate.y])

        # Copy Map
        new_map = deepcopy(self)

        if angle is not None:
            # Calulate the parameters for the affine_transform
            c = np.cos(np.deg2rad(angle))
            s = np.sin(np.deg2rad(angle))
            rmatrix = np.matrix([[c, -s], [s, c]])

        # Calculate the shape in pixels to contain all of the image data
        extent = np.max(np.abs(np.vstack((new_map.data.shape * rmatrix,
                                          new_map.data.shape * rmatrix.T))), axis=0)
        # Calculate the needed padding or unpadding
        diff = np.asarray(np.ceil((extent - new_map.data.shape) / 2)).ravel()
        # Pad the image array
        pad_x = np.max((diff[1], 0))
        pad_y = np.max((diff[0], 0))
        new_map.data = np.pad(new_map.data,
                              ((pad_y, pad_y), (pad_x, pad_x)),
                              mode='constant',
                              constant_values=(missing, missing))
        new_map.meta['crpix1'] += pad_x
        new_map.meta['crpix2'] += pad_y

        # All of the following pixel calculations use a pixel origin of 0

        pixel_array_center = (np.flipud(new_map.data.shape) - 1) / 2.0

        # Convert the axis of rotation from data coordinates to pixel coordinates
        pixel_rotation_center = u.Quantity(new_map.data_to_pixel(*rotation_center,
                                                                 origin=0)).value
        if recenter:
            pixel_center = pixel_rotation_center
        else:
            pixel_center = pixel_array_center

        # Apply the rotation to the image data
        new_map.data = affine_transform(new_map.data.T,
                                        np.asarray(rmatrix),
                                        order=order, scale=scale,
                                        image_center=np.flipud(pixel_center),
                                        recenter=recenter, missing=missing,
                                        use_scipy=use_scipy).T

        if recenter:
            new_reference_pixel = pixel_array_center
        else:
            # Calculate new pixel coordinates for the rotation center
            new_reference_pixel = pixel_center + np.dot(rmatrix,
                                                        pixel_rotation_center - pixel_center)
            new_reference_pixel = np.array(new_reference_pixel).ravel()

        # Define the new reference_pixel
        new_map.meta['crval1'] = rotation_center[0].value
        new_map.meta['crval2'] = rotation_center[1].value
        new_map.meta['crpix1'] = new_reference_pixel[0] + 1 # FITS pixel origin is 1
        new_map.meta['crpix2'] = new_reference_pixel[1] + 1 # FITS pixel origin is 1

        # Unpad the array if necessary
        unpad_x = -np.min((diff[1], 0))
        if unpad_x > 0:
            new_map.data = new_map.data[:, unpad_x:-unpad_x]
            new_map.meta['crpix1'] -= unpad_x
        unpad_y = -np.min((diff[0], 0))
        if unpad_y > 0:
            new_map.data = new_map.data[unpad_y:-unpad_y, :]
            new_map.meta['crpix2'] -= unpad_y

        # Calculate the new rotation matrix to store in the header by
        # "subtracting" the rotation matrix used in the rotate from the old one
        # That being calculate the dot product of the old header data with the
        # inverse of the rotation matrix.
        pc_C = np.dot(new_map.rotation_matrix, rmatrix.I)
        new_map.meta['PC1_1'] = pc_C[0,0]
        new_map.meta['PC1_2'] = pc_C[0,1]
        new_map.meta['PC2_1'] = pc_C[1,0]
        new_map.meta['PC2_2'] = pc_C[1,1]

        # Update pixel size if image has been scaled.
        if scale != 1.0:
            new_map.meta['cdelt1'] = (new_map.scale.x / scale).value
            new_map.meta['cdelt2'] = (new_map.scale.y / scale).value

        # Remove old CROTA kwargs because we have saved a new PCi_j matrix.
        new_map.meta.pop('CROTA1', None)
        new_map.meta.pop('CROTA2', None)
        # Remove CDi_j header
        new_map.meta.pop('CD1_1', None)
        new_map.meta.pop('CD1_2', None)
        new_map.meta.pop('CD2_1', None)
        new_map.meta.pop('CD2_2', None)

        return new_map