def jupyter2pdf(jupyterFile, outputDir=None): ''' convert jupyter2pdf using pdfkit. <jupyterFile>: a string or a Path-lib object <outputDir>: outputDir, default:cwd ''' from RZutilpy.system import Path, makedirs import pdfkit jupyterFile = Path(jupyterFile) outputDir = Path.cwd() if outputDir is None else Path(outputDir) makedirs(outputDir) # create the dir if no exists name = jupyterFile.pstem !jupyter nbconvert --to html {name} htmlFile = Path(jupyterFile.strnosuffix+'.html') pdfkit.from_file(htmlFile.str, (outputDir/(name+'.pdf')).str) !rm -f {name}.html
def t1warp(t1, outputDir=None, template=None, maskvol=None, skullstrip_kw=[], affine_kw=[], affonly=False): ''' Nonlinear (or linear warping) warping t1 file to a template. In afni_proc, they use @SSwarper by default. For a standard human T1 file, @SSwarper produce similar warping files as this script. But this script might be useful for like unusual cases, for example, Monkey data Input: <t1>: t1 file, a string or a pathlib object <outputDir>: output directory, we first copy t1 file into this outputDir. if None, use cwd. <template>: the template t1 file,a string or a pathlib object. if None, we use afni MNI152_2009_template_SSW.nii.gz file <maskvol>: mask volume, a str or a pathlib object. You can supply an maskvol file here so the skullstrip step will be skipped. This is useful when you want to manually edit the mask volume and supply it to skullstrip <skullstrip_kw>, <affine_kw>: lists, extra options supply to '3dSkullstrip' and 'align_epi_anat.py' commands. This is useful for like monkey data. Default: [] (empty list) <affonly>: boolean (default: False), only need affine transform, no nonlinear warping. Output: this will produce several files in <outputDir>, they are generated in order **.nii.gz: copy of t1 file **_ss.nii.gz: skullstriped(ss) T1 **_ssmask.nii.gz: skullstriped(ss) brain mask **_ssiu.nii.gz: intenstivity unifize(iu) t1 after ss **_ssiu_shft: shift to center of mass of the template after iu **_ssiu_shft_aff: affine tranformed T1 in the template space **_ssiu_shft_aff_mat.aff12.1D: affine transformation matrix **_ssui_shft_aff_matINV.aff12.1D: inverse affine transformation **_ssiu_shft_aff_nl.nii.gz: nonlinear warp to the template in the template space WARP.nii.gz: nonlinear warp file Note: 1. Here we do three transform, shift-affine-nlwarp. The later two can be concatenated but it is not recommended to concatenate all three because combining shift into affine and nlwarp will make the "3dNwarpapply" take a lot of memory and very long time (and might fail). If you want to inversely warp an atlas from template space into native T1 space. You can do like # first do inverse (affine+nlwarp) cmd = ['3dNwarpApply', '-prefix', f'{template.strnosuffix}_nl2t1.nii.gz', \ '-nwarp', f'inv({(outputDir/"WARP.nii.gz").str} {t1.strnosuffix}_ssiu_shft_aff2NMT_mat.aff12.1D)', \ '-source', template.str, '-master', f'{t1.strnosuffix}_ssiu_shft.nii.gz', '-overwrite'] unix_wrapper(cmd) # then inverse the shift # Note that here do not use 3dAllineate as it will resample the data one more time # Use 3drefit, it will not resample data cmd = f'3drefit -duporigin {t1.strnosuffix}_ssiu.nii.gz {template.strnosuffix}_nl2t1.nii.gz}' unix_wrapper(cmd) 2. Be very CAREFUL about the order when concatenating affine and nlwarp! To do: 1. automatically perform inversed warp atlas?? History: 20190413 RZ create ''' from RZutilpy.system import Path, unix_wrapper, makedirs t1 = Path(t1) if template: template = Path(template) if ~isinstance(template, Path) else template else: # default template is mni template afnipath = unix_wrapper(f'which afni') # get the afni install Path template = Path(afnipath).parent / 'MNI152_2009_template_SSW.nii.gz' outputDir = Path.cwd() if outputDir is None else Path(outputDir) makedirs(outputDir) # create outputdir if not exist unix_wrapper(f'cp {t1} {outputDir}') # copy t1 file to outputdir t1 = outputDir / f'{t1.name}' # switch to new t1 # ======= step 1, skull strip ===================== if maskvol is None: # make brain mask, this step typically is good for a human brain, but tricky for a monkey brain cmd = ['3dSkullStrip', \ '-input', t1.str, \ '-prefix', f'{t1.strnosuffix}_ssmask.nii.gz', \ '-mask_vol'] cmd = cmd + skullstrip_kw unix_wrapper(cmd) maskvol = Path(f'{t1.strnosuffix}_ssmask.nii.gz') # skullstrip-based on mask cmd=['3dcalc', '-a', t1.str, '-b', maskvol.str, \ '-expr', "a*step(b)", \ '-prefix', f'{t1.strnosuffix}_ss.nii.gz'] unix_wrapper(cmd) # ===== step 2, # intensity unifize =========== cmd = f'3dUnifize -prefix {t1.strnosuffix}_ssiu.nii.gz {t1.strnosuffix}_ss.nii.gz' unix_wrapper(cmd) # ====== step 3, affine transform to NMT template ================== # first we can align center of mass cmd = f'@Align_Centers -base {template.str} -dset {t1.strnosuffix}_ssiu.nii.gz -cm' unix_wrapper( cmd) # this step will generate {t1.strnosuffix}_ssiu_shft.nii.gz shftfile = Path.cwd() / f'{t1.pstem}_ssiu_shft.1D' unix_wrapper(f'mv {shftfile.str} {outputDir.str}') # This step will generate {t1.strnosuffix}_ssiu_shft.nii.gz # This step will also generate a 1D transformation file under CWD not OUTPUTDIR # we have to copy this 1D file into <outputDir> # do affine alignment, note that data will be resampled on NMT grid cmd = ['align_epi_anat.py', '-dset1', f'{t1.strnosuffix}_ssiu_shft.nii.gz', \ '-dset2', template.str, \ '-master_dset1', template.str,\ '-suffix', '_aff.nii.gz',\ '-dset1to2', '-dset1_strip','None','-dset2_strip','None','-overwrite',\ '-output_dir', outputDir.str] cmd = cmd + affine_kw unix_wrapper(cmd) # change transformation file name unix_wrapper( f'mv {t1.strnosuffix}_ssiu_shft_aff.nii.gz_mat.aff12.1D {t1.strnosuffix}_ssiu_shft_aff_mat.aff12.1D' ) # calc the inverse affine transformation cmd = f'cat_matvec -ONELINE {t1.strnosuffix}_ssiu_shft_aff_mat.aff12.1D -I > {t1.strnosuffix}_ssiu_shft_aff_matINV.aff12.1D' unix_wrapper(cmd) # ================= step 4, nonlinear registration ============== # note that this step should be run after completing affine transformation # Also here, input dataset is the anat dataset but put on the base grid, unix_wrapper(f'rm -rf {(outputDir/"awpy*").str}') # use superhard, to get the best alignment, but it take a long time cmd = ['auto_warp.py', '-base', template.str, '-skip_affine', 'yes', \ '-input', f'{t1.strnosuffix}_ssiu_shft_aff.nii.gz', '-overwrite', \ '-output_dir', (outputDir/'awpy').str, '-qw_opts','-iwarp','-superhard'] unix_wrapper(cmd) # copy and delete redundant files unix_wrapper( f'cp {(outputDir/"awpy"/"anat.un.qw_WARP.nii").str} {(outputDir/"WARP.nii").str}' ) unix_wrapper(f'gzip -f {(outputDir/"WARP.nii").str}' ) # compress nonlinear warp file unix_wrapper(f'rm -rf {(outputDir/"awpy")}') # apply transform warp t1 to the template space cmd = ['3dNwarpApply', '-prefix', f'{t1.strnosuffix}_ssiu_shft_aff_nl.nii.gz', \ '-nwarp', f'{(outputDir/"WARP.nii.gz").str} {t1.strnosuffix}_ssiu_shft_aff_mat.aff12.1D', \ '-source', f'{t1.strnosuffix}_ssiu_shft.nii.gz', '-master', template.str, '-overwrite'] unix_wrapper(cmd)
def makeimagestack3dfiles(m, outputprefix=None, skips=(5, 5, 5), k=(0, 0, 0), \ cmap='gray', returnstack=False, **stack_kw): ''' makeimagestack3dfiles(m, outputprefix=None, skips=[5, 5, 5], k=[0, 0, 0], \ cmap='gray', **kwargs): Input: <m>: is a 3D matrix or a nibabel image object <outputprefix>: is a output prefix,if it is NONE, then do not write images <skips> (optional) is a sequence of numbers of slices to skip in each of the 3 dimensions. Default: [5, 5, 5]. <k> (optional) is a sequence with numbers containing the times to CCW rotate matrix rotation info. See np.rot90. Default: [0, 0, 0]. <k[i]> indicates rotate the image when writing image stack along ith dimension <cmap> is the colormap to use. Default: gray(256), any matplotlib colormap input is fine <returnstack>: boolean, whether to return the 3 imagestack. Useful to visualize and can help adjust <skips> and <k>. Default:False <stack_kw>: kwargs for makeimagestack, include <wantnorm>, <addborder> <csize>,<bordersize> Output: <imglist>: a 1x3 list containing the image matrix for 2,1,0 dimensions. note here the order is not from 0-2 dimensions. Each image is within range 0~1 float 64. We take <m> and write out three .png files, one for each slicing: <outputprefix>_view0.png <outputprefix>_view1.png <outputprefix>_view2.png The first slicing is through the first dimension with ordering [0 1 2]. The second slicing is through the second dimension with ordering [1 3 2]. The third slicing is through the third dimension with ordering [2 3 1]. Note that this is different from KK's makeimagestack3dfiles.m After slicing, rotation (if supplied) is applied within the first two dimensions using np.rot90 Example: vol = makegaussian3d([100 100 100],[.7 .3 .5],[.1 .4 .1]); makeimagestack3dfiles(vol,'test',(10, 10, 10),k=(5,5,5),wantnorm=(0, 1)) To do: 1. Fix the filepath problem History 20180621 RZ added <returnstack> input 20180502 RZ fixed the k rotdaion bug, now should be more clear. 20180412 RZ changed the default cmap to 'gray' 20180419 RZ changed the functionality of outputprefix, not saving images if None.. ''' from matplotlib.pyplot import imsave from nibabel.nifti1 import Nifti1Image as nifti from RZutilpy.imageprocess import makeimagestack from RZutilpy.system import Path, makedirs import numpy as np import os if isinstance(m, nifti): m = m.get_data() _is_writeimage = True if outputprefix is None: outputprefix = Path.cwd() _is_writeimage = False # create the folder if not exist. assert makedirs(Path(outputprefix).parent) # define permutes imglist = [] # we make the dimension to slice to the last one permutes = np.array([[1, 2, 0], [0, 2, 1], [0, 1, 2]]) for dim in range(3): temp = m if dim == 0: # note that the first element in the output image is along 1st dimension temp = temp[::skips[dim], :, :].transpose(permutes[dim, :]) elif dim == 1: temp = temp[:, ::skips[dim], :].transpose(permutes[dim, :]) elif dim == 2: temp = temp[:, :, ::skips[dim]].transpose(permutes[dim, :]) # rotate image if k[dim]: # note temp = np.rot90(temp, k=k[dim], axes=(0, 1)) # CCW rotate f = makeimagestack(temp, **stack_kw) imglist.append(f) # write the image fname = Path(outputprefix).parent / f'{Path(outputprefix).pstem}_view{dim}.png' # note that plt.imsave can automatically recognize the vmin and vmax, no # need to convert it to uint8 like in matlab if _is_writeimage: # if not, only return the images of three views imsave(fname.str, f, cmap=cmap) # return the imagestacks for visualization and debugging if returnstack: return imglist # reverse list to keep compatible the original axis