/* * This test is illustrative of an issue in IJ1 where the internal stack gets * deleted in certain cases. If you setProcessor() on an ImagePlus with a * stack of 1 slice the stack gets deleted. A subsequent call to getStack() * will hatch a new one using the pixels of the current ImageProcessor. * Basically to avoid problems the legacy layer should never call * setProcessor() or if it does it should do a getStack() rather than cache * calls to previous getStack() calls. */ @Test public void testStackKiller() { final ImageStack stack = new ImageStack(2, 2); final byte[] slice = new byte[] {1, 2, 3, 4}; stack.addSlice("one slice", slice); final ImagePlus imp = new ImagePlus("fred", stack); assertEquals(stack, imp.getStack()); final byte[] slice2 = new byte[] {5, 6, 7, 8}; final ByteProcessor proc = new ByteProcessor(2, 2, slice2, null); imp.setProcessor(proc); final ImageStack secondStack = imp.getStack(); assertNotSame(stack, secondStack); assertEquals(slice, stack.getPixels(1)); assertEquals(slice2, secondStack.getPixels(1)); }
/** Performs actual projection using specified method. */ public void doProjection() { if (imp == null) return; sliceCount = 0; if (method < AVG_METHOD || method > MEDIAN_METHOD) method = AVG_METHOD; for (int slice = startSlice; slice <= stopSlice; slice += increment) sliceCount++; if (method == MEDIAN_METHOD) { projImage = doMedianProjection(); return; } // Create new float processor for projected pixels. FloatProcessor fp = new FloatProcessor(imp.getWidth(), imp.getHeight()); ImageStack stack = imp.getStack(); RayFunction rayFunc = getRayFunction(method, fp); if (IJ.debugMode == true) { IJ.log("\nProjecting stack from: " + startSlice + " to: " + stopSlice); } // Determine type of input image. Explicit determination of // processor type is required for subsequent pixel // manipulation. This approach is more efficient than the // more general use of ImageProcessor's getPixelValue and // putPixel methods. int ptype; if (stack.getProcessor(1) instanceof ByteProcessor) ptype = BYTE_TYPE; else if (stack.getProcessor(1) instanceof ShortProcessor) ptype = SHORT_TYPE; else if (stack.getProcessor(1) instanceof FloatProcessor) ptype = FLOAT_TYPE; else { IJ.error("Z Project", "Non-RGB stack required"); return; } // Do the projection. for (int n = startSlice; n <= stopSlice; n += increment) { IJ.showStatus("ZProjection " + color + ": " + n + "/" + stopSlice); IJ.showProgress(n - startSlice, stopSlice - startSlice); projectSlice(stack.getPixels(n), rayFunc, ptype); } // Finish up projection. if (method == SUM_METHOD) { fp.resetMinAndMax(); projImage = new ImagePlus(makeTitle(), fp); } else if (method == SD_METHOD) { rayFunc.postProcess(); fp.resetMinAndMax(); projImage = new ImagePlus(makeTitle(), fp); } else { rayFunc.postProcess(); projImage = makeOutputImage(imp, fp, ptype); } if (projImage == null) IJ.error("Z Project", "Error computing projection."); }
private byte getPixel( final ImageStack image, final int x, final int y, final int z, final int w, final int h, final int d) { if (x >= 0 && x < w && y >= 0 && y < h && z >= 0 && z < d) return ((byte[]) image.getPixels(z + 1))[x + y * w]; return (byte) 255; } /* end getPixel */
private ImagePlus findSurfaceVoxels(final ImagePlus imp) { final int w = imp.getWidth(); final int h = imp.getHeight(); final int d = imp.getImageStackSize(); final ImageStack stack = imp.getImageStack(); final ImageStack surfaceStack = new ImageStack(w, h, d); for (int z = 0; z < d; z++) { IJ.showStatus("Finding surface voxels"); final byte[] pixels = (byte[]) stack.getPixels(z + 1); surfaceStack.setPixels(pixels.clone(), z + 1); final ImageProcessor surfaceIP = surfaceStack.getProcessor(z + 1); for (int y = 0; y < h; y++) { checkNeighbours: for (int x = 0; x < w; x++) { if (getPixel(stack, x, y, z, w, h, d) == (byte) 0) continue; for (int nz = -1; nz < 2; nz++) { final int znz = z + nz; for (int ny = -1; ny < 2; ny++) { final int yny = y + ny; for (int nx = -1; nx < 2; nx++) { final int xnx = x + nx; final byte pixel = getPixel(stack, xnx, yny, znz, w, h, d); if (pixel == (byte) 0) continue checkNeighbours; } } } // we checked all the neighbours for a 0 // but didn't find one, so this is not a surface voxel surfaceIP.set(x, y, (byte) 1); } } } // turn all the 1's into 0's final int wh = w * h; for (int z = 0; z < d; z++) { IJ.showStatus("Finding surface voxels"); final ImageProcessor ip = surfaceStack.getProcessor(z + 1); for (int i = 0; i < wh; i++) { if (ip.get(i) == (byte) 1) ip.set(i, (byte) 0); } } final ImagePlus surfaceImp = new ImagePlus("Surface"); surfaceImp.setStack(surfaceStack); surfaceImp.setCalibration(imp.getCalibration()); return surfaceImp; }
/** * Saito-Toriwaki algorithm for Euclidian Distance Transformation. Direct application of Algorithm * 1. Bob Dougherty 8/8/2006 * * <ul> * <li>Version S1A: lower memory usage. * <li>Version S1A.1 A fixed indexing bug for 666-bin data set * <li>Version S1A.2 Aug. 9, 2006. Changed noResult value. * <li>Version S1B Aug. 9, 2006. Faster. * <li>Version S1B.1 Sept. 6, 2006. Changed comments. * <li>Version S1C Oct. 1, 2006. Option for inverse case. <br> * Fixed inverse behavior in y and z directions. * <li>Version D July 30, 2007. Multithread processing for step 2. * </ul> * * <p>This version assumes the input stack is already in memory, 8-bit, and outputs to a new * 32-bit stack. Versions that are more stingy with memory may be forthcoming. * * @param imp 8-bit (binary) ImagePlus */ private float[][] geometryToDistanceMap(ImagePlus imp, boolean inv) { final int w = imp.getWidth(); final int h = imp.getHeight(); final int d = imp.getStackSize(); int nThreads = Runtime.getRuntime().availableProcessors(); // Create references to input data ImageStack stack = imp.getStack(); byte[][] data = new byte[d][]; for (int k = 0; k < d; k++) data[k] = (byte[]) stack.getPixels(k + 1); // Create 32 bit floating point stack for output, s. Will also use it // for g in Transformation 1. float[][] s = new float[d][]; for (int k = 0; k < d; k++) { ImageProcessor ipk = new FloatProcessor(w, h); s[k] = (float[]) ipk.getPixels(); } float[] sk; // Transformation 1. Use s to store g. IJ.showStatus("EDT transformation 1/3"); Step1Thread[] s1t = new Step1Thread[nThreads]; for (int thread = 0; thread < nThreads; thread++) { s1t[thread] = new Step1Thread(thread, nThreads, w, h, d, inv, s, data); s1t[thread].start(); } try { for (int thread = 0; thread < nThreads; thread++) { s1t[thread].join(); } } catch (InterruptedException ie) { IJ.error("A thread was interrupted in step 1 ."); } // Transformation 2. g (in s) -> h (in s) IJ.showStatus("EDT transformation 2/3"); Step2Thread[] s2t = new Step2Thread[nThreads]; for (int thread = 0; thread < nThreads; thread++) { s2t[thread] = new Step2Thread(thread, nThreads, w, h, d, s); s2t[thread].start(); } try { for (int thread = 0; thread < nThreads; thread++) { s2t[thread].join(); } } catch (InterruptedException ie) { IJ.error("A thread was interrupted in step 2 ."); } // Transformation 3. h (in s) -> s IJ.showStatus("EDT transformation 3/3"); Step3Thread[] s3t = new Step3Thread[nThreads]; for (int thread = 0; thread < nThreads; thread++) { s3t[thread] = new Step3Thread(thread, nThreads, w, h, d, inv, s, data); s3t[thread].start(); } try { for (int thread = 0; thread < nThreads; thread++) { s3t[thread].join(); } } catch (InterruptedException ie) { IJ.error("A thread was interrupted in step 3 ."); } // Find the largest distance for scaling // Also fill in the background values. float distMax = 0; final int wh = w * h; float dist; for (int k = 0; k < d; k++) { sk = s[k]; for (int ind = 0; ind < wh; ind++) { if (((data[k][ind] & 255) < 128) ^ inv) { sk[ind] = 0; } else { dist = (float) Math.sqrt(sk[ind]); sk[ind] = dist; distMax = (dist > distMax) ? dist : distMax; } } } IJ.showProgress(1.0); IJ.showStatus("Done"); return s; }
public void Calc_5Fr(ImagePlus imp1, ImagePlus imp2) { if (imp1.getType() != imp2.getType()) { error(); return; } if (imp1.getType() == 0) { // getType returns 0 for 8-bit, 1 for 16-bit bitDepth = "8-bit"; Prefs.set("ps.bitDepth", bitDepth); } else { bitDepth = "16-bit"; Prefs.set("ps.bitDepth", bitDepth); } int width = imp1.getWidth(); int height = imp1.getHeight(); if (width != imp2.getWidth() || height != imp2.getHeight()) { error(); return; } ImageStack stack1 = imp1.getStack(); // if (bgStackTitle != "NoBg") ImageStack stack2 = imp2.getStack(); ImageStack stack2 = imp2.getStack(); ImageProcessor ip = imp1.getProcessor(); int dimension = width * height; byte[] pixB; short[] pixS; float[][] pixF = new float[5][dimension]; float[][] pixFBg = new float[5][dimension]; float a; float b; float den; float aSmp; float bSmp; float denSmp; float aBg; float bBg; float denBg; float retF; float azimF; byte[] retB = new byte[dimension]; short[] retS = new short[dimension]; byte[] azimB = new byte[dimension]; short[] azimS = new short[dimension]; // Derived Variables: float swingAngle = 2f * (float) Math.PI * swing; float tanSwingAngleDiv2 = (float) Math.tan(swingAngle / 2.f); float tanSwingAngleDiv2DivSqrt2 = (float) (Math.tan(swingAngle / 2.f) / Math.sqrt(2)); float wavelengthDiv2Pi = wavelength / (2f * (float) Math.PI); // get the pixels of each slice in the stack and convert to float for (int i = 0; i < 5; i++) { if (bitDepth == "8-bit") { pixB = (byte[]) stack1.getPixels(i + 3); for (int j = 0; j < dimension; j++) pixF[i][j] = 0xff & pixB[j]; if (bgStackTitle != "NoBg") { pixB = (byte[]) stack2.getPixels(i + 3); for (int j = 0; j < dimension; j++) pixFBg[i][j] = 0xff & pixB[j]; } } else { pixS = (short[]) stack1.getPixels(i + 3); for (int j = 0; j < dimension; j++) pixF[i][j] = (float) pixS[j]; if (bgStackTitle != "NoBg") { pixS = (short[]) stack2.getPixels(i + 3); for (int j = 0; j < dimension; j++) pixFBg[i][j] = (float) pixS[j]; } } } // Algorithm // terms a and b for (int j = 0; j < dimension; j++) { denSmp = (pixF[1][j] + pixF[2][j] + pixF[3][j] + pixF[4][j] - 4 * pixF[0][j]) / 2; denBg = denSmp; a = (pixF[4][j] - pixF[1][j]); aSmp = a; aBg = a; b = (pixF[2][j] - pixF[3][j]); bSmp = b; bBg = b; if (bgStackTitle != "NoBg") { denBg = (pixFBg[1][j] + pixFBg[2][j] + pixFBg[3][j] + pixFBg[4][j] - 4 * pixFBg[0][j]) / 2; aBg = pixFBg[4][j] - pixFBg[1][j]; bBg = pixFBg[2][j] - pixFBg[3][j]; } // Special case of sample retardance half wave, denSmp = 0 if (denSmp == 0) { retF = (float) wavelength / 4; azimF = (float) (a == 0 & b == 0 ? 0 : (azimRef + 90 + 90 * Math.atan2(a, b) / Math.PI) % 180); } else { // Retardance, the background correction can be improved by separately considering sample // retardance values larger than a quarter wave if (bgStackTitle != "NoBg") { a = aSmp / denSmp - aBg / denBg; b = bSmp / denSmp - bBg / denBg; } else { a = aSmp / denSmp; b = bSmp / denSmp; } retF = (float) Math.atan(tanSwingAngleDiv2 * Math.sqrt(a * a + b * b)); if (denSmp < 0) retF = (float) Math.PI - retF; retF = retF * wavelengthDiv2Pi; // convert to nm if (retF > retCeiling) retF = retCeiling; // Orientation if ((bgStackTitle == "NoBg") || ((bgStackTitle != "NoBg") && (Math.abs(denSmp) < 1))) { a = aSmp; b = bSmp; } azimF = (float) (a == 0 & b == 0 ? 0 : (azimRef + 90 + 90 * Math.atan2(a, b) / Math.PI) % 180); } if (bitDepth == "8-bit") retB[j] = (byte) (((int) (255 * retF / retCeiling)) & 0xff); else retS[j] = (short) (4095 * retF / retCeiling); if (mirror == "Yes") azimF = 180 - azimF; if (bitDepth == "8-bit") azimB[j] = (byte) (((int) azimF) & 0xff); else azimS[j] = (short) (azimF * 10f); } // show the resulting images in slice 1 and 2 imp1.setSlice(3); if (bitDepth == "8-bit") { stack1.setPixels(retB, 1); stack1.setPixels(azimB, 2); } else { stack1.setPixels(retS, 1); stack1.setPixels(azimS, 2); } imp1.setSlice(1); IJ.selectWindow(imp1.getTitle()); Prefs.set("ps.sampleStackTitle", sampleStackTitle); Prefs.set("ps.bgStackTitle", bgStackTitle); Prefs.set("ps.mirror", mirror); Prefs.set("ps.wavelength", wavelength); Prefs.set("ps.swing", swing); Prefs.set("ps.retCeiling", retCeiling); Prefs.set("ps.azimRef", azimRef); Prefs.savePreferences(); }