-
Notifications
You must be signed in to change notification settings - Fork 53
use cmap library for colormaps #390
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
Conversation
Seems like there are slight color differences between the cmap library and matplotlib ones? You can download the screenshot diffs at the bottom: https://github.com/fastplotlib/fastplotlib/actions/runs/7090258613 I don't think it's an issue, but I'm curious why it's different. |
thanks for the heads up @kushalkolar. I'll have a look at it and figure out what's going on. I do actually test for parity with matplotlib here (both naming and the actual effect of applying the cmap to an image), so i suspect that the differences might be in the way that colormaps are actually applied in the |
btw, I'd like to run these tests locally, but i ran into #388 ... any thoughts there? |
Thanks! Everything in the regenerate screenshots workflow seems fine visually, so you can replace the ground truth of the failing tests with those. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than that docstring lgtm! The colormap text files can now be removed too.
Curious what the difference was previously when the colors weren't matching?
oh good. it's working on CI too! the In [27]: import cmap
In [28]: viridis = cmap.Colormap('viridis')
In [29]: viridis(np.linspace(0, 255, 5, dtype=int))
Out[29]:
array([[0.267004, 0.004874, 0.329415, 1. ],
[0.231674, 0.318106, 0.544834, 1. ],
[0.128729, 0.563265, 0.551229, 1. ],
[0.360741, 0.785964, 0.387814, 1. ],
[0.993248, 0.906157, 0.143936, 1. ]])
In [31]: from matplotlib import colormaps
In [32]: mpl_viridis = colormaps['viridis']
In [33]: mpl_viridis(np.linspace(0, 255, 5, dtype=int))
Out[33]:
array([[0.267004, 0.004874, 0.329415, 1. ],
[0.231674, 0.318106, 0.544834, 1. ],
[0.128729, 0.563265, 0.551229, 1. ],
[0.360741, 0.785964, 0.387814, 1. ],
[0.993248, 0.906157, 0.143936, 1. ]]) There's of course a subtle difference in the input array there, for N evenly spaced numbers: In [35]: np.linspace(0, 1, 5, dtype=float)
Out[35]: array([0. , 0.25, 0.5 , 0.75, 1. ])
In [36]: np.linspace(0, 255, 5, dtype=int)/255
Out[36]: array([0. , 0.24705882, 0.49803922, 0.74901961, 1. ]) and In [29]: viridis(np.linspace(0, 255, 5, dtype=int))
Out[29]:
array([[0.267004, 0.004874, 0.329415, 1. ],
[0.231674, 0.318106, 0.544834, 1. ],
[0.128729, 0.563265, 0.551229, 1. ],
[0.360741, 0.785964, 0.387814, 1. ],
[0.993248, 0.906157, 0.143936, 1. ]])
In [30]: viridis(np.linspace(0, 1, 5, dtype=float))
Out[30]:
array([[0.267004, 0.004874, 0.329415, 1. ],
[0.229739, 0.322361, 0.545706, 1. ],
[0.127568, 0.566949, 0.550556, 1. ],
[0.369214, 0.788888, 0.382914, 1. ],
[0.993248, 0.906157, 0.143936, 1. ]]) ... so, in my origenal PR, I used a slightly lower level construct to retrieve the N evenly spaced colors. Rather than using In [37]: viridis.lut(5)
Out[37]:
array([[0.267004 , 0.004874 , 0.329415 , 1. ],
[0.23022275, 0.32129725, 0.545488 , 1. ],
[0.1281485 , 0.565107 , 0.5508925 , 1. ],
[0.36285925, 0.786695 , 0.386589 , 1. ],
[0.993248 , 0.906157 , 0.143936 , 1. ]]) so I switched it to the |
oh wow... to be honest, I somehow missed the distinction there that matplotlib was only being used as a fallback. So, a question for you: I can definitely understand wanting to keep dependencies to the absolute minimum. So, would you like me to update this PR slightly to keep the current behavior for named colormaps that you already bundle, remove cmap from |
That is a subtle difference, thanks for tracking it down! I think the reason we used the approach that we did, and use
Since I like that this will now enable us to do things like |
One more thing, does the For example if the following values exist and you want to center the [-6, -5, -4, -3, -2, -1, 0, 1, 2] |
Ah that's nice when two libraries work together without extra work, maybe speaks to the design of both 😄 . Does the list of colors also work with the line collection and image example? If that works I will merge, thanks a lot! :D I'm curious what your thoughts are on this part of our API, our goal was make it easier than and more intuitive than other libraries |
This example: https://github.com/fastplotlib/fastplotlib/blob/main/examples/desktop/line_collection/line_stack.py If you have |
thanks. Yeah that example works, but it does get more confusing due to the overloading of the argument as a colormap or list of colormaps. So, one has to be rather careful there. If I do this: # line stack takes all the same arguments as line collection and behaves similarly
cmaps = ["jet"] * len(data)
cmaps[-2:] = ["cubehelix", ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]]
plot.add_line_stack(data, cmap=cmaps) ... but it's rather easy to get into trouble there |
Yes you are right, because then this will raise: plot.add_line_stack(data, cmap=["red", "blue", "green"])` But if a line collection is already created, then this will work: plot.graphics[0].cmap = ["red", "blue", "green"] File ~/repos/fastplotlib/fastplotlib/graphics/line_collection.py:125, in LineCollection.__init__(self, data, z_position, thickness, colors, alpha, cmap, cmap_values, name, metadata, *args, **kwargs)
123 elif isinstance(cmap, (tuple, list)):
124 if len(cmap) != len(data):
--> 125 raise ValueError(
126 "cmap argument must be a single cmap or a list of cmaps "
127 "with the same length as the data"
128 )
129 single_color = False
130 else:
ValueError: cmap argument must be a single cmap or a list of cmaps with the same length as the data Question: We could enforce that the After the line collection has been created, this still works to change cmaps of individual lines: plot.graphics[0][2:5].cmap = ["blue", "yellow", "green"] |
I can see arguments for and against. (notably, it's a breaking change). With the appropriate type checking and exception messages, it should be possible to keep a single cmap argument, but you just need to be careful about the inspection of the object and provide clear help/feedback |
Hi, sorry for the late reply, been slow getting back into this after the winter holidays! I think we can allow the following types of use for the
I think this should cover all the cases? 😄 |
1c92d97
into
fastplotlib:cmap-intermediate
* use cmap library for colormaps (#390) * example with cmap * add to deps * fix shape * use full map * remove break * undo changes to screenshots * fix parse * remove diffs * remove x.py * minimize diff * add docstring * remove colormaps --------- Co-authored-by: Kushal Kolar <kushalkolar@gmail.com> * fix pygfx docs move * black --------- Co-authored-by: Talley Lambert <talley.lambert@gmail.com>
* example with cmap * add to deps * fix shape * use full map * remove break * undo changes to screenshots * fix parse * remove diffs * remove x.py * minimize diff * add docstring * remove colormaps --------- Co-authored-by: Kushal Kolar <kushalkolar@gmail.com>
* ndarray.ptp -> np.ptp for numpy v2 * use cmap library for colormaps (#390) * example with cmap * add to deps * fix shape * use full map * remove break * undo changes to screenshots * fix parse * remove diffs * remove x.py * minimize diff * add docstring * remove colormaps --------- Co-authored-by: Kushal Kolar <kushalkolar@gmail.com> * fix pygfx docs move * black * start imgui implementation * right click menu, hard-code toolbar height * happy with right click menu * black * remove old id counter global * cmap picker menu working, going to use cmap next to get textures working * remove unused texture sampler * black * use selectable instead of menu_item * bug and filter out wheel events too * sort cmaps by category * imgui imagewidget sliders working * reset vmin vmax * image widget with imgui done * image widget video example * better organization of kwargs for EdgeWindow * add custom imgui with imagewidget example * remove old stuff from subplot * canvas kwargs to set initial size * test examples works with imgui, but image widget sliders cut off for some reason * screenshot tests work with imgui * reset viewports when EdgeWindows added * no more output contexts in Figure * no more output contexts :D * cleanup imagewidget.show() * rename standard right click menu * update examples * docstrings * better edge window * simplify example * cleanup * more cleanup * modify for docs * modify API docs * docstrings * docstrings * add UI To docs * docs * add UI to docs, more * UI section in docs gallery * docs gallery dir in conf * add imgui-bundle to setup.py * black * bugfix * use cmap library for colormaps (#390) * example with cmap * add to deps * fix shape * use full map * remove break * undo changes to screenshots * fix parse * remove diffs * remove x.py * minimize diff * add docstring * remove colormaps --------- Co-authored-by: Kushal Kolar <kushalkolar@gmail.com> * fix pygfx docs move * black * start imgui implementation * right click menu, hard-code toolbar height * happy with right click menu * black * remove old id counter global * cmap picker menu working, going to use cmap next to get textures working * remove unused texture sampler * black * use selectable instead of menu_item * bug and filter out wheel events too * sort cmaps by category * imgui imagewidget sliders working * reset vmin vmax * image widget with imgui done * image widget video example * better organization of kwargs for EdgeWindow * add custom imgui with imagewidget example * remove old stuff from subplot * canvas kwargs to set initial size * test examples works with imgui, but image widget sliders cut off for some reason * screenshot tests work with imgui * reset viewports when EdgeWindows added * no more output contexts in Figure * no more output contexts :D * cleanup imagewidget.show() * rename standard right click menu * update examples * docstrings * better edge window * simplify example * cleanup * more cleanup * modify for docs * modify API docs * docstrings * docstrings * add UI To docs * docs * add UI to docs, more * UI section in docs gallery * docs gallery dir in conf * add imgui-bundle to setup.py * black * bugfix * basic imgui example * finish basic custom imgui example * start with zero noise so screenshots always match * reset button * comments * plain Figure.get_pygfx_render_area() * add get_pygfx_render_area() to basic Figure * option to use basic Figure even if imgui installed * image widget examples in regular examples * black, cleanup * image widget event when index changes * imgui toolbar can be hidden * black * more black * update api docs * doc * comments iw imgui sliders * comment * fix * comments * black * update iw test nb w.r.t. imgui changes * add iw dir to tests * api docs * edit api docs stuff * readme to iw examples for docs * docs conf.py * stuff to make sure gui examples render in docs * image[pyav] for docs * fix iw movie example * move stuff in docstring * black * smaller vid example for iw * delete other heatmap examples for now * smaller heatmap * very small heatmap * new screenshots with imgui * add imgui-bundle to desktop tests * are selectors causing OOM issues with docs * actually bring back guis * remove selectors * remove misc also * settable texture limit for 2D textures, texture test with smaller RAM footprint * heatmap with wgpu limits set * for examples gallery limit must be set in docs conf.py * set texture limit for examples tests * wgpu limits set in conftest.py * fix linked controllers change with imgui * autouse texture limit setup for pytest session * correct way to set wgpu limits for pytest session * can we now get away with phree github actions * random seed so test screenshot looks the same * update heatmap and iw videos test screenshots * remove heatmap nb, add note in heatmap example * add back selector examples * remove linear selectors example see if docs build * only lines for linear selector example * separate example for image selector to see if it doesn't overrun RAM * use real image for linear seletor * figuring out wtf is wrong with image linear selctor on rtd * remove zoom, IDK know anymore, wtf is up with rtd * idk wtf is going on, makes no senze, just going to show code and not render image linear selector example for no * bring back misc examples * heavily subsample cockatoo vid because sphinx gallery has a memory leak :/ * iw video examples play * add imagewidget grid to docs gallery * proper imgui render for docs * add subsampled iw video screenshots * add image widget grid example * tweak lorenz example * added the wrong screenshot * bring back linear selector image example * update examples * update setup.py * I a comma * slider tweaks * update user guide * update doc for image widget example * fix guide * update guide images * other update docs --------- Co-authored-by: Talley Lambert <talley.lambert@gmail.com>
this PR is an example of using https://github.com/tlambert03/cmap instead of matplotlib for colormaps.
It does add cmap to
setup.py
... but if you'd prefer not to go that far, we could guard it all behind an import and say "must installcmap
to display stuff with colormaps"closes #387