rordenlab / MRIcroGL

v1.2 GLSL volume rendering. Able to view NIfTI, DICOM, MGH, MHD, NRRD, AFNI format images.
https://www.nitrc.org/plugins/mwiki/index.php/mricrogl:MainPage
Other
192 stars 32 forks source link

Module not found error with scripting #60

Open BonnieAlexander opened 8 months ago

BonnieAlexander commented 8 months ago

Hi,

Thank you for your software!

We are trying to update to MRIcroGL_QT (MRIcroGL 1.2.20220720c QT5 x86-64 LLVM) since updating to Ubuntu version: Release 22.04.3 LTS (Jammy Jellyfish) 64-bit Kernel Linux 6.2.0-37-generic x86_64 MATE 1.26.0

MRIcroGL_QT itself opens and works great, but running a python script for screenshots isn't working. The error in shown in the scripting part via the GUI is:

Running Python script Traceback (most recent call last): File "", line 12, in ModuleNotFoundError: No module named 'glob' Python Successfully Executed

Any suggestions would be much appreciated.

Thanks very much,

Bonnie.

neurolabusc commented 8 months ago

Are your sure your script is running the "gl" module, not "glob"? I am unable to replicate your problem on Ubuntu 23.10: the savebmp function works fine for me. I ran this script:

import gl
gl.resetdefaults()
#open background image
gl.loadimage('spm152')
#open overlay: show positive regions
gl.overlayload('spmMotor')
gl.minmax(1, 4, 4)
gl.opacity(1,50)
#open overlay: show negative regions
gl.overlayload('spmMotor')
gl.minmax(2, -4, -4)
gl.colorname (2,"3blue")
gl.mosaic("A L+ H -0.2 -24 -16 16 40; 48 56 S X R 0");
gl.savebmp('mytest.png')

mytest

The Help/About menu item reports:

MRIcroGL 1.2.20220720c QT5 x86-64 LLVM
Author: Chris Rorden
License: BSD 2-Clause
Linux
NVIDIA Corporation; OpenGL= 4.6.0 NVIDIA 535.129.03; Shader=4.60 NVIDIA
2.1 Max 3D texture: 16384
Current texture: 207×256×215 43mb

And the command line reports:

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 23.10
Release:    23.10
Codename:   mantic
$ uname -a
Linux ryzen 6.5.0-13-generic #13-Ubuntu SMP PREEMPT_DYNAMIC Fri Nov  3 12:16:05 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
BonnieAlexander commented 8 months ago

Hi,

Thanks for your quick reply. Here is a generic example of a script we might use, which works fine for MRIcroGL_QT (MRIcroGL 1.2.20210317 QT5 x86-64 LLVM) on Ubuntu version: 20.04.6 LTS (Focal Fossa) 64-bit Kernel Linux 5.15.0-88-generic x86_64

import gl
import os
import sys
import glob

brain_azimuth = 295
brain_elevation = 155
intensity_window_min = 40
intensity_window_max = 300

display_scripts_directory = os.getcwd()
main_display_directory = os.path.dirname(display_scripts_directory)

output_screenshots_directory = os.path.join(main_display_directory, 'T1_screenshots')

if not os.path.isdir(output_screenshots_directory):
     os.mkdir(output_screenshots_directory)

imagelist = glob.glob(os.path.join(main_display_directory, 'nifti', '*T1*'))
imagelist.sort(reverse = True)

for n,i in enumerate(imagelist):
     gl.resetdefaults()
     layer_number = 0 
     print(i)   
     gl.loadimage(i)
     gl.minmax(layer_number, intensity_window_min, intensity_window_max)  

     for x in [[brain_azimuth, brain_elevation]]:
        gl.azimuthelevation(x[0], x[1])
        gl.wait(200)
        gl.savebmp(os.path.join(output_screenshots_directory, 'T1_screenshot.png'))

gl.quit()

Thanks,

Bonnie.

neurolabusc commented 8 months ago

Bonnie, MRIcroGL uses its own standalone version of Python (in the /Resources/python37 folder rather than the system Python. This means it does not have access to the same installation of Python as you get from the command line. This is required by many distributions (like MacOS) which see access to the system Python by a graphical application as a vulnerability. In theory, you can recompile MRIcroGL with the PYTHON_DYNAMIC compiler directive to use the system's dynamic Python library rather than the static library. This feature is also outlined in the Debian build:

lazbuild  -B MRIcroGL_Debian.lpi

I have not tried this in years, so your mileage may vary.

Alternatively, I recommend the method suggested in the manual where you run a Python script from the command line using the Python of your current environment (with all its packages). Here is an example script that finds all the scans of a specific modality from a BIDS dataset and generates a bitmap image to ensure successful defacing:

#!/usr/bin/python

# Create bitmaps to check defacing of all NIFTI images
#  python bids_deface_bitmaps.py

from pathlib import Path
import os
import os.path
import glob
import sys
import shutil

if __name__ == "__main__":
    inputRoot = '/path/to/bids/'
    outRoot = '/path/to/bmps/'
    exe = '/path/to/MRIcroGL'
    if not shutil.which(exe):
        sys.exit("Unable to find exe: "+exe)

    #inputRoot = '/Users/chris/bids/'
    #modalities to deface
    mods =  ["T2w.nii", "T1w.nii", "FLAIR.nii"]
    #mods =  ["FLAIR.nii"]
    subfolders= [f.path for f in os.scandir(inputRoot) if f.is_dir()]
    for subname in list(subfolders):
        sub = Path(subname).stem
        if not sub.startswith("sub-"):
            continue;
        path = subname + '/**/*.nii.gz'
        files = glob.glob(path, recursive=True)
        for m in mods:
            for f in files:
                if m in f:
                    fnm = os.path.basename(f)
                    fnm = os.path.splitext(fnm)[0]
                    if fnm.endswith(".nii"): #file.nii.gz -> file
                        fnm = os.path.splitext(fnm)[0]
                    if fnm.startswith('_') or fnm.startswith('.'):
                        continue
                    bmp = os.path.join(outRoot, fnm + ".png")
                    cmd = exe + ' -s \'import gl\ngl.loadimage("' + f + '")\ngl.mosaic("A 0.5 S 0.5 C 0.5 S R 0.5")\ngl.colorbarposition(0)\ngl.savebmp("' + bmp + '")\ngl.quit()\'\n'
                    os.system(cmd)
richardbeare commented 6 months ago

Hi Chris, I've been helping Bonnie and co out with this. I have what I think is a temporary fix, but hardly ideal. Assuming we want to stick with the MRIcro distributed python, is there a "correct" way of extending that distribution.

For example, glob.py isn't in that distribution, while pathlib.py is. However the pathlib dependencies are not. My hack (for ubuntu 22.04) was to create a minimal python3.7 environment in miniconda and sync the lib folder into Resources/python37/lib/python3.7/. Hardly ideal, but works for the moment.

I couldn't find anything in the sources indicating a correct way of extending the python installation during build time.

Interested in hearing your advice on options. Thanks

neurolabusc commented 6 months ago

@richardbeare I am not a Python expert. I continue to use the methods I describe above.

richardbeare commented 6 months ago

Happy to have more of a play - how did you decide on the original content of Resources/python37/ ?