#!/usr/bin/perl #---------------------------------------------------------------------------------------- # # Script to get average timeseries surrounding an event type of interest # from FSL pre-processed raw data, as percent signal change from baseline # # # More info at http://www.jonaskaplan.com/peate.html # # Jonas T. Kaplan # # #---------------------------------------------------------------------------------------- my $version="2.61"; #Version 2.61, Dec 2008 use strict; use Math::Spline; my $configfile; ##If there is a configuration file as an argument, then do not use GUI ## this command line mode is also used by the Cocoa wrapper my $mode = "GUI"; if ($ARGV[0] =~ /\.pcf$/) { print "Running in command line mode\n"; $mode = "CL"; $configfile = $ARGV[0]; } else { use Tk; use Tk::FileSelect; use Tk::Dialog; use Tk::ProgressBar; use Tk::PNG; } ##GUI variables must now be global my $mw; my $dataFileEntry; my $windowFromEntry; my $windowToEntry; my $outputEntry; my $bypassFileEntry; my $interpResEntry; my $maskFileEntry; my $progressbar; my $timeleftlabel; my $statusFrame; my $currentjob; my $interpkindoption; my $interpResEntered; my $entryFrame; my $dataFileByHand; my $dataFileLabel; my $dataFileSelect; my $maskFileSelect; my $maskFileByHand; my $spaceMenu; my $maskFileLabel; my $spaceMenuLabel; my $windowLabel; my $windowLabel2; my $optionsFrame; my $entry; my $interpKindMenu; my $interpLabel; my $baselineMenu; my $outputLabel; my $baselineLabel; my $bypassCBox; my $peaksButton; my $plotCBox; my $peaksButton; my $verboseCBox; my $bypassFrame; my $bypassLabel; my $bypassTREntry; my $bypassFileSelect; my $statusTitle; my $proglabel; my $workingon; my $buttonsFrame; my $goButton; my $geomy; my $geomY; my $geomX; my $bypassFileLabel; my $bypassfile; my $cancelButton; my $dataentryfile;my $catchCBox; my $limitPeaks; my $prefix; my @featdirs; my @masks; my $spaceOption; my $fslversion; ##Default values my $secondsstart=4; #time window to average - prior to event onset my $secondsend=12; #time window to average - after event onset my $interpolationchecked=1; #is data interopolated to 1 second resolution my $interpresolution=1; #target resolution of data my $baselineOption="pre-window"; #data converted to percent change over pre-event window my $homedirectory = $ENV{'PWD'}; #home directory for file chooser my $TR=2; #TR of dataset ##Define GUI colors my $bgm="slategray"; #Main window background color my $bgc="slateblue"; #File entry frame background my $bgi="lightslateblue"; #File entry frame entry color my $opc="darkviolet"; #Options frame background color my $opi="purple"; #Options frame entry color #Other globals my @thelinesmask; #text entered in mask text window my @thelines; #text entered in feat dirs window my $peaks; #should we find peaks? my $peakWindowKind; #search whole window or not? my $peakWindowStart; #window to search for peaks: start my $peakWindowEnd; #window to search for peaks: end my $peakWindow; #peak options window my $peakKindPulldown; #pulldown menu in peak options my $fromEntry; #for peak options my $toEntry; #for peak options my $fromText; #for peak options my $toText; #for peak options my $bypass; my $verbose; my $ignore; #ignore or catch blank EV files my $peaks; my $plot; my $outputdir = $ENV{'PWD'}; my @EVsToDo; #If this var is defined, only EVs in this list will be calc'd my @listOfEVs; #list of EVs that will be calc'd - all if EVsToDo is undefined #added v2.5 my $useCoords; #use coordinate instead of mask file my $XCoord; #x coord of voxel to use my $YCoord; #y coord of voxel to use my $ZCoord; #z coord of voxel to use my $coordSpace; #space that coordinates are given in #final data stucture - [folder],[EV],[timepoint] my @alldata = ([],[],[]); #structure to hold peaks - [folder],[EV] my @thepeaks = ([],[]); #-------------GUI SETUP-------------------------------------------------------------- #------------------------------------------------------------------------------------ if ($mode=~/GUI/) { print "setting up GUI\n"; $mw = new MainWindow(-background=>"$bgm"); $geomX = 750; $geomy = 390; my $geomXplace=200; my $geomyplace=200; $mw->geometry("$geomX"."x"."$geomy+$geomXplace"."+"."$geomyplace"); $mw->fontCreate('mainfont',-family=>'arial',-weight=>'bold', -size=>12); $mw->fontCreate('entryfont',-family=>'arial',-weight=>'normal', -size=>12); $mw->fontCreate('smallfont', -family=>'arial', -size=>'10'); $mw->Label(-font=>"mainfont",-text=>"PEATE - Perl Event-related Average Timecourse Extraction V$version",-background=>"$bgm", -foreground=>"white")->grid("-","-"); ##-File Entry section $entryFrame=$mw->Frame(-relief=>"groove", -borderwidth=>2 ,-background=>"$bgc"); $dataFileSelect=$entryFrame->Button(-font=>"mainfont",-text=>"Select", -image=>$mw->Getimage("openfold"), -command=>\&dataFileChoose,-background=>"$bgc"); $dataFileByHand=$entryFrame->Button(-font=>"mainfont",-text=>"Enter", -image=>$mw->Getimage("textfile"),-background=>"$bgc", -command=>\&viewText); $dataFileEntry=$entryFrame->Entry(-font=>"entryfont",-width=>50, -background=>"$bgi", -highlightbackground=>"$bgi", -foreground=>"white"); $dataFileLabel=$entryFrame->Label(-font=>"mainfont",-text=>"Input filenames:",-background=>"$bgc")->grid($dataFileEntry,"-","-","-",$dataFileSelect,$dataFileByHand); $maskFileSelect=$entryFrame->Button(-font=>"mainfont",-text=>"Select", -image=>$mw->Getimage("openfold"), -command=>\&maskFileChoose,-background=>"$bgc"); $maskFileEntry=$entryFrame->Entry(-font=>"entryfont",-width=>50, -background=>"$bgi",-highlightbackground=>"$bgi", -foreground=>"white"); $maskFileByHand=$entryFrame->Button(-font=>"mainfont",-text=>"Enter", -image=>$mw->Getimage("textfile"),-background=>"$bgc", -command=>\&viewMaskText); $spaceOption; $spaceMenu=$entryFrame->Optionmenu(-font=>"mainfont",-variable=>\$spaceOption, -options=>["st","hr","ih","ef"], -background=>"$bgc", -activebackground=>"$bgc", -indicatoron=>0); $maskFileLabel=$entryFrame->Label(-font=>"mainfont",-text=>"Mask image(s):",-background=>"$bgc")->grid($maskFileEntry,"-","-","-",$maskFileSelect,$maskFileByHand); $spaceMenuLabel=$entryFrame->Label(-font=>"mainfont",-text=>"mask space:",-background=>"$bgc"); $outputEntry=$entryFrame->Entry(-font=>"entryfont",-width=>35, -background=>"$bgi",-highlightbackground=>"$bgi", -foreground=>"white"); $outputLabel=$entryFrame->Label(-font=>"mainfont",-text=>" Output prefix:", -background=>"$bgc")->grid($outputEntry,"-","-",$spaceMenuLabel,$spaceMenu,"-",-sticky=>"w"); $windowFromEntry=$entryFrame->Entry(-font=>"entryfont",-width=>3, -text=>"$secondsstart", -background=>"$bgi",-highlightbackground=>"$bgi", -foreground=>"white"); $windowLabel2=$entryFrame->Label(-font=>"mainfont",-text=>"AFTER",-background=>"$bgc"); $windowToEntry=$entryFrame->Entry(-font=>"entryfont",-width=>3, -text=>"$secondsend", -background=>"$bgi",-highlightbackground=>"$bgi", -foreground=>"white"); $windowLabel=$entryFrame->Label(-font=>"mainfont",-text=>"Time window BEFORE",-background=>"$bgc")->grid($windowFromEntry, $windowLabel2,$windowToEntry, -sticky=>"w"); $entryFrame->grid("-","-"); ##-Options section $optionsFrame=$mw->Frame(-relief=>"groove", -borderwidth=>2, -background=>"$opc"); $optionsFrame->Label(-font=>"mainfont",-text=>"Options", -background=>"$opc", -foreground=>"white")->grid("-","-"); $interpResEntry=$optionsFrame->Entry(-font=>"entryfont",-width=>4, -text=>"1.0",-background=>"$opi",-foreground=>"white", -validate=> "focusout", -validatecommand => sub { $entry = $_[0]; return 1 if ($entry =~ /^[0-9,\.]*$/); return 0; }, -invalidcommand => sub {\&doError("Final temporal resolution must be a number.") }); $interpkindoption; $interpKindMenu=$optionsFrame->Optionmenu(-font=>"mainfont",-variable=>\$interpkindoption, -options=>["spline","linear","none"], -background=>"$opi", -activebackground=>"$opi", -command=>\&doInterpKindMenu); $interpLabel=$optionsFrame->Label(-font=>"mainfont",-text=>"interpolation: ", -background=>"$opc")->grid($interpKindMenu,$interpResEntry,-sticky=>'w',-pady=>5,-padx=>5); $baselineMenu=$optionsFrame->Optionmenu(-font=>"mainfont",-variable=>\$baselineOption, -options=>["pre-window","average","none"], -background=>"$opi", -activebackground=>"$opi"); $baselineLabel=$optionsFrame->Label(-font=>"mainfont",-text=>"baseline: ", -background=>"$opc")->grid($baselineMenu,"-",-sticky=>'w',-pady=>5,-padx=>5); $bypassCBox=$optionsFrame->Checkbutton( -font=>"mainfont",-text=>"bypass",-background=>"$opc", -highlightbackground=>"$opc", -variable=>\$bypass, -activebackground=>"$opc", -command=>\&doBypass); $peaksButton=$optionsFrame->Button( -font=>"mainfont",-text=>"find peaks [OFF]",-background=>"$opi", -highlightbackground=>"$opi", -activebackground=>"$opc", -command=>\&peakOptions); $plotCBox=$optionsFrame->Checkbutton( -font=>"mainfont",-text=>"plot",-background=>"$opc", -highlightbackground=>"$opc", -variable=>\$plot, -activebackground=>"$opc"); $verboseCBox=$optionsFrame->Checkbutton( -font=>"mainfont",-text=>"verbose",-background=>"$opc", -highlightbackground=>"$opc", -variable=>\$verbose, -activebackground=>"$opc"); #->grid($plotCBox,$peaksCBox,$bypassCBox,-pady=>5,-padx=>15); $catchCBox=$optionsFrame->Checkbutton( -font=>"mainfont",-text=>"ignore blank EV files",-background=>"$opc", -highlightbackground=>"$opc", -variable=>\$ignore, -activebackground=>"$opc"); $plotCBox->grid($peaksButton,-pady=>5,-padx=>15); $verboseCBox->grid($bypassCBox, $catchCBox, -pady=>5,-padx=>5); $bypassFrame=$mw->Frame(-relief=>"groove", -borderwidth=>2, -background=>"$opc"); $bypassFileSelect=$bypassFrame->Button(-font=>"mainfont",-text=>"Select", -image=>$mw->Getimage("openfold"), -command=>\&bypassFileChoose,-background=>"$opc"); $bypassFileEntry=$bypassFrame->Entry(-font=>"entryfont",-width=>50, -background=>"$opi",-highlightbackground=>"$opi", -foreground=>"white"); $bypassLabel=$bypassFrame->Label(-font=>"mainfont",-text=>"TR",-background=>"$opc"); $bypassTREntry=$bypassFrame->Entry(-font=>"entryfont",-width=>2, -background=>"$opi",-highlightbackground=>"$opi", -foreground=>"white", -text=>"$TR"); $bypassFileLabel=$bypassFrame->Label(-font=>"mainfont",-text=>"EVs file:",-background=>"$opc")->grid($bypassFileEntry,$bypassFileSelect,$bypassLabel,$bypassTREntry); ##-Status section $statusFrame=$mw->Frame(-relief=>"raised", -borderwidth=>0, -background=>"$bgm"); $statusTitle=$statusFrame->Label(-font=>"mainfont",-text=>"Status", -background=>"$bgm", -foreground=>"white")->grid(-padx=>20,-ipadx=>10); $proglabel=$statusFrame->Label(-font=>"mainfont",-text=>"Working on mask: none",-background=>"$bgm")->grid(-ipadx=>20,-padx=>20); $workingon=$statusFrame->Label(-font=>"mainfont",-text=>"Directory: none",-background=>"$bgm")->grid(-ipadx=>20,-padx=>30); $currentjob=$statusFrame->Label(-font=>"mainfont",-text=>"waiting",-background=>"$bgm",-foreground=>"yellow")->grid(-ipadx=>20,-padx=>20); $progressbar=$statusFrame->ProgressBar()->grid(-ipady=>10,-ipadx=>50,-pady=>2,-padx=>10); $timeleftlabel=$statusFrame->Label(-font=>"smallfont", -text=>"Time remaining: (estimating)",-background=>"$bgm"); $optionsFrame->grid($statusFrame,-pady=>10,-padx=>5, -sticky=>"w"); ##-Action buttons $buttonsFrame=$mw->Frame(-relief=>"groove"); $goButton=$buttonsFrame->Button(-font=>"mainfont",-text=>"Go", -command=>\&doMain,-background=>"$bgm"); $cancelButton=$buttonsFrame->Button(-font=>"mainfont",-text=>"Cancel",-command=>sub{exit},-background=>"$bgm"); Tk::grid("x","x","x",$cancelButton,$goButton,"x",-sticky=>"nsew"); $buttonsFrame->grid("-","-"); } #------------MAINLOOP----------------------------------------------------------------------------- #------------------------------------------------------------------------------------------------- if ($mode=~/GUI/) { print "starting gui: $mode\n"; MainLoop; } else { print "mode: $mode\n"; &doMain; } #------------MAIN PROGRAM-------------------------------------------------------------------------- #------------------------------------------------------------------------------------------------- sub doMain { print "inmain\n"; my $numEVs; my $windowlength; my $interpfactor; my @EVfiles; my $theTR; my $theStartTime; my $theCurrentTime; my $theTimeRemaining; my @EVtypes; my @EVtypesnumerical; #get start time $theStartTime = `date '+%s'`; #check fsl version open (FSLVERS,"$ENV{FSLDIR}/etc/fslversion") or &doError("No FSL version file at $ENV{FSLDIR}/etc/") && return; $fslversion = ; close FSLVERS; print "FSL Version is $fslversion\n"; #check for gnuplot if plot checked #if ($plot) { # $plot = &checkForGnuplot; # if ($plot==9) {return;} #} #get input options if ($mode=~/GUI/) { #get from GUI $dataentryfile=$dataFileEntry->get(); $secondsstart = $windowFromEntry->get(); $secondsend = $windowToEntry->get(); $prefix = $outputEntry->get(); $bypassfile = $bypassFileEntry->get(); $interpResEntered=$interpResEntry->get(); #Get masks or list of masks my $maskentryfile = $maskFileEntry->get(); if ($thelinesmask[0]){ @masks=@thelinesmask; } else { if ($maskentryfile=~/\.(hdr$|img$|nii\.gz$)/i){ open (MASKFILE,$maskentryfile) or &doError("Could not open mask $maskentryfile") && return; close MASKFILE; @masks=$maskentryfile; } else { open (MASKFILE,$maskentryfile) or &doError("Could not open mask file $maskentryfile") && return; @masks = ; close (MASKFILE); } chomp(@masks); } #iflinesmask #Get FEAT directories from file or text entry if ($thelines[0]) { @featdirs=@thelines; } else { if ($dataentryfile=~/\.feat$/) { @featdirs=$dataentryfile; } else { open (FEATFILE,$dataentryfile) or &doError("Could not open feat directory list $dataentryfile") && return; @featdirs = ; close (FEATFILE); } } #if thelines #set peak window if ($peakWindowKind =~ /entire/) { $peakWindowStart=-$secondsstart; $peakWindowEnd=$secondsend; } } else { #get info from config file print "config file is: $configfile\n"; open (CONFIG,$configfile); my @configlines = ; close CONFIG; my $line; my $counter = 0; my $numlines = scalar(@configlines); #my @featdirs; #my @masks; foreach $line (@configlines) { chomp($line); if ($line=~/#/) { #header row detected if ($line=~/Feat directories/) { my $i=$counter; my $theline = $configlines[$i]; chomp($theline); while ($theline) { $i++; $theline = $configlines[$i]; chomp($theline); push(@featdirs,$theline); } pop(@featdirs); } elsif ($line=~/Mask files/) { my $i=$counter; my $theline = $configlines[$i]; chomp($theline); while ($theline) { $i++; $theline = $configlines[$i]; chomp($theline); push(@masks,$theline); } pop(@masks); } elsif ($line=~/Mask space/) { $spaceOption = $configlines[$counter+1]; chomp($spaceOption); } elsif ($line=~/Seconds before/) { $secondsstart = $configlines[$counter+1]; chomp($secondsstart); } elsif ($line=~/Seconds after/) { $secondsend = $configlines[$counter+1]; chomp($secondsend); } elsif ($line=~/Baseline/) { $baselineOption = $configlines[$counter+1]; chomp($baselineOption); } elsif ($line=~/Interpolation kind/) { $interpkindoption = $configlines[$counter+1]; chomp($interpkindoption); } elsif ($line=~/Interpolation resolution/) { $interpResEntered = $configlines[$counter+1]; chomp($interpResEntered); } elsif ($line=~/Plot/) { $plot = $configlines[$counter+1]; chomp($plot); } elsif ($line=~/Find peaks/) { $peaks = $configlines[$counter+1]; chomp($peaks); } elsif ($line=~/Limit peaks/) { $limitPeaks = $configlines[$counter+1]; chomp($limitPeaks); } elsif ($line=~/Limit from/) { $peakWindowStart = $configlines[$counter+1]; chomp($peakWindowStart); } elsif ($line=~/Limit to/) { $peakWindowEnd = $configlines[$counter+1]; chomp($peakWindowEnd); } elsif ($line=~/Verbose/) { $verbose = $configlines[$counter+1]; chomp($verbose); } elsif ($line=~/Only do EVs/) { my $EVsToDo = $configlines[$counter+1]; chomp($EVsToDo); @EVsToDo = split /\s/, $EVsToDo; } elsif ($line=~/Ignore Blank EV Files/) { $ignore = $configlines[$counter+1]; chomp($ignore); } elsif ($line=~/Use Coordinates/) { $useCoords = $configlines[$counter+1]; chomp($useCoords); } elsif ($line=~/Xcoord/) { $XCoord = $configlines[$counter+1]; chomp($XCoord); } elsif ($line=~/Ycoord/) { $YCoord = $configlines[$counter+1]; chomp($YCoord); } elsif ($line=~/Zcoord/) { $ZCoord = $configlines[$counter+1]; chomp($ZCoord); } elsif ($line=~/Coordinate space/) { $coordSpace = $configlines[$counter+1]; chomp($coordSpace); } #end header switch }#end header found ++$counter; } #end loop through lines if (!$limitPeaks) { $peakWindowStart = $secondsstart; $peakWindowEnd = $secondsend; } $_=$configfile; if ($configfile=~/\//) { $_=$configfile; /\/([^\/]+)\.pcf$/; $prefix = $1; } else { $_=$configfile; /(.*)\.pcf/; $prefix = $1; } print "prefix is $prefix\n"; } #if GUI if ($useCoords) { $masks[0] = "USECOORDS"; } my $origsecondsstart = $secondsstart; my $origsecondsend = $secondsend; #Setup progress bar my $percentdone; my $dirnum=0; my $numdirectories = @featdirs; my $nummasks = @masks; my $lastvalue=$numdirectories*$nummasks*3; my $halfway=$lastvalue/3; my $ending=$lastvalue-$lastvalue/3; my $loopTimeTotal; my $numLoops; if ($mode=~/GUI/) { $progressbar->configure(-from=>0,-to=>$lastvalue, -blocks=>$lastvalue,-colors=>[0,"lightblue",$halfway,"mediumslateblue",$ending,"slateblue"], -variable=>\$percentdone, -gap=>1); $timeleftlabel->grid(-ipadx=>20,-padx=>20); $statusFrame->update; } else { print "#lv $lastvalue\n"; } my $nummask=0; $percentdone=0; #Analyze each mask foreach (@masks) { my $matrixfile; my $mask; my $shortmask; my $sourceimage; my @designtext; $mask=$_; if ($mask eq "USECOORDS") { #routines for coordinates instead of mask file print "#mask: voxel at $XCoord $YCoord $ZCoord\n"; ++$nummask; if ($mode=~/GUI/) { $proglabel->configure(-text=>"Working on voxel: $XCoord $YCoord $ZCoord"); $statusFrame->update(); } if ($coordSpace eq "st" | $coordSpace eq "standard") { $matrixfile="reg/example_func2standard.mat"; $sourceimage="reg/standard"; } elsif ($coordSpace eq "hr" | $coordSpace eq "highres") { $matrixfile="reg/example_func2highres.mat"; $sourceimage="reg/highres"; } elsif ($coordSpace eq "ih" | $coordSpace eq "initial_highres") { $matrixfile="reg/example_func2initial_highres.mat"; $sourceimage="reg/initial_highres"; } elsif ($coordSpace eq "ef" | $coordSpace eq "example_func") { $matrixfile = "peate/notransform.mat"; $sourceimage="example_func"; } print "source image: $sourceimage\n"; } else { print "#mask: $_\n"; ++$nummask; #Make sure mask exists if (!-e $mask) {&doError("Mask $mask does not exist"); return;} #Shorten mask name for display if mask is full path if ($mask=~/\//) { $_=$mask; /\/([^\/]+)$/; $shortmask=$1; } else { $shortmask=$mask; } #make sure the mask name is still not too long to display if ($shortmask=~/.{10,}/) { $_=$shortmask; /(.{10})$/; $shortmask=$1; $shortmask="..."."$shortmask"; } if ($mode=~/GUI/) { $proglabel->configure(-text=>"Working on mask: $shortmask"); $statusFrame->update(); } #Interpret mask space if ($spaceOption eq "st" | $spaceOption eq "standard") { $matrixfile="reg/standard2example_func.mat"; } elsif ($spaceOption eq "hr" | $spaceOption eq "highres") { $matrixfile="reg/highres2example_func.mat"; } elsif ($spaceOption eq "ih" | $spaceOption eq "initial_highres") { $matrixfile="reg/intial_highres2example_func.mat"; } elsif ($spaceOption eq "ef" | $spaceOption eq "example_func") { $matrixfile = "peate/notransform.mat"; } } my $dirnum=0; #DO FOR EACH FEAT DIR foreach my $currentdir (@featdirs) { print "#dir: $currentdir\n"; my $theLoopStartTime = `date '+%s'`; my $warpedvoxel; chomp ($currentdir); ++$dirnum; #Make sure directory exists if (!-e $currentdir){ &doError("Feat directory $currentdir does not exist"); return; } #Make temporary peate directory if (!-e "$currentdir/peate") {system "mkdir $currentdir/peate\n";} #Make identity matrix if necessary if ($spaceOption eq "ef" | $spaceOption eq "example_func" | $coordSpace eq "ef" | $coordSpace eq "example_func") { open (NEWMATRIX, ">$currentdir/peate/notransform.mat"); print NEWMATRIX "1 0 0 0\n"; print NEWMATRIX "0 1 0 0\n"; print NEWMATRIX "0 0 1 0\n"; print NEWMATRIX "0 0 0 1\n"; close NEWMATRIX; } my $shortdirname; #Update status, make sure dir name is short enough if ($currentdir=~/.{20,}/) { #dir name is too long to display in status window $_=$currentdir; /(.{20})$/; $shortdirname=$1; $shortdirname="..."."$shortdirname"; } else {$shortdirname=$currentdir;} if ($mask eq "USECOORDS") { #convert the coordinates into subject space coordinates if ($mode=~/GUI/) { $workingon->configure(-text=>"Directory: $shortdirname", -background=>"$bgm"); $statusFrame->update; $currentjob->configure(-text=>"Warping voxel coords to example_func"); $statusFrame->update(); } else { print "#status Warping voxel coords to example_func\n"; } if (!-e "$currentdir/$sourceimage.hdr" && !-e "$currentdir/$sourceimage.nii.gz") {&doError("$currentdir/$sourceimage does not exist, needed for transformation"); return;} #create voxel file open (COORDFILE,">$currentdir/peate/voxel"); print COORDFILE "$XCoord $YCoord $ZCoord"; close COORDFILE; #warp voxel $warpedvoxel = `std2imgcoord -img $currentdir/example_func -std $currentdir/$sourceimage -xfm $currentdir/$matrixfile -vox $currentdir/peate/voxel`; chomp($warpedvoxel); print "voxel in ef space is $warpedvoxel\n"; } else { if ($mode=~/GUI/) { $workingon->configure(-text=>"Directory: $shortdirname", -background=>"$bgm"); $statusFrame->update; $currentjob->configure(-text=>"Warping mask to example_func"); $statusFrame->update(); } else { print "#status Warping mask to example_func\n"; } #warp mask to example_func system "flirt -in $mask -ref $currentdir/example_func -applyxfm -init $currentdir/$matrixfile -interp nearestneighbour -o $currentdir/peate/$prefix-mask$nummask"; } ++$percentdone; if ($mode=~/GUI/) { $statusFrame->update(); } else { print "#inc $percentdone\n"; } #extract timeseries if ($mode=~/GUI/) { $currentjob->configure(-text=>"Extracting timeseries"); $statusFrame->update(); } else { print "#status Extracting timeseries\n"; } if ($mask eq "USECOORDS") { #get timeseries from voxel print "getting timeseries from voxel\n"; if ($fslversion =="3.1" | $fslversion == "3.2" | $fslversion == "3.3" | $fslversion =="3.3") { system "avwmeants -i $currentdir/filtered_func_data -o $currentdir/peate/$prefix-ts$nummask.txt -c $warpedvoxel"; } elsif ($fslversion == "4.0") { system "fslmeants -i $currentdir/filtered_func_data -o $currentdir/peate/$prefix-ts$nummask.txt -c $warpedvoxel"; } else { system "fslmeants -i $currentdir/filtered_func_data -o $currentdir/peate/$prefix-ts$nummask.txt -c $warpedvoxel"; } } else { #get timeseries from masked region print "getting timeseries from masked region\n"; if ($fslversion =="3.1") { system "avwmeants $currentdir/filtered_func_data $currentdir/peate/$prefix-ts$nummask.txt $currentdir/peate/$prefix-mask$nummask > /dev/null"; } elsif ( $fslversion == "3.2" | $fslversion == "3.3" ) { system "avwmeants -i $currentdir/filtered_func_data -o $currentdir/peate/$prefix-ts$nummask.txt -m $currentdir/peate/$prefix-mask$nummask > /dev/null"; } elsif ($fslversion == "4.0") { system "fslmeants -i $currentdir/filtered_func_data -o $currentdir/peate/$prefix-ts$nummask.txt -m $currentdir/peate/$prefix-mask$nummask"; } else { #unknown version go with latest system "fslmeants -i $currentdir/filtered_func_data -o $currentdir/peate/$prefix-ts$nummask.txt -m $currentdir/peate/$prefix-mask$nummask"; } #fslversioncheck system "rm $currentdir/peate/$prefix-mask$nummask.*"; } open (TIMESERIES,"$currentdir/peate/$prefix-ts$nummask.txt") or &doError("Can't open timeseries file") && return; my @timeseries = ; close (TIMESERIES); chop(@timeseries); ++$percentdone; if ($mode=~/GUI/) { $currentjob->configure(-text=>"Processing timings"); $statusFrame->update; } else { print "#status Processing timings\n"; print "#inc $percentdone\n"; } #system "rm $currentdir/peate/$prefix-ts$nummask.txt"; my $maxtimepoint = scalar(@timeseries); my $temptp; my $sumall; foreach $temptp (@timeseries) { $sumall = $sumall + $temptp; } my $averageOverTimeseries = $sumall/$maxtimepoint; if ($timeseries[0]=~/nan/) { &doError ("Problem with feat directory $shortdirname and $shortmask. This may happen if the mask has very few voxels; when warped into the subjects' space the new image may contain no voxels.") && return; } #open design file to get EVs and TR if (!$bypass) { open (DESIGNFILE,"$currentdir/design.fsf") or &doError("Can't open $_/design.fsf") && return; @designtext = ; close (DESIGNFILE); @EVtypes = grep /set fmri\(shape/, @designtext; my $x = 0; foreach my $EVtype (@EVtypes) { chomp($EVtype); $_=$EVtype; /(\d)$/; $EVtypesnumerical[$x] = $1; ++$x; } #foreach EVtype $numEVs = scalar(@EVtypes); my @TR = grep /set fmri\(tr/, @designtext; $TR[0] =~ s/set fmri\(tr\) //; chop($TR[0]); $theTR = $TR[0]; } #if !bypass my $maxinseconds = $maxtimepoint * $theTR; #This section bypasses reading EVs and TR from design.fsf if option is checked if ($bypass) { open (BYPASSFILE,"$bypassfile") or &doError("Cannot open file EV bypass file $bypassfile") && return; @EVfiles=; close (BYPASSFILE); $numEVs = scalar(@EVfiles); chomp(@EVfiles); if ($mode=~/GUI/) {$theTR=$bypassTREntry->get();} for (my $y=0; $y<$numEVs; ++$y) { $EVtypesnumerical[$y] = 9; $EVtypes[$y] = "bypass"; } } #if bypass #interpret interpolation options my $dointerp; if ($interpkindoption ne "none") { #test to make sure final temporal resolution is acceptable if ($interpResEntered > $theTR) {doError("Final temporal resolution cannot be greater than TR") && return;} $a=$theTR*10; $b=$interpResEntered*10; my $remainder = $a % $b; if ($remainder > 0) {doError("Temporal resolution must divide evenly into TR") && return;} $interpresolution=$interpResEntered; $dointerp=1; } elsif ($interpkindoption eq "none") { $interpresolution=$theTR; $dointerp=0; } #Convert start and end window time to final resolution units $interpfactor=1/$interpresolution; $secondsstart = $origsecondsstart * $interpfactor; $secondsend = $origsecondsend * $interpfactor; #interpolate data to final resolution (only if option is selected) if ($mode=~/GUI/) { $currentjob->configure(-text=>"Interpolating timeseries"); $statusFrame->update(); } else { print "#status Interpolating timeseries\n"; } my @interpseries; if ($dointerp) { if ($interpkindoption eq "linear") { @interpseries = &interpolatelinear(@timeseries,$theTR,$interpfactor); } elsif ($interpkindoption eq "spline") { @interpseries = &interpolatespline(@timeseries,$theTR,$interpfactor); } } else { @interpseries = @timeseries; } #if dointerp if (!@interpseries) { &doError("interpseries error") && return; } my $k=0; #EV file counter my @averages; my @eventtimes; my $numrealevents; #Generate list of EVs to calculate - all or subset if (@EVsToDo) { @listOfEVs = @EVsToDo; } else { undef @listOfEVs; for (my $t = 1; $t<=$numEVs; ++$t) { push (@listOfEVs,$t); } } if ($verbose) {print "----EVs that will be looped thru: @listOfEVs\n";} my $currentEV; foreach $currentEV (@listOfEVs) { @averages = (); if ($verbose) {print " Working on EV $currentEV\n";} if ($EVtypesnumerical[$currentEV-1] == 3) { @eventtimes = &doThreeColumnExtract(@designtext,$currentEV,$interpfactor,$currentdir); } elsif ($EVtypesnumerical[$currentEV-1] == 0 ) { @eventtimes = &doSquareWaveExtract(@designtext,$currentEV,$interpfactor, $maxinseconds); } elsif ($EVtypesnumerical[$currentEV-1] == 2) { @eventtimes = &doOneColumnExtract(@designtext,$currentEV,$interpfactor, $theTR,$currentdir); } elsif ($EVtypesnumerical[$currentEV-1] == 9) { @eventtimes = &doBypassExtract(@EVfiles,$currentEV,$interpfactor); } $numrealevents = scalar(@eventtimes); my $skipEV = 0; if (!$numrealevents) {return;} if ($eventtimes[0] eq "NULL") {$skipEV=1;} #extra verbose output if ($verbose) { print " Currentdir: $currentdir\n"; print " numevents: $numrealevents\n"; print " ignoring this EV: $skipEV\n"; print " event times: "; foreach my $tp (@eventtimes) { my $tpinsecs = $tp/$interpfactor; print "$tpinsecs "; } print "\n"; } #loop through timepoints and average $windowlength = $secondsstart + $secondsend; my $r=0; my $averageval; my $thepeak; my $thepeakval; for (my $w=-$secondsstart; $w<=$secondsend; ++$w) { if (!$skipEV) { my $thetotal = 0; ++$r; my $nonzeropoints=0; for (my $t=0;$t<$numrealevents;++$t) { my $timepoint = $eventtimes[$t]+$w; if ($verbose) { my $timepointinseconds = $timepoint/$interpfactor; print "adding in timepoint $timepointinseconds: $interpseries[$timepoint-1]\n";} if ($interpseries[$timepoint-1]!=0) { $thetotal = $thetotal + $interpseries[$timepoint-1]; ++$nonzeropoints; } } #if ($verbose) {print "total: $thetotal numevents: $numevents\n";} if ($nonzeropoints) {$averageval=$thetotal/$nonzeropoints}; push(@averages,$averageval); if ($averageval>$thepeakval) { #this is the highest value so far if ($w >= $peakWindowStart && $w<=$peakWindowEnd) { #treat this as the peak only if its in the allowed window $thepeakval=$averageval; $thepeak=$w/$interpfactor; } } } else { if ($verbose) {print "skipping EV $currentEV due to lack of events\n";} push(@averages,"NULL"); $thepeak="NULL"; } #skipEV } #loop through timepoints $thepeaks[$dirnum][$currentEV]=$thepeak; #compute baseline my $baselinetotal=0; my $timeposition=0; my $baseline; if ($baselineOption eq "pre-window") { my $endbaselineperiod=$secondsstart; for (my $x=0; $timeposition<$endbaselineperiod; ++$x) { $baselinetotal += $averages[$x]; ++$timeposition; } $baseline = $baselinetotal/$endbaselineperiod; } elsif ($baselineOption eq "average") { $baseline = $averageOverTimeseries; } #baselineoptions #convert to percent change over baseline and store in data matrix if ($verbose) {print "baseline: $baseline\n";} $timeposition = -$secondsstart; for (my $y=0; $y<=$windowlength; ++$y){ if ($skipEV) { $alldata[$dirnum][$currentEV][$y]=$averages[$y]; } else { if ($baselineOption ne "none") { if ($baseline) { my $percentchange = $averages[$y]/$baseline*100-100; $alldata[$dirnum][$currentEV][$y]=$percentchange; } else { &doError ("Error computing averages. Timeseries may be all zeros. Check coordinates or mask. ") && return; } } else { $alldata[$dirnum][$currentEV][$y]=$averages[$y]; } } ++$timeposition; } #baselineloop } #foreach EV file ++$percentdone; if ($mode=~/GUI/) {$statusFrame->update;} else {print "#inc $percentdone\n";} system "rm -r $currentdir/peate"; my $theLoopEndTime = `date '+%s'`; my $theLoopTime = $theLoopEndTime - $theLoopStartTime; $loopTimeTotal = $loopTimeTotal + $theLoopTime; ++$numLoops; my $avgLoopTime = $loopTimeTotal/$numLoops; my $totaldirs = $nummasks * $numdirectories; my $donedirs = ($nummask-1) * $numdirectories + $dirnum; my $dirsRemaining = $totaldirs - $donedirs; $theTimeRemaining = $theLoopTime * $dirsRemaining; my $elapsedTime = $theLoopEndTime - $theStartTime; my $timeleftseconds=$theTimeRemaining % 60; my $temptime = $theTimeRemaining - $timeleftseconds; my $timeleftminutes=$temptime/60; my $timeleftformatted=sprintf("%02d:%02d",$timeleftminutes,$timeleftseconds); if ($mode=~/GUI/) {$timeleftlabel->configure(-text=>"Time remaining: $timeleftformatted");} #print "looptime: $theLoopTime avglooptime: $avgLoopTime elapsed: $elapsedTime remaining: $theTimeRemaining\n"; #DB } #foreach featdir ##Make outputfiles my @averages=""; my @peakaverages=""; open (OUTPUT,">>$outputdir/$prefix-timeseries.txt") or &doError ("Could not open output file for writing. Check write permissions in the output directory.") && return; if ($plot) { open (PLOT,">$outputdir/$prefix-plot.txt") or &doError ("Could not open output file for writing. Check write permissions in the output directory.") && return; } print OUTPUT "Mask file: $mask\n"; my $j; #Generate list of EVs to calculate - all or subset foreach $j (@listOfEVs) { print OUTPUT "EV $j\n"; print OUTPUT "TIME\t"; for (my $h=1;$h<=$numdirectories;++$h){ print OUTPUT "FEAT$h\t"; } print OUTPUT "AVG"; print OUTPUT "\n"; my $timeposition=-$secondsstart; for (my $u=1;$u<=$windowlength+1;++$u) { my $timepositioninsecs=$timeposition/$interpfactor; printf OUTPUT ("%.1f\t",$timepositioninsecs); my $temptotal=0; my $numrealdirectories=0; for (my $m=1;$m<=$numdirectories;++$m){ if ($alldata[$m][$j][$u-1] eq "NULL") { print OUTPUT "NULL\t"; } else { printf OUTPUT ("%.3f\t",$alldata[$m][$j][$u-1]); $temptotal=$temptotal+$alldata[$m][$j][$u-1]; ++$numrealdirectories; } } #for m my $tempavg; if ($numrealdirectories) { $tempavg=$temptotal/$numrealdirectories; } else { $tempavg="NULL"; } $averages[$j][$u]=$tempavg; if ($tempavg eq "NULL") { print OUTPUT "NULL"; } else { printf OUTPUT ("%.4f",$tempavg); } ++$timeposition; print OUTPUT "\n"; } #for u if ($peaks) { print OUTPUT "PEAK\t"; my $thepeaktotal=0; my $numrealdirectories=0; for (my $h=1;$h<=$numdirectories;++$h){ if ($thepeaks[$h][$j] eq "NULL") { print OUTPUT "NULL\t"; } else { printf OUTPUT ("%.1f\t",$thepeaks[$h][$j]); $thepeaktotal=$thepeaktotal+$thepeaks[$h][$j]; ++$numrealdirectories; } } my $theavgpeak; if ($numrealdirectories) { $theavgpeak = $thepeaktotal/$numrealdirectories; printf OUTPUT ("%.3f",$theavgpeak); } else { $theavgpeak = "NULL"; print OUTPUT "NULL"; } push (@peakaverages,$theavgpeak); print OUTPUT "\n"; } } #for j print OUTPUT "\n"; print OUTPUT "Summary for $mask\n"; print OUTPUT "TIME\t"; print PLOT "#TIME\t"; foreach $j (@listOfEVs){ print OUTPUT "EV$j-AVG\t"; print PLOT "EV$j-AVG\t"; } my $timeposition=-$secondsstart; print OUTPUT "\n"; print PLOT "\n"; for (my $u=1;$u<=$windowlength+1;++$u){ my $timepositioninsecs=$timeposition/$interpfactor; printf OUTPUT ("%.1f\t",$timepositioninsecs); printf PLOT ("%.1f\t",$timepositioninsecs); ++$timeposition; foreach $j (@listOfEVs) { printf OUTPUT ("%.4f\t",$averages[$j][$u]); printf PLOT ("%.4f\t",$averages[$j][$u]); } print OUTPUT "\n"; print PLOT "\n"; } if ($peaks) {print OUTPUT "PEAK\t";} my $numdirs=scalar(@peakaverages)-1; for (my $j=1; $j<=$numdirs;++$j) { printf OUTPUT ("%.3f\t",$peakaverages[$j]); } print OUTPUT "\n\n\n"; close (OUTPUT); @alldata=""; close (PLOT); if ($plot) { if (`which gnuplot`) { &doPlot("$outputdir/$prefix-plot.txt",$secondsstart/$interpfactor,$secondsend/$interpfactor,$numEVs,$prefix,$nummask,$mask); } else { #&doPlotNew("$outputdir/$prefix-plot.txt",$secondsstart/$interpfactor,$secondsend/$interpfactor,$numEVs,$prefix,$nummask,$mask,@designtext); } } } #foreach mask ##Append extra info to end of output file open (OUTPUT,">>$outputdir/$prefix-timeseries.txt"); print OUTPUT "\n\n__________________________________________\n"; for (my $t=1;$t<=$numdirectories;++$t){ print OUTPUT "FEAT$t\t$featdirs[$t-1]\n"; } print OUTPUT "\nBaseline: $baselineOption\n"; print OUTPUT "Interpolation: $interpkindoption\n"; if ($peaks) {print OUTPUT "Peak detection window: $peakWindowStart to $peakWindowEnd\n";} close (OUTPUT); #Cleanup status window if ($mode=~/GUI/) { $currentjob->configure(-text=>"Output file written."); $proglabel->configure(-text=>""); $workingon->configure(-text=>"DONE", -background=>"red"); $timeleftlabel->gridForget; $mw->bell; } else { print "#status Output files written\n"; } print "#done"; } #doMain #----------------------SUBROUTINES------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------------------- sub viewText { #Text window entry for feat directories my $textWindow = new MainWindow; $textWindow->geometry("450x250+300+250"); $textWindow->Label(-background=>"$bgm", -text=>"Enter feat directories")->pack(-side=>"top"); my $textview = $textWindow->Scrolled("Text", -scrollbars=>'osoe', -wrap=>"none")->pack(-side=>"top"); $textview->Subwidget("yscrollbar")->configure(-background=>"$bgc", -troughcolor=>"$bgm"); $textview->Subwidget("xscrollbar")->configure(-background=>"$bgc", -troughcolor=>"$bgm"); $textWindow->configure(-background=>"$bgm"); $textview->configure(-background=>"$bgi",-foreground=>"white", -borderwidth=>3,height=>12,-width=>80); my $txtbuttonFrame=$textWindow->Frame(-background=>"$bgm"); $txtbuttonFrame->Button(-background=>"$bgm",-text=>"Done",-command=>sub { my $thetext=$textview->get('1.0','end'); @thelines=split /\n/, $thetext; $dataFileEntry->configure(-text=>""); $textWindow->destroy; })->pack(-side=>"right"); $txtbuttonFrame->Button(-background=>"$bgm",-text=>"Cancel",-command=>sub {$textWindow->destroy})->pack(-side=>"left"); $txtbuttonFrame->pack(-side=>"bottom"); my $dataFile=$dataFileEntry->get(); if ($thelines[0]) { foreach my $line (@thelines) { $textview->insert("end","$line\n"); } } elsif ($dataFile) { if ($dataFile!=~/\.feat$/i){ my $thefile; open (THEFILE, "$dataFile") or &doError("Could not open file $dataFile") && return; while () { $textview->insert("end","$_"); } close THEFILE; } } } sub viewMaskText { #Text window entry for mask files my $textWindow = new MainWindow; $textWindow->geometry("450x250+300+250"); $textWindow->Label(-background=>"$bgm", -text=>"Enter mask files")->pack(-side=>"top"); my $textview = $textWindow->Scrolled("Text", -scrollbars=>'osoe', -wrap=>"none")->pack(-side=>"top"); $textview->Subwidget("yscrollbar")->configure(-background=>"$bgc", -troughcolor=>"$bgm"); $textview->Subwidget("xscrollbar")->configure(-background=>"$bgc", -troughcolor=>"$bgm"); $textWindow->configure(-background=>"$bgm"); $textview->configure(-background=>"$bgi",-foreground=>"white", -borderwidth=>3,height=>12,-width=>80); my $txtbuttonFrame=$textWindow->Frame(-background=>"$bgm"); $txtbuttonFrame->Button(-background=>"$bgm",-text=>"Done",-command=>sub { my $thetext=$textview->get('1.0','end'); @thelinesmask=split /\n/, $thetext; $maskFileEntry->configure(-text=>""); $textWindow->destroy; })->pack(-side=>"right"); $txtbuttonFrame->Button(-background=>"$bgm",-text=>"Cancel",-command=>sub {$textWindow->destroy})->pack(-side=>"left"); $txtbuttonFrame->pack(-side=>"bottom"); my $maskFile=$maskFileEntry->get(); if ($thelinesmask[0]) { foreach my $line (@thelinesmask) { $textview->insert("end","$line\n"); } } elsif ($maskFile) { if ($maskFile=~/\.(hdr$|img$|nii\.gz$)/i) {} else { my $thefile; open (THEFILE,$maskFile) or &doError("Could not open file $maskFile") && return; while () {$textview->insert("end","$_");} close THEFILE; } } } sub doBypass { #Hide and show file entry for bypass file if ($bypass) { my $newGeomY = $geomY + 30; $mw->geometry("$geomX"."x"."$newGeomY"); $buttonsFrame->gridForget; $bypassFrame->grid("-","-"); $buttonsFrame->grid("-","-",-pady=>5); } else { $bypassFrame->gridForget; $mw->geometry("$geomX"."x"."$geomY"); } } sub doError { #Show error messages in a dialog box if ($mode=~/GUI/) { $mw->bell(); my $errordialog=$mw->Dialog(-title=>"Error", -text=>"@_", -default_button=>"OK", -image => $mw->Getimage("warning"), -background=>"$bgm", -buttons=>["OK"]); #$errordialog->geometry("400x150"); $currentjob->configure(-text=>"stopped - error"); $errordialog->Show; } else { print "#error @_\n"; } } sub dataFileChoose { #File selector for feat dirs file my $fileentry = $mw->FileSelect(-directory => "$homedirectory", -title=>"Select file to open", ); $fileentry->geometry("600x400"); my $dataentryfile = $fileentry->Show; if ($dataentryfile) { $dataFileEntry->configure(-text=>"$dataentryfile"); @thelines=""; }; } sub bypassFileChoose { #File selector for bypass file my $fileentry = $mw->FileSelect(-directory => "$homedirectory", -title=>"Select file to open", ); $fileentry->geometry("600x400"); my $bypassfile = $fileentry->Show; if ($bypassfile) { $bypassFileEntry->configure(-text=>"$bypassfile"); }; } sub maskFileChoose { #File selector for mask file my $fileentry = $mw->FileSelect(-directory => "$homedirectory", -title=>"Select mask file", -filter=>"*", ); $fileentry->geometry("600x400"); my $maskentryfile = $fileentry->Show; if ($maskentryfile) { chomp ($maskentryfile); $maskFileEntry->configure(-text=>"$maskentryfile"); @thelinesmask=""; }; } sub doInterpKindMenu { #Disable/Enable interpolation input if ($interpkindoption eq "none") { $interpResEntry->configure(-state=>"disabled"); } else { $interpResEntry->configure(-state=>"normal"); } } sub interpolatelinear { #linearly interpolates data #pass data, oldresolution, interpfactor my @data=@_; my $interpfactor=pop(@data); my $oldresolution = pop(@data) * $interpfactor; my $numtimepoints = @data; my @newvals; my @newdata; for (my $i = 1;$i<=$numtimepoints-1;++$i) { my $difference = $data[$i]-$data[$i-1]; my $increment = $difference/$oldresolution; push (@newdata,$data[$i-1]); for (my $j = 0;$j<=($oldresolution-2);++$j) { my $f = $j+1; $newvals[$j] = $data[$i-1] + $increment * $f; push (@newdata,$newvals[$j]); } } #add the last value on push (@newdata,$data[$numtimepoints-1]); return @newdata; } sub interpolatespline { #cubic spline interpolation #pass data, oldresolution, interpfactor my @data=@_; my $interpfactor = pop(@data); my $oldresolution = pop(@data); my $numtimepoints = @data; my @index=(1..$numtimepoints); my $spline=new Math::Spline(\@index,\@data); my @newdata; my $increment=$interpresolution/$oldresolution; for (my $i=1;$i<=$numtimepoints;$i=$i+$increment) { my $val = $spline->evaluate($i); push (@newdata,$val); } return @newdata; } sub doThreeColumnExtract { #retrieve event times in custom three column format my $currentdir = pop(@_); my $interpfactor = pop (@_); my $EVnum = pop(@_); my @designtext = @_; my @eventtimes; my $EVfile; #with newer versions of FSL, the custom timing file is copied into the feat directory #better to use that instead if (-e "$currentdir/custom_timing_files") { $EVfile = "$currentdir/custom_timing_files/ev$EVnum.txt"; } else { #older versions of FSL, use the original file pointed to in the design.fsf my @EVfiles = grep /set fmri\(custom$EVnum/, @designtext; $EVfile = @EVfiles[0]; $_=$EVfile; /"(.*)"/; $EVfile =$1; } if ($verbose) {print "Using EV file: \n $EVfile\n"} open (EVFILE,$EVfile) or &doError("Cannot open EV file $EVfile ") && return; my @EVtext = ; close (EVFILE); my $numevents = scalar(@EVtext); #parse data in stimulus file - extract and round event times my $numrealevents = 0; for (my $i=0; $i<$numevents; ++$i) { chomp($EVtext[$i]); if ($EVtext[$i]) { #remove whitespace from beginning of line #print "line is $EVtext[$i]\n"; #$EVtext[$i]=s/^\s+//; #print "trimmed: $EVtext[$i]\n"; #remove any leading blank space $EVtext[$i]=~s/^\s+//; my @eventparsed = split/\s+/,$EVtext[$i]; if ($eventparsed[2] > 0) { $eventtimes[$numrealevents]=$eventparsed[0]*$interpfactor; #do rounding to nearest unit $eventtimes[$numrealevents] = sprintf ("%.0f",$eventtimes[$numrealevents]); ++$numrealevents; } } } #rounding loop if (!$numrealevents) { if (!$ignore) { &doError("There seems to be a problem with EV file $EVfile. No events were detected.") && return; } else { $eventtimes[0]="NULL"; } } return @eventtimes; } sub doOneColumnExtract { #retrieve event times in custom one column format my $currentdir = pop(@_); my $TR = pop(@_); my $interpfactor = pop (@_); my $EVnum = pop(@_); my @designtext = @_; my @eventtimes; my $EVfile; #with newer versions of FSL, the custom timing file is copied into the feat directory #better to use that instead if (-e "$currentdir/custom_timing_files") { $EVfile = "$currentdir/custom_timing_files/ev$EVnum.txt"; } else { #older versions of FSL, use the original file pointed to in the design.fsf my @EVfiles = grep /set fmri\(custom$EVnum/, @designtext; $EVfile = @EVfiles[0]; $_=$EVfile; /"(.*)"/; $EVfile =$1; } if ($verbose) {print "Using EV file: \n $EVfile\n"} open (EVFILE,$EVfile) or &doError("Cannot open EV file $EVfile ") && return; my @EVtext = ; close (EVFILE); my $counter = 0; my $preventry = 0; my $numrealevents = 0; foreach my $entry (@EVtext) { if ($entry > 0 && $preventry <=0) { my $time = $counter * $TR * $interpfactor; push (@eventtimes,$time); ++$numrealevents; } $preventry=$entry; ++$counter; } if (!$numrealevents) { if (!$ignore) { &doError("There seems to be a problem with EV file $EVfile. No events were detected.") && return; } else { $eventtimes[0]="NULL"; } } return @eventtimes; } sub doSquareWaveExtract { #retrieve event times from square wave EVs my $maxtimepoint = pop(@_); my $interpfactor = pop (@_); my $EVnum = pop(@_); my @designtext = @_; my @eventtimes; #get necessary info from design my @temp = grep /set fmri\(skip$EVnum/, @designtext; $_ = $temp[0]; /(\d*)$/; my $skip = $1; @temp = grep /set fmri\(off$EVnum/, @designtext; $_ = $temp[0]; /(\d*)$/; my $off = $1; @temp = grep /set fmri\(on$EVnum/, @designtext; $_ = $temp[0]; /(\d*)$/; my $on = $1; @temp = grep /set fmri\(phase$EVnum/, @designtext; $_ = $temp[0]; /(\d*)$/; my $phase = $1; @temp = grep /set fmri\(stop$EVnum/, @designtext; $_ = $temp[0]; my $stop; if ($_=~/-1/) {$stop = "-1";} else { /(\d*)$/; $stop = $1; } my $period = $off + $on; my $i=0; my $continue=1; while ($continue) { my $timepoint = $skip + ($i * $period) + $off - $phase; if ($stop ne "-1") { if ($i > $stop-1) {$continue=0;} } else { if ($timepoint > $maxtimepoint) {$continue=0;} } $timepoint = $timepoint * $interpfactor; if ($continue) {push (@eventtimes,$timepoint);} ++$i; } return @eventtimes; } sub doBypassExtract { #retrieve event times in custom three column format, fed list of EVfiles from bypass rather than design.fsf my $interpfactor = pop (@_); my $EVnum = pop (@_); my @EVfiles = @_; my @eventtimes; my $EVfile = @EVfiles[$EVnum-1]; open (EVFILE,$EVfile) or &doError("Cannot open EV file $EVfile ") && return; my @EVtext = ; close (EVFILE); my $numevents = scalar(@EVtext); #parse data in stimulus file - extract and round event times my $numrealevents = 0; for (my $i=0; $i<$numevents; ++$i) { chomp($EVtext[$i]); if ($EVtext[$i]) { my @eventparsed = split/\s/,$EVtext[$i]; if ($eventparsed[2]) { $eventtimes[$numrealevents]=$eventparsed[0]*$interpfactor; #do rounding to nearest unit $eventtimes[$numrealevents] = sprintf ("%.0f",$eventtimes[$numrealevents]); ++$numrealevents; } } } #rounding loop return @eventtimes; } sub doPlotNew { (my $plotfile,my $fromX,my $toX,my $numEVs,my $prefix,my $nummask, my $mask, my @designtext) = (@_); #legend values my $labellist; ##Get EV titles from design file my @EVtitles = grep /set fmri\(evtitle/, @designtext; my $titleline; my $s = 1; foreach $titleline (@EVtitles) { $_ = $titleline; /\"(.*)\"/; #" regex screws up syntax coloring in xcode my $title = $1; if (!$title) { $title = "EV$s"; } $labellist = $labellist."$title,"; ++$s; } print "labellist: $labellist\n"; #output filename my $outfile = "$prefix-mask$nummask"; my $endCol = $numEVs + 1; ##trim plot file -- fsl_tsplot will not plot negative numbers on the x axis, so start at zero ##no, there's not point in doing this since I still can't manipulate the x labels to match temporal res #my $numlines = $secondsend; #system "tail -n $numlines $plotfile > $plotfile-short"; #plot command system "fsl_tsplot -i $plotfile -o $outfile -t 'Peate timeseries' -a $labellist -x 'Time from stimulus onset' -y 'Percent signal change' --start=2 --finish=$endCol -h 400"; } sub doPlot { (my $plotfile,my $fromX,my $toX,my $numEVs,my $prefix,my $nummask, my $mask) = (@_); #Makes plot of results using gnuplot open (PLOTCOMMANDS,">$outputdir/plotcommands") or &doError("Could not open file for writing.") && return; print PLOTCOMMANDS "set term png; set size .75,.75; set title 'PEATE Average Timecourse'; set xlabel 'Time from stimulus onset (sec)';\n"; print PLOTCOMMANDS "set ylabel 'Percent BOLD signal change from baseline'; set xzeroaxis; set yzeroaxis; set xrange [-$fromX:$toX];\n"; print PLOTCOMMANDS "set xtics -$fromX,2,$toX; set data style lines;\n"; print PLOTCOMMANDS "plot "; my $j; my $k = 0; my $numToPlot = scalar(@listOfEVs); foreach $j (@listOfEVs) { ++$k; my $y=$k+1; print PLOTCOMMANDS "'$plotfile' using 1:$y title 'EV$j'"; if ($k == $numToPlot) {print PLOTCOMMANDS ";";} else {print PLOTCOMMANDS ", ";} } close (PLOTCOMMANDS); system "cat $outputdir/plotcommands | gnuplot > $outputdir/$prefix-mask$nummask.png"; #system "convert $outputdir/$prefix-mask$nummask.ppm $outputdir/$prefix-mask$nummask.gif"; #system "rm $outputdir/$prefix-mask$nummask.ppm"; if ($mode=~/GUI/) { my $plotwindow = new MainWindow(-background=>"white",-title=>'Plot'); $plotwindow -> Photo('img', -file => "$outputdir/$prefix-mask$nummask.png"); my $l = $plotwindow->Label('-image' => 'img')->pack; $plotwindow-> Button(-text=>'Close',-background=>"white",-command=>sub{$plotwindow->destroy;})->pack(-side=>'bottom'); $plotwindow-> Label(-text=>"Plot for mask $mask",-background=>"white")->pack(-side=>'bottom'); } system rm "$outputdir/plotcommands"; } sub checkForGnuplot { #Checks to see if gnuplot is installed if (`which gnuplot`) { $plot = 1; } else { $mw->bell(); my $errordialog=$mw->Dialog(-title=>"Error", -text=>"Gnuplot is either not installed or not in path. Continue without plotting?", -default_button=>"OK", -image => $mw->Getimage("warning"), -background=>"$bgm", -buttons=>["OK","STOP"]); #$errordialog->geometry("400x150"); $currentjob->configure(-text=>"stopped - error"); my $answer = $errordialog->Show; if ($answer eq 'STOP') { $plot = 9; } else { $plot = 0; } } return $plot; } sub peakOptions { #Brings up window to display peak finding options $peakWindow = new MainWindow(-background=>"$opc"); $peakWindow->geometry("200x200+400+350"); my $peakCBox=$peakWindow->Checkbutton( -text=>"find peaks",-background=>"$opc", -highlightbackground=>"$opc", -variable=>\$peaks, -command=>\&peakBoxChecked, -activebackground=>"$opc")->grid("-","-","-", -pady=>'5'); $peakKindPulldown=$peakWindow->Optionmenu(-variable=>\$peakWindowKind, -options=>["search entire window","limit window"], -background=>"$opi", -command=>\&doPeakPulldown, -activebackground=>"$opi")->grid("-","-","-", -pady=>'5'); $toText=$peakWindow->Label(-text=>"end",-background=>"$opc"); $fromEntry=$peakWindow->Entry(-width=>'4',-background=>"$opi", -text=>$peakWindowStart); $toEntry=$peakWindow->Entry(-width=>'4',-background=>"$opi",-text=>$peakWindowEnd); $fromText=$peakWindow->Label(-text=>"start",-background=>"$opc")->grid($fromEntry,$toText,$toEntry,-pady=>'5'); my $OKButton = $peakWindow->Button(-text=>'OK',-background=>"$bgm",-command=>\&peakOptionsClose)->grid("-","-","-",-pady=>'5'); #Run widget controllers to enable/disable widgets appropriately &peakBoxChecked; &doPeakPulldown; } sub peakBoxChecked { #Reponds to 'find peaks' checkbox by enabling/disabling other widgets appropriately if ($peaks) { $peakKindPulldown->configure(-state=>"normal"); if ($peakWindowKind =~/entire/) { } else { $fromEntry->configure(-state=>"normal"); $toEntry->configure(-state=>"normal"); } } else { $fromEntry->configure(-state=>"disabled"); $toEntry->configure(-state=>"disabled"); $peakKindPulldown->configure(-state=>"disabled"); $fromText->configure(-state=>"disabled"); $toText->configure(-state=>"disabled"); } } sub doPeakPulldown { #Reponds to peak window pulldown by enabling/disabling other widgets appropriately if ($peakWindowKind =~/entire/) { if ($fromEntry) { $fromEntry->configure(-state=>"disabled"); $toEntry->configure(-state=>"disabled"); $fromText->configure(-state=>"disabled"); $toText->configure(-state=>"disabled"); } } else { $fromEntry->configure(-state=>"normal"); $toEntry->configure(-state=>"normal"); $fromText->configure(-state=>"normal"); $toText->configure(-state=>"normal"); } } sub peakOptionsClose { #Executed when peak options window is closed #store values from window in variables $peakWindowStart=$fromEntry->get; $peakWindowEnd=$toEntry->get; $peakWindow->destroy; #Adjust 'find peaks' button accordingly if ($peaks) { $peaksButton->configure(-text=>"find peaks [ON]",-background=>"$opi"); } else { $peaksButton->configure(-text=>"find peaks [OFF]",-background=>"$opc"); } }