Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Errors in rendering boundaries between filled paths and within a quadmesh #1188

Closed
efiring opened this issue Sep 1, 2012 · 33 comments
Closed
Assignees
Labels
status: closed as inactive Issues closed by the "Stale" Github Action. Please comment on any you think should still be open. status: confirmed bug status: inactive Marked by the “Stale” Github Action

Comments

@efiring
Copy link
Member

efiring commented Sep 1, 2012

This is the underlying problem raised in #1178.
It is illustrated by the test below; note that boundary anomalies are visible in all forms--agg on the screen, and pdf and svg displayed with a viewer--but in different places depending on the viewer and the size of the figure as rendered.
Note that the colorbar is rendered using pcolormesh, which has its own renderer with agg but otherwise is handled by draw_path_collection.

import numpy as np
import matplotlib.pyplot as plt

z = np.arange(150)
z.shape = (10,15)

fig, axs = plt.subplots(2,2)

ax = axs[0,0]
cs0 = ax.contourf(z, 20)
cbar0 = fig.colorbar(cs0, ax=ax)

ax = axs[0,1]
cs1 = ax.contourf(z, 20, alpha=0.3)
cbar1 = fig.colorbar(cs1, ax=ax)

ax = axs[1,0]
im2 = ax.imshow(z, interpolation='nearest')
cbar2 = fig.colorbar(im2, ax=ax)

ax = axs[1,1]
im3 = ax.imshow(z, interpolation='nearest', alpha=0.3)
cbar3 = fig.colorbar(im3, ax=ax)

plt.savefig("test1.pdf")
plt.savefig("test1.svg")
plt.show()
@dmcdougall
Copy link
Member

If you start the code block with ```python you'll get some syntax highlighting.

@jenshnielsen
Copy link
Member

To me it seems like the pdf backend draws them correctly without addition edges however some viewers render these incorrectly and adds some white space. In the agg backend however even without edges there is an overlap between the paths visible only when alpha is less than 1. I guess this is an unrelated bug in the agg drawing code. It is correctly called with no edges. The pngs from the cairo backends are drawn without any overlap. However it does seem like this backend does not support alpha for imshow. I will open a separate issue for this.

@jenshnielsen
Copy link
Member

The pdf problem in evince is probably equivalent to this one
https://bugs.launchpad.net/ubuntu/+source/poppler/+bug/318130
which was reported in 2009

@jenshnielsen
Copy link
Member

Indeed the pdf problem in evince is fixed by applying the patch to poppler linked to in that bug report (0001-Align-fills-to-device-integer-coordinates.patch) so it seems like an aliasing problem.

jenshnielsen added a commit to jenshnielsen/matplotlib that referenced this issue Sep 23, 2012
the colorbar. Resolve problems with white lines in colorbars.
Not enabled by default.  See matplotlib#1178 and matplotlib#1188
jenshnielsen added a commit to jenshnielsen/matplotlib that referenced this issue Sep 26, 2012
mdboom added a commit that referenced this issue Sep 28, 2012
Add documentation of colorbar issue #1188 to colorbar documentation.
@dmcdougall
Copy link
Member

Did we decide that this is a viewer problem? If so, should this be closed as it requires an upstream fix?

@jenshnielsen
Copy link
Member

No there is another issue. The agg backend still renders the colorbar with overlap like the pdf did
before meaning that the colorbar with alpha < 1 will look wrong in png. Furthermore alpha is ignored for images (#1193) in the cairo backend so it is not actually possible to render png colorbars with alpha < 1 correctly.

@dmcdougall
Copy link
Member

@jenshnielsen Thanks for clarifying.

@awehrfritz
Copy link

Is there still somebody looking into this one? I'm using matplotlib 1.4.2 (linux-x64) and still get these white stripes in the colorbar or contourf plots.

I tested the pdf-files with various readers, i.e. okular (libpoppler), google-chrome, Adobe Reader/Acrobat XI‎ (both Windows), and always see these stripes.
Interestingly, for the ps-files the issue is less pronounced but I would say still present.
The svg-files also have the same issue as the pdf-files.

The stripes almost disappear when I plot the same data twice (see the script below), but I consider this as a very bad workaround.

The only fix that works consistently is to set edgecolor="face", but I guess that was also regarded as a workaround and not a fix.

Here is a script that shows how to reproduce and work around the white stripes for contourf plots and the colorbar. I hope it helps to debug and fix this problem.

#!/usr/bin/env python

""" This script illustrates a rendering bug for PDF and SVG files
"""

from matplotlib import pyplot as plt
import numpy as np

plt.close('all')

def gaussian_2d(x, y, x0, y0, xsig, ysig):
    return np.exp(-0.5*(((x-x0) / xsig)**2 + ((y-y0) / ysig)**2))

delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z = gaussian_2d(X, Y, 1., 1., 1.5, 0.5)

fig, axs = plt.subplots(2, 2, sharex=True, sharey=True)

ax = axs[0,0]
C = ax.contourf(X,Y,Z, 50, cmap='gnuplot')
fig.colorbar(C, ax=ax)
ax.set_title('contourf')

ax = axs[0,1]
C = ax.contourf(X,Y,Z, 50, cmap='gnuplot')
C = ax.contourf(X,Y,Z, 50, cmap='gnuplot')
fig.colorbar(C, ax=ax)
ax.set_title('2x contourf')

ax = axs[1,0]
C = ax.contourf(X,Y,Z, 50, cmap='gnuplot')
Cbar = fig.colorbar(C, ax=ax)
Cbar.solids.set_edgecolor("face")
plt.setp(C.collections, edgecolor="face")
ax.set_title('contourf + edgecolor="face"')

ax = axs[1,1]
C = ax.contourf(X,Y,Z, 50, cmap='gnuplot')
plt.setp(C.collections, rasterized=True)
fig.colorbar(C, ax=ax)
ax.set_title('contourf + rasterized')

fig.savefig('./Gaussian2D.ps')
fig.savefig('./Gaussian2D.pdf')
fig.savefig('./Gaussian2D.svg')
fig.show()

@jenshnielsen
Copy link
Member

As far as I understand this is a bug in the pdf renders which there is very little we can do about. If you draw 2 faces next to each other with no overlap a gab will often show due roundoff/snapping (Adobe seems to handle this better than most others in my tests).

We can't draw them with an overlap in general since that would break partially transparent contours with alpha< 1. Try adding alpha=0.5 to all your contourf calls to see what I mean i.e ax.contourf(X,Y,Z, 50, cmap='gnuplot', alpha=0.5) and so on.

Using edgecolor='face' is a workaround which adds a small border to all faces. The border gives an non zero overlap which looks strange when alpha<1 but is otherwise perfectly fine. I would consider that a way better solution than plotting the data twice.

@awehrfritz
Copy link

Thanks for your quick answer!

Alright, I see! So it really is related to the PDF renderer after all, this hasn't been clear to me from the discussion above.
But, as I said all PDF renderer I tested, including the newest Adobe ones, show the same issue. I cannot even say that Adobe is doing better then the other ones.

So the reason why edgecolor='face' is not set by default is due to issues related to transparency. In that case, couldn't one add edgecolor='face' for all plots where alpha is not explicitly set, something like:

if alpha is None:
    edgecolor='face'

I have no idea of the matplotlib internals, so I don't know where this should be best implemented.

@jenshnielsen
Copy link
Member

It might be possible to do something like that. I would have to think some more about this.

In my original use case way back in 2012 this was only an issue for colorbars where the edges are straight. In this case Adobe seems to handle it correctly and the patch in the attached bug report fixed it for Evince on Ubuntu. It seems to be somewhat worse in your use case and still not really fixed.

Briefly:

Using preview on OSX:
I see stripes in both the contourf and the colorbar.

Using Adobe reader XI:
I only see the stripes in the contourf but the colorbar looks fine.

@awehrfritz
Copy link

I actually agree with you that for plots with alpha=1 this workaround just works "perfectly fine".

So, I took a look at the source code, and for contourf plots, this workaround could probably be implemented like this:

--- anaconda/lib/python2.7/site-packages/matplotlib/contour.py  2014-10-23 20:53:19.000000000 +0300
+++ tmp/contour.py  2015-02-03 16:45:46.267598199 +0200
@@ -942,10 +942,14 @@
                 paths = self._make_paths(segs, kinds)
                 # Default zorder taken from Collection
                 zorder = kwargs.get('zorder', 1)
+                if self.alpha is None:
+                    edgecolors='face'
+                else
+                    edgecolors='none'
                 col = mcoll.PathCollection(
                     paths,
                     antialiaseds=(self.antialiased,),
-                    edgecolors='none',
+                    edgecolors=edgecolors,
                     alpha=self.alpha,
                     transform=self.get_transform(),
                     zorder=zorder)

@awehrfritz
Copy link

For pcolormesh this could be fixed directly in the high level function declaration like this:

--- anaconda/lib/python2.7/site-packages/matplotlib/axes/_axes.py   2015-02-03 16:35:25.849881579 +0200
+++ tmp/_axes.py    2015-02-03 18:13:43.624257733 +0200
@@ -5083,7 +5083,11 @@
         vmax = kwargs.pop('vmax', None)
         shading = kwargs.pop('shading', 'flat').lower()
         antialiased = kwargs.pop('antialiased', False)
-        kwargs.setdefault('edgecolors', 'None')
+        if alpha is None:
+            kwargs.setdefault('edgecolors', 'face')
+            kwargs.setdefault('linewidths', 0.0)
+        else
+            kwargs.setdefault('edgecolors', 'None')

         allmatch = (shading == 'gouraud')

Please note that all these suggestions are untested, and the colorbar would still need some additional work, since the function call to pcolormesh sets edgecolors='None' explicitly.

@tacaswell
Copy link
Member

@dkxls Sorry your comments went unnoticed for so long. Can you put those changes in a PR?

@efiring
Copy link
Member Author

efiring commented Jul 22, 2015

I'm uncomfortable with this hack; at the very least it needs to be checked with all backends to see what it actually does. Although the postscript standard is for linewidths of zero to be a device-dependent minimum, I think that in all backends we are treating linewidths of zero as "don't draw that line at all". If so, the hack will need some arbitrary small value for the linewidths.

@efiring
Copy link
Member Author

efiring commented Jul 22, 2015

As far as the colorbar is concerned, the problem has been reduced by #4481, which is also a bit of a hack.

@tacaswell tacaswell removed this from the 2.1 (next point release) milestone Sep 24, 2017
@mlincett
Copy link

is there a recommended best practice?

It depends on what you are trying to do and what you are trying to save to. edgecolor='face' is a reasonable workaround for most cases.

I am trying to save the product of Axes.hist2d as a vector graphics file, and I had naively chosen PDF a output format. Since the final aim is the embedding into LaTeX documents, probably I could try with (e)ps but I am not sure if the problem could reappear in the final PDF output.

By the way, trying edgecolor='face' produces a funny jigsaw artifact (regardless of png/pdf output), see attachment. (It is not my intention to turn this issue into a support request, but since the edgecolor workaround was been proposed as a solution to be implemented in pcolormesh itself I thought it was worth mentioning.)

jigsaw

@jklymak
Copy link
Member

jklymak commented Apr 22, 2021

Try hist2d(... rasterized=True) and then specify an appropriate dpi for savefig. That rasterizes the 2-d grid, but usually you don't need the polygons for a 2-D histogram.

@mlincett
Copy link

Try hist2d(... rasterized=True) and then specify an appropriate dpi for savefig. That rasterizes the 2-d grid, but usually you don't need the polygons for a 2-D histogram.

Thanks, I think this is viable.

@jklymak
Copy link
Member

jklymak commented Apr 22, 2021

I almost always do that anyways because usually a raster is smaller than a saved QuadMesh.

The real problem is contour which does not rasterize so far as I know.

@godot11
Copy link

godot11 commented Aug 27, 2021

It seems a bit annoying that this problem couldn't have been solved for almost a decade.

It's blocking me from creating a phase plot with an amplitude overlay. I found no way to overcome it, neither of the suggestions here or in a related SE thread helped. All backends seem to suffer from this problem, and it propagates to the saved figure as well.

If all plots using any colormap with transparency suffer from this bug, that seems like a huge issue to me - it makes this functionality utterly unusable.

Matplotlib version: 3.3.2

Example code:

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

# generate data
x = y = np.linspace(-2, 2, 300)
xx, yy = np.meshgrid(x, y)
ampl = np.exp(-(xx ** 2 + yy ** 2))
phase = (xx ** 2 - yy ** 2) * 3 * np.pi
data = ampl * np.exp(1j * phase)

# construct a simple b/w colormap with alpha
amp_cmap = {'red': [(0, 0, 0), (1, 1, 1)],
            'green': [(0, 0, 0), (1, 1, 1)],
            'blue': [(0, 0, 0), (1, 1, 1)],
            'alpha': [(0, 1, 1), (1, 0, 0)]}
ampcm = LinearSegmentedColormap('alpha', amp_cmap)

# plot
fig, ax = plt.subplots()
ax.pcolormesh(xx, yy, np.angle(data), cmap='hsv')
ax.pcolormesh(xx, yy, np.abs(data), cmap=ampcm, linewidth=0, rasterized=True, edgecolor='face')
plt.show()

Figure_2

@jklymak
Copy link
Member

jklymak commented Aug 27, 2021

@godot11 I cannot reproduce this on 3.4.2, so maybe upgrade? If you still have problems, please open a new issue with your explicit save command specified.

@godot11
Copy link

godot11 commented Aug 27, 2021

@godot11 I cannot reproduce this on 3.4.2, so maybe upgrade?

Oops, seems it was indeed matplotlib. It slipped my eyes that I did a conda update wrongly before I posted.
In case it may help here's my conda update output:

conda update log
nagyg -> conda update --all
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: /home/nagyg/anaconda3/envs/radpolpy


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    fftw-3.3.9                 |       h27cfd23_1         2.3 MB
    gsl-2.4                    |       h14c3975_4         1.8 MB
    ipython-7.26.0             |   py38hb070fc8_0         994 KB
    jedi-0.18.0                |   py38h06a4308_1         911 KB
    matplotlib-3.4.2           |   py38h06a4308_0          26 KB
    matplotlib-base-3.4.2      |   py38hab158f2_0         5.6 MB
    matplotlib-inline-0.1.2    |     pyhd3eb1b0_2          12 KB
    notebook-6.4.3             |   py38h06a4308_0         4.2 MB
    parso-0.8.2                |     pyhd3eb1b0_0          69 KB
    progressbar2-3.37.1        |   py38h06a4308_0          38 KB
    python-3.8.11              |h12debd9_0_cpython        18.2 MB
    termcolor-1.1.0            |   py38h06a4308_1           9 KB
    ------------------------------------------------------------
                                           Total:        34.2 MB

The following NEW packages will be INSTALLED:

  _openmp_mutex      pkgs/main/linux-64::_openmp_mutex-4.5-1_gnu
  brotli             pkgs/main/linux-64::brotli-1.0.9-he6710b0_2
  fonttools          pkgs/main/noarch::fonttools-4.25.0-pyhd3eb1b0_0
  libgomp            pkgs/main/linux-64::libgomp-9.3.0-h5101ec6_17
  matplotlib-inline  pkgs/main/noarch::matplotlib-inline-0.1.2-pyhd3eb1b0_2
  munkres            pkgs/main/noarch::munkres-1.1.4-py_0
  pip                pkgs/main/linux-64::pip-21.0.1-py38h06a4308_0
  wheel              pkgs/main/noarch::wheel-0.37.0-pyhd3eb1b0_0

The following packages will be REMOVED:

  libblas-3.8.0-21_mkl
  libcblas-3.8.0-21_mkl

The following packages will be UPDATED:

  fftw                                     3.3.8-h7b6447c_3 --> 3.3.9-h27cfd23_1
  ipython                             7.19.0-py38hb070fc8_0 --> 7.26.0-py38hb070fc8_0
  jedi                                0.17.2-py38h06a4308_1 --> 0.18.0-py38h06a4308_1
  libgcc-ng                                9.1.0-hdf63c60_0 --> 9.3.0-h5101ec6_17
  libxml2                                 2.9.10-hb55368b_3 --> 2.9.12-h03d6c58_0
  matplotlib                           3.3.4-py38h06a4308_0 --> 3.4.2-py38h06a4308_0
  matplotlib-base                      3.3.4-py38h62a2d02_0 --> 3.4.2-py38hab158f2_0
  mkl                                            2020.2-256 --> 2021.3.0-h06a4308_520
  mkl-service                          2.3.0-py38he904b0f_0 --> 2.4.0-py38h7f8727e_0
  mkl_fft                              1.3.0-py38h54f3939_0 --> 1.3.0-py38h42c9631_2
  mkl_random         conda-forge::mkl_random-1.2.0-py38hc5~ --> pkgs/main::mkl_random-1.2.2-py38h51133e4_0
  notebook                                     6.1.4-py38_0 --> 6.4.3-py38h06a4308_0
  numpy                               1.19.2-py38h54aff64_0 --> 1.20.3-py38hf144106_0
  numpy-base                          1.19.2-py38hfa32c7d_0 --> 1.20.3-py38h74d4b33_0
  parso               conda-forge::parso-0.7.1-pyh9f0ad1d_0 --> pkgs/main::parso-0.8.2-pyhd3eb1b0_0
  python                                   3.8.5-h7579374_1 --> 3.8.11-h12debd9_0_cpython
  scipy                                1.5.2-py38h0b6359f_0 --> 1.6.2-py38had2a1c9_1

The following packages will be SUPERSEDED by a higher-priority channel:

  gsl                       conda-forge::gsl-2.6-hf94e986_0 --> pkgs/main::gsl-2.4-h14c3975_4

The following packages will be DOWNGRADED:

  progressbar2                                3.37.1-py38_0 --> 3.37.1-py38h06a4308_0
  termcolor                                    1.1.0-py38_1 --> 1.1.0-py38h06a4308_1


Proceed ([y]/n)? y

(....)

@github-actions
Copy link

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Feb 27, 2023
@gkatev
Copy link

gkatev commented Feb 27, 2023

Re: github-actions, this is still present in 3.6.3.

@jklymak
Copy link
Member

jklymak commented Feb 27, 2023

@gkatev Can you tell us exactly what is still present? This thread has many different issues.

@gkatev
Copy link

gkatev commented Feb 27, 2023

Hi, yes, sorry for being vague. Below is where the issue is exhibited for me -- please let me know if it's not relevant.

This script reproduces the issue. Use this dataset: data.txt

#!/usr/bin/python

import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('data.txt', delim_whitespace=True)
df = df.pivot(index='core_1', columns='core_2', values='latency')

plt.pcolor(df, cmap='YlGnBu', rasterized=False)
plt.savefig('c2c.svg')

The resulting svg file has unexpected white lines between the cells (viewing it in eog or in firefox).

c2c

@QuLogic
Copy link
Member

QuLogic commented Feb 27, 2023

I know I assigned this ages ago, but I'm planning to work on it for 3.8. Though at the moment, I don't know that there is any way to fix SVG.

@QuLogic QuLogic removed the status: inactive Marked by the “Stale” Github Action label Feb 27, 2023
@jklymak
Copy link
Member

jklymak commented Feb 27, 2023

I think this is really hard to fix. This is basically the same issue as with contours and arises because it is impossible to anti-alias properly: https://github.com/jklymak/contourfIssues. If you make your svg viewer not anti-alias this problem goes away. However, I really suggest rasterizing the pcolor. It can make for larger files, but pdf/svg viewers are pretty efficient at dealing with images in the svg.

I actually think this should be closed as "can't fix", but maybe someone has a solution.

@QuLogic
Copy link
Member

QuLogic commented Feb 27, 2023

The original post is about all formats, and at least Agg and PDF can be fixed.

Copy link

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Feb 28, 2024
@github-actions github-actions bot added the status: closed as inactive Issues closed by the "Stale" Github Action. Please comment on any you think should still be open. label Mar 30, 2024
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Mar 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: closed as inactive Issues closed by the "Stale" Github Action. Please comment on any you think should still be open. status: confirmed bug status: inactive Marked by the “Stale” Github Action
Projects
None yet
Development

No branches or pull requests

10 participants