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
switch from papaya to brainsprite in plotting.view_stat_map #1766
Conversation
updating simexp/nilearn
Co-authored-by: Christian Dansereau <christiandansereau@gmail.com> Co-authored-by: Sebastian Urchs <sebastian.urchs@gmail.com>
Co-authored-by: Christian Dansereau <christiandansereau@gmail.com> Co-authored-by: Sebastian Urchs <sebastian.urchs@gmail.com>
That would be a great feature to add. |
…. 2. switched to a nearest interpolation for background, because it is faster, and avoids an annoying warning for images with integer precision.
I just tested the functionality, and it works very well. In addition to
the memory savings (which are a big deal), I also prefer the
look-and-feel. The fact that it is very similar to what the other
plotting functions give is very useful. In particular the display of the
cut coordinates. Thank you!
A few comments on the look and feel:
- Could we have the same default behavior as plot_stats_map: threshold
being set to 1e-6, to cut out near zeros.
- The IFrame is now too tall. Can you make it smaller, so that it fits
closely the plot. This more compact view will be, by itself, a good
gain. Indeed, I found that working in a complex jupyter notebook with
those very large plots was not very convenient.
Maybe more later. I need to switch to something else.
|
@GaelVaroquaux great to hear you like it! I ran the code through http://pep8online.com/ and fixed all detected issues. For the iframe, I will make it fit (and try to adjust size to screen, currently even the papaya viewer is broken when visualized on a phone). For the threshold, I tried but somehow the result is inconsistent with plot_stat_map. I'll dig further. As I said there are lots of work left, from the tests to the doc, to other minor things listed here. |
this will be a great improvement. thanks a lot! I'll add some more detailed comments later this week |
I think I solved the fit of the iframe. Check this example to see how it looks now. To solve this issue, I had to change the code of With the new code,
I've set the default width at 75%, and the default ratio at 68%. I've checked and Note that the Regarding the code, the action happens here Finally, when it comes to brainsprite, after this change to |
I think I would prefer to write the style rather than use a bootstrap class, in case someone wants to put the brainsprite in a webpage that doesn't use bootstrap |
nilearn/plotting/html_stat_map.py
Outdated
if isinstance(cm, str): | ||
cmap = plt.cm.get_cmap(cmap) | ||
|
||
img = check_niimg_3d(img, dtype='auto') |
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.
could we allow 4d images with only one frame (note: the current behaviour is that of master, but other functions allow it)
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.
is there a simple helper function for that, like check_niimg_3d ?
nilearn/plotting/html_stat_map.py
Outdated
threshold = fast_abs_percentile(data) - 1e-5 | ||
|
||
# threshold | ||
threshold = float(threshold) if threshold is not None else None |
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.
please use check_threshold from utils.param_validation
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.
So I checked this function. The syntax is a bit awkward, as it forces me to also import a function percentile_func
. And as far as I understand, the behavior of threshold
would change, as it would allow for threshold like "90%", which is not documented. I am tempted to put that off as future amelioration. What is currently implemented actually fits the doc (and plot_stat_map).
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.
Now I see it's included in the tests. I'll fix that.
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.
OK so I have added the feature, as well as a short description in the doc of threshold
.
nilearn/plotting/html_stat_map.py
Outdated
def view_stat_map(stat_map_img, bg_img='MNI152', cut_coords=None, | ||
colorbar=True, title=None, threshold=None, annotate=True, | ||
draw_cross=True, black_bg='auto', cmap=cm.cold_hot, | ||
symmetric_cbar='auto', dim='auto', vmax=None, |
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.
please keep the symmetric_cmap option to enable plotting anatomical images
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.
This has been addressed, hasn't it?
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.
This has been addressed, hasn't it?
no, symmetric_cmap
is still missing, which is required to plot anatomical images
why did the jquery version need to change? |
please run flake8 again, there are a few things to fix, in particular unused imports and variables |
nilearn/plotting/html_stat_map.py
Outdated
title : string or None (default=None) | ||
The title displayed on the figure (or None: no title). | ||
This parameter is not currently supported. | ||
threshold : str, number or None (default=None) |
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.
this breaks if a string is given, because find_cut_coords is passed a string. please use check_threshold
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.
was also brought up by @kchawla-pi . Will work on that.
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.
mmhh, now I realize I did not understand the doc. I thought str referred to the case 'auto'. But what you are saying is that threshold="90%" should be supported?
Hi @pbellec Thanks for doing this! |
I am having second thoughts about the strategy to resize the iframe. In any case we will need different rules for different screen sizes, so let's discuss this in another thread. All I will do in the |
Yes, I agree. There are several parts of the code that could be naturally splitted. Will work on this. |
It doesn't, but brainsprite somehow broke with the newer version. I reverted back to an old version to open the PR, but I will update and fix the incompatibility asap. |
An alternative would be to not display the value at all when it is NaN.
Well it is strictly showing what the colorbar is showing.
I do appreciate being able to read exact values, especially when showing peaks. Reading from the colorbar is very imprecise. Another consideration in favor of getting rid of the value is that color discretization comes with rounding approximation that some user may pick on: those are not strictly the values you will read in your data array using nibabel, but rather the values you get after spatial resampling and moving to 256 discrete colors. Let me know what you think. |
Once CircleCI passes, I am merging this. |
yes as you point out here we are not reading the exact values, we are reading what is shown on the plot + colorbar. so we get the same imprecisions, thresholding and cropping. in particular when showing peaks, we are not reading the peaks' values but only vmax.
but it is known that the information we get from the colorbar is a bit imprecise. on the other hand, if we display an actual number, as a user I would expect it to be the true value in the original image, not something that is read from the plot. So I guess I would be in favor of not displaying a value at all. I'm sorry to realize this so late, especially after you added the function to de-duplicate colormaps. But this is only my opinion, maybe @GaelVaroquaux or @KamalakerDadi will prefer displaying the value anyway. appart from a few details I'm glad to see this is getting really close to merge! |
awesome! can we finish the discussion about displaying values first? it will not be a big change either way |
I can wait. There is also the option of you creating a new PR after this is merged and making the change. I think that will be best. Lemme know. |
It is likely that we will decide not to change anything at all. but if we decide not to display the value, it would be best if @pbellec does it since he wrote the code. |
Agreed.
Happy to contribute to future development as well. |
Cool. @pbellec can do a new PR, while we can merge the rest and remove any merge conflicts that may crop up. |
…ub.com/SIMEXP/nilearn) * 'master' of https://github.com/SIMEXP/nilearn: (103 commits) fix doc line too long. Added brief summary of the PR in whats_new.rst Disable `value` when `colorbar=False` in `view_stat_map`. Improved error message when improper cut_coords is provided. Getting rid of _get_vmin_vmax, which is not used. Improving doc and comments, thanks to @KamalakerDadi's feedback. Added a `See Also` section to `view_stat_map`. Using `_utils.compat._encodebytes` instead of local import for python2/3 compatibility. Added `_encodbytes`, mapping to `base64.encodebytes` in python 3 and `base64.encodestring` in python 2. Some docs improvement, following up on @KamalakerDadi's suggestions. Use _utils.compat to deal with python2/3 compatibility. Bug fix in the doc. The screenshot for view_stat_map using brainsprite. Updated doc / screenshot from the papaya viewer to the brainsprite viewer. Updating brainsprite.js to fix a rounding error for `cut_coords` that resulted in broken value being reported in some situations. A bunch of improvements, mostly on naming and docstring. Added a test that `_get_vmin_vmax` works properly with NaNs in the data. Removed () following `assert`. Added some error messages for failed tests. Improve the docstring for the viewer. 1. remove variable names with png. 2. force the colormap image to float. ...
Any consensus on making the changes or not? |
@jeromedockes In this case, you suggest to display the value of underlying image (bg_img) rather than NaN ?
Yes, because vmax is set by the user. By default, we provide vmax=None which displays according to the values of inside the images ? Anything we could do to improve the current situation about displaying value ? |
The problem is that there is a design issue with the way brainsprite displays We have data, a 3d image, and a first view of it, a plot + colorbar. The view, Then, as is often done in such interactive viewers, brainsprite wants to offer a But in brainsprite, this second view doesn't have access to the data: it tries I don't think this "value = ..." can be made to work correctly unless its design Moreover, this field is not essential; the viewer is already super useful I would therefore suggest we don't display this field for now. If in the future |
I disagree, it makes much more sense to leave it aside and add it when we have a |
We are saying the same thing. Deal with this in a separate PR later. |
No. displaying image values needed to be removed before merging this. then possibly added back in the future (probably not in the near future since it would require important changes in brainsprite). |
@jeromedockes I disagree with the fact the feature is broken. It has the limitations inherent to the visualization presented to the user, no more no less. It is impossible to change the design of brainsprite to address your concern: it would require to store the original data, which is exactly what brainsprite was designed not to do. In any case, if you think this feature is problematic, I have no problem removing the value and the colormap deduplication. I don't think I can commit in #1856, but I can open a separate PR, which can be merged in #1856 or in master directly. |
I think I'd rather remove the feature for the moment, because it will probably lead to some confusion on the user side. |
Absolutely no problem. It was some work, but useful work. The ability to retrieve values after click is critical to make the viewer interact with other plots. My vision for brainsprite is to become a building block for complex dashboards. I'll move the deduplicate_cmap code to a different code base. It's just not useful for view_stat_map, and that's ok :) |
I would be in favor of this. sorry again for reacting so late on this. let's repeat the most important point: this PR is a huge win for nilearn, with or without this feature. thanks again @pbellec for this great contribution! |
OK so we have a consensus, will work on this asap. Now that the big merge has occured (!) I would like to thank you a lot for your patience and sharing your insights @jeromedockes @KamalakerDadi @kchawla-pi and @GaelVaroquaux . I have learned tons going through this PR thanks to you, and I believe this experience is going to help me a lot producing better code. Best python course ever :) |
Awesome work by you @pbellec .Congratulations! |
Yes, this is an awesome PR, and I am really happy to see this addition to nilearn.
We'll be releasing soon, now that this is merged.
…On Nov 11, 2018, 00:57, at 00:57, KamalakerDadi ***@***.***> wrote:
Awesome work by you @pbellec .Congratulations!
--
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub:
#1766 (comment)
|
+1 ! |
The rename of |
thanks a lot! great work! |
I really love the new 3D interactive viewer (
plotting.view_stat_map
), but the notebooks it is producing are huge. In this PR, I am proposing to switch from papaya to brainsprite, which is a js library I developed for the exact purpose of embedding lightweight 3D viewers in html pages (http://github.com/simexp/brainsprite.js).The first difference with papaya is that it is using a jpg or a png containing all sagital slices of a volume as well as json metadata to store the brain images. That tend to be quite smaller than a nifti (depending on the numerical precision of the nifti). That also means that brainsprite can render brains with core html5 features, and no dependencies. So the brainsprite library weighs 15kb (500 lines...), as opposed to 2Mb for the current papaya html template. I have attached two brain viewers embedded in jupyter notebooks. The Papaya-based notebook is 12Mb, while the brainsprite-based notebook is 500kb. Again, this reflects a core difference in design: papaya is a full brain viewer app, featuring nifti reading as well as colorbar etc. Brainsprite is a minimal, fast brain viewer working from a pre-generated sprite.
Which makes a transition with the second point: all the action for the generation of the brain volume happens in python. There is a new function called save_sprite that generates the brain sprite as well as the json meta data. It relies on matplotlib, as well as nilearn's own functions. In particular, thresholding and colormap generation are all done with nilearn's code. Resampling as well. This means that it will be easier to maintain and evolve for nilearn's developpers. The current version replicates all the arguments of plot_stat_map, including draw_cross, annotate, cut_coords and a few other (with a few as bonus, such as opacity).
This PR is far from polished, there are a few oustanding issues, here. I also need to look into the doc and testing. Finally, I dumped some functions in
html_stat_map.py
which should probably live elsewhere. But I think it is time to get feedback, and in particular I'd like to know if there is an interest in merging this PR at all...