/* * Copyright (C) 2010-2014 Andreas Maier * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */ package edu.stanford.rsl.conrad.utils; //TODO: Use our own matrices instead of Jama.Matrix import java.awt.Frame; import java.awt.image.IndexColorModel; import java.util.ArrayList; import edu.stanford.rsl.conrad.data.Grid; import edu.stanford.rsl.conrad.data.numeric.Grid2D; import edu.stanford.rsl.conrad.data.numeric.Grid2DComplex; import edu.stanford.rsl.conrad.data.numeric.Grid3D; import edu.stanford.rsl.conrad.data.numeric.Grid4D; import edu.stanford.rsl.conrad.data.numeric.MultiChannelGrid2D; import edu.stanford.rsl.conrad.data.numeric.MultiChannelGrid3D; import edu.stanford.rsl.conrad.data.numeric.NumericGrid; import edu.stanford.rsl.conrad.data.numeric.NumericPointwiseOperators; import edu.stanford.rsl.conrad.filtering.ImageFilteringTool; import edu.stanford.rsl.conrad.geometry.General; import edu.stanford.rsl.conrad.geometry.trajectories.Trajectory; import edu.stanford.rsl.conrad.io.ImagePlusDataSink; import edu.stanford.rsl.conrad.io.ImagePlusProjectionDataSource; import edu.stanford.rsl.conrad.pipeline.ParallelImageFilterPipeliner; import ij.IJ; import ij.ImageJ; import ij.ImagePlus; import ij.ImageStack; import ij.gui.ImageWindow; import ij.measure.Calibration; import ij.process.FloatProcessor; import ij.process.ImageProcessor; public abstract class ImageUtil { public static void saveAs(Grid3D grid, String string) { ImagePlus imp = ImageUtil.wrapGrid3D(grid, string); IJ.save(imp, string); } /** * Method to display 3DGrids properly. This method is also able to handle multi channel data. * @param grid the grid * @param title the title * @return the ImagePlus */ public static ImagePlus wrapGrid3D(Grid3D grid, String title){ if (grid != null) { ImageStack stack; if (grid.getSubGrid(0) instanceof Grid2DComplex) stack = new ImageStack(grid.getSize()[0]*2, grid.getSize()[1], grid.getSize()[2]); else stack = new ImageStack(grid.getSize()[0], grid.getSize()[1], grid.getSize()[2]); if (grid.getSubGrid(0) instanceof MultiChannelGrid2D){ MultiChannelGrid2D first = (MultiChannelGrid2D) grid.getSubGrid(0); String [] names = first.getChannelNames(); // finalize the hyperstack ImagePlus hyper = new ImagePlus(); ImageStack hyperStack = new ImageStack(grid.getSize()[0], grid.getSize()[1]); int dimz = grid.getSize()[2]; for (int c=0;c < first.getNumberOfChannels(); c++){ for (int i=0; i< dimz; i++){ MultiChannelGrid2D current = (MultiChannelGrid2D) grid.getSubGrid(i); String frameTitle = "Slice z = " +(i-1); if (names != null){ frameTitle += " Channel: " + names[c]; } else { frameTitle += " Channel: " + c; } hyperStack.addSlice(frameTitle, ImageUtil.wrapGrid2D(current.getChannel(c))); } } setCalibrationToImagePlus(hyper); hyper.setStack(title, hyperStack); hyper.setDimensions(1, dimz, first.getNumberOfChannels()); hyper.setOpenAsHyperStack(true); return hyper; } else { for (int i=0; i< stack.getSize(); i++){ stack.setPixels(grid.getSubGrid(i).getBuffer(), i+1); } ImagePlus imagePlus = new ImagePlus(title, stack); setCalibrationToImagePlus(imagePlus, grid); return imagePlus; } } else return null; } public static ImagePlus wrapGrid4D(Grid4D grid, String title){ if (grid != null) { ImageStack stack = new ImageStack(grid.getSize()[0], grid.getSize()[1], grid.getSize()[2]); if (grid.getSubGrid(0) instanceof MultiChannelGrid3D){ MultiChannelGrid3D first = (MultiChannelGrid3D) grid.getSubGrid(0); String [] names = first.getChannelNames(); // finalize the hyperstack ImagePlus hyper = new ImagePlus(); ImageStack hyperStack = new ImageStack(grid.getSize()[0], grid.getSize()[1]); int dimz = grid.getSize()[2]; int dimy= grid.getSize()[1]; for (int c=0;c < first.getNumberOfChannels(); c++){ for (int i=0; i< dimz; i++){ for(int j=0; j<dimy; j++){ MultiChannelGrid3D current = (MultiChannelGrid3D) grid.getSubGrid(i); String frameTitle = "Slice z = " +(i-1); if (names != null){ frameTitle += " Channel: " + names[c]; } else { frameTitle += " Channel: " + c; } hyperStack.addSlice(frameTitle, ImageUtil.wrapGrid2D(current.getChannel(c).getSubGrid(j))); } } } setCalibrationToImagePlus(hyper); hyper.setStack(title, hyperStack); hyper.setDimensions(1, dimz, first.getNumberOfChannels()); hyper.setOpenAsHyperStack(true); return hyper; } else { for (int i=0; i< stack.getSize(); i++){ stack.setPixels(grid.getSubGrid(i).getBuffer(), i+1); } ImagePlus imagePlus = new ImagePlus(title, stack); setCalibrationToImagePlus(imagePlus, grid); return imagePlus; } } else return null; } private static void setCalibrationToImagePlus(ImagePlus image){ Calibration calibration = image.getCalibration(); calibration.xOrigin = Configuration.getGlobalConfiguration().getGeometry().getOriginInPixelsX(); calibration.yOrigin = Configuration.getGlobalConfiguration().getGeometry().getOriginInPixelsY(); calibration.zOrigin = Configuration.getGlobalConfiguration().getGeometry().getOriginInPixelsZ(); calibration.pixelWidth = Configuration.getGlobalConfiguration().getGeometry().getVoxelSpacingX(); calibration.pixelHeight = Configuration.getGlobalConfiguration().getGeometry().getVoxelSpacingY(); calibration.pixelDepth = Configuration.getGlobalConfiguration().getGeometry().getVoxelSpacingZ(); } private static void setCalibrationToImagePlus2D(ImagePlus image){ Calibration calibration = image.getCalibration(); calibration.xOrigin = Configuration.getGlobalConfiguration().getGeometry().getOriginInPixelsX(); calibration.yOrigin = Configuration.getGlobalConfiguration().getGeometry().getOriginInPixelsY(); calibration.pixelWidth = Configuration.getGlobalConfiguration().getGeometry().getVoxelSpacingX(); calibration.pixelHeight = Configuration.getGlobalConfiguration().getGeometry().getVoxelSpacingY(); } private static void setCalibrationToImagePlus(ImagePlus imagePlus, Grid grid){ Calibration calibration = imagePlus.getCalibration(); calibration.xOrigin = General.worldToVoxel(-grid.getOrigin()[0], grid.getSpacing()[0], 0); calibration.yOrigin = General.worldToVoxel(-grid.getOrigin()[1], grid.getSpacing()[1], 0); calibration.zOrigin = General.worldToVoxel(-grid.getOrigin()[2], grid.getSpacing()[2], 0); calibration.pixelWidth = grid.getSpacing()[0]; calibration.pixelHeight = grid.getSpacing()[1]; calibration.pixelDepth = grid.getSpacing()[2]; } private static void setCalibrationToImagePlus2D(ImagePlus imagePlus, Grid grid){ Calibration calibration = imagePlus.getCalibration(); calibration.xOrigin = General.worldToVoxel(-grid.getOrigin()[0], grid.getSpacing()[0], 0); calibration.yOrigin = General.worldToVoxel(-grid.getOrigin()[1], grid.getSpacing()[1], 0); calibration.pixelWidth = grid.getSpacing()[0]; calibration.pixelHeight = grid.getSpacing()[1]; } /** * Method to display 3DGrids properly. This method is also able to handle multi channel data. * @param grid the grid * @param title the title * @return the ImagePlus */ public static ImagePlus wrapGrid(NumericGrid grid, String title){ if (grid != null) { if (grid instanceof Grid3D) return wrapGrid3D((Grid3D) grid, title); else if (grid instanceof Grid2D){ if (grid instanceof MultiChannelGrid2D){ MultiChannelGrid2D first = (MultiChannelGrid2D) grid; String [] names = first.getChannelNames(); // finalize the hyperstack ImagePlus hyper = new ImagePlus(); ImageStack hyperStack = new ImageStack(grid.getSize()[0], grid.getSize()[1]); for (int c=0;c < first.getNumberOfChannels(); c++){ String frameTitle = ""; if (names != null){ frameTitle += "Channel: " + names[c]; } else { frameTitle += "Channel: " + c; } hyperStack.addSlice(frameTitle, ImageUtil.wrapGrid2D(first.getChannel(c))); } setCalibrationToImagePlus2D(hyper); hyper.setStack(title, hyperStack); hyper.setDimensions(1, 1, first.getNumberOfChannels()); hyper.setOpenAsHyperStack(true); return hyper; } else { FloatProcessor proc = wrapGrid2D((Grid2D) grid); ImagePlus iPlus = new ImagePlus(title, proc); setCalibrationToImagePlus2D(iPlus, grid); return iPlus; } } } return null; } public static FloatProcessor wrapGrid2D(Grid2D grid){ return new FloatProcessor(grid.getWidth(), grid.getHeight(), grid.getBuffer(), null); } public static Grid3D wrapImagePlus(ImagePlus image){ return wrapImagePlus(image, false, false); } public static Grid3D wrapImagePlus(ImagePlus image, boolean copy){ return wrapImagePlus(image, copy, false); } public static Grid3D wrapImagePlus(ImagePlus image, boolean copy, boolean invertStack){ int dimz = image.getDimensions()[3]; Grid3D revan = new Grid3D(image.getWidth(), image.getHeight(), dimz, false); if (image.isHyperStack()){ if (invertStack) throw new RuntimeException ("Stack inversion is not implemented for hyperstacks yet. Sorry"); int channels = image.getDimensions()[4]; String [] channelNames= new String[channels]; for (int c=0; c<channels;c++){ if (image.getImageStack().getSliceLabel((c*dimz)+1) != null){ String [] splitStrings =image.getImageStack().getSliceLabel((c*dimz)+1).split("Channel: "); if (splitStrings != null){ if (splitStrings.length > 1){ channelNames[c]=splitStrings[1]; } else { channelNames[c]=splitStrings[0]; } } else { channelNames[c]="Channel " + c; } } else { channelNames[c]="Channel " +c; } } for (int i = 0; i < dimz; i++){ MultiChannelGrid2D multiChannelGrid2D = new MultiChannelGrid2D(image.getWidth(), image.getHeight(), channels); multiChannelGrid2D.setChannelNames(channelNames); for (int c= 0; c<channels;c++){ Grid2D grid2d = wrapImagePlusSlice(image, (i+(dimz*c))+1, copy); multiChannelGrid2D.setChannel(c, grid2d); } revan.setSubGrid(i, multiChannelGrid2D); } } else { for (int i = 0; i < image.getDimensions()[3]; i++){ int pos = i; if (invertStack) pos = image.getDimensions()[3] - 1 - i; revan.setSubGrid(pos, wrapImagePlusSlice(image, i+1, copy)); } } revan.setSpacing(image.getCalibration().pixelWidth, image.getCalibration().pixelHeight, image.getCalibration().pixelDepth); double xorigin = General.voxelToWorld(-image.getCalibration().xOrigin, revan.getSpacing()[0], 0); double yorigin = General.voxelToWorld(-image.getCalibration().yOrigin, revan.getSpacing()[1], 0); double zorigin = General.voxelToWorld(-image.getCalibration().zOrigin, revan.getSpacing()[2], 0); //System.out.println(image.getCalibration().pixelWidth + " " + image.getCalibration().pixelHeight + " " + image.getCalibration().pixelDepth); revan.setOrigin(xorigin, yorigin, zorigin); return revan; } public static void applyConradImageCalibration(ImagePlus image, boolean isReconstruction){ Trajectory traj = Configuration.getGlobalConfiguration().getGeometry(); // FIXME: If we have a projection domain output the x and y spacing and origin refer to the detector // In case of a reconstruction output this dimensions should be equal to the global settings! // FIXME: Find a nicer way to handle difference between projection and reconstructed domain!! Calibration cal = image.getCalibration(); if (isReconstruction){ cal.setXUnit("mm"); cal.setYUnit("mm"); cal.setZUnit("mm"); cal.xOrigin = traj.getOriginInPixelsX(); cal.yOrigin = traj.getOriginInPixelsY(); cal.zOrigin = traj.getOriginInPixelsZ(); cal.pixelWidth = traj.getVoxelSpacingX(); cal.pixelHeight = traj.getVoxelSpacingY(); cal.pixelDepth = traj.getVoxelSpacingZ(); } else { cal.setXUnit("mm"); cal.setYUnit("mm"); cal.setZUnit("degree"); cal.xOrigin = traj.getDetectorOffsetU(); cal.yOrigin = traj.getDetectorOffsetV(); cal.zOrigin = 0;//traj.getPrimaryAngles()[0]/traj.getAverageAngularIncrement(); cal.pixelWidth = traj.getPixelDimensionX(); cal.pixelHeight = traj.getPixelDimensionY(); cal.pixelDepth = traj.getAverageAngularIncrement(); } } public static Grid2D wrapImagePlusSlice(ImagePlus image, int n, boolean copy){ ImageProcessor ip = image.getStack().getProcessor(n); return wrapImageProcessor(ip, copy); } public static Grid2D wrapImageProcessor(ImageProcessor ip){ return wrapImageProcessor(ip, false); } public static Grid2D wrapImageProcessor(ImageProcessor ip, boolean copy){ if (ip instanceof FloatProcessor){ if(!copy) return wrapFloatProcessor((FloatProcessor) ip); else return wrapFloatProcessor((FloatProcessor) ip.duplicate()); } else { return wrapFloatProcessor((FloatProcessor) ip.toFloat(0, null)); } } public static Grid2D wrapFloatProcessor(FloatProcessor fl){ return new Grid2D((float[])fl.getPixels(), fl.getWidth(), fl.getHeight()); } public static IndexColorModel getDefaultColorModel(){ byte[] r = new byte[256]; byte[] g = new byte[256]; byte[] b = new byte[256]; for(int i=0; i<256; i++) { r[i]=(byte)i; g[i]=(byte)i; b[i]=(byte)i; } return new IndexColorModel(8, 256, r, g, b); } public static float [] estimateConvolutionKernel(FloatProcessor before, FloatProcessor after, int kernelSize, int number){ //float [] revan = new float [kernelSize * kernelSize]; Jama.Matrix observationsAfter = new Jama.Matrix(1, number); Jama.Matrix observationsBefore = new Jama.Matrix(kernelSize * kernelSize, number); java.util.Random random = new java.util.Random(); int offset = (-1) * kernelSize / 2; // Fill arrays with randomly picked values from the image processors for (int n = 0; n < number; n++){ int x = kernelSize + (int) (random.nextDouble() * (before.getWidth() - (2 * kernelSize)) ); int y = kernelSize + (int) (random.nextDouble() * (before.getHeight() - (2 * kernelSize)) ); if (after.getPixelValue(x, y) != 0){ observationsAfter.set(0, n, after.getPixelValue(x, y)); int count = 0; for (int i=0;i<kernelSize;i++){ for (int j=0;j<kernelSize;j++){ observationsBefore.set(count, n, before.getPixelValue(offset + x + i, offset + y + j)); count++; } } } else { // redo this point; n--; } } //observationsBefore.print(8, 3); Jama.SingularValueDecomposition svd = observationsBefore.transpose().svd(); //System.out.println(observationsBefore.rank()); Jama.Matrix sigma = svd.getS().inverse(); Jama.Matrix inverse = svd.getV().times(sigma).times(svd.getU().transpose()).transpose(); Jama.Matrix kernelEstimate = observationsAfter.times(inverse); int count =0; for (int i=0;i<kernelSize;i++){ for (int j=0;j<kernelSize;j++){ if(kernelEstimate.get(0, count) < 0) kernelEstimate.set(0, count, kernelEstimate.get(0, count) * -1) ; count ++; } System.out.println(); } return ij.util.Tools.toFloat(kernelEstimate.getColumnPackedCopy()); } public static ArrayList<ImagePlus> getAvailableImagePlus(){ ArrayList<ImagePlus> list = new ArrayList<ImagePlus>(); Frame [] frames = ImageJ.getFrames(); for (Frame frame: frames){ if (frame instanceof ImageWindow){ ImageWindow window = (ImageWindow)frame; if (! window.isClosed()){ list.add(window.getImagePlus()); } } } return list; } public static ImagePlus [] getAvailableImagePlusAsArray(){ ArrayList<ImagePlus> list = getAvailableImagePlus(); ImagePlus [] array = new ImagePlus[list.size()]; for(int i=0; i< list.size(); i++){ array[i] = list.get(i); } return array; } /** * Determines the minimal value of a given ImagePlus. * * @param image the ImagePlus * @return the minimal value */ public static double minOfImagePlusValues(ImagePlus image){ if (image.getStackSize() == 1){ return minOfImageProcessor(image.getChannelProcessor()); } else { double [] mins = new double[image.getStackSize()]; for (int i = 0; i < image.getStackSize(); i++){ mins[i] = minOfImageProcessor(image.getStack().getProcessor(i+1)); } return DoubleArrayUtil.minOfArray(mins); } } /** * returns the minimal value of a given ImageProcessor * @param imp the ImageProcessor * @return the minimal value */ public static double minOfImageProcessor(ImageProcessor imp){ double min = Double.MAX_VALUE; for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ if (imp.getPixelValue(i, j) < min) { min = imp.getPixelValue(i, j); } } } return min; } /** * returns the minimal and the maxiaml value of a given ImageProcessor * @param imp the ImageProcessor * @return the minimal and the maximal value as double array */ public static double [] minAndMaxOfImageProcessor(ImageProcessor imp){ double [] revan = new double [2]; revan[0] = Double.MAX_VALUE; revan[1] = -Double.MAX_VALUE; for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ if (imp.getPixelValue(i, j) < revan[0]) { revan[0] = imp.getPixelValue(i, j); } if (imp.getPixelValue(i, j) > revan[1]) { revan[1] = imp.getPixelValue(i, j); } } } return revan; } /** * returns the minimal and the maxiaml value of a given ImageProcessor * @param image the ImageProcessor * @return the minimal and the maximal value as double array */ public static double [] minAndMaxOfImageProcessor(ImagePlus image){ double [] revan = new double [2]; revan[0] = Double.MAX_VALUE; revan[1] = Double.MIN_VALUE; for (int k = 1; k <= image.getStackSize(); k++){ ImageProcessor imp = image.getStack().getProcessor(k); for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ if (imp.getPixelValue(i, j) < revan[0]) { revan[0] = imp.getPixelValue(i, j); } if (imp.getPixelValue(i, j) > revan[1]) { revan[1] = imp.getPixelValue(i, j); } } } } return revan; } /** * Increases the pixel values of all pixels in the ImagePlus by addition of value * * @param image the ImagePlus * @param value the value to add */ public static void addToImagePlusValues(ImagePlus image, double value){ if (image.getStackSize() == 1){ image.getChannelProcessor().add(value); } else { for (int i = 0; i < image.getStackSize(); i++){ image.getStack().getProcessor(i+1).add(value); } } } /** * Adds the pixel values of all pixels in the ImagePlus by the second Image * * @param image the ImagePlus * @param image2 the values to add to */ public static void addImagePlusValues(ImagePlus image, ImagePlus image2){ if (image.getStackSize() == 1){ addProcessors(image.getChannelProcessor(), image2.getChannelProcessor()); } else { for (int i = 0; i < image.getStackSize(); i++){ addProcessors(image.getStack().getProcessor(i), image2.getStack().getProcessor(i)); } } } /** * Multiplies the pixel values of all pixels in the ImagePlus by value * * @param image the ImagePlus * @param value the value to multiply */ public static void multiplyImagePlusValues(ImagePlus image, double value){ if (image.getStackSize() == 1){ image.getChannelProcessor().multiply(value); } else { for (int i = 0; i < image.getStackSize(); i++){ image.getStack().getProcessor(i+1).multiply(value); } } } /** * Normalizes all pixel values of an ImagePlus to mean 0 and standard deviation 1. * * @param image the imagePlus * @return an array with two entries. The first one is the mean of all pixel values. The second one the standard deviation of all pixel values. */ public static double [] normalizeImagePlusCutOff(ImagePlus image, int numStandardDeviations){ if (image.getStackSize() == 1){ return ImageUtil.normalizeImageProcessorCutOff(image.getChannelProcessor(), numStandardDeviations); } else { double [] revan = new double [2]; double [] means = new double [image.getStackSize()]; double [] stddevs = new double [image.getStackSize()]; for (int i = 0; i < image.getStackSize(); i++){ double [] temp = ImageUtil.normalizeImageProcessorCutOff(image.getStack().getProcessor(i+1), numStandardDeviations); means[i] = temp[0]; stddevs[i] = temp[1]; } revan[0] = DoubleArrayUtil.computeMean(means); revan[1] = DoubleArrayUtil.computeMean(stddevs); return revan; } } /** * Normalizes all pixel values of an ImagePlus to mean 0 and standard deviation 1. * * @param image the imagePlus * @return an array with two times stack size entries. The respective first one is the mean of all pixel values. The second one the standard deviation of all pixel values. */ public static double [] normalizeImagePlus(ImagePlus image){ if (image.getStackSize() == 1){ return ImageUtil.normalizeImageProcessor(image.getChannelProcessor()); } else { double [] revan = new double [image.getStackSize() * 2]; for (int i = 0; i < image.getStackSize(); i++){ double [] temp = ImageUtil.normalizeImageProcessor(image.getStack().getProcessor(i+1)); revan[2 * i] = temp[0]; revan[(2 * i) + 1] = temp[1]; } return revan; } } /** * Normalizes all pixel values of an ImagePlus to minimum 0 and maximum 1. * * @param image the imagePlus * @return an array with two entries. The first one is the mean of all pixel values. The second one the standard deviation of all pixel values. */ public static double [] normalizeImagePlusMinMax(ImagePlus image){ if (image.getStackSize() == 1){ return ImageUtil.normalizeImageProcessorMinMax(image.getChannelProcessor()); } else { double [] revan = new double [2]; double [] means = new double [image.getStackSize()]; double [] stddevs = new double [image.getStackSize()]; for (int i = 0; i < image.getStackSize(); i++){ double [] temp = ImageUtil.normalizeImageProcessorMinMax(image.getStack().getProcessor(i+1)); means[i] = temp[0]; stddevs[i] = temp[1]; } revan[0] = DoubleArrayUtil.computeMean(means); revan[1] = DoubleArrayUtil.computeMean(stddevs); return revan; } } /** Normalizes an ImageProcessor to minimum 0 and maximum 1 * * @param imp the image processor * @return an double array with two entries. The first one is the mean and the second one is the standard deviation. */ public static double [] normalizeImageProcessorMinMax(ImageProcessor imp){ double [] revan = ImageUtil.minAndMaxOfImageProcessor(imp); imp.add((-1) * revan[0]); double range = revan[1] - revan[0]; imp.multiply(1 / range); //CONRAD.log("Min: " + revan[0] + " Max: " + revan[1]); return revan; } /** Normalizes an ImageProcessor to mean 0 and standard deviation 1 * * @param imp the image processor * @return an double array with two entries. The first one is the mean and the second one is the standard deviation. */ public static double [] normalizeImageProcessor(ImageProcessor imp){ double [] revan = new double [2]; for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ revan[0] += imp.getPixelValue(i, j); } } revan[0] /= (imp.getWidth() * imp.getHeight()); for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ double value = imp.getPixelValue(i, j) - revan[0]; imp.putPixelValue(i, j, value); revan[1] += Math.pow(value, 2); } } revan[1] /= imp.getWidth() * imp.getHeight(); revan[1] = Math.sqrt(revan[1]); for (int i=0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ double value = imp.getPixelValue(i, j) / revan[1]; imp.putPixelValue(i, j, value); } } //CONRAD.log("Mean: " + revan[0] + " Standard deviation: " + revan[1]); return revan; } /** Normalizes an ImageProcessor to mean 0 and standard deviation 1. Cuts off values greater than a certain amount of standard deviations. * * @param imp the image processor * @param numStandardDeviations Number of standard deviations after which the values are cut off * @return an double array with two entries. The first one is the mean and the second one is the standard deviation. */ public static double [] normalizeImageProcessorCutOff(ImageProcessor imp, int numStandardDeviations){ double [] revan = new double [2]; for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ revan[0] += imp.getPixelValue(i, j); } } revan[0] /= (imp.getWidth() * imp.getHeight()); for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ double value = imp.getPixelValue(i, j) - revan[0]; imp.putPixelValue(i, j, value); revan[1] += Math.pow(value, 2); } } revan[1] /= imp.getWidth() * imp.getHeight(); revan[1] = Math.sqrt(revan[1]); for (int i=0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ double value = imp.getPixelValue(i, j) / revan[1]; if (value > numStandardDeviations){ value = numStandardDeviations; } if (value < numStandardDeviations * (-1)){ value = - numStandardDeviations; } imp.putPixelValue(i, j, value); } } //CONRAD.log("Mean: " + revan[0] + " Standard deviation: " + revan[1]); return revan; } /** Normalizes an ImageProcessor to mean 0 and standard deviation 1 * * @param imp the image processor * @return an double array with two entries. The first one is the mean and the second one is the standard deviation. */ public static double [] computeMeanAndStandardDeviation(ImageProcessor imp){ double [] revan = new double [2]; for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ revan[0] += imp.getPixelValue(i, j); } } revan[0] /= imp.getWidth() * imp.getHeight(); for (int i = 0; i < imp.getWidth(); i++){ for (int j = 0; j < imp.getHeight(); j++){ revan[1] += Math.pow(imp.getPixelValue(i, j) - revan[0], 2); } } revan[1] /= imp.getWidth() * imp.getHeight(); revan[1] = Math.sqrt(revan[1]); return revan; } public static FloatProcessor divideImages(ImagePlus nominator, ImagePlus denominator){ FloatProcessor information = (FloatProcessor) nominator.getChannelProcessor().duplicate(); for (int i = 0; i < information.getWidth(); i++) { for (int j = 0; j < information.getHeight(); j++) { double value = Double.MAX_VALUE; if (nominator.getChannelProcessor().getPixelValue(i, j) != 0) { value = nominator.getChannelProcessor().getPixelValue(i, j) / denominator.getChannelProcessor().getPixelValue(i, j); } information.putPixelValue(i, j, value); } } return information; } public static FloatProcessor divideImages(ImageProcessor nominator, ImageProcessor denominator){ FloatProcessor information = (FloatProcessor) nominator.duplicate(); for (int i = 0; i < information.getWidth(); i++) { for (int j = 0; j < information.getHeight(); j++) { double value = Double.MAX_VALUE; if (nominator.getPixelValue(i, j) != 0) { value = nominator.getPixelValue(i, j) / denominator.getPixelValue(i, j); } information.putPixelValue(i, j, value); } } return information; } public static FloatProcessor multiplyImages(ImagePlus nominator, ImagePlus denominator, int n){ FloatProcessor information = (FloatProcessor) denominator.getStack().getProcessor(n).duplicate(); for (int i = 0; i < information.getWidth(); i++) { for (int j = 0; j < information.getHeight(); j++) { double value = Double.MAX_VALUE; if (nominator.getChannelProcessor().getPixelValue(i, j) != Float.NaN) { value = nominator.getStack().getProcessor(n).getPixelValue(i, j) * denominator.getStack().getProcessor(n).getPixelValue(i, j); } information.putPixelValue(i, j, value); } } return information; } public static FloatProcessor multiplyImages(ImageProcessor nominator, ImageProcessor denominator){ FloatProcessor information = denominator.duplicate().toFloat(0, null); for (int i = 0; i < information.getWidth(); i++) { for (int j = 0; j < information.getHeight(); j++) { double value = Double.MAX_VALUE; if (nominator.getPixelValue(i, j) != Float.NaN) { value = nominator.getPixelValue(i, j) * denominator.getPixelValue(i, j); } information.putPixelValue(i, j, value); } } return information; } /** * Divides two image processors in the two given ImagePlus. Will allocate a new FloatProcessor for the result. * * @param nominator the nominator * @param denominator the denominator * @param n the index of the stack (starts with 0). * @return the division result. */ public static FloatProcessor divideImages(ImagePlus nominator, ImagePlus denominator, int n){ FloatProcessor information = (FloatProcessor) nominator.getStack().getProcessor(n+1).duplicate(); for (int i = 0; i < information.getWidth(); i++) { for (int j = 0; j < information.getHeight(); j++) { double value = Double.MAX_VALUE; if (nominator.getChannelProcessor().getPixelValue(i, j) != 0) { value = nominator.getStack().getProcessor(n+1).getPixelValue(i, j) / denominator.getStack().getProcessor(n+1).getPixelValue(i, j); } information.putPixelValue(i, j, value); } } return information; } /** * Adds two image processors. Works in place. First ImageProcessor is modified. * * @param left the first processor * @param right the second processor */ public static void addProcessors(ImageProcessor left, ImageProcessor right){ for (int i = 0; i < left.getWidth(); i++) { for (int j = 0; j < left.getHeight(); j++) { double value = Double.MAX_VALUE; if (left.getPixelValue(i, j) != Float.NaN) { value = left.getPixelValue(i, j) + right.getPixelValue(i, j); } left.putPixelValue(i, j, value); } } } /** * Subtracts two image processors. Works in place. First ImageProcessor is modified. * * @param left the first processor * @param right the second processor */ public static void subtractProcessors(ImageProcessor left, ImageProcessor right){ for (int i = 0; i < left.getWidth(); i++) { for (int j = 0; j < left.getHeight(); j++) { double value = Double.MAX_VALUE; if (left.getPixelValue(i, j) != Float.NaN) { value = left.getPixelValue(i, j) - right.getPixelValue(i, j); } left.putPixelValue(i, j, value); } } } /** * Creates a unigue String representation of an array of ImagePlus * @param images * @return the String [] */ public static String[] getStringArrayRepresentation(ImagePlus[] images) { String [] names = new String[images.length]; for (int i= 0; i < names.length; i++){ names[i]= images[i].toString() + " (Image " + i + ")"; } return names; } /** * Returns the matching image given it's String representation * @param name the String representation of the image * @param images the array of ImagePlus * @return the ImagePlus */ public static ImagePlus getImagePlusFromString(String name, ImagePlus [] images){ ImagePlus selection = null; for (int i= 0; i < images.length; i++){ if (name.equals(images[i].toString() + " (Image " + i + ")")){ selection = images[i]; break; } } return selection; } /** * Creates a two-dimensional symmetric gaussian filter kernel equal to the Matlab method * @param sizeX The kernel x size * @param sizeY The kernel y size * @param sigma The kernel's standard deviation value * @return the grid2d */ public static Grid2D create2DGauss(int sizeX, int sizeY, double sigma){ Grid2D out = new Grid2D(sizeX,sizeY); //double maxEps = Double.MIN_VALUE; float sum = 0; for (int y = 0; y < out.getHeight(); y++) { for (int x = 0; x < out.getWidth(); x++) { double xx = x - (double)out.getWidth()/2.0 + 0.5; double yy = y - (double)out.getHeight()/2.0 + 0.5; //out.setAtIndex(x, y, (float)(((double)size) * Math.exp(-0.5*(xx*xx+yy*yy)/sigma/sigma)/(2*Math.PI*Math.sqrt(2)*sigma))); float val = (float)Math.exp(-(xx*xx+yy*yy)/2/sigma/sigma); out.setAtIndex(x, y, val); sum += val; //if (val > maxEps) // maxEps = val; } } // set very small values to 0 and normalize kernel to sum 1 /* maxEps *= 1.1921e-07; for (int y = 0; y < out.getHeight(); y++) { for (int x = 0; x < out.getWidth(); x++) { if (out.getAtIndex(x, y) > maxEps) out.setAtIndex(x, y, out.getAtIndex(x, y)/sum); else out.setAtIndex(x,y,0); } }*/ NumericPointwiseOperators.divideBy(out, sum); return out; } /** * * @param inputStack the input stack * @param filter the tool to apply * @return Grid3D the output of the parallel processing */ public static Grid3D applyFilterInParallel(Grid3D inputStack, ImageFilteringTool filter){ // run all the filters in parallel on the slices return applyFilterInParallel(inputStack, filter, false); } /** * * @param inputStack the input stack * @param filter the tool to apply * @return Grid3D the output of the parallel processing */ public static Grid3D applyFilterInParallel(Grid3D inputStack, ImageFilteringTool filter, boolean showStatus){ // run all the filters in parallel on the slices return applyFiltersInParallel(inputStack, new ImageFilteringTool[] {filter}, showStatus); } /** * * @param inputStack * @param filters * @return Grid3D the output of the parallel processing */ public static Grid3D applyFiltersInParallel(Grid3D inputStack, ImageFilteringTool[] filters){ return applyFiltersInParallel(inputStack, filters, false); } /** * * @param inputStack * @param filters * @return Grid3D the output of the parallel processing */ public static Grid3D applyFiltersInParallel(Grid3D inputStack, ImageFilteringTool[] filters, boolean showStatus){ // run all the filters in parallel on the slices Grid3D outputStack=null; try { ImagePlusDataSink sink = new ImagePlusDataSink(); sink.configure(); ImagePlusProjectionDataSource pSource = new ImagePlusProjectionDataSource(); pSource.setImage(inputStack); ParallelImageFilterPipeliner filteringPipeline = new ParallelImageFilterPipeliner(pSource, filters, sink); filteringPipeline.project(showStatus); outputStack = sink.getResult(); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return outputStack; } }