package edu.stanford.rsl.conrad.utils;
import java.awt.Color;
import edu.stanford.rsl.conrad.data.numeric.Grid2D;
import edu.stanford.rsl.conrad.data.numeric.Grid3D;
import edu.stanford.rsl.conrad.data.numeric.MultiChannelGrid2D;
import edu.stanford.rsl.conrad.fitting.Function;
import edu.stanford.rsl.conrad.fitting.LinearFunction;
import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
import edu.stanford.rsl.conrad.geometry.splines.BSpline;
import ij.ImageJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.Line;
import ij.gui.Overlay;
import ij.gui.Plot;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
public abstract class VisualizationUtil {
public static synchronized Plot createPlot(String title, double [] yValues){
return createPlot(yValues, title, "Column", "Power");
}
public static synchronized Plot createPlot(String title, float [] yValues){
double[] tmp = new double[yValues.length];
for(int i=0; i<yValues.length; ++i)
tmp[i] = yValues[i];
return createPlot(tmp, title, "Column", "Power");
}
public static synchronized Plot createPlot(float [] yValues){
double[] tmp = new double[yValues.length];
for(int i=0; i<yValues.length; ++i)
tmp[i] = yValues[i];
return createPlot(tmp, "Current Plot", "Column", "Power");
}
public static synchronized Plot createPlot(double [] yValues){
return createPlot(yValues, "Average Row Weighting", "Column", "Power");
}
public static Plot createSplinePlot(BSpline spline){
int length = 100;
double [] x = new double[length];
double [] y = new double[length];
for (int i = 0; i< length; i++){
PointND p = spline.evaluate(((double) i) / (length));
x[i] = p.get(0);
y[i] = p.get(1);
}
return new Plot("Spline Plot", "x", "y", x, y);
}
public static Plot createPlot(double [] yValues, String title, String xLabel, String yLabel){
double [] xValues = new double [yValues.length];
double min = Double.MAX_VALUE;
double max = -Double.MAX_VALUE;
for (int i = 0; i < xValues.length; i ++){
min = (yValues[i] < min) ? yValues[i] : min;
max = (yValues[i] > max) ? yValues[i] : max;
xValues[i] = i + 1;
}
if (min == max){
max++;
}
Plot plot = new Plot(title, xLabel, yLabel, xValues, yValues, Plot.DEFAULT_FLAGS);
plot.setLimits(1, xValues.length, min, max);
return plot;
}
public static Plot createPlot(double [] xValues, double [] yValues, String title, String xLabel, String yLabel){
double miny = Double.MAX_VALUE;
double maxy = -Double.MAX_VALUE;
double minx = Double.MAX_VALUE;
double maxx = -Double.MAX_VALUE;
for (int i = 0; i < xValues.length; i ++){
miny = (yValues[i] < miny) ? yValues[i] : miny;
maxy = (yValues[i] > maxy) ? yValues[i] : maxy;
minx = (xValues[i] < minx) ? xValues[i] : minx;
maxx = (xValues[i] > maxx) ? xValues[i] : maxx;
}
if (miny == maxy){
maxy++;
}
if (minx == maxx){
maxx++;
}
Plot plot = new Plot(title, xLabel, yLabel, xValues, yValues, Plot.DEFAULT_FLAGS);
plot.setLimits(minx, maxx, miny, maxy);
return plot;
}
public static Plot createComplexPowerPlot(double [] yValues, String title){
double [] absValues = new double [yValues.length / 2];
for (int i = 0; i < absValues.length; i ++){
absValues[i] = FFTUtil.abs(i, yValues);
}
return createPlot(absValues, title, "Frequency (Center = +/- Nyquist)", "Power");
}
public static Plot createHalfComplexPowerPlot(double [] yValues, String title){
double [] absValues = new double [yValues.length / 4];
for (int i = 0; i < absValues.length; i ++){
absValues[i] = FFTUtil.abs(i, yValues);
}
return createPlot(absValues, title, "Frequency", "Power");
}
public static Plot createHalfComplexPowerPlot(double [] yValues, double [] xValues, String title){
double [] absValues = new double [yValues.length / 4];
for (int i = 0; i < absValues.length; i ++){
absValues[i] = FFTUtil.abs(i, yValues);
}
return createPlot(xValues, absValues, title, "Frequency", "Power");
}
public static Plot createComplexPowerPlot(double [] yValues){
return createComplexPowerPlot(yValues, "Complex Power Plot");
}
public static ImagePlus showGrid2D(Grid2D grid, String title){
if (grid instanceof MultiChannelGrid2D){
MultiChannelGrid2D multiChannelGrid2D = (MultiChannelGrid2D) grid;
ImagePlus imp = new ImagePlus();
ImageStack stack = new ImageStack(grid.getWidth(),grid.getHeight());
for (int c = 0; c < multiChannelGrid2D.getNumberOfChannels(); c++){
if (multiChannelGrid2D.getChannelNames() == null) {
stack.addSlice("Channel " + c,ImageUtil.wrapGrid2D(multiChannelGrid2D.getChannel(c)));
} else {
stack.addSlice(multiChannelGrid2D.getChannelNames()[c],ImageUtil.wrapGrid2D(multiChannelGrid2D.getChannel(c)));
}
}
imp.setStack(title, stack);
imp.show();
return imp;
}
return showImageProcessor(ImageUtil.wrapGrid2D(grid), title);
}
public static ImagePlus showImageProcessor(ImageProcessor image, String title){
ImagePlus imp = new ImagePlus();
ImageStack stack = new ImageStack(image.getWidth(),image.getHeight());
stack.addSlice(title, image);
imp.setStack(title, stack);
imp.show();
return imp;
}
public static ImagePlus showImageProcessor(ImageProcessor image){
return showImageProcessor(image, "Untitled Image");
}
public static ImagePlus showGrid3DX( Grid3D image, String title){
int[] size = image.getSize();
int nSlices = size[0];
int width = size[1];
int height = size[2];
System.out.print("Grid size: " + width + " X " + height + " X " + nSlices + ".\n" );
//this is more memory efficient to use grid3D directly
new ImageJ();
ImagePlus imp = ImageUtil.wrapGrid3D(image, "");
imp.show();
imp.setTitle( title );
/*
ImagePlus imp = new ImagePlus();
ImageStack stack = new ImageStack( width, height);
for( int n = 0 ; n < nSlices; n++){
//System.out.print("Slice " + n + " \n" );
FloatProcessor slice = new FloatProcessor( width, height);
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
slice.setf(x, y, image.getAtIndex(n, x, y));
}
}
stack.addSlice( " " + n, slice );
}
imp.setStack(title, stack);
imp.show();
*/
return imp;
}
public static ImagePlus showGrid3DX( Grid3D image){
return showGrid3DX( image, "Untitled Image");
}
public static ImagePlus showGrid3DZ( Grid3D image, String title){
int[] size = image.getSize();
int width = size[0];
int height = size[1];
int nSlices = size[2];
System.out.print("Grid size: " + width + " X " + height + " X " + nSlices + ".\n" );
//not sure if imageJ could rotate the grid3D data, so i copy and rearrange the 3D image to show cornal view
new ImageJ();
ImagePlus imp = new ImagePlus();
ImageStack stack = new ImageStack( width, height);
for( int n = 0 ; n < nSlices; n++){
//System.out.print("Slice " + n + " \n" );
FloatProcessor slice = new FloatProcessor( width, height);
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
slice.setf(x, y, image.getAtIndex(x, y, n));
}
}
stack.addSlice( " " + n, slice );
}
imp.setStack(title, stack);
imp.show();
return imp;
}
public static ImagePlus showGrid3DZ( Grid3D image){
return showGrid3DZ( image, "Untitled Image");
}
/**
* Takes an image overlay for Image Plus images and draws a cross at the 2D position defined by pos.
* @param ov the overlay to draw on
* @param pos the position where the cross is to be drawn
* @param slice the slice number where the point should be drawn
* @param crossSize half the width of the cross' bounding box
* @param col the color of the cross
*/
public static void printCrossAtPoint(Overlay ov, PointND pos, int slice, double crossSize, Color col){
double pos0 = pos.get(0)+0.5;
double pos1 = pos.get(1)+0.5;
Line line = new Line(pos0, pos1-crossSize, pos0, pos1+crossSize);
line.setStrokeWidth(1);
line.setStrokeColor(col);
line.setPosition(slice+1);
line.setStrokeWidth(0.5);
ov.add(line);
line = new Line(pos0-crossSize, pos1, pos0+crossSize, pos1);
line.setStrokeWidth(1);
line.setStrokeColor(col);
line.setPosition(slice+1);
line.setStrokeWidth(0.5);
ov.add(line);
}
public static Plot plotCompareGrayValues(ImageProcessor before, ImageProcessor after, Function func){
double [] xp = new double[before.getWidth()*before.getHeight()];
double [] yp = new double[before.getWidth()*before.getHeight()];
for (int i=0;i<before.getWidth();i++){
for (int j=0;j<before.getHeight();j++){
xp[before.getHeight()*i+j] = before.getPixelValue(i, j);
yp[before.getHeight()*i+j] = after.getPixelValue(i, j);
}
}
return createScatterPlot(xp, yp, func);
}
public static Plot createScatterPlot(double [] xCoords, double [] yCoords, Function func){
return createScatterPlot("Plot", xCoords, yCoords, func);
}
public static Plot createScatterPlot(String title, double [] xCoords, double [] yCoords, Function func){
double margin = 0.15;
int scale = 200;
func.fitToPoints(xCoords, yCoords);
float [] xVals = new float[scale];
float [] yVals = new float[scale];
double [] stats = DoubleArrayUtil.minAndMaxOfArray(xCoords);
double range = (stats[1] - stats[0]) * (1+(2*margin));
for (int i=0;i<scale;i++){
double x = stats[0] - (range * margin) + ((i + 0.0) / scale) * range;
xVals[i] = (float) x;
yVals[i] = (float) func.evaluate(x);
}
double avg = 0;
for (int i=0;i<xCoords.length;i++){
double other = func.evaluate(xCoords[i]);
avg += Math.pow(other - yCoords[i],2);
}
avg /= xCoords.length;
avg = Math.sqrt(avg);
Plot plot = new Plot(title + " (r = "+DoubleArrayUtil.correlateDoubleArrays(xCoords, yCoords)+" SSIM: " + DoubleArrayUtil.computeSSIMDoubleArrays(xCoords, yCoords) + ") Model: " + func.toString() + " standard deviation along model: " + avg, "X", "Y", xVals, yVals, Plot.DEFAULT_FLAGS);
plot.addPoints(xCoords, yCoords, Plot.CIRCLE);
return plot;
}
/**
* Creates a scatter plot for two given double arrays with the given title.
* @param title The Title
* @param xCoords the array for the x-coordinates
* @param yCoords the array for the y-coordinates
* @return the resulting plot.
*/
public static Plot createScatterPlot(String title, double [] xCoords, double [] yCoords){
Function func = new LinearFunction();
func.fitToPoints(xCoords, yCoords);
double margin = 0.15;
int scale = 200;
float [] xVals = new float[scale];
float [] yVals = new float[scale];
double [] stats = DoubleArrayUtil.minAndMaxOfArray(xCoords);
double range = (stats[1] - stats[0]) * (1+(2*margin));
for (int i=0;i<scale;i++){
double x = stats[0] - (range * margin) + ((i + 0.0) / scale) * range;
xVals[i] = (float) x;
yVals[i] = (float) func.evaluate(x);
}
Plot plot = new Plot(title + "Plot (r = "+DoubleArrayUtil.correlateDoubleArrays(xCoords, yCoords)+")", "X", "Y", xVals, yVals, Plot.DEFAULT_FLAGS);
plot.addPoints(xCoords, yCoords, Plot.CROSS);
return plot;
}
}
/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/