// // Frame_Selection.java // Creator : Nilanjan Ray (nray1@ualberta.ca) // Date : May 23, 2014 // // Works on RGB or 8bit grayscale stacks or hyper stacks // Works with single channel stacks or hyper stacks // User input (1): which channels to use? "all" means R, G, and B colors are used // User input (2): Number of images per time point (nImage). Default value is 3 // User input (3): Sigma for Gaussian smoothing. Default value 2. If input is less than 0.5, no smoothing is applied // // For a stack it selects one of nImage images. For a hyperstack it selects one of nImage images for each Zstack // It modifies the input stack or the hyperstack by deleting images, which were not selected // // Algorithm: Dynamic programming to enforce similarity between neighboring images // Publication: // import ij.*; import ij.process.*; import ij.gui.*; import java.awt.*; import ij.plugin.*; import ij.plugin.frame.*; public class Frame_Selection implements PlugIn { int nImage=3; int width=0; int height=0; int nSlice=0; int nFrame=0; int nTP=0; float sigmaGauss = 2; int[][] allIndices; public void run(String arg) { // get image from current window ImagePlus imp=WindowManager.getCurrentImage(); if (null == imp){ IJ.noImage(); return; } // Dialogbox to get some user inputs GenericDialog gd = new GenericDialog("Frame Selection"); final String[] imChannels = new String[] {"red", "green", "blue","all"} ; gd.addChoice("Channels to use: ", imChannels, imChannels[3]); gd.addNumericField("# of images / timepoint: ", nImage, 0); gd.addNumericField("Sigma for Gaussian smooothong: ", sigmaGauss, 1); gd.showDialog(); if (gd.wasCanceled()) return; final int itype = gd.getNextChoiceIndex(); nImage = (int)gd.getNextNumber(); sigmaGauss = (float)gd.getNextNumber(); if(nImage<2){ IJ.showMessage("Frame_Selection","# images / timepoint should be >= 2"); return; } // get dimensions of the stack / hyperstack and set variables int[] dim = imp.getDimensions(); if(dim[2] != 1){ IJ.showMessage("Frame_Selection","Requires # channels to be 1"); return; } width=dim[0]; height=dim[1]; nSlice=dim[3]; nFrame=dim[4]; nTP = nSlice / nImage; // get stack ImageStack stack=imp.getStack(); if(stack.getBitDepth() != 24 && stack.getBitDepth() != 8){ // suppots RGB and 8-bit grayscale image IJ.showMessage("Frame_Selection","Unsupported image format"); return; } // preprocess stack: most expensive component when blurring is applied ImageStack newStack = preprocess(stack); // indices for image selection allIndices = new int[nTP][nFrame]; ImageStack[] stackChannels = null; float[][][] cost = new float[nImage][nImage][nTP-1]; if(stack.getBitDepth()==24) // RGB image stackChannels = ChannelSplitter.splitRGB(newStack,false); for(int nf=0; nf=0.5) imDuplicate.blurGaussian(sigmaGauss); // Gaussian smoothing filter to suppress noise imDuplicate.setInterpolationMethod(ImageProcessor.BILINEAR); ImageProcessor im1 = imDuplicate.resize((int)(stack.getWidth()/resizeFactor),(int)(stack.getHeight()/resizeFactor)); stackOut.addSlice(im1); IJ.showProgress(n, nSlice*nFrame); } return stackOut; } private float[][][] buildCost(ImageStack stack, int whichFrame){ float[][][] cost = new float[nImage][nImage][nTP-1]; for(int n=0; n0) ip1 = stack.getProcessor((whichFrame-1)*nTP*nImage + n*nImage + allIndices[n][whichFrame-1]+1); for(int j=0;j0){ ImageProcessor ip2 = stack.getProcessor((whichFrame-1)*nTP*nImage + (n+1)*nImage + allIndices[n+1][whichFrame-1]+1); float c1 = mad((byte[])im1.getPixels(), (byte[])ip1.getPixels(), im1.getWidth()*im1.getHeight()); float c2 = mad((byte[])im2.getPixels(), (byte[])ip2.getPixels(), im1.getWidth()*im1.getHeight()); cost[i][j][n] += (c1+c2)/2; } } } } return cost; } private float mad(byte[] im1, byte[] im2, int len){ float pixelMad=0; for(int i=0; i=0;j--){ indices[j] = vi[indices[j+1]][j+1]; } return indices; } }