All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.rocsg.fijirelax.mrialgo.MRUtils Maven / Gradle / Ivy

Go to download

FijiRelax : 3D+t MRI analysis and exploration using multi-echo spin-echo sequences

There is a newer version: 4.0.10
Show newest version
/*
 * 
 */
package io.github.rocsg.fijirelax.mrialgo;


import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.math3.distribution.ChiSquaredDistribution;
import io.github.rocsg.fijiyama.registration.TransformUtils;
import io.github.rocsg.fijiyama.common.VitimageUtils;
import ij.IJ;
import ij.ImagePlus;
import ij.plugin.Duplicator;
import ij.process.ImageProcessor;
import io.github.rocsg.fijirelax.curvefit.LMDualCurveFitterNoBias;
import io.github.rocsg.fijirelax.curvefit.SimplexDualCurveFitterNoBias;

/**
 * This class is a utility class to hold many helpers used for simulating MRI echoes, computing rice noise estimation, reading information into TIFF metadata.
 * 	Copyright (C) 2022  
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see 
*/
public class MRUtils  {
	
	
	
	
	


	/** The Constant epsilon. */
	public static final double epsilon=10E-10;
	
	/** The Constant infinity. */
	public static final double infinity=1/epsilon;
	
	/** The Constant ERROR_VALUE. */
	public static final int ERROR_VALUE= 0;
	
	/** The Constant ERROR_KHI2. */
	public static final int ERROR_KHI2= 9999;
	
	/** The Constant SIMPLEX. */
	public static final int SIMPLEX = 1;
    
    /** The Constant LM. */
    public static final int LM=2;
    
    /** The Constant fititems2. */
    public final static String[] fititems2={"Simplex","Levenberg-Marquardt"};
    
    /** The Constant constitems2. */
    public final static int[] constitems2={SIMPLEX,LM};
    
    /** The Constant MSEC. */
    public static final int MSEC=11;
    
    /** The Constant SEC. */
    public static final int SEC=12;
	
	/** The Constant timeunits. */
	public final static String[] timeunits={"ms", "s"};
    
    /** The Constant timeitems. */
    public final static int[] timeitems={MSEC, SEC};

    /** The Constant TWOPOINTS. */
    public static final int TWOPOINTS=300;

    /** The Constant T1_MONO. */
    public static final int T1_MONO =1; //T1 exponential : PD  (1- exp(-tr/T1))
    
    /** The Constant T2_MONO. */
    public static final int T2_MONO =2; // T2 exponential : M0 exp(-te/T2)
    
    /** The Constant T2_MULTI. */
    public static final int T2_MULTI=3; // Two components exponential : PD1 exp(-te/T21) + PD2 exp(-te/T22)
	
	/** The Constant T1T2_MONO. */
	public static final int T1T2_MONO = 4; //T1-T2 cycle with a single T2 component : PD exp(-te/T2) (1- exp(-tr/T1))
	
	/** The Constant T1T2_MULTI. */
	public static final int T1T2_MULTI = 5; //T1-T2 cycle with two T2 component : ( PD1exp(-te/T21) + PD2exp(-te/T22) ) (1- exp(-tr/T1))

	/** The Constant T1_MONO_BIAS. */
	public static final int T1_MONO_BIAS =11;//The same, with an additive offset 
    
    /** The Constant T2_MONO_BIAS. */
    public static final int T2_MONO_BIAS =12; 
    
    /** The Constant T2_MULTI_BIAS. */
    public static final int T2_MULTI_BIAS=13;
	
	/** The Constant T1T2_MONO_BIAS. */
	public static final int T1T2_MONO_BIAS = 14; 
	
	/** The Constant T1T2_MULTI_BIAS. */
	public static final int T1T2_MULTI_BIAS = 15; 

	/** The Constant T1_MONO_RICE. */
	public static final int T1_MONO_RICE =21;//The same, with an estimated rice noise contribution
    
    /** The Constant T2_MONO_RICE. */
    public static final int T2_MONO_RICE =22;
    
    /** The Constant T2_MULTI_RICE. */
    public static final int T2_MULTI_RICE=23; 
	
	/** The Constant T1T2_MONO_RICE. */
	public static final int T1T2_MONO_RICE = 24; 
	
	/** The Constant T1T2_MULTI_RICE. */
	public static final int T1T2_MULTI_RICE = 25;
	
	
	/** The Constant T1T2_BIONANO. */
	public static final int T1T2_BIONANO = 30; //Calcul de T1 par la méthode des polynomes, calcul de T2 par la méthode des deux points
	
	/** The Constant T1T2_DEFAULT_T2_MONO_RICE. */
	public static final int T1T2_DEFAULT_T2_MONO_RICE=31;//Amendement de T1T2 avec un delta T2 de 25 ms
	
	/** The Constant T1T2_DEFAULT_T2_MULTI_RICE. */
	public static final int T1T2_DEFAULT_T2_MULTI_RICE=32;//Amendement de T1T2 avec un delta T2 de 25 ms
	
	/** The min delta chi 2. */
	public static double minDeltaChi2=1E-6;

	/** The Constant PARAM_SIGMA. */
	public static final int PARAM_SIGMA=31;
	
	/** The Constant PARAM_TR. */
	public static final int PARAM_TR=32;
	
	/** The Constant PARAM_TE. */
	public static final int PARAM_TE=33;
    
    /** The Constant DELTA_TE_BOUTURE_TRICK. */
    public static final double DELTA_TE_BOUTURE_TRICK=20;
    
 
    /** The Constant factorT1M0MaxRatio. */
    public static final double factorT1M0MaxRatio=3;
    
    /** The Constant factorT1MinRatio. */
    public static final double factorT1MinRatio=1;
    
    /** The Constant factorT1MaxRatio. */
    public static final double factorT1MaxRatio=3;

    /** The Constant factorT2M0MaxRatio. */
    public static final double factorT2M0MaxRatio=3;
    
    /** The Constant factorT2MinRatio. */
    public static final double factorT2MinRatio=1;
    
    /** The Constant factorT2MaxRatio. */
    public static final double factorT2MaxRatio=3;
	
	/** The Constant THRESHOLD_RATIO_BETWEEN_MAX_ECHO_AND_SIGMA_RICE. */
	public static final double THRESHOLD_RATIO_BETWEEN_MAX_ECHO_AND_SIGMA_RICE=5.25;//1.25 stands for mean Rayleigh and 4 for the three std around the mean;
	
	/** The Constant THRESHOLD_RATIO_BETWEEN_MEAN_ECHO_AND_SIGMA_RICE. */
	public static final double THRESHOLD_RATIO_BETWEEN_MEAN_ECHO_AND_SIGMA_RICE=2;
    
	
	/** The max khi 2. */
	protected static double maxKhi2=5;
	
	/** The max khi 2 MONO. */
	protected static double maxKhi2MONO=10E8;
	
	/** The max khi 2 after fit. */
	protected static float maxKhi2AfterFit=10;

    /** The Constant minAcceptableBionanoT1. */
    public static final double minAcceptableBionanoT1=500;
	
	/** The Constant minAcceptableBionanoT1Bouture. */
	public static final double minAcceptableBionanoT1Bouture=100;
	
	/** The Constant minAcceptableBionanoT2. */
	public static final double minAcceptableBionanoT2=10;
	
	/** The Constant N_ITER_T1T2. */
	public static final int N_ITER_T1T2=400;
	
	/** The Constant maxAcceptableBionanoM0. */
	public static final double maxAcceptableBionanoM0=2*MRUtils.maxM0BionanoForNormalization;
	
	/** The Constant maxAcceptableBionanoT2. */
	public static final double maxAcceptableBionanoT2=500;
	
	/** The Constant maxDisplayedBionanoT2. */
	public static final int maxDisplayedBionanoT2=150;
	
	/** The Constant maxDisplayedBionanoT1. */
	public static final int maxDisplayedBionanoT1=3200;
	
	/** The Constant maxDisplayedBionanoM0. */
	public static final int maxDisplayedBionanoM0=8666;
	
	/** The Constant maxDisplayedBionanoGE3D. */
	public static final int maxDisplayedBionanoGE3D=20000;
	
	/** The Constant maxM0BionanoForNormalization. */
	public static final int maxM0BionanoForNormalization=6500;
	
	/** The Constant maxDisplayedPDratio. */
	public static final double maxDisplayedPDratio=1.05;
	
	/** The Constant maxDisplayedT1ratio. */
	public static final double maxDisplayedT1ratio=1.5;
	
	/** The Constant maxDisplayedT2ratio. */
	public static final double maxDisplayedT2ratio=0.7;
	
	/** The Constant THRESHOLD_BOUTURE_FACTOR. */
	public static final double THRESHOLD_BOUTURE_FACTOR=0.5;
	
	/** The Constant maxAcceptableBionanoT1. */
	public static final int maxAcceptableBionanoT1=3500;
    
    /** The use boundaries. */
    public static boolean useBoundaries=false;
	
	/** The Constant DEFAULT_N_ITER_T1T2. */
	protected static final int DEFAULT_N_ITER_T1T2 = 500;
	
	/** The Constant minPossibleMaxT1. */
	public static final int minPossibleMaxT1=3500;
    
    
    
    
	
	
	
	
	/**
	 * Instantiates a new MR utils.
	 */
	public MRUtils() {	}

	
	/**
	 * Eliminate the first echo of the sequence
	 *
	 * @param tab the initial measures
	 * @return the double[][]
	 */
	public static double[][]forgetLastT2(double[][]tab){
		double[][]tab2=new double[tab.length][];
		for(int i=0;imaxMag)maxMag=echoVals[i][pix];
					}
				}
			}
		}

		for(int i=0;imaxTr && Tr[i]<10000)maxTr=Tr[i];
			if(Te[i]>maxTe)maxTe=Te[i];
		}

		tab[0]=maps[0];//PD
		tab[nMapsOutput-1]=VitimageUtils.nullImage(tab[0]);//Mask
		if(hasT1T2) {tab[1]=maps[1];tab[2]=maps[2];}
		else if(hasT1)tab[1]=maps[1];
		else if(hasT2)tab[1]=maps[2];

		ImagePlus hyper=VitimageUtils.hyperStackingChannels(tab);
		for(int c=nMapsOutput;c list=new ArrayList();
		for(double dou : tab)list.add(dou);
		Collections.sort(list);
		for(int i=0;i1) {
			IJ.showMessage("Critical fail in MRUtils : get "+nbCat+" categories instead of 1 \nwhen calling getDataTypeOfThisMagneticResonanceSlice(ImagePlus img,"+c+","+z+","+f+"\nLabel was : "+label);
		}
		if(nbCat<1)return MRDataType.OTHER;
		return dataT;
	}
	
	/**
	 * Read values of the capillary in slices label
	 *
	 * @param img the image to test
	 * @param c the channel
	 * @param z the zslice
	 * @param t the timeframe
	 * @return the double[]
	 */
	public static double[] readCapValuesInSliceLabel(ImagePlus img,int c, int z, int t) {
		double []ret=new double[2];
		String label=img.getStack().getSliceLabel(VitimageUtils.getCorrespondingSliceInHyperImage(img, c, z, t));
		String[]strTab=label.split("_");
		for(String str:strTab) {
			if(str.contains("CAP")) {
				ret[0]=Double.parseDouble((str.split("=")[1]).split("\\+-")[0]);
				ret[1]=Double.parseDouble((str.split("=")[1]).split("\\+-")[1]);
			}
		}
		return ret;
	}

	/**
	 * Compute the mean of the double tab
	 *
	 * @param tab the tab
	 * @return the double
	 */
	protected static double mean(double[] tab) {
		double m=0;
		for(int i=0;i1) {
				if(prm0Sigma_1Tr_2Te==PARAM_SIGMA && prm[0].equals("SIGMARICE"))return Double.parseDouble(prm[1]);
				if(prm0Sigma_1Tr_2Te==PARAM_TR && prm[0].equals("TR"))return Double.parseDouble(prm[1]);
				if(prm0Sigma_1Tr_2Te==PARAM_TE && prm[0].equals("TE"))return Double.parseDouble(prm[1]);
			}
		}
		return 0;
	}

	/**
	 * Modify Rice noise sigma, and write it in the slice label
	 *
	 * @param hyperImg the hyper img
	 * @param c the channel
	 * @param z the zslice
	 * @param t the timeframe
	 * @param newSigma the new sigma to write
	 */
	public static void modifySigma(ImagePlus hyperImg,int c,int z,int t,double newSigma) {
		String label=hyperImg.getStack().getSliceLabel(VitimageUtils.getCorrespondingSliceInHyperImage(hyperImg, c, z, t));
		String[]strTab=label.split("_");
		String newLab="";
		for(int i=0;itab[i]*1.3 || tabTemp[j]xMax-1) || (yCor>yMax-1)) {IJ.log("Bad coordinates. Data set to 0"); return null;}
		for(int z=zm;z<=zM;z++) {
			for(int x=xm;x<=xM;x++) {
				for(int y=ym;y<=yM;y++) {
					for(int ec=0;ecxMax-1) || (yCor>yMax-1)) {IJ.log("Bad coordinates. Data set to 0"); return null;}
		for(int z=zm;z<=zM;z++) {
			for(int x=xm;x<=xM;x++) {
				for(int y=ym;y<=yM;y++) {
					for(int ec=0;ecxMax-1) || (yCor>yMax-1)) {IJ.log("Bad coordinates. Data set to 0"); return data;}
		double[][][]weights=new double[xM-xm+1][yM-ym+1][zM-zm+1];
		double sigmaX=crossWidth;
		double sigmaY=crossWidth;
		double sigmaZ=crossThick;
		
		
		double sum=0;
		for(int x=xm;x<=xM;x++) {
			for(int y=ym;y<=yM;y++) {
				for(int z=zm;z<=zM;z++) {	
					if(!gaussianWeighting)weights[x-xm][y-ym][z-zm]=1.0/nHits;
					else {
						if(sigmaX<=0 && sigmaZ<=0) {//One point
							weights[x-xm][y-ym][z-zm]=1;
						}
						else if(sigmaX<=0 && sigmaZ>0) {//Mono dimensional along z
							weights[x-xm][y-ym][z-zm]=1/(Math.pow(2*Math.PI,0.5)*sigmaZ) * Math.exp(- (z-zCor)*(z-zCor)/(sigmaZ*sigmaZ));
						}
						else if(sigmaX>0 && sigmaZ<=0) {//Two dimensional along x and y
							weights[x-xm][y-ym][z-zm]=1/(Math.pow(2*Math.PI,1)*sigmaX*sigmaY) * Math.exp(-  (x-xCor)*(x-xCor)/(sigmaX*sigmaX)  -  (y-yCor)*(y-yCor)/(sigmaY*sigmaY));
						}
						else {//Three dimensional gaussian
							weights[x-xm][y-ym][z-zm]=1/(Math.pow(2*Math.PI,1.5)*sigmaX*sigmaY*sigmaZ) * Math.exp(-  (x-xCor)*(x-xCor)/(sigmaX*sigmaX)  -  (y-yCor)*(y-yCor)/(sigmaY*sigmaY)  -  (z-zCor)*(z-zCor)/(sigmaZ*sigmaZ));
						}
					}
					sum+=weights[x-xm][y-ym][z-zm];
				}
			}
		}
		if(gaussianWeighting) {
			for(int x=xm;x<=xM;x++) {
				for(int y=ym;y<=yM;y++) {
					for(int z=zm;z<=zM;z++) {	
						weights[x-xm][y-ym][z-zm]/=sum;
					}
				}
			}
		}
		for(int ec=0;ec0)for(int p=0;p9999 ? param[3] : 0)) / param[2])  ),sigma);break;
			case T1T2_DEFAULT_T2_MULTI_RICE:ret=RiceEstimator.besFunkCost(  (param[0]*Math.exp(-(  (techo+(trecov>9999 ? param[5] : 0))   / param[3]) )+param[1]*Math.exp(-((techo+(trecov>9999 ? param[5] : 0)) / param[4])) * (1-Math.exp(-((trecov) / param[2])) ) ) , sigma);break;
			default:IJ.showMessage("In MRUtils.getFitFunctionValue, unexpected fit type : "+fitType+" . \nBrutal stop now.");ret=1/0;
			}
		return ret;
	}

	/**
	 * Compute normalised khi2 and p values according to the sigma parameter of the Rice noise
	 * the number of points averaged for the estimation is used to estimate sigma of the sum
	 * Another interesting point is that sigma is reestimated locally for each magnitude value
	 * as the rice sigma is not equal to the observed sigma for different values of the magnitude.
	 *
	 * @param tabData the tab data
	 * @param tabTrTimes the tab of recovery times
	 * @param tabTeTimes the tab of echo times
	 * @param sigma the sigma of Rice noise
	 * @param estimatedParams the estimated parameters of the fitting function
	 * @param fitType the fit type (ex: T2_MONO_BIAS)
	 * @param debug the debug flag in order to display some debugging infformations
	 * @param riceEstimator the RiceEstimator object in use to handle the analysis
	 * @param nbPoints the nb of points averaged for getting this data
	 * @return the double[]{khi2, p}
	 */
	public static double[] fittingAccuracies(double[] tabData,double[] tabTrTimes,double[] tabTeTimes,double sigma,double[] estimatedParams,int fitType,boolean debug,RiceEstimator riceEstimator,int nbPoints){		
		int N_PARAMS=getNparams(fitType);
		double []realP=fittenRelaxationCurve(tabTrTimes,tabTeTimes,estimatedParams,sigma,fitType);
		double khi2=0;		double diff=0;		double sigmaEst=0;
		for(int indT=0;indT(X-radiusAroundCapillary-5) || coords[z][0]>(Y-radiusAroundCapillary-5) ) {
				IJ.showMessage("Warning in normalizeBeforeComputation : unsteady mean computation at z="+z+" because capillary is detected near the border, at coordinates "+coords[z][0]+" , "+coords[z][1]+" with an indicated security radius of "+radiusAroundCapillary);
			}
			
			//Mesurer la moyenne autour du capillaire
			meanOverCap[z]=VitimageUtils.meanValueofImageAround(sliceTemp, coords[z][0], coords[z][1], 0,radiusAroundCapillary);
			
			
			//Mesurer la moyenne de l'image
			int radiusImg=Math.min(X/2,Y/2)-2;
			meanOverAll[z]=VitimageUtils.meanValueofImageAround(sliceTemp, X/2, Y/2, 0,radiusImg);
			
			//Oter l'un à l'autre
			meanOverRest[z]=meanOverAll[z]*(2*radiusImg+1)*(2*radiusImg+1) - meanOverCap[z]*(2*radiusAroundCapillary+1)*(2*radiusAroundCapillary+1);
			meanOverRest[z]=meanOverRest[z]/( (2*radiusImg+1)*(2*radiusImg+1) - (2*radiusAroundCapillary+1)*(2*radiusAroundCapillary+1) );

		}
		//Faire la moyenne des valeurs mesurées along Z
		globalMeanOverRest=VitimageUtils.statistics1D(meanOverRest)[0];
		globalMeanOverCap=VitimageUtils.statistics1D(meanOverCap)[0];

		int radiusSq=radiusAroundCapillary*radiusAroundCapillary;
		double distSq;
		//Pour chaque Z
		for(int im=0;im




© 2015 - 2024 Weber Informatics LLC | Privacy Policy