From 913f1724fff8fb84825e7c5127e670a3cc0a0e42 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 3 Mar 2018 16:23:13 -0500 Subject: [PATCH] Backport PR #10613: FIX: resolution of imshow for floats and 2-D greyscale PIL images --- lib/matplotlib/image.py | 28 +++++++++++++++++- .../test_image/imshow_bignumbers_real.png | Bin 0 -> 3312 bytes lib/matplotlib/tests/test_image.py | 25 +++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index de87c2afaa44..f2a7280af553 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -396,6 +396,28 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # scaled data A_scaled = np.empty(A.shape, dtype=scaled_dtype) A_scaled[:] = A + # clip scaled data around norm if necessary. + # This is necessary for big numbers at the edge of + # float64's ability to represent changes. Applying + # a norm first would be good, but ruins the interpolation + # of over numbers. + if self.norm.vmin is not None and self.norm.vmax is not None: + dv = (np.float64(self.norm.vmax) - + np.float64(self.norm.vmin)) + vmid = self.norm.vmin + dv / 2 + newmin = vmid - dv * 1.e7 + if newmin < a_min: + newmin = None + else: + a_min = np.float64(newmin) + newmax = vmid + dv * 1.e7 + if newmax > a_max: + newmax = None + else: + a_max = np.float64(newmax) + if newmax is not None or newmin is not None: + A_scaled = np.clip(A_scaled, newmin, newmax) + A_scaled -= a_min # a_min and a_max might be ndarray subclasses so use # asscalar to ensure they are scalars to avoid errors @@ -614,7 +636,11 @@ def set_data(self, A): """ # check if data is PIL Image without importing Image if hasattr(A, 'getpixel'): - self._A = pil_to_array(A) + if A.mode == 'L': + # greyscale image, but our logic assumes rgba: + self._A = pil_to_array(A.convert('RGBA')) + else: + self._A = pil_to_array(A) else: self._A = cbook.safe_masked_invalid(A, copy=True) diff --git a/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png b/lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png new file mode 100644 index 0000000000000000000000000000000000000000..ce3404488ebb88daece7f11dc5ba9519bccb6766 GIT binary patch literal 3312 zcmeH~ZA_C_6vt1yrYpt@lO?F-r3t$Ed$Rz@k*QajT^TRMeO+U~(G`?3$)M>bA!b8}Db z$<00a-TyiF@^Q`w#7#Rk0RS=mNZLmLSOoxPmVie+aZe0$=!7dxO+P_EO+v_TK-WRS zBfL_8;M}!=`Ig;PfI3fz4xJKxjGPx$OG*onA~J)@pwdh7 zVyHr-RK$p*Q}g-d^!EfY^jQ7@Ml2(jLcSmp2@`3wzhbFKiGb!m8LI}^%t%i=ctYGU zKXvfCGx{2hDf(rczV6PCIpf3q`N9@r{?B=|uj&UCkxN@X+4=|h_?2<`v2i@(D89y6 zbEEiC;mNyG^T*z=+K$^hzGG+RUdigRG|Dg{z2MYiZbVs+awAbgue_Zr2nLwd>;ep& z!;vAS5pdA(jshSC8v^9RfQ1Ky6i{r#5FjLaW7EG&!B5k6O!LEg-FavAHm^)aPbNRcrX7FJ7fveU{ysXLU!mRjUP z7VfTf!-3&Zr1SHew#27pgG1TWw#yd*j8{o-Quu7XTrW`1=#)6P^#vGah0I2`MY6Bcy%Q9VBv@k(ueFz= zwQombeju(Nq5E6FQJTd`OFi9*aTZx=|DTvDN)5;X3CH8!u2?_`I>(6qNvAAS`~JiT(x+{|~vl+nzid!_$}D zv{gyfXQ-ii6joBaf)w7Y>7API*2&OH83Z}|G5sSZ;Wn$=v(mxonrs0$;6|DMj{vT- zy0hheb%1?w>wv}UP`4*zjLwxQwoVC%*e93!?ys6mxr^5-l}Vq;mq>#WPl;g$t$i3U z42NY)Vpv|g3RQs+1!>iZcmDc)rg?(Pj>wjIENGbwZA+G+HN?K;El#{X=C8t0T6bzr?Vm7^lmP$$ literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 16be520e325f..26e3b4a7ea26 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -511,6 +511,18 @@ def test_nonuniformimage_setnorm(): im.set_norm(plt.Normalize()) +@needs_pillow +def test_jpeg_2d(): + # smoke test that mode-L pillow images work. + imd = np.ones((10, 10), dtype='uint8') + for i in range(10): + imd[i, :] = np.linspace(0.0, 1.0, 10) * 255 + im = Image.new('L', (10, 10)) + im.putdata(imd.flatten()) + fig, ax = plt.subplots() + ax.imshow(im) + + @needs_pillow def test_jpeg_alpha(): plt.figure(figsize=(1, 1), dpi=300) @@ -855,7 +867,18 @@ def test_imshow_bignumbers(): img = np.array([[1, 2, 1e12],[3, 1, 4]], dtype=np.uint64) pc = ax.imshow(img) pc.set_clim(0, 5) - plt.show() + + +@image_comparison(baseline_images=['imshow_bignumbers_real'], + remove_text=True, style='mpl20', + extensions=['png']) +def test_imshow_bignumbers_real(): + # putting a big number in an array of integers shouldn't + # ruin the dynamic range of the resolved bits. + fig, ax = plt.subplots() + img = np.array([[2., 1., 1.e22],[4., 1., 3.]]) + pc = ax.imshow(img) + pc.set_clim(0, 5) @pytest.mark.parametrize(