#### surfcluster or FDR: mult comparison correct ####
-------------------------------------------
funct: "Run SurfCluster (left-click-CS)"
  set preclustfthr <e.g.: 5.0>
  set clustarea <e.g., 300>
  run_surfclust
-------------------------------------------
functs: "FDR Controls Pop-Up (mid-clk-CS)"
  smooth <steps> val
  clear_uniqvoxdata
  setfile label */<scandir>/$hemi-UniqSampVtxs.label
  [optional: read_uniqsamp_vertices]
  set noisedf <noise-deg-freedom>
  pval_from_fourier_fstat
  find_FDR_from_pvals <desired_FDR>
-------------------------------------------


Detailed Description of "CS" Button Actions

-------------------------------------------
funct: "Run SurfCluster (left-click-CS)"
  set preclustfthr <e.g.: 5.0>
  set clustarea <e.g., 300>
  run_surfclust
-------------------------------------------

A default left-click on the "CS" button on the
"val:" entry line uses the commandline program
"surfclust" to apply a surface-based cluster size
exclusion filter to data currently loaded into
the vertex-wise .stat field.  The .stat field is
can be loaded in two ways from the "val3d:" line.

For complex-valued data with complex amplitude
replaced by the sqrt(F) (BRIK's w/infixes:
_r,_i), the .stat field will automatically be
loaded with F when "PAINT" is clicked.

For a real-valued stat data BRIK, first click PNT
(paint) to sample it to the surface, then click
S/V to swap it into the surface .stat field.

The clustering parameters are controlled by the
tksurfer commandline options:

  -preclustfthr <F>			hard thresh before filter
  -clustarea <mm^2>			min cluster area

or the corresponding tcl variables, $preclustfthr
and $clustarea in a script.  The initial values
are loaded from the Fourier panel, but can also
be controlled live from tksurfer using the pop-up
accessed by a R-click on "label:" (see bottom of
pop-up).

The sequence of actions done by run_surfclust is:

  1) swap stat and val (stat to writable 'val' position)
  2) write out stat to temp wfile
  3) run surfclust on temp wfile
  4) read output back into .val
  5) swap stat and val again (back to .stat)

Here is an example of the full surfclust
commandline run when "CS" is clicked:

  surfclust
     -instem ./TmpCplxFstatsVtxListFile_f
     -name martys
     -outstem ./TmpCplxClustFstatsVtxListFile_h
     -hemi lh
     -thresh 5.0
     -minarea 300
     -surf smoothwm

Here is the set of tcl commands required to load
a complex-valued pair of 3D volume phase-stat
files, paint them onto the surface and load the
.stat field with unclustered F (which is square
of _r,_i amplitude), and finally run the cluster
exclusion filter to modify the data in the .stat
field:

  setfile statpatt */scandir1/pol1_r+orig.BRIK
  read_native_stat 0   
  setfile statpatt */scandir1/pol1_i+orig.BRIK
  read_native_stat 1
  # "1" arg on next line means load F into .stat
  paint_surface 1
  set preclustfthr 5.0
  set clustarea 300
  run_surfclust

To save the resulting clustered F stats wfile
(N.B.: these are hemisphere-specific) in the
current scandir ("scandir1" below), use:

  swap_stat_val
  setfile */scandir1/pol1+orig_h-$hemi.w
  write_binary_values

The _h infix is the default for surface-based
clustered stats.


-------------------------------------------
functs: "FDR Controls Pop-Up (mid-clk-CS)"
  smooth <steps> val
  clear_uniqvoxdata
  setfile label */<scandir>/$hemi-UniqSampVtxs.label
  [optional: read_uniqsamp_vertices]
  set noisedf <noise-deg-freedom>
  pval_from_fourier_fstat
  find_FDR_from_pvals <desired_FDR>
-------------------------------------------

Calculate p-Value for Given False Discovery Rate

An middle-click on the "CS" button provides an
alternate non-parametric FDR method of correcting
for multiple comparisons.

The Benjamini/Hochberg (1995) method as
introduced to fMRI by Nichols estimates a
corrected p-value to use for a desired false
discovery rate (FDR) as follows:

  read F-stat onto surface
  calc p-val for each vertex (fcdf)
  sort p's in ascending order
  find highest p less than or equal to (i/N * q), where
      i = index of that p
      N = total num vertices
      q = desired probability

A middle-click on the "CS" button brings up a
popup with 6 adjustable parameters:

  smoothsteps
  val,weight
  desired_FDR
  noisedf
  uniqsampvtxflag
  arbitrFDRcovarflag

Optional "smoothsteps" are applied to the F-stats
before converting to p-values and sorting.  The
smoothing method ("val,weight") can be either
"val" (=standard nearest neighbor) or the gentler
"weight" (Gaussian distance-weighted, 0.8mm FWHM,
adjustable with $halfweightdist: def=0.4).

Now enter "desired_FDR" (as a p-value, e.g.,
0.05) and "noisedf", the noise degrees of
freedom.  Assuming that the F-stats were
calculated by csurf fourier methods, this will
typically be 256 or 512 frequencies minus 11
nuisance regressors (very low frequencies, 2nd
and 3rd harmonics, and -1/+1 neighbors of the
1st-3rd harmonics).

The "uniqsampvtxflag" tick controls whether all
vertices are used, or whether only a single
vertex is used for each sampled voxel, namely,
the vertex that is closest to average position of
the set of vertices that sampled that voxel (~10
vertices/voxel for 3mm^3 data).

The required file of unique vertices used by
"uniqsampvtxflag" will have been generated if
PAINT was run after ticking "UniqVtx" on the
Setup Functional panel.

Finally, the "arbitrFDRcovarflag" tick implements
the Benjamini/Yekutieli (2001) non-parametric
correction to q for arbitrary covariance by
dividing q by the sum of 1/i (approximately equal
to log(N) + 0.5772) instead of by 1.

N.B.: vertices with a value of 0.0 are excluded
from the calculation because these are off the
edge of the functional volume.

The corrected p-value is returned in a final
pop-up and written into the csurf log.

To use the corrected estimate, enter the
corresponding F into the "sfmid" field for the
mask (right-hand "fmid" entry).

Loading F-stat Data

The CS button only requires F-stats in the
invisible .stat field.  An easy way to do this
from the csurf "View Functional Data" panel:

  (1) select _r file from "Paintfile:" dropdown
  (2) click "sig+(clust)Fmask" =>auto-selects _h mask (clust)
  (3) select _f file instead from "StatMask Paintfile: dropdown
  (4) SURFACE-STATS

Alternatively, the .stat field can be loaded
interactively from tksurfer as follows:

  (1) click S/V to save/swap existing map data
  (2) select _f file from "val:" dropdown
  (3) read it into .val with "R"
  (4) click S/V to swap F-stat into .stat field
        (restores map data)

Regions of Interest

To further restrict the set of vertices over
which the desired_FDR is calculated, cut away
parts of the surface (cut-away vertices are
ignored for the FDR calculation).  An existing
label can be used for this purpose by selecting
it from the dropdown and left-clicking "C"
(re-cut) on the "label:" line.

FDR via Tcl Script for tksurfer

The FDR calculation (including using
unique vertex sampling) can be run
quickly (runtime less than 1sec) with a
commandline tcl script for tksurfer that
doesn't bring up any windows.

Save the following script into a file (e.g.,
zz.tcl in the scripts dir of a functional
session), after first replacing <scandir>,
<stem_up_to_+orig>, <session>, and <subject>
below with your values:

  set scandir <scandir>
  setfile val */$scandir/<stem_up_to_+orig>_f-$hemi.w
  read_binary_values
  smooth_val 0
  swap_stat_val
  set noisedf 245
  setfile label */$scandir/$hemi-UniqSampVtxs.label
  read_uniqsamp_vertices
  pval_from_fourier_fstat
  find_FDR_from_pvals 0.05
  exit

Then run the tcl script from a shell (bash or tcsh):

  cd $FUNCTIONALS_DIR/<session>/image/scripts
  tksurfer <subject> rh inflated -tcl zz.tcl

This will print some lines, ending with:

  ...
  % tksurfer: find_FDR_from_pvals(q=0.050000):
  % tksurfer:  use only uniqsamp vtxs
  % tksurfer:  for desired FDR=0.0500 use pval=0.0171 (n=12501)
