pilutil.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. """
  2. A collection of image utilities using the Python Imaging Library (PIL).
  3. Note that PIL is not a dependency of SciPy and this module is not
  4. available on systems that don't have PIL installed.
  5. """
  6. from __future__ import division, print_function, absolute_import
  7. # Functions which need the PIL
  8. import numpy
  9. import tempfile
  10. from numpy import (amin, amax, ravel, asarray, arange, ones, newaxis,
  11. transpose, iscomplexobj, uint8, issubdtype, array)
  12. try:
  13. from PIL import Image, ImageFilter
  14. except ImportError:
  15. import Image
  16. import ImageFilter
  17. if not hasattr(Image, 'frombytes'):
  18. Image.frombytes = Image.fromstring
  19. __all__ = ['fromimage', 'toimage', 'imsave', 'imread', 'bytescale',
  20. 'imrotate', 'imresize', 'imshow', 'imfilter']
  21. @numpy.deprecate(message="`bytescale` is deprecated in SciPy 1.0.0, "
  22. "and will be removed in 1.2.0.")
  23. def bytescale(data, cmin=None, cmax=None, high=255, low=0):
  24. """
  25. Byte scales an array (image).
  26. Byte scaling means converting the input image to uint8 dtype and scaling
  27. the range to ``(low, high)`` (default 0-255).
  28. If the input image already has dtype uint8, no scaling is done.
  29. This function is only available if Python Imaging Library (PIL) is installed.
  30. Parameters
  31. ----------
  32. data : ndarray
  33. PIL image data array.
  34. cmin : scalar, optional
  35. Bias scaling of small values. Default is ``data.min()``.
  36. cmax : scalar, optional
  37. Bias scaling of large values. Default is ``data.max()``.
  38. high : scalar, optional
  39. Scale max value to `high`. Default is 255.
  40. low : scalar, optional
  41. Scale min value to `low`. Default is 0.
  42. Returns
  43. -------
  44. img_array : uint8 ndarray
  45. The byte-scaled array.
  46. Examples
  47. --------
  48. >>> from scipy.misc import bytescale
  49. >>> img = np.array([[ 91.06794177, 3.39058326, 84.4221549 ],
  50. ... [ 73.88003259, 80.91433048, 4.88878881],
  51. ... [ 51.53875334, 34.45808177, 27.5873488 ]])
  52. >>> bytescale(img)
  53. array([[255, 0, 236],
  54. [205, 225, 4],
  55. [140, 90, 70]], dtype=uint8)
  56. >>> bytescale(img, high=200, low=100)
  57. array([[200, 100, 192],
  58. [180, 188, 102],
  59. [155, 135, 128]], dtype=uint8)
  60. >>> bytescale(img, cmin=0, cmax=255)
  61. array([[91, 3, 84],
  62. [74, 81, 5],
  63. [52, 34, 28]], dtype=uint8)
  64. """
  65. if data.dtype == uint8:
  66. return data
  67. if high > 255:
  68. raise ValueError("`high` should be less than or equal to 255.")
  69. if low < 0:
  70. raise ValueError("`low` should be greater than or equal to 0.")
  71. if high < low:
  72. raise ValueError("`high` should be greater than or equal to `low`.")
  73. if cmin is None:
  74. cmin = data.min()
  75. if cmax is None:
  76. cmax = data.max()
  77. cscale = cmax - cmin
  78. if cscale < 0:
  79. raise ValueError("`cmax` should be larger than `cmin`.")
  80. elif cscale == 0:
  81. cscale = 1
  82. scale = float(high - low) / cscale
  83. bytedata = (data - cmin) * scale + low
  84. return (bytedata.clip(low, high) + 0.5).astype(uint8)
  85. @numpy.deprecate(message="`imread` is deprecated in SciPy 1.0.0, "
  86. "and will be removed in 1.2.0.\n"
  87. "Use ``imageio.imread`` instead.")
  88. def imread(name, flatten=False, mode=None):
  89. """
  90. Read an image from a file as an array.
  91. This function is only available if Python Imaging Library (PIL) is installed.
  92. Parameters
  93. ----------
  94. name : str or file object
  95. The file name or file object to be read.
  96. flatten : bool, optional
  97. If True, flattens the color layers into a single gray-scale layer.
  98. mode : str, optional
  99. Mode to convert image to, e.g. ``'RGB'``. See the Notes for more
  100. details.
  101. Returns
  102. -------
  103. imread : ndarray
  104. The array obtained by reading the image.
  105. Notes
  106. -----
  107. `imread` uses the Python Imaging Library (PIL) to read an image.
  108. The following notes are from the PIL documentation.
  109. `mode` can be one of the following strings:
  110. * 'L' (8-bit pixels, black and white)
  111. * 'P' (8-bit pixels, mapped to any other mode using a color palette)
  112. * 'RGB' (3x8-bit pixels, true color)
  113. * 'RGBA' (4x8-bit pixels, true color with transparency mask)
  114. * 'CMYK' (4x8-bit pixels, color separation)
  115. * 'YCbCr' (3x8-bit pixels, color video format)
  116. * 'I' (32-bit signed integer pixels)
  117. * 'F' (32-bit floating point pixels)
  118. PIL also provides limited support for a few special modes, including
  119. 'LA' ('L' with alpha), 'RGBX' (true color with padding) and 'RGBa'
  120. (true color with premultiplied alpha).
  121. When translating a color image to black and white (mode 'L', 'I' or
  122. 'F'), the library uses the ITU-R 601-2 luma transform::
  123. L = R * 299/1000 + G * 587/1000 + B * 114/1000
  124. When `flatten` is True, the image is converted using mode 'F'.
  125. When `mode` is not None and `flatten` is True, the image is first
  126. converted according to `mode`, and the result is then flattened using
  127. mode 'F'.
  128. """
  129. im = Image.open(name)
  130. return fromimage(im, flatten=flatten, mode=mode)
  131. @numpy.deprecate(message="`imsave` is deprecated in SciPy 1.0.0, "
  132. "and will be removed in 1.2.0.\n"
  133. "Use ``imageio.imwrite`` instead.")
  134. def imsave(name, arr, format=None):
  135. """
  136. Save an array as an image.
  137. This function is only available if Python Imaging Library (PIL) is installed.
  138. .. warning::
  139. This function uses `bytescale` under the hood to rescale images to use
  140. the full (0, 255) range if ``mode`` is one of ``None, 'L', 'P', 'l'``.
  141. It will also cast data for 2-D images to ``uint32`` for ``mode=None``
  142. (which is the default).
  143. Parameters
  144. ----------
  145. name : str or file object
  146. Output file name or file object.
  147. arr : ndarray, MxN or MxNx3 or MxNx4
  148. Array containing image values. If the shape is ``MxN``, the array
  149. represents a grey-level image. Shape ``MxNx3`` stores the red, green
  150. and blue bands along the last dimension. An alpha layer may be
  151. included, specified as the last colour band of an ``MxNx4`` array.
  152. format : str
  153. Image format. If omitted, the format to use is determined from the
  154. file name extension. If a file object was used instead of a file name,
  155. this parameter should always be used.
  156. Examples
  157. --------
  158. Construct an array of gradient intensity values and save to file:
  159. >>> from scipy.misc import imsave
  160. >>> x = np.zeros((255, 255))
  161. >>> x = np.zeros((255, 255), dtype=np.uint8)
  162. >>> x[:] = np.arange(255)
  163. >>> imsave('gradient.png', x)
  164. Construct an array with three colour bands (R, G, B) and store to file:
  165. >>> rgb = np.zeros((255, 255, 3), dtype=np.uint8)
  166. >>> rgb[..., 0] = np.arange(255)
  167. >>> rgb[..., 1] = 55
  168. >>> rgb[..., 2] = 1 - np.arange(255)
  169. >>> imsave('rgb_gradient.png', rgb)
  170. """
  171. im = toimage(arr, channel_axis=2)
  172. if format is None:
  173. im.save(name)
  174. else:
  175. im.save(name, format)
  176. return
  177. @numpy.deprecate(message="`fromimage` is deprecated in SciPy 1.0.0. "
  178. "and will be removed in 1.2.0.\n"
  179. "Use ``np.asarray(im)`` instead.")
  180. def fromimage(im, flatten=False, mode=None):
  181. """
  182. Return a copy of a PIL image as a numpy array.
  183. This function is only available if Python Imaging Library (PIL) is installed.
  184. Parameters
  185. ----------
  186. im : PIL image
  187. Input image.
  188. flatten : bool
  189. If true, convert the output to grey-scale.
  190. mode : str, optional
  191. Mode to convert image to, e.g. ``'RGB'``. See the Notes of the
  192. `imread` docstring for more details.
  193. Returns
  194. -------
  195. fromimage : ndarray
  196. The different colour bands/channels are stored in the
  197. third dimension, such that a grey-image is MxN, an
  198. RGB-image MxNx3 and an RGBA-image MxNx4.
  199. """
  200. if not Image.isImageType(im):
  201. raise TypeError("Input is not a PIL image.")
  202. if mode is not None:
  203. if mode != im.mode:
  204. im = im.convert(mode)
  205. elif im.mode == 'P':
  206. # Mode 'P' means there is an indexed "palette". If we leave the mode
  207. # as 'P', then when we do `a = array(im)` below, `a` will be a 2-D
  208. # containing the indices into the palette, and not a 3-D array
  209. # containing the RGB or RGBA values.
  210. if 'transparency' in im.info:
  211. im = im.convert('RGBA')
  212. else:
  213. im = im.convert('RGB')
  214. if flatten:
  215. im = im.convert('F')
  216. elif im.mode == '1':
  217. # Workaround for crash in PIL. When im is 1-bit, the call array(im)
  218. # can cause a seg. fault, or generate garbage. See
  219. # https://github.com/scipy/scipy/issues/2138 and
  220. # https://github.com/python-pillow/Pillow/issues/350.
  221. #
  222. # This converts im from a 1-bit image to an 8-bit image.
  223. im = im.convert('L')
  224. a = array(im)
  225. return a
  226. _errstr = "Mode is unknown or incompatible with input array shape."
  227. @numpy.deprecate(message="`toimage` is deprecated in SciPy 1.0.0, "
  228. "and will be removed in 1.2.0.\n"
  229. "Use Pillow's ``Image.fromarray`` directly instead.")
  230. def toimage(arr, high=255, low=0, cmin=None, cmax=None, pal=None,
  231. mode=None, channel_axis=None):
  232. """Takes a numpy array and returns a PIL image.
  233. This function is only available if Python Imaging Library (PIL) is installed.
  234. The mode of the PIL image depends on the array shape and the `pal` and
  235. `mode` keywords.
  236. For 2-D arrays, if `pal` is a valid (N,3) byte-array giving the RGB values
  237. (from 0 to 255) then ``mode='P'``, otherwise ``mode='L'``, unless mode
  238. is given as 'F' or 'I' in which case a float and/or integer array is made.
  239. .. warning::
  240. This function uses `bytescale` under the hood to rescale images to use
  241. the full (0, 255) range if ``mode`` is one of ``None, 'L', 'P', 'l'``.
  242. It will also cast data for 2-D images to ``uint32`` for ``mode=None``
  243. (which is the default).
  244. Notes
  245. -----
  246. For 3-D arrays, the `channel_axis` argument tells which dimension of the
  247. array holds the channel data.
  248. For 3-D arrays if one of the dimensions is 3, the mode is 'RGB'
  249. by default or 'YCbCr' if selected.
  250. The numpy array must be either 2 dimensional or 3 dimensional.
  251. """
  252. data = asarray(arr)
  253. if iscomplexobj(data):
  254. raise ValueError("Cannot convert a complex-valued array.")
  255. shape = list(data.shape)
  256. valid = len(shape) == 2 or ((len(shape) == 3) and
  257. ((3 in shape) or (4 in shape)))
  258. if not valid:
  259. raise ValueError("'arr' does not have a suitable array shape for "
  260. "any mode.")
  261. if len(shape) == 2:
  262. shape = (shape[1], shape[0]) # columns show up first
  263. if mode == 'F':
  264. data32 = data.astype(numpy.float32)
  265. image = Image.frombytes(mode, shape, data32.tostring())
  266. return image
  267. if mode in [None, 'L', 'P']:
  268. bytedata = bytescale(data, high=high, low=low,
  269. cmin=cmin, cmax=cmax)
  270. image = Image.frombytes('L', shape, bytedata.tostring())
  271. if pal is not None:
  272. image.putpalette(asarray(pal, dtype=uint8).tostring())
  273. # Becomes a mode='P' automagically.
  274. elif mode == 'P': # default gray-scale
  275. pal = (arange(0, 256, 1, dtype=uint8)[:, newaxis] *
  276. ones((3,), dtype=uint8)[newaxis, :])
  277. image.putpalette(asarray(pal, dtype=uint8).tostring())
  278. return image
  279. if mode == '1': # high input gives threshold for 1
  280. bytedata = (data > high)
  281. image = Image.frombytes('1', shape, bytedata.tostring())
  282. return image
  283. if cmin is None:
  284. cmin = amin(ravel(data))
  285. if cmax is None:
  286. cmax = amax(ravel(data))
  287. data = (data*1.0 - cmin)*(high - low)/(cmax - cmin) + low
  288. if mode == 'I':
  289. data32 = data.astype(numpy.uint32)
  290. image = Image.frombytes(mode, shape, data32.tostring())
  291. else:
  292. raise ValueError(_errstr)
  293. return image
  294. # if here then 3-d array with a 3 or a 4 in the shape length.
  295. # Check for 3 in datacube shape --- 'RGB' or 'YCbCr'
  296. if channel_axis is None:
  297. if (3 in shape):
  298. ca = numpy.flatnonzero(asarray(shape) == 3)[0]
  299. else:
  300. ca = numpy.flatnonzero(asarray(shape) == 4)
  301. if len(ca):
  302. ca = ca[0]
  303. else:
  304. raise ValueError("Could not find channel dimension.")
  305. else:
  306. ca = channel_axis
  307. numch = shape[ca]
  308. if numch not in [3, 4]:
  309. raise ValueError("Channel axis dimension is not valid.")
  310. bytedata = bytescale(data, high=high, low=low, cmin=cmin, cmax=cmax)
  311. if ca == 2:
  312. strdata = bytedata.tostring()
  313. shape = (shape[1], shape[0])
  314. elif ca == 1:
  315. strdata = transpose(bytedata, (0, 2, 1)).tostring()
  316. shape = (shape[2], shape[0])
  317. elif ca == 0:
  318. strdata = transpose(bytedata, (1, 2, 0)).tostring()
  319. shape = (shape[2], shape[1])
  320. if mode is None:
  321. if numch == 3:
  322. mode = 'RGB'
  323. else:
  324. mode = 'RGBA'
  325. if mode not in ['RGB', 'RGBA', 'YCbCr', 'CMYK']:
  326. raise ValueError(_errstr)
  327. if mode in ['RGB', 'YCbCr']:
  328. if numch != 3:
  329. raise ValueError("Invalid array shape for mode.")
  330. if mode in ['RGBA', 'CMYK']:
  331. if numch != 4:
  332. raise ValueError("Invalid array shape for mode.")
  333. # Here we know data and mode is correct
  334. image = Image.frombytes(mode, shape, strdata)
  335. return image
  336. @numpy.deprecate(message="`imrotate` is deprecated in SciPy 1.0.0, "
  337. "and will be removed in 1.2.0.\n"
  338. "Use ``skimage.transform.rotate`` instead.")
  339. def imrotate(arr, angle, interp='bilinear'):
  340. """
  341. Rotate an image counter-clockwise by angle degrees.
  342. This function is only available if Python Imaging Library (PIL) is installed.
  343. .. warning::
  344. This function uses `bytescale` under the hood to rescale images to use
  345. the full (0, 255) range if ``mode`` is one of ``None, 'L', 'P', 'l'``.
  346. It will also cast data for 2-D images to ``uint32`` for ``mode=None``
  347. (which is the default).
  348. Parameters
  349. ----------
  350. arr : ndarray
  351. Input array of image to be rotated.
  352. angle : float
  353. The angle of rotation.
  354. interp : str, optional
  355. Interpolation
  356. - 'nearest' : for nearest neighbor
  357. - 'bilinear' : for bilinear
  358. - 'lanczos' : for lanczos
  359. - 'cubic' : for bicubic
  360. - 'bicubic' : for bicubic
  361. Returns
  362. -------
  363. imrotate : ndarray
  364. The rotated array of image.
  365. """
  366. arr = asarray(arr)
  367. func = {'nearest': 0, 'lanczos': 1, 'bilinear': 2, 'bicubic': 3, 'cubic': 3}
  368. im = toimage(arr)
  369. im = im.rotate(angle, resample=func[interp])
  370. return fromimage(im)
  371. @numpy.deprecate(message="`imshow` is deprecated in SciPy 1.0.0, "
  372. "and will be removed in 1.2.0.\n"
  373. "Use ``matplotlib.pyplot.imshow`` instead.")
  374. def imshow(arr):
  375. """
  376. Simple showing of an image through an external viewer.
  377. This function is only available if Python Imaging Library (PIL) is installed.
  378. Uses the image viewer specified by the environment variable
  379. SCIPY_PIL_IMAGE_VIEWER, or if that is not defined then `see`,
  380. to view a temporary file generated from array data.
  381. .. warning::
  382. This function uses `bytescale` under the hood to rescale images to use
  383. the full (0, 255) range if ``mode`` is one of ``None, 'L', 'P', 'l'``.
  384. It will also cast data for 2-D images to ``uint32`` for ``mode=None``
  385. (which is the default).
  386. Parameters
  387. ----------
  388. arr : ndarray
  389. Array of image data to show.
  390. Returns
  391. -------
  392. None
  393. Examples
  394. --------
  395. >>> a = np.tile(np.arange(255), (255,1))
  396. >>> from scipy import misc
  397. >>> misc.imshow(a)
  398. """
  399. im = toimage(arr)
  400. fnum, fname = tempfile.mkstemp('.png')
  401. try:
  402. im.save(fname)
  403. except Exception:
  404. raise RuntimeError("Error saving temporary image data.")
  405. import os
  406. os.close(fnum)
  407. cmd = os.environ.get('SCIPY_PIL_IMAGE_VIEWER', 'see')
  408. status = os.system("%s %s" % (cmd, fname))
  409. os.unlink(fname)
  410. if status != 0:
  411. raise RuntimeError('Could not execute image viewer.')
  412. @numpy.deprecate(message="`imresize` is deprecated in SciPy 1.0.0, "
  413. "and will be removed in 1.3.0.\n"
  414. "Use Pillow instead: ``numpy.array(Image.fromarray(arr).resize())``.")
  415. def imresize(arr, size, interp='bilinear', mode=None):
  416. """
  417. Resize an image.
  418. This function is only available if Python Imaging Library (PIL) is installed.
  419. .. warning::
  420. This function uses `bytescale` under the hood to rescale images to use
  421. the full (0, 255) range if ``mode`` is one of ``None, 'L', 'P', 'l'``.
  422. It will also cast data for 2-D images to ``uint32`` for ``mode=None``
  423. (which is the default).
  424. Parameters
  425. ----------
  426. arr : ndarray
  427. The array of image to be resized.
  428. size : int, float or tuple
  429. * int - Percentage of current size.
  430. * float - Fraction of current size.
  431. * tuple - Size of the output image (height, width).
  432. interp : str, optional
  433. Interpolation to use for re-sizing ('nearest', 'lanczos', 'bilinear',
  434. 'bicubic' or 'cubic').
  435. mode : str, optional
  436. The PIL image mode ('P', 'L', etc.) to convert `arr` before resizing.
  437. If ``mode=None`` (the default), 2-D images will be treated like
  438. ``mode='L'``, i.e. casting to long integer. For 3-D and 4-D arrays,
  439. `mode` will be set to ``'RGB'`` and ``'RGBA'`` respectively.
  440. Returns
  441. -------
  442. imresize : ndarray
  443. The resized array of image.
  444. See Also
  445. --------
  446. toimage : Implicitly used to convert `arr` according to `mode`.
  447. scipy.ndimage.zoom : More generic implementation that does not use PIL.
  448. """
  449. im = toimage(arr, mode=mode)
  450. ts = type(size)
  451. if issubdtype(ts, numpy.signedinteger):
  452. percent = size / 100.0
  453. size = tuple((array(im.size)*percent).astype(int))
  454. elif issubdtype(type(size), numpy.floating):
  455. size = tuple((array(im.size)*size).astype(int))
  456. else:
  457. size = (size[1], size[0])
  458. func = {'nearest': 0, 'lanczos': 1, 'bilinear': 2, 'bicubic': 3, 'cubic': 3}
  459. imnew = im.resize(size, resample=func[interp])
  460. return fromimage(imnew)
  461. @numpy.deprecate(message="`imfilter` is deprecated in SciPy 1.0.0, "
  462. "and will be removed in 1.2.0.\n"
  463. "Use Pillow filtering functionality directly.")
  464. def imfilter(arr, ftype):
  465. """
  466. Simple filtering of an image.
  467. This function is only available if Python Imaging Library (PIL) is installed.
  468. .. warning::
  469. This function uses `bytescale` under the hood to rescale images to use
  470. the full (0, 255) range if ``mode`` is one of ``None, 'L', 'P', 'l'``.
  471. It will also cast data for 2-D images to ``uint32`` for ``mode=None``
  472. (which is the default).
  473. Parameters
  474. ----------
  475. arr : ndarray
  476. The array of Image in which the filter is to be applied.
  477. ftype : str
  478. The filter that has to be applied. Legal values are:
  479. 'blur', 'contour', 'detail', 'edge_enhance', 'edge_enhance_more',
  480. 'emboss', 'find_edges', 'smooth', 'smooth_more', 'sharpen'.
  481. Returns
  482. -------
  483. imfilter : ndarray
  484. The array with filter applied.
  485. Raises
  486. ------
  487. ValueError
  488. *Unknown filter type.* If the filter you are trying
  489. to apply is unsupported.
  490. """
  491. _tdict = {'blur': ImageFilter.BLUR,
  492. 'contour': ImageFilter.CONTOUR,
  493. 'detail': ImageFilter.DETAIL,
  494. 'edge_enhance': ImageFilter.EDGE_ENHANCE,
  495. 'edge_enhance_more': ImageFilter.EDGE_ENHANCE_MORE,
  496. 'emboss': ImageFilter.EMBOSS,
  497. 'find_edges': ImageFilter.FIND_EDGES,
  498. 'smooth': ImageFilter.SMOOTH,
  499. 'smooth_more': ImageFilter.SMOOTH_MORE,
  500. 'sharpen': ImageFilter.SHARPEN
  501. }
  502. im = toimage(arr)
  503. if ftype not in _tdict:
  504. raise ValueError("Unknown filter type.")
  505. return fromimage(im.filter(_tdict[ftype]))