From 104e6242a1d51abbc53d498de13f237edc250583 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Mon, 26 Feb 2018 13:22:57 -0800 Subject: [PATCH 1/5] FIX: fix big number color resolution issue --- lib/matplotlib/image.py | 16 ++++++++++++++++ lib/matplotlib/tests/test_image.py | 2 -- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 9d6b3c04ff18..7cf61b75cb02 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -396,6 +396,22 @@ 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 = self.norm.vmax - self.norm.vmin + vmid = self.norm.vmin + dv / 2 + newmin = vmid - dv * 1.e7 + if newmin > a_min: + A_scaled[A_scaled < newmin ] = newmin + a_min = np.float64(newmin) + newmax = vmid + dv * 1.e7 + if newmax < a_max: + A_scaled[A_scaled > newmax] = newmax + a_max = np.float64(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 diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 16be520e325f..35b69729d074 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -855,8 +855,6 @@ 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() - @pytest.mark.parametrize( "make_norm", From 4529d6f8106527817f1af87a1208e76a4ffa4ce0 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Mon, 26 Feb 2018 13:29:13 -0800 Subject: [PATCH 2/5] TST: test for big floats in array being clipped properly if clim reset --- .../test_image/imshow_bignumbers_real.png | Bin 0 -> 3312 bytes lib/matplotlib/tests/test_image.py | 13 +++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 lib/matplotlib/tests/baseline_images/test_image/imshow_bignumbers_real.png 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 35b69729d074..4aa72a5a3ad4 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -856,6 +856,19 @@ def test_imshow_bignumbers(): pc = ax.imshow(img) pc.set_clim(0, 5) + +@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( "make_norm", [colors.Normalize, From 2dd9d54f7424164ea1e9b0084b1a61d153dcc264 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Mon, 26 Feb 2018 14:48:34 -0800 Subject: [PATCH 3/5] Make clip and force vmin and vmax to float64 --- lib/matplotlib/image.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 7cf61b75cb02..e11497b69801 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -402,16 +402,22 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # 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 = self.norm.vmax - self.norm.vmin + 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: - A_scaled[A_scaled < newmin ] = newmin + if newmin < a_min: + newmin = None + else: a_min = np.float64(newmin) newmax = vmid + dv * 1.e7 - if newmax < a_max: - A_scaled[A_scaled > newmax] = newmax + 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 From 332edf03970f7a9cc3297c6b894a66f797991d20 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Tue, 27 Feb 2018 09:05:13 -0800 Subject: [PATCH 4/5] FIX convert 2-d PIL image --- lib/matplotlib/image.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index e11497b69801..45436619f227 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -636,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) From e0fab0d22451cdd88cdb7d2a5ad9d906b744e1d6 Mon Sep 17 00:00:00 2001 From: Jody Klymak Date: Tue, 27 Feb 2018 09:29:30 -0800 Subject: [PATCH 5/5] TST convert 2-d PIL image --- lib/matplotlib/tests/test_image.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 4aa72a5a3ad4..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)