Kevin Mader [1,2,3], Leah Rae Donahue [4], Ralph Müller [5], and Marco Stampanoni [1,2]
8 April 2014
Motivation
Scalability
Results
Performance
Beyond
The only technique which can do all
[1] Mokso et al., J. Phys. D, 46(49),2013
So we have the imaging modality, now what?
1300 bone samples measured at 0.7 \( \mu \) m resolution in 2 weeks, automatically
Just like genetics, the burden of imaging is moving from acquisition to post-processing
[3] Sboner A,et. al. Genome Biology, 2011
There are three different types of problems that we will run into.
When a ton of heterogeneous information is coming in fast.
Performant, scalable, and flexible
10X, 100X, 1000X is the same amount of effort
Most imaging groups are overwhelmed with data (8GB/s adds up very quickly)
Parallel computing requires a significant of coordinating between computers for non-easily parallelizable tasks.
If you have two cores / computers trying to write the same information at the same it is no longer deterministic (not good)
Taking turns and waiting for every independent process to run completely negates the benefits of parallel computing
If you have 1000 computers working on solving a problem and one fails, you do not want your whole job to crash
How can you access and process data from many different computers quickly without very expensive infrastructure
Each parallel library / tool requires different tools with different constraints CUDA, OpenCL, OpenMPI, Matlab Distributed Toolbox, KORUS, .NET Concurrent Framework, Python Multiprocessing, Hadoop, Storm, Impala, Redshift

Zaharia, M., et. al (2012). Resilient distributed datasets: a fault-tolerant abstraction for in-memory cluster computing
Developed at ETH Zurich, Paul Scherrer Institut, and 4Quant
TIPL is a flexible framework for image processing and analysis
Report Generation
1300 bone samples analyzed using the Merlin Cluster at PSI in 6 weeks
If we do that, we miss a lot!
| Phenotype | Within | Between | Ratio (%) |
|---|---|---|---|
| Length | 36.97 | 4.279 | 864.1 |
| Width | 27.92 | 4.734 | 589.9 |
| Height | 25.15 | 4.636 | 542.5 |
| Volume | 67.85 | 12.479 | 543.7 |
| Nearest Canal Distance | 70.35 | 333.395 | 21.1 |
| Density (Lc.Te.V) | 144.40 | 27.658 | 522.1 |
| Nearest Neighbors (Lc.DN) | 31.86 | 1.835 | 1736.1 |
| Stretch (Lc.St) | 13.98 | 2.360 | 592.5 |
| Oblateness (Lc.Ob) | 141.27 | 18.465 | 765.1 |
The results in the table show the within and between sample variation for selected phenotypes in the first two columns and the ratio of the within and between sample numbers
How does this result look visually? Each line shows the mean \( \pm \) standard deviation for sample. The range within a single sample is clearly much larger than between
Phenotype values at D9Mit259 Marker located on chromosome 9 in the mouse
With the ability to scale to millions of samples there is no need to condense. We can analyze the entire dataset in real-time and try and identify groups or trends within the whole data instead of just precalculated views of it.
1276 comma separated text files with 56 columns of data and 15-60K rows
| Task | Single Core Time | Spark Time (40 cores) |
|---|---|---|
| Load and Preprocess | 360 minutes | 10 minutes |
| Single Column Average | 4.6s | 400ms |
| 1 K-means Iteration | 2 minutes | 1s |
We found several composite phenotypes which are more consistent within samples than between samples
Tools built for table-like data data structures and much better adapted to it.
Dozens of major companies (Apple, Google, Facebook, Cisco, …) donate over $30M a year to development of Spark and the Berkeley Data Analytics Stack
Can handle static data or live data coming in from a 'streaming' device like a camera to do real-time analysis. The exact same code can be used for real-time analysis and static code
Source could the camera server itself or a watched directory on a machine.
Projects at AMPLab like Spark and BlinkDB are moving towards approximate results.
mean(volume)
mean(volume).within_time(5)mean(volume).within_ci(0.95)For real-time image processing it might be the only feasible solution and could drastically reduce the amount of time spent on analysis.
Collaboration with Keith Cheng, Ian Foster, Xuying Xin, Patrick La Raviere, Steve Wang
1000s of samples of full adult animals, imaged at 0.74 \( \mu m \) resolution: Images 11500 x 2800 x 628 \( \longrightarrow \) 20-40GVx / sample
Collaboration with Alberto Astolfo, Matthias Schneider, Bruno Weber, Marco Stampanoni
1 \( cm^3 \) scanned at 1 \( \mu m \) resolution: Images \( \longrightarrow \) 1000 GVx / sample
We are interested in partnerships and collaborations
Spin up your own cluster in an hour
start-master.sh start-worker.sh spark://master-ip:7077 -c #CORES -m #MEM
spark-shell.shval threshVal=127
val labelImg=inImg.filter(_._2>threshVal)
Take a region of interest between 0 and 100 in X,Y, and Z
def roiFunc(pvec: ((Int,Int,Int),Double)) =
{pvec._1._1>=0 & pvec._1._1<100 & // X
pvec._1._2>=0 & pvec._1._2<100 & //Y
pvec._1._3>=0 & pvec._1._3<100} // Z
rImg.filter(roiFunc)
def spread_voxels(pvec: ((Int,Int,Int),Double), windSize: Int = 1) = {
val wind=(-windSize to windSize)
val pos=pvec._1
val scalevalue=pvec._2/(wind.length**3)
for(x<-wind; y<-wind; z<-wind)
yield ((pos._1+x,pos._2+y,pos._3+z),scalevalue)
}
val filtImg=roiImg.
flatMap(cvec => spread_voxels(cvec)).
filter(roiFun).reduceByKey(_ + _)
var groupList=Array((0L,0))
var running=true
var iterations=0
while (running) {
val newLabels=labelImg.
flatMap(spread_voxels(_,1)).
reduceByKey((a,b) => (math.min(a._1,b._1),(a._2 | b._2))).
filter(_._2._2). // keep only voxels which contain original pixels
map(pvec => (pvec._1,pvec._2._1))
// make a list of each label and how many voxels are in it
val curGroupList=newLabels.map(pvec => (pvec._2,1)).
reduceByKey(_ + _).sortByKey(true).collect
// if the list isn't the same as before, continue running since we need to wait for swaps to stop
running = (curGroupList.deep!=groupList.deep)
groupList=curGroupList
labelImg=newLabels
iterations+=1
print("Iter #"+iterations+":"+groupList.mkString(","))
}
groupList
val outThresh=inImg.map(threshFunc)
val outLabels=runCL(outThresh)
outLabels.filter(roiFunc).saveImage('test.tif')