test_image.py 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. from contextlib import ExitStack
  2. from copy import copy
  3. import io
  4. import os
  5. from pathlib import Path
  6. import platform
  7. import sys
  8. import urllib.request
  9. import warnings
  10. import numpy as np
  11. from numpy import ma
  12. from numpy.testing import assert_array_equal
  13. from matplotlib import (
  14. colors, image as mimage, patches, pyplot as plt, style,
  15. rc_context, rcParams)
  16. from matplotlib.cbook import MatplotlibDeprecationWarning
  17. from matplotlib.image import (AxesImage, BboxImage, FigureImage,
  18. NonUniformImage, PcolorImage)
  19. from matplotlib.testing.decorators import check_figures_equal, image_comparison
  20. from matplotlib.transforms import Bbox, Affine2D, TransformedBbox
  21. import pytest
  22. @image_comparison(['image_interps'], style='mpl20')
  23. def test_image_interps():
  24. 'make the basic nearest, bilinear and bicubic interps'
  25. # Remove this line when this test image is regenerated.
  26. plt.rcParams['text.kerning_factor'] = 6
  27. X = np.arange(100)
  28. X = X.reshape(5, 20)
  29. fig = plt.figure()
  30. ax1 = fig.add_subplot(311)
  31. ax1.imshow(X, interpolation='nearest')
  32. ax1.set_title('three interpolations')
  33. ax1.set_ylabel('nearest')
  34. ax2 = fig.add_subplot(312)
  35. ax2.imshow(X, interpolation='bilinear')
  36. ax2.set_ylabel('bilinear')
  37. ax3 = fig.add_subplot(313)
  38. ax3.imshow(X, interpolation='bicubic')
  39. ax3.set_ylabel('bicubic')
  40. @image_comparison(['interp_alpha.png'], remove_text=True)
  41. def test_alpha_interp():
  42. 'Test the interpolation of the alpha channel on RGBA images'
  43. fig, (axl, axr) = plt.subplots(1, 2)
  44. # full green image
  45. img = np.zeros((5, 5, 4))
  46. img[..., 1] = np.ones((5, 5))
  47. # transparent under main diagonal
  48. img[..., 3] = np.tril(np.ones((5, 5), dtype=np.uint8))
  49. axl.imshow(img, interpolation="none")
  50. axr.imshow(img, interpolation="bilinear")
  51. @image_comparison(['interp_nearest_vs_none'],
  52. extensions=['pdf', 'svg'], remove_text=True)
  53. def test_interp_nearest_vs_none():
  54. 'Test the effect of "nearest" and "none" interpolation'
  55. # Setting dpi to something really small makes the difference very
  56. # visible. This works fine with pdf, since the dpi setting doesn't
  57. # affect anything but images, but the agg output becomes unusably
  58. # small.
  59. rcParams['savefig.dpi'] = 3
  60. X = np.array([[[218, 165, 32], [122, 103, 238]],
  61. [[127, 255, 0], [255, 99, 71]]], dtype=np.uint8)
  62. fig = plt.figure()
  63. ax1 = fig.add_subplot(121)
  64. ax1.imshow(X, interpolation='none')
  65. ax1.set_title('interpolation none')
  66. ax2 = fig.add_subplot(122)
  67. ax2.imshow(X, interpolation='nearest')
  68. ax2.set_title('interpolation nearest')
  69. def do_figimage(suppressComposite):
  70. """Helper for the next two tests."""
  71. fig = plt.figure(figsize=(2, 2), dpi=100)
  72. fig.suppressComposite = suppressComposite
  73. x, y = np.ix_(np.arange(100) / 100.0, np.arange(100) / 100)
  74. z = np.sin(x**2 + y**2 - x*y)
  75. c = np.sin(20*x**2 + 50*y**2)
  76. img = z + c/5
  77. fig.figimage(img, xo=0, yo=0, origin='lower')
  78. fig.figimage(img[::-1, :], xo=0, yo=100, origin='lower')
  79. fig.figimage(img[:, ::-1], xo=100, yo=0, origin='lower')
  80. fig.figimage(img[::-1, ::-1], xo=100, yo=100, origin='lower')
  81. @image_comparison(['figimage-0'], extensions=['png', 'pdf'])
  82. def test_figimage0():
  83. suppressComposite = False
  84. do_figimage(suppressComposite)
  85. @image_comparison(['figimage-1'], extensions=['png', 'pdf'])
  86. def test_figimage1():
  87. suppressComposite = True
  88. do_figimage(suppressComposite)
  89. def test_image_python_io():
  90. fig, ax = plt.subplots()
  91. ax.plot([1, 2, 3])
  92. buffer = io.BytesIO()
  93. fig.savefig(buffer)
  94. buffer.seek(0)
  95. plt.imread(buffer)
  96. @check_figures_equal(extensions=['png'])
  97. def test_imshow_subsample(fig_test, fig_ref):
  98. # data is bigger than figure, so subsampling with hanning
  99. np.random.seed(19680801)
  100. dpi = 100
  101. A = np.random.rand(int(dpi * 5), int(dpi * 5))
  102. for fig in [fig_test, fig_ref]:
  103. fig.set_size_inches(2, 2)
  104. axs = fig_test.subplots()
  105. axs.set_position([0, 0, 1, 1])
  106. axs.imshow(A, interpolation='antialiased')
  107. axs = fig_ref.subplots()
  108. axs.set_position([0, 0, 1, 1])
  109. axs.imshow(A, interpolation='hanning')
  110. @check_figures_equal(extensions=['png'])
  111. def test_imshow_samesample(fig_test, fig_ref):
  112. # exact resample, so should be same as nearest....
  113. np.random.seed(19680801)
  114. dpi = 100
  115. A = np.random.rand(int(dpi * 5), int(dpi * 5))
  116. for fig in [fig_test, fig_ref]:
  117. fig.set_size_inches(5, 5)
  118. axs = fig_test.subplots()
  119. axs.set_position([0, 0, 1, 1])
  120. axs.imshow(A, interpolation='antialiased')
  121. axs = fig_ref.subplots()
  122. axs.set_position([0, 0, 1, 1])
  123. axs.imshow(A, interpolation='nearest')
  124. @check_figures_equal(extensions=['png'])
  125. def test_imshow_doublesample(fig_test, fig_ref):
  126. # should be exactly a double sample, so should use nearest neighbour
  127. # which is the same as "none"
  128. np.random.seed(19680801)
  129. dpi = 100
  130. A = np.random.rand(int(dpi * 5), int(dpi * 5))
  131. for fig in [fig_test, fig_ref]:
  132. fig.set_size_inches(10, 10)
  133. axs = fig_test.subplots()
  134. axs.set_position([0, 0, 1, 1])
  135. axs.imshow(A, interpolation='antialiased')
  136. axs = fig_ref.subplots()
  137. axs.set_position([0, 0, 1, 1])
  138. axs.imshow(A, interpolation='nearest')
  139. @check_figures_equal(extensions=['png'])
  140. def test_imshow_upsample(fig_test, fig_ref):
  141. # should be less than 3 upsample, so should be nearest...
  142. np.random.seed(19680801)
  143. dpi = 100
  144. A = np.random.rand(int(dpi * 3), int(dpi * 3))
  145. for fig in [fig_test, fig_ref]:
  146. fig.set_size_inches(2.9, 2.9)
  147. axs = fig_test.subplots()
  148. axs.set_position([0, 0, 1, 1])
  149. axs.imshow(A, interpolation='antialiased')
  150. axs = fig_ref.subplots()
  151. axs.set_position([0, 0, 1, 1])
  152. axs.imshow(A, interpolation='hanning')
  153. @check_figures_equal(extensions=['png'])
  154. def test_imshow_upsample3(fig_test, fig_ref):
  155. # should be greater than 3 upsample, so should be nearest...
  156. np.random.seed(19680801)
  157. dpi = 100
  158. A = np.random.rand(int(dpi * 3), int(dpi * 3))
  159. for fig in [fig_test, fig_ref]:
  160. fig.set_size_inches(9.1, 9.1)
  161. axs = fig_test.subplots()
  162. axs.set_position([0, 0, 1, 1])
  163. axs.imshow(A, interpolation='antialiased')
  164. axs = fig_ref.subplots()
  165. axs.set_position([0, 0, 1, 1])
  166. axs.imshow(A, interpolation='nearest')
  167. @check_figures_equal(extensions=['png'])
  168. def test_imshow_zoom(fig_test, fig_ref):
  169. # should be less than 3 upsample, so should be nearest...
  170. np.random.seed(19680801)
  171. dpi = 100
  172. A = np.random.rand(int(dpi * 3), int(dpi * 3))
  173. for fig in [fig_test, fig_ref]:
  174. fig.set_size_inches(2.9, 2.9)
  175. axs = fig_test.subplots()
  176. axs.imshow(A, interpolation='nearest')
  177. axs.set_xlim([10, 20])
  178. axs.set_ylim([10, 20])
  179. axs = fig_ref.subplots()
  180. axs.imshow(A, interpolation='antialiased')
  181. axs.set_xlim([10, 20])
  182. axs.set_ylim([10, 20])
  183. @check_figures_equal()
  184. def test_imshow_pil(fig_test, fig_ref):
  185. style.use("default")
  186. PIL = pytest.importorskip("PIL")
  187. # Pillow<=6.0 fails to open pathlib.Paths on Windows (pillow#3823), and
  188. # Matplotlib's builtin png opener doesn't handle them either.
  189. png_path = str(
  190. Path(__file__).parent / "baseline_images/pngsuite/basn3p04.png")
  191. tiff_path = str(
  192. Path(__file__).parent / "baseline_images/test_image/uint16.tif")
  193. axs = fig_test.subplots(2)
  194. axs[0].imshow(PIL.Image.open(png_path))
  195. axs[1].imshow(PIL.Image.open(tiff_path))
  196. axs = fig_ref.subplots(2)
  197. axs[0].imshow(plt.imread(png_path))
  198. axs[1].imshow(plt.imread(tiff_path))
  199. def test_imread_pil_uint16():
  200. pytest.importorskip("PIL")
  201. img = plt.imread(os.path.join(os.path.dirname(__file__),
  202. 'baseline_images', 'test_image', 'uint16.tif'))
  203. assert img.dtype == np.uint16
  204. assert np.sum(img) == 134184960
  205. def test_imread_fspath():
  206. pytest.importorskip("PIL")
  207. img = plt.imread(
  208. Path(__file__).parent / 'baseline_images/test_image/uint16.tif')
  209. assert img.dtype == np.uint16
  210. assert np.sum(img) == 134184960
  211. @pytest.mark.parametrize("fmt", ["png", "jpg", "jpeg", "tiff"])
  212. def test_imsave(fmt):
  213. if fmt in ["jpg", "jpeg", "tiff"]:
  214. pytest.importorskip("PIL")
  215. has_alpha = fmt not in ["jpg", "jpeg"]
  216. # The goal here is that the user can specify an output logical DPI
  217. # for the image, but this will not actually add any extra pixels
  218. # to the image, it will merely be used for metadata purposes.
  219. # So we do the traditional case (dpi == 1), and the new case (dpi
  220. # == 100) and read the resulting PNG files back in and make sure
  221. # the data is 100% identical.
  222. np.random.seed(1)
  223. # The height of 1856 pixels was selected because going through creating an
  224. # actual dpi=100 figure to save the image to a Pillow-provided format would
  225. # cause a rounding error resulting in a final image of shape 1855.
  226. data = np.random.rand(1856, 2)
  227. buff_dpi1 = io.BytesIO()
  228. plt.imsave(buff_dpi1, data, format=fmt, dpi=1)
  229. buff_dpi100 = io.BytesIO()
  230. plt.imsave(buff_dpi100, data, format=fmt, dpi=100)
  231. buff_dpi1.seek(0)
  232. arr_dpi1 = plt.imread(buff_dpi1, format=fmt)
  233. buff_dpi100.seek(0)
  234. arr_dpi100 = plt.imread(buff_dpi100, format=fmt)
  235. assert arr_dpi1.shape == (1856, 2, 3 + has_alpha)
  236. assert arr_dpi100.shape == (1856, 2, 3 + has_alpha)
  237. assert_array_equal(arr_dpi1, arr_dpi100)
  238. @pytest.mark.parametrize("fmt", ["png", "pdf", "ps", "eps", "svg"])
  239. def test_imsave_fspath(fmt):
  240. plt.imsave(Path(os.devnull), np.array([[0, 1]]), format=fmt)
  241. def test_imsave_color_alpha():
  242. # Test that imsave accept arrays with ndim=3 where the third dimension is
  243. # color and alpha without raising any exceptions, and that the data is
  244. # acceptably preserved through a save/read roundtrip.
  245. np.random.seed(1)
  246. for origin in ['lower', 'upper']:
  247. data = np.random.rand(16, 16, 4)
  248. buff = io.BytesIO()
  249. plt.imsave(buff, data, origin=origin, format="png")
  250. buff.seek(0)
  251. arr_buf = plt.imread(buff)
  252. # Recreate the float -> uint8 conversion of the data
  253. # We can only expect to be the same with 8 bits of precision,
  254. # since that's what the PNG file used.
  255. data = (255*data).astype('uint8')
  256. if origin == 'lower':
  257. data = data[::-1]
  258. arr_buf = (255*arr_buf).astype('uint8')
  259. assert_array_equal(data, arr_buf)
  260. def test_imsave_pil_kwargs_png():
  261. Image = pytest.importorskip("PIL.Image")
  262. from PIL.PngImagePlugin import PngInfo
  263. buf = io.BytesIO()
  264. pnginfo = PngInfo()
  265. pnginfo.add_text("Software", "test")
  266. plt.imsave(buf, [[0, 1], [2, 3]],
  267. format="png", pil_kwargs={"pnginfo": pnginfo})
  268. im = Image.open(buf)
  269. assert im.info["Software"] == "test"
  270. def test_imsave_pil_kwargs_tiff():
  271. Image = pytest.importorskip("PIL.Image")
  272. from PIL.TiffTags import TAGS_V2 as TAGS
  273. buf = io.BytesIO()
  274. pil_kwargs = {"description": "test image"}
  275. plt.imsave(buf, [[0, 1], [2, 3]], format="tiff", pil_kwargs=pil_kwargs)
  276. im = Image.open(buf)
  277. tags = {TAGS[k].name: v for k, v in im.tag_v2.items()}
  278. assert tags["ImageDescription"] == "test image"
  279. @image_comparison(['image_alpha'], remove_text=True)
  280. def test_image_alpha():
  281. plt.figure()
  282. np.random.seed(0)
  283. Z = np.random.rand(6, 6)
  284. plt.subplot(131)
  285. plt.imshow(Z, alpha=1.0, interpolation='none')
  286. plt.subplot(132)
  287. plt.imshow(Z, alpha=0.5, interpolation='none')
  288. plt.subplot(133)
  289. plt.imshow(Z, alpha=0.5, interpolation='nearest')
  290. def test_cursor_data():
  291. from matplotlib.backend_bases import MouseEvent
  292. fig, ax = plt.subplots()
  293. im = ax.imshow(np.arange(100).reshape(10, 10), origin='upper')
  294. x, y = 4, 4
  295. xdisp, ydisp = ax.transData.transform([x, y])
  296. event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  297. assert im.get_cursor_data(event) == 44
  298. # Now try for a point outside the image
  299. # Tests issue #4957
  300. x, y = 10.1, 4
  301. xdisp, ydisp = ax.transData.transform([x, y])
  302. event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  303. assert im.get_cursor_data(event) is None
  304. # Hmm, something is wrong here... I get 0, not None...
  305. # But, this works further down in the tests with extents flipped
  306. #x, y = 0.1, -0.1
  307. #xdisp, ydisp = ax.transData.transform([x, y])
  308. #event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  309. #z = im.get_cursor_data(event)
  310. #assert z is None, "Did not get None, got %d" % z
  311. ax.clear()
  312. # Now try with the extents flipped.
  313. im = ax.imshow(np.arange(100).reshape(10, 10), origin='lower')
  314. x, y = 4, 4
  315. xdisp, ydisp = ax.transData.transform([x, y])
  316. event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  317. assert im.get_cursor_data(event) == 44
  318. fig, ax = plt.subplots()
  319. im = ax.imshow(np.arange(100).reshape(10, 10), extent=[0, 0.5, 0, 0.5])
  320. x, y = 0.25, 0.25
  321. xdisp, ydisp = ax.transData.transform([x, y])
  322. event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  323. assert im.get_cursor_data(event) == 55
  324. # Now try for a point outside the image
  325. # Tests issue #4957
  326. x, y = 0.75, 0.25
  327. xdisp, ydisp = ax.transData.transform([x, y])
  328. event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  329. assert im.get_cursor_data(event) is None
  330. x, y = 0.01, -0.01
  331. xdisp, ydisp = ax.transData.transform([x, y])
  332. event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  333. assert im.get_cursor_data(event) is None
  334. @pytest.mark.parametrize(
  335. "data, text_without_colorbar, text_with_colorbar", [
  336. ([[10001, 10000]], "[1e+04]", "[10001]"),
  337. ([[.123, .987]], "[0.123]", "[0.123]"),
  338. ])
  339. def test_format_cursor_data(data, text_without_colorbar, text_with_colorbar):
  340. from matplotlib.backend_bases import MouseEvent
  341. fig, ax = plt.subplots()
  342. im = ax.imshow(data)
  343. xdisp, ydisp = ax.transData.transform([0, 0])
  344. event = MouseEvent('motion_notify_event', fig.canvas, xdisp, ydisp)
  345. assert im.get_cursor_data(event) == data[0][0]
  346. assert im.format_cursor_data(im.get_cursor_data(event)) \
  347. == text_without_colorbar
  348. fig.colorbar(im)
  349. fig.canvas.draw() # This is necessary to set up the colorbar formatter.
  350. assert im.get_cursor_data(event) == data[0][0]
  351. assert im.format_cursor_data(im.get_cursor_data(event)) \
  352. == text_with_colorbar
  353. @image_comparison(['image_clip'], style='mpl20')
  354. def test_image_clip():
  355. d = [[1, 2], [3, 4]]
  356. fig, ax = plt.subplots()
  357. im = ax.imshow(d)
  358. patch = patches.Circle((0, 0), radius=1, transform=ax.transData)
  359. im.set_clip_path(patch)
  360. @image_comparison(['image_cliprect'], style='mpl20')
  361. def test_image_cliprect():
  362. import matplotlib.patches as patches
  363. fig, ax = plt.subplots()
  364. d = [[1, 2], [3, 4]]
  365. im = ax.imshow(d, extent=(0, 5, 0, 5))
  366. rect = patches.Rectangle(
  367. xy=(1, 1), width=2, height=2, transform=im.axes.transData)
  368. im.set_clip_path(rect)
  369. @image_comparison(['imshow'], remove_text=True, style='mpl20')
  370. def test_imshow():
  371. fig, ax = plt.subplots()
  372. arr = np.arange(100).reshape((10, 10))
  373. ax.imshow(arr, interpolation="bilinear", extent=(1, 2, 1, 2))
  374. ax.set_xlim(0, 3)
  375. ax.set_ylim(0, 3)
  376. @image_comparison(['no_interpolation_origin'], remove_text=True)
  377. def test_no_interpolation_origin():
  378. fig, axs = plt.subplots(2)
  379. axs[0].imshow(np.arange(100).reshape((2, 50)), origin="lower",
  380. interpolation='none')
  381. axs[1].imshow(np.arange(100).reshape((2, 50)), interpolation='none')
  382. @image_comparison(['image_shift'], remove_text=True, extensions=['pdf', 'svg'])
  383. def test_image_shift():
  384. from matplotlib.colors import LogNorm
  385. imgData = [[1 / x + 1 / y for x in range(1, 100)] for y in range(1, 100)]
  386. tMin = 734717.945208
  387. tMax = 734717.946366
  388. fig, ax = plt.subplots()
  389. ax.imshow(imgData, norm=LogNorm(), interpolation='none',
  390. extent=(tMin, tMax, 1, 100))
  391. ax.set_aspect('auto')
  392. def test_image_edges():
  393. f = plt.figure(figsize=[1, 1])
  394. ax = f.add_axes([0, 0, 1, 1], frameon=False)
  395. data = np.tile(np.arange(12), 15).reshape(20, 9)
  396. im = ax.imshow(data, origin='upper', extent=[-10, 10, -10, 10],
  397. interpolation='none', cmap='gray')
  398. x = y = 2
  399. ax.set_xlim([-x, x])
  400. ax.set_ylim([-y, y])
  401. ax.set_xticks([])
  402. ax.set_yticks([])
  403. buf = io.BytesIO()
  404. f.savefig(buf, facecolor=(0, 1, 0))
  405. buf.seek(0)
  406. im = plt.imread(buf)
  407. r, g, b, a = sum(im[:, 0])
  408. r, g, b, a = sum(im[:, -1])
  409. assert g != 100, 'Expected a non-green edge - but sadly, it was.'
  410. @image_comparison(['image_composite_background'],
  411. remove_text=True, style='mpl20')
  412. def test_image_composite_background():
  413. fig, ax = plt.subplots()
  414. arr = np.arange(12).reshape(4, 3)
  415. ax.imshow(arr, extent=[0, 2, 15, 0])
  416. ax.imshow(arr, extent=[4, 6, 15, 0])
  417. ax.set_facecolor((1, 0, 0, 0.5))
  418. ax.set_xlim([0, 12])
  419. @image_comparison(['image_composite_alpha'], remove_text=True)
  420. def test_image_composite_alpha():
  421. """
  422. Tests that the alpha value is recognized and correctly applied in the
  423. process of compositing images together.
  424. """
  425. fig, ax = plt.subplots()
  426. arr = np.zeros((11, 21, 4))
  427. arr[:, :, 0] = 1
  428. arr[:, :, 3] = np.concatenate(
  429. (np.arange(0, 1.1, 0.1), np.arange(0, 1, 0.1)[::-1]))
  430. arr2 = np.zeros((21, 11, 4))
  431. arr2[:, :, 0] = 1
  432. arr2[:, :, 1] = 1
  433. arr2[:, :, 3] = np.concatenate(
  434. (np.arange(0, 1.1, 0.1), np.arange(0, 1, 0.1)[::-1]))[:, np.newaxis]
  435. ax.imshow(arr, extent=[1, 2, 5, 0], alpha=0.3)
  436. ax.imshow(arr, extent=[2, 3, 5, 0], alpha=0.6)
  437. ax.imshow(arr, extent=[3, 4, 5, 0])
  438. ax.imshow(arr2, extent=[0, 5, 1, 2])
  439. ax.imshow(arr2, extent=[0, 5, 2, 3], alpha=0.6)
  440. ax.imshow(arr2, extent=[0, 5, 3, 4], alpha=0.3)
  441. ax.set_facecolor((0, 0.5, 0, 1))
  442. ax.set_xlim([0, 5])
  443. ax.set_ylim([5, 0])
  444. @image_comparison(['rasterize_10dpi'],
  445. extensions=['pdf', 'svg'], remove_text=True, style='mpl20')
  446. def test_rasterize_dpi():
  447. # This test should check rasterized rendering with high output resolution.
  448. # It plots a rasterized line and a normal image with imshow. So it will
  449. # catch when images end up in the wrong place in case of non-standard dpi
  450. # setting. Instead of high-res rasterization I use low-res. Therefore
  451. # the fact that the resolution is non-standard is easily checked by
  452. # image_comparison.
  453. img = np.asarray([[1, 2], [3, 4]])
  454. fig, axs = plt.subplots(1, 3, figsize=(3, 1))
  455. axs[0].imshow(img)
  456. axs[1].plot([0, 1], [0, 1], linewidth=20., rasterized=True)
  457. axs[1].set(xlim=(0, 1), ylim=(-1, 2))
  458. axs[2].plot([0, 1], [0, 1], linewidth=20.)
  459. axs[2].set(xlim=(0, 1), ylim=(-1, 2))
  460. # Low-dpi PDF rasterization errors prevent proper image comparison tests.
  461. # Hide detailed structures like the axes spines.
  462. for ax in axs:
  463. ax.set_xticks([])
  464. ax.set_yticks([])
  465. for spine in ax.spines.values():
  466. spine.set_visible(False)
  467. rcParams['savefig.dpi'] = 10
  468. @image_comparison(['bbox_image_inverted'], remove_text=True, style='mpl20')
  469. def test_bbox_image_inverted():
  470. # This is just used to produce an image to feed to BboxImage
  471. image = np.arange(100).reshape((10, 10))
  472. fig, ax = plt.subplots()
  473. bbox_im = BboxImage(
  474. TransformedBbox(Bbox([[100, 100], [0, 0]]), ax.transData),
  475. interpolation='nearest')
  476. bbox_im.set_data(image)
  477. bbox_im.set_clip_on(False)
  478. ax.set_xlim(0, 100)
  479. ax.set_ylim(0, 100)
  480. ax.add_artist(bbox_im)
  481. image = np.identity(10)
  482. bbox_im = BboxImage(TransformedBbox(Bbox([[0.1, 0.2], [0.3, 0.25]]),
  483. ax.figure.transFigure),
  484. interpolation='nearest')
  485. bbox_im.set_data(image)
  486. bbox_im.set_clip_on(False)
  487. ax.add_artist(bbox_im)
  488. def test_get_window_extent_for_AxisImage():
  489. # Create a figure of known size (1000x1000 pixels), place an image
  490. # object at a given location and check that get_window_extent()
  491. # returns the correct bounding box values (in pixels).
  492. im = np.array([[0.25, 0.75, 1.0, 0.75], [0.1, 0.65, 0.5, 0.4],
  493. [0.6, 0.3, 0.0, 0.2], [0.7, 0.9, 0.4, 0.6]])
  494. fig, ax = plt.subplots(figsize=(10, 10), dpi=100)
  495. ax.set_position([0, 0, 1, 1])
  496. ax.set_xlim(0, 1)
  497. ax.set_ylim(0, 1)
  498. im_obj = ax.imshow(
  499. im, extent=[0.4, 0.7, 0.2, 0.9], interpolation='nearest')
  500. fig.canvas.draw()
  501. renderer = fig.canvas.renderer
  502. im_bbox = im_obj.get_window_extent(renderer)
  503. assert_array_equal(im_bbox.get_points(), [[400, 200], [700, 900]])
  504. @image_comparison(['zoom_and_clip_upper_origin.png'],
  505. remove_text=True, style='mpl20')
  506. def test_zoom_and_clip_upper_origin():
  507. image = np.arange(100)
  508. image = image.reshape((10, 10))
  509. fig, ax = plt.subplots()
  510. ax.imshow(image)
  511. ax.set_ylim(2.0, -0.5)
  512. ax.set_xlim(-0.5, 2.0)
  513. def test_nonuniformimage_setcmap():
  514. ax = plt.gca()
  515. im = NonUniformImage(ax)
  516. im.set_cmap('Blues')
  517. def test_nonuniformimage_setnorm():
  518. ax = plt.gca()
  519. im = NonUniformImage(ax)
  520. im.set_norm(plt.Normalize())
  521. def test_jpeg_2d():
  522. Image = pytest.importorskip('PIL.Image')
  523. # smoke test that mode-L pillow images work.
  524. imd = np.ones((10, 10), dtype='uint8')
  525. for i in range(10):
  526. imd[i, :] = np.linspace(0.0, 1.0, 10) * 255
  527. im = Image.new('L', (10, 10))
  528. im.putdata(imd.flatten())
  529. fig, ax = plt.subplots()
  530. ax.imshow(im)
  531. def test_jpeg_alpha():
  532. Image = pytest.importorskip('PIL.Image')
  533. plt.figure(figsize=(1, 1), dpi=300)
  534. # Create an image that is all black, with a gradient from 0-1 in
  535. # the alpha channel from left to right.
  536. im = np.zeros((300, 300, 4), dtype=float)
  537. im[..., 3] = np.linspace(0.0, 1.0, 300)
  538. plt.figimage(im)
  539. buff = io.BytesIO()
  540. plt.savefig(buff, facecolor="red", format='jpg', dpi=300)
  541. buff.seek(0)
  542. image = Image.open(buff)
  543. # If this fails, there will be only one color (all black). If this
  544. # is working, we should have all 256 shades of grey represented.
  545. num_colors = len(image.getcolors(256))
  546. assert 175 <= num_colors <= 185
  547. # The fully transparent part should be red.
  548. corner_pixel = image.getpixel((0, 0))
  549. assert corner_pixel == (254, 0, 0)
  550. def test_nonuniformimage_setdata():
  551. ax = plt.gca()
  552. im = NonUniformImage(ax)
  553. x = np.arange(3, dtype=float)
  554. y = np.arange(4, dtype=float)
  555. z = np.arange(12, dtype=float).reshape((4, 3))
  556. im.set_data(x, y, z)
  557. x[0] = y[0] = z[0, 0] = 9.9
  558. assert im._A[0, 0] == im._Ax[0] == im._Ay[0] == 0, 'value changed'
  559. def test_axesimage_setdata():
  560. ax = plt.gca()
  561. im = AxesImage(ax)
  562. z = np.arange(12, dtype=float).reshape((4, 3))
  563. im.set_data(z)
  564. z[0, 0] = 9.9
  565. assert im._A[0, 0] == 0, 'value changed'
  566. def test_figureimage_setdata():
  567. fig = plt.gcf()
  568. im = FigureImage(fig)
  569. z = np.arange(12, dtype=float).reshape((4, 3))
  570. im.set_data(z)
  571. z[0, 0] = 9.9
  572. assert im._A[0, 0] == 0, 'value changed'
  573. def test_pcolorimage_setdata():
  574. ax = plt.gca()
  575. im = PcolorImage(ax)
  576. x = np.arange(3, dtype=float)
  577. y = np.arange(4, dtype=float)
  578. z = np.arange(6, dtype=float).reshape((3, 2))
  579. im.set_data(x, y, z)
  580. x[0] = y[0] = z[0, 0] = 9.9
  581. assert im._A[0, 0] == im._Ax[0] == im._Ay[0] == 0, 'value changed'
  582. def test_minimized_rasterized():
  583. # This ensures that the rasterized content in the colorbars is
  584. # only as thick as the colorbar, and doesn't extend to other parts
  585. # of the image. See #5814. While the original bug exists only
  586. # in Postscript, the best way to detect it is to generate SVG
  587. # and then parse the output to make sure the two colorbar images
  588. # are the same size.
  589. from xml.etree import ElementTree
  590. np.random.seed(0)
  591. data = np.random.rand(10, 10)
  592. fig, ax = plt.subplots(1, 2)
  593. p1 = ax[0].pcolormesh(data)
  594. p2 = ax[1].pcolormesh(data)
  595. plt.colorbar(p1, ax=ax[0])
  596. plt.colorbar(p2, ax=ax[1])
  597. buff = io.BytesIO()
  598. plt.savefig(buff, format='svg')
  599. buff = io.BytesIO(buff.getvalue())
  600. tree = ElementTree.parse(buff)
  601. width = None
  602. for image in tree.iter('image'):
  603. if width is None:
  604. width = image['width']
  605. else:
  606. if image['width'] != width:
  607. assert False
  608. def test_load_from_url():
  609. path = Path(__file__).parent / "baseline_images/test_image/imshow.png"
  610. url = ('file:'
  611. + ('///' if sys.platform == 'win32' else '')
  612. + path.resolve().as_posix())
  613. plt.imread(url)
  614. plt.imread(urllib.request.urlopen(url))
  615. @image_comparison(['log_scale_image'], remove_text=True)
  616. def test_log_scale_image():
  617. Z = np.zeros((10, 10))
  618. Z[::2] = 1
  619. fig, ax = plt.subplots()
  620. ax.imshow(Z, extent=[1, 100, 1, 100], cmap='viridis', vmax=1, vmin=-1,
  621. aspect='auto')
  622. ax.set(yscale='log')
  623. @image_comparison(['rotate_image'], remove_text=True)
  624. def test_rotate_image():
  625. delta = 0.25
  626. x = y = np.arange(-3.0, 3.0, delta)
  627. X, Y = np.meshgrid(x, y)
  628. Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
  629. Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
  630. (2 * np.pi * 0.5 * 1.5))
  631. Z = Z2 - Z1 # difference of Gaussians
  632. fig, ax1 = plt.subplots(1, 1)
  633. im1 = ax1.imshow(Z, interpolation='none', cmap='viridis',
  634. origin='lower',
  635. extent=[-2, 4, -3, 2], clip_on=True)
  636. trans_data2 = Affine2D().rotate_deg(30) + ax1.transData
  637. im1.set_transform(trans_data2)
  638. # display intended extent of the image
  639. x1, x2, y1, y2 = im1.get_extent()
  640. ax1.plot([x1, x2, x2, x1, x1], [y1, y1, y2, y2, y1], "r--", lw=3,
  641. transform=trans_data2)
  642. ax1.set_xlim(2, 5)
  643. ax1.set_ylim(0, 4)
  644. def test_image_preserve_size():
  645. buff = io.BytesIO()
  646. im = np.zeros((481, 321))
  647. plt.imsave(buff, im, format="png")
  648. buff.seek(0)
  649. img = plt.imread(buff)
  650. assert img.shape[:2] == im.shape
  651. def test_image_preserve_size2():
  652. n = 7
  653. data = np.identity(n, float)
  654. fig = plt.figure(figsize=(n, n), frameon=False)
  655. ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0])
  656. ax.set_axis_off()
  657. fig.add_axes(ax)
  658. ax.imshow(data, interpolation='nearest', origin='lower', aspect='auto')
  659. buff = io.BytesIO()
  660. fig.savefig(buff, dpi=1)
  661. buff.seek(0)
  662. img = plt.imread(buff)
  663. assert img.shape == (7, 7, 4)
  664. assert_array_equal(np.asarray(img[:, :, 0], bool),
  665. np.identity(n, bool)[::-1])
  666. @image_comparison(['mask_image_over_under.png'], remove_text=True)
  667. def test_mask_image_over_under():
  668. delta = 0.025
  669. x = y = np.arange(-3.0, 3.0, delta)
  670. X, Y = np.meshgrid(x, y)
  671. Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
  672. Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
  673. (2 * np.pi * 0.5 * 1.5))
  674. Z = 10*(Z2 - Z1) # difference of Gaussians
  675. palette = copy(plt.cm.gray)
  676. palette.set_over('r', 1.0)
  677. palette.set_under('g', 1.0)
  678. palette.set_bad('b', 1.0)
  679. Zm = ma.masked_where(Z > 1.2, Z)
  680. fig, (ax1, ax2) = plt.subplots(1, 2)
  681. im = ax1.imshow(Zm, interpolation='bilinear',
  682. cmap=palette,
  683. norm=colors.Normalize(vmin=-1.0, vmax=1.0, clip=False),
  684. origin='lower', extent=[-3, 3, -3, 3])
  685. ax1.set_title('Green=low, Red=high, Blue=bad')
  686. fig.colorbar(im, extend='both', orientation='horizontal',
  687. ax=ax1, aspect=10)
  688. im = ax2.imshow(Zm, interpolation='nearest',
  689. cmap=palette,
  690. norm=colors.BoundaryNorm([-1, -0.5, -0.2, 0, 0.2, 0.5, 1],
  691. ncolors=256, clip=False),
  692. origin='lower', extent=[-3, 3, -3, 3])
  693. ax2.set_title('With BoundaryNorm')
  694. fig.colorbar(im, extend='both', spacing='proportional',
  695. orientation='horizontal', ax=ax2, aspect=10)
  696. @image_comparison(['mask_image'], remove_text=True)
  697. def test_mask_image():
  698. # Test mask image two ways: Using nans and using a masked array.
  699. fig, (ax1, ax2) = plt.subplots(1, 2)
  700. A = np.ones((5, 5))
  701. A[1:2, 1:2] = np.nan
  702. ax1.imshow(A, interpolation='nearest')
  703. A = np.zeros((5, 5), dtype=bool)
  704. A[1:2, 1:2] = True
  705. A = np.ma.masked_array(np.ones((5, 5), dtype=np.uint16), A)
  706. ax2.imshow(A, interpolation='nearest')
  707. @image_comparison(['imshow_endianess.png'], remove_text=True)
  708. def test_imshow_endianess():
  709. x = np.arange(10)
  710. X, Y = np.meshgrid(x, x)
  711. Z = np.hypot(X - 5, Y - 5)
  712. fig, (ax1, ax2) = plt.subplots(1, 2)
  713. kwargs = dict(origin="lower", interpolation='nearest', cmap='viridis')
  714. ax1.imshow(Z.astype('<f8'), **kwargs)
  715. ax2.imshow(Z.astype('>f8'), **kwargs)
  716. @image_comparison(['imshow_masked_interpolation'],
  717. tol={'aarch64': 0.02}.get(platform.machine(), 0.0),
  718. remove_text=True, style='mpl20')
  719. def test_imshow_masked_interpolation():
  720. cm = copy(plt.get_cmap('viridis'))
  721. cm.set_over('r')
  722. cm.set_under('b')
  723. cm.set_bad('k')
  724. N = 20
  725. n = colors.Normalize(vmin=0, vmax=N*N-1)
  726. data = np.arange(N*N, dtype='float').reshape(N, N)
  727. data[5, 5] = -1
  728. # This will cause crazy ringing for the higher-order
  729. # interpolations
  730. data[15, 5] = 1e5
  731. # data[3, 3] = np.nan
  732. data[15, 15] = np.inf
  733. mask = np.zeros_like(data).astype('bool')
  734. mask[5, 15] = True
  735. data = np.ma.masked_array(data, mask)
  736. fig, ax_grid = plt.subplots(3, 6)
  737. interps = sorted(mimage._interpd_)
  738. interps.remove('antialiased')
  739. for interp, ax in zip(interps, ax_grid.ravel()):
  740. ax.set_title(interp)
  741. ax.imshow(data, norm=n, cmap=cm, interpolation=interp)
  742. ax.axis('off')
  743. def test_imshow_no_warn_invalid():
  744. with warnings.catch_warnings(record=True) as warns:
  745. warnings.simplefilter("always")
  746. plt.imshow([[1, 2], [3, np.nan]])
  747. assert len(warns) == 0
  748. @pytest.mark.parametrize(
  749. 'dtype', [np.dtype(s) for s in 'u2 u4 i2 i4 i8 f4 f8'.split()])
  750. def test_imshow_clips_rgb_to_valid_range(dtype):
  751. arr = np.arange(300, dtype=dtype).reshape((10, 10, 3))
  752. if dtype.kind != 'u':
  753. arr -= 10
  754. too_low = arr < 0
  755. too_high = arr > 255
  756. if dtype.kind == 'f':
  757. arr = arr / 255
  758. _, ax = plt.subplots()
  759. out = ax.imshow(arr).get_array()
  760. assert (out[too_low] == 0).all()
  761. if dtype.kind == 'f':
  762. assert (out[too_high] == 1).all()
  763. assert out.dtype.kind == 'f'
  764. else:
  765. assert (out[too_high] == 255).all()
  766. assert out.dtype == np.uint8
  767. @image_comparison(['imshow_flatfield.png'], remove_text=True, style='mpl20')
  768. def test_imshow_flatfield():
  769. fig, ax = plt.subplots()
  770. im = ax.imshow(np.ones((5, 5)), interpolation='nearest')
  771. im.set_clim(.5, 1.5)
  772. @image_comparison(['imshow_bignumbers.png'], remove_text=True, style='mpl20')
  773. def test_imshow_bignumbers():
  774. rcParams['image.interpolation'] = 'nearest'
  775. # putting a big number in an array of integers shouldn't
  776. # ruin the dynamic range of the resolved bits.
  777. fig, ax = plt.subplots()
  778. img = np.array([[1, 2, 1e12], [3, 1, 4]], dtype=np.uint64)
  779. pc = ax.imshow(img)
  780. pc.set_clim(0, 5)
  781. @image_comparison(['imshow_bignumbers_real.png'],
  782. remove_text=True, style='mpl20')
  783. def test_imshow_bignumbers_real():
  784. rcParams['image.interpolation'] = 'nearest'
  785. # putting a big number in an array of integers shouldn't
  786. # ruin the dynamic range of the resolved bits.
  787. fig, ax = plt.subplots()
  788. img = np.array([[2., 1., 1.e22], [4., 1., 3.]])
  789. pc = ax.imshow(img)
  790. pc.set_clim(0, 5)
  791. @pytest.mark.parametrize(
  792. "make_norm",
  793. [colors.Normalize,
  794. colors.LogNorm,
  795. lambda: colors.SymLogNorm(1),
  796. lambda: colors.PowerNorm(1)])
  797. def test_empty_imshow(make_norm):
  798. fig, ax = plt.subplots()
  799. with pytest.warns(UserWarning,
  800. match="Attempting to set identical left == right"):
  801. im = ax.imshow([[]], norm=make_norm())
  802. im.set_extent([-5, 5, -5, 5])
  803. fig.canvas.draw()
  804. with pytest.raises(RuntimeError):
  805. im.make_image(fig._cachedRenderer)
  806. def test_imshow_float128():
  807. fig, ax = plt.subplots()
  808. ax.imshow(np.zeros((3, 3), dtype=np.longdouble))
  809. with (ExitStack() if np.can_cast(np.longdouble, np.float64, "equiv")
  810. else pytest.warns(UserWarning)):
  811. # Ensure that drawing doesn't cause crash.
  812. fig.canvas.draw()
  813. def test_imshow_bool():
  814. fig, ax = plt.subplots()
  815. ax.imshow(np.array([[True, False], [False, True]], dtype=bool))
  816. def test_full_invalid():
  817. fig, ax = plt.subplots()
  818. ax.imshow(np.full((10, 10), np.nan))
  819. with pytest.warns(UserWarning):
  820. fig.canvas.draw()
  821. @pytest.mark.parametrize("fmt,counted",
  822. [("ps", b" colorimage"), ("svg", b"<image")])
  823. @pytest.mark.parametrize("composite_image,count", [(True, 1), (False, 2)])
  824. def test_composite(fmt, counted, composite_image, count):
  825. # Test that figures can be saved with and without combining multiple images
  826. # (on a single set of axes) into a single composite image.
  827. X, Y = np.meshgrid(np.arange(-5, 5, 1), np.arange(-5, 5, 1))
  828. Z = np.sin(Y ** 2)
  829. fig, ax = plt.subplots()
  830. ax.set_xlim(0, 3)
  831. ax.imshow(Z, extent=[0, 1, 0, 1])
  832. ax.imshow(Z[::-1], extent=[2, 3, 0, 1])
  833. plt.rcParams['image.composite_image'] = composite_image
  834. buf = io.BytesIO()
  835. fig.savefig(buf, format=fmt)
  836. assert buf.getvalue().count(counted) == count
  837. def test_relim():
  838. fig, ax = plt.subplots()
  839. ax.imshow([[0]], extent=(0, 1, 0, 1))
  840. ax.relim()
  841. ax.autoscale()
  842. assert ax.get_xlim() == ax.get_ylim() == (0, 1)
  843. def test_deprecation():
  844. data = [[1, 2], [3, 4]]
  845. ax = plt.figure().subplots()
  846. for obj in [ax, plt]:
  847. with pytest.warns(None) as record:
  848. obj.imshow(data)
  849. assert len(record) == 0
  850. with pytest.warns(MatplotlibDeprecationWarning):
  851. obj.imshow(data, shape=None)
  852. with pytest.warns(MatplotlibDeprecationWarning):
  853. # Enough arguments to pass "shape" positionally.
  854. obj.imshow(data, *[None] * 10)
  855. def test_respects_bbox():
  856. fig, axs = plt.subplots(2)
  857. for ax in axs:
  858. ax.set_axis_off()
  859. im = axs[1].imshow([[0, 1], [2, 3]], aspect="auto", extent=(0, 1, 0, 1))
  860. im.set_clip_path(None)
  861. # Make the image invisible in axs[1], but visible in axs[0] if we pan
  862. # axs[1] up.
  863. im.set_clip_box(axs[0].bbox)
  864. buf_before = io.BytesIO()
  865. fig.savefig(buf_before, format="rgba")
  866. assert {*buf_before.getvalue()} == {0xff} # All white.
  867. axs[1].set(ylim=(-1, 0))
  868. buf_after = io.BytesIO()
  869. fig.savefig(buf_after, format="rgba")
  870. assert buf_before.getvalue() != buf_after.getvalue() # Not all white.
  871. def test_image_cursor_formatting():
  872. fig, ax = plt.subplots()
  873. # Create a dummy image to be able to call format_cursor_data
  874. im = ax.imshow(np.zeros((4, 4)))
  875. data = np.ma.masked_array([0], mask=[True])
  876. assert im.format_cursor_data(data) == '[]'
  877. data = np.ma.masked_array([0], mask=[False])
  878. assert im.format_cursor_data(data) == '[0]'
  879. data = np.nan
  880. assert im.format_cursor_data(data) == '[nan]'
  881. @check_figures_equal()
  882. def test_image_array_alpha(fig_test, fig_ref):
  883. '''per-pixel alpha channel test'''
  884. x = np.linspace(0, 1)
  885. xx, yy = np.meshgrid(x, x)
  886. zz = np.exp(- 3 * ((xx - 0.5) ** 2) + (yy - 0.7 ** 2))
  887. alpha = zz / zz.max()
  888. cmap = plt.get_cmap('viridis')
  889. ax = fig_test.add_subplot(111)
  890. ax.imshow(zz, alpha=alpha, cmap=cmap, interpolation='nearest')
  891. ax = fig_ref.add_subplot(111)
  892. rgba = cmap(colors.Normalize()(zz))
  893. rgba[..., -1] = alpha
  894. ax.imshow(rgba, interpolation='nearest')