We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
skimage.novice not handling alpha values (or maybe something worse)
We'd like to use `skimage.novice` to teach image manipulation in Software Carpentry, but it looks like `skimage.novice` isn't handling alpha values correctly (though the problem may be deeper).

Test image is a shrunken survey map of Antarctic:

![ant](https://cloud.githubusercontent.com/assets/911566/5956124/ec8f23c4-a777-11e4-8d15-d32ffa153dbe.jpg)

Step 1: load and display using ipython 2.2.0 via conda 3.7.3 on Mac OS X 10.10.1:

```
In [1]: from skimage import novice
In [2]: p1 = novice.open('ant.jpg')
In [3]: p1.show()
```

![screen shot 2015-01-29 at 5 23 22 am](https://cloud.githubusercontent.com/assets/911566/5956100/b6e1caa6-a777-11e4-8ddc-cd4d15a7410f.png)

That's not right...

Step 2: paint the lower left green:

```
In [4]: p1.size
Out[4]: (120, 121)
In [5]: p1[0:60, 0:60] = (0, 255, 0)
In [6]: p1.show()
```

![screen shot 2015-01-29 at 5 24 02 am](https://cloud.githubusercontent.com/assets/911566/5956102/be1fda60-a777-11e4-8bcb-9a7f22ab8cb3.png)

Looks like alpha blending is going on.

Step 3: create a new blank white canvas:

```
In [7]: p2 = novice.Picture.from_size((200, 200), (255, 255, 255))
In [8]: p2.show()
```

![screen shot 2015-01-29 at 5 24 32 am](https://cloud.githubusercontent.com/assets/911566/5956106/c9872b2e-a777-11e4-8c7b-26ecb18a2c88.png)

That looks OK, but now color part of it green:

```
In [9]: p2[0:60, 0:60] = (0, 255, 0)
In [10]: p2.show()
```

![screen shot 2015-01-29 at 5 24 54 am](https://cloud.githubusercontent.com/assets/911566/5956119/e4e3828c-a777-11e4-8a53-f276d4dde3bf.png)

and the green doesn't show up at all.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `skimage/io/_plugins/pil_plugin.py`
Content:
```
1 __all__ = ['imread', 'imsave']
2 
3 import numpy as np
4 from six import string_types
5 from PIL import Image
6 
7 from ...util import img_as_ubyte, img_as_uint
8 from ...external.tifffile import imread as tif_imread, imsave as tif_imsave
9 
10 
11 def imread(fname, dtype=None, img_num=None, **kwargs):
12     """Load an image from file.
13 
14     Parameters
15     ----------
16     fname : str
17        File name.
18     dtype : numpy dtype object or string specifier
19        Specifies data type of array elements.
20     img_num : int, optional
21        Specifies which image to read in a file with multiple images
22        (zero-indexed).
23     kwargs : keyword pairs, optional
24         Addition keyword arguments to pass through (only applicable to Tiff
25         files for now,  see `tifffile`'s `imread` function).
26 
27     Notes
28     -----
29     Tiff files are handled by Christophe Golhke's tifffile.py [1]_, and support many
30     advanced image types including multi-page and floating point.
31 
32     All other files are read using the Python Imaging Libary.
33     See PIL docs [2]_ for a list of supported formats.
34 
35     References
36     ----------
37     .. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html
38     .. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
39 
40     """
41     if hasattr(fname, 'lower') and dtype is None:
42         kwargs.setdefault('key', img_num)
43         if fname.lower().endswith(('.tiff', '.tif')):
44             return tif_imread(fname, **kwargs)
45 
46     im = Image.open(fname)
47     try:
48         # this will raise an IOError if the file is not readable
49         im.getdata()[0]
50     except IOError:
51         site = "http://pillow.readthedocs.org/en/latest/installation.html#external-libraries"
52         raise ValueError('Could not load "%s"\nPlease see documentation at: %s' % (fname, site))
53     else:
54         return pil_to_ndarray(im, dtype=dtype, img_num=img_num)
55 
56 
57 def pil_to_ndarray(im, dtype=None, img_num=None):
58     """Import a PIL Image object to an ndarray, in memory.
59 
60     Parameters
61     ----------
62     Refer to ``imread``.
63 
64     """
65     frames = []
66     grayscale = None
67     i = 0
68     while 1:
69         try:
70             im.seek(i)
71         except EOFError:
72             break
73 
74         frame = im
75 
76         if not img_num is None and img_num != i:
77             im.getdata()[0]
78             i += 1
79             continue
80 
81         if im.mode == 'P':
82             if grayscale is None:
83                 grayscale = _palette_is_grayscale(im)
84 
85             if grayscale:
86                 frame = im.convert('L')
87             else:
88                 frame = im.convert('RGB')
89 
90         elif im.mode == '1':
91             frame = im.convert('L')
92 
93         elif 'A' in im.mode:
94             frame = im.convert('RGBA')
95 
96 
97         if im.mode.startswith('I;16'):
98             shape = im.size
99             dtype = '>u2' if im.mode.endswith('B') else '<u2'
100             if 'S' in im.mode:
101                 dtype = dtype.replace('u', 'i')
102             frame = np.fromstring(frame.tobytes(), dtype)
103             frame.shape = shape[::-1]
104 
105         else:
106             frame = np.array(frame, dtype=dtype)
107 
108         frames.append(frame)
109         i += 1
110 
111     if hasattr(im, 'fp') and im.fp:
112         im.fp.close()
113 
114     if img_num is None and len(frames) > 1:
115         return np.array(frames)
116     elif frames:
117         return frames[0]
118     elif img_num:
119         raise IndexError('Could not find image  #%s' % img_num)
120 
121 
122 def _palette_is_grayscale(pil_image):
123     """Return True if PIL image in palette mode is grayscale.
124 
125     Parameters
126     ----------
127     pil_image : PIL image
128         PIL Image that is in Palette mode.
129 
130     Returns
131     -------
132     is_grayscale : bool
133         True if all colors in image palette are gray.
134     """
135     assert pil_image.mode == 'P'
136     # get palette as an array with R, G, B columns
137     palette = np.asarray(pil_image.getpalette()).reshape((256, 3))
138     # Not all palette colors are used; unused colors have junk values.
139     start, stop = pil_image.getextrema()
140     valid_palette = palette[start:stop]
141     # Image is grayscale if channel differences (R - G and G - B)
142     # are all zero.
143     return np.allclose(np.diff(valid_palette), 0)
144 
145 
146 def ndarray_to_pil(arr, format_str=None):
147     """Export an ndarray to a PIL object.
148 
149     Parameters
150     ----------
151     Refer to ``imsave``.
152 
153     """
154     if arr.ndim == 3:
155         arr = img_as_ubyte(arr)
156         mode = {3: 'RGB', 4: 'RGBA'}[arr.shape[2]]
157 
158     elif format_str in ['png', 'PNG']:
159         mode = 'I;16'
160         mode_base = 'I'
161 
162         if arr.dtype.kind == 'f':
163             arr = img_as_uint(arr)
164 
165         elif arr.max() < 256 and arr.min() >= 0:
166             arr = arr.astype(np.uint8)
167             mode = mode_base = 'L'
168 
169         else:
170             arr = img_as_uint(arr)
171 
172     else:
173         arr = img_as_ubyte(arr)
174         mode = 'L'
175         mode_base = 'L'
176 
177     if arr.ndim == 2:
178         im = Image.new(mode_base, arr.T.shape)
179         try:
180             im.frombytes(arr.tobytes(), 'raw', mode)
181         except AttributeError:
182             im.frombytes(arr.tostring(), 'raw', mode)
183 
184     else:
185         try:
186             im = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),
187                                  arr.tobytes())
188         except AttributeError:
189             im = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),
190                                   arr.tostring())
191     return im
192 
193 
194 def imsave(fname, arr, format_str=None):
195     """Save an image to disk.
196 
197     Parameters
198     ----------
199     fname : str or file-like object
200         Name of destination file.
201     arr : ndarray of uint8 or float
202         Array (image) to save.  Arrays of data-type uint8 should have
203         values in [0, 255], whereas floating-point arrays must be
204         in [0, 1].
205     format_str: str
206         Format to save as, this is defaulted to PNG if using a file-like
207         object; this will be derived from the extension if fname is a string
208 
209     Notes
210     -----
211     Tiff files are handled by Christophe Golhke's tifffile.py [1]_,
212     and support many advanced image types including multi-page and
213     floating point.
214 
215     All other image formats use the Python Imaging Libary.
216     See PIL docs [2]_ for a list of other supported formats.
217     All images besides single channel PNGs are converted using `img_as_uint8`.
218     Single Channel PNGs have the following behavior:
219     - Integer values in [0, 255] and Boolean types -> img_as_uint8
220     - Floating point and other integers -> img_as_uint16
221 
222     References
223     ----------
224     .. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html
225     .. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
226     """
227     # default to PNG if file-like object
228     if not isinstance(fname, string_types) and format_str is None:
229         format_str = "PNG"
230     # Check for png in filename
231     if (isinstance(fname, string_types)
232             and fname.lower().endswith(".png")):
233         format_str = "PNG"
234 
235     arr = np.asanyarray(arr).squeeze()
236 
237     if arr.dtype.kind == 'b':
238         arr = arr.astype(np.uint8)
239 
240     use_tif = False
241     if hasattr(fname, 'lower'):
242         if fname.lower().endswith(('.tiff', '.tif')):
243             use_tif = True
244     if not format_str is None:
245         if format_str.lower() in ['tiff', 'tif']:
246             use_tif = True
247 
248     if use_tif:
249         tif_imsave(fname, arr)
250         return
251 
252     if arr.ndim not in (2, 3):
253         raise ValueError("Invalid shape for image array: %s" % arr.shape)
254 
255     if arr.ndim == 3:
256         if arr.shape[2] not in (3, 4):
257             raise ValueError("Invalid number of channels in image array.")
258 
259     img = ndarray_to_pil(arr, format_str=format_str)
260     img.save(fname, format=format_str)
261 
262 
263 def imshow(arr):
264     """Display an image, using PIL's default display command.
265 
266     Parameters
267     ----------
268     arr : ndarray
269        Image to display.  Images of dtype float are assumed to be in
270        [0, 1].  Images of dtype uint8 are in [0, 255].
271 
272     """
273     Image.fromarray(img_as_ubyte(arr)).show()
274 
275 
276 def _app_show():
277     pass
278
```
--- 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()

```

diff --git a/skimage/io/_plugins/pil_plugin.py b/skimage/io/_plugins/pil_plugin.py
--- a/skimage/io/_plugins/pil_plugin.py
+++ b/skimage/io/_plugins/pil_plugin.py
@@ -73,7 +73,7 @@
 
         frame = im
 
-        if not img_num is None and img_num != i:
+        if img_num is not None and img_num != i:
             im.getdata()[0]
             i += 1
             continue
@@ -93,6 +93,8 @@
         elif 'A' in im.mode:
             frame = im.convert('RGBA')
 
+        elif im.mode == 'CMYK':
+            frame = im.convert('RGB')
 
         if im.mode.startswith('I;16'):
             shape = im.size

{"golden_diff": "diff --git a/skimage/io/_plugins/pil_plugin.py b/skimage/io/_plugins/pil_plugin.py\n--- a/skimage/io/_plugins/pil_plugin.py\n+++ b/skimage/io/_plugins/pil_plugin.py\n@@ -73,7 +73,7 @@\n \n         frame = im\n \n-        if not img_num is None and img_num != i:\n+        if img_num is not None and img_num != i:\n             im.getdata()[0]\n             i += 1\n             continue\n@@ -93,6 +93,8 @@\n         elif 'A' in im.mode:\n             frame = im.convert('RGBA')\n \n+        elif im.mode == 'CMYK':\n+            frame = im.convert('RGB')\n \n         if im.mode.startswith('I;16'):\n             shape = im.size\n", "issue": "skimage.novice not handling alpha values (or maybe something worse)\nWe'd like to use `skimage.novice` to teach image manipulation in Software Carpentry, but it looks like `skimage.novice` isn't handling alpha values correctly (though the problem may be deeper).\n\nTest image is a shrunken survey map of Antarctic:\n\n![ant](https://cloud.githubusercontent.com/assets/911566/5956124/ec8f23c4-a777-11e4-8d15-d32ffa153dbe.jpg)\n\nStep 1: load and display using ipython 2.2.0 via conda 3.7.3 on Mac OS X 10.10.1:\n\n```\nIn [1]: from skimage import novice\nIn [2]: p1 = novice.open('ant.jpg')\nIn [3]: p1.show()\n```\n\n![screen shot 2015-01-29 at 5 23 22 am](https://cloud.githubusercontent.com/assets/911566/5956100/b6e1caa6-a777-11e4-8ddc-cd4d15a7410f.png)\n\nThat's not right...\n\nStep 2: paint the lower left green:\n\n```\nIn [4]: p1.size\nOut[4]: (120, 121)\nIn [5]: p1[0:60, 0:60] = (0, 255, 0)\nIn [6]: p1.show()\n```\n\n![screen shot 2015-01-29 at 5 24 02 am](https://cloud.githubusercontent.com/assets/911566/5956102/be1fda60-a777-11e4-8bcb-9a7f22ab8cb3.png)\n\nLooks like alpha blending is going on.\n\nStep 3: create a new blank white canvas:\n\n```\nIn [7]: p2 = novice.Picture.from_size((200, 200), (255, 255, 255))\nIn [8]: p2.show()\n```\n\n![screen shot 2015-01-29 at 5 24 32 am](https://cloud.githubusercontent.com/assets/911566/5956106/c9872b2e-a777-11e4-8c7b-26ecb18a2c88.png)\n\nThat looks OK, but now color part of it green:\n\n```\nIn [9]: p2[0:60, 0:60] = (0, 255, 0)\nIn [10]: p2.show()\n```\n\n![screen shot 2015-01-29 at 5 24 54 am](https://cloud.githubusercontent.com/assets/911566/5956119/e4e3828c-a777-11e4-8a53-f276d4dde3bf.png)\n\nand the green doesn't show up at all.\n\n", "before_files": [{"content": "__all__ = ['imread', 'imsave']\n\nimport numpy as np\nfrom six import string_types\nfrom PIL import Image\n\nfrom ...util import img_as_ubyte, img_as_uint\nfrom ...external.tifffile import imread as tif_imread, imsave as tif_imsave\n\n\ndef imread(fname, dtype=None, img_num=None, **kwargs):\n    \"\"\"Load an image from file.\n\n    Parameters\n    ----------\n    fname : str\n       File name.\n    dtype : numpy dtype object or string specifier\n       Specifies data type of array elements.\n    img_num : int, optional\n       Specifies which image to read in a file with multiple images\n       (zero-indexed).\n    kwargs : keyword pairs, optional\n        Addition keyword arguments to pass through (only applicable to Tiff\n        files for now,  see `tifffile`'s `imread` function).\n\n    Notes\n    -----\n    Tiff files are handled by Christophe Golhke's tifffile.py [1]_, and support many\n    advanced image types including multi-page and floating point.\n\n    All other files are read using the Python Imaging Libary.\n    See PIL docs [2]_ for a list of supported formats.\n\n    References\n    ----------\n    .. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html\n    .. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html\n\n    \"\"\"\n    if hasattr(fname, 'lower') and dtype is None:\n        kwargs.setdefault('key', img_num)\n        if fname.lower().endswith(('.tiff', '.tif')):\n            return tif_imread(fname, **kwargs)\n\n    im = Image.open(fname)\n    try:\n        # this will raise an IOError if the file is not readable\n        im.getdata()[0]\n    except IOError:\n        site = \"http://pillow.readthedocs.org/en/latest/installation.html#external-libraries\"\n        raise ValueError('Could not load \"%s\"\\nPlease see documentation at: %s' % (fname, site))\n    else:\n        return pil_to_ndarray(im, dtype=dtype, img_num=img_num)\n\n\ndef pil_to_ndarray(im, dtype=None, img_num=None):\n    \"\"\"Import a PIL Image object to an ndarray, in memory.\n\n    Parameters\n    ----------\n    Refer to ``imread``.\n\n    \"\"\"\n    frames = []\n    grayscale = None\n    i = 0\n    while 1:\n        try:\n            im.seek(i)\n        except EOFError:\n            break\n\n        frame = im\n\n        if not img_num is None and img_num != i:\n            im.getdata()[0]\n            i += 1\n            continue\n\n        if im.mode == 'P':\n            if grayscale is None:\n                grayscale = _palette_is_grayscale(im)\n\n            if grayscale:\n                frame = im.convert('L')\n            else:\n                frame = im.convert('RGB')\n\n        elif im.mode == '1':\n            frame = im.convert('L')\n\n        elif 'A' in im.mode:\n            frame = im.convert('RGBA')\n\n\n        if im.mode.startswith('I;16'):\n            shape = im.size\n            dtype = '>u2' if im.mode.endswith('B') else '<u2'\n            if 'S' in im.mode:\n                dtype = dtype.replace('u', 'i')\n            frame = np.fromstring(frame.tobytes(), dtype)\n            frame.shape = shape[::-1]\n\n        else:\n            frame = np.array(frame, dtype=dtype)\n\n        frames.append(frame)\n        i += 1\n\n    if hasattr(im, 'fp') and im.fp:\n        im.fp.close()\n\n    if img_num is None and len(frames) > 1:\n        return np.array(frames)\n    elif frames:\n        return frames[0]\n    elif img_num:\n        raise IndexError('Could not find image  #%s' % img_num)\n\n\ndef _palette_is_grayscale(pil_image):\n    \"\"\"Return True if PIL image in palette mode is grayscale.\n\n    Parameters\n    ----------\n    pil_image : PIL image\n        PIL Image that is in Palette mode.\n\n    Returns\n    -------\n    is_grayscale : bool\n        True if all colors in image palette are gray.\n    \"\"\"\n    assert pil_image.mode == 'P'\n    # get palette as an array with R, G, B columns\n    palette = np.asarray(pil_image.getpalette()).reshape((256, 3))\n    # Not all palette colors are used; unused colors have junk values.\n    start, stop = pil_image.getextrema()\n    valid_palette = palette[start:stop]\n    # Image is grayscale if channel differences (R - G and G - B)\n    # are all zero.\n    return np.allclose(np.diff(valid_palette), 0)\n\n\ndef ndarray_to_pil(arr, format_str=None):\n    \"\"\"Export an ndarray to a PIL object.\n\n    Parameters\n    ----------\n    Refer to ``imsave``.\n\n    \"\"\"\n    if arr.ndim == 3:\n        arr = img_as_ubyte(arr)\n        mode = {3: 'RGB', 4: 'RGBA'}[arr.shape[2]]\n\n    elif format_str in ['png', 'PNG']:\n        mode = 'I;16'\n        mode_base = 'I'\n\n        if arr.dtype.kind == 'f':\n            arr = img_as_uint(arr)\n\n        elif arr.max() < 256 and arr.min() >= 0:\n            arr = arr.astype(np.uint8)\n            mode = mode_base = 'L'\n\n        else:\n            arr = img_as_uint(arr)\n\n    else:\n        arr = img_as_ubyte(arr)\n        mode = 'L'\n        mode_base = 'L'\n\n    if arr.ndim == 2:\n        im = Image.new(mode_base, arr.T.shape)\n        try:\n            im.frombytes(arr.tobytes(), 'raw', mode)\n        except AttributeError:\n            im.frombytes(arr.tostring(), 'raw', mode)\n\n    else:\n        try:\n            im = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),\n                                 arr.tobytes())\n        except AttributeError:\n            im = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),\n                                  arr.tostring())\n    return im\n\n\ndef imsave(fname, arr, format_str=None):\n    \"\"\"Save an image to disk.\n\n    Parameters\n    ----------\n    fname : str or file-like object\n        Name of destination file.\n    arr : ndarray of uint8 or float\n        Array (image) to save.  Arrays of data-type uint8 should have\n        values in [0, 255], whereas floating-point arrays must be\n        in [0, 1].\n    format_str: str\n        Format to save as, this is defaulted to PNG if using a file-like\n        object; this will be derived from the extension if fname is a string\n\n    Notes\n    -----\n    Tiff files are handled by Christophe Golhke's tifffile.py [1]_,\n    and support many advanced image types including multi-page and\n    floating point.\n\n    All other image formats use the Python Imaging Libary.\n    See PIL docs [2]_ for a list of other supported formats.\n    All images besides single channel PNGs are converted using `img_as_uint8`.\n    Single Channel PNGs have the following behavior:\n    - Integer values in [0, 255] and Boolean types -> img_as_uint8\n    - Floating point and other integers -> img_as_uint16\n\n    References\n    ----------\n    .. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html\n    .. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html\n    \"\"\"\n    # default to PNG if file-like object\n    if not isinstance(fname, string_types) and format_str is None:\n        format_str = \"PNG\"\n    # Check for png in filename\n    if (isinstance(fname, string_types)\n            and fname.lower().endswith(\".png\")):\n        format_str = \"PNG\"\n\n    arr = np.asanyarray(arr).squeeze()\n\n    if arr.dtype.kind == 'b':\n        arr = arr.astype(np.uint8)\n\n    use_tif = False\n    if hasattr(fname, 'lower'):\n        if fname.lower().endswith(('.tiff', '.tif')):\n            use_tif = True\n    if not format_str is None:\n        if format_str.lower() in ['tiff', 'tif']:\n            use_tif = True\n\n    if use_tif:\n        tif_imsave(fname, arr)\n        return\n\n    if arr.ndim not in (2, 3):\n        raise ValueError(\"Invalid shape for image array: %s\" % arr.shape)\n\n    if arr.ndim == 3:\n        if arr.shape[2] not in (3, 4):\n            raise ValueError(\"Invalid number of channels in image array.\")\n\n    img = ndarray_to_pil(arr, format_str=format_str)\n    img.save(fname, format=format_str)\n\n\ndef imshow(arr):\n    \"\"\"Display an image, using PIL's default display command.\n\n    Parameters\n    ----------\n    arr : ndarray\n       Image to display.  Images of dtype float are assumed to be in\n       [0, 1].  Images of dtype uint8 are in [0, 255].\n\n    \"\"\"\n    Image.fromarray(img_as_ubyte(arr)).show()\n\n\ndef _app_show():\n    pass\n", "path": "skimage/io/_plugins/pil_plugin.py"}], "after_files": [{"content": "__all__ = ['imread', 'imsave']\n\nimport numpy as np\nfrom six import string_types\nfrom PIL import Image\n\nfrom ...util import img_as_ubyte, img_as_uint\nfrom ...external.tifffile import imread as tif_imread, imsave as tif_imsave\n\n\ndef imread(fname, dtype=None, img_num=None, **kwargs):\n    \"\"\"Load an image from file.\n\n    Parameters\n    ----------\n    fname : str\n       File name.\n    dtype : numpy dtype object or string specifier\n       Specifies data type of array elements.\n    img_num : int, optional\n       Specifies which image to read in a file with multiple images\n       (zero-indexed).\n    kwargs : keyword pairs, optional\n        Addition keyword arguments to pass through (only applicable to Tiff\n        files for now,  see `tifffile`'s `imread` function).\n\n    Notes\n    -----\n    Tiff files are handled by Christophe Golhke's tifffile.py [1]_, and support many\n    advanced image types including multi-page and floating point.\n\n    All other files are read using the Python Imaging Libary.\n    See PIL docs [2]_ for a list of supported formats.\n\n    References\n    ----------\n    .. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html\n    .. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html\n\n    \"\"\"\n    if hasattr(fname, 'lower') and dtype is None:\n        kwargs.setdefault('key', img_num)\n        if fname.lower().endswith(('.tiff', '.tif')):\n            return tif_imread(fname, **kwargs)\n\n    im = Image.open(fname)\n    try:\n        # this will raise an IOError if the file is not readable\n        im.getdata()[0]\n    except IOError:\n        site = \"http://pillow.readthedocs.org/en/latest/installation.html#external-libraries\"\n        raise ValueError('Could not load \"%s\"\\nPlease see documentation at: %s' % (fname, site))\n    else:\n        return pil_to_ndarray(im, dtype=dtype, img_num=img_num)\n\n\ndef pil_to_ndarray(im, dtype=None, img_num=None):\n    \"\"\"Import a PIL Image object to an ndarray, in memory.\n\n    Parameters\n    ----------\n    Refer to ``imread``.\n\n    \"\"\"\n    frames = []\n    grayscale = None\n    i = 0\n    while 1:\n        try:\n            im.seek(i)\n        except EOFError:\n            break\n\n        frame = im\n\n        if img_num is not None and img_num != i:\n            im.getdata()[0]\n            i += 1\n            continue\n\n        if im.mode == 'P':\n            if grayscale is None:\n                grayscale = _palette_is_grayscale(im)\n\n            if grayscale:\n                frame = im.convert('L')\n            else:\n                frame = im.convert('RGB')\n\n        elif im.mode == '1':\n            frame = im.convert('L')\n\n        elif 'A' in im.mode:\n            frame = im.convert('RGBA')\n\n        elif im.mode == 'CMYK':\n            frame = im.convert('RGB')\n\n        if im.mode.startswith('I;16'):\n            shape = im.size\n            dtype = '>u2' if im.mode.endswith('B') else '<u2'\n            if 'S' in im.mode:\n                dtype = dtype.replace('u', 'i')\n            frame = np.fromstring(frame.tobytes(), dtype)\n            frame.shape = shape[::-1]\n\n        else:\n            frame = np.array(frame, dtype=dtype)\n\n        frames.append(frame)\n        i += 1\n\n    if hasattr(im, 'fp') and im.fp:\n        im.fp.close()\n\n    if img_num is None and len(frames) > 1:\n        return np.array(frames)\n    elif frames:\n        return frames[0]\n    elif img_num:\n        raise IndexError('Could not find image  #%s' % img_num)\n\n\ndef _palette_is_grayscale(pil_image):\n    \"\"\"Return True if PIL image in palette mode is grayscale.\n\n    Parameters\n    ----------\n    pil_image : PIL image\n        PIL Image that is in Palette mode.\n\n    Returns\n    -------\n    is_grayscale : bool\n        True if all colors in image palette are gray.\n    \"\"\"\n    assert pil_image.mode == 'P'\n    # get palette as an array with R, G, B columns\n    palette = np.asarray(pil_image.getpalette()).reshape((256, 3))\n    # Not all palette colors are used; unused colors have junk values.\n    start, stop = pil_image.getextrema()\n    valid_palette = palette[start:stop]\n    # Image is grayscale if channel differences (R - G and G - B)\n    # are all zero.\n    return np.allclose(np.diff(valid_palette), 0)\n\n\ndef ndarray_to_pil(arr, format_str=None):\n    \"\"\"Export an ndarray to a PIL object.\n\n    Parameters\n    ----------\n    Refer to ``imsave``.\n\n    \"\"\"\n    if arr.ndim == 3:\n        arr = img_as_ubyte(arr)\n        mode = {3: 'RGB', 4: 'RGBA'}[arr.shape[2]]\n\n    elif format_str in ['png', 'PNG']:\n        mode = 'I;16'\n        mode_base = 'I'\n\n        if arr.dtype.kind == 'f':\n            arr = img_as_uint(arr)\n\n        elif arr.max() < 256 and arr.min() >= 0:\n            arr = arr.astype(np.uint8)\n            mode = mode_base = 'L'\n\n        else:\n            arr = img_as_uint(arr)\n\n    else:\n        arr = img_as_ubyte(arr)\n        mode = 'L'\n        mode_base = 'L'\n\n    if arr.ndim == 2:\n        im = Image.new(mode_base, arr.T.shape)\n        try:\n            im.frombytes(arr.tobytes(), 'raw', mode)\n        except AttributeError:\n            im.frombytes(arr.tostring(), 'raw', mode)\n\n    else:\n        try:\n            im = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),\n                                 arr.tobytes())\n        except AttributeError:\n            im = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),\n                                  arr.tostring())\n    return im\n\n\ndef imsave(fname, arr, format_str=None):\n    \"\"\"Save an image to disk.\n\n    Parameters\n    ----------\n    fname : str or file-like object\n        Name of destination file.\n    arr : ndarray of uint8 or float\n        Array (image) to save.  Arrays of data-type uint8 should have\n        values in [0, 255], whereas floating-point arrays must be\n        in [0, 1].\n    format_str: str\n        Format to save as, this is defaulted to PNG if using a file-like\n        object; this will be derived from the extension if fname is a string\n\n    Notes\n    -----\n    Tiff files are handled by Christophe Golhke's tifffile.py [1]_,\n    and support many advanced image types including multi-page and\n    floating point.\n\n    All other image formats use the Python Imaging Libary.\n    See PIL docs [2]_ for a list of other supported formats.\n    All images besides single channel PNGs are converted using `img_as_uint8`.\n    Single Channel PNGs have the following behavior:\n    - Integer values in [0, 255] and Boolean types -> img_as_uint8\n    - Floating point and other integers -> img_as_uint16\n\n    References\n    ----------\n    .. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html\n    .. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html\n    \"\"\"\n    # default to PNG if file-like object\n    if not isinstance(fname, string_types) and format_str is None:\n        format_str = \"PNG\"\n    # Check for png in filename\n    if (isinstance(fname, string_types)\n            and fname.lower().endswith(\".png\")):\n        format_str = \"PNG\"\n\n    arr = np.asanyarray(arr).squeeze()\n\n    if arr.dtype.kind == 'b':\n        arr = arr.astype(np.uint8)\n\n    use_tif = False\n    if hasattr(fname, 'lower'):\n        if fname.lower().endswith(('.tiff', '.tif')):\n            use_tif = True\n    if not format_str is None:\n        if format_str.lower() in ['tiff', 'tif']:\n            use_tif = True\n\n    if use_tif:\n        tif_imsave(fname, arr)\n        return\n\n    if arr.ndim not in (2, 3):\n        raise ValueError(\"Invalid shape for image array: %s\" % arr.shape)\n\n    if arr.ndim == 3:\n        if arr.shape[2] not in (3, 4):\n            raise ValueError(\"Invalid number of channels in image array.\")\n\n    img = ndarray_to_pil(arr, format_str=format_str)\n    img.save(fname, format=format_str)\n\n\ndef imshow(arr):\n    \"\"\"Display an image, using PIL's default display command.\n\n    Parameters\n    ----------\n    arr : ndarray\n       Image to display.  Images of dtype float are assumed to be in\n       [0, 1].  Images of dtype uint8 are in [0, 255].\n\n    \"\"\"\n    Image.fromarray(img_as_ubyte(arr)).show()\n\n\ndef _app_show():\n    pass\n", "path": "skimage/io/_plugins/pil_plugin.py"}]}