// {{{ Copyright etc. /* feat_model - create FEAT design matrix, contrasts etc. Stephen Smith and Matthew Webster, FMRIB Analysis Group Copyright (C) 1999-2010 University of Oxford */ /* Part of FSL - FMRIB's Software Library http://www.fmrib.ox.ac.uk/fsl fsl@fmrib.ox.ac.uk Developed at FMRIB (Oxford Centre for Functional Magnetic Resonance Imaging of the Brain), Department of Clinical Neurology, Oxford University, Oxford, UK LICENCE FMRIB Software Library, Release 5.0 (c) 2012, The University of Oxford (the "Software") The Software remains the property of the University of Oxford ("the University"). The Software is distributed "AS IS" under this Licence solely for non-commercial use in the hope that it will be useful, but in order that the University as a charitable foundation protects its assets for the benefit of its educational and research purposes, the University makes clear that no condition is made or to be implied, nor is any warranty given or to be implied, as to the accuracy of the Software, or that it will be suitable for any particular purpose or for use under any specific conditions. Furthermore, the University disclaims all responsibility for the use which is made of the Software. It further disclaims any liability for the outcomes arising from using the Software. The Licensee agrees to indemnify the University and hold the University harmless from and against any and all claims, damages and liabilities asserted by third parties (including claims for negligence) which arise directly or indirectly from the use of the Software or the sale of any products based on the Software. No part of the Software may be reproduced, modified, transmitted or transferred in any form or by any means, electronic or mechanical, without the express permission of the University. The permission of the University is not required if the said reproduction, modification, transmission or transference is done without financial return, the conditions of this Licence are imposed upon the receiver of the product, and all original and amended source code is included in any transmitted product. You may be held legally responsible for any copyright infringement that is caused or encouraged by your failure to abide by these terms and conditions. You are not permitted under this Licence to use this Software commercially. Use for which any financial return is received shall be defined as commercial use, and includes (1) integration of all or part of the source code or the Software into a product for sale or license by or on behalf of Licensee to third parties or (2) use of the Software or any derivative of it for research with the final aim of developing software products for sale or license to a third party or (3) use of the Software or any derivative of it for research with the final aim of developing non-software products for sale or license to a third party, or (4) use of the Software to provide any service to an external organisation for which payment is received. If you are interested in using the Software commercially, please contact Isis Innovation Limited ("Isis"), the technology transfer company of the University, to negotiate a licence. Contact details are: innovation@isis.ox.ac.uk quoting reference DE/9564. */ #define _GNU_SOURCE 1 #define POSIX_SOURCE 1 #include "featlib.h" #include "miscmaths/miscprob.h" #include "miscmaths/t2z.h" #include "libprob.h" #include #include using namespace MISCMATHS; using namespace NEWIMAGE; #define DT 0.05 /* temporal sampling for initial model, in seconds */ #define NEGSECS 30.0 /* amount of negative modelling allowed, for custom 3 etc, in seconds */ class Contrasts { public: Matrix C; vector name; ColumnVector realHeights; Contrasts( const int nRealEVs, const int nContrasts ); int nC() const { return C.Ncols(); }; int nEV() const { return C.Nrows(); }; ColumnVector RE; }; Contrasts::Contrasts( const int nRealEVs, const int nContrasts ) { C.ReSize(nRealEVs,nContrasts); //this is the transpose of FEAT's design.con format realHeights.ReSize(nContrasts); C=0; name.resize(nContrasts); RE.ReSize(nContrasts); } class Regressors { public: int nReal; int nRealNoMotion; int nOriginal; int nOriginalNoMotion; int nMotion; int nTimepoints; vector nRealPerOrig; vector usesDeriv; vector usesFilter; Matrix orthogonals; Matrix copyOfRealDesign; ColumnVector realHeights; DiagonalMatrix eigenvals; vector basisorth; // whether or not to orth basis functions wrt each other int equivalentRealLocation(const int orginalLocation); }; int Regressors::equivalentRealLocation(const int originalLocation) { int currentRealEV(0); for (int ev=1; ev < originalLocation; ev++) currentRealEV+=nRealPerOrig[ev-1]; return(++currentRealEV); } Regressors peakAndFilter(const bool estimateRealHeights, Regressors& evs, Contrasts& contrasts, const double hp_sigma, const double lp_sigma, const int nTimepoints, const Matrix& preWhitening, const float critical_t, const float noise, const string filename); void writeSubmodel( const string filename, const Regressors& evs); void writeDesignMatrix(const string filenameRoot, const ColumnVector& realDesignHeights, const Matrix& realDesign); void writeTriggers(const string filename, const vector& triggers, const int* shape, const Regressors& evs, const double factor); void writeContrastMatrix(const string filename, const Contrasts& contrasts, const ColumnVector& RE ); void writeFMatrix(const string filename, const Contrasts& contrasts, const Matrix& F, const int nFTests, const Matrix& realDesign ); void writeGroupFile(const string filename, const vector G, const int nTimepoints, const Matrix& realDesign); ColumnVector filterRegressor(const ColumnVector& inputRegressor, const double hp_sigma, const double lp_sigma) { ColumnVector outputRegressor(inputRegressor); volume4D im(1,1,1,inputRegressor.Nrows()); for(int t=0;t1e-5) { old_diff=tmax-tmin; float t=(tmax+tmin)/2; float z=T2z::getInstance().convert(t,dof); if (z>absz) tmax=t; else tmin=t; } if (z<0) tmin=-tmin; return tmin; } // }}} // {{{ mygammapdf ReturnMatrix mygammapdf(const int npts, const float mult, const float delay, const float sigma) { ColumnVector grot(npts); for(int i=0; i1) { SVD(real_X,eigenvals); SortDescending(eigenvals); float inv_condition = eigenvals.Minimum() / eigenvals.Maximum(); //cout << "eigenvals = " << eigenvals << endl; //cout << "inv_condition = " << inv_condition << endl; if (inv_condition<1e-7) { cout << "Warning: at least one EV is (close to) a linear combination of the others. You probably need to alter your design.\n(Design matrix is rank deficient - ratio of min:max eigenvalues in SVD of matrix is " << inv_condition << ")\n Contrasts involving these combinations will be set to zero.\n" << endl; } } eigenvals.Release(); return eigenvals; } // }}} // {{{ renorm kernel // void renorm_kernel(ColumnVector &X) // { // double kernelsum=0; // for(int i=0; i& titles, const float tr, const float nltffwhm, const int nTimepoints, const vector& G ); void error_exit(const char *outkey ) { printf("%s",outkey); exit(1); } void setup_font(FONT_DATA *font_data) { // {{{ Default Font /* The default font, packed in hex so this source file doesn't get huge. You can replace this with your own font using pbm_dumpfont(). */ static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = { {0x00000000,0x20000c00,0x10000000,0x00000000}, {0xc600a000,0x42000810,0x00000002,0x00000063}, {0x6c00a000,0x45000810,0x00000002,0x00000036}, {0x6c00a000,0x88800808,0xf2e1dee2,0x00000036}, {0x54000000,0x80000800,0x11122442,0x0000002a}, {0x54000001,0x00000800,0x11122442,0x0000002a}, {0x54000001,0x00000800,0x11122282,0x0000002a}, {0x44000102,0x00000800,0x11122382,0x00000022}, {0xee000102,0x00000800,0x11e1e102,0x00000077}, {0x00000204,0x00000800,0x11002102,0x00000000}, {0x00000000,0x00000c00,0x11002102,0x00000000}, {0x00000000,0x003f8000,0xe3807600,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x02000080,0x00040000,0x00120000,0x00000001}, {0x04000082,0x828e1838,0x20210100,0x00000002}, {0x04000082,0x82912448,0x20210100,0x00000002}, {0x08000082,0x8fd01940,0x404087c2,0x00000004}, {0x08000080,0x050c0622,0x00408102,0x00000004}, {0x10000080,0x05061874,0x0040828f,0x00008008}, {0x10000080,0x1f912688,0x00408002,0x00000008}, {0x20000000,0x0a11098c,0x00408002,0x00000010}, {0x20000080,0x0a0e0672,0x00210000,0x00000010}, {0x40000000,0x00040000,0x00210000,0x00000020}, {0x00000000,0x00000000,0x00120000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x004e0838,0x7023e1cf,0x00008000}, {0x00000000,0x00913844,0x88620208,0x00008000}, {0x08000000,0x00910844,0x08a20401,0x00000004}, {0x10000000,0x01110844,0x08a20401,0x00000008}, {0x20000000,0x01110808,0x3123c781,0x00000010}, {0x400003e0,0x02110810,0x0a202441,0x00000020}, {0x20000000,0x02110820,0x0bf02442,0x00000010}, {0x10008000,0x04110844,0x88242442,0x00000008}, {0x08008002,0x040e3e7c,0x7073c382,0x00000004}, {0x00010000,0x08000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x0000e1c0,0x00000000,0x00000000,0x00000000}, {0x00011220,0x00000000,0x70e38f87,0x00000000}, {0x20011220,0x00020020,0x89108448,0x00008010}, {0x10011220,0x00040010,0x09314448,0x00008008}, {0x0800e221,0x02083e08,0x11514788,0x00000004}, {0x040111e0,0x00100004,0x2153e448,0x00000002}, {0x08011020,0x00083e08,0x213a2448,0x00008004}, {0x10011040,0x02040010,0x01022448,0x00008008}, {0x2000e381,0x02020020,0x20e77f87,0x00000010}, {0x00000000,0x04000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x3803e7ef,0xc73bbe3d,0xdb863ce7,0x0000001c}, {0x44011224,0x48910808,0x91036648,0x00008022}, {0x4c011285,0x48910808,0xa1036648,0x00008026}, {0x54011387,0x081f0808,0xc102a548,0x0000802a}, {0x54011285,0x09910808,0xe102a548,0x0000802a}, {0x4e011204,0x08910848,0x9112a4c8,0x00008027}, {0x40011224,0x08910848,0x891224c8,0x00008020}, {0x3803e7ef,0x073bbe31,0xcff77e47,0x0000001c}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000003,0x00000000}, {0x0003e1cf,0x87bff7ef,0xdfbf77c2,0x00000000}, {0x00013224,0x48a4a244,0x89122442,0x00000000}, {0x00011224,0x4824a244,0xa8a14482,0x00000000}, {0x00013227,0x8e04226c,0xa8414102,0x00000000}, {0x0001e224,0x83842228,0xa8a08102,0x00000000}, {0x00010224,0x40842228,0xd8a08242,0x00000000}, {0x00010224,0x48843638,0x51108442,0x00000000}, {0x0003c1ce,0x6f1f1c10,0x53b9c7c2,0x00000000}, {0x00000060,0x00000000,0x00000002,0x00000000}, {0x00000000,0x00000000,0x00000003,0x00000000}, {0xfe000000,0x00000000,0x00000000,0x0000007f}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00010180,0x000000c0,0x003001c0,0x00000000}, {0x08008081,0x00040040,0x00100200,0x00000004}, {0x10008082,0x80040040,0x00100200,0x00000008}, {0x10004084,0x40023c78,0x70f1c7c7,0x00004008}, {0x10004080,0x00000244,0x89122208,0x00008008}, {0x20002080,0x00001e44,0x8113e208,0x00008010}, {0x10002080,0x00002244,0x81120208,0x00008008}, {0x10001080,0x00002244,0x89122208,0x00008008}, {0x10001080,0x00001db8,0x70e9c787,0x00008008}, {0x10000880,0x00000000,0x00000000,0x00008008}, {0x08000180,0x00000000,0x00000000,0x00008004}, {0x00000000,0x1fc00000,0x00000007,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00030080,0x981c0000,0x00000000,0x00000000}, {0x20010000,0x08040000,0x00000000,0x00000010}, {0x10010000,0x08040000,0x00000000,0x00000008}, {0x10016387,0x898474b8,0x72e1d5c7,0x00000008}, {0x10019080,0x8a042a64,0x89122208,0x00008008}, {0x08011080,0x8c042a44,0x89122207,0x00000004}, {0x10011080,0x8a042a44,0x89122200,0x00008008}, {0x10011080,0x89042a44,0x89122208,0x00008008}, {0x1003bbe0,0x98dfebe6,0x71e1e787,0x00000008}, {0x10000000,0x80000000,0x01002000,0x00000008}, {0x20000000,0x80000000,0x01002000,0x00000010}, {0x00000007,0x00000000,0x03807000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00008000,0x00000000,0x10410000,0x00000000}, {0x00008000,0x00000000,0x20408000,0x00000000}, {0x0001f66e,0xfdfbf77c,0x20408000,0x00000000}, {0x24008224,0x488a2248,0x20408240,0x00000012}, {0x54008224,0x4a842210,0x40404540,0x0000002a}, {0x48008222,0x8a8a1420,0x20408480,0x00000024}, {0x00008a23,0x85111c44,0x20408000,0x00000000}, {0x000071d1,0x0531887c,0x20408000,0x00000000}, {0x00000000,0x00000800,0x20408000,0x00000000}, {0x00000000,0x00000800,0x10410000,0x00000000}, {0x00000000,0x00003000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x00000000,0x00000000,0x00000000}, {0x00000000,0x20000c00,0x10000000,0x00000000}, {0xc600a000,0x42000810,0x00000002,0x00000063}, {0x6c00a000,0x45000810,0x00000002,0x00000036}, {0x6c00a000,0x88800808,0xf2e1dee2,0x00000036}, {0x54000000,0x80000800,0x11122442,0x0000002a}, {0x54000001,0x00000800,0x11122442,0x0000002a}, {0x54000001,0x00000800,0x11122282,0x0000002a}, {0x44000102,0x00000800,0x11122382,0x00000022}, {0xee000102,0x00000800,0x11e1e102,0x00000077}, {0x00000204,0x00000800,0x11002102,0x00000000}, {0x00000000,0x00000c00,0x11002102,0x00000000}, {0x00000000,0x003f8000,0xe3807600,0x00000000} }; // }}} unsigned char b; int rows, cols, scol, brow, d, bcol, ch; unsigned long l; // {{{ text explanation /* ** This routine expects a font bitmap representing the following text: ** ** (0,0) ** M ",/^_[`jpqy| M ** ** / !"#$%&'()*+ / ** < ,-./01234567 < ** > 89:;<=>?@ABC > ** @ DEFGHIJKLMNO @ ** _ PQRSTUVWXYZ[ _ ** { \]^_`abcdefg { ** } hijklmnopqrs } ** ~ tuvwxyz{|}~ ~ ** ** M ",/^_[`jpqy| M ** ** The bitmap must be cropped exactly to the edges. ** ** The dissection works by finding the first blank row and column; that ** gives the height and width of the maximum-sized character, which is ** not too useful. But the distance from there to the opposite side is ** an integral multiple of the cell size, and that's what we need. Then ** it's just a matter of filling in all the coordinates. */ // }}} // {{{ create font bits for ( rows = 0; rows < DEFAULTFONT_ROWS; ++rows ) { for ( cols = 0; cols < DEFAULTFONT_COLS; cols += 32 ) { l = defaultfont_bits[rows][cols / 32]; if (cols + 32 < DEFAULTFONT_COLS) ch = cols + 32; else ch = DEFAULTFONT_COLS; for ( scol = ch - 1; scol >= cols; --scol ) { if ( l & 1 ) font_data -> font[rows][scol] = 1; else font_data -> font[rows][scol] = 0; l >>= 1; } } } // }}} // {{{ Find first blank row for ( brow = 0; brow < DEFAULTFONT_ROWS / 6; ++brow ) { b = font_data -> font[brow][0]; for ( cols = 1; cols < DEFAULTFONT_COLS; ++ cols ) if ( font_data -> font[brow][cols] != b ) goto nextrow; goto gotblankrow; nextrow: ; } error_exit("Couldn't find blank row in font\n"); // }}} // {{{ gotblankrow: gotblankrow: /* Find first blank col. */ for ( bcol = 0; bcol < DEFAULTFONT_COLS / 8; ++bcol ) { b = font_data -> font[0][bcol]; for ( rows = 1; rows < DEFAULTFONT_ROWS; ++ rows ) if ( font_data -> font[rows][bcol] != b ) goto nextcol; goto gotblankcol; nextcol: ; } error_exit("Couldn't find blank col in font.\n"); // }}} // {{{ gotblankcol: gotblankcol: /* Now compute character cell size. */ d = DEFAULTFONT_ROWS - brow; font_data -> char_height = d / 11; if ( font_data -> char_height * 11 != d ) error_exit("problem computing character cell height"); d = DEFAULTFONT_COLS - bcol; font_data -> char_width = d / 15; if ( font_data -> char_width * 15 != d ) error_exit("problem computing character cell width"); /*printf("height=%d width=%d\n",font_data -> char_height, font_data -> char_width);*/ /* Now fill in the 0,0 coords. */ rows = font_data -> char_height * 2; cols = font_data -> char_width * 2; for ( ch = 0; ch < 95; ++ch ) { font_data -> char_row0[ch] = rows; font_data -> char_col0[ch] = cols; cols += font_data -> char_width; if ( cols >= font_data -> char_width * 14 ) { cols = font_data -> char_width * 2; rows += font_data -> char_height; } } // }}} } // }}} // {{{ write_string void write_string(unsigned char *in, int x, int y, const char *the_string, FONT_DATA *font_data, int colour, int x_size, int y_size) { char string_bit; int i, r, c, X, Y; for (i=0; the_string[i] != '\0'; i++) { string_bit = the_string[i] - ' '; for ( r = 0; r < font_data->char_height; ++r ) for ( c = 0; c < font_data->char_width; ++c ) { X = (i*font_data->char_width)+c+x; Y = r+y; if ( (X>-1) && (X-1) && (Yfont[font_data->char_row0[(int)string_bit] + r][font_data->char_col0[(int)string_bit] + c]==1) ) in[Y*x_size+X]=colour; } } } void write_string_rgb(unsigned char *r, unsigned char *g, unsigned char *b, int x, int y, const char *the_string, FONT_DATA *font_data, int cr, int cg, int cb, int x_size, int y_size) { write_string(r, x, y, the_string, font_data, cr, x_size, y_size); write_string(g, x, y, the_string, font_data, cg, x_size, y_size); write_string(b, x, y, the_string, font_data, cb, x_size, y_size); } // }}} double RequiredEffect(const Matrix& realDesign, const Matrix& Q, const ColumnVector& singleContrast, const Matrix& preWhitening, const float critical_t, double& height, const float noise, const bool usePreWhitening=true) { Matrix X2( realDesign * Q * singleContrast * pinv(singleContrast.t() * Q * singleContrast) ); if ( usePreWhitening ) // whiten X2 X2 = preWhitening*X2; if ( height == -1 ) // make sure that 0 is included in the X2 min:max range this is so that (eg) all-1s EVs have height 1 (etc) height= Max( X2.Maximum() , 0.0f ) - Min( X2.Minimum() , 0.0f ); float D = height / sqrt( (X2.t() * X2).AsScalar() ); return ( critical_t * D * noise ); } int main(int argc, char **argv) { // {{{ variables int i, t, mnpts, level, orig_ev, real_ev, shape[10000], nftests, f; vector G; vector titles; float tr, mult, trmult, nltffwhm=0, maxconvwin=0; char fl[10000], *FSLDIR; string fn, filename; FONT_DATA *font_data = new FONT_DATA[1]; Regressors evs; // }}} // {{{ read arguments and prepare variables if (argc<2) { cout << "Usage: feat_model [confound matrix text file]" << endl; exit(1); } Matrix motionparams(0,0); if (argc==3) motionparams=remmean(read_ascii_matrix(argv[2])); FSLDIR=getenv("FSLDIR"); fn = string(argv[1])+".fsf"; level = atoi(find_line(fn, "fmri(level)", fl)); tr = atof(find_line(fn, "fmri(tr)", fl)); int nTimepoints = atoi(find_line(fn, "fmri(npts)", fl)); evs.nTimepoints=nTimepoints; int ndelete = atoi(find_line(fn, "fmri(ndelete)", fl)); evs.nOriginalNoMotion = atoi(find_line(fn, "fmri(evs_orig)", fl)); evs.nOriginal=evs.nOriginalNoMotion + (motionparams.Ncols()>0); evs.nRealNoMotion = atoi(find_line(fn, "fmri(evs_real)", fl)); evs.nReal= evs.nRealNoMotion + motionparams.Ncols(); int nVoxelwiseEVs= atoi(find_line(fn, "fmri(evs_vox)", fl)); int nContrasts = atoi(find_line(fn, "fmri(ncon_real)", fl)); nftests = atoi(find_line(fn, "fmri(nftests_real)", fl)); titles.resize(evs.nOriginal); evs.usesFilter.resize(evs.nOriginal,true); evs.usesDeriv.resize(evs.nOriginalNoMotion,false); evs.orthogonals.ReSize(evs.nOriginalNoMotion,evs.nOriginalNoMotion+1); //the '0' column (1st) is whether any ortho's are present evs.orthogonals=0; for(int orig_ev=0; orig_ev triggers(evs.nOriginal*2*10*nTimepoints,-1e7);/* factor of 10 for safety */ vector negpts(evs.nOriginal,0); vector convolve_interaction(evs.nOriginal,0); evs.basisorth.resize(evs.nOriginal,0); float critical_t = z2t( atof(find_line(fn, "fmri(critical_z)", fl)) , MAX(nTimepoints-evs.nReal,1) ); float noise = atof(find_line(fn, "fmri(noise)", fl)); float noisear = atof(find_line(fn, "fmri(noisear)", fl)); /* setup prewhitening stuff */ SymmetricMatrix pwV(nTimepoints); for(int i=1; i<=nTimepoints; i++) for(int j=1; j<=i; j++) pwV(i,j)=pow(noisear,(double)abs(i-j)); Matrix pwA = (Cholesky(pwV)).i(); // note newmat definition opposite of matlab //cout << pwA * pwV * pwA.t() << endl; // should be identity // }}} // {{{ read contrasts // modified from find_line FILE *fd; char tmp_fl[10000]; fd=fopen(fn.c_str(),"rb"); while ( fgets(tmp_fl, 1000, fd) != NULL ) { if (strncmp(tmp_fl,"set fmri(conname_real.",22)==0) { //tokenize tmp_vals... int con=atoi(strtok(tmp_fl+22,") ")); contrasts.name[con-1]=string(strtok(NULL,"\n\r\f")); size_t found( contrasts.name[con-1].find_first_of('\"') ); if ( found != string::npos ) //These lines are to match the output of the previous feat_model ( spacing the png and removing quotes etc ) contrasts.name[con-1].erase(contrasts.name[con-1].begin(),contrasts.name[con-1].begin()+found+1); found = contrasts.name[con-1].find_last_of('\"'); if ( found != string::npos ) contrasts.name[con-1].replace(found,1,1,' '); } if (strncmp(tmp_fl,"set fmri(con_real",17)==0) { //tokenize tmp_vals... int con=atoi(strtok(tmp_fl+17,"."));// before period is contrastnum real_ev=atoi(strtok(NULL,") "));//between period and ") " is real_ev num contrasts.C(real_ev,con)=atof(strtok(NULL," \0\n\t")); // rest is value } if (nftests>0) if (strncmp(tmp_fl,"set fmri(ftest_real",19)==0) { //tokenize tmp_vals... f=atoi(strtok(tmp_fl+19,"."));// before period is f-testnum int con=atoi(strtok(NULL,") "));//between period and ") " is contrast num F(f,con)=atof(strtok(NULL," \0\n\t")); // rest is value } } fclose(fd); if ( testForZeroContrasts(contrasts) ) exit(1); // }}} // {{{ read and create design matrix if (level==1) { bool temphp_yn( atoi(find_line(fn, "fmri(temphp_yn)", fl))); bool templp_yn( atoi(find_line(fn, "fmri(templp_yn)", fl))); double hp_sigma(-1), lp_sigma(-1); nltffwhm = atof(find_line(fn, "fmri(paradigm_hp)", fl)); if ( templp_yn ) lp_sigma=2.8/tr; if ( temphp_yn ) hp_sigma=0.5*nltffwhm/tr; // {{{ create basic shape for(orig_ev=0; orig_evmnpts) ) stop=mnpts-skip; for(t=(int)skip;t<(int)(skip+stop);t++) { // do modulo maths in float not int - necessary for very short TR and block length if ( t+phase-skip - ((int)((t+phase-skip)/(off+on)))*(off+on) >= off ) originalHighResDesign(t+1,orig_ev+1)=1.0; else originalHighResDesign(t+1,orig_ev+1)=0.0; } } break; case 1: // {{{ sinusoid { int skip=(int)(atof(find_line(fn, tclKey("skip",orig_ev+1), fl))*mult); float period=atof(find_line(fn, tclKey("period",orig_ev+1), fl)) * mult * 0.5 / M_PI; float phase=atof(find_line(fn, tclKey("phase",orig_ev+1), fl)) * mult; int stop=(int)(atof(find_line(fn, tclKey("stop",orig_ev+1), fl))*mult); if ( (stop<0) || (stop+skip>mnpts) ) stop=mnpts-skip; for(t=skip;t0) && (stop-start<1) ) stop=start+1.1; /* if very short stim time make sure it happens */ if (start<0) start=0; /* don't enter stuff before t=-negpts */ if (start>mnpts) stop=0; /* don't do anything */ if (stop>mnpts) stop=mnpts; /* don't overrun */ for(t=(int)start; t<(int)stop; t++) { originalHighResDesign(t+1,orig_ev+1)=value; success=1; } } fclose(ifp2); if ( success==0 ) { cout << "No valid\n[onset duration strength]\ntriplets found in " << filename << endl; exit(1); } } break; // }}} case 4: // {{{ interactions // complication; if the EVs used to feed into the interaction EV don't // have the same convolution settings, then we cannot do this // interaction before convolving the interaction - we must do the convolutions first.... { originalHighResDesign.Column(orig_ev+1)=1; int tmp, convEV=-1, other_convolves=-1; for(tmp=0; tmp ev_image; if ( read_volume4D(ev_image,find_line(fn, tclKey("evs_vox_",orig_ev+1), fl)) ) cout << "Warning: voxelwise EV " << orig_ev+1 << " isn't readable" << endl; for(i=0,t=0; t0) { int convolve_phase=(int)(atof(find_line(fn, tclKey("convolve_phase",c_orig_ev+1), fl))*mult); if ( (convolve>3) && (convolve<8) ) { evs.basisorth[orig_ev]=atoi(find_line(fn, tclKey("basisorth",c_orig_ev+1), fl)); } switch (convolve) { case 1: // {{{ Gaussian { float sigma=atof(find_line(fn, tclKey("gausssigma",c_orig_ev+1), fl))*mult; float delay=atof(find_line(fn, tclKey("gaussdelay",c_orig_ev+1), fl))*mult; int fw = (int)(delay + sigma*5); maxconvwin=MAX(fw,maxconvwin); ColumnVector cX(fw); for(i=0; i0) { int skip=(int)(atof(find_line(fn, tclKey("skip",c_orig_ev+1), fl))*mult); float period=atof(find_line(fn, tclKey("period",c_orig_ev+1), fl)) * mult * 0.5 / M_PI; float phase=atof(find_line(fn, tclKey("phase",c_orig_ev+1), fl)) * mult; int stop=(int)(atof(find_line(fn, tclKey("stop",c_orig_ev+1), fl))*mult); if ( (stop<0) || (stop+skip>mnpts) ) stop=mnpts-skip; evs.nRealPerOrig[orig_ev]+=nharmonics; for(int harm=1; harm<=nharmonics; harm++) { real_ev++; for(t=skip;t 0 ) { for(orig_ev=real_ev=0; orig_ev tempNReal= evs.nRealPerOrig; evs.copyOfRealDesign=realDesign; evs.nRealPerOrig=tempNReal; peakAndFilter(true, evs, contrasts, -1, -1, nTimepoints, pwA, critical_t, noise, string(argv[1])+".frf"); /*for(double mod=90.0/(2.0*tr);mod<=3*nTimepoints;mod+=1) { evs.copyOfRealDesign=realDesign; evs.nRealPerOrig=tempNReal; peakAndFilter(false, evs, contrasts, mod, -1, nTimepoints, pwA, critical_t, noise, string(argv[1])+".frf"); for(int con=1; con<=contrasts.nC(); con++) cerr << contrasts.RE(con) << endl; cerr << "****************" << endl; }*/ evs.copyOfRealDesign=realDesign; evs.nRealPerOrig=tempNReal; peakAndFilter(false, evs, contrasts, hp_sigma, lp_sigma, nTimepoints, pwA, critical_t, noise, string(argv[1])+".frf"); realDesign=evs.copyOfRealDesign; //NEW vector fileList; ifstream inputFile("vef.dat"); if ( inputFile.is_open() ) { while (!inputFile.eof() ) { string input; inputFile >> skipws >> input >> skipws; if ( input.size() != 0 ) fileList.push_back(input); } inputFile.close(); } for( unsigned int currentRegressor=0; currentRegressor < fileList.size(); currentRegressor++ ) { volume4D inputImage; read_volume4D(inputImage,fileList[currentRegressor]); save_volume4D(inputImage,"Input"+fileList[currentRegressor]); inputImage=bandpass_temporal_filter(inputImage, hp_sigma, lp_sigma); save_volume4D(inputImage,fileList[currentRegressor]); } //END NEW } else { // group level design for(real_ev=0; real_ev ev_image; if ( read_volume4D(ev_image,find_line(fn, tclKey("evs_vox_",1+real_ev-(evs.nReal-nVoxelwiseEVs)), fl)) ) cout << "Warning: voxelwise EV " << 1+real_ev-nVoxelwiseEVs << " isn't readable" << endl; for(t=0;t0) for(int tmp=0; tmp 1e6 ) contrasts.realHeights(con) = 0; // try to catch dodgy heights, e.g. from empty EVs } } else { writeSubmodel( filename, evs); // check rank of DM and do final contrast estimability test // first do real rank deficiency test evs.eigenvals = feat_svd(evs.copyOfRealDesign); // now do "meaningful" rank deficiency test for(int con=1; con<=contrasts.nC(); con++) { ColumnVector dumbregressor( evs.copyOfRealDesign * contrasts.C.Column(con) ); /* just in order to test if it's empty */ if ( dumbregressor.Maximum() - dumbregressor.Minimum() > 1e-10) { /* i.e. not an 'empty' contrast, e.g. from empty EVs */ contrasts.RE(con) = RequiredEffect(evs.copyOfRealDesign, pinv(evs.copyOfRealDesign.t() * evs.copyOfRealDesign), contrasts.C.Column(con), preWhitening, critical_t, contrasts.realHeights(con), noise); } else { contrasts.realHeights(con)=0; contrasts.C.Column(con)=0; contrasts.RE(con) = 0; } } } return evs; } void writeDesignMatrix(const string filename, const ColumnVector& realDesignHeights, const Matrix& realDesign) { FILE *outputFile; if ((outputFile=fopen(filename.c_str(),"wb"))==NULL) { cout << "Can't open " << filename << " for writing" << endl; exit(1); } fprintf(outputFile,"/NumWaves %d\n", realDesign.Ncols() ); fprintf(outputFile,"/NumPoints %d\n", realDesign.Nrows() ); fprintf(outputFile,"/PPheights "); for(int currentEV=1; currentEV <= realDesign.Ncols(); currentEV++) fprintf(outputFile," %e",realDesignHeights(currentEV)); fprintf(outputFile,"\n"); fprintf(outputFile,"\n/Matrix\n"); for(int t=1; t <= realDesign.Nrows(); t++) { for(int currentEV=1; currentEV <= realDesign.Ncols(); currentEV++) fprintf(outputFile,"%e ",realDesign(t,currentEV)); fprintf(outputFile,"\n"); } fclose(outputFile); } void writeTriggers(const string filename, const vector& triggers, const int* shape, const Regressors& evs, const double factor) { FILE* triggerFile; if ((triggerFile=fopen(filename.c_str(),"wb"))==NULL) { cout << "Can't open " << filename << " for writing" << endl; exit(1); } for( int orig_ev=0; orig_ev-1e6) ) { /* check for at least one trigger pair */ ColumnVector diffs_list(2*evs.nTimepoints); int j; for(j=0;triggers[j*evs.nOriginal+orig_ev]>-1e6;j+=2) { fprintf(triggerFile,"%e ",triggers[j*evs.nOriginal+orig_ev]); if (triggers[(j+1)*evs.nOriginal+orig_ev]>-1e6) diffs_list(j/2+1)=triggers[(j+1)*evs.nOriginal+orig_ev]-triggers[j*evs.nOriginal+orig_ev]; } diffs_list=diffs_list.Rows(1,j/2); fprintf(triggerFile,"%f\n",median(diffs_list) + factor); } else fprintf(triggerFile,"0\n"); } fclose(triggerFile); } void writeSubmodel( const string filename, const Regressors& evs) { FILE* outputFile; if ( (outputFile=fopen(filename.c_str(),"wb") )==NULL) { cout << "Can't open " << filename << " for writing" << endl; exit(1); } for(int orig_ev=0; orig_ev0) { for(int f=1; f<=nFTests; f++) { Matrix Fmat(contrasts.nC(),contrasts.nEV()); int Fmat_rows(0); for(int con=1; con<=contrasts.nC(); con++) if (F(f,con)) { Fmat_rows++; Fmat.Row(Fmat_rows)=( contrasts.C.Column(con)).t(); } Fmat=Fmat.Rows(1,Fmat_rows); // test that F(X'X)^-1F' is invertible, i.e. of full rank if ( (Fmat.Nrows()==0) || ( rank(Fmat*pinv(realDesign.t()*realDesign)*Fmat.t()) < Fmat.Nrows() ) ) { cout << "F-test " << f << " isn't valid - each included contrast cannot be a linear combination of the others." << endl; exit(1); } } FILE *outputFile; if ((outputFile=fopen(filename.c_str(),"wb"))==NULL) { cout << "Can't open " << filename << " for writing" << endl; exit(1); } fprintf(outputFile,"/NumWaves %d\n",contrasts.nC()); fprintf(outputFile,"/NumContrasts %d\n",nFTests); fprintf(outputFile,"\n/Matrix\n"); for(int f=1; f<=nFTests; f++) { for(int con=1; con<=contrasts.nC(); con++) fprintf(outputFile,"%d ",(int)F(f,con)); fprintf(outputFile,"\n"); } fclose(outputFile); } } void writeGroupFile(const string filename, const vector G, const int nTimepoints, const Matrix& realDesign) { int maxG(0), isok, isnotzero; for(int t=0;t1) { vector sub_X(nTimepoints*maxG,0); vector n_sub_X(maxG,0); for( int real_ev=1; real_ev<=realDesign.Ncols(); real_ev++) { for(int i=0; i < maxG; i++) n_sub_X[i]=0; for(int t=0; t < nTimepoints; t++) sub_X[(G[t]-1)*nTimepoints+n_sub_X[G[t]-1]++] = realDesign(t+1,real_ev); isok=2; for(int i=0; i < maxG; i++) { isnotzero=0; for(int t=0; t < n_sub_X[i]; t++) if ( sub_X[i*nTimepoints+t] != 0 ) isnotzero=1; isok-=isnotzero; } if ( isok < 1 ) printf("Warning - design matrix uses different groups (for different variances), but these do not contain \"separable\" EVs for the different groups (it is necessary that, for each EV, only one of the groups has non-zero values).\n"); } } fprintf(outputFile,"/NumWaves 1\n"); fprintf(outputFile,"/NumPoints %d\n",nTimepoints); fprintf(outputFile,"\n/Matrix\n"); for(int t=0; t& titles, const float tr, const float nltffwhm, const int nTimepoints, const vector& G ) { FILE *outputFile; unsigned char *r,*g,*b; int border=5, temp=15, fsize=11, nameLength=temp; char the_string[10000]; /* how much space for contrast names? */ for(int con =0; con nameLength ) nameLength=tmp; } int xmag = MIN( MAX( 1 , 600/contrasts.nEV() ) , 50); int ymag = MIN( MAX( 1 , 400/nTimepoints ) , 20); int xsize = contrasts.nEV()*xmag + border*(contrasts.nEV()+3+nFTests+(nFTests>0)) + nameLength + nFTests*fsize; int ysize = nTimepoints*ymag + (contrasts.nC()+1)*FONT_HEIGHT + border*(4+contrasts.nC()); // }}} // {{{ reset X[] range (but don't change offset) for(int real_ev=1; real_ev <= contrasts.nEV(); real_ev++) if ( realDesign.Column(real_ev).MaximumAbsoluteValue() > 0 ) realDesign.Column(real_ev) /= realDesign.Column(real_ev).MaximumAbsoluteValue(); // }}} // {{{ malloc images and fill in background r=(unsigned char *)malloc(xsize*ysize); g=(unsigned char *)malloc(xsize*ysize); b=(unsigned char *)malloc(xsize*ysize); memset((void *)r,(unsigned char)180,xsize*ysize); memset((void *)g,(unsigned char)215,xsize*ysize); memset((void *)b,(unsigned char)255,xsize*ysize); // }}} // {{{ time if (level==1) for(int t=0; t1 && x3 && x 0 && y > 0 && x < fsize-1 && y < fsize-1 ) { if (F(f+1,con+1)==1) r[offset] = 175; else { r[ offset ]=180; g[ offset ]=215; b[ offset ]=255; } } } } } // }}} // {{{ second-level group memberships if (level==2) for(int t=0;t 1 ) { cov = (float *)malloc(contrasts.nEV()*contrasts.nEV()*sizeof(float)); covnorm = (float *)malloc(contrasts.nEV()*sizeof(float)); mag = MIN( MAX( 1 , 300/contrasts.nEV() ) , 50); size = contrasts.nEV()*mag; xsize = size*2 + border*3; } ysize = size + border*2 + (level==1)*(border*( contrasts.nC()+2 ) + FONT_HEIGHT*( contrasts.nC()+1 )); // }}} // {{{ malloc images and fill in background r=(unsigned char *)malloc(xsize*ysize); g=(unsigned char *)malloc(xsize*ysize); b=(unsigned char *)malloc(xsize*ysize); memset((void *)r,(unsigned char)180,xsize*ysize); memset((void *)g,(unsigned char)215,xsize*ysize); memset((void *)b,(unsigned char)255,xsize*ysize); // }}} if (contrasts.nEV()>1) { // {{{ cov matrix orig for(int evx=0; evx5) red_colour=255;*/ write_string_rgb(r, g, b, border, size+(4+yy)*border+(yy+1)*FONT_HEIGHT, the_string, font_data, red_colour, 0, 0, xsize, ysize); } } // }}} // {{{ output image if ((outputfile=fopen(filename.c_str(),"wb"))==NULL) { cout << "Can't open " << filename << " for writing" << endl; exit(1); } fprintf(outputfile,"P6\n"); fprintf(outputfile,"%d %d\n",xsize,ysize); fprintf(outputfile,"255\n"); for(int y=0; y