Skip to main content

Library for MRI noise analysis

Project description

License: MIT example workflow Documentation Status

MRIna: A library for MRI Noise Analysis

MRIna is a library for the analysis of reconstruction noise from undersampled 4D flow MRI. For additional details, please refer to the publication below:

Lauren Partin, Daniele E. Schiavazzi and Carlos A. Sing-Long Collao, An analysis of reconstruction noise from undersampled 4D flow MRI arXiv

The complete set of results from the above paper can be found at this link


Installation and documentation

You can install MRIna with pip (link to PyPI)

pip install PyWavelets mrina

For the documentation follow this link.


What you can do with MRIna.

The MRIna library provides the following functionalities.

  • It generates k-space undersampling masks of various types including Bernoulli, variable density triangular, variable density Gaussian, variable density exponential and Halton quasi-random sequences.

  • It supports arbitrary operators that implement a forward call (eval), and inverse call (adjoint), column restriction (colRestrict), shape and norm.

  • It supports various non-linear reconstruction methods including l1-norm minimization with iterative thresholding and orthogonal matching pursuit based greedy heuristics.

  • It provides a number of scripts to

    • generate ensembles of synthetic, subsampled and noisy k-space images (4 complex images);
    • reconstruct image density and velocities;
    • post-process to compute correlations, MSE, error patterns and relative errors.

Single-image examples

Example of recovering a 64x64 pixel image from its undersampled frequency information using a Gaussian mask in k-space, 75% undersampling (only 1 every 4 frequencies is retained) and adding a SNR equal to 50.

Original image 1 Wavelet transform 1
k-space mask 1 Noisy k-space measurements 1
Noiseless reconstruction: 1 Reconstruction: CS 1
Reconstruction: CSDEB 1 Reconstruction: stOMP 1

Read grayscale image

import cv2
im = cv2.imread('city.png', cv2.IMREAD_GRAYSCALE)/255.0

Generate undersampling mask

from mrina import generateSamplingMask

# Set an undesampling ratio (refers to the frequencies that are dropped)
delta = 0.75
# Generate an undersampling mask
omega = generateSamplingMask(im.shape, delta, 'bernoulli')
# Verify the undersampling ratio
nsamp = np.sum((omega == 1).ravel())/np.prod(omega.shape)
print('Included frequencies: %.1f%%' % (nsamp*100))

Compute and show wavelet representation

import pywt

waveName = 'haar'
waveMode = 'zero'
wim = pywt.coeffs_to_array(pywt.wavedec2(im, wavelet=waveName, mode=waveMode))[0]
plt.figure(figsize=(8,8))
plt.imshow(np.log(np.abs(wim)+1.0e-5), cmap='gray')
plt.axis('off')
plt.show()

Initialize a WaveletToFourier operator and generate noiseless k-space measurements

from mrina import OperatorWaveletToFourier

# Create a new operator
A = OperatorWaveletToFourier(im.shape, samplingSet=omega[0], waveletName=waveName)
yim = A.eval(wim, 1)

Noiseless recovery using l1-norm minimization

from mrina import RecoveryL1NormNoisy

# Recovery - for low values of eta it is better to use SoS-L1Ball
wimrec_cpx, _ = RecoveryL1NormNoisy(0.01, yim, A, disp=True, method='SoS-L1Ball')
# The recovered coefficients could be complex.
imrec_cpx = A.getImageFromWavelet(wimrec_cpx)
imrec = np.abs(imrec_cpx)

Generate noise in the frequency domain

# Target SNR
SNR = 50
# Signal power. The factor 2 accounts for real/imaginary parts
yim_pow = la.norm(yim.ravel()) ** 2 / (2 * yim.size)
# Set noise standard deviation
sigma = np.sqrt(yim_pow / SNR)
# Add noise
y = yim + sigma * (np.random.normal(size=yim.shape) + 1j * np.random.normal(size=yim.shape))

Image recovery with l1-norm minimization

# Set the eta parameter
eta = np.sqrt(2 * y.size) * sigma
# Run recovery with CS
wimrec_noisy_cpx, _ = RecoveryL1NormNoisy(eta, y, A, disp=True, disp_method=False, method='BPDN')
# The recovered coefficients could be complex...
imrec_noisy = np.abs(A.getImageFromWavelet(wimrec_noisy_cpx))

Estimator debiasing

# Get the support from the CS solution
wim_supp = np.where(np.abs(wimrec_noisy_cpx) > 1E-4 * la.norm(wimrec_noisy_cpx.ravel(), np.inf), True, False)
# Restrict the operator
Adeb = A.colRestrict(wim_supp)
# Solve a least-squares problem
lsqr = lsQR(Adeb)  
lsqr.solve(y[Adeb.samplingSet])
wimrec_noisy_cpx_deb = np.zeros(Adeb.wavShape,dtype=np.complex)
wimrec_noisy_cpx_deb[Adeb.basisSet] = lsqr.x[:]
# The recovered coefficients could be complex...
imrec_noisy_deb = np.abs(Adeb.getImageFromWavelet(wimrec_noisy_cpx_deb))

Image recovery with stOMP

from mrina import lsQR,OMPRecovery
# Run stOMP recovery
wimrec_noisy_cpx, _ = OMPRecovery(A, y)
# The recovered coefficients could be complex...
imrec_noisy_cpx = A.getImageFromWavelet(wimrec_noisy_cpx)
imrec_noisy = np.abs(imrec_noisy_cpx)

Script functionalities

MRIna also provides scripts to automate:

  • the generation of noisy k-space signals.
  • linear and non-linear image reconstruction.
  • post-processing of reconstructed images.

Image data

The image data should be stored on a numpy tensor in npy format with shape (r, i, n, im_1, im_2), where:

  • r is the number of image repetitions.
  • i is the image number. For 4D flow MRI you need 4 images, i.e., one density and three velocity components.
  • im_1,im_2 are the two image dimensions.

Sample generation

  python -m mrina.gen_samples --fromdir $KSPACEDIR \
                             --repetitions $REALIZATIONS \
                             --origin $IMGNAME \
                             --dest $RECDIR \
                             --utype $SAMPTYPE \
                             --urate $PVAL \
                             --noisepercent $NOISEVAL

For additional information on the script input parameters, type

python -m mrina.gen_samples --help

Image recovery

  python -m mrina.recover --noisepercent $NOISEVAL \
                          --urate $PVAL \
                          --utype $SAMPTYPE \
                          --repetitions $REALIZATIONS \
                          --numprocesses $PROCESSES \
                          --fromdir $KSPACEDIR \
                          --recdir $RECDIR \
                          --maskdir $PATTERNDIR \
                          --method $SOLVERMODE \
                          --wavelet $WAVETYPE \
                          --savevels

For additional information on the script input parameters, type

python -m mrina.recover --help

Post-processing - Saving reconstructed images

  python -m mrina.saveimgs --numsamples $REALIZATIONS \
                           --maindir $MAINDIR \
                           --recdir $RECDIR \
                           --maskdir $MASKDIR \
                           --outputdir $OUTDIR \
                           --savetrue \
                           --savemask \
                           --saverec \
                           --savenoise \
                           --savelin \
                           --usetrueasref \
                           --printlevel $PRINTLEV \
                           --savelin \
                           --limits $LIMITS \
                           --fluidmaskfile $FMFILE

For additional information on the script input parameters, type

python -m mrina.saveimgs --help

Post-processing - Computing correlations

python -m mrina.correlation --numsamples $REALIZATIONS \
                             --numpts 50 \
                             --recdir ./CS/ \
                             --ptsdir ./ \
                             --vencdir ./ \
                             --maindir ./ \
                             --usefluidmask \
                             --printlevel 1

For additional information on the script input parameters, type

python -m mrina.correlation --help

Post-processing - Plot correlations

python -m mrina.plot_corr --noise 0.1 0.01 0.05 0.3 \
                         --uval 0.75 0.25 0.5 \
                         --utype vardengauss bernoulli \
                         --method cs csdebias omp \
                         --wavelet haar db8 \
                         --numsamples 100 \
                         --numpts 50 \
                         --dir ./ \
                         --outputdir ./OUT/02_corr/ \
                         --usefluidmask \
                         --printlevel 1

For additional information on the script input parameters, type

python -m mrina.plot_corr --help

Post-processing - Compute MSE and relative errors

python -m mrina.plot_mse --noise 0.1 0.01 0.05 0.3 \
                        --uval 0.75 0.25 0.5 \
                        --utype vardengauss bernoulli \
                        --method cs csdebias omp \
                        --wavelet haar db8 \
                        --numsamples 100 \
                        --numpts 50 \
                        --dir ./ \
                        --outputdir ./OUT/03_mse/ \
                        --maskdir ./ \
                        --usecompleximgs \
                        --addlinearrec \
                        --usetrueimg \
                        --usefluidmask \
                        --fluidmaskfile ia_mask.npy \
                        --printlevel 1 \
                        --percstring 1

For additional information on the script input parameters, type

python -m mrina.plot_mse --help

Core Dependencies

Citation

Did you find this useful? Cite us using:

@misc{partin2022analysis,
      title={An analysis of reconstruction noise from undersampled 4D flow MRI}, 
      author={Lauren Partin and Daniele E. Schiavazzi and Carlos A. Sing Long},
      year={2022},
      eprint={2201.03715},
      archivePrefix={arXiv},
      primaryClass={eess.IV}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mrina-0.1.5.tar.gz (136.0 kB view details)

Uploaded Source

Built Distribution

mrina-0.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (750.3 kB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ x86-64

File details

Details for the file mrina-0.1.5.tar.gz.

File metadata

  • Download URL: mrina-0.1.5.tar.gz
  • Upload date:
  • Size: 136.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.27.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for mrina-0.1.5.tar.gz
Algorithm Hash digest
SHA256 4a30072b8afdf87755e58957ac997af45c673f16e6c5b117e97541ae20c05fb7
MD5 9f1a817e9ad521bde1275f0cf797f0cf
BLAKE2b-256 8379599c46f327af3e5dd473717aa97256e65cac15df1d16ebd21edb768bcea2

See more details on using hashes here.

File details

Details for the file mrina-0.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for mrina-0.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 527a60368be196d81dd3d313422a869e44c08eb64b34a9854e315986e6c8e23a
MD5 b79e2739244d8e12f7a3a7147c28d7e5
BLAKE2b-256 ceb274201f16f88d646b03b181a09ad605c91580bbbe2b0f77b43a8a27ab73bf

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page