package edu.stanford.rsl.apps;
import ij.IJ;
import ij.ImagePlus;
import ij.gui.Roi;
import ij.io.Opener;
import ij.io.RoiDecoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.jogamp.opencl.CLContext;
import com.jogamp.opencl.CLDevice;
import edu.stanford.rsl.apps.gui.roi.EvaluateROI;
import edu.stanford.rsl.conrad.filtering.ImageFilteringTool;
import edu.stanford.rsl.conrad.geometry.General;
import edu.stanford.rsl.conrad.geometry.motion.OpenCLParzenWindowMotionField;
import edu.stanford.rsl.conrad.geometry.motion.ParzenWindowMotionField;
import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
import edu.stanford.rsl.conrad.geometry.trajectories.Trajectory;
import edu.stanford.rsl.conrad.io.FileProjectionSource;
import edu.stanford.rsl.conrad.io.IndividualFilesProjectionDataSink;
import edu.stanford.rsl.conrad.io.VTKVectorField;
import edu.stanford.rsl.conrad.numerics.SimpleVector;
import edu.stanford.rsl.conrad.opencl.OpenCLUtil;
import edu.stanford.rsl.conrad.phantom.AnalyticPhantom;
import edu.stanford.rsl.conrad.phantom.AnalyticPhantom4D;
import edu.stanford.rsl.conrad.phantom.renderer.ParallelProjectionPhantomRenderer;
import edu.stanford.rsl.conrad.phantom.renderer.PhantomRenderer;
import edu.stanford.rsl.conrad.phantom.renderer.SliceParallelVolumePhantomRenderer;
import edu.stanford.rsl.conrad.physics.absorption.DensityAbsorptionModel;
import edu.stanford.rsl.conrad.physics.absorption.PolychromaticAbsorptionModel;
import edu.stanford.rsl.conrad.physics.detector.SimpleMonochromaticDetector;
import edu.stanford.rsl.conrad.physics.detector.SimplePolychromaticDetector;
import edu.stanford.rsl.conrad.pipeline.BufferedProjectionSink;
import edu.stanford.rsl.conrad.pipeline.ParallelImageFilterPipeliner;
import edu.stanford.rsl.conrad.pipeline.ProjectionSource;
import edu.stanford.rsl.conrad.utils.CONRAD;
import edu.stanford.rsl.conrad.utils.Configuration;
import edu.stanford.rsl.conrad.utils.FloatArrayUtil;
import edu.stanford.rsl.conrad.utils.ImageUtil;
import edu.stanford.rsl.conrad.utils.RegKeys;
import edu.stanford.rsl.conrad.utils.UserUtil;
public class Conrad {
public static PhantomRenderer phantom;
public static AnalyticPhantom4D phantom4D;
/**
* @param args
*/
public static void main(String[] args) {
printStartup();
if (args.length == 0){
printSynopsis();
} else {
String command = args[0];
if (command.equals("help")){
if (args.length != 2){
printListOfCommands();
} else {
printHelp(args[1]);
}
} else if (command.equals("execute")){
if (args.length == 5){
executePipeline(args[1], args[2], args[3], args[4]);
} else {
System.out.println("Wrong number of arguments for 'execute'. (n = " + args.length + ")\n");
printHelp(command);
}
} else if (command.equals("evaluate")){
if (args.length == 6){
evaluateROI(args[1], args[2], args[3], args[4], Integer.parseInt(args[5]));
} else {
System.out.println("Wrong number of arguments for 'evaluate'. (n = " + args.length + ")\n");
printHelp(command);
}
} else if (command.equals("render")){
if (args.length == 3){
renderPhantom(args[1], args[2], -1);
} else if (args.length == 4){
renderPhantom(args[1], args[2], Integer.parseInt(args[3]));
} else {
System.out.println("Wrong number of arguments for 'render'. (n = " + args.length + ")\n");
printHelp(command);
}
} else if (command.equals("motionfield")){
if (args.length == 6){
createMotionfield(args[1], args[2], Double.parseDouble(args[3]), Double.parseDouble(args[4]), args[5]);
}else {
System.out.println("Wrong number of arguments for 'motionfield'. (n = " + args.length + ")\n");
printHelp(command);
}
} else if (command.equals("compareVTKvectorfields")){
if (args.length == 4){
compareVTKVectorFields(args[1], args[2], args[3]);
}else {
System.out.println("Wrong number of arguments for 'compareVTKvectorfields'. (n = " + args.length + ")\n");
printHelp(command);
}
} else {
System.out.println("Command was not recognized.\n");
printSynopsis();
}
}
}
private static void compareVTKVectorFields(String configfile, String file1, String file2){
System.out.print("Reading configuration ... ");
Configuration.setGlobalConfiguration(Configuration.loadConfiguration(configfile));
System.out.println("done");
try {
System.out.print("Reading vector field 1 ... ");
float[] motionfield1 = VTKVectorField.readFromFile3D(file1);
System.out.println("done");
System.out.print("Reading vector field 2 ... ");
float[] motionfield2 = VTKVectorField.readFromFile3D(file2);
System.out.println("done");
System.out.println("Root Mean Square Error: " + Math.sqrt(FloatArrayUtil.computeMeanSquareError(motionfield1, motionfield2)));
} catch (IOException e) {
e.printStackTrace();
}
}
private static void createMotionfield(String configFile, String outfile, double fromTime, double toTime, String interpolationType){
Configuration.setGlobalConfiguration(Configuration.loadConfiguration(configFile));
try {
System.out.print("Reading phantom ... ");
if (Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.SPLINE_4D_LOCATION) != null) {
FileInputStream fis;
fis = new FileInputStream(Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.SPLINE_4D_LOCATION));
ObjectInputStream ois = new ObjectInputStream(fis);
phantom4D = (AnalyticPhantom4D) ois.readObject();
ois.close();
} else {
phantom4D = (AnalyticPhantom4D) UserUtil.queryPhantom("Select phantom:", "Phantom Selection");
phantom4D.configure();
}
System.out.println("done");
// phantom loaded.
// write vtkmesh to file.
Trajectory traj = Configuration.getGlobalConfiguration().getGeometry();
if (("true").equals(Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.RENDER_PHANTOM_VOLUME_AUTO_CENTER))) {
traj.setOriginToPhantomCenter(phantom4D);
}
float [] motionfield = null;
Configuration config = Configuration.getGlobalConfiguration();
System.out.println("Creating motion field ... ");
long time = System.currentTimeMillis();
if (interpolationType.equals("ParzenCPU")) {
motionfield = new float[config.getGeometry().getReconDimensionX()*config.getGeometry().getReconDimensionY()*config.getGeometry().getReconDimensionZ()*3];
int zPitch = config.getGeometry().getReconDimensionX() *config.getGeometry().getReconDimensionY();
int yPitch = config.getGeometry().getReconDimensionX();
for (int z = 0; z<config.getGeometry().getReconDimensionZ(); z++){
System.out.println("Computing slice " + z);
for (int y = 0; y<config.getGeometry().getReconDimensionY(); y++){
for (int x = 0; x<config.getGeometry().getReconDimensionX(); x++){
PointND currentVoxel = new PointND(
General.voxelToWorld(x, config.getGeometry().getVoxelSpacingX(), config.getGeometry().getOriginX()),
General.voxelToWorld(y, config.getGeometry().getVoxelSpacingY(), config.getGeometry().getOriginY()),
General.voxelToWorld(z, config.getGeometry().getVoxelSpacingZ(), config.getGeometry().getOriginZ()));
PointND destination = phantom4D.getPosition(currentVoxel, fromTime, toTime);
SimpleVector direction = destination.getAbstractVector();
direction.subtract(currentVoxel.getAbstractVector());
if(direction.getElement(1) == Double.POSITIVE_INFINITY){
System.out.println("Infinity");
}
int idx = z*zPitch+y*yPitch+x;
motionfield[idx*3] =(float) direction.getElement(0);
motionfield[idx*3+1] =(float) direction.getElement(1);
motionfield[idx*3+2] =(float) direction.getElement(2);
}
}
}
}
if (interpolationType.equals("ParzenGPU")) {
CLContext context = OpenCLUtil.createContext();
CLDevice device = context.getMaxFlopsDevice();
ParzenWindowMotionField motion = (ParzenWindowMotionField) phantom4D.getMotionField();
OpenCLParzenWindowMotionField cl = new OpenCLParzenWindowMotionField(motion, context, device);
motionfield = cl.getMotionFieldAsArray(fromTime, toTime);
}
if (interpolationType.equals("ParzenGPUZFilter")) {
CLContext context = OpenCLUtil.createContext();
CLDevice device = context.getMaxFlopsDevice();
ParzenWindowMotionField motion = (ParzenWindowMotionField) phantom4D.getMotionField();
OpenCLParzenWindowMotionField cl = new OpenCLParzenWindowMotionField(motion, context, device);
motionfield = cl.getMotionFieldAsArrayReduceZ(fromTime, toTime);
}
if (interpolationType.equals("ParzenGPUZFilterGridXY")) {
CLContext context = OpenCLUtil.createContext();
CLDevice device = context.getMaxFlopsDevice();
ParzenWindowMotionField motion = (ParzenWindowMotionField) phantom4D.getMotionField();
OpenCLParzenWindowMotionField cl = new OpenCLParzenWindowMotionField(motion, context, device);
int factor = 16;
motionfield = cl.getMotionFieldAsArrayReduceZGridXY(fromTime, toTime, config.getGeometry().getReconDimensionX()/factor, config.getGeometry().getReconDimensionY()/factor);
}
if (interpolationType.equals("ParzenGPURBC")) {
CLContext context = OpenCLUtil.createContext();
CLDevice device = context.getMaxFlopsDevice();
ParzenWindowMotionField motion = (ParzenWindowMotionField) phantom4D.getMotionField();
OpenCLParzenWindowMotionField cl = new OpenCLParzenWindowMotionField(motion, context, device);
motionfield = cl.getMotionFieldAsArrayRandomBallCover(fromTime, toTime, 500000);
}
time = System.currentTimeMillis() - time;
System.out.print("Writing to file ... ");
VTKVectorField.writeToFile3D(outfile, motionfield);
System.out.println("done");
System.out.println("Interpolation took: " + time);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void renderPhantom(String configFile, String outfile, int frame){
Configuration.setGlobalConfiguration(Configuration.loadConfiguration(configFile));
try {
IndividualFilesProjectionDataSink sink = new IndividualFilesProjectionDataSink();
sink.setDirectory(outfile);
sink.setPrefix("phantom.");
sink.setFormat(IndividualFilesProjectionDataSink.Float32Bit);
sink.setLittleEndian(true);
sink.configured();
if (Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.SPLINE_4D_LOCATION) != null) {
FileInputStream fis = new FileInputStream(Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.SPLINE_4D_LOCATION));
ObjectInputStream ois = new ObjectInputStream(fis);
phantom = new ParallelProjectionPhantomRenderer();
AnalyticPhantom phan = (AnalyticPhantom4D) ois.readObject();
ParallelProjectionPhantomRenderer para = (ParallelProjectionPhantomRenderer) phantom;
para.configure(phan, Configuration.getGlobalConfiguration().getDetector());
ois.close();
} else {
phantom = (PhantomRenderer) UserUtil.chooseObject("Select Phantom: ", "Phantom Selection", PhantomRenderer.getPhantoms(), PhantomRenderer.getPhantoms()[0]);
phantom.configure();
}
long time = System.currentTimeMillis();
if (frame == -1) {
Thread thread = new Thread(new Runnable(){public void run(){phantom.createPhantom();}});
thread.start();
ParallelImageFilterPipeliner pipeliner = new ParallelImageFilterPipeliner(phantom, new ImageFilteringTool[]{}, sink);
pipeliner.project(true);
sink.getResult();
} else {
SliceParallelVolumePhantomRenderer renderer = (SliceParallelVolumePhantomRenderer) phantom;
renderer.getModelWorker().workOnSlice(frame);
sink.process(renderer.getModelWorker().getImageProcessorBufferValue().get(frame),frame);
sink.close();
sink.getResult();
}
time = System.currentTimeMillis() - time;
System.out.println("Runtime: " + time/1000.0 + " s");
System.out.println("Saving to " + outfile);
System.out.println("All done.\nThanks for using Conrad today. As far as we know, Conrad has never had an undetected error.");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void evaluateROI(String configFile, String method, String roiFile, String dataFile, int slice){
Configuration.setGlobalConfiguration(Configuration.loadConfiguration(configFile));
try {
System.out.println("Evaluating: " + dataFile);
Opener open = new Opener();
ImagePlus imp = open.openImage(dataFile);
imp.setPosition(slice+1);
RoiDecoder rd = new RoiDecoder(roiFile);
Roi roi = rd.getRoi();
EvaluateROI eval = (EvaluateROI) Class.forName(method).newInstance();
eval.setRoi(roi);
eval.setImage(imp);
eval.setConfigured(true);
eval.evaluate();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void executePipeline(String sinkName, String configFile, String inFile, String outFile){
Configuration.setGlobalConfiguration(Configuration.loadConfiguration(configFile));
try {
ProjectionSource pSource = FileProjectionSource.openProjectionStream(inFile);
ImageFilteringTool [] filters = Configuration.getGlobalConfiguration().getFilterPipeline();
File out = new File(outFile);
boolean isConfigured = true;
for (int i = 0; i < filters.length; i++){
if (!filters[i].isConfigured()) isConfigured = false;
}
if (isConfigured) {
try {
BufferedProjectionSink sink = (BufferedProjectionSink) Class.forName(sinkName).newInstance();
System.out.println("Running pipeline with " + sink.getName());
sink.configure();
ParallelImageFilterPipeliner filteringPipeline = new ParallelImageFilterPipeliner(pSource, filters, sink);
long time = System.currentTimeMillis();
try {
filteringPipeline.project();
} catch (Exception e) {
e.printStackTrace();
}
sink.getResult();
time = System.currentTimeMillis() - time;
System.out.println("Runtime: " + time/1000.0 + " s");
//if (sink instanceof ReconstructionFilter) {
// ReconstructionFilter recon = (ReconstructionFilter) sink;
// if (Configuration.getGlobalConfiguration().getUseHounsfieldScaling()) recon.applyHounsfieldScaling();
//}
ImagePlus result = ImageUtil.wrapGrid3D(sink.getResult(),"Result of " + configFile);
// save to out-file; much simpler than I expected!
System.out.println("Saving to " + outFile);
IJ.saveAs(result, out.getName(), out.getAbsolutePath());
System.out.println("All done.\nThanks for using Conrad today. As far as we know, Conrad has never had an undetected error.");
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println("Pipeline is not configured.");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void printHelp(String command){
if (command.equals("execute")) {
System.out.println("Usage: Conrad execute projection-sink config-file data-file out-file\n\nThis command will apply the pipeline defined in the config-file to the data-file and store the result in out-file. The out-file type is determined by its extension.\nUse the ReconstructionPipelineFrame in conrad.gui to create a config-file.\n");
BufferedProjectionSink [] sinks = BufferedProjectionSink.getProjectionDataSinks();
System.out.println("projection-sink has to be one of this list:\n");
for (BufferedProjectionSink sink: sinks) {
System.out.println(sink.getClass().getName() +"\t" + sink.getName());
}
} else if (command.equals("help")) {
printListOfCommands();
} else if (command.equals("evaluate")) {
System.out.println("Usage: Conrad evaluate config-file method roi-file data-file slice\n\nThis command will apply the method with the parameters defined in the config-file to the data-file \nUse the ReconstructionPipelineFrame in conrad.gui to create a config-file.\n");
EvaluateROI [] methods = EvaluateROI.knownMethods();
System.out.println("method has to be one of this list:\n");
for (EvaluateROI method: methods) {
System.out.println(method.getClass().getName() +"\t" + method.toString());
}
} else if (command.equals("render")) {
System.out.println("Usage: Conrad render config-file outfile [frame]\n\nThis command will render a phantom and save it in the out-file. The out-file type is determined by its extension.\nUse the ReconstructionPipelineFrame in conrad.gui to create a config-file.\n");
} else if (command.equals("motionfield")) {
System.out.println("Usage: Conrad motionfield config-file outfile timeFrom timeTo\n\nThis command will generate the motionfield of a phantom and save it in the out-file. The out-file type is determined by its extension.\nUse the ReconstructionPipelineFrame in conrad.gui to create a config-file.\n");
} else if (command.equals("compareVTKvectorfields")) {
System.out.println("Usage: Conrad compareVTKvectorfields config-file file1 file2\n\nThis command will compute the root mean square error between the two given vector fields.\n");
} else {
System.out.println("Command '" + command + "' unknown.\n");
printListOfCommands();
}
}
private static void printListOfCommands(){
System.out.println("List of available commands:\n");
System.out.println("help - print this screen");
System.out.println("execute - execute a pipeline on a specified data set");
System.out.println("evaluate - evaluate an roi in a given dataset");
System.out.println("render - create projection data");
System.out.println("motionfield - generate a motionfield in vtk format");
System.out.println("compareVTKvectorfields - compare two vector fields in vtk format");
}
private static void printStartup(){
System.out.println("CONRAD Commandline Tool\n" + CONRAD.VersionString +"\n");
}
private static void printSynopsis(){
System.out.println("Usage:\njava edu.stanford.rsl.apps.Conrad command\n\nUse command 'help' for a list of commands.\n\n");
}
}
/*
* Copyright (C) 2010-2014 - Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/