nipy / PySurfer

Cortical neuroimaging visualization in Python
https://pysurfer.github.io/
BSD 3-Clause "New" or "Revised" License
241 stars 97 forks source link

Save_montage command with "both" hemispheres truncated one of the two #282

Closed mattvan83 closed 4 years ago

mattvan83 commented 4 years ago

Dear PySurfers,

I would like to save a montage with both hemispheres in ventral and dorsal views, whose data have been added through multiple calls to add_data (conjunction maps). When windows display it seems OK, but when using save_montage to save both views of both hemispheres, one of the two hemisphere is truncated.

Please find below my code:

min_thr_color = 1.3

"""
Load fdg overlay arrays.
"""
## Load TFCE p-corrected maps
overlay_file_fdg_lh = os.path.join("fdg", "lh.palm.fdr0.CorrPos_tfce_tstat_fwep.mgz")
overlay_file_fdg_rh = os.path.join("fdg", "rh.palm.fdr0.CorrPos_tfce_tstat_fwep.mgz")

overlay_array_fdg_lh = nib.load(overlay_file_fdg_lh)
overlay_array_fdg_lh = overlay_array_fdg_lh.get_fdata()
overlay_array_fdg_lh = np.squeeze(overlay_array_fdg_lh)

overlay_array_fdg_rh = nib.load(overlay_file_fdg_rh)
overlay_array_fdg_rh = overlay_array_fdg_rh.get_fdata()
overlay_array_fdg_rh = np.squeeze(overlay_array_fdg_rh)

"""
Check if one of both hemispheres contains values above the minimum threshold (p=0.05 --> -log10(p)=1.3).
"""
lh_size_fdg_over_thr = overlay_array_fdg_lh[np.where(overlay_array_fdg_lh >= min_thr_color)].size
rh_size_fdg_over_thr = overlay_array_fdg_rh[np.where(overlay_array_fdg_rh >= min_thr_color)].size

"""
Load eAV45 overlay arrays.
"""
## Load TFCE p-corrected maps
overlay_file_eAV45_lh = os.path.join("eAV45", "lh.palm.fdr0.CorrPos_tfce_tstat_fwep.mgz")
overlay_file_eAV45_rh = os.path.join("eAV45", "rh.palm.fdr0.CorrPos_tfce_tstat_fwep.mgz")

overlay_array_eAV45_lh = nib.load(overlay_file_eAV45_lh)
overlay_array_eAV45_lh = overlay_array_eAV45_lh.get_fdata()
overlay_array_eAV45_lh = np.squeeze(overlay_array_eAV45_lh)

overlay_array_eAV45_rh = nib.load(overlay_file_eAV45_rh)
overlay_array_eAV45_rh = overlay_array_eAV45_rh.get_fdata()
overlay_array_eAV45_rh = np.squeeze(overlay_array_eAV45_rh)

"""
Check if one of both hemispheres contains values above the minimum threshold (p=0.05 --> -log10(p)=1.3).
"""
lh_size_eAV45_over_thr = overlay_array_eAV45_lh[np.where(overlay_array_eAV45_lh >= min_thr_color)].size
rh_size_eAV45_over_thr = overlay_array_eAV45_rh[np.where(overlay_array_eAV45_rh >= min_thr_color)].size

"""
Define filename of the overlap between fdg and eAV45 patterns.
"""
filename = "test"

"""
Define conjunction overlay arrays.
"""
conjunct_lh = np.min(np.vstack((overlay_array_fdg_lh, overlay_array_eAV45_lh)), axis=0)
conjunct_rh = np.min(np.vstack((overlay_array_fdg_rh, overlay_array_eAV45_rh)), axis=0)

brain = Brain("fsaverage", "both", "white", background="black")

"""
Load rh overlays.
"""
if rh_size_fdg_over_thr > 0:
    brain.add_data(overlay_array_fdg_rh, min=1.3, mid=1.3+0.01, max=20, thresh=1.3, colormap="Blues", colorbar=False, hemi="rh", alpha=1)
if rh_size_eAV45_over_thr > 0:
    brain.add_data(overlay_array_eAV45_rh, min=1.3, mid=1.3+0.01, max=20, thresh=1.3, colormap="Reds", colorbar=False, hemi="rh", alpha=1)
if rh_size_fdg_over_thr > 0 and rh_size_eAV45_over_thr > 0:
    brain.add_data(conjunct_rh, min=1.3, mid=1.3+0.01, max=20, thresh=1.3, colormap="Purples", colorbar=False, hemi="rh", alpha=1)
"""
Load lh overlays.
"""
if lh_size_fdg_over_thr > 0:
    brain.add_data(overlay_array_fdg_lh, min=1.3, mid=1.3+0.01, max=20, thresh=1.3, colormap="Blues", colorbar=False, hemi="lh", alpha=1)
if lh_size_eAV45_over_thr > 0:
    brain.add_data(overlay_array_eAV45_lh, min=1.3, mid=1.3+0.01, max=20, thresh=1.3, colormap="Reds", colorbar=False, hemi="lh", alpha=1)
if lh_size_fdg_over_thr > 0 and lh_size_eAV45_over_thr > 0:
    brain.add_data(conjunct_lh, min=1.3, mid=1.3+0.01, max=20, thresh=1.3, colormap="Purples", colorbar=False, hemi="lh", alpha=1)
"""
Save lh/rh montage.
"""
brain.save_montage("both." + filename + ".png", order=['dorsal','ventral'], orientation='v', border_size=15, colorbar=None, row=-1, col=-1)

Please find attached the raw data and resulting images montage.

ConjunctionMaps.zip both test

larsoner commented 4 years ago

@mattvan83 unfortunately I cannot replicate

both test

For all of these rendering problems I would try pulling the data to your local machine and debugging there

mattvan83 commented 4 years ago

@larsoner unfortunately I tried with my local computer (MBP OSX, following this installation tutorial:

https://gist.github.com/danjgale/4f64ca81f5e91cc0669d0f744c7a9f82) and it failed... What should I test?

However, one thing that didn't work for you also is the fact that overlapping regions in both orange and blue in the right hemisphere should be coloured as purple according code. Do you know why it didn't work?

larsoner commented 4 years ago

How did the installation fail? We have mayavi running on several CIs (including macOS) over in MNE, some with pip and others with conda. You could try following the MNE-Python installation instructions:

https://mne.tools/dev/install/mne_python.html

Regarding the conjunction, I have not worked with overlapping maps, hopefully someone else can help there

mwaskom commented 4 years ago

Let's try to keep the discussion of the conjunction problem on the relevant issue (#281)

mattvan83 commented 4 years ago

@larsoner the installation didn't failed but the results were identical than before and didn't work.

mwaskom commented 4 years ago

Like @larsoner, I can't replicate this on OSX, either for a single image or a montage with two views:

b = Brain("fsaverage", "both", "white", cortex=None, background="black")
WD = os.getcwd()
b.save_montage(os.path.join(WD, "montage_single.png"),
               order=['dorsal'], orientation='v', border_size=15,
               colorbar=None, row=-1, col=-1)
b.save_montage(os.path.join(WD, "montage_double.png"),
               order=['dorsal', 'ventral'], orientation='v', border_size=15,
               colorbar=None, row=-1, col=-1)
b.close()

Single:

montage_single

Double:

montage_double

If you look at the code, it does two things. The first is save multiple images using Brain.save_imageset and the second is stitching them together into one image in surfer.viz.make_montage. Can you use save_imageset and see what the output looks like? That will help narrow down where the problem is.

mattvan83 commented 4 years ago

I tried your example code above, but replaced save_montage by save_imageset:

b = Brain("fsaverage", "both", "white", cortex=None, background="black")
WD = os.getcwd()
b.save_imageset(os.path.join(WD, "montage_single_save_imageset"), ['dorsal'], filetype='png', colorbar=None, row=-1, col=-1)
b.save_imageset(os.path.join(WD, "montage_double_save_imageset"), ['dorsal', 'ventral'], filetype='png', colorbar=None, row=-1, col=-1)
b.close()

And got the following error:

libpng warning: Application built with libpng-1.4.12 but running with 1.6.37
ERROR: In ../IO/Image/vtkPNGWriter.cxx, line 232
vtkPNGWriter (0x7ff2f4f15190): Unable to write PNG file!

libpng warning: Application built with libpng-1.4.12 but running with 1.6.37
ERROR: In ../IO/Image/vtkPNGWriter.cxx, line 232
vtkPNGWriter (0x7ff2f7076200): Unable to write PNG file!

libpng warning: Application built with libpng-1.4.12 but running with 1.6.37
ERROR: In ../IO/Image/vtkPNGWriter.cxx, line 232
vtkPNGWriter (0x7ff2fa14e3e0): Unable to write PNG file!

I don't understand since I followed installation instructions given by @larsoner in #280.

So I save back images using tiff format, please find attached those images: save_imageset.zip

The images look correct in both cases (single and both images). But when using save_montage truncation appear as below: montage_single montage_double

mwaskom commented 4 years ago

What happens with the Python process when you see the libpng error? The text you copied looks like C++ writing to stderr, but do you get a Python exception?

Does running the following code produce the same error? Please describe exactly what happens:

import matplotlib.pyplot as plt
f, ax = plt.subplots()
f.savefig("test.png")

My best guess is that you have somehow ended up with a corruption between your matplotlib/PIL and the underlying image libraries, but it's not obvious how that happened. I just followed the instructions in #280 from @larsoner verbatim on Mac OS and still cannot reproduce your issue.

mattvan83 commented 4 years ago

The Python process fails to produce valid .png images. Images are corrupted but I didn't get Python exception.

Running the code you mentioned above didn't produce any error and gave me the following figure: test

How could I know that there would be a corruption between matplotlib/PIL since I verified in my conda environment and the pillow package (PIL) is not installed?

mwaskom commented 4 years ago

If you're running make_montage, you must have some version of PIL/pillow installed: https://github.com/nipy/PySurfer/blob/master/surfer/viz.py#L78

mwaskom commented 4 years ago

And, interesting, apparently pysurfer has two different ways of saving images.

make_montage goes save_imageset > save_single_image > mlab.savefig (https://github.com/nipy/PySurfer/blob/master/surfer/viz.py#L2312),

but

save_image goes Brain._screenshot_figure > matplotlib.pyplot.savefig.

So I guess it's actually a conflict between mayavi and libpng, not matplotlib.

mattvan83 commented 4 years ago

I understand and will install it, however my error using save_imageset with .png format is not solved yet before trying to mitigate with the truncated hemisphere when using save_montage.

What could be the error knowing that no conflict with PIL is possible?

mattvan83 commented 4 years ago

OK so how could I have provoked a conflict between mayavi and libpng following the instructions of @larsoner in #280 ?

When you installed in the same way, what are your current versions of matplotlib/mayavi ?

mwaskom commented 4 years ago

Sorry, at this point I'm just speculating since I can't reproduce the issues you're having and it's not clear that they are issues in pysurfer itself rather than its dependencies. We do our best to provide guidance on how to set up an environment to use pysurfer, but some of the dependencies are complex and, once dynamically loaded C libraries are in the mix, it's not always possible to create a completely segregated environment.

I understand and will install it, however my error using save_imageset with .png format is not solved yet before trying to mitigate with the truncated hemisphere when using save_montage.

You have a number of confusing issues and I'm trying to understand the details of your setup. Unless I am missing something, if you are calling make_montage and not seeing an ImportError, your Python must be able to import PIL. If that's true despite you not seeing it installed in the environment you're using, your Python session must be pulling in libraries from somewhere else. And that could help resolve why your mayavi and libpng appear unmatched.

mattvan83 commented 4 years ago

OK, thanks anyway for all the support up to now!

I managed to find solution to my confusing issues. I begin from scratch the installation of anaconda and conda environment for pysurfer. The issues may to be linked to the priority channel conda-forge for conda installation that was set in my case. Suppressing it and using defaults for conda installation gave me normal results.

I close this issue.