Brian B. Avants et al.
2017-07-30
# set this for your own compilation
set.seed( 11 )
library( ANTsR )
## Loading required package: ANTsRCore
##
## Attaching package: 'ANTsRCore'
## The following object is masked from 'package:stats':
##
## var
## The following objects are masked from 'package:base':
##
## all, any, apply, max, min, prod, range, sum
library( pander )
library( visreg )
bd="/Users/bavants/data/antsExamples/ANTsTutorial/"
templatefn=paste(bd,"phantomData/phantomtemplate.jpg",sep='')
if ( ! file.exists(templatefn) )
stop( paste( "no image", templatefn ) )
allfns=Sys.glob( paste(bd,"phantomData/ph*wmgm.jpg",sep='') )
demog=read.csv(paste(bd,'phantomData/phantpredictors.csv',sep=''))
demog$vol[ demog$vol == 1 ] = "Smaller"
demog$vol[ demog$vol == 0 ] = "Bigger"
ilist=imageFileNames2ImageList( allfns )
sccansig=0
Manually drawn three tissue images with one group intentionally created to have a thicker “cortex”
Brain-like, two-dimensional slices
Pre-built template (./ANTsTutorial/phantomData/phantomtemplate.jpg
)
We show how to do a log-jacobian based study
… and a tissue study.
plot( ilist[[1]], window.img=c(0,255) )
NULL
plot( ilist[[2]], window.img=c(0,255) )
NULL
plot( ilist[[5]], window.img=c(0,255) )
NULL
plot( ilist[[6]], window.img=c(0,255) )
NULL
template=antsImageRead( templatefn )
ilist=imageFileNames2ImageList( allfns )
plist=imageFileNames2ImageList( allfns )
for ( x in 1:length(ilist) )
{
mask = getMask( ilist[[ x ]] ) %>% iMath("MD",1)
img = antsImageClone( ilist[[ x ]] )
km = kmeansSegmentation( img, k = 3, kmask = mask ) # segment
gm = km$probabilityimages[[2]]
plist[[ x ]] = gm
}
plot( ilist[[1]] )
## NULL
mask = getMask( ilist[[1]] ) %>% iMath("MD",1)
img = ilist[[ 1 ]]
km = kmeansSegmentation( img, 3, mask ) # segment
plot( km$segmentation, window.img=c(0,3) )
## NULL
template = antsImageRead( templatefn )
jlist = imageFileNames2ImageList( allfns )
txList = list( )
jList2 = list( )
myvolsj = rep( NA, length( ilist ) )
myvolsi = rep( NA, length( ilist ) )
for ( x in 1:length(ilist) )
{
gm = plist[[x]]
tx = antsRegistration( template, ilist[[x]], "SyNCC",
gradStep = 0.1, synSampling = 2,
flowSigma = 3, totalSigma = 0.0,
regIterations = c(100,50,40) )
gmw = antsApplyTransforms( template, gm, tx$fwdtransforms)
plist[[ x ]] = gmw
jlist[[ x ]] = createJacobianDeterminantImage( template,
tx$fwdtransforms[1], 1, 0 ) %>% smoothImage(1.5)
txList[[ x ]] = composeTransformsToField( template, tx$fwdtransforms )
jList2[[ x ]] = createJacobianDeterminantImage( template, txList[[ x ]] , FALSE, TRUE )
lo = 120
hi = 150 # cortex
# lo = 150
# hi = Inf # wm
myvolsi[ x ] = sum( thresholdImage( ilist[[x]], lo, hi ) )
myvolsj[ x ] = sum( thresholdImage( template, lo, hi ) * jList2[[ x ]] )
}
# Validate the jacobian calculations against the "real" volume change
# We must look at both the deformable and affine component, together.
plot( myvolsi, myvolsj , main='correlation in image-based and jacobian-based volume')
hist( myvolsi - myvolsj , main='difference in volume' )
print( paste("cor:", cor( myvolsi, myvolsj ), "err:", mean( abs( myvolsi - myvolsj ) ), "serr:", mean( myvolsi - myvolsj ) ) )
## [1] "cor: 0.992319514493284 err: 54.6502276211977 serr: -53.3713649809361"
# "cor: 0.99426596560049 err: 21.2326674573123 serr: 11.3190616928041" grad 0.05 synsampling = 1 flow 3 total 0
plot( template )
## NULL
plot( tx$warpedmovout )
## NULL
plot( template, tx$warpedmovout %>% iMath("Canny",1,5,12),
window.overlay=c(0.5,1) )
## NULL
plot( jlist[[1]])
## NULL
plot( jlist[[5]])
## NULL
…. but we should look at the demographics first.
Look at the demographics file …
pander( demog )
vol | gender |
---|---|
Bigger | M |
Bigger | F |
Bigger | M |
Bigger | F |
Smaller | M |
Smaller | F |
Smaller | F |
Smaller | M |
templatemask=getMask( template )
jmat=imageListToMatrix( jlist, templatemask )
# simple t-test to look at overall trends
rjmeans = rowMeans( jmat )
g1 = demog$vol == "Bigger"
g2 = demog$vol == "Smaller"
print( t.test( rjmeans[g1], rjmeans[g2] , paired=FALSE ) )
##
## Welch Two Sample t-test
##
## data: rjmeans[g1] and rjmeans[g2]
## t = 2.066, df = 5.702, p-value = 0.0868
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -0.009539579 0.105164016
## sample estimates:
## mean of x mean of y
## -0.02601552 -0.07382774
Check out bigLMStats
. It is a great little function for large (on the left) statistical studies …
demog$vol = factor( demog$vol )
umdl=lm( jmat ~ vol + gender, data=demog )
mdl=bigLMStats( umdl )
volstats=mdl$beta.pval[ "volSmaller", ]
qv=p.adjust(volstats,'BH')
qvimg=makeImage( templatemask, 1 - qv )
for large on the right studies, use a quick implementation in RcppEigen
or RcppArmadillo
Use visreg
to see the regression at the voxel with the maximum significant difference.
vdemog = data.frame( demog, voxel = jmat[,which.min(qv)])
vmdl=lm( voxel ~ vol + gender, data=vdemog )
visreg::visreg( vmdl, "vol", main="Regression at a voxel" )
plot( template, qvimg, window.overlay=c(0.95,1))
## NULL
…
templateCortexMask = thresholdImage( template, lo, hi )
gmat=imageListToMatrix( plist, templateCortexMask )
mdl=lm( gmat ~ vol + gender, data=demog )
mdl=bigLMStats( mdl )
gmstats=mdl$beta.pval[ "volSmaller", ]
gqv=p.adjust(gmstats,'BH')
gqvimg=makeImage( templatemask, 1-gqv )
## Warning in makeImage(templatemask, 1 - gqv): Number of voxels not the same
## as the positive values
… this is always important to think about …
Do we believe that many aspects of cognition are fundamentally multivariate?
volClasses = c( rep( 1, 4 ), rep(0, 4 ) )
mypreds<-scale( as.matrix( cbind( volClasses, as.numeric( demog$gender ) ) ) )
scaledMatrix = scale( jmat )
sccan<-sparseDecom2(
inmatrix = list( scaledMatrix, mypreds ),
inmask = c( templatemask , NA ) ,
mycoption = 0,
sparseness=c( 0.25, 0.5 ),
nvecs=3, its=5,
cthresh=c(50,0),
smooth=0.5, perms=200, robust=1 ) # might increase perms to be > 100
sccanimgs = matrixToImages( t(sccan$eig1) , templatemask )
sccansol <- abs(sccanimgs[[1]]) %>% iMath("Normalize")
sccansol2 <- abs(sccanimgs[[2]]) %>% iMath("Normalize")
pander( sccan$ccasummary )
corrs | pvalues |
---|---|
0.9954 | 0.005 |
0.9146 | 0.51 |
0.1518 | 0.6 |
Exercise: What happens to the permutation based significance when you vary parameters?
plot( template, sccansol, window.overlay=c(0.1,1.01) )
## NULL
plot( template, sccansol2, window.overlay=c(0.1,1.01) )
## NULL
No explicit use of demographics here ….
spatmat = t( imageDomainToSpatialMatrix( templatemask, templatemask ) )
smoomat = knnSmoothingMatrix( spatmat, k = 27, sigma = 25 )
## Note: method with signature 'CsparseMatrix#Matrix#missing#replValue' chosen for function '[<-',
## target signature 'dsCMatrix#lsCMatrix#missing#numeric'.
## "Matrix#lsparseMatrix#missing#replValue" would also be valid
spatmat = t( imageDomainToSpatialMatrix( templateCortexMask, templateCortexMask ) )
smoomat2 = knnSmoothingMatrix( spatmat, k = 27, sigma = 100 )
# map these - via matrix - to observed features
params = matrix( nrow = 2, ncol = 3 )
params[1,] = c(1,2,1)
params[2,] = c(2,1,1)
x = list( scale( jmat, scale = F ), scale( gmat, scale = F ) )
jj = jointSmoothMatrixReconstruction( x, 2, params,
gamma = 0.01, sparsenessQuantile=0.66, iterations=25,
subIterations=20, positivity = TRUE,
smoothingMatrix = list(smoomat2,smoomat), verbose=F )
p1 = jmat %*% jj$v[[2]]
p2 = gmat %*% jj$v[[1]]
diag( cor( p1, p2 ) )
## u.1 u.2
## -0.5822389 -0.5025775
mdl1=lm( p1 ~ vol + gender, data=demog )
mdl2=lm( p2 ~ vol + gender, data=demog )
mdllist = lappend( summary( mdl1 ), summary( mdl2 ) )
pander( mdllist[[1]] )
Estimate | Std. Error | t value | Pr(>|t|) | |
---|---|---|---|---|
volSmaller | -0.0101 | 0.001946 | -5.19 | 0.003496 |
genderM | 0.0005721 | 0.001946 | 0.2939 | 0.7806 |
(Intercept) | 0.004767 | 0.001686 | 2.828 | 0.03675 |
Observations | Residual Std. Error | \(R^2\) | Adjusted \(R^2\) |
---|---|---|---|
8 | 0.002753 | 0.8439 | 0.7814 |
pander( mdllist[[2]] )
Estimate | Std. Error | t value | Pr(>|t|) | |
---|---|---|---|---|
volSmaller | -0.002941 | 0.0005752 | -5.113 | 0.003728 |
genderM | 0.0005423 | 0.0005752 | 0.9427 | 0.3891 |
(Intercept) | 0.0002254 | 0.0004982 | 0.4525 | 0.6699 |
Observations | Residual Std. Error | \(R^2\) | Adjusted \(R^2\) |
---|---|---|---|
8 | 0.0008135 | 0.8439 | 0.7815 |
pander( mdllist[[3]] )
Estimate | Std. Error | t value | Pr(>|t|) | |
---|---|---|---|---|
volSmaller | 0.0002413 | 0.0001212 | 1.992 | 0.103 |
genderM | 0.0001996 | 0.0001212 | 1.648 | 0.1603 |
(Intercept) | 0.003344 | 0.0001049 | 31.87 | 5.715e-07 |
Observations | Residual Std. Error | \(R^2\) | Adjusted \(R^2\) |
---|---|---|---|
8 | 0.0001713 | 0.572 | 0.4008 |
pander( mdllist[[4]] )
Estimate | Std. Error | t value | Pr(>|t|) | |
---|---|---|---|---|
volSmaller | 5.381e-05 | 2.444e-05 | 2.202 | 0.07893 |
genderM | 3.92e-05 | 2.444e-05 | 1.604 | 0.1697 |
(Intercept) | 0.0008137 | 2.117e-05 | 38.44 | 2.245e-07 |
Observations | Residual Std. Error | \(R^2\) | Adjusted \(R^2\) |
---|---|---|---|
8 | 3.457e-05 | 0.5974 | 0.4364 |
# look at the predictor image
temp = makeImage( templatemask, abs(jj$v[[2]][,1]) ) %>% iMath( "Normalize" )
plot( template, temp, window.overlay = c( 0.1, 1 ) )
## NULL
temp = makeImage( templateCortexMask, abs(jj$v[[1]][,2]) ) %>% iMath( "Normalize" )
plot( template, temp, window.overlay = c( 0.1, 1 ) )
## NULL
We implemented a simple approach to mapping a custom template, and its blobs, to MNI coordinates.
See ?getTemplateCoordinates
for an example.
We also report summary regional labels.
First, get a template, here the ch2
brain.
if ( ! exists("tem") )
tem<-antsImageRead( getANTsRData("ch2") )
clust <- thresholdImage( tem, 82, 90 ) %>%
iMath("ME",1) %>% labelClusters()
if ( ! exists("mymni") ) {
# try getANTsRData if you have www access
mymni<-list( antsImageRead(getANTsRData("mni") ),
antsImageRead(getANTsRData("mnib") ),
antsImageRead(getANTsRData("mnia") ) )
}
template_cluster_pair<-list(tem,clust)
t1=Sys.time()
gcoords<-getTemplateCoordinates( template_cluster_pair ,
mymni , convertToTal = TRUE )
t2=Sys.time()
print(t2-t1)
## Time difference of 33.02606 secs
Show the table of results.
pander( gcoords$templatepoints )
x | y | z | t | Brodmann | AAL |
---|---|---|---|---|---|
-12 | 13 | -1 | 0 | 0 | Caudate_L |
13 | 15 | 6 | 0 | 0 | Caudate_R |
-8 | -24 | 7 | 0 | 0 | Thalamus_L |
19 | 12 | -7 | 0 | 48 | Putamen_R |
7 | -23 | 6 | 0 | 0 | Thalamus_R |
28 | 2 | -16 | 0 | 34 | Amygdala_R |
0 | -58 | -25 | 0 | 0 | Vermis_8 |
40 | 20 | -5 | 0 | 47 | Insula_R |
17 | -80 | -34 | 0 | 0 | Cerebelum_Crus2_R |
A simple reproducible example for a phantom morphometry study
Plotting, segmentation, registration
Statistics of two flavors ….
Mapping to MNI coordinates.