Title: Rasterization creates multiple bitmap elements and large file sizes · Issue #17149 · matplotlib/matplotlib · GitHub
Open Graph Title: Rasterization creates multiple bitmap elements and large file sizes · Issue #17149 · matplotlib/matplotlib
X Title: Rasterization creates multiple bitmap elements and large file sizes · Issue #17149 · matplotlib/matplotlib
Description: Bug summary I often work with plots have have a large number of lines, for example showing the trajectories of a large number of particles in a physics simulation. For publication it is good for the axis to be in vector format, but keepi...
Open Graph Description: Bug summary I often work with plots have have a large number of lines, for example showing the trajectories of a large number of particles in a physics simulation. For publication it is good for th...
X Description: Bug summary I often work with plots have have a large number of lines, for example showing the trajectories of a large number of particles in a physics simulation. For publication it is good for th...
Opengraph URL: https://github.com/matplotlib/matplotlib/issues/17149
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Rasterization creates multiple bitmap elements and large file sizes","articleBody":"**Bug summary**\r\n\r\nI often work with plots have have a large number of lines, for example showing the trajectories of a large number of particles in a physics simulation. For publication it is good for the axis to be in vector format, but keeping all the tracks as vector can create large file sizes.\r\n\r\nRasterization of some elements in the plot can solve this. Matplotlib offers `rasterized` kwarg to `plot()`, or `Artist.set_rasterized()`. But for this use case it results in a separate bitmap element for each line, making the file size even larger.\r\n\r\nI'm not the first person to have issues related to this #13718\r\n\r\n**Code for reproduction**\r\n\r\nFor example, this creates a 2.6MB file:\r\n\r\n```\r\nN = 20\r\nrast = True\r\n\r\ns = np.arange(100)\r\ntracks = []\r\n\r\nfor a in np.logspace(0.001, 0.5, N):\r\n\tfor t in np.linspace(0.1, pi, N):\r\n\t\tt = a*np.sin(s*0.05 + t)\r\n\t\ttracks.append(t)\r\n\r\nfor t in tracks:\r\n\tplt.plot(s, t, \"-k\", alpha=0.5, lw=0.5, rasterized=rast)\r\nplt.savefig(\"tracks1.svg\")\r\n```\r\n`grep \"\u003cimage\" tracks1.svg | wc -l` shows that there are 400 image elements.\r\n\r\nCalling plot just once with all the tracks makes no difference.\r\n\r\n```\r\ntracks_na = np.array(tracks).T\r\nplt.plot(s, tracks_na, \"-k\", alpha=0.5, lw=0.5, rasterized=rast)\r\n```\r\n\r\n\r\n**RFC**\r\n\r\nThis happens due to the way `start_rasterizing()` and `stop_rasterizing()` get called the from `allow_rasterization()` wrapper. If `rasterized` is set on the Line2D objects, then start and stop are called around each Line2D.draw(). `stop_rasterizing()` renders out to bitmap and calls `_renderer.draw_image()`. The only way to stay in rasterizing mode is `set_rasterized()` on the parent artist of the lines, which would be the axes, but that means that the axes labels and everything else get rasterized.\r\n\r\nI have thought of a few solutions, but I would like some feedback before starting.\r\n\r\nI think an ideal solution would be automatic for the user. matplotlib would just merge the bitmaps in an optimal way. This would need not effect draw order, so would need to be smart about zorder. It might avoid merging 2 non-overlapping bitmaps if that increases total area of bitmap.\r\n\r\nA possible implementation would be changes to the `allow_rasterization` wrapper so that the rasterization does not stop between consecutive rasterized artists.\r\n\r\nA more manual approach is to create an object that can contain multiple artists, and therefor keep the render in rasterization mode for drawing those artists. `Collections` don't fit this role as they have some limitations, e.g. `LineCollection` does not have markers. I also looked at `Container` but that seems designed for specific use cases. So this could be `ArtistGroup` or `ArtistCollection`. It would derive from Artist, and its `draw()` method would iterate through its children and draw them.\r\n\r\nI have a minimal prototype, that lets me do:\r\n```\r\nag = ArtistGroup()\r\nfor t in tracks:\r\n\tag.add_child(matplotlib.lines.Line2D(s, t, color=\"k\", linestyle=\"-\", alpha=0.5, lw=0.5))\r\nag.set_rasterized(True)\r\nplt.gca().add_artist(ag)\r\nplt.savefig(\"tracks3.svg\")\r\n```\r\nThis results in a 304 kB file with a single `\u003cimage\u003e` element (compared with 2.6 MB with original code). Increasing `N` makes the file size difference even larger.\r\n\r\nIf this is a good approach it could maybe be used inside the `plot()` and similar commands, so that lines drawn with a single plot would end up in a single rasterization (when requested) with no further user input.\r\n\r\n**Matplotlib version**\r\n\r\n * Operating system: Linux\r\n * Matplotlib version: 3.0.3 and git master\r\n * Matplotlib backend: svg\r\n * Python version: 3.7.6\r\n\r\nNote, I have been testing with the SVG backend, because its easier to see what is going on the output files. Judging from file size and loading times all this is true in the PDF backend too.","author":{"url":"https://github.com/samtygier","@type":"Person","name":"samtygier"},"datePublished":"2020-04-15T10:50:22.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":8},"url":"https://github.com/17149/matplotlib/issues/17149"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:d70e922b-be1e-bb90-8da5-f81d9d1e1d00 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | CE76:1D4F69:54D81D:70B0E0:6A52F9D0 |
| html-safe-nonce | 0116e2a6285d1479c5a87dedaa7c83cf7c3bf3f52de3bbec9c3ac503a833b9aa |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDRTc2OjFENEY2OTo1NEQ4MUQ6NzBCMEUwOjZBNTJGOUQwIiwidmlzaXRvcl9pZCI6IjE5NTMwODA1NDkzNTk5NDIwOTYiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 5c0c141aecb1a66fff1904d1a461ef16d862591c639122fc6f63b4c630019825 |
| hovercard-subject-tag | issue:600205697 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/matplotlib/matplotlib/17149/issue_layout |
| twitter:image | https://opengraph.githubassets.com/2e7d9933f665235a70770ec38c60c362023ffe2a7b52dacd6b5febbdc0797508/matplotlib/matplotlib/issues/17149 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/2e7d9933f665235a70770ec38c60c362023ffe2a7b52dacd6b5febbdc0797508/matplotlib/matplotlib/issues/17149 |
| og:image:alt | Bug summary I often work with plots have have a large number of lines, for example showing the trajectories of a large number of particles in a physics simulation. For publication it is good for th... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | samtygier |
| hostname | github.com |
| expected-hostname | github.com |
| None | b9a586c06a05a7a86fc7e3f4dbd03e42f6869085879aa184aa6369456dbd50fb |
| turbo-cache-control | no-preview |
| go-import | github.com/matplotlib/matplotlib git https://github.com/matplotlib/matplotlib.git |
| octolytics-dimension-user_id | 215947 |
| octolytics-dimension-user_login | matplotlib |
| octolytics-dimension-repository_id | 1385122 |
| octolytics-dimension-repository_nwo | matplotlib/matplotlib |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1385122 |
| octolytics-dimension-repository_network_root_nwo | matplotlib/matplotlib |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 07a982c1d40157c619b364352b704c3ce66bb332 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width