prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Reduce import time for `sunpy.image.transform`
  (I was getting annoyed by our import times, and was inspired by astropy/astropy#4598 to look deeper.)
  
  This one's easy.  Importing `sunpy.image.transform` takes 1.7 seconds on my machine (see below).  0.7 seconds is spent importing `skimage.transform`.  We should defer that import to run-time.
  
  (see #3445 for dealing with `pkg_resources`)
  
  ```
  python -X importtime -c "import sunpy.image.transform" 2> transform.log
  tuna transform.log
  ```
  ![transform](https://user-images.githubusercontent.com/991759/67576249-34433a00-f70c-11e9-9cef-4f7b3804e66c.png)
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `sunpy/image/transform.py`
  Content:
  ```
  1 """
  2 Functions for geometrical image transformation and warping.
  3 """
  4 import warnings
  5 
  6 import numpy as np
  7 import scipy.ndimage.interpolation
  8 
  9 from sunpy.util.exceptions import SunpyUserWarning
  10 
  11 try:
  12     import skimage.transform
  13     scikit_image_not_found = False
  14 except ImportError:
  15     warnings.warn("scikit-image could not be imported. Image rotation will use scipy",
  16                   ImportWarning)
  17     scikit_image_not_found = True
  18 
  19 
  20 __all__ = ['affine_transform']
  21 
  22 
  23 def affine_transform(image, rmatrix, order=3, scale=1.0, image_center=None,
  24                      recenter=False, missing=0.0, use_scipy=False):
  25     """
  26     Rotates, shifts and scales an image.
  27 
  28     Will use `skimage.transform.warp` unless scikit-image can't be imported
  29     then it will use`scipy.ndimage.interpolation.affine_transform`.
  30 
  31     Parameters
  32     ----------
  33     image : `numpy.ndarray`
  34         2D image to be rotated.
  35     rmatrix : `numpy.ndarray` that is 2x2
  36         Linear transformation rotation matrix.
  37     order : `int` 0-5, optional
  38         Interpolation order to be used, defaults to 3. When using scikit-image this parameter
  39         is passed into `skimage.transform.warp` (e.g., 3 corresponds to bi-cubic interpolation).
  40         When using scipy it is passed into
  41         `scipy.ndimage.interpolation.affine_transform` where it controls the order of the spline.
  42     scale : `float`
  43         A scale factor for the image with the default being no scaling.
  44     image_center : tuple, optional
  45         The point in the image to rotate around (axis of rotation).
  46         Defaults to the center of the array.
  47     recenter : `bool` or array-like, optional
  48         Move the axis of rotation to the center of the array or recenter coords.
  49         Defaults to `True` i.e., recenter to the center of the array.
  50     missing : `float`, optional
  51         The value to replace any missing data after the transformation.
  52     use_scipy : `bool`, optional
  53         Force use of `scipy.ndimage.interpolation.affine_transform`.
  54         Will set all "NaNs" in image to zero before doing the transform.
  55         Defaults to `False`, unless scikit-image can't be imported.
  56 
  57     Returns
  58     -------
  59     `numpy.ndarray`:
  60         New rotated, scaled and translated image.
  61 
  62     Notes
  63     -----
  64     This algorithm uses an affine transformation as opposed to a polynomial
  65     geometrical transformation, which by default is `skimage.transform.warp`.
  66     One can specify using `scipy.ndimage.interpolation.affine_transform` as
  67     an alternative affine transformation. The two transformations use different
  68     algorithms and thus do not give identical output.
  69 
  70     When using for `skimage.transform.warp` with order >= 4 or using
  71     `scipy.ndimage.interpolation.affine_transform` at all, "NaN" values will
  72     replaced with zero prior to rotation. No attempt is made to retain the NaN
  73     values.
  74 
  75     Input arrays with integer data are cast to float 64 and can be re-cast using
  76     `numpy.ndarray.astype` if desired.
  77 
  78     Although this function is analogous to the IDL's ``rot`` function, it does not
  79     use the same algorithm as the IDL ``rot`` function.
  80     IDL's ``rot`` calls the `POLY_2D <https://www.harrisgeospatial.com/docs/poly_2d.html>`__
  81     method to calculate the inverse mapping of original to target pixel
  82     coordinates. This is a polynomial geometrical transformation.
  83     Then optionally it uses a bicubic convolution interpolation
  84     algorithm to map the original to target pixel values.
  85     """
  86     rmatrix = rmatrix / scale
  87     array_center = (np.array(image.shape)[::-1]-1)/2.0
  88 
  89     # Make sure the image center is an array and is where it's supposed to be
  90     if image_center is not None:
  91         image_center = np.asanyarray(image_center)
  92     else:
  93         image_center = array_center
  94 
  95     # Determine center of rotation based on use (or not) of the recenter keyword
  96     if recenter:
  97         rot_center = array_center
  98     else:
  99         rot_center = image_center
  100 
  101     displacement = np.dot(rmatrix, rot_center)
  102     shift = image_center - displacement
  103 
  104     if use_scipy or scikit_image_not_found:
  105         if np.any(np.isnan(image)):
  106             warnings.warn("Setting NaNs to 0 for SciPy rotation.", SunpyUserWarning)
  107         # Transform the image using the scipy affine transform
  108         rotated_image = scipy.ndimage.interpolation.affine_transform(
  109                 np.nan_to_num(image).T, rmatrix, offset=shift, order=order,
  110                 mode='constant', cval=missing).T
  111     else:
  112         # Make the rotation matrix 3x3 to include translation of the image
  113         skmatrix = np.zeros((3, 3))
  114         skmatrix[:2, :2] = rmatrix
  115         skmatrix[2, 2] = 1.0
  116         skmatrix[:2, 2] = shift
  117         tform = skimage.transform.AffineTransform(skmatrix)
  118 
  119         # Transform the image using the skimage function
  120         if not np.issubdtype(image.dtype, np.float64):
  121             warnings.warn("Input data has been cast to float64.", SunpyUserWarning)
  122             adjusted_image = image.astype(np.float64)
  123         else:
  124             adjusted_image = image.copy()
  125         if np.any(np.isnan(adjusted_image)) and order >= 4:
  126             warnings.warn("Setting NaNs to 0 for higher-order scikit-image rotation.", SunpyUserWarning)
  127             adjusted_image = np.nan_to_num(adjusted_image)
  128 
  129         rotated_image = skimage.transform.warp(adjusted_image, tform, order=order,
  130                                                mode='constant', cval=missing)
  131 
  132     return rotated_image
  133
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/sunpy/image/transform.py b/sunpy/image/transform.py
  --- a/sunpy/image/transform.py
  +++ b/sunpy/image/transform.py
  @@ -8,15 +8,6 @@
   
   from sunpy.util.exceptions import SunpyUserWarning
   
  -try:
  -    import skimage.transform
  -    scikit_image_not_found = False
  -except ImportError:
  -    warnings.warn("scikit-image could not be imported. Image rotation will use scipy",
  -                  ImportWarning)
  -    scikit_image_not_found = True
  -
  -
   __all__ = ['affine_transform']
   
   
  @@ -84,7 +75,7 @@
       algorithm to map the original to target pixel values.
       """
       rmatrix = rmatrix / scale
  -    array_center = (np.array(image.shape)[::-1]-1)/2.0
  +    array_center = (np.array(image.shape)[::-1] - 1) / 2.0
   
       # Make sure the image center is an array and is where it's supposed to be
       if image_center is not None:
  @@ -100,14 +91,20 @@
   
       displacement = np.dot(rmatrix, rot_center)
       shift = image_center - displacement
  -
  -    if use_scipy or scikit_image_not_found:
  +    if not use_scipy:
  +        try:
  +            import skimage.transform
  +        except ImportError:
  +            warnings.warn("scikit-image could not be imported. Image rotation will use scipy",
  +                          ImportWarning)
  +            use_scipy = True
  +    if use_scipy:
           if np.any(np.isnan(image)):
               warnings.warn("Setting NaNs to 0 for SciPy rotation.", SunpyUserWarning)
           # Transform the image using the scipy affine transform
           rotated_image = scipy.ndimage.interpolation.affine_transform(
  -                np.nan_to_num(image).T, rmatrix, offset=shift, order=order,
  -                mode='constant', cval=missing).T
  +            np.nan_to_num(image).T, rmatrix, offset=shift, order=order,
  +            mode='constant', cval=missing).T
       else:
           # Make the rotation matrix 3x3 to include translation of the image
           skmatrix = np.zeros((3, 3))
  
verification_info: {"golden_diff": "diff --git a/sunpy/image/transform.py b/sunpy/image/transform.py\n--- a/sunpy/image/transform.py\n+++ b/sunpy/image/transform.py\n@@ -8,15 +8,6 @@\n \n from sunpy.util.exceptions import SunpyUserWarning\n \n-try:\n-    import skimage.transform\n-    scikit_image_not_found = False\n-except ImportError:\n-    warnings.warn(\"scikit-image could not be imported. Image rotation will use scipy\",\n-                  ImportWarning)\n-    scikit_image_not_found = True\n-\n-\n __all__ = ['affine_transform']\n \n \n@@ -84,7 +75,7 @@\n     algorithm to map the original to target pixel values.\n     \"\"\"\n     rmatrix = rmatrix / scale\n-    array_center = (np.array(image.shape)[::-1]-1)/2.0\n+    array_center = (np.array(image.shape)[::-1] - 1) / 2.0\n \n     # Make sure the image center is an array and is where it's supposed to be\n     if image_center is not None:\n@@ -100,14 +91,20 @@\n \n     displacement = np.dot(rmatrix, rot_center)\n     shift = image_center - displacement\n-\n-    if use_scipy or scikit_image_not_found:\n+    if not use_scipy:\n+        try:\n+            import skimage.transform\n+        except ImportError:\n+            warnings.warn(\"scikit-image could not be imported. Image rotation will use scipy\",\n+                          ImportWarning)\n+            use_scipy = True\n+    if use_scipy:\n         if np.any(np.isnan(image)):\n             warnings.warn(\"Setting NaNs to 0 for SciPy rotation.\", SunpyUserWarning)\n         # Transform the image using the scipy affine transform\n         rotated_image = scipy.ndimage.interpolation.affine_transform(\n-                np.nan_to_num(image).T, rmatrix, offset=shift, order=order,\n-                mode='constant', cval=missing).T\n+            np.nan_to_num(image).T, rmatrix, offset=shift, order=order,\n+            mode='constant', cval=missing).T\n     else:\n         # Make the rotation matrix 3x3 to include translation of the image\n         skmatrix = np.zeros((3, 3))\n", "issue": "Reduce import time for `sunpy.image.transform`\n(I was getting annoyed by our import times, and was inspired by astropy/astropy#4598 to look deeper.)\r\n\r\nThis one's easy.  Importing `sunpy.image.transform` takes 1.7 seconds on my machine (see below).  0.7 seconds is spent importing `skimage.transform`.  We should defer that import to run-time.\r\n\r\n(see #3445 for dealing with `pkg_resources`)\r\n\r\n```\r\npython -X importtime -c \"import sunpy.image.transform\" 2> transform.log\r\ntuna transform.log\r\n```\r\n![transform](https://user-images.githubusercontent.com/991759/67576249-34433a00-f70c-11e9-9cef-4f7b3804e66c.png)\n", "before_files": [{"content": "\"\"\"\nFunctions for geometrical image transformation and warping.\n\"\"\"\nimport warnings\n\nimport numpy as np\nimport scipy.ndimage.interpolation\n\nfrom sunpy.util.exceptions import SunpyUserWarning\n\ntry:\n    import skimage.transform\n    scikit_image_not_found = False\nexcept ImportError:\n    warnings.warn(\"scikit-image could not be imported. Image rotation will use scipy\",\n                  ImportWarning)\n    scikit_image_not_found = True\n\n\n__all__ = ['affine_transform']\n\n\ndef affine_transform(image, rmatrix, order=3, scale=1.0, image_center=None,\n                     recenter=False, missing=0.0, use_scipy=False):\n    \"\"\"\n    Rotates, shifts and scales an image.\n\n    Will use `skimage.transform.warp` unless scikit-image can't be imported\n    then it will use`scipy.ndimage.interpolation.affine_transform`.\n\n    Parameters\n    ----------\n    image : `numpy.ndarray`\n        2D image to be rotated.\n    rmatrix : `numpy.ndarray` that is 2x2\n        Linear transformation rotation matrix.\n    order : `int` 0-5, optional\n        Interpolation order to be used, defaults to 3. When using scikit-image this parameter\n        is passed into `skimage.transform.warp` (e.g., 3 corresponds to bi-cubic interpolation).\n        When using scipy it is passed into\n        `scipy.ndimage.interpolation.affine_transform` where it controls the order of the spline.\n    scale : `float`\n        A scale factor for the image with the default being no scaling.\n    image_center : tuple, optional\n        The point in the image to rotate around (axis of rotation).\n        Defaults to the center of the array.\n    recenter : `bool` or array-like, optional\n        Move the axis of rotation to the center of the array or recenter coords.\n        Defaults to `True` i.e., recenter to the center of the array.\n    missing : `float`, optional\n        The value to replace any missing data after the transformation.\n    use_scipy : `bool`, optional\n        Force use of `scipy.ndimage.interpolation.affine_transform`.\n        Will set all \"NaNs\" in image to zero before doing the transform.\n        Defaults to `False`, unless scikit-image can't be imported.\n\n    Returns\n    -------\n    `numpy.ndarray`:\n        New rotated, scaled and translated image.\n\n    Notes\n    -----\n    This algorithm uses an affine transformation as opposed to a polynomial\n    geometrical transformation, which by default is `skimage.transform.warp`.\n    One can specify using `scipy.ndimage.interpolation.affine_transform` as\n    an alternative affine transformation. The two transformations use different\n    algorithms and thus do not give identical output.\n\n    When using for `skimage.transform.warp` with order >= 4 or using\n    `scipy.ndimage.interpolation.affine_transform` at all, \"NaN\" values will\n    replaced with zero prior to rotation. No attempt is made to retain the NaN\n    values.\n\n    Input arrays with integer data are cast to float 64 and can be re-cast using\n    `numpy.ndarray.astype` if desired.\n\n    Although this function is analogous to the IDL's ``rot`` function, it does not\n    use the same algorithm as the IDL ``rot`` function.\n    IDL's ``rot`` calls the `POLY_2D <https://www.harrisgeospatial.com/docs/poly_2d.html>`__\n    method to calculate the inverse mapping of original to target pixel\n    coordinates. This is a polynomial geometrical transformation.\n    Then optionally it uses a bicubic convolution interpolation\n    algorithm to map the original to target pixel values.\n    \"\"\"\n    rmatrix = rmatrix / scale\n    array_center = (np.array(image.shape)[::-1]-1)/2.0\n\n    # Make sure the image center is an array and is where it's supposed to be\n    if image_center is not None:\n        image_center = np.asanyarray(image_center)\n    else:\n        image_center = array_center\n\n    # Determine center of rotation based on use (or not) of the recenter keyword\n    if recenter:\n        rot_center = array_center\n    else:\n        rot_center = image_center\n\n    displacement = np.dot(rmatrix, rot_center)\n    shift = image_center - displacement\n\n    if use_scipy or scikit_image_not_found:\n        if np.any(np.isnan(image)):\n            warnings.warn(\"Setting NaNs to 0 for SciPy rotation.\", SunpyUserWarning)\n        # Transform the image using the scipy affine transform\n        rotated_image = scipy.ndimage.interpolation.affine_transform(\n                np.nan_to_num(image).T, rmatrix, offset=shift, order=order,\n                mode='constant', cval=missing).T\n    else:\n        # Make the rotation matrix 3x3 to include translation of the image\n        skmatrix = np.zeros((3, 3))\n        skmatrix[:2, :2] = rmatrix\n        skmatrix[2, 2] = 1.0\n        skmatrix[:2, 2] = shift\n        tform = skimage.transform.AffineTransform(skmatrix)\n\n        # Transform the image using the skimage function\n        if not np.issubdtype(image.dtype, np.float64):\n            warnings.warn(\"Input data has been cast to float64.\", SunpyUserWarning)\n            adjusted_image = image.astype(np.float64)\n        else:\n            adjusted_image = image.copy()\n        if np.any(np.isnan(adjusted_image)) and order >= 4:\n            warnings.warn(\"Setting NaNs to 0 for higher-order scikit-image rotation.\", SunpyUserWarning)\n            adjusted_image = np.nan_to_num(adjusted_image)\n\n        rotated_image = skimage.transform.warp(adjusted_image, tform, order=order,\n                                               mode='constant', cval=missing)\n\n    return rotated_image\n", "path": "sunpy/image/transform.py"}], "after_files": [{"content": "\"\"\"\nFunctions for geometrical image transformation and warping.\n\"\"\"\nimport warnings\n\nimport numpy as np\nimport scipy.ndimage.interpolation\n\nfrom sunpy.util.exceptions import SunpyUserWarning\n\n__all__ = ['affine_transform']\n\n\ndef affine_transform(image, rmatrix, order=3, scale=1.0, image_center=None,\n                     recenter=False, missing=0.0, use_scipy=False):\n    \"\"\"\n    Rotates, shifts and scales an image.\n\n    Will use `skimage.transform.warp` unless scikit-image can't be imported\n    then it will use`scipy.ndimage.interpolation.affine_transform`.\n\n    Parameters\n    ----------\n    image : `numpy.ndarray`\n        2D image to be rotated.\n    rmatrix : `numpy.ndarray` that is 2x2\n        Linear transformation rotation matrix.\n    order : `int` 0-5, optional\n        Interpolation order to be used, defaults to 3. When using scikit-image this parameter\n        is passed into `skimage.transform.warp` (e.g., 3 corresponds to bi-cubic interpolation).\n        When using scipy it is passed into\n        `scipy.ndimage.interpolation.affine_transform` where it controls the order of the spline.\n    scale : `float`\n        A scale factor for the image with the default being no scaling.\n    image_center : tuple, optional\n        The point in the image to rotate around (axis of rotation).\n        Defaults to the center of the array.\n    recenter : `bool` or array-like, optional\n        Move the axis of rotation to the center of the array or recenter coords.\n        Defaults to `True` i.e., recenter to the center of the array.\n    missing : `float`, optional\n        The value to replace any missing data after the transformation.\n    use_scipy : `bool`, optional\n        Force use of `scipy.ndimage.interpolation.affine_transform`.\n        Will set all \"NaNs\" in image to zero before doing the transform.\n        Defaults to `False`, unless scikit-image can't be imported.\n\n    Returns\n    -------\n    `numpy.ndarray`:\n        New rotated, scaled and translated image.\n\n    Notes\n    -----\n    This algorithm uses an affine transformation as opposed to a polynomial\n    geometrical transformation, which by default is `skimage.transform.warp`.\n    One can specify using `scipy.ndimage.interpolation.affine_transform` as\n    an alternative affine transformation. The two transformations use different\n    algorithms and thus do not give identical output.\n\n    When using for `skimage.transform.warp` with order >= 4 or using\n    `scipy.ndimage.interpolation.affine_transform` at all, \"NaN\" values will\n    replaced with zero prior to rotation. No attempt is made to retain the NaN\n    values.\n\n    Input arrays with integer data are cast to float 64 and can be re-cast using\n    `numpy.ndarray.astype` if desired.\n\n    Although this function is analogous to the IDL's ``rot`` function, it does not\n    use the same algorithm as the IDL ``rot`` function.\n    IDL's ``rot`` calls the `POLY_2D <https://www.harrisgeospatial.com/docs/poly_2d.html>`__\n    method to calculate the inverse mapping of original to target pixel\n    coordinates. This is a polynomial geometrical transformation.\n    Then optionally it uses a bicubic convolution interpolation\n    algorithm to map the original to target pixel values.\n    \"\"\"\n    rmatrix = rmatrix / scale\n    array_center = (np.array(image.shape)[::-1] - 1) / 2.0\n\n    # Make sure the image center is an array and is where it's supposed to be\n    if image_center is not None:\n        image_center = np.asanyarray(image_center)\n    else:\n        image_center = array_center\n\n    # Determine center of rotation based on use (or not) of the recenter keyword\n    if recenter:\n        rot_center = array_center\n    else:\n        rot_center = image_center\n\n    displacement = np.dot(rmatrix, rot_center)\n    shift = image_center - displacement\n    if not use_scipy:\n        try:\n            import skimage.transform\n        except ImportError:\n            warnings.warn(\"scikit-image could not be imported. Image rotation will use scipy\",\n                          ImportWarning)\n            use_scipy = True\n    if use_scipy:\n        if np.any(np.isnan(image)):\n            warnings.warn(\"Setting NaNs to 0 for SciPy rotation.\", SunpyUserWarning)\n        # Transform the image using the scipy affine transform\n        rotated_image = scipy.ndimage.interpolation.affine_transform(\n            np.nan_to_num(image).T, rmatrix, offset=shift, order=order,\n            mode='constant', cval=missing).T\n    else:\n        # Make the rotation matrix 3x3 to include translation of the image\n        skmatrix = np.zeros((3, 3))\n        skmatrix[:2, :2] = rmatrix\n        skmatrix[2, 2] = 1.0\n        skmatrix[:2, 2] = shift\n        tform = skimage.transform.AffineTransform(skmatrix)\n\n        # Transform the image using the skimage function\n        if not np.issubdtype(image.dtype, np.float64):\n            warnings.warn(\"Input data has been cast to float64.\", SunpyUserWarning)\n            adjusted_image = image.astype(np.float64)\n        else:\n            adjusted_image = image.copy()\n        if np.any(np.isnan(adjusted_image)) and order >= 4:\n            warnings.warn(\"Setting NaNs to 0 for higher-order scikit-image rotation.\", SunpyUserWarning)\n            adjusted_image = np.nan_to_num(adjusted_image)\n\n        rotated_image = skimage.transform.warp(adjusted_image, tform, order=order,\n                                               mode='constant', cval=missing)\n\n    return rotated_image\n", "path": "sunpy/image/transform.py"}]}