-
Mark Woolrich authoredMark Woolrich authored
AutoCorrEstimator.cc 12.92 KiB
/* AutoCorrEstimator.cc
Mark Woolrich, FMRIB Image Analysis Group
Copyright (C) 1999-2000 University of Oxford */
/* CCOPYRIGHT */
#include <iostream>
#include <strstream>
#include "AutoCorrEstimator.h"
#include "miscmaths.h"
#include "Log.h"
#include "Volume.h"
#include "histogram.h"
using namespace NEWMAT;
using namespace UTILS;
namespace TACO {
void AutoCorrEstimator::setDesignMatrix(const Matrix& dm) {
Tracer tr("AutoCorrEstimator::setDesignMatrix");
int sizeTS = xdata.getNumVolumes();
int numPars = dm.Ncols();
dminFFTReal.ReSize(zeropad, numPars);
dminFFTImag.ReSize(zeropad, numPars);
ColumnVector dmrow;
dmrow.ReSize(zeropad);
ColumnVector dm_fft_real, dm_fft_imag;
ColumnVector dummy(zeropad);
ColumnVector realifft(zeropad);
// FFT design matrix
for(int k = 1; k <= numPars; k++)
{
dummy = 0;
dmrow = 0;
mn(k) = MISCMATHS::mean(dm.Column(k));
dmrow.Rows(1,sizeTS) = dm.Column(k) - mn(k);
FFT(dmrow, dummy, dm_fft_real, dm_fft_imag);
dminFFTImag.Column(k) = dm_fft_imag;
dminFFTReal.Column(k) = dm_fft_real;
}
}
void AutoCorrEstimator::preWhiten(ColumnVector& in, ColumnVector& ret, int i, Matrix& dmret) {
Tracer tr("AutoCorrEstimator::preWhiten");
int sizeTS = xdata.getNumVolumes();
int numPars = dminFFTReal.getNumSeries();
ret.ReSize(sizeTS);
dmret.ReSize(sizeTS, numPars);
// FFT auto corr estimate
dummy = 0;
vrow = 0;
vrow.Rows(1,sizeTS/2) = acEst.getSeries(i).Rows(1,sizeTS/2);
vrow.Rows(zeropad - sizeTS/2 + 2, zeropad) = acEst.getSeries(i).Rows(2, sizeTS/2).Reverse();
FFT(vrow, dummy, ac_fft_real, ac_fft_im);
// FFT x data
dummy = 0;
xrow = 0;
xrow.Rows(1,sizeTS) = in;
FFT(xrow, dummy, x_fft_real, x_fft_im);
// inverse auto corr to give prewhitening filter:
// no DC component so set first value to 0;
ac_fft_real(1) = 0.0;
for(int j = 2; j <= zeropad; j++)
{
ac_fft_real(j) = 1.0/ac_fft_real(j);
}
// filter design matrix
for(int k = 1; k <= numPars; k++)
{
dm_fft_real = dminFFTReal.getSeries(k);
dm_fft_imag = dminFFTImag.getSeries(k);
FFTI(SP(ac_fft_real, dm_fft_real), SP(ac_fft_real, dm_fft_imag), realifft, dummy);
// place result into ret:
dmret.Column(k) = realifft.Rows(1,sizeTS);
float std = pow(MISCMATHS::var(dmret.Column(k)),0.5);
dmret.Column(k) = (dmret.Column(k)/std) + mn(k);
}
// Do filtering and inverse FFT:
FFTI(SP(ac_fft_real, x_fft_real), SP(ac_fft_real, x_fft_im), realifft, dummy);
// place result into ret:
ret = realifft.Rows(1,sizeTS);
}
void AutoCorrEstimator::preWhiten(VolumeSeries& in, VolumeSeries& ret)
{
Tracer tr("AutoCorrEstimator::preWhiten");
cerr << "Prewhitening... ";
int sizeTS = xdata.getNumVolumes();
int numTS = xdata.getNumSeries();
ret.ReSize(sizeTS, numTS);
// make sure p_vrow is cyclic (even function)
ColumnVector vrow, xrow;
vrow.ReSize(zeropad);
xrow.ReSize(zeropad);
ColumnVector x_fft_real, ac_fft_real;
ColumnVector x_fft_im, ac_fft_im;
ColumnVector dummy(zeropad);
ColumnVector realifft(zeropad);
int co = 1;
for(int i = 1; i <= numTS; i++)
{
// FFT auto corr estimate
dummy = 0;
vrow = 0;
vrow.Rows(1,sizeTS/2) = acEst.getSeries(i).Rows(1,sizeTS/2);
vrow.Rows(zeropad - sizeTS/2 + 2, zeropad) = acEst.getSeries(i).Rows(2, sizeTS/2).Reverse();
FFT(vrow, dummy, ac_fft_real, ac_fft_im);
// FFT x data
dummy = 0;
xrow = 0;
xrow.Rows(1,sizeTS/2) = in.getSeries(i).Rows(1,sizeTS/2);
xrow.Rows(zeropad - sizeTS/2 + 2, zeropad) = in.getSeries(i).Rows(2, sizeTS/2).Reverse();
FFT(xrow, dummy, x_fft_real, x_fft_im);
// inverse auto corr to give prewhitening filter:
// no DC component so set first value to 0;
ac_fft_real(1) = 0.0;
for(int j = 2; j <= zeropad; j++)
{
ac_fft_real(j) = 1.0/ac_fft_real(j);
}
// Do filtering and inverse FFT:
FFTI(SP(ac_fft_real, x_fft_real), SP(ac_fft_real, x_fft_im), realifft, dummy);
// place result into ret:
ret.Column(i) = realifft.Rows(1,sizeTS);
if(co > 100)
{
co = 1;
cerr << (float)i/(float)numTS << ",";
}
else
co++;
}
cerr << " Completed" << endl;
}
void AutoCorrEstimator::fitAutoRegressiveModel()
{
Tracer trace("AutoCorrEstimator::fitAutoRegressiveModel");
cerr << "Fitting autoregressive model..." << endl;
const int maxorder = 10;
const int minorder = 0;
int sizeTS = xdata.getNumVolumes();
int numTS = xdata.getNumSeries();
// setup temp variables
ColumnVector x(sizeTS);
ColumnVector order(numTS);
ColumnVector betas(maxorder);
acEst.ReSize(sizeTS, numTS);
acEst = 0;
int co = 1;
for(int i = 1; i <= numTS; i++)
{
x = xdata.getSeries(i).AsColumn();
order(i) = SIGPROC::Pacf(x, minorder, maxorder, betas);
if(order(i) != -1)
{
// Calculate auto corr:
ColumnVector Krow(sizeTS);
Krow = 0;
Krow(sizeTS) = 1;
if(order(i) > 0)
Krow.Rows(sizeTS-order(i), sizeTS-1) = -betas.Rows(1,order(i)).Reverse();
Matrix Kinv(sizeTS, sizeTS);
Kinv = 0;
for(int j = 1; j <= sizeTS; j++)
{
Kinv.SubMatrix(j,j,1,j) = Krow.Rows(sizeTS-j+1,sizeTS).t();
}
// Kinv now becomes V:
Kinv = (Kinv.t()*Kinv).i();
acEst.SubMatrix(1,sizeTS/2+1,i,i) = (Kinv.SubMatrix(sizeTS/2, sizeTS/2, sizeTS/2, sizeTS)/Kinv.MaximumAbsoluteValue()).AsColumn();
if(co > 200)
{
co = 1;
cerr << (float)i/(float)numTS << ",";
}
else
co++;
}
}
Log::getInstance().out("order", order);
cerr << " Completed" << endl;
}
int AutoCorrEstimator::establishUsanThresh(const Volume& epivol)
{
int usanthresh = 100;
int num = epivol.getVolumeSize();
Histogram hist(epivol, num/200);
hist.generate();
float mode = hist.mode();
cerr << "mode = " << mode << endl;
float sum = 0.0;
int count = 0;
// Work out standard deviation from mode for values greater than mode:
for(int i = 1; i <= num; i++) {
if(epivol(i) > mode) {
sum = sum + (epivol(i) - mode)*(epivol(i) - mode);
count++;
}
}
int sig = (int)pow(sum/num, 0.5);
cerr << "sig = " << sig << endl;
usanthresh = sig/3;
return usanthresh;
}
void AutoCorrEstimator::spatiallySmooth(const string& usanfname, const Volume& epivol, int masksize, const string& epifname, const string& susanpath) {
Tracer trace("AutoCorrEstimator::spatiallySmooth");
Log& logger = Log::getInstance();
// Establish epi thresh to use:
int usanthresh = establishUsanThresh(epivol);
// Setup external call to susan program:
char callsusanstr[1000];
ostrstream osc3(callsusanstr,1000);
string preSmoothVol = "preSmoothVol";
string postSmoothVol = "postSmoothVol";
osc3 << susanpath << " "
<< logger.getDir() << "/" << preSmoothVol << " 1 "
<< logger.getDir() << "/" << postSmoothVol << " "
<< masksize << " 3D 0 1 " << usanfname << " " << usanthresh << " "
<< logger.getDir() << "/" << "usanSize" << '\0';
// Loop through first third of volumes
// assume the rest are zero
int factor = 10000;
// Setup volume for reading and writing volumes:
Volume vol(acEst.getNumSeries(), xdata.getDims(), xdata.getPreThresholdPositions());
int i = 2;
cerr << "Spatially smoothing auto corr estimates" << endl;
cerr << callsusanstr << endl;
for(; i < 20; i++)
{
// output unsmoothed estimates:
vol = acEst.getVolume(i).AsColumn()*factor;
vol.unthreshold();
vol.writeAsInt(logger.getDir() + "/" + preSmoothVol);
// call susan:
system(callsusanstr);
// read in smoothed volume:
vol.read(logger.getDir() + "/" + postSmoothVol);
vol.threshold();
acEst.getVolume(i) = vol.AsRow()/factor;
cerr << ".";
}
cerr << endl;
// Clear unwanted written files
char rmfilesstr[1000];
ostrstream osc(rmfilesstr,1000);
osc << "rm -rf "
<< logger.getDir() + "/" + postSmoothVol + "* "
<< logger.getDir() + "/" + preSmoothVol + "* "
<< logger.getDir() + "/" + epifname + "* "
<< logger.getDir() + "/usanSize*" << '\0';
cerr << rmfilesstr << endl;
system(rmfilesstr);
cerr << "Completed" << endl;
}
void AutoCorrEstimator::calcRaw() {
cerr << "Calculating raw AutoCorrs...";
AutoCorr(xdata, acEst, zeropad);
cerr << " Completed" << endl;
}
void AutoCorrEstimator::filter(const ColumnVector& filterFFT) {
Tracer tr("AutoCorrEstimator::filter");
cerr << "Combining temporal filtering effects with AutoCorr estimates... ";
// This function adjusts the autocorrelations as if the
// xdata has been filtered by the passed in filterFFT
// DOES NOT filter the xdata itself
ColumnVector vrow;
// make sure p_vrow is cyclic (even function)
vrow.ReSize(zeropad);
ColumnVector fft_real;
ColumnVector fft_im;
ColumnVector dummy(zeropad);
ColumnVector realifft(zeropad);
int sizeTS = xdata.getNumVolumes();
for(int i = 1; i <= xdata.getNumSeries(); i++)
{
dummy = 0;
vrow = 0;
vrow.Rows(1,sizeTS/2) = acEst.getSeries(i).Rows(1,sizeTS/2);
vrow.Rows(zeropad - sizeTS/2 + 2, zeropad) = acEst.getSeries(i).Rows(2, sizeTS/2).Reverse();
FFT(vrow, dummy, fft_real, fft_im);
FFTI(SP(fft_real, filterFFT), dummy, realifft, dummy);
// place result into acEst:
acEst.Column(i) = realifft.Rows(1,sizeTS)/realifft(1);
}
cerr << " Completed" << endl;
}
void AutoCorrEstimator::pava() {
Tracer tr("AutoCorrEstimator::pava");
cerr << "Using New PAVA on AutoCorr estimates... ";
for(int i = 1; i <= xdata.getNumSeries(); i++) {
int sizeTS = xdata.getNumVolumes();
int stopat = (int)sizeTS/2;
// 5% point of distribution of autocorr about zero
const float th = (-1/sizeTS)+(2/sqrt(sizeTS));
ColumnVector values = acEst.Column(i);
ColumnVector zero(1);
zero = 0;
values = values.Rows(1,stopat) & zero;
ColumnVector gm(stopat + 1);
for(int j = 1; j <= stopat + 1; gm(j) = j++);
ColumnVector weights(stopat+1);
weights = 1;
bool anyviolators = true;
while(anyviolators) {
anyviolators = false;
for(int k = 2; k <= values.Nrows(); k++) {
if(values(k) > values(k-1)) {
anyviolators = true;
values(k-1) = (values(k-1)*weights(k-1) + values(k)*weights(k))/(weights(k-1) + weights(k));
values = values.Rows(1,k-1) & values.Rows(k+1,values.Nrows());
weights(k-1) = weights(k) + weights(k-1);
weights = weights.Rows(1,k-1) & weights.Rows(k+1,weights.Nrows());
for(int j = 1; j <= stopat + 1; j++) {
if(gm(j) >= k)
gm(j) = gm(j)-1;
}
break;
}
}
}
acEst.Column(i) = 0.0;
int j=1;
for(; j <= stopat; j++) {
acEst(j,i) = values(gm(j));
if(acEst(j,i) <= 0.0)
{
acEst(j,i) = 0.0;
break;
}
}
if(acEst(2,i) < th/2)
{
acEst.SubMatrix(2,stopat,i,i) = 0;
}
else if(j > 2)
//if(j > 2)
{
int endst = j;
int stst = j-(int)(1+(j/8.0));
const int expwidth = MISCMATHS::Max((endst - stst)/2,1);
const int exppow = 2;
for(j = stst; j <= endst; j++)
{
acEst(j,i) = acEst(j,i)*exp(-pow((j-stst)/expwidth,exppow));
}
}
}
cerr << " Completed" << endl;
}
void AutoCorrEstimator::applyConstraints() {
Tracer tr("AutoCorrEstimator::applyConstraints");
cerr << "Applying constraints to AutoCorr estimates... ";
for(int i = 1; i <= xdata.getNumSeries(); i++)
{
int sizeTS = xdata.getNumVolumes();
int j = 3;
int stopat = (int)sizeTS/4;
// found1 is last valid value above threshold
int found1 = stopat;
// 5% point of distribution of autocorr about zero
const float thresh = (-1/sizeTS)+(2/sqrt(sizeTS));
acEst(2,i) = (acEst(2,i)+ acEst(3,i))/2;
if(acEst(2,i) < 0)
{
acEst(2,i) = 0;
}
else
{
float grad = 0.0;
while(j <= stopat && j < found1 + 2)
{
grad = ((acEst(j,i) + acEst(j+1,i))/2 - acEst(j-1,i))/1.5;
if(grad < 0)
acEst(j,i) = grad + acEst(j-1,i);
else
acEst(j,i) = acEst(j-1,i);
// look for threshold
if(acEst(j,i) < thresh/3.0 && found1 == stopat)
{
found1 = j;
}
if(acEst(j,i) < 0)
{
acEst(j,i) = 0;
}
j++;
}
}
// set rest to zero:
for(; j <= sizeTS; j++)
{
acEst(j,i) = 0;
}
}
cerr << "Completed" << endl;
}
void AutoCorrEstimator::getMeanEstimate(ColumnVector& ret)
{
Tracer tr("AutoCorrEstimator::getMeanEstimate");
ret.ReSize(acEst.getNumVolumes());
// Calc global Vrow:
for(int i = 1; i <= acEst.getNumVolumes(); i++)
{
ret(i) = MISCMATHS::mean(acEst.getVolume(i).AsColumn());
}
}
}